Exemple #1
0
def write(infofile, version, serial_number, calibfile, no_calib):
    if infofile is not None:
        if serial_number is not None or version is not None:
            raise click.UsageError(("--infofile and --version/--serial_number"
                                    " are mutually exclusive"))
        cape_data = CapeData.from_yaml(infofile)
        with EEPROM() as eeprom:
            eeprom.write_cape_data(cape_data)
    elif serial_number is not None or version is not None:
        if version is None or serial_number is None:
            raise click.UsageError(
                ("--version and --serial_number are required"))
        cape_data = CapeData.from_values(serial_number, version)
        with EEPROM() as eeprom:
            eeprom.write_cape_data(cape_data)

    if calibfile is not None:
        if no_calib:
            raise click.UsageError(
                "--no-calib and --calibfile are mutually exclusive")
        calib = CalibrationData.from_yaml(calibfile)
        with EEPROM() as eeprom:
            cape_data = eeprom.write_calibration(calib)
    if no_calib:
        calib = CalibrationData.from_default()

        with EEPROM() as eeprom:
            eeprom.write_calibration(calib)
Exemple #2
0
    def __init__(
            self,
            shepherd_mode: str = "emulation",
            initial_buffers: list = None,
            calibration_recording:
        CalibrationData = None,  # TODO: make clearer that these is "THE RECORDING"
            calibration_emulation: CalibrationData = None,
            set_target_io_lvl_conv: bool = False,
            sel_target_for_io: bool = True,
            sel_target_for_pwr: bool = True,
            aux_target_voltage: float = 0.0,
            settings_virtsource: VirtualSourceData = None):

        logger.debug(f"Emulator-Init in {shepherd_mode}-mode")
        super().__init__(shepherd_mode)
        self._initial_buffers = initial_buffers

        if calibration_emulation is None:
            calibration_emulation = CalibrationData.from_default()
            logger.warning(
                "No emulation calibration data provided - using defaults")
        if calibration_recording is None:
            calibration_recording = CalibrationData.from_default()
            logger.warning(
                "No recording calibration data provided - using defaults")

        self._cal_recording = calibration_recording
        self._cal_emulation = calibration_emulation
        self._settings_virtsource = settings_virtsource

        self._set_target_io_lvl_conv = set_target_io_lvl_conv
        self._sel_target_for_io = sel_target_for_io
        self._sel_target_for_pwr = sel_target_for_pwr
        self._aux_target_voltage = aux_target_voltage
Exemple #3
0
    def get_calibration_data(self) -> CalibrationData:
        """Reads calibration data from hdf5 file.

        Returns:
            Calibration data as CalibrationData object
        """
        nested_dict = lambda: defaultdict(nested_dict)
        calib = CalibrationData.from_default()
        for channel, parameter in product(["current", "voltage"],
                                          cal_parameter_list):
            cal_channel = cal_channel_harvest_dict[channel]
            calib._data["harvesting"][cal_channel][parameter] = self._h5file[
                "data"][channel].attrs[parameter]
        return CalibrationData(calib)
Exemple #4
0
    def read_calibration(self) -> CalibrationData:
        """Reads and returns shepherd calibration data from EEPROM

        Returns:
            CalibrationData object containing data extracted from EEPROM
        """
        data = self._read(
            calibration_data_format["offset"], calibration_data_format["size"]
        )
        try:
            cal = CalibrationData.from_bytestr(data)
        except struct.error:
            cal = CalibrationData.from_default()
            logger.warning("EEPROM seems to have no usable data - will set calibration from default-values")
        return cal
Exemple #5
0
def make(filename, output_path):
    cd = CalibrationData.from_measurements(filename)
    if output_path is None:
        print(repr(cd))
    else:
        with open(output_path, "w") as f:
            f.write(repr(cd))
