Exemplo n.º 1
0
def measure_sampling_rate(device: Hantek1008, used_sampling_rate: int,
                          measurment_duration: float) -> float:
    required_samples = max(
        4, int(math.ceil(measurment_duration * used_sampling_rate)))
    counter = -1
    start_time = 0
    for data in device.request_samples_roll_mode(
            sampling_rate=used_sampling_rate):
        if counter == -1:  # skip first samples to ignore the duration of initialisation
            start_time = time.perf_counter()
            counter = 0
        counter += len(data[0])
        if counter >= required_samples:
            break

    duration = time.perf_counter() - start_time
    return counter / duration
Exemplo n.º 2
0
def connect(
    ns_per_div: int,
    vertical_scale_factor: Union[float, List[float]],
    selected_channels: List[int],
    correction_data: Optional[CorrectionDataType] = None,
    zero_offset_shift_compensation_channel: Optional[int] = None,
    zero_offset_shift_compensation_function: Optional[
        ZeroOffsetShiftCompensationFunctionType] = None,
    zero_offset_shift_compensation_function_time_offset_sec: int = 0
) -> Hantek1008:
    device = Hantek1008(
        ns_per_div=ns_per_div,
        vertical_scale_factor=vertical_scale_factor,
        active_channels=selected_channels,
        correction_data=correction_data,
        zero_offset_shift_compensation_channel=
        zero_offset_shift_compensation_channel,
        zero_offset_shift_compensation_function=
        zero_offset_shift_compensation_function,
        zero_offset_shift_compensation_function_time_offset_sec=
        zero_offset_shift_compensation_function_time_offset_sec)

    try:
        log.info("Connecting...")
        try:
            device.connect()
        except RuntimeError as e:
            log.error(str(e))
            sys.exit(1)
        log.info("Connection established")

        log.info("Initialising...")
        try:
            device.init()
        except RuntimeError as e:
            log.error(str(e))
            sys.exit(1)
        log.info("Initialisation completed")
    except KeyboardInterrupt:
        device.close()
        sys.exit(0)

    return device
Exemplo n.º 3
0
def calibration_routine(device: Hantek1008, calibrate_file_path: str,
                        channels_at_once: int):
    assert channels_at_once in [1, 2, 4, 8]

    print(
        "This interactive routine will generate a calibration that can later be used "
        "to get more precise results. It works by connecting different well known "
        "voltages one after another to a channel. Once all calibration voltages are "
        "measured, the same is done for every other channel.")

    import json
    required_calibration_samples_nun = 512
    calibration_data = {}  # dictionary of lists
    device.pause()

    test_voltages = None
    while test_voltages is None:
        try:
            in_str = input("Calibration voltages (x, y, z, ...): ")
            test_voltages = [float(v) for v in in_str.split(',')]
            if len(test_voltages) < 1:
                print("Input must contain at least one voltage")
        except ValueError:
            print("Input must be comma separated floats")

    print(
        f"Calibration voltages are: {' '.join([ f'{v}V' for v in test_voltages])}"
    )

    for channel_id in range(8):
        calibration_data[channel_id] = []

    for channel_id in range(0, 8, channels_at_once):

        for test_voltage in test_voltages:
            cmd = input(
                f"Do {test_voltage}V measurement on channel {channel_id+1}"
                f"{(' to ' + str(channel_id+channels_at_once)) if channels_at_once>1 else ''} (Enter),"
                f" skip voltage (s), skip channel (ss) or quit (q): ")
            if cmd == 'q':
                return
            elif cmd == 'ss':
                break
            elif cmd == 's':
                continue

            device.cancel_pause()

            print(
                f"Measure {required_calibration_samples_nun} values for {test_voltage}V..."
            )
            data = []
            for _, row in zip(
                    range(required_calibration_samples_nun),
                    device.request_samples_roll_mode_single_row(mode="raw")):
                data.append(row)
                pass

            device.pause()

            channel_data = list(zip(*data))

            for calibrated_channel_id in range(channel_id,
                                               channel_id + channels_at_once):
                cd = channel_data[calibrated_channel_id]
                avg = sum(cd) / len(cd)

                calibration_data[calibrated_channel_id].append({
                    "test_voltage":
                    test_voltage,
                    "measured_value":
                    round(avg, 2),
                    "vscale":
                    device.get_vscales()[calibrated_channel_id],
                    "zero_offset":
                    round(
                        device.get_zero_offset(
                            channel_id=calibrated_channel_id), 2)
                })

    with open(calibrate_file_path, 'w') as calibration_file:
        calibration_file.write(json.dumps(calibration_data))
