29#ifndef SBL_CONTROL_CALIBRATION_HPP_
30#define SBL_CONTROL_CALIBRATION_HPP_
43 constexpr float apply(
float measured_su)
const {
return measured_su *
scale +
offset; }
55inline bool fit_points(
const float* measured,
const float* expected, uint8_t n,
57 if (n < 2)
return false;
59 float sum_x = 0.0f, sum_y = 0.0f;
60 for (uint8_t i = 0; i < n; ++i) {
64 const float inv_n = 1.0f /
static_cast<float>(n);
65 const float mean_x = sum_x * inv_n;
66 const float mean_y = sum_y * inv_n;
68 float cov = 0.0f, var = 0.0f;
69 for (uint8_t i = 0; i < n; ++i) {
70 const float dx = measured[i] - mean_x;
71 cov += dx * (expected[i] - mean_y);
74 if (var <= 1e-12f)
return false;
76 out.
scale = cov / var;
111template<u
int8_t N,
typename Standard = VOctStandard>
113 static_assert(N >= 2,
"a line needs two points");
118 for (uint8_t i = 0; i < N; ++i) references_[i] = references[i];
134 measured_[point_] = measured_su;
136 if (point_ < N)
return false;
139 for (uint8_t i = 0; i < N; ++i) expected[i] = Standard::expected_su(references_[i]);
142 if (
fit_points(measured_, expected, N, fitted)) {
164 float apply(
float measured_su)
const {
return fit_.
apply(measured_su); }
170 uint8_t
point()
const {
return point_; }
171 static constexpr uint8_t
points() {
return N; }
174 float reference()
const {
return references_[point_ < N ? point_ : N - 1]; }
182 float references_[N]{};
183 float measured_[N]{};
Capture N reference points, then fit.
void accept()
Leave the routine, keeping the new fit (the app persists it).
bool capture(float measured_su)
Record the source's reading for the current reference.
PointCalibration(const float(&references)[N])
void begin()
Start capturing at point 0, remembering the current fit.
void cancel()
Abandon: restore the fit from before begin().
void set_fit(const LinearFit &fit)
Load a stored fit (boot) or install one by hand.
const LinearFit & fit() const
float reference() const
The reference the app should patch for the current point.
uint8_t point() const
Points captured so far — the reference to patch next, for the UI.
static constexpr uint8_t points()
float apply(float measured_su) const
Correct a reading. Identity until a fit is computed or loaded.
Control Stack v2: scheduler, event queue, merge, CC mapping, calibration, presets.
@ Capturing
waiting for the next reference point
@ Failed
the points were degenerate; cancel() to restore
@ Idle
not calibrating; fit() is whatever was loaded or measured
@ Done
a fit was computed; accept() or cancel()
bool fit_points(const float *measured, const float *expected, uint8_t n, LinearFit &out)
Least-squares line through captured points.
measured su → corrected su. Identity until a fit is computed or loaded.
constexpr float apply(float measured_su) const
1 V per octave in SBL pitch su (1.0 su spans 8 octaves)
static constexpr float kSuPerVolt
static constexpr float expected_su(float volts)