Source code for device.base.inoprobebase

import time
import pathlib
import numpy as np

from . import base, arduinobase


# TODO Check units of V, I returned by Arduino - graph, file header. Also check
#      the order of columns (voltage, amps, time).

# TODO Arduino code writes 2x beyond the size of data arrays POCET_MERENI.
#      Probably should fix the conditions, lines 146, 174.


[docs] class ArduinoProbeBase(arduinobase.ArduinoBase): """Arduino controller that handles sweeped measurments of probe I-V characteristic. Communication: - serial interface to Arduino """ PROBE_SAMPLES = 300 # Hardcoded in Arduino code, limited size of SRAM DIR_SAVED_DATA = pathlib.Path.home() / "saved_data" SWEEP_CMD = "a" def __init__(self, bus, start_msg="START", **kwargs): """Constructor :param str bus: Name of serial bus where Arduino is connected, e.g. '/dev/ttyUSB0' """ super().__init__( serial_cfg=arduinobase.SerialConfig(bus, 115200, start_msg), **kwargs, ) # ==== Commands ====
[docs] @base.base_command def measure_sweep(self, device_client, frequency, amplitude): """Execute a sweeped measurement of the I-V characteristic. Returns numpy array, shape (3, 300), columns [volts, amps, time]. :param float frequency: Approximate frequency of the waveform in kHz. :param float amplitude: Approximate amplitude of wavefrom 10-100%. """ frequency, amplitude = float(frequency) * 1e3, float(amplitude) if not (1e3 <= frequency <= 100e3): raise base.CommandError("Value frequency must be between 1-100 kHz") if not (10.0 <= amplitude <= 100.0): raise base.CommandError("Value amplitude must be between 10-100%") # Set initial values. Then we try to optimize them to match the # requested frequency & amplitude as best as possible. byte_amp = round(amplitude / 100 * 255) byte_step = 1 byte_delay = 1 # 1 microsecond # High frequency requested -> Increase byte_step up to a reasonable max. while byte_step < (byte_amp // 5): if byte_delay * (byte_amp // byte_step) <= (0.5 / frequency): break byte_step += 1 # Match amplitude to step size byte_amp -= byte_amp % byte_step # Low frequency requested -> Increase delay up to max. while byte_delay < 255: if (byte_delay + 1) * (byte_amp // byte_step) >= (0.5 / frequency): break byte_delay += 1 t0 = time.time() self._write(f"{self.SWEEP_CMD} {byte_delay:d} {byte_amp:d} {byte_step:d}") time.sleep(self.PROBE_SAMPLES * byte_delay * 1e-6) timeout = self._ardu.timeout self._ardu.timeout = 5 text = "" for _ in range(self.PROBE_SAMPLES): recv = self._readline() if recv == "": break text += recv + " " self._ardu.timeout = timeout self._last.probe_data = np.fromstring( text, dtype=float, sep=" ").reshape(-1, 3).T[[2, 0, 1]] def save_data(data, header=""): filepath = "trap_probe_{}_{{}}.txt".format(time.strftime("%Y-%m-%d_%H-%M")) suffix = 1 while (self.DIR_SAVED_DATA / filepath.format(suffix)).exists(): suffix += 1 filepath = self.DIR_SAVED_DATA / filepath.format(suffix) np.savetxt(str(filepath), data.T, fmt="%.4f", header=header) return filepath filepath = save_data( self._last.probe_data, header=f"timestamp {t0:.4f}\ntime[s], volts[V?], amps[A?]", ) if device_client is not None: # Emit data to server with filepath.open("r") as file: device_client.emit_datafile(file, filepath.name) # Emit graph preview of I-V characteristic device_client.emit("graph", { "title": "I-V characteristic", "x": self._last.probe_data[1].tolist(), "y": self._last.probe_data[2].tolist(), "xlabel": "voltage [V?]", "ylabel": "current [A?]", "id": "graph_iv_char", }) return str(filepath)