Exemplo n.º 1
0
    def __init__(self, port=None, calib_file=None, calib_cf=[0,1,0], debug=False):
        """calib_file: rontec calibration file, should be like:
        
        #calibration factors
        0 0.00410435 1.2e-9
        """
        self.sl = Serial(port, baudrate=38400, eol='\r')
        self.calib_done = False
        self.preset_erange=None
        self.live_t = False
        self.roi_channel = None
        self.type = None
        self.times = {}
        self.debug = debug
        self.roi_dict = {}

        #connect to the hardware and make a reset
        self.reset(calib_file, calib_cf)
Exemplo n.º 2
0
class Rontec:

    ERANGE = {0:"10keV", 1:"20keV", 2:"40keV", 3:"80keV"}
    MCA_DEFAULTS = {"chmin":0, "chmax":4095}
    MCA_ERROR = {0: "General error or buffer overflow",
                 1: "Unknown command",
                 2: "Numeric parameter expected",
                 4: "Boolean parameter expected",
                 5: "Additional parameter expected",
                 6: "Unexpected parameter or character",
                 7: "Illegal numeric value",
                 8: "Unknown subcommand",
                 9: "Function not implemented or no hardware support",
                 13: "Hardware error",
                 14: "Illegal baud rate"}
                 
    def __init__(self, port=None, calib_file=None, calib_cf=[0,1,0], debug=False):
        """calib_file: rontec calibration file, should be like:
        
        #calibration factors
        0 0.00410435 1.2e-9
        """
        self.sl = Serial(port, baudrate=38400, eol='\r')
        self.calib_done = False
        self.preset_erange=None
        self.live_t = False
        self.roi_channel = None
        self.type = None
        self.times = {}
        self.debug = debug
        self.roi_dict = {}

        #connect to the hardware and make a reset
        self.reset(calib_file, calib_cf)

    def exit(self):
        self.sl.close()

    def _check_answer(self, asw, cmd):
        if not asw.startswith('!') or asw.startswith('!E'):
            e = 'Invalid answer from %s'% cmd
            if asw.startswith('!E'):
                _,err = asw.split(": ")
                e = e+': %s' %  Rontec.MCA_ERROR[int(err)]
            raise RuntimeError(e)
        else:
            if self.debug:
                print asw
            return asw

    def _calib_getch(self, energy):
        if not self.calib[2]:
            if self.calib[1]:
                return int((energy - self.calib[0])/self.calib[1])
            else:
                return 0
        cc = 1
        print self.calib
        if self.calib[1] > 0:
            cc = -1
        return int((self.calib[1]-cc*math.sqrt(math.pow(self.calib[1], 2)-4*(self.calib[0]-energy)*self.calib[2]))/(2*self.calib[2]))


    def _calib_getE(self, chan):
        return self.calib[0] + self.calib[1]*chan + self.calib[2]*math.pow(chan,2)

    def set_calibration(self, fname=None, calib_cf=None):
        """Set the energy calibration. Give a filename or a list of calibration factors.
           
           Kwargs:
               fname (str): optional filename with the calibration factors
               calib_cf (list): optional list of calibration factors
        
           Returns:
               list. Calibration factors.

           Raises:
               IOError, ValueError
        """
        if fname is None and calib_cf is None:
            raise ValueError("Either calibration file or calibration factors must be provided.")
        if isinstance(fname, str):
            calib_cf = []
            try:
                f = open(fname)
                for line in f:
                    if not line.startswith('#'):
                        calib_cf = map(float, line.split())
                    else:
                        pass
                f.close()
            except IOError:
                raise IOError('Cannot open %s' % fname)
        self.calib = calib_cf
        self.calib_done = calib_cf != [0,1,0]
        return self.calib

    def get_calibration(self):
        """Get the calibration factors list

        Returns:
            list. Calibration factors.
        """
        return self.calib
                
    def reset(self, calib_file=None, calib_cf=[0,1,0]):
        """Reset the controller and sets calibration

        Kwargs:
            calib_file (str): optional filename with the calibration factors
            calib_cf (list): optional list of calibration factors

        Returns:
            None

        Raises:
            RuntimeError, IOError, ValueError
        """
        self.sl.flush()

        #at reset Rontec sends two lines, we want the second one only
        asw = str(self.sl.write_readline("$##\r"))
        if not asw.__len__():
            raise RuntimeError('Invalid answer from reset')
        asw = self.sl.readline()
        if not asw.__len__():
            raise RuntimeError('Invalid answer from reset')
        type = asw.split()
        try:
            type.__len__() > 1
            #check if MAX/MMAX...
            if type[3] == "MMAX" or type[3] == "XDN":
                self.type = 2
            elif type[3] == "MAX":
                self.type = 1
        except Exception:
            raise RuntimeError('Invalid answer from reset')

        #check if live time accepted - depends on the firmware version
        self.sl.flush()
        asw = self.sl.write_readline("$LS\r")
        try:
            self._check_answer(asw, 'reset:livetime')
            self.live_t = True
        except RuntimeError as e:
            print e
            self.live_t = False

        self.chmin = Rontec.MCA_DEFAULTS["chmin"]
        self.chmax = Rontec.MCA_DEFAULTS["chmax"]
        if calib_file:
            self.set_calibration(calib_file)
        else:
            self.set_calibration(calib_cf=calib_cf)
        self.emin = self._calib_getE(self.chmin)
        self.emax = self._calib_getE(self.chmax)

        #last but not least - format the reading to be 4 bytes/channel, no clear after reading
        self.stop_acq()
        self.sl.flush()
        asw = str(self.sl.write_readline("$SM 4 0\r"))
        self._check_answer(asw, 'reset:set read fromat/mode')

    def set_presets(self, **kwargs):
        """Set presets parameters

           Keyword Args:
              ctime (float): real time [s]
              erange (int): the energy range, 0: 10 keV, 1: 20 keV, 2: 40 keV, 3: 80 keV
              fname (str): file name (full path) to save the raw data
           
           Returns:
              None
        """
        if kwargs.has_key("ctime"):
            #we want ms or cps - this is the IC/OC counters time(gate)
            ms_time = kwargs["ctime"] * 1000
            if ms_time < 0.001 or ms_time > 2000:
                gate_time = 1000 #1s - we want ICR and OCR in cps
            else:
                gate_time = ms_time
            asw = str(self.sl.write_readline("$CT %u\r" % gate_time))
            self._check_answer(asw, 'set_presets: ctime')

            self.times["real_time_preset"] = kwargs["ctime"]
            self.times["cycle_time_preset"] = gate_time/1000

        if kwargs.has_key("erange"):
            #set the energy range
            if kwargs["erange"] in Rontec.ERANGE:
                asw = str(self.sl.write_readline("$SE %d\r" % kwargs["erange"]))
                self._check_answer(asw, 'set_presets:erange')
                self.preset_erange = kwargs["erange"]

        if kwargs.has_key("fname"):
            self.fname = kwargs["fname"]

    def clear_spectrum(self):
        """Clear the acquired spectrum"""        
        self.sl.flush()
        asw = str(self.sl.write_readline("$CC\r"))
        self._check_answer(asw, 'clear_spectrum')

    def stop_acq(self):
        """Stop the running acquisition"""
        self.sl.flush()
        asw = str(self.sl.write_readline("$MP ON\r"))
        self._check_answer(asw, 'stop_acq')

    def start_acq(self, cnt_time=None):
        """Starts new acquisition. If cnt_time is not specified, counts for preset real time.
 
        Keyword Args:
            cnt_time (float, optional): count time in seconds; 0 means to count indefinitely.
  
        Returns:
            None 
        """
        if cnt_time >= 0:
            self.set_presets(ctime=cnt_time)
        else:
            cnt_time = self.times["real_time_preset"]
            
            
        #cnt_time is in s, firmware needs ms
        self.sl.flush()
        asw = str(self.sl.write_readline("$MT %d\r" % (cnt_time*1000)))
        self._check_answer(asw, 'start_acq')

    def set_roi(self, emin, emax, **kwargs):
        """Configure a ROI
 
        Args:
            emin (float): energy [keV] or channel number
            emax (float): energy [keV] or channel number

        Keyword Args:
            channel (int): output connector channel number (1-8)
            element (str): element name as in periodic table
            atomic_nb (int): element atomic number

        Returns:
            None

        Raises:
            KeyError
        """
        #check if input is energy [keV] or channels
        if emax > 80:
            self.chmin = emin
            self.chmax = emax
            if self.calib_done:
                self.emin = self._calib_getE(self.chmin)
                self.emax = self._calib_getE(self.chmax)
        else:
            self.emin = emin
            self.emax = emax
            if self.calib_done:
                self.chmin = self._calib_getch(self.emin)
                self.chmax = self._calib_getch(self.emax)
        if kwargs.has_key("channel") and self.type == 2:
            roi_channel = kwargs.get("channel",1)
            #test if channel is between 1 and 8
            if roi_channel < 1 or roi_channel > 8:
                raise KeyError("Channel number is should be between 1 and 8")
            self.roi_dict[roi_channel] = "%2.4f(%d) %2.4f(%d)" % (self.emin, self.chmin, self.emax, self.chmax)
            roi_str = "$SK %d %d %s %d %d\r" % (roi_channel, kwargs.get("atomic_nb", 34), kwargs.get("element", "Se"), emin*1000, emax*1000)
            self.sl.flush()
            asw = str(self.sl.write_readline(roi_str))
            self._check_answer(asw, 'set_roi')

    def clear_roi(self, **kwargs):
         """Clear ROI settings

         Keyword Args:
            channel (int): optional output connector channel number (1-8)
    
         Returns:
            None
         """
         self.chmin = Rontec.MCA_DEFAULTS["chmin"]
         self.chmax = Rontec.MCA_DEFAULTS["chmax"]
         if self.calib_done:
            self.emin = self._calib_getE(self.chmin)
            self.emax = self._calib_getE(self.chmax)
         if kwargs.has_key("channel") and self.type == 2:
             self.sl.flush()
             roi_channel = kwargs[channel]
             asw = str(self.sl.write_readline("$SK %d 0 0 0\r" % roi_channel))
             self._check_answer(asw, 'clear_roi')
             try:
                 self.roi_dict.pop(roi_channel)
             except KeyError:
                 pass
 
    def get_roi(self, **kwargs):
        """Get ROI settings

        Keyword Args:
            channel (int): output connector channel number (1-8)

        Returns:
            dict. ROI dictionary.
        """
        argout = {}
        argout["chmin"]= self.chmin
        argout["chmax"] =  self.chmax
        if kwargs.has_key("channel"):
            roi_channel = int(kwargs.get("channel"))
            #test if channel is between 1 and 8
            if roi_channel < 1 or roi_channel > 8:
                return argout
        else:
            roi_channel = 0

        if self.type == 2 and roi_channel:
            asw = str(self.sl.write_readline("$GK %d\r"% roi_channel))
            if self.debug:
                print asw
            self._check_answer(asw, 'get_roi')
            asw = asw[4:]
            argout["ext_roi"] = asw
            _,_,self.emin,self.emax = asw.split()
            self.emin = float(self.emin) / 1000
            self.emax = float(self.emax) / 1000
            self.roi_dict[roi_channel] = "%2.4f(%d) %2.4f(%d)" % (self.emin, self.chmin, self.emax, self.chmax)
        else:
            try:
                argout.pop("ext_roi")
            except KeyError:
                pass

        if self.calib_done:
            argout["chmin"] = self._calib_getch(self.emin)
            argout["chmax"] = self._calib_getch(self.emax)
        else:
            self.emin = self._calib_getE(self.chmin)
            self.emax = self._calib_getE(self.chmax)
        
        argout["emin"] = self.emin
        argout["emax"] = self.emax
        return argout

    def get_times(self):
        """Return a dictionary with the preset and elapsed real time [s], 
        elapsed live time (if possible) [s] and the dead time [%].

        Returns:
            dict. Times dictionary.

        Raises:
            RuntimeError
        """
        #real time elapsed
        self.sl.flush()
        asw = str(self.sl.write_readline("$MR\r"))
        self._check_answer(asw, 'get_time:real time elapsed')
        if self.debug:
            print asw
        try:
            _,rt = asw.split()
            #the answer is in ms, we return time in s
            self.times["real_time_elapsed"] = float(rt)/1000
            if self.times["real_time_preset"]:
                self.times["real_time_elapsed"] = self.times["real_time_preset"] - self.times["real_time_elapsed"]
                
        except:
            raise RuntimeError('Cannot get the elapsed real time')

        #dead time
        #get the ICR
        self.sl.flush()
        asw = str(self.sl.write_readline("$BC\r"))
        self._check_answer(asw, 'get_time:ICR')
        try:
            _,icr = asw.split()
            self.times["ICR"] = float(icr)
        except:
            raise RuntimeError('Cannot get the ICR')

        #get the OCR
        self.sl.flush()
        asw = str(self.sl.write_readline("$NC\r"))
        self._check_answer(asw, 'get_time:OCR')
        try:
            _,ocr = asw.split()
            #correct with the cycle time
            if ocr > self.times["cycle_time_preset"]:
                ocr = float(ocr) - self.times["cycle_time_preset"]
            self.times["OCR"] = float(ocr)
        except:
            raise RuntimeError('Cannot get the OCR')
        
        #calculate the dead time in %
        if self.times["ICR"] < 1000 or self.times["ICR"] < self.times["OCR"]:
            self.times["dead_time"] = 0
        else:
            self.times["deat_time"] = ((self.times["ICR"]-self.times["OCR"])/self.times["ICR"])*100.
        
        #live time elapsed
        if self.live_t:
            self.sl.flush()
            asw = str(self.sl.write_readline("$LR\r"))
            self._check_answer(asw, 'get_time:live time elapsed')
            try:
                _,lt = asw.split()
                #the answer is in ms, we return time in s
                self.times["live_time"] = float(lt)/1000
            except:
                raise RuntimeError('Cannot get the elapsed live time')
        return self.times
            
    def get_presets(self, **kwargs):
        """Get the preset parameters

        Keyword Args:
            ctime (float): Real time
            erange (int): energy range
            fname (str): filename where the data are stored

        Returns:
            dict.

        Raises:
            RuntimeError
        """
        if kwargs.has_key("ctime"):
            try:
                return self.times["real_time_preset"]
            except:
                raise RuntimeError('Count time not set')
        if kwargs.has_key("erange"):
            if self.preset_erange:
                return Rontec.ERANGE[self.preset_erange]
            else:
                self.sl.flush()
                asw = str(self.sl.write_readline("$FE\r"))
                self._check_answer(asw, 'get_presets:energy range')
                try:
                    _,rr = asw.split()
                    self.preset_erange = int(rr)
                    return Rontec.ERANGE[self.preset_erange]
                except:
                    raise RuntimeError('Energy range not set')

    def read_roi_data(self, save_data=False):
        """Reads ROI data

        Keyword Args:
            save_data (bool): save data in the file or not, defaults to False
     
        Returns:
            list. Raw data for the predefined ROI channels.
        """
        return self.read_raw_data(self.chmin, self.chmax, save_data)
        
    def read_data(self, chmin=0, chmax=4095, calib=False, save_data=False):
        """Reads the data

        Keyword Args:
            chmin (float): channel number or energy [keV], defaults to 0
            chmax (float): channel number or energy [keV], defaults to 4095
            calib (bool): use calibration, defaults to False
            save_data (bool): save data in the file or not, defaults to False
     
        Returns:
            numpy.array. x - channels or energy (if calib=True), y - data.
        """
        # the input is energy and the calibration is done
        if chmax < 30 and self.calib_done:
            chmin  = _calib_getch(chmin)
            chmax  = _calib_getch(chmax)
        y = self.read_raw_data(chmin, chmax, save_data)
        x = numpy.arange(y.__len__()).astype(numpy.float)
        if calib:
           x = self.calib[0] + self.calib[1]*x + self.calib[2]*math.pow(x,2)
        y = numpy.array(y).astype(numpy.float)
        data = numpy.array([x,y])
        #data = data.transpose()
        return data

    def read_raw_data(self, chmin=0, chmax=4095, save_data=False):
        """Reads raw data

        Keyword Args:
            chmin (int): channel number, defaults to 0
            chmax (int): channel number, defaults to 4095
            save_data (bool): save data in the file or not, defaults to False
     
        Returns:
            list. Raw data. 
        """
        size = int(chmax - chmin + 1)
        #read only what is asked
        self.sl.flush()
        asw = str(self.sl.write_readline("$SS %d,1,1,%d\r" % (chmin, size)))
        self._check_answer(asw, 'read_raw_data:handshake answer reading')
        #read again to get the data
        raw_data = self.sl.read(size=size*4,timeout=10)
        data = ' '.join([ "%02x" % ord(i) for i in raw_data]).split()
        if self.debug:
            print "read %d characters" % data.__len__()
        #we read 4 bytes/ch (hhhhhhhh ........ ........ llllllll)
        dd = [int('0x'+i+j+k+l,16) for i,j,k,l in zip(data[::4], data[1::4], data[2::4], data[3::4])]
        
        if save_data:
            fd = open(self.fname, "a+")
            fd.write("#\n#S 1  mcaacq %d\n" % self.times["real_time_preset"])
            if self.calib_done:
                fd.write("#@CALIB %g %g %g\n@A" % (self.calib[0], self.calib[1],self.calib[2]))
                fd.write(' '.join(map(str, dd)) + "\n")
        return dd