STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
ControllerLUT.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include <cstring>
6#include <fstream>
7#include <iostream>
8#include <sstream>
9
10namespace fsw {
11namespace control {
12
13namespace {
14constexpr uint8_t MAGIC[] = {'L', 'U', 'T', 'C'};
15constexpr uint8_t VERSION = 1;
16constexpr size_t MAX_AXES = 8;
17constexpr size_t MAX_OUTPUTS = 32;
18constexpr size_t MAX_NAME_LEN = 255;
19} // namespace
20
21bool ControllerLUT::load(const std::string& path) {
22 axes_.clear();
23 outputs_.clear();
24
25 std::ifstream f(path, std::ios::binary);
26 if (!f) {
27 std::cerr << "[ControllerLUT] Failed to open: " << path << std::endl;
28 return false;
29 }
30
31 uint8_t magic[4];
32 if (!f.read(reinterpret_cast<char*>(magic), 4) || std::memcmp(magic, MAGIC, 4) != 0) {
33 std::cerr << "[ControllerLUT] Invalid magic in " << path << std::endl;
34 return false;
35 }
36
37 uint8_t version, num_axes, num_outputs, reserved;
38 if (!f.read(reinterpret_cast<char*>(&version), 1) || version != VERSION) {
39 std::cerr << "[ControllerLUT] Unsupported version " << (int)version << std::endl;
40 return false;
41 }
42 if (!f.read(reinterpret_cast<char*>(&num_axes), 1) || num_axes > MAX_AXES) {
43 std::cerr << "[ControllerLUT] Invalid num_axes " << (int)num_axes << std::endl;
44 return false;
45 }
46 if (!f.read(reinterpret_cast<char*>(&num_outputs), 1) || num_outputs > MAX_OUTPUTS) {
47 std::cerr << "[ControllerLUT] Invalid num_outputs " << (int)num_outputs << std::endl;
48 return false;
49 }
50 if (!f.read(reinterpret_cast<char*>(&reserved), 1))
51 return false;
52
53 for (uint8_t a = 0; a < num_axes; ++a) {
54 Axis ax;
55 uint8_t name_len;
56 if (!f.read(reinterpret_cast<char*>(&name_len), 1))
57 return false;
58 ax.name.resize(std::min(static_cast<size_t>(name_len), MAX_NAME_LEN));
59 if (!f.read(&ax.name[0], ax.name.size()))
60 return false;
61 if (name_len > MAX_NAME_LEN)
62 f.ignore(name_len - MAX_NAME_LEN);
63
64 uint32_t grid_len;
65 if (!f.read(reinterpret_cast<char*>(&grid_len), 4))
66 return false;
67 ax.grid.resize(grid_len);
68 if (!f.read(reinterpret_cast<char*>(ax.grid.data()), grid_len * sizeof(double)))
69 return false;
70
71 axes_.push_back(std::move(ax));
72 }
73
74 for (uint8_t o = 0; o < num_outputs; ++o) {
75 Output out;
76 uint8_t name_len;
77 if (!f.read(reinterpret_cast<char*>(&name_len), 1))
78 return false;
79 out.name.resize(std::min(static_cast<size_t>(name_len), MAX_NAME_LEN));
80 if (!f.read(&out.name[0], out.name.size()))
81 return false;
82 if (name_len > MAX_NAME_LEN)
83 f.ignore(name_len - MAX_NAME_LEN);
84
85 out.shape.resize(axes_.size());
86 if (!f.read(reinterpret_cast<char*>(out.shape.data()), axes_.size() * sizeof(uint32_t)))
87 return false;
88
89 size_t total = 1;
90 for (uint32_t s : out.shape)
91 total *= s;
92 out.data.resize(total);
93 if (!f.read(reinterpret_cast<char*>(out.data.data()), total * sizeof(double)))
94 return false;
95
96 outputs_.push_back(std::move(out));
97 }
98
99 std::cout << "[ControllerLUT] Loaded " << path << " (" << axes_.size() << " axes, "
100 << outputs_.size() << " outputs)" << std::endl;
101 return true;
102}
103
104bool ControllerLUT::evaluate(const std::map<std::string, double>& point,
105 std::map<std::string, double>& out) const {
106 out.clear();
107 if (axes_.empty() || outputs_.empty())
108 return false;
109
110 const size_t n_axes = axes_.size();
111 if (n_axes > 20)
112 return false;
113
114 struct AxisInfo {
115 size_t lo, hi;
116 double w0, w1;
117 };
118 std::vector<AxisInfo> infos;
119 infos.reserve(n_axes);
120
121 for (const auto& ax : axes_) {
122 auto it = point.find(ax.name);
123 if (it == point.end()) {
124 std::cerr << "[ControllerLUT] Missing axis '" << ax.name << "' in point" << std::endl;
125 return false;
126 }
127 double x = it->second;
128 const auto& grid = ax.grid;
129 if (grid.empty())
130 return false;
131
132 size_t hi = std::lower_bound(grid.begin(), grid.end(), x) - grid.begin();
133 if (hi == 0)
134 hi = 1;
135 if (hi >= grid.size())
136 hi = grid.size() - 1;
137 size_t lo = hi - 1;
138
139 double x0 = grid[lo];
140 double x1 = grid[hi];
141 double t = (x1 == x0) ? 0.0 : (x - x0) / (x1 - x0);
142 t = std::clamp(t, 0.0, 1.0);
143 infos.push_back({lo, hi, 1.0 - t, t});
144 }
145
146 for (const auto& output : outputs_) {
147 std::vector<size_t> strides(n_axes);
148 strides[n_axes - 1] = 1;
149 for (int i = static_cast<int>(n_axes) - 2; i >= 0; --i)
150 strides[i] = strides[i + 1] * output.shape[i + 1];
151
152 double result = 0.0;
153 const size_t n_corners = 1ULL << n_axes;
154 for (size_t corner = 0; corner < n_corners; ++corner) {
155 double w = 1.0;
156 size_t flat_idx = 0;
157 for (size_t i = 0; i < n_axes; ++i) {
158 size_t idx = (corner >> i) & 1 ? infos[i].hi : infos[i].lo;
159 double wi = (corner >> i) & 1 ? infos[i].w1 : infos[i].w0;
160 w *= wi;
161 flat_idx += idx * strides[i];
162 }
163 if (w >= 1e-10 && flat_idx < output.data.size()) {
164 double v = output.data[flat_idx];
165 if (std::isfinite(v))
166 result += w * v;
167 }
168 }
169 out[output.name] = result;
170 }
171 return true;
172}
173
174} // namespace control
175} // namespace fsw
bool load(const std::string &path)
Load LUT from binary file.
std::vector< Axis > axes_
std::vector< Output > outputs_
bool evaluate(const std::map< std::string, double > &point, std::map< std::string, double > &out) const
Evaluate LUT at the given point.
static const uint8_t MAGIC[MAGIC_SIZE]
Definition main.cpp:31
*brief create JSON pointer *sa reference_tokens const string_t & a
Definition json.hpp:13877
return o
Definition json.hpp:13897
json_pointer result
Definition json.hpp:14064
Motorized ball valve position: raw ADC counts -> % open.