Merge branch 'master' of https://github.com/ahupowerdns/galmon into openbsd-fixes

Make hardware handshaking settable, default to off for OpenBSD
pull/1/head
Otto Moerbeek 2019-09-05 10:19:43 +02:00
commit 428111de84
23 changed files with 878 additions and 128 deletions

15
.github/workflows/ccpp.yml vendored 100644
View File

@ -0,0 +1,15 @@
name: C/C++ CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: make
run: make
- name: make check
run: make check

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "ext/powerblog"]
path = ext/powerblog
url = https://github.com/ahupowerdns/powerblog.git
[submodule "ext/sgp4"]
path = ext/sgp4
url = https://github.com/dnwrnr/sgp4.git

View File

@ -1,6 +1,12 @@
CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/fmt-5.2.1/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/
CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/CLI11 \
-Iext/fmt-5.2.1/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/ \
-I/usr/local/opt/openssl/include/ \
-Iext/sgp4/libsgp4/ \
-I/usr/local/include
PROGRAMS = navparse ubxtool navnexus navrecv navdump
# CXXFLAGS += -Wno-delete-non-virtual-dtor
PROGRAMS = navparse ubxtool navnexus navrecv navdump testrunner navdisplay tlecatch
all: navmon.pb.cc $(PROGRAMS)
@ -12,23 +18,33 @@ clean:
H2OPP=ext/powerblog/h2o-pp.o
SIMPLESOCKETS=ext/powerblog/ext/simplesocket/swrappers.o ext/powerblog/ext/simplesocket/sclasses.o ext/powerblog/ext/simplesocket/comboaddress.o
navparse: navparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf # -lwslay
navparse: navparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf # -lwslay
navdump: navdump.o ext/fmt-5.2.1/src/format.o bits.o navmon.pb.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -lprotobuf
navdump: navdump.o ext/fmt-5.2.1/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o navmon.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
navdisplay: navdisplay.o ext/fmt-5.2.1/src/format.o bits.o navmon.pb.o gps.o ephemeris.o beidou.o glonass.o ephemeris.o navmon.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf -lncurses
navnexus: navnexus.o ext/fmt-5.2.1/src/format.o $(SIMPLESOCKETS) ubx.o bits.o navmon.pb.o storage.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -lprotobuf
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
navrecv: navrecv.o ext/fmt-5.2.1/src/format.o $(SIMPLESOCKETS) navmon.pb.o storage.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -lprotobuf
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
tlecatch: tlecatch.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc))
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -pthread -lprotobuf
navmon.pb.cc: navmon.proto
protoc --cpp_out=./ navmon.proto
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o ext/fmt-5.2.1/src/format.o galileo.o gps.o
$(CXX) -std=gnu++17 $^ -o $@ -lprotobuf
ubxtool: navmon.pb.o ubxtool.o ubx.o bits.o ext/fmt-5.2.1/src/format.o galileo.o gps.o beidou.o navmon.o ephemeris.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf
testrunner: navmon.pb.o testrunner.o ubx.o bits.o ext/fmt-5.2.1/src/format.o galileo.o gps.o beidou.o ephemeris.o
$(CXX) -std=gnu++17 $^ -o $@ -L/usr/local/lib -lprotobuf
check: testrunner
./testrunner

View File

@ -1,6 +1,7 @@
#include "beidou.hh"
#include "bits.hh"
#include <iostream>
using namespace std;
// with immense gratitude to https://stackoverflow.com/questions/24612436/how-to-check-a-bch15-11-1-code-checksum-for-bds-beidou-satellite-system
@ -72,3 +73,54 @@ int beidouBitconv(int their)
return their - (1 + 4 + (wordcount -1)*8);
}
bool processBeidouAlmanac(const BeidouMessage& bm, struct BeidouAlmanacEntry& bae)
{
bae.alma = bm.alma;
int pageno = bm.alma.pageno;
if(bm.fraid == 5 && pageno >= 11 && pageno <= 23) {
/*
cout<<" AmEpID "<< bm.alma.AmEpID;
cout << " AmID "<<bm.alma.AmID; */
if(bm.alma.AmEpID != 3) {
// cout<<" skipping page because AmEpID tells us to "<<endl;
return false;
}
}
if(ephAge(bm.sow, bm.alma.getT0e()) < 0) {
// cout <<" almanac too old t0a "<<bm.alma.getT0e()<<" sow "<<bm.sow << " days " << 1.0*(bm.alma.getT0e()-bm.sow)/86400<< endl;
return false;
}
if(bm.alma.sqrtA == 0) {
// cout<<"sqrtA==0 - likely not a present satellite "<<endl;
return false;
}
Point sat;
if(bm.fraid ==4 && pageno <= 5)
bae.alma.geo=true;
else
bae.alma.geo=false;
int offset = 0;
if(bm.fraid == 4)
offset = 0;
else if(bm.fraid == 5 && pageno <=6)
offset = 24;
else if(bm.fraid == 5 && bm.alma.AmID ==1)
offset = 20;
else if(bm.fraid == 5 && bm.alma.AmID ==2)
offset = 34;
else if(bm.fraid == 5 && bm.alma.AmID ==3)
offset = 46;
bae.sv = pageno+offset;
// cout<<" eff-id "<<bae.sv;
if(bae.sv == 59)
bae.alma.geo = true;
return true;
}

View File