Exemplo n.º 4
0
def sample(device: Hantek1008,
           raw_or_volt: bool,
           selected_channels: List[int],
           sampling_rate: float,
           vertical_scale_factor: float,
           csv_file_path: str,
           measured_sampling_rate: float = None):
    log.info(
        f"Processing data of channel{'' if len(selected_channels) == 1 else 's'}:"
        f" {' '.join([str(i+1) for i in selected_channels])}")

    if raw_or_volt == "volt+raw":  # add the coresponding raw values to the selected channel list
        selected_channels += [sc + 8 for sc in selected_channels]

    try:
        # output_csv_filename = "channel_data.csv"
        if csv_file_path == '-':
            log.info("Exporting data to stdout...")
            csv_file = sys.stdout
        elif csv_file_path.endswith(".xz"):
            log.info(
                f"Exporting data lzma-compressed to file '{csv_file_path}'...")
            csv_file = lzma.open(csv_file_path, 'at', newline='')
        else:
            log.info(f"Exporting data to file '{csv_file_path}'...")
            csv_file = open(csv_file_path, 'at', newline='')
        #csv_writer = csv.writer(csv_file, delimiter=',')
        csv_writer = ThreadedCsvWriter(csv_file, delimiter=',')
        # channel >= 8 are the raw values of the corresponding channels < 8
        channel_titles = [
            f'ch_{i+1 if i < 8 else (str(i+1-8)+"_raw")}'
            for i in selected_channels
        ]
        csv_writer.write_comment(f"{', '.join(channel_titles)}")
        csv_writer.write_comment(f"samplingrate: {sampling_rate} Hz")
        if measured_sampling_rate:
            csv_writer.write_comment(
                f"measured samplingrate: {measured_sampling_rate} Hz")
        now = datetime.datetime.now()
        csv_writer.write_comment(f"UNIX-Time: {now.timestamp()}")
        csv_writer.write_comment(f"UNIX-Time: {now.isoformat()}")
        csv_writer.write_comment(
            f"vscale: {', '.join(str(f) for f in vertical_scale_factor)}")
        csv_writer.write_comment("# zero offset data:")
        for vscale, zero_offset in sorted(device.get_zero_offsets().items()):
            csv_writer.write_comment(
                f"zero_offset [{vscale:<4}]: {' '.join([str(round(v, 1)) for v in zero_offset])}"
            )

        # TODO: make these configurable
        roll_mode = True
        milli_volt_int_representation = False

        if roll_mode:
            for channel_data in device.request_samples_roll_mode(
                    mode=raw_or_volt, sampling_rate=sampling_rate):
                channel_data = [channel_data[ch] for ch in selected_channels]
                if milli_volt_int_representation:
                    channel_data = [[
                        f"{round(value*1000)}" for value in single_channel
                    ] for single_channel in channel_data]
                csv_writer.write_rows(zip(*channel_data))
                csv_writer.write_comment(
                    f"UNIX-Time: {datetime.datetime.now().timestamp()}")
        else:
            while True:
                channel_data2, channel_data3 = device.request_samples_burst_mode(
                )

                print(len(channel_data2[0]), len(channel_data3[0]))
                # channel_data = [cd2 + cd3 for cd2, cd3 in zip(channel_data2, channel_data3)]
                # channel_data = [cd[70:] + cd[:70] for cd in channel_data]

                csv_writer.write_rows(zip(*channel_data2))
                csv_writer.write_rows(zip(*channel_data3))
                csv_writer.write_comment(
                    f"UNIX-Time: { datetime.datetime.now().timestamp()}")
    except KeyboardInterrupt:
        log.info("Sample collection was stopped by user")
        pass

    if csv_writer:
        csv_writer.close()
