MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_sol.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_sol.c : solution I/O functions
3 *
4 * Copyright (C) 2026 H.SHIONO (MRTKLIB Project)
5 * Copyright (C) 2023-2025 Cabinet Office, Japan
6 * Copyright (C) 2024-2025 Lighthouse Technology & Consulting Co. Ltd.
7 * Copyright (C) 2023-2025 Japan Aerospace Exploration Agency
8 * Copyright (C) 2023-2025 TOSHIBA ELECTRONIC TECHNOLOGIES CORPORATION
9 * Copyright (C) 2015- Mitsubishi Electric Corp.
10 * Copyright (C) 2014 Geospatial Information Authority of Japan
11 * Copyright (C) 2014 T.SUZUKI
12 * Copyright (C) 2007-2023 T.TAKASU
13 *
14 * SPDX-License-Identifier: BSD-2-Clause
15 *----------------------------------------------------------------------------*/
16#include "mrtklib/mrtk_sol.h"
17
18#include <ctype.h>
19#include <math.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "mrtklib/mrtk_coords.h"
24#include "mrtklib/mrtk_sys.h"
25#include "mrtklib/mrtk_trace.h"
26
27/*--- local constants (duplicated to avoid rtklib.h dependency) -------------*/
28#define SYS_NONE 0x00
29#define SYS_GPS 0x01
30#define SYS_SBS 0x02
31#define SYS_GLO 0x04
32#define SYS_GAL 0x08
33#define SYS_QZS 0x10
34#define SYS_CMP 0x20
35#define SYS_IRN 0x40
36
37static const double PI = 3.1415926535897932;
38static const double D2R = 3.1415926535897932 / 180.0;
39static const double R2D = 180.0 / 3.1415926535897932;
40static const double RE_WGS84 = 6378137.0;
41static const double FE_WGS84 = 1.0 / 298.257223563;
42
43#define COMMENTH "%"
44#define MSG_DISCONN "$DISCONNECT"
45
46/*--- forward declarations for legacy functions resolved at link time -------*/
47extern double dms2deg(const double* dms);
48extern void deg2dms(double deg, double* dms, int ndec);
49extern double norm(const double* a, int n);
50extern void covecef(const double* pos, const double* Q, double* P);
51extern void enu2ecef(const double* pos, const double* e, double* r);
52extern gtime_t gpst2time(int week, double sec);
53extern double geoidh(const double* pos);
54
55/* constants and macros ------------------------------------------------------*/
56
57#define SQR(x) ((x) < 0.0 ? -(x) * (x) : (x) * (x))
58#define SQRT(x) ((x) < 0.0 || (x) != (x) ? 0.0 : sqrt(x))
59
60#define NMEA_TID "GN" /* NMEA talker ID for RMC and GGA sentences */
61#define MAXFIELD 64 /* max number of fields in a record */
62#define MAXNMEA 256 /* max length of nmea sentence */
63
64#define KNOT2M 0.514444444 /* m/knot */
65
66static const int nmea_sys[] = {/* NMEA systems */
68static const char* nmea_tid[] = {/* NMEA talker IDs [2] */
69 "GP", "GL", "GA", "GB", "GQ", "GI", ""};
70static const int nmea_sid[] = {/* NMEA system IDs [1] table 21 */
71 1, 2, 3, 4, 5, 6, 0};
72static const int nmea_solq[] = {/* NMEA GPS quality indicator [1] */
73 /* 0=Fix not available or invalidi */
74 /* 1=GPS SPS Mode, fix valid */
75 /* 2=Differential GPS, SPS Mode, fix valid */
76 /* 3=GPS PPS Mode, fix valid */
77 /* 4=Real Time Kinematic. System used in RTK mode with fixed integers */
78 /* 5=Float RTK. Satellite system used in RTK mode, floating integers */
79 /* 6=Estimated (dead reckoning) Mode */
80 /* 7=Manual Input Mode */
81 /* 8=Simulation Mode */
84/* solution option to field separator ----------------------------------------*/
85static const char* opt2sep(const solopt_t* opt) {
86 if (!*opt->sep) {
87 return " ";
88 } else if (!strcmp(opt->sep, "\\t")) {
89 return "\t";
90 }
91 return opt->sep;
92}
93/* separate fields -----------------------------------------------------------*/
94static int tonum(char* buff, const char* sep, double* v) {
95 int n, len = (int)strlen(sep);
96 char *p, *q;
97
98 for (p = buff, n = 0; n < MAXFIELD; p = q + len) {
99 if ((q = strstr(p, sep))) {
100 *q = '\0';
101 }
102 if (*p) {
103 v[n++] = atof(p);
104 }
105 if (!q) {
106 break;
107 }
108 }
109 return n;
110}
111/* sqrt of covariance --------------------------------------------------------*/
112static double sqvar(double covar) { return covar < 0.0 ? -sqrt(-covar) : sqrt(covar); }
113/* convert ddmm.mm in nmea format to deg -------------------------------------*/
114static double dmm2deg(double dmm) { return floor(dmm / 100.0) + fmod(dmm, 100.0) / 60.0; }
115/* convert time in nmea format to time ---------------------------------------*/
116static void septime(double t, double* t1, double* t2, double* t3) {
117 *t1 = floor(t / 10000.0);
118 t -= *t1 * 10000.0;
119 *t2 = floor(t / 100.0);
120 *t3 = t - *t2 * 100.0;
121}
122/* solution to covariance ----------------------------------------------------*/
123static void soltocov(const sol_t* sol, double* P) {
124 P[0] = sol->qr[0]; /* xx or ee */
125 P[4] = sol->qr[1]; /* yy or nn */
126 P[8] = sol->qr[2]; /* zz or uu */
127 P[1] = P[3] = sol->qr[3]; /* xy or en */
128 P[5] = P[7] = sol->qr[4]; /* yz or nu */
129 P[2] = P[6] = sol->qr[5]; /* zx or ue */
130}
131/* covariance to solution ----------------------------------------------------*/
132static void covtosol(const double* P, sol_t* sol) {
133 sol->qr[0] = (float)P[0]; /* xx or ee */
134 sol->qr[1] = (float)P[4]; /* yy or nn */
135 sol->qr[2] = (float)P[8]; /* zz or uu */
136 sol->qr[3] = (float)P[1]; /* xy or en */
137 sol->qr[4] = (float)P[5]; /* yz or nu */
138 sol->qr[5] = (float)P[2]; /* zx or ue */
139}
140/* solution to velocity covariance -------------------------------------------*/
141static void soltocov_vel(const sol_t* sol, double* P) {
142 P[0] = sol->qv[0]; /* xx */
143 P[4] = sol->qv[1]; /* yy */
144 P[8] = sol->qv[2]; /* zz */
145 P[1] = P[3] = sol->qv[3]; /* xy */
146 P[5] = P[7] = sol->qv[4]; /* yz */
147 P[2] = P[6] = sol->qv[5]; /* zx */
148}
149/* velocity covariance to solution -------------------------------------------*/
150static void covtosol_vel(const double* P, sol_t* sol) {
151 sol->qv[0] = (float)P[0]; /* xx */
152 sol->qv[1] = (float)P[4]; /* yy */
153 sol->qv[2] = (float)P[8]; /* zz */
154 sol->qv[3] = (float)P[1]; /* xy */
155 sol->qv[4] = (float)P[5]; /* yz */
156 sol->qv[5] = (float)P[2]; /* zx */
157}
158/* decode NMEA RMC (Recommended Minumum Specific GNSS Data) sentence ---------*/
159static int decode_nmearmc(char** val, int n, sol_t* sol) {
160 double tod = 0.0, lat = 0.0, lon = 0.0, vel = 0.0, dir = 0.0, date = 0.0, ang = 0.0, ep[6];
161 double pos[3] = {0};
162 char act = ' ', ns = 'N', ew = 'E', mew = 'E', mode = 'A';
163 int i;
164
165 trace(NULL, 4, "decode_nmearmc: n=%d\n", n);
166
167 for (i = 0; i < n; i++) {
168 switch (i) {
169 case 0:
170 tod = atof(val[i]);
171 break; /* time in utc (hhmmss) */
172 case 1:
173 act = *val[i];
174 break; /* A=active,V=void */
175 case 2:
176 lat = atof(val[i]);
177 break; /* latitude (ddmm.mmm) */
178 case 3:
179 ns = *val[i];
180 break; /* N=north,S=south */
181 case 4:
182 lon = atof(val[i]);
183 break; /* longitude (dddmm.mmm) */
184 case 5:
185 ew = *val[i];
186 break; /* E=east,W=west */
187 case 6:
188 vel = atof(val[i]);
189 break; /* speed (knots) */
190 case 7:
191 dir = atof(val[i]);
192 break; /* track angle (deg) */
193 case 8:
194 date = atof(val[i]);
195 break; /* date (ddmmyy) */
196 case 9:
197 ang = atof(val[i]);
198 break; /* magnetic variation */
199 case 10:
200 mew = *val[i];
201 break; /* E=east,W=west */
202 case 11:
203 mode = *val[i];
204 break; /* mode indicator (>nmea 2) */
205 /* A=autonomous,D=differential */
206 /* E=estimated,N=not valid,S=simulator */
207 }
208 }
209 if ((act != 'A' && act != 'V') || (ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) {
210 trace(NULL, 3, "invalid nmea rmc format\n");
211 return 0;
212 }
213 pos[0] = (ns == 'S' ? -1.0 : 1.0) * dmm2deg(lat) * D2R;
214 pos[1] = (ew == 'W' ? -1.0 : 1.0) * dmm2deg(lon) * D2R;
215 septime(date, ep + 2, ep + 1, ep);
216 septime(tod, ep + 3, ep + 4, ep + 5);
217 ep[0] += ep[0] < 80.0 ? 2000.0 : 1900.0;
218 sol->time = utc2gpst(epoch2time(ep));
219 pos2ecef(pos, sol->rr);
220 sol->stat = mode == 'D' ? SOLQ_DGPS : SOLQ_SINGLE;
221 sol->ns = 0;
222
223 sol->type = 0; /* position type = xyz */
224
225 trace(NULL, 5, "decode_nmearmc: %s rr=%.3f %.3f %.3f stat=%d ns=%d vel=%.2f dir=%.0f ang=%.0f mew=%c mode=%c\n",
226 time_str(sol->time, 0), sol->rr[0], sol->rr[1], sol->rr[2], sol->stat, sol->ns, vel, dir, ang, mew, mode);
227
228 return 2; /* update time */
229}
230/* decode NMEA ZDA (Time and Date) sentence ----------------------------------*/
231static int decode_nmeazda(char** val, int n, sol_t* sol) {
232 double tod = 0.0, ep[6] = {0};
233 int i;
234
235 trace(NULL, 4, "decode_nmeazda: n=%d\n", n);
236
237 for (i = 0; i < n; i++) {
238 switch (i) {
239 case 0:
240 tod = atof(val[i]);
241 break; /* time in utc (hhmmss) */
242 case 1:
243 ep[2] = atof(val[i]);
244 break; /* day (0-31) */
245 case 2:
246 ep[1] = atof(val[i]);
247 break; /* mon (1-12) */
248 case 3:
249 ep[0] = atof(val[i]);
250 break; /* year */
251 }
252 }
253 septime(tod, ep + 3, ep + 4, ep + 5);
254 sol->time = utc2gpst(epoch2time(ep));
255 sol->ns = 0;
256
257 trace(NULL, 5, "decode_nmeazda: %s\n", time_str(sol->time, 0));
258
259 return 2; /* update time */
260}
261/* decode NMEA GGA (Global Positioning System Fix Data) sentence -------------*/
262static int decode_nmeagga(char** val, int n, sol_t* sol) {
263 gtime_t time;
264 double tod = 0.0, lat = 0.0, lon = 0.0, hdop = 0.0, alt = 0.0, msl = 0.0, ep[6], tt;
265 double pos[3] = {0}, age = 0.0;
266 char ns = 'N', ew = 'E', ua = ' ', um = ' ';
267 int i, solq = 0, nrcv = 0;
268
269 trace(NULL, 4, "decode_nmeagga: n=%d\n", n);
270
271 for (i = 0; i < n; i++) {
272 switch (i) {
273 case 0:
274 tod = atof(val[i]);
275 break; /* UTC of position (hhmmss) */
276 case 1:
277 lat = atof(val[i]);
278 break; /* Latitude (ddmm.mmm) */
279 case 2:
280 ns = *val[i];
281 break; /* N=north,S=south */
282 case 3:
283 lon = atof(val[i]);
284 break; /* Longitude (dddmm.mmm) */
285 case 4:
286 ew = *val[i];
287 break; /* E=east,W=west */
288 case 5:
289 solq = atoi(val[i]);
290 break; /* GPS quality indicator */
291 case 6:
292 nrcv = atoi(val[i]);
293 break; /* # of satellites in use */
294 case 7:
295 hdop = atof(val[i]);
296 break; /* HDOP */
297 case 8:
298 alt = atof(val[i]);
299 break; /* Altitude MSL */
300 case 9:
301 ua = *val[i];
302 break; /* unit (M) */
303 case 10:
304 msl = atof(val[i]);
305 break; /* Geoid separation */
306 case 11:
307 um = *val[i];
308 break; /* unit (M) */
309 case 12:
310 age = atof(val[i]);
311 break; /* Age of differential */
312 }
313 }
314 if ((ns != 'N' && ns != 'S') || (ew != 'E' && ew != 'W')) {
315 trace(NULL, 3, "invalid nmea gga format\n");
316 return 0;
317 }
318 if (sol->time.time == 0) {
319 trace(NULL, 3, "no date info for nmea gga\n");
320 return 0;
321 }
322 pos[0] = (ns == 'N' ? 1.0 : -1.0) * dmm2deg(lat) * D2R;
323 pos[1] = (ew == 'E' ? 1.0 : -1.0) * dmm2deg(lon) * D2R;
324 pos[2] = alt + msl;
325
326 time2epoch(sol->time, ep);
327 septime(tod, ep + 3, ep + 4, ep + 5);
328 time = utc2gpst(epoch2time(ep));
329 tt = timediff(time, sol->time);
330 if (tt < -43200.0) {
331 sol->time = timeadd(time, 86400.0);
332 } else if (tt > 43200.0) {
333 sol->time = timeadd(time, -86400.0);
334 } else {
335 sol->time = time;
336 }
337 pos2ecef(pos, sol->rr);
338 sol->stat = 0 <= solq && solq <= 8 ? nmea_solq[solq] : SOLQ_NONE;
339 sol->ns = nrcv;
340 sol->age = (float)age;
341
342 sol->type = 0; /* position type = xyz */
343
344 trace(NULL, 5, "decode_nmeagga: %s rr=%.3f %.3f %.3f stat=%d ns=%d hdop=%.1f ua=%c um=%c\n", time_str(sol->time, 0),
345 sol->rr[0], sol->rr[1], sol->rr[2], sol->stat, sol->ns, hdop, ua, um);
346
347 return 1;
348}
349/* test NMEA sentence header -------------------------------------------------*/
350static int test_nmea(const char* buff) {
351 if (strlen(buff) < 6 || buff[0] != '$') {
352 return 0;
353 }
354 return !strncmp(buff + 1, "GP", 2) || !strncmp(buff + 1, "GA", 2) || /* NMEA 4.10 [1] */
355 !strncmp(buff + 1, "GL", 2) || !strncmp(buff + 1, "GN", 2) || !strncmp(buff + 1, "GB", 2) ||
356 !strncmp(buff + 1, "GQ", 2) || /* NMEA 4.11 [2] */
357 !strncmp(buff + 1, "GI", 2) || !strncmp(buff + 1, "BD", 2) || !strncmp(buff + 1, "QZ", 2); /* extension */
358}
359/* test solution status message header ---------------------------------------*/
360static int test_solstat(const char* buff) {
361 if (strlen(buff) < 7 || buff[0] != '$') {
362 return 0;
363 }
364 return !strncmp(buff + 1, "POS", 3) || !strncmp(buff + 1, "VELACC", 6) || !strncmp(buff + 1, "CLK", 3) ||
365 !strncmp(buff + 1, "ION", 3) || !strncmp(buff + 1, "TROP", 4) || !strncmp(buff + 1, "HWBIAS", 6) ||
366 !strncmp(buff + 1, "TRPG", 4) || !strncmp(buff + 1, "AMB", 3) || !strncmp(buff + 1, "SAT", 3);
367}
368/* decode NMEA sentence ------------------------------------------------------*/
369static int decode_nmea(char* buff, sol_t* sol) {
370 char *p, *q, *val[MAXFIELD];
371 int n = 0;
372
373 trace(NULL, 4, "decode_nmea: buff=%s\n", buff);
374
375 /* parse fields */
376 for (p = buff; *p && n < MAXFIELD; p = q + 1) {
377 if ((q = strchr(p, ',')) || (q = strchr(p, '*'))) {
378 val[n++] = p;
379 *q = '\0';
380 } else {
381 break;
382 }
383 }
384 if (n < 1) {
385 return 0;
386 }
387 if (!strcmp(val[0] + 3, "RMC")) { /* $xxRMC */
388 return decode_nmearmc(val + 1, n - 1, sol);
389 } else if (!strcmp(val[0] + 3, "ZDA")) { /* $xxZDA */
390 return decode_nmeazda(val + 1, n - 1, sol);
391 } else if (!strcmp(val[0] + 3, "GGA")) { /* $xxGGA */
392 return decode_nmeagga(val + 1, n - 1, sol);
393 }
394 return 0;
395}
396/* decode solution time ------------------------------------------------------*/
397static char* decode_soltime(char* buff, const solopt_t* opt, gtime_t* time) {
398 double v[MAXFIELD];
399 char *p, *q, s[64] = " ";
400 int n, len;
401
402 trace(NULL, 4, "decode_soltime:\n");
403
404 if (!strcmp(opt->sep, "\\t")) {
405 strcpy(s, "\t");
406 } else if (*opt->sep) {
407 strcpy(s, opt->sep);
408 }
409 len = (int)strlen(s);
410
411 if (opt->posf == SOLF_STAT) {
412 return buff;
413 }
414 if (opt->posf == SOLF_GSIF) {
415 if (sscanf(buff, "%lf %lf %lf %lf:%lf:%lf", v, v + 1, v + 2, v + 3, v + 4, v + 5) < 6) {
416 return NULL;
417 }
418 *time = timeadd(epoch2time(v), -12.0 * 3600.0);
419 if (!(p = strchr(buff, ':')) || !(p = strchr(p + 1, ':'))) {
420 return NULL;
421 }
422 for (p++; isdigit((int)*p) || *p == '.';) {
423 p++;
424 }
425 return p + len;
426 }
427 /* yyyy/mm/dd hh:mm:ss or yyyy mm dd hh:mm:ss */
428 if (sscanf(buff, "%lf/%lf/%lf %lf:%lf:%lf", v, v + 1, v + 2, v + 3, v + 4, v + 5) >= 6) {
429 if (v[0] < 100.0) {
430 v[0] += v[0] < 80.0 ? 2000.0 : 1900.0;
431 }
432 *time = epoch2time(v);
433 if (opt->times == TIMES_UTC) {
434 *time = utc2gpst(*time);
435 } else if (opt->times == TIMES_JST) {
436 *time = utc2gpst(timeadd(*time, -9 * 3600.0));
437 }
438 if (!(p = strchr(buff, ':')) || !(p = strchr(p + 1, ':'))) {
439 return NULL;
440 }
441 for (p++; isdigit((int)*p) || *p == '.';) {
442 p++;
443 }
444 return p + len;
445 } else { /* wwww ssss */
446 for (p = buff, n = 0; n < 2; p = q + len) {
447 if ((q = strstr(p, s))) {
448 *q = '\0';
449 }
450 if (sscanf(p, "%lf", v + n) == 1) {
451 n++;
452 }
453 if (!q) {
454 break;
455 }
456 }
457 if (n >= 2 && 0.0 <= v[0] && v[0] <= 3000.0 && 0.0 <= v[1] && v[1] < 604800.0) {
458 *time = gpst2time((int)v[0], v[1]);
459 return p;
460 }
461 }
462 return NULL;
463}
464/* decode x/y/z-ecef ---------------------------------------------------------*/
465static int decode_solxyz(char* buff, const solopt_t* opt, sol_t* sol) {
466 double val[MAXFIELD], P[9] = {0};
467 int i = 0, j, n;
468 const char* sep = opt2sep(opt);
469
470 trace(NULL, 4, "decode_solxyz:\n");
471
472 if ((n = tonum(buff, sep, val)) < 3) {
473 return 0;
474 }
475
476 for (j = 0; j < 3; j++) {
477 sol->rr[j] = val[i++]; /* xyz */
478 }
479 if (i < n) {
480 sol->stat = (uint8_t)val[i++];
481 }
482 if (i < n) {
483 sol->ns = (uint8_t)val[i++];
484 }
485 if (i + 3 <= n) {
486 P[0] = SQR(val[i]);
487 i++; /* sdx */
488 P[4] = SQR(val[i]);
489 i++; /* sdy */
490 P[8] = SQR(val[i]);
491 i++; /* sdz */
492 if (i + 3 <= n) {
493 P[1] = P[3] = SQR(val[i]);
494 i++; /* sdxy */
495 P[5] = P[7] = SQR(val[i]);
496 i++; /* sdyz */
497 P[2] = P[6] = SQR(val[i]);
498 i++; /* sdzx */
499 }
500 covtosol(P, sol);
501 }
502 if (i < n) {
503 sol->age = (float)val[i++];
504 }
505 if (i < n) {
506 sol->ratio = (float)val[i++];
507 }
508
509 if (i + 3 <= n) { /* velocity */
510 for (j = 0; j < 3; j++) {
511 sol->rr[j + 3] = val[i++]; /* xyz */
512 }
513 }
514 if (i + 3 <= n) {
515 for (j = 0; j < 9; j++) {
516 P[j] = 0.0;
517 }
518 P[0] = SQR(val[i]);
519 i++; /* sdx */
520 P[4] = SQR(val[i]);
521 i++; /* sdy */
522 P[8] = SQR(val[i]);
523 i++; /* sdz */
524 if (i + 3 < n) {
525 P[1] = P[3] = SQR(val[i]);
526 i++; /* sdxy */
527 P[5] = P[7] = SQR(val[i]);
528 i++; /* sdyz */
529 P[2] = P[6] = SQR(val[i]);
530 i++; /* sdzx */
531 }
532 covtosol_vel(P, sol);
533 }
534 sol->type = 0; /* position type = xyz */
535
536 if (MAXSOLQ < sol->stat) {
537 sol->stat = SOLQ_NONE;
538 }
539 return 1;
540}
541/* decode lat/lon/height -----------------------------------------------------*/
542static int decode_solllh(char* buff, const solopt_t* opt, sol_t* sol) {
543 double val[MAXFIELD], pos[3], vel[3], Q[9] = {0}, P[9];
544 int i = 0, j, n;
545 const char* sep = opt2sep(opt);
546
547 trace(NULL, 4, "decode_solllh:\n");
548
549 n = tonum(buff, sep, val);
550
551 if (!opt->degf) {
552 if (n < 3) {
553 return 0;
554 }
555 pos[0] = val[i++] * D2R; /* lat/lon/hgt (ddd.ddd) */
556 pos[1] = val[i++] * D2R;
557 pos[2] = val[i++];
558 } else {
559 if (n < 7) {
560 return 0;
561 }
562 pos[0] = dms2deg(val) * D2R; /* lat/lon/hgt (ddd mm ss) */
563 pos[1] = dms2deg(val + 3) * D2R;
564 pos[2] = val[6];
565 i += 7;
566 }
567 pos2ecef(pos, sol->rr);
568 if (i < n) {
569 sol->stat = (uint8_t)val[i++];
570 }
571 if (i < n) {
572 sol->ns = (uint8_t)val[i++];
573 }
574 if (i + 3 <= n) {
575 Q[4] = SQR(val[i]);
576 i++; /* sdn */
577 Q[0] = SQR(val[i]);
578 i++; /* sde */
579 Q[8] = SQR(val[i]);
580 i++; /* sdu */
581 if (i + 3 < n) {
582 Q[1] = Q[3] = SQR(val[i]);
583 i++; /* sdne */
584 Q[2] = Q[6] = SQR(val[i]);
585 i++; /* sdeu */
586 Q[5] = Q[7] = SQR(val[i]);
587 i++; /* sdun */
588 }
589 covecef(pos, Q, P);
590 covtosol(P, sol);
591 }
592 if (i < n) {
593 sol->age = (float)val[i++];
594 }
595 if (i < n) {
596 sol->ratio = (float)val[i++];
597 }
598
599 if (i + 3 <= n) { /* velocity */
600 vel[1] = val[i++]; /* vel-n */
601 vel[0] = val[i++]; /* vel-e */
602 vel[2] = val[i++]; /* vel-u */
603 enu2ecef(pos, vel, sol->rr + 3);
604 }
605 if (i + 3 <= n) {
606 for (j = 0; j < 9; j++) {
607 Q[j] = 0.0;
608 }
609 Q[4] = SQR(val[i]);
610 i++; /* sdn */
611 Q[0] = SQR(val[i]);
612 i++; /* sde */
613 Q[8] = SQR(val[i]);
614 i++; /* sdu */
615 if (i + 3 <= n) {
616 Q[1] = Q[3] = SQR(val[i]);
617 i++; /* sdne */
618 Q[2] = Q[6] = SQR(val[i]);
619 i++; /* sdeu */
620 Q[5] = Q[7] = SQR(val[i]);
621 i++; /* sdun */
622 }
623 covecef(pos, Q, P);
624 covtosol_vel(P, sol);
625 }
626 sol->type = 0; /* position type = xyz */
627
628 if (MAXSOLQ < sol->stat) {
629 sol->stat = SOLQ_NONE;
630 }
631 return 1;
632}
633/* decode e/n/u-baseline -----------------------------------------------------*/
634static int decode_solenu(char* buff, const solopt_t* opt, sol_t* sol) {
635 double val[MAXFIELD], Q[9] = {0};
636 int i = 0, j, n;
637 const char* sep = opt2sep(opt);
638
639 trace(NULL, 4, "decode_solenu:\n");
640
641 if ((n = tonum(buff, sep, val)) < 3) {
642 return 0;
643 }
644
645 for (j = 0; j < 3; j++) {
646 sol->rr[j] = val[i++]; /* enu */
647 }
648 if (i < n) {
649 sol->stat = (uint8_t)val[i++];
650 }
651 if (i < n) {
652 sol->ns = (uint8_t)val[i++];
653 }
654 if (i + 3 <= n) {
655 Q[0] = SQR(val[i]);
656 i++; /* sde */
657 Q[4] = SQR(val[i]);
658 i++; /* sdn */
659 Q[8] = SQR(val[i]);
660 i++; /* sdu */
661 if (i + 3 <= n) {
662 Q[1] = Q[3] = SQR(val[i]);
663 i++; /* sden */
664 Q[5] = Q[7] = SQR(val[i]);
665 i++; /* sdnu */
666 Q[2] = Q[6] = SQR(val[i]);
667 i++; /* sdue */
668 }
669 covtosol(Q, sol);
670 }
671 if (i < n) {
672 sol->age = (float)val[i++];
673 }
674 if (i < n) {
675 sol->ratio = (float)val[i++];
676 }
677
678 sol->type = 1; /* position type = enu */
679
680 if (MAXSOLQ < sol->stat) {
681 sol->stat = SOLQ_NONE;
682 }
683 return 1;
684}
685/* decode solution status ----------------------------------------------------*/
686static int decode_solsss(char* buff, sol_t* sol) {
687 double tow, pos[3], std[3] = {0};
688 int i, week, solq;
689
690 trace(NULL, 4, "decode_solsss:\n");
691
692 if (sscanf(buff, "$POS,%d,%lf,%d,%lf,%lf,%lf,%lf,%lf,%lf", &week, &tow, &solq, pos, pos + 1, pos + 2, std, std + 1,
693 std + 2) < 6) {
694 return 0;
695 }
696 if (week <= 0 || norm(pos, 3) <= 0.0 || solq == SOLQ_NONE) {
697 return 0;
698 }
699 sol->time = gpst2time(week, tow);
700 for (i = 0; i < 6; i++) {
701 sol->rr[i] = i < 3 ? pos[i] : 0.0;
702 sol->qr[i] = i < 3 ? (float)SQR(std[i]) : 0.0f;
703 sol->dtr[i] = 0.0;
704 }
705 sol->ns = 0;
706 sol->age = sol->ratio = sol->thres = 0.0f;
707 sol->type = 0; /* position type = xyz */
708 sol->stat = solq;
709 return 1;
710}
711/* decode GSI F solution -----------------------------------------------------*/
712static int decode_solgsi(char* buff, const solopt_t* opt, sol_t* sol) {
713 double val[MAXFIELD];
714 int i = 0, j;
715
716 trace(NULL, 4, "decode_solgsi:\n");
717
718 if (tonum(buff, " ", val) < 3) {
719 return 0;
720 }
721
722 for (j = 0; j < 3; j++) {
723 sol->rr[j] = val[i++]; /* xyz */
724 }
725 sol->stat = SOLQ_FIX;
726 return 1;
727}
728/* decode solution position --------------------------------------------------*/
729static int decode_solpos(char* buff, const solopt_t* opt, sol_t* sol) {
730 sol_t sol0 = {{0}};
731 char* p = buff;
732
733 trace(NULL, 4, "decode_solpos: buff=%s\n", buff);
734
735 *sol = sol0;
736
737 /* decode solution time */
738 if (!(p = decode_soltime(p, opt, &sol->time))) {
739 return 0;
740 }
741 /* decode solution position */
742 switch (opt->posf) {
743 case SOLF_XYZ:
744 return decode_solxyz(p, opt, sol);
745 case SOLF_LLH:
746 return decode_solllh(p, opt, sol);
747 case SOLF_ENU:
748 return decode_solenu(p, opt, sol);
749 case SOLF_GSIF:
750 return decode_solgsi(p, opt, sol);
751 }
752 return 0;
753}
754/* decode reference position -------------------------------------------------*/
755static void decode_refpos(char* buff, const solopt_t* opt, double* rb) {
756 double val[MAXFIELD], pos[3];
757 int i, n;
758 const char* sep = opt2sep(opt);
759
760 trace(NULL, 3, "decode_refpos: buff=%s\n", buff);
761
762 if ((n = tonum(buff, sep, val)) < 3) {
763 return;
764 }
765
766 if (opt->posf == SOLF_XYZ) { /* xyz */
767 for (i = 0; i < 3; i++) {
768 rb[i] = val[i];
769 }
770 } else if (opt->degf == 0) { /* lat/lon/hgt (ddd.ddd) */
771 pos[0] = val[0] * D2R;
772 pos[1] = val[1] * D2R;
773 pos[2] = val[2];
774 pos2ecef(pos, rb);
775 } else if (opt->degf == 1 && n >= 7) { /* lat/lon/hgt (ddd mm ss) */
776 pos[0] = dms2deg(val) * D2R;
777 pos[1] = dms2deg(val + 3) * D2R;
778 pos[2] = val[6];
779 pos2ecef(pos, rb);
780 }
781}
782/* decode solution -----------------------------------------------------------*/
783static int decode_sol(char* buff, const solopt_t* opt, sol_t* sol, double* rb) {
784 char* p;
785
786 trace(NULL, 4, "decode_sol: buff=%s\n", buff);
787
788 if (test_nmea(buff)) { /* decode nmea */
789 return decode_nmea(buff, sol);
790 } else if (test_solstat(buff)) { /* decode solution status */
791 return decode_solsss(buff, sol);
792 }
793 if (!strncmp(buff, COMMENTH, 1)) { /* reference position */
794 if (!strstr(buff, "ref pos") && !strstr(buff, "slave pos")) {
795 return 0;
796 }
797 if (!(p = strchr(buff, ':'))) {
798 return 0;
799 }
800 decode_refpos(p + 1, opt, rb);
801 return 0;
802 }
803 /* decode position record */
804 return decode_solpos(buff, opt, sol);
805}
806/* decode solution options ---------------------------------------------------*/
807static void decode_solopt(char* buff, solopt_t* opt) {
808 char* p;
809
810 trace(NULL, 4, "decode_solhead: buff=%s\n", buff);
811
812 if (strncmp(buff, COMMENTH, 1) && strncmp(buff, "+", 1)) {
813 return;
814 }
815
816 if (strstr(buff, "GPST")) {
817 opt->times = TIMES_GPST;
818 } else if (strstr(buff, "UTC")) {
819 opt->times = TIMES_UTC;
820 } else if (strstr(buff, "JST")) {
821 opt->times = TIMES_JST;
822 }
823
824 if ((p = strstr(buff, "x-ecef(m)"))) {
825 opt->posf = SOLF_XYZ;
826 opt->degf = 0;
827 strncpy(opt->sep, p + 9, 1);
828 opt->sep[1] = '\0';
829 } else if ((p = strstr(buff, "latitude(d'\")"))) {
830 opt->posf = SOLF_LLH;
831 opt->degf = 1;
832 strncpy(opt->sep, p + 14, 1);
833 opt->sep[1] = '\0';
834 } else if ((p = strstr(buff, "latitude(deg)"))) {
835 opt->posf = SOLF_LLH;
836 opt->degf = 0;
837 strncpy(opt->sep, p + 13, 1);
838 opt->sep[1] = '\0';
839 } else if ((p = strstr(buff, "e-baseline(m)"))) {
840 opt->posf = SOLF_ENU;
841 opt->degf = 0;
842 strncpy(opt->sep, p + 13, 1);
843 opt->sep[1] = '\0';
844 } else if ((p = strstr(buff, "+SITE/INF"))) { /* gsi f2/f3 solution */
845 opt->times = TIMES_GPST;
846 opt->posf = SOLF_GSIF;
847 opt->degf = 0;
848 strcpy(opt->sep, " ");
849 }
850}
851/* read solution option ------------------------------------------------------*/
852static void readsolopt(FILE* fp, solopt_t* opt) {
853 char buff[MAXSOLMSG + 1];
854 int i;
855
856 trace(NULL, 3, "readsolopt:\n");
857
858 for (i = 0; fgets(buff, sizeof(buff), fp) && i < 100; i++) { /* only 100 lines */
859
860 /* decode solution options */
861 decode_solopt(buff, opt);
862 }
863}
864/* input solution data from stream ---------------------------------------------
865 * input solution data from stream
866 * args : uint8_t data I stream data
867 * gtime_t ts I start time (ts.time==0: from start)
868 * gtime_t te I end time (te.time==0: to end)
869 * double tint I time interval (0: all)
870 * int qflag I quality flag (0: all)
871 * solbuf_t *solbuf IO solution buffer
872 * return : status (1:solution received,0:no solution,-1:disconnect received)
873 *-----------------------------------------------------------------------------*/
874int inputsol(uint8_t data, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t* opt, solbuf_t* solbuf) {
875 sol_t sol = {{0}};
876 int stat;
877
878 trace(NULL, 4, "inputsol: data=0x%02x\n", data);
879
880 sol.time = solbuf->time;
881
882 if (data == '$' || (!isprint(data) && data != '\r' && data != '\n')) { /* sync header */
883 solbuf->nb = 0;
884 }
885 if (data != '\r' && data != '\n') {
886 solbuf->buff[solbuf->nb++] = data;
887 }
888 if (data != '\n' && solbuf->nb < MAXSOLMSG) {
889 return 0; /* sync trailer */
890 }
891
892 solbuf->buff[solbuf->nb] = '\0';
893 solbuf->nb = 0;
894
895 /* check disconnect message */
896 if (!strncmp((char*)solbuf->buff, MSG_DISCONN, strlen(MSG_DISCONN) - 2)) {
897 trace(NULL, 3, "disconnect received\n");
898 return -1;
899 }
900 /* decode solution */
901 sol.time = solbuf->time;
902 if ((stat = decode_sol((char*)solbuf->buff, opt, &sol, solbuf->rb)) > 0) {
903 if (stat) {
904 solbuf->time = sol.time; /* update current time */
905 }
906 if (stat != 1) {
907 return 0;
908 }
909 }
910 if (stat != 1 || !screent(sol.time, ts, te, tint) || (qflag && sol.stat != qflag)) {
911 return 0;
912 }
913 /* add solution to solution buffer */
914 return addsol(solbuf, &sol);
915}
916/* read solution data --------------------------------------------------------*/
917static int readsoldata(FILE* fp, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t* opt,
918 solbuf_t* solbuf) {
919 int c;
920
921 trace(NULL, 3, "readsoldata:\n");
922
923 while ((c = fgetc(fp)) != EOF) {
924 /* input solution */
925 inputsol((uint8_t)c, ts, te, tint, qflag, opt, solbuf);
926 }
927 return solbuf->n > 0;
928}
929/* compare solution data -----------------------------------------------------*/
930static int cmpsol(const void* p1, const void* p2) {
931 sol_t *q1 = (sol_t*)p1, *q2 = (sol_t*)p2;
932 double tt = timediff(q1->time, q2->time);
933 return tt < -0.0 ? -1 : (tt > 0.0 ? 1 : 0);
934}
935/* sort and uniq solution data -----------------------------------------------*/
936static int sort_solbuf(solbuf_t* solbuf) {
937 sol_t* solbuf_data;
938 int i, n;
939
940 trace(NULL, 4, "sort_solbuf: n=%d\n", solbuf->n);
941
942 if (solbuf->n <= 0) {
943 return 0;
944 }
945
946 if (!(solbuf_data = (sol_t*)realloc(solbuf->data, sizeof(sol_t) * solbuf->n))) {
947 trace(NULL, 1, "sort_solbuf: memory allocation error\n");
948 free(solbuf->data);
949 solbuf->data = NULL;
950 solbuf->n = solbuf->nmax = 0;
951 return 0;
952 }
953 solbuf->data = solbuf_data;
954 qsort(solbuf->data, solbuf->n, sizeof(sol_t), cmpsol);
955
956 /* remove duplicated data */
957 for (i = n = 0; i < solbuf->n; i++) {
958 if (i == 0 || timediff(solbuf->data[i].time, solbuf->data[i - 1].time) > 1e-3) {
959 solbuf->data[n++] = solbuf->data[i];
960 }
961 }
962 solbuf->n = n;
963 solbuf->start = 0;
964 solbuf->end = solbuf->n - 1;
965 return 1;
966}
967/* read solutions data from solution files -------------------------------------
968 * read solution data from soluiton files
969 * args : char *files[] I solution files
970 * int nfile I number of files
971 * (gtime_t ts) I start time (ts.time==0: from start)
972 * (gtime_t te) I end time (te.time==0: to end)
973 * (double tint) I time interval (0: all)
974 * (int qflag) I quality flag (0: all)
975 * solbuf_t *solbuf O solution buffer
976 * return : status (1:ok,0:no data or error)
977 *-----------------------------------------------------------------------------*/
978int readsolt(char* files[], int nfile, gtime_t ts, gtime_t te, double tint, int qflag, solbuf_t* solbuf) {
979 FILE* fp;
981 int i;
982
983 trace(NULL, 3, "readsolt: nfile=%d\n", nfile);
984
985 initsolbuf(solbuf, 0, 0);
986
987 for (i = 0; i < nfile; i++) {
988 if (!(fp = fopen(files[i], "rb"))) {
989 trace(NULL, 2, "readsolt: file open error %s\n", files[i]);
990 continue;
991 }
992 /* read solution options in header */
993 readsolopt(fp, &opt);
994 rewind(fp);
995
996 /* read solution data */
997 if (!readsoldata(fp, ts, te, tint, qflag, &opt, solbuf)) {
998 trace(NULL, 2, "readsolt: no solution in %s\n", files[i]);
999 }
1000 fclose(fp);
1001 }
1002 return sort_solbuf(solbuf);
1003}
1004int readsol(char* files[], int nfile, solbuf_t* sol) {
1005 gtime_t time = {0};
1006
1007 trace(NULL, 3, "readsol: nfile=%d\n", nfile);
1008
1009 return readsolt(files, nfile, time, time, 0.0, 0, sol);
1010}
1011/* add solution data to solution buffer ----------------------------------------
1012 * add solution data to solution buffer
1013 * args : solbuf_t *solbuf IO solution buffer
1014 * sol_t *sol I solution data
1015 * return : status (1:ok,0:error)
1016 *-----------------------------------------------------------------------------*/
1017int addsol(solbuf_t* solbuf, const sol_t* sol) {
1018 sol_t* solbuf_data;
1019
1020 trace(NULL, 4, "addsol:\n");
1021
1022 if (solbuf->cyclic) { /* ring buffer */
1023 if (solbuf->nmax <= 1) {
1024 return 0;
1025 }
1026 solbuf->data[solbuf->end] = *sol;
1027 if (++solbuf->end >= solbuf->nmax) {
1028 solbuf->end = 0;
1029 }
1030 if (solbuf->start == solbuf->end) {
1031 if (++solbuf->start >= solbuf->nmax) {
1032 solbuf->start = 0;
1033 }
1034 } else {
1035 solbuf->n++;
1036 }
1037
1038 return 1;
1039 }
1040 if (solbuf->n >= solbuf->nmax) {
1041 solbuf->nmax = solbuf->nmax == 0 ? 8192 : solbuf->nmax * 2;
1042 if (!(solbuf_data = (sol_t*)realloc(solbuf->data, sizeof(sol_t) * solbuf->nmax))) {
1043 trace(NULL, 1, "addsol: memory allocation error\n");
1044 free(solbuf->data);
1045 solbuf->data = NULL;
1046 solbuf->n = solbuf->nmax = 0;
1047 return 0;
1048 }
1049 solbuf->data = solbuf_data;
1050 }
1051 solbuf->data[solbuf->n++] = *sol;
1052 return 1;
1053}
1054/* get solution data from solution buffer --------------------------------------
1055 * get solution data by index from solution buffer
1056 * args : solbuf_t *solbuf I solution buffer
1057 * int index I index of solution (0...)
1058 * return : solution data pointer (NULL: no solution, out of range)
1059 *-----------------------------------------------------------------------------*/
1060sol_t* getsol(solbuf_t* solbuf, int index) {
1061 trace(NULL, 4, "getsol: index=%d\n", index);
1062
1063 if (index < 0 || solbuf->n <= index) {
1064 return NULL;
1065 }
1066 if ((index = solbuf->start + index) >= solbuf->nmax) {
1067 index -= solbuf->nmax;
1068 }
1069 return solbuf->data + index;
1070}
1071/* initialize solution buffer --------------------------------------------------
1072 * initialize position solutions
1073 * args : solbuf_t *solbuf I solution buffer
1074 * int cyclic I solution data buffer type (0:linear,1:cyclic)
1075 * int nmax I initial number of solution data
1076 * return : status (1:ok,0:error)
1077 *-----------------------------------------------------------------------------*/
1078void initsolbuf(solbuf_t* solbuf, int cyclic, int nmax) {
1079#if 0
1080 gtime_t time0={0};
1081#endif
1082 int i;
1083
1084 trace(NULL, 3, "initsolbuf: cyclic=%d nmax=%d\n", cyclic, nmax);
1085
1086 solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = solbuf->nb = 0;
1087 solbuf->cyclic = cyclic;
1088#if 0
1089 solbuf->time=time0;
1090#endif
1091 solbuf->data = NULL;
1092 for (i = 0; i < 3; i++) {
1093 solbuf->rb[i] = 0.0;
1094 }
1095 if (cyclic) {
1096 if (nmax <= 2) {
1097 nmax = 2;
1098 }
1099 if (!(solbuf->data = malloc(sizeof(sol_t) * nmax))) {
1100 trace(NULL, 1, "initsolbuf: memory allocation error\n");
1101 return;
1102 }
1103 solbuf->nmax = nmax;
1104 }
1105}
1106/* free solution ---------------------------------------------------------------
1107 * free memory for solution buffer
1108 * args : solbuf_t *solbuf I solution buffer
1109 * return : none
1110 *-----------------------------------------------------------------------------*/
1111void freesolbuf(solbuf_t* solbuf) {
1112 int i;
1113
1114 trace(NULL, 3, "freesolbuf: n=%d\n", solbuf->n);
1115
1116 free(solbuf->data);
1117 solbuf->n = solbuf->nmax = solbuf->start = solbuf->end = solbuf->nb = 0;
1118 solbuf->data = NULL;
1119 for (i = 0; i < 3; i++) {
1120 solbuf->rb[i] = 0.0;
1121 }
1122}
1123void freesolstatbuf(solstatbuf_t* solstatbuf) {
1124 trace(NULL, 3, "freesolstatbuf: n=%d\n", solstatbuf->n);
1125
1126 solstatbuf->n = solstatbuf->nmax = 0;
1127 free(solstatbuf->data);
1128 solstatbuf->data = NULL;
1129}
1130/* compare solution status ---------------------------------------------------*/
1131static int cmpsolstat(const void* p1, const void* p2) {
1132 solstat_t *q1 = (solstat_t*)p1, *q2 = (solstat_t*)p2;
1133 double tt = timediff(q1->time, q2->time);
1134 return tt < -0.0 ? -1 : (tt > 0.0 ? 1 : 0);
1135}
1136/* sort and uniq solution status ---------------------------------------------*/
1137static int sort_solstat(solstatbuf_t* statbuf) {
1138 solstat_t* statbuf_data;
1139 int i, n;
1140
1141 trace(NULL, 4, "sort_solstat: n=%d\n", statbuf->n);
1142
1143 if (statbuf->n <= 0) {
1144 return 0;
1145 }
1146
1147 if (!(statbuf_data = realloc(statbuf->data, sizeof(solstat_t) * statbuf->n))) {
1148 trace(NULL, 1, "sort_solstat: memory allocation error\n");
1149 free(statbuf->data);
1150 statbuf->data = NULL;
1151 statbuf->n = statbuf->nmax = 0;
1152 return 0;
1153 }
1154 statbuf->data = statbuf_data;
1155 qsort(statbuf->data, statbuf->n, sizeof(solstat_t), cmpsolstat);
1156
1157 /* remove duplicated data */
1158 for (i = n = 0; i < statbuf->n; i++) {
1159 if (i == 0 || timediff(statbuf->data[i].time, statbuf->data[i - 1].time) > 1e-3) {
1160 statbuf->data[n++] = statbuf->data[i];
1161 }
1162 }
1163 statbuf->n = n;
1164 return 1;
1165}
1166/* decode solution status ----------------------------------------------------*/
1167static int decode_solstat(char* buff, solstat_t* stat) {
1168 static const solstat_t stat0 = {{0}};
1169 double tow, az, el, resp, resc, snr;
1170 int n, week, sat, frq, vsat, fix, slip, lock, outc, slipc, rejc;
1171 char id[32] = "", *p;
1172
1173 trace(NULL, 4, "decode_solstat: buff=%s\n", buff);
1174
1175 if (strstr(buff, "$SAT") != buff) {
1176 return 0;
1177 }
1178
1179 for (p = buff; *p; p++) {
1180 if (*p == ',') {
1181 *p = ' ';
1182 }
1183 }
1184
1185 n = sscanf(buff, "$SAT%d%lf%s%d%lf%lf%lf%lf%d%lf%d%d%d%d%d%d", &week, &tow, id, &frq, &az, &el, &resp, &resc, &vsat,
1186 &snr, &fix, &slip, &lock, &outc, &slipc, &rejc);
1187
1188 if (n < 15) {
1189 trace(NULL, 2, "invalid format of solution status: %s\n", buff);
1190 return 0;
1191 }
1192 if ((sat = satid2no(id)) <= 0) {
1193 trace(NULL, 2, "invalid satellite in solution status: %s\n", id);
1194 return 0;
1195 }
1196 *stat = stat0;
1197 stat->time = gpst2time(week, tow);
1198 stat->sat = (uint8_t)sat;
1199 stat->frq = (uint8_t)frq;
1200 stat->az = (float)(az * D2R);
1201 stat->el = (float)(el * D2R);
1202 stat->resp = (float)resp;
1203 stat->resc = (float)resc;
1204 stat->flag = (uint8_t)((vsat << 5) + (slip << 3) + fix);
1205 stat->snr = (uint16_t)(snr / SNR_UNIT + 0.5);
1206 stat->lock = (uint16_t)lock;
1207 stat->outc = (uint16_t)outc;
1208 stat->slipc = (uint16_t)slipc;
1209 stat->rejc = (uint16_t)rejc;
1210 return 1;
1211}
1212/* add solution status data --------------------------------------------------*/
1213static void addsolstat(solstatbuf_t* statbuf, const solstat_t* stat) {
1214 solstat_t* statbuf_data;
1215
1216 trace(NULL, 4, "addsolstat:\n");
1217
1218 if (statbuf->n >= statbuf->nmax) {
1219 statbuf->nmax = statbuf->nmax == 0 ? 8192 : statbuf->nmax * 2;
1220 if (!(statbuf_data = (solstat_t*)realloc(statbuf->data, sizeof(solstat_t) * statbuf->nmax))) {
1221 trace(NULL, 1, "addsolstat: memory allocation error\n");
1222 free(statbuf->data);
1223 statbuf->data = NULL;
1224 statbuf->n = statbuf->nmax = 0;
1225 return;
1226 }
1227 statbuf->data = statbuf_data;
1228 }
1229 statbuf->data[statbuf->n++] = *stat;
1230}
1231/* read solution status data -------------------------------------------------*/
1232static int readsolstatdata(FILE* fp, gtime_t ts, gtime_t te, double tint, solstatbuf_t* statbuf) {
1233 solstat_t stat = {{0}};
1234 char buff[MAXSOLMSG + 1];
1235
1236 trace(NULL, 3, "readsolstatdata:\n");
1237
1238 while (fgets(buff, sizeof(buff), fp)) {
1239 /* decode solution status */
1240 if (!decode_solstat(buff, &stat)) {
1241 continue;
1242 }
1243
1244 /* add solution to solution buffer */
1245 if (screent(stat.time, ts, te, tint)) {
1246 addsolstat(statbuf, &stat);
1247 }
1248 }
1249 return statbuf->n > 0;
1250}
1251/* read solution status --------------------------------------------------------
1252 * read solution status from solution status files
1253 * args : char *files[] I solution status files
1254 * int nfile I number of files
1255 * (gtime_t ts) I start time (ts.time==0: from start)
1256 * (gtime_t te) I end time (te.time==0: to end)
1257 * (double tint) I time interval (0: all)
1258 * solstatbuf_t *statbuf O solution status buffer
1259 * return : status (1:ok,0:no data or error)
1260 *-----------------------------------------------------------------------------*/
1261int readsolstatt(char* files[], int nfile, gtime_t ts, gtime_t te, double tint, solstatbuf_t* statbuf) {
1262 FILE* fp;
1263 char path[1024], *p;
1264 int i;
1265
1266 trace(NULL, 3, "readsolstatt: nfile=%d\n", nfile);
1267
1268 statbuf->n = statbuf->nmax = 0;
1269 statbuf->data = NULL;
1270
1271 for (i = 0; i < nfile; i++) {
1272 if ((p = strrchr(files[i], '.')) && !strcmp(p, ".stat")) {
1273 sprintf(path, "%s", files[i]);
1274 } else {
1275 sprintf(path, "%s.stat", files[i]);
1276 }
1277 if (!(fp = fopen(path, "r"))) {
1278 trace(NULL, 2, "readsolstatt: file open error %s\n", path);
1279 continue;
1280 }
1281 /* read solution status data */
1282 if (!readsolstatdata(fp, ts, te, tint, statbuf)) {
1283 trace(NULL, 2, "readsolstatt: no solution in %s\n", path);
1284 }
1285 fclose(fp);
1286 }
1287#if 0
1288 return sort_solstat(statbuf);
1289#else
1290 return 1;
1291#endif
1292}
1293int readsolstat(char* files[], int nfile, solstatbuf_t* statbuf) {
1294 gtime_t time = {0};
1295
1296 trace(NULL, 3, "readsolstat: nfile=%d\n", nfile);
1297
1298 return readsolstatt(files, nfile, time, time, 0.0, statbuf);
1299}
1300/* output solution as the form of x/y/z-ecef ---------------------------------*/
1301static int outecef(uint8_t* buff, const char* s, const sol_t* sol, const solopt_t* opt) {
1302 const char* sep = opt2sep(opt);
1303 char* p = (char*)buff;
1304
1305 trace(NULL, 3, "outecef:\n");
1306
1307 p += sprintf(p,
1308 "%s%s%14.4f%s%14.4f%s%14.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s"
1309 "%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f",
1310 s, sep, sol->rr[0], sep, sol->rr[1], sep, sol->rr[2], sep, sol->stat, sep, sol->ns, sep,
1311 SQRT(sol->qr[0]), sep, SQRT(sol->qr[1]), sep, SQRT(sol->qr[2]), sep, sqvar(sol->qr[3]), sep,
1312 sqvar(sol->qr[4]), sep, sqvar(sol->qr[5]), sep, sol->age, sep, sol->ratio);
1313
1314 if (opt->outvel) { /* output velocity */
1315 p += sprintf(p,
1316 "%s%10.5f%s%10.5f%s%10.5f%s%9.5f%s%8.5f%s%8.5f%s%8.5f%s"
1317 "%8.5f%s%8.5f",
1318 sep, sol->rr[3], sep, sol->rr[4], sep, sol->rr[5], sep, SQRT(sol->qv[0]), sep, SQRT(sol->qv[1]),
1319 sep, SQRT(sol->qv[2]), sep, sqvar(sol->qv[3]), sep, sqvar(sol->qv[4]), sep, sqvar(sol->qv[5]));
1320 }
1321 p += sprintf(p, "\r\n");
1322 return p - (char*)buff;
1323}
1324/* output solution as the form of lat/lon/height -----------------------------*/
1325static int outpos(uint8_t* buff, const char* s, const sol_t* sol, const solopt_t* opt) {
1326 double pos[3], vel[3], dms1[3], dms2[3], P[9], Q[9];
1327 const char* sep = opt2sep(opt);
1328 char* p = (char*)buff;
1329
1330 trace(NULL, 3, "outpos :\n");
1331
1332 ecef2pos(sol->rr, pos);
1333 soltocov(sol, P);
1334 covenu(pos, P, Q);
1335 if (opt->height == 1) { /* geodetic height */
1336 pos[2] -= geoidh(pos);
1337 }
1338 if (opt->degf) {
1339 deg2dms(pos[0] * R2D, dms1, 5);
1340 deg2dms(pos[1] * R2D, dms2, 5);
1341 p += sprintf(p, "%s%s%4.0f%s%02.0f%s%08.5f%s%4.0f%s%02.0f%s%08.5f", s, sep, dms1[0], sep, dms1[1], sep, dms1[2],
1342 sep, dms2[0], sep, dms2[1], sep, dms2[2]);
1343 } else {
1344 p += sprintf(p, "%s%s%14.9f%s%14.9f", s, sep, pos[0] * R2D, sep, pos[1] * R2D);
1345 }
1346 p += sprintf(p,
1347 "%s%10.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f%s%8.4f"
1348 "%s%6.2f%s%6.1f",
1349 sep, pos[2], sep, sol->stat, sep, sol->ns, sep, SQRT(Q[4]), sep, SQRT(Q[0]), sep, SQRT(Q[8]), sep,
1350 sqvar(Q[1]), sep, sqvar(Q[2]), sep, sqvar(Q[5]), sep, sol->age, sep, sol->ratio);
1351
1352 if (opt->outvel) { /* output velocity */
1353 soltocov_vel(sol, P);
1354 ecef2enu(pos, sol->rr + 3, vel);
1355 covenu(pos, P, Q);
1356 p += sprintf(p,
1357 "%s%10.5f%s%10.5f%s%10.5f%s%9.5f%s%8.5f%s%8.5f%s%8.5f%s"
1358 "%8.5f%s%8.5f",
1359 sep, vel[1], sep, vel[0], sep, vel[2], sep, SQRT(Q[4]), sep, SQRT(Q[0]), sep, SQRT(Q[8]), sep,
1360 sqvar(Q[1]), sep, sqvar(Q[2]), sep, sqvar(Q[5]));
1361 }
1362 p += sprintf(p, "\r\n");
1363 return p - (char*)buff;
1364}
1365/* output solution as the form of e/n/u-baseline -----------------------------*/
1366static int outenu(uint8_t* buff, const char* s, const sol_t* sol, const double* rb, const solopt_t* opt) {
1367 double pos[3], rr[3], enu[3], P[9], Q[9];
1368 int i;
1369 const char* sep = opt2sep(opt);
1370 char* p = (char*)buff;
1371
1372 trace(NULL, 3, "outenu :\n");
1373
1374 for (i = 0; i < 3; i++) {
1375 rr[i] = sol->rr[i] - rb[i];
1376 }
1377 ecef2pos(rb, pos);
1378 soltocov(sol, P);
1379 covenu(pos, P, Q);
1380 ecef2enu(pos, rr, enu);
1381 p += sprintf(p,
1382 "%s%s%14.4f%s%14.4f%s%14.4f%s%3d%s%3d%s%8.4f%s%8.4f%s%8.4f%s"
1383 "%8.4f%s%8.4f%s%8.4f%s%6.2f%s%6.1f\r\n",
1384 s, sep, enu[0], sep, enu[1], sep, enu[2], sep, sol->stat, sep, sol->ns, sep, SQRT(Q[0]), sep,
1385 SQRT(Q[4]), sep, SQRT(Q[8]), sep, sqvar(Q[1]), sep, sqvar(Q[5]), sep, sqvar(Q[2]), sep, sol->age, sep,
1386 sol->ratio);
1387 return p - (char*)buff;
1388}
1389/* output solution in the form of NMEA RMC sentence --------------------------*/
1390int outnmea_rmc(uint8_t* buff, const sol_t* sol) {
1391 static double dirp = 0.0;
1392 gtime_t time;
1393 double ep[6], pos[3], enuv[3], dms1[3], dms2[3], vel, dir, amag = 0.0;
1394 char *p = (char*)buff, *q, sum;
1395 const char *emag = "E", *mode = "A", *status = "V";
1396
1397 trace(NULL, 3, "outnmea_rmc:\n");
1398
1399 if (sol->stat <= SOLQ_NONE) {
1400 p += sprintf(p, "$%sRMC,,,,,,,,,,,,,", NMEA_TID);
1401 for (q = (char*)buff + 1, sum = 0; *q; q++) {
1402 sum ^= *q;
1403 }
1404 p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A);
1405 return p - (char*)buff;
1406 }
1407 time = gpst2utc(sol->time);
1408 if (time.sec >= 0.995) {
1409 time.time++;
1410 time.sec = 0.0;
1411 }
1412 time2epoch(time, ep);
1413 ecef2pos(sol->rr, pos);
1414 ecef2enu(pos, sol->rr + 3, enuv);
1415 vel = norm(enuv, 3);
1416 if (vel >= 1.0) {
1417 dir = atan2(enuv[0], enuv[1]) * R2D;
1418 if (dir < 0.0) {
1419 dir += 360.0;
1420 }
1421 dirp = dir;
1422 } else {
1423 dir = dirp;
1424 }
1425 if (sol->stat == SOLQ_DGPS || sol->stat == SOLQ_SBAS) {
1426 mode = "D";
1427 } else if (sol->stat == SOLQ_FLOAT || sol->stat == SOLQ_FIX) {
1428 mode = "R";
1429 } else if (sol->stat == SOLQ_PPP) {
1430 mode = "P";
1431 }
1432 deg2dms(fabs(pos[0]) * R2D, dms1, 7);
1433 deg2dms(fabs(pos[1]) * R2D, dms2, 7);
1434 p += sprintf(p,
1435 "$%sRMC,%02.0f%02.0f%05.2f,A,%02.0f%010.7f,%s,%03.0f%010.7f,"
1436 "%s,%4.2f,%4.2f,%02.0f%02.0f%02d,%.1f,%s,%s,%s",
1437 NMEA_TID, ep[3], ep[4], ep[5], dms1[0], dms1[1] + dms1[2] / 60.0, pos[0] >= 0 ? "N" : "S", dms2[0],
1438 dms2[1] + dms2[2] / 60.0, pos[1] >= 0 ? "E" : "W", vel / KNOT2M, dir, ep[2], ep[1], (int)ep[0] % 100,
1439 amag, emag, mode, status);
1440 for (q = (char*)buff + 1, sum = 0; *q; q++) {
1441 sum ^= *q; /* check-sum */
1442 }
1443 p += sprintf(p, "*%02X\r\n", sum);
1444 return p - (char*)buff;
1445}
1446/* output solution in the form of NMEA GGA sentence --------------------------*/
1447int outnmea_gga(uint8_t* buff, const sol_t* sol) {
1448 gtime_t time;
1449 double h, ep[6], pos[3], dms1[3], dms2[3], dop = 1.0;
1450 int solq, refid = 0;
1451 char *p = (char*)buff, *q, sum;
1452
1453 trace(NULL, 3, "outnmea_gga:\n");
1454
1455 if (sol->stat <= SOLQ_NONE) {
1456 p += sprintf(p, "$%sGGA,,,,,,,,,,,,,,", NMEA_TID);
1457 for (q = (char*)buff + 1, sum = 0; *q; q++) {
1458 sum ^= *q;
1459 }
1460 p += sprintf(p, "*%02X%c%c", sum, 0x0D, 0x0A);
1461 return p - (char*)buff;
1462 }
1463 for (solq = 0; solq < 8; solq++) {
1464 if (nmea_solq[solq] == sol->stat) {
1465 break;
1466 }
1467 }
1468 if (solq >= 8) {
1469 solq = 0;
1470 }
1471 time = gpst2utc(sol->time);
1472 if (time.sec >= 0.995) {
1473 time.time++;
1474 time.sec = 0.0;
1475 }
1476 time2epoch(time, ep);
1477 ecef2pos(sol->rr, pos);
1478 h = geoidh(pos);
1479 deg2dms(fabs(pos[0]) * R2D, dms1, 7);
1480 deg2dms(fabs(pos[1]) * R2D, dms2, 7);
1481 p += sprintf(p,
1482 "$%sGGA,%02.0f%02.0f%05.2f,%02.0f%010.7f,%s,%03.0f%010.7f,%s,"
1483 "%d,%02d,%.1f,%.3f,M,%.3f,M,%.1f,%04d",
1484 NMEA_TID, ep[3], ep[4], ep[5], dms1[0], dms1[1] + dms1[2] / 60.0, pos[0] >= 0 ? "N" : "S", dms2[0],
1485 dms2[1] + dms2[2] / 60.0, pos[1] >= 0 ? "E" : "W", solq, sol->ns, dop, pos[2] - h, h, sol->age, refid);
1486 for (q = (char*)buff + 1, sum = 0; *q; q++) {
1487 sum ^= *q; /* check-sum */
1488 }
1489 p += sprintf(p, "*%02X\r\n", sum);
1490 return p - (char*)buff;
1491}
1492/* output solution in the form of NMEA GSA sentences -------------------------*/
1493int outnmea_gsa(uint8_t* buff, const sol_t* sol, const ssat_t* ssat) {
1494 double azel[MAXSAT * 2], dop[4];
1495 char *p = (char*)buff, *q, *s, sum;
1496 int i, j, sys, prn, nsat, mask = 0, nsys = 0, sats[MAXSAT];
1497
1498 trace(NULL, 3, "outnmea_gsa:\n");
1499
1500 for (i = nsat = 0; i < MAXSAT; i++) {
1501 if (!ssat[i].vs) {
1502 continue;
1503 }
1504 sys = satsys(i + 1, NULL);
1505 if (!(sys & mask)) {
1506 nsys++; /* # of systems */
1507 }
1508 mask |= sys;
1509 azel[2 * nsat] = ssat[i].azel[0];
1510 azel[2 * nsat + 1] = ssat[i].azel[1];
1511 sats[nsat++] = i + 1;
1512 }
1513 dops(nsat, azel, 0.0, dop);
1514
1515 for (i = 0; nmea_sys[i]; i++) {
1516 for (j = nsat = 0; j < MAXSAT && nsat < 12; j++) {
1517 if (!(satsys(j + 1, NULL) & nmea_sys[i])) {
1518 continue;
1519 }
1520 if (ssat[j].vs) {
1521 sats[nsat++] = j + 1;
1522 }
1523 }
1524 if (nsat > 0) {
1525 s = p;
1526 p += sprintf(p, "$%sGSA,A,%d", nsys > 1 ? "GN" : nmea_tid[i], sol->stat ? 3 : 1);
1527 for (j = 0; j < 12; j++) {
1528 sys = satsys(sats[j], &prn);
1529 if (sys == SYS_SBS) {
1530 prn -= 87; /* SBS: 33-64 */
1531 } else if (sys == SYS_GLO) {
1532 prn += 64; /* GLO: 65-99 */
1533 } else if (sys == SYS_QZS) {
1534 prn -= 192; /* QZS: 01-10 */
1535 }
1536 if (j < nsat) {
1537 p += sprintf(p, ",%02d", prn);
1538 } else {
1539 p += sprintf(p, ",");
1540 }
1541 }
1542 p += sprintf(p, ",%3.1f,%3.1f,%3.1f,%d", dop[1], dop[2], dop[3], nmea_sid[i]);
1543 for (q = s + 1, sum = 0; *q; q++) {
1544 sum ^= *q; /* check-sum */
1545 }
1546 p += sprintf(p, "*%02X\r\n", sum);
1547 }
1548 }
1549 return p - (char*)buff;
1550}
1551/* output solution in the form of NMEA GSV sentences -------------------------*/
1552int outnmea_gsv(uint8_t* buff, const sol_t* sol, const ssat_t* ssat) {
1553 double az, el, snr;
1554 int i, j, k, n, nsat, nmsg, prn, sys, sats[MAXSAT];
1555 char *p = (char*)buff, *q, *s, sum;
1556
1557 trace(NULL, 3, "outnmea_gsv:\n");
1558
1559 for (i = 0; nmea_sys[i]; i++) {
1560 for (j = nsat = 0; j < MAXSAT && nsat < 36; j++) {
1561 if (!(satsys(j + 1, NULL) & nmea_sys[i])) {
1562 continue;
1563 }
1564 if (ssat[j].azel[1] > 0.0) {
1565 sats[nsat++] = j + 1;
1566 }
1567 }
1568 nmsg = (nsat + 3) / 4;
1569
1570 for (j = n = 0; j < nmsg; j++) {
1571 s = p;
1572 p += sprintf(p, "$%sGSV,%d,%d,%02d", nmea_tid[i], nmsg, j + 1, nsat);
1573 for (k = 0; k < 4; k++, n++) {
1574 if (n < nsat) {
1575 sys = satsys(sats[n], &prn);
1576 if (sys == SYS_SBS) {
1577 prn -= 87; /* SBS: 33-64 */
1578 } else if (sys == SYS_GLO) {
1579 prn += 64; /* GLO: 65-99 */
1580 } else if (sys == SYS_QZS) {
1581 prn -= 192; /* QZS: 01-10 */
1582 }
1583 az = ssat[sats[n] - 1].azel[0] * R2D;
1584 if (az < 0.0) {
1585 az += 360.0;
1586 }
1587 el = ssat[sats[n] - 1].azel[1] * R2D;
1588 snr = ssat[sats[n] - 1].snr[0] * SNR_UNIT;
1589 p += sprintf(p, ",%02d,%02.0f,%03.0f,%02.0f", prn, el, az, snr);
1590 } else {
1591 p += sprintf(p, ",,,,");
1592 }
1593 }
1594 p += sprintf(p, ",0"); /* all signals */
1595 for (q = s + 1, sum = 0; *q; q++) {
1596 sum ^= *q; /* check-sum */
1597 }
1598 p += sprintf(p, "*%02X\r\n", sum);
1599 }
1600 }
1601 return p - (char*)buff;
1602}
1603/* output processing options ---------------------------------------------------
1604 * output processing options to buffer
1605 * args : uint8_t *buff IO output buffer
1606 * prcopt_t *opt I processing options
1607 * return : number of output bytes
1608 *-----------------------------------------------------------------------------*/
1609int outprcopts(uint8_t* buff, const prcopt_t* opt) {
1610 const int sys[] = {SYS_GPS, SYS_GLO, SYS_GAL, SYS_QZS, SYS_CMP, SYS_IRN, SYS_SBS, 0};
1611 const char* s1[] = {
1612 "Single", "DGPS", "Kinematic", "Static", "Moving-Base", "Fixed", "PPP Kinematic", "PPP Static",
1613 "PPP Fixed", "", "", ""};
1614 const char* s2[] = {"L1", "L1+2", "L1+2+3", "L1+2+3+4", "L1+2+3+4+5", "L1+2+3+4+5+6", "", "", ""};
1615 const char* s3[] = {"Forward", "Backward", "Combined", "", "", ""};
1616 const char* s4[] = {"OFF", "Broadcast", "SBAS", "Iono-Free LC", "Estimate TEC", "IONEX TEC", "QZSS Broadcast", "",
1617 "", "", ""};
1618 const char* s5[] = {"OFF", "Saastamoinen", "SBAS", "Estimate ZTD", "Estimate ZTD+Grad", "", "", ""};
1619 const char* s6[] = {"Broadcast", "Precise", "Broadcast+SBAS", "Broadcast+SSR APC", "Broadcast+SSR CoM", "", "", ""};
1620 const char* s7[] = {"GPS", "GLONASS", "Galileo", "QZSS", "BDS", "NavIC", "SBAS", "", "", ""};
1621 const char* s8[] = {"OFF", "Continuous", "Instantaneous", "Fix and Hold", "", "", ""};
1622 int i;
1623 char* p = (char*)buff;
1624
1625 trace(NULL, 3, "outprcopts:\n");
1626
1627 p += sprintf(p, "%s pos mode : %s\r\n", COMMENTH, s1[opt->mode]);
1628
1629 if (PMODE_DGPS <= opt->mode && opt->mode <= PMODE_PPP_FIXED) {
1630 p += sprintf(p, "%s freqs : %s\r\n", COMMENTH, s2[opt->nf - 1]);
1631 }
1632 if (opt->mode > PMODE_SINGLE) {
1633 p += sprintf(p, "%s solution : %s\r\n", COMMENTH, s3[opt->soltype]);
1634 }
1635 p += sprintf(p, "%s elev mask : %.1f deg\r\n", COMMENTH, opt->elmin * R2D);
1636 if (opt->mode > PMODE_SINGLE) {
1637 p += sprintf(p, "%s dynamics : %s\r\n", COMMENTH, opt->dynamics ? "on" : "off");
1638 p += sprintf(p, "%s tidecorr : %s\r\n", COMMENTH, opt->tidecorr ? "on" : "off");
1639 }
1640 if (opt->mode <= PMODE_PPP_FIXED) {
1641 p += sprintf(p, "%s ionos opt : %s\r\n", COMMENTH, s4[opt->ionoopt]);
1642 }
1643 p += sprintf(p, "%s tropo opt : %s\r\n", COMMENTH, s5[opt->tropopt]);
1644 p += sprintf(p, "%s ephemeris : %s\r\n", COMMENTH, s6[opt->sateph]);
1645 p += sprintf(p, "%s navi sys :", COMMENTH);
1646 for (i = 0; sys[i]; i++) {
1647 if (opt->navsys & sys[i]) {
1648 p += sprintf(p, " %s", s7[i]);
1649 }
1650 }
1651 p += sprintf(p, "\r\n");
1652 p += sprintf(p, "%s ar sys :", COMMENTH);
1653 for (i = 0; sys[i]; i++) {
1654 if (opt->arsys & sys[i]) {
1655 p += sprintf(p, " %s", s7[i]);
1656 }
1657 }
1658 p += sprintf(p, "\r\n");
1659 if (PMODE_KINEMA <= opt->mode && opt->mode <= PMODE_PPP_FIXED) {
1660 p += sprintf(p, "%s amb res : %s\r\n", COMMENTH, s8[opt->modear]);
1661 if (opt->thresar[0] > 0.0) {
1662 p += sprintf(p, "%s val thres : %.1f\r\n", COMMENTH, opt->thresar[0]);
1663 }
1664 }
1665 if (opt->mode == PMODE_MOVEB && opt->baseline[0] > 0.0) {
1666 p += sprintf(p, "%s baseline : %.4f %.4f m\r\n", COMMENTH, opt->baseline[0], opt->baseline[1]);
1667 }
1668 for (i = 0; i < 2; i++) {
1669 if (opt->mode == PMODE_SINGLE || (i >= 1 && opt->mode > PMODE_FIXED)) {
1670 continue;
1671 }
1672 p += sprintf(p, "%s antenna%d : %-21s (%7.4f %7.4f %7.4f)\r\n", COMMENTH, i + 1, opt->anttype[i],
1673 opt->antdel[i][0], opt->antdel[i][1], opt->antdel[i][2]);
1674 }
1675 return p - (char*)buff;
1676}
1677/* output solution header ------------------------------------------------------
1678 * output solution header to buffer
1679 * args : uint8_t *buff IO output buffer
1680 * solopt_t *opt I solution options
1681 * return : number of output bytes
1682 *-----------------------------------------------------------------------------*/
1683int outsolheads(uint8_t* buff, const solopt_t* opt) {
1684 const char *s1[] = {"WGS84", "Tokyo"}, *s2[] = {"ellipsoidal", "geodetic"};
1685 const char *s3[] = {"GPST", "UTC ", "JST "}, *sep = opt2sep(opt);
1686 const char* leg1 = "Q=1:fix,2:float,3:sbas,4:dgps,5:single,6:ppp";
1687 const char* leg2 = "ns=# of satellites";
1688 char* p = (char*)buff;
1689 int timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu);
1690
1691 trace(NULL, 3, "outsolheads:\n");
1692
1693 if (opt->posf == SOLF_NMEA || opt->posf == SOLF_STAT || opt->posf == SOLF_GSIF) {
1694 return 0;
1695 }
1696 if (opt->outhead) {
1697 p += sprintf(p, "%s (", COMMENTH);
1698 if (opt->posf == SOLF_XYZ) {
1699 p += sprintf(p, "x/y/z-ecef=WGS84");
1700 } else if (opt->posf == SOLF_ENU) {
1701 p += sprintf(p, "e/n/u-baseline=WGS84");
1702 } else {
1703 p += sprintf(p, "lat/lon/height=%s/%s", s1[opt->datum], s2[opt->height]);
1704 }
1705 p += sprintf(p, ",%s,%s)\r\n", leg1, leg2);
1706 }
1707 p += sprintf(p, "%s %-*s%s", COMMENTH, (opt->timef ? 16 : 8) + timeu + 1, s3[opt->times], sep);
1708
1709 if (opt->posf == SOLF_LLH) { /* lat/lon/hgt */
1710 if (opt->degf) {
1711 p += sprintf(p,
1712 "%16s%s%16s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s"
1713 "%8s%s%6s%s%6s",
1714 "latitude(d'\")", sep, "longitude(d'\")", sep, "height(m)", sep, "Q", sep, "ns", sep, "sdn(m)",
1715 sep, "sde(m)", sep, "sdu(m)", sep, "sdne(m)", sep, "sdeu(m)", sep, "sdue(m)", sep, "age(s)",
1716 sep, "ratio");
1717 } else {
1718 p += sprintf(p,
1719 "%14s%s%14s%s%10s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s"
1720 "%8s%s%6s%s%6s",
1721 "latitude(deg)", sep, "longitude(deg)", sep, "height(m)", sep, "Q", sep, "ns", sep, "sdn(m)",
1722 sep, "sde(m)", sep, "sdu(m)", sep, "sdne(m)", sep, "sdeu(m)", sep, "sdun(m)", sep, "age(s)",
1723 sep, "ratio");
1724 }
1725 if (opt->outvel) {
1726 p += sprintf(p, "%s%10s%s%10s%s%10s%s%9s%s%8s%s%8s%s%8s%s%8s%s%8s", sep, "vn(m/s)", sep, "ve(m/s)", sep,
1727 "vu(m/s)", sep, "sdvn", sep, "sdve", sep, "sdvu", sep, "sdvne", sep, "sdveu", sep, "sdvun");
1728 }
1729 } else if (opt->posf == SOLF_XYZ) { /* x/y/z-ecef */
1730 p += sprintf(p,
1731 "%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s"
1732 "%s%6s%s%6s",
1733 "x-ecef(m)", sep, "y-ecef(m)", sep, "z-ecef(m)", sep, "Q", sep, "ns", sep, "sdx(m)", sep, "sdy(m)",
1734 sep, "sdz(m)", sep, "sdxy(m)", sep, "sdyz(m)", sep, "sdzx(m)", sep, "age(s)", sep, "ratio");
1735
1736 if (opt->outvel) {
1737 p += sprintf(p, "%s%10s%s%10s%s%10s%s%9s%s%8s%s%8s%s%8s%s%8s%s%8s", sep, "vx(m/s)", sep, "vy(m/s)", sep,
1738 "vz(m/s)", sep, "sdvx", sep, "sdvy", sep, "sdvz", sep, "sdvxy", sep, "sdvyz", sep, "sdvzx");
1739 }
1740 } else if (opt->posf == SOLF_ENU) { /* e/n/u-baseline */
1741 p += sprintf(p,
1742 "%14s%s%14s%s%14s%s%3s%s%3s%s%8s%s%8s%s%8s%s%8s%s%8s%s%8s"
1743 "%s%6s%s%6s",
1744 "e-baseline(m)", sep, "n-baseline(m)", sep, "u-baseline(m)", sep, "Q", sep, "ns", sep, "sde(m)",
1745 sep, "sdn(m)", sep, "sdu(m)", sep, "sden(m)", sep, "sdnu(m)", sep, "sdue(m)", sep, "age(s)", sep,
1746 "ratio");
1747 }
1748 p += sprintf(p, "\r\n");
1749 return p - (char*)buff;
1750}
1751/* std-dev of soltuion -------------------------------------------------------*/
1752static double sol_std(const sol_t* sol) {
1753 /* approximate as max std-dev of 3-axis std-devs */
1754 if (sol->qr[0] > sol->qr[1] && sol->qr[0] > sol->qr[2]) {
1755 return SQRT(sol->qr[0]);
1756 }
1757 if (sol->qr[1] > sol->qr[2]) {
1758 return SQRT(sol->qr[1]);
1759 }
1760 return SQRT(sol->qr[2]);
1761}
1762/* output solution body --------------------------------------------------------
1763 * output solution body to buffer
1764 * args : uint8_t *buff IO output buffer
1765 * sol_t *sol I solution
1766 * double *rb I base station position {x,y,z} (ecef) (m)
1767 * solopt_t *opt I solution options
1768 * return : number of output bytes
1769 *-----------------------------------------------------------------------------*/
1770int outsols(uint8_t* buff, const sol_t* sol, const double* rb, const solopt_t* opt) {
1771 gtime_t time, ts = {0};
1772 double gpst;
1773 int week, timeu;
1774 const char* sep = opt2sep(opt);
1775 char s[64];
1776 uint8_t* p = buff;
1777
1778 trace(NULL, 3, "outsols :\n");
1779
1780 /* suppress output if std is over opt->maxsolstd */
1781 if (opt->maxsolstd > 0.0 && sol_std(sol) > opt->maxsolstd) {
1782 return 0;
1783 }
1784 if (opt->posf == SOLF_NMEA) {
1785 if (opt->nmeaintv[0] < 0.0) {
1786 return 0;
1787 }
1788 if (!screent(sol->time, ts, ts, opt->nmeaintv[0])) {
1789 return 0;
1790 }
1791 }
1792 if (sol->stat <= SOLQ_NONE || (opt->posf == SOLF_ENU && norm(rb, 3) <= 0.0)) {
1793 return 0;
1794 }
1795 timeu = opt->timeu < 0 ? 0 : (opt->timeu > 20 ? 20 : opt->timeu);
1796
1797 time = sol->time;
1798 if (opt->times >= TIMES_UTC) {
1799 time = gpst2utc(time);
1800 }
1801 if (opt->times == TIMES_JST) {
1802 time = timeadd(time, 9 * 3600.0);
1803 }
1804
1805 if (opt->timef) {
1806 time2str(time, s, timeu);
1807 } else {
1808 gpst = time2gpst(time, &week);
1809 if (86400 * 7 - gpst < 0.5 / pow(10.0, timeu)) {
1810 week++;
1811 gpst = 0.0;
1812 }
1813 sprintf(s, "%4d%.16s%*.*f", week, sep, 6 + (timeu <= 0 ? 0 : timeu + 1), timeu, gpst);
1814 }
1815 switch (opt->posf) {
1816 case SOLF_LLH:
1817 p += outpos(p, s, sol, opt);
1818 break;
1819 case SOLF_XYZ:
1820 p += outecef(p, s, sol, opt);
1821 break;
1822 case SOLF_ENU:
1823 p += outenu(p, s, sol, rb, opt);
1824 break;
1825 case SOLF_NMEA:
1826 p += outnmea_rmc(p, sol);
1827 p += outnmea_gga(p, sol);
1828 break;
1829 }
1830 return p - buff;
1831}
1832/* output solution extended ----------------------------------------------------
1833 * output solution exteneded information
1834 * args : uint8_t *buff IO output buffer
1835 * sol_t *sol I solution
1836 * ssat_t *ssat I satellite status
1837 * solopt_t *opt I solution options
1838 * return : number of output bytes
1839 * notes : only support nmea
1840 *-----------------------------------------------------------------------------*/
1841int outsolexs(uint8_t* buff, const sol_t* sol, const ssat_t* ssat, const solopt_t* opt) {
1842 gtime_t ts = {0};
1843 uint8_t* p = buff;
1844
1845 trace(NULL, 3, "outsolexs:\n");
1846
1847 /* suppress output if std is over opt->maxsolstd */
1848 if (opt->maxsolstd > 0.0 && sol_std(sol) > opt->maxsolstd) {
1849 return 0;
1850 }
1851 if (opt->posf == SOLF_NMEA) {
1852 if (opt->nmeaintv[1] < 0.0) {
1853 return 0;
1854 }
1855 if (!screent(sol->time, ts, ts, opt->nmeaintv[1])) {
1856 return 0;
1857 }
1858 }
1859 if (opt->posf == SOLF_NMEA) {
1860 p += outnmea_gsa(p, sol, ssat);
1861 p += outnmea_gsv(p, sol, ssat);
1862 }
1863 return p - buff;
1864}
1865/* output processing option ----------------------------------------------------
1866 * output processing option to file
1867 * args : FILE *fp I output file pointer
1868 * prcopt_t *opt I processing options
1869 * return : none
1870 *-----------------------------------------------------------------------------*/
1871void outprcopt(FILE* fp, const prcopt_t* opt) {
1872 uint8_t buff[MAXSOLMSG + 1];
1873 int n;
1874
1875 trace(NULL, 3, "outprcopt:\n");
1876
1877 if ((n = outprcopts(buff, opt)) > 0) {
1878 fwrite(buff, n, 1, fp);
1879 }
1880}
1881/* output solution header ------------------------------------------------------
1882 * output solution heade to file
1883 * args : FILE *fp I output file pointer
1884 * solopt_t *opt I solution options
1885 * return : none
1886 *-----------------------------------------------------------------------------*/
1887void outsolhead(FILE* fp, const solopt_t* opt) {
1888 uint8_t buff[MAXSOLMSG + 1];
1889 int n;
1890
1891 trace(NULL, 3, "outsolhead:\n");
1892
1893 if ((n = outsolheads(buff, opt)) > 0) {
1894 fwrite(buff, n, 1, fp);
1895 }
1896}
1897/* output solution body --------------------------------------------------------
1898 * output solution body to file
1899 * args : FILE *fp I output file pointer
1900 * sol_t *sol I solution
1901 * double *rb I base station position {x,y,z} (ecef) (m)
1902 * solopt_t *opt I solution options
1903 * return : none
1904 *-----------------------------------------------------------------------------*/
1905void outsol(FILE* fp, const sol_t* sol, const double* rb, const solopt_t* opt) {
1906 uint8_t buff[MAXSOLMSG + 1];
1907 int n;
1908
1909 trace(NULL, 3, "outsol :\n");
1910
1911 if ((n = outsols(buff, sol, rb, opt)) > 0) {
1912 fwrite(buff, n, 1, fp);
1913 }
1914}
1915/* output solution extended ----------------------------------------------------
1916 * output solution exteneded information to file
1917 * args : FILE *fp I output file pointer
1918 * sol_t *sol I solution
1919 * ssat_t *ssat I satellite status
1920 * solopt_t *opt I solution options
1921 * return : output size (bytes)
1922 * notes : only support nmea
1923 *-----------------------------------------------------------------------------*/
1924void outsolex(FILE* fp, const sol_t* sol, const ssat_t* ssat, const solopt_t* opt) {
1925 uint8_t buff[MAXSOLMSG + 1];
1926 int n;
1927
1928 trace(NULL, 3, "outsolex:\n");
1929
1930 if ((n = outsolexs(buff, sol, ssat, opt)) > 0) {
1931 fwrite(buff, n, 1, fp);
1932 }
1933}
int satid2no(const char *id)
Definition mrtk_nav.c:753
#define D2R
Definition mrtk_coords.c:36
void pos2ecef(const double *pos, double *r)
Transform geodetic position to ECEF position.
#define FE_WGS84
Definition mrtk_coords.c:39
#define PI
Definition mrtk_coords.c:35
#define MAXSAT
Definition mrtk_coords.c:45
void covenu(const double *pos, const double *P, double *Q)
Transform ECEF covariance to local tangential coordinate.
#define RE_WGS84
Definition mrtk_coords.c:38
void dops(int ns, const double *azel, double elmin, double *dop)
Compute DOP (dilution of precision).
#define SQRT(x)
void ecef2pos(const double *r, double *pos)
Transform ECEF position to geodetic position.
void ecef2enu(const double *pos, const double *r, double *e)
Transform ECEF vector to local tangential coordinate.
MRTKLIB Coordinates Module — Coordinate transformations and geometry.
int satsys(int sat, int *prn)
Definition mrtk_nav.c:117
#define SYS_GPS
Definition mrtk_eph.c:65
#define SYS_SBS
Definition mrtk_eph.c:66
#define SYS_QZS
Definition mrtk_eph.c:69
#define SYS_IRN
Definition mrtk_eph.c:71
#define SYS_CMP
Definition mrtk_eph.c:70
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
#define SNR_UNIT
#define MAXSOLMSG
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
#define R2D
Definition mrtk_obs.c:51
int screent(gtime_t time, gtime_t ts, gtime_t te, double tint)
Screen data by time range and interval.
Definition mrtk_obs.c:424
const solopt_t solopt_default
Definition mrtk_opt.c:92
#define SOLF_STAT
Definition mrtk_opt.h:67
#define SOLQ_DR
Definition mrtk_opt.h:77
#define SOLQ_SINGLE
Definition mrtk_opt.h:75
#define TIMES_JST
Definition mrtk_opt.h:86
#define PMODE_MOVEB
Definition mrtk_opt.h:49
#define TIMES_GPST
Definition mrtk_opt.h:84
#define SOLF_GSIF
Definition mrtk_opt.h:68
#define SOLQ_FLOAT
Definition mrtk_opt.h:72
#define SOLQ_DGPS
Definition mrtk_opt.h:74
#define SOLF_NMEA
Definition mrtk_opt.h:66
#define PMODE_DGPS
Definition mrtk_opt.h:46
#define SOLF_ENU
Definition mrtk_opt.h:65
#define MAXSOLQ
Definition mrtk_opt.h:78
#define PMODE_KINEMA
Definition mrtk_opt.h:47
#define PMODE_PPP_FIXED
Definition mrtk_opt.h:53
#define TIMES_UTC
Definition mrtk_opt.h:85
#define PMODE_FIXED
Definition mrtk_opt.h:50
#define SOLQ_NONE
Definition mrtk_opt.h:70
#define SOLQ_PPP
Definition mrtk_opt.h:76
#define SOLF_XYZ
Definition mrtk_opt.h:64
#define SOLF_LLH
Definition mrtk_opt.h:63
#define SOLQ_SBAS
Definition mrtk_opt.h:73
#define SOLQ_FIX
Definition mrtk_opt.h:71
#define PMODE_SINGLE
Definition mrtk_opt.h:45
#define COMMENTH
void freesolstatbuf(solstatbuf_t *solstatbuf)
Free solution status buffer memory.
Definition mrtk_sol.c:1123
static int decode_solxyz(char *buff, const solopt_t *opt, sol_t *sol)
Definition mrtk_sol.c:465
static int decode_nmearmc(char **val, int n, sol_t *sol)
Definition mrtk_sol.c:159
static double sol_std(const sol_t *sol)
Definition mrtk_sol.c:1752
int outprcopts(uint8_t *buff, const prcopt_t *opt)
Output processing options to buffer.
Definition mrtk_sol.c:1609
static char * decode_soltime(char *buff, const solopt_t *opt, gtime_t *time)
Definition mrtk_sol.c:397
int readsolstat(char *files[], int nfile, solstatbuf_t *statbuf)
Read solution status from files.
Definition mrtk_sol.c:1293
void freesolbuf(solbuf_t *solbuf)
Free solution buffer memory.
Definition mrtk_sol.c:1111
int inputsol(uint8_t data, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf)
Input solution data from stream.
Definition mrtk_sol.c:874
void enu2ecef(const double *pos, const double *e, double *r)
static void covtosol_vel(const double *P, sol_t *sol)
Definition mrtk_sol.c:150
#define NMEA_TID
Definition mrtk_sol.c:60
int outsols(uint8_t *buff, const sol_t *sol, const double *rb, const solopt_t *opt)
Output solution to buffer.
Definition mrtk_sol.c:1770
static int test_solstat(const char *buff)
Definition mrtk_sol.c:360
static const char * opt2sep(const solopt_t *opt)
Definition mrtk_sol.c:85
int addsol(solbuf_t *solbuf, const sol_t *sol)
Add solution to buffer.
Definition mrtk_sol.c:1017
static const int nmea_sid[]
Definition mrtk_sol.c:70
int readsolstatt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf)
Read solution status from files with time filter.
Definition mrtk_sol.c:1261
static int tonum(char *buff, const char *sep, double *v)
Definition mrtk_sol.c:94
void outprcopt(FILE *fp, const prcopt_t *opt)
Output processing options to file.
Definition mrtk_sol.c:1871
#define MSG_DISCONN
Definition mrtk_sol.c:44
int outnmea_gsv(uint8_t *buff, const sol_t *sol, const ssat_t *ssat)
Output NMEA GSV sentence.
Definition mrtk_sol.c:1552
static void septime(double t, double *t1, double *t2, double *t3)
Definition mrtk_sol.c:116
static const int nmea_sys[]
Definition mrtk_sol.c:66
static int readsolstatdata(FILE *fp, gtime_t ts, gtime_t te, double tint, solstatbuf_t *statbuf)
Definition mrtk_sol.c:1232
static double dmm2deg(double dmm)
Definition mrtk_sol.c:114
void outsolex(FILE *fp, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt)
Output solution extended data to file.
Definition mrtk_sol.c:1924
static int decode_nmea(char *buff, sol_t *sol)
Definition mrtk_sol.c:369
static int decode_solenu(char *buff, const solopt_t *opt, sol_t *sol)
Definition mrtk_sol.c:634
static double sqvar(double covar)
Definition mrtk_sol.c:112
static int outpos(uint8_t *buff, const char *s, const sol_t *sol, const solopt_t *opt)
Definition mrtk_sol.c:1325
void covecef(const double *pos, const double *Q, double *P)
gtime_t gpst2time(int week, double sec)
Definition mrtk_time.c:165
void initsolbuf(solbuf_t *solbuf, int cyclic, int nmax)
Initialize solution buffer.
Definition mrtk_sol.c:1078
int outsolexs(uint8_t *buff, const sol_t *sol, const ssat_t *ssat, const solopt_t *opt)
Output solution extended data to buffer.
Definition mrtk_sol.c:1841
static const char * nmea_tid[]
Definition mrtk_sol.c:68
static int decode_solpos(char *buff, const solopt_t *opt, sol_t *sol)
Definition mrtk_sol.c:729
static int cmpsol(const void *p1, const void *p2)
Definition mrtk_sol.c:930
static const int nmea_solq[]
Definition mrtk_sol.c:72
static int cmpsolstat(const void *p1, const void *p2)
Definition mrtk_sol.c:1131
static int decode_nmeazda(char **val, int n, sol_t *sol)
Definition mrtk_sol.c:231
static int decode_solgsi(char *buff, const solopt_t *opt, sol_t *sol)
Definition mrtk_sol.c:712
static int readsoldata(FILE *fp, gtime_t ts, gtime_t te, double tint, int qflag, const solopt_t *opt, solbuf_t *solbuf)
Definition mrtk_sol.c:917
static int outecef(uint8_t *buff, const char *s, const sol_t *sol, const solopt_t *opt)
Definition mrtk_sol.c:1301
static int sort_solstat(solstatbuf_t *statbuf)
Definition mrtk_sol.c:1137
static void readsolopt(FILE *fp, solopt_t *opt)
Definition mrtk_sol.c:852
#define MAXFIELD
Definition mrtk_sol.c:61
static int decode_solstat(char *buff, solstat_t *stat)
Definition mrtk_sol.c:1167
static int decode_solllh(char *buff, const solopt_t *opt, sol_t *sol)
Definition mrtk_sol.c:542
static void covtosol(const double *P, sol_t *sol)
Definition mrtk_sol.c:132
static void decode_solopt(char *buff, solopt_t *opt)
Definition mrtk_sol.c:807
int outnmea_rmc(uint8_t *buff, const sol_t *sol)
Output NMEA RMC sentence.
Definition mrtk_sol.c:1390
void outsolhead(FILE *fp, const solopt_t *opt)
Output solution header to file.
Definition mrtk_sol.c:1887
#define SQR(x)
Definition mrtk_sol.c:57
static int decode_solsss(char *buff, sol_t *sol)
Definition mrtk_sol.c:686
static int outenu(uint8_t *buff, const char *s, const sol_t *sol, const double *rb, const solopt_t *opt)
Definition mrtk_sol.c:1366
int outnmea_gga(uint8_t *buff, const sol_t *sol)
Output NMEA GGA sentence.
Definition mrtk_sol.c:1447
double norm(const double *a, int n)
Definition mrtk_mat.c:165
int outnmea_gsa(uint8_t *buff, const sol_t *sol, const ssat_t *ssat)
Output NMEA GSA sentence.
Definition mrtk_sol.c:1493
void outsol(FILE *fp, const sol_t *sol, const double *rb, const solopt_t *opt)
Output solution to file.
Definition mrtk_sol.c:1905
static void decode_refpos(char *buff, const solopt_t *opt, double *rb)
Definition mrtk_sol.c:755
int readsolt(char *files[], int nfile, gtime_t ts, gtime_t te, double tint, int qflag, solbuf_t *solbuf)
Read solutions from files with time filter.
Definition mrtk_sol.c:978
#define KNOT2M
Definition mrtk_sol.c:64
void deg2dms(double deg, double *dms, int ndec)
Definition mrtk_coords.c:63
static int decode_nmeagga(char **val, int n, sol_t *sol)
Definition mrtk_sol.c:262
static int decode_sol(char *buff, const solopt_t *opt, sol_t *sol, double *rb)
Definition mrtk_sol.c:783
static int test_nmea(const char *buff)
Definition mrtk_sol.c:350
int outsolheads(uint8_t *buff, const solopt_t *opt)
Output solution header to buffer.
Definition mrtk_sol.c:1683
static void soltocov_vel(const sol_t *sol, double *P)
Definition mrtk_sol.c:141
static void soltocov(const sol_t *sol, double *P)
Definition mrtk_sol.c:123
sol_t * getsol(solbuf_t *solbuf, int index)
Get solution from buffer.
Definition mrtk_sol.c:1060
double geoidh(const double *pos)
Definition mrtk_geoid.c:268
#define SQRT(x)
Definition mrtk_sol.c:58
static int sort_solbuf(solbuf_t *solbuf)
Definition mrtk_sol.c:936
int readsol(char *files[], int nfile, solbuf_t *sol)
Read solutions from files.
Definition mrtk_sol.c:1004
static void addsolstat(solstatbuf_t *statbuf, const solstat_t *stat)
Definition mrtk_sol.c:1213
double dms2deg(const double *dms)
Definition mrtk_coords.c:86
MRTKLIB Solution Module — Solution data types and I/O functions.
MRTKLIB System Module — OS/filesystem utility functions.
gtime_t epoch2time(const double *ep)
Convert calendar day/time to gtime_t struct.
Definition mrtk_time.c:113
void time2epoch(gtime_t t, double *ep)
Convert gtime_t struct to calendar day/time.
Definition mrtk_time.c:136
void time2str(gtime_t t, char *s, int n)
Convert gtime_t struct to string.
Definition mrtk_time.c:507
gtime_t gpst2utc(gtime_t t)
Convert GPS time to UTC considering leap seconds.
Definition mrtk_time.c:422
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
double time2gpst(gtime_t t, int *week)
Convert gtime_t to GPS week/tow.
Definition mrtk_time.c:181
gtime_t timeadd(gtime_t t, double sec)
Add seconds to gtime_t.
Definition mrtk_time.c:265
char * time_str(gtime_t t, int n)
Get time string (not reentrant).
Definition mrtk_time.c:530
gtime_t utc2gpst(gtime_t t)
Convert UTC to GPS time considering leap seconds.
Definition mrtk_time.c:440
void trace(mrtk_ctx_t *ctx, int level, const char *format,...)
Output trace message.
Definition mrtk_trace.c:78
MRTKLIB Trace Module — debug trace and logging functions.
GNSS time representation.
Definition mrtk_time.h:49
double sec
Definition mrtk_time.h:51
time_t time
Definition mrtk_time.h:50
int soltype
Definition mrtk_opt.h:173
double elmin
Definition mrtk_opt.h:176
int ionoopt
Definition mrtk_opt.h:187
char anttype[2][MAXANT]
Definition mrtk_opt.h:222
int arsys
Definition mrtk_opt.h:182
int dynamics
Definition mrtk_opt.h:189
int tropopt
Definition mrtk_opt.h:188
double baseline[2]
Definition mrtk_opt.h:219
double thresar[8]
Definition mrtk_opt.h:211
int modear
Definition mrtk_opt.h:179
int mode
Definition mrtk_opt.h:172
int tidecorr
Definition mrtk_opt.h:190
double antdel[2][3]
Definition mrtk_opt.h:223
int navsys
Definition mrtk_opt.h:175
int sateph
Definition mrtk_opt.h:178
float ratio
Definition mrtk_sol.h:57
float qr[6]
Definition mrtk_sol.h:48
double rr[6]
Definition mrtk_sol.h:46
float thres
Definition mrtk_sol.h:58
uint8_t type
Definition mrtk_sol.h:53
uint8_t stat
Definition mrtk_sol.h:54
double dtr[NSYS+1]
Definition mrtk_sol.h:52
float age
Definition mrtk_sol.h:56
uint8_t ns
Definition mrtk_sol.h:55
float qv[6]
Definition mrtk_sol.h:51
gtime_t time
Definition mrtk_sol.h:45
int nmax
Definition mrtk_sol.h:64
int n
Definition mrtk_sol.h:64
gtime_t time
Definition mrtk_sol.h:67
int nb
Definition mrtk_sol.h:71
uint8_t buff[MAXSOLMSG+1]
Definition mrtk_sol.h:70
double rb[3]
Definition mrtk_sol.h:69
int end
Definition mrtk_sol.h:66
int cyclic
Definition mrtk_sol.h:65
sol_t * data
Definition mrtk_sol.h:68
int start
Definition mrtk_sol.h:66
int datum
Definition mrtk_opt.h:360
int height
Definition mrtk_opt.h:361
int degf
Definition mrtk_opt.h:356
double nmeaintv[2]
Definition mrtk_opt.h:366
int timef
Definition mrtk_opt.h:354
int outvel
Definition mrtk_opt.h:359
int times
Definition mrtk_opt.h:353
int timeu
Definition mrtk_opt.h:355
double maxsolstd
Definition mrtk_opt.h:370
int posf
Definition mrtk_opt.h:352
int outhead
Definition mrtk_opt.h:357
char sep[64]
Definition mrtk_opt.h:368
float az
Definition mrtk_sol.h:78
uint8_t flag
Definition mrtk_sol.h:81
uint8_t sat
Definition mrtk_sol.h:76
gtime_t time
Definition mrtk_sol.h:75
uint16_t slipc
Definition mrtk_sol.h:85
float resp
Definition mrtk_sol.h:79
uint16_t lock
Definition mrtk_sol.h:83
uint16_t outc
Definition mrtk_sol.h:84
float el
Definition mrtk_sol.h:78
uint16_t rejc
Definition mrtk_sol.h:86
uint16_t snr
Definition mrtk_sol.h:82
uint8_t frq
Definition mrtk_sol.h:77
float resc
Definition mrtk_sol.h:80
solstat_t * data
Definition mrtk_sol.h:91
double azel[2]
Definition mrtk_sol.h:101
uint16_t snr[5]
Definition mrtk_sol.h:105
#define malloc(x)
Definition toml.c:52
#define free(x)
Definition toml.c:53