MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_rinex.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_rinex.c : RINEX file I/O functions
3 *
4 * Copyright (C) 2026 H.SHIONO (MRTKLIB Project)
5 * Copyright (C) 2023-2025 Cabinet Office, Japan
6 * Copyright (C) 2024-2025 Lighthouse Technology & Consulting Co. Ltd.
7 * Copyright (C) 2023-2025 Japan Aerospace Exploration Agency
8 * Copyright (C) 2023-2025 TOSHIBA ELECTRONIC TECHNOLOGIES CORPORATION
9 * Copyright (C) 2015- Mitsubishi Electric Corp.
10 * Copyright (C) 2014 Geospatial Information Authority of Japan
11 * Copyright (C) 2014 T.SUZUKI
12 * Copyright (C) 2007-2023 T.TAKASU
13 *
14 * SPDX-License-Identifier: BSD-2-Clause
15 *----------------------------------------------------------------------------*/
16/**
17 * @file mrtk_rinex.c
18 * @brief MRTKLIB RINEX Module — RINEX file read/write functions.
19 *
20 * This file contains all RINEX file reading and writing functions
21 * originally in rinex.c, migrated to the mrtklib modular architecture.
22 *
23 * @note All functions in this module are pure cut-and-paste extractions
24 * from rinex.c with zero algorithmic changes.
25 */
26#include "mrtklib/mrtk_rinex.h"
27
28#include <math.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "mrtklib/mrtk_mat.h"
33#include "mrtklib/mrtk_sys.h"
34#include "mrtklib/mrtk_trace.h"
35
36/* local constants (duplicated to avoid rtklib.h dependency) */
37#define SYS_NONE 0x00
38#define SYS_GPS 0x01
39#define SYS_SBS 0x02
40#define SYS_GLO 0x04
41#define SYS_GAL 0x08
42#define SYS_QZS 0x10
43#define SYS_CMP 0x20
44#define SYS_IRN 0x40
45#define SYS_BD2 0x100
46#define SYS_ALL 0xFF
47
48#define TSYS_GPS 0
49#define TSYS_UTC 1
50#define TSYS_GLO 2
51#define TSYS_GAL 3
52#define TSYS_QZS 4
53#define TSYS_CMP 5
54#define TSYS_IRN 6
55
56#define LLI_SLIP 0x01
57#define LLI_HALFC 0x02
58#define LLI_BOCTRK 0x04
59#define LLI_HALFA 0x40
60#define LLI_HALFS 0x80
61
62#define FILEPATHSEP '/'
63
64/* forward declaration of rtklib function resolved at link time */
65
66/* constants/macros ----------------------------------------------------------*/
67
68#define SQR(x) ((x) * (x))
69
70#define NAVEXP "D" /* exponent letter in RINEX NAV */
71#define NUMSYS 8 /* number of systems (incl. BD2) */
72#define MAXRNXLEN (16 * MAXOBSTYPE + 4) /* max RINEX record length */
73#define MAXPOSHEAD 1024 /* max head line position */
74#define MINFREQ_GLO -7 /* min frequency number GLONASS */
75#define MAXFREQ_GLO 13 /* max frequency number GLONASS */
76#define NINCOBS 262144 /* incremental number of obs data */
77
78static const int navsys[] = {/* satellite systems */
80static const char syscodes[] = "GREJSCI"; /* satellite system codes */
81
82static const char obscodes[] = "CLDS"; /* observation type codes */
83
84static const double ura_eph[] = {/* URA values (ref [3] 20.3.3.3.1.1) */
85 2.4, 3.4, 4.85, 6.85, 9.65, 13.65, 24.0, 48.0,
86 96.0, 192.0, 384.0, 768.0, 1536.0, 3072.0, 6144.0, 0.0};
87static const double ura_nominal[] = {/* URA nominal values */
88 2.0, 2.8, 4.0, 5.7, 8.0, 11.3, 16.0, 32.0,
89 64.0, 128.0, 256.0, 512.0, 1024.0, 2048.0, 4096.0, 8192.0};
90/* type definition -----------------------------------------------------------*/
91typedef struct { /* signal index type */
92 int n; /* number of index */
93 int idx[MAXOBSTYPE]; /* signal freq-index */
94 int pos[MAXOBSTYPE]; /* signal index in obs data (-1:no) */
95 uint8_t pri[MAXOBSTYPE]; /* signal priority (15-0) */
96 uint8_t type[MAXOBSTYPE]; /* type (0:C,1:L,2:D,3:S) */
97 uint8_t code[MAXOBSTYPE]; /* obs-code (CODE_L??) */
98 double shift[MAXOBSTYPE]; /* phase shift (cycle) */
99} sigind_t;
100
101/* set string without tail space ---------------------------------------------*/
102static void setstr(char* dst, const char* src, int n) {
103 char* p = dst;
104 const char* q = src;
105 while (*q && q < src + n) {
106 *p++ = *q++;
107 }
108 *p-- = '\0';
109 while (p >= dst && *p == ' ') {
110 *p-- = '\0';
111 }
112}
113/* adjust time considering week handover -------------------------------------*/
115 double tt = timediff(t, t0);
116 if (tt < -302400.0) {
117 return timeadd(t, 604800.0);
118 }
119 if (tt > 302400.0) {
120 return timeadd(t, -604800.0);
121 }
122 return t;
123}
124/* adjust time considering week handover -------------------------------------*/
126 double tt = timediff(t, t0);
127 if (tt < -43200.0) {
128 return timeadd(t, 86400.0);
129 }
130 if (tt > 43200.0) {
131 return timeadd(t, -86400.0);
132 }
133 return t;
134}
135/* time string for ver.3 (yyyymmdd hhmmss UTC) -------------------------------*/
136static void timestr_rnx(char* str) {
137 gtime_t time;
138 double ep[6];
139 time = timeget();
140 time.sec = 0.0;
141 time2epoch(time, ep);
142 sprintf(str, "%04.0f%02.0f%02.0f %02.0f%02.0f%02.0f UTC", ep[0], ep[1], ep[2], ep[3], ep[4], ep[5]);
143}
144/* satellite to satellite code -----------------------------------------------*/
145static int sat2code(int sat, char* code) {
146 int prn;
147 switch (satsys(sat, &prn)) {
148 case SYS_GPS:
149 sprintf(code, "G%02d", prn - MINPRNGPS + 1);
150 break;
151 case SYS_GLO:
152 sprintf(code, "R%02d", prn - MINPRNGLO + 1);
153 break;
154 case SYS_GAL:
155 sprintf(code, "E%02d", prn - MINPRNGAL + 1);
156 break;
157 case SYS_SBS:
158 sprintf(code, "S%02d", prn - 100);
159 break;
160 case SYS_QZS:
161 sprintf(code, "J%02d", prn - MINPRNQZS + 1);
162 break;
163 case SYS_CMP:
164 sprintf(code, "C%02d", prn - MINPRNCMP + 1);
165 break;
166 case SYS_IRN:
167 sprintf(code, "I%02d", prn - MINPRNIRN + 1);
168 break;
169 default:
170 return 0;
171 }
172 return 1;
173}
174/* URA index to URA nominal value (m) ----------------------------------------*/
175static double uravalue(int sva) { return 0 <= sva && sva < 15 ? ura_nominal[sva] : 8192.0; }
176/* URA value (m) to URA index ------------------------------------------------*/
177static int uraindex(double value) {
178 int i;
179 for (i = 0; i < 15; i++) {
180 if (ura_eph[i] >= value) {
181 break;
182 }
183 }
184 return i;
185}
186/* Galileo SISA index to SISA nominal value (m) ------------------------------*/
187static double sisa_value(int sisa) {
188 if (sisa <= 49) {
189 return sisa * 0.01;
190 }
191 if (sisa <= 74) {
192 return 0.5 + (sisa - 50) * 0.02;
193 }
194 if (sisa <= 99) {
195 return 1.0 + (sisa - 75) * 0.04;
196 }
197 if (sisa <= 125) {
198 return 2.0 + (sisa - 100) * 0.16;
199 }
200 return -1.0; /* unknown or NAPA */
201}
202/* Galileo SISA value (m) to SISA index --------------------------------------*/
203static int sisa_index(double value) {
204 if (value < 0.0 || value > 6.0) {
205 return 255; /* unknown or NAPA */
206 } else if (value <= 0.5) {
207 return (int)(value / 0.01);
208 } else if (value <= 1.0) {
209 return (int)((value - 0.5) / 0.02) + 50;
210 } else if (value <= 2.0) {
211 return (int)((value - 1.0) / 0.04) + 75;
212 }
213 return ((int)(value - 2.0) / 0.16) + 100;
214}
215/* initialize station parameter ----------------------------------------------*/
216static void init_sta(sta_t* sta) {
217 int i;
218 *sta->name = '\0';
219 *sta->marker = '\0';
220 *sta->antdes = '\0';
221 *sta->antsno = '\0';
222 *sta->rectype = '\0';
223 *sta->recver = '\0';
224 *sta->recsno = '\0';
225 sta->antsetup = sta->itrf = sta->deltype = 0;
226 for (i = 0; i < 3; i++) {
227 sta->pos[i] = 0.0;
228 }
229 for (i = 0; i < 3; i++) {
230 sta->del[i] = 0.0;
231 }
232 sta->hgt = 0.0;
233}
234/*------------------------------------------------------------------------------
235 * input RINEX functions
236 *-----------------------------------------------------------------------------*/
237
238/* convert RINEX obs-type ver.2 -> ver.3 -------------------------------------*/
239static void convcode(int ver, int sys, const char* str, char* type) {
240 strcpy(type, " ");
241
242 if (!strcmp(str, "P1")) { /* ver.2.11 GPS L1PY,GLO L2P */
243 if (sys == SYS_GPS) {
244 sprintf(type, "%c1W", 'C');
245 } else if (sys == SYS_GLO) {
246 sprintf(type, "%c1P", 'C');
247 }
248 } else if (!strcmp(str, "P2")) { /* ver.2.11 GPS L2PY,GLO L2P */
249 if (sys == SYS_GPS) {
250 sprintf(type, "%c2W", 'C');
251 } else if (sys == SYS_GLO) {
252 sprintf(type, "%c2P", 'C');
253 }
254 } else if (!strcmp(str, "C1")) { /* ver.2.11 GPS L1C,GLO L1C/A */
255 if (ver >= 212) {
256 ; /* reject C1 for 2.12 */
257 } else if (sys == SYS_GPS) {
258 sprintf(type, "%c1C", 'C');
259 } else if (sys == SYS_GLO) {
260 sprintf(type, "%c1C", 'C');
261 } else if (sys == SYS_GAL) {
262 sprintf(type, "%c1X", 'C'); /* ver.2.12 */
263 } else if (sys == SYS_QZS) {
264 sprintf(type, "%c1C", 'C');
265 } else if (sys == SYS_SBS) {
266 sprintf(type, "%c1C", 'C');
267 }
268 } else if (!strcmp(str, "C2")) {
269 if (sys == SYS_GPS) {
270 if (ver >= 212) {
271 sprintf(type, "%c2W", 'C'); /* L2P(Y) */
272 } else {
273 sprintf(type, "%c2X", 'C'); /* L2C */
274 }
275 } else if (sys == SYS_GLO) {
276 sprintf(type, "%c2C", 'C');
277 } else if (sys == SYS_QZS) {
278 sprintf(type, "%c2X", 'C');
279 } else if (sys == SYS_CMP) {
280 sprintf(type, "%c2X", 'C'); /* ver.2.12 B1_2 */
281 }
282 } else if (ver >= 2.12 && str[1] == 'A') { /* ver.2.12 L1C/A */
283 if (sys == SYS_GPS) {
284 sprintf(type, "%c1C", str[0]);
285 } else if (sys == SYS_GLO) {
286 sprintf(type, "%c1C", str[0]);
287 } else if (sys == SYS_QZS) {
288 sprintf(type, "%c1C", str[0]);
289 } else if (sys == SYS_SBS) {
290 sprintf(type, "%c1C", str[0]);
291 }
292 } else if (ver >= 2.12 && str[1] == 'B') { /* ver.2.12 GPS L1C */
293 if (sys == SYS_GPS) {
294 sprintf(type, "%c1X", str[0]);
295 } else if (sys == SYS_QZS) {
296 sprintf(type, "%c1X", str[0]);
297 }
298 } else if (ver >= 2.12 && str[1] == 'C') { /* ver.2.12 GPS L2C */
299 if (sys == SYS_GPS) {
300 sprintf(type, "%c2X", str[0]);
301 } else if (sys == SYS_QZS) {
302 sprintf(type, "%c2X", str[0]);
303 }
304 } else if (ver >= 2.12 && str[1] == 'D') { /* ver.2.12 GLO L2C/A */
305 if (sys == SYS_GLO) {
306 sprintf(type, "%c2C", str[0]);
307 }
308 } else if (ver >= 2.12 && str[1] == '1') { /* ver.2.12 GPS L1PY,GLO L1P */
309 if (sys == SYS_GPS) {
310 sprintf(type, "%c1W", str[0]);
311 } else if (sys == SYS_GLO) {
312 sprintf(type, "%c1P", str[0]);
313 } else if (sys == SYS_GAL) {
314 sprintf(type, "%c1X", str[0]); /* tentative */
315 } else if (sys == SYS_CMP) {
316 sprintf(type, "%c2X", str[0]); /* extension */
317 }
318 } else if (ver < 2.12 && str[1] == '1') {
319 if (sys == SYS_GPS) {
320 sprintf(type, "%c1C", str[0]);
321 } else if (sys == SYS_GLO) {
322 sprintf(type, "%c1C", str[0]);
323 } else if (sys == SYS_GAL) {
324 sprintf(type, "%c1X", str[0]); /* tentative */
325 } else if (sys == SYS_QZS) {
326 sprintf(type, "%c1C", str[0]);
327 } else if (sys == SYS_SBS) {
328 sprintf(type, "%c1C", str[0]);
329 }
330 } else if (str[1] == '2') {
331 if (sys == SYS_GPS) {
332 sprintf(type, "%c2W", str[0]);
333 } else if (sys == SYS_GLO) {
334 sprintf(type, "%c2P", str[0]);
335 } else if (sys == SYS_QZS) {
336 sprintf(type, "%c2X", str[0]);
337 } else if (sys == SYS_CMP) {
338 sprintf(type, "%c2X", str[0]); /* ver.2.12 B1_2 */
339 }
340 } else if (str[1] == '5') {
341 if (sys == SYS_GPS) {
342 sprintf(type, "%c5X", str[0]);
343 } else if (sys == SYS_GAL) {
344 sprintf(type, "%c5X", str[0]);
345 } else if (sys == SYS_QZS) {
346 sprintf(type, "%c5X", str[0]);
347 } else if (sys == SYS_SBS) {
348 sprintf(type, "%c5X", str[0]);
349 }
350 } else if (str[1] == '6') {
351 if (sys == SYS_GAL) {
352 sprintf(type, "%c6X", str[0]);
353 } else if (sys == SYS_QZS) {
354 sprintf(type, "%c6X", str[0]);
355 } else if (sys == SYS_CMP) {
356 sprintf(type, "%c6X", str[0]); /* ver.2.12 B3 */
357 }
358 } else if (str[1] == '7') {
359 if (sys == SYS_GAL) {
360 sprintf(type, "%c7X", str[0]);
361 } else if (sys == SYS_CMP) {
362 sprintf(type, "%c7X", str[0]); /* ver.2.12 B2b */
363 }
364 } else if (str[1] == '8') {
365 if (sys == SYS_GAL) {
366 sprintf(type, "%c8X", str[0]);
367 }
368 }
369 trace(NULL, 3, "convcode: ver=%.2f sys=%2d type= %s -> %s\n", ver / 100.0, sys, str, type);
370}
371/* decode RINEX observation data file header ---------------------------------*/
372static void decode_obsh(FILE* fp, char* buff, int ver, int* tsys, char tobs[][MAXOBSTYPE][4], nav_t* nav, sta_t* sta) {
373 /* default codes for unknown code */
374 const char frqcodes[] = "1256789";
375 const char* defcodes[] = {
376 "CWX ", /* GPS: L125____ */
377 "CCXX X ", /* GLO: L1234_6_ */
378 "C XXXX ", /* GAL: L1_5678_ */
379 "CXXX ", /* QZS: L1256___ */
380 "C X ", /* SBS: L1_5____ */
381 "XIXIIX ", /* BDS: L125678_ */
382 " A A" /* IRN: L__5___9 */
383 };
384 double del[3];
385 int i, j, k, n, nt, prn, fcn;
386 const char* p;
387 char *label = buff + 60, str[4];
388
389 trace(NULL, 4, "decode_obsh: ver=%.2f\n", ver / 100.0);
390
391 if (strstr(label, "MARKER NAME")) {
392 if (sta) {
393 setstr(sta->name, buff, 60);
394 }
395 } else if (strstr(label, "MARKER NUMBER")) { /* opt */
396 if (sta) {
397 setstr(sta->marker, buff, 20);
398 }
399 } else if (strstr(label, "MARKER TYPE")) {
400 ; /* ver.3 */
401 } else if (strstr(label, "OBSERVER / AGENCY")) {
402 /* ignored */
403 } else if (strstr(label, "REC # / TYPE / VERS")) {
404 if (sta) {
405 setstr(sta->recsno, buff, 20);
406 setstr(sta->rectype, buff + 20, 20);
407 setstr(sta->recver, buff + 40, 20);
408 }
409 } else if (strstr(label, "ANT # / TYPE")) {
410 if (sta) {
411 setstr(sta->antsno, buff, 20);
412 setstr(sta->antdes, buff + 20, 20);
413 }
414 } else if (strstr(label, "APPROX POSITION XYZ")) {
415 if (sta) {
416 for (i = 0, j = 0; i < 3; i++, j += 14) {
417 sta->pos[i] = str2num(buff, j, 14);
418 }
419 }
420 } else if (strstr(label, "ANTENNA: DELTA H/E/N")) {
421 if (sta) {
422 for (i = 0, j = 0; i < 3; i++, j += 14) {
423 del[i] = str2num(buff, j, 14);
424 }
425 sta->del[2] = del[0]; /* h */
426 sta->del[0] = del[1]; /* e */
427 sta->del[1] = del[2]; /* n */
428 }
429 } else if (strstr(label, "ANTENNA: DELTA X/Y/Z")) {
430 ; /* opt ver.3 */
431 } else if (strstr(label, "ANTENNA: PHASECENTER")) {
432 ; /* opt ver.3 */
433 } else if (strstr(label, "ANTENNA: B.SIGHT XYZ")) {
434 ; /* opt ver.3 */
435 } else if (strstr(label, "ANTENNA: ZERODIR AZI")) {
436 ; /* opt ver.3 */
437 } else if (strstr(label, "ANTENNA: ZERODIR XYZ")) {
438 ; /* opt ver.3 */
439 } else if (strstr(label, "CENTER OF MASS: XYZ")) {
440 ; /* opt ver.3 */
441 } else if (strstr(label, "SYS / # / OBS TYPES")) { /* ver.3, opt ver.4 */
442 if (!(p = strchr(syscodes, buff[0]))) {
443 trace(NULL, 2, "invalid system code: sys=%c\n", buff[0]);
444 return;
445 }
446 i = (int)(p - syscodes);
447 n = (int)str2num(buff, 3, 3);
448 for (j = nt = 0, k = 7; j < n; j++, k += 4) {
449 if (k > 58) {
450 if (!fgets(buff, MAXRNXLEN, fp)) {
451 break;
452 }
453 k = 7;
454 }
455 if (nt < MAXOBSTYPE - 1) {
456 setstr(tobs[i][nt++], buff + k, 3);
457 }
458 }
459 *tobs[i][nt] = '\0';
460
461 /* change BDS B1 code: 3.02 */
462 if (i == 5 && ver == 302) {
463 for (j = 0; j < nt; j++) {
464 if (tobs[i][j][1] == '1') {
465 tobs[i][j][1] = '2';
466 }
467 }
468 }
469 /* if unknown code in ver.3, set default code */
470 for (j = 0; j < nt; j++) {
471 if (tobs[i][j][2]) {
472 continue;
473 }
474 if (!(p = strchr(frqcodes, tobs[i][j][1]))) {
475 continue;
476 }
477 tobs[i][j][2] = defcodes[i][(int)(p - frqcodes)];
478 trace(NULL, 2, "set default for unknown code: sys=%c code=%s\n", buff[0], tobs[i][j]);
479 }
480 } else if (strstr(label, "WAVELENGTH FACT L1/2")) {
481 ; /* opt ver.2 */
482 } else if (strstr(label, "# / TYPES OF OBSERV")) { /* ver.2 */
483 n = (int)str2num(buff, 0, 6);
484 for (i = nt = 0, j = 10; i < n; i++, j += 6) {
485 if (j > 58) {
486 if (!fgets(buff, MAXRNXLEN, fp)) {
487 break;
488 }
489 j = 10;
490 }
491 if (nt >= MAXOBSTYPE - 1) {
492 continue;
493 }
494 if (ver <= 299) {
495 setstr(str, buff + j, 2);
496 convcode(ver, SYS_GPS, str, tobs[0][nt]);
497 convcode(ver, SYS_GLO, str, tobs[1][nt]);
498 convcode(ver, SYS_GAL, str, tobs[2][nt]);
499 convcode(ver, SYS_QZS, str, tobs[3][nt]);
500 convcode(ver, SYS_SBS, str, tobs[4][nt]);
501 convcode(ver, SYS_CMP, str, tobs[5][nt]);
502 }
503 nt++;
504 }
505 *tobs[0][nt] = '\0';
506 } else if (strstr(label, "SIGNAL STRENGTH UNIT")) {
507 ; /* opt ver.3 */
508 } else if (strstr(label, "INTERVAL")) {
509 ; /* opt */
510 } else if (strstr(label, "TIME OF FIRST OBS")) {
511 if (!strncmp(buff + 48, "GPS", 3)) {
512 *tsys = TSYS_GPS;
513 } else if (!strncmp(buff + 48, "GLO", 3)) {
514 *tsys = TSYS_UTC;
515 } else if (!strncmp(buff + 48, "GAL", 3)) {
516 *tsys = TSYS_GAL;
517 } else if (!strncmp(buff + 48, "QZS", 3)) {
518 *tsys = TSYS_QZS; /* ver.3.02 */
519 } else if (!strncmp(buff + 48, "BDT", 3)) {
520 *tsys = TSYS_CMP; /* ver.3.02 */
521 } else if (!strncmp(buff + 48, "IRN", 3)) {
522 *tsys = TSYS_IRN; /* ver.3.03 */
523 }
524 } else if (strstr(label, "TIME OF LAST OBS")) {
525 ; /* opt */
526 } else if (strstr(label, "RCV CLOCK OFFS APPL")) {
527 ; /* opt */
528 } else if (strstr(label, "SYS / DCBS APPLIED")) {
529 ; /* opt ver.3 */
530 } else if (strstr(label, "SYS / PCVS APPLIED")) {
531 ; /* opt ver.3 */
532 } else if (strstr(label, "SYS / SCALE FACTOR")) {
533 ; /* opt ver.3 */
534 } else if (strstr(label, "SYS / PHASE SHIFT")) {
535 ; /* ver.3.01 */
536 } else if (strstr(label, "GLONASS SLOT / FRQ #")) { /* ver.3.02 */
537 for (i = 0; i < 8; i++) {
538 if (buff[4 + i * 7] != 'R') {
539 continue;
540 }
541 prn = (int)str2num(buff, 5 + i * 7, 2);
542 fcn = (int)str2num(buff, 8 + i * 7, 2);
543 if (prn < 1 || prn > MAXPRNGLO || fcn < -7 || fcn > 6) {
544 continue;
545 }
546 if (nav) {
547 nav->glo_fcn[prn - 1] = fcn + 8;
548 }
549 }
550 } else if (strstr(label, "GLONASS COD/PHS/BIS")) { /* ver.3.02, opt ver.4 */
551 if (sta) {
552 sta->glo_cp_bias[0] = str2num(buff, 5, 8);
553 sta->glo_cp_bias[1] = str2num(buff, 18, 8);
554 sta->glo_cp_bias[2] = str2num(buff, 31, 8);
555 sta->glo_cp_bias[3] = str2num(buff, 44, 8);
556 }
557 } else if (strstr(label, "LEAP SECONDS")) { /* opt ver.3*/
558 if (nav) {
559 nav->utc_gps[4] = str2num(buff, 0, 6);
560 nav->utc_gps[7] = str2num(buff, 6, 6);
561 nav->utc_gps[5] = str2num(buff, 12, 6);
562 nav->utc_gps[6] = str2num(buff, 18, 6);
563 }
564 } else if (strstr(label, "# OF SALTELLITES")) { /* opt */
565 /* skip */;
566 } else if (strstr(label, "PRN / # OF OBS")) { /* opt */
567 /* skip */;
568 } else if (strstr(label, "DOI")) {
569 ; /* opt ver.4(skip) */
570 } else if (strstr(label, "LICENSE OF USE")) {
571 ; /* opt ver.4(skip) */
572 } else if (strstr(label, "STATION INFORMATION")) {
573 ; /* opt ver.4(skip) */
574 }
575}
576/* decode RINEX NAV header ---------------------------------------------------*/
577static void decode_navh(char* buff, nav_t* nav) {
578 int i, j;
579 char* label = buff + 60;
580
581 trace(NULL, 4, "decode_navh:\n");
582
583 if (strstr(label, "ION ALPHA")) { /* opt ver.2 */
584 if (nav) {
585 for (i = 0, j = 2; i < 4; i++, j += 12) {
586 nav->ion_gps[i] = str2num(buff, j, 12);
587 }
588 }
589 } else if (strstr(label, "ION BETA")) { /* opt ver.2 */
590 if (nav) {
591 for (i = 0, j = 2; i < 4; i++, j += 12) {
592 nav->ion_gps[i + 4] = str2num(buff, j, 12);
593 }
594 }
595 } else if (strstr(label, "DELTA-UTC: A0,A1,T,W")) { /* opt ver.2 */
596 if (nav) {
597 for (i = 0, j = 3; i < 2; i++, j += 19) {
598 nav->utc_gps[i] = str2num(buff, j, 19);
599 }
600 for (; i < 4; i++, j += 9) {
601 nav->utc_gps[i] = str2num(buff, j, 9);
602 }
603 }
604 } else if (strstr(label, "IONOSPHERIC CORR")) { /* opt ver.3 */
605 if (nav) {
606 if (!strncmp(buff, "GPSA", 4)) {
607 for (i = 0, j = 5; i < 4; i++, j += 12) {
608 nav->ion_gps[i] = str2num(buff, j, 12);
609 }
610 } else if (!strncmp(buff, "GPSB", 4)) {
611 for (i = 0, j = 5; i < 4; i++, j += 12) {
612 nav->ion_gps[i + 4] = str2num(buff, j, 12);
613 }
614 } else if (!strncmp(buff, "GAL", 3)) {
615 for (i = 0, j = 5; i < 4; i++, j += 12) {
616 nav->ion_gal[i] = str2num(buff, j, 12);
617 }
618 } else if (!strncmp(buff, "QZSA", 4)) { /* v.3.02 */
619 for (i = 0, j = 5; i < 4; i++, j += 12) {
620 nav->ion_qzs[i] = str2num(buff, j, 12);
621 }
622 } else if (!strncmp(buff, "QZSB", 4)) { /* v.3.02 */
623 for (i = 0, j = 5; i < 4; i++, j += 12) {
624 nav->ion_qzs[i + 4] = str2num(buff, j, 12);
625 }
626 } else if (!strncmp(buff, "BDSA", 4)) { /* v.3.02 */
627 for (i = 0, j = 5; i < 4; i++, j += 12) {
628 nav->ion_cmp[i] = str2num(buff, j, 12);
629 }
630 } else if (!strncmp(buff, "BDSB", 4)) { /* v.3.02 */
631 for (i = 0, j = 5; i < 4; i++, j += 12) {
632 nav->ion_cmp[i + 4] = str2num(buff, j, 12);
633 }
634 } else if (!strncmp(buff, "IRNA", 4)) { /* v.3.03 */
635 for (i = 0, j = 5; i < 4; i++, j += 12) {
636 nav->ion_irn[i] = str2num(buff, j, 12);
637 }
638 } else if (!strncmp(buff, "IRNB", 4)) { /* v.3.03 */
639 for (i = 0, j = 5; i < 4; i++, j += 12) {
640 nav->ion_irn[i + 4] = str2num(buff, j, 12);
641 }
642 }
643 }
644 } else if (strstr(label, "TIME SYSTEM CORR")) { /* opt ver.3 */
645 if (nav) {
646 if (!strncmp(buff, "GPUT", 4)) {
647 nav->utc_gps[0] = str2num(buff, 5, 17);
648 nav->utc_gps[1] = str2num(buff, 22, 16);
649 nav->utc_gps[2] = str2num(buff, 38, 7);
650 nav->utc_gps[3] = str2num(buff, 45, 5);
651 } else if (!strncmp(buff, "GLUT", 4)) {
652 nav->utc_glo[0] = -str2num(buff, 5, 17); /* tau_C */
653 } else if (!strncmp(buff, "GLGP", 4)) {
654 nav->utc_glo[1] = str2num(buff, 5, 17); /* tau_GPS */
655 } else if (!strncmp(buff, "GAUT", 4)) { /* v.3.02 */
656 nav->utc_gal[0] = str2num(buff, 5, 17);
657 nav->utc_gal[1] = str2num(buff, 22, 16);
658 nav->utc_gal[2] = str2num(buff, 38, 7);
659 nav->utc_gal[3] = str2num(buff, 45, 5);
660 } else if (!strncmp(buff, "QZUT", 4)) { /* v.3.02 */
661 nav->utc_qzs[0] = str2num(buff, 5, 17);
662 nav->utc_qzs[1] = str2num(buff, 22, 16);
663 nav->utc_qzs[2] = str2num(buff, 38, 7);
664 nav->utc_qzs[3] = str2num(buff, 45, 5);
665 } else if (!strncmp(buff, "BDUT", 4)) { /* v.3.02 */
666 nav->utc_cmp[0] = str2num(buff, 5, 17);
667 nav->utc_cmp[1] = str2num(buff, 22, 16);
668 nav->utc_cmp[2] = str2num(buff, 38, 7);
669 nav->utc_cmp[3] = str2num(buff, 45, 5);
670 } else if (!strncmp(buff, "SBUT", 4)) { /* v.3.02 */
671 nav->utc_sbs[0] = str2num(buff, 5, 17);
672 nav->utc_sbs[1] = str2num(buff, 22, 16);
673 nav->utc_sbs[2] = str2num(buff, 38, 7);
674 nav->utc_sbs[3] = str2num(buff, 45, 5);
675 } else if (!strncmp(buff, "IRUT", 4)) { /* v.3.03 */
676 nav->utc_irn[0] = str2num(buff, 5, 17);
677 nav->utc_irn[1] = str2num(buff, 22, 16);
678 nav->utc_irn[2] = str2num(buff, 38, 7);
679 nav->utc_irn[3] = str2num(buff, 45, 5);
680 nav->utc_irn[8] = 0.0; /* A2 */
681 }
682 }
683 } else if (strstr(label, "LEAP SECONDS")) { /* opt ver.3 */
684 if (nav) {
685 nav->utc_gps[4] = str2num(buff, 0, 6);
686 nav->utc_gps[7] = str2num(buff, 6, 6);
687 nav->utc_gps[5] = str2num(buff, 12, 6);
688 nav->utc_gps[6] = str2num(buff, 18, 6);
689 }
690 } else if (strstr(label, "REC # / TYPE / VERS")) {
691 ; /* opt ver.4(skip) */
692 } else if (strstr(label, "MERGED FILE")) {
693 ; /* opt ver.4(skip) */
694 } else if (strstr(label, "DOI")) {
695 ; /* opt ver.4(skip) */
696 } else if (strstr(label, "LICENSE OF USE")) {
697 ; /* opt ver.4(skip) */
698 } else if (strstr(label, "STATION INFORMATION")) {
699 ; /* opt ver.4(skip) */
700 }
701}
702/* decode GNAV header --------------------------------------------------------*/
703static void decode_gnavh(char* buff, nav_t* nav) {
704 char* label = buff + 60;
705
706 trace(NULL, 4, "decode_gnavh:\n");
707
708 if (strstr(label, "CORR TO SYTEM TIME")) {
709 } /* opt */
710 else if (strstr(label, "LEAP SECONDS")) {
711 } /* opt */
712}
713/* decode GEO NAV header -----------------------------------------------------*/
714static void decode_hnavh(char* buff, nav_t* nav) {
715 char* label = buff + 60;
716
717 trace(NULL, 4, "decode_hnavh:\n");
718
719 if (strstr(label, "CORR TO SYTEM TIME")) {
720 } /* opt */
721 else if (strstr(label, "D-UTC A0,A1,T,W,S,U")) {
722 } /* opt */
723 else if (strstr(label, "LEAP SECONDS")) {
724 } /* opt */
725}
726/* read RINEX file header ----------------------------------------------------*/
727static int readrnxh(FILE* fp, int* ver, char* type, int* sys, int* tsys, char tobs[][MAXOBSTYPE][4], nav_t* nav,
728 sta_t* sta) {
729 char buff[MAXRNXLEN], *label = buff + 60, ssys;
730 int i = 0;
731
732 trace(NULL, 3, "readrnxh:\n");
733
734 *ver = 210;
735 *type = ' ';
736 *sys = SYS_GPS;
737
738 while (fgets(buff, MAXRNXLEN, fp)) {
739 if (strlen(buff) <= 60) {
740 continue;
741 } else if (strstr(label, "RINEX VERSION / TYPE")) {
742 *ver = (int)(str2num(buff, 0, 9) * 100);
743 if (*ver >= 304 && *(buff + 21) == 'C') { /* CLOCK RINEX 3.04 */
744 *type = 'C';
745 ssys = *(buff + 42);
746 } else {
747 *type = *(buff + 20);
748 ssys = *(buff + 40);
749 }
750 /* satellite system */
751 switch (ssys) {
752 case ' ':
753 case 'G':
754 *sys = SYS_GPS;
755 *tsys = TSYS_GPS;
756 break;
757 case 'R':
758 *sys = SYS_GLO;
759 *tsys = TSYS_UTC;
760 break;
761 case 'E':
762 *sys = SYS_GAL;
763 *tsys = TSYS_GAL;
764 break; /* v.2.12 */
765 case 'S':
766 *sys = SYS_SBS;
767 *tsys = TSYS_GPS;
768 break;
769 case 'J':
770 *sys = SYS_QZS;
771 *tsys = TSYS_QZS;
772 break; /* v.3.02 */
773 case 'C':
774 *sys = SYS_CMP;
775 *tsys = TSYS_CMP;
776 break; /* v.2.12 */
777 case 'I':
778 *sys = SYS_IRN;
779 *tsys = TSYS_IRN;
780 break; /* v.3.03 */
781 case 'M':
782 *sys = SYS_NONE;
783 *tsys = TSYS_GPS;
784 break; /* mixed */
785 default:
786 trace(NULL, 2, "not supported satellite system: %c\n", *(buff + 40));
787 break;
788 }
789 continue;
790 } else if (strstr(label, "PGM / RUN BY / DATE")) {
791 continue;
792 } else if (strstr(label, "COMMENT")) {
793 continue;
794 }
795 switch (*type) { /* file type */
796 case 'O':
797 decode_obsh(fp, buff, *ver, tsys, tobs, nav, sta);
798 break;
799 case 'N':
800 decode_navh(buff, nav);
801 break;
802 case 'G':
803 decode_gnavh(buff, nav);
804 break;
805 case 'H':
806 decode_hnavh(buff, nav);
807 break;
808 case 'J':
809 decode_navh(buff, nav);
810 break; /* extension */
811 case 'L':
812 decode_navh(buff, nav);
813 break; /* extension */
814 }
815 if (strstr(label, "END OF HEADER")) {
816 return 1;
817 }
818
819 if (++i >= MAXPOSHEAD && *type == ' ') {
820 break; /* no RINEX file */
821 }
822 }
823 return 0;
824}
825/* decode observation epoch --------------------------------------------------*/
826static int decode_obsepoch(FILE* fp, char* buff, int ver, gtime_t* time, int* flag, int* sats) {
827 int i, j, n;
828 char satid[8] = "";
829
830 trace(NULL, 4, "decode_obsepoch: ver=%.2f\n", ver / 100.0);
831
832 if (ver <= 299) { /* ver.2 */
833 if ((n = (int)str2num(buff, 29, 3)) <= 0) {
834 return 0;
835 }
836
837 /* epoch flag: 3:new site,4:header info,5:external event */
838 *flag = (int)str2num(buff, 28, 1);
839
840 if (3 <= *flag && *flag <= 5) {
841 return n;
842 }
843
844 if (str2time(buff, 0, 26, time)) {
845 trace(NULL, 2, "rinex obs invalid epoch: epoch=%26.26s\n", buff);
846 return 0;
847 }
848 for (i = 0, j = 32; i < n; i++, j += 3) {
849 if (j >= 68) {
850 if (!fgets(buff, MAXRNXLEN, fp)) {
851 break;
852 }
853 j = 32;
854 }
855 if (i < MAXOBS) {
856 strncpy(satid, buff + j, 3);
857 sats[i] = satid2no(satid);
858 }
859 }
860 } else { /* ver.3 */
861 if ((n = (int)str2num(buff, 32, 3)) <= 0) {
862 return 0;
863 }
864
865 *flag = (int)str2num(buff, 31, 1);
866
867 if (3 <= *flag && *flag <= 5) {
868 return n;
869 }
870
871 if (buff[0] != '>' || str2time(buff, 1, 28, time)) {
872 trace(NULL, 2, "rinex obs invalid epoch: epoch=%29.29s\n", buff);
873 return 0;
874 }
875 }
876 trace(NULL, 4, "decode_obsepoch: time=%s flag=%d\n", time_str(*time, 3), *flag);
877 return n;
878}
879/* convert SNR value to 16 bit SNR -------------------------------------------*/
880static uint16_t snr_val(int sat, double snr) {
881 if (snr < 0.0 || snr >= 100.0) {
882 trace(NULL, 3, "invalid SNR value: sat=%d snr=%.3f\n", sat, snr);
883 return (uint16_t)0;
884 }
885 return (uint16_t)(snr / SNR_UNIT + 0.5);
886}
887/* decode observation data ---------------------------------------------------*/
888static int decode_obsdata(FILE* fp, char* buff, int ver, int mask, sigind_t* index, obsd_t* obs) {
889 sigind_t* ind;
890 double val[MAXOBSTYPE] = {0};
891 uint8_t lli[MAXOBSTYPE] = {0};
892 char satid[8] = "";
893 int i, j, n, m, stat = 1, p[MAXOBSTYPE], k[16], l[16];
894
895 trace(NULL, 4, "decode_obsdata: ver=%.2f\n", ver / 100.0);
896
897 if (ver > 299) { /* ver.3 */
898 sprintf(satid, "%.3s", buff);
899 obs->sat = (uint8_t)satid2no(satid);
900 }
901 if (!obs->sat) {
902 trace(NULL, 4, "decode_obsdata: unsupported sat sat=%s\n", satid);
903 stat = 0;
904 } else if (!(satsys(obs->sat, NULL) & mask)) {
905 stat = 0;
906 }
907 /* read observation data fields (use satsys_bd2 to route BDS-2 to BD2 index) */
908 switch (satsys_bd2(obs->sat, NULL)) {
909 case SYS_GLO:
910 ind = index + 1;
911 break;
912 case SYS_GAL:
913 ind = index + 2;
914 break;
915 case SYS_QZS:
916 ind = index + 3;
917 break;
918 case SYS_SBS:
919 ind = index + 4;
920 break;
921 case SYS_CMP:
922 ind = index + 5;
923 break;
924 case SYS_IRN:
925 ind = index + 6;
926 break;
927 case SYS_BD2:
928 ind = index + 7;
929 break;
930 default:
931 ind = index;
932 break;
933 }
934 for (i = 0, j = (ver <= 299) ? 0 : 3; i < ind->n; i++, j += 16) {
935 if (ver <= 299 && j >= 80) { /* ver.2 */
936 if (!fgets(buff, MAXRNXLEN, fp)) {
937 break;
938 }
939 j = 0;
940 }
941 if (stat) {
942 val[i] = str2num(buff, j, 14) + ind->shift[i];
943 lli[i] = (uint8_t)str2num(buff, j + 14, 1) & 3;
944 }
945 }
946 if (!stat) {
947 return 0;
948 }
949
950 for (i = 0; i < NFREQ + NEXOBS; i++) {
951 obs->P[i] = obs->L[i] = 0.0;
952 obs->D[i] = 0.0f;
953 obs->SNR[i] = obs->LLI[i] = obs->code[i] = 0;
954 }
955 /* assign position in observation data */
956 for (i = n = m = 0; i < ind->n; i++) {
957 p[i] = (ver <= 211) ? ind->idx[i] : ind->pos[i];
958
959 if (ind->type[i] == 0 && p[i] == 0) {
960 k[n++] = i; /* C1? index */
961 }
962 if (ind->type[i] == 0 && p[i] == 1) {
963 l[m++] = i; /* C2? index */
964 }
965 }
966 if (ver <= 211) {
967 /* if multiple codes (C1/P1,C2/P2), select higher priority */
968 if (n >= 2) {
969 if (val[k[0]] == 0.0 && val[k[1]] == 0.0) {
970 p[k[0]] = -1;
971 p[k[1]] = -1;
972 } else if (val[k[0]] != 0.0 && val[k[1]] == 0.0) {
973 p[k[0]] = 0;
974 p[k[1]] = -1;
975 } else if (val[k[0]] == 0.0 && val[k[1]] != 0.0) {
976 p[k[0]] = -1;
977 p[k[1]] = 0;
978 } else if (ind->pri[k[1]] > ind->pri[k[0]]) {
979 p[k[1]] = 0;
980 p[k[0]] = NEXOBS < 1 ? -1 : NFREQ;
981 } else {
982 p[k[0]] = 0;
983 p[k[1]] = NEXOBS < 1 ? -1 : NFREQ;
984 }
985 }
986 if (m >= 2) {
987 if (val[l[0]] == 0.0 && val[l[1]] == 0.0) {
988 p[l[0]] = -1;
989 p[l[1]] = -1;
990 } else if (val[l[0]] != 0.0 && val[l[1]] == 0.0) {
991 p[l[0]] = 1;
992 p[l[1]] = -1;
993 } else if (val[l[0]] == 0.0 && val[l[1]] != 0.0) {
994 p[l[0]] = -1;
995 p[l[1]] = 1;
996 } else if (ind->pri[l[1]] > ind->pri[l[0]]) {
997 p[l[1]] = 1;
998 p[l[0]] = NEXOBS < 2 ? -1 : NFREQ + 1;
999 } else {
1000 p[l[0]] = 1;
1001 p[l[1]] = NEXOBS < 2 ? -1 : NFREQ + 1;
1002 }
1003 }
1004 }
1005 /* save observation data */
1006 for (i = 0; i < ind->n; i++) {
1007 if (p[i] < 0 || val[i] == 0.0) {
1008 continue;
1009 }
1010 switch (ind->type[i]) {
1011 case 0:
1012 obs->P[p[i]] = val[i];
1013 obs->code[p[i]] = ind->code[i];
1014 break;
1015 case 1:
1016 obs->L[p[i]] = val[i];
1017 obs->LLI[p[i]] = lli[i];
1018 break;
1019 case 2:
1020 obs->D[p[i]] = (float)val[i];
1021 break;
1022 case 3:
1023 obs->SNR[p[i]] = snr_val(obs->sat, val[i]);
1024 break;
1025 }
1026 }
1027 trace(NULL, 4, "decode_obsdata: time=%s sat=%2d\n", time_str(obs->time, 0), obs->sat);
1028 return 1;
1029}
1030/* save cycle slips ----------------------------------------------------------*/
1031static void saveslips(uint8_t slips[][NFREQ + NEXOBS], obsd_t* data) {
1032 int i;
1033 for (i = 0; i < NFREQ + NEXOBS; i++) {
1034 if (data->LLI[i] & 1) {
1035 slips[data->sat - 1][i] |= LLI_SLIP;
1036 }
1037 }
1038}
1039/* restore cycle slips -------------------------------------------------------*/
1040static void restslips(uint8_t slips[][NFREQ + NEXOBS], obsd_t* data) {
1041 int i;
1042 for (i = 0; i < NFREQ + NEXOBS; i++) {
1043 if (slips[data->sat - 1][i] & 1) {
1044 data->LLI[i] |= LLI_SLIP;
1045 }
1046 slips[data->sat - 1][i] = 0;
1047 }
1048}
1049/* add observation data ------------------------------------------------------*/
1050static int addobsdata(obs_t* obs, const obsd_t* data) {
1051 obsd_t* obs_data;
1052
1053 if (obs->nmax <= obs->n) {
1054 if (obs->nmax <= 0) {
1055 obs->nmax = NINCOBS;
1056 } else {
1057 obs->nmax *= 2;
1058 }
1059 if (!(obs_data = (obsd_t*)realloc(obs->data, sizeof(obsd_t) * obs->nmax))) {
1060 trace(NULL, 1, "addobsdata: malloc error n=%dx%d\n", sizeof(obsd_t), obs->nmax);
1061 free(obs->data);
1062 obs->data = NULL;
1063 obs->n = obs->nmax = 0;
1064 return -1;
1065 }
1066 obs->data = obs_data;
1067 }
1068 obs->data[obs->n++] = *data;
1069 return 1;
1070}
1071/* set system mask -----------------------------------------------------------*/
1072static int set_sysmask(const char* opt) {
1073 const char* p;
1074 int mask = SYS_NONE;
1075
1076 if (!(p = strstr(opt, "-SYS="))) {
1077 return SYS_ALL;
1078 }
1079
1080 for (p += 5; *p && *p != ' '; p++) {
1081 switch (*p) {
1082 case 'G':
1083 mask |= SYS_GPS;
1084 break;
1085 case 'R':
1086 mask |= SYS_GLO;
1087 break;
1088 case 'E':
1089 mask |= SYS_GAL;
1090 break;
1091 case 'J':
1092 mask |= SYS_QZS;
1093 break;
1094 case 'C':
1095 mask |= SYS_CMP;
1096 break;
1097 case 'I':
1098 mask |= SYS_IRN;
1099 break;
1100 case 'S':
1101 mask |= SYS_SBS;
1102 break;
1103 }
1104 }
1105 return mask;
1106}
1107/* set signal index ----------------------------------------------------------*/
1108static void set_index(int ver, int sys, const char* opt, char tobs[MAXOBSTYPE][4], sigind_t* ind) {
1109 const char* p;
1110 char str[8], *optstr = "";
1111 double shift;
1112 int i, j, k, n;
1113
1114 for (i = n = 0; *tobs[i]; i++, n++) {
1115 ind->code[i] = obs2code(tobs[i] + 1);
1116 p = strchr(obscodes, tobs[i][0]);
1117 ind->type[i] = p ? (int)(p - obscodes) : 0;
1118 ind->idx[i] = code2freq_idx(sys, ind->code[i]);
1119 ind->pri[i] = getcodepri(sys, ind->code[i], opt);
1120 ind->pos[i] = -1;
1121 }
1122 /* parse phase shift options */
1123 switch (sys) {
1124 case SYS_GPS:
1125 optstr = "-GL%2s=%lf";
1126 break;
1127 case SYS_GLO:
1128 optstr = "-RL%2s=%lf";
1129 break;
1130 case SYS_GAL:
1131 optstr = "-EL%2s=%lf";
1132 break;
1133 case SYS_QZS:
1134 optstr = "-JL%2s=%lf";
1135 break;
1136 case SYS_SBS:
1137 optstr = "-SL%2s=%lf";
1138 break;
1139 case SYS_CMP:
1140 optstr = "-CL%2s=%lf";
1141 break;
1142 case SYS_IRN:
1143 optstr = "-IL%2s=%lf";
1144 break;
1145 }
1146 for (p = opt; p && (p = strchr(p, '-')); p++) {
1147 if (sscanf(p, optstr, str, &shift) < 2) {
1148 continue;
1149 }
1150 for (i = 0; i < n; i++) {
1151 if (strcmp(code2obs(ind->code[i]), str)) {
1152 continue;
1153 }
1154 ind->shift[i] = shift;
1155 trace(NULL, 2, "phase shift: sys=%2d tobs=%s shift=%.3f\n", sys, tobs[i], shift);
1156 }
1157 }
1158 /* assign index for highest priority code */
1159 for (i = 0; i < NFREQ; i++) {
1160 for (j = 0, k = -1; j < n; j++) {
1161 if (ind->idx[j] == i && ind->pri[j] && (k < 0 || ind->pri[j] > ind->pri[k])) {
1162 k = j;
1163 }
1164 }
1165 if (k < 0) {
1166 continue;
1167 }
1168
1169 for (j = 0; j < n; j++) {
1170 if (ind->code[j] == ind->code[k]) {
1171 ind->pos[j] = i;
1172 }
1173 }
1174 }
1175 /* assign index of extended observation data */
1176 for (i = 0; i < NEXOBS; i++) {
1177 for (j = 0; j < n; j++) {
1178 if (ind->code[j] && ind->pri[j] && ind->pos[j] < 0) {
1179 break;
1180 }
1181 }
1182 if (j >= n) {
1183 break;
1184 }
1185
1186 for (k = 0; k < n; k++) {
1187 if (ind->code[k] == ind->code[j]) {
1188 ind->pos[k] = NFREQ + i;
1189 }
1190 }
1191 }
1192 for (i = 0; i < n; i++) {
1193 if (!ind->code[i] || !ind->pri[i] || ind->pos[i] >= 0) {
1194 continue;
1195 }
1196 trace(NULL, 4, "reject obs type: sys=%2d, obs=%s\n", sys, tobs[i]);
1197 }
1198 ind->n = n;
1199
1200#if 0 /* for debug */
1201 for (i=0;i<n;i++) {
1202 trace(NULL,2,"set_index: sys=%2d,tobs=%s code=%2d pri=%2d idx=%d pos=%d shift=%5.2f\n",
1203 sys,tobs[i],ind->code[i],ind->pri[i],ind->idx[i],ind->pos[i],
1204 ind->shift[i]);
1205 }
1206#endif
1207}
1208/* read RINEX observation data body ------------------------------------------*/
1209static int readrnxobsb(FILE* fp, const char* opt, int ver, int* tsys, char tobs[][MAXOBSTYPE][4], int* flag,
1210 obsd_t* data, sta_t* sta) {
1211 gtime_t time = {0};
1212 sigind_t index[NUMSYS] = {{0}};
1213 char buff[MAXRNXLEN];
1214 int i = 0, n = 0, nsat = 0, nsys = NUMSYS, sats[MAXOBS] = {0}, mask;
1215
1216 /* set system mask */
1217 mask = set_sysmask(opt);
1218
1219 /* set signal index */
1220 if (nsys >= 1) {
1221 set_index(ver, SYS_GPS, opt, tobs[0], index);
1222 }
1223 if (nsys >= 2) {
1224 set_index(ver, SYS_GLO, opt, tobs[1], index + 1);
1225 }
1226 if (nsys >= 3) {
1227 set_index(ver, SYS_GAL, opt, tobs[2], index + 2);
1228 }
1229 if (nsys >= 4) {
1230 set_index(ver, SYS_QZS, opt, tobs[3], index + 3);
1231 }
1232 if (nsys >= 5) {
1233 set_index(ver, SYS_SBS, opt, tobs[4], index + 4);
1234 }
1235 if (nsys >= 6) {
1236 set_index(ver, SYS_CMP, opt, tobs[5], index + 5);
1237 }
1238 if (nsys >= 7) {
1239 set_index(ver, SYS_IRN, opt, tobs[6], index + 6);
1240 }
1241 if (nsys >= 8) {
1242 set_index(ver, SYS_BD2, opt, tobs[5], index + 7); /* BDS-2 */
1243 }
1244
1245 /* read record */
1246 while (fgets(buff, MAXRNXLEN, fp)) {
1247 /* decode observation epoch */
1248 if (i == 0) {
1249 if ((nsat = decode_obsepoch(fp, buff, ver, &time, flag, sats)) <= 0) {
1250 continue;
1251 }
1252 } else if ((*flag <= 2 || *flag == 6) && n < MAXOBS) {
1253 data[n].time = time;
1254 data[n].sat = (uint8_t)sats[i - 1];
1255
1256 /* decode RINEX observation data */
1257 if (decode_obsdata(fp, buff, ver, mask, index, data + n)) {
1258 n++;
1259 }
1260 } else if (*flag == 3 || *flag == 4) { /* new site or header info follows */
1261
1262 /* decode RINEX observation data file header */
1263 decode_obsh(fp, buff, ver, tsys, tobs, NULL, sta);
1264 }
1265 if (++i > nsat) {
1266 return n;
1267 }
1268 }
1269 return -1;
1270}
1271/* read RINEX observation data -----------------------------------------------*/
1272static int readrnxobs(FILE* fp, gtime_t ts, gtime_t te, double tint, const char* opt, int rcv, int ver, int* tsys,
1273 char tobs[][MAXOBSTYPE][4], obs_t* obs, sta_t* sta) {
1274 obsd_t* data;
1275 uint8_t slips[MAXSAT][NFREQ + NEXOBS] = {{0}};
1276 int i, n, flag = 0, stat = 0;
1277
1278 trace(NULL, 4, "readrnxobs: rcv=%d ver=%.2f tsys=%d\n", rcv, ver / 100.0, tsys);
1279
1280 if (!obs || rcv > MAXRCV) {
1281 return 0;
1282 }
1283
1284 if (!(data = (obsd_t*)malloc(sizeof(obsd_t) * MAXOBS))) {
1285 return 0;
1286 }
1287
1288 /* read RINEX observation data body */
1289 while ((n = readrnxobsb(fp, opt, ver, tsys, tobs, &flag, data, sta)) >= 0 && stat >= 0) {
1290 for (i = 0; i < n; i++) {
1291 /* UTC -> GPST */
1292 if (*tsys == TSYS_UTC) {
1293 data[i].time = utc2gpst(data[i].time);
1294 }
1295
1296 /* save cycle slip */
1297 saveslips(slips, data + i);
1298 }
1299 /* screen data by time */
1300 if (n > 0 && !screent(data[0].time, ts, te, tint)) {
1301 continue;
1302 }
1303
1304 for (i = 0; i < n; i++) {
1305 /* restore cycle slip */
1306 restslips(slips, data + i);
1307
1308 data[i].rcv = (uint8_t)rcv;
1309
1310 /* save obs data */
1311 if ((stat = addobsdata(obs, data + i)) < 0) {
1312 break;
1313 }
1314 }
1315 }
1316 trace(NULL, 4, "readrnxobs: nobs=%d stat=%d\n", obs->n, stat);
1317
1318 free(data);
1319
1320 return stat;
1321}
1322/* decode GPS/QZSS CNAV/CNV2 ephemeris --------------------------------------*/
1323static int decode_eph_cnav(int ver, int sat, gtime_t toc, const double* data, int ndata, int v4type, eph_t* eph) {
1324 eph_t eph0 = {0};
1325 int sys;
1326
1327 trace(NULL, 4, "decode_eph_cnav: ver=%.2f sat=%2d v4type=%d ndata=%d\n", ver / 100.0, sat, v4type, ndata);
1328
1329 sys = satsys(sat, NULL);
1330
1331 if (!(sys & (SYS_GPS | SYS_QZS))) {
1332 trace(NULL, 3, "cnav ephemeris error: invalid satellite sat=%2d\n", sat);
1333 return 0;
1334 }
1335 *eph = eph0;
1336
1337 eph->sat = sat;
1338 eph->toc = toc;
1339
1340 /* clock parameters (orbit 0) */
1341 eph->f0 = data[0];
1342 eph->f1 = data[1];
1343 eph->f2 = data[2];
1344
1345 /* Keplerian elements (orbits 1-4, same layout as LNAV data[4..18]) */
1346 eph->Adot = data[3]; /* A_DOT (CNAV-specific) */
1347 eph->crs = data[4];
1348 eph->deln = data[5];
1349 eph->M0 = data[6];
1350 eph->cuc = data[7];
1351 eph->e = data[8];
1352 eph->cus = data[9];
1353 eph->A = SQR(data[10]);
1354 eph->toes = data[11]; /* t_op */
1355 eph->cic = data[12];
1356 eph->OMG0 = data[13];
1357 eph->cis = data[14];
1358 eph->i0 = data[15];
1359 eph->crc = data[16];
1360 eph->omg = data[17];
1361 eph->OMGd = data[18];
1362
1363 /* orbit 5 */
1364 eph->idot = data[19];
1365 eph->ndot = data[20]; /* Delta_n0_dot (CNAV-specific) */
1366
1367 /* orbit 6: URAI_ED, SV health, TGD, URAI_NED2 */
1368 eph->sva = (int)data[23]; /* URAI_ED (already an index) */
1369 eph->svh = (int)data[24]; /* SV health */
1370 eph->tgd[0] = data[25]; /* TGD */
1371
1372 /* orbit 7: ISC corrections */
1373 eph->tgd[1] = data[27]; /* ISC_L1CA */
1374 eph->tgd[2] = data[28]; /* ISC_L2C */
1375 eph->tgd[3] = data[29]; /* ISC_L5I5 */
1376 eph->tgd[4] = data[30]; /* ISC_L5Q5 */
1377
1378 if (v4type == 2 || v4type == 42) {
1379 /* CNAV: 8 orbits */
1380 eph->ttr = adjweek(gpst2time((int)data[32], data[31]), toc);
1381 eph->week = (int)data[32]; /* wn_op */
1382 eph->type = 1; /* CNAV */
1383 } else {
1384 /* CNV2: 9 orbits */
1385 eph->tgd[5] = data[31]; /* ISC_L1Cd */
1386 eph->ttr = adjweek(gpst2time((int)data[36], data[35]), toc);
1387 eph->week = (int)data[36]; /* wn_op */
1388 eph->type = 2; /* CNV2 */
1389 }
1390 eph->toe = adjweek(gpst2time(eph->week, eph->toes), toc);
1391
1392 /* CNAV has no traditional IODE — derive pseudo-IODE from toes for
1393 * uniqeph() deduplication (sat + iode + type must be unique) */
1394 eph->iode = (int)(eph->toes / 300) % 256;
1395 eph->iodc = eph->iode;
1396 eph->fit = 0.0;
1397
1398 return 1;
1399}
1400/* decode BDS CNAV-1/2/3 ephemeris ------------------------------------------*/
1401static int decode_eph_bds_cnav(int ver, int sat, gtime_t toc, const double* data, int ndata, int v4type, eph_t* eph) {
1402 eph_t eph0 = {0};
1403 int sys, prn;
1404
1405 trace(NULL, 4, "decode_eph_bds_cnav: ver=%.2f sat=%2d v4type=%d ndata=%d\n", ver / 100.0, sat, v4type, ndata);
1406
1407 sys = satsys(sat, &prn);
1408
1409 if (sys != SYS_CMP) {
1410 trace(NULL, 3, "bds cnav ephemeris error: invalid satellite sat=%2d\n", sat);
1411 return 0;
1412 }
1413 *eph = eph0;
1414
1415 eph->sat = sat;
1416 eph->toc = bdt2gpst(toc); /* BDT -> GPST */
1417
1418 /* clock parameters (orbit 0) */
1419 eph->f0 = data[0];
1420 eph->f1 = data[1];
1421 eph->f2 = data[2];
1422
1423 /* Keplerian elements (orbits 1-4, same layout as CNAV) */
1424 eph->Adot = data[3]; /* A_DOT */
1425 eph->crs = data[4];
1426 eph->deln = data[5];
1427 eph->M0 = data[6];
1428 eph->cuc = data[7];
1429 eph->e = data[8];
1430 eph->cus = data[9];
1431 eph->A = SQR(data[10]);
1432 eph->toes = data[11]; /* ToE (BDT) */
1433 eph->cic = data[12];
1434 eph->OMG0 = data[13];
1435 eph->cis = data[14];
1436 eph->i0 = data[15];
1437 eph->crc = data[16];
1438 eph->omg = data[17];
1439 eph->OMGd = data[18];
1440
1441 /* orbit 5 */
1442 eph->idot = data[19];
1443 eph->ndot = data[20]; /* Delta_n0_dot */
1444
1445 /* derive BDT week from Toc (no week field in BDS CNAV records) */
1446 {
1447 int bdt_week;
1448 time2bdt(toc, &bdt_week);
1449
1450 if (v4type == 55) {
1451 /* BDS CNV3 (Table A24): 8 orbits
1452 * orbit 6: SISAI_oe, SISAI_ocb, SISAI_oc1, SISAI_oc2
1453 * orbit 7: SISMAI, Health, IntFlags, TGD_B2bI
1454 * orbit 8: t_tm */
1455 eph->sva = (int)data[27]; /* SISMAI (index) */
1456 eph->svh = (int)data[28]; /* Health */
1457 eph->tgd[1] = data[30]; /* TGD_B2bI (B2 group delay) */
1458 eph->week = bdt_week;
1459 eph->ttr = bdt2gpst(bdt2time(bdt_week, data[31])); /* t_tm */
1460 /* CNV3 has no IODE — derive pseudo-IODE from toes for
1461 * uniqeph() deduplication (sat + iode + type must be unique) */
1462 eph->iode = (int)(eph->toes / 300) % 256;
1463 eph->iodc = eph->iode;
1464 eph->type = 4; /* CNAV-3 */
1465 } else {
1466 /* BDS CNV1 (Table A22) / CNV2 (Table A23): 9 orbits
1467 * orbit 6: SISAI_oe, SISAI_ocb, SISAI_oc1, SISAI_oc2
1468 * orbit 7: ISC_B1Cd/spare, spare/ISC_B2ad, TGD_B1Cp, TGD_B2ap
1469 * orbit 8: SISMAI, Health, IntFlags, IODC
1470 * orbit 9: t_tm, spare(x2), IODE */
1471 eph->sva = (int)data[31]; /* SISMAI (index) */
1472 eph->svh = (int)data[32]; /* Health */
1473 eph->tgd[2] = data[29]; /* TGD_B1Cp */
1474 eph->tgd[3] = data[30]; /* TGD_B2ap */
1475 if (v4type == 53) {
1476 eph->tgd[4] = data[27]; /* ISC_B1Cd (CNV1) */
1477 } else {
1478 eph->tgd[5] = data[28]; /* ISC_B2ad (CNV2) */
1479 }
1480 eph->iodc = (int)data[34];
1481 eph->week = bdt_week;
1482 eph->ttr = bdt2gpst(bdt2time(bdt_week, data[35])); /* t_tm */
1483 eph->iode = (int)data[38]; /* IODE */
1484 eph->type = (v4type == 53) ? 2 : 3; /* CNAV-1 or CNAV-2 */
1485 }
1486 }
1487 eph->toe = bdt2gpst(bdt2time(eph->week, eph->toes));
1488 eph->toe = adjweek(eph->toe, eph->toc);
1489 eph->ttr = adjweek(eph->ttr, eph->toc);
1490
1491 return 1;
1492}
1493/* decode ephemeris ----------------------------------------------------------*/
1494static int decode_eph(int ver, int sat, gtime_t toc, const double* data, eph_t* eph) {
1495 eph_t eph0 = {0};
1496 int sys, prn;
1497
1498 trace(NULL, 4, "decode_eph: ver=%.2f sat=%2d\n", ver / 100.0, sat);
1499
1500 sys = satsys(sat, &prn);
1501
1502 if (!(sys & (SYS_GPS | SYS_GAL | SYS_QZS | SYS_CMP | SYS_IRN))) {
1503 trace(NULL, 3, "ephemeris error: invalid satellite sat=%2d\n", sat);
1504 return 0;
1505 }
1506 *eph = eph0;
1507
1508 eph->sat = sat;
1509 eph->toc = toc;
1510
1511 eph->f0 = data[0];
1512 eph->f1 = data[1];
1513 eph->f2 = data[2];
1514
1515 eph->A = SQR(data[10]);
1516 eph->e = data[8];
1517 eph->i0 = data[15];
1518 eph->OMG0 = data[13];
1519 eph->omg = data[17];
1520 eph->M0 = data[6];
1521 eph->deln = data[5];
1522 eph->OMGd = data[18];
1523 eph->idot = data[19];
1524 eph->crc = data[16];
1525 eph->crs = data[4];
1526 eph->cuc = data[7];
1527 eph->cus = data[9];
1528 eph->cic = data[12];
1529 eph->cis = data[14];
1530
1531 if (sys == SYS_GPS || sys == SYS_QZS) {
1532 eph->iode = (int)data[3]; /* IODE */
1533 eph->iodc = (int)data[26]; /* IODC */
1534 eph->toes = data[11]; /* Toe (s) in GPS week */
1535 eph->week = (int)data[21]; /* GPS week */
1536 eph->toe = adjweek(gpst2time(eph->week, data[11]), toc);
1537 eph->ttr = adjweek(gpst2time(eph->week, data[27]), toc);
1538
1539 eph->code = (int)data[20]; /* GPS: codes on L2 ch */
1540 eph->svh = (int)data[24]; /* SV health */
1541 eph->sva = uraindex(data[23]); /* URA index (m->index) */
1542 eph->flag = (int)data[22]; /* GPS: L2 P data flag */
1543
1544 eph->tgd[0] = data[25]; /* TGD */
1545 if (sys == SYS_GPS) {
1546 eph->fit = data[28]; /* fit interval (h) */
1547 } else {
1548 eph->fit = data[28] == 0.0 ? 1.0 : 2.0; /* fit interval (0:1h,1:>2h) */
1549 }
1550 eph->type = 0; /* ephemeris type = LNAV */
1551 } else if (sys == SYS_GAL) { /* GAL ver.3 */
1552 eph->iode = (int)data[3]; /* IODnav */
1553 eph->toes = data[11]; /* Toe (s) in Galileo week */
1554 eph->week = (int)data[21]; /* Galileo week = GPS week */
1555 eph->toe = adjweek(gpst2time(eph->week, data[11]), toc);
1556 eph->ttr = adjweek(gpst2time(eph->week, data[27]), toc);
1557
1558 eph->code = (int)data[20]; /* data sources */
1559 /* bit 0 set: I/NAV E1-B */
1560 /* bit 1 set: F/NAV E5a-I */
1561 /* bit 2 set: I/NAV E5b-I */
1562 /* bit 8 set: af0-af2 toc are for E5a.E1 */
1563 /* bit 9 set: af0-af2 toc are for E5b.E1 */
1564 eph->svh = (int)data[24]; /* sv health */
1565 /* bit 0: E1B DVS */
1566 /* bit 1-2: E1B HS */
1567 /* bit 3: E5a DVS */
1568 /* bit 4-5: E5a HS */
1569 /* bit 6: E5b DVS */
1570 /* bit 7-8: E5b HS */
1571 eph->sva = sisa_index(data[23]); /* sisa (m->index) */
1572
1573 eph->tgd[0] = data[25]; /* BGD E5a/E1 */
1574 eph->tgd[1] = data[26]; /* BGD E5b/E1 */
1575 eph->type = (eph->code & 5) ? 0 : 1; /* ephemeris type = I/NAV or F/NAV */
1576 } else if (sys == SYS_CMP) { /* BeiDou v.3.02 */
1577 eph->toc = bdt2gpst(eph->toc); /* bdt -> gpst */
1578 eph->iode = (int)data[3]; /* AODE */
1579 eph->iodc = (int)data[28]; /* AODC */
1580 eph->toes = data[11]; /* Toe (s) in BDT week */
1581 eph->week = (int)data[21]; /* bdt week */
1582 eph->toe = bdt2gpst(bdt2time(eph->week, data[11])); /* BDT -> GPST */
1583 eph->ttr = bdt2gpst(bdt2time(eph->week, data[27])); /* BDT -> GPST */
1584 eph->toe = adjweek(eph->toe, toc);
1585 eph->ttr = adjweek(eph->ttr, toc);
1586
1587 eph->svh = (int)data[24]; /* satH1 */
1588 eph->sva = uraindex(data[23]); /* URA index (m->index) */
1589
1590 eph->tgd[0] = data[25]; /* TGD1 B1/B3 */
1591 eph->tgd[1] = data[26]; /* TGD2 B2/B3 */
1592 eph->type = (prn >= 6 && prn <= 58) ? 0 : 1; /* ephemeris type = D1 or D2 */
1593 } else if (sys == SYS_IRN) { /* IRNSS v.3.03 */
1594 eph->iode = (int)data[3]; /* IODEC */
1595 eph->toes = data[11]; /* Toe (s) in IRNSS week */
1596 eph->week = (int)data[21]; /* IRNSS week */
1597 eph->toe = adjweek(gpst2time(eph->week, data[11]), toc);
1598 eph->ttr = adjweek(gpst2time(eph->week, data[27]), toc);
1599 eph->svh = (int)data[24]; /* SV health */
1600 eph->sva = uraindex(data[23]); /* URA index (m->index) */
1601 eph->tgd[0] = data[25]; /* TGD */
1602 eph->type = 0; /* ephemeris type = LNAV */
1603 }
1604 if (eph->iode < 0 || 1023 < eph->iode) {
1605 trace(NULL, 2, "rinex nav invalid: sat=%2d iode=%d\n", sat, eph->iode);
1606 }
1607 if (eph->iodc < 0 || 1023 < eph->iodc) {
1608 trace(NULL, 2, "rinex nav invalid: sat=%2d iodc=%d\n", sat, eph->iodc);
1609 }
1610 return 1;
1611}
1612/* decode GLONASS ephemeris --------------------------------------------------*/
1613static int decode_geph(int ver, int sat, gtime_t toc, double* data, geph_t* geph) {
1614 geph_t geph0 = {0};
1615 gtime_t tof;
1616 double tow, tod;
1617 int week, dow;
1618
1619 trace(NULL, 4, "decode_geph: ver=%.2f sat=%2d\n", ver / 100.0, sat);
1620
1621 if (satsys(sat, NULL) != SYS_GLO) {
1622 trace(NULL, 3, "glonass ephemeris error: invalid satellite sat=%2d\n", sat);
1623 return 0;
1624 }
1625 *geph = geph0;
1626
1627 geph->sat = sat;
1628
1629 /* Toc rounded by 15 min in utc */
1630 tow = time2gpst(toc, &week);
1631 toc = gpst2time(week, floor((tow + 450.0) / 900.0) * 900);
1632 dow = (int)floor(tow / 86400.0);
1633
1634 /* time of frame in UTC */
1635 tod = (ver <= 299) ? data[2] : fmod(data[2], 86400.0); /* Tod (v.2), Tow (v.3) in UTC */
1636 tof = gpst2time(week, tod + dow * 86400.0);
1637 tof = adjday(tof, toc);
1638
1639 geph->toe = utc2gpst(toc); /* Toc (GPST) */
1640 geph->tof = utc2gpst(tof); /* Tof (GPST) */
1641
1642 /* IODE = Tb (7bit), Tb =index of UTC+3H within current day */
1643 geph->iode = (int)(fmod(tow + 10800.0, 86400.0) / 900.0 + 0.5);
1644
1645 geph->taun = -data[0]; /* -taun */
1646 geph->gamn = data[1]; /* +gamman */
1647
1648 geph->pos[0] = data[3] * 1E3;
1649 geph->pos[1] = data[7] * 1E3;
1650 geph->pos[2] = data[11] * 1E3;
1651 geph->vel[0] = data[4] * 1E3;
1652 geph->vel[1] = data[8] * 1E3;
1653 geph->vel[2] = data[12] * 1E3;
1654 geph->acc[0] = data[5] * 1E3;
1655 geph->acc[1] = data[9] * 1E3;
1656 geph->acc[2] = data[13] * 1E3;
1657
1658 geph->svh = (int)data[6]; /* MSB of B_n */
1659 geph->frq = (int)data[10];
1660 geph->age = (int)data[14];
1661
1662 if (ver >= 305) { /* RINEX 3.05 */
1663 geph->flags = (int)data[15]; /* status flags */
1664 /*geph->dtaun=data[16];*/ /* delta_TAU_n (s) (L1/L2 GD difference) */
1665 geph->sva = (int)data[17]; /* F_T (user range accuracy index) */
1666 geph->svh |= ((int)data[18]) << 1; /* extended SVH */
1667 }
1668 /* some receiver output >128 for minus frequency number */
1669 if (geph->frq > 128) {
1670 geph->frq -= 256;
1671 }
1672
1673 if (geph->frq < MINFREQ_GLO || MAXFREQ_GLO < geph->frq) {
1674 trace(NULL, 2, "rinex gnav invalid freq: sat=%2d fn=%d\n", sat, geph->frq);
1675 }
1676 return 1;
1677}
1678/* decode GEO ephemeris ------------------------------------------------------*/
1679static int decode_seph(int ver, int sat, gtime_t toc, double* data, seph_t* seph) {
1680 seph_t seph0 = {0};
1681 int week;
1682
1683 trace(NULL, 4, "decode_seph: ver=%.2f sat=%2d\n", ver / 100.0, sat);
1684
1685 if (satsys(sat, NULL) != SYS_SBS) {
1686 trace(NULL, 3, "geo ephemeris error: invalid satellite sat=%2d\n", sat);
1687 return 0;
1688 }
1689 *seph = seph0;
1690
1691 seph->sat = sat;
1692 seph->t0 = toc;
1693
1694 time2gpst(toc, &week);
1695 seph->tof = adjweek(gpst2time(week, data[2]), toc);
1696
1697 seph->af0 = data[0];
1698 seph->af1 = data[1];
1699
1700 seph->pos[0] = data[3] * 1E3;
1701 seph->pos[1] = data[7] * 1E3;
1702 seph->pos[2] = data[11] * 1E3;
1703 seph->vel[0] = data[4] * 1E3;
1704 seph->vel[1] = data[8] * 1E3;
1705 seph->vel[2] = data[12] * 1E3;
1706 seph->acc[0] = data[5] * 1E3;
1707 seph->acc[1] = data[9] * 1E3;
1708 seph->acc[2] = data[13] * 1E3;
1709
1710 seph->svh = (int)data[6];
1711 seph->sva = uraindex(data[10]);
1712
1713 return 1;
1714}
1715/* read RINEX navigation data body -------------------------------------------*/
1716static int readrnxnavb(FILE* fp, const char* opt, int ver, int sys, int* type, eph_t* eph, geph_t* geph, seph_t* seph,
1717 nav_t* nav) {
1718 /* Note, the definition of v4type for RINEX version 4 is as follows. -------
1719 *
1720 * (1) Navigation message
1721 * ------- ----------- ----------- ----------- ----------- -----------
1722 * EPH Gxx LNAV 1 CNAV 2 CNV2 3
1723 * EPH Sxx SBAS 11
1724 * EPH Rxx FDMA 21 L1OC 22(*) L3OC 23(*)
1725 * EPH Exx INAV 31 FNAV 32
1726 * EPH Jxx LNAV 41 CNAV 42 CNV2 43
1727 * EPH Cxx D1 51 D2 52 CNV1 53 CNV2 54 CNV3 55
1728 * EPH Ixx L1NV 61
1729 * ------- ----------- ----------- ----------- ----------- -----------
1730 *
1731 * (2) System Time and UTC Offset
1732 * ------- ----------- -----------
1733 * STO Gxx LNAV 101 CNVX 102
1734 * STO Rxx FDMA 121
1735 * STO Exx IFNV 131
1736 * STO Jxx LNAV 141 CNVX 142
1737 * STO Cxx D1D2 151 CNVX 152
1738 * STO Ixx LNAV 161
1739 * ------- ----------- -----------
1740 *
1741 * (3) Earth Orientation Parameters
1742 * ------- -----------
1743 * STO Gxx CNVX 201(*)
1744 * STO Jxx CNVX 241(*)
1745 * STO Cxx CNVX 251(*)
1746 * STO Ixx LNAV 261(*)
1747 * ------- -----------
1748 *
1749 * (4) Ionosphere Model Parameters
1750 * ------- ----------- -----------
1751 * ION Gxx LNAV 301 CNVX 302
1752 * ION Exx IFNV 331
1753 * ION Jxx LNAV 341 CNVX 342
1754 * ION Cxx D1D2 351 CNVX 352
1755 * ION Ixx LNAV 361
1756 * ------- ----------- -----------
1757 *
1758 * (*) Not supported.
1759 *------------------------------------------------------------------------*/
1760
1761 gtime_t toc;
1762 double data[64];
1763 int j, prn, mask;
1764 int i = 0, sat = 0, sp = 3, v4type = 0;
1765 char buff[MAXRNXLEN], *p;
1766 char id[8] = "", stotype[8] = "";
1767
1768 trace(NULL, 4, "readrnxnavb: ver=%.2f sys=%d\n", ver / 100.0, sys);
1769
1770 /* set system mask */
1771 mask = set_sysmask(opt);
1772
1773 while (fgets(buff, MAXRNXLEN, fp)) {
1774 if (ver >= 400) {
1775 if (buff[0] == '>') {
1776 buff[7] = buff[8] = 'x';
1777 if (!strncmp(buff + 2, "EPH Gxx LNAV", 12)) {
1778 v4type = 1;
1779 } else if (!strncmp(buff + 2, "EPH Sxx SBAS", 12)) {
1780 v4type = 11;
1781 } else if (!strncmp(buff + 2, "EPH Rxx FDMA", 12)) {
1782 v4type = 21;
1783 } else if (!strncmp(buff + 2, "EPH Exx INAV", 12)) {
1784 v4type = 31;
1785 } else if (!strncmp(buff + 2, "EPH Exx FNAV", 12)) {
1786 v4type = 32;
1787 } else if (!strncmp(buff + 2, "EPH Jxx LNAV", 12)) {
1788 v4type = 41;
1789 } else if (!strncmp(buff + 2, "EPH Cxx D1 ", 12)) {
1790 v4type = 51;
1791 } else if (!strncmp(buff + 2, "EPH Cxx D2 ", 12)) {
1792 v4type = 52;
1793 } else if (!strncmp(buff + 2, "EPH Gxx CNAV", 12)) {
1794 v4type = 2;
1795 } else if (!strncmp(buff + 2, "EPH Gxx CNV2", 12)) {
1796 v4type = 3;
1797 } else if (!strncmp(buff + 2, "EPH Jxx CNAV", 12)) {
1798 v4type = 42;
1799 } else if (!strncmp(buff + 2, "EPH Jxx CNV2", 12)) {
1800 v4type = 43;
1801 } else if (!strncmp(buff + 2, "EPH Cxx CNV1", 12)) {
1802 v4type = 53;
1803 } else if (!strncmp(buff + 2, "EPH Cxx CNV2", 12)) {
1804 v4type = 54;
1805 } else if (!strncmp(buff + 2, "EPH Cxx CNV3", 12)) {
1806 v4type = 55;
1807 } else if (!strncmp(buff + 2, "EPH Ixx LNAV", 12)) {
1808 v4type = 61;
1809 } else if (!strncmp(buff + 2, "STO Gxx LNAV", 12)) {
1810 v4type = 101;
1811 } else if (!strncmp(buff + 2, "STO Rxx FDMA", 12)) {
1812 v4type = 121;
1813 } else if (!strncmp(buff + 2, "STO Exx IFNV", 12)) {
1814 v4type = 131;
1815 } else if (!strncmp(buff + 2, "STO Jxx LNAV", 12)) {
1816 v4type = 141;
1817 } else if (!strncmp(buff + 2, "STO Cxx D1D2", 12)) {
1818 v4type = 151;
1819 } else if (!strncmp(buff + 2, "STO Ixx LNAV", 12)) {
1820 v4type = 161;
1821 } else if (!strncmp(buff + 2, "STO Gxx CNVX", 12)) {
1822 v4type = 102; /* GPS CNAV STO (same data as LNAV STO) */
1823 } else if (!strncmp(buff + 2, "STO Jxx CNVX", 12)) {
1824 v4type = 142;
1825 } else if (!strncmp(buff + 2, "STO Cxx CNVX", 12)) {
1826 v4type = 152;
1827 } else if (!strncmp(buff + 2, "ION Gxx LNAV", 12)) {
1828 v4type = 301;
1829 } else if (!strncmp(buff + 2, "ION Exx IFNV", 12)) {
1830 v4type = 331;
1831 } else if (!strncmp(buff + 2, "ION Jxx LNAV", 12)) {
1832 v4type = 341;
1833 } else if (!strncmp(buff + 2, "ION Cxx D1D2", 12)) {
1834 v4type = 351;
1835 } else if (!strncmp(buff + 2, "ION Ixx LNAV", 12)) {
1836 v4type = 361;
1837 } else if (!strncmp(buff + 2, "ION Gxx CNVX", 12)) {
1838 v4type = 302; /* GPS CNAV Klobuchar ION */
1839 } else if (!strncmp(buff + 2, "ION Jxx CNVX", 12)) {
1840 v4type = 342;
1841 } else if (!strncmp(buff + 2, "ION Cxx CNVX", 12)) {
1842 v4type = 352;
1843 } else {
1844 v4type = 0;
1845 }
1846 continue;
1847 }
1848 if (v4type == 0) {
1849 continue;
1850 /* EPH */
1851 } else if (v4type < 100) {
1852 ; /* through */
1853 /* STO */
1854 } else if (v4type < 200) {
1855 if (!strlen(stotype)) {
1856 strncpy(stotype, buff + 24, 4);
1857 } else {
1858 for (j = 0, p = buff + 4; j < 4; j++, p += 19) {
1859 data[j] = str2num(p, 0, 19);
1860 }
1861
1862 /* System Time Offset (STO) Message */
1863 if (!strncmp(stotype, "GPUT", 4)) {
1864 for (j = 0; j < 4; j++) {
1865 nav->utc_gps[j] = data[j];
1866 }
1867 }
1868 if (!strncmp(stotype, "GLUT", 4)) {
1869 nav->utc_glo[0] = data[0]; /* tau_C */
1870 }
1871 if (!strncmp(stotype, "GLGP", 4)) {
1872 nav->utc_glo[0] = data[1]; /* tau_GPS */
1873 }
1874 if (!strncmp(stotype, "GAUT", 4)) {
1875 for (j = 0; j < 4; j++) {
1876 nav->utc_gal[j] = data[j];
1877 }
1878 }
1879 if (!strncmp(stotype, "QZUT", 4)) {
1880 for (j = 0; j < 4; j++) {
1881 nav->utc_qzs[j] = data[j];
1882 }
1883 }
1884 if (!strncmp(stotype, "BDUT", 4)) {
1885 for (j = 0; j < 4; j++) {
1886 nav->utc_cmp[j] = data[j];
1887 }
1888 }
1889 if (!strncmp(stotype, "IRUT", 4)) {
1890 for (j = 0; j < 4; j++) {
1891 nav->utc_irn[j] = data[j];
1892 }
1893 }
1894 strcpy(stotype, "");
1895 }
1896 continue;
1897 }
1898 /* ION */
1899 else if (v4type < 400) {
1900 if (i == 0) {
1901 for (j = 0, p = buff + 23; j < 3; j++, p += 19) {
1902 data[i++] = str2num(p, 0, 19);
1903 }
1904 } else {
1905 for (j = 0, p = buff + 4; j < 4; j++, p += 19) {
1906 data[i++] = str2num(p, 0, 19);
1907 }
1908 }
1909
1910 /* Ionosphere (ION) Klobuchar Model Message */
1911 if ((v4type == 301 || v4type == 302) && i >= 8) {
1912 for (j = 0; j < 8; j++) {
1913 nav->ion_gps[j] = data[j];
1914 }
1915 i = 0;
1916 }
1917 if ((v4type == 341 || v4type == 342) && i >= 8) {
1918 for (j = 0; j < 8; j++) {
1919 nav->ion_qzs[j] = data[j];
1920 }
1921 i = 0;
1922 }
1923 if ((v4type == 351 || v4type == 352) && i >= 8) {
1924 for (j = 0; j < 8; j++) {
1925 nav->ion_cmp[j] = data[j];
1926 }
1927 i = 0;
1928 }
1929 if (v4type == 361 && i >= 8) {
1930 for (j = 0; j < 8; j++) {
1931 nav->ion_irn[j] = data[j];
1932 }
1933 i = 0;
1934 }
1935 /* Ionosphere (ION) NEQUICK-G Model Message */
1936 if (v4type == 331 && i >= 4) {
1937 for (j = 0; j < 4; j++) {
1938 nav->ion_gal[j] = data[j];
1939 }
1940 i = 0;
1941 }
1942 continue;
1943 }
1944 }
1945 /* EPH */
1946 if (i == 0) {
1947 /* decode satellite field */
1948 if (ver >= 300 || sys == SYS_GAL || sys == SYS_QZS) { /* ver.3 or GAL/QZS */
1949 sprintf(id, "%.3s", buff);
1950 sat = satid2no(id);
1951 sp = 4;
1952 if (ver >= 300) {
1953 sys = satsys(sat, NULL);
1954 if (!sys) {
1955 if (id[0] == 'S') {
1956 sys = SYS_SBS;
1957 } else if (id[0] == 'R') {
1958 sys = SYS_GLO;
1959 } else {
1960 sys = SYS_GPS;
1961 }
1962 }
1963 }
1964 } else {
1965 prn = (int)str2num(buff, 0, 2);
1966
1967 if (sys == SYS_SBS) {
1968 sat = satno(SYS_SBS, prn + 100);
1969 } else if (sys == SYS_GLO) {
1970 sat = satno(SYS_GLO, prn);
1971 } else if (93 <= prn && prn <= 99) { /* extension: J193..J199 */
1972 sat = satno(SYS_QZS, prn + 100);
1973 } else {
1974 sat = satno(SYS_GPS, prn);
1975 }
1976 }
1977 /* decode Toc field */
1978 if (str2time(buff + sp, 0, 19, &toc)) {
1979 trace(NULL, 2, "rinex nav toc error: %23.23s\n", buff);
1980 return 0;
1981 }
1982 /* decode data fields */
1983 for (j = 0, p = buff + sp + 19; j < 3; j++, p += 19) {
1984 data[i++] = str2num(p, 0, 19);
1985 }
1986 } else {
1987 /* decode data fields */
1988 for (j = 0, p = buff + sp; j < 4; j++, p += 19) {
1989 data[i++] = str2num(p, 0, 19);
1990 }
1991 /* decode ephemeris */
1992 if (sys == SYS_GLO && i >= ((ver >= 305) ? 19 : 15)) {
1993 if (!(mask & sys)) {
1994 return 0;
1995 }
1996 *type = 1;
1997 return decode_geph(ver, sat, toc, data, geph);
1998 } else if (sys == SYS_SBS && i >= 15) {
1999 if (!(mask & sys)) {
2000 return 0;
2001 }
2002 *type = 2;
2003 return decode_seph(ver, sat, toc, data, seph);
2004 } else {
2005 /* v4type-aware data field count threshold */
2006 int ndata_eph = 31; /* default: 7 orbits (LNAV/INAV/FNAV/D1/D2) */
2007 if (v4type == 2 || v4type == 42 || v4type == 55) {
2008 ndata_eph = 35; /* 8 orbits (GPS/QZSS CNAV, BDS CNV3) */
2009 } else if (v4type == 3 || v4type == 43 || v4type == 53 || v4type == 54) {
2010 ndata_eph = 39; /* 9 orbits (GPS/QZSS CNV2, BDS CNV1/2) */
2011 }
2012 if (i >= ndata_eph) {
2013 if (!(mask & sys)) {
2014 return 0;
2015 }
2016 *type = 0;
2017 if (v4type == 2 || v4type == 3 || v4type == 42 || v4type == 43) {
2018 return decode_eph_cnav(ver, sat, toc, data, i, v4type, eph);
2019 } else if (v4type >= 53 && v4type <= 55) {
2020 return decode_eph_bds_cnav(ver, sat, toc, data, i, v4type, eph);
2021 } else {
2022 return decode_eph(ver, sat, toc, data, eph);
2023 }
2024 }
2025 }
2026 }
2027 }
2028 return -1;
2029}
2030/* add ephemeris to navigation data ------------------------------------------*/
2031static int add_eph(nav_t* nav, const eph_t* eph) {
2032 eph_t* nav_eph;
2033
2034 if (nav->nmax <= nav->n) {
2035 nav->nmax += 1024;
2036 if (!(nav_eph = (eph_t*)realloc(nav->eph, sizeof(eph_t) * nav->nmax))) {
2037 trace(NULL, 1, "decode_eph malloc error: n=%d\n", nav->nmax);
2038 free(nav->eph);
2039 nav->eph = NULL;
2040 nav->n = nav->nmax = 0;
2041 return 0;
2042 }
2043 nav->eph = nav_eph;
2044 }
2045 nav->eph[nav->n++] = *eph;
2046 return 1;
2047}
2048static int add_geph(nav_t* nav, const geph_t* geph) {
2049 geph_t* nav_geph;
2050
2051 if (nav->ngmax <= nav->ng) {
2052 nav->ngmax += 1024;
2053 if (!(nav_geph = (geph_t*)realloc(nav->geph, sizeof(geph_t) * nav->ngmax))) {
2054 trace(NULL, 1, "decode_geph malloc error: n=%d\n", nav->ngmax);
2055 free(nav->geph);
2056 nav->geph = NULL;
2057 nav->ng = nav->ngmax = 0;
2058 return 0;
2059 }
2060 nav->geph = nav_geph;
2061 }
2062 nav->geph[nav->ng++] = *geph;
2063 return 1;
2064}
2065static int add_seph(nav_t* nav, const seph_t* seph) {
2066 seph_t* nav_seph;
2067
2068 if (nav->nsmax <= nav->ns) {
2069 nav->nsmax += 1024;
2070 if (!(nav_seph = (seph_t*)realloc(nav->seph, sizeof(seph_t) * nav->nsmax))) {
2071 trace(NULL, 1, "decode_seph malloc error: n=%d\n", nav->nsmax);
2072 free(nav->seph);
2073 nav->seph = NULL;
2074 nav->ns = nav->nsmax = 0;
2075 return 0;
2076 }
2077 nav->seph = nav_seph;
2078 }
2079 nav->seph[nav->ns++] = *seph;
2080 return 1;
2081}
2082/* read RINEX navigation data ------------------------------------------------*/
2083static int readrnxnav(FILE* fp, const char* opt, int ver, int sys, nav_t* nav) {
2084 eph_t eph;
2085 geph_t geph;
2086 seph_t seph;
2087 int stat, type;
2088
2089 trace(NULL, 3, "readrnxnav: ver=%.2f sys=%d\n", ver / 100.0, sys);
2090
2091 if (!nav) {
2092 return 0;
2093 }
2094
2095 /* read RINEX navigation data body */
2096 while ((stat = readrnxnavb(fp, opt, ver, sys, &type, &eph, &geph, &seph, nav)) >= 0) {
2097 /* add ephemeris to navigation data */
2098 if (stat) {
2099 switch (type) {
2100 case 1:
2101 stat = add_geph(nav, &geph);
2102 break;
2103 case 2:
2104 stat = add_seph(nav, &seph);
2105 break;
2106 default:
2107 stat = add_eph(nav, &eph);
2108 break;
2109 }
2110 if (!stat) {
2111 return 0;
2112 }
2113 }
2114 }
2115
2116 trace(NULL, 4, "nav n=%d, ng=%d, ns=%d\n", nav->n, nav->ng, nav->ns);
2117
2118 trace(NULL, 4, "nav utc_gps=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_gps[0], nav->utc_gps[1], nav->utc_gps[2],
2119 nav->utc_gps[3]);
2120 trace(NULL, 4, "nav utc_sbs=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_sbs[0], nav->utc_sbs[1], nav->utc_sbs[2],
2121 nav->utc_sbs[3]);
2122 trace(NULL, 4, "nav utc_glo=%10.3e,%10.3e\n", nav->utc_glo[0], nav->utc_glo[1]);
2123 trace(NULL, 4, "nav utc_gal=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_gal[0], nav->utc_gal[1], nav->utc_gal[2],
2124 nav->utc_gal[3]);
2125 trace(NULL, 4, "nav utc_qzs=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_qzs[0], nav->utc_qzs[1], nav->utc_qzs[2],
2126 nav->utc_qzs[3]);
2127 trace(NULL, 4, "nav utc_cmp=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_cmp[0], nav->utc_cmp[1], nav->utc_cmp[2],
2128 nav->utc_cmp[3]);
2129 trace(NULL, 4, "nav utc_irn=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->utc_irn[0], nav->utc_irn[1], nav->utc_irn[2],
2130 nav->utc_irn[3]);
2131
2132 trace(NULL, 4, "nav ion_gps=%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e\n", nav->ion_gps[0],
2133 nav->ion_gps[1], nav->ion_gps[2], nav->ion_gps[3], nav->ion_gps[4], nav->ion_gps[5], nav->ion_gps[6],
2134 nav->ion_gps[7]);
2135 trace(NULL, 4, "nav ion_gal=%10.3e,%10.3e,%10.3e,%10.3e\n", nav->ion_gal[0], nav->ion_gal[1], nav->ion_gal[2],
2136 nav->ion_gal[3]);
2137 trace(NULL, 4, "nav ion_qzs=%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e\n", nav->ion_qzs[0],
2138 nav->ion_qzs[1], nav->ion_qzs[2], nav->ion_qzs[3], nav->ion_qzs[4], nav->ion_qzs[5], nav->ion_qzs[6],
2139 nav->ion_qzs[7]);
2140 trace(NULL, 4, "nav ion_cmp=%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e\n", nav->ion_cmp[0],
2141 nav->ion_cmp[1], nav->ion_cmp[2], nav->ion_cmp[3], nav->ion_cmp[4], nav->ion_cmp[5], nav->ion_cmp[6],
2142 nav->ion_cmp[7]);
2143 trace(NULL, 4, "nav ion_irn=%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e,%10.3e\n", nav->ion_irn[0],
2144 nav->ion_irn[1], nav->ion_irn[2], nav->ion_irn[3], nav->ion_irn[4], nav->ion_irn[5], nav->ion_irn[6],
2145 nav->ion_irn[7]);
2146
2147 return nav->n > 0 || nav->ng > 0 || nav->ns > 0;
2148}
2149/* read RINEX clock ----------------------------------------------------------*/
2150static int readrnxclk(FILE* fp, const char* opt, int ver, int index, nav_t* nav) {
2151 pclk_t* nav_pclk;
2152 gtime_t time;
2153 double data[2];
2154 int i, j, sat, mask;
2155 char buff[MAXRNXLEN], satid[8] = "";
2156
2157 trace(NULL, 3, "readrnxclk: index=%d\n", index);
2158
2159 if (!nav) {
2160 return 0;
2161 }
2162
2163 /* set system mask */
2164 mask = set_sysmask(opt);
2165
2166 while (fgets(buff, sizeof(buff), fp)) {
2167 if (str2time(buff, (ver >= 304 ? 13 : 8), 26, &time)) {
2168 trace(NULL, 2, "rinex clk invalid epoch: %34.34s\n", buff);
2169 continue;
2170 }
2171 sprintf(satid, "%.4s", buff + 3);
2172
2173 /* only read AS (satellite clock) record */
2174 if (strncmp(buff, "AS", 2) || !(sat = satid2no(satid))) {
2175 continue;
2176 }
2177
2178 if (!(satsys(sat, NULL) & mask)) {
2179 continue;
2180 }
2181
2182 for (i = 0, j = (ver >= 304 ? 45 : 40); i < 2; i++, j += 20) {
2183 data[i] = str2num(buff, j, 19);
2184 }
2185
2186 if (nav->nc >= nav->ncmax) {
2187 nav->ncmax += 1024;
2188 if (!(nav_pclk = (pclk_t*)realloc(nav->pclk, sizeof(pclk_t) * (nav->ncmax)))) {
2189 trace(NULL, 1, "readrnxclk malloc error: nmax=%d\n", nav->ncmax);
2190 free(nav->pclk);
2191 nav->pclk = NULL;
2192 nav->nc = nav->ncmax = 0;
2193 return -1;
2194 }
2195 nav->pclk = nav_pclk;
2196 }
2197 if (nav->nc <= 0 || fabs(timediff(time, nav->pclk[nav->nc - 1].time)) > 1E-9) {
2198 nav->nc++;
2199 nav->pclk[nav->nc - 1].time = time;
2200 nav->pclk[nav->nc - 1].index = index;
2201 for (i = 0; i < MAXSAT; i++) {
2202 nav->pclk[nav->nc - 1].clk[i][0] = 0.0;
2203 nav->pclk[nav->nc - 1].std[i][0] = 0.0f;
2204 }
2205 }
2206 nav->pclk[nav->nc - 1].clk[sat - 1][0] = data[0];
2207 nav->pclk[nav->nc - 1].std[sat - 1][0] = (float)data[1];
2208 }
2209 return nav->nc > 0;
2210}
2211/* read RINEX file -----------------------------------------------------------*/
2212static int readrnxfp(FILE* fp, gtime_t ts, gtime_t te, double tint, const char* opt, int flag, int index, char* type,
2213 obs_t* obs, nav_t* nav, sta_t* sta) {
2214 int ver, sys, tsys = TSYS_GPS;
2215 char tobs[NUMSYS][MAXOBSTYPE][4] = {{""}};
2216
2217 trace(NULL, 3, "readrnxfp: flag=%d index=%d\n", flag, index);
2218
2219 /* read RINEX file header */
2220 if (!readrnxh(fp, &ver, type, &sys, &tsys, tobs, nav, sta)) {
2221 return 0;
2222 }
2223
2224 /* flag=0:except for clock,1:clock */
2225 if ((!flag && *type == 'C') || (flag && *type != 'C')) {
2226 return 0;
2227 }
2228
2229 /* read RINEX file body */
2230 switch (*type) {
2231 case 'O':
2232 return readrnxobs(fp, ts, te, tint, opt, index, ver, &tsys, tobs, obs, sta);
2233 case 'N':
2234 return readrnxnav(fp, opt, ver, sys, nav);
2235 case 'G':
2236 return readrnxnav(fp, opt, ver, SYS_GLO, nav);
2237 case 'H':
2238 return readrnxnav(fp, opt, ver, SYS_SBS, nav);
2239 case 'J':
2240 return readrnxnav(fp, opt, ver, SYS_QZS, nav); /* extension */
2241 case 'L':
2242 return readrnxnav(fp, opt, ver, SYS_GAL, nav); /* extension */
2243 case 'C':
2244 return readrnxclk(fp, opt, ver, index, nav);
2245 }
2246 trace(NULL, 2, "unsupported rinex type ver=%.2f type=%c\n", ver / 100.0, *type);
2247 return 0;
2248}
2249/* uncompress and read RINEX file --------------------------------------------*/
2250static int readrnxfile(const char* file, gtime_t ts, gtime_t te, double tint, const char* opt, int flag, int index,
2251 char* type, obs_t* obs, nav_t* nav, sta_t* sta) {
2252 FILE* fp;
2253 int cstat, stat;
2254 char tmpfile[1024];
2255
2256 trace(NULL, 3, "readrnxfile: file=%s flag=%d index=%d\n", file, flag, index);
2257
2258 if (sta) {
2259 init_sta(sta);
2260 }
2261
2262 /* uncompress file */
2263 if ((cstat = rtk_uncompress(file, tmpfile)) < 0) {
2264 trace(NULL, 2, "rinex file uncompact error: %s\n", file);
2265 return 0;
2266 }
2267 if (!(fp = fopen(cstat ? tmpfile : file, "r"))) {
2268 trace(NULL, 2, "rinex file open error: %s\n", cstat ? tmpfile : file);
2269 return 0;
2270 }
2271 /* read RINEX file */
2272 stat = readrnxfp(fp, ts, te, tint, opt, flag, index, type, obs, nav, sta);
2273
2274 fclose(fp);
2275
2276 /* delete temporary file */
2277 if (cstat) {
2278 remove(tmpfile);
2279 }
2280
2281 return stat;
2282}
2283/* read RINEX OBS and NAV files ------------------------------------------------
2284 * read RINEX OBS and NAV files
2285 * args : char *file I file (wild-card * expanded) ("": stdin)
2286 * int rcv I receiver number for obs data
2287 * (gtime_t ts) I observation time start (ts.time==0: no limit)
2288 * (gtime_t te) I observation time end (te.time==0: no limit)
2289 * (double tint) I observation time interval (s) (0:all)
2290 * char *opt I RINEX options (see below,"": no option)
2291 * obs_t *obs IO observation data (NULL: no input)
2292 * nav_t *nav IO navigation data (NULL: no input)
2293 * sta_t *sta IO station parameters (NULL: no input)
2294 * return : status (1:ok,0:no data,-1:error)
2295 * notes : read data are appended to obs and nav struct
2296 * before calling the function, obs and nav should be initialized.
2297 * observation data and navigation data are not sorted.
2298 * navigation data may be duplicated.
2299 * call sortobs() or uniqnav() to sort data or delete duplicated eph.
2300 *
2301 * RINEX options (separated by spaces) :
2302 *
2303 * -GLss[=shift]: select GPS signal ss (ss: RINEX 3 code, "1C","2W"...)
2304 * -RLss[=shift]: select GLO signal ss
2305 * -ELss[=shift]: select GAL signal ss
2306 * -JLss[=shift]: select QZS signal ss
2307 * -CLss[=shift]: select BDS signal ss
2308 * -ILss[=shift]: select IRN signal ss
2309 * -SLss[=shift]: select SBS signal ss
2310 *
2311 * shift: carrier phase shift to be added (cycle)
2312 *
2313 * -SYS=sys[,sys...]: select navigation systems
2314 * (sys=G:GPS,R:GLO,E:GAL,J:QZS,C:BDS,I:IRN,S:SBS)
2315 *
2316 *-----------------------------------------------------------------------------*/
2317int readrnxt(const char* file, int rcv, gtime_t ts, gtime_t te, double tint, const char* opt, obs_t* obs, nav_t* nav,
2318 sta_t* sta) {
2319 int i, n, stat = 0;
2320 const char* p;
2321 char type = ' ', *files[MAXEXFILE] = {0};
2322
2323 trace(NULL, 3, "readrnxt: file=%s rcv=%d\n", file, rcv);
2324
2325 if (!*file) {
2326 return readrnxfp(stdin, ts, te, tint, opt, 0, 1, &type, obs, nav, sta);
2327 }
2328 for (i = 0; i < MAXEXFILE; i++) {
2329 if (!(files[i] = (char*)malloc(1024))) {
2330 for (i--; i >= 0; i--) {
2331 free(files[i]);
2332 }
2333 return -1;
2334 }
2335 }
2336 /* expand wild-card */
2337 if ((n = expath(file, files, MAXEXFILE)) <= 0) {
2338 for (i = 0; i < MAXEXFILE; i++) {
2339 free(files[i]);
2340 }
2341 return 0;
2342 }
2343 /* read rinex files */
2344 for (i = 0; i < n && stat >= 0; i++) {
2345 stat = readrnxfile(files[i], ts, te, tint, opt, 0, rcv, &type, obs, nav, sta);
2346 }
2347 /* if station name empty, set 4-char name from file head */
2348 if (type == 'O' && sta) {
2349 if (!(p = strrchr(file, FILEPATHSEP))) {
2350 p = file - 1;
2351 }
2352 if (!*sta->name) {
2353 setstr(sta->name, p + 1, 4);
2354 }
2355 }
2356 for (i = 0; i < MAXEXFILE; i++) {
2357 free(files[i]);
2358 }
2359
2360 return stat;
2361}
2362int readrnx(const char* file, int rcv, const char* opt, obs_t* obs, nav_t* nav, sta_t* sta) {
2363 gtime_t t = {0};
2364
2365 trace(NULL, 3, "readrnx : file=%s rcv=%d\n", file, rcv);
2366
2367 return readrnxt(file, rcv, t, t, 0.0, opt, obs, nav, sta);
2368}
2369/* compare precise clock -----------------------------------------------------*/
2370static int cmppclk(const void* p1, const void* p2) {
2371 pclk_t *q1 = (pclk_t*)p1, *q2 = (pclk_t*)p2;
2372 double tt = timediff(q1->time, q2->time);
2373 return tt < -1E-9 ? -1 : (tt > 1E-9 ? 1 : q1->index - q2->index);
2374}
2375/* combine precise clock -----------------------------------------------------*/
2376static void combpclk(nav_t* nav) {
2377 pclk_t* nav_pclk;
2378 int i, j, k;
2379
2380 trace(NULL, 3, "combpclk: nc=%d\n", nav->nc);
2381
2382 if (nav->nc <= 0) {
2383 return;
2384 }
2385
2386 qsort(nav->pclk, nav->nc, sizeof(pclk_t), cmppclk);
2387
2388 for (i = 0, j = 1; j < nav->nc; j++) {
2389 if (fabs(timediff(nav->pclk[i].time, nav->pclk[j].time)) < 1E-9) {
2390 for (k = 0; k < MAXSAT; k++) {
2391 if (nav->pclk[j].clk[k][0] == 0.0) {
2392 continue;
2393 }
2394 nav->pclk[i].clk[k][0] = nav->pclk[j].clk[k][0];
2395 nav->pclk[i].std[k][0] = nav->pclk[j].std[k][0];
2396 }
2397 } else if (++i < j) {
2398 nav->pclk[i] = nav->pclk[j];
2399 }
2400 }
2401 nav->nc = i + 1;
2402
2403 if (!(nav_pclk = (pclk_t*)realloc(nav->pclk, sizeof(pclk_t) * nav->nc))) {
2404 free(nav->pclk);
2405 nav->pclk = NULL;
2406 nav->nc = nav->ncmax = 0;
2407 trace(NULL, 1, "combpclk malloc error nc=%d\n", nav->nc);
2408 return;
2409 }
2410 nav->pclk = nav_pclk;
2411 nav->ncmax = nav->nc;
2412
2413 trace(NULL, 4, "combpclk: nc=%d\n", nav->nc);
2414}
2415/* read RINEX clock files ------------------------------------------------------
2416 * read RINEX clock files
2417 * args : char *file I file (wild-card * expanded)
2418 * nav_t *nav IO navigation data (NULL: no input)
2419 * return : number of precise clock
2420 *-----------------------------------------------------------------------------*/
2421int readrnxc(const char* file, nav_t* nav) {
2422 gtime_t t = {0};
2423 int i, n, index = 0, stat = 1;
2424 char *files[MAXEXFILE] = {0}, type;
2425
2426 trace(NULL, 3, "readrnxc: file=%s\n", file);
2427
2428 for (i = 0; i < MAXEXFILE; i++) {
2429 if (!(files[i] = (char*)malloc(1024))) {
2430 for (i--; i >= 0; i--) {
2431 free(files[i]);
2432 }
2433 return 0;
2434 }
2435 }
2436 /* expand wild-card */
2437 n = expath(file, files, MAXEXFILE);
2438
2439 /* read rinex clock files */
2440 for (i = 0; i < n; i++) {
2441 if (readrnxfile(files[i], t, t, 0.0, "", 1, index++, &type, NULL, nav, NULL)) {
2442 continue;
2443 }
2444 stat = 0;
2445 break;
2446 }
2447 for (i = 0; i < MAXEXFILE; i++) {
2448 free(files[i]);
2449 }
2450
2451 if (!stat) {
2452 return 0;
2453 }
2454
2455 /* unique and combine ephemeris and precise clock */
2456 combpclk(nav);
2457
2458 return nav->nc;
2459}
2460/* initialize RINEX control ----------------------------------------------------
2461 * initialize RINEX control struct and reallocate memory for observation and
2462 * ephemeris buffer in RINEX control struct
2463 * args : rnxctr_t *rnx IO RINEX control struct
2464 * return : status (1:ok,0:memory allocation error)
2465 *-----------------------------------------------------------------------------*/
2467 gtime_t time0 = {0};
2468 obsd_t data0 = {{0}};
2469 eph_t eph0 = {0, -1, -1};
2470 geph_t geph0 = {0, -1};
2471 seph_t seph0 = {0};
2472 int i, j;
2473
2474 trace(NULL, 3, "init_rnxctr:\n");
2475
2476 rnx->obs.data = NULL;
2477 rnx->nav.eph = NULL;
2478 rnx->nav.geph = NULL;
2479 rnx->nav.seph = NULL;
2480
2481 if (!(rnx->obs.data = (obsd_t*)malloc(sizeof(obsd_t) * MAXOBS)) ||
2482 !(rnx->nav.eph = (eph_t*)malloc(sizeof(eph_t) * MAXSAT * 2)) ||
2483 !(rnx->nav.geph = (geph_t*)malloc(sizeof(geph_t) * NSATGLO)) ||
2484 !(rnx->nav.seph = (seph_t*)malloc(sizeof(seph_t) * NSATSBS * 2))) {
2485 free_rnxctr(rnx);
2486 return 0;
2487 }
2488 rnx->time = time0;
2489 rnx->ver = 0;
2490 rnx->sys = rnx->tsys = 0;
2491 for (i = 0; i < 6; i++) {
2492 for (j = 0; j < MAXOBSTYPE; j++) {
2493 rnx->tobs[i][j][0] = '\0';
2494 }
2495 }
2496 rnx->obs.n = 0;
2497 rnx->nav.n = MAXSAT * 2;
2498 rnx->nav.ng = NSATGLO;
2499 rnx->nav.ns = NSATSBS * 2;
2500 for (i = 0; i < MAXOBS; i++) {
2501 rnx->obs.data[i] = data0;
2502 }
2503 for (i = 0; i < MAXSAT * 2; i++) {
2504 rnx->nav.eph[i] = eph0;
2505 }
2506 for (i = 0; i < NSATGLO; i++) {
2507 rnx->nav.geph[i] = geph0;
2508 }
2509 for (i = 0; i < NSATSBS * 2; i++) {
2510 rnx->nav.seph[i] = seph0;
2511 }
2512 rnx->ephsat = rnx->ephset = 0;
2513 rnx->opt[0] = '\0';
2514
2515 return 1;
2516}
2517/* free RINEX control ----------------------------------------------------------
2518 * free observation and ephemris buffer in RINEX control struct
2519 * args : rnxctr_t *rnx IO RINEX control struct
2520 * return : none
2521 *-----------------------------------------------------------------------------*/
2523 trace(NULL, 3, "free_rnxctr:\n");
2524
2525 free(rnx->obs.data);
2526 rnx->obs.data = NULL;
2527 rnx->obs.n = 0;
2528 free(rnx->nav.eph);
2529 rnx->nav.eph = NULL;
2530 rnx->nav.n = 0;
2531 free(rnx->nav.geph);
2532 rnx->nav.geph = NULL;
2533 rnx->nav.ng = 0;
2534 free(rnx->nav.seph);
2535 rnx->nav.seph = NULL;
2536 rnx->nav.ns = 0;
2537}
2538/* open RINEX data -------------------------------------------------------------
2539 * fetch next RINEX message and input a messsage from file
2540 * args : rnxctr_t *rnx IO RINEX control struct
2541 * FILE *fp I file pointer
2542 * return : status (-2: end of file, 0: no message, 1: input observation data,
2543 * 2: input navigation data)
2544 *-----------------------------------------------------------------------------*/
2545int open_rnxctr(rnxctr_t* rnx, FILE* fp) {
2546 const char* rnxtypes = "ONGLJHC";
2547 char type, tobs[NUMSYS][MAXOBSTYPE][4] = {{""}};
2548 int i, j, ver, sys, tsys;
2549
2550 trace(NULL, 3, "open_rnxctr:\n");
2551
2552 /* read RINEX header from file */
2553 if (!readrnxh(fp, &ver, &type, &sys, &tsys, tobs, &rnx->nav, &rnx->sta)) {
2554 trace(NULL, 2, "open_rnxctr: rinex header read error\n");
2555 return 0;
2556 }
2557 if (!strchr(rnxtypes, type)) {
2558 trace(NULL, 2, "open_rnxctr: not supported rinex type type=%c\n", type);
2559 return 0;
2560 }
2561 rnx->ver = ver;
2562 rnx->type = type;
2563 rnx->sys = sys;
2564 rnx->tsys = tsys;
2565 for (i = 0; i < NUMSYS; i++) {
2566 for (j = 0; j < MAXOBSTYPE && *tobs[i][j]; j++) {
2567 strcpy(rnx->tobs[i][j], tobs[i][j]);
2568 }
2569 }
2570 rnx->ephset = rnx->ephsat = 0;
2571 return 1;
2572}
2573/* input RINEX control ---------------------------------------------------------
2574 * fetch next RINEX message and input a messsage from file
2575 * args : rnxctr_t *rnx IO RINEX control struct
2576 * FILE *fp I file pointer
2577 * return : status (-2: end of file, 0: no message, 1: input observation data,
2578 * 2: input navigation data)
2579 * notes : if status=1, input obs data are set to rnx as follows:
2580 * rnx->time : obs data epoch time
2581 * rnx->obs.n : number of obs data
2582 * rnx->obs.data[]: obs data
2583 * if status=2, input nav data are set to rnx as follows:
2584 * rnx->time : ephemeris frame time
2585 * rnx->ephsat : sat-no of input ephemeris
2586 * rnx->ephset : set-no of input ephemeris (0:set1,1:set2)
2587 * rnx->nav.geph[prn-1] : GLOASS ephemeris (prn=slot-no)
2588 * rnx->nav.seph[prn-MINPRNSBS]: SBAS ephemeris (prn=PRN-no)
2589 * rnx->nav.eph [sat-1] : other ephemeris set1 (sat=sat-no)
2590 * rnx->nav.eph [sat-1+MAXSAT] : other ephemeris set2 (sat=sat-no)
2591 *-----------------------------------------------------------------------------*/
2592int input_rnxctr(rnxctr_t* rnx, FILE* fp) {
2593 eph_t eph = {0};
2594 geph_t geph = {0};
2595 seph_t seph = {0};
2596 int n, sys, stat, flag, prn, type, set;
2597
2598 trace(NULL, 4, "input_rnxctr:\n");
2599
2600 /* read RINEX OBS data */
2601 if (rnx->type == 'O') {
2602 if ((n = readrnxobsb(fp, rnx->opt, rnx->ver, &rnx->tsys, rnx->tobs, &flag, rnx->obs.data, &rnx->sta)) <= 0) {
2603 rnx->obs.n = 0;
2604 return n < 0 ? -2 : 0;
2605 }
2606 rnx->time = rnx->obs.data[0].time;
2607 rnx->obs.n = n;
2608 return 1;
2609 }
2610 /* read RINEX NAV data */
2611 switch (rnx->type) {
2612 case 'N':
2613 sys = SYS_NONE;
2614 break;
2615 case 'G':
2616 sys = SYS_GLO;
2617 break;
2618 case 'H':
2619 sys = SYS_SBS;
2620 break;
2621 case 'L':
2622 sys = SYS_GAL;
2623 break; /* extension */
2624 case 'J':
2625 sys = SYS_QZS;
2626 break; /* extension */
2627 default:
2628 return 0;
2629 }
2630 if ((stat = readrnxnavb(fp, rnx->opt, rnx->ver, sys, &type, &eph, &geph, &seph, &rnx->nav)) <= 0) {
2631 return stat < 0 ? -2 : 0;
2632 }
2633 if (type == 1) { /* GLONASS ephemeris */
2634 sys = satsys(geph.sat, &prn);
2635 rnx->nav.geph[prn - 1] = geph;
2636 rnx->time = geph.tof;
2637 rnx->ephsat = geph.sat;
2638 rnx->ephset = 0;
2639 } else if (type == 2) { /* SBAS ephemeris */
2640 sys = satsys(seph.sat, &prn);
2641 rnx->nav.seph[prn - MINPRNSBS] = seph;
2642 rnx->time = seph.tof;
2643 rnx->ephsat = seph.sat;
2644 rnx->ephset = 0;
2645 } else { /* other ephemeris */
2646 sys = satsys(eph.sat, &prn);
2647 set = (sys == SYS_GAL && (eph.code & (1 << 9))) ? 1 : 0; /* GAL 0:I/NAV,1:F/NAV */
2648 rnx->nav.eph[eph.sat - 1 + MAXSAT * set] = eph;
2649 rnx->time = eph.ttr;
2650 rnx->ephsat = eph.sat;
2651 rnx->ephset = set;
2652 }
2653 return 2;
2654}
2655/*------------------------------------------------------------------------------
2656 * output RINEX functions
2657 *-----------------------------------------------------------------------------*/
2658
2659/* output obs-types RINEX ver.2 ----------------------------------------------*/
2660static void outobstype_ver2(FILE* fp, const rnxopt_t* opt) {
2661 const char label[] = "# / TYPES OF OBSERV";
2662 int i;
2663
2664 trace(NULL, 3, "outobstype_ver2:\n");
2665
2666 fprintf(fp, "%6d", opt->nobs[0]);
2667
2668 for (i = 0; i < opt->nobs[0]; i++) {
2669 if (i > 0 && i % 9 == 0) {
2670 fprintf(fp, " ");
2671 }
2672
2673 fprintf(fp, "%6s", opt->tobs[0][i]);
2674
2675 if (i % 9 == 8) {
2676 fprintf(fp, "%-20s\n", label);
2677 }
2678 }
2679 if (opt->nobs[0] == 0 || i % 9 > 0) {
2680 fprintf(fp, "%*s%-20s\n", (9 - i % 9) * 6, "", label);
2681 }
2682}
2683/* output obs-types RINEX ver.3 ----------------------------------------------*/
2684static void outobstype_ver3(FILE* fp, const rnxopt_t* opt) {
2685 const char label[] = "SYS / # / OBS TYPES";
2686 char tobs[8];
2687 int i, j;
2688
2689 trace(NULL, 3, "outobstype_ver3:\n");
2690
2691 for (i = 0; navsys[i]; i++) {
2692 if (!(navsys[i] & opt->navsys) || !opt->nobs[i]) {
2693 continue;
2694 }
2695
2696 fprintf(fp, "%c %3d", syscodes[i], opt->nobs[i]);
2697
2698 for (j = 0; j < opt->nobs[i]; j++) {
2699 if (j > 0 && j % 13 == 0) {
2700 fprintf(fp, " ");
2701 }
2702
2703 strcpy(tobs, opt->tobs[i][j]);
2704
2705 /* BDS B2x -> 1x (3.02), 2x (other) */
2706 if (navsys[i] == SYS_CMP) {
2707 if (opt->rnxver == 302 && tobs[1] == '2') {
2708 tobs[1] = '1';
2709 }
2710 }
2711 fprintf(fp, " %3s", tobs);
2712
2713 if (j % 13 == 12) {
2714 fprintf(fp, " %-20s\n", label);
2715 }
2716 }
2717 if (j % 13 > 0) {
2718 fprintf(fp, "%*s %-20s\n", (13 - j % 13) * 4, "", label);
2719 }
2720 }
2721}
2722/* output RINEX phase shift --------------------------------------------------*/
2723static void outrnx_phase_shift(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
2724 static const uint8_t ref_code[][10] = {
2725 /* reference signal [9] table A23 */
2726 {CODE_L1C, CODE_L2P, CODE_L5I, 0}, /* GPS */
2727 {CODE_L1C, CODE_L4A, CODE_L2C, CODE_L6A, CODE_L3I, 0}, /* GLO */
2728 {CODE_L1B, CODE_L5I, CODE_L7I, CODE_L8I, CODE_L6B, 0}, /* GAL */
2729 {CODE_L1C, CODE_L2S, CODE_L5I, CODE_L5D, CODE_L6S, 0}, /* QZS */
2730 {CODE_L1C, CODE_L5I, 0}, /* SBS */
2732 {CODE_L5A, CODE_L9A, 0} /* IRN */
2733 };
2734 const char* label = "SYS / PHASE SHIFT";
2735 char obs[8];
2736 int i, j, k;
2737
2738 for (i = 0; navsys[i]; i++) {
2739 if (!(navsys[i] & opt->navsys) || !opt->nobs[i]) {
2740 continue;
2741 }
2742 for (j = 0; j < opt->nobs[i]; j++) {
2743 if (opt->tobs[i][j][0] != 'L') {
2744 continue;
2745 }
2746 strcpy(obs, opt->tobs[i][j]);
2747 for (k = 0; ref_code[i][k]; k++) {
2748 if (obs2code(obs + 1) == ref_code[i][k]) {
2749 break;
2750 }
2751 }
2752 if (navsys[i] == SYS_CMP) { /* BDS B2x -> 1x (3.02), 2x (other) */
2753 if (opt->rnxver == 302 && obs[1] == '2') {
2754 obs[1] = '1';
2755 }
2756 }
2757 if (ref_code[i][k]) {
2758 fprintf(fp, "%c %3s %54s%-20s\n", syscodes[i], obs, "", label);
2759 } else {
2760 fprintf(fp, "%c %3s %8.5f%46s%-20s\n", syscodes[i], obs, opt->shift[i][j], "", label);
2761 }
2762 }
2763 }
2764}
2765/* output RINEX GLONASS slot/freq # ------------------------------------------*/
2766static void outrnx_glo_fcn(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
2767 const char* label = "GLONASS SLOT / FRQ #";
2768 int i, j, k, n = 0, sat, prn[MAXPRNGLO], fcn[MAXPRNGLO];
2769
2770 if (opt->navsys & SYS_GLO) {
2771 for (i = 0; i < MAXPRNGLO; i++) {
2772 sat = satno(SYS_GLO, i + 1);
2773 if (nav->geph[i].sat == sat) {
2774 prn[n] = i + 1;
2775 fcn[n++] = nav->geph[i].frq;
2776 } else if (nav->glo_fcn[i]) {
2777 prn[n] = i + 1;
2778 fcn[n++] = nav->glo_fcn[i] - 8;
2779 }
2780 }
2781 }
2782 for (i = j = 0; i < (n <= 0 ? 1 : (n - 1) / 8 + 1); i++) {
2783 if (i == 0) {
2784 fprintf(fp, "%3d", n);
2785 } else {
2786 fprintf(fp, " ");
2787 }
2788 for (k = 0; k < 8 && j < n; k++, j++) {
2789 fprintf(fp, " R%02d %2d", prn[j], fcn[j]);
2790 }
2791 fprintf(fp, "%*s %-20s\n", (8 - k) * 7, "", label);
2792 }
2793}
2794/* output RINEX GLONASS code/phase/bias --------------------------------------*/
2795static void outrnx_glo_bias(FILE* fp, const rnxopt_t* opt) {
2796 const char* label = "GLONASS COD/PHS/BIS";
2797 const char* tobs[4] = {"C1C", "C1P", "C2C", "C2P"};
2798
2799 if (opt->navsys & SYS_GLO) {
2800 fprintf(fp, " %s %8.3f %s %8.3f %s %8.3f %s %8.3f%8s%-20s\n", tobs[0], opt->glo_cp_bias[0], tobs[1],
2801 opt->glo_cp_bias[1], tobs[2], opt->glo_cp_bias[2], tobs[3], opt->glo_cp_bias[3], "", label);
2802 } else {
2803 fprintf(fp, "%*s%-20s\n", 60, "", label);
2804 }
2805}
2806/* output RINEX observation data file header -----------------------------------
2807 * output RINEX observation data file header
2808 * args : FILE *fp I output file pointer
2809 * rnxopt_t *opt I RINEX options
2810 * nav_t *nav I navigation data
2811 * return : status (1:ok, 0:output error)
2812 *-----------------------------------------------------------------------------*/
2813int outrnxobsh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
2814 double ep[6], pos[3] = {0}, del[3] = {0};
2815 char date[32], *sys, *tsys = "GPS";
2816 int i;
2817
2818 trace(NULL, 3, "outrnxobsh:\n");
2819
2820 timestr_rnx(date);
2821
2822 if (opt->navsys == SYS_GPS) {
2823 sys = "G: GPS";
2824 } else if (opt->navsys == SYS_GLO) {
2825 sys = "R: GLONASS";
2826 } else if (opt->navsys == SYS_GAL) {
2827 sys = "E: Galielo";
2828 } else if (opt->navsys == SYS_QZS) {
2829 sys = "J: QZSS"; /* ver.3.02 */
2830 } else if (opt->navsys == SYS_CMP) {
2831 sys = "C: BeiDou"; /* ver.3.02 */
2832 } else if (opt->navsys == SYS_IRN) {
2833 sys = "I: IRNSS"; /* ver.3.03 */
2834 } else if (opt->navsys == SYS_SBS) {
2835 sys = "S: SBAS Payload";
2836 } else {
2837 sys = "M: Mixed";
2838 }
2839
2840 fprintf(fp, "%9.2f%-11s%-20s%-20s%-20s\n", opt->rnxver / 100.0, "", "OBSERVATION DATA", sys,
2841 "RINEX VERSION / TYPE");
2842 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
2843
2844 for (i = 0; i < MAXCOMMENT; i++) {
2845 if (!*opt->comment[i]) {
2846 continue;
2847 }
2848 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
2849 }
2850 fprintf(fp, "%-60.60s%-20s\n", opt->marker, "MARKER NAME");
2851 fprintf(fp, "%-20.20s%-40.40s%-20s\n", opt->markerno, "", "MARKER NUMBER");
2852
2853 if (opt->rnxver >= 300) {
2854 fprintf(fp, "%-20.20s%-40.40s%-20s\n", opt->markertype, "", "MARKER TYPE");
2855 }
2856 fprintf(fp, "%-20.20s%-40.40s%-20s\n", opt->name[0], opt->name[1], "OBSERVER / AGENCY");
2857 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->rec[0], opt->rec[1], opt->rec[2], "REC # / TYPE / VERS");
2858 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->ant[0], opt->ant[1], "", "ANT # / TYPE");
2859
2860 for (i = 0; i < 3; i++) {
2861 if (fabs(opt->apppos[i]) < 1E8) {
2862 pos[i] = opt->apppos[i];
2863 }
2864 }
2865 for (i = 0; i < 3; i++) {
2866 if (fabs(opt->antdel[i]) < 1E8) {
2867 del[i] = opt->antdel[i];
2868 }
2869 }
2870 fprintf(fp, "%14.4f%14.4f%14.4f%-18s%-20s\n", pos[0], pos[1], pos[2], "", "APPROX POSITION XYZ");
2871 fprintf(fp, "%14.4f%14.4f%14.4f%-18s%-20s\n", del[0], del[1], del[2], "", "ANTENNA: DELTA H/E/N");
2872
2873 if (opt->rnxver <= 299) { /* ver.2 */
2874 fprintf(fp, "%6d%6d%-48s%-20s\n", 1, 1, "", "WAVELENGTH FACT L1/2");
2875 outobstype_ver2(fp, opt);
2876 } else { /* ver.3 */
2877 outobstype_ver3(fp, opt);
2878 }
2879 if (opt->tint > 0.0) {
2880 fprintf(fp, "%10.3f%50s%-20s\n", opt->tint, "", "INTERVAL");
2881 }
2882 time2epoch(opt->tstart, ep);
2883 fprintf(fp, " %04.0f %02.0f %02.0f %02.0f %02.0f %010.7f %-12s%-20s\n", ep[0], ep[1], ep[2],
2884 ep[3], ep[4], ep[5], tsys, "TIME OF FIRST OBS");
2885
2886 time2epoch(opt->tend, ep);
2887 fprintf(fp, " %04.0f %02.0f %02.0f %02.0f %02.0f %010.7f %-12s%-20s\n", ep[0], ep[1], ep[2],
2888 ep[3], ep[4], ep[5], tsys, "TIME OF LAST OBS");
2889
2890 if (opt->rnxver >= 301) {
2891 outrnx_phase_shift(fp, opt, nav); /* SYS / PHASE SHIFT */
2892 }
2893 if (opt->rnxver >= 302) {
2894 outrnx_glo_fcn(fp, opt, nav); /* GLONASS SLOT / FRQ # */
2895 }
2896 if (opt->rnxver >= 302) {
2897 outrnx_glo_bias(fp, opt); /* GLONASS COD/PHS/BIS */
2898 }
2899 return fprintf(fp, "%-60.60s%-20s\n", "", "END OF HEADER") != EOF;
2900}
2901/* output observation data field ---------------------------------------------*/
2902static void outrnxobsf(FILE* fp, double obs, int lli) {
2903 if (obs == 0.0 || obs <= -1E9 || obs >= 1E9) {
2904 fprintf(fp, " ");
2905 } else {
2906 fprintf(fp, "%14.3f", obs);
2907 }
2908 if (lli < 0 || !(lli & (LLI_SLIP | LLI_HALFC | LLI_BOCTRK))) {
2909 fprintf(fp, " ");
2910 } else {
2911 fprintf(fp, "%1.1d ", lli & (LLI_SLIP | LLI_HALFC | LLI_BOCTRK));
2912 }
2913}
2914/* search obsservattion data index -------------------------------------------*/
2915static int obsindex(int rnxver, int sys, const uint8_t* code, const char* tobs, const char* mask) {
2916 char* id;
2917 int i;
2918
2919 for (i = 0; i < NFREQ + NEXOBS; i++) {
2920 /* signal mask */
2921 if (mask[code[i] - 1] == '0') {
2922 continue;
2923 }
2924
2925 if (rnxver <= 299) { /* ver.2 */
2926 if (!strcmp(tobs, "C1") &&
2927 (sys == SYS_GPS || sys == SYS_GLO || sys == SYS_QZS || sys == SYS_SBS || sys == SYS_CMP)) {
2928 if (code[i] == CODE_L1C) {
2929 return i;
2930 }
2931 } else if (!strcmp(tobs, "P1")) {
2932 if (code[i] == CODE_L1P || code[i] == CODE_L1W || code[i] == CODE_L1Y || code[i] == CODE_L1N) {
2933 return i;
2934 }
2935 } else if (!strcmp(tobs, "C2") && (sys == SYS_GPS || sys == SYS_QZS)) {
2936 if (code[i] == CODE_L2S || code[i] == CODE_L2L || code[i] == CODE_L2X) {
2937 return i;
2938 }
2939 } else if (!strcmp(tobs, "C2") && sys == SYS_GLO) {
2940 if (code[i] == CODE_L2C) {
2941 return i;
2942 }
2943 } else if (!strcmp(tobs, "P2")) {
2944 if (code[i] == CODE_L2P || code[i] == CODE_L2W || code[i] == CODE_L2Y || code[i] == CODE_L2N ||
2945 code[i] == CODE_L2D) {
2946 return i;
2947 }
2948 } else if (rnxver >= 212 && tobs[1] == 'A') { /* L1C/A */
2949 if (code[i] == CODE_L1C) {
2950 return i;
2951 }
2952 } else if (rnxver >= 212 && tobs[1] == 'B') { /* L1C */
2953 if (code[i] == CODE_L1S || code[i] == CODE_L1L || code[i] == CODE_L1X) {
2954 return i;
2955 }
2956 } else if (rnxver >= 212 && tobs[1] == 'C') { /* L2C */
2957 if (code[i] == CODE_L2S || code[i] == CODE_L2L || code[i] == CODE_L2X) {
2958 return i;
2959 }
2960 } else if (rnxver >= 212 && tobs[1] == 'D' && sys == SYS_GLO) { /* GLO L2C/A */
2961 if (code[i] == CODE_L2C) {
2962 return i;
2963 }
2964 } else if (tobs[1] == '2' && sys == SYS_CMP) { /* BDS B1 */
2965 if (code[i] == CODE_L2I || code[i] == CODE_L2Q || code[i] == CODE_L2X) {
2966 return i;
2967 }
2968 } else {
2969 id = code2obs(code[i]);
2970 if (id[0] == tobs[1]) {
2971 return i;
2972 }
2973 }
2974 } else { /* ver.3 */
2975 id = code2obs(code[i]);
2976 if (!strcmp(id, tobs + 1)) {
2977 return i;
2978 }
2979 }
2980 }
2981 return -1;
2982}
2983/* output RINEX observation data body ------------------------------------------
2984 * output RINEX observation data body
2985 * args : FILE *fp I output file pointer
2986 * rnxopt_t *opt I RINEX options
2987 * obsd_t *obs I observation data
2988 * int n I number of observation data
2989 * int flag I epoch flag (0:ok,1:power failure,>1:event flag)
2990 * return : status (1:ok, 0:output error)
2991 *-----------------------------------------------------------------------------*/
2992int outrnxobsb(FILE* fp, const rnxopt_t* opt, const obsd_t* obs, int n, int flag) {
2993 const char* mask;
2994 double ep[6], dL;
2995 char sats[MAXOBS][4] = {""};
2996 int i, j, k, m, ns, sys, ind[MAXOBS], s[MAXOBS] = {0};
2997
2998 trace(NULL, 3, "outrnxobsb: n=%d\n", n);
2999
3000 time2epoch(obs[0].time, ep);
3001
3002 for (i = ns = 0; i < n && ns < MAXOBS; i++) {
3003 sys = satsys(obs[i].sat, NULL);
3004 if (!(sys & opt->navsys) || opt->exsats[obs[i].sat - 1]) {
3005 continue;
3006 }
3007 if (!sat2code(obs[i].sat, sats[ns])) {
3008 continue;
3009 }
3010 switch (sys) {
3011 case SYS_GPS:
3012 s[ns] = 0;
3013 break;
3014 case SYS_GLO:
3015 s[ns] = 1;
3016 break;
3017 case SYS_GAL:
3018 s[ns] = 2;
3019 break;
3020 case SYS_QZS:
3021 s[ns] = 3;
3022 break;
3023 case SYS_SBS:
3024 s[ns] = 4;
3025 break;
3026 case SYS_CMP:
3027 s[ns] = 5;
3028 break;
3029 case SYS_IRN:
3030 s[ns] = 6;
3031 break;
3032 }
3033 if (!opt->nobs[(opt->rnxver <= 299) ? 0 : s[ns]]) {
3034 continue;
3035 }
3036 ind[ns++] = i;
3037 }
3038 if (ns <= 0) {
3039 return 1;
3040 }
3041
3042 if (opt->rnxver <= 299) { /* ver.2 */
3043 fprintf(fp, " %02d %02.0f %02.0f %02.0f %02.0f %010.7f %d%3d", (int)ep[0] % 100, ep[1], ep[2], ep[3], ep[4],
3044 ep[5], flag, ns);
3045 for (i = 0; i < ns; i++) {
3046 if (i > 0 && i % 12 == 0) {
3047 fprintf(fp, "\n%32s", "");
3048 }
3049 fprintf(fp, "%-3s", sats[i]);
3050 }
3051 } else { /* ver.3 */
3052 fprintf(fp, "> %04.0f %02.0f %02.0f %02.0f %02.0f %010.7f %d%3d%21s\n", ep[0], ep[1], ep[2], ep[3], ep[4],
3053 ep[5], flag, ns, "");
3054 }
3055 for (i = 0; i < ns; i++) {
3056 sys = satsys(obs[ind[i]].sat, NULL);
3057
3058 if (opt->rnxver <= 299) { /* ver.2 */
3059 m = 0;
3060 mask = opt->mask[s[i]];
3061 } else { /* ver.3 */
3062 fprintf(fp, "%-3s", sats[i]);
3063 m = s[i];
3064 mask = opt->mask[s[i]];
3065 }
3066 for (j = 0; j < opt->nobs[m]; j++) {
3067 if (opt->rnxver <= 299) { /* ver.2 */
3068 if (j % 5 == 0) {
3069 fprintf(fp, "\n");
3070 }
3071 }
3072 /* search obs data index */
3073 if ((k = obsindex(opt->rnxver, sys, obs[ind[i]].code, opt->tobs[m][j], mask)) < 0) {
3074 outrnxobsf(fp, 0.0, -1);
3075 continue;
3076 }
3077 /* phase shift (cyc) */
3078 dL = (obs[ind[i]].L[k] != 0.0) ? opt->shift[m][j] : 0.0;
3079
3080 /* output field */
3081 switch (opt->tobs[m][j][0]) {
3082 case 'C':
3083 case 'P':
3084 outrnxobsf(fp, obs[ind[i]].P[k], -1);
3085 break;
3086 case 'L':
3087 outrnxobsf(fp, obs[ind[i]].L[k] + dL, obs[ind[i]].LLI[k]);
3088 break;
3089 case 'D':
3090 outrnxobsf(fp, obs[ind[i]].D[k], -1);
3091 break;
3092 case 'S':
3093 outrnxobsf(fp, obs[ind[i]].SNR[k] * SNR_UNIT, -1);
3094 break;
3095 }
3096 }
3097 if (opt->rnxver >= 300 && fprintf(fp, "\n") == EOF) {
3098 return 0;
3099 }
3100 }
3101 if (opt->rnxver >= 300) {
3102 return 1;
3103 }
3104
3105 return fprintf(fp, "\n") != EOF;
3106}
3107/* output data field in RINEX navigation data --------------------------------*/
3108static void outnavf_n(FILE* fp, double value, int n) {
3109 double e = (fabs(value) < 1E-99) ? 0.0 : floor(log10(fabs(value)) + 1.0);
3110
3111 fprintf(fp, " %s.%0*.0f%s%+03.0f", value < 0.0 ? "-" : " ", n, fabs(value) / pow(10.0, e - n), NAVEXP, e);
3112}
3113static void outnavf(FILE* fp, double value) { outnavf_n(fp, value, 12); }
3114/* output iono correction for a system ---------------------------------------*/
3115static void out_iono_sys(FILE* fp, const char* sys, const double* ion, int n) {
3116 const char *label1[] = {"ION ALPHA", "ION BETA"}, *label2 = "IONOSPHERIC CORR";
3117 char str[32];
3118 int i, j;
3119
3120 if (norm(ion, n) <= 0.0) {
3121 return;
3122 }
3123
3124 for (i = 0; i < (n + 3) / 4; i++) {
3125 sprintf(str, "%s%c", sys, (!*sys || n < 4) ? ' ' : 'A' + i);
3126 fprintf(fp, "%-*s ", !*sys ? 1 : 4, str);
3127 for (j = 0; j < 4 && i * 4 + j < n; j++) {
3128 fprintf(fp, " ");
3129 outnavf_n(fp, ion[i * 4 + j], 4);
3130 }
3131 fprintf(fp, "%*s%-20s\n", !*sys ? 10 : 7 + 12 * (4 - j), "", !*sys ? label1[i] : label2);
3132 }
3133}
3134/* output iono corrections --------------------------------------------------*/
3135static void out_iono(FILE* fp, int sys, const rnxopt_t* opt, const nav_t* nav) {
3136 if (!opt->outiono) {
3137 return;
3138 }
3139
3140 if (sys & opt->navsys & SYS_GPS) {
3141 if (opt->rnxver <= 211) {
3142 out_iono_sys(fp, "", nav->ion_gps, 8);
3143 } else {
3144 out_iono_sys(fp, "GPS", nav->ion_gps, 8);
3145 }
3146 }
3147 if ((sys & opt->navsys & SYS_GAL) && opt->rnxver >= 212) {
3148 out_iono_sys(fp, "GAL", nav->ion_gal, 3);
3149 }
3150 if ((sys & opt->navsys & SYS_QZS) && opt->rnxver >= 302) {
3151 out_iono_sys(fp, "QZS", nav->ion_qzs, 8);
3152 }
3153 if ((sys & opt->navsys & SYS_CMP) && opt->rnxver >= 302) {
3154 out_iono_sys(fp, "BDS", nav->ion_cmp, 8);
3155 }
3156 if ((sys & opt->navsys & SYS_IRN) && opt->rnxver >= 303) {
3157 out_iono_sys(fp, "IRN", nav->ion_irn, 8);
3158 }
3159}
3160/* output time system correction for a system --------------------------------*/
3161static void out_time_sys(FILE* fp, const char* sys, const double* utc) {
3162 const char *label1 = "TIME SYSTEM CORR", *label2 = "DELTA-UTC: A0,A1,T,W";
3163
3164 if (norm(utc, 3) <= 0.0) {
3165 return;
3166 }
3167
3168 if (*sys) {
3169 fprintf(fp, "%-4s ", sys);
3170 outnavf_n(fp, utc[0], 10);
3171 outnavf_n(fp, utc[1], 9);
3172 fprintf(fp, "%7.0f%5.0f%10s%-20s\n", utc[2], utc[3], "", label1);
3173 } else {
3174 fprintf(fp, " ");
3175 outnavf_n(fp, utc[0], 12);
3176 outnavf_n(fp, utc[1], 12);
3177 fprintf(fp, "%9.0f%9.0f %-20s\n", utc[2], utc[3], label2);
3178 }
3179}
3180/* output time system corrections --------------------------------------------*/
3181static void out_time(FILE* fp, int sys, const rnxopt_t* opt, const nav_t* nav) {
3182 double utc[8] = {0};
3183
3184 if (!opt->outtime) {
3185 return;
3186 }
3187
3188 if (sys & opt->navsys & SYS_GPS) {
3189 if (opt->rnxver <= 211) {
3190 out_time_sys(fp, "", nav->utc_gps);
3191 } else {
3192 out_time_sys(fp, "GPUT", nav->utc_gps);
3193 }
3194 }
3195 if ((sys & opt->navsys & SYS_GLO) && opt->rnxver >= 212) {
3196 /* RINEX 2.12-3.02: tau_C, 3.03- : -tau_C */
3197 utc[0] = (opt->rnxver <= 302) ? nav->utc_glo[0] : -nav->utc_glo[0];
3198 out_time_sys(fp, "GLUT", utc);
3199 }
3200 if ((sys & opt->navsys & SYS_SBS) && opt->rnxver >= 212) {
3201 out_time_sys(fp, "SBUT", nav->utc_sbs);
3202 }
3203 if ((sys & opt->navsys & SYS_GAL) && opt->rnxver >= 212) {
3204 out_time_sys(fp, "GAUT", nav->utc_gal);
3205 }
3206 if ((sys & opt->navsys & SYS_QZS) && opt->rnxver >= 302) {
3207 out_time_sys(fp, "QZUT", nav->utc_qzs);
3208 }
3209 if ((sys & opt->navsys & SYS_CMP) && opt->rnxver >= 302) {
3210 out_time_sys(fp, "BDUT", nav->utc_cmp);
3211 }
3212 if ((sys & opt->navsys & SYS_IRN) && opt->rnxver >= 303) {
3213 out_time_sys(fp, "IRUT", nav->utc_irn);
3214 }
3215}
3216/* output leap seconds -------------------------------------------------------*/
3217static void out_leaps(FILE* fp, int sys, const rnxopt_t* opt, const nav_t* nav) {
3218 const char* label = "LEAP SECONDS";
3219 const double* leaps;
3220
3221 if (!opt->outleaps) {
3222 return;
3223 }
3224
3225 switch (sys) {
3226 case SYS_GAL:
3227 leaps = nav->utc_gal + 4;
3228 break;
3229 case SYS_QZS:
3230 leaps = nav->utc_qzs + 4;
3231 break;
3232 case SYS_CMP:
3233 leaps = nav->utc_cmp + 4;
3234 break;
3235 case SYS_IRN:
3236 leaps = nav->utc_irn + 4;
3237 break;
3238 default:
3239 leaps = nav->utc_gps + 4;
3240 break;
3241 }
3242 if (leaps[0] == 0.0) {
3243 return;
3244 }
3245
3246 if (opt->rnxver <= 300) {
3247 if (sys == SYS_GPS) {
3248 fprintf(fp, "%6.0f%54s%-20s\n", leaps[0], "", label);
3249 }
3250 } else if (norm(leaps + 1, 3) <= 0.0) {
3251 fprintf(fp, "%6.0f%18s%3s%33s%-20s\n", leaps[0], "", (sys == SYS_CMP) ? "BDS" : "", "", label);
3252 } else {
3253 fprintf(fp, "%6.0f%6.0f%6.0f%6.0f%3s%33s%-20s\n", leaps[0], leaps[3], leaps[1], leaps[2],
3254 (sys == SYS_CMP) ? "BDS" : "", "", label);
3255 }
3256}
3257/* output RINEX navigation data file header ------------------------------------
3258 * output RINEX navigation data file header
3259 * args : FILE *fp I output file pointer
3260 * rnxopt_t *opt I RINEX options
3261 * nav_t nav I navigation data (NULL: no input)
3262 * return : status (1:ok, 0:output error)
3263 *-----------------------------------------------------------------------------*/
3264int outrnxnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3265 int i;
3266 char date[64], *sys;
3267
3268 trace(NULL, 3, "outrnxnavh:\n");
3269
3270 timestr_rnx(date);
3271
3272 if (opt->rnxver <= 299) { /* ver.2 */
3273 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GPS NAV DATA", "",
3274 "RINEX VERSION / TYPE");
3275 } else { /* ver.3 */
3276 if (opt->navsys == SYS_GPS) {
3277 sys = "G: GPS";
3278 } else if (opt->navsys == SYS_GLO) {
3279 sys = "R: GLONASS";
3280 } else if (opt->navsys == SYS_GAL) {
3281 sys = "E: Galileo";
3282 } else if (opt->navsys == SYS_QZS) {
3283 sys = "J: QZSS"; /* v.3.02 */
3284 } else if (opt->navsys == SYS_CMP) {
3285 sys = "C: BeiDou"; /* v.3.02 */
3286 } else if (opt->navsys == SYS_IRN) {
3287 sys = "I: IRNSS"; /* v.3.03 */
3288 } else if (opt->navsys == SYS_SBS) {
3289 sys = "S: SBAS Payload";
3290 } else if (opt->sep_nav) {
3291 sys = "G: GPS";
3292 } else {
3293 sys = "M: Mixed";
3294 }
3295
3296 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", sys,
3297 "RINEX VERSION / TYPE");
3298 }
3299 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3300
3301 for (i = 0; i < MAXCOMMENT; i++) {
3302 if (!*opt->comment[i]) {
3303 continue;
3304 }
3305 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3306 }
3307 out_iono(fp, opt->sep_nav ? SYS_GPS : SYS_ALL, opt, nav);
3308 out_time(fp, opt->sep_nav ? SYS_GPS : SYS_ALL, opt, nav);
3309 out_leaps(fp, SYS_GPS, opt, nav);
3310
3311 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3312}
3313/* output RINEX navigation data file body --------------------------------------
3314 * output RINEX navigation data file body
3315 * args : FILE *fp I output file pointer
3316 * rnxopt_t *opt I RINEX options
3317 * eph_t *eph I ephemeris
3318 * return : status (1:ok, 0:output error)
3319 *-----------------------------------------------------------------------------*/
3320int outrnxnavb(FILE* fp, const rnxopt_t* opt, const eph_t* eph) {
3321 double ep[6], ttr;
3322 int week, sys, prn;
3323 char code[32], *sep;
3324
3325 trace(NULL, 3, "outrnxnavb: sat=%2d\n", eph->sat);
3326
3327 if (!(sys = satsys(eph->sat, &prn)) || !(sys & opt->navsys)) {
3328 return 0;
3329 }
3330
3331 if (sys != SYS_CMP) {
3332 time2epoch(eph->toc, ep);
3333 } else {
3334 time2epoch(gpst2bdt(eph->toc), ep); /* gpst -> bdt */
3335 }
3336 if ((opt->rnxver >= 300 && sys == SYS_GPS) || (opt->rnxver >= 212 && sys == SYS_GAL) ||
3337 (opt->rnxver >= 302 && sys == SYS_QZS) || (opt->rnxver >= 302 && sys == SYS_CMP) ||
3338 (opt->rnxver >= 303 && sys == SYS_IRN)) {
3339 if (!sat2code(eph->sat, code)) {
3340 return 0;
3341 }
3342 fprintf(fp, "%-3s %04.0f %02.0f %02.0f %02.0f %02.0f %02.0f", code, ep[0], ep[1], ep[2], ep[3], ep[4], ep[5]);
3343 sep = " ";
3344 } else if (opt->rnxver <= 299 && sys == SYS_GPS) {
3345 fprintf(fp, "%2d %02d %02.0f %02.0f %02.0f %02.0f %04.1f", prn, (int)ep[0] % 100, ep[1], ep[2], ep[3], ep[4],
3346 ep[5]);
3347 sep = " ";
3348 } else {
3349 return 0;
3350 }
3351 outnavf(fp, eph->f0);
3352 outnavf(fp, eph->f1);
3353 outnavf(fp, eph->f2);
3354 fprintf(fp, "\n%s", sep);
3355
3356 outnavf(fp, eph->iode); /* GPS/QZS: IODE, GAL: IODnav, BDS: AODE */
3357 outnavf(fp, eph->crs);
3358 outnavf(fp, eph->deln);
3359 outnavf(fp, eph->M0);
3360 fprintf(fp, "\n%s", sep);
3361
3362 outnavf(fp, eph->cuc);
3363 outnavf(fp, eph->e);
3364 outnavf(fp, eph->cus);
3365 outnavf(fp, sqrt(eph->A));
3366 fprintf(fp, "\n%s", sep);
3367
3368 outnavf(fp, eph->toes);
3369 outnavf(fp, eph->cic);
3370 outnavf(fp, eph->OMG0);
3371 outnavf(fp, eph->cis);
3372 fprintf(fp, "\n%s", sep);
3373
3374 outnavf(fp, eph->i0);
3375 outnavf(fp, eph->crc);
3376 outnavf(fp, eph->omg);
3377 outnavf(fp, eph->OMGd);
3378 fprintf(fp, "\n%s", sep);
3379
3380 outnavf(fp, eph->idot);
3381 outnavf(fp, eph->code);
3382 outnavf(fp, eph->week); /* GPS/QZS: GPS week, GAL: GAL week, BDS: BDT week */
3383 if (sys == SYS_GPS || sys == SYS_QZS) {
3384 outnavf(fp, eph->flag);
3385 } else {
3386 outnavf(fp, 0.0); /* spare */
3387 }
3388 fprintf(fp, "\n%s", sep);
3389
3390 if (sys == SYS_GAL) {
3391 outnavf(fp, sisa_value(eph->sva));
3392 } else {
3393 outnavf(fp, uravalue(eph->sva));
3394 }
3395 outnavf(fp, eph->svh);
3396 outnavf(fp, eph->tgd[0]); /* GPS/QZS:TGD, GAL:BGD E5a/E1, BDS: TGD1 B1/B3 */
3397 if (sys == SYS_GAL || sys == SYS_CMP) {
3398 outnavf(fp, eph->tgd[1]); /* GAL:BGD E5b/E1, BDS: TGD2 B2/B3 */
3399 } else if (sys == SYS_GPS || sys == SYS_QZS) {
3400 outnavf(fp, eph->iodc); /* GPS/QZS:IODC */
3401 } else {
3402 outnavf(fp, 0.0); /* spare */
3403 }
3404 fprintf(fp, "\n%s", sep);
3405
3406 if (sys != SYS_CMP) {
3407 ttr = time2gpst(eph->ttr, &week);
3408 } else {
3409 ttr = time2bdt(gpst2bdt(eph->ttr), &week); /* gpst -> bdt */
3410 }
3411 outnavf(fp, ttr + (week - eph->week) * 604800.0);
3412
3413 if (sys == SYS_GPS) {
3414 outnavf(fp, eph->fit);
3415 } else if (sys == SYS_QZS) {
3416 outnavf(fp, eph->fit > 2.0 ? 1.0 : 0.0);
3417 } else if (sys == SYS_CMP) {
3418 outnavf(fp, eph->iodc); /* AODC */
3419 } else {
3420 outnavf(fp, 0.0); /* spare */
3421 }
3422 return fprintf(fp, "\n") != EOF;
3423}
3424/* output RINEX GNAV file header -----------------------------------------------
3425 * output RINEX GNAV (GLONASS navigation data) file header
3426 * args : FILE *fp I output file pointer
3427 * rnxopt_t *opt I RINEX options
3428 * nav_t nav I navigation data (NULL: no input)
3429 * return : status (1:ok, 0:output error)
3430 *-----------------------------------------------------------------------------*/
3431int outrnxgnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3432 int i;
3433 char date[64];
3434
3435 trace(NULL, 3, "outrnxgnavh:\n");
3436
3437 timestr_rnx(date);
3438
3439 if (opt->rnxver <= 299) { /* ver.2 */
3440 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "GLONASS NAV DATA", "",
3441 "RINEX VERSION / TYPE");
3442 } else { /* ver.3 */
3443 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "R: GLONASS",
3444 "RINEX VERSION / TYPE");
3445 }
3446 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3447
3448 for (i = 0; i < MAXCOMMENT; i++) {
3449 if (!*opt->comment[i]) {
3450 continue;
3451 }
3452 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3453 }
3454 out_time(fp, SYS_GLO, opt, nav);
3455 out_leaps(fp, SYS_GPS, opt, nav);
3456
3457 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3458}
3459/* output RINEX GNAV file body -------------------------------------------------
3460 * output RINEX GNAV (GLONASS navigation data) file body
3461 * args : FILE *fp I output file pointer
3462 * rnxopt_t *opt I rinex options
3463 * geph_t *geph I glonass ephemeris
3464 * return : status (1:ok, 0:output error)
3465 *-----------------------------------------------------------------------------*/
3466int outrnxgnavb(FILE* fp, const rnxopt_t* opt, const geph_t* geph) {
3467 gtime_t toe;
3468 double ep[6], tof;
3469 int prn;
3470 char code[32], *sep;
3471
3472 trace(NULL, 3, "outrnxgnavb: sat=%2d\n", geph->sat);
3473
3474 if ((satsys(geph->sat, &prn) & opt->navsys) != SYS_GLO) {
3475 return 0;
3476 }
3477
3478 tof = time2gpst(gpst2utc(geph->tof), NULL); /* v.3: tow in utc */
3479 if (opt->rnxver <= 299) {
3480 tof = fmod(tof, 86400.0); /* v.2: tod in utc */
3481 }
3482
3483 toe = gpst2utc(geph->toe); /* gpst -> utc */
3484 time2epoch(toe, ep);
3485
3486 if (opt->rnxver <= 299) { /* ver.2 */
3487 fprintf(fp, "%2d %02d %02.0f %02.0f %02.0f %02.0f %04.1f", prn, (int)ep[0] % 100, ep[1], ep[2], ep[3], ep[4],
3488 ep[5]);
3489 sep = " ";
3490 } else { /* ver.3 */
3491 if (!sat2code(geph->sat, code)) {
3492 return 0;
3493 }
3494 fprintf(fp, "%-3s %04.0f %02.0f %02.0f %02.0f %02.0f %02.0f", code, ep[0], ep[1], ep[2], ep[3], ep[4], ep[5]);
3495 sep = " ";
3496 }
3497 outnavf(fp, -geph->taun);
3498 outnavf(fp, geph->gamn);
3499 outnavf(fp, tof);
3500 fprintf(fp, "\n%s", sep);
3501
3502 outnavf(fp, geph->pos[0] / 1E3);
3503 outnavf(fp, geph->vel[0] / 1E3);
3504 outnavf(fp, geph->acc[0] / 1E3);
3505 outnavf(fp, geph->svh & 1);
3506 fprintf(fp, "\n%s", sep);
3507
3508 outnavf(fp, geph->pos[1] / 1E3);
3509 outnavf(fp, geph->vel[1] / 1E3);
3510 outnavf(fp, geph->acc[1] / 1E3);
3511 outnavf(fp, geph->frq);
3512 fprintf(fp, "\n%s", sep);
3513
3514 outnavf(fp, geph->pos[2] / 1E3);
3515 outnavf(fp, geph->vel[2] / 1E3);
3516 outnavf(fp, geph->acc[2] / 1E3);
3517 outnavf(fp, geph->age);
3518
3519 if (opt->rnxver >= 305) { /* RINEX 3.05 */
3520 fprintf(fp, "\n%s", sep);
3521 outnavf(fp, geph->flags);
3522 outnavf(fp, geph->dtaun);
3523 outnavf(fp, geph->sva);
3524 outnavf(fp, (geph->svh >> 1) & 7);
3525 }
3526 return fprintf(fp, "\n") != EOF;
3527}
3528/* output RINEX GEO navigation data file header --------------------------------
3529 * output RINEX GEO navigation data file header
3530 * args : FILE *fp I output file pointer
3531 * rnxopt_t *opt I RINEX options
3532 * nav_t nav I navigation data (NULL: no input)
3533 * return : status (1:ok, 0:output error)
3534 *-----------------------------------------------------------------------------*/
3535int outrnxhnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3536 int i;
3537 char date[64];
3538
3539 trace(NULL, 3, "outrnxhnavh:\n");
3540
3541 timestr_rnx(date);
3542
3543 if (opt->rnxver <= 299) { /* ver.2 */
3544 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "H: GEO NAV MSG DATA", "",
3545 "RINEX VERSION / TYPE");
3546 } else { /* ver.3 */
3547 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "S: SBAS Payload",
3548 "RINEX VERSION / TYPE");
3549 }
3550 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3551
3552 for (i = 0; i < MAXCOMMENT; i++) {
3553 if (!*opt->comment[i]) {
3554 continue;
3555 }
3556 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3557 }
3558 out_time(fp, SYS_SBS, opt, nav);
3559 out_leaps(fp, SYS_GPS, opt, nav);
3560
3561 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3562}
3563/* output RINEX GEO navigation data file body ----------------------------------
3564 * output RINEX GEO navigation data file body
3565 * args : FILE *fp I output file pointer
3566 * rnxopt_t *opt I RINEX options
3567 * seph_t *seph I SBAS ephemeris
3568 * return : status (1:ok, 0:output error)
3569 *-----------------------------------------------------------------------------*/
3570int outrnxhnavb(FILE* fp, const rnxopt_t* opt, const seph_t* seph) {
3571 double ep[6];
3572 int prn;
3573 char code[32], *sep;
3574
3575 trace(NULL, 3, "outrnxhnavb: sat=%2d\n", seph->sat);
3576
3577 if ((satsys(seph->sat, &prn) & opt->navsys) != SYS_SBS) {
3578 return 0;
3579 }
3580
3581 time2epoch(seph->t0, ep);
3582
3583 if (opt->rnxver <= 299) { /* ver.2 */
3584 fprintf(fp, "%2d %02d %2.0f %2.0f %2.0f %2.0f %4.1f", prn - 100, (int)ep[0] % 100, ep[1], ep[2], ep[3], ep[4],
3585 ep[5]);
3586 sep = " ";
3587 } else { /* ver.3 */
3588 if (!sat2code(seph->sat, code)) {
3589 return 0;
3590 }
3591 fprintf(fp, "%-3s %04.0f %2.0f %2.0f %2.0f %2.0f %2.0f", code, ep[0], ep[1], ep[2], ep[3], ep[4], ep[5]);
3592 sep = " ";
3593 }
3594 outnavf(fp, seph->af0);
3595 outnavf(fp, seph->af1);
3596 outnavf(fp, time2gpst(seph->tof, NULL));
3597 fprintf(fp, "\n%s", sep);
3598
3599 outnavf(fp, seph->pos[0] / 1E3);
3600 outnavf(fp, seph->vel[0] / 1E3);
3601 outnavf(fp, seph->acc[0] / 1E3);
3602 outnavf(fp, seph->svh);
3603 fprintf(fp, "\n%s", sep);
3604
3605 outnavf(fp, seph->pos[1] / 1E3);
3606 outnavf(fp, seph->vel[1] / 1E3);
3607 outnavf(fp, seph->acc[1] / 1E3);
3608 outnavf(fp, uravalue(seph->sva));
3609 fprintf(fp, "\n%s", sep);
3610
3611 outnavf(fp, seph->pos[2] / 1E3);
3612 outnavf(fp, seph->vel[2] / 1E3);
3613 outnavf(fp, seph->acc[2] / 1E3);
3614 outnavf(fp, 0);
3615
3616 return fprintf(fp, "\n") != EOF;
3617}
3618/* output RINEX Galileo NAV header ---------------------------------------------
3619 * output RINEX Galileo NAV file header (2.12)
3620 * args : FILE *fp I output file pointer
3621 * rnxopt_t *opt I RINEX options
3622 * nav_t nav I navigation data (NULL: no input)
3623 * return : status (1:ok, 0:output error)
3624 *-----------------------------------------------------------------------------*/
3625int outrnxlnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3626 int i;
3627 char date[64];
3628
3629 trace(NULL, 3, "outrnxlnavh:\n");
3630
3631 if (opt->rnxver < 212) {
3632 return 0;
3633 }
3634
3635 timestr_rnx(date);
3636
3637 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "E: Galileo",
3638 "RINEX VERSION / TYPE");
3639
3640 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3641
3642 for (i = 0; i < MAXCOMMENT; i++) {
3643 if (!*opt->comment[i]) {
3644 continue;
3645 }
3646 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3647 }
3648 out_iono(fp, SYS_GAL, opt, nav);
3649 out_time(fp, SYS_GAL, opt, nav);
3650 out_leaps(fp, SYS_GAL, opt, nav);
3651
3652 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3653}
3654/* output RINEX QZSS navigation data file header -------------------------------
3655 * output RINEX QZSS navigation data file header
3656 * args : FILE *fp I output file pointer
3657 * rnxopt_t *opt I RINEX options
3658 * nav_t nav I navigation data (NULL: no input)
3659 * return : status (1:ok, 0:output error)
3660 *-----------------------------------------------------------------------------*/
3661int outrnxqnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3662 int i;
3663 char date[64];
3664
3665 trace(NULL, 3, "outrnxqnavh:\n");
3666
3667 if (opt->rnxver < 302) {
3668 return 0;
3669 }
3670
3671 timestr_rnx(date);
3672
3673 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "J: QZSS",
3674 "RINEX VERSION / TYPE");
3675
3676 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3677
3678 for (i = 0; i < MAXCOMMENT; i++) {
3679 if (!*opt->comment[i]) {
3680 continue;
3681 }
3682 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3683 }
3684 out_iono(fp, SYS_QZS, opt, nav);
3685 out_time(fp, SYS_QZS, opt, nav);
3686 out_leaps(fp, SYS_QZS, opt, nav);
3687
3688 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3689}
3690/* output RINEX BDS navigation data file header --------------------------------
3691 * output RINEX BDS navigation data file header
3692 * args : FILE *fp I output file pointer
3693 * rnxopt_t *opt I RINEX options
3694 * nav_t nav I navigation data (NULL: no input)
3695 * return : status (1:ok, 0:output error)
3696 *-----------------------------------------------------------------------------*/
3697int outrnxcnavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3698 int i;
3699 char date[64];
3700
3701 trace(NULL, 3, "outrnxcnavh:\n");
3702
3703 if (opt->rnxver < 302) {
3704 return 0;
3705 }
3706
3707 timestr_rnx(date);
3708
3709 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "C: BeiDou",
3710 "RINEX VERSION / TYPE");
3711
3712 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3713
3714 for (i = 0; i < MAXCOMMENT; i++) {
3715 if (!*opt->comment[i]) {
3716 continue;
3717 }
3718 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3719 }
3720 out_iono(fp, SYS_CMP, opt, nav);
3721 out_time(fp, SYS_CMP, opt, nav);
3722 out_leaps(fp, SYS_CMP, opt, nav);
3723
3724 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3725}
3726/* output RINEX NavIC/IRNSS navigation data file header ------------------------
3727 * output RINEX NavIC/IRNSS navigation data file header
3728 * args : FILE *fp I output file pointer
3729 * rnxopt_t *opt I RINEX options
3730 * nav_t nav I navigation data (NULL: no input)
3731 * return : status (1:ok, 0:output error)
3732 *-----------------------------------------------------------------------------*/
3733int outrnxinavh(FILE* fp, const rnxopt_t* opt, const nav_t* nav) {
3734 int i;
3735 char date[64];
3736
3737 trace(NULL, 3, "outrnxinavh:\n");
3738
3739 if (opt->rnxver < 303) {
3740 return 0;
3741 }
3742
3743 timestr_rnx(date);
3744
3745 fprintf(fp, "%9.2f %-20s%-20s%-20s\n", opt->rnxver / 100.0, "N: GNSS NAV DATA", "I: IRNSS",
3746 "RINEX VERSION / TYPE");
3747
3748 fprintf(fp, "%-20.20s%-20.20s%-20.20s%-20s\n", opt->prog, opt->runby, date, "PGM / RUN BY / DATE");
3749
3750 for (i = 0; i < MAXCOMMENT; i++) {
3751 if (!*opt->comment[i]) {
3752 continue;
3753 }
3754 fprintf(fp, "%-60.60s%-20s\n", opt->comment[i], "COMMENT");
3755 }
3756 out_iono(fp, SYS_IRN, opt, nav);
3757 out_time(fp, SYS_IRN, opt, nav);
3758 out_leaps(fp, SYS_IRN, opt, nav);
3759
3760 return fprintf(fp, "%60s%-20s\n", "", "END OF HEADER") != EOF;
3761}
uint8_t obs2code(const char *obs)
Definition mrtk_obs.c:173
int satid2no(const char *id)
Definition mrtk_nav.c:753
char * code2obs(uint8_t code)
Definition mrtk_obs.c:190
static const int navsys[]
#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_IRN
Definition mrtk_eph.c:71
#define SYS_CMP
Definition mrtk_eph.c:70
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
struct nav_s nav_t
Definition mrtk_eph.h:40
#define SNR_UNIT
#define CODE_L2N
#define CODE_L2Q
#define CODE_L1S
#define NSATSBS
#define MAXRCV
#define MINPRNIRN
#define CODE_L5I
#define MINPRNCMP
#define CODE_L2X
#define CODE_L1L
#define MAXOBS
#define MINPRNQZS
#define CODE_L2P
#define CODE_L2I
#define CODE_L6S
#define CODE_L1C
#define CODE_L6I
#define CODE_L2C
#define CODE_L7I
#define CODE_L9A
#define CODE_L1Y
#define CODE_L5D
#define CODE_L3I
#define CODE_L1B
#define CODE_L7D
#define CODE_L2S
#define CODE_L1W
#define CODE_L6B
#define CODE_L1N
#define CODE_L2W
#define MINPRNGAL
#define CODE_L2L
#define CODE_L1D
#define CODE_L5A
#define MINPRNGLO
#define CODE_L2D
#define CODE_L2Y
#define NSATGLO
#define CODE_L8I
#define CODE_L1P
#define MAXCOMMENT
#define CODE_L6A
#define MAXEXFILE
#define CODE_L8D
#define MAXPRNGLO
#define MAXOBSTYPE
#define CODE_L1X
#define CODE_L4A
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
MRTKLIB Matrix Module — Matrix/vector operations and estimation.
int satno(int sys, int prn)
Convert satellite system+prn/slot number to satellite number.
Definition mrtk_nav.c:58
int satsys_bd2(int sat, int *prn)
Distinguish BDS-2 from BDS-3.
Definition mrtk_nav.c:159
int getcodepri(int sys, uint8_t code, const char *opt)
Get code priority for multiple codes in a frequency.
Definition mrtk_obs.c:276
#define SYS_ALL
Definition mrtk_obs.c:63
#define SYS_BD2
Definition mrtk_obs.c:62
static char * obscodes[]
Definition mrtk_obs.c:89
int code2freq_idx(int sys, uint8_t code)
Convert system and obs code to frequency index (obsdef-based).
Definition mrtk_obs.c:225
int screent(gtime_t time, gtime_t ts, gtime_t te, double tint)
Screen data by time range and interval.
Definition mrtk_obs.c:424
#define MINPRNSBS
#define MINPRNGPS
static void set_index(int ver, int sys, const char *opt, char tobs[MAXOBSTYPE][4], sigind_t *ind)
static int add_geph(nav_t *nav, const geph_t *geph)
int readrnxt(const char *file, int rcv, gtime_t ts, gtime_t te, double tint, const char *opt, obs_t *obs, nav_t *nav, sta_t *sta)
Read RINEX OBS and NAV files with time span.
static int set_sysmask(const char *opt)
static void outrnx_glo_fcn(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
static int decode_obsepoch(FILE *fp, char *buff, int ver, gtime_t *time, int *flag, int *sats)
Definition mrtk_rinex.c:826
static int readrnxclk(FILE *fp, const char *opt, int ver, int index, nav_t *nav)
static int add_seph(nav_t *nav, const seph_t *seph)
#define TSYS_IRN
Definition mrtk_rinex.c:54
static void decode_navh(char *buff, nav_t *nav)
Definition mrtk_rinex.c:577
#define TSYS_QZS
Definition mrtk_rinex.c:52
static int uraindex(double value)
Definition mrtk_rinex.c:177
#define MINFREQ_GLO
Definition mrtk_rinex.c:74
static void outrnxobsf(FILE *fp, double obs, int lli)
static void outobstype_ver2(FILE *fp, const rnxopt_t *opt)
static int decode_geph(int ver, int sat, gtime_t toc, double *data, geph_t *geph)
#define MAXPOSHEAD
Definition mrtk_rinex.c:73
int outrnxobsh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX observation data file header.
static void timestr_rnx(char *str)
Definition mrtk_rinex.c:136
static int readrnxh(FILE *fp, int *ver, char *type, int *sys, int *tsys, char tobs[][MAXOBSTYPE][4], nav_t *nav, sta_t *sta)
Definition mrtk_rinex.c:727
static gtime_t adjweek(gtime_t t, gtime_t t0)
Definition mrtk_rinex.c:114
static void decode_gnavh(char *buff, nav_t *nav)
Definition mrtk_rinex.c:703
static int readrnxfp(FILE *fp, gtime_t ts, gtime_t te, double tint, const char *opt, int flag, int index, char *type, obs_t *obs, nav_t *nav, sta_t *sta)
static void init_sta(sta_t *sta)
Definition mrtk_rinex.c:216
static void setstr(char *dst, const char *src, int n)
Definition mrtk_rinex.c:102
int init_rnxctr(rnxctr_t *rnx)
Initialize RINEX control struct.
#define LLI_HALFC
Definition mrtk_rinex.c:57
static void outrnx_phase_shift(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
static int decode_eph(int ver, int sat, gtime_t toc, const double *data, eph_t *eph)
#define TSYS_GPS
Definition mrtk_rinex.c:48
#define NUMSYS
Definition mrtk_rinex.c:71
static void combpclk(nav_t *nav)
int outrnxlnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX Galileo navigation data file header.
int outrnxgnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX GLONASS navigation data file header.
static int add_eph(nav_t *nav, const eph_t *eph)
int outrnxnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX navigation data file header.
static void out_iono(FILE *fp, int sys, const rnxopt_t *opt, const nav_t *nav)
static void out_time_sys(FILE *fp, const char *sys, const double *utc)
int open_rnxctr(rnxctr_t *rnx, FILE *fp)
Open RINEX data (read header).
static void saveslips(uint8_t slips[][5+5], obsd_t *data)
static int readrnxobs(FILE *fp, gtime_t ts, gtime_t te, double tint, const char *opt, int rcv, int ver, int *tsys, char tobs[][MAXOBSTYPE][4], obs_t *obs, sta_t *sta)
static void convcode(int ver, int sys, const char *str, char *type)
Definition mrtk_rinex.c:239
#define NINCOBS
Definition mrtk_rinex.c:76
static const double ura_eph[]
Definition mrtk_rinex.c:84
#define TSYS_UTC
Definition mrtk_rinex.c:49
static int decode_obsdata(FILE *fp, char *buff, int ver, int mask, sigind_t *index, obsd_t *obs)
Definition mrtk_rinex.c:888
int outrnxqnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX QZSS navigation data file header.
#define LLI_SLIP
Definition mrtk_rinex.c:56
static int sisa_index(double value)
Definition mrtk_rinex.c:203
static int readrnxobsb(FILE *fp, const char *opt, int ver, int *tsys, char tobs[][MAXOBSTYPE][4], int *flag, obsd_t *data, sta_t *sta)
static void restslips(uint8_t slips[][5+5], obsd_t *data)
#define TSYS_GAL
Definition mrtk_rinex.c:51
static int obsindex(int rnxver, int sys, const uint8_t *code, const char *tobs, const char *mask)
static int decode_eph_bds_cnav(int ver, int sat, gtime_t toc, const double *data, int ndata, int v4type, eph_t *eph)
#define MAXRNXLEN
Definition mrtk_rinex.c:72
static void out_time(FILE *fp, int sys, const rnxopt_t *opt, const nav_t *nav)
static const char syscodes[]
Definition mrtk_rinex.c:80
int outrnxhnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX GEO navigation data file header.
int outrnxnavb(FILE *fp, const rnxopt_t *opt, const eph_t *eph)
Output RINEX navigation data file body.
static int readrnxnav(FILE *fp, const char *opt, int ver, int sys, nav_t *nav)
int outrnxobsb(FILE *fp, const rnxopt_t *opt, const obsd_t *obs, int n, int flag)
Output RINEX observation data body.
#define SQR(x)
Definition mrtk_rinex.c:68
static void out_iono_sys(FILE *fp, const char *sys, const double *ion, int n)
int readrnxc(const char *file, nav_t *nav)
Read RINEX clock files.
int outrnxgnavb(FILE *fp, const rnxopt_t *opt, const geph_t *geph)
Output RINEX GLONASS navigation data file body.
static int sat2code(int sat, char *code)
Definition mrtk_rinex.c:145
static void outrnx_glo_bias(FILE *fp, const rnxopt_t *opt)
static int readrnxfile(const char *file, gtime_t ts, gtime_t te, double tint, const char *opt, int flag, int index, char *type, obs_t *obs, nav_t *nav, sta_t *sta)
static void outobstype_ver3(FILE *fp, const rnxopt_t *opt)
int readrnx(const char *file, int rcv, const char *opt, obs_t *obs, nav_t *nav, sta_t *sta)
Read RINEX OBS and NAV files.
static int decode_seph(int ver, int sat, gtime_t toc, double *data, seph_t *seph)
static int addobsdata(obs_t *obs, const obsd_t *data)
#define MAXFREQ_GLO
Definition mrtk_rinex.c:75
int input_rnxctr(rnxctr_t *rnx, FILE *fp)
Input RINEX control (read next message).
int outrnxinavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX NavIC/IRNSS navigation data file header.
#define LLI_BOCTRK
Definition mrtk_rinex.c:58
static int decode_eph_cnav(int ver, int sat, gtime_t toc, const double *data, int ndata, int v4type, eph_t *eph)
static void outnavf_n(FILE *fp, double value, int n)
int outrnxcnavh(FILE *fp, const rnxopt_t *opt, const nav_t *nav)
Output RINEX BDS navigation data file header.
static double uravalue(int sva)
Definition mrtk_rinex.c:175
void free_rnxctr(rnxctr_t *rnx)
Free RINEX control struct buffers.
static double sisa_value(int sisa)
Definition mrtk_rinex.c:187
static void outnavf(FILE *fp, double value)
static const double ura_nominal[]
Definition mrtk_rinex.c:87
static gtime_t adjday(gtime_t t, gtime_t t0)
Definition mrtk_rinex.c:125
static void out_leaps(FILE *fp, int sys, const rnxopt_t *opt, const nav_t *nav)
static uint16_t snr_val(int sat, double snr)
Definition mrtk_rinex.c:880
int outrnxhnavb(FILE *fp, const rnxopt_t *opt, const seph_t *seph)
Output RINEX GEO navigation data file body.
#define NAVEXP
Definition mrtk_rinex.c:70
static int cmppclk(const void *p1, const void *p2)
static int readrnxnavb(FILE *fp, const char *opt, int ver, int sys, int *type, eph_t *eph, geph_t *geph, seph_t *seph, nav_t *nav)
#define TSYS_CMP
Definition mrtk_rinex.c:53
static void decode_hnavh(char *buff, nav_t *nav)
Definition mrtk_rinex.c:714
static void decode_obsh(FILE *fp, char *buff, int ver, int *tsys, char tobs[][MAXOBSTYPE][4], nav_t *nav, sta_t *sta)
Definition mrtk_rinex.c:372
MRTKLIB RINEX Module — RINEX file read/write types and functions.
#define FILEPATHSEP
Definition mrtk_sys.c:48
int expath(const char *path, char *paths[], int nmax)
Expand file path with wild-card (*) in file.
Definition mrtk_sys.c:96
int rtk_uncompress(const char *file, char *uncfile)
Uncompress file (gzip/zip/tar/Hatanaka).
Definition mrtk_sys.c:367
double str2num(const char *s, int i, int n)
Convert substring in string to number.
Definition mrtk_sys.c:60
MRTKLIB System Module — OS/filesystem utility functions.
gtime_t bdt2time(int week, double sec)
Convert BeiDou time week/tow to gtime_t.
Definition mrtk_time.c:229
gtime_t gpst2time(int week, double sec)
Convert GPS week/tow to gtime_t.
Definition mrtk_time.c:165
void time2epoch(gtime_t t, double *ep)
Convert gtime_t struct to calendar day/time.
Definition mrtk_time.c:136
gtime_t gpst2bdt(gtime_t t)
Convert GPS time to BeiDou time.
Definition mrtk_time.c:458
gtime_t gpst2utc(gtime_t t)
Convert GPS time to UTC considering leap seconds.
Definition mrtk_time.c:422
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
double time2gpst(gtime_t t, int *week)
Convert gtime_t to GPS week/tow.
Definition mrtk_time.c:181
double time2bdt(gtime_t t, int *week)
Convert gtime_t to BeiDou time week/tow.
Definition mrtk_time.c:245
static double leaps[64+1][7]
Leap seconds table (y,m,d,h,m,s,utc-gpst).
Definition mrtk_time.c:49
gtime_t timeadd(gtime_t t, double sec)
Add seconds to gtime_t.
Definition mrtk_time.c:265
gtime_t bdt2gpst(gtime_t t)
Convert BeiDou time to GPS time.
Definition mrtk_time.c:465
char * time_str(gtime_t t, int n)
Get time string (not reentrant).
Definition mrtk_time.c:530
int str2time(const char *s, int i, int n, gtime_t *t)
Convert substring in string to gtime_t struct.
Definition mrtk_time.c:87
gtime_t utc2gpst(gtime_t t)
Convert UTC to GPS time considering leap seconds.
Definition mrtk_time.c:440
gtime_t timeget(void)
Get current time in UTC.
Definition mrtk_time.c:289
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.
GPS/GAL/QZS/BDS/IRN broadcast ephemeris type.
Definition mrtk_eph.h:77
double OMG0
Definition mrtk_eph.h:90
double omg
Definition mrtk_eph.h:90
int sat
Definition mrtk_eph.h:78
gtime_t ttr
Definition mrtk_eph.h:88
double OMGd
Definition mrtk_eph.h:90
double f1
Definition mrtk_eph.h:94
double f0
Definition mrtk_eph.h:94
int svh
Definition mrtk_eph.h:81
double e
Definition mrtk_eph.h:90
double idot
Definition mrtk_eph.h:90
double M0
Definition mrtk_eph.h:90
double A
Definition mrtk_eph.h:90
double cis
Definition mrtk_eph.h:91
int flag
Definition mrtk_eph.h:86
gtime_t toe
Definition mrtk_eph.h:88
double fit
Definition mrtk_eph.h:93
int week
Definition mrtk_eph.h:82
gtime_t toc
Definition mrtk_eph.h:88
double cic
Definition mrtk_eph.h:91
double tgd[6]
Definition mrtk_eph.h:95
double cus
Definition mrtk_eph.h:91
double cuc
Definition mrtk_eph.h:91
double toes
Definition mrtk_eph.h:92
int code
Definition mrtk_eph.h:83
int type
Definition mrtk_eph.h:100
double Adot
Definition mrtk_eph.h:105
double crs
Definition mrtk_eph.h:91
double i0
Definition mrtk_eph.h:90
int iode
Definition mrtk_eph.h:79
int sva
Definition mrtk_eph.h:80
double ndot
Definition mrtk_eph.h:105
double deln
Definition mrtk_eph.h:90
double crc
Definition mrtk_eph.h:91
double f2
Definition mrtk_eph.h:94
int iodc
Definition mrtk_eph.h:79
GLONASS broadcast ephemeris type.
Definition mrtk_eph.h:111
double vel[3]
Definition mrtk_eph.h:121
double dtaun
Definition mrtk_eph.h:124
gtime_t toe
Definition mrtk_eph.h:118
double pos[3]
Definition mrtk_eph.h:120
int sva
Definition mrtk_eph.h:117
int flags
Definition mrtk_eph.h:116
int iode
Definition mrtk_eph.h:113
double gamn
Definition mrtk_eph.h:123
double acc[3]
Definition mrtk_eph.h:122
int age
Definition mrtk_eph.h:117
int svh
Definition mrtk_eph.h:115
double taun
Definition mrtk_eph.h:123
gtime_t tof
Definition mrtk_eph.h:119
int sat
Definition mrtk_eph.h:112
int frq
Definition mrtk_eph.h:114
GNSS time representation.
Definition mrtk_time.h:49
double sec
Definition mrtk_time.h:51
time_t time
Definition mrtk_time.h:50
int ng
Definition mrtk_nav.h:394
int glo_fcn[32]
Definition mrtk_nav.h:426
double utc_glo[8]
Definition mrtk_nav.h:415
double utc_qzs[8]
Definition mrtk_nav.h:417
seph_t * seph
Definition mrtk_nav.h:408
double ion_irn[8]
Definition mrtk_nav.h:425
double utc_gal[8]
Definition mrtk_nav.h:416
double utc_gps[8]
Definition mrtk_nav.h:414
geph_t * geph
Definition mrtk_nav.h:407
double utc_cmp[8]
Definition mrtk_nav.h:418
double utc_sbs[4]
Definition mrtk_nav.h:420
double ion_cmp[8]
Definition mrtk_nav.h:424
pclk_t * pclk
Definition mrtk_nav.h:410
double ion_gal[4]
Definition mrtk_nav.h:422
double utc_irn[9]
Definition mrtk_nav.h:419
int ncmax
Definition mrtk_nav.h:397
eph_t * eph
Definition mrtk_nav.h:400
int nc
Definition mrtk_nav.h:397
int nsmax
Definition mrtk_nav.h:395
double ion_gps[8]
Definition mrtk_nav.h:421
int ngmax
Definition mrtk_nav.h:394
int nmax
Definition mrtk_nav.h:393
int ns
Definition mrtk_nav.h:395
int n
Definition mrtk_nav.h:393
double ion_qzs[8]
Definition mrtk_nav.h:423
Observation data collection.
Definition mrtk_obs.h:59
obsd_t * data
Definition mrtk_obs.h:61
int nmax
Definition mrtk_obs.h:60
int n
Definition mrtk_obs.h:60
Observation data record.
Definition mrtk_obs.h:44
float D[5+5]
Definition mrtk_obs.h:52
gtime_t time
Definition mrtk_obs.h:45
uint8_t sat
Definition mrtk_obs.h:46
uint8_t LLI[5+5]
Definition mrtk_obs.h:48
double L[5+5]
Definition mrtk_obs.h:50
uint8_t rcv
Definition mrtk_obs.h:46
double P[5+5]
Definition mrtk_obs.h:51
uint16_t SNR[5+5]
Definition mrtk_obs.h:47
uint8_t code[5+5]
Definition mrtk_obs.h:49
int index
Definition mrtk_nav.h:77
double clk[MAXSAT][1]
Definition mrtk_nav.h:78
gtime_t time
Definition mrtk_nav.h:76
float std[MAXSAT][1]
Definition mrtk_nav.h:79
RINEX control struct type.
Definition mrtk_rinex.h:93
nav_t nav
Definition mrtk_rinex.h:101
char opt[256]
Definition mrtk_rinex.h:105
gtime_t time
Definition mrtk_rinex.h:94
obs_t obs
Definition mrtk_rinex.h:100
sta_t sta
Definition mrtk_rinex.h:102
char type
Definition mrtk_rinex.h:96
char tobs[8][MAXOBSTYPE][4]
Definition mrtk_rinex.h:99
RINEX options type.
Definition mrtk_rinex.h:49
double tint
Definition mrtk_rinex.h:51
uint8_t exsats[MAXSAT]
Definition mrtk_rinex.h:73
char marker[64]
Definition mrtk_rinex.h:62
char markertype[32]
Definition mrtk_rinex.h:64
double antdel[3]
Definition mrtk_rinex.h:69
char markerno[32]
Definition mrtk_rinex.h:63
int rnxver
Definition mrtk_rinex.h:54
int outleaps
Definition mrtk_rinex.h:77
int outiono
Definition mrtk_rinex.h:75
char mask[7][MAXCODE]
Definition mrtk_rinex.h:58
int nobs[7]
Definition mrtk_rinex.h:87
int sep_nav
Definition mrtk_rinex.h:81
double shift[7][MAXOBSTYPE]
Definition mrtk_rinex.h:86
int navsys
Definition mrtk_rinex.h:55
double apppos[3]
Definition mrtk_rinex.h:68
char rec[3][32]
Definition mrtk_rinex.h:66
double glo_cp_bias[4]
Definition mrtk_rinex.h:70
char prog[32]
Definition mrtk_rinex.h:60
char ant[3][32]
Definition mrtk_rinex.h:67
gtime_t tend
Definition mrtk_rinex.h:83
char comment[MAXCOMMENT][64]
Definition mrtk_rinex.h:71
gtime_t tstart
Definition mrtk_rinex.h:82
char runby[32]
Definition mrtk_rinex.h:61
int outtime
Definition mrtk_rinex.h:76
char tobs[7][MAXOBSTYPE][4]
Definition mrtk_rinex.h:85
char name[2][32]
Definition mrtk_rinex.h:65
SBAS ephemeris type.
Definition mrtk_eph.h:130
int sva
Definition mrtk_eph.h:134
double pos[3]
Definition mrtk_eph.h:136
double vel[3]
Definition mrtk_eph.h:137
double acc[3]
Definition mrtk_eph.h:138
gtime_t tof
Definition mrtk_eph.h:133
double af0
Definition mrtk_eph.h:139
double af1
Definition mrtk_eph.h:139
int sat
Definition mrtk_eph.h:131
gtime_t t0
Definition mrtk_eph.h:132
int svh
Definition mrtk_eph.h:135
int pos[MAXOBSTYPE]
Definition mrtk_rinex.c:94
uint8_t pri[MAXOBSTYPE]
Definition mrtk_rinex.c:95
int idx[MAXOBSTYPE]
Definition mrtk_rinex.c:93
double shift[MAXOBSTYPE]
Definition mrtk_rinex.c:98
uint8_t type[MAXOBSTYPE]
Definition mrtk_rinex.c:96
uint8_t code[MAXOBSTYPE]
Definition mrtk_rinex.c:97
int itrf
Definition mrtk_nav.h:378
double pos[3]
Definition mrtk_nav.h:380
char antsno[MAXANT]
Definition mrtk_nav.h:373
char recsno[MAXANT]
Definition mrtk_nav.h:376
int deltype
Definition mrtk_nav.h:379
char recver[MAXANT]
Definition mrtk_nav.h:375
double hgt
Definition mrtk_nav.h:382
double del[3]
Definition mrtk_nav.h:381
char rectype[MAXANT]
Definition mrtk_nav.h:374
char name[MAXANT]
Definition mrtk_nav.h:370
int antsetup
Definition mrtk_nav.h:377
double glo_cp_bias[4]
Definition mrtk_nav.h:384
char marker[MAXANT]
Definition mrtk_nav.h:371
char antdes[MAXANT]
Definition mrtk_nav.h:372
#define malloc(x)
Definition toml.c:52
#define free(x)
Definition toml.c:53