STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
calibration_main.cpp File Reference

Standalone Calibration Service — subscribes directly to Elodin, applies calibration, publishes calibrated VTables back to Elodin. More...

#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <deque>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <mutex>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include "calibration/CaptureWindow.hpp"
#include "calibration/CommandDedupe.hpp"
#include "calibration/CubicCalibrationStore.hpp"
#include "calibration/HpPtCode.hpp"
#include "calibration/LcTareStore.hpp"
#include "calibration/PTCalibration.hpp"
#include "calibration/PtSmoothing.hpp"
#include "calibration/RobustCalibrationManager.hpp"
#include "calibration/SensorCalibration.hpp"
#include "calibration/TypeK.hpp"
#include "calibration/ValvePosition.hpp"
#include "comms/messages/sensor/CalibratedPTMessage.hpp"
#include "comms/messages/sensor/CalibratedSensorMessages.hpp"
#include "comms/messages/sensor/T7Messages.hpp"
#include "config/Config.hpp"
#include "config/LoadActiveBoards.hpp"
#include "elodin/DatabaseConfig.hpp"
#include "elodin/ElodinClient.hpp"
#include "elodin/SubscriptionTracker.hpp"
#include "t7/T7Calibration.hpp"
#include "t7/T7Convert.hpp"
#include "t7/T7Stream.hpp"
Include dependency graph for calibration_main.cpp:

Go to the source code of this file.

Namespaces

namespace  anonymous_namespace{calibration_main.cpp}

Enumerations

enum class  anonymous_namespace{calibration_main.cpp}::PtModel { anonymous_namespace{calibration_main.cpp}::Cubic , anonymous_namespace{calibration_main.cpp}::Robust , anonymous_namespace{calibration_main.cpp}::Blend , anonymous_namespace{calibration_main.cpp}::Physics }
 Which model a PT streams: operator/factory cubic, the robust learner, the 75/25 blend, or the datasheet physics conversion (ratiometric for 0-5 V boards, 4-20 mA for current-loop). More...
enum class  anonymous_namespace{calibration_main.cpp}::LcModel { anonymous_namespace{calibration_main.cpp}::Linear , anonymous_namespace{calibration_main.cpp}::Cubic , anonymous_namespace{calibration_main.cpp}::Physics }
 Which model an LC streams: a straight-line fit through the operator's captured points, a cubic fit through them, or the datasheet physics conversion (sensitivity/PGA-gain formula). More...

Functions

static bool verbose ()
static bool env_flag_true (const char *name)
static PtModel parse_pt_model (const std::string &s)
 Parse a config model string to the enum; "cubic" and anything unrecognized map to Cubic.
static PtModel pt_model_for (uint16_t uid)
 Per-sensor streaming model; sensors absent from the map default to factory cubic.
static double pt_full_scale_for (uint16_t uid)
 Per-sensor physics full-scale PSI; default 1000 (0-5 V -> 0-1000 PSI) when unset.
static double pt_sense_resistor_for (uint16_t uid)
 Per-sensor 4-20 mA shunt resistance (Ω); default 120 when unset.
static const char * pt_model_name (PtModel m)
 Config-string form of a model, for the cubic store's per-uid active_model tag.
