MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_rtksvr.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_rtksvr.c : real-time RTK server 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_rtksvr.h"
17
18#include <math.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "mrtklib/mrtk_coords.h"
24#include "mrtklib/mrtk_eph.h"
25#include "mrtklib/mrtk_l6.h"
26#include "mrtklib/mrtk_madoca.h"
28#include "mrtklib/mrtk_mat.h"
29#include "mrtklib/mrtk_nav.h"
30#include "mrtklib/mrtk_obs.h"
31#include "mrtklib/mrtk_opt.h"
32#include "mrtklib/mrtk_peph.h"
33#include "mrtklib/mrtk_ppp.h"
34#include "mrtklib/mrtk_rcvraw.h"
35#include "mrtklib/mrtk_rinex.h"
36#include "mrtklib/mrtk_rtcm.h"
37#include "mrtklib/mrtk_rtkpos.h"
38#include "mrtklib/mrtk_sbas.h"
39#include "mrtklib/mrtk_sol.h"
40#include "mrtklib/mrtk_spp.h"
41#include "mrtklib/mrtk_stream.h"
42#include "mrtklib/mrtk_sys.h"
43#include "mrtklib/mrtk_time.h"
44#include "mrtklib/mrtk_trace.h"
45
46/* Local constants (duplicated from rtklib.h to avoid dependency) */
47#define SYS_GPS 0x01
48#define SYS_GLO 0x04
49#define SYS_GAL 0x08
50#define SYS_QZS 0x10
51#define SYS_CMP 0x20
52
53#define R2D (180.0 / 3.1415926535897932)
54
55#define COMMENTH "%"
56
57/* Forward declarations for functions in rtklib (resolved at link time) */
58extern void timeset(gtime_t t);
59
60#define MIN_INT_RESET 30000 /* mininum interval of reset command (ms) */
61
62/* write solution header to output stream ------------------------------------*/
63static void writesolhead(stream_t* stream, const solopt_t* solopt) {
64 uint8_t buff[1024];
65 int n;
66
67 n = outsolheads(buff, solopt);
68 strwrite(stream, buff, n);
69}
70/* save output buffer --------------------------------------------------------*/
71static void saveoutbuf(rtksvr_t* svr, uint8_t* buff, int n, int index) {
72 rtksvrlock(svr);
73
74 n = n < svr->buffsize - svr->nsb[index] ? n : svr->buffsize - svr->nsb[index];
75 memcpy(svr->sbuf[index] + svr->nsb[index], buff, n);
76 svr->nsb[index] += n;
77
78 rtksvrunlock(svr);
79}
80/* write solution to output stream -------------------------------------------*/
81static void writesol(rtksvr_t* svr, int index) {
82 solopt_t solopt = solopt_default;
83 uint8_t buff[MAXSOLMSG + 1];
84 int i, n;
85
86 tracet(NULL, 4, "writesol: index=%d\n", index);
87
88 for (i = 0; i < 2; i++) {
89 if (svr->solopt[i].posf == SOLF_STAT) {
90 /* output solution status */
91 rtksvrlock(svr);
92 n = rtkoutstat(&svr->rtk, (char*)buff);
93 rtksvrunlock(svr);
94 } else {
95 /* output solution */
96 n = outsols(buff, &svr->rtk.sol, svr->rtk.rb, svr->solopt + i);
97 }
98 strwrite(svr->stream + i + 3, buff, n);
99
100 /* save output buffer */
101 saveoutbuf(svr, buff, n, i);
102
103 /* output extended solution */
104 n = outsolexs(buff, &svr->rtk.sol, svr->rtk.ssat, svr->solopt + i);
105 strwrite(svr->stream + i + 3, buff, n);
106
107 /* save output buffer */
108 saveoutbuf(svr, buff, n, i);
109 }
110 /* output solution to monitor port */
111 if (svr->moni) {
112 n = outsols(buff, &svr->rtk.sol, svr->rtk.rb, &solopt);
113 strwrite(svr->moni, buff, n);
114 }
115 /* save solution buffer */
116 if (svr->nsol < MAXSOLBUF) {
117 rtksvrlock(svr);
118 svr->solbuf[svr->nsol++] = svr->rtk.sol;
119 rtksvrunlock(svr);
120 }
121}
122/* update glonass frequency channel number in raw data struct ----------------*/
123static void update_glofcn(rtksvr_t* svr) {
124 int i, j, sat, frq;
125
126 for (i = 0; i < MAXPRNGLO; i++) {
127 sat = satno(SYS_GLO, i + 1);
128
129 for (j = 0, frq = -999; j < 3; j++) {
130 if (svr->raw[j].nav.geph[i].sat != sat) {
131 continue;
132 }
133 frq = svr->raw[j].nav.geph[i].frq;
134 }
135 if (frq < -7 || frq > 6) {
136 continue;
137 }
138
139 for (j = 0; j < 3; j++) {
140 if (svr->raw[j].nav.geph[i].sat == sat) {
141 continue;
142 }
143 svr->raw[j].nav.geph[i].sat = sat;
144 svr->raw[j].nav.geph[i].frq = frq;
145 }
146 }
147}
148/* update observation data ---------------------------------------------------*/
149static void update_obs(rtksvr_t* svr, obs_t* obs, int index, int iobs) {
150 int i, n = 0, sat, sys;
151
152 if (iobs < MAXOBSBUF) {
153 for (i = 0; i < obs->n; i++) {
154 sat = obs->data[i].sat;
155 sys = satsys(sat, NULL);
156 if (svr->rtk.opt.exsats[sat - 1] == 1 || !(sys & svr->rtk.opt.navsys)) {
157 continue;
158 }
159 svr->obs[index][iobs].data[n] = obs->data[i];
160 svr->obs[index][iobs].data[n++].rcv = index + 1;
161 }
162 svr->obs[index][iobs].n = n;
163 sortobs(&svr->obs[index][iobs]);
164 }
165 svr->nmsg[index][0]++;
166}
167/* update ephemeris ----------------------------------------------------------*/
168static void update_eph(rtksvr_t* svr, nav_t* nav, int ephsat, int ephset, int index) {
169 eph_t *eph1, *eph2, *eph3;
170 geph_t *geph1, *geph2, *geph3;
171 int prn;
172
173 if (satsys(ephsat, &prn) != SYS_GLO) {
174 if (!svr->navsel || svr->navsel == index + 1) {
175 /* svr->nav.eph={current_set1,current_set2,prev_set1,prev_set2} */
176 eph1 = nav->eph + ephsat - 1 + MAXSAT * ephset; /* received */
177 eph2 = svr->nav.eph + ephsat - 1 + MAXSAT * ephset; /* current */
178 eph3 = svr->nav.eph + ephsat - 1 + MAXSAT * (2 + ephset); /* previous */
179 if (eph2->ttr.time == 0 || (eph1->iode != eph3->iode && eph1->iode != eph2->iode) ||
180 (timediff(eph1->toe, eph3->toe) != 0.0 && timediff(eph1->toe, eph2->toe) != 0.0) ||
181 (timediff(eph1->toc, eph3->toc) != 0.0 && timediff(eph1->toc, eph2->toc) != 0.0)) {
182 *eph3 = *eph2; /* current ->previous */
183 *eph2 = *eph1; /* received->current */
184 }
185 }
186 svr->nmsg[index][1]++;
187 } else {
188 if (!svr->navsel || svr->navsel == index + 1) {
189 geph1 = nav->geph + prn - 1;
190 geph2 = svr->nav.geph + prn - 1;
191 geph3 = svr->nav.geph + prn - 1 + MAXPRNGLO;
192 if (geph2->tof.time == 0 || (geph1->iode != geph3->iode && geph1->iode != geph2->iode)) {
193 *geph3 = *geph2;
194 *geph2 = *geph1;
195 update_glofcn(svr);
196 }
197 }
198 svr->nmsg[index][6]++;
199 }
200}
201/* update sbas message -------------------------------------------------------*/
202static void update_sbs(rtksvr_t* svr, sbsmsg_t* sbsmsg, int index) {
203 int i, sbssat = svr->rtk.opt.sbassatsel;
204
205 if (sbsmsg && (sbssat == sbsmsg->prn || sbssat == 0)) {
206 sbsmsg->rcv = index + 1;
207 if (svr->nsbs < MAXSBSMSG) {
208 svr->sbsmsg[svr->nsbs++] = *sbsmsg;
209 } else {
210 for (i = 0; i < MAXSBSMSG - 1; i++) {
211 svr->sbsmsg[i] = svr->sbsmsg[i + 1];
212 }
213 svr->sbsmsg[i] = *sbsmsg;
214 }
215 sbsupdatecorr(sbsmsg, &svr->nav);
216 }
217 svr->nmsg[index][3]++;
218}
219/* update ion/utc parameters -------------------------------------------------*/
220static void update_ionutc(rtksvr_t* svr, nav_t* nav, int index) {
221 if (svr->navsel == 0 || svr->navsel == index + 1) {
222 matcpy(svr->nav.utc_gps, nav->utc_gps, 8, 1);
223 matcpy(svr->nav.utc_glo, nav->utc_glo, 8, 1);
224 matcpy(svr->nav.utc_gal, nav->utc_gal, 8, 1);
225 matcpy(svr->nav.utc_qzs, nav->utc_qzs, 8, 1);
226 matcpy(svr->nav.utc_cmp, nav->utc_cmp, 8, 1);
227 matcpy(svr->nav.utc_irn, nav->utc_irn, 9, 1);
228 matcpy(svr->nav.utc_sbs, nav->utc_sbs, 4, 1);
229 matcpy(svr->nav.ion_gps, nav->ion_gps, 8, 1);
230 matcpy(svr->nav.ion_gal, nav->ion_gal, 4, 1);
231 matcpy(svr->nav.ion_qzs, nav->ion_qzs, 8, 1);
232 matcpy(svr->nav.ion_cmp, nav->ion_cmp, 8, 1);
233 matcpy(svr->nav.ion_irn, nav->ion_irn, 8, 1);
234 }
235 svr->nmsg[index][2]++;
236}
237/* update antenna position ---------------------------------------------------*/
238static void update_antpos(rtksvr_t* svr, int index) {
239 sta_t* sta;
240 double pos[3], del[3] = {0}, dr[3];
241 int i;
242
243 if (svr->rtk.opt.refpos == POSOPT_RTCM && index == 1) {
244 if (svr->format[1] == STRFMT_RTCM2 || svr->format[1] == STRFMT_RTCM3) {
245 sta = &svr->rtcm[1].sta;
246 } else {
247 sta = &svr->raw[1].sta;
248 }
249 /* update base station position */
250 for (i = 0; i < 3; i++) {
251 svr->rtk.rb[i] = sta->pos[i];
252 }
253 /* antenna delta */
254 ecef2pos(svr->rtk.rb, pos);
255 if (sta->deltype) { /* xyz */
256 del[2] = -sta->hgt; /* marker = ARP - ant-height */
257 enu2ecef(pos, del, dr);
258 for (i = 0; i < 3; i++) {
259 svr->rtk.rb[i] += sta->del[i] + dr[i];
260 }
261 } else { /* enu */
262 enu2ecef(pos, sta->del, dr);
263 for (i = 0; i < 3; i++) {
264 svr->rtk.rb[i] += dr[i];
265 }
266 }
267 }
268 svr->nmsg[index][4]++;
269}
270/* update ssr corrections ----------------------------------------------------*/
271static void update_ssr(rtksvr_t* svr, int index) {
272 int i, sys, prn, iode;
273 int ephset = 0; /* augmentation target is Galileo I/NAV ref [1] */
274
275 for (i = 0; i < MAXSAT; i++) {
276 if (!svr->rtcm[index].ssr[i].update) {
277 continue;
278 }
279
280 /* check consistency between iods of orbit and clock */
281 if (svr->rtcm[index].ssr[i].iod[0] != svr->rtcm[index].ssr[i].iod[1]) {
282 continue;
283 }
284 svr->rtcm[index].ssr[i].update = 0;
285
286 iode = svr->rtcm[index].ssr[i].iode;
287 sys = satsys(i + 1, &prn);
288
289 /* check corresponding ephemeris exists */
290 if (sys == SYS_GPS || sys == SYS_GAL || sys == SYS_QZS) {
291 if (svr->nav.eph[i + MAXSAT * ephset].iode != iode &&
292 svr->nav.eph[i + MAXSAT * (2 + ephset)].iode != iode) {
293 continue;
294 }
295 } else if (sys == SYS_GLO) {
296 if (svr->nav.geph[prn - 1].iode != iode && svr->nav.geph[prn - 1 + MAXPRNGLO].iode != iode) {
297 continue;
298 }
299 }
300 svr->nav.ssr_ch[0][i] = svr->rtcm[index].ssr[i];
301 }
302 svr->nmsg[index][7]++;
303}
304/* update stat corrections ---------------------------------------------------*/
305static void update_stat(rtksvr_t* svr, int index) {
306 int stano;
307 char staname[8];
308 stat_t* stat = &svr->nav.stat;
309 sstat_t* sstat = &svr->sstat;
310
311 /* update stat corrections */
312 sprintf(staname, "sta%d", index);
313 stano = getstano(stat, staname);
314
315 stat->time[stano] = sstat->time;
316 memcpy(&stat->sion[stano], &sstat->sion, sizeof(ion_t) * MAXSAT);
317 memcpy(&stat->strp[stano], &sstat->strp, sizeof(trp_t));
318
319 svr->nmsg[index][10]++;
320}
321/* update local corrections ---------------------------------------------------*/
322static void update_lcl(rtksvr_t* svr, int index) {
323 if (block2stat(&svr->rtcm[index], &svr->nav.stat) == 0) {
324 return;
325 }
326
327 svr->nmsg[index][11]++;
328}
329/* SSR ownership of nav.ssr_ch[0] ---------------------------------------------
330 * In CLAS/PPP-RTK mode the CLAS decoder owns nav->ssr_ch[0]: MADOCA-PPP SSR
331 * must not overwrite the CLAS biases, whether it comes from a mixed UBX/SBF
332 * L6D+L6E stream (code 15) or a raw L6E stream (STRFMT_L6E, code 10). In
333 * PPP/MADOCA mode svr->clas is NULL and MADOCA SSR is applied like any other
334 * ssr_ch[0] source; RTCM3 SSR (code 10) is applied in every mode.
335 *-----------------------------------------------------------------------------*/
336extern int rtksvr_ssr_applicable(const rtksvr_t* svr, int index, int ret) {
337 if (!svr->clas) {
338 return 1;
339 }
340 if (ret == 15) {
341 return 0;
342 }
343 return !(ret == 10 && svr->format[index] == STRFMT_L6E);
344}
345/* update rtk server struct --------------------------------------------------*/
346static void update_svr(rtksvr_t* svr, int ret, obs_t* obs, nav_t* nav, int ephsat, int ephset, sbsmsg_t* sbsmsg,
347 int index, int iobs) {
348 tracet(NULL, 4, "updatesvr: ret=%d ephsat=%d ephset=%d index=%d\n", ret, ephsat, ephset, index);
349
350 if (ret == 1) { /* observation data */
351 update_obs(svr, obs, index, iobs);
352 } else if (ret == 2) { /* ephemeris */
353 update_eph(svr, nav, ephsat, ephset, index);
354 } else if (ret == 3) { /* sbas message */
355 update_sbs(svr, sbsmsg, index);
356 } else if (ret == 9) { /* ion/utc parameters */
357 update_ionutc(svr, nav, index);
358 } else if (ret == 5) { /* antenna position */
359 update_antpos(svr, index);
360 } else if (ret == 7) { /* dgps correction */
361 svr->nmsg[index][5]++;
362 } else if (ret == 10 || ret == 15) { /* ssr message / MADOCA L6E SSR (raw SBF/UBX) */
363 if (rtksvr_ssr_applicable(svr, index, ret)) update_ssr(svr, index);
364 } else if (ret == 17) { /* QZNMA L6 frame (UBX/SBF): no consumer until CMA (#353) */
365 svr->nmsg[index][8]++;
366 } else if (ret == 11) { /* stat message */
367 update_stat(svr, index);
368 } else if (ret == 12) { /* local message */
369 update_lcl(svr, index);
370 } else if (ret == -1) { /* error */
371 svr->nmsg[index][9]++;
372 }
373}
374/* decode receiver raw/rtcm data ---------------------------------------------*/
375static int decoderaw(rtksvr_t* svr, int index) {
376 obs_t* obs = {0};
377 nav_t* nav = {0};
378 sbsmsg_t* sbsmsg = NULL;
379 int i, ret = 0, ephsat = 0, ephset = 0, fobs = 0;
380 static int init_flg = 1;
381 static char buff[3][4096];
382 static int nbyte[3] = {0};
383
384 tracet(NULL, 4, "decoderaw: index=%d\n", index);
385
386 rtksvrlock(svr);
387
388 if (init_flg && svr->rtcm[0].time.time != 0) {
389 init_mcssr(svr->rtcm[0].time);
390 init_flg = 0;
391 }
392
393 for (i = 0; i < svr->nb[index]; i++) {
394 /* input rtcm/receiver raw data from stream */
395 if (svr->format[index] == STRFMT_RTCM2) {
396 ret = input_rtcm2(svr->rtcm + index, svr->buff[index][i]);
397 obs = &svr->rtcm[index].obs;
398 nav = &svr->rtcm[index].nav;
399 ephsat = svr->rtcm[index].ephsat;
400 ephset = svr->rtcm[index].ephset;
401 } else if (svr->format[index] == STRFMT_RTCM3) {
402 ret = input_rtcm3(svr->rtcm + index, svr->buff[index][i]);
403 obs = &svr->rtcm[index].obs;
404 nav = &svr->rtcm[index].nav;
405 ephsat = svr->rtcm[index].ephsat;
406 ephset = svr->rtcm[index].ephset;
407 } else if (svr->format[index] == STRFMT_L6E) {
408 ret = input_qzssl6e(svr->rtcm + index, svr->buff[index][i]);
409 } else if (svr->format[index] == STRFMT_CLAS) {
410 if (svr->clas) {
411 int ch = (index == 1) ? 1 : 0; /* index 1 (inpstr2)→ch1, index 2 (inpstr3)→ch0 */
412
413 /* rate-limit: stop processing L6 when it runs too
414 * far ahead of observation time. This prevents the
415 * bank ring buffer from overwriting corrections that
416 * the positioning engine still needs. The 60-second
417 * margin accommodates the initial time offset between
418 * obs and L6 data while keeping corrections within
419 * the trop age limit (120 s). */
420 {
421 gtime_t l6t = svr->clas->l6buf[ch].time;
422 gtime_t obst = svr->raw[0].time;
423 if (l6t.time > 0 && obst.time > 0 && timediff(l6t, obst) > 60.0) {
424 /* shift unconsumed bytes to start of buffer */
425 int remain = svr->nb[index] - i;
426 if (remain > 0) {
427 memmove(svr->buff[index], svr->buff[index] + i, remain);
428 }
429 svr->nb[index] = remain;
430 break;
431 }
432 }
433
434 /* initialize GPS week reference from obs time (once) */
435 if (svr->clas->week_ref[0] == 0) {
436 gtime_t t = svr->raw[0].time;
437 if (t.time > 0) {
438 int j, week;
439 time2gpst(t, &week);
440 for (j = 0; j < CSSR_REF_MAX; j++) {
441 svr->clas->week_ref[j] = week;
442 svr->clas->tow_ref[j] = -1;
443 }
444 }
445 }
446 clas_input_cssr(svr->clas, svr->buff[index][i], ch);
447 ret = clas_decode_msg(svr->clas, ch);
448 if (ret == 10) {
449 int net = svr->clas->grid[ch].network;
450 if (net > 0) {
451 int rc =
452 clas_bank_get_close(svr->clas, svr->clas->l6buf[ch].time, net, ch, &svr->clas->current[ch]);
453 if (rc == 0) {
454 clas_update_global(&svr->nav, &svr->clas->current[ch], ch);
455 clas_check_grid_status(svr->clas, &svr->clas->current[ch], ch);
456 }
457 } else if (svr->clas->bank[ch] && svr->clas->bank[ch]->use) {
458 /* bootstrap: scan all networks until grid resolved */
459 clas_corr_t* tmp = (clas_corr_t*)calloc(1, sizeof(clas_corr_t));
460 if (tmp) {
461 int net2, nfound = 0;
462 for (net2 = 1; net2 < CLAS_MAX_NETWORK; net2++) {
463 if (clas_bank_get_close(svr->clas, svr->clas->l6buf[ch].time, net2, ch, tmp) == 0) {
464 nfound++;
465 clas_check_grid_status(svr->clas, tmp, ch);
466 clas_update_global(&svr->nav, tmp, ch);
467 }
468 }
469 free(tmp);
470 }
471 }
472 }
473 }
474 } else if (svr->format[index] == STRFMT_STAT) {
475 /* decode stat */
476 ret = input_stat(&svr->sstat, svr->buff[index][i], buff[index], &nbyte[index]);
477 } else {
478 ret = input_raw(svr->raw + index, svr->rtcm + index, svr->format[index], svr->buff[index][i]);
479 obs = &svr->raw[index].obs;
480 nav = &svr->raw[index].nav;
481 ephsat = svr->raw[index].ephsat;
482 ephset = svr->raw[index].ephset;
483 sbsmsg = &svr->raw[index].sbsmsg;
484 }
485#if 0 /* record for receiving tick for debug */
486 if (ret==1) {
487 trace(NULL,0,"%d %10d T=%s NS=%2d\n",index,tickget(),
488 time_str(obs->data[0].time,0),obs->n);
489 }
490#endif
491 /* update rtk server (skip update_ssr for UBX/SBF L6 frame-ready codes:
492 * ret=10 means "CLAS L6 frame ready for redirect" and ret=16 means
493 * "MADOCA-PPP iono L6 frame ready", neither is a decoded SSR) */
494 if (ret > 0 &&
495 !((ret == 10 || ret == 16) && (svr->format[index] == STRFMT_UBX || svr->format[index] == STRFMT_SEPT))) {
496 update_svr(svr, ret, obs, nav, ephsat, ephset, sbsmsg, index, fobs);
497 }
498 /* redirect a CLAS L6 frame to the CLAS decoder (UBX/SBF → CLAS path).
499 * Raw L6D byte streams use STRFMT_CLAS above; STRFMT_L6E carries
500 * MADOCA-PPP only (CLAS is broadcast on L6D only). */
501 if (svr->clas && ret == 10 && (svr->format[index] == STRFMT_UBX || svr->format[index] == STRFMT_SEPT)) {
502 int k, ch, cret, max_cret = 0;
503
504 /* UBX/SBF: a multi-channel L6 receiver (e.g. u-blox D9C, 2ch)
505 * interleaves L6D frames from several QZS PRNs in one stream.
506 * Route each frame to a CLAS channel keyed by transmit pattern,
507 * keeping each channel locked to a single active PRN so subframe
508 * assembly stays coherent across a satellite handover (#197).
509 * ch<0 means the frame is from a non-active PRN — drop it so the
510 * locked channel's subframe stream is not corrupted by mixing. */
511 l6_header_t hdr;
512 l6_parse_header(svr->rtcm[index].buff, &hdr);
513 /* current rover observation time drives the demux re-lock
514 * timeout. The rover decodes into raw[0] (receiver-raw formats)
515 * or rtcm[0] (RTCM2/3); pick whichever is fresher so the clock
516 * advances for RTCM3 rovers too — otherwise the timeout never
517 * fires and a set satellite's stale lock is never released (#205). */
518 gtime_t now = svr->raw[0].time;
519 if (!now.time || (svr->rtcm[0].time.time && timediff(svr->rtcm[0].time, now) > 0)) {
520 now = svr->rtcm[0].time;
521 }
522 ch = clas_route_l6frame(svr->clas, hdr.prn, hdr.ptn, svr->rtk.opt.l6mrg, now);
523 if (ch < 0) {
524 continue;
525 }
526
527 trace(NULL, 3, "L6 redirect: fmt=%d ch=%d prn=%d index=%d\n", svr->format[index], ch,
528 svr->rtcm[index].buff[4], index);
529 /* initialize week_ref from obs time (once) */
530 if (svr->clas->week_ref[0] == 0) {
531 gtime_t t = svr->raw[0].time;
532 if (t.time > 0) {
533 int j, week;
534 time2gpst(t, &week);
535 for (j = 0; j < CSSR_REF_MAX; j++) {
536 svr->clas->week_ref[j] = week;
537 svr->clas->tow_ref[j] = -1;
538 }
539 trace(NULL, 2, "L6 redirect: week_ref initialized to %d\n", week);
540 }
541 }
542 /* feed 250-byte L6 frame from rtcm->buff to CLAS decoder */
543 clas_set_l6tag(svr->clas, ch, svr->raw[index].l6tag);
544 for (k = 0; k < 250; k++) {
545 clas_input_cssr(svr->clas, svr->rtcm[index].buff[k], ch);
546 cret = clas_decode_msg(svr->clas, ch);
547 if (cret > max_cret) {
548 max_cret = cret;
549 }
550 if (cret == 10) {
551 trace(NULL, 2, "L6 CSSR epoch: ch=%d k=%d\n", ch, k);
552 int net = svr->clas->grid[ch].network;
553 if (net > 0) {
554 if (clas_bank_get_close(svr->clas, svr->clas->l6buf[ch].time, net, ch,
555 &svr->clas->current[ch]) == 0) {
556 clas_update_global(&svr->nav, &svr->clas->current[ch], ch);
557 clas_check_grid_status(svr->clas, &svr->clas->current[ch], ch);
558 }
559 } else if (svr->clas->bank[ch] && svr->clas->bank[ch]->use) {
560 clas_corr_t* tmp = (clas_corr_t*)calloc(1, sizeof(clas_corr_t));
561 if (tmp) {
562 int net2;
563 for (net2 = 1; net2 < CLAS_MAX_NETWORK; net2++) {
564 if (clas_bank_get_close(svr->clas, svr->clas->l6buf[ch].time, net2, ch, tmp) == 0) {
565 clas_check_grid_status(svr->clas, tmp, ch);
566 clas_update_global(&svr->nav, tmp, ch);
567 }
568 }
569 free(tmp);
570 }
571 }
572 }
573 }
574 if (max_cret > 0) {
575 trace(NULL, 2, "L6 redirect: max_cret=%d ch=%d nframe=%d havebit=%d nbit=%d\n", max_cret, ch,
576 svr->clas->l6buf[ch].nframe, svr->clas->l6buf[ch].havebit, svr->clas->l6buf[ch].nbit);
577 }
578 }
579 /* route an L6D MADOCA-PPP ionospheric frame (SBF/UBX, ret==16) to the
580 * per-PRN iono decoder; on a completed message copy the decoded region
581 * into nav.pppiono->re[rid], exactly as update_qzssl6d() does in
582 * mrtk_postpos.c. The decoder slot comes from the L6 PRN table
583 * (PRN 200/201); the L6 header PRN is at frame byte 4. */
584 if (svr->mdciono && ret == 16 && (svr->format[index] == STRFMT_UBX || svr->format[index] == STRFMT_SEPT)) {
585 int prn = svr->rtcm[index].buff[4];
586 const l6_prn_info_t* info = l6_prn_info(prn);
587 int j = info ? info->iono_slot : -1;
588 if (j >= 0) {
589 int k, a, s;
590 /* lazily anchor the GPST week from the freshest stream time so a
591 * recorded-stream replay decodes in the correct week (live RT
592 * falls back to wall-clock in adjweek() otherwise). */
593 if (svr->mdciono[0].time.time == 0) {
594 gtime_t t = svr->raw[index].time.time ? svr->raw[index].time : svr->raw[0].time;
595 if (t.time) {
596 int m;
597 init_miono(t);
598 for (m = 0; m < MIONO_MAX_PRN; m++) {
599 svr->mdciono[m].time = t;
600 svr->mdciono[m].nbyte = 0;
601 svr->mdciono[m].re.rvalid = 0;
602 strncpy(svr->mdciono[m].opt, svr->rtk.opt.pppopt, sizeof(svr->mdciono[m].opt) - 1);
603 svr->mdciono[m].opt[sizeof(svr->mdciono[m].opt) - 1] = '\0'; /* guarantee NUL termination */
604 }
605 }
606 }
607 if (svr->mdciono[0].time.time) { /* feed only once the week is anchored */
608 for (k = 0; k < 250; k++) {
609 if (input_qzssl6d(&svr->mdciono[j], svr->rtcm[index].buff[k]) == 10 &&
610 svr->mdciono[j].re.rvalid) {
611 int rid = svr->mdciono[j].rid;
612 svr->nav.pppiono->re[rid] = svr->mdciono[j].re;
613 svr->mdciono[j].re.rvalid = 0;
614 for (a = 0; a < MIONO_MAX_ANUM; a++) {
615 svr->mdciono[j].re.area[a].avalid = 0;
616 for (s = 0; s < MAXSAT; s++) {
617 svr->mdciono[j].re.area[a].sat[s].t0.time = 0;
618 }
619 }
620 trace(NULL, 3, "L6D iono: prn=%d rid=%d index=%d\n", prn, rid, index);
621 }
622 }
623 }
624 }
625 }
626 /* route a staged Galileo HAS page (any SBF stream slot) to the HAS
627 * decoder; on a completed MT1 (ret==10) copy corrections into the SSR
628 * channel with the same gating as mrtk_postpos.c update_gal_has(). */
629 if (svr->has && ret == 13) {
630 gtime_t htime = svr->raw[index].has_time;
631 int hret = has_input_page(svr->has, svr->raw[index].has_prn, svr->raw[index].has_page, htime);
632 if (hret == 10) {
633 int k;
634 for (k = 0; k < MAXSAT; k++) {
635 if (!svr->has->ssr[k].update || svr->has->ssr[k].iod[0] != svr->has->ssr[k].iod[1] ||
636 timediff(htime, svr->has->ssr[k].t0[0]) < -1E-3) {
637 continue;
638 }
639 svr->nav.ssr_ch[0][k] = svr->has->ssr[k];
640 svr->has->ssr[k].update = 0;
641 }
642 trace(NULL, 2, "HAS MT1 decoded: prn=%d index=%d\n", svr->raw[index].has_prn, index);
643 }
644 }
645 /* observation data received */
646 if (ret == 1) {
647 if (fobs < MAXOBSBUF) {
648 fobs++;
649 } else {
650 svr->prcout++;
651 }
652 }
653 }
654 svr->nb[index] = 0;
655
656 rtksvrunlock(svr);
657
658 return fobs;
659}
660/* decode download file ------------------------------------------------------*/
661static void decodefile(rtksvr_t* svr, int index) {
662 nav_t* nav;
663 char file[1024];
664 int nb;
665
666 tracet(NULL, 4, "decodefile: index=%d\n", index);
667
668 rtksvrlock(svr);
669
670 /* check file path completed */
671 if ((nb = svr->nb[index]) <= 2 || svr->buff[index][nb - 2] != '\r' || svr->buff[index][nb - 1] != '\n') {
672 rtksvrunlock(svr);
673 return;
674 }
675 strncpy(file, (char*)svr->buff[index], nb - 2);
676 file[nb - 2] = '\0';
677 svr->nb[index] = 0;
678
679 rtksvrunlock(svr);
680
681 /* heap-allocate nav_t (too large for thread stack with ssr_ch[2][MAXSAT]) */
682 if (!(nav = (nav_t*)calloc(1, sizeof(nav_t)))) {
683 tracet(NULL, 1, "decodefile: nav_t alloc error\n");
684 return;
685 }
686
687 if (svr->format[index] == STRFMT_SP3) { /* precise ephemeris */
688
689 /* read sp3 precise ephemeris */
690 readsp3(file, nav, 0);
691 if (nav->ne <= 0) {
692 tracet(NULL, 1, "sp3 file read error: %s\n", file);
693 free(nav);
694 return;
695 }
696 /* update precise ephemeris */
697 rtksvrlock(svr);
698
699 if (svr->nav.peph) {
700 free(svr->nav.peph);
701 }
702 svr->nav.ne = svr->nav.nemax = nav->ne;
703 svr->nav.peph = nav->peph;
704 svr->ftime[index] = utc2gpst(timeget());
705 strcpy(svr->files[index], file);
706
707 rtksvrunlock(svr);
708 } else if (svr->format[index] == STRFMT_RNXCLK) { /* precise clock */
709
710 /* read rinex clock */
711 if (readrnxc(file, nav) <= 0) {
712 tracet(NULL, 1, "rinex clock file read error: %s\n", file);
713 free(nav);
714 return;
715 }
716 /* update precise clock */
717 rtksvrlock(svr);
718
719 if (svr->nav.pclk) {
720 free(svr->nav.pclk);
721 }
722 svr->nav.nc = svr->nav.ncmax = nav->nc;
723 svr->nav.pclk = nav->pclk;
724 svr->ftime[index] = utc2gpst(timeget());
725 strcpy(svr->files[index], file);
726
727 rtksvrunlock(svr);
728 }
729 free(nav);
730}
731
732/* periodic command ----------------------------------------------------------*/
733static void periodic_cmd(int cycle, const char* cmd, stream_t* stream) {
734 const char *p = cmd, *q;
735 char msg[1024], *r;
736 int n, period;
737
738 for (p = cmd;; p = q + 1) {
739 for (q = p;; q++) {
740 if (*q == '\r' || *q == '\n' || *q == '\0') {
741 break;
742 }
743 }
744 n = (int)(q - p);
745 strncpy(msg, p, n);
746 msg[n] = '\0';
747
748 period = 0;
749 if ((r = strrchr(msg, '#'))) {
750 sscanf(r, "# %d", &period);
751 *r = '\0';
752 while (*--r == ' ') {
753 *r = '\0'; /* delete tail spaces */
754 }
755 }
756 if (period <= 0) {
757 period = 1000;
758 }
759 if (*msg && cycle % period == 0) {
760 strsendcmd(stream, msg);
761 }
762 if (!*q) {
763 break;
764 }
765 }
766}
767/* baseline length -----------------------------------------------------------*/
768static double baseline_len(const rtk_t* rtk) {
769 double dr[3];
770 int i;
771
772 if (norm(rtk->sol.rr, 3) <= 0.0 || norm(rtk->rb, 3) <= 0.0) {
773 return 0.0;
774 }
775
776 for (i = 0; i < 3; i++) {
777 dr[i] = rtk->sol.rr[i] - rtk->rb[i];
778 }
779 return norm(dr, 3) * 0.001; /* (km) */
780}
781/* send nmea request to base/nrtk input stream -------------------------------*/
782static void send_nmea(rtksvr_t* svr, uint32_t* tickreset) {
783 sol_t sol_nmea = {{0}};
784 double vel, bl;
785 uint32_t tick = tickget();
786 int i;
787
788 if (svr->stream[1].state != 1) {
789 return;
790 }
791
792 if (svr->nmeareq == 1) { /* lat-lon-hgt mode */
793 sol_nmea.stat = SOLQ_SINGLE;
794 sol_nmea.time = utc2gpst(timeget());
795 matcpy(sol_nmea.rr, svr->nmeapos, 3, 1);
796 strsendnmea(svr->stream + 1, &sol_nmea);
797 } else if (svr->nmeareq == 2) { /* single-solution mode */
798 if (norm(svr->rtk.sol.rr, 3) <= 0.0) {
799 return;
800 }
801 sol_nmea.stat = SOLQ_SINGLE;
802 sol_nmea.time = utc2gpst(timeget());
803 matcpy(sol_nmea.rr, svr->rtk.sol.rr, 3, 1);
804 strsendnmea(svr->stream + 1, &sol_nmea);
805 } else if (svr->nmeareq == 3) { /* reset-and-single-sol mode */
806
807 /* send reset command if baseline over threshold */
808 bl = baseline_len(&svr->rtk);
809 if (bl >= svr->bl_reset && (int)(tick - *tickreset) > MIN_INT_RESET) {
810 strsendcmd(svr->stream + 1, svr->cmd_reset);
811
812 tracet(NULL, 2, "send reset: bl=%.3f rr=%.3f %.3f %.3f rb=%.3f %.3f %.3f\n", bl, svr->rtk.sol.rr[0],
813 svr->rtk.sol.rr[1], svr->rtk.sol.rr[2], svr->rtk.rb[0], svr->rtk.rb[1], svr->rtk.rb[2]);
814 *tickreset = tick;
815 }
816 if (norm(svr->rtk.sol.rr, 3) <= 0.0) {
817 return;
818 }
819 sol_nmea.stat = SOLQ_SINGLE;
820 sol_nmea.time = utc2gpst(timeget());
821 matcpy(sol_nmea.rr, svr->rtk.sol.rr, 3, 1);
822
823 /* set predicted position if velocity > 36km/h */
824 if ((vel = norm(svr->rtk.sol.rr + 3, 3)) > 10.0) {
825 for (i = 0; i < 3; i++) {
826 sol_nmea.rr[i] += svr->rtk.sol.rr[i + 3] / vel * svr->bl_reset * 0.8;
827 }
828 }
829 strsendnmea(svr->stream + 1, &sol_nmea);
830
831 tracet(NULL, 3, "send nmea: rr=%.3f %.3f %.3f\n", sol_nmea.rr[0], sol_nmea.rr[1], sol_nmea.rr[2]);
832 }
833}
834/* rtk server thread ---------------------------------------------------------*/
835static void* rtksvrthread(void* arg) {
836 rtksvr_t* svr = (rtksvr_t*)arg;
837 mrtk_ctx_t* ctx = svr->ctx;
838 obs_t obs;
839 obsd_t* data = (obsd_t*)malloc(MAXOBS * 2 * sizeof(obsd_t));
840 sol_t sol = {{0}};
841 double tt;
842 uint32_t tick, ticknmea, tick1hz, tickreset;
843 uint8_t *p, *q;
844 char msg[128];
845 int i, j, n, fobs[3] = {0}, cycle, cputime;
846
847 tracet(ctx, 3, "rtksvrthread:\n");
848
849 if (!data) {
850 return 0;
851 }
852 svr->state = 1;
853 obs.data = data;
854 svr->tick = tickget();
855 ticknmea = tick1hz = svr->tick - 1000;
856 tickreset = svr->tick - MIN_INT_RESET;
857
858 for (cycle = 0; svr->state; cycle++) {
859 tick = tickget();
860 for (i = 0; i < 3; i++) {
861 p = svr->buff[i] + svr->nb[i];
862 q = svr->buff[i] + svr->buffsize;
863
864 /* read receiver raw/rtcm data from input stream */
865 if ((n = strread(svr->stream + i, p, q - p)) <= 0) {
866 continue;
867 }
868 /* write receiver raw/rtcm data to log stream */
869 strwrite(svr->stream + i + 5, p, n);
870 svr->nb[i] += n;
871
872 /* save peek buffer */
873 rtksvrlock(svr);
874 n = n < svr->buffsize - svr->npb[i] ? n : svr->buffsize - svr->npb[i];
875 memcpy(svr->pbuf[i] + svr->npb[i], p, n);
876 svr->npb[i] += n;
877 rtksvrunlock(svr);
878 }
879 for (i = 0; i < 3; i++) {
880 if (svr->format[i] == STRFMT_SP3 || svr->format[i] == STRFMT_RNXCLK) {
881 /* decode download file */
882 decodefile(svr, i);
883 } else {
884 /* decode receiver raw/rtcm data */
885 fobs[i] = decoderaw(svr, i);
886 }
887 }
888 /* averaging single base pos */
889 if (fobs[1] > 0 && svr->rtk.opt.refpos == POSOPT_SINGLE) {
890 if ((svr->rtk.opt.maxaveep <= 0 || svr->nave < svr->rtk.opt.maxaveep) &&
891 pntpos(ctx, svr->obs[1][0].data, svr->obs[1][0].n, &svr->nav, &svr->rtk.opt, &sol, NULL, NULL, msg)) {
892 svr->nave++;
893 for (i = 0; i < 3; i++) {
894 svr->rb_ave[i] += (sol.rr[i] - svr->rb_ave[i]) / svr->nave;
895 }
896 }
897 for (i = 0; i < 3; i++) {
898 svr->rtk.opt.rb[i] = svr->rb_ave[i];
899 }
900 }
901 for (i = 0; i < fobs[0]; i++) { /* for each rover observation data */
902 obs.n = 0;
903 for (j = 0; j < svr->obs[0][i].n && obs.n < MAXOBS * 2; j++) {
904 obs.data[obs.n++] = svr->obs[0][i].data[j];
905 }
906 for (j = 0; j < svr->obs[1][0].n && obs.n < MAXOBS * 2; j++) {
907 obs.data[obs.n++] = svr->obs[1][0].data[j];
908 }
909 /* rtk positioning */
910 rtksvrlock(svr);
911 rtkpos(ctx, &svr->rtk, obs.data, obs.n, &svr->nav);
912 {
913 int ii;
914 for (ii = 0; ii < 6; ii++) {
915 svr->rtk.prev_qr[ii] = svr->rtk.sol.qr[ii];
916 }
917 }
918 rtksvrunlock(svr);
919
920 if (svr->rtk.sol.stat != SOLQ_NONE) {
921 /* adjust current time */
922 tt = (int)(tickget() - tick) / 1000.0 + DTTOL;
923 timeset(gpst2utc(timeadd(svr->rtk.sol.time, tt)));
924
925 /* write solution */
926 writesol(svr, i);
927 }
928 /* if cpu overload, inclement obs outage counter and break */
929 if ((int)(tickget() - tick) >= svr->cycle) {
930 svr->prcout += fobs[0] - i - 1;
931 }
932 }
933 /* send null solution if no solution (1hz) */
934 if (svr->rtk.sol.stat == SOLQ_NONE && (int)(tick - tick1hz) >= 1000) {
935 writesol(svr, 0);
936 tick1hz = tick;
937 }
938 /* write periodic command to input stream */
939 for (i = 0; i < 3; i++) {
940 periodic_cmd(cycle * svr->cycle, svr->cmds_periodic[i], svr->stream + i);
941 }
942 /* send nmea request to base/nrtk input stream */
943 if (svr->nmeacycle > 0 && (int)(tick - ticknmea) >= svr->nmeacycle) {
944 send_nmea(svr, &tickreset);
945 ticknmea = tick;
946 }
947 if ((cputime = (int)(tickget() - tick)) > 0) {
948 svr->cputime = cputime;
949 }
950
951 /* sleep until next cycle */
952 sleepms(svr->cycle - cputime);
953 }
954 for (i = 0; i < MAXSTRRTK; i++) {
955 strclose(svr->stream + i);
956 }
957 for (i = 0; i < 3; i++) {
958 svr->nb[i] = svr->npb[i] = 0;
959 free(svr->buff[i]);
960 svr->buff[i] = NULL;
961 free(svr->pbuf[i]);
962 svr->pbuf[i] = NULL;
963 free_raw(svr->raw + i);
964 free_rtcm(svr->rtcm + i);
965 }
966 for (i = 0; i < 2; i++) {
967 svr->nsb[i] = 0;
968 free(svr->sbuf[i]);
969 svr->sbuf[i] = NULL;
970 }
971 free(data);
972 return 0;
973}
974/* initialize rtk server -------------------------------------------------------
975 * initialize rtk server
976 * args : rtksvr_t *svr IO rtk server
977 * return : status (0:error,1:ok)
978 *-----------------------------------------------------------------------------*/
979extern int rtksvrinit(rtksvr_t* svr) {
980 gtime_t time0 = {0};
981 sol_t sol0 = {{0}};
982 eph_t eph0 = {0, -1, -1};
983 geph_t geph0 = {0, -1};
984 seph_t seph0 = {0};
985 int i, j;
986
987 tracet(NULL, 3, "rtksvrinit:\n");
988
989 svr->state = svr->cycle = svr->nmeacycle = svr->nmeareq = 0;
990 for (i = 0; i < 3; i++) {
991 svr->nmeapos[i] = 0.0;
992 }
993 svr->buffsize = 0;
994 for (i = 0; i < 3; i++) {
995 svr->format[i] = 0;
996 }
997 for (i = 0; i < 2; i++) {
998 svr->solopt[i] = solopt_default;
999 }
1000 svr->navsel = svr->nsbs = svr->nsol = 0;
1001 rtkinit(&svr->rtk, &prcopt_default);
1002 for (i = 0; i < 3; i++) {
1003 svr->nb[i] = 0;
1004 }
1005 for (i = 0; i < 2; i++) {
1006 svr->nsb[i] = 0;
1007 }
1008 for (i = 0; i < 3; i++) {
1009 svr->npb[i] = 0;
1010 }
1011 for (i = 0; i < 3; i++) {
1012 svr->buff[i] = NULL;
1013 }
1014 for (i = 0; i < 2; i++) {
1015 svr->sbuf[i] = NULL;
1016 }
1017 for (i = 0; i < 3; i++) {
1018 svr->pbuf[i] = NULL;
1019 }
1020 for (i = 0; i < MAXSOLBUF; i++) {
1021 svr->solbuf[i] = sol0;
1022 }
1023 for (i = 0; i < 3; i++) {
1024 for (j = 0; j < 12; j++) {
1025 svr->nmsg[i][j] = 0;
1026 }
1027 }
1028 for (i = 0; i < 3; i++) {
1029 svr->ftime[i] = time0;
1030 }
1031 for (i = 0; i < 3; i++) {
1032 svr->files[i][0] = '\0';
1033 }
1034 svr->moni = NULL;
1035 svr->tick = 0;
1036 svr->thread = 0;
1037 svr->cputime = svr->prcout = svr->nave = 0;
1038 for (i = 0; i < 3; i++) {
1039 svr->rb_ave[i] = 0.0;
1040 }
1041
1042 memset(&svr->nav, 0, sizeof(nav_t));
1043 if (!(svr->nav.eph = (eph_t*)malloc(sizeof(eph_t) * MAXSAT * 4)) ||
1044 !(svr->nav.geph = (geph_t*)malloc(sizeof(geph_t) * NSATGLO * 2)) ||
1045 !(svr->nav.seph = (seph_t*)malloc(sizeof(seph_t) * NSATSBS * 2))) {
1046 tracet(NULL, 1, "rtksvrinit: malloc error\n");
1047 return 0;
1048 }
1049 for (i = 0; i < MAXSAT * 4; i++) {
1050 svr->nav.eph[i] = eph0;
1051 }
1052 for (i = 0; i < NSATGLO * 2; i++) {
1053 svr->nav.geph[i] = geph0;
1054 }
1055 for (i = 0; i < NSATSBS * 2; i++) {
1056 svr->nav.seph[i] = seph0;
1057 }
1058 svr->nav.n = MAXSAT * 4;
1059 svr->nav.ng = NSATGLO * 2;
1060 svr->nav.ns = NSATSBS * 2;
1061
1062 memset(&svr->nav.stat, 0, sizeof(stat_t));
1063
1064 for (i = 0; i < 3; i++) {
1065 for (j = 0; j < MAXOBSBUF; j++) {
1066 if (!(svr->obs[i][j].data = (obsd_t*)malloc(sizeof(obsd_t) * MAXOBS))) {
1067 tracet(NULL, 1, "rtksvrinit: malloc error\n");
1068 return 0;
1069 }
1070 }
1071 }
1072 for (i = 0; i < 3; i++) {
1073 memset(svr->raw + i, 0, sizeof(raw_t));
1074 memset(svr->rtcm + i, 0, sizeof(rtcm_t));
1075 }
1076 for (i = 0; i < MAXSTRRTK; i++) {
1077 strinit(svr->stream + i);
1078 }
1079
1080 for (i = 0; i < 3; i++) {
1081 *svr->cmds_periodic[i] = '\0';
1082 }
1083 *svr->cmd_reset = '\0';
1084 svr->bl_reset = 10.0;
1085 svr->clas = NULL;
1086 svr->has = NULL;
1087 svr->mdciono = NULL;
1088 rtk_initlock(&svr->lock);
1089
1090 return 1;
1091}
1092/* free rtk server -------------------------------------------------------------
1093 * free rtk server
1094 * args : rtksvr_t *svr IO rtk server
1095 * return : none
1096 *-----------------------------------------------------------------------------*/
1097extern void rtksvrfree(rtksvr_t* svr) {
1098 int i, j;
1099
1100 free(svr->nav.eph);
1101 free(svr->nav.geph);
1102 free(svr->nav.seph);
1103 for (i = 0; i < 3; i++) {
1104 for (j = 0; j < MAXOBSBUF; j++) {
1105 free(svr->obs[i][j].data);
1106 }
1107 }
1108 rtkfree(&svr->rtk);
1109 if (svr->clas) {
1110 clas_ctx_free(svr->clas);
1111 free(svr->clas);
1112 svr->clas = NULL;
1113 }
1114 if (svr->has) {
1115 has_free(svr->has);
1116 svr->has = NULL;
1117 }
1118 if (svr->mdciono) {
1119 free(svr->mdciono);
1120 svr->mdciono = NULL;
1121 }
1122 /* pppiono is allocated by rtksvrstart only for RT MADOCA-PPP PPP-AR/IONO;
1123 * rtksvrfree frees nav arrays individually (it does not call freenav). */
1124 free(svr->nav.pppiono);
1125 svr->nav.pppiono = NULL;
1126}
1127/* lock/unlock rtk server ------------------------------------------------------
1128 * lock/unlock rtk server
1129 * args : rtksvr_t *svr IO rtk server
1130 * return : status (1:ok 0:error)
1131 *-----------------------------------------------------------------------------*/
1132extern void rtksvrlock(rtksvr_t* svr) { rtk_lock(&svr->lock); }
1133extern void rtksvrunlock(rtksvr_t* svr) { rtk_unlock(&svr->lock); }
1134
1135/* start rtk server ------------------------------------------------------------
1136 * start rtk server thread
1137 * args : rtksvr_t *svr IO rtk server
1138 * int cycle I server cycle (ms)
1139 * int buffsize I input buffer size (bytes)
1140 * int *strs I stream types (STR_???)
1141 * types[0]=input stream rover
1142 * types[1]=input stream base station
1143 * types[2]=input stream correction
1144 * types[3]=output stream solution 1
1145 * types[4]=output stream solution 2
1146 * types[5]=log stream rover
1147 * types[6]=log stream base station
1148 * types[7]=log stream correction
1149 * char *paths I input stream paths
1150 * int *format I input stream formats (STRFMT_???)
1151 * format[0]=input stream rover
1152 * format[1]=input stream base station
1153 * format[2]=input stream correction
1154 * int navsel I navigation message select
1155 * (0:rover,1:base,2:ephem,3:all)
1156 * char **cmds I input stream start commands
1157 * cmds[0]=input stream rover (NULL: no command)
1158 * cmds[1]=input stream base (NULL: no command)
1159 * cmds[2]=input stream corr (NULL: no command)
1160 * char **cmds_periodic I input stream periodic commands
1161 * cmds[0]=input stream rover (NULL: no command)
1162 * cmds[1]=input stream base (NULL: no command)
1163 * cmds[2]=input stream corr (NULL: no command)
1164 * char **rcvopts I receiver options
1165 * rcvopt[0]=receiver option rover
1166 * rcvopt[1]=receiver option base
1167 * rcvopt[2]=receiver option corr
1168 * int nmeacycle I nmea request cycle (ms) (0:no request)
1169 * int nmeareq I nmea request type
1170 * (0:no,1:base pos,2:single sol,3:reset and single)
1171 * double *nmeapos I transmitted nmea position (ecef) (m)
1172 * prcopt_t *prcopt I rtk processing options
1173 * solopt_t *solopt I solution options
1174 * solopt[0]=solution 1 options
1175 * solopt[1]=solution 2 options
1176 * stream_t *moni I monitor stream (NULL: not used)
1177 * gtime_t rst I raw/rtcm data start time
1178 * char *errmsg O error message
1179 * return : status (1:ok 0:error)
1180 *-----------------------------------------------------------------------------*/
1181extern int rtksvrstart(rtksvr_t* svr, int cycle, int buffsize, int* strs, char** paths, int* formats, int navsel,
1182 char** cmds, char** cmds_periodic, char** rcvopts, int nmeacycle, int nmeareq,
1183 const double* nmeapos, prcopt_t* prcopt, solopt_t* solopt, stream_t* moni, gtime_t rst,
1184 char* errmsg) {
1185 gtime_t time, time0 = {0};
1186 int i, j, rw;
1187
1188 tracet(NULL, 3, "rtksvrstart: cycle=%d buffsize=%d navsel=%d nmeacycle=%d nmeareq=%d\n", cycle, buffsize, navsel,
1189 nmeacycle, nmeareq);
1190
1191 if (svr->state) {
1192 sprintf(errmsg, "server already started");
1193 return 0;
1194 }
1195 strinitcom();
1196 svr->cycle = cycle > 1 ? cycle : 1;
1197 svr->nmeacycle = nmeacycle > 1000 ? nmeacycle : 1000;
1198 svr->nmeareq = nmeareq;
1199 for (i = 0; i < 3; i++) {
1200 svr->nmeapos[i] = nmeapos[i];
1201 }
1202 svr->buffsize = buffsize > 4096 ? buffsize : 4096;
1203 for (i = 0; i < 3; i++) {
1204 svr->format[i] = formats[i];
1205 }
1206 svr->navsel = navsel;
1207 svr->nsbs = 0;
1208 svr->nsol = 0;
1209 svr->prcout = 0;
1210 rtkfree(&svr->rtk);
1211 rtkinit(&svr->rtk, prcopt);
1212
1213 if (prcopt->initrst) { /* init averaging pos by restart */
1214 svr->nave = 0;
1215 for (i = 0; i < 3; i++) {
1216 svr->rb_ave[i] = 0.0;
1217 }
1218 }
1219 for (i = 0; i < 3; i++) { /* input/log streams */
1220 svr->nb[i] = svr->npb[i] = 0;
1221 if (!(svr->buff[i] = (uint8_t*)malloc(buffsize)) || !(svr->pbuf[i] = (uint8_t*)malloc(buffsize))) {
1222 tracet(NULL, 1, "rtksvrstart: malloc error\n");
1223 sprintf(errmsg, "rtk server malloc error");
1224 return 0;
1225 }
1226 for (j = 0; j < 12; j++) {
1227 svr->nmsg[i][j] = 0;
1228 }
1229 for (j = 0; j < MAXOBSBUF; j++) {
1230 svr->obs[i][j].n = 0;
1231 }
1232 strcpy(svr->cmds_periodic[i], !cmds_periodic[i] ? "" : cmds_periodic[i]);
1233
1234 /* initialize receiver raw and rtcm control */
1235 init_raw(svr->raw + i, formats[i]);
1236 init_rtcm(svr->rtcm + i);
1237
1238 /* set receiver and rtcm option */
1239 strcpy(svr->raw[i].opt, rcvopts[i]);
1240 strcpy(svr->rtcm[i].opt, rcvopts[i]);
1241
1242 /* #189: propagate the configured signal selection to the raw decoder so
1243 * `[positioning].signals` drives per-band code selection (e.g. GLONASS
1244 * L2C/A as the 2nd frequency). Empty sigcfg leaves sigcfg_set=0 and the
1245 * decoder keeps its legacy -R/-G option behaviour. */
1246 memcpy(svr->raw[i].sigcfg, prcopt->sigcfg, sizeof(svr->raw[i].sigcfg));
1247 svr->raw[i].sigcfg_set = prcopt->sigcfg_set;
1248
1249 /* connect dgps corrections */
1250 svr->rtcm[i].dgps = svr->nav.dgps;
1251 }
1252 for (i = 0; i < 2; i++) { /* output peek buffer */
1253 if (!(svr->sbuf[i] = (uint8_t*)malloc(buffsize))) {
1254 tracet(NULL, 1, "rtksvrstart: malloc error\n");
1255 sprintf(errmsg, "rtk server malloc error");
1256 return 0;
1257 }
1258 }
1259 /* set solution options */
1260 for (i = 0; i < 2; i++) {
1261 svr->solopt[i] = solopt[i];
1262 }
1263 /* set base station position */
1264 if (prcopt->refpos != POSOPT_SINGLE) {
1265 for (i = 0; i < 6; i++) {
1266 svr->rtk.rb[i] = i < 3 ? prcopt->rb[i] : 0.0;
1267 }
1268 }
1269 /* update navigation data */
1270 for (i = 0; i < MAXSAT * 4; i++) {
1271 svr->nav.eph[i].ttr = time0;
1272 }
1273 for (i = 0; i < NSATGLO * 2; i++) {
1274 svr->nav.geph[i].tof = time0;
1275 }
1276 for (i = 0; i < NSATSBS * 2; i++) {
1277 svr->nav.seph[i].tof = time0;
1278 }
1279
1280 /* set monitor stream */
1281 svr->moni = moni;
1282
1283 /* store global context for thread use */
1284 svr->ctx = g_mrtk_ctx;
1285
1286 /* initialize CLAS context if L6 source is available for PPP-RTK */
1287 for (i = 0; i < 3; i++) {
1288 if ((formats[i] == STRFMT_CLAS ||
1289 ((formats[i] == STRFMT_UBX || formats[i] == STRFMT_L6E || formats[i] == STRFMT_SEPT) &&
1290 prcopt->mode == PMODE_PPP_RTK)) &&
1291 !svr->clas) {
1292 svr->clas = (clas_ctx_t*)calloc(1, sizeof(clas_ctx_t));
1293 if (svr->clas) {
1294 clas_ctx_init(svr->clas);
1295 }
1296 }
1297 }
1298 /* wire CLAS context into nav for PPP-RTK engine access */
1299 svr->nav.clas_ctx = svr->clas;
1300
1301 /* initialize Galileo HAS context for HAS float PPP (correction = gal-has).
1302 * Any input stream may carry SBF GALRawCNAV pages; routing is index-agnostic. */
1303 if (prcopt->correction == CORR_GAL_HAS && !svr->has) {
1304 svr->has = has_new();
1305 if (!svr->has) {
1306 tracet(NULL, 1, "rtksvrstart: HAS context alloc error\n");
1307 }
1308 }
1309
1310 /* free any iono state left over from a previous run so an in-process restart
1311 * (the rtkrcv shell can `load` a new config and `restart` in the same
1312 * process) starts clean: (1) a fresh mdciono re-anchors the GPST week in
1313 * decoderaw() instead of reusing the stale mdciono[0].time, and (2) a stale
1314 * nav.pppiono is not left non-NULL — miono_get_corr() consumes it on every
1315 * PPP epoch (mrtk_rtkpos.c) regardless of correction source — when
1316 * restarting into a non-PPP-AR/IONO mode (PR #235 review). */
1317 if (svr->mdciono) {
1318 free(svr->mdciono);
1319 svr->mdciono = NULL;
1320 }
1321 free(svr->nav.pppiono);
1322 svr->nav.pppiono = NULL;
1323
1324 /* initialize MADOCA-PPP L6D ionospheric augmentation contexts for real-time
1325 * PPP-AR/IONO (correction = qzs-madoca, ionosphere = est-stec,
1326 * iono_correction = on). The per-PRN contexts decode L6D PRN 200/201 iono
1327 * into nav.pppiono, mirroring update_qzssl6d() in mrtk_postpos.c. The GPST
1328 * week anchor (init_miono) is set lazily from the first observation time in
1329 * decoderaw(), the same pattern CLAS uses for week_ref. */
1330 if (prcopt->correction == CORR_QZS_MADOCA && prcopt->ionocorr && prcopt->ionoopt == IONOOPT_EST && !svr->mdciono) {
1331 svr->mdciono = (mdcl6d_t*)calloc(MIONO_MAX_PRN, sizeof(mdcl6d_t));
1332 if (svr->mdciono && !svr->nav.pppiono) {
1333 svr->nav.pppiono = (pppiono_t*)calloc(1, sizeof(pppiono_t));
1334 }
1335 if (!svr->mdciono || !svr->nav.pppiono) {
1336 tracet(NULL, 1, "rtksvrstart: MADOCA iono context alloc error\n");
1337 free(svr->mdciono);
1338 svr->mdciono = NULL; /* fall back to plain PPP-AR (ppp_iono no-ops) */
1339 }
1340 }
1341
1342 /* open input streams */
1343 for (i = 0; i < 8; i++) {
1344 rw = i < 3 ? STR_MODE_R : STR_MODE_W;
1345 if (strs[i] != STR_FILE) {
1346 rw |= STR_MODE_W;
1347 }
1348 if (!stropen(svr->stream + i, strs[i], rw, paths[i])) {
1349 sprintf(errmsg, "str%d open error path=%s", i + 1, paths[i]);
1350 for (i--; i >= 0; i--) {
1351 strclose(svr->stream + i);
1352 }
1353 return 0;
1354 }
1355 /* set initial time for rtcm and raw */
1356 if (i < 3) {
1357 if (rst.time != 0) {
1358 time = rst;
1359 } else {
1360 time = utc2gpst(timeget());
1361 }
1362 if (strs[i] == STR_FILE) {
1363 svr->raw[i].time = strgettime(svr->stream + i);
1364 svr->rtcm[i].time = strgettime(svr->stream + i);
1365 } else {
1366 svr->raw[i].time = time;
1367 svr->rtcm[i].time = time;
1368 }
1369 }
1370 }
1371 /* sync input streams */
1372 strsync(svr->stream, svr->stream + 1);
1373 strsync(svr->stream, svr->stream + 2);
1374
1375 /* write start commands to input streams */
1376 for (i = 0; i < 3; i++) {
1377 if (!cmds[i]) {
1378 continue;
1379 }
1380 strwrite(svr->stream + i, (uint8_t*)"", 0); /* for connect */
1381 sleepms(100);
1382 strsendcmd(svr->stream + i, cmds[i]);
1383 }
1384 /* write solution header to solution streams */
1385 for (i = 3; i < 5; i++) {
1386 writesolhead(svr->stream + i, svr->solopt + i - 3);
1387 }
1388 /* create rtk server thread */
1389 {
1390 pthread_attr_t attr;
1391 int rc;
1392 pthread_attr_init(&attr);
1393 pthread_attr_setstacksize(&attr, 8 * 1024 * 1024); /* 8 MB (match Linux default) */
1394 rc = pthread_create(&svr->thread, &attr, rtksvrthread, svr);
1395 pthread_attr_destroy(&attr);
1396 if (rc) {
1397 for (i = 0; i < MAXSTRRTK; i++) {
1398 strclose(svr->stream + i);
1399 }
1400 sprintf(errmsg, "thread create error\n");
1401 return 0;
1402 }
1403 }
1404 return 1;
1405}
1406/* stop rtk server -------------------------------------------------------------
1407 * start rtk server thread
1408 * args : rtksvr_t *svr IO rtk server
1409 * char **cmds I input stream stop commands
1410 * cmds[0]=input stream rover (NULL: no command)
1411 * cmds[1]=input stream base (NULL: no command)
1412 * cmds[2]=input stream ephem (NULL: no command)
1413 * return : none
1414 *-----------------------------------------------------------------------------*/
1415extern void rtksvrstop(rtksvr_t* svr, char** cmds) {
1416 int i;
1417
1418 tracet(NULL, 3, "rtksvrstop:\n");
1419
1420 /* write stop commands to input streams */
1421 rtksvrlock(svr);
1422 for (i = 0; i < 3; i++) {
1423 if (cmds[i]) {
1424 strsendcmd(svr->stream + i, cmds[i]);
1425 }
1426 }
1427 rtksvrunlock(svr);
1428
1429 /* stop rtk server */
1430 svr->state = 0;
1431
1432 /* free rtk server thread */
1433 pthread_join(svr->thread, NULL);
1434}
1435/* open output/log stream ------------------------------------------------------
1436 * open output/log stream
1437 * args : rtksvr_t *svr IO rtk server
1438 * int index I output/log stream index
1439 * (3:solution 1,4:solution 2,5:log rover,
1440 * 6:log base station,7:log correction)
1441 * int str I output/log stream types (STR_???)
1442 * char *path I output/log stream path
1443 * solopt_t *solopt I solution options
1444 * return : status (1:ok 0:error)
1445 *-----------------------------------------------------------------------------*/
1446extern int rtksvropenstr(rtksvr_t* svr, int index, int str, const char* path, const solopt_t* solopt) {
1447 tracet(NULL, 3, "rtksvropenstr: index=%d str=%d path=%s\n", index, str, path);
1448
1449 if (index < 3 || index > 7 || !svr->state) {
1450 return 0;
1451 }
1452
1453 rtksvrlock(svr);
1454
1455 if (svr->stream[index].state > 0) {
1456 rtksvrunlock(svr);
1457 return 0;
1458 }
1459 if (!stropen(svr->stream + index, str, STR_MODE_W, path)) {
1460 tracet(NULL, 2, "stream open error: index=%d\n", index);
1461 rtksvrunlock(svr);
1462 return 0;
1463 }
1464 if (index <= 4) {
1465 svr->solopt[index - 3] = *solopt;
1466
1467 /* write solution header to solution stream */
1468 writesolhead(svr->stream + index, svr->solopt + index - 3);
1469 }
1470 rtksvrunlock(svr);
1471 return 1;
1472}
1473/* close output/log stream -----------------------------------------------------
1474 * close output/log stream
1475 * args : rtksvr_t *svr IO rtk server
1476 * int index I output/log stream index
1477 * (3:solution 1,4:solution 2,5:log rover,
1478 * 6:log base station,7:log correction)
1479 * return : none
1480 *-----------------------------------------------------------------------------*/
1481extern void rtksvrclosestr(rtksvr_t* svr, int index) {
1482 tracet(NULL, 3, "rtksvrclosestr: index=%d\n", index);
1483
1484 if (index < 3 || index > 7 || !svr->state) {
1485 return;
1486 }
1487
1488 rtksvrlock(svr);
1489
1490 strclose(svr->stream + index);
1491
1492 rtksvrunlock(svr);
1493}
1494/* get observation data status -------------------------------------------------
1495 * get current observation data status
1496 * args : rtksvr_t *svr I rtk server
1497 * int rcv I receiver (0:rover,1:base,2:ephem)
1498 * gtime_t *time O time of observation data
1499 * int *sat O satellite prn numbers
1500 * double *az O satellite azimuth angles (rad)
1501 * double *el O satellite elevation angles (rad)
1502 * int **snr O satellite snr for each freq (dBHz)
1503 * snr[i][j] = sat i freq j snr
1504 * int *vsat O valid satellite flag
1505 * return : number of satellites
1506 *-----------------------------------------------------------------------------*/
1507extern int rtksvrostat(rtksvr_t* svr, int rcv, gtime_t* time, int* sat, double* az, double* el, int** snr, int* vsat) {
1508 int i, j, ns;
1509
1510 tracet(NULL, 4, "rtksvrostat: rcv=%d\n", rcv);
1511
1512 if (!svr->state) {
1513 return 0;
1514 }
1515 rtksvrlock(svr);
1516 ns = svr->obs[rcv][0].n;
1517 if (ns > 0) {
1518 *time = svr->obs[rcv][0].data[0].time;
1519 }
1520 for (i = 0; i < ns; i++) {
1521 sat[i] = svr->obs[rcv][0].data[i].sat;
1522 az[i] = svr->rtk.ssat[sat[i] - 1].azel[0];
1523 el[i] = svr->rtk.ssat[sat[i] - 1].azel[1];
1524 for (j = 0; j < NFREQ; j++) {
1525 snr[i][j] = (int)(svr->obs[rcv][0].data[i].SNR[j] * SNR_UNIT + 0.5);
1526 }
1527 if (svr->rtk.sol.stat == SOLQ_NONE || svr->rtk.sol.stat == SOLQ_SINGLE) {
1528 vsat[i] = svr->rtk.ssat[sat[i] - 1].vs;
1529 } else {
1530 vsat[i] = svr->rtk.ssat[sat[i] - 1].vsat[0];
1531 }
1532 }
1533 rtksvrunlock(svr);
1534 return ns;
1535}
1536/* get stream status -----------------------------------------------------------
1537 * get current stream status
1538 * args : rtksvr_t *svr I rtk server
1539 * int *sstat O status of streams
1540 * char *msg O status messages
1541 * return : none
1542 *-----------------------------------------------------------------------------*/
1543extern void rtksvrsstat(rtksvr_t* svr, int* sstat, char* msg) {
1544 int i;
1545 char s[MAXSTRMSG], *p = msg;
1546
1547 tracet(NULL, 4, "rtksvrsstat:\n");
1548
1549 rtksvrlock(svr);
1550 for (i = 0; i < MAXSTRRTK; i++) {
1551 sstat[i] = strstat(svr->stream + i, s);
1552 if (*s) {
1553 p += sprintf(p, "(%d) %s ", i + 1, s);
1554 }
1555 }
1556 rtksvrunlock(svr);
1557}
1558/* mark current position -------------------------------------------------------
1559 * open output/log stream
1560 * args : rtksvr_t *svr IO rtk server
1561 * char *name I marker name
1562 * char *comment I comment string
1563 * return : status (1:ok 0:error)
1564 *-----------------------------------------------------------------------------*/
1565extern int rtksvrmark(rtksvr_t* svr, const char* name, const char* comment) {
1566 char buff[MAXSOLMSG + 1], tstr[32], *p, *q;
1567 double tow, pos[3];
1568 int i, sum, week;
1569
1570 tracet(NULL, 4, "rtksvrmark:name=%s comment=%s\n", name, comment);
1571
1572 if (!svr->state) {
1573 return 0;
1574 }
1575
1576 rtksvrlock(svr);
1577
1578 time2str(svr->rtk.sol.time, tstr, 3);
1579 tow = time2gpst(svr->rtk.sol.time, &week);
1580 ecef2pos(svr->rtk.sol.rr, pos);
1581
1582 for (i = 0; i < 2; i++) {
1583 p = buff;
1584 if (svr->solopt[i].posf == SOLF_STAT) {
1585 p += sprintf(p, "$MARK,%d,%.3f,%d,%.4f,%.4f,%.4f,%s,%s\r\n", week, tow, svr->rtk.sol.stat,
1586 svr->rtk.sol.rr[0], svr->rtk.sol.rr[1], svr->rtk.sol.rr[2], name, comment);
1587 } else if (svr->solopt[i].posf == SOLF_NMEA) {
1588 p += sprintf(p, "$GPTXT,01,01,02,MARK:%s,%s,%.9f,%.9f,%.4f,%d,%s", name, tstr, pos[0] * R2D, pos[1] * R2D,
1589 pos[2], svr->rtk.sol.stat, comment);
1590 for (q = (char*)buff + 1, sum = 0; *q; q++) {
1591 sum ^= *q; /* check-sum */
1592 }
1593 p += sprintf(p, "*%02X\r\n", sum);
1594 } else {
1595 p += sprintf(p, "%s MARK: %s,%s,%.9f,%.9f,%.4f,%d,%s\r\n", COMMENTH, name, tstr, pos[0] * R2D, pos[1] * R2D,
1596 pos[2], svr->rtk.sol.stat, comment);
1597 }
1598 strwrite(svr->stream + i + 3, (uint8_t*)buff, (int)(p - buff));
1599 saveoutbuf(svr, (uint8_t*)buff, (int)(p - buff), i);
1600 }
1601 if (svr->moni) {
1602 p = buff;
1603 p += sprintf(p, "%s MARK: %s,%s,%.9f,%.9f,%.4f,%d,%s\r\n", COMMENTH, name, tstr, pos[0] * R2D, pos[1] * R2D,
1604 pos[2], svr->rtk.sol.stat, comment);
1605 strwrite(svr->moni, (uint8_t*)buff, (int)(p - buff));
1606 }
1607 rtksvrunlock(svr);
1608 return 1;
1609}
void clas_check_grid_status(clas_ctx_t *ctx, const clas_corr_t *corr, int ch)
Check and update grid status flags based on correction availability.
Definition mrtk_clas.c:1320
int clas_route_l6frame(clas_ctx_t *ctx, int prn, int ptn, int l6mrg, gtime_t now)
Route one interleaved L6D frame to a CLAS channel (or drop it).
Definition mrtk_clas.c:3070
int clas_input_cssr(clas_ctx_t *ctx, uint8_t data, int ch)
Input CSSR byte stream (one byte at a time, real-time).
Definition mrtk_clas.c:3127
void clas_set_l6tag(clas_ctx_t *ctx, int ch, double tag)
Set the receiver time tag of the L6 frame about to be input.
Definition mrtk_clas.c:3215
void clas_ctx_free(clas_ctx_t *ctx)
Free CLAS context (release heap-allocated bank control).
Definition mrtk_clas.c:88
int clas_bank_get_close(const clas_ctx_t *ctx, gtime_t time, int network, int ch, clas_corr_t *corr)
Merge closest corrections from all banks into a clas_corr_t snapshot.
Definition mrtk_clas.c:1121
void clas_update_global(nav_t *nav, const clas_corr_t *corr, int ch)
Apply global corrections (orbit/clock/bias) to nav_t.ssr[].
Definition mrtk_clas.c:1430
int clas_ctx_init(clas_ctx_t *ctx)
Initialize CLAS context (allocate bank control on heap).
Definition mrtk_clas.c:54
int clas_decode_msg(clas_ctx_t *ctx, int ch)
Decode the next pending CSSR message from the assembled bit buffer.
Definition mrtk_clas.c:3213
#define CLAS_MAX_NETWORK
Definition mrtk_clas.h:186
@ CSSR_REF_MAX
Definition mrtk_clas.h:169
mrtk_ctx_t * g_mrtk_ctx
Global context pointer for transitional migration.
void enu2ecef(const double *pos, const double *e, double *r)
Transform local tangential coordinate vector to ECEF.
#define MAXSAT
Definition mrtk_coords.c:45
void ecef2pos(const double *r, double *pos)
Transform ECEF position to geodetic position.
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_QZS
Definition mrtk_eph.c:69
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
MRTKLIB Broadcast Ephemeris Module — Satellite orbit/clock types and Kepler/GLONASS/SBAS orbit comput...
struct nav_s nav_t
Definition mrtk_eph.h:40
#define SNR_UNIT
#define MIONO_MAX_PRN
#define MAXOBSBUF
#define NSATSBS
#define MAXSTRRTK
#define MAXOBS
#define rtk_unlock(f)
#define DTTOL
#define rtk_initlock(f)
#define MAXSTRMSG
#define MIONO_MAX_ANUM
#define NSATGLO
#define MAXSOLBUF
#define MAXSOLMSG
#define MAXSBSMSG
#define rtk_lock(f)
#define MAXPRNGLO
void has_free(has_t *has)
Free a Galileo HAS decode context.
Definition mrtk_has.c:801
int has_input_page(has_t *has, int prn, const uint8_t *page56, gtime_t time)
Input one received Galileo HAS page.
Definition mrtk_has.c:804
has_t * has_new(void)
Allocate and initialize a Galileo HAS decode context.
Definition mrtk_has.c:790
const l6_prn_info_t * l6_prn_info(int prn)
Look up the auxiliary information of an L6 PRN.
Definition mrtk_l6.c:74
int l6_parse_header(const uint8_t *frame, l6_header_t *h)
Parse the header of one L6 frame and classify its service.
Definition mrtk_l6.c:44
MRTKLIB L6 Module — QZSS L6 frame header parsing.
void init_mcssr(const gtime_t gt)
Initialize MADOCA-PPP CSSR control struct for current channel.
int input_qzssl6e(rtcm_t *rtcm, const uint8_t data)
Stack QZSS L6E message and synchronize frame with L6 preamble.
MRTKLIB MADOCA-PPP Module — QZSS L6E CSSR message decode functions.
void init_miono(const gtime_t gt)
Initialize MADOCA-PPP L6D ionospheric correction control.
int input_qzssl6d(mdcl6d_t *mdcl6d, const uint8_t data)
Input QZSS L6D message byte-by-byte.
int strwrite(struct stream_tag *stream, uint8_t *buff, int n)
int getstano(stat_t *stat, const char *pntname)
Search for a point by name or add a new point if not found.
int block2stat(rtcm_t *rtcm, stat_t *stat)
Convert RTCM3 block data to station statistics.
int input_stat(sstat_t *sstat, const char data, char *buff, int *nbyte)
Stack stat message and synchronize frame with stat preamble.
MRTKLIB MADOCA Local Correction Module — local correction common functions and constants.
void matcpy(double *A, const double *B, int n, int m)
Copy matrix.
Definition mrtk_mat.c:205
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
MRTKLIB Matrix Module — Matrix/vector operations and estimation.
int satno(int sys, int prn)
Convert satellite system+prn/slot number to satellite number.
Definition mrtk_nav.c:58
MRTKLIB Navigation Module — Navigation data types and satellite/nav utility functions.
int sortobs(obs_t *obs)
Sort observation data by time, receiver, and satellite.
Definition mrtk_obs.c:350
#define R2D
Definition mrtk_obs.c:51
MRTKLIB Observation Module — Observation data types and utility functions.
const prcopt_t prcopt_default
Definition mrtk_opt.c:31
const solopt_t solopt_default
Definition mrtk_opt.c:92
MRTKLIB Options Module — Processing and solution option types.
#define PMODE_PPP_RTK
Definition mrtk_opt.h:54
#define SOLF_STAT
Definition mrtk_opt.h:67
#define SOLQ_SINGLE
Definition mrtk_opt.h:75
#define CORR_QZS_MADOCA
Definition mrtk_opt.h:119
#define POSOPT_SINGLE
Definition mrtk_opt.h:150
#define IONOOPT_EST
Definition mrtk_opt.h:96
#define SOLF_NMEA
Definition mrtk_opt.h:66
#define CORR_GAL_HAS
Definition mrtk_opt.h:120
#define POSOPT_RTCM
Definition mrtk_opt.h:153
#define SOLQ_NONE
Definition mrtk_opt.h:70
void readsp3(const char *file, nav_t *nav, int opt)
Read sp3 precise ephemeris file.
Definition mrtk_peph.c:450
MRTKLIB Precise Ephemeris Module — Earth rotation parameter types and ERP I/O functions.
#define COMMENTH
MRTKLIB Precise Point Positioning (PPP) Module.
void free_raw(raw_t *raw)
Free receiver raw data control struct.
int input_raw(raw_t *raw, rtcm_t *rtcm, int format, uint8_t data)
Input receiver raw data from stream (byte-by-byte).
int init_raw(raw_t *raw, int format)
Initialize receiver raw data control struct.
MRTKLIB Receiver Raw Data Module — Receiver raw data types and decoder functions for various GNSS rec...
#define STRFMT_SP3
Definition mrtk_rcvraw.h:63
#define STRFMT_STAT
Definition mrtk_rcvraw.h:67
#define STRFMT_CLAS
Definition mrtk_rcvraw.h:69
#define STRFMT_RNXCLK
Definition mrtk_rcvraw.h:64
#define STRFMT_UBX
Definition mrtk_rcvraw.h:53
#define STRFMT_RTCM3
Definition mrtk_rcvraw.h:50
#define STRFMT_L6E
Definition mrtk_rcvraw.h:68
#define STRFMT_SEPT
Definition mrtk_rcvraw.h:61
#define STRFMT_RTCM2
Definition mrtk_rcvraw.h:49
int readrnxc(const char *file, nav_t *nav)
Read RINEX clock files.
MRTKLIB RINEX Module — RINEX file read/write types and functions.
void free_rtcm(rtcm_t *rtcm)
Free RTCM control struct.
Definition mrtk_rtcm.c:118
int init_rtcm(rtcm_t *rtcm)
Initialize RTCM control struct.
Definition mrtk_rtcm.c:37
int input_rtcm2(rtcm_t *rtcm, uint8_t data)
Input RTCM 2 message from stream (byte-by-byte).
Definition mrtk_rtcm.c:159
int input_rtcm3(rtcm_t *rtcm, uint8_t data)
Input RTCM 3 message from stream (byte-by-byte).
Definition mrtk_rtcm.c:289
MRTKLIB RTCM Module — RTCM message types and functions.
static void errmsg(rtk_t *rtk, const char *format,...)
int rtkpos(mrtk_ctx_t *ctx, rtk_t *rtk, const obsd_t *obs, int n, nav_t *nav)
Compute rover position by precise positioning.
int rtkoutstat(rtk_t *rtk, char *buff)
Write solution status to buffer.
void rtkfree(rtk_t *rtk)
Free memory for RTK control struct.
void rtkinit(rtk_t *rtk, const prcopt_t *opt)
Initialize RTK control struct.
MRTKLIB RTK Positioning Module — relative and precise positioning.
void rtksvrunlock(rtksvr_t *svr)
Unlock RTK server mutex.
int rtksvrmark(rtksvr_t *svr, const char *name, const char *comment)
Mark current position in solution.
static void update_sbs(rtksvr_t *svr, sbsmsg_t *sbsmsg, int index)
void rtksvrstop(rtksvr_t *svr, char **cmds)
Stop RTK server.
static void update_ionutc(rtksvr_t *svr, nav_t *nav, int index)
static void * rtksvrthread(void *arg)
static void update_glofcn(rtksvr_t *svr)
static void update_lcl(rtksvr_t *svr, int index)
static void update_ssr(rtksvr_t *svr, int index)
static void writesolhead(stream_t *stream, const solopt_t *solopt)
Definition mrtk_rtksvr.c:63
void rtksvrclosestr(rtksvr_t *svr, int index)
Close RTK server output/log stream.
static void update_obs(rtksvr_t *svr, obs_t *obs, int index, int iobs)
static void writesol(rtksvr_t *svr, int index)
Definition mrtk_rtksvr.c:81
static void send_nmea(rtksvr_t *svr, uint32_t *tickreset)
void rtksvrsstat(rtksvr_t *svr, int *sstat, char *msg)
Get stream status.
#define MIN_INT_RESET
Definition mrtk_rtksvr.c:60
int rtksvr_ssr_applicable(const rtksvr_t *svr, int index, int ret)
Decide whether a decoded SSR result may be applied to nav.ssr_ch[0].
static void periodic_cmd(int cycle, const char *cmd, stream_t *stream)
static void update_antpos(rtksvr_t *svr, int index)
static double baseline_len(const rtk_t *rtk)
int rtksvrinit(rtksvr_t *svr)
Initialize RTK server.
int rtksvropenstr(rtksvr_t *svr, int index, int str, const char *path, const solopt_t *solopt)
Open RTK server output/log stream.
int rtksvrostat(rtksvr_t *svr, int rcv, gtime_t *time, int *sat, double *az, double *el, int **snr, int *vsat)
Get observation data status.
int rtksvrstart(rtksvr_t *svr, int cycle, int buffsize, int *strs, char **paths, int *formats, int navsel, char **cmds, char **cmds_periodic, char **rcvopts, int nmeacycle, int nmeareq, const double *nmeapos, prcopt_t *prcopt, solopt_t *solopt, stream_t *moni, gtime_t rst, char *errmsg)
Start RTK server.
void timeset(gtime_t t)
Definition mrtk_time.c:318
static void update_stat(rtksvr_t *svr, int index)
static void update_svr(rtksvr_t *svr, int ret, obs_t *obs, nav_t *nav, int ephsat, int ephset, sbsmsg_t *sbsmsg, int index, int iobs)
void rtksvrfree(rtksvr_t *svr)
Free RTK server resources.
static void update_eph(rtksvr_t *svr, nav_t *nav, int ephsat, int ephset, int index)
static int decoderaw(rtksvr_t *svr, int index)
static void saveoutbuf(rtksvr_t *svr, uint8_t *buff, int n, int index)
Definition mrtk_rtksvr.c:71
static void decodefile(rtksvr_t *svr, int index)
void rtksvrlock(rtksvr_t *svr)
Lock RTK server mutex.
MRTKLIB RTK Server Module — real-time positioning server.
int sbsupdatecorr(const sbsmsg_t *msg, nav_t *nav)
Update SBAS corrections in navigation data.
Definition mrtk_sbas.c:517
MRTKLIB SBAS Module — SBAS message decoding and corrections.
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
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
int outsolheads(uint8_t *buff, const solopt_t *opt)
Output solution header to buffer.
Definition mrtk_sol.c:1683
MRTKLIB Solution Module — Solution data types and I/O functions.
int pntpos(mrtk_ctx_t *ctx, const obsd_t *obs, int n, const nav_t *nav, const prcopt_t *opt, sol_t *sol, double *azel, ssat_t *ssat, char *msg)
Compute receiver position, velocity, clock bias by single-point positioning with pseudorange and dopp...
Definition mrtk_spp.c:865
MRTKLIB Single-Point Positioning Module — SPP functions.
void strinit(stream_t *stream)
Initialize stream structure.
int strstat(stream_t *stream, char *msg)
Get stream status.
gtime_t strgettime(stream_t *stream)
Get stream time.
int strread(stream_t *stream, uint8_t *buff, int n)
Read data from stream.
void strsendnmea(stream_t *stream, const sol_t *sol)
Send NMEA request to stream.
static int buffsize
void strinitcom(void)
Initialize stream communication system.
void strclose(stream_t *stream)
Close stream.
int stropen(stream_t *stream, int type, int mode, const char *path)
Open stream.
void strsync(stream_t *stream1, stream_t *stream2)
Sync streams.
void strsendcmd(stream_t *str, const char *cmd)
Send receiver command to stream.
MRTKLIB Stream Module — stream I/O abstraction layer.
#define STR_FILE
Definition mrtk_stream.h:45
#define STR_MODE_W
Definition mrtk_stream.h:60
#define STR_MODE_R
Definition mrtk_stream.h:59
struct stream_tag stream_t
MRTKLIB System Module — OS/filesystem utility functions.
void sleepms(int ms)
Sleep for specified milliseconds.
Definition mrtk_time.c:588
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
gtime_t timeget(void)
Get current time in UTC.
Definition mrtk_time.c:289
uint32_t tickget(void)
Get current tick in milliseconds.
Definition mrtk_time.c:566
MRTKLIB Time Module — GNSS time types and conversion functions.
void tracet(mrtk_ctx_t *ctx, int level, const char *format,...)
Output trace message with elapsed time.
Definition mrtk_trace.c:91
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.
clas_grid_t grid[2]
Definition mrtk_clas.h:592
clas_corr_t current[2]
Definition mrtk_clas.h:588
clas_l6buf_t l6buf[2]
Definition mrtk_clas.h:608
int tow_ref[CSSR_REF_MAX]
Definition mrtk_clas.h:613
clas_bank_ctrl_t * bank[2]
Definition mrtk_clas.h:585
int week_ref[CSSR_REF_MAX]
Definition mrtk_clas.h:611
gtime_t time
Definition mrtk_clas.h:565
GPS/GAL/QZS/BDS/IRN broadcast ephemeris type.
Definition mrtk_eph.h:77
gtime_t ttr
Definition mrtk_eph.h:88
gtime_t toe
Definition mrtk_eph.h:88
gtime_t toc
Definition mrtk_eph.h:88
int iode
Definition mrtk_eph.h:79
GLONASS broadcast ephemeris type.
Definition mrtk_eph.h:111
int iode
Definition mrtk_eph.h:113
gtime_t tof
Definition mrtk_eph.h:119
int sat
Definition mrtk_eph.h:112
int frq
Definition mrtk_eph.h:114
GNSS time representation.
Definition mrtk_time.h:49
time_t time
Definition mrtk_time.h:50
ssr_t ssr[MAXSAT]
Definition mrtk_has.h:87
Decoded L6 frame header. Raw bit fields; no service-specific remapping.
Definition mrtk_l6.h:68
uint8_t prn
Definition mrtk_l6.h:70
uint8_t ptn
Definition mrtk_l6.h:74
Auxiliary per-PRN information for one L6 PRN.
Definition mrtk_l6.h:92
int8_t iono_slot
Definition mrtk_l6.h:97
QZSS L6D message control struct.
Definition mrtk_ppp.h:50
gtime_t time
Definition mrtk_ppp.h:51
miono_region_t re
Definition mrtk_ppp.h:56
char opt[256]
Definition mrtk_ppp.h:54
int rid
Definition mrtk_ppp.h:55
int nbyte
Definition mrtk_ppp.h:52
miono_sat_t sat[MAXSAT]
Definition mrtk_nav.h:227
miono_area_t area[MIONO_MAX_ANUM]
Definition mrtk_nav.h:234
gtime_t t0
Definition mrtk_nav.h:216
Runtime context for MRTKLIB processing.
pppiono_t * pppiono
Definition mrtk_nav.h:438
int ng
Definition mrtk_nav.h:394
int nemax
Definition mrtk_nav.h:396
double utc_glo[8]
Definition mrtk_nav.h:415
double utc_qzs[8]
Definition mrtk_nav.h:417
seph_t * seph
Definition mrtk_nav.h:408
double ion_irn[8]
Definition mrtk_nav.h:425
double utc_gal[8]
Definition mrtk_nav.h:416
void * clas_ctx
Definition mrtk_nav.h:439
double utc_gps[8]
Definition mrtk_nav.h:414
geph_t * geph
Definition mrtk_nav.h:407
double utc_cmp[8]
Definition mrtk_nav.h:418
double utc_sbs[4]
Definition mrtk_nav.h:420
double ion_cmp[8]
Definition mrtk_nav.h:424
dgps_t dgps[MAXSAT]
Definition mrtk_nav.h:433
pclk_t * pclk
Definition mrtk_nav.h:410
peph_t * peph
Definition mrtk_nav.h:409
stat_t stat
Definition mrtk_nav.h:440
double ion_gal[4]
Definition mrtk_nav.h:422
ssr_t ssr_ch[SSR_CH_NUM][MAXSAT]
Definition mrtk_nav.h:434
double utc_irn[9]
Definition mrtk_nav.h:419
int ncmax
Definition mrtk_nav.h:397
eph_t * eph
Definition mrtk_nav.h:400
int nc
Definition mrtk_nav.h:397
double ion_gps[8]
Definition mrtk_nav.h:421
int ns
Definition mrtk_nav.h:395
int n
Definition mrtk_nav.h:393
double ion_qzs[8]
Definition mrtk_nav.h:423
int ne
Definition mrtk_nav.h:396
Observation data collection.
Definition mrtk_obs.h:59
obsd_t * data
Definition mrtk_obs.h:61
int n
Definition mrtk_obs.h:60
Observation data record.
Definition mrtk_obs.h:44
gtime_t time
Definition mrtk_obs.h:45
uint8_t sat
Definition mrtk_obs.h:46
uint8_t rcv
Definition mrtk_obs.h:46
uint16_t SNR[5+5]
Definition mrtk_obs.h:47
miono_region_t re[MIONO_MAX_RID]
Definition mrtk_nav.h:246
mrtk_sigcfg_t sigcfg[MRTK_NSYS]
Definition mrtk_opt.h:297
int sbassatsel
Definition mrtk_opt.h:195
int sigcfg_set
Definition mrtk_opt.h:298
int ionocorr
Definition mrtk_opt.h:248
int ionoopt
Definition mrtk_opt.h:187
uint8_t exsats[MAXSAT]
Definition mrtk_opt.h:225
char pppopt[256]
Definition mrtk_opt.h:243
int initrst
Definition mrtk_opt.h:234
int maxaveep
Definition mrtk_opt.h:233
int mode
Definition mrtk_opt.h:172
int correction
Definition mrtk_opt.h:300
int navsys
Definition mrtk_opt.h:175
double rb[3]
Definition mrtk_opt.h:221
int l6mrg
Definition mrtk_opt.h:275
int refpos
Definition mrtk_opt.h:197
Receiver raw data control type.
Definition mrtk_rcvraw.h:78
sbsmsg_t sbsmsg
Definition mrtk_rcvraw.h:87
nav_t nav
Definition mrtk_rcvraw.h:83
mrtk_sigcfg_t sigcfg[MRTK_NSYS]
int ephset
Definition mrtk_rcvraw.h:86
int has_prn
int sigcfg_set
obs_t obs
Definition mrtk_rcvraw.h:81
gtime_t has_time
uint8_t has_page[56]
double l6tag
char opt[256]
sta_t sta
Definition mrtk_rcvraw.h:84
int ephsat
Definition mrtk_rcvraw.h:85
gtime_t time
Definition mrtk_rcvraw.h:79
RTCM control struct type.
Definition mrtk_rtcm.h:50
obs_t obs
Definition mrtk_rtcm.h:57
dgps_t * dgps
Definition mrtk_rtcm.h:60
gtime_t time
Definition mrtk_rtcm.h:55
nav_t nav
Definition mrtk_rtcm.h:58
uint8_t buff[1200]
Definition mrtk_rtcm.h:75
ssr_t ssr[MAXSAT]
Definition mrtk_rtcm.h:61
int ephset
Definition mrtk_rtcm.h:67
char opt[256]
Definition mrtk_rtcm.h:79
int ephsat
Definition mrtk_rtcm.h:66
sta_t sta
Definition mrtk_rtcm.h:59
double prev_qr[6]
Definition mrtk_rtkpos.h:65
prcopt_t opt
Definition mrtk_rtkpos.h:63
double rb[6]
Definition mrtk_rtkpos.h:51
ssat_t ssat[MAXSAT]
Definition mrtk_rtkpos.h:60
sol_t sol
Definition mrtk_rtkpos.h:50
uint8_t * buff[3]
Definition mrtk_rtksvr.h:68
nav_t nav
Definition mrtk_rtksvr.h:79
int nb[3]
Definition mrtk_rtksvr.h:65
clas_ctx_t * clas
Definition mrtk_rtksvr.h:94
rtk_t rtk
Definition mrtk_rtksvr.h:64
int nmeareq
Definition mrtk_rtksvr.h:56
double nmeapos[3]
Definition mrtk_rtksvr.h:57
has_t * has
Definition mrtk_rtksvr.h:95
char files[3][MAXSTRPATH]
Definition mrtk_rtksvr.h:77
uint32_t nmsg[3][12]
Definition mrtk_rtksvr.h:72
rtk_lock_t lock
Definition mrtk_rtksvr.h:92
uint8_t * pbuf[3]
Definition mrtk_rtksvr.h:70
rtk_thread_t thread
Definition mrtk_rtksvr.h:84
solopt_t solopt[2]
Definition mrtk_rtksvr.h:60
int nsb[2]
Definition mrtk_rtksvr.h:66
stream_t * moni
Definition mrtk_rtksvr.h:82
sstat_t sstat
Definition mrtk_rtksvr.h:75
int buffsize
Definition mrtk_rtksvr.h:58
uint8_t * sbuf[2]
Definition mrtk_rtksvr.h:69
mrtk_ctx_t * ctx
Definition mrtk_rtksvr.h:93
char cmds_periodic[3][MAXRCVCMD]
Definition mrtk_rtksvr.h:89
sbsmsg_t sbsmsg[MAXSBSMSG]
Definition mrtk_rtksvr.h:80
char cmd_reset[MAXRCVCMD]
Definition mrtk_rtksvr.h:90
double rb_ave[3]
Definition mrtk_rtksvr.h:88
stream_t stream[8]
Definition mrtk_rtksvr.h:81
double bl_reset
Definition mrtk_rtksvr.h:91
obs_t obs[3][MAXOBSBUF]
Definition mrtk_rtksvr.h:78
int cputime
Definition mrtk_rtksvr.h:85
sol_t solbuf[MAXSOLBUF]
Definition mrtk_rtksvr.h:71
int nmeacycle
Definition mrtk_rtksvr.h:55
rtcm_t rtcm[3]
Definition mrtk_rtksvr.h:74
mdcl6d_t * mdciono
Definition mrtk_rtksvr.h:96
int npb[3]
Definition mrtk_rtksvr.h:67
uint32_t tick
Definition mrtk_rtksvr.h:83
int format[3]
Definition mrtk_rtksvr.h:59
gtime_t ftime[3]
Definition mrtk_rtksvr.h:76
raw_t raw[3]
Definition mrtk_rtksvr.h:73
uint8_t prn
Definition mrtk_nav.h:103
uint8_t rcv
Definition mrtk_nav.h:103
SBAS ephemeris type.
Definition mrtk_eph.h:130
gtime_t tof
Definition mrtk_eph.h:133
float qr[6]
Definition mrtk_sol.h:48
double rr[6]
Definition mrtk_sol.h:46
uint8_t stat
Definition mrtk_sol.h:54
gtime_t time
Definition mrtk_sol.h:45
int posf
Definition mrtk_opt.h:352
uint8_t vsat[5]
Definition mrtk_sol.h:104
uint8_t vs
Definition mrtk_sol.h:100
double azel[2]
Definition mrtk_sol.h:101
gtime_t t0[6]
Definition mrtk_nav.h:176
int iod[6]
Definition mrtk_nav.h:178
uint8_t update
Definition mrtk_nav.h:194
int iode
Definition mrtk_nav.h:179
siteion_t sion
Definition mrtk_nav.h:297
gtime_t time
Definition mrtk_nav.h:296
sitetrp_t strp
Definition mrtk_nav.h:298
double pos[3]
Definition mrtk_nav.h:380
int deltype
Definition mrtk_nav.h:379
double hgt
Definition mrtk_nav.h:382
double del[3]
Definition mrtk_nav.h:381
siteion_t sion[MAXSITES]
Definition mrtk_nav.h:291
sitetrp_t strp[MAXSITES]
Definition mrtk_nav.h:292
gtime_t time[MAXSITES]
Definition mrtk_nav.h:289
#define malloc(x)
Definition toml.c:52
#define calloc(x, y)
Definition toml.c:54
#define free(x)
Definition toml.c:53