MRTKLIB 0.7.14
Modernized GNSS Positioning Library
Loading...
Searching...
No Matches
mrtk_antenna.c
Go to the documentation of this file.
1/*------------------------------------------------------------------------------
2 * mrtk_antenna.c : antenna model and PCV 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_antenna.c
18 * @brief MRTKLIB Antenna Module — Implementation.
19 *
20 * All functions and static data in this file are cut-and-paste extractions
21 * from the legacy rtkcmn.c with zero algorithmic changes.
22 */
23
25
26#include <math.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "mrtklib/mrtk_mat.h"
32#include "mrtklib/mrtk_trace.h"
33
34/*============================================================================
35 * Forward Declarations (resolved at link time)
36 *===========================================================================*/
37
38/* satid2no remains in rtkcmn.c */
39extern int satid2no(const char* id);
40extern int satsys(int sat, int* prn);
41extern int freq_num2ant_idx(int sys, int freq_num);
42
43/*============================================================================
44 * Private Constants
45 *===========================================================================*/
46
47#define PI 3.1415926535897932 /* pi */
48#define D2R (PI / 180.0) /* deg to rad */
49#define R2D (180.0 / PI) /* rad to deg */
50
51/*============================================================================
52 * Private Functions
53 *===========================================================================*/
54
55/* decode antenna parameter field --------------------------------------------*/
56static int decodef(char* p, int n, double* v) {
57 int i;
58
59 for (i = 0; i < n; i++) {
60 v[i] = 0.0;
61 }
62 for (i = 0, p = strtok(p, " "); p && i < n; p = strtok(NULL, " ")) {
63 v[i++] = atof(p) * 1E-3;
64 }
65 return i;
66}
67/* add antenna parameter -----------------------------------------------------*/
68static void addpcv(const pcv_t* pcv, pcvs_t* pcvs) {
69 pcv_t* pcvs_pcv;
70
71 if (pcvs->nmax <= pcvs->n) {
72 pcvs->nmax += 256;
73 if (!(pcvs_pcv = (pcv_t*)realloc(pcvs->pcv, sizeof(pcv_t) * pcvs->nmax))) {
74 trace(NULL, 1, "addpcv: memory allocation error\n");
75 free(pcvs->pcv);
76 pcvs->pcv = NULL;
77 pcvs->n = pcvs->nmax = 0;
78 return;
79 }
80 pcvs->pcv = pcvs_pcv;
81 }
82 pcvs->pcv[pcvs->n++] = *pcv;
83}
84/* read ngs antenna parameter file -------------------------------------------*/
85static int readngspcv(const char* file, pcvs_t* pcvs) {
86 FILE* fp;
87 static const pcv_t pcv0 = {0};
88 pcv_t pcv;
89 double neu[3];
90 int n = 0;
91 char buff[256];
92
93 if (!(fp = fopen(file, "r"))) {
94 trace(NULL, 2, "ngs pcv file open error: %s\n", file);
95 return 0;
96 }
97 while (fgets(buff, sizeof(buff), fp)) {
98 if (strlen(buff) >= 62 && buff[61] == '|') {
99 continue;
100 }
101
102 if (buff[0] != ' ') {
103 n = 0; /* start line */
104 }
105 if (++n == 1) {
106 pcv = pcv0;
107 sprintf(pcv.type, "%.61s", buff);
108 } else if (n == 2) {
109 if (decodef(buff, 3, neu) < 3) {
110 continue;
111 }
112 pcv.off[0][0] = neu[1];
113 pcv.off[0][1] = neu[0];
114 pcv.off[0][2] = neu[2];
115 } else if (n == 3) {
116 decodef(buff, 10, pcv.var[0]);
117 } else if (n == 4) {
118 decodef(buff, 9, pcv.var[0] + 10);
119 } else if (n == 5) {
120 if (decodef(buff, 3, neu) < 3) {
121 continue;
122 }
123 pcv.off[1][0] = neu[1];
124 pcv.off[1][1] = neu[0];
125 pcv.off[1][2] = neu[2];
126 } else if (n == 6) {
127 decodef(buff, 10, pcv.var[1]);
128 } else if (n == 7) {
129 decodef(buff, 9, pcv.var[1] + 10);
130 addpcv(&pcv, pcvs);
131 }
132 }
133 fclose(fp);
134
135 return 1;
136}
137/* read antex file (madocalib version with freq_num2ant_idx) -----------------*/
138static int readantex(const char* file, pcvs_t* pcvs) {
139 FILE* fp;
140 static const pcv_t pcv0 = {0};
141 pcv_t pcv;
142 double neu[3];
143 int i, f, idx = -1, state = 0, sys;
144 char buff[256], s = 0;
145
146 trace(NULL, 3, "readantex: file=%s\n", file);
147
148 if (!(fp = fopen(file, "r"))) {
149 trace(NULL, 2, "antex pcv file open error: %s\n", file);
150 return 0;
151 }
152 while (fgets(buff, sizeof(buff), fp)) {
153 if (strlen(buff) < 60 || strstr(buff + 60, "COMMENT")) {
154 continue;
155 }
156
157 if (strstr(buff + 60, "START OF ANTENNA")) {
158 pcv = pcv0;
159 state = 1;
160 }
161 if (strstr(buff + 60, "END OF ANTENNA")) {
162 addpcv(&pcv, pcvs);
163 state = 0;
164 }
165 if (!state) {
166 continue;
167 }
168
169 if (strstr(buff + 60, "TYPE / SERIAL NO")) {
170 strncpy(pcv.type, buff, 20);
171 pcv.type[20] = '\0';
172 strncpy(pcv.code, buff + 20, 20);
173 pcv.code[20] = '\0';
174 if (!strncmp(pcv.code + 3, " ", 8)) {
175 pcv.sat = satid2no(pcv.code);
176 }
177 } else if (strstr(buff + 60, "VALID FROM")) {
178 if (!str2time(buff, 0, 43, &pcv.ts)) {
179 continue;
180 }
181 } else if (strstr(buff + 60, "VALID UNTIL")) {
182 if (!str2time(buff, 0, 43, &pcv.te)) {
183 continue;
184 }
185 } else if (strstr(buff + 60, "START OF FREQUENCY")) {
186 if (pcv.sat) { /* for satellite */
187 if (sscanf(buff + 4, "%d", &f) < 1) {
188 continue;
189 }
190 s = 0;
191 sys = satsys(pcv.sat, NULL);
192 } else { /* for station */
193 if (sscanf(buff + 3, "%c%d", &s, &f) < 2) {
194 continue;
195 }
196 sys = satsys(satid2no(buff + 3), NULL);
197 }
198 if ((idx = freq_num2ant_idx(sys, f)) >= NFREQPCV) {
199 continue;
200 }
201 } else if (strstr(buff + 60, "END OF FREQUENCY")) {
202 idx = -1;
203 } else if (strstr(buff + 60, "NORTH / EAST / UP")) {
204 if (idx < 0 || (NFREQPCV - 1) <= idx) {
205 continue;
206 }
207 if (decodef(buff, 3, neu) < 3) {
208 continue;
209 }
210 pcv.off[idx][0] = neu[pcv.sat ? 0 : 1]; /* x or e */
211 pcv.off[idx][1] = neu[pcv.sat ? 1 : 0]; /* y or n */
212 pcv.off[idx][2] = neu[2]; /* z or u */
213 } else if (strstr(buff, "NOAZI")) {
214 if (idx < 0 || NFREQPCV <= idx) {
215 continue;
216 }
217 if ((i = decodef(buff + 8, 19, pcv.var[idx])) <= 0) {
218 continue;
219 }
220 for (; i < 19; i++) {
221 pcv.var[idx][i] = pcv.var[idx][i - 1];
222 }
223 }
224 }
225 fclose(fp);
226
227 return 1;
228}
229/* interpolate antenna phase center variation --------------------------------*/
230static double interpvar(double ang, const double* var) {
231 double a = ang / 5.0; /* ang=0-90 */
232 int i = (int)a;
233 if (i < 0) {
234 return var[0];
235 } else if (i >= 18) {
236 return var[18];
237 }
238 return var[i] * (1.0 - a + i) + var[i + 1] * (a - i);
239}
240
241/*============================================================================
242 * Public Functions
243 *===========================================================================*/
244
245/* read antenna parameters ------------------------------------------------------
246 * read antenna parameters
247 * args : char *file I antenna parameter file (antex)
248 * pcvs_t *pcvs IO antenna parameters
249 * return : status (1:ok,0:file open error)
250 * notes : file with the externsion .atx or .ATX is recognized as antex
251 * file except for antex is recognized ngs antenna parameters
252 * see reference [3]
253 * only support non-azimuth-depedent parameters
254 *-----------------------------------------------------------------------------*/
255extern int readpcv(const char* file, pcvs_t* pcvs) {
256 pcv_t* pcv;
257 char* ext;
258 int i, f, stat;
259
260 trace(NULL, 3, "readpcv: file=%s\n", file);
261
262 if (!(ext = strrchr(file, '.'))) {
263 ext = "";
264 }
265
266 if (!strcmp(ext, ".pcv") || !strcmp(ext, ".PCV")) {
267 stat = readngspcv(file, pcvs);
268 } else {
269 stat = readantex(file, pcvs);
270 }
271
272 /* use G02 if GPS IIF */
273 for (i = 0; i < pcvs->n; i++) {
274 pcv = pcvs->pcv + i;
275 if (pcv->sat == 0) {
276 continue;
277 }
278 if (!strstr(pcv->type, "BLOCK IIF")) {
279 continue;
280 }
281 if (norm(pcv->off[2], 3) > 0.0 || norm(pcv->var[2], 19) > 0.0) {
282 continue;
283 }
284 matcpy(pcv->off[2], pcv->off[1], 3, 1);
285 matcpy(pcv->var[2], pcv->var[1], 19, 1);
286 }
287 /* antenna index=2 : use G02 if no G05 E05 J05 C05 S05 I05 */
288 for (i = 0; i < pcvs->n; i++) {
289 pcv = pcvs->pcv + i;
290 if (pcv->sat > 0) {
291 continue;
292 }
293 if (norm(pcv->off[2], 3) > 0.0 || norm(pcv->var[2], 19) > 0.0) {
294 continue;
295 }
296 matcpy(pcv->off[2], pcv->off[1], 3, 1);
297 matcpy(pcv->var[2], pcv->var[1], 19, 1);
298 }
299 /* antenna index=3 : use G01 if no R01 R04 */
300 for (i = 0; i < pcvs->n; i++) {
301 pcv = pcvs->pcv + i;
302 if (pcv->sat > 0) {
303 continue;
304 }
305 if (norm(pcv->off[3], 3) > 0.0 || norm(pcv->var[3], 19) > 0.0) {
306 continue;
307 }
308 matcpy(pcv->off[3], pcv->off[0], 3, 1);
309 matcpy(pcv->var[3], pcv->var[0], 19, 1);
310 }
311 /* antenna index=4 : use G01 if no C02 */
312 for (i = 0; i < pcvs->n; i++) {
313 pcv = pcvs->pcv + i;
314 if (pcv->sat > 0) {
315 continue;
316 }
317 if (norm(pcv->off[4], 3) > 0.0 || norm(pcv->var[4], 19) > 0.0) {
318 continue;
319 }
320 matcpy(pcv->off[4], pcv->off[0], 3, 1);
321 matcpy(pcv->var[4], pcv->var[0], 19, 1);
322 }
323 /* antenna index=5 : use G02 if no E06 J06 */
324 for (i = 0; i < pcvs->n; i++) {
325 pcv = pcvs->pcv + i;
326 if (pcv->sat > 0) {
327 continue;
328 }
329 if (norm(pcv->off[5], 3) > 0.0 || norm(pcv->var[5], 19) > 0.0) {
330 continue;
331 }
332 matcpy(pcv->off[5], pcv->off[1], 3, 1);
333 matcpy(pcv->var[5], pcv->var[1], 19, 1);
334 }
335 /* antenna index=6 : use G02 if no C06 */
336 for (i = 0; i < pcvs->n; i++) {
337 pcv = pcvs->pcv + i;
338 if (pcv->sat > 0) {
339 continue;
340 }
341 if (norm(pcv->off[6], 3) > 0.0 || norm(pcv->var[6], 19) > 0.0) {
342 continue;
343 }
344 matcpy(pcv->off[6], pcv->off[1], 3, 1);
345 matcpy(pcv->var[6], pcv->var[1], 19, 1);
346 }
347 /* antenna index=7 : use G02 if no R02 R06 */
348 for (i = 0; i < pcvs->n; i++) {
349 pcv = pcvs->pcv + i;
350 if (pcv->sat > 0) {
351 continue;
352 }
353 if (norm(pcv->off[7], 3) > 0.0 || norm(pcv->var[7], 19) > 0.0) {
354 continue;
355 }
356 matcpy(pcv->off[7], pcv->off[1], 3, 1);
357 matcpy(pcv->var[7], pcv->var[1], 19, 1);
358 }
359 /* antenna index=8 : use G02 if no E07 C07 */
360 for (i = 0; i < pcvs->n; i++) {
361 pcv = pcvs->pcv + i;
362 if (pcv->sat > 0) {
363 continue;
364 }
365 if (norm(pcv->off[8], 3) > 0.0 || norm(pcv->var[8], 19) > 0.0) {
366 continue;
367 }
368 matcpy(pcv->off[8], pcv->off[1], 3, 1);
369 matcpy(pcv->var[8], pcv->var[1], 19, 1);
370 }
371 /* antenna index=9 : use G02 if no R03 */
372 for (i = 0; i < pcvs->n; i++) {
373 pcv = pcvs->pcv + i;
374 if (pcv->sat > 0) {
375 continue;
376 }
377 if (norm(pcv->off[9], 3) > 0.0 || norm(pcv->var[9], 19) > 0.0) {
378 continue;
379 }
380 matcpy(pcv->off[9], pcv->off[1], 3, 1);
381 matcpy(pcv->var[9], pcv->var[1], 19, 1);
382 }
383 /* antenna index=10 : use G05 if no E08 C08 */
384 for (i = 0; i < pcvs->n; i++) {
385 pcv = pcvs->pcv + i;
386 if (pcv->sat > 0) {
387 continue;
388 }
389 if (norm(pcv->off[10], 3) > 0.0 || norm(pcv->var[10], 19) > 0.0) {
390 continue;
391 }
392 matcpy(pcv->off[10], pcv->off[2], 3, 1);
393 matcpy(pcv->var[10], pcv->var[2], 19, 1);
394 }
395
396 for (i = 0; i < pcvs->n; i++) {
397 pcv = pcvs->pcv + i;
398 for (f = 0; f < NFREQPCV; f++) {
399 if (norm(pcv->off[f], 3) == 0.0) {
400 continue;
401 }
402 trace(NULL, 4, "readpcv: sat=%2d type=%20s code=%s f=%2d off=%8.4f %8.4f %8.4f\n", pcv->sat, pcv->type,
403 pcv->code, f, pcv->off[f][0], pcv->off[f][1], pcv->off[f][2]);
404 }
405 }
406 return stat;
407}
408/* search antenna parameter ----------------------------------------------------
409 * read satellite antenna phase center position
410 * args : int sat I satellite number (0: receiver antenna)
411 * char *type I antenna type for receiver antenna
412 * gtime_t time I time to search parameters
413 * pcvs_t *pcvs IO antenna parameters
414 * return : antenna parameter (NULL: no antenna)
415 *-----------------------------------------------------------------------------*/
416extern pcv_t* searchpcv(int sat, const char* type, gtime_t time, const pcvs_t* pcvs) {
417 pcv_t* pcv;
418 char buff[MAXANT], *types[2], *p;
419 int i, j, n = 0;
420
421 trace(NULL, 3, "searchpcv: sat=%2d type=%s\n", sat, type);
422
423 if (sat) { /* search satellite antenna */
424 for (i = 0; i < pcvs->n; i++) {
425 pcv = pcvs->pcv + i;
426 if (pcv->sat != sat) {
427 continue;
428 }
429 if (pcv->ts.time != 0 && timediff(pcv->ts, time) > 0.0) {
430 continue;
431 }
432 if (pcv->te.time != 0 && timediff(pcv->te, time) < 0.0) {
433 continue;
434 }
435 return pcv;
436 }
437 } else {
438 strcpy(buff, type);
439 for (p = strtok(buff, " "); p && n < 2; p = strtok(NULL, " ")) {
440 types[n++] = p;
441 }
442 if (n <= 0) {
443 return NULL;
444 }
445
446 /* search receiver antenna with radome at first */
447 for (i = 0; i < pcvs->n; i++) {
448 pcv = pcvs->pcv + i;
449 for (j = 0; j < n; j++) {
450 if (!strstr(pcv->type, types[j])) {
451 break;
452 }
453 }
454 if (j >= n) {
455 return pcv;
456 }
457 }
458 /* search receiver antenna without radome */
459 for (i = 0; i < pcvs->n; i++) {
460 pcv = pcvs->pcv + i;
461 if (strstr(pcv->type, types[0]) != pcv->type) {
462 continue;
463 }
464
465 trace(NULL, 2, "pcv without radome is used type=%s\n", type);
466 return pcv;
467 }
468 }
469 return NULL;
470}
471/* receiver antenna model ------------------------------------------------------
472 * compute antenna offset by antenna phase center parameters
473 * args : pcv_t *pcv I antenna phase center parameters
474 * double *del I antenna delta {e,n,u} (m)
475 * double *azel I azimuth/elevation for receiver {az,el} (rad)
476 * int opt I option (0:only offset,1:offset+pcv)
477 * double *dant O range offsets for each frequency (m)
478 * return : none
479 * notes : current version does not support azimuth dependent terms
480 *-----------------------------------------------------------------------------*/
481extern void antmodel(const pcv_t* pcv, const double* del, const double* azel, int opt, double* dant) {
482 double e[3], off[3], cosel = cos(azel[1]);
483 int i, j;
484
485 trace(NULL, 4, "antmodel: azel=%6.1f %4.1f opt=%d\n", azel[0] * R2D, azel[1] * R2D, opt);
486
487 e[0] = sin(azel[0]) * cosel;
488 e[1] = cos(azel[0]) * cosel;
489 e[2] = sin(azel[1]);
490
491 for (i = 0; i < NFREQPCV; i++) {
492 for (j = 0; j < 3; j++) {
493 off[j] = pcv->off[i][j] + del[j];
494 }
495
496 dant[i] = -dot(off, e, 3) + (opt ? interpvar(90.0 - azel[1] * R2D, pcv->var[i]) : 0.0);
497 }
498 trace(NULL, 5, "antmodel: dant=%6.3f %6.3f\n", dant[0], dant[1]);
499}
500/* satellite antenna model ------------------------------------------------------
501 * compute satellite antenna phase center parameters
502 * args : pcv_t *pcv I antenna phase center parameters
503 * double nadir I nadir angle for satellite (rad)
504 * double *dant O range offsets for each frequency (m)
505 * return : none
506 *-----------------------------------------------------------------------------*/
507extern void antmodel_s(const pcv_t* pcv, double nadir, double* dant) {
508 int i;
509
510 trace(NULL, 4, "antmodel_s: nadir=%6.1f\n", nadir * R2D);
511
512 for (i = 0; i < NFREQPCV; i++) {
513 dant[i] = interpvar(nadir * R2D * 5.0, pcv->var[i]);
514 }
515 trace(NULL, 5, "antmodel_s: dant=%6.3f %6.3f\n", dant[0], dant[1]);
516}
int satsys(int sat, int *prn)
Definition mrtk_nav.c:117
int readpcv(const char *file, pcvs_t *pcvs)
Read antenna parameters from file (ANTEX or NGS format).
static int readngspcv(const char *file, pcvs_t *pcvs)
static void addpcv(const pcv_t *pcv, pcvs_t *pcvs)
void antmodel(const pcv_t *pcv, const double *del, const double *azel, int opt, double *dant)
Compute receiver antenna offset by antenna phase center parameters.
int freq_num2ant_idx(int sys, int freq_num)
Definition mrtk_nav.c:710
int satid2no(const char *id)
Definition mrtk_nav.c:753
static int decodef(char *p, int n, double *v)
static double interpvar(double ang, const double *var)
pcv_t * searchpcv(int sat, const char *type, gtime_t time, const pcvs_t *pcvs)
Search antenna parameter by satellite number or antenna type.
static int readantex(const char *file, pcvs_t *pcvs)
void antmodel_s(const pcv_t *pcv, double nadir, double *dant)
Compute satellite antenna phase center parameters.
MRTKLIB Antenna Module — Antenna phase center model and parameter I/O.
#define MAXANT
#define NFREQPCV
void matcpy(double *A, const double *B, int n, int m)
Copy matrix.
Definition mrtk_mat.c:205
double norm(const double *a, int n)
Euclidean norm of vector.
Definition mrtk_mat.c:165
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.
#define R2D
Definition mrtk_obs.c:51
double timediff(gtime_t t1, gtime_t t2)
Compute time difference (t1 - t2).
Definition mrtk_time.c:279
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
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 var[NFREQPCV][19]
Definition mrtk_nav.h:51
gtime_t te
Definition mrtk_nav.h:49
gtime_t ts
Definition mrtk_nav.h:49
char type[MAXANT]
Definition mrtk_nav.h:47
double off[NFREQPCV][3]
Definition mrtk_nav.h:50
char code[MAXANT]
Definition mrtk_nav.h:48
int sat
Definition mrtk_nav.h:46
pcv_t * pcv
Definition mrtk_nav.h:57
int n
Definition mrtk_nav.h:56
int nmax
Definition mrtk_nav.h:56
#define free(x)
Definition toml.c:53