Exemple #6
0
def write_dac_aux_voltage(calibration_settings: CalibrationData, voltage_V: float) -> NoReturn:
    """ Sends the auxiliary voltage (dac channel B) to the PRU core.

    Args:
        voltage_V: desired voltage in volt
    """
    if voltage_V is None:
        voltage_V = 0.0
    elif voltage_V is False:
        voltage_V = 0.0
    elif voltage_V is True:
        # set value > 16bit and therefore link both adc-channels
        write_dac_aux_voltage_raw(2 ** 20 - 1)
        return

    if voltage_V < 0.0:
        raise SysfsInterfaceException(f"sending voltage with negative value: {voltage_V}")
    if voltage_V > 5.0:
        raise SysfsInterfaceException(f"sending voltage above limit of 5V: {voltage_V}")

    if calibration_settings is None:
        output = calibration_default.dac_ch_b_voltage_to_raw(voltage_V)
    else:
        output = calibration_settings.convert_value_to_raw("emulation", "dac_voltage_b", voltage_V)

    # TODO: currently only an assumption that it is for emulation, could also be for harvesting
    # TODO: fn would be smoother if it contained the offset/gain-dict of the cal-data. but this requires a general FN for conversion
    write_dac_aux_voltage_raw(output)
Exemple #7
0
    def write_calibration(self, calibration_data: CalibrationData) -> NoReturn:
        """Writes complete BeagleBone cape data to EEPROM

        Args:
            calibration_data (CalibrationData): Calibration data that is going
                to be stored in EEPROM
        """
        self._write(calibration_data_format["offset"],
                    calibration_data.to_bytestr())
Exemple #8
0
    def send_calibration_settings(cal_settings: CalibrationData) -> NoReturn:
        """Sends calibration settings to PRU core

        For the virtual source it is required to have the calibration settings.

        Args:
            cal_settings (CalibrationData): Contains the device's
            calibration settings.
        """
        sysfs_interface.write_calibration_settings(cal_settings.export_for_sysfs())
Exemple #9
0
    def __init__(
        self,
        initial_buffers: list = None,
        calibration_recording: CalibrationData = None,
        calibration_emulation: CalibrationData = None,
        load: str = "node",
        ldo_voltage: float = 0.0
    ):

        shepherd_mode = "emulation"
        self.ldo_voltage = ldo_voltage
        super().__init__(shepherd_mode, load)

        if calibration_emulation is None:
            calibration_emulation = CalibrationData.from_default()
            logger.warning(
                "No emulation calibration data provided - using defaults"
            )
        if calibration_recording is None:
            calibration_recording = CalibrationData.from_default()
            logger.warning(
                "No recording calibration data provided - using defaults"
            )

        self.transform_coeffs = {"voltage": dict(), "current": dict()}

        # Values from recording are binary ADC values. We have to send binary
        # DAC values to the DAC for emulation. To directly convert ADC to DAC
        # values, we precalculate the 'transformation coefficients' based on
        # calibration data from the recorder and the emulator.
        for channel in ["voltage", "current"]:
            self.transform_coeffs[channel]["gain"] = (
                calibration_recording["harvesting"][channel]["gain"]
                * calibration_emulation["emulation"][channel]["gain"]
            )
            self.transform_coeffs[channel]["offset"] = (
                calibration_emulation["emulation"][channel]["gain"]
                * calibration_recording["harvesting"][channel]["offset"]
                + calibration_emulation["emulation"][channel]["offset"]
            )

        self._initial_buffers = initial_buffers
        self._calibration_emulation = calibration_emulation
Exemple #10
0
def calibration_settings():
    calibration_emulation = CalibrationData.from_default()
    current_gain = int(1 / calibration_emulation["load"]["current"]["gain"])
    current_offset = int(
        calibration_emulation["load"]["current"]["offset"]
        / calibration_emulation["load"]["current"]["gain"]
    )
    voltage_gain = int(1 / calibration_emulation["load"]["voltage"]["gain"])
    voltage_offset = int(
        calibration_emulation["load"]["voltage"]["offset"]
        / calibration_emulation["load"]["voltage"]["gain"]
    )
    return current_gain, current_offset, voltage_gain, voltage_offset
