Skip to content
Merged
37 changes: 31 additions & 6 deletions app/consapp/convbin/convbin.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ static const char *help[]={
" -l lfile output RINEX LNAV file",
" -s sfile output SBAS message file",
" -trace level output trace level [off]",
" -safeephemtime refuse to use wall clock and refuse to increment",
Copy link
Copy Markdown

@dgburr dgburr May 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add a new command-line argument instead of just requiring the user to specify -ro SAFEEPHEMTIME (which appears to be how it is implemented internally anyway)? And does it really make sense to call this a "safe" time? I understand that in your specific context, this flag results in the desired behaviour. But for other use cases one could argue that the default behaviour is the "safest". Maybe consider renaming it to something like "NOEPHWEEKWRAP"?

" ephemeris week numbers when decoding ephemerides;",
" requires -tr; affects RTCM3 and SBP eph decoders",
"",
" If not any output file is specified, default output files <file>.obs,",
" <file>.nav, <file>.gnav, <file>.hnav, <file>.qnav, <file>.lnav and",
Expand Down Expand Up @@ -337,7 +340,8 @@ static int get_filetime(const char *file, gtime_t *time)
}
/* parse command line options ------------------------------------------------*/
static int cmdopts(int argc, char **argv, rnxopt_t *opt, char **ifile,
char **ofile, char **dir, int *trace)
char **ofile, char **dir, int *trace, int *safeephemtime,
int *has_tr)
{
double eps[]={1980,1,1,0,0,0},epe[]={2037,12,31,0,0,0};
double epr[]={2010,1,1,0,0,0},span=0.0;
Expand Down Expand Up @@ -365,6 +369,7 @@ static int cmdopts(int argc, char **argv, rnxopt_t *opt, char **ifile,
sscanf(argv[++i],"%lf/%lf/%lf",epr,epr+1,epr+2);
sscanf(argv[++i],"%lf:%lf:%lf",epr+3,epr+4,epr+5);
opt->trtcm=epoch2time(epr);
*has_tr=1;
}
else if (!strcmp(argv[i],"-ti")&&i+1<argc) {
opt->tint=atof(argv[++i]);
Expand Down Expand Up @@ -485,6 +490,9 @@ static int cmdopts(int argc, char **argv, rnxopt_t *opt, char **ifile,
else if (!strcmp(argv[i],"-trace" )&&i+1<argc) {
*trace=atoi(argv[++i]);
}
else if (!strcmp(argv[i],"-safeephemtime")) {
*safeephemtime=1;
}
else if (!strncmp(argv[i],"-",1)) printhelp(opt);

else *ifile=argv[i];
Expand Down Expand Up @@ -546,14 +554,15 @@ static int cmdopts(int argc, char **argv, rnxopt_t *opt, char **ifile,
int main(int argc, char **argv)
{
rnxopt_t opt={{0}};
int format,trace=0,stat;
int format,trace=0,stat,safeephemtime=0,has_tr=0;
char *ifile="",*ofile[NOUTFILE]={0},*dir="";

sprintf(opt.prog,"%s %s (RTKLIB %s%s)",PRGNAME,SWIFT_VER_RTKLIB,VER_RTKLIB,PATCH_LEVEL);

/* parse command line options */
format=cmdopts(argc,argv,&opt,&ifile,ofile,&dir,&trace);

format=cmdopts(argc,argv,&opt,&ifile,ofile,&dir,&trace,&safeephemtime,
&has_tr);

if (!*ifile) {
fprintf(stderr,"no input file\n");
return -1;
Expand All @@ -562,7 +571,23 @@ int main(int argc, char **argv)
fprintf(stderr,"input format can not be recognized\n");
return -1;
}

if (safeephemtime) {
size_t used,toklen;
const char *tok;
if (!has_tr) {
fprintf(stderr,"-safeephemtime requires -tr y/m/d h:m:s\n");
return -1;
}
used=strlen(opt.rcvopt);
tok=(used>0)?" -SAFEEPHEMTIME":"-SAFEEPHEMTIME";
toklen=strlen(tok);
if (used+toklen+1>sizeof(opt.rcvopt)) {
fprintf(stderr,"receiver option string too long to add -SAFEEPHEMTIME\n");
return -1;
}
memcpy(opt.rcvopt+used,tok,toklen+1);
}

if (trace>0) {
traceopen(TRACEFILE);
tracelevel(trace);
Expand Down
41 changes: 40 additions & 1 deletion app/consapp/convbin/gcc/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ install:
clean:
rm -f sbp2rinex sbp2rinex.exe *.o *.obs *.nav *.gnav *.hnav *.qnav *.sbs *.stackdump

test : test1 test2 test3 test4 test5 test6 testclean
test : test1 test2 test3 test4 test5 test6 \
test_safeephem_guard test_safeephem_sbp_anchors test_safeephem_rtcm3_anchors \
testclean

test1: # binary sbp
@echo -e "\n\n****** Test1: sbp binary ********\n"
Expand All @@ -155,5 +157,42 @@ test6: # BDS + GAL + GLO + GPS
diff --strip-trailing-cr ./2018-06-15-json.trunc.obs $(shell pwd)$(SEP)$(DATDIR)$(SEP)2018-06-15-json-ref.obs
tail -n +5 ./2018-06-15-json.nav > 2018-06-15-json.trunc.nav
diff --strip-trailing-cr ./2018-06-15-json.trunc.nav $(shell pwd)$(SEP)$(DATDIR)$(SEP)2018-06-15-json-ref.nav
test_safeephem_guard:
@echo -e "\n\n****** test_safeephem_guard: -safeephemtime without -tr ******\n"
@if ./sbp2rinex -safeephemtime $(DATDIR)$(SEP)2017-05-12-v1.1.26.sbp \
-o se_guard.obs >/dev/null 2>&1; then \
echo "FAIL: expected non-zero exit"; exit 1; \
else \
echo "OK: rejected as expected"; \
fi

test_safeephem_sbp_anchors:
@echo -e "\n\n****** test_safeephem_sbp_anchors: 2017 SBP fixture anchored to 2017 ******\n"
./sbp2rinex -safeephemtime -tr 2017/05/12 0:0:0 \
$(DATDIR)$(SEP)2017-05-12-v1.1.26.sbp \
-o se_sbp.obs -n se_sbp.nav
@if ! grep -qE '^[A-Z][0-9]+ +2017 ' se_sbp.nav; then \
echo "FAIL: no 2017 epochs in SBP nav file"; exit 1; \
fi
@if grep -qE '^[A-Z][0-9]+ +202[5-9] ' se_sbp.nav; then \
echo "FAIL: SBP nav contains current-era epochs (rollover regression)"; exit 1; \
fi
@echo "OK: SBP nav epochs anchored to 2017"

test_safeephem_rtcm3_anchors:
@echo -e "\n\n****** test_safeephem_rtcm3_anchors: 2026 IGS RTCM3 fixture ******\n"
./sbp2rinex -r rtcm3 -safeephemtime -tr 2026/05/01 0:0:0 \
$(DATDIR)$(SEP)igs_ephem_2026-05-01.rtcm \
-o se_rtcm.obs -n se_rtcm.nav
@# C45's first eph in this fixture is at BDS week 1058, toe 597600s,
@# more than 3.5 days from the wall clock. Legacy code nudges
@# eph.week++ producing "2026 04 25"; safemode preserves on-wire bits
@# producing "2026 04 18".
@if ! grep -q '^C45 2026 04 18 22 00 00' se_rtcm.nav; then \
echo "FAIL: C45 first eph not at 2026 04 18 22 00 00"; \
grep '^C45' se_rtcm.nav | head -2; exit 1; \
fi
@echo "OK: C45 first eph at 2026 04 18 22 00 00 (on-wire week preserved)"

testclean:
rm -f *.o *.obs *.nav *.gnav *.hnav *.qnav *.sbs
3 changes: 2 additions & 1 deletion src/convrnx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1275,7 +1275,8 @@ static int convrnx_s(int sess, int format, rnxopt_t *opt, const char *file,
for (i=0;i<MAXEXFILE;i++) free(epath[i]);
return 0;
}
if (format==STRFMT_RTCM2||format==STRFMT_RTCM3||format==STRFMT_RT17) {
if (format==STRFMT_RTCM2||format==STRFMT_RTCM3||format==STRFMT_RT17||
strstr(opt->rcvopt,"-SAFEEPHEMTIME")) {
str->time=opt->trtcm;
}
else if (opt->ts.time) {
Expand Down
44 changes: 27 additions & 17 deletions src/rcv/swiftnav.c
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,8 @@ static void decode_gpsnav_common_dep1(uint8_t *_pBuff, eph_t *_pEph) {
_pEph->iode = U1(_pBuff + 182);
_pEph->iodc = U2(_pBuff + 183);

_pEph->week = adjgpsweek(uWeekE);
/* SBP carries a full 16-bit GPS week; trust it directly. */
_pEph->week = uWeekE;
_pEph->toe = gpst2time(_pEph->week, _pEph->toes);
_pEph->toc = gpst2time(uWeekC, dToc);
}
Expand Down Expand Up @@ -711,7 +712,8 @@ static void decode_gpsnav_common(uint8_t *_pBuff, eph_t *_pEph) {
_pEph->iode = U1(_pBuff + 138);
_pEph->iodc = U2(_pBuff + 139);

_pEph->week = adjgpsweek(uWeekE);
/* SBP carries a full 16-bit GPS week; trust it directly. */
_pEph->week = uWeekE;
_pEph->code = 2; /* SBP payload does not have the "code on L2" flag */
_pEph->toe = gpst2time(_pEph->week, _pEph->toes);
_pEph->toc = gpst2time(uWeekC, dToc);
Expand Down Expand Up @@ -757,8 +759,11 @@ static void decode_bdsnav_common(uint8_t *_pBuff, eph_t *_pEph) {
_pEph->iode = U1(_pBuff + 146);
_pEph->iodc = U2(_pBuff + 147);

_pEph->week = adjgpsweek(uWeekE) - BDS_WEEK_TO_GPS_WEEK;
_pEph->toe = gpst2time(_pEph->week, _pEph->toes);
/* SBP carries full GPS week numbers for BDS messages. Internal rtklib
BDS convention: eph.week is BDS-relative; eph.toe/eph.toc are GPST
gtime_t derived from BDT (week, tow) -- matches rtcm3.c MT 1042. */
_pEph->week = uWeekE - BDS_WEEK_TO_GPS_WEEK;
_pEph->toe = bdt2gpst(bdt2time(_pEph->week, _pEph->toes));
_pEph->toc = gpst2time(uWeekC, dToc);
}

Expand Down Expand Up @@ -802,7 +807,8 @@ static void decode_galnav_common(uint8_t *_pBuff, eph_t *_pEph) {
_pEph->iode = U2(_pBuff + 150);
_pEph->iodc = U2(_pBuff + 152);

_pEph->week = adjgpsweek(uWeekE);
/* SBP carries a full 16-bit GPS week; trust it directly. */
_pEph->week = uWeekE;
_pEph->toe = gpst2time(_pEph->week, _pEph->toes);
_pEph->toc = gpst2time(uWeekC, dToc);
}
Expand Down Expand Up @@ -835,10 +841,11 @@ static int decode_gpsnav_dep_e(raw_t *raw) {

decode_gpsnav_common_dep1(puiTmp, &eph);

if (0 == timediff(raw->time, time0)) {
eph.ttr = timeget();
} else {
if (strstr(raw->opt, "-SAFEEPHEMTIME") ||
timediff(raw->time, time0) != 0) {
eph.ttr = raw->time;
} else {
eph.ttr = timeget();
}

if (!strstr(raw->opt, "EPHALL")) {
Expand Down Expand Up @@ -897,10 +904,11 @@ static int decode_gpsnav_dep_f(raw_t *raw) {

decode_gpsnav_common_dep1(puiTmp - 2, &eph);

if (0 == timediff(raw->time, time0)) {
eph.ttr = timeget();
} else {
if (strstr(raw->opt, "-SAFEEPHEMTIME") ||
timediff(raw->time, time0) != 0) {
eph.ttr = raw->time;
} else {
eph.ttr = timeget();
}

if (!strstr(raw->opt, "EPHALL")) {
Expand Down Expand Up @@ -952,10 +960,11 @@ static int decode_gpsnav(raw_t *raw) {

decode_gpsnav_common(puiTmp - 2, &eph);

if (0 == timediff(raw->time, time0)) {
eph.ttr = timeget();
} else {
if (strstr(raw->opt, "-SAFEEPHEMTIME") ||
timediff(raw->time, time0) != 0) {
eph.ttr = raw->time;
} else {
eph.ttr = timeget();
}

if (!strstr(raw->opt, "EPHALL")) {
Expand Down Expand Up @@ -1015,10 +1024,11 @@ static int decode_qzssnav(raw_t *raw) {

decode_gpsnav_common(puiTmp - 2, &eph);

if (0 == timediff(raw->time, time0)) {
eph.ttr = timeget();
} else {
if (strstr(raw->opt, "-SAFEEPHEMTIME") ||
timediff(raw->time, time0) != 0) {
eph.ttr = raw->time;
} else {
eph.ttr = timeget();
}

if (!strstr(raw->opt, "EPHALL")) {
Expand Down
Loading
Loading