@ -159,10 +159,67 @@ struct BeidouMessage
omega = bbits(252, 32);
}
struct Almanac
{
bool geo{false};
uint32_t sqrtA;
int a0, a1;
int Omega0;
uint32_t e;
int deltai;
uint8_t t0a;
int Omegadot;
int omega;
int M0;
int AmEpID;
int AmID;
int pageno;
double getMu() const { return 3.986004418 * pow(10.0, 14.0); } // m^3/s^2
double getOmegaE() const { return 7.2921150 * pow(10, -5); } // rad/s
uint32_t getT0e() const { return ldexp((int)t0a, 12); }
double getSqrtA() const { return ldexp(sqrtA, -11); }
double getE() const { return ldexp(e, -21); }
double getOmega() const { return ldexp(omega * M_PI, -23); } // radians
double getM0() const { return ldexp(M0 * M_PI, -23); } // radians
double getOmega0() const { return ldexp(Omega0 * M_PI, -23); } // radians
double getOmegadot() const { return ldexp(Omegadot * M_PI, -38); } // radians/s
// XXX for GEO, i0 = 0!!
double getI0() const {
if(geo)
return ldexp(M_PI*deltai, -19);
return M_PI*0.3 + ldexp(M_PI*deltai, -19);
} // radians
double getIdot() const { return 0; } // radians/s
double getCic() const { return 0; } // radians
double getCis() const { return 0; } // radians
double getCuc() const { return 0; } // radians
double getCus() const { return 0; } // radians
double getCrc() const { return 0; } // meters
double getCrs() const { return 0; } // meters
double getDeltan()const { return 0; } //radians/s
} alma;
// 4 is all almanac
int parse4(std::basic_string_view<uint8_t> cond)
{
return bbitu(44, 7);
alma.sqrtA = bbitu(51, 24);
alma.a1 = bbits(91, 11);
alma.a0 = bbits(102, 11);
alma.Omega0 = bbits(121, 24);
alma.e = bbitu(153, 17);
alma.deltai = bbits(170, 16);
alma.t0a = bbitu(194, 8);
alma.Omegadot = bbits(202, 17);
alma.omega = bbits(227, 24);
alma.M0 = bbits(259, 24);
alma.AmEpID = bbitu(291, 2);
alma.pageno = bbitu(44, 7);
return alma.pageno;
}
int a0gps, a1gps, a0gal, a1gal, a0glo, a1glo, a0utc, a1utc;
@ -170,8 +227,8 @@ struct BeidouMessage
int parse5(std::basic_string_view<uint8_t> cond)
{
int pageno = bbitu(44, 7);
if(pageno == 9) {
alma.pageno = bbitu(44, 7);
if(alma.pageno == 9) {
a0gps = bbits(97, 14);
a1gps = bbits(111, 16);
a0gal = bbits(135, 14);
@ -179,13 +236,39 @@ struct BeidouMessage
a0glo = bbits(181, 14);
a1glo = bbits(195, 16);
}
if(pageno == 10) {
if(alma.pageno == 10) {
a0utc = bbits(91, 32);
a1utc = bbits(131, 24);
deltaTLS = bbits(31+12+1+7, 8);
}
return pageno;
else {
alma.sqrtA = bbitu(51, 24);
alma.a1 = bbits(91, 11);
alma.a0 = bbits(102, 11);
alma.Omega0 = bbits(121, 24);
alma.e = bbitu(153, 17);
alma.deltai = bbits(170, 16);
alma.t0a = bbitu(194, 8);
alma.Omegadot = bbits(202, 17);
alma.omega = bbits(227, 24);
alma.M0 = bbits(259, 24);
if(alma.pageno <=6)
alma.AmEpID = bbitu(291, 2);
else if(alma.pageno >= 11 && alma.pageno <= 23)
alma.AmID = bbitu(291, 2);
}
return alma.pageno;
}
};
struct BeidouAlmanacEntry
{
int sv;
BeidouMessage::Almanac alma;
};
bool processBeidouAlmanac(const BeidouMessage& bm, struct BeidouAlmanacEntry& bae);

View File

@ -1,4 +1,5 @@
#include "ephemeris.hh"
#include "minivec.hh"
/* | t0e tow | - > tow - t0e, <3.5 days, so ok
| t0e tow | -> tow - t0e > 3.5 days, so
@ -13,21 +14,37 @@
// positive age = t0e in the past
int ephAge(int tow, int t0e)
{
unsigned int diff;
unsigned int halfweek = 0.5*7*86400;
if(t0e < tow) {
diff = tow - t0e;
if(diff < halfweek)
return diff;
else
return -(7*86400 - tow) - t0e;
}
else { // "t0e in future"
diff = 7*86400 - t0e + tow;
if(diff < halfweek)
return diff;
else
return tow - t0e; // in the future, negative age
}
int diff = tow - t0e;
if(diff > 3.5*86400)
diff -= 604800;
if(diff < -3.5*86400)
diff += 604800;
return diff;
}
// all axes start at earth center of gravity
// x-axis is on equator, 0 longitude
// y-axis is on equator, 90 longitude
// z-axis is straight up to the north pole
// https://en.wikipedia.org/wiki/ECEF#/media/File:Ecef.png
std::pair<double,double> getLongLat(double x, double y, double z)
{
Point core{0,0,0};
Point LatLon0{1,0,0};
Point pos{x, y, z};
Point proj{x, y, 0}; // in equatorial plane now
Vector flat(core, proj);
Vector toLatLon0{core, LatLon0};
double longitude = acos( toLatLon0.inner(flat) / (toLatLon0.length() * flat.length()));
if(y < 0)
longitude *= -1;
Vector toUs{core, pos};
double latitude = acos( flat.inner(toUs) / (toUs.length() * flat.length()));
if(z < 0)
latitude *= -1;
return std::make_pair(longitude, latitude);
}

View File

@ -184,3 +184,5 @@ DopplerData doDoppler(int wn, int tow, const Point& us, const T& eph, double fre
return ret;
}
std::pair<double,double> getLongLat(double x, double y, double z);

1
ext/sgp4 160000

@ -0,0 +1 @@
Subproject commit 6b47861cd47a6e31841260c47a52b579f8cf2fa9

View File

@ -96,10 +96,38 @@ struct GalileoMessage
struct Almanac
{
int svid{-1};
int t0almanac, wnalmanac;
int af0, af1;
int e1bhs, e5bhs;
} alma1, alma2, alma3;
uint32_t e, deltaSqrtA;
int32_t M0, Omega0, deltai, omega, Omegadot;
double getMu() const { return 3.986005 * pow(10.0, 14.0); }
double getOmegaE() const { return 7.2921151467 * pow(10.0, -5.0);} // rad/s
uint32_t getT0e() const { return 600 * t0almanac; }
double getSqrtA() const { return sqrt(29600000) + ldexp(deltaSqrtA, -9); }
double getE() const { return ldexp(e, -16); }
double getI0() const { return M_PI*56.0/180.0 + ldexp(deltai * M_PI, -14); } // radians
double getOmega0() const { return ldexp(Omega0 * M_PI, -15); } // radians
double getOmegadot() const { return ldexp(Omegadot * M_PI, -33); } // radians/s
double getOmega() const { return ldexp(omega * M_PI, -15); } // radians
double getM0() const { return ldexp(M0 * M_PI, -15); } // radians
double getIdot() const { return 0; } // radians/s
double getCic() const { return 0; } // radians
double getCis() const { return 0; } // radians
double getCuc() const { return 0; } // radians
double getCus() const { return 0; } // radians
double getCrc() const { return 0; } // meters
double getCrs() const { return 0; } // meters
double getDeltan()const { return 0; } //radians/s
} alma1, alma2, alma3;
// an ephemeris word
void parse1(std::basic_string_view<uint8_t> page)
@ -206,43 +234,61 @@ struct GalileoMessage
void parse7(std::basic_string_view<uint8_t> page)
{
iodalmanac = getbitu(&page[0], 6, 4);
wnalmanac = getbitu(&page[0], 10, 2);
t0almanac = getbitu(&page[0], 12, 10);
alma1.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2);
alma1.t0almanac = t0almanac = getbitu(&page[0], 12, 10);
alma1.svid = getbitu(&page[0], 22, 6);
alma1.deltaSqrtA = getbitu(&page[0], 28, 13);
alma1.e = getbitu(&page[0], 41, 11);
alma1.omega = getbits(&page[0], 52, 16);
alma1.deltai = getbits(&page[0], 68, 11);
alma1.Omega0 = getbits(&page[0], 79, 16);
alma1.Omegadot = getbits(&page[0], 95, 11);
alma1.M0 = getbits(&page[0], 106, 16);
}
// almanac
void parse8(std::basic_string_view<uint8_t> page)
{
iodalmanac = getbitu(&page[0], 6, 4);
alma1.af0 = getbits(&page[0], 10, 16);
alma1.af1 = getbits(&page[0], 26, 13);
alma1.e5bhs = getbitu(&page[0], 39, 2);
alma1.e1bhs = getbitu(&page[0], 41, 2);
alma2.svid = getbitu(&page[0], 43, 6);
alma2.deltaSqrtA = getbitu(&page[0], 49, 13);
alma2.e = getbitu(&page[0], 62, 11);
alma2.omega = getbits(&page[0], 73, 16);
alma2.deltai = getbits(&page[0], 89, 11);
alma2.Omega0 = getbits(&page[0], 100, 16);
alma2.Omegadot = getbits(&page[0], 116, 11);
}
// almanac
void parse9(std::basic_string_view<uint8_t> page)
{
iodalmanac = getbitu(&page[0], 6, 4);
alma2.wnalmanac = wnalmanac = getbitu(&page[0], 10, 2);
alma2.t0almanac = t0almanac = getbits(&page[0], 12, 10);
alma2.M0 = getbits(&page[0], 22, 16);
alma2.af0 = getbits(&page[0], 38, 16);
alma2.af1 = getbits(&page[0], 54, 13);
alma2.e5bhs = getbitu(&page[0], 67, 2);
alma2.e1bhs = getbitu(&page[0], 69, 2);
alma3.svid = getbitu(&page[0], 71, 6);
}
// almanac + more time stuff (GPS)
void parse10(std::basic_string_view<uint8_t> page)
{
iodalmanac = getbitu(&page[0], 6, 4);
alma3.af0 = getbits(&page[0], 53, 16);
alma3.af1 = getbits(&page[0], 69, 13);
alma3.e5bhs = getbitu(&page[0], 82, 2);
alma3.e1bhs = getbitu(&page[0], 84, 2);
a0g = getbits(&page[0], 86, 16);
a1g = getbits(&page[0], 102, 12);

View File

@ -42,8 +42,19 @@ struct GlonassMessage
Various GLONASS things relate to "the day", so it is important to note which day we are at
*/
uint8_t hour, minute, seconds, P1;
int32_t x, dx, ddx;
int32_t x{0}, dx, ddx; // 2^-11 km, 2^-20 km/s, 2^-30 km/s^2
double getX() { return ldexp(x*1000.0, -11); }
double getY() { return ldexp(y*1000.0, -11); }
double getZ() { return ldexp(z*1000.0, -11); }
double getdX() { return ldexp(dx*1000.0, -20); }
double getdY() { return ldexp(dy*1000.0, -20); }
double getdZ() { return ldexp(dz*1000.0, -20); }
double getRadius() { return sqrt(getX()*getX() + getY()*getY() + getZ()*getZ()); }
void parse1(std::basic_string_view<uint8_t> gstr)
{
hour = getbitu(&gstr[0], 9, 5);
@ -56,7 +67,7 @@ struct GlonassMessage
}
uint8_t Bn, Tb, P2;
int32_t y, dy, ddy;
int32_t y{0}, dy, ddy;
/* The GLONASS ephemeris centered on the "Tb-th" interval, from the start of the Moscow day.
An interval is 15 minutes long, plus a spacer of length described by P1. If P1 is zero, there is no spacer.
@ -74,7 +85,7 @@ struct GlonassMessage
}
int32_t z, dz, ddz;
int32_t z{0}, dz, ddz;
bool l_n;
bool P, P3;
uint16_t gamman;
@ -120,28 +131,67 @@ struct GlonassMessage
uint32_t getGloTime() const;
uint8_t n4{0}; // counting from 1996 ('n4=1'), this is the 4-year plan index we are currently in
uint16_t taugps;
int32_t taugps;
int32_t tauc;
void parse5(std::basic_string_view<uint8_t> gstr)
{
n4=getbitu(&gstr[0], 85-36, 5);
taugps = getbitsglonass(&gstr[0], 85-31, 22);
tauc = getbitsglonass(&gstr[0], 85-69, 32);
l_n = getbitu(&gstr[0], 85 - 9, 1);
}
double omegana; // 2^-15 semi-circles, at instantt of tlambdana
uint8_t hna;
uint32_t tlambdana; // 2^-5s time of ascending node passage, also: t0a, relative to MT midnight
int32_t deltatna; // 2^-9
double gettLambdaNa() const
{
return ldexp(tlambdana, -5);
}
void parse7_9_11_13_15(std::basic_string_view<uint8_t> gstr)
{
l_n = getbitu(&gstr[0], 85 - 9, 1);
omegana = getbitsglonass(&gstr[0], 85-80, 16);
hna = getbitu(&gstr[0], 85 - 14, 5); // this is always positive, but there is a translation table
tlambdana = getbitu(&gstr[0], 85 - 64, 21);
deltatna = getbitsglonass(&gstr[0], 85 - 43, 22);
}
int nA;
bool CnA;
int32_t lambdana; // 2^-20 semi-circles
int32_t deltaina; // 2^-20 semi-circles
int32_t epsilonna; // 2^-20
int32_t tauna; // 2^-18
double getLambdaNaDeg() const
{
return ldexp(180.0*lambdana, -20);
}
double getE() const
{
return ldexp(epsilonna, -20);
}
double getI0() const
{
return M_PI*63.0/180 + ldexp(M_PI* deltaina, -20);
}
void parse6_8_10_12_14(std::basic_string_view<uint8_t> gstr)
{
CnA = getbitu(&gstr[0], 85-80, 1);
nA = getbitu(&gstr[0], 85-77, 5);
lambdana = getbitsglonass(&gstr[0], 85-62, 21);
deltaina = getbitsglonass(&gstr[0], 85-41, 18);
epsilonna = getbitu(&gstr[0], 85- 23, 15);
tauna = getbitsglonass(&gstr[0], 85 - 72, 10);
}
};

View File

@ -14,7 +14,7 @@ function maketable(str, arr)
enter().
append("tr");
var columns = ["sv", "iod", "aodc/e", "eph-age-m", "latest-disco", "sisa", "delta_hz_corr", "health", "a0", "a1","a0g", "a1g", "sources", "db", "elev", "last-seen-s"];
var columns = ["sv", "iod", "aodc/e", "eph-age-m", "latest-disco", "time-disco", "sisa", "delta_hz_corr", "health", "a0", "a1","a0g", "a1g", "sources", "db", "elev", "last-seen-s"];
// append the header row
thead.append("tr")
@ -53,6 +53,8 @@ function maketable(str, arr)
else if(column == "aodc/e") {
if(row["aodc"] != null && row["aode"] != null)
ret.value = row["aodc"]+"/"+row["aode"];
else if(row["aode"] != null)
ret.value = row["aode"];
else
ret.value="";
}
@ -82,9 +84,10 @@ function maketable(str, arr)
}
else if(column == "latest-disco" && row[column] != null)
ret.value = ((100*row[column]).toFixed(2))+" cm";
else if(column == "time-disco" && row[column] != null)
ret.value = row[column].toFixed(2)+" ns";
else if(column == "health") {
ret.value = row[column];
console.log(row["healthissue"]);
if(row["healthissue"] == 1) {
ret.color="orange";
}
@ -99,6 +102,10 @@ function maketable(str, arr)
ret.color="orange";
if(column == "sisa" && parseInt(row[column]) > 312)
ret.color="#ffaaaa";
var myRe = RegExp('[0-9]* m');
if(column == "sisa" && myRe.test(row[column]))
ret.color="#ff2222";
if(column == "sisa" && row[column]=="NO SIS AVAILABLE")
ret.color="#ff2222";
return ret;

View File

@ -54,8 +54,18 @@ tr:nth-child(odd) {background: #FFF}
<p>
The official Galileo constellation status can be found on the <a href="https://www.gsc-europa.eu/system-status/Constellation-Information">European GNSS Service Centre page</a>, which also lists "NAGUs", <a href="https://www.gsc-europa.eu/system-status/user-notifications">notifications about outages or changes</a>.
</p>
<p>
Official GLONASS status can be found on <a href="https://www.glonass-iac.ru/en/GLONASS/">this page</a> from the Russian Information and Analysis Center for Positioning, Navigation and Timing.
</p>
<p>
Sadly reduced status updates on GPS can be found on <a href="https://www.navcen.uscg.gov/?Do=constellationStatus">this page</a> from the US Department of Homeland Security.
</p>
<p>
Information on the status of BeiDou can be found on <a href="http://www.csno-tarc.cn/system/constellation&ce=english">this page</a> from the Chinese Test and Assessment Research Center of China Satellite Navigation Office.
</p>
<p>
Feedback is very welcome on bert@hubertnet.nl or <a href="https://twitter.com/PowerDNS_Bert">@PowerDNS_Bert</a>.
</p>
<script src="d3.v4.min.js"></script>
<script src="ext/moment-with-locales.js"></script>
<script src="doalles.js"></script>

View File

@ -12,6 +12,7 @@
#include "galileo.hh"
#include "navmon.pb.h"
#include <unistd.h>
#include "navmon.hh"
using namespace std;
@ -142,17 +143,17 @@ int main()
for(;;) {
char bert[4];
if(read(0, bert, 4) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') {
if(readn2(0, bert, 4) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') {
cerr<<"EOF or bad magic"<<endl;
break;
}
uint16_t len;
if(read(0, &len, 2) != 2)
if(readn2(0, &len, 2) != 2)
break;
len = htons(len);
char buffer[len];
if(read(0, buffer, len) != len)
if(readn2(0, buffer, len) != len)
break;
NavMonMessage nmm;

View File

@ -1,4 +1,3 @@
#include <string>
#include <stdio.h>
#include <iostream>
@ -22,6 +21,7 @@
#include "beidou.hh"
#include "galileo.hh"
#include "navmon.hh"
#include "tle.hh"
#include <unistd.h>
using namespace std;
@ -62,7 +62,19 @@ string beidouHealth(int in)
}
int main(int argc, char** argv)
try
{
TLERepo tles;
// tles.parseFile("active.txt");
tles.parseFile("galileo.txt");
tles.parseFile("glo-ops.txt");
tles.parseFile("gps-ops.txt");
tles.parseFile("beidou.txt");
ofstream almanac("almanac.txt");
ofstream iodstream("iodstream.csv");
iodstream << "timestamp gnssid sv iodnav t0e age" << endl;
for(;;) {
char bert[4];
int res = readn2(0, bert, 4);
@ -86,23 +98,26 @@ int main(int argc, char** argv)
NavMonMessage nmm;
nmm.ParseFromString(string(buffer, len));
if(nmm.type() == NavMonMessage::ReceptionDataType)
continue;
// if(nmm.type() == NavMonMessage::ReceptionDataType)
// continue;
cout<<humanTime(nmm.localutcseconds())<<" "<<nmm.localutcnanoseconds()<<" ";
cout<<"src "<<nmm.sourceid()<< " ";
if(nmm.type() == NavMonMessage::ReceptionDataType) {
cout<<"receptiondata for "<<nmm.rd().gnssid()<<","<<nmm.rd().gnsssv()<<", db "<<nmm.rd().db()<<" ele "<<nmm.rd().el() <<" azi "<<nmm.rd().azi()<<endl;
cout<<"receptiondata for "<<nmm.rd().gnssid()<<","<<nmm.rd().gnsssv()<<", db "<<nmm.rd().db()<<" ele "<<nmm.rd().el() <<" azi "<<nmm.rd().azi()<<" prRes "<<nmm.rd().prres() << endl;
}
else if(nmm.type() == NavMonMessage::GalileoInavType) {
basic_string<uint8_t> inav((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size());
static map<int, GalileoMessage> gms;
static map<pair<int, int>, GalileoMessage> gmwtypes;
static map<int, GalileoMessage> oldgm4s;
GalileoMessage& gm = gms[nmm.gi().gnsssv()];
int sv = nmm.gi().gnsssv();
GalileoMessage& gm = gms[sv];
int wtype = gm.parse(inav);
cout << "gal inav for "<<nmm.gi().gnssid()<<","<<nmm.gi().gnsssv()<<" tow "<< nmm.gi().gnsstow()<<" wtype "<< wtype << endl;
gm.tow = nmm.gi().gnsstow();
gmwtypes[{nmm.gi().gnsssv(), wtype}] = gm;
cout << "gal inav for "<<nmm.gi().gnssid()<<","<<nmm.gi().gnsssv()<<" tow "<< nmm.gi().gnsstow()<<" wtype "<< wtype<<" ";
static uint32_t tow;
if(wtype == 4) {
// 2^-34 2^-46
@ -113,31 +128,54 @@ int main(int argc, char** argv)
auto newOffset = gm.getAtomicOffset(tow);
cout<<" Timejump: "<<oldOffset.first - newOffset.first<<" after "<<(gm.getT0c() - oldgm4.getT0c() )<<" seconds";
}
cout<<endl;
oldgm4s[nmm.gi().gnsssv()] = gm;
}
if(wtype == 1 || wtype == 2 || wtype == 3) {
iodstream << nmm.localutcseconds()<<" " << nmm.gi().gnssid() <<" "<< nmm.gi().gnsssv() << " " << gm.iodnav << " " << gm.t0e*60 <<" " << ephAge(gm.t0e*60, gm.tow);
if(wtype == 3)
iodstream<<endl;
else
iodstream<<"\n";
}
if(wtype == 0 || wtype == 5 || wtype == 6)
tow = gm.tow;
if(wtype < 7)
gm = GalileoMessage{};
// if(wtype < 7)
// gm = GalileoMessage{};
if(wtype >=7 && wtype<=10)
cout<<" ioda "<<gm.iodalmanac;
// af0 af1 scaling in almanac: 2^-19, 2^2^-38 plus "truncated"
if(wtype == 7) {
cout<<" t0a "<<gm.t0almanac<<", alma sv1 "<<gm.alma1.svid<<", t0a age: "<< ephAge(gm.t0almanac *600, tow) << endl;
cout<<" t0a "<<gm.t0almanac<<", alma sv1 "<<gm.alma1.svid<<", t0a age: "<< ephAge(gm.t0almanac *600, tow) << " ";
if(gm.alma1.svid) {
Point satpos;
getCoordinates(0, gm.tow, gm.alma1, &satpos);
cout<< "("<<satpos.x/1000<<", "<<satpos.y/1000<<", "<<satpos.z/1000<<")";
auto match = tles.getBestMatch(nmm.localutcseconds(), satpos.x, satpos.y, satpos.z);
cout<<" best-tle-match "<<match.name <<" distance "<<match.distance /1000<<" km ";
cout <<" tle-e "<<match.e <<" eph-e " <<gm.alma1.getE() <<" tle-ran "<<match.ran;
cout<<" norad " <<match.norad <<" int-desig " << match.internat;
}
}
else if(wtype == 8 && gm.alma1.svid > 0) {
cout<<" "<<gm.alma1.svid<<" af0 "<<gm.alma1.af0<<" af1 "<< gm.alma1.af1 <<" e5bhs "<< gm.alma1.e5bhs<<" e1bhs "<< gm.alma1.e1bhs<<endl;
else if(wtype == 8 && gm.tow - gmwtypes[{sv,7}].tow < 5 && gmwtypes[{sv,7}].alma1.svid && gm.iodalmanac == gmwtypes[{sv,7}].iodalmanac) {
cout<<" "<<gmwtypes[{sv,7}].alma1.svid<<" af0 "<<gm.alma1.af0<<" af1 "<< gm.alma1.af1 <<" e5bhs "<< gm.alma1.e5bhs<<" e1bhs "<< gm.alma1.e1bhs <<" sv9 "<<gm.alma2.svid;
}
else if(wtype == 9 && gm.alma2.svid > 0) {
cout<<" "<<gm.alma2.svid<<" af0 "<<gm.alma2.af0<<" af1 "<< gm.alma2.af1 <<" e5bhs "<< gm.alma2.e5bhs<<" e1bhs "<< gm.alma2.e1bhs<<endl;
else if(wtype == 9 && gm.tow - gmwtypes[{sv,8}].tow < 30 && gm.iodalmanac == gmwtypes[{sv,8}].iodalmanac) {
if(gmwtypes[{sv,8}].alma2.svid)
cout<<" "<<gmwtypes[{sv,8}].alma2.svid<<" af0 "<<gm.alma2.af0<<" af1 "<< gm.alma2.af1 <<" e5bhs "<< gm.alma2.e5bhs<<" e1bhs "<< gm.alma2.e1bhs;
else
cout<<" empty almanac slot";
}
else if(wtype == 10 && gm.alma3.svid > 0){
cout<<" "<<gm.alma3.svid<<" af0 "<<gm.alma3.af0<<" af1 "<< gm.alma3.af1 <<" e5bhs "<< gm.alma3.e5bhs<<" e1bhs "<< gm.alma3.e1bhs<<endl;
else if(wtype == 10 && gm.tow - gmwtypes[{sv,9}].tow < 5 && gmwtypes[{sv,9}].alma3.svid && gm.iodalmanac == gmwtypes[{sv,9}].iodalmanac) {
if(gm.alma3.e1bhs != 0) {
cout <<" gm.tow "<<gm.tow<<" gmwtypes.tow "<< gmwtypes[{sv,9}].tow <<" ";
}
cout<<" "<<gmwtypes[{sv,9}].alma3.svid <<" af0 "<<gm.alma3.af0<<" af1 "<< gm.alma3.af1 <<" e5bhs "<< gm.alma3.e5bhs<<" e1bhs "<< gm.alma3.e1bhs <<" a0g " << gm.a0g <<" a1g "<< gm.a1g <<" t0g "<<gm.t0g <<" wn0g "<<gm.wn0g;
}
cout<<endl;
}
else if(nmm.type() == NavMonMessage::GPSInavType) {
int sv = nmm.gpsi().gnsssv();
@ -165,9 +203,11 @@ int main(int argc, char** argv)
else if(nmm.type() == NavMonMessage::BeidouInavTypeD1) {
int sv = nmm.bid1().gnsssv();
auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size()));
BeidouMessage bm;
uint8_t pageno;
static map<int, BeidouMessage> bms;
auto& bm = bms[sv];
int fraid = bm.parse(cond, &pageno);
cout<<"BeiDou "<<sv<<": "<<bm.sow<<", FraID "<<fraid;
if(fraid == 1) {
static map<int, BeidouMessage> msgs;
@ -181,31 +221,52 @@ int main(int argc, char** argv)
auto offset = bm.getAtomicOffset();
cout<< ", "<<offset.first<<"ns " << (offset.second * 3600) <<" ns/hour "<< ephAge(bm.sow, bm.t0c*8)<<endl;
}
else if(fraid == 4 && 1<= pageno && pageno <= 24) {
cout <<" pageno "<< (int) pageno<<" AmEpID "<< getbitu(&cond[0], beidouBitconv(291), 2);
}
else if(fraid == 5 && 1<= pageno && pageno <= 6) {
cout <<" pageno "<<(int)pageno<<" AmEpID "<< getbitu(&cond[0], beidouBitconv(291), 2);
}
else if(fraid == 3 && bm.sow) {
Point sat;
getCoordinates(0, bm.sow, bm, &sat);
TLERepo::Match second;
auto match = tles.getBestMatch(nmm.localutcseconds(), sat.x, sat.y, sat.z, &second);
cout<<" best-tle-match "<<match.name <<" dist "<<match.distance /1000<<" km";
cout<<" norad " <<match.norad <<" int-desig " << match.internat;
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km";
else if(fraid == 5 && pageno==7) {
}
else if((fraid == 4 && 1<= pageno && pageno <= 24) ||
(fraid == 5 && 1<= pageno && pageno <= 6) ||
(fraid == 5 && 11<= pageno && pageno <= 23) ) {
struct BeidouAlmanacEntry bae;
if(processBeidouAlmanac(bm, bae)) {
cout<<" alma-sv "<<bae.sv;
Point sat;
getCoordinates(0, bm.sow, bae.alma, &sat);
TLERepo::Match second;
auto match = tles.getBestMatch(nmm.localutcseconds(), sat.x, sat.y, sat.z, &second);
cout<<" best-tle-match "<<match.name <<" dist "<<match.distance /1000<<" km";
cout<<" norad " <<match.norad <<" int-desig " << match.internat;
cout<<" 2nd-match "<<second.name << " dist "<<second.distance/1000<<" km t0e "<<bm.alma.getT0e() << " t " <<nmm.localutcseconds();
}
else
cout <<" no valid alma";
}
else if(bm.fraid == 5 && pageno==7) {
for(int n=0; n<19; ++n)
cout<<" hea"<<(1+n)<<" " << getbitu(&cond[0], beidouBitconv(51+n*9), 9) << " ("<<beidouHealth(getbitu(&cond[0], beidouBitconv(51+n*9), 9)) <<")";
}
else if(fraid == 5 && pageno==8) {
else if(bm.fraid == 5 && pageno==8) {
for(int n=0; n<10; ++n)
cout<<" hea"<<(20+n)<<" " << getbitu(&cond[0], beidouBitconv(51+n*9), 9) << " ("<<beidouHealth(getbitu(&cond[0], beidouBitconv(51+n*9), 9))<<")";
cout<<" WNa "<<getbitu(&cond[0], beidouBitconv(190), 8)<<" t0a "<<getbitu(&cond[0], beidouBitconv(198), 8);
}
else if(fraid == 5 && pageno==24) {
else if(bm.fraid == 5 && pageno==24) {
int AmID= getbitu(&cond[0], beidouBitconv(216), 2);
cout<<" AmID "<< AmID;
for(int n=0; n<14; ++n)
cout<<" hea"<<(31+n)<<" (" << getbitu(&cond[0], beidouBitconv(51+n*9), 9) << " "<<beidouHealth(getbitu(&cond[0], beidouBitconv(51+n*9), 9))<<")";
}
cout<<endl;
}
else if(nmm.type() == NavMonMessage::BeidouInavTypeD2) {
int sv = nmm.bid2().gnsssv();
@ -223,7 +284,7 @@ int main(int argc, char** argv)
int strno = gm.parse(std::basic_string<uint8_t>((uint8_t*)nmm.gloi().contents().c_str(), nmm.gloi().contents().size()));
cout<<"Glonass R"<<nmm.gloi().gnsssv()<<" @ "<<nmm.gloi().freq() <<" strno "<<strno;
cout<<"Glonass R"<<nmm.gloi().gnsssv()<<" @ "<< ((int)nmm.gloi().freq()-7) <<" strno "<<strno;
if(strno == 1) {
cout << ", hour "<<(int)gm.hour <<" minute " <<(int)gm.minute <<" seconds "<<(int)gm.seconds;
// start of period is 1st of January 1996 + (n4-1)*4, 03:00 UTC
@ -232,19 +293,40 @@ int main(int argc, char** argv)
}
if(strno == 2)
cout<<" Tb "<<(int)gm.Tb <<" Bn "<<(int)gm.Bn;
else if(strno == 4)
else if(strno == 4) {
cout<<", taun "<<gm.taun <<" NT "<<gm.NT <<" FT " << (int) gm.FT <<" En " << (int)gm.En;
if(gm.x && gm.y && gm.z) {
auto longlat = getLongLat(gm.getX(), gm.getY(), gm.getZ());
cout<<" long "<< 180* longlat.first/M_PI <<" lat " << 180*longlat.second/M_PI<<" rad "<<gm.getRadius();
cout << " Tb "<<(int)gm.Tb <<" H"<<((gm.Tb/4.0) -3) << " UTC ("<<gm.getX()/1000<<", "<<gm.getY()/1000<<", "<<gm.getZ()/1000<<") -> ";
cout << "("<<gm.getdX()/1000<<", "<<gm.getdY()/1000<<", "<<gm.getdZ()/1000<<")";
time_t now = nmm.localutcseconds();
struct tm tm;
memset(&tm, 0, sizeof(tm));
gmtime_r(&now, &tm);
tm.tm_hour = (gm.Tb/4.0) - 3;
tm.tm_min = (gm.Tb % 4)*15;
tm.tm_sec = 0;
auto match = tles.getBestMatch(timegm(&tm), gm.getX(), gm.getY(), gm.getZ());
cout<<" best-tle-match "<<match.name <<" distance "<<match.distance /1000<<" km";
cout<<" norad " <<match.norad <<" int-desig " << match.internat;
}
}
else if(strno == 5)
cout<<", n4 "<< (int)gm.n4 << " l_n " << gm.l_n;
else if(strno == 7 || strno == 9 || strno == 11 || strno ==13 ||strno ==15) {
cout << " l_n "<< gm.l_n;
else if(strno == 6 || strno ==8 || strno == 10 || strno ==12 ||strno ==14) {
cout<<" nA "<< gm.nA <<" CnA " << gm.CnA <<" LambdaNaDeg "<< gm.getLambdaNaDeg() << " e " <<gm.getE() << " i0 "<< 180.0*gm.getI0()/M_PI;
}
else if(strno == 7 || strno == 9 || strno == 11 || strno ==13 ||strno ==15) {
cout << " l_n "<< gm.l_n << " Tlambdana " <<gm.gettLambdaNa() <<" Hna " << (int)gm.hna;
}
else if(strno == 6 || strno ==8 || strno == 10 || strno ==12 ||strno ==14)
cout<<" nA "<< gm.nA <<" CnA "<<gm.CnA;
cout<<endl;
}
else if(nmm.type() == NavMonMessage::ObserverPositionType) {
cout<<"ECEF"<<endl;
auto lonlat = getLongLat(nmm.op().x(), nmm.op().y(), nmm.op().z());
cout<<"ECEF "<<nmm.op().x()<<", "<<nmm.op().y()<<", "<<nmm.op().z()<< " lon "<< 180*lonlat.first/M_PI << " lat "<<
180*lonlat.second/M_PI<<endl;
}
else if(nmm.type() == NavMonMessage::RFDataType) {
cout<<"RFdata for "<<nmm.rfd().gnssid()<<","<<nmm.rfd().gnsssv()<<endl;
@ -254,4 +336,5 @@ int main(int argc, char** argv)
}
}
}
catch(EofException& ee)
{}

View File

@ -1,4 +1,5 @@
#include "navmon.hh"
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <string.h>

View File

@ -62,7 +62,7 @@ try
cerr<<"New downstream client "<<client.toStringWithPort() << endl;
pair<uint64_t, uint64_t> start = {0,0};
start.first = time(0) - 4*3600; // 4 hours of backlog
start.first = time(0) - 24*3600; // 4 hours of backlog
// so we have a ton of files, and internally these are not ordered
map<string,uint32_t> fpos;

View File

@ -22,19 +22,25 @@
#include "glonass.hh"
#include "beidou.hh"
#include "galileo.hh"
#include "tle.hh"
#include <optional>
#include "navmon.hh"
#include <Tle.h>
using namespace std;
struct EofException{};
Point g_ourpos(3922.505 * 1000, 290.116 * 1000, 5004.189 * 1000);
map<int, BeidouAlmanacEntry> g_beidoualma;
map<int, pair<GlonassMessage, GlonassMessage>> g_glonassalma;
TLERepo g_tles;
struct GNSSReceiver
{
Point position;
};
int g_dtLS{18};
int g_dtLS{18}, g_dtLSBeidou{4};
uint64_t utcFromGST(int wn, int tow)
{
return (935280000 + wn * 7*86400 + tow - g_dtLS);
@ -50,7 +56,6 @@ double utcFromGPS(int wn, double tow)
return (315964800 + wn * 7*86400 + tow - g_dtLS);
}
string humanFt(uint8_t ft)
{
static const char* ret[]={"100 cm", "200 cm", "250 cm", "400 cm", "500 cm", "7 m", "10 m", "12 m", "14 m", "16 m", "32 m", "64 m", "128 m", "256 m", "512 m", "NONE"};
@ -223,12 +228,14 @@ struct SVStat
int t0eMSB{-1}, t0eLSB{-1}, aode{-1}, aodc{-1};
BeidouMessage beidouMessage, oldBeidouMessage;
BeidouMessage lastBeidouMessage1, lastBeidouMessage2;
TLERepo::Match tleMatch;
// new galileo
GalileoMessage galmsg;
// Glonass
GlonassMessage glonassMessage;
pair<uint32_t, GlonassMessage> glonassAlmaEven;
map<uint64_t, SVPerRecv> perrecv;
pair<uint32_t, double> deltaHz;
@ -394,8 +401,10 @@ uint64_t nanoTime(int gnssid, int wn, int tow)
offset = 315964800;
if(gnssid == 2) // Galileo, 22-08-1999
offset = 935280000;
if(gnssid == 3) // Beidou, 01-01-2006
if(gnssid == 3) {// Beidou, 01-01-2006 - I think leap seconds count differently in Beidou!! XXX
offset = 1136073600;
return 1000000000ULL*(offset + wn * 7*86400 + tow - g_dtLSBeidou);
}
if(gnssid == 6) { // GLONASS
offset = 820368000;
return 1000000000ULL*(offset + wn * 7*86400 + tow); // no leap seconds in glonass
@ -544,9 +553,18 @@ double getElevation(const Point& p)
}
int main(int argc, char** argv)
try
{
// g_tles.parseFile("active.txt");
g_tles.parseFile("galileo.txt");
g_tles.parseFile("glo-ops.txt");
g_tles.parseFile("gps-ops.txt");
g_tles.parseFile("beidou.txt");
signal(SIGPIPE, SIG_IGN);
InfluxPusher idb(argc > 3 ? argv[3] : "galileo");
MiniCurl::init();
@ -622,6 +640,69 @@ try
return ret;
});
h2s.addHandler("/almanac", [](auto handler, auto req) {
nlohmann::json ret = nlohmann::json::object();
for(const auto& ae : g_beidoualma) {
nlohmann::json item = nlohmann::json::object();
if(ae.second.alma.getT0e() > 7*86400)
continue;
Point sat;
getCoordinates(0, latestTow(3), ae.second.alma, &sat);
item["eph-ecefX"]= sat.x/1000;
item["eph-ecefY"]= sat.y/1000;
item["eph-ecefZ"]= sat.z/1000;
auto longlat = getLongLat(sat.x, sat.y, sat.z);
item["eph-longitude"] = 180*longlat.first/M_PI;
item["eph-latitude"]= 180*longlat.second/M_PI;
item["t0e"] = ae.second.alma.getT0e();
item["t"]= ephAge(ae.second.alma.getT0e(), latestTow(3))/86400.0;
if(ephAge(ae.second.alma.getT0e(), latestTow(3)) < 0) {
auto match = g_tles.getBestMatch(nanoTime(3, latestWN(3), latestTow(3))/1000000000.0,
sat.x, sat.y, sat.z);
if(match.distance < 200000) {
item["best-tle"] = match.name;
item["best-tle-norad"] = match.norad;
item["best-tle-int-desig"] = match.internat;
item["best-tle-dist"] = match.distance/1000.0;
item["tle-ecefX"] = match.ecefX/1000;
item["tle-ecefY"] = match.ecefY/1000;
item["tle-ecefZ"] = match.ecefZ/1000;
item["tle-eciX"] = match.eciX/1000;
item["tle-eciY"] = match.eciY/1000;
item["tle-eciZ"] = match.eciZ/1000;
item["tle-latitude"] = 180*match.latitude/M_PI;
item["tle-longitude"] = 180*match.longitude/M_PI;
item["tle-altitude"] = match.altitude;
}
}
ret[fmt::sprintf("C%02d", ae.first)] = item;
}
for(const auto& ae : g_glonassalma) {
nlohmann::json item = nlohmann::json::object();
// ae.second.first -> even ae.second.sceond -> odd
item["e"] = ae.second.first.getE();
item["inclination"] = 180 * ae.second.first.getI0() /M_PI;
item["health"] = ae.second.first.CnA;
item["tlambdana"] = ae.second.second.gettLambdaNa();
item["lambdana"] = ae.second.second.getLambdaNaDeg();
item["hna"] = ae.second.second.hna;
ret[fmt::sprintf("R%02d", ae.first)] = item;
}
return ret;
});
@ -650,13 +731,26 @@ try
if(hzCorrection)
item["delta_hz_corr"] = s.second.deltaHz.second - (1561.098/1575.42)* (*hzCorrection);
}
if(s.second.tleMatch.distance >=0) {
item["best-tle"] = s.second.tleMatch.name;
item["best-tle-dist"] = s.second.tleMatch.distance /1000.0;
item["best-tle-norad"] = s.second.tleMatch.norad;
item["best-tle-int-desig"] = s.second.tleMatch.internat;
}
}
else if(s.first.first == 6) { // glonass
if(s.second.glonassMessage.FT < 16)
item["sisa"] = humanFt(s.second.glonassMessage.FT);
item["aode"] = s.second.aode;
item["iod"] = s.second.glonassMessage.Tb;
if(s.second.tleMatch.distance >=0) {
item["best-tle"] = s.second.tleMatch.name;
item["best-tle-dist"] = s.second.tleMatch.distance /1000.0;
item["best-tle-norad"] = s.second.tleMatch.norad;
item["best-tle-int-desig"] = s.second.tleMatch.internat;
}
}
if(s.second.completeIOD()) {
item["iod"]=s.second.getIOD();
@ -678,6 +772,15 @@ try
// this should actually use local time!
getCoordinates(0, s.second.tow, s.second.liveIOD(), &p);
auto match = g_tles.getBestMatch(
s.first.first ?
utcFromGST((int)s.second.wn, (int)s.second.tow) : utcFromGPS(s.second.wn, s.second.tow),
p.x, p.y, p.z);
item["best-tle"] = match.name;
item["best-tle-norad"] = match.norad;
item["best-tle-int-desig"] = match.internat;
item["best-tle-dist"] = match.distance/1000.0;
Vector core2us(core, our);
Vector dx(our, p); // = x-ourx, dy = y-oury, dz = z-ourz;
@ -785,17 +888,17 @@ try
try {
for(;;) {
char bert[4];
if(read(0, bert, 4) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') {
if(readn2(0, bert, 4) != 4 || bert[0]!='b' || bert[1]!='e' || bert[2] !='r' || bert[3]!='t') {
cerr<<"EOF or bad magic"<<endl;
break;
}
uint16_t len;
if(read(0, &len, 2) != 2)
if(readn2(0, &len, 2) != 2)
break;
len = htons(len);
char buffer[len];
if(read(0, buffer, len) != len)
if(readn2(0, buffer, len) != len)
break;
NavMonMessage nmm;
@ -823,6 +926,9 @@ try
auto oldgm = svstat.galmsg;
unsigned int wtype = svstat.galmsg.parse(inav);
if(wtype == 1 || wtype == 2 || wtype == 3) {
idb.addValue(id, "iod-live", svstat.galmsg.iodnav);
}
if(1) {
// cout<<sv <<"\t" << wtype << "\t" << nmm.gi().gnsstow() << "\t"<< nmm.sourceid() << endl;
/* if(g_svstats[id].tow > nmm.gi().gnsstow()) {
@ -992,11 +1098,11 @@ try
pair<int,int> id{nmm.rfd().gnssid(), nmm.rfd().gnsssv()};
if(id.first == 3 && g_svstats[id].oldBeidouMessage.sow >= 0 && g_svstats[id].oldBeidouMessage.sqrtA != 0) {
auto res = doDoppler(nmm.rfd().rcvwn(), nmm.rfd().rcvtow(), g_ourpos, g_svstats[id].oldBeidouMessage, 1561.098 * 1000000);
Point p;
getCoordinates(0, nmm.rfd().rcvtow(), g_svstats[id].oldBeidouMessage, &p);
if(isnan(res.preddop)) {
cerr<<"Problem with doppler calculation for C"<<id.second<<": "<<endl;
Point p;
getCoordinates(0, nmm.rfd().rcvtow(), g_svstats[id].oldBeidouMessage, &p, false);
exit(1);
}
@ -1113,7 +1219,6 @@ try
auto newOffset = bm.getAtomicOffset(bm.sow);
svstat.timeDisco = oldOffset.first - newOffset.first;
idb.addValue(id, "clock_jump_ns", svstat.timeDisco);
}
svstat.lastBeidouMessage1 = bm;
}
@ -1124,24 +1229,43 @@ try
if(fraid == 3) {
svstat.t0eLSB = bm.t0eLSB;
Point oldpoint, newpoint;
if(bm.sow - svstat.lastBeidouMessage2.sow == 6 && svstat.oldBeidouMessage.sow >= 0 && svstat.oldBeidouMessage.getT0e() != svstat.beidouMessage.getT0e()) {
getCoordinates(svstat.wn, svstat.tow, svstat.oldBeidouMessage, &oldpoint);
if(bm.sow - svstat.lastBeidouMessage2.sow == 6 && svstat.oldBeidouMessage.sow >= 0) {
getCoordinates(svstat.wn, svstat.tow, bm, &newpoint);
Vector jump(oldpoint ,newpoint);
cout<<fmt::sprintf("Discontinuity C%02d (%f,%f,%f) -> (%f, %f, %f), jump: %f, seconds: %f\n",
id.second, oldpoint.x, oldpoint.y, oldpoint.z,
newpoint.x, newpoint.y, newpoint.z, jump.length(), (double)bm.getT0e() - svstat.oldBeidouMessage.getT0e());
double hours = (bm.getT0e() - svstat.oldBeidouMessage.getT0e())/3600;
if(hours < 4) {
svstat.latestDisco = jump.length();
idb.addValue(id, "eph-disco", jump.length());
auto match = g_tles.getBestMatch(nanoTime(3, svstat.wn, svstat.tow)/1000000000.0, newpoint.x, newpoint.y, newpoint.z);
svstat.tleMatch = match;
if(svstat.oldBeidouMessage.getT0e() != svstat.beidouMessage.getT0e()) {
getCoordinates(svstat.wn, svstat.tow, svstat.oldBeidouMessage, &oldpoint);
Vector jump(oldpoint ,newpoint);
cout<<fmt::sprintf("Discontinuity C%02d (%f,%f,%f) -> (%f, %f, %f), jump: %f, seconds: %f\n",
id.second, oldpoint.x, oldpoint.y, oldpoint.z,
newpoint.x, newpoint.y, newpoint.z, jump.length(), (double)bm.getT0e() - svstat.oldBeidouMessage.getT0e());
double hours = (bm.getT0e() - svstat.oldBeidouMessage.getT0e())/3600;
if(hours < 4) {
svstat.latestDisco = jump.length();
idb.addValue(id, "eph-disco", jump.length());
}
else
svstat.latestDisco = -1;
}
else
svstat.latestDisco = -1;
}
if(bm.sqrtA) // only copy in if complete
svstat.oldBeidouMessage = bm;
}
else if((fraid == 4 && 1<= pageno && pageno <= 24) ||
(fraid == 5 && 1<= pageno && pageno <= 6) ||
(fraid == 5 && 11<= pageno && pageno <= 23) ) {
struct BeidouAlmanacEntry bae;
// bm.alma.AmEpID = svstat.oldBeidouMessage.alma.AmEpID; // this comes from older messages
if(processBeidouAlmanac(bm, bae)) {
g_beidoualma[bae.sv]=bae;
}
}
if(fraid==5 && pageno == 9) {
svstat.a0g = bm.a0gps;
svstat.a1g = bm.a1gps;
@ -1149,25 +1273,29 @@ try
if(fraid==5 && pageno == 10) {
svstat.a0 = bm.a0utc;
svstat.a1 = bm.a1utc;
svstat.a1 = bm.a1utc;
g_dtLSBeidou = bm.deltaTLS;
// cout<<"Beidou leap seconds: "<<g_dtLSBeidou<<endl;
}
Point core, sat;
// Point core, sat;
getCoordinates(svstat.wn, svstat.tow, bm, &sat);
Vector l(core, sat);
// getCoordinates(svstat.wn, svstat.tow, bm, &sat);
// Vector l(core, sat);
// cout<<"C"<<id.second<< " "<<bm.sow<<" "<<(bm.sow % 30 )<<" FraID "<<fraid<<" "<<fmt::format("({0}, {1}, {2})", sat.x, sat.y, sat.z) <<", r: "<<l.length()<<" elev: "<<getElevation(sat)<<endl;
}
else if(nmm.type()== NavMonMessage::BeidouInavTypeD2) {
auto cond = getCondensedBeidouMessage(std::basic_string<uint8_t>((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size()));
/*
int fraid = getbitu(&cond[0], beidouBitconv(16), 3);
int sow = getbitu(&cond[0], beidouBitconv(19), 20);
int pnum = getbitu(&cond[0], beidouBitconv(43), 4);
int pre = getbitu(&cond[0], beidouBitconv(1), 11);
(void) pre;
// cout<<"C"<< nmm.bid2().gnsssv() << " sent D2 message, pre "<<pre<<" sow "<<sow<<", FraID "<<fraid;
// if(fraid == 1)
// cout <<" pnum "<<pnum;
// cout<<endl;
*/
}
else if(nmm.type()== NavMonMessage::GlonassInavType) {
pair<int,int> id{nmm.gloi().gnssid(), nmm.gloi().gnsssv()};
@ -1181,12 +1309,11 @@ try
uint32_t glotime = gm.getGloTime(); // this starts GLONASS time at 31st of december 1995, 00:00 UTC
svstat.wn = glotime / (7*86400);
svstat.tow = glotime % (7*86400);
}
if(strno == 2) {
else if(strno == 2) {
svstat.gpshealth = gm.Bn;
}
if(strno == 4) {
else if(strno == 4) {
svstat.aode = gm.En * 24;
idb.addValue(id, "glo_taun_ns", gm.getTaunNS());
idb.addValue(id, "ft", gm.FT);
@ -1196,8 +1323,28 @@ try
idb.addValue(id, "clock_jump_ns", svstat.timeDisco);
}
}
if(gm.x && gm.y && gm.z) {
time_t now = nmm.localutcseconds() + 3*3600;
struct tm tm;
memset(&tm, 0, sizeof(tm));
gmtime_r(&now, &tm);
tm.tm_hour = (gm.Tb/4.0);
tm.tm_min = (gm.Tb % 4)*15;
tm.tm_sec = 0;
auto match = g_tles.getBestMatch(timegm(&tm)-3*3600, gm.getX(), gm.getY(), gm.getZ());
svstat.tleMatch = match;
}
}
cout<<"GLONASS R"<<id.second<<" str "<<strno<<endl;
else if(strno == 6 || strno == 8 || strno == 10 || strno == 12 || strno == 14) {
svstat.glonassAlmaEven = {nmm.localutcseconds(), gm};
}
else if(strno == 7 || strno == 9 || strno == 11 || strno == 13 || strno == 15) {
if(nmm.localutcseconds() - svstat.glonassAlmaEven.first < 4 && svstat.glonassAlmaEven.second.strtype == gm.strtype -1) {
g_glonassalma[svstat.glonassAlmaEven.second.nA] = make_pair(svstat.glonassAlmaEven.second, gm);
}
}
// cout<<"GLONASS R"<<id.second<<" str "<<strno<<endl;
}
else {
cout<<"Unknown type "<< (int)nmm.type()<<endl;

View File

@ -11,7 +11,8 @@ TEST_CASE("testing ephemeris age") {
CHECK(ephAge(0,3*86400) == -3*86400);
CHECK(ephAge(0, 3.49*86400) == -3.49*86400);
CHECK(ephAge(0, 3.51*86400) != -3.51*86400);
CHECK(ephAge(0, 3.51*86400) != -3.51*86400);
CHECK(ephAge(0, 3.6*86400) != -3.6*86400);
CHECK(ephAge(2*86400, 0) == 2*86400);

109
tle.cc 100644
View File

@ -0,0 +1,109 @@
#include "tle.hh"
#include "SGP4.h"
#include <iostream>
#include <fstream>
using namespace std;
static void trim(std::string& str)
{
auto pos = str.find_first_of("\r\n");
if(pos != string::npos)
str.resize(pos);
pos = str.find_last_not_of(" \t");
if(pos != string::npos)
str.resize(pos+1);
}
void TLERepo::parseFile(std::string_view fname)
{
ifstream ifs(&fname[0]);
string name, line1, line2;
for(;;) {
if(!getline(ifs, name) || !getline(ifs, line1) || !getline(ifs, line2))
break;
trim(name);
trim(line1);
trim(line2);
Tle tle(line1, line2);
cout<<"name: "<<name<<endl;
cout<<"line1: "<<line1<<endl;
cout<<"line2: "<<line2<<endl;
cout << tle.ToString() << endl;
auto sgp4 = std::make_unique<std::tuple<SGP4, Tle>>(SGP4(tle), tle);
d_sgp4s[name] = std::move(sgp4);
}
}
TLERepo::TLERepo()
{}
TLERepo::~TLERepo()
{}
static TLERepo::Match makeMatch(const DateTime& d, const SGP4& sgp4, const Tle& tle, string name, double distance)
{
TLERepo::Match m;
m.name=name;
m.norad = tle.NoradNumber();
m.internat = tle.IntDesignator();
m.inclination = tle.Inclination(false);
m.ran = tle.RightAscendingNode(false);
m.e = tle.Eccentricity();
m.distance = distance;
auto eci = sgp4.FindPosition(d);
double theta = -eci.GetDateTime().ToGreenwichSiderealTime();
Vector rot = eci.Position();
m.eciX = 1000.0*rot.x;
m.eciY = 1000.0*rot.y;
m.eciZ = 1000.0*rot.z;
rot.x = eci.Position().x * cos(theta) - eci.Position().y * sin(theta);
rot.y = eci.Position().x * sin(theta) + eci.Position().y * cos(theta);
m.ecefX = 1000.0 * rot.x;
m.ecefY = 1000.0 * rot.y;
m.ecefZ = 1000.0 * rot.z;
auto geod = eci.ToGeodetic();
m.latitude = geod.latitude;
m.longitude = geod.longitude;
m.altitude = geod.altitude;
return m;
}
TLERepo::Match TLERepo::getBestMatch(time_t now, double x, double y, double z, TLERepo::Match* secondbest)
{
struct tm tm;
gmtime_r(&now, &tm);
DateTime d(1900 + tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
Vector sat(x/1000.0, y/1000.0, z/1000.0);
multimap<double, string> distances;
for(const auto& sgp4 : d_sgp4s) {
auto eci = get<0>(*sgp4.second).FindPosition(d);
double theta = -eci.GetDateTime().ToGreenwichSiderealTime();
Vector rot = eci.Position();
rot.x = eci.Position().x * cos(theta) - eci.Position().y * sin(theta);
rot.y = eci.Position().x * sin(theta) + eci.Position().y * cos(theta);
distances.insert({1000.0*(rot - sat).Magnitude(),sgp4.first});
}
if(secondbest) {
auto iter = distances.begin();
if(iter != distances.end()) {
++iter;
*secondbest = makeMatch(d, get<0>(*d_sgp4s[iter->second]), get<1>(*d_sgp4s[iter->second]), iter->second, iter->first);
}
}
return makeMatch(d, get<0>(*d_sgp4s[distances.begin()->second]), get<1>(*d_sgp4s[distances.begin()->second]), distances.begin()->second, distances.begin()->first);
}

39
tle.hh 100644
View File

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <map>
#include <memory>
class SGP4;
class Tle;
class TLERepo
{
public:
TLERepo();
~TLERepo();
void parseFile(std::string_view fname);
struct Match
{
std::string name;
int norad;
std::string internat;
double inclination; // radians
double ran; // radians
double e;
double ecefX; // m
double ecefY; // m
double ecefZ; // m
double eciX, eciY, eciZ; // m
double distance{-1}; // m
double latitude, longitude, altitude;
};
Match getBestMatch(time_t, double x, double y, double z, Match* secondbest=0);
private:
std::map<std::string,
std::unique_ptr<std::tuple<SGP4, Tle>>
> d_sgp4s;
};

43
tlecatch.cc 100644
View File

@ -0,0 +1,43 @@
#include "SGP4.h"
#include <string>
#include <time.h>
using namespace std;
int main(int argc, char **argv)
{
string line;
// DateTime d(2019, 8, 31, 1, 52, 30);
time_t now = time(0);
struct tm tm;
gmtime_r(&now, &tm);
for(;;) {
DateTime d(1900 + tm.tm_year, tm.tm_mon+1, tm.tm_mday, 04, 43, 51);
string name, line1, line2;
if(!getline(cin, name) || !getline(cin, line1) || !getline(cin, line2))
break;
name.resize(name.size()-1);
line1.resize(line1.size()-1);
line2.resize(line2.size()-1);
Tle tle(line1, line2);
/*
cout<<"line1: "<<line1<<endl;
cout<<"line2: "<<line2<<endl;
cout << tle.ToString() << endl;
*/
SGP4 sgp4(tle);
for(int n = 0 ; n < 1; ++n) {
auto eci = sgp4.FindPosition(d);
cout << name <<" "<<d <<": "<<eci.Position() << " " <<eci.Velocity() << " " << eci.ToGeodetic()<<endl;
double theta = -eci.GetDateTime().ToGreenwichSiderealTime();
Vector rot = eci.Position();
rot.x = eci.Position().x * cos(theta) - eci.Position().y * sin(theta);
rot.y = eci.Position().x * sin(theta) + eci.Position().y * cos(theta);
cout << " " << rot<< endl;
d = d.AddSeconds(30);
}
}
}

View File

@ -1,3 +1,4 @@
#define _LARGEFILE64_SOURCE
#include <sys/types.h>
#include <sys/time.h>
#include <map>
@ -136,11 +137,15 @@ public:
bool g_fromFile{false};
#if !defined(O_LARGEFILE)
#define O_LARGEFILE 0
#endif
std::pair<UBXMessage, struct timeval> getUBXMessage(int fd)
{
static int logfile;
if(!logfile && !g_fromFile) {
logfile = open("./logfile", O_WRONLY|O_CREAT|O_APPEND, 0600);
logfile = open("./logfile", O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE, 0600);
if(!logfile)
throw std::runtime_error("Failed to open logfile for writing");
}
@ -282,7 +287,7 @@ void readSome(int fd)
struct termios g_oldtio;
int initFD(const char* fname)
int initFD(const char* fname, bool doRTSCTS)
{
int fd;
if(string(fname) != "stdin" && string(fname) != "/dev/stdin" && isCharDevice(fname)) {
@ -298,7 +303,9 @@ int initFD(const char* fname)
}
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | /*CRTSCTS */ CS8 | CLOCAL | CREAD;
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
if (doRTSCTS)
newtio.c_cflag |= CRTSCTS;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
@ -333,13 +340,21 @@ int main(int argc, char** argv)
CLI::App app("ubxtool");
vector<std::string> serial;
bool doGPS{true}, doGalileo{true}, doGlonass{false}, doBeidou{true}, doReset{false};
bool doGPS{true}, doGalileo{true}, doGlonass{false}, doBeidou{true}, doReset{false}, doWait{false}, doRTSCTS{true};
#ifdef OpenBSD
doRTSCTS = false;
#endif
app.add_option("serial", serial, "Serial");
app.add_flag("--wait", doWait, "Wait a bit, do not try to read init messages");
app.add_flag("--reset", doReset, "Reset UBX device");
app.add_flag("--beidou,-c", doBeidou, "Enable BeiDou reception");
app.add_flag("--gps,-g", doGPS, "Enable GPS reception");
app.add_flag("--glonass,-r", doGlonass, "Enable Glonass reception");
app.add_flag("--galileo,-e", doGalileo, "Enable Galileo reception");
app.add_option("--rtscts", doRTSCTS, "Set hardware handshaking");
try {
@ -348,20 +363,22 @@ int main(int argc, char** argv)
return app.exit(e);
}
if(serial.size() != 2) {
cout<<app.help()<<endl;
return EXIT_FAILURE;
}
int fd = initFD(serial[0].c_str());
int fd = initFD(serial[0].c_str(), doRTSCTS);
g_srcid = atoi(serial[1].c_str());
if(!g_fromFile) {
bool doInit = true;
if(doInit) {
readSome(fd);
if(doWait)
sleep(2);
else
readSome(fd);
std::basic_string<uint8_t> msg;
if(doReset) {
@ -373,7 +390,7 @@ int main(int argc, char** argv)
for(int n=0 ; n< 20; ++n) {
cerr<<"Waiting for device to come back"<<endl;
try {
fd = initFD(serial[0].c_str());
fd = initFD(serial[0].c_str(), doRTSCTS);
readSome(fd);
}
catch(...)
@ -758,9 +775,11 @@ int main(int argc, char** argv)
else if(id.first==6) {
// cerr<<"SFRBX from GLONASS "<<id.second<<" @ frequency "<<(int)payload[3]<<", msg of "<<(int)payload[4]<< " words"<<endl;
auto gstr = getGlonassFromSFRBXMsg(payload);
/*
static map<int, GlonassMessage> gms;
GlonassMessage& gm = gms[id.second];
int strno = gm.parse(gstr);
*/
if(id.second != 255) {
NavMonMessage nmm;
nmm.set_localutcseconds(g_gstutc.tv_sec);

5
update-tles 100755
View File

@ -0,0 +1,5 @@
#!/bin/sh
for a in galileo gps-ops beidou glo-ops active
do
wget --backups=1 https://www.celestrak.com/NORAD/elements/$a.txt
done