Exemple #11
0
    def get_calibration_data(self):
        """Reads calibration data from hdf5 file.

        Returns:
            Calibration data as CalibrationData object
        """
        nested_dict = lambda: defaultdict(nested_dict)
        calib = nested_dict()
        for var, attr in product(["voltage", "current"], ["gain", "offset"]):

            calib["harvesting"][var][attr] = self._h5file["data"][var].attrs[
                attr]
        return CalibrationData(calib)
Exemple #12
0
def aux_target_power(on: bool, voltage: float, sel_target_for_aux: bool):
    if not voltage:
        voltage = 3.0
    else:
        if not on:
            raise click.UsageError(
                "Can't set voltage, when Shepherd is switched off")
    for pin_name in ["en_shepherd"]:
        pin = GPIO(gpio_pin_nums[pin_name], "out")
        pin.write(on)
    for pin_name in ["target_pwr_sel"]:
        pin = GPIO(gpio_pin_nums[pin_name], "out")
        pin.write(not sel_target_for_aux)
    cal = CalibrationData.from_default()
    sysfs_interface.write_dac_aux_voltage(cal, voltage)
Exemple #13
0
def read_dac_aux_voltage(cal_settings: CalibrationData) -> float:
    """ Reads the auxiliary voltage (dac channel B) from the PRU core.

    Args:
        cal_settings: dict with offset/gain

    Returns:
        aux voltage
    """
    value_raw = read_dac_aux_voltage_raw()
    if cal_settings is None:
        voltage = calibration_default.dac_ch_a_raw_to_voltage(value_raw)
    else:
        voltage = cal_settings.convert_raw_to_value("emulation", "dac_voltage_b", value_raw)
    return voltage
Exemple #14
0
 def __init__(self):
     super().__init__("debug")
     self._cal = CalibrationData.from_default()
     self._io = TargetIO()
Exemple #15
0
def test_dac_aux_voltage(shepherd_up, value):
    cal_set = CalibrationData.from_default()
    msb_threshold = cal_set.convert_raw_to_value("emulation", "dac_voltage_b", 2)
    sysfs_interface.write_dac_aux_voltage(cal_set, value)
    assert abs(sysfs_interface.read_dac_aux_voltage(cal_set) - value) <= msb_threshold
Exemple #16
0
def calibration_settings():
    calibration_emulation = CalibrationData.from_default()
    return calibration_emulation.export_for_sysfs()
