MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_clas.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_clas.c : QZSS CLAS (L6D) CSSR decoder and bank management
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/**
17 * @file mrtk_clas.c
18 * @brief QZSS CLAS (L6D) CSSR decoder and bank management.
19 *
20 * References:
21 * [1] claslib (https://github.com/mf-arai/claslib) — upstream implementation
22 * [2] IS-QZSS-L6-003 — CLAS Compact SSR Specification
23 *
24 * Notes:
25 * - Ported from upstream claslib/cssr.c with singleton→context conversion.
26 * - All output_cssr_* / CSV debug functions are omitted (not needed in core).
27 * - Decoders write to ctx->dec_ssr[] (clas_dec_ssr_t) as intermediate buffer,
28 * NOT to nav_t.ssr[] directly, to isolate from MRTKLIB ssr_t differences.
29 */
30#include "mrtklib/mrtk_clas.h"
31
32#include <math.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include "mrtklib/mrtk_const.h"
37#include "mrtklib/mrtk_trace.h"
38
39/*============================================================================
40 * Constants
41 *===========================================================================*/
42
43/* SSR update intervals (seconds), indexed by 4-bit UDI field */
44static const double ssrudint[16] = {1, 2, 5, 10, 15, 30, 60, 120, 240, 300, 600, 900, 1800, 3600, 7200, 10800};
45
46#define SQR(x) ((x) * (x))
47#define MAX(x, y) ((x) > (y) ? (x) : (y))
48#define MIN(x, y) ((x) < (y) ? (x) : (y))
49
50/*============================================================================
51 * Context Management
52 *===========================================================================*/
53
54extern int clas_ctx_init(clas_ctx_t* ctx) {
55 int i;
56 if (!ctx) {
57 return -1;
58 }
59
60 memset(ctx, 0, sizeof(clas_ctx_t));
61
62 for (i = 0; i < CLAS_CH_NUM; i++) {
63 ctx->bank[i] = (clas_bank_ctrl_t*)calloc(1, sizeof(clas_bank_ctrl_t));
64 if (!ctx->bank[i]) {
65 clas_ctx_free(ctx);
66 return -1;
67 }
68 }
69 /* initialize banks (sets bank->use = 1) */
70 for (i = 0; i < CLAS_CH_NUM; i++) {
71 clas_bank_init(ctx, i);
72 }
73 /* initialize tow_ref to -1 (unset) */
74 for (i = 0; i < CSSR_REF_MAX; i++) {
75 ctx->tow_ref[i] = -1;
76 }
77 for (i = 0; i < CLAS_CH_NUM; i++) {
78 ctx->l6facility[i] = -1;
79 ctx->l6delivery[i] = -1;
80 ctx->l6pattern[i] = -1;
81 ctx->l6lock_time[i].time = 0;
82 ctx->l6lock_time[i].sec = 0.0;
83 }
84 ctx->initialized = 1;
85 return 0;
86}
87
88extern void clas_ctx_free(clas_ctx_t* ctx) {
89 int i;
90 if (!ctx) {
91 return;
92 }
93
94 for (i = 0; i < CLAS_CH_NUM; i++) {
95 free(ctx->bank[i]);
96 ctx->bank[i] = NULL;
97 }
98 ctx->initialized = 0;
99}
100
101extern void clas_bank_init(clas_ctx_t* ctx, int ch) {
102 clas_bank_ctrl_t* bank;
103 if (!ctx || ch < 0 || ch >= CLAS_CH_NUM || !ctx->bank[ch]) {
104 return;
105 }
106
107 bank = ctx->bank[ch];
108 memset(&bank->LatestTrop, 0, sizeof(clas_latest_trop_t));
109 memset(bank->OrbitBank, 0, sizeof(bank->OrbitBank));
110 memset(bank->ClockBank, 0, sizeof(bank->ClockBank));
111 memset(bank->BiasBank, 0, sizeof(bank->BiasBank));
112 memset(bank->TropBank, 0, sizeof(bank->TropBank));
113 bank->Facility = -1;
114 bank->separation = 0;
115 bank->NextOrbit = 0;
116 bank->NextClock = 0;
117 bank->NextBias = 0;
118 bank->NextTrop = 0;
119 bank->use = 1;
120
121 /* initialize fastfix=1 for all networks (matches upstream init_fastfix_flag):
122 * fastfix=1 → tropless=0 → accept trop entries regardless of future timestamp.
123 * This allows clas_bank_get_close() to find trop data that may be timestamped
124 * slightly ahead of the current observation time. */
125 for (int i = 1; i < CLAS_MAX_NETWORK; i++) {
126 bank->fastfix[i] = 1;
127 }
128}
129
130/*============================================================================
131 * Helper Functions
132 *===========================================================================*/
133
134static double decode_sval(const uint8_t* buff, int i, int n, double lsb) {
135 int slim = -((1 << (n - 1)) - 1) - 1, v;
136 v = getbits(buff, i, n);
137 return (v == slim) ? CSSR_INVALID_VALUE : (double)v * lsb;
138}
139
140extern int cssr_sys2gnss(int sys, int* prn0) {
141 int id = CSSR_SYS_NONE;
142 if (prn0) {
143 *prn0 = 1;
144 }
145
146 switch (sys) {
147 case SYS_GPS:
148 id = CSSR_SYS_GPS;
149 break;
150 case SYS_GLO:
151 id = CSSR_SYS_GLO;
152 break;
153 case SYS_GAL:
154 id = CSSR_SYS_GAL;
155 break;
156 case SYS_CMP:
157 id = CSSR_SYS_BDS;
158 break;
159 case SYS_SBS:
160 id = CSSR_SYS_SBS;
161 if (prn0) {
162 *prn0 = 120;
163 }
164 break;
165 case SYS_QZS:
166 id = CSSR_SYS_QZS;
167 if (prn0) {
168 *prn0 = 193;
169 }
170 break;
171 }
172 return id;
173}
174
175extern int cssr_gnss2sys(int gnss, int* prn0) {
176 int sys = SYS_NONE;
177 if (prn0) {
178 *prn0 = 1;
179 }
180
181 switch (gnss) {
182 case CSSR_SYS_GPS:
183 sys = SYS_GPS;
184 break;
185 case CSSR_SYS_GLO:
186 sys = SYS_GLO;
187 break;
188 case CSSR_SYS_GAL:
189 sys = SYS_GAL;
190 break;
191 case CSSR_SYS_BDS:
192 sys = SYS_CMP;
193 break;
194 case CSSR_SYS_SBS:
195 sys = SYS_SBS;
196 if (prn0) {
197 *prn0 = 120;
198 }
199 break;
200 case CSSR_SYS_QZS:
201 sys = SYS_QZS;
202 if (prn0) {
203 *prn0 = 193;
204 }
205 break;
206 }
207 return sys;
208}
209
210static int svmask2nsat(uint64_t svmask) {
211 int j, nsat = 0;
212 for (j = 0; j < CSSR_MAX_SV_GNSS; j++) {
213 if ((svmask >> (CSSR_MAX_SV_GNSS - 1 - j)) & 1) {
214 nsat++;
215 }
216 }
217 return nsat;
218}
219
220static int sigmask2nsig(uint16_t sigmask) {
221 int j, nsig = 0;
222 for (j = 0; j < CSSR_MAX_SIG; j++) {
223 if ((sigmask >> j) & 1) {
224 nsig++;
225 }
226 }
227 return nsig;
228}
229
230static int svmask2nsatlist(uint64_t svmask, int id, int* sat) {
231 int j, nsat = 0, sys, prn_min;
232 sys = cssr_gnss2sys(id, &prn_min);
233 for (j = 0; j < CSSR_MAX_SV_GNSS; j++) {
234 if ((svmask >> (CSSR_MAX_SV_GNSS - 1 - j)) & 1) {
235 sat[nsat++] = satno(sys, prn_min + j);
236 }
237 }
238 return nsat;
239}
240
241static int svmask2sat(uint64_t* svmask, int* sat) {
242 int j, id, nsat = 0, sys, prn_min;
243 for (id = 0; id < CSSR_MAX_GNSS; id++) {
244 sys = cssr_gnss2sys(id, &prn_min);
245 for (j = 0; j < CSSR_MAX_SV_GNSS; j++) {
246 if ((svmask[id] >> (CSSR_MAX_SV_GNSS - 1 - j)) & 1) {
247 if (sat) {
248 sat[nsat] = satno(sys, prn_min + j);
249 }
250 nsat++;
251 }
252 }
253 }
254 return nsat;
255}
256
257static float decode_cssr_quality_stec(int a, int b) {
258 if ((a == 0 && b == 0) || (a == 7 && b == 7)) {
259 return 9999.0f * 1000.0f;
260 }
261 return (float)((1.0 + b * 0.25) * pow(3.0, a) - 1.0);
262}
263
264static float decode_cssr_quality_trop(int a, int b) {
265 if ((a == 0 && b == 0) || (a == 7 && b == 7)) {
266 return 9999.0f;
267 }
268 return (float)((1.0 + b * 0.25) * pow(3.0, a) - 1.0);
269}
270
271static void check_week_ref(clas_ctx_t* ctx, clas_l6buf_t* l6, int tow, int i) {
272 if (ctx->obs_ref[i].time != 0 || ctx->obs_ref[i].sec != 0.0) {
273 gtime_t time = gpst2time(ctx->week_ref[i], tow);
274 if (timediff(time, ctx->obs_ref[i]) > (86400 * 7 / 2)) {
275 trace(NULL, 2, "check_week_ref(): adjust ref week, subtype=%2d, week=%d\n", i + 1, --ctx->week_ref[i]);
276 }
277 ctx->obs_ref[i].time = 0;
278 ctx->obs_ref[i].sec = 0.0;
279 }
280 if (l6->tow0 != -1) {
281 if (ctx->tow_ref[i] != -1 && ((tow - ctx->tow_ref[i]) < (-86400 * 7 / 2))) {
282 ++ctx->week_ref[i];
283 }
284 ctx->tow_ref[i] = tow;
285 }
286}
287
288/* signal mask to per-satellite signal indices (position-based) */
289static int sigmask2sig_p(int nsat, int* sat, uint16_t* sigmask, uint16_t* cellmask, int* nsig, int* sig) {
290 int j, k, id, sys, sys_p = -1, nsig_s = 0, code[CSSR_MAX_SIG];
291
292 for (j = 0; j < nsat; j++) {
293 sys = satsys(sat[j], NULL);
294 if (sys != sys_p) {
295 id = cssr_sys2gnss(sys, NULL);
296 for (k = 0, nsig_s = 0; k < CSSR_MAX_SIG; k++) {
297 if ((sigmask[id] >> (CSSR_MAX_SIG - 1 - k)) & 1) {
298 code[nsig_s++] = k;
299 }
300 }
301 }
302 sys_p = sys;
303 for (k = 0, nsig[j] = 0; k < nsig_s; k++) {
304 if ((cellmask[j] >> (nsig_s - 1 - k)) & 1) {
305 if (sig) {
306 sig[j * CSSR_MAX_SIG + nsig[j]] = code[k];
307 }
308 nsig[j]++;
309 }
310 }
311 }
312 return 1;
313}
314
315/* signal mask to per-satellite RTKLIB signal codes */
316static int sigmask2sig(int nsat, int* sat, uint16_t* sigmask, uint16_t* cellmask, int* nsig, int* sig) {
317 int j, k, id, sys, sys_p = -1, nsig_s = 0, code[CSSR_MAX_SIG], csize = 0;
318 int* codes = NULL;
319 static const int codes_gps[] = {CODE_L1C, CODE_L1P, CODE_L1W, CODE_L1S, CODE_L1L, CODE_L1X, CODE_L2S,
321 static const int codes_glo[] = {CODE_L1C, CODE_L1P, CODE_L2C, CODE_L2P, CODE_L3I, CODE_L3Q, CODE_L3X};
322 static const int codes_gal[] = {CODE_L1B, CODE_L1C, CODE_L1X, CODE_L5I, CODE_L5Q, CODE_L5X,
324 static const int codes_qzs[] = {CODE_L1C, CODE_L1S, CODE_L1L, CODE_L1X, CODE_L2S,
326 static const int codes_bds[] = {CODE_L2I, CODE_L2Q, CODE_L2X, CODE_L6I, CODE_L6Q,
328 static const int codes_sbs[] = {CODE_L1C, CODE_L5I, CODE_L5Q, CODE_L5X};
329
330 for (j = 0; j < nsat; j++) {
331 sys = satsys(sat[j], NULL);
332 if (sys != sys_p) {
333 id = cssr_sys2gnss(sys, NULL);
334 switch (sys) {
335 case SYS_GPS:
336 codes = (int*)codes_gps;
337 csize = sizeof(codes_gps) / sizeof(int);
338 break;
339 case SYS_GLO:
340 codes = (int*)codes_glo;
341 csize = sizeof(codes_glo) / sizeof(int);
342 break;
343 case SYS_GAL:
344 codes = (int*)codes_gal;
345 csize = sizeof(codes_gal) / sizeof(int);
346 break;
347 case SYS_CMP:
348 codes = (int*)codes_bds;
349 csize = sizeof(codes_bds) / sizeof(int);
350 break;
351 case SYS_QZS:
352 codes = (int*)codes_qzs;
353 csize = sizeof(codes_qzs) / sizeof(int);
354 break;
355 case SYS_SBS:
356 codes = (int*)codes_sbs;
357 csize = sizeof(codes_sbs) / sizeof(int);
358 break;
359 default:
360 codes = NULL;
361 csize = 0;
362 break;
363 }
364 for (k = 0, nsig_s = 0; k < csize && k < CSSR_MAX_SIG; k++) {
365 if ((sigmask[id] >> (CSSR_MAX_SIG - 1 - k)) & 1) {
366 code[nsig_s++] = codes[k];
367 }
368 }
369 }
370 sys_p = sys;
371 for (k = 0, nsig[j] = 0; k < nsig_s; k++) {
372 if ((cellmask[j] >> (nsig_s - 1 - k)) & 1) {
373 if (sig) {
374 sig[j * CSSR_MAX_SIG + nsig[j]] = code[k];
375 }
376 nsig[j]++;
377 }
378 }
379 }
380 return 1;
381}
382
383/*============================================================================
384 * Bank Internal Helpers — GET-SAME (find exact time match in ring buffer)
385 *===========================================================================*/
386
387static clas_orbit_bank_t* get_same_orbit(clas_bank_ctrl_t* bank, gtime_t time, int network) {
388 int i;
389 for (i = 0; i < CLAS_BANK_NUM; i++) {
390 if (bank->OrbitBank[i].use && bank->OrbitBank[i].network == network &&
391 timediff(bank->OrbitBank[i].time, time) == 0.0) {
392 return &bank->OrbitBank[i];
393 }
394 }
395 return NULL;
396}
397
398static clas_clock_bank_t* get_same_clock(clas_bank_ctrl_t* bank, gtime_t time, int network) {
399 int i;
400 for (i = 0; i < CLAS_BANK_NUM; i++) {
401 if (bank->ClockBank[i].use && bank->ClockBank[i].network == network &&
402 timediff(bank->ClockBank[i].time, time) == 0.0) {
403 return &bank->ClockBank[i];
404 }
405 }
406 return NULL;
407}
408
409static clas_bias_bank_t* get_same_bias(clas_bank_ctrl_t* bank, gtime_t time, int network) {
410 int i;
411 for (i = 0; i < CLAS_BANK_NUM; i++) {
412 if (bank->BiasBank[i].use && bank->BiasBank[i].network == network &&
413 timediff(bank->BiasBank[i].time, time) == 0.0) {
414 return &bank->BiasBank[i];
415 }
416 }
417 return NULL;
418}
419
421 int i;
422 for (i = 0; i < CLAS_BANK_NUM; i++) {
423 if (bank->TropBank[i].use && timediff(bank->TropBank[i].time, time) == 0.0) {
424 return &bank->TropBank[i];
425 }
426 }
427 return NULL;
428}
429
430/*============================================================================
431 * Bank Internal Helpers — GET-CLOSE (find nearest time match in ring buffer)
432 *===========================================================================*/
433
434static clas_orbit_bank_t* get_close_orbit(clas_bank_ctrl_t* bank, gtime_t time, int network, double age) {
435 /* Prefer the network-separated partition when its separation bit is set,
436 * but fall back to the global (net=0) partition if that has no fresh entry.
437 * Without the fallback a stale separation bit (set by an ST11 net combo and
438 * never cleared until a facility change) strands the lookup on an empty
439 * partition while global orbit corrections keep arriving — the CLAS
440 * handover fix->single freeze (#218). Mirrors get_close_net_cbias/pbias. */
441 /* guard the shift below: network is normally 1..CLAS_MAX_NETWORK-1; treat
442 * out-of-range / global (net<=0) as the global partition (no UB shift). */
443 int sep = (network >= 1 && network < CLAS_MAX_NETWORK && (bank->separation & (1u << (network - 1)))) ? network : 0;
444 int trysearch[2] = {sep, 0}, ntry = (sep != 0) ? 2 : 1, t;
445
446 for (t = 0; t < ntry; t++) {
447 int search = trysearch[t];
448 int pos = -1, i;
449 for (i = 0; i < CLAS_BANK_NUM; i++) {
450 if (!bank->OrbitBank[i].use || bank->OrbitBank[i].network != search) {
451 continue;
452 }
453 if (fabs(timediff(bank->OrbitBank[i].time, time)) > age) {
454 continue;
455 }
456 if (pos != -1 &&
457 fabs(timediff(bank->OrbitBank[pos].time, time)) < fabs(timediff(bank->OrbitBank[i].time, time))) {
458 continue;
459 }
460 if (timediff(bank->OrbitBank[i].time, time) > 0.0) {
461 continue;
462 }
463 pos = i;
464 }
465 if (pos != -1) {
466 return &bank->OrbitBank[pos];
467 }
468 }
469 return NULL;
470}
471
472static clas_clock_bank_t* get_close_clock(clas_bank_ctrl_t* bank, gtime_t obstime, gtime_t time, int network,
473 double age) {
474 /* Same global (net=0) fallback as get_close_orbit — see #218. */
475 /* guard the shift below: network is normally 1..CLAS_MAX_NETWORK-1; treat
476 * out-of-range / global (net<=0) as the global partition (no UB shift). */
477 int sep = (network >= 1 && network < CLAS_MAX_NETWORK && (bank->separation & (1u << (network - 1)))) ? network : 0;
478 int trysearch[2] = {sep, 0}, ntry = (sep != 0) ? 2 : 1, t;
479
480 for (t = 0; t < ntry; t++) {
481 int search = trysearch[t];
482 int pos = -1, i;
483 for (i = 0; i < CLAS_BANK_NUM; i++) {
484 if (!bank->ClockBank[i].use || bank->ClockBank[i].network != search) {
485 continue;
486 }
487 if (timediff(bank->ClockBank[i].time, time) >= age) {
488 continue;
489 }
490 if (pos != -1 && timediff(bank->ClockBank[pos].time, bank->ClockBank[i].time) > 0.0) {
491 continue;
492 }
493 if (timediff(bank->ClockBank[i].time, obstime) > 0.0) {
494 continue;
495 }
496 pos = i;
497 }
498 if (pos != -1) {
499 return &bank->ClockBank[pos];
500 }
501 }
502 return NULL;
503}
504
505static clas_bias_bank_t* get_close_cbias(clas_bank_ctrl_t* bank, gtime_t time, int network, double age) {
506 int pos = -1, i;
507 if (network < 0 || network >= CLAS_MAX_NETWORK) {
508 return NULL;
509 }
510
511 for (i = 0; i < CLAS_BANK_NUM; i++) {
512 if (!bank->BiasBank[i].use || bank->BiasBank[i].network != network) {
513 continue;
514 }
515 if (timediff(bank->BiasBank[i].time, time) > age) {
516 continue;
517 }
518 if (pos != -1 && timediff(bank->BiasBank[pos].time, bank->BiasBank[i].time) > 0.0) {
519 continue;
520 }
521 if ((bank->BiasBank[i].bflag & 0x01) == 0x01) {
522 pos = i;
523 }
524 }
525 return (pos != -1) ? &bank->BiasBank[pos] : NULL;
526}
527
528static clas_bias_bank_t* get_close_pbias(clas_bank_ctrl_t* bank, gtime_t time, int network, double age) {
529 int pos = -1, i;
530 if (network < 0 || network >= CLAS_MAX_NETWORK) {
531 return NULL;
532 }
533
534 for (i = 0; i < CLAS_BANK_NUM; i++) {
535 if (!bank->BiasBank[i].use || bank->BiasBank[i].network != network) {
536 continue;
537 }
538 if (timediff(bank->BiasBank[i].time, time) > age) {
539 continue;
540 }
541 if (pos != -1 && timediff(bank->BiasBank[pos].time, bank->BiasBank[i].time) > 0.0) {
542 continue;
543 }
544 if ((bank->BiasBank[i].bflag & 0x02) == 0x02) {
545 pos = i;
546 }
547 }
548 return (pos != -1) ? &bank->BiasBank[pos] : NULL;
549}
550
551static clas_bias_bank_t* get_close_net_cbias(clas_bank_ctrl_t* bank, gtime_t time, int network, double age) {
553 if (network != 0 && !(cb = get_close_cbias(bank, time, network, age))) {
554 cb = get_close_cbias(bank, time, 0, age);
555 } else {
556 cb = get_close_cbias(bank, time, (network == 0) ? 0 : network, age);
557 }
558 return cb;
559}
560
561static clas_bias_bank_t* get_close_net_pbias(clas_bank_ctrl_t* bank, gtime_t time, int network, double age) {
563 if (network != 0 && !(pb = get_close_pbias(bank, time, network, age))) {
564 pb = get_close_pbias(bank, time, 0, age);
565 } else {
566 pb = get_close_pbias(bank, time, (network == 0) ? 0 : network, age);
567 }
568 return pb;
569}
570
571static clas_trop_bank_t* get_close_trop(clas_bank_ctrl_t* bank, gtime_t obstime, gtime_t time, int network, double age,
572 int tropless) {
573 int pos = -1, i;
574 if (network < 1 || network >= CLAS_MAX_NETWORK) {
575 return NULL;
576 }
577
578 for (i = 0; i < CLAS_BANK_NUM; i++) {
579 if (!bank->TropBank[i].use) {
580 continue;
581 }
582 if (fabs(timediff(time, bank->TropBank[i].time)) > age) {
583 continue;
584 }
585 if (pos != -1 && timediff(bank->TropBank[pos].time, bank->TropBank[i].time) > 0.0) {
586 continue;
587 }
588 if (tropless && timediff(obstime, bank->TropBank[i].time) < 0.0) {
589 continue;
590 }
591 if (bank->TropBank[i].gridnum[network - 1] > 0) {
592 pos = i;
593 }
594 }
595 return (pos != -1) ? &bank->TropBank[pos] : NULL;
596}
597
598static int get_bias_save_point(clas_bias_bank_t* bias, int prn, int mode) {
599 int i;
600 for (i = 0; i < MAXCODE; i++) {
601 if (bias->smode[prn - 1][i] == mode) {
602 return i;
603 }
604 }
605 for (i = 0; i < MAXCODE; i++) {
606 if (bias->smode[prn - 1][i] == 0) {
607 return i;
608 }
609 }
610 return -1;
611}
612
613/*============================================================================
614 * Bank SET Functions — store decoded corrections into ring buffers
615 *
616 * Read from ctx->dec_ssr[] (intermediate decoder buffer) instead of nav->ssr_ch[][]
617 *===========================================================================*/
618
619static void check_cssr_changed_facility(clas_ctx_t* ctx, int ch, int facility) {
620 clas_bank_ctrl_t* bank = ctx->bank[ch];
621 if (bank->Facility != facility) {
622 trace(NULL, 4, "bank clear: facility changed %d->%d\n", bank->Facility + 1, facility + 1);
623 memset(&bank->LatestTrop, 0, sizeof(clas_latest_trop_t));
624 memset(bank->OrbitBank, 0, sizeof(bank->OrbitBank));
625 memset(bank->ClockBank, 0, sizeof(bank->ClockBank));
626 memset(bank->BiasBank, 0, sizeof(bank->BiasBank));
627 memset(bank->TropBank, 0, sizeof(bank->TropBank));
628 bank->Facility = facility;
629 bank->separation = 0;
630 bank->NextOrbit = bank->NextClock = bank->NextBias = bank->NextTrop = 0;
631 }
632}
633
634static void set_cssr_bank_orbit(clas_ctx_t* ctx, int ch, gtime_t time, int network) {
635 clas_bank_ctrl_t* bank = ctx->bank[ch];
636 clas_orbit_bank_t* orbit;
637 clas_dec_ssr_t* ssr;
638 int i;
639
640 if ((orbit = get_same_orbit(bank, time, network)) == NULL) {
641 orbit = &bank->OrbitBank[bank->NextOrbit];
642 orbit->network = network;
643 orbit->time = time;
644 orbit->use = 1;
645 if (++bank->NextOrbit >= CLAS_BANK_NUM) {
646 bank->NextOrbit = 0;
647 }
648 memset(orbit->prn, 0, sizeof(orbit->prn));
649 } else {
650 /* clear existing entries */
651 for (i = 0; i < MAXSAT; i++) {
652 if (!orbit->prn[i]) {
653 continue;
654 }
655 orbit->prn[i] = 0;
656 orbit->udi[i] = 0.0;
657 orbit->iod[i] = 0;
658 orbit->iode[i] = 0;
659 orbit->deph0[i] = orbit->deph1[i] = orbit->deph2[i] = 0.0;
660 }
661 }
662 for (i = 0; i < MAXSAT; i++) {
663 ssr = &ctx->dec_ssr[i];
664 if (ssr->update_oc != 1) {
665 continue;
666 }
667 if (ssr->deph[0] != CSSR_INVALID_VALUE) {
668 orbit->prn[i] = i + 1;
669 orbit->udi[i] = ssr->udi[0];
670 orbit->iod[i] = ssr->iod[0];
671 orbit->iode[i] = ssr->iode;
672 orbit->deph0[i] = ssr->deph[0];
673 orbit->deph1[i] = ssr->deph[1];
674 orbit->deph2[i] = ssr->deph[2];
675 } else {
676 orbit->prn[i] = 0;
677 }
678 }
679}
680
681static void set_cssr_bank_clock(clas_ctx_t* ctx, int ch, gtime_t time, int network) {
682 clas_bank_ctrl_t* bank = ctx->bank[ch];
684 clas_dec_ssr_t* ssr;
685 int i;
686
687 if ((clk = get_same_clock(bank, time, network)) == NULL) {
688 clk = &bank->ClockBank[bank->NextClock];
689 clk->network = network;
690 clk->time = time;
691 clk->use = 1;
692 if (++bank->NextClock >= CLAS_BANK_NUM) {
693 bank->NextClock = 0;
694 }
695 memset(clk->prn, 0, sizeof(clk->prn));
696 } else {
697 for (i = 0; i < MAXSAT; i++) {
698 if (!clk->prn[i]) {
699 continue;
700 }
701 clk->prn[i] = 0;
702 clk->udi[i] = 0.0;
703 clk->iod[i] = 0;
704 clk->c0[i] = 0.0;
705 }
706 }
707 for (i = 0; i < MAXSAT; i++) {
708 ssr = &ctx->dec_ssr[i];
709 if (ssr->update_cc != 1) {
710 continue;
711 }
712 if (ssr->dclk[0] != CSSR_INVALID_VALUE) {
713 clk->prn[i] = i + 1;
714 clk->udi[i] = ssr->udi[1];
715 clk->iod[i] = ssr->iod[1];
716 clk->c0[i] = ssr->dclk[0];
717 } else {
718 clk->prn[i] = 0;
719 }
720 }
721}
722
723static void set_cssr_bank_cbias(clas_ctx_t* ctx, int ch, gtime_t time, int network, int iod) {
724 clas_bank_ctrl_t* bank = ctx->bank[ch];
725 clas_bias_bank_t* bias;
726 clas_dec_ssr_t* ssr;
727 int i, j, pos;
728
729 if ((bias = get_same_bias(bank, time, network)) == NULL) {
730 bias = &bank->BiasBank[bank->NextBias];
731 bias->network = network;
732 bias->bflag = 0;
733 bias->time = time;
734 bias->use = 1;
735 if (++bank->NextBias >= CLAS_BANK_NUM) {
736 bank->NextBias = 0;
737 }
738 memset(bias->smode, 0, sizeof(bias->smode));
739 memset(bias->sflag, 0, sizeof(bias->sflag));
740 memset(bias->prn, 0, sizeof(bias->prn));
741 }
742 /* clear previous code bias entries */
743 if (bias->bflag & 0x01) {
744 for (i = 0; i < MAXSAT; i++) {
745 for (j = 0; j < MAXCODE; j++) {
746 if (bias->sflag[i][j] & 0x01) {
747 bias->sflag[i][j] ^= 0x01;
748 bias->cbias[i][j] = 0.0;
749 }
750 }
751 }
752 bias->bflag ^= 0x01;
753 }
754 for (i = 0; i < MAXSAT; i++) {
755 ssr = &ctx->dec_ssr[i];
756 if (ssr->update_cb != 1 || ssr->nsig <= 0) {
757 continue;
758 }
759 bias->prn[i] = i + 1;
760 bias->bflag |= 0x01;
761 bias->udi[i] = ssr->udi[4];
762 bias->iod[i] = ssr->iod[4];
763 for (j = 0; j < MAXCODE; j++) {
764 if (ssr->smode[j] != 0 && (pos = get_bias_save_point(bias, bias->prn[i], ssr->smode[j])) != -1) {
765 bias->cbias[i][pos] = ssr->cbias[ssr->smode[j] - 1];
766 bias->smode[i][pos] = ssr->smode[j];
767 bias->sflag[i][pos] |= 0x01;
768 }
769 }
770 }
771}
772
773static void set_cssr_bank_pbias(clas_ctx_t* ctx, int ch, gtime_t time, int network, int iod) {
774 clas_bank_ctrl_t* bank = ctx->bank[ch];
775 clas_bias_bank_t* bias;
776 clas_dec_ssr_t* ssr;
777 int i, j, pos;
778
779 if ((bias = get_same_bias(bank, time, network)) == NULL) {
780 bias = &bank->BiasBank[bank->NextBias];
781 bias->network = network;
782 bias->bflag = 0;
783 bias->time = time;
784 bias->use = 1;
785 if (++bank->NextBias >= CLAS_BANK_NUM) {
786 bank->NextBias = 0;
787 }
788 memset(bias->smode, 0, sizeof(bias->smode));
789 memset(bias->sflag, 0, sizeof(bias->sflag));
790 memset(bias->prn, 0, sizeof(bias->prn));
791 }
792 /* clear previous phase bias entries */
793 if (bias->bflag & 0x02) {
794 for (i = 0; i < MAXSAT; i++) {
795 for (j = 0; j < MAXCODE; j++) {
796 if (bias->sflag[i][j] & 0x02) {
797 bias->sflag[i][j] ^= 0x02;
798 bias->pbias[i][j] = 0.0;
799 }
800 }
801 }
802 bias->bflag ^= 0x02;
803 }
804 for (i = 0; i < MAXSAT; i++) {
805 ssr = &ctx->dec_ssr[i];
806 if (ssr->update_pb != 1 || ssr->nsig <= 0) {
807 continue;
808 }
809 bias->prn[i] = i + 1;
810 bias->bflag |= 0x02;
811 bias->udi[i] = ssr->udi[5];
812 bias->iod[i] = ssr->iod[5];
813 for (j = 0; j < MAXCODE; j++) {
814 if (ssr->smode[j] != 0 && (pos = get_bias_save_point(bias, bias->prn[i], ssr->smode[j])) != -1) {
815 bias->pbias[i][pos] = ssr->pbias[ssr->smode[j] - 1];
816 bias->smode[i][pos] = ssr->smode[j];
817 bias->sflag[i][pos] |= 0x02;
818 }
819 }
820 }
821}
822
823/*============================================================================
824 * Latest Trop/STEC Helpers
825 *===========================================================================*/
826
827static void set_cssr_latest_trop(clas_bank_ctrl_t* bank, gtime_t time, ssrn_t* ssrn, int network) {
828 int i, j, sat;
829
830 for (i = 0; i < ssrn->ngp; i++) {
831 for (j = 0; j < ssrn->nsat[i]; j++) {
832 if (ssrn->stec[i][j] != CSSR_INVALID_VALUE) {
833 sat = ssrn->sat[i][j];
834 bank->LatestTrop.stec0[network - 1][i][sat - 1] = ssrn->stec0[i][j];
835 bank->LatestTrop.stec[network - 1][i][sat - 1] = ssrn->stec[i][j];
836 bank->LatestTrop.stectime[network - 1][i][sat - 1] = time;
837 bank->LatestTrop.prn[network - 1][i][sat - 1] = sat;
838 }
839 }
840 if (ssrn->trop_total[i] != CSSR_INVALID_VALUE && ssrn->trop_wet[i] != CSSR_INVALID_VALUE) {
841 bank->LatestTrop.total[network - 1][i] = ssrn->trop_total[i];
842 bank->LatestTrop.wet[network - 1][i] = ssrn->trop_wet[i];
843 bank->LatestTrop.troptime[network - 1][i] = time;
844 }
845 }
846 bank->LatestTrop.gridnum[network - 1] = ssrn->ngp;
847}
848
849static int get_cssr_latest_trop(clas_bank_ctrl_t* bank, double* total, double* wet, gtime_t time, int network,
850 int index) {
851 if (index < bank->LatestTrop.gridnum[network - 1] &&
852 timediff(time, bank->LatestTrop.troptime[network - 1][index]) <= CSSR_TROPVALIDAGE) {
853 *total = bank->LatestTrop.total[network - 1][index];
854 *wet = bank->LatestTrop.wet[network - 1][index];
855 return 1;
856 }
857 *total = CSSR_INVALID_VALUE;
858 *wet = CSSR_INVALID_VALUE;
859 return 0;
860}
861
862static int get_cssr_latest_iono(clas_bank_ctrl_t* bank, double* iono, gtime_t time, int network, int index, int sat) {
863 if (index < bank->LatestTrop.gridnum[network - 1] && bank->LatestTrop.prn[network - 1][index][sat - 1] == sat &&
864 timediff(time, bank->LatestTrop.stectime[network - 1][index][sat - 1]) <= CSSR_STECVALIDAGE) {
865 if (bank->LatestTrop.stec[network - 1][index][sat - 1] == CSSR_INVALID_VALUE) {
866 *iono = 40.3E16 / (FREQ1 * FREQ2) * bank->LatestTrop.stec0[network - 1][index][sat - 1];
867 } else {
868 *iono = 40.3E16 / (FREQ1 * FREQ2) * bank->LatestTrop.stec[network - 1][index][sat - 1];
869 }
870 return 1;
871 }
872 *iono = CSSR_INVALID_VALUE;
873 return 0;
874}
875
876/*============================================================================
877 * Bank SET — Troposphere/STEC
878 *===========================================================================*/
879
880static void set_cssr_bank_trop(clas_ctx_t* ctx, int ch, gtime_t time, ssrn_t* ssrn, int network) {
881 clas_bank_ctrl_t* bank = ctx->bank[ch];
882 clas_trop_bank_t* trop;
883 int i, j;
884 if ((trop = get_same_trop(bank, time)) == NULL) {
885 trop = &bank->TropBank[bank->NextTrop];
886 trop->time = time;
887 trop->use = 1;
888 if (++bank->NextTrop >= CLAS_BANK_NUM) {
889 bank->NextTrop = 0;
890 }
891 memset(trop->gridnum, 0, sizeof(trop->gridnum));
892 memset(trop->satnum, 0, sizeof(trop->satnum));
893 }
894
895 trop->gridnum[network - 1] = ssrn->ngp;
896 for (i = 0; i < ssrn->ngp; i++) {
897 trop->gridpos[network - 1][i][0] = ssrn->grid[i][0];
898 trop->gridpos[network - 1][i][1] = ssrn->grid[i][1];
899 trop->gridpos[network - 1][i][2] = ssrn->grid[i][2];
900 trop->total[network - 1][i] = ssrn->trop_total[i];
901 trop->wet[network - 1][i] = ssrn->trop_wet[i];
902 trop->satnum[network - 1][i] = ssrn->nsat[i];
903 /* use latest trop if current is invalid */
904 if (trop->total[network - 1][i] == CSSR_INVALID_VALUE || trop->wet[network - 1][i] == CSSR_INVALID_VALUE) {
905 get_cssr_latest_trop(bank, &trop->total[network - 1][i], &trop->wet[network - 1][i], time, network, i);
906 }
907 for (j = 0; j < trop->satnum[network - 1][i]; j++) {
908 if (ssrn->stec[i][j] == CSSR_INVALID_VALUE) {
909 get_cssr_latest_iono(bank, &trop->iono[network - 1][i][j], time, network, i, ssrn->sat[i][j]);
910 } else {
911 trop->iono[network - 1][i][j] = 40.3E16 / (FREQ1 * FREQ2) * ssrn->stec[i][j];
912 }
913 trop->prn[network - 1][i][j] = ssrn->sat[i][j];
914 }
915 }
916}
917
918/*============================================================================
919 * Add Base Value Helpers — merge base-epoch bias with delta corrections
920 *
921 * NOTE: uses static local storage (thread-safety limitation from upstream)
922 *===========================================================================*/
923
925 gtime_t basetime = timeadd(srcbias->time, fmod(time2gpst(srcbias->time, NULL), 30.0) * -1.0);
926 static clas_bias_bank_t cbias;
927 clas_bias_bank_t* temp;
928 int i, j, pos;
929
930 if ((temp = get_same_bias(bank, basetime, network)) && (temp->bflag & 0x01)) {
931 memcpy(&cbias, srcbias, sizeof(clas_bias_bank_t));
932 for (i = 0; i < MAXSAT; i++) {
933 if (cbias.prn[i] == 0 || temp->prn[i] == 0) {
934 continue;
935 }
936 for (j = 0; j < MAXCODE; j++) {
937 if (temp->smode[i][j] == 0 || !(temp->sflag[i][j] & 0x01)) {
938 continue;
939 }
940 if ((pos = get_bias_save_point(&cbias, cbias.prn[i], temp->smode[i][j])) != -1) {
941 if (cbias.cbias[i][pos] != CSSR_INVALID_VALUE && temp->cbias[i][j] != CSSR_INVALID_VALUE) {
942 cbias.cbias[i][pos] += temp->cbias[i][j];
943 } else {
944 cbias.cbias[i][pos] = CSSR_INVALID_VALUE;
945 }
946 cbias.smode[i][pos] = temp->smode[i][j];
947 cbias.sflag[i][pos] |= 0x01;
948 }
949 }
950 }
951 return &cbias;
952 }
953 return srcbias;
954}
955
957 gtime_t basetime = timeadd(srcbias->time, fmod(time2gpst(srcbias->time, NULL), 30.0) * -1.0);
958 static clas_bias_bank_t pbias;
959 clas_bias_bank_t* temp;
960 int i, j, pos;
961
962 if ((temp = get_same_bias(bank, basetime, network)) && (temp->bflag & 0x02)) {
963 memcpy(&pbias, srcbias, sizeof(clas_bias_bank_t));
964 for (i = 0; i < MAXSAT; i++) {
965 if (pbias.prn[i] == 0 || temp->prn[i] == 0) {
966 continue;
967 }
968 for (j = 0; j < MAXCODE; j++) {
969 if (temp->smode[i][j] == 0 || !(temp->sflag[i][j] & 0x02)) {
970 continue;
971 }
972 if ((pos = get_bias_save_point(&pbias, pbias.prn[i], temp->smode[i][j])) != -1) {
973 if (pbias.pbias[i][pos] != CSSR_INVALID_VALUE && temp->pbias[i][j] != CSSR_INVALID_VALUE) {
974 pbias.pbias[i][pos] += temp->pbias[i][j];
975 } else {
976 pbias.pbias[i][pos] = CSSR_INVALID_VALUE;
977 }
978 pbias.smode[i][pos] = temp->smode[i][j];
979 pbias.sflag[i][pos] |= 0x02;
980 }
981 }
982 }
983 return &pbias;
984 }
985 return srcbias;
986}
987
988/*============================================================================
989 * Grid/STEC Data Helpers (from upstream grid.c)
990 *===========================================================================*/
991
992static void init_grid_index(clas_corr_t* corr) {
993 int i;
994 for (i = 0; i < CLAS_MAX_GP; i++) {
995 corr->stec[i].data = &corr->stecdata[i][0];
996 corr->stec[i].nmax = MAXSAT;
997 corr->zwd[i].data = &corr->zwddata[i];
998 corr->zwd[i].nmax = 1;
999 }
1000}
1001
1002static void set_grid_data(clas_corr_t* corr, double* pos, int index) {
1003 corr->stec[index].pos[0] = pos[0] * R2D;
1004 corr->stec[index].pos[1] = pos[1] * R2D;
1005 corr->stec[index].pos[2] = pos[2];
1006 corr->stec[index].n = 0;
1007 corr->zwd[index].pos[0] = (float)(pos[0] * R2D);
1008 corr->zwd[index].pos[1] = (float)(pos[1] * R2D);
1009 corr->zwd[index].pos[2] = (float)pos[2];
1010 corr->zwd[index].n = 0;
1011}
1012
1013static int add_data_stec(stec_t* stec, gtime_t time, int sat, int slip, double iono, double rate, double rms,
1014 double quality) {
1015 if (stec->n >= stec->nmax) {
1016 return 0;
1017 }
1018 stec->data[stec->n].flag = 1;
1019 stec->data[stec->n].time = time;
1020 stec->data[stec->n].sat = (unsigned char)sat;
1021 stec->data[stec->n].slip = (unsigned char)slip;
1022 stec->data[stec->n].iono = (float)iono;
1023 stec->data[stec->n].rate = (float)rate;
1024 stec->data[stec->n].quality = (float)quality;
1025 stec->data[stec->n++].rms = (float)rms;
1026 return 1;
1027}
1028
1029static int add_data_trop(zwd_t* z, gtime_t time, double zwd, double ztd, double quality, double rms, int valid) {
1030 if (z->n >= z->nmax) {
1031 return 0;
1032 }
1033 z->data[z->n].time = time;
1034 z->data[z->n].valid = (unsigned char)valid;
1035 z->data[z->n].zwd = (float)zwd;
1036 z->data[z->n].ztd = (float)ztd;
1037 z->data[z->n].quality = (float)quality;
1038 z->data[z->n++].rms = (float)rms;
1039 return 1;
1040}
1041
1042/*============================================================================
1043 * Bank MERGE — combine closest corrections into clas_corr_t snapshot
1044 *===========================================================================*/
1045
1046static int sub_get_close_cssr(clas_bank_ctrl_t* bank, gtime_t time, int network, clas_orbit_bank_t** orbit,
1047 clas_clock_bank_t** clock, clas_bias_bank_t** cbias, clas_bias_bank_t** pbias,
1048 clas_trop_bank_t** trop, int* flag) {
1049 *orbit = get_close_orbit(bank, time, network, 180.0);
1050 if (!*orbit) {
1051 return 0;
1052 }
1053 *pbias = get_close_net_pbias(bank, (*orbit)->time, network, 0.0);
1054 if (!*pbias) {
1055 return 0;
1056 }
1057 if (!(*trop = get_close_trop(bank, time, (*orbit)->time, network, 30.0, bank->fastfix[network] ? 0 : 1))) {
1058 return 0;
1059 }
1060 /* re-align pbias to trop if needed */
1061 if (timediff((*trop)->time, (*pbias)->time) < 0.0 || timediff((*trop)->time, (*pbias)->time) >= 30.0) {
1062 if (!(*pbias = get_close_net_pbias(bank, (*trop)->time, network, 0.0))) {
1063 trace(NULL, 2, "sub_get_close_cssr(): pbias re-align failed, net=%d\n", network);
1064 return 0;
1065 }
1066 }
1067 /* The cbias epoch matching pbias was lost (CLAS sends the global ST4 code
1068 * bias only in subframe 1, network ST6 may be phase-only): the older one, if
1069 * still in the bank, is past its 60 s IS validity, so take the next cbias,
1070 * newer than pbias by at most 30 s, as the consistency check below allows
1071 * (#406, #242). */
1072 *cbias = get_close_net_cbias(bank, (*pbias)->time, network, 0.0);
1073 if (!*cbias || timediff((*pbias)->time, (*cbias)->time) > 0.0) {
1074 clas_bias_bank_t* next = get_close_net_cbias(bank, (*pbias)->time, network, 30.0);
1075 if (next && timediff(next->time, (*pbias)->time) > 0.0 && timediff(time, next->time) >= 0.0) {
1076 *cbias = next;
1077 }
1078 }
1079 if (!*cbias) {
1080 trace(NULL, 2, "sub_get_close_cssr(): cbias not found, net=%d\n", network);
1081 return 0;
1082 }
1083 if (!(*clock = get_close_clock(bank, time, (*orbit)->time, network, 30.0))) {
1084 trace(NULL, 2, "sub_get_close_cssr(): clock not found, net=%d\n", network);
1085 return 0;
1086 }
1087 /* temporal consistency checks */
1088 if (timediff((*pbias)->time, (*cbias)->time) < -30.0 || timediff((*pbias)->time, (*cbias)->time) > 0.0) {
1089 return 0;
1090 }
1091 if (timediff((*trop)->time, (*orbit)->time) < -30.0 || timediff((*trop)->time, (*orbit)->time) >= 30.0) {
1092 return 0;
1093 }
1094 if (timediff((*trop)->time, (*pbias)->time) < 0.0 || timediff((*trop)->time, (*pbias)->time) >= 30.0) {
1095 return 0;
1096 }
1097
1098 *cbias = add_base_value_to_cbias(bank, *cbias, ((*cbias)->network == 0) ? network : 0);
1099 *pbias = add_base_value_to_pbias(bank, *pbias, ((*pbias)->network == 0) ? network : 0);
1100 *flag = (timediff((*cbias)->time, (*pbias)->time) == 0.0) ? 1 : 0;
1101 return 1;
1102}
1103
1104/* slot of signal mode in a satellite's corr->smode[]: the slot already
1105 * holding it, else the first free one (-1: none) */
1106static int corr_bias_slot(const int smode[MAXCODE], int mode) {
1107 int i;
1108 for (i = 0; i < MAXCODE; i++) {
1109 if (smode[i] == mode) {
1110 return i;
1111 }
1112 }
1113 for (i = 0; i < MAXCODE; i++) {
1114 if (smode[i] == 0) {
1115 return i;
1116 }
1117 }
1118 return -1;
1119}
1120
1121extern int clas_bank_get_close(const clas_ctx_t* ctx, gtime_t time, int network, int ch, clas_corr_t* corr) {
1122 clas_bank_ctrl_t* bank = ctx->bank[ch];
1123 clas_orbit_bank_t* orbit;
1124 clas_clock_bank_t* clk;
1125 clas_bias_bank_t *cbias, *pbias;
1126 clas_trop_bank_t* trop;
1127 int i, j, k, sis;
1128
1129 if (!bank->use) {
1130 return -1;
1131 }
1132
1133 if (!sub_get_close_cssr(bank, time, network, &orbit, &clk, &cbias, &pbias, &trop, &sis)) {
1134 return -1;
1135 }
1136
1137 memset(corr, 0, sizeof(clas_corr_t));
1138 init_grid_index(corr);
1139
1140 /* bias corrections: a bank entry orders its slots by the signals of its
1141 * own mask, and cbias and pbias can come from different masks (cbias up to
1142 * 30 s older, or newer after a lost subframe 1, #406), so pair them by
1143 * signal, not by slot; pbias first keeps its mask order, as before */
1144 for (i = 0; i < MAXSAT; i++) {
1145 if (pbias->prn[i] != 0) {
1146 for (j = 0; j < MAXCODE; j++) {
1147 if (pbias->smode[i][j] != 0 && (pbias->sflag[i][j] & 0x02) &&
1148 (k = corr_bias_slot(corr->smode[i], pbias->smode[i][j])) >= 0) {
1149 corr->pbias[i][k] = pbias->pbias[i][j];
1150 corr->smode[i][k] = pbias->smode[i][j];
1151 }
1152 }
1153 }
1154 if (cbias->prn[i] != 0) {
1155 for (j = 0; j < MAXCODE; j++) {
1156 if (cbias->smode[i][j] != 0 && (cbias->sflag[i][j] & 0x01) &&
1157 (k = corr_bias_slot(corr->smode[i], cbias->smode[i][j])) >= 0) {
1158 corr->cbias[i][k] = cbias->cbias[i][j];
1159 corr->smode[i][k] = cbias->smode[i][j];
1160 }
1161 }
1162 }
1163 corr->time[i][4] = cbias->time;
1164 corr->prn[i][4] = cbias->prn[i];
1165 corr->udi[i][4] = cbias->udi[i];
1166 corr->iod[i][4] = cbias->iod[i];
1167 corr->time[i][5] = pbias->time;
1168 corr->prn[i][5] = pbias->prn[i];
1169 corr->udi[i][5] = pbias->udi[i];
1170 corr->iod[i][5] = pbias->iod[i];
1171 corr->flag[i] |= 0x04;
1172 }
1173 /* orbit corrections */
1174 for (i = 0; i < MAXSAT; i++) {
1175 if (orbit->prn[i] != 0) {
1176 corr->time[i][0] = orbit->time;
1177 corr->prn[i][0] = orbit->prn[i];
1178 corr->udi[i][0] = orbit->udi[i];
1179 corr->iod[i][0] = orbit->iod[i];
1180 corr->iode[i] = orbit->iode[i];
1181 corr->deph0[i] = orbit->deph0[i];
1182 corr->deph1[i] = orbit->deph1[i];
1183 corr->deph2[i] = orbit->deph2[i];
1184 corr->flag[i] |= 0x01;
1185 }
1186 }
1187 /* trop/iono grid corrections */
1188 corr->gridnum = trop->gridnum[network - 1];
1189 for (i = 0; i < corr->gridnum; i++) {
1190 set_grid_data(corr, &trop->gridpos[network - 1][i][0], i);
1191 for (j = 0; j < trop->satnum[network - 1][i]; j++) {
1192 if (trop->iono[network - 1][i][j] != CSSR_INVALID_VALUE) {
1193 add_data_stec(&corr->stec[i], trop->time, trop->prn[network - 1][i][j], 0,
1194 trop->iono[network - 1][i][j], 0.0, 0.0, 0.0);
1195 }
1196 }
1197 add_data_trop(&corr->zwd[i], trop->time, trop->wet[network - 1][i], trop->total[network - 1][i], 9999, 0, 1);
1198 }
1199 /* clock corrections */
1200 for (i = 0; i < MAXSAT; i++) {
1201 if (clk->prn[i] != 0) {
1202 corr->time[i][1] = clk->time;
1203 corr->prn[i][1] = clk->prn[i];
1204 corr->udi[i][1] = clk->udi[i];
1205 corr->iod[i][1] = clk->iod[i];
1206 corr->c0[i] = clk->c0[i];
1207 corr->flag[i] |= 0x02;
1208 }
1209 }
1210 corr->orbit_time = orbit->time;
1211 corr->clock_time = clk->time;
1212 corr->bias_time = cbias->time;
1213 corr->trop_time = trop->time;
1214 corr->facility = bank->Facility;
1215 corr->update_time = time;
1216 corr->network = network;
1217 corr->use = 1;
1218 return 0;
1219}
1220
1222 gtime_t time = corr->clock_time;
1223
1224 if (timediff(corr->orbit_time, time) > 0.0) {
1225 time = corr->orbit_time;
1226 }
1227 if (timediff(corr->bias_time, time) > 0.0) {
1228 time = corr->bias_time;
1229 }
1230 return time;
1231}
1232
1233extern int clas_backup_valid(const clas_ctx_t* ctx, gtime_t obstime, int l6mrg) {
1234 int ch, nch = l6mrg ? SSR_CH_NUM : 1;
1235
1236 if (!ctx) {
1237 return 0;
1238 }
1239 if (nch > CLAS_CH_NUM) {
1240 nch = CLAS_CH_NUM;
1241 }
1242 for (ch = 0; ch < nch; ch++) {
1243 if (ctx->backup[ch].use && timediff(obstime, backup_cssr_time(&ctx->backup[ch])) <= 180.0) {
1244 return 1;
1245 }
1246 }
1247 return 0;
1248}
1249
1250extern void clas_backup_current(clas_ctx_t* ctx, const clas_grid_t* grid, int l6mrg) {
1251 int ch, nch = l6mrg ? SSR_CH_NUM : 1;
1252
1253 if (!ctx || !grid) {
1254 return;
1255 }
1256 if (nch > CLAS_CH_NUM) {
1257 nch = CLAS_CH_NUM;
1258 }
1259 for (ch = 0; ch < nch; ch++) {
1260 memcpy(&ctx->backup[ch], &ctx->current[ch], sizeof(clas_corr_t));
1261 memcpy(&ctx->backup_grid[ch], grid, sizeof(clas_grid_t));
1262 init_grid_index(&ctx->backup[ch]);
1263 }
1264}
1265
1266extern void clas_clear_current(clas_ctx_t* ctx, int ch) {
1267 if (!ctx || ch < 0 || ch >= CLAS_CH_NUM) {
1268 return;
1269 }
1270 memset(&ctx->current[ch], 0, sizeof(clas_corr_t));
1271 init_grid_index(&ctx->current[ch]);
1272}
1273
1274extern void clas_restore_backup(clas_ctx_t* ctx, gtime_t time, clas_grid_t* grid, int l6mrg) {
1275 clas_clock_bank_t* clock;
1276 int i, ch, nch = l6mrg ? SSR_CH_NUM : 1;
1277
1278 if (!ctx || !grid) {
1279 return;
1280 }
1281 if (nch > CLAS_CH_NUM) {
1282 nch = CLAS_CH_NUM;
1283 }
1284 for (ch = 0; ch < nch; ch++) {
1285 clas_bank_ctrl_t* bank = ctx->bank[ch];
1286 if (!bank || !bank->use || !ctx->backup[ch].use) {
1287 continue;
1288 }
1289 if (timediff(time, backup_cssr_time(&ctx->backup[ch])) > 180.0) {
1290 continue;
1291 }
1292 memcpy(&ctx->current[ch], &ctx->backup[ch], sizeof(clas_corr_t));
1293 memcpy(grid, &ctx->backup_grid[ch], sizeof(clas_grid_t));
1294 init_grid_index(&ctx->current[ch]);
1295
1296 if ((clock = get_close_clock(bank, time, ctx->current[ch].orbit_time, grid->network, 30.0)) &&
1297 timediff(ctx->current[ch].clock_time, clock->time) != 0.0) {
1298 ctx->current[ch].clock_time = clock->time;
1299 for (i = 0; i < MAXSAT; i++) {
1300 if (clock->prn[i] != 0) {
1301 ctx->current[ch].time[i][1] = clock->time;
1302 ctx->current[ch].prn[i][1] = clock->prn[i];
1303 ctx->current[ch].udi[i][1] = clock->udi[i];
1304 ctx->current[ch].iod[i][1] = clock->iod[i];
1305 ctx->current[ch].c0[i] = clock->c0[i];
1306 ctx->current[ch].flag[i] |= 0x02;
1307 } else {
1308 ctx->current[ch].flag[i] &= ~0x02;
1309 ctx->current[ch].prn[i][1] = 0;
1310 }
1311 }
1312 }
1313 }
1314}
1315
1316/*============================================================================
1317 * Grid Status Check (from upstream cssr.c:check_cssr_grid_status)
1318 *===========================================================================*/
1319
1320extern void clas_check_grid_status(clas_ctx_t* ctx, const clas_corr_t* corr, int ch) {
1321 int i, valid, network, nvalid = 0;
1322
1323 if (!ctx || ch < 0 || ch >= CLAS_CH_NUM || !corr || !corr->use) {
1324 return;
1325 }
1326
1327 network = corr->network;
1328 if (network <= 0 || network >= CLAS_MAX_NETWORK) {
1329 return;
1330 }
1331
1332 /* orbit + trop corrections must exist */
1333 if (corr->orbit_time.time == 0 || corr->trop_time.time == 0) {
1334 for (i = 0; i < CLAS_MAX_GP; i++) {
1335 ctx->grid_stat[ch][network][i] = 0;
1336 }
1337 return;
1338 }
1339
1340 if (ctx->bank[ch] && ctx->bank[ch]->fastfix[network] && timediff(corr->update_time, corr->trop_time) >= 0.0) {
1341 ctx->bank[ch]->fastfix[network] = 0;
1342 }
1343
1344 for (i = 0; i < corr->gridnum && i < CLAS_MAX_GP; i++) {
1345 /* check trop validity */
1346 if (!corr->zwddata[i].valid) {
1347 ctx->grid_stat[ch][network][i] = 0;
1348 continue;
1349 }
1350 /* count valid STEC entries (added by add_data_stec with flag=1) */
1351 valid = corr->stec[i].n;
1352
1353 ctx->grid_stat[ch][network][i] = (valid >= 8) ? 1 : 0;
1354 if (valid >= 8) {
1355 nvalid++;
1356 }
1357 }
1358 trace(NULL, 4, "grid_status: ch=%d net=%d gridnum=%d nvalid=%d\n", ch, network, corr->gridnum, nvalid);
1359 /* clear remaining grid points */
1360 for (i = corr->gridnum; i < CLAS_MAX_GP; i++) {
1361 ctx->grid_stat[ch][network][i] = 0;
1362 }
1363}
1364
1365extern void clas_check_grid_status_time(clas_ctx_t* ctx, gtime_t time, int ch) {
1366 clas_bank_ctrl_t* bank;
1367 clas_orbit_bank_t* orbit;
1368 clas_trop_bank_t* trop;
1369 int i, j, k, valid, network, preliminary, nvalid;
1370
1371 if (!ctx || ch < 0 || ch >= CLAS_CH_NUM || !(bank = ctx->bank[ch]) || !bank->use) {
1372 return;
1373 }
1374
1375 for (i = 0; i < CLAS_MAX_NETWORK - 1; i++) {
1376 preliminary = i + 1;
1377 network = preliminary;
1378 orbit = NULL;
1379 trop = NULL;
1380
1381 for (j = 0; j < 2; j++) {
1382 orbit = get_close_orbit(bank, time, (j == 0) ? preliminary : 0, 180.0);
1383 if (orbit) {
1384 network = orbit->network ? orbit->network : preliminary;
1385 break;
1386 }
1387 }
1388 if (orbit && !(trop = get_close_trop(bank, time, orbit->time, network, 30.0, 1)) && bank->fastfix[network]) {
1389 trop = get_close_trop(bank, time, orbit->time, network, 30.0, 0);
1390 }
1391
1392 if (!orbit || !trop) {
1393 for (j = 0; j < CLAS_MAX_GP; j++) {
1394 ctx->grid_stat[ch][preliminary][j] = 0;
1395 }
1396 continue;
1397 }
1398 if (bank->fastfix[network] && timediff(time, trop->time) >= 0.0) {
1399 bank->fastfix[network] = 0;
1400 }
1401
1402 nvalid = 0;
1403 for (j = 0; j < trop->gridnum[i] && j < CLAS_MAX_GP; j++) {
1404 if (trop->total[i][j] == CSSR_INVALID_VALUE || trop->wet[i][j] == CSSR_INVALID_VALUE) {
1405 ctx->grid_stat[ch][network][j] = 0;
1406 continue;
1407 }
1408 for (k = 0, valid = trop->satnum[i][j]; k < trop->satnum[i][j]; k++) {
1409 if (trop->iono[i][j][k] == CSSR_INVALID_VALUE) {
1410 valid--;
1411 }
1412 }
1413 ctx->grid_stat[ch][network][j] = (valid >= 8) ? 1 : 0;
1414 if (valid >= 8) {
1415 nvalid++;
1416 }
1417 }
1418 for (; j < CLAS_MAX_GP; j++) {
1419 ctx->grid_stat[ch][network][j] = 0;
1420 }
1421 trace(NULL, 4, "grid_status_time: ch=%d net=%d fastfix=%d gridnum=%d nvalid=%d\n", ch, network,
1422 bank->fastfix[network], trop->gridnum[i], nvalid);
1423 }
1424}
1425
1426/*============================================================================
1427 * Update Functions — apply merged corrections to nav_t
1428 *===========================================================================*/
1429
1430extern void clas_update_global(nav_t* nav, const clas_corr_t* corr, int ch) {
1431 ssr_t* ssr;
1432 int i, j, sat;
1433
1434 for (sat = 1; sat <= MAXSAT; sat++) {
1435 ssr = &nav->ssr_ch[ch][sat - 1];
1436
1437 /* bias corrections */
1438 if (corr->prn[sat - 1][4] != 0) {
1439 for (i = 0; i < MAXCODE; i++) {
1440 ssr->discnt[i] = 0;
1441 ssr->pbias[i] = 0.0;
1442 ssr->cbias[i] = 0.0;
1443 }
1444 for (i = 0; i < MAXCODE; i++) {
1445 /* signal mode from the CSSR signal mask: 1..MAXCODE; out of
1446 * range only on a corrupt mask (claslib 0.9.0 guards it too) */
1447 if (corr->smode[sat - 1][i] >= 1 && corr->smode[sat - 1][i] <= MAXCODE) {
1448 j = corr->smode[sat - 1][i] - 1;
1449 ssr->cbias[j] = (float)corr->cbias[sat - 1][i];
1450 ssr->pbias[j] = corr->pbias[sat - 1][i];
1451 }
1452 }
1453 ssr->udi[4] = corr->udi[sat - 1][4];
1454 ssr->udi[5] = corr->udi[sat - 1][5];
1455 ssr->iod[4] = corr->iod[sat - 1][4];
1456 ssr->iod[5] = corr->iod[sat - 1][5];
1457 ssr->t0[4] = corr->time[sat - 1][4];
1458 ssr->t0[5] = corr->time[sat - 1][5];
1459 } else {
1460 memset(&ssr->t0[4], 0, sizeof(ssr->t0[4]));
1461 memset(&ssr->t0[5], 0, sizeof(ssr->t0[5]));
1462 }
1463
1464 /* orbit corrections */
1465 if (corr->prn[sat - 1][0] != 0) {
1466 ssr->t0[0] = corr->time[sat - 1][0];
1467 ssr->udi[0] = corr->udi[sat - 1][0];
1468 ssr->iod[0] = corr->iod[sat - 1][0];
1469 ssr->iode = corr->iode[sat - 1];
1470 ssr->deph[0] = corr->deph0[sat - 1];
1471 ssr->deph[1] = corr->deph1[sat - 1];
1472 ssr->deph[2] = corr->deph2[sat - 1];
1473 ssr->ddeph[0] = ssr->ddeph[1] = ssr->ddeph[2] = 0.0;
1474 } else {
1475 memset(&ssr->t0[0], 0, sizeof(ssr->t0[0]));
1476 }
1477
1478 /* clock corrections */
1479 if (corr->prn[sat - 1][1] != 0) {
1480 ssr->t0[1] = corr->time[sat - 1][1];
1481 ssr->udi[1] = corr->udi[sat - 1][1];
1482 ssr->iod[1] = corr->iod[sat - 1][1];
1483 ssr->dclk[0] = corr->c0[sat - 1];
1484 ssr->dclk[1] = ssr->dclk[2] = 0.0;
1485 ssr->update = 1;
1486 } else {
1487 memset(&ssr->t0[1], 0, sizeof(ssr->t0[1]));
1488 }
1489 }
1490 nav->facility[ch] = corr->facility;
1491}
1492
1493extern void clas_update_local(nav_t* nav, const clas_corr_t* corr, int ch) {
1494 /* local corrections (stec/zwd) are stored in ctx->current[ch]
1495 * and accessed directly by the positioning engine.
1496 * This function is a placeholder for Phase 4 integration. */
1497 (void)nav;
1498 (void)corr;
1499 (void)ch;
1500}
1501
1502/*============================================================================
1503 * CSSR Header Decoder
1504 *===========================================================================*/
1505
1506static int decode_cssr_head(clas_ctx_t* ctx, clas_l6buf_t* l6, cssr_t* cssr, int* sync, int* tow, int* iod,
1507 double* udint, int* ngnss, int i0) {
1508 int i = i0, udi;
1509
1510 if (l6->subtype == CSSR_TYPE_MASK) {
1511 *tow = getbitu(l6->buff, i, 20);
1512 i += 20;
1513 } else {
1514 *tow = l6->tow0 + getbitu(l6->buff, i, 12);
1515 i += 12;
1516 /* the hourly epoch wrapped but the ST1 at the top of the hour, the only
1517 * message that advances tow0, was lost: without this the messages up to
1518 * the next ST1 are dated one hour old (#392) */
1519 if (l6->time.time != 0 && *tow < l6->tow_last - 1800) {
1520 l6->tow0 += 3600;
1521 if (l6->tow0 >= 604800) {
1522 l6->tow0 -= 604800;
1523 }
1524 *tow = l6->tow0 + getbitu(l6->buff, i - 12, 12);
1525 trace(NULL, 2, "decode_cssr_head(): hourly epoch wrapped without ST1, tow0=%d\n", l6->tow0);
1526 }
1527 }
1528 l6->tow_last = *tow;
1529 udi = getbitu(l6->buff, i, 4);
1530 i += 4;
1531 *sync = getbitu(l6->buff, i, 1);
1532 i += 1;
1533 *udint = ssrudint[udi];
1534 *iod = getbitu(l6->buff, i, 4);
1535 i += 4;
1536
1537 if (l6->subtype == CSSR_TYPE_MASK) {
1538 cssr->iod = *iod;
1539 *ngnss = getbitu(l6->buff, i, 4);
1540 i += 4;
1541 }
1542 return i;
1543}
1544
1545/*============================================================================
1546 * Bit Width Check Functions — verify buffer has enough bits before decoding
1547 *===========================================================================*/
1548
1549static int check_bit_width_mask(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1550 int k, ngnss, cmi, nsat, nsig;
1551 uint64_t svmask;
1552 uint16_t sigmask;
1553
1554 /* i0 is past the 16-bit message number and subtype: the rest of the header
1555 * (epoch 20, interval 4, multiple 1, IOD 4, ngnss 4) is 33 bits. Upstream
1556 * (claslib 0.8.0 on) counts 49 bits from here and reads ngnss from the
1557 * satellite mask, so a mask that fits in the first frame waited for the
1558 * second one, and a frame lost in between cost the whole mask (#363). */
1559 if (i0 + 33 > l6->havebit) {
1560 return 0;
1561 }
1562 ngnss = getbitu(l6->buff, i0 + 29, 4);
1563 i0 += 33;
1564 for (k = 0; k < ngnss; k++) {
1565 if (i0 + 61 > l6->havebit) {
1566 return 0;
1567 }
1568 /* GNSS ID 4, satellite mask 40, signal mask 16, cell-mask flag 1; only
1569 * the cell mask follows the block (upstream read both masks again
1570 * from the bits after it) */
1571 svmask = (uint64_t)getbitu(l6->buff, i0 + 4, 8) << 32;
1572 svmask |= (uint64_t)getbitu(l6->buff, i0 + 12, 32);
1573 sigmask = getbitu(l6->buff, i0 + 44, 16);
1574 cmi = getbitu(l6->buff, i0 + 60, 1);
1575 i0 += 61;
1576 if (cmi) {
1577 nsat = svmask2nsat(svmask);
1578 nsig = sigmask2nsig(sigmask);
1579 if (i0 + nsat * nsig > l6->havebit) {
1580 return 0;
1581 }
1582 i0 += nsat * nsig;
1583 }
1584 }
1585 return 1;
1586}
1587
1588static int check_bit_width_oc(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1589 int k, sat[CSSR_MAX_SV], nsat, prn;
1590 i0 += 21;
1591 if (i0 > l6->havebit) {
1592 return 0;
1593 }
1594 nsat = svmask2sat(cssr->svmask, sat);
1595 for (k = 0; k < nsat; k++) {
1596 i0 += (satsys(sat[k], &prn) == SYS_GAL) ? 51 : 49;
1597 if (i0 > l6->havebit) {
1598 return 0;
1599 }
1600 }
1601 return 1;
1602}
1603
1604static int check_bit_width_cc(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1605 int nsat = svmask2sat(cssr->svmask, NULL);
1606 return (i0 + 21 + 15 * nsat <= l6->havebit);
1607}
1608
1609static int check_bit_width_cb(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1610 int nsig[CSSR_MAX_SV], nsig_total = 0, k, sat[CSSR_MAX_SV], nsat;
1611 nsat = svmask2sat(cssr->svmask, sat);
1612 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, NULL);
1613 for (k = 0; k < nsat; k++) {
1614 nsig_total += nsig[k];
1615 }
1616 return i0 + 21 + nsig_total * 11 <= l6->havebit;
1617}
1618
1619static int check_bit_width_pb(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1620 int nsig[CSSR_MAX_SV], nsig_total = 0, k, sat[CSSR_MAX_SV], nsat;
1621 nsat = svmask2sat(cssr->svmask, sat);
1622 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, NULL);
1623 for (k = 0; k < nsat; k++) {
1624 nsig_total += nsig[k];
1625 }
1626 return i0 + 21 + nsig_total * 17 <= l6->havebit;
1627}
1628
1629static int check_bit_width_bias(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1630 int j, k, nsat, slen = 0, cbflag, pbflag, netflag, netmask = 0;
1631 int sat[CSSR_MAX_SV], nsig[CSSR_MAX_SV];
1632 nsat = svmask2sat(cssr->svmask, sat);
1633 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, NULL);
1634 if (i0 + 24 > l6->havebit) {
1635 return 0;
1636 }
1637 i0 += 21;
1638 cbflag = getbitu(l6->buff, i0, 1);
1639 i0++;
1640 pbflag = getbitu(l6->buff, i0, 1);
1641 i0++;
1642 netflag = getbitu(l6->buff, i0, 1);
1643 i0++;
1644 if (netflag) {
1645 if (i0 + 5 + nsat > l6->havebit) {
1646 return 0;
1647 }
1648 i0 += 5;
1649 netmask = getbitu(l6->buff, i0, nsat);
1650 i0 += nsat;
1651 }
1652 if (cbflag) {
1653 slen += 11;
1654 }
1655 if (pbflag) {
1656 slen += 17;
1657 }
1658 for (k = 0; k < nsat; k++) {
1659 if (netflag && !((netmask >> (nsat - 1 - k)) & 1)) {
1660 continue;
1661 }
1662 for (j = 0; j < nsig[k]; j++) {
1663 if (i0 + slen > l6->havebit) {
1664 return 0;
1665 }
1666 i0 += slen;
1667 }
1668 }
1669 return 1;
1670}
1671
1672static int check_bit_width_ura(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1673 int nsat = svmask2sat(cssr->svmask, NULL);
1674 return i0 + 21 + 6 * nsat <= l6->havebit;
1675}
1676
1677static int check_bit_width_stec(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1678 int j, sat[CSSR_MAX_SV], nsat, stec_type, nsat_local = 0;
1679 uint64_t net_svmask;
1680 static const int slen_t[4] = {20, 44, 54, 70};
1681 nsat = svmask2sat(cssr->svmask, sat);
1682 if (i0 + 28 + nsat > l6->havebit) {
1683 return 0;
1684 }
1685 i0 += 21;
1686 stec_type = getbitu(l6->buff, i0, 2);
1687 i0 += 2;
1688 i0 += 5;
1689 net_svmask = getbitu(l6->buff, i0, nsat);
1690 i0 += nsat;
1691 for (j = 0; j < nsat; j++) {
1692 if ((net_svmask >> (nsat - 1 - j)) & 1) {
1693 nsat_local++;
1694 }
1695 }
1696 return i0 + nsat_local * slen_t[stec_type] <= l6->havebit;
1697}
1698
1699static int check_bit_width_grid(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1700 int k, nsat, trop_type, ngp, sz_trop, sz_idx, sz_stec, nsat_local = 0;
1701 uint64_t net_svmask;
1702 nsat = svmask2sat(cssr->svmask, NULL);
1703 if (i0 + 41 + nsat > l6->havebit) {
1704 return 0;
1705 }
1706 i0 += 21;
1707 trop_type = getbitu(l6->buff, i0, 2);
1708 i0 += 2;
1709 sz_idx = getbitu(l6->buff, i0, 1);
1710 i0++;
1711 i0 += 5;
1712 net_svmask = getbitu(l6->buff, i0, nsat);
1713 i0 += nsat;
1714 i0 += 6;
1715 ngp = getbitu(l6->buff, i0, 6);
1716 i0 += 6;
1717 sz_trop = (trop_type == 0) ? 0 : 17;
1718 sz_stec = (sz_idx == 0) ? 7 : 16;
1719 for (k = 0; k < nsat; k++) {
1720 if ((net_svmask >> (nsat - 1 - k)) & 1) {
1721 nsat_local++;
1722 }
1723 }
1724 return i0 + ngp * (sz_trop + nsat_local * sz_stec) <= l6->havebit;
1725}
1726
1727static int check_bit_width_combo(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1728 int sat[CSSR_MAX_SV], nsat, j, flg_orbit, flg_clock, flg_net, sz;
1729 uint64_t net_svmask = 0;
1730 nsat = svmask2sat(cssr->svmask, sat);
1731 if (i0 + 24 > l6->havebit) {
1732 return 0;
1733 }
1734 i0 += 21;
1735 flg_orbit = getbitu(l6->buff, i0, 1);
1736 i0++;
1737 flg_clock = getbitu(l6->buff, i0, 1);
1738 i0++;
1739 flg_net = getbitu(l6->buff, i0, 1);
1740 i0++;
1741 if (flg_net) {
1742 if (i0 + 5 + nsat > l6->havebit) {
1743 return 0;
1744 }
1745 i0 += 5;
1746 net_svmask = getbitu(l6->buff, i0, nsat);
1747 i0 += nsat;
1748 }
1749 for (j = 0; j < nsat; j++) {
1750 if (flg_net && !((net_svmask >> (nsat - 1 - j)) & 1)) {
1751 continue;
1752 }
1753 if (flg_orbit) {
1754 sz = (satsys(sat[j], NULL) == SYS_GAL) ? 10 : 8;
1755 i0 += sz + 41;
1756 if (i0 > l6->havebit) {
1757 return 0;
1758 }
1759 }
1760 if (flg_clock) {
1761 if ((i0 += 15) > l6->havebit) {
1762 return 0;
1763 }
1764 }
1765 }
1766 return 1;
1767}
1768
1769static int check_bit_width_atmos(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1770 int trop_avail, stec_avail, trop_type, stec_type, ngp, sz_idx, sz, j, nsat;
1771 uint64_t net_svmask;
1772 static const int dstec_sz_t[4] = {4, 4, 5, 7};
1773 static const int trop_sz_t[3] = {9, 23, 30};
1774 static const int stec_sz_t[4] = {14, 38, 48, 64};
1775 nsat = svmask2sat(cssr->svmask, NULL);
1776 if (i0 + 36 > l6->havebit) {
1777 return 0;
1778 }
1779 i0 += 21;
1780 trop_avail = getbitu(l6->buff, i0, 2);
1781 i0 += 2;
1782 stec_avail = getbitu(l6->buff, i0, 2);
1783 i0 += 2;
1784 i0 += 5;
1785 ngp = getbitu(l6->buff, i0, 6);
1786 i0 += 6;
1787 if (trop_avail != 0) {
1788 if (i0 + 6 > l6->havebit) {
1789 return 0;
1790 }
1791 i0 += 6;
1792 }
1793 if ((trop_avail & 0x01) == 0x01) {
1794 if (i0 + 2 > l6->havebit) {
1795 return 0;
1796 }
1797 trop_type = getbitu(l6->buff, i0, 2);
1798 i0 += 2;
1799 sz = trop_sz_t[trop_type];
1800 if (i0 + sz > l6->havebit) {
1801 return 0;
1802 }
1803 i0 += sz;
1804 }
1805 if ((trop_avail & 0x02) == 0x02) {
1806 if (i0 + 5 > l6->havebit) {
1807 return 0;
1808 }
1809 sz_idx = getbitu(l6->buff, i0, 1);
1810 i0++;
1811 i0 += 4;
1812 sz = (sz_idx == 0) ? 6 : 8;
1813 if (i0 + sz * ngp > l6->havebit) {
1814 return 0;
1815 }
1816 i0 += sz * ngp;
1817 }
1818 if (stec_avail != 0) {
1819 if (i0 + nsat > l6->havebit) {
1820 return 0;
1821 }
1822 net_svmask = getbitu(l6->buff, i0, nsat);
1823 i0 += nsat;
1824 for (j = 0; j < nsat; j++) {
1825 if (!((net_svmask >> (nsat - 1 - j)) & 1)) {
1826 continue;
1827 }
1828 if (i0 + 6 > l6->havebit) {
1829 return 0;
1830 }
1831 i0 += 6;
1832 if ((stec_avail & 0x01) == 0x01) {
1833 if (i0 + 2 > l6->havebit) {
1834 return 0;
1835 }
1836 stec_type = getbitu(l6->buff, i0, 2);
1837 i0 += 2;
1838 sz = stec_sz_t[stec_type];
1839 if (i0 + sz > l6->havebit) {
1840 return 0;
1841 }
1842 i0 += sz;
1843 }
1844 if ((stec_avail & 0x02) == 0x02) {
1845 if (i0 + 2 > l6->havebit) {
1846 return 0;
1847 }
1848 sz_idx = getbitu(l6->buff, i0, 2);
1849 i0 += 2;
1850 i0 += ngp * dstec_sz_t[sz_idx];
1851 if (i0 > l6->havebit) {
1852 return 0;
1853 }
1854 }
1855 }
1856 }
1857 return 1;
1858}
1859
1860static int check_bit_width_si(clas_l6buf_t* l6, cssr_t* cssr, int i0) {
1861 int data_sz;
1862 if (i0 + 6 > l6->havebit) {
1863 return 0;
1864 }
1865 i0 += 4;
1866 data_sz = getbitu(l6->buff, i0, 2);
1867 i0 += 2;
1868 return i0 + 40 * (data_sz + 1) <= l6->havebit;
1869}
1870
1871/*============================================================================
1872 * ST1: Mask Message Decoder
1873 *===========================================================================*/
1874
1875static int decode_cssr_mask(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
1876 cssr_t* cssr = &ctx->cssr[ch];
1877 int i, j, k, l, sync, tow, ngnss, iod, nsat_g, id, nsig, ncell = 0;
1878 int sat[CSSR_MAX_SV];
1879 double udint;
1880
1881 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
1882 l6->tow0 = (int)(floor(tow / 3600.0) * 3600.0);
1883 check_week_ref(ctx, l6, tow, CSSR_REF_MASK);
1884 l6->time = gpst2time(ctx->week_ref[CSSR_REF_MASK], tow);
1885
1886 for (j = 0; j < CSSR_MAX_GNSS; j++) {
1887 cssr->cmi[j] = 0;
1888 cssr->svmask[j] = 0;
1889 cssr->sigmask[j] = 0;
1890 }
1891 for (j = 0; j < CSSR_MAX_SV; j++) {
1892 cssr->cellmask[j] = 0;
1893 }
1894
1895 trace(NULL, 2, "decode_cssr_mask: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, cssr->iod);
1896
1897 for (k = 0; k < ngnss; k++) {
1898 id = getbitu(l6->buff, i, 4);
1899 i += 4;
1900 cssr->svmask[id] = (uint64_t)getbitu(l6->buff, i, 8) << 32;
1901 i += 8;
1902 cssr->svmask[id] |= getbitu(l6->buff, i, 32);
1903 i += 32;
1904 cssr->sigmask[id] = getbitu(l6->buff, i, 16);
1905 i += 16;
1906 cssr->cmi[id] = getbitu(l6->buff, i, 1);
1907 i++;
1908
1909 nsig = sigmask2nsig(cssr->sigmask[id]);
1910 nsat_g = svmask2nsatlist(cssr->svmask[id], id, sat);
1911
1912 if (cssr->cmi[id]) {
1913 for (j = 0; j < nsat_g; j++) {
1914 cssr->cellmask[ncell] = getbitu(l6->buff, i, nsig);
1915 i += nsig;
1916 ncell++;
1917 }
1918 } else {
1919 for (j = 0; j < nsat_g; j++) {
1920 for (l = 0; l < nsig; l++) {
1921 cssr->cellmask[ncell] |= ((uint16_t)1 << (nsig - 1 - l));
1922 }
1923 ncell++;
1924 }
1925 }
1926 }
1927 cssr->l6delivery = ctx->l6delivery[ch];
1928 cssr->l6facility = ctx->l6facility[ch];
1929 l6->nbit = i;
1930 return sync ? 0 : 10;
1931}
1932
1933/*============================================================================
1934 * ST2: Orbit Correction Decoder
1935 *===========================================================================*/
1936
1937static int decode_cssr_oc(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
1938 cssr_t* cssr = &ctx->cssr[ch];
1939 int i, j, k, iod, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat, sys, iode;
1940 double udint;
1941 clas_dec_ssr_t* ssr;
1942
1943 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
1944 nsat = svmask2sat(cssr->svmask, sat);
1945 check_week_ref(ctx, l6, tow, CSSR_REF_ORBIT);
1946 l6->time = gpst2time(ctx->week_ref[CSSR_REF_ORBIT], tow);
1947
1948 trace(NULL, 2, "decode_cssr_oc: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, iod);
1949 if (cssr->l6facility != ctx->l6facility[ch]) {
1950 return -1;
1951 }
1952 if (cssr->iod != iod) {
1953 return -1;
1954 }
1955
1956 for (j = 0; j < MAXSAT; j++) {
1957 ssr = &ctx->dec_ssr[j];
1958 ssr->t0[0].sec = 0.0;
1959 ssr->t0[0].time = 0;
1960 ssr->udi[0] = 0;
1961 ssr->iod[0] = 0;
1962 ssr->update_oc = 0;
1963 ssr->update = 0;
1964 ssr->iode = 0;
1965 ssr->deph[0] = ssr->deph[1] = ssr->deph[2] = 0.0;
1966 }
1967
1968 for (j = 0; j < nsat; j++) {
1969 ssr = &ctx->dec_ssr[sat[j] - 1];
1970 sys = satsys(sat[j], NULL);
1971
1972 iode = (sys == SYS_GAL) ? getbitu(l6->buff, i, 10) : getbitu(l6->buff, i, 8);
1973 i += (sys == SYS_GAL) ? 10 : 8;
1974
1975 ssr->deph[0] = decode_sval(l6->buff, i, 15, 0.0016);
1976 i += 15;
1977 ssr->deph[1] = decode_sval(l6->buff, i, 13, 0.0064);
1978 i += 13;
1979 ssr->deph[2] = decode_sval(l6->buff, i, 13, 0.0064);
1980 i += 13;
1981 ssr->iode = iode;
1982
1983 ssr->t0[0] = l6->time;
1984 ssr->udi[0] = udint;
1985 ssr->iod[0] = cssr->iod;
1986 if (ssr->deph[0] == CSSR_INVALID_VALUE || ssr->deph[1] == CSSR_INVALID_VALUE ||
1987 ssr->deph[2] == CSSR_INVALID_VALUE) {
1988 ssr->deph[0] = ssr->deph[1] = ssr->deph[2] = CSSR_INVALID_VALUE;
1989 }
1990 for (k = 0; k < 3; k++) {
1991 ssr->ddeph[k] = 0.0;
1992 }
1993 ssr->update_oc = 1;
1994 ssr->update = 1;
1995 }
1996
1998 set_cssr_bank_orbit(ctx, ch, l6->time, 0);
1999 l6->nbit = i;
2000 return sync ? 0 : 10;
2001}
2002
2003/*============================================================================
2004 * ST3: Clock Correction Decoder
2005 *===========================================================================*/
2006
2007static int decode_cssr_cc(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2008 cssr_t* cssr = &ctx->cssr[ch];
2009 int i, j, iod, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat;
2010 double udint;
2011 clas_dec_ssr_t* ssr;
2012
2013 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2014 check_week_ref(ctx, l6, tow, CSSR_REF_CLOCK);
2015 l6->time = gpst2time(ctx->week_ref[CSSR_REF_CLOCK], tow);
2016 nsat = svmask2sat(cssr->svmask, sat);
2017
2018 trace(NULL, 2, "decode_cssr_cc: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, iod);
2019 if (cssr->l6facility != ctx->l6facility[ch]) {
2020 return -1;
2021 }
2022 if (cssr->iod != iod) {
2023 return -1;
2024 }
2025
2026 for (j = 0; j < MAXSAT; j++) {
2027 ssr = &ctx->dec_ssr[j];
2028 ssr->t0[1].sec = 0.0;
2029 ssr->t0[1].time = 0;
2030 ssr->udi[1] = 0;
2031 ssr->iod[1] = 0;
2032 ssr->update_cc = 0;
2033 ssr->update = 0;
2034 ssr->dclk[0] = 0.0;
2035 }
2036
2037 for (j = 0; j < nsat; j++) {
2038 ssr = &ctx->dec_ssr[sat[j] - 1];
2039 ssr->t0[1] = l6->time;
2040 ssr->udi[1] = udint;
2041 ssr->iod[1] = cssr->iod;
2042 ssr->dclk[0] = decode_sval(l6->buff, i, 15, 0.0016);
2043 i += 15;
2044 ssr->dclk[1] = ssr->dclk[2] = 0.0;
2045 ssr->update_cc = 1;
2046 ssr->update = 1;
2047 }
2048
2050 set_cssr_bank_clock(ctx, ch, l6->time, 0);
2051 l6->nbit = i;
2052 return sync ? 0 : 10;
2053}
2054
2055/*============================================================================
2056 * ST4: Code Bias Decoder
2057 *===========================================================================*/
2058
2059static int decode_cssr_cb(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2060 cssr_t* cssr = &ctx->cssr[ch];
2061 int i, j, k, iod, s, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat;
2062 int nsig[CSSR_MAX_SV], sig[CSSR_MAX_SV * CSSR_MAX_SIG];
2063 double udint;
2064 clas_dec_ssr_t* ssr;
2065
2066 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2067 check_week_ref(ctx, l6, tow, CSSR_REF_CBIAS);
2068 l6->time = gpst2time(ctx->week_ref[CSSR_REF_CBIAS], tow);
2069 nsat = svmask2sat(cssr->svmask, sat);
2070 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, sig);
2071
2072 trace(NULL, 2, "decode_cssr_cb: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, iod);
2073 if (cssr->l6facility != ctx->l6facility[ch]) {
2074 return -1;
2075 }
2076 if (cssr->iod != iod) {
2077 return -1;
2078 }
2079
2080 for (k = 0; k < MAXSAT; k++) {
2081 ssr = &ctx->dec_ssr[k];
2082 ssr->t0[4].sec = 0.0;
2083 ssr->t0[4].time = 0;
2084 ssr->udi[4] = 0;
2085 ssr->iod[4] = 0;
2086 ssr->update_cb = 0;
2087 ssr->update = 0;
2088 ssr->nsig = 0;
2089 for (j = 0; j < MAXCODE; j++) {
2090 ssr->cbias[j] = 0.0;
2091 ssr->smode[j] = 0;
2092 }
2093 }
2094
2095 for (k = 0; k < nsat; k++) {
2096 ssr = &ctx->dec_ssr[sat[k] - 1];
2097 ssr->t0[4] = l6->time;
2098 ssr->udi[4] = udint;
2099 ssr->iod[4] = cssr->iod;
2100 ssr->update_cb = 1;
2101 ssr->update = 1;
2102 for (j = 0; j < nsig[k]; j++) {
2103 s = sig[k * CSSR_MAX_SIG + j];
2104 ssr->cbias[s - 1] = decode_sval(l6->buff, i, 11, 0.02);
2105 i += 11;
2106 ssr->smode[j] = s;
2107 }
2108 ssr->nsig = nsig[k];
2109 }
2110
2112 set_cssr_bank_cbias(ctx, ch, l6->time, 0, 0);
2113 l6->nbit = i;
2114 return sync ? 0 : 10;
2115}
2116
2117/*============================================================================
2118 * ST5: Phase Bias Decoder
2119 *===========================================================================*/
2120
2121static int decode_cssr_pb(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2122 cssr_t* cssr = &ctx->cssr[ch];
2123 int i, j, k, iod, s, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat;
2124 int nsig[CSSR_MAX_SV], sig[CSSR_MAX_SV * CSSR_MAX_SIG];
2125 double udint;
2126 clas_dec_ssr_t* ssr;
2127
2128 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2129 check_week_ref(ctx, l6, tow, CSSR_REF_PBIAS);
2130 l6->time = gpst2time(ctx->week_ref[CSSR_REF_PBIAS], tow);
2131 nsat = svmask2sat(cssr->svmask, sat);
2132 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, sig);
2133
2134 trace(NULL, 2, "decode_cssr_pb: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, iod);
2135 if (cssr->l6facility != ctx->l6facility[ch]) {
2136 return -1;
2137 }
2138 if (cssr->iod != iod) {
2139 return -1;
2140 }
2141
2142 for (k = 0; k < MAXSAT; k++) {
2143 ssr = &ctx->dec_ssr[k];
2144 ssr->t0[5].sec = 0.0;
2145 ssr->t0[5].time = 0;
2146 ssr->udi[5] = 0;
2147 ssr->iod[5] = 0;
2148 ssr->update_pb = 0;
2149 ssr->update = 0;
2150 ssr->nsig = 0;
2151 for (j = 0; j < MAXCODE; j++) {
2152 ssr->pbias[j] = 0.0;
2153 ssr->smode[j] = 0;
2154 }
2155 }
2156
2157 for (k = 0; k < nsat; k++) {
2158 ssr = &ctx->dec_ssr[sat[k] - 1];
2159 ssr->t0[5] = l6->time;
2160 ssr->udi[5] = udint;
2161 ssr->iod[5] = cssr->iod;
2162 ssr->update_pb = 1;
2163 ssr->update = 1;
2164 for (j = 0; j < nsig[k]; j++) {
2165 s = sig[k * CSSR_MAX_SIG + j];
2166 ssr->pbias[s - 1] = decode_sval(l6->buff, i, 15, 0.001);
2167 i += 15;
2168 ssr->discnt[s - 1] = getbitu(l6->buff, i, 2);
2169 i += 2;
2170 ssr->smode[j] = s;
2171 }
2172 ssr->nsig = nsig[k];
2173 }
2174
2176 set_cssr_bank_pbias(ctx, ch, l6->time, 0, 0);
2177 l6->nbit = i;
2178 return sync ? 0 : 10;
2179}
2180
2181/*============================================================================
2182 * ST6: Combined Bias Decoder
2183 *===========================================================================*/
2184
2185static int decode_cssr_bias(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2186 cssr_t* cssr = &ctx->cssr[ch];
2187 int i, j, k, iod, s, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat;
2188 int nsig[CSSR_MAX_SV], sig[CSSR_MAX_SV * CSSR_MAX_SIG];
2189 int cbflag, pbflag, netflag, network, netmask;
2190 double udint;
2191 clas_dec_ssr_t* ssr;
2192
2193 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2194 check_week_ref(ctx, l6, tow, CSSR_REF_BIAS);
2195 l6->time = gpst2time(ctx->week_ref[CSSR_REF_BIAS], tow);
2196
2197 cbflag = getbitu(l6->buff, i, 1);
2198 i++;
2199 pbflag = getbitu(l6->buff, i, 1);
2200 i++;
2201 netflag = getbitu(l6->buff, i, 1);
2202 i++;
2203 network = getbitu(l6->buff, i, netflag ? 5 : 0);
2204 i += netflag ? 5 : 0;
2205
2206 nsat = svmask2sat(cssr->svmask, sat);
2207 sigmask2sig(nsat, sat, cssr->sigmask, cssr->cellmask, nsig, sig);
2208 netmask = getbitu(l6->buff, i, netflag ? nsat : 0);
2209 i += netflag ? nsat : 0;
2210
2211 trace(NULL, 2, "decode_cssr_bias: facility=%d tow=%d iod=%d net=%d\n", ctx->l6facility[ch] + 1, tow, iod, network);
2212 if (cssr->l6facility != ctx->l6facility[ch]) {
2213 return -1;
2214 }
2215 if (cssr->iod != iod) {
2216 return -1;
2217 }
2218
2219 for (k = 0; k < MAXSAT; k++) {
2220 ssr = &ctx->dec_ssr[k];
2221 if (cbflag) {
2222 ssr->t0[4].sec = 0.0;
2223 ssr->t0[4].time = 0;
2224 ssr->udi[4] = 0;
2225 ssr->iod[4] = 0;
2226 ssr->update_cb = 0;
2227 ssr->update = 0;
2228 ssr->nsig = 0;
2229 }
2230 if (pbflag) {
2231 ssr->t0[5].sec = 0.0;
2232 ssr->t0[5].time = 0;
2233 ssr->udi[5] = 0;
2234 ssr->iod[5] = 0;
2235 ssr->update_pb = 0;
2236 ssr->update = 0;
2237 ssr->nsig = 0;
2238 }
2239 for (j = 0; j < MAXCODE; j++) {
2240 if (cbflag) {
2241 ssr->smode[j] = 0;
2242 ssr->cbias[j] = 0.0;
2243 }
2244 if (pbflag) {
2245 ssr->smode[j] = 0;
2246 ssr->pbias[j] = 0.0;
2247 ssr->discnt[j] = 0;
2248 }
2249 }
2250 }
2251
2252 for (k = 0; k < nsat; k++) {
2253 if (netflag && !((netmask >> (nsat - 1 - k)) & 1)) {
2254 continue;
2255 }
2256 ssr = &ctx->dec_ssr[sat[k] - 1];
2257 if (cbflag) {
2258 ssr->t0[4] = l6->time;
2259 ssr->udi[4] = udint;
2260 ssr->iod[4] = cssr->iod;
2261 ssr->update_cb = 1;
2262 ssr->update = 1;
2263 }
2264 if (pbflag) {
2265 ssr->t0[5] = l6->time;
2266 ssr->udi[5] = udint;
2267 ssr->iod[5] = cssr->iod;
2268 ssr->update_pb = 1;
2269 ssr->update = 1;
2270 }
2271 for (j = 0; j < nsig[k]; j++) {
2272 s = sig[k * CSSR_MAX_SIG + j];
2273 if (cbflag) {
2274 ssr->cbias[s - 1] = decode_sval(l6->buff, i, 11, 0.02);
2275 i += 11;
2276 }
2277 if (pbflag) {
2278 ssr->pbias[s - 1] = decode_sval(l6->buff, i, 15, 0.001);
2279 i += 15;
2280 ssr->discnt[s - 1] = getbitu(l6->buff, i, 2);
2281 i += 2;
2282 }
2283 ssr->smode[j] = s;
2284 }
2285 ssr->nsig = nsig[k];
2286 }
2287
2288 if (cbflag) {
2290 set_cssr_bank_cbias(ctx, ch, l6->time, netflag ? network : 0, cssr->iod);
2291 }
2292 if (pbflag) {
2294 set_cssr_bank_pbias(ctx, ch, l6->time, netflag ? network : 0, cssr->iod);
2295 }
2296 l6->nbit = i;
2297 return sync ? 0 : 10;
2298}
2299
2300/*============================================================================
2301 * ST7: URA Decoder
2302 *===========================================================================*/
2303
2304static int decode_cssr_ura(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2305 cssr_t* cssr = &ctx->cssr[ch];
2306 int i, j, iod, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat;
2307 double udint;
2308 clas_dec_ssr_t* ssr;
2309
2310 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2311 check_week_ref(ctx, l6, tow, CSSR_REF_URA);
2312 l6->time = gpst2time(ctx->week_ref[CSSR_REF_URA], tow);
2313 nsat = svmask2sat(cssr->svmask, sat);
2314
2315 trace(NULL, 3, "decode_cssr_ura: facility=%d tow=%d iod=%d\n", ctx->l6facility[ch] + 1, tow, iod);
2316 if (cssr->l6facility != ctx->l6facility[ch]) {
2317 return -1;
2318 }
2319 if (cssr->iod != iod) {
2320 return -1;
2321 }
2322
2323 for (j = 0; j < nsat; j++) {
2324 ssr = &ctx->dec_ssr[sat[j] - 1];
2325 ssr->t0[3] = l6->time;
2326 ssr->udi[3] = udint;
2327 ssr->iod[3] = iod;
2328 ssr->ura = getbitu(l6->buff, i, 6);
2329 i += 6;
2330 ssr->update_ura = 1;
2331 ssr->update = 1;
2332 }
2333
2334 l6->nbit = i;
2335 return sync ? 0 : 10;
2336}
2337
2338/*============================================================================
2339 * ST8: STEC Correction Decoder
2340 *===========================================================================*/
2341
2342static int decode_cssr_stec(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2343 cssr_t* cssr = &ctx->cssr[ch];
2344 int i, j, k, iod, s, sync, tow, ngnss, sat[CSSR_MAX_SV], nsat, inet, a, b;
2345 double udint;
2346 ssrn_t* ssrn;
2347
2348 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2349 check_week_ref(ctx, l6, tow, CSSR_REF_STEC);
2350 l6->time = gpst2time(ctx->week_ref[CSSR_REF_STEC], tow);
2351 nsat = svmask2sat(cssr->svmask, sat);
2352 cssr->opt.stec_type = getbitu(l6->buff, i, 2);
2353 i += 2;
2354 inet = getbitu(l6->buff, i, 5);
2355 i += 5;
2356
2357 trace(NULL, 2, "decode_cssr_stec: facility=%d tow=%d iod=%d net=%d type=%d\n", ctx->l6facility[ch] + 1, tow, iod,
2358 inet, cssr->opt.stec_type);
2359 if (cssr->l6facility != ctx->l6facility[ch]) {
2360 return -1;
2361 }
2362 if (cssr->iod != iod) {
2363 return -1;
2364 }
2365 if (inet >= CSSR_MAX_NET) {
2366 return -1;
2367 }
2368
2369 ssrn = &cssr->ssrn[inet];
2370 cssr->net_svmask[inet] = getbitu(l6->buff, i, nsat);
2371 i += nsat;
2372
2373 ssrn->t0[1] = l6->time;
2374 ssrn->udi[1] = udint;
2375 ssrn->iod[1] = iod;
2376
2377 for (j = 0, s = 0; j < nsat; j++) {
2378 if ((cssr->net_svmask[inet] >> (nsat - 1 - j)) & 1) {
2379 ssrn->sat_f[s] = sat[j];
2380 a = getbitu(l6->buff, i, 3);
2381 i += 3;
2382 b = getbitu(l6->buff, i, 3);
2383 i += 3;
2384 ssrn->quality_f[s] = decode_cssr_quality_stec(a, b);
2385 for (k = 0; k < 4; k++) {
2386 ssrn->a[s][k] = 0.0;
2387 }
2388
2389 ssrn->a[s][0] = decode_sval(l6->buff, i, 14, 0.05);
2390 i += 14;
2391 if (cssr->opt.stec_type > 0) {
2392 ssrn->a[s][1] = decode_sval(l6->buff, i, 12, 0.02);
2393 i += 12;
2394 ssrn->a[s][2] = decode_sval(l6->buff, i, 12, 0.02);
2395 i += 12;
2396 }
2397 if (cssr->opt.stec_type > 1) {
2398 ssrn->a[s][3] = decode_sval(l6->buff, i, 10, 0.02);
2399 i += 10;
2400 }
2401 s++;
2402 }
2403 }
2404 ssrn->nsat_f = s;
2405 ssrn->update[1] = 1;
2406
2407 l6->nbit = i;
2408 return sync ? 0 : 10;
2409}
2410
2411/*============================================================================
2412 * ST9: Grid Correction Decoder
2413 *===========================================================================*/
2414
2415static int decode_cssr_grid(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2416 cssr_t* cssr = &ctx->cssr[ch];
2417 int i, j, k, s, ii, sync, iod, tow, ngnss, sat[CSSR_MAX_SV], nsat, sz;
2418 int trop_type, sz_idx, inet, a, b, hs, wet, valid;
2419 double udint, stec0, dlat, dlon, dstec;
2420 ssrn_t* ssrn;
2421
2422 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2423 check_week_ref(ctx, l6, tow, CSSR_REF_GRID);
2424 l6->time = gpst2time(ctx->week_ref[CSSR_REF_GRID], tow);
2425 nsat = svmask2sat(cssr->svmask, sat);
2426
2427 trop_type = getbitu(l6->buff, i, 2);
2428 i += 2;
2429 sz_idx = getbitu(l6->buff, i, 1);
2430 i++;
2431 inet = getbitu(l6->buff, i, 5);
2432 i += 5;
2433
2434 if (inet >= CSSR_MAX_NET) {
2435 return -1;
2436 }
2437 ssrn = &cssr->ssrn[inet];
2438
2439 cssr->net_svmask[inet] = getbitu(l6->buff, i, nsat);
2440 i += nsat;
2441 a = getbitu(l6->buff, i, 3);
2442 i += 3;
2443 b = getbitu(l6->buff, i, 3);
2444 i += 3;
2445 ssrn->ngp = getbitu(l6->buff, i, 6);
2446 i += 6;
2447 ssrn->t0[0] = l6->time;
2448 ssrn->quality = decode_cssr_quality_trop(a, b);
2449
2450 trace(NULL, 2, "decode_cssr_grid: facility=%d tow=%d iod=%d net=%d trop=%d ngp=%d\n", ctx->l6facility[ch] + 1, tow,
2451 iod, inet, trop_type, ssrn->ngp);
2452 if (cssr->l6facility != ctx->l6facility[ch]) {
2453 return -1;
2454 }
2455 if (cssr->iod != iod) {
2456 return -1;
2457 }
2458
2459 for (j = 0; j < CSSR_MAX_GP; j++) {
2460 ssrn->grid[j][0] = ssrn->grid[j][1] = ssrn->grid[j][2] = 0.0;
2461 ssrn->nsat[j] = 0;
2462 }
2463
2464 for (j = 0; j < ssrn->ngp && j < CLAS_MAX_GP; j++) {
2465 ssrn->grid[j][0] = ctx->grid_pos[inet][j][0] * D2R;
2466 ssrn->grid[j][1] = ctx->grid_pos[inet][j][1] * D2R;
2467 ssrn->grid[j][2] = ctx->grid_pos[inet][j][2];
2468 }
2469 sz = sz_idx ? 16 : 7;
2470
2471 for (j = 0; j < ssrn->ngp && j < CLAS_MAX_GP; j++) {
2472 valid = 1;
2473 switch (trop_type) {
2474 case 0:
2475 break;
2476 case 1:
2477 hs = getbits(l6->buff, i, 9);
2478 i += 9;
2479 wet = getbits(l6->buff, i, 8);
2480 i += 8;
2481 if (hs == (-P2_S9_MAX - 1)) {
2482 valid = 0;
2483 }
2484 if (wet == (-P2_S8_MAX - 1)) {
2485 valid = 0;
2486 }
2487 if (valid) {
2488 ssrn->trop_wet[j] = wet * 0.004 + 0.252;
2489 ssrn->trop_total[j] = (hs + wet) * 0.004 + 0.252 + CSSR_TROP_HS_REF;
2490 } else {
2491 ssrn->trop_wet[j] = CSSR_INVALID_VALUE;
2492 ssrn->trop_total[j] = CSSR_INVALID_VALUE;
2493 }
2494 break;
2495 }
2496
2497 dlat = (ssrn->grid[j][0] - ssrn->grid[0][0]) * R2D;
2498 dlon = (ssrn->grid[j][1] - ssrn->grid[0][1]) * R2D;
2499
2500 for (k = 0, s = 0, ii = 0; k < nsat; k++) {
2501 if ((cssr->net_svmask[inet] >> (nsat - 1 - k)) & 1) {
2502 dstec = decode_sval(l6->buff, i, sz, 0.04);
2503 i += sz;
2504 stec0 = ssrn->a[ii][0] + ssrn->a[ii][1] * dlat + ssrn->a[ii][2] * dlon + ssrn->a[ii][3] * dlat * dlon;
2505 if (dstec == CSSR_INVALID_VALUE) {
2506 ssrn->stec[j][s] = CSSR_INVALID_VALUE;
2507 } else {
2508 ssrn->stec[j][s] = dstec + stec0;
2509 }
2510 ssrn->stec0[j][s] = stec0;
2511 ssrn->sat[j][s] = sat[k];
2512 ii++;
2513 s++;
2514 }
2515 }
2516 ssrn->nsat[j] = s;
2517 }
2518
2519 ssrn->update[0] = 1;
2520 ctx->updateac = 1;
2521
2523 set_cssr_latest_trop(ctx->bank[ch], ssrn->t0[0], ssrn, inet);
2524 set_cssr_bank_trop(ctx, ch, ssrn->t0[0], ssrn, inet);
2525
2526 l6->nbit = i;
2527 return sync ? 0 : 10;
2528}
2529
2530/*============================================================================
2531 * ST10: Service Information Decoder
2532 *===========================================================================*/
2533
2534static int decode_cssr_si(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2535 cssr_t* cssr = &ctx->cssr[ch];
2536 int i, j, sync;
2537
2538 i = i0;
2539 sync = getbitu(l6->buff, i, 1);
2540 i++;
2541 cssr->si_cnt = getbitu(l6->buff, i, 3);
2542 i += 3;
2543 cssr->si_sz = getbitu(l6->buff, i, 2);
2544 i += 2;
2545
2546 for (j = 0; j < cssr->si_sz; j++) {
2547 cssr->si_data[j] = (uint64_t)getbitu(l6->buff, i, 8) << 32;
2548 i += 8;
2549 cssr->si_data[j] |= getbitu(l6->buff, i, 32);
2550 i += 32;
2551 }
2552 l6->nbit = i;
2553 return sync ? 0 : 10;
2554}
2555
2556/*============================================================================
2557 * ST11: Combined Orbit/Clock Decoder
2558 *===========================================================================*/
2559
2560static int decode_cssr_combo(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2561 cssr_t* cssr = &ctx->cssr[ch];
2562 int i, j, k, sync, iod, tow, ngnss, sat[CSSR_MAX_SV], nsat, iode;
2563 int flg_orbit, flg_clock, flg_net, net_svmask, netid;
2564 double udint;
2565 clas_dec_ssr_t* ssr;
2566
2567 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2568 check_week_ref(ctx, l6, tow, CSSR_REF_COMBINED);
2569 l6->time = gpst2time(ctx->week_ref[CSSR_REF_COMBINED], tow);
2570 nsat = svmask2sat(cssr->svmask, sat);
2571
2572 flg_orbit = getbitu(l6->buff, i, 1);
2573 i++;
2574 flg_clock = getbitu(l6->buff, i, 1);
2575 i++;
2576 flg_net = getbitu(l6->buff, i, 1);
2577 i++;
2578 netid = getbitu(l6->buff, i, flg_net ? 5 : 0);
2579 i += flg_net ? 5 : 0;
2580 net_svmask = getbitu(l6->buff, i, flg_net ? nsat : 0);
2581 i += flg_net ? nsat : 0;
2582
2583 trace(NULL, 2, "decode_cssr_combo: facility=%d tow=%d iod=%d net=%d flag=%d %d %d\n", ctx->l6facility[ch] + 1, tow,
2584 iod, netid, flg_orbit, flg_clock, flg_net);
2585 if (cssr->l6facility != ctx->l6facility[ch]) {
2586 return -1;
2587 }
2588 if (cssr->iod != iod) {
2589 return -1;
2590 }
2591
2592 for (j = 0; j < MAXSAT; j++) {
2593 ssr = &ctx->dec_ssr[j];
2594 if (flg_orbit) {
2595 ssr->t0[0].sec = 0.0;
2596 ssr->t0[0].time = 0;
2597 ssr->udi[0] = 0;
2598 ssr->iod[0] = 0;
2599 ssr->update_oc = 0;
2600 ssr->update = 0;
2601 ssr->iode = 0;
2602 ssr->deph[0] = ssr->deph[1] = ssr->deph[2] = 0.0;
2603 }
2604 if (flg_clock) {
2605 ssr->t0[1].sec = 0.0;
2606 ssr->t0[1].time = 0;
2607 ssr->udi[1] = 0;
2608 ssr->iod[1] = 0;
2609 ssr->update_cc = 0;
2610 ssr->update = 0;
2611 ssr->dclk[0] = 0.0;
2612 }
2613 }
2614
2615 for (j = 0; j < nsat; j++) {
2616 if (flg_net && !((net_svmask >> (nsat - 1 - j)) & 1)) {
2617 continue;
2618 }
2619 ssr = &ctx->dec_ssr[sat[j] - 1];
2620
2621 if (flg_orbit) {
2622 iode = (satsys(sat[j], NULL) == SYS_GAL) ? getbitu(l6->buff, i, 10) : getbitu(l6->buff, i, 8);
2623 i += (satsys(sat[j], NULL) == SYS_GAL) ? 10 : 8;
2624
2625 ssr->deph[0] = decode_sval(l6->buff, i, 15, 0.0016);
2626 i += 15;
2627 ssr->deph[1] = decode_sval(l6->buff, i, 13, 0.0064);
2628 i += 13;
2629 ssr->deph[2] = decode_sval(l6->buff, i, 13, 0.0064);
2630 i += 13;
2631 ssr->iode = iode;
2632 ssr->t0[0] = l6->time;
2633 ssr->udi[0] = udint;
2634 ssr->iod[0] = cssr->iod;
2635 for (k = 0; k < 3; k++) {
2636 ssr->ddeph[k] = 0.0;
2637 }
2638 ssr->update_oc = 1;
2639 ssr->update = 1;
2640 if (ssr->deph[0] == CSSR_INVALID_VALUE || ssr->deph[1] == CSSR_INVALID_VALUE ||
2641 ssr->deph[2] == CSSR_INVALID_VALUE) {
2642 ssr->deph[0] = ssr->deph[1] = ssr->deph[2] = CSSR_INVALID_VALUE;
2643 }
2644 }
2645 if (flg_clock) {
2646 ssr->dclk[0] = decode_sval(l6->buff, i, 15, 0.0016);
2647 i += 15;
2648 ssr->dclk[1] = ssr->dclk[2] = 0.0;
2649 ssr->t0[1] = l6->time;
2650 ssr->udi[1] = udint;
2651 ssr->iod[1] = cssr->iod;
2652 ssr->update_cc = 1;
2653 ssr->update = 1;
2654 }
2655 }
2656
2658 if (flg_net) {
2659 ctx->bank[ch]->separation |= (1 << (netid - 1));
2660 }
2661 if (flg_orbit) {
2662 set_cssr_bank_orbit(ctx, ch, l6->time, flg_net ? netid : 0);
2663 }
2664 if (flg_clock) {
2665 set_cssr_bank_clock(ctx, ch, l6->time, flg_net ? netid : 0);
2666 }
2667
2668 l6->nbit = i;
2669 return sync ? 0 : 10;
2670}
2671
2672/*============================================================================
2673 * ST12: Atmospheric Correction Decoder
2674 *===========================================================================*/
2675
2676static int decode_cssr_atmos(clas_ctx_t* ctx, int ch, clas_l6buf_t* l6, int i0) {
2677 cssr_t* cssr = &ctx->cssr[ch];
2678 int i, j, k, s, sync, tow, iod, ngnss, sat[CSSR_MAX_SV], nsat;
2679 int trop_avail, stec_avail, trop_type = -1, stec_type = -1;
2680 int inet, a = 0, b = 0, sz_idx, sz;
2681 double udint, stec0, ct[6] = {0}, ci[6] = {0};
2682 double trop_ofst = 0.0, trop_residual, dlat, dlon, dstec;
2683 ssrn_t* ssrn;
2684 static const double dstec_lsb_t[4] = {0.04, 0.12, 0.16, 0.24};
2685 static const int dstec_sz_t[4] = {4, 4, 5, 7};
2686
2687 i = decode_cssr_head(ctx, l6, cssr, &sync, &tow, &iod, &udint, &ngnss, i0);
2688 check_week_ref(ctx, l6, tow, CSSR_REF_ATMOSPHERIC);
2689 l6->time = gpst2time(ctx->week_ref[CSSR_REF_ATMOSPHERIC], tow);
2690 nsat = svmask2sat(cssr->svmask, sat);
2691
2692 trop_avail = getbitu(l6->buff, i, 2);
2693 i += 2;
2694 stec_avail = getbitu(l6->buff, i, 2);
2695 i += 2;
2696 inet = getbitu(l6->buff, i, 5);
2697 i += 5;
2698
2699 trace(NULL, 2, "decode_cssr_atmos: facility=%d tow=%d iod=%d net=%d\n", ctx->l6facility[ch] + 1, tow, iod, inet);
2700 if (cssr->l6facility != ctx->l6facility[ch]) {
2701 return -1;
2702 }
2703 if (cssr->iod != iod) {
2704 return -1;
2705 }
2706 if (inet >= CSSR_MAX_NET) {
2707 return -1;
2708 }
2709
2710 ssrn = &cssr->ssrn[inet];
2711 ssrn->ngp = getbitu(l6->buff, i, 6);
2712 i += 6;
2713 ssrn->t0[0] = l6->time;
2714
2715 for (j = 0; j < CSSR_MAX_GP; j++) {
2716 ssrn->trop_total[j] = CSSR_INVALID_VALUE;
2717 ssrn->trop_wet[j] = CSSR_INVALID_VALUE;
2718 ssrn->grid[j][0] = ssrn->grid[j][1] = ssrn->grid[j][2] = 0.0;
2719 ssrn->nsat[j] = 0;
2720 }
2721
2722 if (trop_avail != 0) {
2723 a = getbitu(l6->buff, i, 3);
2724 i += 3;
2725 b = getbitu(l6->buff, i, 3);
2726 i += 3;
2727 ssrn->quality = decode_cssr_quality_trop(a, b);
2728 }
2729
2730 if ((trop_avail & 0x01) == 0x01) {
2731 trop_type = getbitu(l6->buff, i, 2);
2732 i += 2;
2733 for (k = 0; k < 4; k++) {
2734 ct[k] = 0.0;
2735 }
2736 ct[0] = decode_sval(l6->buff, i, 9, 0.004);
2737 i += 9;
2738 if (trop_type > 0) {
2739 ct[1] = decode_sval(l6->buff, i, 7, 0.002);
2740 i += 7;
2741 ct[2] = decode_sval(l6->buff, i, 7, 0.002);
2742 i += 7;
2743 }
2744 if (trop_type > 1) {
2745 ct[3] = decode_sval(l6->buff, i, 7, 0.001);
2746 i += 7;
2747 }
2748 }
2749
2750 for (j = 0; j < ssrn->ngp && j < CLAS_MAX_GP; j++) {
2751 ssrn->grid[j][0] = ctx->grid_pos[inet][j][0] * D2R;
2752 ssrn->grid[j][1] = ctx->grid_pos[inet][j][1] * D2R;
2753 ssrn->grid[j][2] = ctx->grid_pos[inet][j][2];
2754
2755 dlat = (ssrn->grid[j][0] - ssrn->grid[0][0]) * R2D;
2756 dlon = (ssrn->grid[j][1] - ssrn->grid[0][1]) * R2D;
2757
2758 ssrn->trop_total[j] = CSSR_TROP_HS_REF + ct[0];
2759 if (trop_type > 0) {
2760 ssrn->trop_total[j] += ct[1] * dlat + ct[2] * dlon;
2761 }
2762 if (trop_type > 1) {
2763 ssrn->trop_total[j] += ct[3] * dlat * dlon;
2764 }
2765 }
2766
2767 if ((trop_avail & 0x02) == 0x02) {
2768 int sz_idx_t = getbitu(l6->buff, i, 1);
2769 i++;
2770 trop_ofst = getbitu(l6->buff, i, 4) * 0.02;
2771 i += 4;
2772 sz = (sz_idx_t == 0) ? 6 : 8;
2773
2774 for (j = 0; j < ssrn->ngp && j < CLAS_MAX_GP; j++) {
2775 trop_residual = decode_sval(l6->buff, i, sz, 0.004);
2776 i += sz;
2777 if (trop_residual != CSSR_INVALID_VALUE) {
2778 ssrn->trop_wet[j] = trop_residual + trop_ofst;
2779 ssrn->trop_total[j] += ssrn->trop_wet[j];
2780 } else {
2781 ssrn->trop_total[j] = CSSR_INVALID_VALUE;
2782 }
2783 }
2784 }
2785
2786 if (stec_avail != 0) {
2787 cssr->net_svmask[inet] = getbitu(l6->buff, i, nsat);
2788 i += nsat;
2789 }
2790 for (j = s = 0; j < nsat; j++) {
2791 if (stec_avail != 0) {
2792 if (!((cssr->net_svmask[inet] >> (nsat - 1 - j)) & 1)) {
2793 continue;
2794 }
2795 a = getbitu(l6->buff, i, 3);
2796 i += 3;
2797 b = getbitu(l6->buff, i, 3);
2798 i += 3;
2799 (void)decode_cssr_quality_stec(a, b);
2800 }
2801 for (k = 0; k < 6; k++) {
2802 ci[k] = 0.0;
2803 }
2804 if ((stec_avail & 0x01) == 0x01) {
2805 stec_type = getbitu(l6->buff, i, 2);
2806 i += 2;
2807 ci[0] = decode_sval(l6->buff, i, 14, 0.05);
2808 i += 14;
2809 if (stec_type > 0) {
2810 ci[1] = decode_sval(l6->buff, i, 12, 0.02);
2811 i += 12;
2812 ci[2] = decode_sval(l6->buff, i, 12, 0.02);
2813 i += 12;
2814 }
2815 if (stec_type > 1) {
2816 ci[3] = decode_sval(l6->buff, i, 10, 0.02);
2817 i += 10;
2818 }
2819 if (stec_type > 2) {
2820 ci[4] = decode_sval(l6->buff, i, 8, 0.005);
2821 i += 8;
2822 ci[5] = decode_sval(l6->buff, i, 8, 0.005);
2823 i += 8;
2824 }
2825 }
2826 if (stec_avail != 0) {
2827 if ((stec_avail & 0x02) == 0x02) {
2828 sz_idx = getbitu(l6->buff, i, 2);
2829 i += 2;
2830 } else {
2831 sz_idx = 0;
2832 }
2833
2834 for (k = 0; k < ssrn->ngp && k < CLAS_MAX_GP; k++) {
2835 dlat = (ssrn->grid[k][0] - ssrn->grid[0][0]) * R2D;
2836 dlon = (ssrn->grid[k][1] - ssrn->grid[0][1]) * R2D;
2837
2838 dstec = 0.0;
2839 if ((stec_avail & 0x02) == 0x02) {
2840 dstec = decode_sval(l6->buff, i, dstec_sz_t[sz_idx], dstec_lsb_t[sz_idx]);
2841 i += dstec_sz_t[sz_idx];
2842 }
2843
2844 stec0 = ci[0];
2845 if (stec_type > 0) {
2846 stec0 += ci[1] * dlat + ci[2] * dlon;
2847 }
2848 if (stec_type > 1) {
2849 stec0 += ci[3] * dlat * dlon;
2850 }
2851 if (stec_type > 2) {
2852 stec0 += ci[4] * dlat * dlat + ci[5] * dlon * dlon;
2853 }
2854
2855 if (dstec == CSSR_INVALID_VALUE) {
2856 ssrn->stec[k][s] = CSSR_INVALID_VALUE;
2857 } else {
2858 ssrn->stec[k][s] = stec0 + dstec;
2859 }
2860 ssrn->stec0[k][s] = stec0;
2861 ssrn->sat[k][s] = sat[j];
2862 ssrn->nsat[k]++;
2863 }
2864 s++;
2865 }
2866 }
2867
2868 ssrn->update[0] = 1;
2869 ctx->updateac = 1;
2870
2872 set_cssr_latest_trop(ctx->bank[ch], ssrn->t0[0], ssrn, inet);
2873 set_cssr_bank_trop(ctx, ch, ssrn->t0[0], ssrn, inet);
2874
2875 l6->nbit = i;
2876 return sync ? 0 : 10;
2877}
2878
2879/*============================================================================
2880 * Dispatch — decode QZS L6 CLAS stream
2881 *===========================================================================*/
2882
2883static int decode_qzs_msg(clas_ctx_t* ctx, int ch) {
2884 cssr_t* cssr = &ctx->cssr[ch];
2885 clas_l6buf_t* l6 = &ctx->l6buf[ch];
2886 int startbit, i, ret = 0;
2887
2888 if (l6->frame == 0x00 || l6->nbit == -1) {
2889 return 0;
2890 }
2891
2892 i = startbit = l6->nbit;
2893 if ((i + 16) > l6->havebit) {
2894 return 0;
2895 }
2896
2897 l6->ctype = getbitu(l6->buff, i, 12);
2898 i += 12;
2899 if (l6->ctype != 4073) {
2900 l6->nbit = -1;
2901 l6->frame = 0;
2902 return 0;
2903 }
2904 l6->subtype = getbitu(l6->buff, i, 4);
2905 i += 4;
2906
2907 if (l6->subtype != CSSR_TYPE_MASK && !l6->decode_start) {
2908 return 0;
2909 }
2910
2911 /* bit width check */
2912 switch (l6->subtype) {
2913 case CSSR_TYPE_MASK:
2914 if (!check_bit_width_mask(l6, cssr, i)) {
2915 return 0;
2916 }
2917 break;
2918 case CSSR_TYPE_OC:
2919 if (!check_bit_width_oc(l6, cssr, i)) {
2920 return 0;
2921 }
2922 break;
2923 case CSSR_TYPE_CC:
2924 if (!check_bit_width_cc(l6, cssr, i)) {
2925 return 0;
2926 }
2927 break;
2928 case CSSR_TYPE_CB:
2929 if (!check_bit_width_cb(l6, cssr, i)) {
2930 return 0;
2931 }
2932 break;
2933 case CSSR_TYPE_PB:
2934 if (!check_bit_width_pb(l6, cssr, i)) {
2935 return 0;
2936 }
2937 break;
2938 case CSSR_TYPE_BIAS:
2939 if (!check_bit_width_bias(l6, cssr, i)) {
2940 return 0;
2941 }
2942 break;
2943 case CSSR_TYPE_URA:
2944 if (!check_bit_width_ura(l6, cssr, i)) {
2945 return 0;
2946 }
2947 break;
2948 case CSSR_TYPE_STEC:
2949 if (!check_bit_width_stec(l6, cssr, i)) {
2950 return 0;
2951 }
2952 break;
2953 case CSSR_TYPE_GRID:
2954 if (!check_bit_width_grid(l6, cssr, i)) {
2955 return 0;
2956 }
2957 break;
2958 case CSSR_TYPE_COMBO:
2959 if (!check_bit_width_combo(l6, cssr, i)) {
2960 return 0;
2961 }
2962 break;
2963 case CSSR_TYPE_ATMOS:
2964 if (!check_bit_width_atmos(l6, cssr, i)) {
2965 return 0;
2966 }
2967 break;
2968 case CSSR_TYPE_SI:
2969 if (!check_bit_width_si(l6, cssr, i)) {
2970 return 0;
2971 }
2972 break;
2973 case 0:
2974 return 0;
2975 }
2976
2977 /* decode */
2978 switch (l6->subtype) {
2979 case CSSR_TYPE_MASK:
2980 ret = decode_cssr_mask(ctx, ch, l6, i);
2981 if (!l6->decode_start) {
2982 trace(NULL, 1, "start CSSR decoding: ch=%d\n", ch);
2983 l6->decode_start = 1;
2984 }
2985 break;
2986 case CSSR_TYPE_OC:
2987 ret = decode_cssr_oc(ctx, ch, l6, i);
2988 break;
2989 case CSSR_TYPE_CC:
2990 ret = decode_cssr_cc(ctx, ch, l6, i);
2991 break;
2992 case CSSR_TYPE_CB:
2993 ret = decode_cssr_cb(ctx, ch, l6, i);
2994 break;
2995 case CSSR_TYPE_PB:
2996 ret = decode_cssr_pb(ctx, ch, l6, i);
2997 break;
2998 case CSSR_TYPE_BIAS:
2999 ret = decode_cssr_bias(ctx, ch, l6, i);
3000 break;
3001 case CSSR_TYPE_URA:
3002 ret = decode_cssr_ura(ctx, ch, l6, i);
3003 break;
3004 case CSSR_TYPE_STEC:
3005 ret = decode_cssr_stec(ctx, ch, l6, i);
3006 break;
3007 case CSSR_TYPE_GRID:
3008 ret = decode_cssr_grid(ctx, ch, l6, i);
3009 break;
3010 case CSSR_TYPE_COMBO:
3011 ret = decode_cssr_combo(ctx, ch, l6, i);
3012 break;
3013 case CSSR_TYPE_ATMOS:
3014 ret = decode_cssr_atmos(ctx, ch, l6, i);
3015 break;
3016 case CSSR_TYPE_SI:
3017 ret = decode_cssr_si(ctx, ch, l6, i);
3018 break;
3019 default:
3020 break;
3021 }
3022
3023 /* on facility/IOD mismatch, invalidate frame */
3024 if (ret == -1) {
3025 l6->nbit = -1;
3026 cssr->iod = -1;
3027 l6->frame = 0;
3028 return 0;
3029 }
3030 return ret;
3031}
3032
3033/*============================================================================
3034 * I/O Functions — byte-by-byte and file-based input
3035 *===========================================================================*/
3036
3037extern int clas_get_correct_fac(int msgid) {
3038 int fac = (msgid & 0x18) >> 3;
3039 int ptn = (msgid & 0x06) >> 1;
3040
3041 if (ptn == 0) {
3042 switch (fac) {
3043 case 0:
3044 return 0;
3045 case 1:
3046 return 2;
3047 case 2:
3048 return 1;
3049 case 3:
3050 return 3;
3051 default:
3052 return -1;
3053 }
3054 } else {
3055 switch (fac) {
3056 case 0:
3057 return 2;
3058 case 1:
3059 return 0;
3060 case 2:
3061 return 3;
3062 case 3:
3063 return 1;
3064 default:
3065 return -1;
3066 }
3067 }
3068}
3069
3070extern int clas_route_l6frame(clas_ctx_t* ctx, int prn, int ptn, int l6mrg, gtime_t now) {
3071 int ch, target, locked_prn, stale;
3072 int nch = l6mrg ? CLAS_CH_NUM : 1;
3073
3074 /* 1. already the active source on some channel → accept and refresh */
3075 for (ch = 0; ch < nch; ch++) {
3076 if (ctx->l6delivery[ch] == prn) {
3077 ctx->l6lock_time[ch] = now;
3078 return ch;
3079 }
3080 }
3081
3082 /* 2. pick the target channel for this frame.
3083 * single channel: ch 0 carries whatever pattern is active.
3084 * dual channel: one channel per pattern (lock pattern→channel). */
3085 if (!l6mrg) {
3086 target = 0;
3087 } else {
3088 target = -1;
3089 for (ch = 0; ch < nch; ch++) {
3090 if (ctx->l6pattern[ch] == ptn) {
3091 target = ch;
3092 break;
3093 }
3094 }
3095 if (target < 0) { /* assign this pattern to a free channel */
3096 for (ch = 0; ch < nch; ch++) {
3097 if (ctx->l6pattern[ch] < 0) {
3098 target = ch;
3099 break;
3100 }
3101 }
3102 }
3103 if (target < 0) {
3104 return -1; /* >CLAS_CH_NUM patterns (not expected): drop */
3105 }
3106 }
3107
3108 /* 3. (re)lock the target channel to this PRN only if it is free or its
3109 * current active source has gone silent past the handover timeout;
3110 * otherwise drop to keep the channel's subframe stream coherent. */
3111 locked_prn = ctx->l6delivery[target];
3112 stale = (ctx->l6lock_time[target].time == 0) || (timediff(now, ctx->l6lock_time[target]) > CLAS_L6_RELOCK_TIMEOUT);
3113
3114 if (locked_prn < 0 || stale) {
3115 if (locked_prn != prn || ctx->l6pattern[target] != ptn) {
3116 trace(NULL, 2, "L6 lock: ch=%d prn=%d pattern=%d (was prn=%d pattern=%d)\n", target, prn, ptn, locked_prn,
3117 ctx->l6pattern[target]);
3118 }
3119 ctx->l6delivery[target] = prn;
3120 ctx->l6pattern[target] = ptn;
3121 ctx->l6lock_time[target] = now;
3122 return target;
3123 }
3124 return -1; /* channel busy with a different, still-active PRN → drop */
3125}
3126
3127extern int clas_input_cssr(clas_ctx_t* ctx, uint8_t data, int ch) {
3128 clas_l6buf_t* l6 = &ctx->l6buf[ch];
3129 l6_header_t hdr;
3130
3131 trace(NULL, 5, "clas_input_cssr: data=%02x ch=%d\n", data, ch);
3132
3133 /* synchronize L6 frame preamble */
3134 if (l6->nbyte == 0) {
3135 l6->preamble = (l6->preamble << 8) | data;
3136 if (l6->preamble != L6FRMPREAMB) {
3137 return 0;
3138 }
3139 l6->preamble = 0;
3140 l6->fbuff[l6->nbyte++] = (L6FRMPREAMB >> 24) & 0xff;
3141 l6->fbuff[l6->nbyte++] = (L6FRMPREAMB >> 16) & 0xff;
3142 l6->fbuff[l6->nbyte++] = (L6FRMPREAMB >> 8) & 0xff;
3143 l6->fbuff[l6->nbyte++] = data;
3144 return 0;
3145 }
3146 l6->fbuff[l6->nbyte++] = data;
3147 l6->len = BLEN_MSG;
3148
3149 if (l6->nbyte < l6->len) {
3150 return 0;
3151 }
3152 l6->nbyte = 0;
3153
3154 l6_parse_header(l6->fbuff, &hdr);
3155 if (hdr.svc != L6_SVC_CLAS) {
3156 /* null message (IS-QZSS-L6-008 5.4.4: CLAS must not be used) or a
3157 * MADOCA-PPP / QZNMA / reserved-vendor frame on a CLAS input: skip it
3158 * without touching the subframe assembly or the delivery state, as
3159 * upstream claslib 0.9.0 does for null messages. Corrections already
3160 * decoded are not cleared. */
3161 trace(NULL, hdr.svc == L6_SVC_NULL ? 2 : 3, "clas_input_cssr: skip %s frame prn=%d ch=%d\n",
3162 l6_service_name(hdr.svc), hdr.prn, ch);
3163 return 0;
3164 }
3165 if (hdr.alert != 0) {
3166 trace(NULL, 1, "CSSR frame alert!: ch=%d\n", ch);
3167 }
3168
3169 ctx->l6delivery[ch] = hdr.prn;
3170 ctx->l6facility[ch] = clas_get_correct_fac(hdr.msgid);
3171
3172 /* a frame lost inside the subframe (RS failure or input loss) would shift
3173 * the bits of the following frames: discard the rest of the subframe, as
3174 * when its first frame is lost (#363) */
3175 if (l6->ftag > 0.0 && l6->ftag_last > 0.0 && !hdr.sf_ind && l6->decode_start && l6->nframe < 5) {
3176 double dt = l6->ftag - l6->ftag_last;
3177 if (dt <= 0.0 || dt > 1.5) {
3178 trace(NULL, 2, "clas_input_cssr: frame gap dt=%.3f s, subframe discarded ch=%d nframe=%d\n", dt, ch,
3179 l6->nframe);
3180 l6->nbit = -1;
3181 l6->frame = 0;
3182 l6->nframe = 5;
3183 }
3184 }
3185 l6->ftag_last = l6->ftag;
3186 l6->ftag = 0.0;
3187
3188 if (hdr.sf_ind) { /* subframe indicator */
3189 l6->havebit = 0;
3190 l6->decode_start = 1;
3191 l6->nbit = 0;
3192 l6->frame = 0;
3193 l6->nframe = 0;
3194 } else if (l6->nframe >= 5) {
3195 return 0;
3196 }
3197
3198 if (l6->decode_start) {
3199 int ii = 1695 * l6->nframe, jj;
3200 setbitu(l6->buff, ii, 7, l6->fbuff[6] & 0x7f);
3201 ii += 7;
3202 for (jj = 0; jj < 211; jj++) {
3203 setbitu(l6->buff, ii, 8, l6->fbuff[7 + jj]);
3204 ii += 8;
3205 }
3206 l6->havebit += 1695;
3207 l6->frame |= (1 << l6->nframe);
3208 l6->nframe++;
3209 }
3210 return 0;
3211}
3212
3213extern int clas_decode_msg(clas_ctx_t* ctx, int ch) { return decode_qzs_msg(ctx, ch); }
3214
3215extern void clas_set_l6tag(clas_ctx_t* ctx, int ch, double tag) { ctx->l6buf[ch].ftag = tag; }
3216
3217extern int clas_input_cssrf(clas_ctx_t* ctx, FILE* fp, int ch) {
3218 int i, data, ret;
3219
3220 for (i = 0; i < 4096; i++) {
3221 if ((ret = decode_qzs_msg(ctx, ch))) {
3222 return ret;
3223 }
3224 if ((data = fgetc(fp)) == EOF) {
3225 return -2;
3226 }
3227 if ((ret = clas_input_cssr(ctx, (uint8_t)data, ch))) {
3228 return ret;
3229 }
3230 }
3231 return 0;
3232}
3233
3234/*============================================================================
3235 * Grid Definition File Reader
3236 *===========================================================================*/
3237
3238extern int clas_read_grid_def(clas_ctx_t* ctx, const char* gridfile) {
3239 int no, lath, latm, lonh, lonm;
3240 double lat, lon, alt;
3241 char buff[1024], *temp, *p;
3242 int inet, grid[CLAS_MAX_NETWORK], isqzss = 0, ret;
3243 FILE* fp;
3244
3245 memset(grid, 0, sizeof(grid));
3246
3247 for (inet = 0; inet < CLAS_MAX_NETWORK; inet++) {
3248 ctx->grid_pos[inet][0][0] = -1.0;
3249 ctx->grid_pos[inet][0][1] = -1.0;
3250 ctx->grid_pos[inet][0][2] = -1.0;
3251 }
3252
3253 trace(NULL, 2, "clas_read_grid_def: gridfile=%s\n", gridfile);
3254 fp = fopen(gridfile, "r");
3255 if (!fp) {
3256 return -1;
3257 }
3258
3259 while (fgets(buff, sizeof(buff), fp)) {
3260 if (strstr(buff, "<CSSR Grid Definition>")) {
3261 while (fgets(buff, sizeof(buff), fp)) {
3262 if ((temp = strstr(buff, "<Version>"))) {
3263 p = temp + 9;
3264 if ((temp = strstr(buff, "</Version>"))) {
3265 *temp = '\0';
3266 }
3267 ctx->gridsel = atoi(p);
3268 break;
3269 }
3270 }
3271 break;
3272 } else if (strstr(buff, "Compact Network ID GRID No.")) {
3273 ctx->gridsel = 3;
3274 isqzss = 1;
3275 break;
3276 } else {
3277 fclose(fp);
3278 return -1;
3279 }
3280 }
3281 fclose(fp);
3282
3283 fp = fopen(gridfile, "r");
3284 if (!fp) {
3285 return -1;
3286 }
3287
3288 if (isqzss == 0) {
3289 while (fgets(buff, sizeof(buff), fp)) {
3290 if (sscanf(buff, "<Network%d>", &inet)) {
3291 while (fscanf(fp, "%d\t%d\t%d\t%lf\t%d\t%d\t%lf\t%lf", &no, &lath, &latm, &lat, &lonh, &lonm, &lon,
3292 &alt) > 0) {
3293 if (inet >= 0 && inet < CLAS_MAX_NETWORK && grid[inet] < CLAS_MAX_GP) {
3294 ctx->grid_pos[inet][grid[inet]][0] = (double)lath + (double)latm / 60.0 + lat / 3600.0;
3295 ctx->grid_pos[inet][grid[inet]][1] = (double)lonh + (double)lonm / 60.0 + lon / 3600.0;
3296 ctx->grid_pos[inet][grid[inet]][2] = alt;
3297 grid[inet]++;
3298 if (grid[inet] < CLAS_MAX_GP) {
3299 ctx->grid_pos[inet][grid[inet]][0] = -1.0;
3300 ctx->grid_pos[inet][grid[inet]][1] = -1.0;
3301 ctx->grid_pos[inet][grid[inet]][2] = -1.0;
3302 }
3303 }
3304 }
3305 }
3306 }
3307 } else {
3308 if (!fgets(buff, sizeof(buff), fp)) {
3309 fclose(fp);
3310 return -1;
3311 }
3312 while ((ret = fscanf(fp, "%d %d %lf %lf %lf", &inet, &no, &lat, &lon, &alt)) != EOF) {
3313 if (inet >= 0 && inet < CLAS_MAX_NETWORK && ret == 5 && grid[inet] < CLAS_MAX_GP) {
3314 ctx->grid_pos[inet][grid[inet]][0] = lat;
3315 ctx->grid_pos[inet][grid[inet]][1] = lon;
3316 ctx->grid_pos[inet][grid[inet]][2] = alt;
3317 grid[inet]++;
3318 if (grid[inet] < CLAS_MAX_GP) {
3319 ctx->grid_pos[inet][grid[inet]][0] = -1.0;
3320 ctx->grid_pos[inet][grid[inet]][1] = -1.0;
3321 ctx->grid_pos[inet][grid[inet]][2] = -1.0;
3322 }
3323 }
3324 }
3325 }
3326 fclose(fp);
3327 return 0;
3328}
void setbitu(uint8_t *buff, int pos, int len, uint32_t data)
Set unsigned bits in byte data.
Definition mrtk_bits.c:122
uint32_t getbitu(const uint8_t *buff, int pos, int len)
Extract unsigned bits from byte data.
Definition mrtk_bits.c:99
int32_t getbits(const uint8_t *buff, int pos, int len)
Extract signed bits from byte data.
Definition mrtk_bits.c:107
static void set_cssr_bank_pbias(clas_ctx_t *ctx, int ch, gtime_t time, int network, int iod)
Definition mrtk_clas.c:773
static void set_grid_data(clas_corr_t *corr, double *pos, int index)
Definition mrtk_clas.c:1002
static int decode_cssr_si(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2534
static void init_grid_index(clas_corr_t *corr)
Definition mrtk_clas.c:992
static int decode_cssr_mask(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:1875
static clas_trop_bank_t * get_same_trop(clas_bank_ctrl_t *bank, gtime_t time)
Definition mrtk_clas.c:420
static int sigmask2sig(int nsat, int *sat, uint16_t *sigmask, uint16_t *cellmask, int *nsig, int *sig)
Definition mrtk_clas.c:316
static int add_data_trop(zwd_t *z, gtime_t time, double zwd, double ztd, double quality, double rms, int valid)
Definition mrtk_clas.c:1029
static int decode_cssr_cc(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2007
static int svmask2sat(uint64_t *svmask, int *sat)
Definition mrtk_clas.c:241
static int decode_cssr_atmos(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2676
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
static int decode_cssr_cb(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2059
static int svmask2nsatlist(uint64_t svmask, int id, int *sat)
Definition mrtk_clas.c:230
static int check_bit_width_stec(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1677
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
static clas_clock_bank_t * get_same_clock(clas_bank_ctrl_t *bank, gtime_t time, int network)
Definition mrtk_clas.c:398
static int decode_cssr_head(clas_ctx_t *ctx, clas_l6buf_t *l6, cssr_t *cssr, int *sync, int *tow, int *iod, double *udint, int *ngnss, int i0)
Definition mrtk_clas.c:1506
static int check_bit_width_pb(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1619
static clas_bias_bank_t * get_close_net_pbias(clas_bank_ctrl_t *bank, gtime_t time, int network, double age)
Definition mrtk_clas.c:561
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
static void set_cssr_bank_trop(clas_ctx_t *ctx, int ch, gtime_t time, ssrn_t *ssrn, int network)
Definition mrtk_clas.c:880
static clas_bias_bank_t * add_base_value_to_cbias(clas_bank_ctrl_t *bank, clas_bias_bank_t *srcbias, int network)
Definition mrtk_clas.c:924
static clas_orbit_bank_t * get_same_orbit(clas_bank_ctrl_t *bank, gtime_t time, int network)
Definition mrtk_clas.c:387
void clas_restore_backup(clas_ctx_t *ctx, gtime_t time, clas_grid_t *grid, int l6mrg)
Definition mrtk_clas.c:1274
static int get_bias_save_point(clas_bias_bank_t *bias, int prn, int mode)
Definition mrtk_clas.c:598
static clas_trop_bank_t * get_close_trop(clas_bank_ctrl_t *bank, gtime_t obstime, gtime_t time, int network, double age, int tropless)
Definition mrtk_clas.c:571
static double decode_sval(const uint8_t *buff, int i, int n, double lsb)
Definition mrtk_clas.c:134
int cssr_sys2gnss(int sys, int *prn0)
Convert RTKLIB system bitmask to CSSR GNSS ID.
Definition mrtk_clas.c:140
static clas_clock_bank_t * get_close_clock(clas_bank_ctrl_t *bank, gtime_t obstime, gtime_t time, int network, double age)
Definition mrtk_clas.c:472
static void set_cssr_bank_clock(clas_ctx_t *ctx, int ch, gtime_t time, int network)
Definition mrtk_clas.c:681
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
static void set_cssr_latest_trop(clas_bank_ctrl_t *bank, gtime_t time, ssrn_t *ssrn, int network)
Definition mrtk_clas.c:827
static gtime_t backup_cssr_time(const clas_corr_t *corr)
Definition mrtk_clas.c:1221
static int get_cssr_latest_trop(clas_bank_ctrl_t *bank, double *total, double *wet, gtime_t time, int network, int index)
Definition mrtk_clas.c:849
static int sigmask2sig_p(int nsat, int *sat, uint16_t *sigmask, uint16_t *cellmask, int *nsig, int *sig)
Definition mrtk_clas.c:289
static int decode_cssr_pb(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2121
static int get_cssr_latest_iono(clas_bank_ctrl_t *bank, double *iono, gtime_t time, int network, int index, int sat)
Definition mrtk_clas.c:862
static float decode_cssr_quality_stec(int a, int b)
Definition mrtk_clas.c:257
void clas_ctx_free(clas_ctx_t *ctx)
Free CLAS context (release heap-allocated bank control).
Definition mrtk_clas.c:88
static int decode_cssr_oc(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:1937
static int svmask2nsat(uint64_t svmask)
Definition mrtk_clas.c:210
static int decode_cssr_ura(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2304
static void check_week_ref(clas_ctx_t *ctx, clas_l6buf_t *l6, int tow, int i)
Definition mrtk_clas.c:271
static int sigmask2nsig(uint16_t sigmask)
Definition mrtk_clas.c:220
int cssr_gnss2sys(int gnss, int *prn0)
Convert CSSR GNSS ID to RTKLIB system bitmask.
Definition mrtk_clas.c:175
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
int clas_input_cssrf(clas_ctx_t *ctx, FILE *fp, int ch)
Input CSSR from file (one message at a time, post-processing).
Definition mrtk_clas.c:3217
static int check_bit_width_oc(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1588
static void set_cssr_bank_orbit(clas_ctx_t *ctx, int ch, gtime_t time, int network)
Definition mrtk_clas.c:634
int clas_get_correct_fac(int msgid)
Get L6 facility ID from message ID byte.
Definition mrtk_clas.c:3037
static clas_bias_bank_t * get_close_pbias(clas_bank_ctrl_t *bank, gtime_t time, int network, double age)
Definition mrtk_clas.c:528
static clas_bias_bank_t * get_close_net_cbias(clas_bank_ctrl_t *bank, gtime_t time, int network, double age)
Definition mrtk_clas.c:551
static clas_bias_bank_t * get_close_cbias(clas_bank_ctrl_t *bank, gtime_t time, int network, double age)
Definition mrtk_clas.c:505
int clas_read_grid_def(clas_ctx_t *ctx, const char *gridfile)
Read CLAS grid definition file.
Definition mrtk_clas.c:3238
static int decode_cssr_grid(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2415
static clas_bias_bank_t * add_base_value_to_pbias(clas_bank_ctrl_t *bank, clas_bias_bank_t *srcbias, int network)
Definition mrtk_clas.c:956
void clas_check_grid_status_time(clas_ctx_t *ctx, gtime_t time, int ch)
Definition mrtk_clas.c:1365
static const double ssrudint[16]
Definition mrtk_clas.c:44
static int check_bit_width_bias(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1629
static int check_bit_width_combo(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1727
void clas_bank_init(clas_ctx_t *ctx, int ch)
Initialize bank control for a channel.
Definition mrtk_clas.c:101
static clas_orbit_bank_t * get_close_orbit(clas_bank_ctrl_t *bank, gtime_t time, int network, double age)
Definition mrtk_clas.c:434
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
static int check_bit_width_si(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1860
static clas_bias_bank_t * get_same_bias(clas_bank_ctrl_t *bank, gtime_t time, int network)
Definition mrtk_clas.c:409
static int corr_bias_slot(const int smode[70], int mode)
Definition mrtk_clas.c:1106
static int check_bit_width_cc(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1604
static float decode_cssr_quality_trop(int a, int b)
Definition mrtk_clas.c:264
int clas_ctx_init(clas_ctx_t *ctx)
Initialize CLAS context (allocate bank control on heap).
Definition mrtk_clas.c:54
static int add_data_stec(stec_t *stec, gtime_t time, int sat, int slip, double iono, double rate, double rms, double quality)
Definition mrtk_clas.c:1013
static void set_cssr_bank_cbias(clas_ctx_t *ctx, int ch, gtime_t time, int network, int iod)
Definition mrtk_clas.c:723
static void check_cssr_changed_facility(clas_ctx_t *ctx, int ch, int facility)
Definition mrtk_clas.c:619
int clas_backup_valid(const clas_ctx_t *ctx, gtime_t obstime, int l6mrg)
Save/restore the latest usable CSSR snapshot for short correction gaps.
Definition mrtk_clas.c:1233
static int decode_cssr_bias(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2185
static int sub_get_close_cssr(clas_bank_ctrl_t *bank, gtime_t time, int network, clas_orbit_bank_t **orbit, clas_clock_bank_t **clock, clas_bias_bank_t **cbias, clas_bias_bank_t **pbias, clas_trop_bank_t **trop, int *flag)
Definition mrtk_clas.c:1046
static int decode_cssr_stec(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2342
void clas_update_local(nav_t *nav, const clas_corr_t *corr, int ch)
Apply local corrections (trop/STEC) to nav_t.
Definition mrtk_clas.c:1493
static int check_bit_width_atmos(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1769
static int check_bit_width_cb(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1609
static int check_bit_width_ura(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1672
static int decode_qzs_msg(clas_ctx_t *ctx, int ch)
Definition mrtk_clas.c:2883
static int decode_cssr_combo(clas_ctx_t *ctx, int ch, clas_l6buf_t *l6, int i0)
Definition mrtk_clas.c:2560
static int check_bit_width_grid(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1699
void clas_backup_current(clas_ctx_t *ctx, const clas_grid_t *grid, int l6mrg)
Definition mrtk_clas.c:1250
void clas_clear_current(clas_ctx_t *ctx, int ch)
Drop the current correction snapshot of one channel.
Definition mrtk_clas.c:1266
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
static int check_bit_width_mask(clas_l6buf_t *l6, cssr_t *cssr, int i0)
Definition mrtk_clas.c:1549
CLAS (QZSS L6D) CSSR decoder and grid correction type definitions.
#define CSSR_MAX_SV_GNSS
Definition mrtk_clas.h:62
#define CSSR_MAX_SV
Definition mrtk_clas.h:63
#define CSSR_TYPE_CB
Definition mrtk_clas.h:90
#define CSSR_TYPE_MASK
Definition mrtk_clas.h:87
#define CSSR_TYPE_BIAS
Definition mrtk_clas.h:92
#define L6FRMPREAMB
Definition mrtk_clas.h:150
#define CSSR_TYPE_URA
Definition mrtk_clas.h:93
#define CSSR_TYPE_STEC
Definition mrtk_clas.h:94
#define CSSR_SYS_QZS
Definition mrtk_clas.h:78
#define CLAS_CH_NUM
Definition mrtk_clas.h:185
#define CLAS_MAX_NETWORK
Definition mrtk_clas.h:186
#define CSSR_SYS_GLO
Definition mrtk_clas.h:75
#define CSSR_SYS_BDS
Definition mrtk_clas.h:77
@ CSSR_REF_PBIAS
Definition mrtk_clas.h:162
@ CSSR_REF_URA
Definition mrtk_clas.h:164
@ CSSR_REF_ORBIT
Definition mrtk_clas.h:159
@ CSSR_REF_CBIAS
Definition mrtk_clas.h:161
@ CSSR_REF_MAX
Definition mrtk_clas.h:169
@ CSSR_REF_MASK
Definition mrtk_clas.h:158
@ CSSR_REF_ATMOSPHERIC
Definition mrtk_clas.h:168
@ CSSR_REF_COMBINED
Definition mrtk_clas.h:167
@ CSSR_REF_CLOCK
Definition mrtk_clas.h:160
@ CSSR_REF_STEC
Definition mrtk_clas.h:165
@ CSSR_REF_GRID
Definition mrtk_clas.h:166
@ CSSR_REF_BIAS
Definition mrtk_clas.h:163
#define CSSR_MAX_SIG
Definition mrtk_clas.h:64
#define CSSR_MAX_NET
Definition mrtk_clas.h:66
#define CSSR_SYS_GAL
Definition mrtk_clas.h:76
#define CLAS_L6_RELOCK_TIMEOUT
Definition mrtk_clas.h:808
#define CSSR_INVALID_VALUE
Definition mrtk_clas.h:144
#define CSSR_TYPE_ATMOS
Definition mrtk_clas.h:98
#define CSSR_TROP_HS_REF
Definition mrtk_clas.h:122
#define CSSR_TYPE_COMBO
Definition mrtk_clas.h:97
#define CLAS_MAX_GP
Definition mrtk_clas.h:188
#define CSSR_TYPE_GRID
Definition mrtk_clas.h:95
#define CSSR_TYPE_SI
Definition mrtk_clas.h:96
#define CSSR_SYS_SBS
Definition mrtk_clas.h:79
#define CSSR_TYPE_OC
Definition mrtk_clas.h:88
#define CSSR_SYS_GPS
Definition mrtk_clas.h:74
#define CSSR_TYPE_PB
Definition mrtk_clas.h:91
#define CSSR_MAX_GNSS
Definition mrtk_clas.h:61
#define P2_S9_MAX
Definition mrtk_clas.h:113
#define CSSR_MAX_GP
Definition mrtk_clas.h:68
#define CSSR_SYS_NONE
Definition mrtk_clas.h:80
#define CSSR_TROPVALIDAGE
Definition mrtk_clas.h:137
#define CSSR_TYPE_CC
Definition mrtk_clas.h:89
#define CLAS_BANK_NUM
Definition mrtk_clas.h:187
#define CSSR_STECVALIDAGE
Definition mrtk_clas.h:138
#define P2_S8_MAX
Definition mrtk_clas.h:114
#define BLEN_MSG
Definition mrtk_clas.h:151
MRTKLIB Constants — Physical, navigation, and system constants.
#define D2R
Definition mrtk_coords.c:36
#define MAXSAT
Definition mrtk_coords.c:45
int satsys(int sat, int *prn)
Definition mrtk_nav.c:117
#define SYS_GPS
Definition mrtk_eph.c:65
#define SYS_NONE
Definition mrtk_eph.c:64
#define SYS_SBS
Definition mrtk_eph.c:66
#define SYS_QZS
Definition mrtk_eph.c:69
#define SYS_CMP
Definition mrtk_eph.c:70
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
struct nav_s nav_t
Definition mrtk_eph.h:40
#define CODE_L5Q
#define CODE_L2Q
#define CODE_L1S
#define CODE_L5I
#define CODE_L2X
#define CODE_L3X
#define CODE_L1L
#define CODE_L5X
#define CODE_L7X
#define CODE_L2P
#define CODE_L2I
#define MAXCODE
#define CODE_L1C
#define CODE_L6I
#define CODE_L2C
#define CODE_L7I
#define CODE_L6X
#define CODE_L8Q
#define CODE_L3Q
#define CODE_L3I
#define CODE_L1B
#define CODE_L2S
#define CODE_L7Q
#define CODE_L1W
#define CODE_L8X
#define SSR_CH_NUM
#define CODE_L2W
#define CODE_L2L
#define CODE_L8I
#define CODE_L1P
#define CODE_L6Q
#define CODE_L1X
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
const char * l6_service_name(l6_service_t svc)
Short name of an L6 service, for logs.
Definition mrtk_l6.c:162
@ L6_SVC_NULL
Definition mrtk_l6.h:60
@ L6_SVC_CLAS
Definition mrtk_l6.h:61
static int search(int n, int m, const double *L, const double *D, const double *zs, double *zn, double *s)
int satno(int sys, int prn)
Convert satellite system+prn/slot number to satellite number.
Definition mrtk_nav.c:58
#define FREQ2
Definition mrtk_obs.c:68
#define R2D
Definition mrtk_obs.c:51
#define FREQ1
Definition mrtk_obs.c:67
gtime_t gpst2time(int week, double sec)
Convert GPS week/tow to gtime_t.
Definition mrtk_time.c:165
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
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_clock_bank_t ClockBank[32]
Definition mrtk_clas.h:443
clas_bias_bank_t BiasBank[32]
Definition mrtk_clas.h:444
clas_trop_bank_t TropBank[32]
Definition mrtk_clas.h:445
clas_orbit_bank_t OrbitBank[32]
Definition mrtk_clas.h:442
clas_latest_trop_t LatestTrop
Definition mrtk_clas.h:447
double pbias[MAXSAT][MAXCODE]
Definition mrtk_clas.h:411
int sflag[MAXSAT][MAXCODE]
Definition mrtk_clas.h:409
int iod[MAXSAT]
Definition mrtk_clas.h:407
int smode[MAXSAT][MAXCODE]
Definition mrtk_clas.h:408
double udi[MAXSAT]
Definition mrtk_clas.h:406
int prn[MAXSAT]
Definition mrtk_clas.h:405
double cbias[MAXSAT][MAXCODE]
Definition mrtk_clas.h:410
double c0[MAXSAT]
Definition mrtk_clas.h:396
double udi[MAXSAT]
Definition mrtk_clas.h:394
int iod[MAXSAT]
Definition mrtk_clas.h:395
int prn[MAXSAT]
Definition mrtk_clas.h:393
gtime_t bias_time
Definition mrtk_clas.h:364
int flag[MAXSAT]
Definition mrtk_clas.h:358
double deph0[MAXSAT]
Definition mrtk_clas.h:345
double cbias[MAXSAT][MAXCODE]
Definition mrtk_clas.h:340
int prn[MAXSAT][6]
Definition mrtk_clas.h:357
zwdd_t zwddata[80]
Definition mrtk_clas.h:336
double udi[MAXSAT][6]
Definition mrtk_clas.h:355
double deph1[MAXSAT]
Definition mrtk_clas.h:346
double pbias[MAXSAT][MAXCODE]
Definition mrtk_clas.h:341
stec_t stec[80]
Definition mrtk_clas.h:333
gtime_t orbit_time
Definition mrtk_clas.h:362
int iode[MAXSAT]
Definition mrtk_clas.h:348
int iod[MAXSAT][6]
Definition mrtk_clas.h:356
gtime_t time[MAXSAT][6]
Definition mrtk_clas.h:354
stecd_t stecdata[80][MAXSAT]
Definition mrtk_clas.h:332
double c0[MAXSAT]
Definition mrtk_clas.h:351
gtime_t trop_time
Definition mrtk_clas.h:365
double deph2[MAXSAT]
Definition mrtk_clas.h:347
int smode[MAXSAT][MAXCODE]
Definition mrtk_clas.h:342
gtime_t update_time
Definition mrtk_clas.h:361
gtime_t clock_time
Definition mrtk_clas.h:363
zwd_t zwd[80]
Definition mrtk_clas.h:337
clas_corr_t current[2]
Definition mrtk_clas.h:588
int l6pattern[2]
Definition mrtk_clas.h:623
gtime_t obs_ref[CSSR_REF_MAX]
Definition mrtk_clas.h:612
clas_l6buf_t l6buf[2]
Definition mrtk_clas.h:608
int tow_ref[CSSR_REF_MAX]
Definition mrtk_clas.h:613
int l6delivery[2]
Definition mrtk_clas.h:616
double grid_pos[13][80][3]
Definition mrtk_clas.h:596
cssr_t cssr[2]
Definition mrtk_clas.h:582
gtime_t l6lock_time[2]
Definition mrtk_clas.h:630
int l6facility[2]
Definition mrtk_clas.h:617
int initialized
Definition mrtk_clas.h:643
clas_dec_ssr_t dec_ssr[MAXSAT]
Definition mrtk_clas.h:605
clas_grid_t backup_grid[2]
Definition mrtk_clas.h:593
clas_bank_ctrl_t * bank[2]
Definition mrtk_clas.h:585
int week_ref[CSSR_REF_MAX]
Definition mrtk_clas.h:611
int grid_stat[2][13][80]
Definition mrtk_clas.h:599
clas_corr_t backup[2]
Definition mrtk_clas.h:589
uint8_t update_oc
Definition mrtk_clas.h:538
uint8_t update_ura
Definition mrtk_clas.h:542
double ddeph[3]
Definition mrtk_clas.h:530
double deph[3]
Definition mrtk_clas.h:529
double pbias[MAXCODE]
Definition mrtk_clas.h:533
int smode[MAXCODE]
Definition mrtk_clas.h:535
double dclk[3]
Definition mrtk_clas.h:531
double udi[6]
Definition mrtk_clas.h:525
gtime_t t0[6]
Definition mrtk_clas.h:524
uint8_t update_cc
Definition mrtk_clas.h:539
double cbias[MAXCODE]
Definition mrtk_clas.h:532
uint8_t update_pb
Definition mrtk_clas.h:541
uint8_t update_cb
Definition mrtk_clas.h:540
int discnt[MAXCODE]
Definition mrtk_clas.h:534
uint8_t update
Definition mrtk_clas.h:537
uint32_t preamble
Definition mrtk_clas.h:563
double ftag_last
Definition mrtk_clas.h:569
uint8_t buff[1200]
Definition mrtk_clas.h:553
uint8_t frame
Definition mrtk_clas.h:564
gtime_t time
Definition mrtk_clas.h:565
double ftag
Definition mrtk_clas.h:568
uint8_t fbuff[L6_FRAME_LEN]
Definition mrtk_clas.h:554
gtime_t troptime[13][80]
Definition mrtk_clas.h:431
double stec[13][80][MAXSAT]
Definition mrtk_clas.h:437
double stec0[13][80][MAXSAT]
Definition mrtk_clas.h:436
double total[13][80]
Definition mrtk_clas.h:432
double wet[13][80]
Definition mrtk_clas.h:433
gtime_t stectime[13][80][MAXSAT]
Definition mrtk_clas.h:434
int prn[13][80][MAXSAT]
Definition mrtk_clas.h:435
int prn[MAXSAT]
Definition mrtk_clas.h:379
double deph1[MAXSAT]
Definition mrtk_clas.h:384
double deph0[MAXSAT]
Definition mrtk_clas.h:383
int iode[MAXSAT]
Definition mrtk_clas.h:382
double deph2[MAXSAT]
Definition mrtk_clas.h:385
int iod[MAXSAT]
Definition mrtk_clas.h:381
double udi[MAXSAT]
Definition mrtk_clas.h:380
double total[13][80]
Definition mrtk_clas.h:420
double gridpos[13][80][3]
Definition mrtk_clas.h:419
int prn[13][80][MAXSAT]
Definition mrtk_clas.h:423
double iono[13][80][MAXSAT]
Definition mrtk_clas.h:424
int satnum[13][80]
Definition mrtk_clas.h:422
double wet[13][80]
Definition mrtk_clas.h:421
uint16_t sigmask[16]
Definition mrtk_clas.h:299
int l6facility
Definition mrtk_clas.h:314
uint64_t net_svmask[32]
Definition mrtk_clas.h:301
ssrn_t ssrn[32]
Definition mrtk_clas.h:315
uint8_t cmi[16]
Definition mrtk_clas.h:297
uint16_t cellmask[64]
Definition mrtk_clas.h:300
uint64_t svmask[16]
Definition mrtk_clas.h:298
int l6delivery
Definition mrtk_clas.h:313
int si_sz
Definition mrtk_clas.h:317
int iod
Definition mrtk_clas.h:293
cssropt_t opt
Definition mrtk_clas.h:292
int si_cnt
Definition mrtk_clas.h:316
uint64_t si_data[4]
Definition mrtk_clas.h:318
int stec_type
Definition mrtk_clas.h:267
GNSS time representation.
Definition mrtk_time.h:49
double sec
Definition mrtk_time.h:51
time_t time
Definition mrtk_time.h:50
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 alert
Definition mrtk_l6.h:78
uint8_t msgid
Definition mrtk_l6.h:71
l6_service_t svc
Definition mrtk_l6.h:79
uint8_t sf_ind
Definition mrtk_l6.h:77
ssr_t ssr_ch[SSR_CH_NUM][MAXSAT]
Definition mrtk_nav.h:434
int facility[SSR_CH_NUM]
Definition mrtk_nav.h:435
double dclk[3]
Definition mrtk_nav.h:185
double ddeph[3]
Definition mrtk_nav.h:184
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
double deph[3]
Definition mrtk_nav.h:183
double pbias[MAXCODE]
Definition mrtk_nav.h:188
float cbias[MAXCODE]
Definition mrtk_nav.h:187
double udi[6]
Definition mrtk_nav.h:177
int discnt[MAXCODE]
Definition mrtk_nav.h:192
float trop_wet[128]
Definition mrtk_clas.h:276
gtime_t t0[2]
Definition mrtk_clas.h:271
float quality
Definition mrtk_clas.h:280
int update[3]
Definition mrtk_clas.h:287
double udi[2]
Definition mrtk_clas.h:272
int sat_f[32]
Definition mrtk_clas.h:279
int nsat[128]
Definition mrtk_clas.h:282
float trop_total[128]
Definition mrtk_clas.h:277
int iod[2]
Definition mrtk_clas.h:273
float stec0[128][32]
Definition mrtk_clas.h:285
float quality_f[32]
Definition mrtk_clas.h:275
double a[32][4]
Definition mrtk_clas.h:281
float stec[128][32]
Definition mrtk_clas.h:284
int nsat_f
Definition mrtk_clas.h:278
int sat[128][32]
Definition mrtk_clas.h:283
double grid[128][3]
Definition mrtk_clas.h:286
int ngp
Definition mrtk_clas.h:274
int nmax
Definition mrtk_clas.h:214
int n
Definition mrtk_clas.h:214
stecd_t * data
Definition mrtk_clas.h:215
double pos[3]
Definition mrtk_clas.h:213
float iono
Definition mrtk_clas.h:205
float rate
Definition mrtk_clas.h:206
float quality
Definition mrtk_clas.h:207
float rms
Definition mrtk_clas.h:208
unsigned char slip
Definition mrtk_clas.h:204
gtime_t time
Definition mrtk_clas.h:202
unsigned char sat
Definition mrtk_clas.h:203
int flag
Definition mrtk_clas.h:209
float pos[3]
Definition mrtk_clas.h:232
zwdd_t * data
Definition mrtk_clas.h:234
int n
Definition mrtk_clas.h:233
int nmax
Definition mrtk_clas.h:233
float quality
Definition mrtk_clas.h:228
gtime_t time
Definition mrtk_clas.h:223
unsigned char valid
Definition mrtk_clas.h:224
float ztd
Definition mrtk_clas.h:226
float rms
Definition mrtk_clas.h:227
float zwd
Definition mrtk_clas.h:225
#define calloc(x, y)
Definition toml.c:54
#define free(x)
Definition toml.c:53