MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_ppp_iono.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_ppp_iono.c : PPP ionospheric correction 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 * references :
17 * [1] CAO IS-QZSS-MDC-002, November, 2023
18 *
19 * version : $Revision:$ $Date:$
20 * history : 2024/01/10 1.0 new, for MADOCALIB
21 * 2024/05/24 1.1 algorithm correction for const_iono_corr().
22 * change convergence threshold (H,V) from
23 * 0.3,0.5 to 2.0,3.0.
24 * 2024/09/27 1.2 delete NM
25 * 2025/03/25 1.3 unuse mapping function.
26 *-----------------------------------------------------------------------------*/
27#include <math.h>
28#include <stdio.h>
29#include <string.h>
30
31#include "mrtklib/mrtk_const.h"
33#include "mrtklib/mrtk_coords.h"
34#include "mrtklib/mrtk_mat.h"
35#include "mrtklib/mrtk_nav.h"
36#include "mrtklib/mrtk_obs.h"
37#include "mrtklib/mrtk_opt.h"
38#include "mrtklib/mrtk_rtkpos.h"
39#include "mrtklib/mrtk_trace.h"
40
41/* forward declarations */
42extern int satsys(int sat, int* prn);
43extern int satid2no(const char* id);
44extern void satno2id(int sat, char* id);
45extern char* time_str(gtime_t t, int n);
46extern double timediff(gtime_t t1, gtime_t t2);
47extern gtime_t gpst2time(int week, double sec);
48extern void ecef2pos(const double* r, double* pos);
49extern void covenu(const double* pos, const double* P, double* Q);
50extern double sat2freq(int sat, uint8_t code, const nav_t* nav);
51
52#define IONO_REJ_STD 1.0 /* reject STEC corr. std (m) */
53#define IONO_COV_RATIO 1.0 /* ratio of STEC corr. cov (m) */
54#define IONO_THRE_H 2.0 /* Horizontal convergence threshold (m) */
55#define IONO_THRE_V 3.0 /* Vertical convergence threshold (m) */
56
57#define SQR(x) ((x) * (x))
58#define SQRT(x) ((x) <= 0.0 || (x) != (x) ? 0.0 : sqrt(x))
59
60/* number and index of states */
61#define NP(opt) ((opt)->dynamics ? 9 : 3)
62#define NC(opt) (NSYS + 1) /* BDS3 and BDS2 */
63#define NT(opt) ((opt)->tropopt < TROPOPT_EST ? 0 : ((opt)->tropopt == TROPOPT_EST ? 1 : 3))
64#define II(s, opt) (NP(opt) + NC(opt) + NT(opt) + (s) - 1)
65
66/* standard deviation by STEC URA --------------------------------------------*/
67static double miono_std_stecura(int ura) {
68 /* ref [1] 6.3.2.3 (5) */
69 if (ura <= 0) {
70 return 5.4665; /* STEC URA undefined/unknown */
71 }
72 if (ura >= 63) {
73 return 5.4665;
74 }
75 return (pow(3.0, (ura >> 3) & 7) * (1.0 + (ura & 7) / 4.0) - 1.0) * 1E-3;
76}
77
78/* L1 ionospheric delay ------------------------------------------------------*/
79static double miono_delay(const nav_t* nav, const int sat, const miono_area_t* a, const double* ll) {
80 const double* c = a->sat[sat - 1].coef;
81 double freq, fact, stec, delay;
82
83 freq = sat2freq(sat, CODE_L1C, nav);
84
85 /* ref [1] 6.3.2.3 (5) */
86 fact = 40.31E16 / freq / freq; /* TECU -> L1 ionospheric delay(m) */
87 stec = c[0]; /* STEC poly.coef.{C00,C01,C10,C11,C02,C20} */
88 if (a->type >= 1) {
89 stec += c[1] * (ll[0] - a->ref[0]) + c[2] * (ll[1] - a->ref[1]);
90 }
91 if (a->type >= 2) {
92 stec += c[3] * (ll[0] - a->ref[0]) * (ll[1] - a->ref[1]);
93 }
94 if (a->type >= 3) {
95 stec += c[4] * SQR(ll[0] - a->ref[0]) + c[2] * SQR(ll[1] - a->ref[1]);
96 }
97
98 delay = fact * stec;
99 trace(NULL, 5, "miono_delay: sat=%d,fact=%5.3f,stec=%8.3f,delay=%7.3f,coef=%7.2f,%7.2f,%7.2f,%7.2f,%8.3f,%8.3f\n",
100 sat, fact, stec, delay, c[0], c[1], c[2], c[3], c[4], c[5]);
101 return delay;
102}
103
104/* select MADOCA-PPP ionospheric correction area -----------------------------*/
105static miono_area_t* miono_sel_area(pppiono_t* pppiono, const double* rr, const double* ll) {
106 const miono_region_t* re;
107 int i, j, k, min_i = -1, min_j = 0;
108 double ref_ecef[3], ref_llh[3] = {0}, diff[3], dist, min_dist = 1E5;
109
110 trace(NULL, 2, "miono_sel_area: lat=%.2f,lon=%.2f\n", ll[0], ll[1]);
111
112 for (i = 0; i < MIONO_MAX_RID; i++) {
113 re = &pppiono->re[i];
114 if (!re->rvalid) {
115 continue;
116 }
117 trace(NULL, 5, "miono_sel_area: region i=%d,ralert=%d\n", i, re->ralert);
118 if (re->ralert) {
119 continue;
120 }
121
122 for (j = 0; j < MIONO_MAX_ANUM; j++) {
123 if (!re->area[j].avalid) {
124 continue;
125 }
126 for (k = 0; k < 2; k++) {
127 ref_llh[k] = re->area[j].ref[k] * D2R; /* deg -> rad */
128 }
129 pos2ecef(ref_llh, ref_ecef);
130 for (k = 0; k < 3; k++) {
131 diff[k] = rr[k] - ref_ecef[k];
132 }
133 dist = norm(diff, 3) / 1E3; /* m -> km */
134 trace(NULL, 5, "miono_sel_area: area i=%d,j=%d,sid=%d,ref=%6.2f,%7.2f,dist=%10.3f,min_dist=%10.3f\n", i, j,
135 re->area[j].sid, re->area[j].ref[0], re->area[j].ref[1], dist, min_dist);
136 if (dist > min_dist) {
137 continue;
138 }
139
140 if (re->area[j].sid == 0) { /* rectangle */
141 if ((ll[0] < (re->area[j].ref[0] - re->area[j].span[0])) ||
142 (ll[0] > (re->area[j].ref[0] + re->area[j].span[0])) ||
143 (ll[1] < (re->area[j].ref[1] - re->area[j].span[1])) ||
144 (ll[1] > (re->area[j].ref[1] + re->area[j].span[1]))) {
145 continue;
146 }
147 } else { /* circle */
148 if (dist > re->area[j].span[0]) {
149 continue;
150 }
151 }
152 trace(NULL, 2, "miono_sel_area: closest RegionID=%d,AreaNo=%d,dist=%.3f\n", i, j, dist);
153 min_i = i;
154 min_j = j;
155 min_dist = dist;
156 }
157 }
158
159 if (min_i == -1) {
160 trace(NULL, 2, "miono_sel_area: no corresponding area. lat=%.2f,lon=%.2f\n", ll[0], ll[1]);
161 return NULL;
162 }
163 pppiono->rid = min_i;
164 pppiono->anum = min_j;
165 return &pppiono->re[min_i].area[min_j];
166}
167
168/* get MADOCA-PPP ionospheric correction data --------------------------------
169 * get MADOCA-PPP ionospheric correction data using the user position
170 * args : double *rr I user position (ECEF)
171 * nav_t *nav IO navigation data (pppiono correction updated)
172 * return : status (0: not updated, 1: updated)
173 *-----------------------------------------------------------------------------*/
174extern int miono_get_corr(const double* rr, nav_t* nav) {
175 miono_area_t* area;
176 int i;
177 double pos[3], latlon[2];
178
179 if (!nav->pppiono) {
180 return 0;
181 }
182
183 trace(NULL, 4, "miono_get_corr: rr=%.3f,%.3f,%.3f\n", rr[0], rr[1], rr[2]);
184
185 if ((rr[0] == 0.0) && (rr[1] == 0.0) && (rr[2] == 0.0)) {
186 return 0;
187 }
188
189 ecef2pos(rr, pos);
190 latlon[0] = pos[0] * R2D;
191 latlon[1] = pos[1] * R2D;
192
193 if (!(area = miono_sel_area(nav->pppiono, rr, latlon))) {
194 return 0;
195 }
196
197 for (i = 0; i < MAXSAT; i++) {
198 if (!area->sat[i].t0.time) {
199 nav->pppiono->corr.t0[i].time = 0;
200 continue;
201 }
202 nav->pppiono->corr.t0[i] = area->sat[i].t0;
203 nav->pppiono->corr.dly[i] = miono_delay(nav, i + 1, area, latlon);
204 nav->pppiono->corr.std[i] = miono_std_stecura(area->sat[i].sqi);
205 }
206 return 1;
207}
208
209/* read stat correction data -------------------------------------------------
210 * read ppp ionosphere correction data from stat file
211 * args : pppiono_corr_t *corr IO ppp ionosphere correction data
212 * FILE *fp I file pointer
213 * return : status (-2: end of file, 0: no data, 1: input correction data)
214 *-----------------------------------------------------------------------------*/
215extern int input_statcorr(pppiono_corr_t* corr, FILE* fp) {
216 gtime_t time;
217 int week, stat, sat, ret = 0;
218 double tow, pos[3], std[3], azel[2], dly, dlystd;
219 char buff[1024], satid[64], *p;
220
221 /* Note, File format :
222 $POS,GPS Week,TOW,status(1:FIX,6:PPP),X(m),Y(m),Z(m),X STD(m), Y STD(m),Z STD(m)
223 $ION,GPS Week,TOW,status(1:FIX,6:PPP),Sat,Az(deg),El(deg),L1 Slant Delay(m),L1 Slant Delay STD(m)
224 */
225
226 while (fgets(buff, sizeof(buff), fp)) {
227 for (p = buff; *p; p++) {
228 if (*p == ',') {
229 *p = ' ';
230 }
231 }
232
233 /* $POS record */
234 if (sscanf(buff, "$POS%d%lf%d%lf%lf%lf%lf%lf%lf", &week, &tow, &stat, pos, pos + 1, pos + 2, std, std + 1,
235 std + 2) >= 9) {
236 time = gpst2time(week, tow);
237 if ((corr->time.time != 0) && (corr->time.time != time.time)) {
238 return ret;
239 }
240 }
241
242 /* $ION record */
243 if (sscanf(buff, "$ION%d%lf%d%s%lf%lf%lf%lf", &week, &tow, &stat, satid, azel, azel + 1, &dly, &dlystd) >= 8) {
244 time = gpst2time(week, tow);
245 if ((sat = satid2no(satid)) != 0) {
246 corr->t0[sat - 1] = corr->time = time;
247 corr->dly[sat - 1] = dly;
248 corr->std[sat - 1] = dlystd;
249 ret = 1;
250 }
251 trace(NULL, 4, "input_statcorr : %s,%s,az=%7.2f,el=%5.2f,dly=%8.4f,std=%8.4f\n", time_str(time, 0), satid,
252 azel[0], azel[1], dly, dlystd);
253 }
254 }
255 return -2;
256}
257
258/* constraint to ionospheric correction --------------------------------------*/
259extern int const_iono_corr(rtk_t* rtk, const obsd_t* obs, const nav_t* nav, const int n, const double* azel,
260 const int* exc, const double* rr, const double* x, double* v, double* H, double* var) {
261 gtime_t time = obs[0].time;
262 pppiono_corr_t corr;
263 int i, j, k, sat, nv = 0, s, ns;
264 double tt, std, sd[2], pos[3], P[9], Q[9];
265 double rejstd = IONO_REJ_STD, covratio = IONO_COV_RATIO, thre[2] = {IONO_THRE_H, IONO_THRE_V};
266 double ave, bsys[NSYS] = {0.0};
267 char *p, *tstr, satid[8];
268 const int sys[NSYS] = {SYS_GPS, SYS_GLO, SYS_GAL, SYS_QZS, 0};
269
270 if (!nav->pppiono) {
271 return 0;
272 }
273
274 if ((p = strstr(rtk->opt.pppopt, "-IONOCORR_THRE_H="))) {
275 sscanf(p, "-IONOCORR_THRE_H=%lf", &thre[0]);
276 }
277 if ((p = strstr(rtk->opt.pppopt, "-IONOCORR_THRE_V="))) {
278 sscanf(p, "-IONOCORR_THRE_V=%lf", &thre[1]);
279 }
280 if ((p = strstr(rtk->opt.pppopt, "-IONOCORR_REJ_STD="))) {
281 sscanf(p, "-IONOCORR_REJ_STD=%lf", &rejstd);
282 }
283 if ((p = strstr(rtk->opt.pppopt, "-IONOCORR_COV_RATIO="))) {
284 sscanf(p, "-IONOCORR_COV_RATIO=%lf", &covratio);
285 }
286
287 ecef2pos(rr, pos);
288 P[0] = rtk->prev_qr[0]; /* xx */
289 P[4] = rtk->prev_qr[1]; /* yy */
290 P[8] = rtk->prev_qr[2]; /* zz */
291 P[1] = P[3] = rtk->prev_qr[3]; /* xy */
292 P[5] = P[7] = rtk->prev_qr[4]; /* yz */
293 P[2] = P[6] = rtk->prev_qr[5]; /* zx */
294 covenu(pos, P, Q);
295 sd[0] = SQRT(Q[0] + Q[4]); /* horizontal std */
296 sd[1] = SQRT(Q[8]); /* vertical std */
297
298 trace(NULL, 3, "const_iono_corr: sd=%6.2f,%6.2f,thre=%6.2f,%6.2f,rejstd=%.3f,covratio=%.3f\n", sd[0], sd[1],
299 thre[0], thre[1], rejstd, covratio);
300
301 if ((sd[0] != 0.0) && (sd[1] != 0.0) && (sd[0] < thre[0]) && (sd[1] < thre[1])) {
302 trace(NULL, 3, "const_iono_corr: skipped to constraint. std H:%6.2f<%6.2f V:%6.2f<%6.2f\n", sd[0], thre[0],
303 sd[1], thre[1]);
304 return 0;
305 }
306
307 corr = nav->pppiono->corr;
308
309 /* estimate system bias */
310 for (s = 0; sys[s]; s++) { /* for each system */
311 ave = 0.0;
312 ns = 0;
313 for (i = 0; i < n; i++) {
314 sat = obs[i].sat;
315 if (satsys(sat, NULL) != sys[s]) {
316 continue;
317 }
318 if (corr.t0[sat - 1].time == 0.0) {
319 continue;
320 }
321 tt = fabs(timediff(time, corr.t0[sat - 1]));
322 tstr = time_str(corr.t0[sat - 1], 0);
323
324 satno2id(sat, satid);
325 if (exc[i]) {
326 continue;
327 }
328 if (tt > MIONO_MAX_AGE) {
329 continue;
330 }
331 if (corr.std[sat - 1] > rejstd) {
332 continue;
333 }
334
335 j = II(sat, &rtk->opt);
336 ave += (corr.dly[sat - 1] - x[j]);
337 ns++;
338 }
339 if (ns > 0) {
340 bsys[s] = ave / (double)ns;
341 }
342 }
343
344 /* constraint to external ionosphere correction */
345 for (i = 0; i < n; i++) {
346 sat = obs[i].sat;
347 if (corr.t0[sat - 1].time == 0.0) {
348 continue;
349 }
350 tt = fabs(timediff(time, corr.t0[sat - 1]));
351 tstr = time_str(corr.t0[sat - 1], 0);
352
353 satno2id(sat, satid);
354
355 switch (satsys(sat, NULL)) {
356 case SYS_GPS:
357 s = 0;
358 break;
359 case SYS_GLO:
360 s = 1;
361 break;
362 case SYS_GAL:
363 s = 2;
364 break;
365 case SYS_QZS:
366 s = 3;
367 break;
368 default:
369 continue;
370 }
371
372 if (exc[i]) {
373 continue;
374 }
375 if (tt > MIONO_MAX_AGE) {
376 continue;
377 }
378 if (corr.std[sat - 1] > rejstd) {
379 continue;
380 }
381
382 std = corr.std[sat - 1];
383 j = II(sat, &rtk->opt);
384 v[nv] = corr.dly[sat - 1] - x[j] - bsys[s];
385
386 trace(NULL, 2,
387 "const_iono_corr: %s,%s,tt=%11d,az=%5.1f,el=%4.1f,sdly=%8.4f,std=%8.4f,v=%8.4f,x=%8.4f,bsys=%8.4f\n",
388 satid, tstr, (int)tt, (azel + i * 2)[0] * R2D, (azel + i * 2)[1] * R2D, corr.dly[sat - 1],
389 corr.std[sat - 1], v[nv], x[j], bsys[s]);
390
391 for (k = 0; k < rtk->nx; k++) {
392 H[k + nv * rtk->nx] = k == j ? 1.0 : 0.0;
393 }
394 var[nv++] = SQR(std) * covratio;
395 }
396 return nv;
397}
MRTKLIB Constants — Physical, navigation, and system constants.
#define MIONO_MAX_AGE
Definition mrtk_const.h:425
MRTKLIB Context — runtime context for processing, trace, and error handling.
#define D2R
Definition mrtk_coords.c:36
void pos2ecef(const double *pos, double *r)
Transform geodetic position to ECEF position.
#define MAXSAT
Definition mrtk_coords.c:45
MRTKLIB Coordinates Module — Coordinate transformations and geometry.
#define SYS_GPS
Definition mrtk_eph.c:65
#define SYS_QZS
Definition mrtk_eph.c:69
#define SYS_GAL
Definition mrtk_eph.c:68
#define SYS_GLO
Definition mrtk_eph.c:67
struct nav_s nav_t
Definition mrtk_eph.h:40
#define NSYS
#define CODE_L1C
#define MIONO_MAX_ANUM
#define MIONO_MAX_RID
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
MRTKLIB Matrix Module — Matrix/vector operations and estimation.
MRTKLIB Navigation Module — Navigation data types and satellite/nav utility functions.
#define R2D
Definition mrtk_obs.c:51
MRTKLIB Observation Module — Observation data types and utility functions.
MRTKLIB Options Module — Processing and solution option types.
void satno2id(int sat, char *id)
Definition mrtk_nav.c:817
int satsys(int sat, int *prn)
Definition mrtk_nav.c:117
static miono_area_t * miono_sel_area(pppiono_t *pppiono, const double *rr, const double *ll)
int input_statcorr(pppiono_corr_t *corr, FILE *fp)
int miono_get_corr(const double *rr, nav_t *nav)
int const_iono_corr(rtk_t *rtk, const obsd_t *obs, const nav_t *nav, const int n, const double *azel, const int *exc, const double *rr, const double *x, double *v, double *H, double *var)
#define IONO_REJ_STD
static double miono_std_stecura(int ura)
gtime_t gpst2time(int week, double sec)
Definition mrtk_time.c:165
#define IONO_THRE_V
void covenu(const double *pos, const double *P, double *Q)
static double miono_delay(const nav_t *nav, const int sat, const miono_area_t *a, const double *ll)
double timediff(gtime_t t1, gtime_t t2)
Definition mrtk_time.c:279
int satid2no(const char *id)
Definition mrtk_nav.c:753
#define SQR(x)
char * time_str(gtime_t t, int n)
Definition mrtk_time.c:530
#define IONO_THRE_H
#define IONO_COV_RATIO
#define SQRT(x)
void ecef2pos(const double *r, double *pos)
double sat2freq(int sat, uint8_t code, const nav_t *nav)
Definition mrtk_obs.c:244
#define II(s, opt)
MRTKLIB RTK Positioning Module — relative and precise positioning.
void trace(mrtk_ctx_t *ctx, int level, const char *format,...)
Output trace message.
Definition mrtk_trace.c:78
MRTKLIB Trace Module — debug trace and logging functions.
GNSS time representation.
Definition mrtk_time.h:49
time_t time
Definition mrtk_time.h:50
double span[2]
Definition mrtk_nav.h:226
miono_sat_t sat[MAXSAT]
Definition mrtk_nav.h:227
double ref[2]
Definition mrtk_nav.h:225
miono_area_t area[MIONO_MAX_ANUM]
Definition mrtk_nav.h:234
gtime_t t0
Definition mrtk_nav.h:216
double coef[6]
Definition mrtk_nav.h:218
pppiono_t * pppiono
Definition mrtk_nav.h:438
Observation data record.
Definition mrtk_obs.h:44
gtime_t time
Definition mrtk_obs.h:45
uint8_t sat
Definition mrtk_obs.h:46
gtime_t time
Definition mrtk_nav.h:238
double std[MAXSAT]
Definition mrtk_nav.h:241
gtime_t t0[MAXSAT]
Definition mrtk_nav.h:239
double dly[MAXSAT]
Definition mrtk_nav.h:240
miono_region_t re[MIONO_MAX_RID]
Definition mrtk_nav.h:246
pppiono_corr_t corr
Definition mrtk_nav.h:245
char pppopt[256]
Definition mrtk_opt.h:243
int nx
Definition mrtk_rtkpos.h:52
double prev_qr[6]
Definition mrtk_rtkpos.h:65
prcopt_t opt
Definition mrtk_rtkpos.h:63