Exemple #17
0
def emulate(
    input_path: Path,
    output_path: Path = None,
    duration: float = None,
    force_overwrite: bool = False,
    no_calib: bool = False,
    load: str = "artificial",
    ldo_voltage: float = None,
    start_time: float = None,
    warn_only: bool = False,
):
    """ Starts emulation.

    Args:
        input_path (Path): path of hdf5 file containing recorded
            harvesting data
        output_path (Path): Path of hdf5 file where load measurements should
            be stored
        duration (float): Maximum time duration of emulation in seconds
        force_overwrite (bool): True to overwrite existing file under output,
            False to store under different name
        no_calib (bool): True to use default calibration values, False to
            read calibration data from EEPROM
        load (str): Type of load. 'artificial' for dummy, 'node' for sensor
            node
        ldo_voltage (float): Pre-charge capacitor to this voltage before
            starting emulation
        start_time (float): Desired start time of emulation in unix epoch time
        warn_only (bool): Set true to continue emulation after recoverable
            error
    """

    if no_calib:
        calib = CalibrationData.from_default()
    else:
        try:
            with EEPROM() as eeprom:
                calib = eeprom.read_calibration()
        except ValueError:
            logger.warning("Couldn't read calibration from EEPROM (val). Falling back to default values.")
            calib = CalibrationData.from_default()
        except FileNotFoundError:
            logger.warning("Couldn't read calibration from EEPROM (FS). Falling back to default values.")
            calib = CalibrationData.from_default()

    if start_time is None:
        start_time = time.time() + 15

    if output_path is not None:
        if not output_path.is_absolute():
            output_path = output_path.absolute()
        if output_path.is_dir():
            timestamp = datetime.datetime.fromtimestamp(start_time)
            timestamp = timestamp.strftime("%Y-%m-%d_%H-%M-%S")  # closest to ISO 8601, avoid ":"
            store_path = output_path / f"emu_{timestamp}.h5"
        else:
            store_path = output_path

        log_writer = LogWriter(
            store_path=store_path,
            force_overwrite=force_overwrite,
            mode="load",
            calibration_data=calib,
        )

    if isinstance(input_path, str):
        input_path = Path(input_path)
    if input_path is None:
        raise ValueError("No Input-File configured for emulation")
    if not input_path.exists():
        raise ValueError("Input-File does not exist")

    log_reader = LogReader(input_path, 10_000)

    with ExitStack() as stack:
        if output_path is not None:
            stack.enter_context(log_writer)

        stack.enter_context(log_reader)

        emu = Emulator(
            calibration_recording=log_reader.get_calibration_data(),
            calibration_emulation=calib,
            initial_buffers=log_reader.read_buffers(end=64),
            ldo_voltage=ldo_voltage,
            load=load,
        )
        stack.enter_context(emu)

        emu.start(start_time, wait_blocking=False)

        logger.info(f"waiting {start_time - time.time():.2f} s until start")
        emu.wait_for_start(start_time - time.time() + 15)

        logger.info("shepherd started!")

        def exit_gracefully(signum, frame):
            stack.close()
            sys.exit(0)

        signal.signal(signal.SIGTERM, exit_gracefully)
        signal.signal(signal.SIGINT, exit_gracefully)

        if duration is None:
            ts_end = sys.float_info.max
        else:
            ts_end = time.time() + duration

        for hrvst_buf in log_reader.read_buffers(start=64):
            try:
                idx, emu_buf = emu.get_buffer(timeout=1)
            except ShepherdIOException as e:
                logger.error(
                    f"ShepherdIOException(ID={e.id}, val={e.value}): {str(e)}"
                )
                if output_path is not None:
                    err_rec = ExceptionRecord(
                        int(time.time() * 1e9), str(e), e.value
                    )
                    log_writer.write_exception(err_rec)

                if not warn_only:
                    raise

            if output_path is not None:
                log_writer.write_buffer(emu_buf)

            emu.return_buffer(idx, hrvst_buf)

            if time.time() > ts_end:
                break

        # Read all remaining buffers from PRU
        while True:
            try:
                idx, emu_buf = emu.get_buffer(timeout=1)
                if output_path is not None:
                    log_writer.write_buffer(emu_buf)
            except ShepherdIOException as e:
                # We're done when the PRU has processed all emulation data buffers
                if e.id == commons.MSG_DEP_ERR_NOFREEBUF:
                    break
                else:
                    if not warn_only:
                        raise
