def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i + 1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get() self.ast_interp = asteval.Interpreter() self.read_scaler_config()
def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): if not prefix.endswith(':'): prefix = "%s:" % prefix self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i+1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get()
def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i+1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get() self.ast_interp = asteval.Interpreter() self.read_scaler_config()
class Struck(Device): """ Very simple implementation of Struck SIS MultiChannelScaler """ attrs = ( 'ChannelAdvance', 'Prescale', 'EraseStart', 'EraseAll', 'StartAll', 'StopAll', 'PresetReal', 'ElapsedReal', 'Dwell', 'Acquiring', 'NuseAll', 'MaxChannels', 'CurrentChannel', 'CountOnStart', # InitialChannelAdvance', 'SoftwareChannelAdvance', 'Channel1Source', 'ReadAll', 'DoReadAll', 'Model', 'Firmware') _nonpvs = ('_prefix', '_pvs', '_delim', '_nchan', 'clockrate', 'scaler', 'mcas', 'ast_interp') def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i + 1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get() self.ast_interp = asteval.Interpreter() self.read_scaler_config() def ExternalMode(self, countonstart=None, initialadvance=None, realtime=0, prescale=1, trigger_width=None): """put Struck in External Mode, with the following options: option meaning default value ---------------------------------------------------------- countonstart set Count on Start None initialadvance set Initial Channel Advance None reatime set Preset Real Time 0 prescale set Prescale value 1 trigger_width set trigger width in sec None here, `None` means "do not change from current value" """ out = self.put('ChannelAdvance', 1) # external if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) if realtime is not None: self.put('PresetReal', realtime) if prescale is not None: self.put('Prescale', prescale) if countonstart is not None: self.put('CountOnStart', countonstart) if initialadvance is not None: self.put('InitialChannelAdvancel', initialadvance) if trigger_width is not None: self.put('LNEOutputWidth', trigger_width) time.sleep(0.002) return out def InternalMode(self, prescale=None): "put Struck in Internal Mode" out = self.put('ChannelAdvance', 0) # internal if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) if prescale is not None: self.put('Prescale', prescale) time.sleep(0.002) return out def set_dwelltime(self, val): "Set Dwell Time" # print("Struck DwellTime ", self._pvs['Dwell'], val) if isinstance(val, (list, tuple, numpy.ndarray)): val = val[0] if val is not None: self.put('Dwell', val) def ContinuousMode(self, dwelltime=None, numframes=None): """set to continuous mode: use for live reading Arguments: dwelltime (None or float): dwelltime per frame in seconds [None] numframes (None or int): number of frames to collect [None] Notes: 1. This sets AquireMode to Continuous. If dwelltime or numframes is not None, they will be set """ self.InternalMode() if numframes is not None: self.put('NuseAll', numframes, wait=True) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.put('CONT', 1, wait=True) self._mode = SCALER_MODE def ScalerMode(self, dwelltime=1.0, numframes=1): """ set to scaler mode: ready for step scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [1.0] numframes (None or int): number of frames to collect [1] Notes: 1. numframes should be 1, unless you know what you're doing. """ if numframes is not None: self.put('NuseAll', numframes, wait=True) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) self._mode = SCALER_MODE def NDArrayMode(self, dwelltime=None, numframes=None, countonstart=True, trigger_width=None): """ set to array mode: ready for slew scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [0.25] numframes (None or int): number of frames to collect [8192] countonstart (None or bool): whether to count on start [True] trigger_width (None or float): output trigger width (in seconds) for optional SIS 3820 [None] Notes: 1. this arms SIS to be ready for slew scanning. 2. setting dwelltime or numframes to None is discouraged, as it can lead to inconsistent data arrays. """ # print("Struck ArrayMode ", dwelltime, numframes) if numframes is not None: self.put('NuseAll', numframes) if dwelltime is not None: self.set_dwelltime(dwelltime) self._mode = NDARRAY_MODE time.sleep(0.01) self.ExternalMode(trigger_width=trigger_width, countonstart=countonstart) def ROIMode(self, dwelltime=None, numframes=None, countonstart=True, trigger_width=None): """set to ROI mode: ready for slew scanning""" self.NDArrayMode(dwelltime=dwelltime, numframes=numframes, countonstart=countonstart, trigger_width=trigger_width) def start(self, wait=False): "Start Struck" if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) return self.put('EraseStart', 1, wait=wait) def stop(self): "Stop Struck Collection" if self.get('Acquiring'): self.put('StopAll', 1) return def erase(self): "Start Struck" return self.put('EraseAll', 1) def mcaNread(self, nmcas=1): "Read a Struck MCA" return self.get('mca%i.NORD' % nmcas) def readmca(self, nmca=1, count=None): "Read a Struck MCA" return self.get('mca%i' % nmca, count=count) def read_all_mcas(self): return [self.readmca(nmca=i + 1) for i in range(self._nchan)] def read_scaler_config(self): """read names and calcs for scaler channels""" if self.scaler is None: return [] conf = [] for n in range(1, self._nchan + 1): name = self.scaler.get('NM%i' % n).strip() if len(name) > 0: name = name.strip().replace(' ', '_') calc = self.scaler.get('expr%i' % n) conf.append((n, name, calc)) return conf def save_arraydata(self, filename='sis.dat', npts=None, **kws): "save MCA spectra to ASCII file" t0 = time.time() # print("SIS Save Array Data ", filename, os.getcwd()) rdata, sdata, names, calcs, fmts = [], [], [], [], [] headers = [] if npts is None: npts = self.NuseAll npts_req = npts avars = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H') adat = {} for name in avars: self.ast_interp.symtable[name] = adat[name] = numpy.zeros(npts) scaler_config = self.read_scaler_config() # read MCAs until all data have a consistent length (up to ~2 seconds) t0 = time.time() time.sleep(0.025) for _i in range(10): npts_chan = [] for nchan, name, calc in scaler_config: dat = self.readmca(nmca=nchan) if (dat is None or not isinstance(dat, numpy.ndarray)): dat = [] npts_chan.append(len(dat)) if npts_req is None: npts_req = npts_chan[0] if (npts_chan[0] == npts_req) and (max(npts_chan) == min(npts_chan)): break time.sleep(0.025 * (_i + 1)) if max(npts_chan) != min(npts_chan): print(" Struck warning, inconsistent number of points!") print(" -- ", npts_chan) # make sure all data is the same length for calcs npts = min(npts, min(npts_chan)) #print(" Struck save_array: read %i in %.3f sec" % (npts, time.time()-t0)) # final read icol = 0 hformat = "# Column.%i: %16s | %s" for nchan, name, calc in scaler_config: icol += 1 dat = self.readmca(nmca=nchan) varname = avars[nchan - 1] adat[varname] = dat label = "%s | %s" % ("%smca%i" % (self._prefix, nchan), varname) if icol == 1 or len(calc) > 1: if icol == 1: calc = 'A / 50.0' label = "calculated | %s" % calc rdata.append(("%s_raw" % name, nchan, varname, dat)) headers.append(hformat % (icol, name, label)) names.append(name) calcs.append(calc) fmt = ' {:14f} ' if icol == 1: fmt = ' {:14.2f} ' fmts.append(fmt) for key, val in adat.items(): try: self.ast_interp.symtable[key] = val[:npts] except TypeError: self.ast_interp.symtable[key] = val for calc in calcs: result = self.ast_interp.eval(calc) if result is None: result = numpy.zeros(1) sdata.append(result) for name, nchan, varname, rdat in rdata: icol += 1 label = "%s | %s" % ("%smca%i" % (self._prefix, nchan), varname) headers.append(hformat % (icol, name, label)) names.append(name) sdata.append(rdat) fmts.append(' {:10.0f} ') try: sdata = numpy.array([s[:npts] for s in sdata]).transpose() npts, nmcas = sdata.shape except: return (0, 0) buff = [ '# Struck MCA data: %s' % self._prefix, '# Nchannels, Nmcas = %i, %i' % (npts, nmcas), '# Time in microseconds' ] buff.extend(headers) buff.append("#%s" % ("-" * 60)) buff.append("# %s" % ' | '.join(names)) fmt = ''.join(fmts) for i in range(npts): buff.append(fmt.format(*sdata[i])) buff.append('') fout = open(filename, 'w') fout.write("\n".join(buff)) fout.close() # print("SIS saved in %.3f seconds" % (time.time()-t0)) return (nmcas, npts)
class Struck(Device): """ Very simple implementation of Struck SIS MultiChannelScaler """ attrs = ('ChannelAdvance', 'Prescale', 'EraseStart', 'EraseAll', 'StartAll', 'StopAll', 'PresetReal', 'ElapsedReal', 'Dwell', 'Acquiring', 'NuseAll', 'MaxChannels', 'CurrentChannel', 'CountOnStart', # InitialChannelAdvance', 'SoftwareChannelAdvance', 'Channel1Source', 'ReadAll', 'DoReadAll', 'Model', 'Firmware') _nonpvs = ('_prefix', '_pvs', '_delim', '_nchan', 'clockrate', 'scaler', 'mcas') def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): if not prefix.endswith(':'): prefix = "%s:" % prefix self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i+1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get() def ExternalMode(self, countonstart=0, initialadvance=None, realtime=0, prescale=1, trigger_width=None): """put Struck in External Mode, with the following options: option meaning default value ---------------------------------------------------------- countonstart set Count on Start 0 initialadvance set Initial Channel Advance None reatime set Preset Real Time 0 prescale set Prescale value 1 trigger_width set trigger width in sec None """ out = self.put('ChannelAdvance', 1) # external if self.scaler is not None: self.scaler.OneShotMode() if realtime is not None: self.put('PresetReal', realtime) if prescale is not None: self.put('Prescale', prescale) if countonstart is not None: self.put('CountOnStart', countonstart) if initialadvance is not None: self.put('InitialChannelAdvancel', initialadvance) if trigger_width is not None: self.put('LNEOutputWidth', trigger_width) return out def InternalMode(self, prescale=None): "put Struck in Internal Mode" out = self.put('ChannelAdvance', 0) # internal if self.scaler is not None: self.scaler.OneShotMode() if prescale is not None: self.put('Prescale', prescale) return out def set_dwelltime(self, val): "Set Dwell Time" # print("Struck DwellTime ", self._pvs['Dwell'], val) if val is not None: self.put('Dwell', val) def ContinuousMode(self, dwelltime=None, numframes=None): """set to continuous mode: use for live reading Arguments: dwelltime (None or float): dwelltime per frame in seconds [None] numframes (None or int): number of frames to collect [None] Notes: 1. This sets AquireMode to Continuous. If dwelltime or numframes is not None, they will be set """ self.InternalMode() if numframes is not None: self.put('NuseAll', numframes) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.AutoCountMode() self._mode = SCALER_MODE def ScalerMode(self, dwelltime=1.0, numframes=1): """ set to scaler mode: ready for step scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [1.0] numframes (None or int): number of frames to collect [1] Notes: 1. numframes should be 1, unless you know what you're doing. """ if numframes is not None: self.put('NuseAll', numframes) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.OneShotMode() self._mode = SCALER_MODE def NDArrayMode(self, dwelltime=None, numframes=None, trigger_width=None): """ set to array mode: ready for slew scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [0.25] numframes (None int): number of frames to collect [8192] trigger_width (None or float): output trigger width (in seconds) for optional SIS 3820 [None] Notes: 1. this arms SIS to be ready for slew scanning. 2. setting dwelltime or numframes to None is discouraged, as it can lead to inconsistent data arrays. """ # print("SIS NDArrayMode ", dwelltime, numframes) if numframes is not None: self.put('NuseAll', numframes) if dwelltime is not None: self.set_dwelltime(dwelltime) self._mode = NDARRAY_MODE time.sleep(0.05) self.ExternalMode(trigger_width=trigger_width, countonstart=False) def ROIMode(self, dwelltime=None, numframes=None, trigger_width=None): """set to ROI mode: ready for slew scanning""" self.NDArrayMode(dwelltime=dwelltime, numframes=numframes, trigger_width=trigger_width) self.put('CountOnStart', 1) def start(self, wait=False): "Start Struck" if self.scaler is not None: self.scaler.OneShotMode() return self.put('EraseStart', 1, wait=wait) def stop(self): "Stop Struck Collection" if self.get('Acquiring'): self.put('StopAll', 1) return def erase(self): "Start Struck" return self.put('EraseAll', 1) def mcaNread(self, nmcas=1): "Read a Struck MCA" return self.get('mca%i.NORD' % nmcas) def readmca(self, nmcas=1, count=None): "Read a Struck MCA" return self.get('mca%i' % nmcas, count=count) def read_all_mcas(self): return [self.readmca(nmcas=i+1) for i in range(self._nchan)] def save_arraydata(self, filename='Struck.dat', ignore_prefix=None, npts=None): "save MCA spectra to ASCII file" # print("SIS Save Array Data") sdata, names, addrs = [], [], [] if npts is None: npts = self.MaxChannels time.sleep(0.025) nmca_chans = [] for nchan in range(self._nchan): nmcas = nchan + 1 _name = 'MCA%i' % nmcas _addr = '%s.mca%i' % (self._prefix, nmcas) time.sleep(0.002) if self.scaler is not None: scaler_name = self.scaler.get('NM%i' % nmcas) if scaler_name is not None: _name = scaler_name.replace(' ', '_') _addr = self.scaler._prefix + 'S%i' % nmcas if len(_name) < 1: continue read_ok = False ntries = 0 while not read_ok and ntries < 10: ntries += 1 mcadat = self.readmca(nmcas=nmcas) try: nmca = len(mcadat) except: nmca = 0 if nmca > npts-1: read_ok = True else: time.sleep(0.1) continue # print("SIS Read Channel: ", nchan, _name, nmca, ntries) if nmca > 2: names.append(_name) addrs.append(_addr) sdata.append(mcadat) nmca_chans.append(nmca) npts = min(npts, min(nmca_chans)) # print("SIS Read Channels, npts = ", npts, nmca_chans) sdata = numpy.array([s[:npts] for s in sdata]).transpose() sdata[:, 0] = sdata[:, 0]/self.clockrate nelem, nmcas = sdata.shape npts = min(nelem, npts) addrs = ' | '.join(addrs) names = ' | '.join(names) formt = '%9i ' * nmcas + '\n' fout = open(filename, 'w') fout.write(HEADER % (self._prefix, npts, nmcas, addrs, names)) for i in range(npts): fout.write(formt % tuple(sdata[i])) fout.close() return (nmcas, npts)
class Struck(Device): """ Very simple implementation of Struck SIS MultiChannelScaler """ attrs = ('ChannelAdvance', 'Prescale', 'EraseStart', 'EraseAll', 'StartAll', 'StopAll', 'PresetReal', 'ElapsedReal', 'Dwell', 'Acquiring', 'NuseAll', 'MaxChannels', 'CurrentChannel', 'CountOnStart', # InitialChannelAdvance', 'SoftwareChannelAdvance', 'Channel1Source', 'ReadAll', 'DoReadAll', 'Model', 'Firmware') _nonpvs = ('_prefix', '_pvs', '_delim', '_nchan', 'clockrate', 'scaler', 'mcas', 'ast_interp') def __init__(self, prefix, scaler=None, nchan=8, clockrate=50.0): self._nchan = nchan self.scaler = None self.clockrate = clockrate # clock rate in MHz self._mode = SCALER_MODE if scaler is not None: self.scaler = Scaler(scaler, nchan=nchan) self.mcas = [] for i in range(nchan): self.mcas.append(MCA(prefix, mca=i+1, nrois=2)) Device.__init__(self, prefix, delim='', attrs=self.attrs, mutable=False) time.sleep(0.05) for pvname, pv in self._pvs.items(): pv.get() self.ast_interp = asteval.Interpreter() self.read_scaler_config() def ExternalMode(self, countonstart=None, initialadvance=None, realtime=0, prescale=1, trigger_width=None): """put Struck in External Mode, with the following options: option meaning default value ---------------------------------------------------------- countonstart set Count on Start None initialadvance set Initial Channel Advance None reatime set Preset Real Time 0 prescale set Prescale value 1 trigger_width set trigger width in sec None here, `None` means "do not change from current value" """ out = self.put('ChannelAdvance', 1) # external if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) if realtime is not None: self.put('PresetReal', realtime) if prescale is not None: self.put('Prescale', prescale) if countonstart is not None: self.put('CountOnStart', countonstart) if initialadvance is not None: self.put('InitialChannelAdvancel', initialadvance) if trigger_width is not None: self.put('LNEOutputWidth', trigger_width) time.sleep(0.002) return out def InternalMode(self, prescale=None): "put Struck in Internal Mode" out = self.put('ChannelAdvance', 0) # internal if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) if prescale is not None: self.put('Prescale', prescale) time.sleep(0.002) return out def set_dwelltime(self, val): "Set Dwell Time" # print("Struck DwellTime ", self._pvs['Dwell'], val) if val is not None: self.put('Dwell', val) def ContinuousMode(self, dwelltime=None, numframes=None): """set to continuous mode: use for live reading Arguments: dwelltime (None or float): dwelltime per frame in seconds [None] numframes (None or int): number of frames to collect [None] Notes: 1. This sets AquireMode to Continuous. If dwelltime or numframes is not None, they will be set """ self.InternalMode() if numframes is not None: self.put('NuseAll', numframes, wait=True) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.put('CONT', 1, wait=True) self._mode = SCALER_MODE def ScalerMode(self, dwelltime=1.0, numframes=1): """ set to scaler mode: ready for step scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [1.0] numframes (None or int): number of frames to collect [1] Notes: 1. numframes should be 1, unless you know what you're doing. """ if numframes is not None: self.put('NuseAll', numframes, wait=True) if dwelltime is not None: self.set_dwelltime(dwelltime) if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) self._mode = SCALER_MODE def NDArrayMode(self, dwelltime=None, numframes=None, countonstart=True, trigger_width=None): """ set to array mode: ready for slew scanning Arguments: dwelltime (None or float): dwelltime per frame in seconds [0.25] numframes (None or int): number of frames to collect [8192] countonstart (None or bool): whether to count on start [True] trigger_width (None or float): output trigger width (in seconds) for optional SIS 3820 [None] Notes: 1. this arms SIS to be ready for slew scanning. 2. setting dwelltime or numframes to None is discouraged, as it can lead to inconsistent data arrays. """ # print("Struck ArrayMode ", dwelltime, numframes) if numframes is not None: self.put('NuseAll', numframes) if dwelltime is not None: self.set_dwelltime(dwelltime) self._mode = NDARRAY_MODE time.sleep(0.01) self.ExternalMode(trigger_width=trigger_width, countonstart=countonstart) def ROIMode(self, dwelltime=None, numframes=None, countonstart=True, trigger_width=None): """set to ROI mode: ready for slew scanning""" self.NDArrayMode(dwelltime=dwelltime, numframes=numframes, countonstart=countonstart, trigger_width=trigger_width) def start(self, wait=False): "Start Struck" if self.scaler is not None: self.scaler.put('CONT', 0, wait=True) return self.put('EraseStart', 1, wait=wait) def stop(self): "Stop Struck Collection" if self.get('Acquiring'): self.put('StopAll', 1) return def erase(self): "Start Struck" return self.put('EraseAll', 1) def mcaNread(self, nmcas=1): "Read a Struck MCA" return self.get('mca%i.NORD' % nmcas) def readmca(self, nmca=1, count=None): "Read a Struck MCA" return self.get('mca%i' % nmca, count=count) def read_all_mcas(self): return [self.readmca(nmca=i+1) for i in range(self._nchan)] def read_scaler_config(self): """read names and calcs for scaler channels""" if self.scaler is None: return [] conf = [] for n in range(1, self._nchan+1): name = self.scaler.get('NM%i' % n).strip() if len(name) > 0: name = name.strip().replace(' ', '_') calc = self.scaler.get('expr%i' % n) conf.append((n, name, calc)) return conf def save_arraydata(self, filename='sis.dat', npts=None, **kws): "save MCA spectra to ASCII file" t0 = time.time() # print("SIS Save Array Data ", filename, os.getcwd()) rdata, sdata, names, calcs, fmts = [], [], [], [], [] headers = [] if npts is None: npts = self.NuseAll npts_req = npts avars = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H') adat = {} for name in avars: self.ast_interp.symtable[name] = adat[name] = numpy.zeros(npts) scaler_config = self.read_scaler_config() # read MCAs until all data have a consistent length (up to ~2 seconds) t0 = time.time() time.sleep(0.025) for _i in range(10): npts_chan = [] for nchan, name, calc in scaler_config: dat = self.readmca(nmca=nchan) if (dat is None or not isinstance(dat, numpy.ndarray)): dat = [] npts_chan.append(len(dat)) if npts_req is None: npts_req = npts_chan[0] if (npts_chan[0] == npts_req) and (max(npts_chan) == min(npts_chan)): break time.sleep(0.025*(_i+1)) if max(npts_chan) != min(npts_chan): print(" Struck warning, inconsistent number of points!") print(" -- ", npts_chan) # make sure all data is the same length for calcs npts = min(npts, min(npts_chan)) #print(" Struck save_array: read %i in %.3f sec" % (npts, time.time()-t0)) # final read icol = 0 hformat = "# Column.%i: %16s | %s" for nchan, name, calc in scaler_config: icol += 1 dat = self.readmca(nmca=nchan) varname = avars[nchan-1] adat[varname] = dat label = "%s | %s" % ("%smca%i" % (self._prefix, nchan), varname) if icol == 1 or len(calc) > 1: if icol == 1: calc = 'A / 50.0' label = "calculated | %s" % calc rdata.append(("%s_raw" % name, nchan, varname, dat)) headers.append(hformat % (icol, name, label)) names.append(name) calcs.append(calc) fmt = ' {:14f} ' if icol == 1: fmt = ' {:14.2f} ' fmts.append(fmt) for key, val in adat.items(): try: self.ast_interp.symtable[key] = val[:npts] except TypeError: self.ast_interp.symtable[key] = val for calc in calcs: result = self.ast_interp.eval(calc) if result is None: result = numpy.zeros(1) sdata.append(result) for name, nchan, varname, rdat in rdata: icol += 1 label = "%s | %s" % ("%smca%i" % (self._prefix, nchan), varname) headers.append(hformat % (icol, name, label)) names.append(name) sdata.append(rdat) fmts.append(' {:10.0f} ') try: sdata = numpy.array([s[:npts] for s in sdata]).transpose() npts, nmcas = sdata.shape except: return (0, 0) buff = ['# Struck MCA data: %s' % self._prefix, '# Nchannels, Nmcas = %i, %i' % (npts, nmcas), '# Time in microseconds'] buff.extend(headers) buff.append("#%s" % ("-"*60)) buff.append("# %s" % ' | '.join(names)) fmt = ''.join(fmts) for i in range(npts): buff.append(fmt.format(*sdata[i])) buff.append('') fout = open(filename, 'w') fout.write("\n".join(buff)) fout.close() # print("SIS saved in %.3f seconds" % (time.time()-t0)) return (nmcas, npts)