[docs]classDemodulationAcquisitionController(AcquisitionController[float]):""" This class represents an example acquisition controller. End users will probably want to use something more sophisticated. It will average all buffers and then perform a fourier transform on the resulting average trace for one frequency component. The amplitude of the result of channel_a will be returned. Args: name: name for this acquisition_conroller as an instrument alazar_name: the name of the alazar instrument such that this controller can communicate with the Alazar demodulation_frequency: the selected component for the fourier transform **kwargs: kwargs are forwarded to the Instrument base class """def__init__(self,name:str,alazar_name:str,demodulation_frequency:float,**kwargs:Any):self.demodulation_frequency=demodulation_frequencyself.acquisitionkwargs:dict[str,Any]={}self.samples_per_record=0self.records_per_buffer=0self.buffers_per_acquisition=0self.number_of_channels=2self.cos_list:np.ndarray|None=Noneself.sin_list:np.ndarray|None=Noneself.buffer:np.ndarray|None=None# make a call to the parent class and by extension, create the parameter# structure of this classsuper().__init__(name,alazar_name,**kwargs)self.acquisition:Parameter=self.add_parameter("acquisition",get_cmd=self.do_acquisition)"""Parameter acquisition"""
[docs]defupdate_acquisitionkwargs(self,**kwargs:Any)->None:""" This method must be used to update the kwargs used for the acquisition with the alazar_driver.acquire :param kwargs: :return: """self.acquisitionkwargs.update(**kwargs)
[docs]defdo_acquisition(self)->float:""" this method performs an acquisition, which is the get_cmd for the acquisiion parameter of this instrument :return: """value:float=self._get_alazar().acquire(acquisition_controller=self,**self.acquisitionkwargs)returnvalue
[docs]defpre_start_capture(self)->None:""" See AcquisitionController """alazar=self._get_alazar()self.samples_per_record=alazar.samples_per_record.get()self.records_per_buffer=alazar.records_per_buffer.get()self.buffers_per_acquisition=alazar.buffers_per_acquisition.get()sample_speed=alazar.get_sample_rate()integer_list=np.arange(self.samples_per_record)angle_list=(2*np.pi*self.demodulation_frequency/sample_speed*integer_list)self.cos_list=np.cos(angle_list)self.sin_list=np.sin(angle_list)self.buffer=np.zeros(self.samples_per_record*self.records_per_buffer*self.number_of_channels)
[docs]defpre_acquire(self)->None:""" See AcquisitionController :return: """# this could be used to start an Arbitrary Waveform Generator, etc...# using this method ensures that the contents are executed AFTER the# Alazar card starts listening for a trigger pulsepass
[docs]defhandle_buffer(self,buffer:np.ndarray,buffer_number:int|None=None)->None:""" See AcquisitionController :return: """assertself.bufferisnotNoneself.buffer+=buffer
[docs]defpost_acquire(self)->float:""" See AcquisitionController :return: """assertself.bufferisnotNonealazar=self._get_alazar()# average all records in a bufferrecords_per_acquisition=(1.0*self.buffers_per_acquisition*self.records_per_buffer)recordA=np.zeros(self.samples_per_record)foriinrange(self.records_per_buffer):i0=i*self.samples_per_recordi1=i0+self.samples_per_recordrecordA+=self.buffer[i0:i1]/records_per_acquisitionrecordB=np.zeros(self.samples_per_record)foriinrange(self.records_per_buffer):i0=i*self.samples_per_record+len(self.buffer)//2i1=i0+self.samples_per_recordrecordB+=self.buffer[i0:i1]/records_per_acquisitionifself.number_of_channels==2:# fit channel A and channel Bres1=self.fit(recordA)self.fit(recordB)# return [alazar.signal_to_volt(1, res1[0] + 127.5),# alazar.signal_to_volt(2, res2[0] + 127.5),# res1[1], res2[1],# (r es1[1] - res2[1]) % 360]returnalazar.signal_to_volt(1,res1[0]+127.5)else:raiseException("Could not find CHANNEL_B during data extraction")
[docs]deffit(self,buf:np.ndarray)->tuple[float,float]:""" the DFT is implemented in this method :param buf: buffer to perform the transform on :return: return amplitude and phase of the resulted transform """# Discrete Fourier Transformassertself.cos_listisnotNoneassertself.sin_listisnotNoneRePart=np.dot(buf-127.5,self.cos_list)/self.samples_per_recordImPart=np.dot(buf-127.5,self.sin_list)/self.samples_per_record# the factor of 2 in the amplitude is due to the fact that there is# a negative frequency as wellampl=2*np.sqrt(RePart**2+ImPart**2)# see manual page 52!!! (using unsigned data)returnampl,math.atan2(ImPart,RePart)*360/(2*math.pi)
classDemodulation_AcquisitionController(DemodulationAcquisitionController):""" Alias for backwards compatibility. Will eventually be deprecated and removed """pass