def __init__(self, config_file=".\\ExExposure_config.dat"): super().__init__() # Initialise the parent classes self.lastImage = np.zeros((32, 32)) # last acquired image self.BufferSize = 0 # number of images that can fit in the buffer self.emg = 1.0 # applied EM gain self.pag = 4.50 # preamp gain sensitivity (e- per AD count) self.Nr = 8.8 # readout noise (counts) self.ind = 0 # counter for number of images taken self.idle_time = 0 # time between acquisitions self.t0 = time.time() # time at start of acquisition self.t1 = 0 # time time just after get acquisition self.t2 = time.time() # time after emitting signals self.timeout = 5e3 # number of milliseconds to wait for acquire self.initialised = 0 # check whether the camera functions were loaded try: self.AF = Andor() # functions for Andor camera self.AF.verbosity = False # Set True for debugging self.AF.connected = False self.initialised = 1 # functions loaded but camera not connected if self.AF.OS == "Windows" and self.AF.architecture == "64bit": self.CameraConnect() self.initialised = 2 # camera connected, default config if self.AF.connected == True: self.AF.SetDriverEvent(int(self.AcquisitionEvent)) self.ApplySettingsFromConfig(config_file=config_file) # self.StabiliseTemperature() self.initialised = 3 # fully initialised else: error("Andor SDK requires Windows 64 bit") except Exception as e: self.initialised = 0 warning('Andor EMCCD not initialised.\n' + str(e))
def add_column_to_array(self): """Make a list of values and add it to the given column in the multirun values array. The function is chosen by the user. Values are repeated a set number of times, ordered according to the ComboBox text. The selected channels are stored in lists.""" try: # make the list of values table = np.array(self.get_table()).T c = [column.astype(float) for column in table if '' not in column] vals = eval(self.col_range.text()) except Exception as e: warning('Add column to multirun: invalid syntax "' + self.col_range.text() + '".\n' + str(e)) return 0 col = int(self.col_index.text()) if self.col_index.text() else 0 # store the selected channels self.ui_param['Order'] = self.order_edit.currentText() for key in self.measures.keys( ): # ['Variable label', 'measure', 'measure_prefix', '1st hist ID'] if self.measures[key].text( ): # don't do anything if the line edit is empty self.ui_param[key] = self.types[key](self.measures[key].text()) # order the list of values if self.ui_param['Order'] == 'descending': vals = list(reversed(vals)) elif 'random' in self.ui_param['Order']: vals = list(vals) shuffle(vals) for i in range(self.table.rowCount()): try: # set vals in table cells self.table.item(i, col).setText('%.4f' % vals[i]) except IndexError: # occurs if invalid range self.table.item(i, col).setText('')
def load_config(self, file_name='daqconfig.dat'): """Load the acquisition settings from the config file.""" self.stats[ 'config_file'] = file_name if file_name else self.try_browse( file_type='dat (*.dat);;all (*)') try: with open(self.stats['config_file'], 'r') as f: for line in f: if len(line.split('=')) == 2: key, val = line.replace('\n', '').split( '=') # there should only be one = per line try: self.stats[key] = self.types[key](val) except KeyError as e: warning( 'Failed to load DAQ default config line: ' + line + '\n' + str(e)) self.set_table() # make sure the updates are displayed self.set_n(self.stats['n']) self.set_save_dir(self.stats['save_dir']) self.set_trace_file(self.stats['trace_file']) self.set_graph_file(self.stats['graph_file']) self.dc.channels = list(self.stats['channels'].keys()) info('DAQ config loaded from ' + self.stats['config_file']) except FileNotFoundError as e: warning('DAQ settings could not find the config file.\n' + str(e))
def set_n(self, dxn): """Change the Dexter run number to the new value. If it's during a multirun, check that the right number of images were taken in the last run.""" if self._k != self._m and self.seq.mr.ind > 1: warning('Run %s took %s / %s images.' % (self._n, self._k, self._m)) self._n = int(dxn) self._k = 0 # reset image count --- each run should start with im0
def check_path(self, path, datepath): """Check if Python has permission to write to the given directory, path. If so, make a dated directory from that path.""" try: # create directory by date if it doesn't already exist os.makedirs(path + datepath, exist_ok=True) # requies version > 3.2 return path + datepath except PermissionError as e: warning('Image saver could not create directory: '+ path +'\nUsing current directory instead\n'+str(e)) os.makedirs('.' + datepath, exist_ok=True) return '.' + datepath
def make_roi_grid(self, toggle=True, method=''): """Create a grid of ROIs and assign them to analysers that are using the same image. Methods: Single ROI -- make all ROIs the same as the first analyser's Square grid -- evenly divide the image into a square region for each of the analysers on this image. 2D Gaussian masks-- fit 2D Gaussians to atoms in the image.""" method = method if method else self.sender().text() pos, shape = self.rh.ROIs[0].roi.pos(), self.rh.ROIs[0].roi.size() if method == 'Single ROI': for r in self.rh.ROIs: r.resize(*map(int, [pos[0], pos[1], shape[0], shape[1]])) elif method == 'Square grid': n = len(self.rh.ROIs) # number of ROIs d = int((n - 1)**0.5 + 1) # number of ROIs per row X = int(self.rh.shape[0] / d) # horizontal distance between ROIs Y = int(self.rh.shape[1] / int((n - 3 / 4)**0.5 + 0.5)) # vertical distance for i in range(n): # ID of ROI try: newx, newy = int(X * (i % d + 0.5)), int(Y * (i // d + 0.5)) if any( [newx // self.rh.shape[0], newy // self.rh.shape[1]]): warning( 'Tried to set square ROI grid with (xc, yc) = (%s, %s)' % (newx, newy) + ' outside of the image') newx, newy = 0, 0 self.rh.ROIs[i].resize(*map(int, [newx, newy, 1, 1])) except ZeroDivisionError as e: error('Invalid parameters for square ROI grid: ' + 'x - %s, y - %s, pic size - %s, roi size - %s.\n' % (pos[0], pos[1], self.rh.shape[0], (shape[0], shape[1])) + 'Calculated width - %s, height - %s.\n' % (X, Y) + str(e)) elif method == '2D Gaussian masks': try: im = self.im_canvas.image.copy() - self.rh.bias if np.size(np.shape(im)) == 2: for r in self.rh.ROIs: r.create_gauss_mask( im) # fit 2D Gaussian to max pixel region # then block that region out of the image try: im[r.x - r.w:r.x + r.w + 1, r.y - r.h:r.y + r.h + 1] = np.zeros( (2 * r.w + 1, 2 * r.h + 1)) + np.min(im) except (IndexError, ValueError): pass except AttributeError: pass
def EmptyBuffer(self): """Get all of the images currently stored in the camera buffer that have not yet been retreived. The dimensions of the returned array are: (# images, # kinetic scans, ROI width, ROI height).""" istart, iend = self.AF.GetNumberNewImages() if iend > istart: if iend >= self.BufferSize: warning("While emptying camera buffer: The camera buffer " "was full, some images may have been overwritten") return self.AF.GetImages(istart, iend, self.AF.ROIwidth, self.AF.ROIheight) else: return []
def apply_slice(self): """Use the text in the index slice line edit to select time steps""" try: self.chan_choices['Time step name'].clearSelection() for i in eval(self.index_slice.text()): try: self.chan_choices['Time step name'].item(i).setSelected( True) except AttributeError: pass # index out of range self.save_chan_selection() except (TypeError, ValueError, NameError) as e: warning('Invalid selection command for multirun timesteps "' + self.index_slice.text() + '".\n' + str(e))
def reset_dates(self, config_file='./config/config.dat', date=time.strftime("%d %b %B %Y", time.localtime()).split(" ")): # load paths used from config.dat self.dirs_dict = self.get_dirs(config_file) # handy dict contains them all self.image_storage_path = self.dirs_dict['Image Storage Path: '] self.dexter_sync_file_name = self.dirs_dict['Dexter Sync File: '] self.results_path = self.dirs_dict['Results Path: '] self.sequences_path = self.dirs_dict['Sequences path: '] if self.image_storage_path: # =0 if get_dirs couldn't find config.dat, else continue # get the date to be used for file labeling self.date = date # day short_month long_month year datepath = r'\%s\%s\%s'%(self.date[3],self.date[2],self.date[0]) self.image_storage_path = self.check_path(self.image_storage_path, datepath) self.results_path = self.check_path(self.results_path, datepath) # self.sequences_path = self.check_path(self.sequences_path, datepath) else: warning('Image saver could not load paths from config file: '+config_file)
def load_from_files(self, trigger=None): """Prompt the user to select image files to process using the file browser. Keyword arguments: trigger: Boolean passed from the QObject that triggers this function.""" im_list = [] file_list = self.try_browse(title='Select Files', file_type='Images(*.asc);;all (*)', open_func=QFileDialog.getOpenFileNames) for file_name in file_list: try: im_vals = self.rh.load_full_im(file_name) im_list.append(im_vals) except Exception as e: # probably file size was wrong warning("Failed to load image file: " + file_name + '\n' + str(e)) return im_list
def set_chan_listbox(self, col): """Set the selected channels and timesteps with the values previously stored for the given column col. If there were no values stored previously or the index is out of range, reset the selection.""" try: col = int(col) if col else 0 mrtype = self.ui_param['Type'][col] antype = self.ui_param['Analogue type'][col] sel = { 'Time step name': self.ui_param['Time step name'][col], 'Analogue channel': self.ui_param['Analogue channel'][col] if any( mrtype == x for x in self.column_options) else [] } list_ind = self.ui_param['list index'][col] col_range_txt = self.col_range_text[col] except (IndexError, ValueError): mrtype, antype = 'Time step length', 'Fast analogue' sel = {'Time step name': [], 'Analogue channel': []} list_ind = 0 col_range_txt = '' self.col_range.setText(col_range_txt) self.list_index.setText(str(list_ind)) self.chan_choices['Type'].setCurrentText(mrtype) self.chan_choices['Analogue type'].setCurrentText(antype) self.chan_choices['Analogue channel'].setEnabled( any(mrtype == x for x in self.column_options)) for key in ['Time step name', 'Analogue channel']: self.chan_choices[key].setCurrentRow( 0, QItemSelectionModel.Clear) # clear previous selection try: for i in sel[key]: # select items at the stored indices self.chan_choices[key].item(i).setSelected(True) except IndexError: pass # perhaps sequence was updated but using old selection indices except AttributeError as e: warning( "Couldn't set channels for the loaded multirun parameters. Load the sequence first, then load multirun parameters.\n" + str(e))
def ApplySettingsFromConfig(self, config_file="./ExExposure_config.dat"): """Read in a configuration file and apply camera settings from it. See the DocString for ApplySettings for descriptions of the parameters. Keyword arguments: config_file -- the file used to load in config settings.""" try: with open(config_file, 'r') as config_file: config_data = config_file.read().split("\n") except FileNotFoundError: warning("Andor camera config.dat file not found. " + "Applying default settings.") self.ApplySettings() return [1] cvals = [] for row in config_data: if row[:2] != '20': cvals.append(int(row.split('=')[-1])) else: # exposure time is a float cvals.append(float(row.split('=')[-1])) errors = [] errors.append(ERROR_CODE[self.AF.CoolerON()]) errors.append(ERROR_CODE[self.AF.SetTemperature(cvals[0])]) errors.append(ERROR_CODE[self.AF.SetCoolerMode(cvals[1])]) errors.append(ERROR_CODE[self.AF.SetShutter(1, cvals[2])]) errors.append(ERROR_CODE[self.AF.SetOutputAmplifier(cvals[3])]) AmpMode = 12 * cvals[3] # the first 12 settings are EM gain mode errors.append(ERROR_CODE[self.AF.SetHSSpeed(cvals[3], cvals[4])]) errors.append(ERROR_CODE[self.AF.SetVSSpeed(cvals[5])]) errors.append(ERROR_CODE[self.AF.SetPreAmpGain(cvals[6])]) try: self.pag = Sensitivity[AmpMode + 3 * cvals[4] + cvals[6] - 1] self.Nr = ReadNoise[AmpMode + 3 * cvals[4] + cvals[6] - 1] except IndexError as e: self.pag = 4.50 self.Nr = 8.8 warning('Invalid camera acquisition settings: ' + 'PAG ' + str(cvals[6]) + ', hsspeed ' + str(cvals[4]) + '\n' + str(e)) errors.append(ERROR_CODE[self.AF.SetEMCCDGain(cvals[7])]) self.emg = cvals[7] self.AF.ROI = (cvals[9], cvals[10], cvals[12], cvals[13]) errors.append(ERROR_CODE[self.AF.SetReadMode(cvals[15])]) errors.append(ERROR_CODE[self.AF.SetAcquisitionMode(cvals[16])]) if cvals[22] > 1: errors.append(ERROR_CODE[self.AF.SetNumberKinetics(cvals[22])]) self.AF.PrevTrigger = cvals[ 17] # store the trigger mode so it can be reset later errors.append(ERROR_CODE[self.AF.SetTriggerMode(cvals[17])]) errors.append(ERROR_CODE[self.AF.SetFrameTransferMode(cvals[18])]) errors.append(ERROR_CODE[self.AF.SetFastExtTrigger(cvals[19])]) # crop mode requires frame transfer and external trigger modes errors.append(ERROR_CODE[self.SetROI(self.AF.ROI, hbin=cvals[8], vbin=cvals[11], crop=cvals[14])]) errors.append(ERROR_CODE[self.AF.SetExposureTime(cvals[20])]) errors.append(ERROR_CODE[self.AF.GetAcquisitionTimings()]) self.BufferSize = self.AF.GetSizeOfCircularBuffer() if abs(cvals[20] - self.AF.exposure) / cvals[20] > 0.01: warning("Tried to set exposure time %.3g s" % cvals[20] + " but acquisition settings require min. exposure time " + "%.3g s." % self.AF.exposure) self.AF.verbosity = bool(cvals[21]) self.AF.kscans = 1 check_success = [e != 'DRV_SUCCESS' for e in errors] if any(check_success): warning("Didn't get DRV_SUCCESS for setting " + str(check_success.index(True))) self.SettingsChanged.emit(self.emg, self.pag, self.Nr, True) return check_success
def ApplySettings(self, setPointT=-60, coolerMode=1, shutterMode=2, outamp=0, hsspeed=2, vsspeed=4, preampgain=3, EMgain=1, ROI=None, hbin=1, vbin=1, cropMode=0, readmode=4, acqumode=5, triggerMode=7, frameTransf=0, fastTrigger=0, expTime=70e-6, verbosity=False, numKin=1): """Apply user settings. Keyword arguments: setPointT -- temperature set point in degrees Celsius. coolerMode -- 1: temperature maintained on camera shutdown 2: ambient temperature on camera shutdown shutterMode -- typ=1: TTL high to open shutter mod=1: internal shutter permanently open mod=2: internal shutter permanently closed outamp -- output amplification setting. 0: electron multiplication 1: conventional hsspeed -- Horizontal shift speed (MHz) value - EM mode shift speed - conventional mode shift speed 0: 17.0 3.0 1: 10.0 1.0 2: 5.0 0.08 3: 1.0 vsspeed -- Vertical shift speeds (us / row). 0: 0.3 1: 0.5 2: 0.9 3: 1.7 4: 3.3 (default) preampgain -- Pre-amp gain setting. The value can be 1, 2 or 3. See the system booklet for what these correspond to. EMgain -- electron-multiplying gain factor. ROI -- Region of Interest on the CCD. A tuple of form: (xmin, xmax, ymin, ymax). hbin -- number of horizontal pixels to bin in software vbin -- number of vertical pixels to bin in software cropMode -- reduce the active area of the CCD to improve throughput. 0: off 1: on readmode -- 4: imaging readout mode acqumode -- Camera acquisition mode 1: Single Scan 2: Accumulate 3. Kinetics 4: Fast Kinetics 5: Run till abort triggerMode -- Mode for camera triggering 0: internal 1: External 6: External Start 7: External Exposure (Bulb) 9: External FVB EM (only valid for EM Newton models) 10: Software Trigger 12: External Charge Shifting frameTransf -- enable/disable frame transfer mode (not compatible with external exposure mode). 0: off 1: on fastTrigger -- enable/disable fast external triggering 0: off 1: on expTime -- exposure time when not in external exposure trigger mode. Units: seconds. verbosity -- True for debugging info numKin -- number of scans in kinetic mode.""" errors = [] errors.append(ERROR_CODE[self.AF.CoolerON()]) errors.append(ERROR_CODE[self.AF.SetCoolerMode(coolerMode)]) errors.append(ERROR_CODE[self.AF.SetTemperature(setPointT)]) errors.append(ERROR_CODE[self.AF.SetShutter(1, shutterMode)]) errors.append(ERROR_CODE[self.AF.SetOutputAmplifier(outamp)]) AmpMode = 12 * outamp # the first 12 settings are EM gain mode errors.append(ERROR_CODE[self.AF.SetHSSpeed(outamp, hsspeed)]) errors.append(ERROR_CODE[self.AF.SetVSSpeed(vsspeed)]) errors.append(ERROR_CODE[self.AF.SetPreAmpGain(preampgain)]) errors.append(ERROR_CODE[self.AF.SetEMCCDGain(EMgain)]) self.emg = EMgain try: self.pag = Sensitivity[AmpMode + 3 * hsspeed + preampgain - 1] self.Nr = ReadNoise[AmpMode + 3 * hsspeed + preampgain - 1] except IndexError as e: self.pag = 4.50 self.Nr = 8.8 warning('Invalid camera acquisition settings: ' + 'PAG ' + str(preampgain) + ', hsspeed ' + str(hsspeed) + '\n' + str(e)) self.AF.ROI = ROI errors.append(ERROR_CODE[self.AF.SetReadMode(readmode)]) errors.append(ERROR_CODE[self.AF.SetAcquisitionMode(acqumode)]) if numKin > 1: errors.append(ERROR_CODE[self.AF.SetNumberKinetics(numKin)]) # errors.append(ERROR_CODE[self.cam.AF.SetFastKineticsEx( # 100, numKin, expTime, 4, 1, 1, 1)]) self.AF.PrevTrigger = triggerMode # store the trigger mode so it can be reset later errors.append(ERROR_CODE[self.AF.SetTriggerMode(triggerMode)]) errors.append(ERROR_CODE[self.AF.SetFastExtTrigger(fastTrigger)]) errors.append(ERROR_CODE[self.AF.SetFrameTransferMode(frameTransf)]) # crop mode requires frame transfer and external trigger modes errors.append(ERROR_CODE[self.SetROI(self.AF.ROI, hbin=hbin, vbin=vbin, crop=cropMode)]) errors.append(ERROR_CODE[self.AF.SetExposureTime(expTime)]) errors.append(ERROR_CODE[self.AF.GetAcquisitionTimings()]) self.BufferSize = self.AF.GetSizeOfCircularBuffer() if abs(expTime - self.AF.exposure) / expTime > 0.01: warning("Tried to set exposure time %.3g s" % expTime + " but acquisition settings require min. exposure time " + "%.3g s." % self.AF.exposure) self.AF.verbosity = verbosity check_success = [e != 'DRV_SUCCESS' for e in errors] if any(check_success): warning("Didn't get DRV_SUCCESS for setting " + str(check_success.index(True))) self.SettingsChanged.emit(self.emg, self.pag, self.Nr, True) return check_success