Exemple #18
0
def record(
    output_path: Path,
    mode: str = "harvesting",
    duration: float = None,
    force_overwrite: bool = False,
    no_calib: bool = False,
    start_time: float = None,
    warn_only: bool = False,
):
    """Starts recording.

    Args:
        output_path (Path): Path of hdf5 file where IV measurements should be
            stored
        mode (str): 'harvesting' for recording harvesting data
        duration (float): Maximum time duration of emulation in seconds
        force_overwrite (bool): True to overwrite existing file under output path,
            False to store under different name
        no_calib (bool): True to use default calibration values, False to
            read calibration data from EEPROM
        start_time (float): Desired start time of emulation in unix epoch time
        warn_only (bool): Set true to continue recording after recoverable
            error
    """
    if no_calib:
        calib = CalibrationData.from_default()
    else:
        try:
            with EEPROM() as eeprom:
                calib = eeprom.read_calibration()
        except ValueError:
            logger.warning(
                "Couldn't read calibration from EEPROM (Val). Falling back to default values."
            )
            calib = CalibrationData.from_default()
        except FileNotFoundError:
            logger.warning(
                "Couldn't read calibration from EEPROM (FS). Falling back to default values."
            )
            calib = CalibrationData.from_default()

    if start_time is None:
        start_time = round(time.time() + 10)

    if not output_path.is_absolute():
        output_path = output_path.absolute()
    if output_path.is_dir():
        timestamp = datetime.datetime.fromtimestamp(start_time)
        timestring = timestamp.strftime(
            "%Y-%m-%d_%H-%M-%S")  # closest to ISO 8601, avoid ":"
        store_path = output_path / f"rec_{timestring}.h5"
    else:
        store_path = output_path

    recorder = Recorder(shepherd_mode=mode)
    log_writer = LogWriter(store_path=store_path,
                           calibration_data=calib,
                           mode=mode,
                           force_overwrite=force_overwrite)
    with ExitStack() as stack:

        stack.enter_context(recorder)
        stack.enter_context(log_writer)

        # in_stream has to be disabled to avoid trouble with pytest
        res = invoke.run("hostname", hide=True, warn=True, in_stream=False)
        log_writer["hostname"] = res.stdout

        recorder.start(start_time, wait_blocking=False)

        logger.info(f"waiting {start_time - time.time():.2f} s until start")
        recorder.wait_for_start(start_time - time.time() + 15)

        logger.info("shepherd started!")

        def exit_gracefully(signum, frame):
            stack.close()
            sys.exit(0)

        signal.signal(signal.SIGTERM, exit_gracefully)
        signal.signal(signal.SIGINT, exit_gracefully)

        if duration is None:
            ts_end = sys.float_info.max
        else:
            ts_end = time.time() + duration

        while time.time() < ts_end:
            try:
                idx, buf = recorder.get_buffer()
            except ShepherdIOException as e:
                logger.error(
                    f"ShepherdIOException(ID={e.id}, val={e.value}): {str(e)}")
                err_rec = ExceptionRecord(int(time.time() * 1e9), str(e),
                                          e.value)
                log_writer.write_exception(err_rec)
                if not warn_only:
                    raise

            log_writer.write_buffer(buf)
            recorder.return_buffer(idx)