static LcModel parse_lc_model (const std::string &s)
 Parse a config model string for an LC role: "linear" and "cubic" map to their fits, everything else (including unrecognized/"robust"/"blend", which LC doesn't support) maps to Physics, as it always has.
static LcModel lc_model_for (uint16_t uid)
 Per-sensor LC streaming model; a uid absent from g_lc_model defaults to Linear, which streams physics until it has a fit (see select_lc_kg), so an uncaptured LC reads as it always did.
static bool lc_streams_physics (uint16_t uid, bool fit_ok)
 True when this LC's published kilograms are its datasheet physics conversion: physics itself, or a linear sensor that has no fit yet.
static double lc_full_scale_for (uint16_t uid, double fallback)
 Per-sensor LC physics full-scale kg; falls back to the [calibration.lc] global when unset.
static double lc_sensitivity_for (uint16_t uid, double fallback)
 Per-sensor LC sensitivity mV/V; falls back to the [calibration.lc] global when unset.
static double lc_pga_gain_for (uint16_t uid, double fallback)
 Per-sensor LC PGA gain; falls back to the [calibration.lc] global when unset.
static const char * lc_model_name (LcModel m)
 Config-string form of an LC model, for the cubic store's per-uid active_model tag.
static double convert_ratiometric_pt_to_pressure (int32_t adc_raw, double full_scale_psi)
static double select_pt_psi (uint16_t uid, double psi_fac, double psi_rob, double psi_phys, bool fac_ok, bool has_robust)
 The single PT source-selection rule, shared by the streaming path and the Zero-All helper so both agree.
static double select_lc_kg (uint16_t uid, double kg_fit, double kg_phys, bool fit_ok)
 The LC source-selection rule, mirroring select_pt_psi minus robust/blend (LC doesn't learn drift).
static double convert_hp_pt_to_pressure (uint8_t local_ch, uint32_t adc_raw, const fsw::config::PtBoardConfig &board, double full_scale_psi, double sense_resistor_ohms, std::array< uint8_t, 11 > &live_state)
 4-20 mA HP PT → PSI.
static double convert_tc_adc_to_temp_c (int32_t adc_raw, double adc_ref_voltage)
 K-type thermocouple: raw ADC → voltage → temperature (°C).
static double clamp_tc_rtd_temp_publish_c (double t_c)
 TC/RTD calibrated temperature before Elodin publish.
static double convert_rtd_adc_to_temp_c (int32_t adc_raw, double adc_ref_voltage, double excitation_ua, double r0_ohm)
 Pt1000 RTD: raw ADC → voltage → resistance → temperature (°C), on the full IEC 60751 curve (rtd::rtd_board_adc_to_temp_c).
static double convert_act_adc_to_current (uint32_t adc_raw)
 Actuator current-sense: 12-bit ADC → current (amps).
static double convert_lc_adc_to_force (int32_t adc_raw, double sensitivity_mv_per_v, double pga_gain, double full_scale_value)
 Ratiometric load cell: raw ADC → force (kg).
static void signalHandler (int)
static double unix_now ()
 Monotonic now, in ns.
static uint64_t mono_ns ()
static uint16_t resolve_pt_sensor_uid (uint8_t type_lo, uint8_t ch, const std::vector< fsw::elodin::BoardChannels > &pt_boards)
 Map Elodin PT raw packet low byte + connector ch → robust calibration uid (board_id*100+ch).
static uint16_t resolve_lc_sensor_uid (uint8_t type_lo, uint8_t ch, const std::vector< fsw::elodin::BoardChannels > &lc_boards)
 Map Elodin LC raw packet low byte + connector ch -> LC uid (board_id*100+ch).
static std::optional< int32_t > invert_abort_psi_to_adc (uint16_t uid, uint8_t pt_log_ch, bool is_loop, const fsw::config::PtBoardConfig *loop_board, double target_psi, bool force_physics, const fsw::calibration::PTCalibrationManager &pt_calibration, fsw::calibration::RobustCalibrationManager &robust_manager)
 Invert an abort-procedure pressure (the level the boards vent DOWN below before completing an autonomous abort) to the raw ADC code the firmware compares against (dp.data < threshold_adc), for uid's model, by bisecting the SAME forward conversion the stream uses — so the board's "vented to safe" gate fires at exactly the pressure the operator configured on the curve they see.
int main (int argc, char *argv[])

Variables

std::map< uint8_t, std::array< uint8_t, 11 > > anonymous_namespace{calibration_main.cpp}::g_hp_ma_live
 Hysteresis: 0 = treat as below loop, 1 = in 4–20 mA band (stops 3.99↔4.01 PSI chatter).
fsw::calibration::PtSmoother anonymous_namespace{calibration_main.cpp}::g_pt_smoother
 Per-sensor PT output smoothing (ESP32 and T7), seeded with each sensor's first reading.
std::unordered_map< uint16_t, PtModel > anonymous_namespace{calibration_main.cpp}::g_pt_model
 uid (board_id*100 + connector) -> streaming model.
std::unordered_map< uint16_t, std::string > anonymous_namespace{calibration_main.cpp}::g_uid_role
 uid -> role name (from [sensor_roles_*]).
std::unordered_map< uint16_t, double > anonymous_namespace{calibration_main.cpp}::g_pt_full_scale
 Per-sensor physics-mode parameters (from [calibration_full_scale_*] / [calibration_sense_resistor_*], falling back to the board defaults).
std::unordered_map< uint16_t, double > anonymous_namespace{calibration_main.cpp}::g_pt_sense_resistor
std::optional< PtModel > anonymous_namespace{calibration_main.cpp}::g_env_override
 Set once at startup: when a CAL_USE_* PT env var is present it forces every sensor, else nullopt and the per-sensor config governs.
std::unordered_map< uint16_t, LcModel > anonymous_namespace{calibration_main.cpp}::g_lc_model
 uid -> LC streaming model.
std::unordered_map< uint16_t, double > anonymous_namespace{calibration_main.cpp}::g_lc_full_scale
 Per-sensor LC physics-mode parameters (from [calibration_full_scale_*] / [calibration_sensitivity_*] / [calibration_pga_*], falling back to [calibration.lc] globals).
std::unordered_map< uint16_t, double > anonymous_namespace{calibration_main.cpp}::g_lc_sensitivity
std::unordered_map< uint16_t, double > anonymous_namespace{calibration_main.cpp}::g_lc_pga_gain
std::set< uint16_t > anonymous_namespace{calibration_main.cpp}::g_lc_uids
 Every LC uid, populated at startup — the discriminant apply_capture/apply_clear/ reload_live_store use to route a shared uid-keyed cubic_store entry to lc_calibration instead of pt_calibration/robust_manager (PT and LC uids share the store; their board_id ranges never collide, but nothing else tags which kind a uid is).
std::set< uint8_t > anonymous_namespace{calibration_main.cpp}::g_lc_board_ids
 Board ids of every LC board, so a connector outside active_connectors still routes as LC.
std::map< uint8_t, double > anonymous_namespace{calibration_main.cpp}::g_t7_cold_junction_c
 Latest thermocouple cold-junction temperature (°C) per T7 slot, from t7_bridge's CJC table {0x26, (slot-1)*0x20 + 0x0F}.
static std::atomic< bool > running {true}
constexpr double kRatiometricPtZeroFrac = 0.1
 Ratiometric PT, 0.5-4.5 V at 5 V excitation: excitation is the ADC reference, so the output is 10 % of full code at zero and 90 % at full scale whatever the supply actually is — psi = (adc / 2^31 - 0.1) / 0.8 * full_scale.
constexpr double kRatiometricPtSpanFrac = 0.8
constexpr int kMinAbortCalPoints = 3
 A sensor needs at least this many captured points before its cubic/robust curve is trusted to invert an abort threshold.

Detailed Description

Standalone Calibration Service — subscribes directly to Elodin, applies calibration, publishes calibrated VTables back to Elodin.

Connects directly to Elodin DB (port 2240) and subscribes to the raw sensor data (PT, TC, RTD, LC). Publishes calibrated values to the same Elodin instance. No relay dependency — fully independent of the relay and backend restart cycles.

Raw data path (always immediate): daq_bridge → Elodin [0x20xx raw] → relay → backend → GUI Calibrated path (~1 ms behind): calibration_service ─(subscribes)→ Elodin [0x20xx raw] ─(publishes)→ Elodin [0x20xx+0x10 cal] → relay → backend → GUI

Usage: ./calibration_service [–config PATH] [–elodin-host HOST] [–elodin-port PORT] CAL_VERBOSE=1 for per-packet debug output LP PT (non–HP board): default = factory cubic from CSV/JSON (same priority as letsfix / stable stack). RobustCalibrationManager still seeds from that factory curve and loads adjustments.json; streaming uses factory unless you opt into robust below. CAL_USE_ROBUST_PT=1 — 100% robust mean for streaming (when sensor initialized) CAL_USE_ROBUST_BLEND=1 — 75% robust + 25% factory CAL_USE_FACTORY_PT=1 — force factory cubic (same as default; explicit) CAL_BACKUP_PATH — override robust prior JSON (else the latest calibration_backups/ JSON)

Definition in file calibration_main.cpp.

Function Documentation

◆ clamp_tc_rtd_temp_publish_c()

double clamp_tc_rtd_temp_publish_c ( double t_c)
static

TC/RTD calibrated temperature before Elodin publish.

Polynomial fits can diverge when raw ADC is far off-scale (e.g. multi-million counts); thin backend rejects absurd temperature_c.

Definition at line 417 of file calibration_main.cpp.

Referenced by main().

◆ convert_act_adc_to_current()

double convert_act_adc_to_current ( uint32_t adc_raw)
static

Actuator current-sense: 12-bit ADC → current (amps).

3.3V reference, V-to-I transfer function = 1:1.

Definition at line 437 of file calibration_main.cpp.

Referenced by main().

◆ convert_hp_pt_to_pressure()

double convert_hp_pt_to_pressure ( uint8_t local_ch,
uint32_t adc_raw,
const fsw::config::PtBoardConfig & board,
double full_scale_psi,
double sense_resistor_ohms,
std::array< uint8_t, 11 > & live_state )
static

4-20 mA HP PT → PSI.

Wire raw is u32 full-scale to 2^31.

A 4-20 mA transmitter regulates its loop current independently of its supply, so the reading is absolute and needs no excitation reference — the shunt voltage against the internal 2.5 V ref is the whole measurement. Optional per-channel mA hysteresis kills threshold flicker at ~4 mA open-circuit noise.

Definition at line 339 of file calibration_main.cpp.

References adc(), and board().

Referenced by invert_abort_psi_to_adc(), and main().

◆ convert_lc_adc_to_force()

double convert_lc_adc_to_force ( int32_t adc_raw,
double sensitivity_mv_per_v,
double pga_gain,
double full_scale_value )
static

Ratiometric load cell: raw ADC → force (kg).

Reference = excitation, so voltage cancels: code_fs = (sensitivity * PGA_gain) * 2^31. force = (code / code_fs) * full_scale_value.

Definition at line 448 of file calibration_main.cpp.

Referenced by main().

◆ convert_ratiometric_pt_to_pressure()

double convert_ratiometric_pt_to_pressure ( int32_t adc_raw,
double full_scale_psi )
static

Definition at line 269 of file calibration_main.cpp.

References kRatiometricPtSpanFrac, and kRatiometricPtZeroFrac.

Referenced by invert_abort_psi_to_adc(), and main().

◆ convert_rtd_adc_to_temp_c()

double convert_rtd_adc_to_temp_c ( int32_t adc_raw,
double adc_ref_voltage,
double excitation_ua,
double r0_ohm )
static

Pt1000 RTD: raw ADC → voltage → resistance → temperature (°C), on the full IEC 60751 curve (rtd::rtd_board_adc_to_temp_c).

NaN for an open or shorted element: publish nothing.

Definition at line 427 of file calibration_main.cpp.

References fsw::calibration::rtd::rtd_board_adc_to_temp_c().

Referenced by main().

◆ convert_tc_adc_to_temp_c()

double convert_tc_adc_to_temp_c ( int32_t adc_raw,
double adc_ref_voltage )
static

K-type thermocouple: raw ADC → voltage → temperature (°C).

ITS-90 rational polynomial with 5 sub-ranges (-6.4 mV to 69.6 mV). Coefficients from NIST ITS-90 Thermocouple Database (Type K inverse). Each range: T = T0 + (x * num) / den, where x = V_mV - V0.

Definition at line 403 of file calibration_main.cpp.

References fsw::calibration::type_k_c_from_mv().

Referenced by main().

◆ env_flag_true()

bool env_flag_true ( const char * name)
static

Definition at line 161 of file calibration_main.cpp.

Referenced by main().

◆ invert_abort_psi_to_adc()

std::optional< int32_t > invert_abort_psi_to_adc ( uint16_t uid,
uint8_t pt_log_ch,
bool is_loop,
const fsw::config::PtBoardConfig * loop_board,
double target_psi,
bool force_physics,
const fsw::calibration::PTCalibrationManager & pt_calibration,
fsw::calibration::RobustCalibrationManager & robust_manager )
static

Invert an abort-procedure pressure (the level the boards vent DOWN below before completing an autonomous abort) to the raw ADC code the firmware compares against (dp.data < threshold_adc), for uid's model, by bisecting the SAME forward conversion the stream uses — so the board's "vented to safe" gate fires at exactly the pressure the operator configured on the curve they see.

force_physics forces the datasheet (ratiometric / 4-20 mA) mapping regardless of the sensor's active model, used when the cubic/robust fit is too sparse to trust (see kMinAbortCalPoints).

Monotonic increasing in adc for every model, so bisection is valid. Returns nullopt when the target is unreachable in [0, 2^31) — e.g. an uncalibrated cubic whose forward is flat 0, or full_scale 0 — so a sensor with no trustworthy mapping gets no threshold (the firmware then relies on its PT_ABORT_THRESHOLD_TIMEOUT) rather than a wrong one.

Definition at line 554 of file calibration_main.cpp.

References a, adc(), fsw::calibration::PTCalibrationManager::calculate_pressure(), convert_hp_pt_to_pressure(), convert_ratiometric_pt_to_pressure(), fsw::calibration::RobustCalibrationManager::has_sensor(), fsw::calibration::PTCalibrationManager::is_calibrated(), fsw::calibration::RobustCalibrationManager::predict_pressure_psi(), pt_full_scale_for(), pt_sense_resistor_for(), and select_pt_psi().

Referenced by main().

◆ lc_full_scale_for()

double lc_full_scale_for ( uint16_t uid,
double fallback )
static

Per-sensor LC physics full-scale kg; falls back to the [calibration.lc] global when unset.

Definition at line 233 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_lc_full_scale.

Referenced by main().

◆ lc_model_for()

LcModel lc_model_for ( uint16_t uid)
static

Per-sensor LC streaming model; a uid absent from g_lc_model defaults to Linear, which streams physics until it has a fit (see select_lc_kg), so an uncaptured LC reads as it always did.

Definition at line 220 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_lc_model, and anonymous_namespace{calibration_main.cpp}::Linear.

Referenced by lc_streams_physics(), main(), and select_lc_kg().

◆ lc_model_name()

const char * lc_model_name ( LcModel m)
static

Config-string form of an LC model, for the cubic store's per-uid active_model tag.

Definition at line 251 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Cubic, anonymous_namespace{calibration_main.cpp}::Linear, and anonymous_namespace{calibration_main.cpp}::Physics.

Referenced by main().

◆ lc_pga_gain_for()

double lc_pga_gain_for ( uint16_t uid,
double fallback )
static

Per-sensor LC PGA gain; falls back to the [calibration.lc] global when unset.

Definition at line 245 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_lc_pga_gain.

Referenced by main().

◆ lc_sensitivity_for()

double lc_sensitivity_for ( uint16_t uid,
double fallback )
static

Per-sensor LC sensitivity mV/V; falls back to the [calibration.lc] global when unset.

Definition at line 239 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_lc_sensitivity.

Referenced by main().

◆ lc_streams_physics()

bool lc_streams_physics ( uint16_t uid,
bool fit_ok )
static

True when this LC's published kilograms are its datasheet physics conversion: physics itself, or a linear sensor that has no fit yet.

Such a reading is "calibrated" in the status sense.

Definition at line 227 of file calibration_main.cpp.

References lc_model_for(), anonymous_namespace{calibration_main.cpp}::Linear, and anonymous_namespace{calibration_main.cpp}::Physics.

Referenced by main().

◆ main()

int main ( int argc,
char * argv[] )

The board's 4-20 mA config, or nullptr when this slot is not a current-loop PT board.

Definition at line 596 of file calibration_main.cpp.

References fsw::calibration::CubicFit::A, fsw::config::Config::abort_pts, fsw::config::ACTUATOR, adc(), fsw::calibration::CaptureResult::adc_avg, fsw::calibration::CubicCalibrationStore::add_point(), fsw::t7::T7CalibrationMap::all(), fsw::elodin::SubscriptionTracker::assign(), fsw::calibration::CubicFit::B, b, fsw::config::BALL_VALVE, fsw::elodin::ball_valve_raw_tables(), fsw::elodin::ElodinClient::begin_batch(), anonymous_namespace{calibration_main.cpp}::Blend, board(), board_id, fsw::calibration::CubicChannel::board_id, fsw::config::Config::boards, fsw::t7::T7CalibrationMap::by_table(), fsw::t7::T7CalibrationMap::by_uid(), fsw::calibration::CubicFit::C, fsw::calibration::cal_target(), fsw::calibration::SensorCalibrationManager::calculate(), fsw::calibration::PTCalibrationManager::calculate_pressure(), fsw::calibration::SensorCalibrationManager::calibrated_count(), fsw::t7::calibrated_entity_name(), fsw::t7::calibrated_field(), fsw::t7::calibrated_table_id(), fsw::config::Config::calibration, fsw::config::Config::calibration_model_for(), fsw::calibration::CaptureWindow::capture(), fsw::calibration::CubicCalibrationStore::channel(), clamp_tc_rtd_temp_publish_c(), fsw::calibration::CaptureWindow::clear(), fsw::calibration::LcTareStore::clear(), fsw::calibration::PTCalibrationManager::clear_calibration(), fsw::calibration::SensorCalibrationManager::clear_calibration(), fsw::calibration::CubicCalibrationStore::clear_channel(), fsw::elodin::ElodinClient::connect(), convert_act_adc_to_current(), convert_hp_pt_to_pressure(), convert_lc_adc_to_force(), convert_ratiometric_pt_to_pressure(), convert_rtd_adc_to_temp_c(), convert_tc_adc_to_temp_c(), count(), anonymous_namespace{calibration_main.cpp}::Cubic, fsw::calibration::CubicFit::D, fsw::calibration::CaptureResult::drift, fsw::calibration::CaptureResult::drift_z, fsw::elodin::SubscriptionTracker::due(), fsw::config::ENCODER, env_flag_true(), fsw::calibration::CubicCalibrationStore::fit_for(), fsw::elodin::ElodinClient::flush_batch(), fsw::calibration::valve_position::from_counts(), fsw::config::Config::full_scale_for(), anonymous_namespace{calibration_main.cpp}::g_env_override, anonymous_namespace{calibration_main.cpp}::g_hp_ma_live, anonymous_namespace{calibration_main.cpp}::g_lc_board_ids, anonymous_namespace{calibration_main.cpp}::g_lc_full_scale, anonymous_namespace{calibration_main.cpp}::g_lc_model, anonymous_namespace{calibration_main.cpp}::g_lc_pga_gain, anonymous_namespace{calibration_main.cpp}::g_lc_sensitivity, anonymous_namespace{calibration_main.cpp}::g_lc_uids, anonymous_namespace{calibration_main.cpp}::g_pt_full_scale, anonymous_namespace{calibration_main.cpp}::g_pt_model, anonymous_namespace{calibration_main.cpp}::g_pt_sense_resistor, anonymous_namespace{calibration_main.cpp}::g_pt_smoother, anonymous_namespace{calibration_main.cpp}::g_t7_cold_junction_c, anonymous_namespace{calibration_main.cpp}::g_uid_role, fsw::calibration::RobustCalibrationManager::has_sensor(), fsw::calibration::hp_pt_shunt_code(), invert_abort_psi_to_adc(), fsw::calibration::PTCalibrationManager::is_calibrated(), fsw::calibration::SensorCalibrationManager::is_calibrated(), fsw::elodin::ElodinClient::is_connected(), fsw::config::is_current_loop(), fsw::elodin::is_error_response(), fsw::elodin::kBallValveCalHi, fsw::elodin::kBallValveRawHi, fsw::t7::kCjcTableOffset, kMinAbortCalPoints, fsw::calibration::kRawUnitMicrovolts, fsw::elodin::kTableCalibrationCommand, fsw::t7::kTypeHi, fsw::calibration::LC, fsw::config::LC, fsw::config::CalibrationConfig::lc_csv_path, lc_full_scale_for(), fsw::config::CalibrationConfig::lc_full_scale_value, fsw::config::CalibrationConfig::lc_json_dir, fsw::t7::lc_kg(), lc_model_for(), lc_model_name(), fsw::config::CalibrationConfig::lc_pga_gain, lc_pga_gain_for(), lc_sensitivity_for(), fsw::config::CalibrationConfig::lc_sensitivity_mv_per_v, lc_streams_physics(), fsw::calibration::lc_tare_entity(), anonymous_namespace{calibration_main.cpp}::Linear, fsw::calibration::CubicChannel::live, fsw::calibration::CubicCalibrationStore::load(), fsw::calibration::LcTareStore::load(), fsw::config::load(), fsw::config::load_active_boards(), fsw::calibration::RobustCalibrationManager::load_adjustments(), fsw::calibration::SensorCalibrationManager::load_calibration(), fsw::calibration::CubicCalibrationStore::load_failed(), fsw::config::load_pt_boards(), fsw::calibration::CubicChannel::logical_ch, mono_ns(), fsw::calibration::CaptureResult::n, fsw::calibration::None, fsw::calibration::CubicCalibrationStore::note_capture(), o, fsw::calibration::CaptureResult::ok, ok, p, parse_lc_model(), parse_pt_model(), fsw::config::Config::pga_gain_for(), anonymous_namespace{calibration_main.cpp}::Physics, pkt_buf, fsw::calibration::CubicChannel::points, fsw::calibration::RobustCalibrationManager::predict_pressure_psi(), fsw::calibration::PT, fsw::config::PT, pt_full_scale_for(), fsw::calibration::pt_logical_calibration_channel(), pt_model_for(), pt_model_name(), fsw::t7::pt_psi(), pt_sense_resistor_for(), fsw::elodin::ElodinClient::publish(), publish(), fsw::calibration::CaptureWindow::push(), r, fsw::elodin::raw_sensor_tables(), fsw::t7::T7CalibrationMap::raw_tables(), fsw::elodin::ElodinClient::read_packet(), fsw::calibration::LcTareStore::recompute(), fsw::calibration::LcTareStore::recompute_all(), fsw::calibration::LcTareStore::recompute_stale(), fsw::elodin::ElodinClient::reconnect(), fsw::elodin::DatabaseConfig::register_ball_valve_calibrated_tables(), fsw::elodin::DatabaseConfig::register_calibrated_tables(), fsw::calibration::CubicCalibrationStore::register_channel(), fsw::elodin::DatabaseConfig::register_t7_calibrated_tables(), fsw::elodin::SubscriptionTracker::rejected(), fsw::calibration::RobustCalibrationManager::reseed_sensor(), fsw::elodin::SubscriptionTracker::reset(), fsw::calibration::RobustCalibrationManager::reset_adjustment(), resolve_lc_sensor_uid(), resolve_pt_sensor_uid(), anonymous_namespace{calibration_main.cpp}::Robust, fsw::calibration::CubicChannel::role, fsw::config::RTD, fsw::config::CalibrationConfig::rtd_adc_ref_voltage, fsw::t7::rtd_c(), fsw::config::CalibrationConfig::rtd_csv_path, fsw::config::CalibrationConfig::rtd_excitation_ua, fsw::config::CalibrationConfig::rtd_json_dir, fsw::config::CalibrationConfig::rtd_r0_ohm, running, fsw::calibration::CubicCalibrationStore::save(), fsw::calibration::LcTareStore::save(), fsw::calibration::RobustCalibrationManager::save_adjustments(), fsw::calibration::CommandDedupe::seen(), select_lc_kg(), select_pt_psi(), fsw::config::Config::sense_resistor_for(), fsw::config::Config::sensitivity_for(), fsw::config::Config::sensor_roles_for(), fsw::calibration::LcTareStore::set(), fsw::calibration::PTCalibrationManager::set_calibration(), fsw::calibration::SensorCalibrationManager::set_calibration(), fsw::calibration::LcTareStore::set_curves_trusted(), fsw::calibration::PTCalibrationManager::set_default_paths(), fsw::calibration::CubicCalibrationStore::set_fit_curve(), fsw::elodin::ElodinClient::set_recv_timeout_ms(), fsw::calibration::CaptureResult::settled, signalHandler(), fsw::calibration::CaptureResult::spread, fsw::calibration::PtSmoother::step(), fsw::elodin::ElodinClient::subscribe_table(), t7(), fsw::config::Config::t7_boards, fsw::t7::t7_counts(), fsw::config::Config::t7_errors, fsw::t7::t7_volts(), tables(), fsw::calibration::LcTareStore::tare_for(), target, fsw::config::TC, fsw::config::CalibrationConfig::tc_adc_ref_voltage, fsw::t7::tc_c(), fsw::config::CalibrationConfig::tc_csv_path, fsw::config::CalibrationConfig::tc_json_dir, fsw::calibration::CaptureWindow::uids(), fsw::calibration::CubicCalibrationStore::uids(), fsw::calibration::LcTareStore::uids(), unix_now(), fsw::calibration::RobustCalibrationManager::update_calibration(), fsw::calibration::CubicFit::valid, verbose(), volts(), and fsw::calibration::CaptureResult::window_ms.

◆ mono_ns()

uint64_t mono_ns ( )
static

Definition at line 473 of file calibration_main.cpp.

Referenced by main().

◆ parse_lc_model()

LcModel parse_lc_model ( const std::string & s)
static

Parse a config model string for an LC role: "linear" and "cubic" map to their fits, everything else (including unrecognized/"robust"/"blend", which LC doesn't support) maps to Physics, as it always has.

Definition at line 212 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Cubic, anonymous_namespace{calibration_main.cpp}::Linear, and anonymous_namespace{calibration_main.cpp}::Physics.

Referenced by main().

◆ parse_pt_model()

PtModel parse_pt_model ( const std::string & s)
static

Parse a config model string to the enum; "cubic" and anything unrecognized map to Cubic.

Definition at line 167 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Blend, anonymous_namespace{calibration_main.cpp}::Cubic, anonymous_namespace{calibration_main.cpp}::Physics, and anonymous_namespace{calibration_main.cpp}::Robust.

Referenced by main().

◆ pt_full_scale_for()

double pt_full_scale_for ( uint16_t uid)
static

Per-sensor physics full-scale PSI; default 1000 (0-5 V -> 0-1000 PSI) when unset.

Definition at line 184 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_pt_full_scale.

Referenced by invert_abort_psi_to_adc(), and main().

◆ pt_model_for()

PtModel pt_model_for ( uint16_t uid)
static

Per-sensor streaming model; sensors absent from the map default to factory cubic.

Definition at line 178 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Cubic, and anonymous_namespace{calibration_main.cpp}::g_pt_model.

Referenced by main(), and select_pt_psi().

◆ pt_model_name()

const char * pt_model_name ( PtModel m)
static

Config-string form of a model, for the cubic store's per-uid active_model tag.

Definition at line 196 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Blend, anonymous_namespace{calibration_main.cpp}::Physics, and anonymous_namespace{calibration_main.cpp}::Robust.

Referenced by main().

◆ pt_sense_resistor_for()

double pt_sense_resistor_for ( uint16_t uid)
static

Per-sensor 4-20 mA shunt resistance (Ω); default 120 when unset.

Definition at line 190 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::g_pt_sense_resistor.

Referenced by invert_abort_psi_to_adc(), and main().

◆ resolve_lc_sensor_uid()

uint16_t resolve_lc_sensor_uid ( uint8_t type_lo,
uint8_t ch,
const std::vector< fsw::elodin::BoardChannels > & lc_boards )
static

Map Elodin LC raw packet low byte + connector ch -> LC uid (board_id*100+ch).

Mirrors resolve_pt_sensor_uid (kept as a separate copy rather than a shared/generalized helper so the PT/abort-threshold path is untouched by this change).

Definition at line 510 of file calibration_main.cpp.

References ch().

Referenced by main().

◆ resolve_pt_sensor_uid()

uint16_t resolve_pt_sensor_uid ( uint8_t type_lo,
uint8_t ch,
const std::vector< fsw::elodin::BoardChannels > & pt_boards )
static

Map Elodin PT raw packet low byte + connector ch → robust calibration uid (board_id*100+ch).

Uses the same slot rule as daq_bridge: slot = board_id % 10, with 0 treated as 10.

Definition at line 483 of file calibration_main.cpp.

References ch().

Referenced by main().

◆ select_lc_kg()

double select_lc_kg ( uint16_t uid,
double kg_fit,
double kg_phys,
bool fit_ok )
static

The LC source-selection rule, mirroring select_pt_psi minus robust/blend (LC doesn't learn drift).

kg_fit / fit_ok are this uid's fitted curve — linear or cubic per its model, since the store fits to the model's degree and that one curve is what lc_calibration holds. A cubic uid with no fit reads 0, the "physics-or-nothing" rule PT's cubic mode uses once explicitly opted in. A linear uid with no fit streams physics instead: linear is the default for every LC, so it must read exactly what an unconfigured LC read before it existed.

Definition at line 319 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Cubic, lc_model_for(), anonymous_namespace{calibration_main.cpp}::Linear, and anonymous_namespace{calibration_main.cpp}::Physics.

Referenced by main().

◆ select_pt_psi()

double select_pt_psi ( uint16_t uid,
double psi_fac,
double psi_rob,
double psi_phys,
bool fac_ok,
bool has_robust )
static

The single PT source-selection rule, shared by the streaming path and the Zero-All helper so both agree.

physics (the datasheet conversion — ratiometric or 4-20 mA per the board) is also the universal fallback: a cubic/robust sensor that isn't yet fit/seeded streams physics rather than

  1. An env override (CAL_USE_*), when set, forces cubic/robust/blend across all sensors.

Definition at line 286 of file calibration_main.cpp.

References anonymous_namespace{calibration_main.cpp}::Blend, anonymous_namespace{calibration_main.cpp}::Cubic, anonymous_namespace{calibration_main.cpp}::g_env_override, anonymous_namespace{calibration_main.cpp}::Physics, pt_model_for(), and anonymous_namespace{calibration_main.cpp}::Robust.

Referenced by invert_abort_psi_to_adc(), and main().

◆ signalHandler()

void signalHandler ( int )
static

Definition at line 459 of file calibration_main.cpp.

References running.

Referenced by main().

◆ unix_now()

double unix_now ( )
static

Monotonic now, in ns.

The capture window judges liveness on OUR clock and never the board's, so a board whose clock is skewed or has just reset cannot make a dead channel look live (or a live one look stale). Wall-clock unix seconds, for stamping a record a human will read.

Definition at line 468 of file calibration_main.cpp.

Referenced by main().

◆ verbose()

bool verbose ( )
static

Variable Documentation

◆ kMinAbortCalPoints

int kMinAbortCalPoints = 3
constexpr

A sensor needs at least this many captured points before its cubic/robust curve is trusted to invert an abort threshold.

Below it, the fit is too loose — a 1-2 point cubic can invert to an ADC far from the intended pressure — so the abort threshold falls back to physics (the datasheet mapping), which is always monotonic and sane. See invert_abort_psi_to_adc / write_abort_thresholds.

Definition at line 537 of file calibration_main.cpp.

Referenced by main().

◆ kRatiometricPtSpanFrac

double kRatiometricPtSpanFrac = 0.8
constexpr

Definition at line 268 of file calibration_main.cpp.

Referenced by convert_ratiometric_pt_to_pressure().

◆ kRatiometricPtZeroFrac

double kRatiometricPtZeroFrac = 0.1
constexpr

Ratiometric PT, 0.5-4.5 V at 5 V excitation: excitation is the ADC reference, so the output is 10 % of full code at zero and 90 % at full scale whatever the supply actually is — psi = (adc / 2^31 - 0.1) / 0.8 * full_scale.

Used by the physics model on non-loop boards. The bench calibrations of 2026-02 put four of the stand's PTs within ~1.5 % of this.

Definition at line 267 of file calibration_main.cpp.

Referenced by convert_ratiometric_pt_to_pressure().

◆ running

std::atomic<bool> running {true}
static

Definition at line 154 of file calibration_main.cpp.

Referenced by bringup_task(), main(), signal_handler(), and signalHandler().