Exemplo n.º 5
0
 parser.add_argument('-l',
                     '--loglevel',
                     dest='log_level',
                     nargs='?',
                     type=str,
                     default="info",
                     choices=str_to_log_level.keys(),
                     help='Set the loglevel to debug')
 parser.add_argument(
     '-v',
     '--vscale',
     metavar='scale',
     nargs="+",
     type=float,
     default=[1.0],
     choices=Hantek1008.valid_vscale_factors(),
     help=
     'Set the pre scale in the hardware, must be 1, 0.125, or 0.02. If one value is given, all '
     'selected channels will use that vscale, otherwise there must be one value per selected'
     'channel')
 parser.add_argument(
     '-c',
     '--calibrationfile',
     dest="calibration_file_path",
     metavar='calibrationfile_path',
     type=str,
     default=None,
     help=
     "Use the content of the given calibration file to correct the measured samples"
 )
 parser.add_argument(
Exemplo n.º 6
0
def sample(device: Hantek1008,
           raw_or_volt: RawVoltMode,
           selected_channels: List[int],
           sampling_mode: SamplingMode,
           sampling_rate: float,
           vertical_scale_factor: List[float],
           csv_file_path: str,
           timestamp_style: TimestampStyle,
           measured_sampling_rate: Optional[float] = None) -> None:
    log.info(
        f"Processing data of channel{'' if len(selected_channels) == 1 else 's'}:"
        f" {' '.join([str(i+1) for i in selected_channels])}")

    computed_actual_sampling_rate = Hantek1008.actual_sampling_rate_factor(
        len(selected_channels)) * sampling_rate
    if len(selected_channels) != Hantek1008.channel_count():
        log.warning(
            f"When not using all 8 channels, the actual sampling rate ({computed_actual_sampling_rate:.2f}) is "
            f"higher than the given sampling_rate ({sampling_rate})! "
            f"Best is to use the --measuresamplingrate flag.")

    if raw_or_volt == RawVoltMode.VOLT_AND_RAW:  # add the coresponding raw values to the selected channel list
        selected_channels += [
            sc + Hantek1008.channel_count() for sc in selected_channels
        ]

    try:
        # csv_file:  IO[str] = None
        # output_csv_filename = "channel_data.csv"
        if csv_file_path == '-':
            log.info("Exporting data to stdout...")
            csv_file: IO[str] = sys.stdout
        elif csv_file_path.endswith(".xz"):
            log.info(
                f"Exporting data lzma-compressed to file '{csv_file_path}'...")
            csv_file = lzma.open(csv_file_path, 'at', newline='')
        else:
            log.info(f"Exporting data to file '{csv_file_path}'...")
            csv_file = open(csv_file_path, 'at', newline='')

        csv_writer: CsvWriter = ThreadedCsvWriter(csv_file, delimiter=',')

        csv_writer.write_comment("HEADER")

        now = datetime.datetime.now()
        # timestamps are by nature UTC
        csv_writer.write_comment(f"UNIX-Time: {now.timestamp()}")
        csv_writer.write_comment(
            f"UNIX-Time: {now.astimezone(datetime.timezone.utc).isoformat()} UTC"
        )

        # channel >= 8 are the raw values of the corresponding channels < 8
        channel_titles = [
            f'ch_{i+1 if i < 8 else (str(i+1-8)+"_raw")}'
            for i in selected_channels
        ]
        if timestamp_style == "first_column":
            channel_titles = ["time"] + channel_titles
        csv_writer.write_comment(f"{', '.join(channel_titles)}")

        csv_writer.write_comment(f"sampling mode: {str(sampling_mode)}")

        csv_writer.write_comment(f"intended samplingrate: {sampling_rate} Hz")
        csv_writer.write_comment(
            f"samplingrate: {computed_actual_sampling_rate} Hz")
        if measured_sampling_rate:
            csv_writer.write_comment(
                f"measured samplingrate: {measured_sampling_rate} Hz")

        csv_writer.write_comment(
            f"vscale: {', '.join(str(f) for f in vertical_scale_factor)}")
        csv_writer.write_comment("# zero offset data:")
        zero_offsets = device.get_zero_offsets()
        assert zero_offsets is not None
        for vscale, zero_offset in sorted(zero_offsets.items()):
            csv_writer.write_comment(
                f"zero_offset [{vscale:<4}]: {' '.join([str(round(v, 1)) for v in zero_offset])}"
            )

        csv_writer.write_comment(
            f"zosc-method: {device.get_used_zero_offsets_shift_compensation_method()}"
        )

        csv_writer.write_comment(f"DATA")

        # TODO: make this configurable
        milli_volt_int_representation = False

        def write_per_channel_data(per_channel_data: Dict[int, Union[List[int], List[float]]],
                                   time_of_first_value: Optional[float],
                                   time_of_last_value: float) \
                -> None:
            # sort all channels the same way as in selected_channels
            per_channel_data_list = [
                per_channel_data[ch] for ch in selected_channels
            ]

            if milli_volt_int_representation:
                per_channel_data_list = [[
                    int(round(value * 1000)) for value in single_channel
                ] for single_channel in per_channel_data_list]

            if timestamp_style == "first_column":
                assert time_of_first_value is not None
                values_per_channel_count = len(per_channel_data_list[0])
                deltatime_per_value = (time_of_last_value - time_of_first_value
                                       ) / values_per_channel_count
                timestamps_interpolated = [
                    time_of_first_value + i * deltatime_per_value
                    for i in range(values_per_channel_count)
                ]
                csv_writer.write_rows(
                    zip(timestamps_interpolated, *per_channel_data_list))
            else:  # timestamp_style == "own_row":
                csv_writer.write_rows(zip(*per_channel_data_list))
                # timestamps are by nature UTC
                csv_writer.write_comment(f"UNIX-Time: {time_of_last_value}")

        if sampling_mode == SamplingMode.ROLL:
            last_timestamp = datetime.datetime.now().timestamp()
            for per_channel_data in device.request_samples_roll_mode(
                    mode=str(raw_or_volt), sampling_rate=sampling_rate):
                now_timestamp = datetime.datetime.now().timestamp()
                write_per_channel_data(per_channel_data, last_timestamp,
                                       now_timestamp)
                last_timestamp = now_timestamp
        else:  # burst mode
            # TODO currently not supported
            # TODO missing features:
            # * timestamp_style
            assert timestamp_style == TimestampStyle.OWN_ROW
            while True:
                per_channel_data = device.request_samples_burst_mode()
                now_timestamp = datetime.datetime.now().timestamp()
                write_per_channel_data(per_channel_data, None, now_timestamp)

    except KeyboardInterrupt:
        log.info("Sample collection was stopped by user")
        pass

    if csv_writer:
        csv_writer.close()
Exemplo n.º 7
0
 parser.add_argument('-l',
                     '--loglevel',
                     dest='log_level',
                     nargs='?',
                     type=str,
                     default="info",
                     choices=str_to_log_level.keys(),
                     help='Sets the log level for debugging.')
 parser.add_argument(
     '-v',
     '--vscale',
     metavar='scale',
     nargs="+",
     type=float,
     default=[1.0],
     choices=Hantek1008.valid_vscale_factors(),
     help=
     'Sets the pre scale in the hardware, must be 1, 0.125, or 0.02. If a single value is '
     'given, all selected channels will use that vscale, otherwise there must be one value '
     'per selected channel.')
 parser.add_argument(
     '-c',
     '--calibrationfile',
     dest="calibration_file_path",
     metavar='calibrationfile_path',
     type=str,
     default=None,
     help=
     "Use the content of the given calibration file to correct the measured samples."
 )
 parser.add_argument(
Exemplo n.º 8
0
def sample(device: Hantek1008,
           raw_or_volt: str,
           selected_channels: List[int],
           sampling_rate: float,
           vertical_scale_factor: List[float],
           csv_file_path: str,
           timestamp_style: str,
           measured_sampling_rate: float = None):
    log.info(
        f"Processing data of channel{'' if len(selected_channels) == 1 else 's'}:"
        f" {' '.join([str(i+1) for i in selected_channels])}")

    if raw_or_volt == "volt+raw":  # add the coresponding raw values to the selected channel list
        selected_channels += [sc + 8 for sc in selected_channels]

    try:
        # output_csv_filename = "channel_data.csv"
        if csv_file_path == '-':
            log.info("Exporting data to stdout...")
            csv_file: IO[str] = sys.stdout
        elif csv_file_path.endswith(".xz"):
            log.info(
                f"Exporting data lzma-compressed to file '{csv_file_path}'...")
            csv_file = lzma.open(csv_file_path, 'at', newline='')
        else:
            log.info(f"Exporting data to file '{csv_file_path}'...")
            csv_file = open(csv_file_path, 'at', newline='')

        csv_writer = ThreadedCsvWriter(csv_file, delimiter=',')

        csv_writer.write_comment("HEADER")

        now = datetime.datetime.now()
        # timestamps are by nature UTC
        csv_writer.write_comment(f"UNIX-Time: {now.timestamp()}")
        csv_writer.write_comment(
            f"UNIX-Time: {now.astimezone(datetime.timezone.utc).isoformat()} UTC"
        )

        # channel >= 8 are the raw values of the corresponding channels < 8
        channel_titles = [
            f'ch_{i+1 if i < 8 else (str(i+1-8)+"_raw")}'
            for i in selected_channels
        ]
        if timestamp_style == "first_column":
            channel_titles = ["time"] + channel_titles
        csv_writer.write_comment(f"{', '.join(channel_titles)}")

        csv_writer.write_comment(f"samplingrate: {sampling_rate} Hz")
        if measured_sampling_rate:
            csv_writer.write_comment(
                f"measured samplingrate: {measured_sampling_rate} Hz")

        csv_writer.write_comment(
            f"vscale: {', '.join(str(f) for f in vertical_scale_factor)}")
        csv_writer.write_comment("# zero offset data:")
        for vscale, zero_offset in sorted(device.get_zero_offsets().items()):
            csv_writer.write_comment(
                f"zero_offset [{vscale:<4}]: {' '.join([str(round(v, 1)) for v in zero_offset])}"
            )

        csv_writer.write_comment(
            f"zosc-method: {device.get_used_zero_offsets_shift_compensation_method()}"
        )

        csv_writer.write_comment(f"DATA")
        # TODO: make these configurable
        roll_mode = True
        milli_volt_int_representation = False

        if roll_mode:
            last_timestamp = datetime.datetime.now().timestamp()
            for per_channel_data in device.request_samples_roll_mode(
                    mode=raw_or_volt, sampling_rate=sampling_rate):
                now_timestamp = datetime.datetime.now().timestamp()
                # per_channel_data contains a list of values per channel
                per_channel_data = [
                    per_channel_data[ch] for ch in selected_channels
                ]
                if milli_volt_int_representation:
                    per_channel_data = [[
                        f"{round(value*1000)}" for value in single_channel
                    ] for single_channel in per_channel_data]

                if timestamp_style == "first_column":
                    values_per_channel_count = len(per_channel_data[0])
                    deltatime_per_value = (now_timestamp - last_timestamp
                                           ) / values_per_channel_count
                    timestamps_interpolated = [
                        last_timestamp + i * deltatime_per_value
                        for i in range(values_per_channel_count)
                    ]
                    csv_writer.write_rows(
                        zip(timestamps_interpolated, *per_channel_data))
                else:  # timestamp_style == "own_row":
                    csv_writer.write_rows(zip(*per_channel_data))
                    # timestamps are by nature UTC
                    csv_writer.write_comment(f"UNIX-Time: {now_timestamp}")

                last_timestamp = now_timestamp
        else:
            while True:
                channel_data2, channel_data3 = device.request_samples_burst_mode(
                )

                print(len(channel_data2[0]), len(channel_data3[0]))
                # channel_data = [cd2 + cd3 for cd2, cd3 in zip(channel_data2, channel_data3)]
                # channel_data = [cd[70:] + cd[:70] for cd in channel_data]

                csv_writer.write_rows(zip(*channel_data2))
                csv_writer.write_rows(zip(*channel_data3))
                # timestamps are by nature UTC
                csv_writer.write_comment(
                    f"UNIX-Time: { datetime.datetime.now().timestamp()}")
    except KeyboardInterrupt:
        log.info("Sample collection was stopped by user")
        pass

    if csv_writer:
        csv_writer.close()