Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.1 KiB
C++
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
|