def paint(self, p, opt, widget): vb = self._view() color = self._cmdp[self._instance_prefix + 'color'] p.resetTransform() vb_rect = vb.geometry() y = self.scene_pos_y() p1 = pg.Point(vb_rect.left(), y) p2 = pg.Point(vb_rect.right(), y) pen = pg.mkPen(color) pen.setWidth(1) p.setPen(pen) p.drawLine(p1, p2) show_stats = self.statistics_show if show_stats != 'off': pen = pg.mkPen([255, 255, 255, 255]) p.setPen(pen) if self.pair is None: txt = three_sig_figs(self.y, self._units) else: dy = abs(self.y - self.pair.y) t1 = three_sig_figs(self.y, self._units) t2 = three_sig_figs(dy, self._units) txt = f'{t1}, Δ={t2}' if show_stats == 'bottom': font = QtGui.QFont() h = QtGui.QFontMetrics(font).height() p1 += pg.Point(0, h) else: p1 += pg.Point(0, -2) p.drawText(p1, txt)
def _statistics_to_user_str(self): if self._statistics is None: return "no statistics" duration = self._statistics['time']['range'] charge = self._statistics['accumulators']['charge'] energy = self._statistics['accumulators']['energy'] duration_str = f"{int(duration['value'][0])} {duration['units']}" charge_str = three_sig_figs(charge['value'], charge['units']) energy_str = three_sig_figs(energy['value'], energy['units']) msg = f'duration = {duration_str}, charge = {charge_str}, energy = {energy_str}' return msg
def _on_device_statistics(self, topic, statistics): """Update the multimeter display :param statistics: The statistics data structure """ if not statistics: return if self.accumulateButton.isChecked(): self._accumulate_duration += statistics['time']['delta']['value'] if self._accumulate_start is None: self._accumulate_start = datetime.datetime.now().isoformat( ).split('.')[0] elapsed_time = self._cmdp.elapsed_time_formatter( self._accumulate_duration) accum_txt = f'{elapsed_time} | Started at {self._accumulate_start}' else: self._accumulate_duration = statistics['time']['delta']['value'] accum_txt = three_sig_figs(self._accumulate_duration, 's') for name, field in statistics['signals'].items(): if name not in self.values: continue self.values[name].update_value(field) accum_time = statistics['time']['accumulator'] energy = statistics['accumulators']['energy']['value'] charge = statistics['accumulators']['charge']['value'] self.values['energy'].update_energy(accum_time['value'], energy, charge) self.accumulateDurationLabel.setText(accum_txt) self.accumulateDurationLabel.mousePressEvent = self._on_accumulate_duration_mouse_press
def _on_device_state_status(self, topic, status): for root_key, root_value in status.items(): if root_key == 'endpoints': root_value = root_value.get('2', {}) for key, value in root_value.items(): # print(f'{root_key}.{key} = {value}') s = self._status.get(key) if s is None: # create # print(f'Create {key} : {self._status_row}') label_name = QtWidgets.QLabel(self._device_status_widget) label_value = QtWidgets.QLabel(self._device_status_widget) label_units = QtWidgets.QLabel(self._device_status_widget) self._device_status_layout.addWidget(label_name, self._status_row, 0, 1, 1) self._device_status_layout.addWidget(label_value, self._status_row, 1, 1, 1) self._device_status_layout.addWidget(label_units, self._status_row, 2, 1, 1) label_name.setText(key) min_height = label_name.sizeHint().height() + 5 label_name.setMinimumHeight(min_height) self._device_status_layout.setRowMinimumHeight(self._status_row, min_height) self._status_row += 1 s = [label_name, label_value, label_units] self._status[key] = s fmt = value.get('format', None) v = value['value'] c = '' if fmt is None: v, c, _ = unit_prefix(v) k = three_sig_figs(v) else: k = fmt.format(v) units = str(c + value['units']) s[1].setText(k) s[2].setText(units)
def _on_device_state(self, topic, data): if topic == 'Device/#state/statistics': v = data.get('accumulators', {}).get('energy') v = self._cmdp.convert_units('energy', v) s = three_sig_figs(v['value'], v['units']) self._ui.energyValueLabel.setText(s) elif topic == 'Device/#state/source': if data in 'USB': self._ui.playButton.setEnabled(True) self._ui.iRangeComboBox.setEnabled(True) self._ui.vRangeComboBox.setEnabled(True) elif data == 'Buffer': self._ui.playButton.setEnabled(True) self._ui.recordButton.setEnabled(False) self._ui.iRangeComboBox.setEnabled(False) self._ui.vRangeComboBox.setEnabled(False) else: self._ui.playButton.setChecked(False) self._ui.playButton.setEnabled(False) self._ui.recordButton.setChecked(False) self._ui.recordButton.setEnabled(False) self._ui.iRangeComboBox.setEnabled(False) self._ui.vRangeComboBox.setEnabled(False) elif self._cmdp['Device/#state/source'] in ['USB', 'Buffer']: if topic == 'Device/#state/play': self._ui.playButton.setChecked(data) self._ui.recordButton.setEnabled(data) elif topic == 'Device/#state/record': self._ui.recordButton.setChecked(data)
def accum_update(self): field = self._cmdp['Units/accumulator'] if self._accum_history is None: txt = '0 s' else: time_str = self._accum_history['time_str'] a = self._accum_history['accumulators'] v = a[field] units = self._cmdp.preferences.get('Units/' + field, default=v['units']) v = convert_units(v['value'], v['units'], units) s = three_sig_figs(v['value'], v['units']) txt = ACCUM_TEMPLATE.format(field=field.capitalize(), value=s, time=time_str) self._accumLabel.setText(txt)
def paint(self, p, opt, widget): vb = self._view() color = self._cmdp[self._instance_prefix + 'color'] p.resetTransform() vb_rect = vb.geometry() y = self.scene_pos_y() p1 = pg.Point(vb_rect.left(), y) p2 = pg.Point(vb_rect.right(), y) pen = pg.mkPen(color) pen.setWidth(1) p.setPen(pen) p.drawLine(p1, p2) pen = pg.mkPen([255, 255, 255, 255]) p.setPen(pen) if self.pair is None: txt = three_sig_figs(self._y, self._units) else: dy = abs(self._y - self.pair._y) t1 = three_sig_figs(self._y, self._units) t2 = three_sig_figs(dy, self._units) txt = f'y={t1}, Δ={t2}' p.drawText(p1 - 2, txt)
def run(): statistics_queue = queue.Queue() # resynchronize to main thread def stop_fn(*args, **kwargs): statistics_queue.put(None) # None signals quit signal.signal(signal.SIGINT, stop_fn) # also quit on CTRL-C with scan_require_one(config='auto') as device: device.statistics_callback = statistics_queue.put # put data in queue device.start(stop_fn=stop_fn) print('CTRL-C to exit') while True: data = statistics_queue.get() if data is None: break energy = data['accumulators']['energy'] print(three_sig_figs(energy['value'], units=energy['units']))
def _on_device_statistics(self, topic, statistics): """Update the multimeter display :param statistics: The statistics data structure """ if not statistics: return if self.accumulateButton.isChecked(): self._accumulate_duration += statistics['time']['delta']['value'] else: self._accumulate_duration = statistics['time']['delta']['value'] for name, field in statistics['signals'].items(): if name not in self.values: continue self.values[name].update_value(field) accum_time = statistics['time']['accumulator'] energy = statistics['accumulators']['energy']['value'] charge = statistics['accumulators']['charge']['value'] self.values['energy'].update_energy(accum_time['value'], energy, charge) self.accumulateDurationLabel.setText( three_sig_figs(self._accumulate_duration, 's'))
def _update_delta_time(self): if self.is_left: style = self._time_style() axis = self._axis() if axis is None: return axis_top = axis.geometry().top() vb = axis.linkedView() if vb is None: return x_left = self._x x_right = self._pair._x if x_left is None or x_right is None: self._delta_time_text.setHtml('') return dx = abs(x_right - x_left) x_center = (x_left + x_right) / 2 x_scene = vb.mapViewToScene(pg.Point(x_center, 0.0)).x() dx_str = three_sig_figs(dx, 's') self._delta_time_text.setHtml(f'<div><span style="{style}">Δt={dx_str}</span></div>') self._delta_time_text.setPos(x_scene, axis_top) elif self.is_right: self._pair._update_delta_time()
def run(): statistics_queue = queue.Queue() # resynchronize to main thread def stop_fn(*args, **kwargs): statistics_queue.put(None) # None signals quit signal.signal(signal.SIGINT, stop_fn) # also quit on CTRL-C with scan_require_one(config='off') as device: device.statistics_callback_register(statistics_queue.put, 'sensor') device.parameter_set('i_range', 'auto') device.parameter_set('v_range', '15V') print('CTRL-C to exit') while True: device.status() time.sleep(0.1) try: data = statistics_queue.get(block=False) if data is None: break energy = data['accumulators']['energy'] print(three_sig_figs(energy['value'], units=energy['units'])) except queue.Empty: pass
def test_negative_numbers(self): self.assertEqual('-1.50 A', units.three_sig_figs(-1.5, 'A')) self.assertEqual('-150 mA', units.three_sig_figs(-0.15, 'A')) self.assertEqual('-15.0 mA', units.three_sig_figs(-0.015, 'A')) self.assertEqual('-1.50 mA', units.three_sig_figs(-0.0015, 'A'))
def test_three_sig_figs_boundary(self): self.assertEqual('1.00 A', units.three_sig_figs(1.0, 'A')) self.assertEqual('100 mA', units.three_sig_figs(0.10, 'A')) self.assertEqual('10.0 mA', units.three_sig_figs(0.010, 'A')) self.assertEqual('1.00 mA', units.three_sig_figs(0.0010, 'A')) self.assertEqual('100 µA', units.three_sig_figs(0.00010, 'A'))
def test_three_sig_figs_with_units(self): self.assertEqual('1.50 A', units.three_sig_figs(1.5, 'A')) self.assertEqual('150 mA', units.three_sig_figs(0.15, 'A')) self.assertEqual('15.0 mA', units.three_sig_figs(0.015, 'A')) self.assertEqual('1.50 mA', units.three_sig_figs(0.0015, 'A')) self.assertEqual('150 µA', units.three_sig_figs(0.00015, 'A'))
def test_three_sig_figs_no_units(self): self.assertEqual('1.50', units.three_sig_figs(1.5)) self.assertEqual('150m', units.three_sig_figs(0.15)) self.assertEqual('150µ', units.three_sig_figs(0.00015)) self.assertEqual('1.50k', units.three_sig_figs(1500)) self.assertEqual('1.50M', units.three_sig_figs(1500000))
def update(self, x, data): if not self.widget.isVisible(): return self._x = x self._y = data if x is not None and data is not None: z_mean = data[:, self._column, 0] self._curve_mean.updateData(x, z_mean) if self._show_min_max: self._curve_min.updateData(x, data[:, self._column, 2]) self._curve_max.updateData(x, data[:, self._column, 3]) z_valid = np.isfinite(z_mean) z_mean = z_mean[z_valid] z_var = data[z_valid, self._column, 1] z_min = data[z_valid, self._column, 2] z_max = data[z_valid, self._column, 3] if not len(z_mean): v_mean = np.nan v_std = np.nan v_min = np.nan v_max = np.nan else: v_mean = np.mean(z_mean) v_min = np.min(z_min) if not np.isfinite(v_min): v_min = np.min(z_mean) v_max = np.max(z_max) if not np.isfinite(v_max): v_max = np.max(z_mean) mean_delta = z_mean - v_mean # combine variances across the combined samples v_std = np.sqrt(np.sum(np.square(mean_delta, out=mean_delta) + z_var) / len(z_mean)) if self.ui.zoomAutoYButton.isChecked(): _, (vb_min, vb_max) = self.vb.viewRange() vb_range = vb_max - vb_min v_range = v_max - v_min update_range = (v_max > vb_max) or (v_min < vb_min) if vb_range > 0: update_range |= (v_range / vb_range) < AUTO_RANGE_FRACT if update_range: self.vb.setYRange(v_min, v_max) self.ui.meanValue.setText(three_sig_figs(v_mean, self._units)) self.ui.stdValue.setText(three_sig_figs(v_std, self._units)) self.ui.p2pValue.setText(three_sig_figs(v_max - v_min, self._units)) self.ui.minValue.setText(three_sig_figs(v_min, self._units)) self.ui.maxValue.setText(three_sig_figs(v_max, self._units)) self.vb.setXRange(x[0], x[-1], padding=0.0) self.plot.update() self.on_marker() return self._curve_min.clear() self._curve_min.update() self._curve_max.clear() self._curve_max.update() self._curve_mean.clear() self._curve_mean.update() self.ui.meanValue.setText('') self.ui.stdValue.setText('') self.ui.p2pValue.setText('') self.ui.minValue.setText('') self.ui.maxValue.setText('') self.plot.update()