def test_get_pulses(self): pulse_sequence = PulseSequence() self.assertListEqual(pulse_sequence.get_pulses(), []) pulse1 = DCPulse(name='dc1', amplitude=1.5, duration=10, t_start=1) pulse2 = DCPulse(name='dc2', amplitude=2.5, duration=10, t_start=1) pulse3 = TriggerPulse(name='trig', duration=12, t_start=1) pulse_sequence.add(pulse1, pulse2, pulse3) subset_pulses = pulse_sequence.get_pulses() self.assertEqual(subset_pulses[0], pulse1) self.assertListEqual(subset_pulses, [pulse1, pulse2, pulse3]) subset_pulses = pulse_sequence.get_pulses(t_start=1) self.assertListEqual(subset_pulses, [pulse1, pulse2, pulse3]) subset_pulses = pulse_sequence.get_pulses(duration=10) self.assertListEqual(subset_pulses, [pulse1, pulse2]) subset_pulses = pulse_sequence.get_pulses(amplitude=1.5) self.assertListEqual(subset_pulses, [pulse1]) subset_pulses = pulse_sequence.get_pulses(amplitude=('>', 1.5)) self.assertListEqual(subset_pulses, [pulse2]) subset_pulses = pulse_sequence.get_pulses(amplitude=('>=', 1.5)) self.assertListEqual(subset_pulses, [pulse1, pulse2]) pulse = pulse_sequence.get_pulse(amplitude=1.5) self.assertEqual(pulse, pulse1) pulse = pulse_sequence.get_pulse(duration=12) self.assertEqual(pulse, pulse3) with self.assertRaises(RuntimeError): pulse_sequence.get_pulse(duration=10)
def get_additional_pulses(self, connections) -> list: """Additional pulses required for instrument, e.g. trigger pulses. Args: connections: List of all connections in the layout Returns: * Empty list if there are no acquisition pulses. * A single trigger pulse at start of acquisition if using triggered acquisition controller. * AcquisitionPulse and TriggerWaitPulse if using the steered initialization controller Raises: NotImplementedError Using continous acquisition controller """ if not self.pulse_sequence.get_pulses(acquire=True): # No pulses need to be acquired return [] elif self.acquisition_controller() == 'Triggered': # Add a single trigger pulse when starting acquisition if not self.capture_full_trace(): t_start = min(pulse.t_start for pulse in self.pulse_sequence.get_pulses(acquire=True)) else: t_start = 0 return [TriggerPulse(t_start=t_start, connection_requirements={ 'input_instrument': self.instrument_name(), 'trigger': True})] elif self.acquisition_controller() == 'Continuous': raise NotImplementedError("Continuous mode not implemented") elif self.acquisition_controller() == 'SteeredInitialization': # TODO add possibility of continuous acquisition having correct # timing even if it should acquire for the entire duration of the # pulse sequence. t_start = min(pulse.t_start for pulse in self.pulse_sequence.get_pulses(acquire=True)) t_stop = max(pulse.t_stop for pulse in self.pulse_sequence.get_pulses(acquire=True)) # Add a marker high for readout stage # Get steered initialization pulse initialization = self.pulse_sequence.get_pulse(initialize=True) acquisition_pulse = MarkerPulse( t_start=t_start, t_stop=t_stop, connection_requirements={ 'connection': initialization.trigger_connection}) trigger_wait_pulse = TriggerWaitPulse( t_start=self.pulse_sequence.duration, connection_requirements={ 'output_arg': 'ATS.software_trig_out'}) return [acquisition_pulse, trigger_wait_pulse] else: raise Exception("No controller found")
def _get_trigger_pulse(self, t: float) -> TriggerPulse: """Create input trigger pulse Args: t: trigger start time Returns: Trigger pulse with specified start time """ trigger_pulse = TriggerPulse(t_start=t, duration=self.trigger_in_duration(), connection_requirements={ 'input_instrument': self.instrument_name(), 'trigger': True }) return trigger_pulse
def get_additional_pulses(self, **kwargs): # Currently the only supported sequence_mode is `once`, i.e. one trigger # at the start of the pulse sequence # Request a single trigger at the start of the pulse sequence logger.info(f"Creating trigger for Keysight 81180A: {self.name}") return [ TriggerPulse( name=self.name + "_trigger", t_start=0, duration=self.trigger_in_duration(), connection_requirements={ "input_instrument": self.instrument_name(), "trigger": True, }, ) ]
def get_additional_pulses(self, connections) -> List[Pulse]: """Get list of pulses required by instrument (trigger pulses) A trigger pulse is returned for each pulse start and stop time. """ assert self.use_trig_in( ), "Interface not yet programmed for pxi triggering" # Get list of unique pulse start and stop times t_list = self.pulse_sequence.t_list trigger_pulses = [ TriggerPulse(t_start=t, duration=self.trigger_in_duration(), connection_requirements={ 'input_instrument': self.instrument.name, 'input_channel': self._input_channels['trig_in'] }) for t in t_list if t != self.pulse_sequence.duration ] return trigger_pulses
def get_additional_pulses(self, connections) -> List[Pulse]: """Additional pulses needed by instrument after targeting of main pulses Args: connections: List of all connections in the layout Returns: List containing trigger pulse if not primary instrument """ # Request one trigger at the start if not primary if not self.is_primary(): return [ TriggerPulse(t_start=0, connection_requirements={ 'input_instrument': self.instrument_name(), 'trigger': True }) ] else: return []
def get_additional_pulses(self, connections) -> List[Pulse]: """Additional pulses needed by instrument after targeting of main pulses Trigger pulses are requested if trigger mode is hardware. Trigger at t_start is requested if there is a connection.trigger. Trigger at t=0 is requested if there is a connection.trigger_start/ Args: connections: List of all connections in the layout Returns: List of additional pulses, empty by default. """ if self.is_primary(): # Instrument does not require triggers return [] elif not self.pulse_sequence: # AWG does not output pulses return [] elif self.trigger_mode() in ['software', 'none']: return [] else: # Hardware trigger if self.input_pulse_sequence.get_pulses(trigger=True) \ or self.input_pulse_sequence.get_pulses(trigger_start=True): logger.warning(f'Trigger(s) manually defined for {self.name}: ' f'{self.input_pulse_sequence.get_pulses(trigger=True)}') return [] trigger_connection = next( connection for connection in connections if (connection.trigger or connection.trigger_start) and connection.input['instrument'] == self.instrument_name()) return [TriggerPulse(name=f'{self.name}_trigger', t_start=0, duration=15e-6, connection=trigger_connection)]
def get_additional_pulses(self, **kwargs): # Return empty list if no pulses are in the pulse sequence if not self.pulse_sequence or self.is_primary(): return [] active_channels = list( set(pulse.connection.output['channel'].name for pulse in self.pulse_sequence)) connections = { ch: self.pulse_sequence.get_connection( output_instrument=self.instrument.name, output_channel=ch) for ch in active_channels } # If a pulse starts on one channel and needs a trigger while another # pulse is still active on the other channel, this would cause the other # pulse to move onto the next pulse prematurely. This only happens if # the other pulse finished its waveform and is waiting for a trigger. # Here we check that this does not occur. if len(active_channels) > 1: t = 0 gap_pulses = [] for t_start in self.pulse_sequence.t_start_list: if t_start < t: raise RuntimeError( f'Pulse starting before end of previous pulse {t}') elif t_start > t: # Add gap pulses for each channel for ch in active_channels: gap_pulse = DCPulse(t_start=t, t_stop=t_start, amplitude=0, connection=connections[ch]) gap_pulses.append( self.get_pulse_implementation(gap_pulse)) t = t_start pulses = { ch: self.pulse_sequence.get_pulse(t_start=t_start, output_channel=ch) for ch in active_channels } if pulses['ch1'] is None and pulses['ch2'] is None: raise RuntimeError( f"pulse sequence has t_start={t_start}, " "but couldn't find pulse for either channel") elif pulses['ch1'] is not None and pulses['ch2'] is not None: assert pulses['ch1'].t_stop == pulses['ch2'].t_stop, \ f"t_stop of pulses starting at {t_start} must be equal." \ f"This is a current limitation of the AWG interface." t = pulses['ch1'].t_stop elif pulses['ch1'] is not None: # add gap pulse for ch2 gap_pulse = DCPulse(t_start=t_start, t_stop=pulses['ch1'].t_stop, amplitude=0, connection=connections['ch2']) gap_pulses.append(self.get_pulse_implementation(gap_pulse)) t = pulses['ch1'].t_stop else: # add gap pulse for ch1 gap_pulse = DCPulse(t_start=t_start, t_stop=pulses['ch2'].t_stop, amplitude=0, connection=connections['ch1']) gap_pulses.append(self.get_pulse_implementation(gap_pulse)) t = pulses['ch2'].t_stop else: ch = active_channels[0] # only add gap pulses for active channel connection = self.pulse_sequence.get_connection( output_instrument=self.instrument.name, output_channel=ch) gap_pulses = [] t = 0 for t_start in self.pulse_sequence.t_start_list: if t_start < t: raise RuntimeError('Pulse starting before previous pulse ' f'finished {t} s') elif t_start > t: # Add gap pulse gap_pulse = DCPulse(t_start=t, t_stop=t_start, amplitude=0, connection=connection) gap_pulses.append(self.get_pulse_implementation(gap_pulse)) pulse = self.pulse_sequence.get_pulse(t_start=t_start, output_channel=ch) # Pulse will be None if the pulse sequence has a final delay if pulse is not None: t = pulse.t_stop if t != self.pulse_sequence.duration: for ch in active_channels: gap_pulse = DCPulse(t_start=t, t_stop=self.pulse_sequence.duration, amplitude=0, connection=connections[ch]) gap_pulses.append(self.get_pulse_implementation(gap_pulse)) if gap_pulses: self.pulse_sequence.add(*gap_pulses) # TODO test if first waveform needs trigger as well additional_pulses = [ TriggerPulse(t_start=t_start, duration=self.trigger_in_duration(), connection_requirements={ 'input_instrument': self.instrument_name(), 'trigger': True }) for t_start in self.pulse_sequence.t_start_list + [self.pulse_sequence.duration] ] return additional_pulses