MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_spp.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_spp.c : standard single-point positioning
3 *
4 * Copyright (C) 2026 H.SHIONO (MRTKLIB Project)
5 * Copyright (C) 2023-2025 Cabinet Office, Japan
6 * Copyright (C) 2024-2025 Lighthouse Technology & Consulting Co. Ltd.
7 * Copyright (C) 2023-2025 Japan Aerospace Exploration Agency
8 * Copyright (C) 2023-2025 TOSHIBA ELECTRONIC TECHNOLOGIES CORPORATION
9 * Copyright (C) 2015- Mitsubishi Electric Corp.
10 * Copyright (C) 2014 Geospatial Information Authority of Japan
11 * Copyright (C) 2014 T.SUZUKI
12 * Copyright (C) 2007-2023 T.TAKASU
13 *
14 * SPDX-License-Identifier: BSD-2-Clause
15 *----------------------------------------------------------------------------*/
16#include "mrtklib/mrtk_spp.h"
17
18#include <math.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include "mrtklib/mrtk_atmos.h"
24#include "mrtklib/mrtk_coords.h"
25#include "mrtklib/mrtk_eph.h"
26#include "mrtklib/mrtk_mat.h"
27#include "mrtklib/mrtk_trace.h"
28
29/* local constants -----------------------------------------------------------*/
30static const double CLIGHT = 299792458.0;
31static const double D2R = 3.1415926535897932 / 180.0;
32static const double R2D = 180.0 / 3.1415926535897932;
33static const double OMGE = 7.2921151467E-5;
34
35static const double FREQ1 = 1.57542E9;
36static const double FREQ2 = 1.22760E9;
37static const double FREQ5 = 1.17645E9;
38static const double FREQ7 = 1.20714E9;
39static const double FREQ9 = 2.492028E9;
40static const double FREQ1_GLO = 1.60200E9;
41static const double FREQ2_GLO = 1.24600E9;
42static const double FREQ1_CMP = 1.561098E9;
43static const double FREQ2_CMP = 1.20714E9;
44
45static const double EFACT_GPS = 1.0;
46static const double EFACT_GLO = 1.5;
47static const double EFACT_SBS = 3.0;
48
49#define SYS_NONE 0x00
50#define SYS_GPS 0x01
51#define SYS_SBS 0x02
52#define SYS_GLO 0x04
53#define SYS_GAL 0x08
54#define SYS_QZS 0x10
55#define SYS_CMP 0x20
56#define SYS_IRN 0x40
57#define SYS_BD2 0x100
58
59/*--- forward declarations for legacy functions resolved at link time -------*/
60
61/* chi-sqr(n) table (alpha=0.001) — moved from rtkcmn.c */
62const double chisqr[100] = {10.8, 13.8, 16.3, 18.5, 20.5, 22.5, 24.3, 26.1, 27.9, 29.6, 31.3, 32.9, 34.5, 36.1, 37.7,
63 39.3, 40.8, 42.3, 43.8, 45.3, 46.8, 48.3, 49.7, 51.2, 52.6, 54.1, 55.5, 56.9, 58.3, 59.7,
64 61.1, 62.5, 63.9, 65.2, 66.6, 68.0, 69.3, 70.7, 72.1, 73.4, 74.7, 76.0, 77.3, 78.6, 80.0,
65 81.3, 82.6, 84.0, 85.4, 86.7, 88.0, 89.3, 90.6, 91.9, 93.3, 94.7, 96.0, 97.4, 98.7, 100,
66 101, 102, 103, 104, 105, 107, 108, 109, 110, 112, 113, 114, 115, 116, 118,
67 119, 120, 122, 123, 125, 126, 127, 128, 129, 131, 132, 133, 134, 135, 137,
68 138, 139, 140, 142, 143, 144, 145, 147, 148, 149};
69
70/* constants/macros ----------------------------------------------------------*/
71
72#define SQR(x) ((x) * (x))
73
74#define NT 7 /* # of estimated time system (gps,glo,gal,bds3,irn,qzs,bds2) */
75#define NX (3 + NT) /* # of estimated parameters */
76#define MAXITR 10 /* max number of iteration for point pos */
77#define ERR_CBIAS 0.3 /* code bias error Std (m) */
78#define ERR_TDCP 0.03 /* #116 P4: TDCP phase-rate error Std (m/s) */
79#define MIN_EL (5.0 * D2R) /* min elevation for measurement error (rad) */
80
81/* pseudorange measurement error variance ------------------------------------*/
82static double varerr(const prcopt_t* opt, double el, double snr, int sys) {
83 double fact, varr;
84 fact = sys == SYS_GLO ? EFACT_GLO : (sys == SYS_SBS ? EFACT_SBS : EFACT_GPS);
85 if (el < MIN_EL) {
86 el = MIN_EL;
87 }
88 varr = SQR(opt->err[0]) * (SQR(opt->err[1]) + SQR(opt->err[2]) / sin(el));
89 if (opt->ionoopt == IONOOPT_IFLC) {
90 varr *= SQR(3.0); /* iono-free */
91 }
92 varr = SQR(fact) * varr;
93
94 /* #116 P1: optional C/N0 (Sigma-epsilon) term, identical in form to the RTK
95 * varerr(). Off when err[6]<=0 (default) -> bit-identical to the previous
96 * elevation-only model; skipped when SNR is absent (snr==0) so a receiver
97 * that reports no C/N0 keeps the legacy behaviour. */
98 if (opt->err[6] > 0.0 && snr > 0.0) {
99 double e = fact * opt->err[6];
100 double d = opt->err[5] - snr; /* dB-Hz below snr_max */
101 varr += SQR(e) * pow(10.0, 0.1 * (d > 0.0 ? d : 0.0));
102 }
103 return varr;
104}
105/* get group delay parameter (m) ---------------------------------------------*/
106static double gettgd(int sat, const nav_t* nav, int type) {
107 int i, sys = satsys(sat, NULL);
108
109 if (sys == SYS_GLO) {
110 for (i = 0; i < nav->ng; i++) {
111 if (nav->geph[i].sat == sat) {
112 break;
113 }
114 }
115 return (i >= nav->ng) ? 0.0 : -nav->geph[i].dtaun * CLIGHT;
116 } else {
117 for (i = 0; i < nav->n; i++) {
118 if (nav->eph[i].sat == sat) {
119 break;
120 }
121 }
122 return (i >= nav->n) ? 0.0 : nav->eph[i].tgd[type] * CLIGHT;
123 }
124}
125/* index of the 2nd frequency for the iono-free combination -------------------
126 * Upstream claslib SPP uses L1-L5 for GAL/SBS and L1-L2 otherwise; apply that
127 * choice only while CLAS processing is active (the upstream profiles disagree
128 * here, and GAL/SBS rows without an E5a/L5 observation would otherwise drop
129 * out of non-CLAS SPP). Preserve the #135 IGS-product fallback for receivers
130 * whose conventional slot 1 is absent.
131 * nav may be NULL to skip the carrier-frequency validity check (e.g. for SNR
132 * masking, which only needs the observation slot). */
133static int iflc_freq2_idx(const obsd_t* obs, const nav_t* nav, const prcopt_t* opt) {
134 int sys = satsys(obs->sat, NULL);
135 int j;
136 if (opt->ionoopt == IONOOPT_IFLC && opt->correction == CORR_QZS_CLAS && NFREQ >= 3 && (sys & (SYS_GAL | SYS_SBS))) {
137 return 2;
138 }
139 if (opt->ionoopt == IONOOPT_IFLC && opt->correction == CORR_IGS && obs->P[1] == 0.0) {
140 for (j = 2; j < NFREQ; j++) {
141 if (obs->P[j] != 0.0 && (!nav || sat2freq(obs->sat, obs->code[j], nav) != 0.0)) {
142 return j;
143 }
144 }
145 }
146 return 1;
147}
148/* test SNR mask -------------------------------------------------------------*/
149static int snrmask(const obsd_t* obs, const double* azel, const prcopt_t* opt) {
150 if (testsnr(0, 0, azel[1], obs->SNR[0] * SNR_UNIT, &opt->snrmask)) {
151 return 0;
152 }
153 if (opt->ionoopt == IONOOPT_IFLC) {
154 int i2 = iflc_freq2_idx(obs, NULL, opt); /* #135: mask the actually-used 2nd freq */
155 if (testsnr(0, i2, azel[1], obs->SNR[i2] * SNR_UNIT, &opt->snrmask)) {
156 return 0;
157 }
158 }
159 return 1;
160}
161/* psendorange with code bias correction -------------------------------------*/
162static double prange(const obsd_t* obs, const nav_t* nav, const prcopt_t* opt, double* var) {
163 double P1, P2, gamma, b1, b2, freq1, freq2;
164 int sat, sys, i2 = 1;
165
166 sat = obs->sat;
167 sys = satsys(sat, NULL);
168 P1 = obs->P[0];
169 *var = 0.0;
170
171 /* #135: data-driven 2nd-frequency selection for IGS-product PPP. No-op when
172 * slot 1 is present, so all existing dual-frequency data is bit-identical. */
173 i2 = iflc_freq2_idx(obs, nav, opt);
174 P2 = obs->P[i2];
175
176 if (P1 == 0.0 || (opt->ionoopt == IONOOPT_IFLC && P2 == 0.0)) {
177 return 0.0;
178 }
179
180 freq1 = sat2freq(sat, obs->code[0], nav);
181 freq2 = sat2freq(sat, obs->code[i2], nav);
182 gamma = SQR(freq1 / freq2);
183
184 /* P1-C1,P2-C2 DCB correction */
185 if (sys == SYS_GPS || sys == SYS_GLO) {
186 if (obs->code[0] == CODE_L1C) {
187 P1 += nav->cbias[sat - 1][1]; /* C1->P1 */
188 }
189 if (obs->code[i2] == CODE_L2C) {
190 P2 += nav->cbias[sat - 1][2]; /* C2->P2 */
191 }
192 }
193 if (opt->ionoopt == IONOOPT_IFLC) { /* dual-frequency */
194
195 if (P1 == 0.0 || P2 == 0.0) {
196 return 0.0;
197 }
198
199 if (sys == SYS_GPS || sys == SYS_QZS) {
200 return (P2 - gamma * P1) / (1.0 - gamma);
201 } else if (sys == SYS_GLO) {
202 return (P2 - gamma * P1) / (1.0 - gamma);
203 } else if (sys == SYS_GAL) {
204 if (getseleph(SYS_GAL)) { /* F/NAV */
205 P2 -= gettgd(sat, nav, 0) - gettgd(sat, nav, 1); /* BGD_E5aE5b */
206 }
207 return (P2 - gamma * P1) / (1.0 - gamma);
208 } else if (sys == SYS_CMP) {
209 if (obs->code[0] == CODE_L2I) {
210 b1 = gettgd(sat, nav, 0); /* TGD_B1I */
211 } else if (obs->code[0] == CODE_L1P) {
212 b1 = gettgd(sat, nav, 2); /* TGD_B1Cp */
213 } else {
214 b1 = gettgd(sat, nav, 2) + gettgd(sat, nav, 4); /* TGD_B1Cp+ISC_B1Cd */
215 }
216 b2 = gettgd(sat, nav, 1); /* TGD_B2I/B2bI (m) */
217 return ((P2 - gamma * P1) - (b2 - gamma * b1)) / (1.0 - gamma);
218 } else if (sys == SYS_IRN) {
219 return (P2 - gamma * P1) / (1.0 - gamma);
220 }
221 } else { /* single-freq (L1/E1/B1) */
222 *var = SQR(ERR_CBIAS);
223
224 if (sys == SYS_GPS || sys == SYS_QZS) { /* L1 */
225 b1 = gettgd(sat, nav, 0); /* TGD (m) */
226 return P1 - b1;
227 } else if (sys == SYS_GLO) { /* G1 */
228 b1 = gettgd(sat, nav, 0); /* -dtaun (m) */
229 return P1 - b1 / (gamma - 1.0);
230 } else if (sys == SYS_GAL) { /* E1 */
231 if (getseleph(SYS_GAL)) {
232 b1 = gettgd(sat, nav, 0); /* BGD_E1E5a */
233 } else {
234 b1 = gettgd(sat, nav, 1); /* BGD_E1E5b */
235 }
236 return P1 - b1;
237 } else if (sys == SYS_CMP) { /* B1I/B1Cp/B1Cd */
238 if (obs->code[0] == CODE_L2I) {
239 b1 = gettgd(sat, nav, 0); /* TGD_B1I */
240 } else if (obs->code[0] == CODE_L1P) {
241 b1 = gettgd(sat, nav, 2); /* TGD_B1Cp */
242 } else {
243 b1 = gettgd(sat, nav, 2) + gettgd(sat, nav, 4); /* TGD_B1Cp+ISC_B1Cd */
244 }
245 return P1 - b1;
246 } else if (sys == SYS_IRN) { /* L5 */
247 b1 = gettgd(sat, nav, 0); /* TGD (m) */
248 return P1 - gamma * b1;
249 }
250 }
251 return P1;
252}
253/* ionocorr, tropcorr moved to mrtk_atmos.c */
254
255/* pseudorange residuals -----------------------------------------------------*/
256static int rescode(int iter, const obsd_t* obs, int n, const double* rs, const double* dts, const double* vare,
257 const int* svh, const nav_t* nav, const double* x, const prcopt_t* opt, double* v, double* H,
258 double* var, double* azel, int* vsat, double* resp, int* ns, const int* outp) {
259 gtime_t time;
260 double r, freq, dion = 0.0, dtrp = 0.0, vmeas, vion = 0.0, vtrp = 0.0, rr[3], pos[3], dtr;
261 double e[3], P, fact_ion;
262 int i, j, nv = 0, sat, sys, mask[NX - 3] = {0};
263
264 trace(NULL, 3, "resprng : n=%d\n", n);
265
266 for (i = 0; i < 3; i++) {
267 rr[i] = x[i];
268 }
269 dtr = x[3];
270
271 ecef2pos(rr, pos);
272
273 for (i = *ns = 0; i < n && i < MAXOBS; i++) {
274 vsat[i] = 0;
275 azel[i * 2] = azel[1 + i * 2] = resp[i] = 0.0;
276 time = obs[i].time;
277 sat = obs[i].sat;
278 if (!(sys = satsys_bd2(sat, NULL))) {
279 continue;
280 }
281
282 /* reject duplicated observation data */
283 if (i < n - 1 && i < MAXOBS - 1 && sat == obs[i + 1].sat) {
284 trace(NULL, 2, "duplicated obs data %s sat=%d\n", time_str(time, 3), sat);
285 i++;
286 continue;
287 }
288 /* excluded satellite? */
289 if (sat >= 1 && sat <= MAXSAT && outp && outp[sat - 1]) {
290 continue;
291 }
292 if (satexclude(sat, vare[i], svh[i], opt)) {
293 continue;
294 }
295
296 /* geometric distance */
297 if ((r = geodist(rs + i * 6, rr, e)) <= 0.0) {
298 continue;
299 }
300
301 if (iter > 0) {
302 /* test elevation mask */
303 if (satazel(pos, e, azel + i * 2) < opt->elmin) {
304 continue;
305 }
306
307 /* test SNR mask */
308 if (!snrmask(obs + i, azel + i * 2, opt)) {
309 continue;
310 }
311
312 /* ionospheric correction */
313 if (!ionocorr(time, nav, sat, pos, azel + i * 2, opt->ionoopt, &dion, &vion)) {
314 continue;
315 }
316 if ((freq = sat2freq(sat, obs[i].code[0], nav)) == 0.0) {
317 continue;
318 }
319 fact_ion = SQR(FREQ1 / freq);
320 dion *= fact_ion;
321 vion *= SQR(fact_ion);
322
323 /* tropospheric correction */
324 if (!tropcorr(time, nav, pos, azel + i * 2, opt->tropopt, &dtrp, &vtrp)) {
325 continue;
326 }
327 }
328 /* psendorange with code bias correction */
329 if ((P = prange(obs + i, nav, opt, &vmeas)) == 0.0) {
330 continue;
331 }
332
333 /* pseudorange residual */
334 v[nv] = P - (r + dtr - CLIGHT * dts[i * 2] + dion + dtrp);
335
336 /* design matrix */
337 for (j = 0; j < NX; j++) {
338 H[j + nv * NX] = j < 3 ? -e[j] : (j == 3 ? 1.0 : 0.0);
339 }
340 /* time system offset and receiver bias correction */
341 if (sys == SYS_GLO) {
342 v[nv] -= x[4];
343 H[4 + nv * NX] = 1.0;
344 mask[1] = 1;
345 } else if (sys == SYS_GAL) {
346 v[nv] -= x[5];
347 H[5 + nv * NX] = 1.0;
348 mask[2] = 1;
349 } else if (sys == SYS_CMP) {
350 v[nv] -= x[6];
351 H[6 + nv * NX] = 1.0;
352 mask[3] = 1;
353 } /* BDS-3 */
354 else if (sys == SYS_IRN) {
355 v[nv] -= x[7];
356 H[7 + nv * NX] = 1.0;
357 mask[4] = 1;
358 } else if (sys == SYS_QZS) {
359 v[nv] -= x[8];
360 H[8 + nv * NX] = 1.0;
361 mask[5] = 1;
362 } else if (sys == SYS_BD2) {
363 v[nv] -= x[9];
364 H[9 + nv * NX] = 1.0;
365 mask[6] = 1;
366 } /* BDS-2 */
367 else {
368 mask[0] = 1;
369 }
370
371 vsat[i] = 1;
372 resp[i] = v[nv];
373 (*ns)++;
374
375 /* variance of pseudorange error */
376 var[nv++] = varerr(opt, azel[1 + i * 2], obs[i].SNR[0] * SNR_UNIT, sys) + vare[i] + vmeas + vion + vtrp;
377
378 trace(NULL, 4, "sat=%2d azel=%5.1f %4.1f res=%7.3f sig=%5.3f\n", obs[i].sat, azel[i * 2] * R2D,
379 azel[1 + i * 2] * R2D, resp[i], sqrt(var[nv - 1]));
380 }
381 /* constraint to avoid rank-deficient */
382 for (i = 0; i < NT; i++) {
383 if (mask[i]) {
384 continue;
385 }
386 v[nv] = 0.0;
387 for (j = 0; j < NX; j++) {
388 H[j + nv * NX] = j == i + 3 ? 1.0 : 0.0;
389 }
390 var[nv++] = 0.01;
391 }
392 return nv;
393}
394/* validate solution ---------------------------------------------------------*/
395static int valsol(const double* azel, const int* vsat, int n, const prcopt_t* opt, const double* v, int nv, int nx,
396 char* msg) {
397 double azels[MAXOBS * 2], dop[4], vv;
398 int i, ns;
399
400 trace(NULL, 3, "valsol : n=%d nv=%d\n", n, nv);
401
402 /* Chi-square validation of residuals */
403 vv = dot(v, v, nv);
404 if (nv > nx && vv > chisqr[nv - nx - 1]) {
405 sprintf(msg, "chi-square error nv=%d vv=%.1f cs=%.1f", nv, vv, chisqr[nv - nx - 1]);
406 if (!opt->ign_chierr) {
407 return 0;
408 }
409 trace(NULL, 2, "ignore %s\n", msg);
410 }
411 /* large GDOP check */
412 for (i = ns = 0; i < n; i++) {
413 if (!vsat[i]) {
414 continue;
415 }
416 azels[ns * 2] = azel[i * 2];
417 azels[1 + ns * 2] = azel[1 + i * 2];
418 ns++;
419 }
420 dops(ns, azels, opt->elmin, dop);
421 trace(NULL, 4, "valsol : n=%d nv=%d vv=%.1f cs=%.1f maxgdop=%.1f gdop=%.1f pdop=%.1f hdop=%.1f vdop=%.1f\n", n, nv,
422 vv, nv > nx ? chisqr[nv - nx - 1] : 0.0, opt->maxgdop, dop[0], dop[1], dop[2], dop[3]);
423 if (dop[0] <= 0.0 || dop[0] > opt->maxgdop) {
424 sprintf(msg, "gdop error nv=%d gdop=%.1f", nv, dop[0]);
425 return 0;
426 }
427 return 1;
428}
429/* exclude satellites with large SPP pseudorange residuals ---------------------*/
430static void exclsat(const obsd_t* obs, const double* resp, const prcopt_t* opt, int n, const int* vsat, int* outp) {
431 double v2 = 0.0, thres = opt->rejethres * opt->rejethres;
432 int i, j;
433
434 for (i = j = 0; i < n && i < MAXOBS; i++) {
435 if (!vsat[i]) {
436 continue;
437 }
438 v2 += resp[i] * resp[i];
439 j++;
440 }
441 if (j == 0 || v2 / j > thres) {
442 return;
443 }
444 for (i = 0; i < n && i < MAXOBS; i++) {
445 int sat = obs[i].sat;
446 if (!vsat[i] || sat < 1 || sat > MAXSAT || resp[i] * resp[i] <= thres) {
447 continue;
448 }
449 outp[sat - 1] = 1;
450 trace(NULL, 2, "pntpos : excluded obs due to large residuals :sat=%2d res=%.2f rms=%.2f\n", sat, resp[i],
451 sqrt(v2 / j));
452 }
453}
454/* IGG-III three-segment equivalent-weight factor (#116 P2) ------------------
455 * rt = standardized residual; returns a multiplicative weight in [0,1].
456 * |rt|<=k0: full weight; k0<|rt|<=k1: smooth down-weight; |rt|>k1: reject. */
457static double igg3_weight(double rt, double k0, double k1) {
458 double a = fabs(rt), t;
459 if (a <= k0) {
460 return 1.0;
461 }
462 if (a <= k1) {
463 t = (k1 - a) / (k1 - k0);
464 return (k0 / a) * t * t;
465 }
466 return 0.0;
467}
468/* ascending compare for qsort of doubles ------------------------------------*/
469static int cmp_dbl(const void* a, const void* b) {
470 double d = *(const double*)a - *(const double*)b;
471 return (d < 0.0) ? -1 : (d > 0.0 ? 1 : 0);
472}
473/* median of |x[0..n-1]| using work[] as scratch (work may equal nothing live)*/
474static double median_abs(const double* x, int n, double* work) {
475 int i;
476 if (n <= 0) {
477 return 0.0;
478 }
479 for (i = 0; i < n; i++) {
480 work[i] = fabs(x[i]);
481 }
482 qsort(work, n, sizeof(double), cmp_dbl);
483 return (n % 2) ? work[n / 2] : 0.5 * (work[n / 2 - 1] + work[n / 2]);
484}
485/* estimate receiver position ------------------------------------------------*/
486static int estpos(const obsd_t* obs, int n, const double* rs, const double* dts, const double* vare, const int* svh,
487 const nav_t* nav, const prcopt_t* opt, sol_t* sol, double* azel, int* vsat, double* resp, char* msg) {
488 double x[NX] = {0}, dx[NX], Q[NX * NX], *v, *H, *var, *vpre, sig;
489 int i, j, k, info, stat = 1, nv = 0, ns;
490 int outp[MAXSAT] = {0};
491
492 trace(NULL, 3, "estpos : n=%d\n", n);
493
494 v = mat(n + NT, 1);
495 H = mat(NX, n + NT);
496 var = mat(n + NT, 1);
497 vpre = mat(n + NT, 1); /* #116 P3: pre-robust all-sat residuals for the acceptance gate */
498
499 for (i = 0; i < 3; i++) {
500 x[i] = sol->rr[i];
501 }
502
503 for (i = 0; i < MAXITR; i++) {
504 if (i > 0 && nv > opt->rejeminsat && opt->rejethres > 0.0) {
505 exclsat(obs, resp, opt, n, vsat, outp);
506 }
507 /* pseudorange residuals (m) */
508 nv = rescode(i, obs, n, rs, dts, vare, svh, nav, x, opt, v, H, var, azel, vsat, resp, &ns, outp);
509
510 if (nv < NX) {
511 sprintf(msg, "lack of valid sats ns=%d", nv);
512 break;
513 }
514 /* weighted by Std */
515 for (j = 0; j < nv; j++) {
516 sig = sqrt(var[j]);
517 v[j] /= sig;
518 for (k = 0; k < NX; k++) {
519 H[k + j * NX] /= sig;
520 }
521 }
522 /* #116 P2: IGG-III robust re-weighting of the pseudorange rows (0..ns-1;
523 * the trailing rank constraints are left untouched). Applied from the
524 * 2nd Gauss-Newton step (i>=1) so the clock is already estimated and the
525 * standardized residuals are meaningful. Off when opt->robust==0, so the
526 * default solution is bit-identical. var[] is stale here (recomputed by
527 * rescode next iteration), so it doubles as the median scratch buffer. */
528 int gated = (opt->robust == 1 && i >= 1 && ns >= 5);
529 if (gated) {
530 double k0 = opt->robustk[0] > 0.0 ? opt->robustk[0] : 1.5;
531 double k1 = opt->robustk[1] > 0.0 ? opt->robustk[1] : 4.0;
532 double s0;
533 /* #116 P3: snapshot the pre-robust residuals over ALL satellites for
534 * the acceptance gate. The gate must include the outlier rows the
535 * robust pass is about to suppress — otherwise it accepts epochs whose
536 * excluded sats disagree (consistent-bias urban epochs), which is the
537 * gate-defeat that exploded the tail in §4.3. The robust solution is
538 * still what gets output; only the accept/reject test uses vpre. */
539 for (j = 0; j < nv; j++) {
540 vpre[j] = v[j];
541 }
542 s0 = 1.4826 * median_abs(v, ns, var); /* MAD robust scale */
543 if (s0 < 1.0) {
544 s0 = 1.0; /* guard against over-rejection when residuals are small */
545 }
546 for (j = 0; j < ns; j++) {
547 double w = sqrt(igg3_weight(v[j] / s0, k0, k1));
548 v[j] *= w;
549 for (k = 0; k < NX; k++) {
550 H[k + j * NX] *= w;
551 }
552 }
553 }
554 /* least square estimation */
555 if ((info = lsq(H, v, NX, nv, dx, Q))) {
556 sprintf(msg, "lsq error info=%d", info);
557 break;
558 }
559 for (j = 0; j < NX; j++) {
560 x[j] += dx[j];
561 }
562 if (norm(dx, NX) < 1E-4) {
563 sol->type = 0;
564 sol->time = timeadd(obs[0].time, -x[3] / CLIGHT);
565 sol->dtr[0] = x[3] / CLIGHT; /* receiver clock bias (s) */
566 sol->dtr[1] = x[4] / CLIGHT; /* GLO-GPS time offset (s) */
567 sol->dtr[2] = x[5] / CLIGHT; /* GAL-GPS time offset (s) */
568 sol->dtr[3] = x[6] / CLIGHT; /* BDS3-GPS time offset (s) */
569 sol->dtr[4] = x[7] / CLIGHT; /* IRN-GPS time offset (s) */
570 sol->dtr[5] = x[8] / CLIGHT; /* QZS-GPS time offset (s) */
571 sol->dtr[6] = x[9] / CLIGHT; /* BDS2-GPS time offset (s) */
572 for (j = 0; j < 6; j++) {
573 sol->rr[j] = j < 3 ? x[j] : 0.0;
574 }
575 for (j = 0; j < 3; j++) {
576 sol->qr[j] = (float)Q[j + j * NX];
577 }
578 sol->qr[3] = (float)Q[1]; /* cov xy */
579 sol->qr[4] = (float)Q[2 + NX]; /* cov yz */
580 sol->qr[5] = (float)Q[2]; /* cov zx */
581 sol->ns = (uint8_t)ns;
582 sol->age = sol->ratio = 0.0;
583
584 /* validate solution (#116 P3: when the robust pass ran, gate on the
585 * pre-robust all-satellite residuals so the down-weighting cannot
586 * defeat the acceptance test) */
587 if (!opt->posopt[4] || (stat = valsol(azel, vsat, n, opt, gated ? vpre : v, nv, NX, msg))) {
588 sol->stat = opt->sateph == EPHOPT_SBAS ? SOLQ_SBAS : SOLQ_SINGLE;
589 }
590 free(v);
591 free(H);
592 free(var);
593 free(vpre);
594 return stat;
595 }
596 }
597 if (i >= MAXITR) {
598 sprintf(msg, "iteration divergent i=%d", i);
599 }
600
601 free(v);
602 free(H);
603 free(var);
604 free(vpre);
605 return 0;
606}
607/* RAIM FDE (failure detection and exclution) -------------------------------*/
608static int raim_fde(const obsd_t* obs, int n, const double* rs, const double* dts, const double* vare, const int* svh,
609 const nav_t* nav, const prcopt_t* opt, sol_t* sol, double* azel, int* vsat, double* resp,
610 char* msg) {
611 obsd_t* obs_e;
612 sol_t sol_e = {{0}};
613 char tstr[32], name[16], msg_e[128];
614 double *rs_e, *dts_e, *vare_e, *azel_e, *resp_e, rms_e, rms = 100.0;
615 int i, j, k, nvsat, stat = 0, *svh_e, *vsat_e, sat = 0;
616
617 trace(NULL, 3, "raim_fde: %s n=%2d\n", time_str(obs[0].time, 0), n);
618
619 if (!(obs_e = (obsd_t*)malloc(sizeof(obsd_t) * n))) {
620 return 0;
621 }
622 rs_e = mat(6, n);
623 dts_e = mat(2, n);
624 vare_e = mat(1, n);
625 azel_e = zeros(2, n);
626 svh_e = imat(1, n);
627 vsat_e = imat(1, n);
628 resp_e = mat(1, n);
629
630 for (i = 0; i < n; i++) {
631 /* satellite exclution */
632 for (j = k = 0; j < n; j++) {
633 if (j == i) {
634 continue;
635 }
636 obs_e[k] = obs[j];
637 matcpy(rs_e + 6 * k, rs + 6 * j, 6, 1);
638 matcpy(dts_e + 2 * k, dts + 2 * j, 2, 1);
639 vare_e[k] = vare[j];
640 svh_e[k++] = svh[j];
641 }
642 /* estimate receiver position without a satellite */
643 if (!estpos(obs_e, n - 1, rs_e, dts_e, vare_e, svh_e, nav, opt, &sol_e, azel_e, vsat_e, resp_e, msg_e)) {
644 trace(NULL, 3, "raim_fde: exsat=%2d (%s)\n", obs[i].sat, msg);
645 continue;
646 }
647 for (j = nvsat = 0, rms_e = 0.0; j < n - 1; j++) {
648 if (!vsat_e[j]) {
649 continue;
650 }
651 rms_e += SQR(resp_e[j]);
652 nvsat++;
653 }
654 if (nvsat < 5) {
655 trace(NULL, 3, "raim_fde: exsat=%2d lack of satellites nvsat=%2d\n", obs[i].sat, nvsat);
656 continue;
657 }
658 rms_e = sqrt(rms_e / nvsat);
659
660 trace(NULL, 3, "raim_fde: exsat=%2d rms=%8.3f\n", obs[i].sat, rms_e);
661
662 if (rms_e > rms) {
663 continue;
664 }
665
666 /* save result */
667 for (j = k = 0; j < n; j++) {
668 if (j == i) {
669 continue;
670 }
671 matcpy(azel + 2 * j, azel_e + 2 * k, 2, 1);
672 vsat[j] = vsat_e[k];
673 resp[j] = resp_e[k++];
674 }
675 stat = 1;
676 *sol = sol_e;
677 sat = obs[i].sat;
678 rms = rms_e;
679 vsat[i] = 0;
680 strcpy(msg, msg_e);
681 }
682 if (stat) {
683 time2str(obs[0].time, tstr, 2);
684 satno2id(sat, name);
685 trace(NULL, 2, "%s: %s excluded by raim\n", tstr + 11, name);
686 }
687 free(obs_e);
688 free(rs_e);
689 free(dts_e);
690 free(vare_e);
691 free(azel_e);
692 free(svh_e);
693 free(vsat_e);
694 free(resp_e);
695 return stat;
696}
697/* SPP cycle-slip detection for TDCP (#116 P4) -------------------------------
698 * Sets slip[i]=1 for obs[i] that lost lock (LLI) or whose L1 phase rate
699 * disagrees with the Doppler beyond thresdop after removing the common-mode
700 * (receiver clock) offset — the single-receiver detector from demo5/detslp_dop,
701 * kept SPP-local so PPP/RTK are untouched. ssat holds the previous epoch's
702 * phase in the pntpos-private spt/sph slots, populated at the end of each
703 * pntpos call. pt[0]/ph[0] must NOT be used here: they belong to the engines'
704 * own slip detectors (detslp_ll/detslp_dop), and overwriting them from pntpos
705 * silently disabled rover slip detection in relative RTK (issue #318). */
706static void spp_detslp(const obsd_t* obs, int n, const ssat_t* ssat, double thresdop, int* slip) {
707 double dph, dpt, tt, mean = 0.0, dif[MAXOBS] = {0};
708 int i, sat, ndop = 0, valid[MAXOBS] = {0};
709
710 for (i = 0; i < n && i < MAXOBS; i++) {
711 slip[i] = (obs[i].LLI[0] & 1) ? 1 : 0; /* loss-of-lock indicator */
712 sat = obs[i].sat;
713 if (thresdop <= 0.0 || obs[i].L[0] == 0.0 || obs[i].D[0] == 0.0 || ssat[sat - 1].sph[0] == 0.0) {
714 continue;
715 }
716 tt = timediff(obs[i].time, ssat[sat - 1].spt[0]);
717 if (fabs(tt) < DTTOL || fabs(tt) > 3.0) {
718 continue;
719 }
720 dph = (obs[i].L[0] - ssat[sat - 1].sph[0]) / tt; /* phase rate (cyc/s) */
721 dpt = -obs[i].D[0]; /* Doppler-predicted rate */
722 dif[i] = dph - dpt;
723 valid[i] = 1; /* explicit validity — dif can legitimately be 0.0 */
724 if (fabs(dif[i]) < 3.0 * thresdop) {
725 mean += dif[i];
726 ndop++;
727 }
728 }
729 if (ndop == 0) {
730 return;
731 }
732 mean /= ndop; /* common-mode (receiver clock) drift */
733 for (i = 0; i < n && i < MAXOBS; i++) {
734 if (valid[i] && fabs(dif[i] - mean) > thresdop) {
735 slip[i] = 1;
736 }
737 }
738}
739/* range rate residuals ------------------------------------------------------*/
740/* #116 P4: per satellite use the TDCP phase rate (mm/s-class) when the phase is
741 * locked and unslipped, otherwise fall back to the Doppler (preserving the
742 * Doppler-absence invariant). ssat/slip may be NULL/absent → pure Doppler. */
743static int resdop(const obsd_t* obs, int n, const double* rs, const double* dts, const nav_t* nav, const double* rr,
744 const double* x, const double* azel, const int* vsat, double err, const ssat_t* ssat, const int* slip,
745 int tdcp, double* v, double* H) {
746 double freq, rate, pos[3], E[9], a[3], e[3], vs[3], cosel, sig, lam, meas, tt;
747 int i, j, nv = 0, sat, use;
748
749 trace(NULL, 3, "resdop : n=%d\n", n);
750
751 ecef2pos(rr, pos);
752 xyz2enu(pos, E);
753
754 for (i = 0; i < n && i < MAXOBS; i++) {
755 freq = sat2freq(obs[i].sat, obs[i].code[0], nav);
756 if (freq == 0.0 || !vsat[i] || norm(rs + 3 + i * 6, 3) <= 0.0) {
757 continue;
758 }
759 lam = CLIGHT / freq;
760 sat = obs[i].sat;
761
762 /* measured range rate (cyc/s) and its Std (m/s): TDCP primary, Doppler fallback */
763 use = 0;
764 meas = sig = 0.0;
765 if (tdcp && ssat && slip && !slip[i] && obs[i].L[0] != 0.0 && ssat[sat - 1].sph[0] != 0.0) {
766 tt = timediff(obs[i].time, ssat[sat - 1].spt[0]);
767 if (fabs(tt) >= DTTOL && fabs(tt) <= 3.0) {
768 meas = (obs[i].L[0] - ssat[sat - 1].sph[0]) / tt; /* == dph (cyc/s) */
769 sig = ERR_TDCP; /* phase-rate Std (m/s) */
770 use = 1;
771 }
772 }
773 if (!use && obs[i].D[0] != 0.0) {
774 meas = -obs[i].D[0]; /* Doppler-predicted rate (cyc/s) */
775 sig = (err <= 0.0) ? 1.0 : err * lam; /* m/s */
776 use = 1;
777 }
778 if (!use) {
779 continue;
780 }
781 /* LOS (line-of-sight) vector in ECEF */
782 cosel = cos(azel[1 + i * 2]);
783 a[0] = sin(azel[i * 2]) * cosel;
784 a[1] = cos(azel[i * 2]) * cosel;
785 a[2] = sin(azel[1 + i * 2]);
786 matmul("TN", 3, 1, 3, 1.0, E, a, 0.0, e);
787
788 /* satellite velocity relative to receiver in ECEF */
789 for (j = 0; j < 3; j++) {
790 vs[j] = rs[j + 3 + i * 6] - x[j];
791 }
792 /* range rate with earth rotation correction */
793 rate =
794 dot(vs, e, 3) +
795 OMGE / CLIGHT * (rs[4 + i * 6] * rr[0] + rs[1 + i * 6] * x[0] - rs[3 + i * 6] * rr[1] - rs[i * 6] * x[1]);
796
797 /* range rate residual (m/s) */
798 v[nv] = (meas * lam - (rate + x[3] - CLIGHT * dts[1 + i * 2])) / sig;
799
800 /* design matrix */
801 for (j = 0; j < 4; j++) {
802 H[j + nv * 4] = ((j < 3) ? -e[j] : 1.0) / sig;
803 }
804 nv++;
805 }
806 return nv;
807}
808/* estimate receiver velocity ------------------------------------------------*/
809/* returns 1 if the velocity converged (sol->rr[3..5] written), 0 otherwise */
810static int estvel(const obsd_t* obs, int n, const double* rs, const double* dts, const nav_t* nav, const prcopt_t* opt,
811 sol_t* sol, const double* azel, const int* vsat, const ssat_t* ssat, const int* slip) {
812 double x[4] = {0}, dx[4], Q[16], *v, *H;
813 double err = opt->err[4]; /* Doppler error (Hz) */
814 int i, j, nv, stat = 0, tdcp = (opt->tdcp == 1 && ssat != NULL);
815
816 trace(NULL, 3, "estvel : n=%d\n", n);
817
818 v = mat(n, 1);
819 H = mat(4, n);
820
821 for (i = 0; i < MAXITR; i++) {
822 /* range rate residuals (m/s) */
823 if ((nv = resdop(obs, n, rs, dts, nav, sol->rr, x, azel, vsat, err, ssat, slip, tdcp, v, H)) < 4) {
824 break;
825 }
826 /* least square estimation */
827 if (lsq(H, v, 4, nv, dx, Q)) {
828 break;
829 }
830
831 for (j = 0; j < 4; j++) {
832 x[j] += dx[j];
833 }
834
835 if (norm(dx, 4) < 1E-6) {
836 matcpy(sol->rr + 3, x, 3, 1);
837 sol->qv[0] = (float)Q[0]; /* xx */
838 sol->qv[1] = (float)Q[5]; /* yy */
839 sol->qv[2] = (float)Q[10]; /* zz */
840 sol->qv[3] = (float)Q[1]; /* xy */
841 sol->qv[4] = (float)Q[6]; /* yz */
842 sol->qv[5] = (float)Q[2]; /* zx */
843 stat = 1;
844 break;
845 }
846 }
847 free(v);
848 free(H);
849 return stat;
850}
851/* single-point positioning ----------------------------------------------------
852 * compute receiver position, velocity, clock bias by single-point positioning
853 * with pseudorange and doppler observables
854 * args : mrtk_ctx_t *ctx I MRTKLIB context (for trace logging)
855 * obsd_t *obs I observation data
856 * int n I number of observation data
857 * nav_t *nav I navigation data
858 * prcopt_t *opt I processing options
859 * sol_t *sol IO solution
860 * double *azel IO azimuth/elevation angle (rad) (NULL: no output)
861 * ssat_t *ssat IO satellite status (NULL: no output)
862 * char *msg O error message for error exit
863 * return : status(1:ok,0:error)
864 *-----------------------------------------------------------------------------*/
865int pntpos(mrtk_ctx_t* ctx, const obsd_t* obs, int n, const nav_t* nav, const prcopt_t* opt, sol_t* sol, double* azel,
866 ssat_t* ssat, char* msg) {
867 prcopt_t opt_ = *opt;
868 double *rs, *dts, *var, *azel_, *resp;
869 int i, stat, vsat[MAXOBS] = {0}, svh[MAXOBS], slip[MAXOBS] = {0};
870 double prev_rr[3];
871 gtime_t prev_time;
872 int has_prev;
873
874 trace(ctx, 3, "pntpos : tobs=%s n=%d\n", time_str(obs[0].time, 3), n);
875
876 sol->stat = SOLQ_NONE;
877
878 if (n <= 0) {
879 strcpy(msg, "no observation data");
880 return 0;
881 }
882 /* #116 P4: snapshot the previous solution (for TDCP jump-rejection) before it
883 * is overwritten by this epoch */
884 prev_time = sol->time;
885 matcpy(prev_rr, sol->rr, 3, 1);
886 has_prev = (prev_time.time != 0 && norm(prev_rr, 3) > 0.0);
887
888 sol->time = obs[0].time;
889 msg[0] = '\0';
890
891 rs = mat(6, n);
892 dts = mat(2, n);
893 var = mat(1, n);
894 azel_ = zeros(2, n);
895 resp = mat(1, n);
896
897 if (opt_.mode != PMODE_SINGLE) { /* for precise positioning */
898 if (opt_.nf > 1) {
899 opt_.ionoopt = IONOOPT_IFLC;
900 } else {
901 opt_.ionoopt = IONOOPT_BRDC;
902 }
903 opt_.tropopt = TROPOPT_SAAS;
904 }
905 /* satellite positons, velocities and clocks */
906 satposs(sol->time, obs, n, nav, opt_.sateph, rs, dts, var, svh);
907
908 /* estimate receiver position with pseudorange */
909 stat = estpos(obs, n, rs, dts, var, svh, nav, &opt_, sol, azel_, vsat, resp, msg);
910
911 /* RAIM FDE */
912 if (!stat && n >= 6 && opt->posopt[4]) {
913 stat = raim_fde(obs, n, rs, dts, var, svh, nav, &opt_, sol, azel_, vsat, resp, msg);
914 }
915 /* #116 P4: cycle-slip detection so TDCP only uses slip-free phase */
916 if (opt_.tdcp == 1 && ssat) {
917 spp_detslp(obs, n, ssat, opt_.thresdop, slip);
918 }
919 /* estimate receiver velocity (TDCP primary, Doppler fallback) */
920 if (stat) {
921 int velok = estvel(obs, n, rs, dts, nav, &opt_, sol, azel_, vsat, ssat, slip);
922
923 /* #116 P4b: TDCP jump-rejection — drop epochs whose code position change
924 * disagrees with the TDCP-derived displacement (velocity * dt). Catches
925 * the "jumpy" tail; the stable-bias tail is the EKF's remit (P6).
926 * Requires a valid velocity this epoch (estvel converged) — otherwise
927 * sol->rr[3..5] is stale/zero and would falsely reject moving epochs. */
928 if (opt_.tdcp == 1 && ssat && has_prev && velok) {
929 double tt = timediff(obs[0].time, prev_time);
930 if (fabs(tt) > DTTOL && fabs(tt) <= 3.0) {
931 double d[3], dn, thr = opt_.tdcpjump > 0.0 ? opt_.tdcpjump : 5.0;
932 for (i = 0; i < 3; i++) {
933 d[i] = (sol->rr[i] - prev_rr[i]) - sol->rr[i + 3] * tt;
934 }
935 dn = norm(d, 3);
936 if (dn > thr) {
937 sprintf(msg, "tdcp jump reject %.1fm", dn);
938 trace(ctx, 2, "pntpos : %s\n", msg);
939 stat = 0;
940 sol->stat = SOLQ_NONE;
941 }
942 }
943 }
944 }
945 if (azel) {
946 for (i = 0; i < n * 2; i++) {
947 azel[i] = azel_[i];
948 }
949 }
950 if (ssat) {
951 for (i = 0; i < MAXSAT; i++) {
952 ssat[i].vs = 0;
953 ssat[i].azel[0] = ssat[i].azel[1] = 0.0;
954 ssat[i].resp[0] = ssat[i].resc[0] = 0.0;
955 ssat[i].snr[0] = 0;
956 }
957 for (i = 0; i < n; i++) {
958 int f;
959 ssat[obs[i].sat - 1].azel[0] = azel_[i * 2];
960 ssat[obs[i].sat - 1].azel[1] = azel_[1 + i * 2];
961 ssat[obs[i].sat - 1].snr[0] = obs[i].SNR[0];
962 /* #116 P4: store carrier phase for next-epoch TDCP / slip detection
963 * (unconditional — keep continuity even for this epoch's unused sats).
964 * Private spt/sph slots: writing pt[0]/ph[0] here clobbered the
965 * engines' previous-phase bookkeeping every epoch, disabling rover
966 * detslp_ll/detslp_dop in relative RTK and detslp_dop in PPP
967 * (issue #318). */
968 for (f = 0; f < NFREQ; f++) {
969 ssat[obs[i].sat - 1].sph[f] = obs[i].L[f];
970 ssat[obs[i].sat - 1].spt[f] = obs[i].time;
971 }
972 if (!vsat[i]) {
973 continue;
974 }
975 ssat[obs[i].sat - 1].vs = 1;
976 ssat[obs[i].sat - 1].resp[0] = resp[i];
977 }
978 }
979 free(rs);
980 free(dts);
981 free(var);
982 free(azel_);
983 free(resp);
984 return stat;
985}
int ionocorr(gtime_t time, const nav_t *nav, int sat, const double *pos, const double *azel, int ionoopt, double *ion, double *var)
Compute ionospheric correction.
Definition mrtk_atmos.c:310
int tropcorr(gtime_t time, const nav_t *nav, const double *pos, const double *azel, int tropopt, double *trp, double *var)
Compute tropospheric correction.
Definition mrtk_atmos.c:350
MRTKLIB Atmosphere Module — Ionospheric and tropospheric models.
void satno2id(int sat, char *id)
Definition mrtk_nav.c:817
double sat2freq(int sat, uint8_t code, const nav_t *nav)
Definition mrtk_obs.c:244
double geodist(const double *rs, const double *rr, double *e)
Compute geometric distance and receiver-to-satellite unit vector.
#define D2R
Definition mrtk_coords.c:36
double satazel(const double *pos, const double *e, double *azel)
Compute satellite azimuth/elevation angle.
#define CLIGHT
Definition mrtk_coords.c:41
void xyz2enu(const double *pos, double *E)
Compute ECEF to local coordinate transformation matrix.
#define MAXSAT
Definition mrtk_coords.c:45
#define OMGE
Definition mrtk_coords.c:40
void dops(int ns, const double *azel, double elmin, double *dop)
Compute DOP (dilution of precision).
void ecef2pos(const double *r, double *pos)
Transform ECEF position to geodetic position.
MRTKLIB Coordinates Module — Coordinate transformations and geometry.
int satsys(int sat, int *prn)
Definition mrtk_nav.c:117
int getseleph(int sys)
Get selected satellite ephemeris.
Definition mrtk_eph.c:1040
#define SYS_GPS
Definition mrtk_eph.c:65
#define SYS_SBS
Definition mrtk_eph.c:66
#define SYS_QZS
Definition mrtk_eph.c:69
void satposs(gtime_t teph, const obsd_t *obs, int n, const nav_t *nav, int ephopt, double *rs, double *dts, double *var, int *svh)
Compute satellite positions, velocities and clocks.
Definition mrtk_eph.c:940
#define SYS_IRN
Definition mrtk_eph.c:71
#define SYS_CMP
Definition mrtk_eph.c:70
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
#define EPHOPT_SBAS
Definition mrtk_eph.c:85
MRTKLIB Broadcast Ephemeris Module — Satellite orbit/clock types and Kepler/GLONASS/SBAS orbit comput...
struct nav_s nav_t
Definition mrtk_eph.h:40
#define SNR_UNIT
#define MAXOBS
#define CODE_L2I
#define CODE_L1C
#define CODE_L2C
#define DTTOL
#define CODE_L1P
#define MIN_EL
Definition mrtk_ionex.c:34
double * mat(int n, int m)
Allocate memory for a double matrix.
Definition mrtk_mat.c:76
double * zeros(int n, int m)
Allocate a new zero matrix.
Definition mrtk_mat.c:108
int lsq(const double *A, const double *y, int n, int m, double *x, double *Q)
Least square estimation by solving normal equation.
Definition mrtk_mat.c:470
void matcpy(double *A, const double *B, int n, int m)
Copy matrix.
Definition mrtk_mat.c:205
int * imat(int n, int m)
Allocate memory for an integer matrix.
Definition mrtk_mat.c:92
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
void matmul(const char *tr, int n, int k, int m, double alpha, const double *A, const double *B, double beta, double *C)
Multiply matrix by matrix (C = alpha*A*B + beta*C).
Definition mrtk_mat.c:310
double dot(const double *a, const double *b, int n)
Inner product of vectors.
Definition mrtk_mat.c:151
MRTKLIB Matrix Module — Matrix/vector operations and estimation.
int satsys_bd2(int sat, int *prn)
Distinguish BDS-2 from BDS-3.
Definition mrtk_nav.c:159
int satexclude(int sat, double var, int svh, const struct prcopt_t *opt)
Test excluded satellite.
Definition mrtk_nav.c:1169
#define FREQ5
Definition mrtk_obs.c:69
#define FREQ2
Definition mrtk_obs.c:68
#define FREQ2_CMP
Definition mrtk_obs.c:82
#define FREQ9
Definition mrtk_obs.c:73
#define R2D
Definition mrtk_obs.c:51
#define SYS_BD2
Definition mrtk_obs.c:62
#define FREQ7
Definition mrtk_obs.c:71
#define FREQ1
Definition mrtk_obs.c:67
#define FREQ2_GLO
Definition mrtk_obs.c:76
#define FREQ1_GLO
Definition mrtk_obs.c:74
#define FREQ1_CMP
Definition mrtk_obs.c:81
int testsnr(int base, int idx, double el, double snr, const snrmask_t *mask)
Test SNR mask.
Definition mrtk_obs.c:132
#define SOLQ_SINGLE
Definition mrtk_opt.h:75
#define TROPOPT_SAAS
Definition mrtk_opt.h:103
#define SOLQ_NONE
Definition mrtk_opt.h:70
#define IONOOPT_BRDC
Definition mrtk_opt.h:93
#define CORR_IGS
Definition mrtk_opt.h:117
#define SOLQ_SBAS
Definition mrtk_opt.h:73
#define IONOOPT_IFLC
Definition mrtk_opt.h:95
#define CORR_QZS_CLAS
Definition mrtk_opt.h:122
#define PMODE_SINGLE
Definition mrtk_opt.h:45
#define NX(opt)
Definition mrtk_ppp.c:224
#define NT(opt)
static const double chisqr[100]
#define EFACT_GPS
Definition mrtk_rtkpos.c:60
#define EFACT_GLO
Definition mrtk_rtkpos.c:61
#define EFACT_SBS
Definition mrtk_rtkpos.c:62
static double varerr(const prcopt_t *opt, double el, double snr, int sys)
Definition mrtk_spp.c:82
static void spp_detslp(const obsd_t *obs, int n, const ssat_t *ssat, double thresdop, int *slip)
Definition mrtk_spp.c:706
#define MAXITR
Definition mrtk_spp.c:76
static int snrmask(const obsd_t *obs, const double *azel, const prcopt_t *opt)
Definition mrtk_spp.c:149
static double igg3_weight(double rt, double k0, double k1)
Definition mrtk_spp.c:457
static double median_abs(const double *x, int n, double *work)
Definition mrtk_spp.c:474
static double gettgd(int sat, const nav_t *nav, int type)
Definition mrtk_spp.c:106
static int cmp_dbl(const void *a, const void *b)
Definition mrtk_spp.c:469
static int valsol(const double *azel, const int *vsat, int n, const prcopt_t *opt, const double *v, int nv, int nx, char *msg)
Definition mrtk_spp.c:395
static double prange(const obsd_t *obs, const nav_t *nav, const prcopt_t *opt, double *var)
Definition mrtk_spp.c:162
static int raim_fde(const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const prcopt_t *opt, sol_t *sol, double *azel, int *vsat, double *resp, char *msg)
Definition mrtk_spp.c:608
static int resdop(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const double *rr, const double *x, const double *azel, const int *vsat, double err, const ssat_t *ssat, const int *slip, int tdcp, double *v, double *H)
Definition mrtk_spp.c:743
#define SQR(x)
Definition mrtk_spp.c:72
static int estvel(const obsd_t *obs, int n, const double *rs, const double *dts, const nav_t *nav, const prcopt_t *opt, sol_t *sol, const double *azel, const int *vsat, const ssat_t *ssat, const int *slip)
Definition mrtk_spp.c:810
static int iflc_freq2_idx(const obsd_t *obs, const nav_t *nav, const prcopt_t *opt)
Definition mrtk_spp.c:133
static void exclsat(const obsd_t *obs, const double *resp, const prcopt_t *opt, int n, const int *vsat, int *outp)
Definition mrtk_spp.c:430
int pntpos(mrtk_ctx_t *ctx, const obsd_t *obs, int n, const nav_t *nav, const prcopt_t *opt, sol_t *sol, double *azel, ssat_t *ssat, char *msg)
Compute receiver position, velocity, clock bias by single-point positioning with pseudorange and dopp...
Definition mrtk_spp.c:865
#define ERR_CBIAS
Definition mrtk_spp.c:77
static int rescode(int iter, const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const double *x, const prcopt_t *opt, double *v, double *H, double *var, double *azel, int *vsat, double *resp, int *ns, const int *outp)
Definition mrtk_spp.c:256
#define ERR_TDCP
Definition mrtk_spp.c:78
static int estpos(const obsd_t *obs, int n, const double *rs, const double *dts, const double *vare, const int *svh, const nav_t *nav, const prcopt_t *opt, sol_t *sol, double *azel, int *vsat, double *resp, char *msg)
Definition mrtk_spp.c:486
MRTKLIB Single-Point Positioning Module — SPP functions.
void time2str(gtime_t t, char *s, int n)
Convert gtime_t struct to string.
Definition mrtk_time.c:507
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
gtime_t timeadd(gtime_t t, double sec)
Add seconds to gtime_t.
Definition mrtk_time.c:265
char * time_str(gtime_t t, int n)
Get time string (not reentrant).
Definition mrtk_time.c:530
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.
int sat
Definition mrtk_eph.h:78
double tgd[6]
Definition mrtk_eph.h:95
double dtaun
Definition mrtk_eph.h:124
int sat
Definition mrtk_eph.h:112
GNSS time representation.
Definition mrtk_time.h:49
time_t time
Definition mrtk_time.h:50
Runtime context for MRTKLIB processing.
int ng
Definition mrtk_nav.h:394
geph_t * geph
Definition mrtk_nav.h:407
double cbias[MAXSAT][3]
Definition mrtk_nav.h:427
eph_t * eph
Definition mrtk_nav.h:400
int n
Definition mrtk_nav.h:393
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
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
double thresdop
Definition mrtk_opt.h:215
snrmask_t snrmask
Definition mrtk_opt.h:177
double elmin
Definition mrtk_opt.h:176
double robustk[2]
Definition mrtk_opt.h:304
double maxgdop
Definition mrtk_opt.h:218
int ionoopt
Definition mrtk_opt.h:187
double tdcpjump
Definition mrtk_opt.h:308
double err[8]
Definition mrtk_opt.h:201
int tropopt
Definition mrtk_opt.h:188
int tdcp
Definition mrtk_opt.h:307
int rejeminsat
Definition mrtk_opt.h:321
int posopt[13]
Definition mrtk_opt.h:237
int ign_chierr
Definition mrtk_opt.h:226
double rejethres
Definition mrtk_opt.h:320
int mode
Definition mrtk_opt.h:172
int robust
Definition mrtk_opt.h:303
int correction
Definition mrtk_opt.h:300
int sateph
Definition mrtk_opt.h:178
float ratio
Definition mrtk_sol.h:57
float qr[6]
Definition mrtk_sol.h:48
double rr[6]
Definition mrtk_sol.h:46
uint8_t type
Definition mrtk_sol.h:53
uint8_t stat
Definition mrtk_sol.h:54
double dtr[NSYS+1]
Definition mrtk_sol.h:52
float age
Definition mrtk_sol.h:56
uint8_t ns
Definition mrtk_sol.h:55
float qv[6]
Definition mrtk_sol.h:51
gtime_t time
Definition mrtk_sol.h:45
gtime_t spt[5]
Definition mrtk_sol.h:118
uint8_t vs
Definition mrtk_sol.h:100
double azel[2]
Definition mrtk_sol.h:101
double resp[5]
Definition mrtk_sol.h:102
double sph[5]
Definition mrtk_sol.h:121
uint16_t snr[5]
Definition mrtk_sol.h:105
double resc[5]
Definition mrtk_sol.h:103
#define malloc(x)
Definition toml.c:52
#define free(x)
Definition toml.c:53