def initialize(self): # for some reason the initialization was not previously carried out in an __Init__ , at first i didnt't want to change this at the moment # since it was working with the hardware. But will give it a try count_time = self.parameters[1] reset_time = self.parameters[2] samples = self.parameters[0] threshold = self.parameters[4] numavgs = self.parameters[3] start = self.scan['start'] step = self.scan['stepsize'] numsteps = self.scan['steps'] use_pts = self.mw['PTS'][0] enable_scan_pts = self.mw['PTS'][2] current_freq = self.mw['PTS'][1] start_freq = self.mw['PTS'][3] step_freq = self.mw['PTS'][4] num_freq_steps = self.mw['PTS'][5] stop_freq = self.mw['PTS'][6] do_enable_iq = self.awgparams['enable IQ'] self.adw = ADwin.ADwin() try: # boot the adwin with the bootloader self.adw.Boot(self.adw.ADwindir + 'ADwin11.btl') # Measurement protocol is configured as process 2, external triggered measure_proc = os.path.join(os.path.dirname(__file__), 'AdWIN', 'Measure_Protocol.TB2') self.adw.Load_Process(measure_proc) # TrialCounter is configured as process 1 count_proc = os.path.join(os.path.dirname(__file__), 'ADWIN\\TrialCounter.TB1') self.adw.Load_Process(count_proc) # TODO: set the parameters in the ADWIN -- check the .BAS files # from what I could tell of Adbasic, these values seem to be ignored in the Measure protocol # double check with Elijah and maybe correct the .bas file self.adw.Set_Par(3, count_time) self.adw.Set_Par(4, reset_time) self.adw.Set_Par(5, samples) # start the Measure protocol self.adw.Start_Process(2) self.logger.info('Adwin parameter 5 is {:d}'.format(self.adw.Get_Par(5))) # seem to be printing the samples # value # again? except ADwin.ADwinError as e: sys.stderr.write(e.errorText) self.conn.send('Abort!') self.scanning = False # initialize the PTS and output the current frequency if use_pts: self.pts = PTS() self.pts.write(int(current_freq * _MHZ)) else: self.logger.error('No microwave synthesizer selected') self.awgcomm = AWG520() self.awgcomm.setup(do_enable_iq) # removed the setup of AWG in Upload thread, so do it now. self.awgcomm.run() # places the AWG into enhanced run mode. time.sleep(0.2)
class ScanProcess(multiprocessing.Process): """This is where the scanning actually happens. It inherits nearly all the same params as the ScanThread, except for one more parameter: conn which is the child connector of the Pipe used to communicate to ScanThread.""" # def __init__(self,parent=None, conn = None,scan = None,parameters = None,awgparams = None,pulseparams = None, # mwparams =None, timeRes = 1,maxcounts=100): # super().__init__(parent) # self.timeRes = timeRes # self.maxcounts = maxcounts # self.logger = logging.getLogger('threadlogger.scanThread.scanproc') # if scan == None: # self.scan = dict([('type', 'amplitude'), ('start', '0'), ('stepsize', '50'), ('steps', '20')]) # else: # self.scan = scan # if mwparams == None: # self.mw = {'PTS': [True, '2.870', False, '2.840', '0.001', '100', '2.940'], \ # 'SRS': [False, '2.870', False, '2.840', '0.001', '100', '2.940']} # else: # self.mw = mwparams # if awgparams == None: # self.awgparams = {'awg device': 'awg520', 'time resolution': 1, \ # 'pulseshape': 'Square', 'enable IQ': False} # else: # self.awgparams = awgparams # if pulseparams == None: # self.pulseparams = {'amplitude': 0, 'pulsewidth': 20, 'SB freq': 0.00, 'IQ scale factor': 1.0, # 'phase': 0.0, 'skew phase': 0.0, 'num pulses': 1} # else: # self.pulseparams = pulseparams # if parameters == None: # self.parameters = [50000, 300, 1000, 10, 50, 820, 10] # else: # self.parameters = parameters # # # should make into dictionary with keys 'sample', 'count time', # # 'reset time', 'avg', 'threshold', 'AOM delay', 'microwave delay' # self.conn = conn # self.scanning = False # self.initialize() def __init__(self, parent=None): super().__init__(parent) self.logger = logging.getLogger('threadlogger.scanproc') def get_conn(self, conn): self.conn = conn self.scanning = False #@log_with(modlogger) def initialize(self): # for some reason the initialization was not previously carried out in an __Init__ , at first i didnt't want to change this at the moment # since it was working with the hardware. But will give it a try count_time = self.parameters[1] reset_time = self.parameters[2] samples = self.parameters[0] threshold = self.parameters[4] numavgs = self.parameters[3] start = float(self.scan['start']) step = float(self.scan['stepsize']) numsteps = int(self.scan['steps']) use_pts = self.mw['PTS'][0] enable_scan_pts = self.mw['PTS'][2] current_freq = float(self.mw['PTS'][1]) start_freq = float(self.mw['PTS'][3]) step_freq = float(self.mw['PTS'][4]) num_freq_steps = float(self.mw['PTS'][5]) stop_freq = float(self.mw['PTS'][6]) do_enable_iq = self.awgparams['enable IQ'] self.adw = ADwin.ADwin() try: # boot the adwin with the bootloader self.adw.Boot(self.adw.ADwindir + 'ADwin11.btl') # Measurement protocol is configured as process 2, external triggered measure_proc = os.path.join(os.path.dirname(__file__), 'AdWIN', 'Measure_Protocol.TB2') self.adw.Load_Process(measure_proc) # TrialCounter is configured as process 1 count_proc = os.path.join(os.path.dirname(__file__), 'ADWIN\\TrialCounter.TB1') self.adw.Load_Process(count_proc) # TODO: set the parameters in the ADWIN -- check the .BAS files # from what I could tell of Adbasic, these values seem to be ignored in the Measure protocol # double check with Elijah and maybe correct the .bas file self.adw.Set_Par(3, count_time) self.adw.Set_Par(4, reset_time) self.adw.Set_Par(5, samples) # start the Measure protocol self.adw.Start_Process(2) self.logger.info('Adwin parameter 5 is {:d}'.format( self.adw.Get_Par(5))) # seem to be printing the samples # value # again? except ADwin.ADwinError as e: sys.stderr.write(e.errorText) self.conn.send('Abort!') self.scanning = False # initialize the PTS and output the current frequency if use_pts: self.pts = PTS(_PTS_PORT) self.pts.write(int(current_freq * _MHZ)) self.logger.info('The PTS freq is now {0:d}'.format( int(current_freq * _MHZ))) else: self.logger.error('No microwave synthesizer selected') self.awgcomm = AWG520() #changed the awg savedir path because the root directory of the awg has a memory limit self.awgcomm.setup(enable_iq=True, seqfilename="./pulsed_esr/scan.seq" ) #removed the setup of AWG in Upload # thread, # so do it now. time.sleep(0.2) self.awgcomm.run() # places the AWG into enhanced run mode. time.sleep(0.2) #@log_with(modlogger) def run(self): self.scanning = True self.initialize( ) # why is initialize called in run? it would seem best to initialize hardware first numavgs = self.parameters[3] start = float(self.scan['start']) step = float(self.scan['stepsize']) numsteps = int(self.scan['steps']) use_pts = self.mw['PTS'][0] scan_carrier_freq = (self.scan['type'] == 'Carrier frequency') current_freq = float(self.mw['PTS'][1]) # start_freq = float(self.mw['PTS'][3]) # step_freq = float(self.mw['PTS'][4]) # num_freq_steps = float(self.mw['PTS'][5]) # stop_freq = float(self.mw['PTS'][6]) # do_enable_iq = self.awgparams['enable IQ'] # TODO: this is still a bit ugly but because I moved the number of pulses to be scanned into pulseparams # TODO: I need to check if the iterate pulses is on. # TODO: maybe simples if in the main GUI i simply replace the scan line edits and do strict type checking in the app # TODO: above todo is now nearly implemented but keeping it here jic i forgot something. # npulses = self.pulseparams['numpulses'] # if self.scan['type'] == 'frequency': # # we can scan frequency either using PTS or using the SB freq, but if we are scanning a wide range using # # the PTS we must simply output the sequence specified by user on the AWG. # # self.scan['type'] = 'frequency' # num_scan_points = num_freq_steps # else: # num_scan_points = numsteps try: for avg in list(range( numavgs)): # we will keep scanning for this many averages self.awgcomm.trigger( ) # trigger the awg for the arm sequence which turns on the laser. time.sleep( 0.2) # Not sure why but shorter wait time causes problem. for x in list(range(numsteps)): self.logger.info( 'The current avg. is No.{:d}/{:d} and the the current point is {:d}/{:d}' .format((avg + 1), numavgs, (x + 1), numsteps) ) # pubudu: added +1 because avg and x starts from 0. if not self.scanning: raise Abort() if use_pts and scan_carrier_freq: # this part implements frequency scanning freq = int((start + step * x) * _MHZ) temp = 1 # try to communicate with PTS and make sure it has put out the right frequency while not (self.pts.write(freq)): time.sleep(temp) temp *= 2 if temp > 10: self.pts.__init__() temp = 1 # get the signal and reference data sig, ref = self.getData(x) #print('id and value are',id(self.parameters[4]),self.parameters[4]) threshold = self.parameters[4] # track the NV position if the reference counts is too low while ref < threshold: threshold = self.parameters[4] print("THRESHOLD IS: ", threshold) if not self.scanning: raise Abort() if ref < 0: # this condition arises if the adwin did not update correctly self.logger.debug( 'the ref is less than 0, probably adwin did not update' ) raise Abort() else: # turning on the microwave for finetrack self.awgcomm.sendcommand( 'SOUR1:MARK1:VOLT:LOW 2.0\n') self.pts.write(int(2700 * _MHZ)) self.finetrack() self.awgcomm.sendcommand( 'SOUR1:MARK1:VOLT:LOW 0\n') time.sleep(0.1) self.awgcomm.sendcommand( 'SOUR1:MARK1:VOLT:HIGH 2.0\n') time.sleep(0.1) self.pts.write(int(current_freq * _MHZ)) sig, ref = self.getData( x, 'jump' ) # we have to execute the sequence again. if sig == 0: self.logger.warning( 'sig is 0 ,executing again') sig, ref = self.getData(x, 'jump') self.conn.send([sig, ref]) self.logger.info( 'signal {0:d} and reference {1:d} sent from ScanProc to ScanThread' .format(sig, ref)) self.conn.poll(None) self.parameters[4], self.scanning = self.conn.recv( ) # receive the threshold and scanning status except Abort: self.conn.send('Abort!') self.cleanup() #@log_with(modlogger) def getData(self, x, *args): '''This is the main function that gets the data from teh Adwin. to understand the code, it helps to know that the AWGFile class that was used to upload the sequences first creates an arm_sequence which is the 1st line in the scan.seq file. The 2md line of the scan.seq file will therefore be the 1st point in the scan list and so on. The arm_sequence is executed during the finetrack function when the counts from NV are low. There are 3 possible ways getData function is called: getData(0) = 1st time getData is called it will jump to the 2nd line of the scan.seq file which corresponds to the first actual point in the scan. it will then trigger to output that wfm on the AWG getData(x) = not the 1st time, will direct trigger to output the current line of the scan.seq file and move to the next getData(x,'jump') = including the case x = 0, this is called when we finished tracking and maximizing counts using fine_track func. If we have taken x points of data and now want to move to the (x+1)th point, then the scan index is x (because python indexing starts at 0 for lists). So again when we come back from finetrack, we need to jump the (x+2) line of the scan.seq function and output that wfm by triggering. For some reason the trigger has to be done twice here according to Kai Zhang. Thus the params to be sent are: 1. x : data point number 2. args : only one arg 'jump' is supported at this time ''' modlogger.info( 'entering getData with arguments data point {0:d}, and {1:}'. format(x, args)) flag = self.adw.Get_Par(10) samples = self.parameters[0] # flag=int(x) self.logger.info('The flag is {0:d}'.format(flag)) if x == 0 or args != ( ): # if this is the first point we need to jump over the arm_sequence to the 2nd line of # scan.seq. If not first point, we still need to add 2 again to get to the right line number self.awgcomm.jump(x + 2) time.sleep( 0.005 ) # This delay is necessary. Otherwise neither jump nor trigger would be recognized by awg. self.awgcomm.trigger( ) # now we output the line number in the scan.seq file time.sleep(0.2) if args != (): time.sleep(0.1) self.awgcomm.trigger( ) # if the arg is 'jump' we have to trigger again for some reason. self.logger.info( f'Adwin Par_10 just before while is {self.adw.Get_Par(10):d}') # wait until data updates while flag == self.adw.Get_Par(10): time.sleep(0.1) self.logger.info( f'Adwin Par_10 within while is {self.adw.Get_Par(10):d}') sig = self.adw.Get_Par(1) ref = self.adw.Get_Par(2) # dat1 = self.adw.GetData_Long(1,1,samples) # dat2 = self.adw.GetData_Long(2,1,samples) # dat3 = self.adw.GetData_Long(3,1,samples) return sig, ref # # #wait until data updates # flag_counter = 0 # while True: # adw_updated = self.adw.Get_Par(10) # if flag == adw_updated: # self.logger.info(f'Adwin Par_10 is {adw_updated:d}') # break # else: # flag_counter +=1 # time.sleep(0.1) # if flag_counter >= 100: # self.logger.error("Adwin did not update correctly") # break # if flag_counter < 100: # sig=self.adw.Get_Par(1) # ref=self.adw.Get_Par(2) # return sig,ref # else: # return -1,-1 def track(self): self.axis = 'z' position = self.nd.SingleReadN(self.axis, self.handle) def finetrack(self): modlogger.info('entering tracking from ScanProc') self.adw.Stop_Process(2) # Stop the ADWin measure process self.awgcomm.jump( 1 ) # Jumping to line 1 which turns the green light on (arm sequence) time.sleep( 0.005 ) # This delay is necessary. Otherwise neither jump nor trigger would be recognized by awg. self.awgcomm.trigger() try: self.nd = MCL_NanoDrive() self.handle = self.nd.InitHandles().get('L') except IOError as err: self.logger.error("Error initializing NanoDrive {0}".format(err)) raise self.accuracy = 0.025 self.axis = 'x' self.scan_track() # Readjust the x axis self.axis = 'y' self.scan_track() # Readjust the y axis self.axis = 'z' self.scan_track(ran=0.5) # Increase range for z self.nd.ReleaseAllHandles() self.adw.Start_Process(2) # Restart the ADWin measure process time.sleep(0.2) def go(self, command): # we need to check if the position has really gone to the command position position = self.nd.SingleReadN(self.axis, self.handle) i = 0 while abs(position - command) > self.accuracy: self.logger.info(f'moving to {command} from {position}') position = self.nd.MonitorN(command, self.axis, self.handle) time.sleep(0.1) i += 1 if i == 20: break def count(self): # this function uses the Adwin process 1 to simply record the counts total_count_time = (50000 * 300) self.adw.Set_Par(30, total_count_time) self.adw.Start_Process(1) time.sleep(total_count_time * 1e-9 + 0.1) counts = self.adw.Get_Par(1) print(f'counts collected from the count method is {counts}') self.adw.Stop_Process(1) return counts def scan_track(self, ran=0.25, step=0.05): '''This is the function that maximizes the counts by scanning a small range around the current position. Params are 1. ran : range to scan in microns ie 250 nm is default 2. step = step size in microns, 50 nm is default''' positionList = [] position = self.nd.SingleReadN(self.axis, self.handle) counts_data = [] p = position - ran / 2 while p <= position + ran / 2: positionList.append(p) p += step for each_position in positionList: self.go(each_position) data = self.count() self.conn.send(data) print(f'data collected from the scan_track method is {data}') self.conn.poll(None) r = self.conn.recv() self.parameters[4] = r[0] counts_data.append(data) self.go(positionList[counts_data.index(max(counts_data))]) def cleanup(self): self.awgcomm.stop() self.awgcomm.cleanup() self.adw.Stop_Process(2) #self.amp.switch(False) self.pts.cleanup()