e1608: link and device layers (discovery, handshake, framed commands, calibration, decode)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
558d13d521
commit
5ac1b36a1f
5 files changed
+766
No files matched your search
@@ -0,0 +1,91 @@
|
||||
// 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
|
||||
Reference in new issue
Block a user