Larus glider flight sensor system 3.9.2024
Software-In-The-Loop test and validation system
Loading...
Searching...
No Matches
sensor_data_analyzer.cpp
Go to the documentation of this file.
1/***********************************************************************/
25#ifdef _WIN32
26# include "windows.h"
27#else
28# include <unistd.h>
29#endif
30#include <iostream>
31#include <fstream>
32#include <chrono>
33#include <thread>
34#include "stdio.h"
35#include "stdlib.h"
36#include "string.h"
37#include "time.h"
38#include "math.h"
39#include "data_structures.h"
40#include "persistent_data.h"
41#include "EEPROM_emulation.h"
42#include "organizer.h"
43#include "NMEA_format.h"
44#include <fenv.h>
45#include "CAN_output.h"
46#include "NMEA_format.h"
47#include "TCP_server.h"
48#include "USB_serial.h"
49#include "system_state.h"
51#include "ascii_support.h"
52#include "CAN_socket_driver.h"
53#include "CAN_gateway.h"
54#include "math.h"
55#include "random"
56
57using namespace std;
58
59uint32_t system_state // fake system state here in lack of hardware
61
62#define N_TWEAKS 6
63#define N_TRIALS 50
64#define TRIAL_STEPSIZE 0.003
65
66double randn()
67{
68 // Standard normal random variable
69 static std::default_random_engine rn_generator = std::default_random_engine();
70 static std::normal_distribution<double> standard_normal_dist{0.0, 1.0};
71
73}
74
75int
76main (int argc, char *argv[])
77{
78 bool do_optimize = false;
79 unsigned start_count;
80 unsigned stop_count;
81
82#ifndef _WIN32
83 // feenableexcept( FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
84 // don't enable UNDERFLOW as this can happen regularly when filter outputs decay
86#endif
87
88 if (argc == 2)
89 {
90 do_optimize = false;
91 stop_count = 1;
92 }
93 else if (argc != 4)
94 {
95 printf ("usage: %s infile.f37 count_start count_stop\n", argv[0]);
96 return -1;
97 }
98 else
99 {
100 start_count = atoi (argv[2]);
101 stop_count = atoi (argv[3]);
102 do_optimize = true;
103 }
104
106
108 if (!file.is_open ())
109 {
110 cout << "Unable to open file";
111 return -1;
112 }
113
114 // cut off file extension
115 char basename[100];
116 strcpy (basename, argv[1]);
117 char *dot = strchr (basename, '.');
118 if ((dot != 0) && (dot[1] == 'f')) // old format: filename.f37.EEPROM new: filename.EEPROM
119 *dot = 0; // cut off .f37 extension
120
121// try to read "config.EEPROM" first
122 char config_path[200];
124 char *slash_location = strrchr (config_path, '/');
125 *slash_location = 0;
126 strcat (config_path, "/config");
127
129 {
130 // try to read the EEPROM file accompanying the data file
132 {
133 cout << "Unable to open EEPROM file";
134 return -1;
135 }
136 }
137
139
141
142 streampos size = file.tellg ();
143
145 in_data = (observations_type*) new char[size];
146 unsigned records = size / sizeof(observations_type);
147
148 size_t outfile_size = records * sizeof(output_data_t);
150
151 file.seekg (0, ios::beg);
152 file.read ((char*) in_data, size);
153 file.close ();
154
155// ************************************************************
156
157 organizer.initialize_before_measurement ();
158
159 output_data[0].m = in_data[0].m;
160 output_data[0].c = in_data[0].c;
161
162 organizer.initialize_after_first_measurement (output_data[0]);
163 organizer.update_GNSS_data (output_data[0].c);
164 organizer.update_magnetic_induction_data (output_data[0].c.latitude,
165 output_data[0].c.longitude);
166
167 bool have_GNSS_fix = false;
168
169 int32_t nano = 0;
170 int delta_time;
171 unsigned counter_10Hz = 10;
172
173 float tweaks[N_TWEAKS] =
174 { 0.0f };
175 if (do_optimize)
176 {
177 // run algorithms until test sequence starts
178 for (unsigned count = 1; count < start_count; ++count)
179 {
182 organizer.on_new_pressure_data (output_data[count]);
183
184 if (have_GNSS_fix == false)
185 {
186 if (output_data[count].c.sat_fix_type > 0)
187 {
188 organizer.update_magnetic_induction_data (
189 output_data[count].c.latitude,
190 output_data[count].c.longitude);
191 have_GNSS_fix = true;
192 }
193 }
194
195 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
196 {
197 delta_time = output_data[count].c.nano - nano;
198 if (delta_time < 0)
199 delta_time += 1000000000;
200 nano = output_data[count].c.nano;
201
202 organizer.update_GNSS_data (output_data[count].c);
203 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
204 }
205
206 organizer.update_every_10ms (output_data[count], tweaks);
207
208 --counter_10Hz;
209 if (counter_10Hz == 0)
210 {
211 organizer.update_every_100ms (output_data[count]);
212 counter_10Hz = 10;
213 }
214 organizer.report_data (output_data[count]);
215 }
216
217 double average_error = 0.0f;
218 unsigned error_count = 0;
219
221
222 // run algorithms through segment of interest
223 // to initialize the quality indicator
224 for (unsigned count = start_count; count < stop_count; ++count)
225 {
228 organizer.on_new_pressure_data (output_data[count]);
229
230 if (have_GNSS_fix == false)
231 {
232 if (output_data[count].c.sat_fix_type > 0)
233 {
234 organizer.update_magnetic_induction_data (
235 output_data[count].c.latitude,
236 output_data[count].c.longitude);
237 have_GNSS_fix = true;
238 }
239 }
240
241 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
242 {
243 delta_time = output_data[count].c.nano - nano;
244 if (delta_time < 0)
245 delta_time += 1000000000;
246 nano = output_data[count].c.nano;
247
248 organizer.update_GNSS_data (output_data[count].c);
249 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
250 }
251
252 organizer.update_every_10ms (output_data[count], tweaks);
253
254 --counter_10Hz;
255 if (counter_10Hz == 0)
256 {
257 organizer.update_every_100ms (output_data[count]);
258 counter_10Hz = 10;
259 }
260 organizer.report_data (output_data[count]);
261
262 average_error += output_data[count].gyro_correction_power;
263// average_error += output_data[count].magnetic_disturbance;
264 ++error_count;
265 }
266
268 printf ("%e\n", reference_error);
269
270 for (unsigned trial = 0; trial < N_TRIALS; ++trial)
271 {
272 for (unsigned try_this = 0; try_this < N_TWEAKS; ++try_this)
273 {
275
276 float old_tweak = tweaks[try_this];
278 average_error = 0.0;
279 error_count = 0;
280
281 // run algorithms through segment of interest
282 for (unsigned count = start_count; count < stop_count; ++count)
283 {
286 organizer.on_new_pressure_data (output_data[count]);
287
288 if (have_GNSS_fix == false)
289 {
290 if (output_data[count].c.sat_fix_type > 0)
291 {
292 organizer.update_magnetic_induction_data (
293 output_data[count].c.latitude,
294 output_data[count].c.longitude);
295 have_GNSS_fix = true;
296 }
297 }
298
299 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
300 {
301 delta_time = output_data[count].c.nano - nano;
302 if (delta_time < 0)
303 delta_time += 1000000000;
304 nano = output_data[count].c.nano;
305
306 organizer.update_GNSS_data (output_data[count].c);
307 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
308 }
309
310 organizer.update_every_10ms (output_data[count], tweaks);
311
312 --counter_10Hz;
313 if (counter_10Hz == 0)
314 {
315 organizer.update_every_100ms (output_data[count]);
316 counter_10Hz = 10;
317 }
318 organizer.report_data (output_data[count]);
319
320 average_error += output_data[count].gyro_correction_power;
321 // average_error += output_data[count].magnetic_disturbance;
322 ++error_count;
323 }
324
327 {
329 printf ("%e ", reference_error);
330 for (unsigned i = 0; i < N_TWEAKS; ++i)
331 printf ("%f ", tweaks[i]);
332
333 printf ("\n");
334 }
335 else
336 tweaks[try_this] = old_tweak; // keep old setting
337 }
338 }
339
340#if 0 // many parameters optimized
341 for (unsigned trial = 0; trial < 100; ++trial)
342 {
343 for (unsigned try_this = 0; try_this < N_TWEAKS; ++try_this)
344 {
345 float old_tweak = tweaks[try_this];
346 tweaks[try_this] += randn () * 0.01f;
347 average_error = 0.0;
348 error_count = 0;
349
350 // run algorithms through segment of interest
351 for (unsigned count = start_count; count < stop_count; ++count)
352 {
355 organizer.on_new_pressure_data (output_data[count]);
356
357 if (have_GNSS_fix == false)
358 {
359 if (output_data[count].c.sat_fix_type > 0)
360 {
361 organizer.update_magnetic_induction_data (
362 output_data[count].c.latitude,
363 output_data[count].c.longitude);
364 have_GNSS_fix = true;
365 }
366 }
367
368 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
369 {
370 delta_time = output_data[count].c.nano - nano;
371 if (delta_time < 0)
372 delta_time += 1000000000;
373 nano = output_data[count].c.nano;
374
375 organizer.update_GNSS_data (output_data[count].c);
376 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
377 }
378
379 organizer.update_every_10ms (output_data[count], tweaks);
380
381 --counter_10Hz;
382 if (counter_10Hz == 0)
383 {
384 organizer.update_every_100ms (output_data[count]);
385 counter_10Hz = 10;
386 }
387 organizer.report_data (output_data[count]);
388
389 average_error += output_data[count].magnetic_disturbance;
390 ++error_count;
391 }
392
396 else
397 tweaks[try_this] = old_tweak; // keep old setting
398
399 printf ("%f ", reference_error);
400 for (unsigned i = 0; i < N_TWEAKS; ++i)
401 printf ("%f ", tweaks[i]);
402
403 printf ("\r");
404 }
405#endif
406 // run algorithms a last time through segment of interest with all parameters optimized
407 for (unsigned count = start_count; count < stop_count; ++count)
408 {
411 organizer.on_new_pressure_data (output_data[count]);
412
413 if (have_GNSS_fix == false)
414 {
415 if (output_data[count].c.sat_fix_type > 0)
416 {
417 organizer.update_magnetic_induction_data (
418 output_data[count].c.latitude,
419 output_data[count].c.longitude);
420 have_GNSS_fix = true;
421 }
422 }
423
424 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
425 {
426 delta_time = output_data[count].c.nano - nano;
427 if (delta_time < 0)
428 delta_time += 1000000000;
429 nano = output_data[count].c.nano;
430
431 organizer.update_GNSS_data (output_data[count].c);
432 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
433 }
434
435 organizer.update_every_10ms (output_data[count], tweaks);
436
437 --counter_10Hz;
438 if (counter_10Hz == 0)
439 {
440 organizer.update_every_100ms (output_data[count]);
441 counter_10Hz = 10;
442 }
443 organizer.report_data (output_data[count]);
444
445 average_error += output_data[count].gyro_correction_power;
446 ++error_count;
447 }
448 }
449 // run algorithms until end of data
450 for (unsigned count = stop_count; count < records; ++count)
451 {
454 organizer.on_new_pressure_data (output_data[count]);
455
456 if (have_GNSS_fix == false)
457 {
458 if (output_data[count].c.sat_fix_type > 0)
459 {
460 organizer.update_magnetic_induction_data (
461 output_data[count].c.latitude,
462 output_data[count].c.longitude);
463 have_GNSS_fix = true;
464 }
465 }
466
467 if (output_data[count].c.nano != nano) // 10 Hz by GNSS
468 {
469 delta_time = output_data[count].c.nano - nano;
470 if (delta_time < 0)
471 delta_time += 1000000000;
472 nano = output_data[count].c.nano;
473
474 organizer.update_GNSS_data (output_data[count].c);
475 counter_10Hz = 1; // synchronize the 10Hz processing as early as new data are observed
476 }
477
478 organizer.update_every_10ms (output_data[count], tweaks);
479
480 --counter_10Hz;
481 if (counter_10Hz == 0)
482 {
483 organizer.update_every_100ms (output_data[count]);
484 counter_10Hz = 10;
485 }
486 organizer.report_data (output_data[count]);
487 }
488
489 char buf[200];
490 char ascii_len[10];
491 sprintf (ascii_len, "%d", (int) (sizeof(output_data_t) / sizeof(float)));
492 strcpy (buf, argv[1]);
493 strcat (buf, ".f");
494 strcat (buf, ascii_len);
495
497 if (outfile.is_open ())
498 {
499 outfile.write ((const char*) output_data,
500 records * sizeof(output_data_t));
501 outfile.close ();
502 }
503
504 char *path_end = strrchr (buf, '/');
505 *path_end = 0;
506 write_EEPROM_dump (buf); // make new magnetic data permanent
507
508 delete[] in_data;
509 delete[] output_data;
510}
511
514{
515 return;
516
518 char buffer[50];
519 char *next = buffer;
520
521 printf (type == 'm' ? "\nMagnetic:\n" : "\nSatellite:\n");
522
523 for (unsigned i = 0; i < 3; ++i)
524 {
525 char *next = buffer;
526 next = my_ftoa (next, magnetic_induction_report.calibration[i].offset);
527 *next++ = ' ';
528 next = my_ftoa (next, magnetic_induction_report.calibration[i].scale);
529 *next++ = ' ';
530 next = my_ftoa (next,
531 SQRT(magnetic_induction_report.calibration[i].variance));
532 *next++ = ' ';
533 *next++ = 0;
534 printf ("%s\t", buffer);
535 }
536 printf ("\n");
537}
538
539bool CAN_gateway_poll(CANpacket&, unsigned int)
540{
541 return false; // presently just an empty stub
542}
Portable interface to some CAN-bus driver.
I/O over the generic Linux CANsocket interface.
int read_EEPROM_file(char *basename)
bool write_EEPROM_dump(char *basename)
Replacement on the PC for the nonvolatile memory of the micro-controller.
converters for NMEA string output
TCP server to feed XCsoar with flight data.
Interface to USB -> RS232 (interface)
char * my_ftoa(char *target, float value)
Simple and fast ASCII converters.
basic CAN packet type
set of algorithms and data to be used by Larus flight sensor
Definition organizer.h:35
mathematical vector of arbitrary type and size
Definition vector.h:40
vector(void)
Definition vector.h:45
#define SQRT(x)
combine algorithms to be used by the flight sensor
void ensure_EEPROM_parameter_integrity(void)
definitions for persistent data in EEPROM or config. file
double randn()
#define N_TWEAKS
int main(int argc, char *argv[])
void report_magnetic_calibration_has_changed(magnetic_induction_report_t *p_magnetic_induction_report, char type)
#define TRIAL_STEPSIZE
bool CAN_gateway_poll(CANpacket &, unsigned int)
#define N_TRIALS
uint32_t system_state
bits collected from availability_bits
helper struct containing magnetic calibration data
this structure contains all the observations from all sensors and the GNSS-receiver
combination of all input and output data in one structure
@ GNSS_AVAILABLE
@ MS5611_STATIC_AVAILABLE
@ PITOT_SENSOR_AVAILABLE
@ MTI_SENSOR_AVAILABE