diff --git a/SpectrumAWG/SpectrumCard.py b/SpectrumAWG/SpectrumCard.py index a3dd892d53781c80991abcd8c8587ec3ebaedf82..aefab2c2389dc0fa96052a93cae60c22c2d55378 100644 --- a/SpectrumAWG/SpectrumCard.py +++ b/SpectrumAWG/SpectrumCard.py @@ -6,8 +6,6 @@ from .spcm_tools import * import ctypes import numpy as np -#just needed for the demo -import time class SpectrumCard: @@ -1047,119 +1045,3 @@ def generate_multi_tone(frequencies, amplitudes, phases, num_samples, sample_rat signal = signal * normalization_factor return np.int16(signal) - -# Main Code -if __name__ == "__main__": - - # Open Connection to Card - tweezerAWG = SpectrumCard('/dev/spcm0') - tweezerAWG.open() - #tweezerAWG.print_available_pci_features() - #tweezerAWG.print_sequence_setting_limits() - - # Set sampling rate, clock and output channels - sample_rate = MEGA(1250) - tweezerAWG.set_clock('external',MEGA(10)) - tweezerAWG.set_sample_rate(sample_rate) - tweezerAWG.set_channel_status(0,True) - tweezerAWG.set_channel_enable(0,True) - tweezerAWG.set_channel_amplitude(0,100) - tweezerAWG.set_channel_filter(0,False) - tweezerAWG.set_channel_mode(0,'double') - - # Trigger Settings - #tweezerAWG.print_available_trigger_sources() - #tweezerAWG.print_available_ext_trigger_modes() - #tweezerAWG.print_available_and_trigger_sources() - #tweezerAWG.print_available_or_trigger_sources() - tweezerAWG.set_ext_trigger_mode('ext0','pos',rearm=True) - tweezerAWG.set_ext_trigger_level('ext0',800,2000) - tweezerAWG.set_trigger_or_mask({'ext0'}) - - # Set Generation Mode - demo_mode = 'seq' # 'single', 'seq', 'fifo' - # 'single': Configures the card for single-shot data replay, where a fixed set of data is played once after the first trigger. - # 'seq': Sets the card to sequence mode, allowing for complex, multi-step sequences of data to be replayed in a specified order. - # 'fifo': Configures the card for FIFO mode, enabling continuous data streaming by replenishing data in real-time. - - if demo_mode == 'single': - tweezerAWG.set_generation_mode(mode='single') - tweezerAWG.set_loops(0) - elif demo_mode == 'seq': - tweezerAWG.set_generation_mode(mode='sequence') - tweezerAWG.seq_set_memory_segments(2**16) - elif demo_mode == 'fifo': - tweezerAWG.set_generation_mode(mode='fifo_single') - - - # Generate Data - if demo_mode == 'single' or demo_mode == 'fifo': - num_samples = 4096*320 # Has to be a multiple of 4096 !!! - fundamental_frequency = sample_rate/num_samples # Only integer multiples of this frequency can be generated - - # Generate Single Tone - frequency = fundamental_frequency*100 - samples = generate_single_tone(frequency, num_samples) - - # Generate Multi Frequency Tones - #unique_random_numbers = random.sample(range(100, 2000, 10), 100) - #frequency_list = [num * fundamental_frequency for num in unique_random_numbers] - #samples=generate_multi_tone(frequency_list,np.ones(len(frequency_list)),num_samples,sample_rate) - - # Write Data to Card - if demo_mode == 'single': - tweezerAWG.transfer_single_replay_samples(samples) - if demo_mode == 'fifo': - notify_size = 4096*32 - tweezerAWG.fifo_initialize_buffer(samples,notify_size) - - # Generate and Write Sequence Data - if demo_mode == 'seq': - num_samples = 4096 - fundamental_frequency = sample_rate/num_samples - - for i in range(100): - data = generate_single_tone(fundamental_frequency*(i+1),num_samples) - tweezerAWG.transfer_sequence_replay_samples(i,data) - if i < 99: - tweezerAWG.seq_set_sequence_step(i,i,i+1,1,'on_trigger',last_step=False) - else: - tweezerAWG.seq_set_sequence_step(i,i,0,1,'on_trigger',last_step=True) - - - tweezerAWG.card_write_setup() - tweezerAWG.card_start() - - input() - tweezerAWG.card_force_trigger() - if demo_mode == 'single': - input() - - if demo_mode == 'seq': - for i in range(100): - input() - tweezerAWG.card_force_trigger() - - if demo_mode == 'fifo': - fifo_bytes_avail_user = int32(0) - fifo_pos_pc = int32(0) - - start_time = time.time() - while (time.time()-start_time) < 5: - - # Get number of free bytes on card memory that can be written: - spcm_dwGetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_USER_LEN, byref(fifo_bytes_avail_user)) - # Position of pointer in PC buffer (would be needed if data is actually updated) - spcm_dwGetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_USER_POS, byref(fifo_pos_pc)) - - #If enough bytes are free, write new data to FIFO - if fifo_bytes_avail_user.value > notify_size: - dwError = spcm_dwSetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_CARD_LEN, notify_size) - if dwError != ERR_OK: - tweezerAWG.handle_error() - - #Wait for current data transfer (of size notify_size) to be finished - dwError = spcm_dwSetParam_i32(tweezerAWG.hCard, SPC_M2CMD, M2CMD_DATA_WAITDMA) - - tweezerAWG.card_stop() - tweezerAWG.card_close() \ No newline at end of file diff --git a/SpectrumAWG/blacs_workers.py b/SpectrumAWG/blacs_workers.py index 652256c645bc9572b4820f641b162bdc5f817bdc..b2b3730df302b8e11920a3d8b3c3e3a72071828c 100644 --- a/SpectrumAWG/blacs_workers.py +++ b/SpectrumAWG/blacs_workers.py @@ -2,8 +2,6 @@ import labscript_utils.h5_lock import h5py from blacs.tab_base_classes import Worker from . import SpectrumCard -from numpy import round - class SpectrumAWGWorker(Worker): def init(self): @@ -13,7 +11,7 @@ class SpectrumAWGWorker(Worker): if self.external_clock_rate is None: self.AWG.set_clock('internal') else: - self.AWG.set_clock('external',self.external_clock_rate) + self.AWG.set_clock('external',int(self.external_clock_rate)) self.AWG.set_sample_rate(int(self.sample_rate)) self.channels = [] @@ -27,8 +25,8 @@ class SpectrumAWGWorker(Worker): self.AWG.set_channel_mode(ch,None) self.AWG.set_ext_trigger_mode('ext0','pos',rearm=True) - self.AWG.set_ext_trigger_level('ext0',800,2000) - self.AWG.set_trigger_or_mask({'ext0'}) + self.AWG.set_ext_trigger_level('ext0',2000,800) # 2V tirgger, 0.8V rearm + self.AWG.set_trigger_or_mask(['ext0']) self.AWG.set_generation_mode(mode='sequence') self.AWG.seq_set_memory_segments(self.memory_segments) @@ -44,14 +42,14 @@ class SpectrumAWGWorker(Worker): return{} elif type(values) is float: # Stream single frequency - data = SpectrumCard.generate_single_tone(values,4096,self.sample_rate) # TODO: Set num_samples dynamically + data = SpectrumCard.generate_single_tone(values*1e6,4096,self.sample_rate) # TODO: Set num_samples dynamically self.AWG.transfer_sequence_replay_samples(len(self.smart_cache),data) # Write in next free memory - self.AWG.seq_set_sequence_step(0,len(self.smart_cache),0,1,'on_trigger',last_step=True) + self.AWG.seq_set_sequence_step(0,len(self.smart_cache),0,1,'on_trigger',last_step=False) elif type(values) is int: if values == -1: return {} # not memory index selected # Stream sample from memory - self.AWG.seq_set_sequence_step(0,values,0,1,'on_trigger',last_step=True) + self.AWG.seq_set_sequence_step(0,values,0,1,'on_trigger',last_step=False) else: return{} self.AWG.card_write_setup() @@ -60,14 +58,15 @@ class SpectrumAWGWorker(Worker): return {} def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): + self.AWG.card_stop() # If card was still running, e.g. from manual mode with h5py.File(h5_file,'r') as f: group = f[f"devices/{device_name}"] - if fresh or len(self.smart_cache)+len(group[ch].attrs) > self.memory_segments: - # Reset smart programming and start writing memory from the beginning - # TODO: What is we want to always keep specific instruction in the memory? - self.smart_cache = {} - for ch in self.channels: + if fresh or len(self.smart_cache)+len(group[ch].attrs) > self.memory_segments: + # Reset smart programming and start writing memory from the beginning + # TODO: What is we want to always keep specific instruction in the memory? + self.smart_cache = {} + last_index = len(group[ch].attrs)-1 for index,t in enumerate(group[ch].attrs): ### LOOP TROUGH STREAMING STEPS ### @@ -94,17 +93,23 @@ class SpectrumAWGWorker(Worker): initial_values[memory_index] = f"f:{freq}, a:{ampl}, p:{phase}" else: raise RuntimeError("Instruction length does not match, what happened??") + if t in group[ch]["labels"].attrs: + initial_values[memory_index] = group[ch]["labels"].attrs[t] self.AWG.transfer_sequence_replay_samples(memory_index,data) if index!=last_index: self.AWG.seq_set_sequence_step(index,memory_index,index+1,1,'on_trigger',last_step=False) else: - self.AWG.seq_set_sequence_step(index,memory_index,0,1,'on_trigger',last_step=True) + self.AWG.seq_set_sequence_step(index,memory_index,index,1,'on_trigger',last_step=False) + # If there is a trigger at the stop time, repeat one last sequence + # and then stop (is not card_stop() was called already) + self.AWG.seq_set_sequence_step(index+1,memory_index,0,1,'always',last_step=True) # ONLY IMPLEMENTED FOR ONE CHANNEL, IF TWO CHANNELS ARE NEEDED WE ALREADY GET AN ERROR IN LABSCRIPT # FOR IMPLEMENTATION ONE HASE TO INTERWEAVE THE DATA FOR BOTH CHANNELS break self.AWG.card_write_setup() # TODO: Do we have to call that every shot or just once after the initialization? self.AWG.card_start() + self.AWG.card_enable_trigger() return initial_values def transition_to_manual(self): diff --git a/SpectrumAWG/labscript_devices.py b/SpectrumAWG/labscript_devices.py index c665dc49051b50fb1f4b1861bf9097f3304f323d..d840f5e5bcd1ccb0c4e1af3529f711aa04b2ccad 100644 --- a/SpectrumAWG/labscript_devices.py +++ b/SpectrumAWG/labscript_devices.py @@ -49,7 +49,7 @@ class AWGOutput(Output): num_samples = np.round(sample_duration*self.parent_device.sample_rate/4096)*4096 return num_samples - def generate_single_tone(self, t, sample_duration, frequency): + def generate_single_tone(self, t, sample_duration, frequency, label=None): """ Parameters ---------- @@ -59,6 +59,8 @@ class AWGOutput(Output): Duration of sample (that gets repeated), in seconds. frequency : float in Hz. + label : str, optional + Description of the instruction """ num_samples = self.calculate_num_samples(sample_duration) @@ -67,9 +69,9 @@ class AWGOutput(Output): if frequency<fundamental_frequency: raise LabscriptError(f"Freqeuncy of '{self.name}' at t={t} is smaller than the resolution ({fundamental_frequency:i}), change frequency or sample size/duration.") - self.add_instruction(t,(num_samples,frequency)) + self.add_instruction(t,(num_samples,frequency,label)) - def generate_multiple_tones(self, t, sample_duration, frequencies, amplitudes=None, phases=None): + def generate_multiple_tones(self, t, sample_duration, frequencies, amplitudes=None, phases=None, label=None): """ Parameters ---------- @@ -81,6 +83,8 @@ class AWGOutput(Output): Relative amplitude of each tone. If None, all have the same amplitude. phase : (optional) Phase for each tone. If None, all tones of zero (the same) phase. + label : str, optional + Description of the instruction """ num_samples = self.calculate_num_samples(sample_duration) frequencies = np.array(frequencies) @@ -98,7 +102,7 @@ class AWGOutput(Output): if not len(frequencies)==len(amplitudes)==len(phases): raise LabscriptError(f"Instruction for '{self.name}' at t={t} must have a frequency, amplitude and phase for all tones or none.") - self.add_instruction(t, (num_samples,*frequencies,*amplitudes, *phases)) + self.add_instruction(t, (num_samples,*frequencies,*amplitudes, *phases, label)) def do_checks(self): for t,instruction in self.instructions.items(): @@ -159,9 +163,12 @@ class SpectrumAWG(Device): change_times = output.get_change_times() group.require_group(output.connection) + group[output.connection].require_group("labels") for t in change_times: - group[output.connection].attrs[str(t)] = output.instructions[t] + group[output.connection].attrs[str(t)] = output.instructions[t][:-1] + if output.instructions[t][-1] is not None: + group[output.connection]["labels"].attrs[str(t)] = output.instructions[t][-1] diff --git a/SpectrumAWG/testing/SpectrumAWG_testing.py b/SpectrumAWG/testing/SpectrumAWG_testing.py index a41845a2f97da8323b28ed42c5d87247e579fdf3..5055a90635d2422e31240e9e089a10628feebc79 100644 --- a/SpectrumAWG/testing/SpectrumAWG_testing.py +++ b/SpectrumAWG/testing/SpectrumAWG_testing.py @@ -1,41 +1,118 @@ -from labscript import start, stop, MHz, s -from user_devices.SpectrumAWG.labscript_devices import SpectrumAWG,AWGOutput -# from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster,PrawnDirectTrigger -from user_devices.ADwinProII.labscript_devices import ADwinProII -from user_devices.ADwinProII.labscript_devices_ADwin_modules import ADwinAI8,ADwinAO8,ADwinDIO32,ADwinAnalogOut -import numpy as np +import time +from user_devices.SpectrumAWG.SpectrumCard import * +# Main Code +if __name__ == "__main__": -################################################################ -################## INIT DEVICES ################################ -################################################################ + # Open Connection to Card + tweezerAWG = SpectrumCard('/dev/spcm0') + tweezerAWG.open() + #tweezerAWG.print_available_pci_features() + #tweezerAWG.print_sequence_setting_limits() -# Initialize ADwin-Pro II -ADwinProII(name="ADwin", process_buffered=r"none.TC1", process_manual=r"none.TC2", mock=True) -ADwinDIO32("DIO32_1", ADwin, module_address=1, TiCo=True) -ADwinDIO32("DIO32_2", ADwin, module_address=2, TiCo=True) -ADwinAO8("AO8_1", ADwin, module_address=5) -ADwinAO8("AO8_2", ADwin, module_address=6) -ADwinAI8("AI8_1", ADwin, module_address=3) -ADwinAI8("AI8_2", ADwin, module_address=4) + # Set sampling rate, clock and output channels + sample_rate = MEGA(1250) + tweezerAWG.set_clock('external',MEGA(10)) + tweezerAWG.set_sample_rate(sample_rate) + tweezerAWG.set_channel_status(0,True) + tweezerAWG.set_channel_enable(0,True) + tweezerAWG.set_channel_amplitude(0,100) + tweezerAWG.set_channel_filter(0,False) + tweezerAWG.set_channel_mode(0,'double') -ADwinAnalogOut("Test", AO8_1, "1") + # Trigger Settings + #tweezerAWG.print_available_trigger_sources() + #tweezerAWG.print_available_ext_trigger_modes() + #tweezerAWG.print_available_and_trigger_sources() + #tweezerAWG.print_available_or_trigger_sources() + tweezerAWG.set_ext_trigger_mode('ext0','pos',rearm=True) + tweezerAWG.set_ext_trigger_level('ext0',800,2000) + tweezerAWG.set_trigger_or_mask({'ext0'}) -# PrawnBlaster(name="Prawn", trigger_device=None, trigger_connection=None, com_port="COM3", use_wait_monitor=True) -# PrawnDirectTrigger(name="SpectrumTrigger", parent_device=Prawn.clocklines[0], trigger_edge_type='rising') + # Set Generation Mode + demo_mode = 'seq' # 'single', 'seq', 'fifo' + # 'single': Configures the card for single-shot data replay, where a fixed set of data is played once after the first trigger. + # 'seq': Sets the card to sequence mode, allowing for complex, multi-step sequences of data to be replayed in a specified order. + # 'fifo': Configures the card for FIFO mode, enabling continuous data streaming by replenishing data in real-time. -SpectrumAWG("TestAWG","/dev/spcm0",timeout=5000,sample_rate=1250e6) -# AWGOutput("Tweezers", TestAWG, "0", SpectrumTrigger, "trigger", 100) -AWGOutput("Tweezers", TestAWG, "0", DIO32_1, "1", 100) + if demo_mode == 'single': + tweezerAWG.set_generation_mode(mode='single') + tweezerAWG.set_loops(0) + elif demo_mode == 'seq': + tweezerAWG.set_generation_mode(mode='sequence') + tweezerAWG.seq_set_memory_segments(2**16) + elif demo_mode == 'fifo': + tweezerAWG.set_generation_mode(mode='fifo_single') -frequencies = np.arange(100*MHz,210*MHz,10*MHz) -if __name__ == '__main__': - start() + # Generate Data + if demo_mode == 'single' or demo_mode == 'fifo': + num_samples = 4096*320 # Has to be a multiple of 4096 !!! + fundamental_frequency = sample_rate/num_samples # Only integer multiples of this frequency can be generated - t = 0 - for i in range(frequencies.size): - Tweezers.generate_multiple_tones(t,2e-5,frequencies[:i+1]) - t = 1*s + # Generate Single Tone + frequency = fundamental_frequency*100 + samples = generate_single_tone(frequency, num_samples) - stop(t) + # Generate Multi Frequency Tones + #unique_random_numbers = random.sample(range(100, 2000, 10), 100) + #frequency_list = [num * fundamental_frequency for num in unique_random_numbers] + #samples=generate_multi_tone(frequency_list,np.ones(len(frequency_list)),num_samples,sample_rate) + + # Write Data to Card + if demo_mode == 'single': + tweezerAWG.transfer_single_replay_samples(samples) + if demo_mode == 'fifo': + notify_size = 4096*32 + tweezerAWG.fifo_initialize_buffer(samples,notify_size) + + # Generate and Write Sequence Data + if demo_mode == 'seq': + num_samples = 4096 + fundamental_frequency = sample_rate/num_samples + + for i in range(100): + data = generate_single_tone(fundamental_frequency*(i+1),num_samples) + tweezerAWG.transfer_sequence_replay_samples(i,data) + if i < 99: + tweezerAWG.seq_set_sequence_step(i,i,i+1,1,'on_trigger',last_step=False) + else: + tweezerAWG.seq_set_sequence_step(i,i,0,1,'on_trigger',last_step=True) + + + tweezerAWG.card_write_setup() + tweezerAWG.card_start() + + input() + tweezerAWG.card_force_trigger() + if demo_mode == 'single': + input() + + if demo_mode == 'seq': + for i in range(100): + input() + tweezerAWG.card_force_trigger() + + if demo_mode == 'fifo': + fifo_bytes_avail_user = int32(0) + fifo_pos_pc = int32(0) + + start_time = time.time() + while (time.time()-start_time) < 5: + + # Get number of free bytes on card memory that can be written: + spcm_dwGetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_USER_LEN, byref(fifo_bytes_avail_user)) + # Position of pointer in PC buffer (would be needed if data is actually updated) + spcm_dwGetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_USER_POS, byref(fifo_pos_pc)) + + #If enough bytes are free, write new data to FIFO + if fifo_bytes_avail_user.value > notify_size: + dwError = spcm_dwSetParam_i32(tweezerAWG.hCard, SPC_DATA_AVAIL_CARD_LEN, notify_size) + if dwError != ERR_OK: + tweezerAWG.handle_error() + + #Wait for current data transfer (of size notify_size) to be finished + dwError = spcm_dwSetParam_i32(tweezerAWG.hCard, SPC_M2CMD, M2CMD_DATA_WAITDMA) + + tweezerAWG.card_stop() + tweezerAWG.card_close() \ No newline at end of file