Files
udpscope/src/source/e1608/e1608_device.hpp
T

92 lines
3.1 KiB
C++

// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 Martino Ferrari
#pragma once
#include "source/e1608/e1608_link.hpp"
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace udpscope::e1608 {
enum class Range : std::uint8_t { Bip10V = 0, Bip5V = 1, Bip2V = 2, Bip1V = 3 };
double full_scale(Range r); ///< 10, 5, 2, 1
const char* range_name(Range r); ///< "BIP10V" ...
/// Case-insensitive: "BIP10V", "bip5v", ...
bool parse_range(const std::string& text, Range& out);
enum class Trigger : std::uint8_t { None = 0, Rising = 4, Falling = 8, High = 12, Low = 16 };
const char* trigger_name(Trigger t); ///< "none", "rising", "falling", "high", "low"
bool parse_trigger(const std::string& text, Trigger& out); ///< case-insensitive
struct Cal {
double slope = 1.0;
double intercept = 0.0;
};
/// The factory calibration: one (slope, intercept) per range for single-ended and differential
/// inputs, one per DAC. Defaults are the nominal (uncalibrated) values.
struct Calibration {
Cal se[4];
Cal df[4];
Cal ao[2];
bool loaded = false;
/// A raw ADC count as calibrated volts.
double volts(std::uint16_t raw, Range range, bool diff) const;
/// The DAC code that makes `ch` output `volts` (clamped to +-10 V).
std::uint16_t dac_code(int ch, double volts) const;
};
/// One entry of the scan queue. DIFF channels are `channel + 8` on the wire.
struct QueueEntry {
std::uint8_t wire_channel;
Range range;
bool diff() const noexcept { return wire_channel >= 8; }
};
/// Turns `scans` interleaved scans of `queue.size()` raw counts into calibrated volts:
/// `out[queue index][scan]`.
void decode_scans(const std::uint16_t* raw, std::size_t scans, const std::vector<QueueEntry>& queue,
const Calibration& cal, std::vector<std::vector<double>>& out);
/// Typed commands over a `Link`. Holds a reference: the link must outlive it.
class Device {
public:
explicit Device(Link& link) : link_(link) {}
/// Fills `cal` from the board's calibration memory and sets `cal.loaded`. A refusal from the
/// board (`LinkError::Kind::Command`) leaves the nominal values and returns false; any other
/// error propagates.
bool read_calibration(Calibration& cal);
std::uint8_t din();
void dout(std::uint8_t value);
/// `inputs_mask`: a set bit makes that line an input.
void dconf(std::uint8_t inputs_mask);
/// Writes `volts` (clamped to +-10 V, calibrated) to DAC `ch`; returns the clamped volts.
double aout(int ch, double volts, const Calibration& cal);
std::uint32_t counter();
void counter_reset();
/// Bit 0 scan socket open, bit 1 scan running, bit 2 overrun.
std::uint16_t status();
/// Opens the scan socket, writes the queue, starts the scan and sends the filler byte.
/// `aggregate_hz` is the scan rate times the number of channels. On failure the scan socket
/// is closed again.
void scan_start(const std::vector<QueueEntry>& queue, double aggregate_hz, bool ext_clock,
Trigger trigger);
/// Closes the scan socket and tells the board to stop; board errors are ignored.
void scan_stop();
private:
Link& link_;
};
} // namespace udpscope::e1608