STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
AllanVariance.cpp
Go to the documentation of this file.
2
3#include <Eigen/Dense>
4#include <algorithm>
5#include <cmath>
6#include <numeric>
7
8namespace fsw {
9namespace calibration {
10
11void compute_allan_variance(const std::vector<double>& timeseries, double tau0,
12 std::vector<double>& tau_out, std::vector<double>& sigma2_out) {
13 tau_out.clear();
14 sigma2_out.clear();
15 const size_t n = timeseries.size();
16 if (n < 10) {
17 if (n > 0) {
18 double mean = 0;
19 for (double x : timeseries)
20 mean += x;
21 mean /= static_cast<double>(n);
22 double v = 0;
23 for (double x : timeseries) {
24 double d = x - mean;
25 v += d * d;
26 }
27 v /= static_cast<double>(n);
28 tau_out.push_back(tau0);
29 sigma2_out.push_back(v);
30 }
31 return;
32 }
33
34 size_t max_m = std::max<size_t>(2, n / 4);
35 size_t m = 1;
36 while (m <= max_m && n >= 3 * m) {
37 size_t num_triplets = (n - 2 * m) / m;
38 if (num_triplets < 1)
39 break;
40
41 std::vector<double> clusters;
42 clusters.reserve(num_triplets + 3);
43 for (size_t i = 0; i < num_triplets + 2 && (i + 1) * m <= n; ++i) {
44 double sum = 0;
45 for (size_t k = 0; k < m; ++k)
46 sum += timeseries[i * m + k];
47 clusters.push_back(sum / static_cast<double>(m));
48 }
49 if (clusters.size() < 3)
50 break;
51 double acc = 0;
52 size_t cnt = 0;
53 for (size_t i = 0; i + 2 < clusters.size(); ++i) {
54 double d = clusters[i + 2] - 2.0 * clusters[i + 1] + clusters[i];
55 acc += d * d;
56 ++cnt;
57 }
58 if (cnt == 0)
59 break;
60 double sigma2 = 0.5 * (acc / static_cast<double>(cnt));
61 tau_out.push_back(static_cast<double>(m) * tau0);
62 sigma2_out.push_back(sigma2);
63 m = (m >= 2) ? m * 2 : m + 1;
64 }
65}
66
67NoiseCoefficients fit_noise_coefficients(const std::vector<double>& tau_arr,
68 const std::vector<double>& sigma2_arr) {
70 if (tau_arr.size() < 3 || sigma2_arr.size() != tau_arr.size()) {
71 return c;
72 }
73
74 const size_t n = tau_arr.size();
75 Eigen::MatrixXd X(static_cast<Eigen::Index>(n), 4);
76 Eigen::VectorXd y(static_cast<Eigen::Index>(n));
77 for (size_t i = 0; i < n; ++i) {
78 double t = tau_arr[i];
79 X(static_cast<Eigen::Index>(i), 0) = 1.0 / (2.0 * t);
80 X(static_cast<Eigen::Index>(i), 1) = 2.0 * std::log(2.0);
81 X(static_cast<Eigen::Index>(i), 2) = t / 6.0;
82 X(static_cast<Eigen::Index>(i), 3) = t * t / 20.0;
83 y(static_cast<Eigen::Index>(i)) = sigma2_arr[i];
84 }
85
86 Eigen::VectorXd beta = X.colPivHouseholderQr().solve(y);
87 c.Q = std::sqrt(std::max(beta(0), 1e-12));
88 c.B = std::sqrt(std::max(beta(1), 1e-12));
89 c.K = std::sqrt(std::max(beta(2), 1e-12));
90 c.R = std::sqrt(std::max(beta(3), 1e-12));
91
92 c.tau_min = std::sqrt(3.0 * c.Q * c.Q / (c.K * c.K + 1e-20));
93 if (c.K <= 1e-10)
94 c.tau_min = tau_arr[n / 2];
95 c.tau_min = std::clamp(c.tau_min, tau_arr.front(), tau_arr.back());
96 double tm = c.tau_min;
97 c.sigma_min = std::sqrt(c.Q * c.Q / (2.0 * tm) + 2.0 * std::log(2.0) * c.B * c.B +
98 c.K * c.K * tm / 6.0 + c.R * c.R * tm * tm / 20.0);
99 return c;
100}
101
102} // namespace calibration
103} // namespace fsw
Output smoothing for calibrated PT pressure, per sensor uid (ESP32 and LabJack T7 alike).
void compute_allan_variance(const std::vector< double > &timeseries, double tau0, std::vector< double > &tau_out, std::vector< double > &sigma2_out)
NoiseCoefficients fit_noise_coefficients(const std::vector< double > &tau_arr, const std::vector< double > &sigma2_arr)
Motorized ball valve position: raw ADC counts -> % open.
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
Noise coefficients from Allan variance fit (scripts/calibration/allan_variance.py).