MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_ionex.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_ionex.c : IONEX TEC grid 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_ionex.h"
17
18#include <math.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "mrtklib/mrtk_atmos.h"
23#include "mrtklib/mrtk_sys.h"
24#include "mrtklib/mrtk_trace.h"
25
26/*--- local constants (duplicated to avoid rtklib.h dependency) -------------*/
27static const double PI = 3.1415926535897932;
28static const double R2D = 180.0 / 3.1415926535897932;
29static const double CLIGHT = 299792458.0;
30static const double FREQ1 = 1.57542E9;
31
32#define SQR(x) ((x) * (x))
33#define VAR_NOTEC SQR(30.0) /* variance of no tec */
34#define MIN_EL 0.0 /* min elevation angle (rad) */
35#define MIN_HGT -1000.0 /* min user height (m) */
36
37/*--- forward declarations for legacy functions resolved at link time -------*/
38
39/* get index -----------------------------------------------------------------*/
40static int getindex(double value, const double* range) {
41 if (range[2] == 0.0) {
42 return 0;
43 }
44 if (range[1] > 0.0 && (value < range[0] || range[1] < value)) {
45 return -1;
46 }
47 if (range[1] < 0.0 && (value < range[1] || range[0] < value)) {
48 return -1;
49 }
50 return (int)floor((value - range[0]) / range[2] + 0.5);
51}
52/* get number of items -------------------------------------------------------*/
53static int nitem(const double* range) { return getindex(range[1], range) + 1; }
54/* data index (i:lat,j:lon,k:hgt) --------------------------------------------*/
55static int dataindex(int i, int j, int k, const int* ndata) {
56 if (i < 0 || ndata[0] <= i || j < 0 || ndata[1] <= j || k < 0 || ndata[2] <= k) {
57 return -1;
58 }
59 return i + ndata[0] * (j + ndata[1] * k);
60}
61/* add tec data to navigation data -------------------------------------------*/
62static tec_t* addtec(const double* lats, const double* lons, const double* hgts, double rb, nav_t* nav) {
63 tec_t *p, *nav_tec;
64 gtime_t time0 = {0};
65 int i, n, ndata[3];
66
67 trace(NULL, 3, "addtec :\n");
68
69 ndata[0] = nitem(lats);
70 ndata[1] = nitem(lons);
71 ndata[2] = nitem(hgts);
72 if (ndata[0] <= 1 || ndata[1] <= 1 || ndata[2] <= 0) {
73 return NULL;
74 }
75
76 if (nav->nt >= nav->ntmax) {
77 nav->ntmax += 256;
78 if (!(nav_tec = (tec_t*)realloc(nav->tec, sizeof(tec_t) * nav->ntmax))) {
79 trace(NULL, 1, "readionex malloc error ntmax=%d\n", nav->ntmax);
80 free(nav->tec);
81 nav->tec = NULL;
82 nav->nt = nav->ntmax = 0;
83 return NULL;
84 }
85 nav->tec = nav_tec;
86 }
87 p = nav->tec + nav->nt;
88 p->time = time0;
89 p->rb = rb;
90 for (i = 0; i < 3; i++) {
91 p->ndata[i] = ndata[i];
92 p->lats[i] = lats[i];
93 p->lons[i] = lons[i];
94 p->hgts[i] = hgts[i];
95 }
96 n = ndata[0] * ndata[1] * ndata[2];
97
98 if (!(p->data = (double*)malloc(sizeof(double) * n)) || !(p->rms = (float*)malloc(sizeof(float) * n))) {
99 return NULL;
100 }
101 for (i = 0; i < n; i++) {
102 p->data[i] = 0.0;
103 p->rms[i] = 0.0f;
104 }
105 nav->nt++;
106 return p;
107}
108/* read ionex dcb aux data ----------------------------------------------------*/
109static void readionexdcb(FILE* fp, double* dcb, double* rms) {
110 int i, sat;
111 char buff[1024], id[32], *label;
112
113 trace(NULL, 3, "readionexdcb:\n");
114
115 for (i = 0; i < MAXSAT; i++) {
116 dcb[i] = rms[i] = 0.0;
117 }
118
119 while (fgets(buff, sizeof(buff), fp)) {
120 if (strlen(buff) < 60) {
121 continue;
122 }
123 label = buff + 60;
124
125 if (strstr(label, "PRN / BIAS / RMS") == label) {
126 strncpy(id, buff + 3, 3);
127 id[3] = '\0';
128
129 if (!(sat = satid2no(id))) {
130 trace(NULL, 2, "ionex invalid satellite: %s\n", id);
131 continue;
132 }
133 dcb[sat - 1] = str2num(buff, 6, 10);
134 rms[sat - 1] = str2num(buff, 16, 10);
135 } else if (strstr(label, "END OF AUX DATA") == label) {
136 break;
137 }
138 }
139}
140/* read ionex header ---------------------------------------------------------*/
141static double readionexh(FILE* fp, double* lats, double* lons, double* hgts, double* rb, double* nexp, double* dcb,
142 double* rms) {
143 double ver = 0.0;
144 char buff[1024], *label;
145
146 trace(NULL, 3, "readionexh:\n");
147
148 while (fgets(buff, sizeof(buff), fp)) {
149 if (strlen(buff) < 60) {
150 continue;
151 }
152 label = buff + 60;
153
154 if (strstr(label, "IONEX VERSION / TYPE") == label) {
155 if (buff[20] == 'I') {
156 ver = str2num(buff, 0, 8);
157 }
158 } else if (strstr(label, "BASE RADIUS") == label) {
159 *rb = str2num(buff, 0, 8);
160 } else if (strstr(label, "HGT1 / HGT2 / DHGT") == label) {
161 hgts[0] = str2num(buff, 2, 6);
162 hgts[1] = str2num(buff, 8, 6);
163 hgts[2] = str2num(buff, 14, 6);
164 } else if (strstr(label, "LAT1 / LAT2 / DLAT") == label) {
165 lats[0] = str2num(buff, 2, 6);
166 lats[1] = str2num(buff, 8, 6);
167 lats[2] = str2num(buff, 14, 6);
168 } else if (strstr(label, "LON1 / LON2 / DLON") == label) {
169 lons[0] = str2num(buff, 2, 6);
170 lons[1] = str2num(buff, 8, 6);
171 lons[2] = str2num(buff, 14, 6);
172 } else if (strstr(label, "EXPONENT") == label) {
173 *nexp = str2num(buff, 0, 6);
174 } else if (strstr(label, "START OF AUX DATA") == label && strstr(buff, "DIFFERENTIAL CODE BIASES")) {
175 readionexdcb(fp, dcb, rms);
176 } else if (strstr(label, "END OF HEADER") == label) {
177 return ver;
178 }
179 }
180 return 0.0;
181}
182/* read ionex body -----------------------------------------------------------*/
183static int readionexb(FILE* fp, const double* lats, const double* lons, const double* hgts, double rb, double nexp,
184 nav_t* nav) {
185 tec_t* p = NULL;
186 gtime_t time = {0};
187 double lat, lon[3], hgt, x;
188 int i, j, k, n, m, index, type = 0;
189 char buff[1024], *label = buff + 60;
190
191 trace(NULL, 3, "readionexb:\n");
192
193 while (fgets(buff, sizeof(buff), fp)) {
194 if (strlen(buff) < 60) {
195 continue;
196 }
197
198 if (strstr(label, "START OF TEC MAP") == label) {
199 if ((p = addtec(lats, lons, hgts, rb, nav))) {
200 type = 1;
201 }
202 } else if (strstr(label, "END OF TEC MAP") == label) {
203 type = 0;
204 p = NULL;
205 } else if (strstr(label, "START OF RMS MAP") == label) {
206 type = 2;
207 p = NULL;
208 } else if (strstr(label, "END OF RMS MAP") == label) {
209 type = 0;
210 p = NULL;
211 } else if (strstr(label, "EPOCH OF CURRENT MAP") == label) {
212 if (str2time(buff, 0, 36, &time)) {
213 trace(NULL, 2, "ionex epoch invalid: %-36.36s\n", buff);
214 continue;
215 }
216 if (type == 2) {
217 for (i = nav->nt - 1; i >= 0; i--) {
218 if (fabs(timediff(time, nav->tec[i].time)) >= 1.0) {
219 continue;
220 }
221 p = nav->tec + i;
222 break;
223 }
224 } else if (p) {
225 p->time = time;
226 }
227 } else if (strstr(label, "LAT/LON1/LON2/DLON/H") == label && p) {
228 lat = str2num(buff, 2, 6);
229 lon[0] = str2num(buff, 8, 6);
230 lon[1] = str2num(buff, 14, 6);
231 lon[2] = str2num(buff, 20, 6);
232 hgt = str2num(buff, 26, 6);
233
234 i = getindex(lat, p->lats);
235 k = getindex(hgt, p->hgts);
236 n = nitem(lon);
237
238 for (m = 0; m < n; m++) {
239 if (m % 16 == 0 && !fgets(buff, sizeof(buff), fp)) {
240 break;
241 }
242
243 j = getindex(lon[0] + lon[2] * m, p->lons);
244 if ((index = dataindex(i, j, k, p->ndata)) < 0) {
245 continue;
246 }
247
248 if ((x = str2num(buff, m % 16 * 5, 5)) == 9999.0) {
249 continue;
250 }
251
252 if (type == 1) {
253 p->data[index] = x * pow(10.0, nexp);
254 } else {
255 p->rms[index] = (float)(x * pow(10.0, nexp));
256 }
257 }
258 }
259 }
260 return 1;
261}
262/* combine tec grid data -----------------------------------------------------*/
263static void combtec(nav_t* nav) {
264 tec_t tmp;
265 int i, j, n = 0;
266
267 trace(NULL, 3, "combtec : nav->nt=%d\n", nav->nt);
268
269 for (i = 0; i < nav->nt - 1; i++) {
270 for (j = i + 1; j < nav->nt; j++) {
271 if (timediff(nav->tec[j].time, nav->tec[i].time) < 0.0) {
272 tmp = nav->tec[i];
273 nav->tec[i] = nav->tec[j];
274 nav->tec[j] = tmp;
275 }
276 }
277 }
278 for (i = 0; i < nav->nt; i++) {
279 if (i > 0 && timediff(nav->tec[i].time, nav->tec[n - 1].time) == 0.0) {
280 free(nav->tec[n - 1].data);
281 free(nav->tec[n - 1].rms);
282 nav->tec[n - 1] = nav->tec[i];
283 continue;
284 }
285 nav->tec[n++] = nav->tec[i];
286 }
287 nav->nt = n;
288
289 trace(NULL, 4, "combtec : nav->nt=%d\n", nav->nt);
290}
291/* read ionex tec grid file ----------------------------------------------------
292 * read ionex ionospheric tec grid file
293 * args : char *file I ionex tec grid file
294 * (wind-card * is expanded)
295 * nav_t *nav IO navigation data
296 * nav->nt, nav->ntmax and nav->tec are modified
297 * int opt I read option (1: no clear of tec data,0:clear)
298 * return : none
299 * notes : see ref [1]
300 *-----------------------------------------------------------------------------*/
301void readtec(const char* file, nav_t* nav, int opt) {
302 FILE* fp;
303 double lats[3] = {0}, lons[3] = {0}, hgts[3] = {0}, rb = 0.0, nexp = -1.0;
304 double dcb[MAXSAT] = {0}, rms[MAXSAT] = {0};
305 int i, n;
306 char* efiles[MAXEXFILE];
307
308 trace(NULL, 3, "readtec : file=%s\n", file);
309
310 /* clear of tec grid data option */
311 if (!opt) {
312 free(nav->tec);
313 nav->tec = NULL;
314 nav->nt = nav->ntmax = 0;
315 }
316 for (i = 0; i < MAXEXFILE; i++) {
317 if (!(efiles[i] = (char*)malloc(1024))) {
318 for (i--; i >= 0; i--) {
319 free(efiles[i]);
320 }
321 return;
322 }
323 }
324 /* expand wild card in file path */
325 n = expath(file, efiles, MAXEXFILE);
326
327 for (i = 0; i < n; i++) {
328 if (!(fp = fopen(efiles[i], "r"))) {
329 trace(NULL, 2, "ionex file open error %s\n", efiles[i]);
330 continue;
331 }
332 /* read ionex header */
333 if (readionexh(fp, lats, lons, hgts, &rb, &nexp, dcb, rms) <= 0.0) {
334 trace(NULL, 2, "ionex file format error %s\n", efiles[i]);
335 continue;
336 }
337 /* read ionex body */
338 readionexb(fp, lats, lons, hgts, rb, nexp, nav);
339
340 fclose(fp);
341 }
342 for (i = 0; i < MAXEXFILE; i++) {
343 free(efiles[i]);
344 }
345
346 /* combine tec grid data */
347 if (nav->nt > 0) {
348 combtec(nav);
349 }
350
351 /* P1-P2 dcb */
352 for (i = 0; i < MAXSAT; i++) {
353 nav->cbias[i][0] = CLIGHT * dcb[i] * 1E-9; /* ns->m */
354 }
355}
356/* interpolate tec grid data -------------------------------------------------*/
357static int interptec(const tec_t* tec, int k, const double* posp, double* value, double* rms) {
358 double dlat, dlon, a, b, d[4] = {0}, r[4] = {0};
359 int i, j, n, index;
360
361 trace(NULL, 3, "interptec: k=%d posp=%.2f %.2f\n", k, posp[0] * R2D, posp[1] * R2D);
362 *value = *rms = 0.0;
363
364 if (tec->lats[2] == 0.0 || tec->lons[2] == 0.0) {
365 return 0;
366 }
367
368 dlat = posp[0] * R2D - tec->lats[0];
369 dlon = posp[1] * R2D - tec->lons[0];
370 if (tec->lons[2] > 0.0) {
371 dlon -= floor(dlon / 360) * 360.0; /* 0<=dlon<360 */
372 } else {
373 dlon += floor(-dlon / 360) * 360.0; /* -360<dlon<=0 */
374 }
375
376 a = dlat / tec->lats[2];
377 b = dlon / tec->lons[2];
378 i = (int)floor(a);
379 a -= i;
380 j = (int)floor(b);
381 b -= j;
382
383 /* get gridded tec data */
384 for (n = 0; n < 4; n++) {
385 if ((index = dataindex(i + (n % 2), j + (n < 2 ? 0 : 1), k, tec->ndata)) < 0) {
386 continue;
387 }
388 d[n] = tec->data[index];
389 r[n] = tec->rms[index];
390 }
391 if (d[0] > 0.0 && d[1] > 0.0 && d[2] > 0.0 && d[3] > 0.0) {
392 /* bilinear interpolation (inside of grid) */
393 *value = (1.0 - a) * (1.0 - b) * d[0] + a * (1.0 - b) * d[1] + (1.0 - a) * b * d[2] + a * b * d[3];
394 *rms = (1.0 - a) * (1.0 - b) * r[0] + a * (1.0 - b) * r[1] + (1.0 - a) * b * r[2] + a * b * r[3];
395 }
396 /* nearest-neighbour extrapolation (outside of grid) */
397 else if (a <= 0.5 && b <= 0.5 && d[0] > 0.0) {
398 *value = d[0];
399 *rms = r[0];
400 } else if (a > 0.5 && b <= 0.5 && d[1] > 0.0) {
401 *value = d[1];
402 *rms = r[1];
403 } else if (a <= 0.5 && b > 0.5 && d[2] > 0.0) {
404 *value = d[2];
405 *rms = r[2];
406 } else if (a > 0.5 && b > 0.5 && d[3] > 0.0) {
407 *value = d[3];
408 *rms = r[3];
409 } else {
410 i = 0;
411 for (n = 0; n < 4; n++) {
412 if (d[n] > 0.0) {
413 i++;
414 *value += d[n];
415 *rms += r[n];
416 }
417 }
418 if (i == 0) {
419 return 0;
420 }
421 *value /= i;
422 *rms /= i;
423 }
424 return 1;
425}
426/* ionosphere delay by tec grid data -----------------------------------------*/
427static int iondelay(gtime_t time, const tec_t* tec, const double* pos, const double* azel, int opt, double* delay,
428 double* var) {
429 const double fact = 40.30E16 / FREQ1 / FREQ1; /* tecu->L1 iono (m) */
430 double fs, posp[3] = {0}, vtec, rms, hion, rp;
431 int i;
432
433 trace(NULL, 3, "iondelay: time=%s pos=%.1f %.1f azel=%.1f %.1f\n", time_str(time, 0), pos[0] * R2D, pos[1] * R2D,
434 azel[0] * R2D, azel[1] * R2D);
435
436 *delay = *var = 0.0;
437
438 for (i = 0; i < tec->ndata[2]; i++) { /* for a layer */
439
440 hion = tec->hgts[0] + tec->hgts[2] * i;
441
442 /* ionospheric pierce point position */
443 fs = ionppp(pos, azel, tec->rb, hion, posp);
444
445 if (opt & 2) {
446 /* modified single layer mapping function (M-SLM) ref [2] */
447 rp = tec->rb / (tec->rb + hion) * sin(0.9782 * (PI / 2.0 - azel[1]));
448 fs = 1.0 / sqrt(1.0 - rp * rp);
449 }
450 if (opt & 1) {
451 /* earth rotation correction (sun-fixed coordinate) */
452 posp[1] += 2.0 * PI * timediff(time, tec->time) / 86400.0;
453 }
454 /* interpolate tec grid data */
455 if (!interptec(tec, i, posp, &vtec, &rms)) {
456 return 0;
457 }
458
459 *delay += fact * fs * vtec;
460 *var += fact * fact * fs * fs * rms * rms;
461 }
462 trace(NULL, 4, "iondelay: delay=%7.2f std=%6.2f\n", *delay, sqrt(*var));
463
464 return 1;
465}
466/* ionosphere model by tec grid data -------------------------------------------
467 * compute ionospheric delay by tec grid data
468 * args : gtime_t time I time (gpst)
469 * nav_t *nav I navigation data
470 * double *pos I receiver position {lat,lon,h} (rad,m)
471 * double *azel I azimuth/elevation angle {az,el} (rad)
472 * int opt I model option
473 * bit0: 0:earth-fixed,1:sun-fixed
474 * bit1: 0:single-layer,1:modified single-layer
475 * double *delay O ionospheric delay (L1) (m)
476 * double *var O ionospheric dealy (L1) variance (m^2)
477 * return : status (1:ok,0:error)
478 * notes : before calling the function, read tec grid data by calling readtec()
479 * return ok with delay=0 and var=VAR_NOTEC if el<MIN_EL or h<MIN_HGT
480 *-----------------------------------------------------------------------------*/
481int iontec(gtime_t time, const nav_t* nav, const double* pos, const double* azel, int opt, double* delay, double* var) {
482 double dels[2], vars[2], a, tt;
483 int i, stat[2];
484
485 trace(NULL, 3, "iontec : time=%s pos=%.1f %.1f azel=%.1f %.1f\n", time_str(time, 0), pos[0] * R2D, pos[1] * R2D,
486 azel[0] * R2D, azel[1] * R2D);
487
488 if (azel[1] < MIN_EL || pos[2] < MIN_HGT) {
489 *delay = 0.0;
490 *var = VAR_NOTEC;
491 return 1;
492 }
493 for (i = 0; i < nav->nt; i++) {
494 if (timediff(nav->tec[i].time, time) > 0.0) {
495 break;
496 }
497 }
498 if (i == 0 || i >= nav->nt) {
499 trace(NULL, 2, "%s: tec grid out of period\n", time_str(time, 0));
500 return 0;
501 }
502 if ((tt = timediff(nav->tec[i].time, nav->tec[i - 1].time)) == 0.0) {
503 trace(NULL, 2, "tec grid time interval error\n");
504 return 0;
505 }
506 /* ionospheric delay by tec grid data */
507 stat[0] = iondelay(time, nav->tec + i - 1, pos, azel, opt, dels, vars);
508 stat[1] = iondelay(time, nav->tec + i, pos, azel, opt, dels + 1, vars + 1);
509
510 if (!stat[0] && !stat[1]) {
511 trace(NULL, 2, "%s: tec grid out of area pos=%6.2f %7.2f azel=%6.1f %5.1f\n", time_str(time, 0), pos[0] * R2D,
512 pos[1] * R2D, azel[0] * R2D, azel[1] * R2D);
513 return 0;
514 }
515 if (stat[0] && stat[1]) { /* linear interpolation by time */
516 a = timediff(time, nav->tec[i - 1].time) / tt;
517 *delay = dels[0] * (1.0 - a) + dels[1] * a;
518 *var = vars[0] * (1.0 - a) + vars[1] * a;
519 } else if (stat[0]) { /* nearest-neighbour extrapolation by time */
520 *delay = dels[0];
521 *var = vars[0];
522 } else {
523 *delay = dels[1];
524 *var = vars[1];
525 }
526 trace(NULL, 3, "iontec : delay=%5.2f std=%5.2f\n", *delay, sqrt(*var));
527 return 1;
528}
double ionppp(const double *pos, const double *azel, double re, double hion, double *posp)
Compute ionospheric pierce point (IPP) position and slant factor.
Definition mrtk_atmos.c:131
MRTKLIB Atmosphere Module — Ionospheric and tropospheric models.
int satid2no(const char *id)
Definition mrtk_nav.c:753
#define CLIGHT
Definition mrtk_coords.c:41
#define PI
Definition mrtk_coords.c:35
#define MAXSAT
Definition mrtk_coords.c:45
struct nav_s nav_t
Definition mrtk_eph.h:40
#define MAXEXFILE
static const double range[4]
Definition mrtk_geoid.c:37
static tec_t * addtec(const double *lats, const double *lons, const double *hgts, double rb, nav_t *nav)
Definition mrtk_ionex.c:62
static void combtec(nav_t *nav)
Definition mrtk_ionex.c:263
#define VAR_NOTEC
Definition mrtk_ionex.c:33
#define MIN_HGT
Definition mrtk_ionex.c:35
void readtec(const char *file, nav_t *nav, int opt)
Read IONEX ionospheric TEC grid file.
Definition mrtk_ionex.c:301
int iontec(gtime_t time, const nav_t *nav, const double *pos, const double *azel, int opt, double *delay, double *var)
Compute ionospheric delay by TEC grid data.
Definition mrtk_ionex.c:481
#define MIN_EL
Definition mrtk_ionex.c:34
static void readionexdcb(FILE *fp, double *dcb, double *rms)
Definition mrtk_ionex.c:109
static int dataindex(int i, int j, int k, const int *ndata)
Definition mrtk_ionex.c:55
static int readionexb(FILE *fp, const double *lats, const double *lons, const double *hgts, double rb, double nexp, nav_t *nav)
Definition mrtk_ionex.c:183
static double readionexh(FILE *fp, double *lats, double *lons, double *hgts, double *rb, double *nexp, double *dcb, double *rms)
Definition mrtk_ionex.c:141
static int getindex(double value, const double *range)
Definition mrtk_ionex.c:40
static int nitem(const double *range)
Definition mrtk_ionex.c:53
static int iondelay(gtime_t time, const tec_t *tec, const double *pos, const double *azel, int opt, double *delay, double *var)
Definition mrtk_ionex.c:427
static int interptec(const tec_t *tec, int k, const double *posp, double *value, double *rms)
Definition mrtk_ionex.c:357
MRTKLIB IONEX Module — IONEX ionospheric TEC map I/O and interpolation.
#define R2D
Definition mrtk_obs.c:51
#define FREQ1
Definition mrtk_obs.c:67
int expath(const char *path, char *paths[], int nmax)
Expand file path with wild-card (*) in file.
Definition mrtk_sys.c:96
double str2num(const char *s, int i, int n)
Convert substring in string to number.
Definition mrtk_sys.c:60
MRTKLIB System Module — OS/filesystem utility functions.
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
char * time_str(gtime_t t, int n)
Get time string (not reentrant).
Definition mrtk_time.c:530
int str2time(const char *s, int i, int n, gtime_t *t)
Convert substring in string to gtime_t struct.
Definition mrtk_time.c:87
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
int nt
Definition mrtk_nav.h:399
tec_t * tec
Definition mrtk_nav.h:412
int ntmax
Definition mrtk_nav.h:399
double cbias[MAXSAT][3]
Definition mrtk_nav.h:427
gtime_t time
Definition mrtk_nav.h:87
float * rms
Definition mrtk_nav.h:94
double hgts[3]
Definition mrtk_nav.h:92
int ndata[3]
Definition mrtk_nav.h:88
double lats[3]
Definition mrtk_nav.h:90
double rb
Definition mrtk_nav.h:89
double lons[3]
Definition mrtk_nav.h:91
double * data
Definition mrtk_nav.h:93
#define malloc(x)
Definition toml.c:52
#define free(x)
Definition toml.c:53