diff --git a/html/index.html b/html/index.html index 51f6201..cbdd0df 100644 --- a/html/index.html +++ b/html/index.html @@ -49,7 +49,7 @@ input[type="checkbox"] { - Last update: . More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found here. Live map here!. Contact me if you want access to the Grafana dashboard.
+ Last update: . More information about this Galileo/GPS/BeiDou/Glonass open source monitor can be found here and here. Live map here!. Contact me if you want access to the Grafana dashboard.

   @@ -70,28 +70,38 @@ input[type="checkbox"] { Stale:

- This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States, Tonga, Brazil, Singapore, Austria ans Uruguay. + This table shows live output from four Galileo/GPS/BeiDou/GLONASS receivers hosted in Nootdorp, The Netherlands and California, United States, Tonga, Brazil, Singapore, Austria, India and Uruguay. It is very much a work in progress, and will not be available at all times. Extremely rough code is on GitHub. - Some technical detail behind this setup can be found in this post. - +

+ Some technical detail behind this setup can be found in this post. +

+

For updates, follow @GalileoSats on Twitter, or join us on our IRC channel (chat) via the web gateway. - - The meaning of the fields is as follows: +

+

+ The meaning of the fields is explained in this document and can be summarised as follows: +

+ - - - - - - + + + + + + + + + + +
svSatellite Vehicle, an identifier for a Galileo satellite. Not the actual name of the satellite, other satellites could take over this number in case of failures
iodIssue of Data. Satellites periodically get sent updates on their orbit & other details, each update has a new IOD number. It is coincidence that all SVs currently receive the same IOD numbers, this is by no means guaranteed. Currently however, if an SV has a lower IOD, it has not received new updates recently.
eph‑age‑mAge of ephemeris in minutes. Denotes how old the current set of orbit data is. Could be very old if SV is out of sight (see below). An acceptable limit is 4 hours (240 minutes).
latest-disco"jump" of the orbit prediction at the latest ephemeris change. Centimeters are good.
time-disco"jump" of the atomic clock at the latest ephemeris change.
sisaSignal In Space Accuracy, how well the position of an SV is known.
e1bhs, e1bdvs, e5bhs, e5bdvsHealth flags for E1 (common) and E5 (uncommon) frequencies.
a0, a1Offset of the Galileo Standard Time to UTC. a0 is (more or less) the offset in nanoseconds, a1 is a measure of the rate of change
elevElevation of an SV over my horizon (90 is straight up), reported by receiver
calc-elevElevation of an SV over or under my horizon (90 is straight up), calculated by this website
dbA measure of signal to noise ratio (in unknown units, 40 is good)
last‑seen‑sNumber of seconds since we've last received from this SV. A satellite can be out of sight for a long time
healthIf a satellite considers itself healthy.
delta-UTCOffset of the GNSS Time to UTC, plus trend
delta-GPSOffset of the GNSS Time to GPS, plus trend
alma-distDistance between precise satellite position and almanac summary position
tle-distDistance between precise satellite position and TLE position
best-tleFrom TLE database, closest satellite to reported position
prresPseudorange residual: measure of how far away the satellite appears to be from where it should be, according to a receiver. Meters.
delta-HzDifference between calculated (expected) Doppler shift and measured Doppler shift. Measure of orbit correctness. Hz.
elevElevation of an SV over or under my horizon (90 is straight up), calculated by this website
dbA measure of signal to noise ratio (40+ is good)
last‑seen‑sTime since we've last received from this SV.

diff --git a/navdump.cc b/navdump.cc index a0d9f9e..765a6d3 100644 --- a/navdump.cc +++ b/navdump.cc @@ -4,6 +4,7 @@ #include #include "fmt/format.h" #include "fmt/printf.h" +#include "CLI/CLI.hpp" #include #include #include @@ -22,12 +23,33 @@ #include "galileo.hh" #include "navmon.hh" #include "tle.hh" +#include "sp3.hh" #include using namespace std; Point g_ourpos; + +vector g_sp3s; + +bool sp3Order(const SP3Entry& a, const SP3Entry& b) +{ + return tie(a.gnss, a.sv, a.t) < tie(b.gnss, b.sv, b.t); +} + +void readSP3s(string_view fname) +{ + SP3Reader sp3(fname); + SP3Entry e; + while(sp3.get(e)) { + g_sp3s.push_back(e); + } + + sort(g_sp3s.begin(), g_sp3s.end(), sp3Order); + +} + string beidouHealth(int in) { string ret; @@ -82,9 +104,55 @@ void doOrbitDump(int gnss, int sv, int wn, const T& oldEph, const T& newEph, int } +struct SVFilter +{ + struct SatID + { + int gnss, sv, sigid; + bool operator<(const SatID& rhs) const + { + return std::tie(gnss, sv, sigid) < std::tie(rhs.gnss, rhs.sv, rhs.sigid); + } + }; + + void addFilter(string_view str) + { + SatID satid{0,0,-1}; + satid.gnss = atoi(&str[0]); + auto pos= str.find(','); + if( pos != string::npos) + satid.sv = atoi(&str[pos+1]); + + pos = str.find(',', pos+1); + if(pos != string::npos) + satid.sigid = atoi(&str[pos+1]); + d_filter.insert(satid); + } + bool check(int gnss, int sv, int sigid=-1) + { + if(d_filter.empty()) + return true; + if(d_filter.count({gnss,0,-1})) // gnss match + return true; + if(d_filter.count({gnss,sv,-1})) // gnss, sv match + return true; + if(d_filter.count({gnss,sv,sigid})) // gnss, sv match, sigid + return true; + + return false; + } + set d_filter; + +}; + int main(int argc, char** argv) try { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + CLI::App app("navdump"); + + TLERepo tles; // tles.parseFile("active.txt"); tles.parseFile("galileo.txt"); @@ -92,10 +160,26 @@ try tles.parseFile("gps-ops.txt"); tles.parseFile("beidou.txt"); - bool skipGPS{false}; - bool skipBeidou{false}; - bool skipGalileo{false}; - bool skipGlonass{false}; + readSP3s("all.sp3"); + if(!g_sp3s.empty()) { + // sort(g_sp3s.begin(), g_sp3s.end(), [](const auto& a, const auto&b) { return a.t < b.t; }); + cout<<"Have "<t) <<" to "<< humanTime(g_sp3s.rbegin()->t)< svpairs; + bool doReceptionData{false}; + bool doRFData{false}; + bool doObserverPosition{false}; + app.add_option("--svs", svpairs, "Listen to specified svs. '0' = gps, '2' = Galileo, '2,1' is E01"); + try { + app.parse(argc, argv); + } catch(const CLI::Error &e) { + return app.exit(e); + } + SVFilter svfilter; + for(const auto& svp : svpairs) { + svfilter.addFilter(svp); + } ofstream almanac("almanac.txt"); ofstream iodstream("iodstream.csv"); @@ -103,6 +187,11 @@ try ofstream csv("delta.csv"); csv <<"timestamp gnssid sv tow tle_distance alma_distance utc_dist x y z vx vy vz rad inclination e iod"< inav((uint8_t*)nmm.gi().contents().c_str(), nmm.gi().contents().size()); static map gms; static map, GalileoMessage> gmwtypes; static map oldgm4s; int sv = nmm.gi().gnsssv(); + if(!svfilter.check(2, sv, nmm.gi().sigid())) + continue; + etstamp(); + + int64_t imptow = (((uint32_t)round(nmm.localutcseconds() + nmm.localutcnanoseconds()/1000000000.0) - 935280000 + 18) % (7*86400)) - 2; + if(nmm.gi().sigid() != 5) { + if(!(imptow % 2)) + imptow--; + } + else if((imptow % 2)) + imptow--; + + if(imptow != nmm.gi().gnsstow()) + cout<< " !!"<< (imptow - nmm.gi().gnsstow()) <<"!! "; + + GalileoMessage& gm = gms[sv]; int wtype = gm.parse(inav); gm.tow = nmm.gi().gnsstow(); gmwtypes[{nmm.gi().gnsssv(), wtype}] = gm; static map oldEph; - cout << "gal inav for "<gnss == e.gnss && bestSP3->sv == sv) { + static set> haveSeen; + if(!haveSeen.count({sv, bestSP3->t})) { + haveSeen.insert({sv, bestSP3->t}); + Point newPoint; + getCoordinates(gm.tow + (bestSP3->t - start), gm, &newPoint); + Point sp3Point(bestSP3->x, bestSP3->y, bestSP3->z); + Vector dist(newPoint, sp3Point); + cout<<"\nsp3 "<<(bestSP3->t - start)<<" E"<t)<<" (" << newPoint.x/1000.0 <<", "<x/1000.0) <<", " << (bestSP3->y/1000.0) <<", " << (bestSP3->z/1000.0) << ") "<clockBias << " " << gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first<< " " << dist.length()<< " "; + cout << (bestSP3->clockBias - gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first); + cout << " " << gm.af0 <<" " << gm.af1; + cout << endl; + + sp3csv <t << " 2 "<< sv <<" " << ephAge(gm.tow+(bestSP3->t - start), gm.getT0e()) <<" "<x<<" " << bestSP3->y<<" " <z <<" " << newPoint.x<<" " <clockBias <<" "; + sp3csv << gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first<<" " << dist.length() <<" "; + sp3csv << (bestSP3->clockBias - gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first) << endl; + } + + } + + if(!oldEph[sv].sqrtA) oldEph[sv] = gm; else if(oldEph[sv].iodnav != gm.iodnav) { @@ -181,6 +322,8 @@ try auto newAtomic = gm.getAtomicOffset(gm.tow); cout<<" clock-jump "<((uint8_t*)nmm.gpsi().contents().c_str(), nmm.gpsi().contents().size())); struct GPSState gs; static map eph; @@ -329,12 +486,14 @@ try cout<<"\n"; } else if(nmm.type() == NavMonMessage::BeidouInavTypeD1) { - if(skipBeidou) { - cout<((uint8_t*)nmm.bid1().contents().c_str(), nmm.bid1().contents().size())); uint8_t pageno; static map bms; @@ -403,6 +562,11 @@ try } else if(nmm.type() == NavMonMessage::BeidouInavTypeD2) { int sv = nmm.bid2().gnsssv(); + + if(!svfilter.check(3, sv)) + continue; + etstamp(); + auto cond = getCondensedBeidouMessage(std::basic_string((uint8_t*)nmm.bid2().contents().c_str(), nmm.bid2().contents().size())); BeidouMessage bm; uint8_t pageno; @@ -412,11 +576,9 @@ try } else if(nmm.type() == NavMonMessage::GlonassInavType) { - if(skipGlonass) { - cout< gms; auto& gm = gms[nmm.gloi().gnsssv()]; @@ -456,16 +618,23 @@ try cout< #include #include +#include "fmt/format.h" +#include "fmt/printf.h" + using namespace std; @@ -32,6 +35,18 @@ std::string humanTime(time_t t) gmtime_r(&t, &tm); char buffer[80]; - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", &tm); + strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S %z", &tm); + return buffer; +} + +std::string humanTime(time_t t, uint32_t nanoseconds) +{ + struct tm tm={0}; + gmtime_r(&t, &tm); + + char buffer[80]; + std::string fmt = "%a, %d %b %Y %H:%M:"+fmt::sprintf("%06.04f", tm.tm_sec + nanoseconds/1000000000.0) +" %z"; + + strftime(buffer, sizeof(buffer), fmt.c_str(), &tm); return buffer; } diff --git a/navmon.hh b/navmon.hh index a0c2410..198282a 100644 --- a/navmon.hh +++ b/navmon.hh @@ -3,7 +3,19 @@ #include #include #include +#include struct EofException{}; size_t readn2(int fd, void* buffer, size_t len); std::string humanTime(time_t t); +std::string humanTime(time_t t, uint32_t nanoseconds); +struct SatID +{ + uint32_t gnss{255}; // these could all be 'int16_t' but leads to howling numbers of warnings with protobuf + uint32_t sv{0}; + uint32_t sigid{0}; + bool operator<(const SatID& rhs) const + { + return std::tie(gnss, sv, sigid) < std::tie(rhs.gnss, rhs.sv, rhs.sigid); + } +}; diff --git a/navparse.cc b/navparse.cc index 43e1182..1963e4c 100644 --- a/navparse.cc +++ b/navparse.cc @@ -408,16 +408,6 @@ void SVStat::addGalileoWord(std::basic_string_view page) } } -struct SatID -{ - uint32_t gnss{255}; // these could all be 'int16_t' but leads to howling numbers of warnings with protobuf - uint32_t sv{0}; - uint32_t sigid{0}; - bool operator<(const SatID& rhs) const - { - return tie(gnss, sv, sigid) < tie(rhs.gnss, rhs.sv, rhs.sigid); - } -}; typedef std::map svstats_t; svstats_t g_svstats; @@ -667,6 +657,10 @@ std::optional getHzCorrection(time_t now, int src, unsigned int gnssid, std::string humanBhs(int bhs) { static vector options{"ok", "out of service", "will be out of service", "test"}; + if(bhs >= options.size()) { + cerr<<"Asked for humanBHS "<pool, &req->res.headers, H2O_TOKEN_CACHE_CONTROL, + NULL, H2O_STRLIT("max-age=3")); for(const auto& s: svstats) { nlohmann::json item = nlohmann::json::object(); diff --git a/ubxtool.cc b/ubxtool.cc index de217e2..8f261ee 100644 --- a/ubxtool.cc +++ b/ubxtool.cc @@ -900,6 +900,12 @@ int main(int argc, char** argv) else if(wtype==3) { msgTOW = curCycleTOW + 23; } + else { // dummy + if(id.second != 20) // known broken XXX + cerr<<"galileo E"<