// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #pragma once #include "source/e1608/e1608_link.hpp" #include #include #include #include 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& queue, const Calibration& cal, std::vector>& 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& 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