Merge branch 'master' of https://github.com/ahupowerdns/galmon into openbsd-fixes
Make hardware handshaking settable, default to off for OpenBSDpull/1/head
commit
428111de84
|
@ -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
|
|
@ -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
|
||||
|
|
36
Makefile
36
Makefile
|
@ -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
|
||||
|
|
52
beidou.cc
52
beidou.cc
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
93
beidou.hh
93
beidou.hh
|
@ -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);
|
||||
|
|
49
ephemeris.cc
49
ephemeris.cc
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 6b47861cd47a6e31841260c47a52b579f8cf2fa9
|
60
galileo.hh
60
galileo.hh
|
@ -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);
|
||||
|
|
58
glonass.hh
58
glonass.hh
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
161
navdump.cc
161
navdump.cc
|
@ -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)
|
||||
{}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "navmon.hh"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
|
|
@ -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;
|
||||
|
|
211
navparse.cc
211
navparse.cc
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
35
ubxtool.cc
35
ubxtool.cc
|
@ -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);
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue