REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
cluster.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
4
5#include "carrier/cluster.h"
6#include "bus/bus.h"
7#include "utils/logging.h"
8#include "utils/running_avg.h"
9
10#include "block/cblock.h"
11#include "block/iblock.h"
12#include "block/mblock.h"
13#include "block/ublock.h"
14
15FLASHMEM std::array<blocks::FunctionBlock *, 6> platform::Cluster::get_blocks() const {
16 return {m0block, m1block, ublock, cblock, iblock, shblock};
17}
18
19FLASHMEM bool platform::Cluster::init() {
20 LOG(ANABRID_DEBUG_INIT, __PRETTY_FUNCTION__);
21
22 // Dynamically detect installed blocks
23 // Check if a block is already set, which may happen with a special constructor in the future
24 LOG(ANABRID_DEBUG_INIT, "Detecting installed blocks...");
25 if (!m0block) {
26 m0block = entities::detect<blocks::MBlock>(bus::idx_to_addr(cluster_idx, bus::M0_BLOCK_IDX, 0));
27 if (!m0block)
28 LOG(ANABRID_DEBUG_INIT, "Warning: M0-block is missing or unknown.");
29 }
30 if (!m1block) {
31 m1block = entities::detect<blocks::MBlock>(bus::idx_to_addr(cluster_idx, bus::M1_BLOCK_IDX, 0));
32 if (!m1block)
33 LOG(ANABRID_DEBUG_INIT, "Warning: M1-block is missing or unknown.");
34 }
35 if (!m0block and !m1block)
36 LOG(ANABRID_DEBUG_INIT, "Error: Both M0 and M1-blocks are missing or unknown.");
37
38 if (!ublock) {
39 ublock = entities::detect<blocks::UBlock>(bus::idx_to_addr(cluster_idx, bus::U_BLOCK_IDX, 0));
40 if (!ublock)
41 LOG_ERROR("Error: U-block is missing or unknown.");
42 }
43 if (!cblock) {
44 cblock = entities::detect<blocks::CBlock>(bus::idx_to_addr(cluster_idx, bus::C_BLOCK_IDX, 0));
45 if (!cblock)
46 LOG_ERROR("Error: C-block is missing or unknown.");
47 }
48 if (!iblock) {
49 iblock = entities::detect<blocks::IBlock>(bus::idx_to_addr(cluster_idx, bus::I_BLOCK_IDX, 0));
50 if (!iblock)
51 LOG_ERROR("Error: I-block is missing or unknown.");
52 }
53 if (!shblock) {
54 shblock = entities::detect<blocks::SHBlock>(bus::idx_to_addr(cluster_idx, bus::SH_BLOCK_IDX, 0));
55 if (!shblock)
56 LOG_ERROR("Error: SH-block is missing or unknown.");
57 }
58
59 LOG(ANABRID_DEBUG_INIT, "Initialising detected blocks...");
60 for (auto block : get_blocks()) {
61 if (block && !block->init())
62 return false;
63 }
64 LOG(ANABRID_DEBUG_INIT, "Cluster initialized.");
65 reset(entities::ResetAction::EVERYTHING);
66 return true;
67}
68
69FLASHMEM platform::Cluster::Cluster(uint8_t cluster_idx)
70 : entities::Entity(std::to_string(cluster_idx)), cluster_idx(cluster_idx) {
71 classifier.class_enum = CLASS_;
72}
73
74FLASHMEM bool platform::Cluster::calibrate_offsets() {
75 LOG_ANABRID_DEBUG_CALIBRATION("Calibrating offsets");
76 if (!ublock or !shblock)
77 return false; // Fatal error preventing any further regular operation in the system
78
79 auto old_transmission_modes = ublock->get_all_transmission_modes();
80
81 ublock->change_all_transmission_modes(blocks::UBlock::Transmission_Mode::GROUND);
82 if (!ublock->write_to_hardware())
83 return false; // Fatal error preventing any further regular operation in the system
84
85 delay(10);
86 shblock->compensate_hardware_offsets();
87
88 ublock->change_all_transmission_modes(old_transmission_modes);
89 if (!ublock->write_to_hardware())
90 return false; // Fatal error preventing any further regular operation in the system
91
92 return true;
93}
94
95FLASHMEM bool platform::Cluster::calibrate_routes() {
96 bool success = true;
97 // CARE: This function assumes that certain preparations have been made, see Carrier::calibrate.
98
99 // Save current U-block transmission modes and set them to zero
100 LOG_ANABRID_DEBUG_CALIBRATION("Starting calibration");
101 auto old_transmission_modes = ublock->get_all_transmission_modes();
102 auto old_reference_magnitude = ublock->get_reference_magnitude();
103
104 // Save C-Block factors
105 auto old_c_block_factors = cblock->get_factors();
106 cblock->set_factors({});
107
108 // Actually write to hardware
109 LOG_ANABRID_DEBUG_CALIBRATION("Reset c-block");
110 if (!cblock->write_to_hardware())
111 return false; // Fatal error preventing any further regular operation in the system
112
113 auto old_i_block_upscaling = iblock->get_upscales();
114 iblock->reset_upscaling();
115 if (!iblock->write_to_hardware())
116 return false; // Fatal error preventing any further regular operation in the system
117
118 // Next, we iterate through all connections on the I-block and calibrate each lane individually.
119 // This is suboptimal, as in principle we could measure 8 gain corrections at the same time.
120 // Iterating over I-block connections is easier than iterating over U-block outputs.
121 for (auto i_out_idx : blocks::IBlock::OUTPUT_IDX_RANGE()) {
122 for (auto i_in_idx : blocks::IBlock::INPUT_IDX_RANGE()) {
123 // Only do something is this lane is used in the original I-block configuration
124 if (!iblock->is_connected(i_in_idx, i_out_idx))
125 continue;
126
127 // If we don't have a complete connection through the u block, we don't need / we can't calibrate this
128 // route from here
129 if (!ublock->is_output_connected(i_in_idx))
130 continue;
131
132 LOG_ANABRID_DEBUG_CALIBRATION("Calibrating connection: ");
133 LOG_ANABRID_DEBUG_CALIBRATION(i_in_idx);
134 LOG_ANABRID_DEBUG_CALIBRATION(i_out_idx);
135
136 // Depending on whether upscaling is enabled for this lane, we apply +1 or +0.1 reference
137 // This is done on all lanes (but no other I-block connection exists, so no other current flows)
138 bool upscaled_channel = iblock->get_upscaling(i_in_idx);
139 ublock->change_all_transmission_modes(blocks::UBlock::Transmission_Mode::POS_REF);
140 ublock->change_reference_magnitude(upscaled_channel ? blocks::UBlock::Reference_Magnitude::ONE_TENTH
141 : blocks::UBlock::Reference_Magnitude::ONE);
142 // Actually write to hardware
143 if (!ublock->write_to_hardware())
144 return false; // Fatal error preventing any further regular operation in the system
145
146 // Allow this connection to go up to full scale. Those values are allways legal so we can ignore the
147 // return value
148 (void)cblock->set_factor(i_in_idx, 1.0f);
149 (void)cblock->set_gain_correction(i_in_idx, 1.0f);
150 // Actually write to hardware
151 if (!cblock->write_to_hardware())
152 return false; // Fatal error preventing any further regular operation in the system
153
154 // Calibrate offsets for this specific route
155 if (!calibrate_offsets())
156 return false; // Fatal error preventing any further regular operation in the system
157
158 // Change SH-block into gain mode and select correct gain channel group
159 if (i_out_idx < 8)
160 shblock->set_state(blocks::SHState::GAIN_ZERO_TO_SEVEN);
161 else
162 shblock->set_state(blocks::SHState::GAIN_EIGHT_TO_FIFTEEN);
163 if (!shblock->write_to_hardware())
164 return false; // Fatal error preventing any further regular operation in the system
165
166 // Chill for a bit
167 delay(10);
168
169 // Measure gain output
170 auto m_adc = daq::average(daq::sample, 4, 10)[i_out_idx % 8];
171 LOG_ANABRID_DEBUG_CALIBRATION(m_adc);
172 // Calculate necessary gain correction
173 auto gain_correction = 1.0f / m_adc;
174 LOG_ANABRID_DEBUG_CALIBRATION(gain_correction);
175 // Set gain correction on C-block, which will automatically get applied when writing to hardware
176 if (!cblock->set_gain_correction(i_in_idx, gain_correction)) {
177 LOG_ANABRID_DEBUG_CALIBRATION("Gain correction could not be set as it is out of range. Resetting this "
178 "channel to default corretion");
179 (void)cblock->set_gain_correction(i_in_idx, 1.0f);
180 success = false;
181 }
182
183 // Deactivate this lane again
184 (void)cblock->set_factor(i_in_idx, 0.0f);
185 if (!cblock->write_to_hardware()) // This write_to_hardware could be left out, but it's a nice safety
186 // measure
187 return false; // Fatal error preventing any further regular operation in the system
188 LOG_ANABRID_DEBUG_CALIBRATION(" ");
189 }
190 }
191
192 // Restore C-block factors
193 cblock->set_factors(old_c_block_factors);
194 LOG_ANABRID_DEBUG_CALIBRATION("Restoring c-block");
195 if (!cblock->write_to_hardware())
196 return false; // Fatal error preventing any further regular operation in the system
197
198 // Calibrate offsets again, since they have been changed by correcting the coefficients
199
200 // Restore original U-block transmission modes and reference
201 ublock->change_all_transmission_modes(old_transmission_modes);
202 ublock->change_reference_magnitude(old_reference_magnitude);
203 // Write them to hardware
204 LOG_ANABRID_DEBUG_CALIBRATION("Restoring u-block");
205 if (!ublock->write_to_hardware())
206 return false; // Fatal error preventing any further regular operation in the system
207
208 iblock->set_upscaling(old_i_block_upscaling);
209 if (!iblock->write_to_hardware()) {
210 return false; // Fatal error preventing any further regular operation in the system
211 }
212
213 if (!calibrate_offsets())
214 return false; // Fatal error preventing any further regular operation in the system
215
216 return success;
217}
218
219FLASHMEM utils::status platform::Cluster::write_to_hardware() {
220 for (auto block : get_blocks()) {
221 if (block)
222 if (!block->write_to_hardware()) {
223 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
224 return utils::status::failure();
225 }
226 }
227 return utils::status::success();
228}
229
230FLASHMEM bool platform::Cluster::route(uint8_t u_in, uint8_t u_out, float c_factor, uint8_t i_out) {
231 if (fabs(c_factor) > 1.0f) {
232 c_factor = c_factor * 0.1f;
233 iblock->set_upscaling(u_out, true);
234 } else
235 iblock->set_upscaling(u_out, false);
236
237 if (!ublock->connect(u_in, u_out))
238 return false;
239 if (!cblock->set_factor(u_out, c_factor))
240 return false;
241 if (!iblock->connect(u_out, i_out))
242 return false;
243 return true;
244}
245
246FLASHMEM bool platform::Cluster::add_constant(blocks::UBlock::Transmission_Mode signal_type, uint8_t u_out,
247 float c_factor, uint8_t i_out) {
248 if (fabs(c_factor) > 1.0f) {
249 c_factor = c_factor * 0.1f;
250 iblock->set_upscaling(u_out, true);
251 } else
252 iblock->set_upscaling(u_out, false);
253
254 if (!ublock->connect_alternative(signal_type, u_out))
255 return false;
256 if (!cblock->set_factor(u_out, c_factor))
257 return false;
258 if (!iblock->connect(u_out, i_out))
259 return false;
260 return true;
261}
262
263FLASHMEM bool platform::Cluster::route_in_external(uint8_t in, uint8_t i_out) {
264 if (in > 7)
265 return false;
266
267 return iblock->connect(in + 24, i_out);
268}
269
270FLASHMEM bool platform::Cluster::route_out_external(uint8_t u_in, uint8_t out, float c_factor) {
271 if (out > 7)
272 return false;
273
274 if (!ublock->connect(u_in, out + 24))
275 return false;
276 return cblock->set_factor(out + 24, c_factor);
277}
278
279FLASHMEM void platform::Cluster::reset(entities::ResetAction action) {
280 for (auto block : get_blocks()) {
281 if (block)
282 block->reset(action);
283 }
284}
285
286FLASHMEM entities::Entity *platform::Cluster::get_child_entity(const std::string &child_id) {
287 if (child_id == "M0")
288 return m0block;
289 else if (child_id == "M1")
290 return m1block;
291 else if (child_id == "U")
292 return ublock;
293 else if (child_id == "C")
294 return cblock;
295 else if (child_id == "I")
296 return iblock;
297 else if (child_id == "SH")
298 return shblock;
299 return nullptr;
300}
301
302FLASHMEM utils::status platform::Cluster::config_self_from_json(JsonObjectConst cfg) {
303#ifdef ANABRID_DEBUG_ENTITY_CONFIG
304 Serial.println(__PRETTY_FUNCTION__);
305#endif
306 // Cluster has no own configuration parameters currently
307 // TODO: Have an option to fail on unexpected configuration
308 return utils::status::success();
309}
310
311FLASHMEM std::vector<entities::Entity *> platform::Cluster::get_child_entities() {
312#ifdef ANABRID_DEBUG_ENTITY_CONFIG
313 Serial.println(__PRETTY_FUNCTION__);
314#endif
315 return {m0block, m1block, ublock, cblock, iblock, shblock};
316}
317
318FLASHMEM uint8_t platform::Cluster::get_cluster_idx() const { return cluster_idx; }
static constexpr int success
Definition flasher.cpp:275