Exemple #19
0
def emulate(
    input_path: Path,
    output_path: Path = None,
    duration: float = None,
    force_overwrite: bool = False,
    no_calib: bool = False,
    start_time: float = None,
    set_target_io_lvl_conv: bool = False,
    sel_target_for_io: bool = True,
    sel_target_for_pwr: bool = True,
    aux_target_voltage: float = 0.0,
    settings_virtsource: VirtualSourceData = None,
    warn_only: bool = False,
):
    """ Starts emulation.

    Args:
        input_path (Path): path of hdf5 file containing recorded harvesting data
        output_path (Path): Path of hdf5 file where power measurements should
            be stored
        duration (float): Maximum time duration of emulation in seconds
        force_overwrite (bool): True to overwrite existing file under output,
            False to store under different name
        no_calib (bool): True to use default calibration values, False to
            read calibration data from EEPROM
        start_time (float): Desired start time of emulation in unix epoch time
        set_target_io_lvl_conv: Enables or disables the GPIO level converter to targets.
        sel_target_for_io: choose which targets gets the io-connection (serial, swd, gpio) from beaglebone, True = Target A, False = Target B
        sel_target_for_pwr: choose which targets gets the supply with current-monitor, True = Target A, False = Target B
        aux_target_voltage: Sets, Enables or disables the voltage for the second target, 0.0 or False for Disable, True for linking it to voltage of other Target
        settings_virtsource (VirtualSourceData): Settings which define the behavior of virtsource emulation
        warn_only (bool): Set true to continue emulation after recoverable
            error
    """

    if no_calib:
        calib = CalibrationData.from_default()
    else:
        try:
            with EEPROM() as eeprom:
                calib = eeprom.read_calibration()
        except ValueError:
            logger.warning(
                "Couldn't read calibration from EEPROM (Val). Falling back to default values."
            )
            calib = CalibrationData.from_default()
        except FileNotFoundError:
            logger.warning(
                "Couldn't read calibration from EEPROM (FS). Falling back to default values."
            )
            calib = CalibrationData.from_default()

    if start_time is None:
        start_time = round(time.time() + 10)

    if set_target_io_lvl_conv is None:
        set_target_io_lvl_conv = True

    if sel_target_for_io is None:
        sel_target_for_io = True

    if sel_target_for_pwr is None:
        sel_target_for_pwr = True

    if aux_target_voltage is None:
        aux_target_voltage = 0.0

    if output_path is not None:
        if not output_path.is_absolute():
            output_path = output_path.absolute()
        if output_path.is_dir():
            timestamp = datetime.datetime.fromtimestamp(start_time)
            timestring = timestamp.strftime(
                "%Y-%m-%d_%H-%M-%S")  # closest to ISO 8601, avoid ":"
            store_path = output_path / f"emu_{timestring}.h5"
        else:
            store_path = output_path

        log_writer = LogWriter(
            store_path=store_path,
            force_overwrite=force_overwrite,
            mode="emulation",
            calibration_data=calib,
        )

    if isinstance(input_path, str):
        input_path = Path(input_path)
    if input_path is None:
        raise ValueError("No Input-File configured for emulation")
    if not input_path.exists():
        raise ValueError("Input-File does not exist")

    log_reader = LogReader(input_path, 10_000)

    with ExitStack() as stack:
        if output_path is not None:
            stack.enter_context(log_writer)

        stack.enter_context(log_reader)

        emu = Emulator(
            shepherd_mode="emulation",
            initial_buffers=log_reader.read_buffers(end=64),
            calibration_recording=log_reader.get_calibration_data(),
            calibration_emulation=calib,
            set_target_io_lvl_conv=set_target_io_lvl_conv,
            sel_target_for_io=sel_target_for_io,
            sel_target_for_pwr=sel_target_for_pwr,
            aux_target_voltage=aux_target_voltage,
            settings_virtsource=settings_virtsource,
        )
        stack.enter_context(emu)

        emu.start(start_time, wait_blocking=False)

        logger.info(f"waiting {start_time - time.time():.2f} s until start")
        emu.wait_for_start(start_time - time.time() + 15)

        logger.info("shepherd started!")

        def exit_gracefully(signum, frame):
            stack.close()
            sys.exit(0)

        signal.signal(signal.SIGTERM, exit_gracefully)
        signal.signal(signal.SIGINT, exit_gracefully)

        if duration is None:
            ts_end = sys.float_info.max
        else:
            ts_end = time.time() + duration

        for hrvst_buf in log_reader.read_buffers(start=64):
            try:
                idx, emu_buf = emu.get_buffer(timeout=1)
            except ShepherdIOException as e:
                logger.error(
                    f"ShepherdIOException(ID={e.id}, val={e.value}): {str(e)}")

                err_rec = ExceptionRecord(int(time.time() * 1e9), str(e),
                                          e.value)
                if output_path is not None:
                    log_writer.write_exception(err_rec)
                if not warn_only:
                    raise

            if output_path is not None:
                log_writer.write_buffer(emu_buf)

            emu.return_buffer(idx, hrvst_buf)

            if time.time() > ts_end:
                break

        # Read all remaining buffers from PRU
        while True:
            try:
                idx, emu_buf = emu.get_buffer(timeout=1)
                if output_path is not None:
                    log_writer.write_buffer(emu_buf)
            except ShepherdIOException as e:
                # We're done when the PRU has processed all emulation data buffers
                if e.id == commons.MSG_DEP_ERR_NOFREEBUF:
                    break
                else:
                    if not warn_only:
                        raise