예제 #1
0
    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)
예제 #2
0
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()