def test_units(self): hdl = MemHandler() class Spam(Driver): _logger = get_logger('test.feat') _logger.addHandler(hdl) _logger.setLevel(logging.DEBUG) _eggs = {'answer': 42} @DictFeat(units='s') def eggs(self_, key): return self_._eggs[key] @eggs.setter def eggs(self_, key, value): self_._eggs[key] = value obj = Spam() self.assertQuantityEqual(obj.eggs['answer'], Q_(42, 's')) obj.eggs['answer'] = Q_(46, 'ms') self.assertQuantityEqual(obj.eggs['answer'], Q_(46 / 1000, 's')) with must_warn(DimensionalityWarning, 1) as msg: obj.eggs['answer'] = 42 self.assertFalse(msg, msg=msg)
def test_action(self): obj = aDriver() self.assertEqual(obj.run(), 42) self.assertEqual(obj.run2(2), 42 * 2) self.assertEqual(obj.run3(3), 42 * 3) self.assertEqual(obj.run4(Q_(3, 'ms')), 3) self.assertEqual(obj.run4b(Q_(3, 'ms')), 3) self.assertEqual(obj.run4(Q_(3, 's')), 3000)
def at_pos(self, axis, pos, delta_z=Q_(0.1, 'um'), iter_n=10, delay=Q_(0.01, 's')): for i in range(iter_n): time.sleep(delay) if abs(self.position[axis].to('um').magnitude - pos) > delta_z: return False return True
def test_Self_exceptions(self): class X(Driver): @Feat(units=Self.units) def value(self): return 1 @value.setter def value(self, value): pass @Feat() def units(self): return self._units @units.setter def units(self, value): self._units = value x = X() self.assertRaises(Exception, getattr, x, 'value') self.assertRaises(Exception, setattr, x, 'value', 1) x.units = 'ms' self.assertEqual(x.feats.value.units, 'ms') self.assertEqual(x.value, Q_(1, 'ms'))
def test_units_tuple(self): hdl = MemHandler() class Spam(Driver): _logger = get_logger('test.feat', False) _logger.addHandler(hdl) _logger.setLevel(logging.DEBUG) _eggs = (8, 1) # Transform each element of the return vector # based on the set signature @Feat(units=('s', 's')) def eggs(self_): return self_._eggs @eggs.setter def eggs(self_, values): self_._eggs = values class Spam2(Driver): _logger = get_logger('test.feat', False) _logger.addHandler(hdl) _logger.setLevel(logging.DEBUG) _eggs = (8, 1) # Transform each element of the return vector # based on the set signature @Feat(units=('s', None)) def eggs(self_): return self_._eggs @eggs.setter def eggs(self_, values): self_._eggs = values obj = Spam() self.assertQuantityEqual(obj.eggs, (Q_(8, 's'), Q_(1, 's'))) self.assertEqual(setattr(obj, "eggs", (Q_(3, 'ms'), Q_(4, 'ms'))), None) self.assertQuantityEqual(obj.eggs, (Q_(3 / 1000, 's'), Q_(4 / 1000, 's'))) with must_warn(DimensionalityWarning, 2) as msg: self.assertEqual(setattr(obj, "eggs", (3, 1)), None) self.assertFalse(msg, msg=msg) obj = Spam2() self.assertQuantityEqual(obj.eggs, (Q_(8, 's'), 1)) self.assertEqual(setattr(obj, "eggs", (Q_(3, 'ms'), 4)), None) self.assertQuantityEqual(obj.eggs, (Q_(3 / 1000, 's'), 4))
def test_instance_specific(self): x = aDriver() y = aDriver() val = Q_(3, 's') self.assertEqual(x.run4(val), y.run4(val)) x.actions.run4.param('value', units='s') self.assertNotEqual(x.run4(val), y.run4(val)) self.assertEqual(x.run4(val), 3)
def assertQuantityEqual(self, q1, q2, msg=None, delta=None): """ Make sure q1 and q2 are the same quantities to within the given precision. """ delta = 1e-5 if delta is None else delta msg = '' if msg is None else ' (%s)' % msg q1 = Q_(q1) q2 = Q_(q2) d1 = getattr(q1, '_dimensionality', None) d2 = getattr(q2, '_dimensionality', None) if (d1 or d2) and not (d1 == d2): raise self.failureException( "Dimensionalities are not equal (%s vs %s)%s" % (d1, d2, msg))
def __init__(self, channel_dict=default_digi_dict, laser_time=150 * Q_(1, "us"), readout_time=150 * Q_(1, "us"), buffer_after_init=450 * Q_(1, "us"), buffer_after_readout=2 * Q_(1, "us"), polarize_time=900 * Q_(1, "us"), settle=150 * Q_(1, "us"), reset=100 * Q_(1, "ns"), IQ=[0.5, 0], ip="192.168.1.111"): """ :param channel_dict: Dictionary of which channels correspond to which instr controls :param laser_time: Laser time in us :param CTR: When False CTR0 When True CTR1 :param readout_time: Readout time in us :param buffer_after_init: Buffer after laser turns off in us to allow the population to thermalize :param buffer_after_readout: Buffer between the longest pulse and the readout in us to prevent laser to leak in :param IQ: IQ vector that rotates the spin """ super().__init__() self.channel_dict = channel_dict self.laser_time = int(round(laser_time.to("ns").magnitude)) self.readout_time = int(round(readout_time.to("ns").magnitude)) self.buffer_after_init = int( round(buffer_after_init.to("ns").magnitude)) self.buffer_after_readout = int( round(buffer_after_readout.to("ns").magnitude)) self.polarize_time = int(round(polarize_time.to("ns").magnitude)) self.settle = int(round(settle.to("ns").magnitude)) self.reset = int(round(reset.to("ns").magnitude)) self._normalize_IQ(IQ) self.Pulser = PulseStreamer(ip)
def relative_move(self, axis, delta): delta = Q_(delta, 'um') if abs(delta) > MAX_RELATIVE_MOVE: raise Exception( "Relative move <delta> is greater then the MAX_RELATIVE_MOVE") else: target = self.position + delta target = target.to('m').magnitude print(target)
def __init__(self, input_ch, trigger_ch, acq_time=Q_('10 ms'), pts=1000): self.task = AnalogInputTask('SA201') self.task.add_channel(VoltageInputChannel(input_ch)) self.task.configure_trigger_digital_edge_start(trigger_ch, edge='rising') self.acq_time = acq_time self.pts = self.pts return
def initialize(self, devNo=None): if not devNo is None: self.devNo = devNo device = c_void_p() self.check_error(self.lib.connect(self.dev_no, pointer(device))) self.device = device # Wait until we get something else then 0 on the position while (self.position[2] == Q_(0, 'um')): time.sleep(0.025)
def read(self, samples_per_channel=None, timeout=Q_(10.0, 's')): """Read multiple 32-bit integer samples from a counter task. Use this function when counter samples are returned unscaled, such as for edge counting. :param samples_per_channel: The number of samples, per channel, to read. The default value of -1 (DAQmx_Val_Auto) reads all available samples. If readArray does not contain enough space, this function returns as many samples as fit in readArray. NI-DAQmx determines how many samples to read based on whether the task acquires samples continuously or acquires a finite number of samples. If the task acquires samples continuously and you set this parameter to -1, this function reads all the samples currently available in the buffer. If the task acquires a finite number of samples and you set this parameter to -1, the function waits for the task to acquire all requested samples, then reads those samples. If you set the Read All Available Samples property to TRUE, the function reads the samples currently available in the buffer and does not wait for the task to acquire all requested samples. :param timeout: The amount of time, in seconds, to wait for the function to read the sample(s). The default value is 10.0 seconds. To specify an infinite wait, pass -1 (DAQmx_Val_WaitInfinitely). This function returns an error if the timeout elapses. A value of 0 indicates to try once to read the requested samples. If all the requested samples are read, the function is successful. Otherwise, the function returns a timeout error and returns the samples that were actually read. :return: The array of samples read. """ if samples_per_channel is None: samples_per_channel = self.samples_per_channel_available() data = np.zeros((samples_per_channel, ), dtype=np.int32) err, count = self.lib.ReadCounterU32(samples_per_channel, float(timeout.to('s').magnitude), data.ctypes.data, data.size, RetValue('i32'), None) return data[:count]
def test_set_units(self): class Spam(Driver): _eggs = 8 @Feat(values={1, 2.2, 10}, units='second') def eggs(self_): return self_._eggs @eggs.setter def eggs(self_, value): self_._eggs = value obj = Spam() for mult, units in ((1., 'second'), (1000., 'millisecond'), (0.001, 'kilosecond')): val = Q_(2.2 * mult, units) obj.eggs = val self.assertEqual(obj.eggs, val) self.assertRaises(ValueError, setattr, obj, "eggs", Q_(11. * mult, units)) self.assertRaises(ValueError, setattr, obj, "eggs", Q_(0.9 * mult, units))
def absolute_move(self, axis, target, max_move=MAX_ABSOLUTE_MOVE): if not max_move is None: if abs(self.position[axis] - Q_(target, 'm')) > max_move: raise Exception( "Relative move (target-current) is greater then the max_move" ) self.check_error(self.lib.setTargetPosition(self.device, axis, target)) enable = 0x01 relative = 0x00 self.check_error( self.lib.startAutoMove(self.device, axis, enable, relative)) return
def test_Self(self): class X(Driver): @Feat(units=Self.a_value_units('s')) def a_value(self): return 1 @Feat() def a_value_units(self): return self._units @a_value_units.setter def a_value_units(self, new_units): self._units = new_units x = X() self.assertEqual(x.feats.a_value.units, 's') self.assertEqual(x.a_value, Q_(1, 's')) x.a_value_units = 'ms' self.assertEqual(x.feats.a_value.units, 'ms') self.assertEqual(x.a_value, Q_(1, 'ms'))
def decode_data(data): if not msg_complete(data): raise Exception("Incomplete/Invalid data") length = data[:10] d = data[10:] obj = pickle.loads(d) # Special case to handle Quantity if repr( type(obj) ) == "<class 'pint.quantity.build_quantity_class.<locals>.Quantity'>": obj = Q_(str(obj)) return obj
def get_units(units): """Creates and display a UnitInputDialog and return new units. Parameters ---------- units : str current units. Returns ------- str or None output compatible units. Returns None if cancelled. """ if isinstance(units, str): units = Q_(1, units) dialog = UnitInputDialog(Q_(1, units.units)) if dialog.exec_(): return dialog.destination_units.text() return None
def bind_feat(self, feat): super().bind_feat(feat) #: self._units are the current units displayed by the widget. #: Respects units declared in the suffix if feat.units: suf = (self.suffix() if hasattr(self, 'suffix') else feat.units) or feat.units self._units = Q_(1, suf) self.change_units(self._units) else: self._units = None if feat.limits: self.change_limits(None)
def __init__(self, channel_dict=default_digi_dict, laser_time=150 * Q_(1, "us"), readout_time=150 * Q_(1, "us"), buf_after_init=450 * Q_(1, "us"), buf_after_readout=2 * Q_(1, "us"), polarize_time=900 * Q_(1, "us"), settle=150 * Q_(1, "us"), reset=100 * Q_(1, "ns"), IQ=[0.5, 0], ip="192.168.1.111"): """ :param channel_dict: Dictionary of which channels correspond to which instr controls :param laser_time: Laser time in us :param CTR: When False CTR0 When True CTR1 :param readout_time: Readout time in us :param buf_after_init: buf after laser turns off in us to allow the population to thermalize :param buf_after_readout: buf between the longest pulse and the readout in us to prevent laser to leak in :param IQ: IQ vector that rotates the spin """ super().__init__() self.channel_dict = channel_dict self._reverse_dict = { 0: "laser", 1: "offr_laser", 2: 'current_gate', 3: 'laser2', 4: "EOM", 5: "CTR", 6: "switch", 7: "gate", 8: "I", 9: "V" } # rev_dict self.laser_time = int(round(laser_time.to("ns").magnitude)) self.readout_time = int(round(readout_time.to("ns").magnitude)) self.buf_after_init = int(round(buf_after_init.to("ns").magnitude)) self.buf_after_readout = int( round(buf_after_readout.to("ns").magnitude)) self.polarize_time = int(round(polarize_time.to("ns").magnitude)) self.settle = int(round(settle.to("ns").magnitude)) self.reset = int(round(reset.to("ns").magnitude)) self._normalize_IQ(IQ) self.Pulser = PulseStreamer(ip) self.latest_streamed = pd.DataFrame({}) self.total_time = -1 # update when a pulse sequence is streamed
def __init__(self, ch, sw_ch, active_trig_ch, passive_trig_ch=None): self._points = 1000 self._period = Q_(10, 'ms') self._selectivity = 0.1 self.ch = ch self.sw_ch = sw_ch self.active_trig_ch = active_trig_ch self.passive_trig_ch = passive_trig_ch self._single_mode = True self._peak_locations = list() self._trace = np.zeros(self.points) self._sw_state = False self._trig_ch = self.passive_trig_ch self.acq_task = AnalogInputTask('fp_readout') self.sw_task = DigitalOutputTask('fp_sw_task') return
def check(self): units = self.destination_units.text().strip() if not units: return try: new_units = Q_(1, units) factor = self.units.to(new_units).magnitude except LookupError or SyntaxError: self.message.setText('Cannot parse units') self.buttonBox.setEnabled(False) except ValueError: self.message.setText('Incompatible units') self.buttonBox.setEnabled(False) except AttributeError: self.message.setText('Unknown units') self.buttonBox.setEnabled(False) else: self.message.setText('factor {:f}'.format(factor)) self.buttonBox.setEnabled(True)
def run_calibration(self, power_fun, npoints=500, min_pt=0, max_pt=5, delay_per_point=0.1): voltages = np.linspace(min_pt, max_pt, npoints) powers = np.zeros(npoints) for i, v in enumerate(voltages): self.voltage = Q_(v, 'V') time.sleep(delay_per_point) powers[i] = power_fun().to('W').m print('{} V = {} W'.format(v, powers[i])) data = np.transpose(np.array([voltages, powers])) np.savetxt(self.calibration_file, data, delimiter=",", header='voltage,power', comments='') return data
def request_new_units(current_units): """Ask for new units using a dialog box and return them. Parameters ---------- current_units : Quantity current units or magnitude. Returns ------- None or Quantity """ new_units = UnitInputDialog.get_units(current_units) if new_units is None: return None try: return Q_(1, new_units) except LookupError: # cannot parse units return None
def read_scalar(self, timeout=Q_(10.0, 's')): """Read a single floating-point sample from a counter task. Use this function when the counter sample is scaled to a floating-point value, such as for frequency and period measurement. :param float: The amount of time, in seconds, to wait for the function to read the sample(s). The default value is 10.0 seconds. To specify an infinite wait, pass -1 (DAQmx_Val_WaitInfinitely). This function returns an error if the timeout elapses. A value of 0 indicates to try once to read the requested samples. If all the requested samples are read, the function is successful. Otherwise, the function returns a timeout error and returns the samples that were actually read. :return: The sample read from the task. """ err, value = self.lib.ReadCounterScalarF64( timeout.to('s').magnitude, RetValue('f64'), None) return value
def _relabel(self, axis): """Builds the actual label using the label and units for a given axis. Also builds a quantity to be used to normalized the data. Parameters ---------- axis : x' or 'y' Returns ------- """ label = self._labels[axis] units = self._units[axis] if label and units: label = '%s [%s]' % (label, units) elif units: label = '[%s]' % units self.pw.setLabel(self._axis[axis], label) if units: setattr(self, '_q' + axis, Q_(1, units))
def _set_position(self, x, y): target = enforce_point_units([x, y]) step_voltages = self.ao_smooth_func(self._position, target) if step_voltages.size: clock_config = { 'source': 'OnboardClock', 'rate': self.ao_smooth_rate.to('Hz').m, 'sample_mode': 'finite', 'samples_per_channel': step_voltages.shape[0], } self.task.configure_timing_sample_clock(**clock_config) self.task.configure_trigger_disable_start() task_config = { 'data': step_voltages, 'auto_start': False, 'timeout': Q_(0, 's'), 'group_by': 'scan', } self.task.write(**task_config) self.task.start() time.sleep( (step_voltages.shape[0] / self.ao_smooth_rate).to('s').m) self.task.stop() self._position = target
def change_limits(self, new_units): """Change the limits (range) of the control taking the original values from the feat and scaling them to the new_units. """ if not hasattr(self, 'setRange'): return rng = self._feat.limits if not rng: return if new_units: conv = lambda ndx: Q_(rng[ndx], self._feat.units).to(new_units ).magnitude else: conv = lambda ndx: rng[ndx] if len(rng) == 1: self.setRange(0, conv(0)) else: self.setRange(conv(0), conv(1)) if len(rng) == 3: self.setSingleStep(conv(2))
def cl_move(self, axis, pos, delta_z=Q_(0.1, 'um'), iter_n=10, delay=Q_(0.01, 's'), debug=False, max_iter=1000): i = 0 while (not self.at_pos(axis, Q_(pos, 'um'), delta_z=Q_(delta_z, 'um'), iter_n=iter_n, delay=Q_(delay, 's'))): self.position[axis] = Q_(pos, 'um') i += 1 if i >= max_iter: raise Exception("Reached max_iter") if debug: print("It took {} iterations to move to position".format(i)) return
def read(self, samples_per_channel=None, timeout=Q_(10.0, 's'), group_by='channel'): """Reads multiple floating-point samples from a task that contains one or more analog input channels. :param samples_per_channel: The number of samples, per channel, to read. The default value of -1 (DAQmx_Val_Auto) reads all available samples. If readArray does not contain enough space, this function returns as many samples as fit in readArray. NI-DAQmx determines how many samples to read based on whether the task acquires samples continuously or acquires a finite number of samples. If the task acquires samples continuously and you set this parameter to -1, this function reads all the samples currently available in the buffer. If the task acquires a finite number of samples and you set this parameter to -1, the function waits for the task to acquire all requested samples, then reads those samples. If you set the Read All Available Samples property to TRUE, the function reads the samples currently available in the buffer and does not wait for the task to acquire all requested samples. :param timeout: float The amount of time, in seconds, to wait for the function to read the sample(s). The default value is 10.0 seconds. To specify an infinite wait, pass -1 (DAQmx_Val_WaitInfinitely). This function returns an error if the timeout elapses. A value of 0 indicates to try once to read the requested samples. If all the requested samples are read, the function is successful. Otherwise, the function returns a timeout error and returns the samples that were actually read. :param group_by: 'channel' Group by channel (non-interleaved):: ch0:s1, ch0:s2, ..., ch1:s1, ch1:s2,..., ch2:s1,.. 'scan' Group by scan number (interleaved):: ch0:s1, ch1:s1, ch2:s1, ch0:s2, ch1:s2, ch2:s2,... :rtype: numpy.ndarray """ if samples_per_channel is None: samples_per_channel = self.samples_per_channel_available() number_of_channels = self.number_of_channels() if group_by == Constants.Val_GroupByScanNumber: data = np.zeros((samples_per_channel, number_of_channels), dtype=np.float64) else: data = np.zeros((number_of_channels, samples_per_channel), dtype=np.float64) err, count = self.lib.ReadAnalogF64(samples_per_channel, timeout, group_by, data.ctypes.data, data.size, RetValue('i32'), None) if samples_per_channel < count: if group_by == 'scan': return data[:count] else: return data[:, :count] return data
def write(self, data, auto_start=True, timeout=Q_(10.0, 'seconds'), group_by='scan'): """ Writes multiple samples to each digital line in a task. When you create your write array, each sample per channel must contain the number of bytes returned by the DAQmx_Read_DigitalLines_BytesPerChan property. Note: If you configured timing for your task, your write is considered a buffered write. Buffered writes require a minimum buffer size of 2 samples. If you do not configure the buffer size using DAQmxCfgOutputBuffer, NI-DAQmx automatically configures the buffer when you configure sample timing. If you attempt to write one sample for a buffered write without configuring the buffer, you will receive an error. Parameters ---------- data : array The samples to write to the task. auto_start : bool Specifies whether or not this function automatically starts the task if you do not start it. timeout : float The amount of time, in seconds, to wait for this function to write all the samples. The default value is 10.0 seconds. To specify an infinite wait, pass -1 (DAQmx_Val_WaitInfinitely). This function returns an error if the timeout elapses. A value of 0 indicates to try once to write the submitted samples. If this function successfully writes all submitted samples, it does not return an error. Otherwise, the function returns a timeout error and returns the number of samples actually written. layout : {'group_by_channel', 'group_by_scan_number'} Specifies how the samples are arranged, either interleaved or noninterleaved: 'group_by_channel' - Group by channel (non-interleaved). 'group_by_scan_number' - Group by scan number (interleaved). """ number_of_channels = self.number_of_channels() if np.isscalar(data): data = np.array([data] * number_of_channels, dtype=np.uint8) else: data = np.asarray(data, dtype=np.uint8) if data.ndim == 1: if number_of_channels == 1: samples_per_channel = data.shape[0] shape = (samples_per_channel, 1) else: samples_per_channel = data.size / number_of_channels shape = (samples_per_channel, number_of_channels) if not group_by == Constants.Val_GroupByScanNumber: shape = tuple(reversed(shape)) data.reshape(shape) else: if group_by == Constants.Val_GroupByScanNumber: samples_per_channel = data.shape[0] else: samples_per_channel = data.shape[-1] err, count = self.lib.WriteDigitalLines(samples_per_channel, auto_start, timeout, group_by, data.ctypes.data, RetValue('u32'), None) return count