Larus glider flight sensor system 3.9.2024
Software-In-The-Loop test and validation system
Loading...
Searching...
No Matches
NMEA_format.cpp
Go to the documentation of this file.
1/***********************************************************************/
25#include "NMEA_format.h"
26#include "ascii_support.h"
27#include "embedded_math.h"
28
29#define ANGLE_SCALE 1e-7f
30#define MPS_TO_NMPH 1.944f // 90 * 60 NM / 10000km * 3600 s/h
31#define RAD_TO_DEGREE_10 572.958f
32#define RAD_TO_DEGREE 57.2958f
33#define METER_TO_FEET 3.2808f
34#define MPS_TO_KMPH 3.6f
35
36ROM char HEX[]="0123456789ABCDEF";
37
38// ********* Generic stuff ************************************************
39inline char hex4( uint8_t data)
40{
41 return HEX[data];
42}
43
45char * NMEA_append_tail( char *p)
46 {
47 uint8_t checksum = 0;
48 assert( p[0] =='$');
49 for( p=p+1; *p && *p !='*'; ++p)
50 checksum ^= *p;
51 p[0] = '*';
52 p[1] = hex4(checksum >> 4);
53 p[2] = hex4(checksum & 0x0f);
54 p[3] = '\r';
55 p[4] = '\n';
56 p[5] = 0;
57 return p + 5;
58 }
59
63{
64 if( number < 0)
65 {
66 *s++='-';
67 number = -number;
68 }
69 if( number >= 1000)
70 {
71 s = format_integer( s, number / 1000);
72 number %= 1000;
73 }
74 *s++=HEX[number / 100]; // format 1 digit plus exactly 2 decimals
75 *s++='.';
76 number %= 100;
77 *s++=HEX[number / 10];
78 *s++=HEX[number % 10];
79 *s=0;
80 return s;
81}
82
86{
87 if( number < 0)
88 {
89 *s++='-';
90 number = -number;
91 }
92 if( number >= 100)
93 {
94 s = format_integer( s, number / 100);
95 number %= 100;
96 }
97 *s++=HEX[number / 10]; // format exactly 1 decimal
98 *s++='.';
99 *s++=HEX[number % 10];
100 *s=0;
101 return s;
102}
103
105void angle_format ( double angle, char posc, char negc, char * &p)
106{
107 bool pos = angle > 0.0f;
108 if (!pos)
109 angle = -angle;
110
111 int degree = (int) angle;
112 double minutes = (angle - (double)degree) * 60.0;
113
114 // add 3rd digit if required
115 if( degree >= 100)
116 {
117 *p++ = (char)(degree / 100 + '0');
118 degree %= 100;
119 }
120
121 // otherwise 2 digits fixed
122 *p++ = (char)(degree / 10 + '0');
123 *p++ = (char)(degree % 10 + '0');
124
125 int min = (int) minutes;
126 *p++ = (char)(min / 10 + '0');
127 *p++ = (char)(min % 10 + '0');
128
129 *p++ = '.';
130
131 minutes -= min;
132 minutes *= 100000;
133 min = (int) round(minutes);
134
135 p[4] = (char)(min % 10 + '0');
136 min /= 10;
137 p[3] = (char)(min % 10 + '0');
138 min /= 10;
139 p[2] = (char)(min % 10 + '0');
140 min /= 10;
141 p[1] = (char)(min % 10 + '0');
142 min /= 10;
143 p[0] = (char)(min % 10 + '0');
144
145 p += 5;
146
147 *p++ = ',';
148 *p++ = pos ? posc : negc;
149}
150
151void format_GNSS_timestamp(const coordinates_t &coordinates, char * &p)
152{
153 unsigned hundredth_seconds;
154 if( coordinates.nano < 0)
155 hundredth_seconds=0; // just ignore any (small) negative difference
156 else
157 hundredth_seconds=coordinates.nano / 10000000;
158
159 *p++ = (coordinates.hour) / 10 + '0';
160 *p++ = (coordinates.hour) % 10 + '0';
161 *p++ = (coordinates.minute) / 10 + '0';
162 *p++ = (coordinates.minute) % 10 + '0';
163 *p++ = (coordinates.second) / 10 + '0';
164 *p++ = (coordinates.second) % 10 + '0';
165 *p++ = '.';
166 *p++ = (char)(hundredth_seconds / 10 +'0');
167 *p++ = (char)(hundredth_seconds % 10 +'0');
168 *p++ = ',';
169}
170
171ROM char GPRMC[]="$GPRMC,";
172
174void format_RMC (const coordinates_t &coordinates, char * &p)
175{
176 char * line_start = p;
177 p = append_string( p, GPRMC);
178 format_GNSS_timestamp( coordinates, p);
179
180 *p++ = coordinates.sat_fix_type != 0 ? 'A' : 'V';
181 *p++ = ',';
182
183 angle_format (coordinates.latitude, 'N', 'S', p);
184 *p++ = ',';
185
186 angle_format (coordinates.longitude, 'E', 'W', p);
187 *p++ = ',';
188
189 float value = coordinates.speed_motion * MPS_TO_NMPH;
190
191 //Clipping to realistic values for a glider. Some ASCII functions crash if given to high values. TODO: fix
192 value = CLIP<float>(value, 0, (100.0f * MPS_TO_NMPH));
193
194 unsigned knots = (unsigned)(value * 10.0f + 0.5f);
195 *p++ = (char)(knots / 1000 + '0');
196 knots %= 1000;
197 *p++ = (char)(knots / 100 + '0');
198 knots %= 100;
199 *p++ = (char)(knots / 10 + '0');
200 *p++ = '.';
201 *p++ = (char)(knots % 10 + '0');
202 *p++ = ',';
203
204 float true_track = coordinates.heading_motion;
205 if( true_track < 0.0f)
206 true_track += 360.0f;
207 int angle_10 = (int) round(true_track * 10.0f);
208
209 *p++ = (char)(angle_10 / 1000 + '0');
210 angle_10 %= 1000;
211 *p++ = (char)(angle_10 / 100 + '0');
212 angle_10 %= 100;
213 *p++ = (char)(angle_10 / 10 + '0');
214 *p++ = '.';
215 *p++ = (char)(angle_10 % 10 + '0');
216
217 *p++ = ',';
218
219 *p++ = (coordinates.day) / 10 + '0';
220 *p++ = (coordinates.day) % 10 + '0';
221 *p++ = (coordinates.month) / 10 + '0';
222 *p++ = (coordinates.month) % 10 + '0';
223 *p++ = ((coordinates.year)%100) / 10 + '0';
224 *p++ = ((coordinates.year)%100) % 10 + '0';
225
226 p=append_string( p, ",,,A");
228}
229
230ROM char GPGGA[]="$GPGGA,";
231
233void format_GGA( const coordinates_t &coordinates, char * &p)
234{
235 char * line_start = p;
236 p = append_string( p, GPGGA);
237 format_GNSS_timestamp( coordinates, p);
238
239 angle_format (coordinates.latitude, 'N', 'S', p);
240 *p++ = ',';
241
242 angle_format (coordinates.longitude, 'E', 'W', p);
243 *p++ = ',';
244
245 *p++ = coordinates.sat_fix_type > 0 ? '1' : '0';
246 *p++ = ',';
247
248 *p++ = (coordinates.SATS_number) / 10 + '0';
249 *p++ = (coordinates.SATS_number) % 10 + '0';
250 *p++ = ',';
251
252 *p++ = '1'; // fake HDOP
253 *p++ = '.';
254 *p++ = '0';
255 *p++ = ',';
256
257 int32_t altitude_msl_dm = (int32_t)(coordinates.position[DOWN] * -10.0f);
259 *p++ = ',';
260 *p++ = 'M';
261 *p++ = ',';
262
263 int32_t geo_sep_10 = coordinates.geo_sep_dm;
265 *p++ = ',';
266 *p++ = 'M';
267 *p++ = ','; // no DGPS
268 *p++ = ',';
269 *p=0;
270
272}
273
274// ********* Larus-specific protocols *************************************
275
276ROM char PLARD[]="$PLARD,";
277
278void format_PLARD ( float density, char type, char * &p)
279{
280 char * line_start = p;
281 p = append_string( p, PLARD);
282 p = to_ascii_2_decimals( round( density * 1e5f), p); // units = g / m^3, * 100 to get 2 decimals
283 *p++ = ',';
284 *p++ = type;
285 *p=0;
286
288}
289
290ROM char PLARB[]="$PLARB,";
291
292void format_PLARB ( float voltage, char * &p)
293{
294 char * line_start = p;
295 p = append_string( p, PLARB);
296 p = to_ascii_2_decimals( round( voltage * 100.0f), p);
298}
299
300ROM char PLARA[]="$PLARA,";
301
302void format_PLARA ( float roll, float pitch, float yaw, char * &p)
303{
304 char * line_start = p;
305 p = append_string( p, PLARA);
306
308
309 p = append_string( p, ",");
311
312 if( yaw < 0.0f)
313 yaw += 6.2832f;
314 p = append_string( p, ",");
316
318}
319
320ROM char PLARW[]="$PLARW,";
321
323void format_PLARW ( float wind_north, float wind_east, char windtype, char * &p)
324{
325 char * line_start = p;
326 p = append_string( p, PLARW);
327
328 //Clipping to realistic values for a glider. Some ASCII functions crash if given to high values. TODO: fix
329 wind_north = CLIP<float>(wind_north, -50.0, 50.0);
330 wind_east = CLIP<float>(wind_east, -50.0, 50.0);
331
332 float direction;
333 // report WHERE the wind the comes from, instead of our wind speed vector, so negative sign
334 if( ( SQR(wind_east) + SQR( wind_north)) < SQR( NEGLECTABLE_WIND)) // avoid circling of neglectable wind
335 direction = 0.0f;
336 else
338
339 // map to 0..359 degrees
341 if( angle < 0)
342 angle += 360;
343 p=format_integer( p, angle);
344 *p++ = ',';
345
347 p=format_integer( p, speed);
348 *p++ = ',';
349
350 *p++ = windtype;
351
352 p = append_string( p, ",A"); // always report "valid" for the moment
354}
355
356ROM char PLARV[]="$PLARV,";
357
359void format_PLARV ( float variometer, float avg_variometer, float pressure_altitude, float TAS, char * &p)
360{
361 char * line_start = p;
362 p = append_string( p, PLARV);
363
364 //Clipping to realistic values for a glider. Some ASCII functions crash if given to high values. TODO: fix
365 variometer = CLIP<float>(variometer, -50.0, 50.0);
367 TAS = CLIP<float>(TAS, 0, 100);
368
369 p=to_ascii_2_decimals( round( variometer * 100.0f), p);
370 *p++ = ',';
371
372 p=to_ascii_2_decimals( round( avg_variometer * 100.0f), p);
373 *p++ = ',';
374
375 p=format_integer( p, (int) round( pressure_altitude));
376 *p++ = ',';
377
378 p=format_integer( p, (int) round( TAS * MPS_TO_KMPH));
379
381}
382
383ROM char PLARS[]="$PLARS,L,";
384ROM char PLARS_MC[]="MC,";
385ROM char PLARS_BAL[]="BAL,";
386ROM char PLARS_BUGS[]="BUGS,";
387ROM char PLARS_QNH[]="QNH,";
389void format_PLARS ( float value, PLARS_TYPES option, char * &p)
390{
391 char * line_start = p;
392 p = append_string( p, PLARS);
393 enum PLARS_TYPES type = option;
394
395 switch (type) {
396 case MC: //MC MacCready m/s (0.0 - 9.9)
397 p = append_string( p, PLARS_MC);
398 p=to_ascii_1_decimal(float32_t(value * 10.0), p);
399
400 break;
401 case BAL: //BAL Ballast (fraction of water ballast 0.000 - 1.000)
402 p = append_string( p, PLARS_BAL);
403 p=to_ascii_2_decimals(float32_t(value * 100.0), p);
404
405 break;
406 case BUGS: //BUGS Bugs in % (0 - 50)
407 p = append_string( p, PLARS_BUGS);
408 p=to_ascii_2_decimals(float32_t(value * 100.0), p);
409
410 break;
411 case QNH: //QNH QNH in hPa
412 p = append_string( p, PLARS_QNH);
413 p=to_ascii_2_decimals(float32_t(value * 100.0), p);
414
415 break;
416 default:
417 //NOTE: this shall not happen
418 break;
419 }
421}
422
424bool NMEA_checksum( const char *line)
425 {
426 uint8_t checksum = 0;
427 if( line[0]!='$')
428 return false;
429 const char * p;
430 for( p=line+1; *p && *p !='*'; ++p)
431 checksum ^= *p;
432 return ( (p[0] == '*') && hex4( checksum >> 4) == p[1]) && ( hex4( checksum & 0x0f) == p[2]) && (p[3] == 0);
433 }
434
437{
438 char *next = NMEA_buf.string + NMEA_buf.length;
439
440 // aircraft attitude
442 format_PLARA(output_data.euler.r, output_data.euler.p, output_data.euler.y, next);
443 else
445
446 // report instant and average total-energy-compensated variometer, pressure altitude, TAS
448 output_data.integrator_vario,
449 output_data.pressure_altitude,
450 output_data.TAS,
451 next);
452
453 // instant wind
454 format_PLARW (output_data.wind[NORTH], output_data.wind[EAST], 'I', next);
455
456// assert( next - NMEA_buf.string < string_buffer_t::BUFLEN);
457 NMEA_buf.length = next - NMEA_buf.string;
458}
459
462{
463 char *next = NMEA_buf.string + NMEA_buf.length;
464
465 // NMEA-format time, position, groundspeed, track data
467
468 // NMEA-format position report, sat number and GEO separation
470
471 // battery_voltage
472 format_PLARB( output_data.m.supply_voltage, next);
473
474 // air density
475 format_PLARD( output_data.air_density, 'M', next);
476
477 // average wind
478 format_PLARW (output_data.wind_average[NORTH], output_data.wind_average[EAST], 'A', next);
479
480// assert( next - NMEA_buf.string < string_buffer_t::BUFLEN);
481 NMEA_buf.length = next - NMEA_buf.string;
482}
@ DOWN
Definition AHRS.h:42
@ EAST
Definition AHRS.h:42
@ NORTH
Definition AHRS.h:42
#define NEGLECTABLE_WIND
ROM char PLARB[]
ROM char PLARS[]
ROM char PLARS_BUGS[]
char * to_ascii_2_decimals(int32_t number, char *s)
ROM char PLARS_BAL[]
char * NMEA_append_tail(char *p)
add end delimiter, evaluate and add checksum and add CR+LF
void format_PLARB(float voltage, char *&p)
#define RAD_TO_DEGREE
char * to_ascii_1_decimal(int32_t number, char *s)
bool NMEA_checksum(const char *line)
test a line for valid NMEA checksum
void format_PLARA(float roll, float pitch, float yaw, char *&p)
#define MPS_TO_KMPH
ROM char PLARV[]
void format_PLARS(float value, PLARS_TYPES option, char *&p)
format setting NMEA for MacCready, Ballast, Bugs, QNH
void format_GGA(const coordinates_t &coordinates, char *&p)
NMEA-format position report, sat number and GEO separation.
ROM char HEX[]
void format_PLARV(float variometer, float avg_variometer, float pressure_altitude, float TAS, char *&p)
TEK vario, average vario, pressure altitude and speed (TAS)
#define RAD_TO_DEGREE_10
void format_NMEA_string_fast(const output_data_t &output_data, string_buffer_t &NMEA_buf, bool horizon_available)
this procedure formats all our NMEA sequences
void format_NMEA_string_slow(const output_data_t &output_data, string_buffer_t &NMEA_buf)
this procedure formats all our NMEA sequences
ROM char PLARD[]
void format_PLARW(float wind_north, float wind_east, char windtype, char *&p)
format wind reporting NMEA sequence
ROM char PLARS_MC[]
void format_GNSS_timestamp(const coordinates_t &coordinates, char *&p)
void format_PLARD(float density, char type, char *&p)
char hex4(uint8_t data)
ROM char PLARW[]
ROM char PLARA[]
ROM char GPGGA[]
ROM char PLARS_QNH[]
ROM char GPRMC[]
void format_RMC(const coordinates_t &coordinates, char *&p)
NMEA-format time, position, groundspeed, track data.
void angle_format(double angle, char posc, char negc, char *&p)
append an angle in ASCII into a NMEA string
#define MPS_TO_NMPH
converters for NMEA string output
PLARS_TYPES
Definition NMEA_format.h:21
@ BUGS
Definition NMEA_format.h:24
@ BAL
Definition NMEA_format.h:23
@ MC
Definition NMEA_format.h:22
@ QNH
Definition NMEA_format.h:25
char * format_integer(char *s, int32_t value)
signed integer to ASCII returning the string end
Simple and fast ASCII converters.
char * append_string(char *target, const char *source)
basically: kind of strcat returning the pointer to the string-end
float s[2][6]
contains a string including it's length
Definition NMEA_format.h:14
mathematical vector of arbitrary type and size
Definition vector.h:40
defines platform-dependent algorithms and constants
float float32_t
#define ZERO
#define SQRT(x)
#define ATAN2(y, x)
#define ROM
#define SQR(x)
Contains all important data from the GNSS.
Definition GNSS.h:104
float heading_motion
ground track in degrees
Definition GNSS.h:108
float speed_motion
ground speed m/s
Definition GNSS.h:109
uint8_t second
Definition GNSS.h:122
uint8_t day
Definition GNSS.h:118
uint8_t hour
Definition GNSS.h:119
float3vector position
NED / meters.
Definition GNSS.h:105
uint8_t SATS_number
number of tracked satellites
Definition GNSS.h:123
uint8_t year
Definition GNSS.h:116
double latitude
latitude / degrees
Definition GNSS.h:113
int16_t geo_sep_dm
(WGS ellipsoid height - elevation MSL) in 0.1m units
Definition GNSS.h:130
uint8_t month
Definition GNSS.h:117
double longitude
longitude / degrees
Definition GNSS.h:114
uint8_t sat_fix_type
bit 0: SAT FIX, bit 1: SAT HEADING availale
Definition GNSS.h:124
uint8_t minute
Definition GNSS.h:121
combination of all input and output data in one structure
#define assert(x)
Definition vector.h:29