trkmeas work, plus many other things

pull/76/head
bert hubert 2020-01-09 14:33:02 +01:00
parent a58a9f139f
commit 9dd4b4232d
8 changed files with 1027 additions and 49 deletions

View File

@ -1,3 +1,5 @@
CFLAGS = -O3 -Wall -ggdb
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/ \
@ -6,10 +8,17 @@ CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/C
# CXXFLAGS += -Wno-delete-non-virtual-dtor
ifneq (,$(wildcard ubxsec.c))
EXTRADEP = ubxsec.o
else ifneq (,$(wildcard ubxsec.o))
EXTRADEP = ubxsec.o
endif
CHEAT_ARG := $(shell ./update-git-hash-if-necessary)
PROGRAMS = navparse ubxtool navnexus navcat navrecv navdump testrunner navdisplay tlecatch reporter
PROGRAMS = navparse ubxtool navnexus navcat navrecv navdump testrunner navdisplay tlecatch reporter \
galmonmon
all: navmon.pb.cc $(PROGRAMS)
@ -24,15 +33,20 @@ clean:
rm -f *~ *.o *.d ext/*/*.o $(PROGRAMS) navmon.pb.h navmon.pb.cc $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) $(H2OPP) $(SIMPLESOCKETS)
rm -f ext/fmt-5.2.1/src/format.o
decrypt: decrypt.o bits.o ext/fmt-5.2.1/src/format.o
$(CXX) -std=gnu++17 $^ -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 ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o coverage.o osen.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 ephemeris.o beidou.o glonass.o $(patsubst %.cc,%.o,$(wildcard ext/sgp4/libsgp4/*.cc)) tle.o navmon.o coverage.o osen.o trkmeas.o ${EXTRADEP}
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf $(WSLAY)
reporter: reporter.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 coverage.o osen.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -L/usr/local/opt/openssl/lib/ -lh2o-evloop -lssl -lcrypto -lz -lcurl -lprotobuf $(WSLAY)
reporter: reporter.o ext/fmt-5.2.1/src/format.o $(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 coverage.o osen.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
galmonmon: galmonmon.o ext/fmt-5.2.1/src/format.o $(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 coverage.o osen.o githash.o
$(CXX) -std=gnu++17 $^ -o $@ -pthread -L/usr/local/lib -lprotobuf -lcurl
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 sp3.o osen.o
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 sp3.o osen.o trkmeas.o ${EXTRADEP}
$(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 osen.o

290
galmonmon.cc 100644
View File

@ -0,0 +1,290 @@
#include "minicurl.hh"
#include <iostream>
#include "navmon.hh"
#include "fmt/format.h"
#include "fmt/printf.h"
#include "ext/powerblog/h2o-pp.hh"
#include <variant>
#include "githash.h"
using namespace std;
/*
Monitoring the satellites for sensible alerts.
A satellite transitions state if:
* galmon is up
* galmon is up to date
* We are seeing recent messages for the satellite
* For more than 60 seconds this state is different than it was
A satellite becomes 'unobserved' if:
* galmon is up
* galmon is up to date
* We haven't seen a message since an hour
A satellite becomes observed again if we have seen it for > 60 seconds.
At startup, every satellite assumes, without alert, the status we find for it, which is not a transition.
*/
class StateKeeper
{
public:
typedef std::variant<bool, double, string> var_t;
void setBoolNames(string_view name, string_view offName, string_view onName);
std::optional<string> reportState(string_view thing, string_view name, var_t state, time_t now=0);
std::optional<string> getState(string_view thing, string_view name);
std::optional<string> getPrevState(string_view thing, string_view name);
private:
struct Names
{
string offName, onName;
};
map<string, Names> names;
struct State
{
var_t state;
time_t since;
};
struct ThingState
{
std::optional<State> prev, live, provisional;
};
map<string, map<string, ThingState> > states;
private:
std::string getName(string_view name, var_t state)
{
if(auto ptr = std::get_if<bool>(&state)) {
if(*ptr) {
return names[(string)name].onName;
}
else
return names[(string)name].offName;
}
if(auto ptr = std::get_if<string>(&state)) {
return *ptr;
}
if(auto ptr = std::get_if<double>(&state)) {
return to_string(*ptr);
}
return "?";
}
};
void StateKeeper::setBoolNames(string_view name, string_view offName, string_view onName)
{
names[(string)name].offName = offName;
names[(string)name].onName = onName;
}
std::optional<string> StateKeeper::getState(string_view thing, string_view name)
{
if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].live) {
return getName(name, states[(string)thing][(string)name].live->state);
}
return std::optional<string>();
}
std::optional<string> StateKeeper::getPrevState(string_view thing, string_view name)
{
if(states.count((string)thing) && states[(string)thing].count((string) name) && states[(string)thing][(string)name].prev) {
return getName(name, states[(string)thing][(string)name].prev->state);
}
return std::optional<string>();
}
std::optional<string> StateKeeper::reportState(string_view thing, string_view name, var_t newstate, time_t now)
{
auto& state = states[(string)thing][(string)name];
std::optional<string> ret;
if(!now)
now = time(0);
if(!state.live) { // we had no state yet
state.live = State{newstate, now};
state.provisional.reset(); // for good measure
return ret;
}
else if(state.live->state == newstate) { // confirmation of current state
state.provisional.reset();
return ret;
}
else if(!state.provisional) { // new provisional state
state.provisional = State{newstate, now};
return ret;
}
else {
if(now - state.provisional->since > 60) { // provisional state has been confirmed
state.prev = state.live;
state.live = state.provisional;
state.provisional.reset();
return getName(name, state.live->state);
}
}
return ret;
}
StateKeeper g_sk;
static std::string string_replace(const std::string& str, const std::string& match,
const std::string& replacement, unsigned int max_replacements = UINT_MAX)
{
size_t pos = 0;
std::string newstr = str;
unsigned int replacements = 0;
while ((pos = newstr.find(match, pos)) != std::string::npos
&& replacements < max_replacements)
{
newstr.replace(pos, match.length(), replacement);
pos += replacement.length();
replacements++;
}
return newstr;
}
void sendTweet(const string& tweet)
{
string etweet = string_replace(tweet, "+", "%2b");
system((string("twurl -X POST \"/1.1/statuses/update.json?status=")+etweet+"\" >> twitter.log").c_str());
}
int main(int argc, char **argv)
{
MiniCurl mc;
MiniCurl::MiniCurlHeaders mch;
// string url="https://galmon.eu/svs.json";
string url="http://[::1]:10000/";
g_sk.setBoolNames("health", "healthy", "unhealthy");
g_sk.setBoolNames("eph-too-old", "ephemeris fresh", "ephemeris too old");
g_sk.setBoolNames("silent", "observed", "not observed");
std::variant<bool, string> tst;
extern const char* g_gitHash;
auto observers = nlohmann::json::parse(mc.getURL(url+"observers.json"));
cout<<("Galmonmon " +string(g_gitHash)+ " started, " + std::to_string(observers.size()) +" observers seen")<<endl;
for(;;) {
try {
auto res = mc.getURL(url+"global.json");
auto j = nlohmann::json::parse(res);
if(!j.count("last-seen") || time(0) - (long) j["last-seen"] > 30) {
cout<<"Galmon at "<<url<<" is not current"<<endl;
sleep(10);
continue;
}
/*
if(auto iter = j.find("last-seen"); iter != j.end()) {
cout<<"Galmon behind by " << (time(0) - (long) iter.value()) <<" seconds"<<endl;
}
*/
res = mc.getURL(url+"svs.json");
j = nlohmann::json::parse(res);
bool first=true;
for(const auto& sv : j) {
int gnssid = sv["gnssid"];
string fullName = sv["fullName"];
if(!(gnssid == 2 && sv["sigid"]==1) &&
!(gnssid == 0 && sv["sigid"]==0) &&
!(gnssid == 3 && sv["sigid"]==0) &&
!(gnssid == 6 && sv["sigid"]==0))
continue;
int numfresh=0;
// we only track "received status" for GPS and Galileo
bool notseen= gnssid ==0 || gnssid==2;
if(!sv.count("sisa") && !sv.count("eph-age-m"))
continue;
if(sv.count("perrecv")) {
for(const auto& recv : sv["perrecv"]) {
if((int)recv["last-seen-s"] < 60)
numfresh++;
if((int)recv["last-seen-s"] < 3600)
notseen=false;
}
}
auto healthchange = g_sk.reportState(fullName, "health", sv["healthissue"]!=0);
std::optional<string> tooOldChange;
if(gnssid == 2)
tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 120);
else
tooOldChange = g_sk.reportState(fullName, "eph-too-old", sv["eph-age-m"] > 140);
auto seenChange = g_sk.reportState(fullName, "silent", notseen);
auto sisaChange = g_sk.reportState(fullName, "sisa", (string)sv["sisa"]);
/*
cout<<fullName<<": numfresh "<<numfresh << " healthissue "<<sv["healthissue"];
cout<<" eph-age-m "<< fmt::sprintf("%.2f", (double)sv["eph-age-m"]);
cout<<" not-seen "<<notseen;
if(auto val = g_sk.getState(fullName, "eph-too-old"); val) {
cout<<" eph-too-old \""<<*val<<"\"";
}
if(auto val = g_sk.getState(fullName, "health"); val) {
cout<<" health \""<<*val<<"\"";
}
*/
if(healthchange || tooOldChange || seenChange || sisaChange) {
if(first)
cout<<"\n";
first=false;
ostringstream out;
if(gnssid == 0)
out<<"GPS";
else if(gnssid == 2)
out<<"Galileo";
else if(gnssid == 3)
out<<"BeiDou";
else if(gnssid== 6)
out<<"GLONASS";
out<<" "<<fullName<<": ";
if(healthchange)
out<< *healthchange<<" ";
if(tooOldChange) {
out<< *tooOldChange<<", new value: "<< fmt::sprintf("%.02f", (double)sv["eph-age-m"])<<" minutes, old: ";
out<< *g_sk.getPrevState(fullName, "eph-too-old");
}
if(seenChange)
out<< *seenChange<<" ";
if(sisaChange) {
out<< "SISA/URA reported ranging accuracy new: "<<*sisaChange<<", old: " << *g_sk.getPrevState(fullName, "sisa");
}
if(out.str().find("200 cm") == string::npos || out.str().find("282 cm") == string::npos)
sendTweet("TESTING: "+out.str());
cout<<humanTimeNow()<<" CHANGE ";
cout<<out.str()<<endl;
}
}
cout<<".";
cout.flush();
}
catch(std::exception& e) {
cerr<<"\nError: "<<e.what()<<endl;
}
sleep(20);
}
}

View File

@ -24,7 +24,7 @@
#include "navmon.hh"
#include "tle.hh"
#include "sp3.hh"
#include "ubx.hh"
#include <unistd.h>
using namespace std;
@ -145,6 +145,82 @@ struct SVFilter
};
struct FixStat
{
double iTow{-1};
struct SatStat
{
double bestrange1{-1}; // corrected for clock
double bestrange5{-1}; // corrected for clock
double doppler1{-1}; // corrected for clock
double doppler5{-1}; // corrected for clock
double radvel{-1};
double ephrange{-1};
GalileoMessage ephemeris;
};
map<pair<int,int>, SatStat> sats;
};
double g_rcvoffset;
void emitFixState(int src, double iTow, FixStat& fs, int n)
{
cout<<"\nFix state for source "<<src<<", have "<<fs.sats.size()<<" satellites, n="<<n<<endl;
// for(double dt = -0.2; dt < 0.2; dt+=0.0001)
double dt=0;
double offset=0;
{
int count=0;
for(const auto& s : fs.sats) {
if(s.first.second==14 || s.first.second==18)
continue;
Point sat;
double E=getCoordinates(iTow, s.second.ephemeris, &sat);
if(getElevationDeg(sat, g_ourpos) < 20)
continue;
/*
Point sat;
auto [toffset, trend] = s.second.ephemeris.getAtomicOffset(iTow +dt);
(void)trend;
getCoordinates(iTow + toffset/1000000000.0 + dt, s.second.ephemeris, &sat);
double range = Vector(g_ourpos, sat).length();
getCoordinates(iTow + range/299792458.0 + toffset/1000000000.0 + dt, s.second.ephemeris, &sat);
range = Vector(g_ourpos, sat).length();
*/
double range = s.second.ephrange;
if(s.second.bestrange1 != -1) {
offset += s.second.bestrange1 - range;
count++;
}
if(s.second.bestrange5 != -1) {
offset += s.second.bestrange5 - range;
count++;
}
}
if(!count) {
fs.sats.clear();
return;
}
cout<< " dt "<<dt<<" err "<<offset << " " << count << " avg " << offset/count<< " "<<offset/count/3e5<<"ms"<<endl;
offset/=count;
}
for(const auto& s : fs.sats) {
Point sat;
double E=getCoordinates(iTow, s.second.ephemeris, &sat);
cout<<""<<s.first.first<<","<<s.first.second<<": "<<s.second.bestrange1-offset<<" "<<s.second.bestrange5-offset<<" " << s.second.ephrange<<", delta1 " << (s.second.bestrange1-offset-s.second.ephrange)<<", delta5 "<< (s.second.bestrange5-offset-s.second.ephrange)<<" dd "<< s.second.bestrange1 - s.second.bestrange5 <<" t0e " << s.second.ephemeris.getT0e()<< " elev " << getElevationDeg(sat, g_ourpos) << " E " << E<< " clock " << s.second.ephemeris.getAtomicOffset(iTow).first/1000000<<"ms doppler1 "<<s.second.doppler1 << " doppler5 " <<s.second.doppler5<<" radvel " <<s.second.radvel<< " frac " << (s.second.bestrange1-offset-s.second.ephrange)/s.second.radvel;
cout<< " fixed "<<((s.second.bestrange1 - offset-s.second.ephrange) + (s.second.ephrange/299792458.0) * s.second.radvel)<< " BGD-ns "<<ldexp(s.second.ephemeris.BGDE1E5b,-32)*1000000000<< endl;
}
fs.sats.clear();
}
int main(int argc, char** argv)
try
{
@ -167,10 +243,12 @@ try
}
*/
vector<string> svpairs;
vector<int> stations;
bool doReceptionData{false};
bool doRFData{true};
bool doObserverPosition{false};
app.add_option("--svs", svpairs, "Listen to specified svs. '0' = gps, '2' = Galileo, '2,1' is E01");
app.add_option("--stations", stations, "Listen to specified stations.");
app.add_option("--positions,-p", doObserverPosition, "Print out observer positions (or not)");
app.add_option("--rfdata,-r", doRFData, "Print out RF data (or not)");
@ -183,6 +261,10 @@ try
for(const auto& svp : svpairs) {
svfilter.addFilter(svp);
}
set<int> statset;
for(const auto& i : stations)
statset.insert(i);
ofstream almanac("almanac.txt");
ofstream iodstream("iodstream.csv");
@ -194,7 +276,13 @@ try
ofstream sp3csv;
sp3csv.open ("sp3.csv", std::ofstream::out | std::ofstream::app);
sp3csv<<"timestamp gnssid sv ephAge sp3X sp3Y sp3Z ephX ephY ephZ sp3Clock ephClock distance clockDelta"<<endl;
sp3csv<<"timestamp gnssid sv ephAge sp3X sp3Y sp3Z ephX ephY ephZ sp3Clock ephClock distance along clockDelta E speed"<<endl;
ofstream loccsv;
loccsv.open ("jeff.csv", std::ofstream::out | std::ofstream::app);
//loccsv<<"timestamp lat lon altitude accuracy\n";
for(;;) {
char bert[4];
@ -220,6 +308,9 @@ try
NavMonMessage nmm;
nmm.ParseFromString(string(buffer, len));
if(!statset.empty() && !statset.count(nmm.sourceid()))
continue;
// if(nmm.type() == NavMonMessage::ReceptionDataType)
// continue;
@ -232,7 +323,7 @@ try
cout<< "imptow "<<imptow-2<<" ";
};
static map<int,GalileoMessage> galEphemeris;
if(nmm.type() == NavMonMessage::ReceptionDataType) {
if(doReceptionData) {
etstamp();
@ -273,6 +364,7 @@ try
// 2^-34 2^-46
cout <<" iodnav "<<gm.iodnav <<" af0 "<<gm.af0 <<" af1 "<<gm.af1 <<", scaled: "<<ldexp(1.0*gm.af0, 19-34)<<", "<<ldexp(1.0*gm.af1, 38-46);
if(tow && oldgm4s.count(nmm.gi().gnsssv()) && oldgm4s[nmm.gi().gnsssv()].iodnav != gm.iodnav) {
auto& oldgm4 = oldgm4s[nmm.gi().gnsssv()];
auto oldOffset = oldgm4.getAtomicOffset(tow);
auto newOffset = gm.getAtomicOffset(tow);
@ -286,6 +378,7 @@ try
gmwtypes[{sv,3}].iodnav == gmwtypes[{sv,4}].iodnav) {
cout <<" have complete ephemeris at " << gm.iodnav;
galEphemeris[sv] = gm;
int start = utcFromGST(gm.wn, gm.tow);
@ -296,9 +389,16 @@ try
if(!haveSeen.count({sv, bestSP3->t})) {
haveSeen.insert({sv, bestSP3->t});
Point newPoint;
getCoordinates(gm.tow + (bestSP3->t - start), gm, &newPoint);
double E=getCoordinates(gm.tow + (bestSP3->t - start), gm, &newPoint, false);
Point sp3Point(bestSP3->x, bestSP3->y, bestSP3->z);
Vector dist(newPoint, sp3Point);
Vector nspeed;
getSpeed(gm.tow + (bestSP3->t - start), gm, &nspeed);
Vector speed = nspeed;
nspeed.norm();
double along = nspeed.inner(dist);
cout<<"\nsp3 "<<(bestSP3->t - start)<<" E"<<sv<<" "<<humanTime(bestSP3->t)<<" (" << newPoint.x/1000.0 <<", "<<newPoint.y/1000.0<<", "<<newPoint.z/1000.0<< ") (" <<
(bestSP3->x/1000.0) <<", " << (bestSP3->y/1000.0) <<", " << (bestSP3->z/1000.0) << ") "<<bestSP3->clockBias << " " << gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first<< " " << dist.length()<< " ";
cout << (bestSP3->clockBias - gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first);
@ -306,8 +406,8 @@ try
cout << endl;
sp3csv <<std::fixed<< bestSP3->t << " 2 "<< sv <<" " << ephAge(gm.tow+(bestSP3->t - start), gm.getT0e()) <<" "<<bestSP3->x<<" " << bestSP3->y<<" " <<bestSP3->z <<" " << newPoint.x<<" " <<newPoint.y <<" " <<newPoint.z << " " <<bestSP3->clockBias <<" ";
sp3csv << gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first<<" " << dist.length() <<" ";
sp3csv << (bestSP3->clockBias - gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first) << endl;
sp3csv << gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first<<" " << dist.length() <<" " << along <<" ";
sp3csv << (bestSP3->clockBias - gm.getAtomicOffset(gm.tow + (bestSP3->t-start)).first) << " " << E << " " << speed.length()<<endl;
}
}
@ -626,6 +726,7 @@ try
cout<<endl;
}
else if(nmm.type() == NavMonMessage::ObserverPositionType) {
g_ourpos = Point(nmm.op().x(), nmm.op().y(), nmm.op().z());
if(!doObserverPosition)
continue;
etstamp();
@ -635,16 +736,25 @@ try
cout<<", WGS84 lon "<< 180*std::get<1>(latlonh)/M_PI
<<" lat "<< 180*std::get<0>(latlonh)/M_PI
<<" elev "<< std::get<2>(latlonh) << " acc "<<nmm.op().acc()<<" m "<<endl;
g_ourpos = Point(nmm.op().x(), nmm.op().y(), nmm.op().z());
loccsv<<std::fixed<<nmm.localutcseconds()+nmm.localutcnanoseconds()/1000000000.0<<" "<<180*std::get<1>(latlonh)/M_PI<<" "<<
180*std::get<0>(latlonh)/M_PI<<" "<<std::get<2>(latlonh)<<" "<<nmm.op().acc()<<"\n";
}
else if(nmm.type() == NavMonMessage::RFDataType) {
if(!doRFData)
continue;
if(!svfilter.check(nmm.rfd().gnssid(), nmm.rfd().gnsssv(), nmm.gi().sigid()))
continue;
etstamp();
cout<<"RFdata for "<<nmm.rfd().gnssid()<<","<<nmm.rfd().gnsssv()<<","<<(nmm.rfd().has_sigid() ? nmm.rfd().sigid() : 0) <<": ";
cout<<std::fixed<<"RFdata for "<<nmm.rfd().gnssid()<<","<<nmm.rfd().gnsssv()<<","<<(nmm.rfd().has_sigid() ? nmm.rfd().sigid() : 0) <<": ";
cout<<" doppler-hz " << nmm.rfd().doppler();
cout<<" carrier-phase " << nmm.rfd().carrierphase();
cout<<" pseudo-range " << nmm.rfd().pseudorange();
cout<<" carrier-phase " << nmm.rfd().carrierphase();
cout<<" rcv-tow "<<nmm.rfd().rcvtow();
cout<<" pr-std " << nmm.rfd().prstd();
cout<<" dop-std " << nmm.rfd().dostd();
cout<<" cp-std " << nmm.rfd().cpstd();
@ -652,8 +762,195 @@ try
if(nmm.rfd().has_cno()) {
cout<<" cno-db " <<nmm.rfd().cno();
}
if(nmm.rfd().has_prvalid()) {
cout<<" prvalid " <<nmm.rfd().prvalid();
}
if(nmm.rfd().has_cpvalid()) {
cout<<" cpvalid " <<nmm.rfd().cpvalid();
}
if(nmm.rfd().has_clkreset()) {
cout<<" clkreset " <<nmm.rfd().clkreset();
}
static map<int,FixStat> fixes;
if(nmm.rfd().gnssid()==2 && galEphemeris.count(nmm.rfd().gnsssv())) { // galileo
const auto& eph = galEphemeris[nmm.rfd().gnsssv()];
Point sat;
auto [offset, trend] = eph.getAtomicOffset(round(nmm.rfd().rcvtow()));
(void)trend;
static int n;
double E=getCoordinates(nmm.rfd().rcvtow(), eph, &sat);
double range = Vector(g_ourpos, sat).length();
double origrange = range;
E=getCoordinates(nmm.rfd().rcvtow() - range/299792458.0, eph, &sat);
range = Vector(g_ourpos, sat).length();
cout << " d "<<origrange-range;
origrange=range;
/*
E=getCoordinates(nmm.rfd().rcvtow() + range/299792458.0 + offset/1000000000.0 - 0.018, eph, &sat);
range = Vector(g_ourpos, sat).length();
cout << " d "<< 10000.0*(origrange-range);
origrange=range;
*/
constexpr double omegaE = 2*M_PI /86164.091 ;
/*
double theta = -(2*M_PI*range / 299792458.0) /86164.091; // sidereal day
Point rot;
rot.x = sat.x * cos(theta) - sat.y * sin(theta);
rot.y = sat.x * sin(theta) + sat.y * cos(theta);
rot.z = sat.z;
double oldrange=range;
range = Vector(g_ourpos, rot).length(); // second try
cout<<" rot-shift "<<oldrange-range <<" abs-move "<<Vector(rot, sat).length();
*/
double rotcor = omegaE * (sat.x*g_ourpos.y - sat.y * g_ourpos.x) / 299792458.0;
cout<<" rot-shift "<<rotcor;
range += rotcor;
double bestrange = nmm.rfd().pseudorange();
double gap = range - bestrange;
cout <<" pseudo-gap " << gap/1000.0;
constexpr double speedOfLightPerNS = 299792458.0 / 1000000000.0;
bestrange += speedOfLightPerNS * offset;
// Δtr=F e A1/2 sin(E)
constexpr double F = 1000000000.0*-4.442807309e-10; // "in ns"
double dtr = F * eph.getE() * eph.getSqrtA() * sin(E);
bestrange -= speedOfLightPerNS * dtr;
cout<<" relcor "<<speedOfLightPerNS * dtr;
// multi-freq adjustment is done in emitFixState
cout<<" clockcor "<< offset/1000000.0 << "ms gap " << gap/1000.0;
if(fixes[nmm.sourceid()].iTow != nmm.rfd().rcvtow()) {
emitFixState(nmm.sourceid(), nmm.rfd().rcvtow(), fixes[nmm.sourceid()], n);
fixes[nmm.sourceid()].iTow = nmm.rfd().rcvtow();
n++;
}
auto& satstat=fixes[nmm.sourceid()].sats[{(int)nmm.rfd().gnssid(), (int)nmm.rfd().gnsssv()}];
satstat.ephrange = range;
auto dop = doDoppler(nmm.rfd().rcvtow(), g_ourpos, eph, 1575420000);
satstat.radvel = dop.radvel;
if(nmm.rfd().sigid()==1) {
satstat.bestrange1 = bestrange;
satstat.doppler1 = nmm.rfd().doppler();
}
else if(nmm.rfd().sigid()==5) {
satstat.bestrange5 = bestrange;
satstat.doppler5 = nmm.rfd().doppler();
}
satstat.ephemeris = eph;
}
cout<<endl;
}
else if(nmm.type() == NavMonMessage::ObserverDetailsType) {
etstamp();
cout<<"Got observerdetails message"<<endl;
}
else if(nmm.type() == NavMonMessage::DebuggingType) {
etstamp();
cout<<"Got debugging message of type "<<nmm.dm().type()<<endl;
auto res = parseTrkMeas(basic_string<uint8_t>((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size()));
uint64_t maxt=0;
for(const auto& sv : res) {
if(sv.gnss != 2) continue;
if(sv.tr > maxt)
maxt = sv.tr;
}
double ttag = round( (ldexp(1.0*maxt, -32)/1000.0 + 0.08) / 0.1) * 0.1;
double rtow = round(ldexp(1.0*maxt, -32) /1000.0); // this was the rounded tow of transmission
sort(res.begin(), res.end(), [&](const auto& a, const auto& b) {
double elevA=0, elevB=0;
if(galEphemeris.count(a.sv)) {
const auto& eph = galEphemeris[a.sv];
Point sat;
getCoordinates(rtow, eph, &sat);
elevA=getElevationDeg(sat, g_ourpos);
}
if(galEphemeris.count(b.sv)) {
const auto& eph = galEphemeris[b.sv];
Point sat;
getCoordinates(rtow, eph, &sat);
elevB=getElevationDeg(sat, g_ourpos);
}
return elevB < elevA;
});
double toffsetms=0;
bool first = true;
for(const auto sv : res) {
if(sv.gnss != 2) continue;
if(!galEphemeris.count(sv.sv))
continue;
const auto& eph = galEphemeris[sv.sv];
double clockoffms = eph.getAtomicOffset(rtow).first/1000000.0;
Point sat;
double E=getCoordinates(rtow - clockoffms/1000.0, eph, &sat, sv.sv != 14);
double range = Vector(g_ourpos, sat).length();
getCoordinates(rtow - clockoffms/1000.0 - range/299792458.0, eph, &sat);
range = Vector(g_ourpos, sat).length();
double trmsec = ldexp(maxt - sv.tr, -32) + clockoffms;
constexpr double omegaE = 2*M_PI /86164.091 ;
double rotcor = omegaE * (sat.x*g_ourpos.y - sat.y * g_ourpos.x) / 299792458.0;
range += rotcor;
double bgdcor = 299792458.0 *ldexp(eph.BGDE1E5b,-32);
range -= bgdcor;
constexpr double speedOfLightPerNS = 299792458.0 / 1000000000.0;
// Δtr=F e A1/2 sin(E)
constexpr double F = 1000000000.0*-4.442807309e-10; // "in ns"
double dtr = F * eph.getE() * eph.getSqrtA() * sin(E);
double relcor = speedOfLightPerNS * dtr;
range -= relcor;
double predTrmsec = range / 299792.4580;
if(first) {
toffsetms = trmsec - predTrmsec;
first = false;
cout<<"Set toffsetms to "<< toffsetms << " for tow "<<rtow<<" ttag " << ttag << " raw " << (ldexp(1.0*maxt, -32) /1000.0) << endl;
}
trmsec -= toffsetms;
cout<<std::fixed<<"gnssid "<< sv.gnss<<" sv "<<sv.sv <<": doppler "<<sv.dopplerHz <<" range-ms " << range / 299792.4580;
cout << " actual-ms " << trmsec << " delta " << ((range / 299792.4580 - trmsec))<< " delta-m " << 299792.4580*((range / 299792.4580 - trmsec));
cout<<" rotcor "<< rotcor;
cout<<" relcor "<<relcor;
cout<<" elev " << getElevationDeg(sat, g_ourpos);
cout<<" bgd-m " << bgdcor;
cout<<" clockoff-ms " << clockoffms << endl;
}
}
else {
etstamp();

View File

@ -36,6 +36,20 @@ struct ObserverPosition
{
Point pos;
double groundSpeed{-1};
double accuracy{-1.0};
string serialno;
string hwversion;
string swversion;
string mods;
string vendor;
double clockOffsetNS{-1};
double clockOffsetDriftNS{-1};
double clockAccuracyNS{-1};
double freqAccuracyPS{-1};
time_t uptime;
string githash;
string owner;
string remark;
time_t lastSeen{0};
};
std::map<int, ObserverPosition> g_srcpos;
@ -343,7 +357,7 @@ struct InfluxPusher
checkSend();
}
void addValueObserver(int src, string_view name, const initializer_list<pair<const char*, double>>& values, time_t t)
void addValueObserver(int src, string_view name, const initializer_list<pair<const char*, double>>& values, time_t t, std::optional<SatID> satid=std::optional<SatID>())
{
if(d_mute)
return;
@ -358,7 +372,9 @@ struct InfluxPusher
}
d_buffer+= string(name)+",src="+to_string(src);
if(satid) {
d_buffer+=",gnssid="+to_string(satid->gnss)+ +",sv=" +to_string(satid->sv)+",sigid="+to_string(satid->sigid);
}
d_buffer+= " ";
bool lefirst=true;
@ -754,8 +770,19 @@ try
item["eph-age"] = ephAge(latestTow(2, svstats), ae.second.getT0e());
item["i0"] = 180.0 * ae.second.getI0()/ M_PI;
item["inclination"] = 180 * ae.second.getI0() /M_PI;
item["omega"] = ae.second.getOmega();
item["sqrtA"]= ae.second.getSqrtA();
item["M0"] = ae.second.getM0();
item["delta-n"] = ae.second.getDeltan();
item["omega-dot"] = ae.second.getOmegadot();
item["omega0"] = ae.second.getOmega0();
item["idot"] = ae.second.getIdot();
item["t0e"] = ae.second.getT0e();
Point sat;
getCoordinates(latestTow(2, svstats), ae.second, &sat);
double E=getCoordinates(latestTow(2, svstats), ae.second, &sat);
item["E"]=E;
item["eph-ecefX"]= sat.x/1000;
item["eph-ecefY"]= sat.y/1000;
item["eph-ecefZ"]= sat.z/1000;
@ -875,15 +902,34 @@ try
for(const auto& src : g_srcpos) {
nlohmann::json obj;
obj["id"] = src.first;
auto longlat = getLongLat(src.second.pos.x, src.second.pos.y, src.second.pos.z);
longlat.first *= 180.0/M_PI;
longlat.second *= 180.0/M_PI;
longlat.first = ((int)(10*longlat.first))/10.0;
longlat.second = ((int)(10*longlat.second))/10.0;
obj["longitude"] = longlat.first;
obj["latitude"] = longlat.second;
auto latlonh = ecefToWGS84(src.second.pos.x, src.second.pos.y, src.second.pos.z);
get<0>(latlonh) *= 180.0/M_PI;
get<1>(latlonh) *= 180.0/M_PI;
get<0>(latlonh) = ((int)(10*get<0>(latlonh)))/10.0;
get<1>(latlonh) = ((int)(10*get<1>(latlonh)))/10.0;
get<2>(latlonh) = ((int)(10*get<2>(latlonh)))/10.0;
obj["latitude"] = get<0>(latlonh);
obj["longitude"] = get<1>(latlonh);
obj["last-seen"] = src.second.lastSeen;
obj["ground-speed"] = src.second.groundSpeed;
obj["swversion"] = src.second.swversion;
obj["hwversion"] = src.second.hwversion;
obj["mods"] = src.second.mods;
obj["serialno"] = src.second.serialno;
obj["clockoffsetns"]= src.second.clockOffsetNS;
obj["clockdriftns"]= src.second.clockOffsetDriftNS;
obj["clockacc"]= src.second.clockAccuracyNS;
obj["freqacc"]= src.second.freqAccuracyPS;
obj["uptime"]= src.second.uptime;
obj["githash"]= src.second.githash;
obj["owner"]= src.second.owner;
obj["vendor"]= src.second.vendor;
obj["remark"]= src.second.remark;
obj["acc"] = src.second.accuracy;
obj["h"] = get<2>(latlonh);
auto svstats = g_statskeeper.get();
nlohmann::json svs = nlohmann::json::object();
@ -898,8 +944,14 @@ try
Point sat;
if((sv.first.gnss == 0 || sv.first.gnss == 2) && sv.second.completeIOD())
if((sv.first.gnss == 0 || sv.first.gnss == 2) && sv.second.completeIOD()) {
svo["delta_hz"] = iter->second.deltaHz;
auto hzCorrection = getHzCorrection(time(0), src.first , sv.first.gnss, sv.first.sigid, svstats);
if(hzCorrection)
svo["delta_hz_corr"] = iter->second.deltaHz - *hzCorrection;
getCoordinates(latestTow(sv.first.gnss, svstats), sv.second.liveIOD(), & sat);
}
if(sv.first.gnss == 3 && sv.second.oldBeidouMessage.sow >= 0 && sv.second.oldBeidouMessage.sqrtA != 0) {
getCoordinates(latestTow(sv.first.gnss, svstats), sv.second.oldBeidouMessage, &sat);
}
@ -914,8 +966,11 @@ try
svo["azi"] = getAzimuthDeg(sat, our);
}
svo["prres"] = iter->second.prres;
svo["qi"] = iter->second.qi;
svo["used"] = iter->second.used;
svo["age-s"] = time(0) - iter->second.t;
svo["last-seen"] = iter->second.t;
svo["gnss"] = sv.first.gnss;
@ -1064,6 +1119,21 @@ try
ret["latitude"] = 180.0*longlat.second/M_PI;
}
nlohmann::json recvs = nlohmann::json::object();
for(const auto& perrecv : s.perrecv) {
nlohmann::json recv = nlohmann::json::object();
recv["db"] = perrecv.second.db;
recv["qi"] = perrecv.second.qi;
recv["used"] = perrecv.second.used;
recv["prres"] = perrecv.second.prres;
recv["elev"] = perrecv.second.el;
recv["last-seen-s"] = time(0) - perrecv.second.t;
recvs[std::to_string(perrecv.first)] = recv;
}
ret["recvs"]=recvs;
return ret;
}
);
@ -1404,6 +1474,8 @@ try
det["db"] = pr.second.db;
det["last-seen-s"] = time(0) - pr.second.t;
det["prres"] = pr.second.prres;
det["qi"] = pr.second.qi;
det["used"] = pr.second.used;
if(time(0) - pr.second.deltaHzTime < 60) {
det["delta_hz"] = pr.second.deltaHz;
@ -1484,11 +1556,24 @@ try
sigid = 1;
SatID id{(uint32_t)gnssid, (uint32_t)sv, (uint32_t)sigid};
g_svstats[id].perrecv[nmm.sourceid()].db = nmm.rd().db();
g_svstats[id].perrecv[nmm.sourceid()].el = nmm.rd().el();
g_svstats[id].perrecv[nmm.sourceid()].azi = nmm.rd().azi();
g_svstats[id].perrecv[nmm.sourceid()].prres = nmm.rd().prres();
auto& perrecv = g_svstats[id].perrecv[nmm.sourceid()];
perrecv.db = nmm.rd().db();
perrecv.el = nmm.rd().el();
perrecv.azi = nmm.rd().azi();
perrecv.prres = nmm.rd().prres();
if(nmm.rd().has_qi())
perrecv.qi = nmm.rd().qi();
else
perrecv.qi = -1;
if(nmm.rd().has_used())
perrecv.used = nmm.rd().used();
else
perrecv.used = -1;
Point sat{0,0,0};
//cout<<"Got recdata for "<<id.gnss<<","<<id.sv<<","<<id.sigid<<": count="<<g_svstats.count(id)<<endl;
// XXX this needs to be unified somehow
@ -1498,13 +1583,18 @@ try
else if(g_svstats[id].completeIOD()) {
getCoordinates(g_svstats[id].tow, g_svstats[id].liveIOD(), &sat);
}
if(sat.x != 0 && g_srcpos[nmm.sourceid()].pos.x != 0) {
if(!(random() % 4))
idb.addValue(id, nmm.localutcseconds()*1000000000, "recdata",
{
{"db", nmm.rd().db()},
{"azi", getAzimuthDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"ele", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", nmm.rd().prres()}}, nmm.sourceid());
{"prres", nmm.rd().prres()},
{"qi", perrecv.qi},
{"used", perrecv.used}
}, nmm.sourceid());
}
}
@ -1527,12 +1617,13 @@ try
if(wtype == 1 || wtype == 2 || wtype == 3) {
idb.addValue(id, "iod-live", svstat.galmsg.iodnav);
idb.addValue(id, "eph-age", ephAge(gm.tow, gm.getT0e()));
}
if(wtype == 5 && svstat.galmsgTyped.count(5)) {
const auto& old5gm = svstat.galmsgTyped[5];
if(make_tuple(old5gm.e5bhs, old5gm.e1bhs, old5gm.e5bdvs, old5gm.e1bdvs) !=
make_tuple(gm.e5bhs, gm.e1bhs, gm.e5bdvs, gm.e1bdvs)) {
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" Galileo "<<id.sv <<" sigid "<<id.sigid<<" change in health: ["<<humanBhs(old5gm.e5bhs)<<", "<<humanBhs(old5gm.e1bhs)<<", "<<(int)old5gm.e5bdvs <<", " << (int)old5gm.e1bdvs<<"] -> ["<< humanBhs(gm.e5bhs)<<", "<< humanBhs(gm.e1bhs)<<", "<< (int)gm.e5bdvs <<", " << (int)gm.e1bdvs<<"], lastseen "<<ephAge(old5gm.tow, gm.tow)/3600.0 <<" hours"<<endl;
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" src "<<nmm.sourceid()<<" Galileo "<<id.sv <<" sigid "<<id.sigid<<" change in health: ["<<humanBhs(old5gm.e5bhs)<<", "<<humanBhs(old5gm.e1bhs)<<", "<<(int)old5gm.e5bdvs <<", " << (int)old5gm.e1bdvs<<"] -> ["<< humanBhs(gm.e5bhs)<<", "<< humanBhs(gm.e1bhs)<<", "<< (int)gm.e5bdvs <<", " << (int)gm.e1bdvs<<"], lastseen "<<ephAge(old5gm.tow, gm.tow)/3600.0 <<" hours"<<endl;
}
}
@ -1560,10 +1651,7 @@ try
idb.addValue(id, "af2", g_svstats[id].iods[iod].af2);
idb.addValue(id, "t0c", g_svstats[id].iods[iod].t0c * 60);
double age = ephAge(g_svstats[id].tow, g_svstats[id].iods[iod].t0c * 60);
double offset = ldexp(1000.0*(1.0*g_svstats[id].iods[iod].af0 + ldexp(age*g_svstats[id].iods[iod].af1, -12)), -34);
idb.addValue(id, "atomic_offset_ns", 1000000.0*offset);
idb.addValue(id, "atomic_offset_ns", svstat.galmsg.getAtomicOffset(svstat.tow).first);
if(oldgm.af0 && oldgm.t0c != svstat.galmsg.t0c) {
auto oldOffset = oldgm.getAtomicOffset(svstat.tow);
@ -1717,6 +1805,7 @@ try
g_srcpos[nmm.sourceid()].groundSpeed = nmm.op().groundspeed();
}
g_srcpos[nmm.sourceid()].accuracy = nmm.op().acc();
// idb.addValueObserver(nmm.sourceid(), "accfix", nmm.op().acc(), nmm.localutcseconds());
auto latlonh = ecefToWGS84(nmm.op().x(), nmm.op().y(), nmm.op().z());
@ -1747,12 +1836,20 @@ try
{{"carrierphase", nmm.rfd().carrierphase()},
{"doppler", nmm.rfd().doppler()},
{"locktime", nmm.rfd().locktimems()},
{"pseudorange", nmm.rfd().pseudorange()}});
{"pseudorange", nmm.rfd().pseudorange()},
{"prstd", nmm.rfd().prstd()},
{"cpstd", nmm.rfd().cpstd()},
{"dostd", nmm.rfd().dostd()}
}, nmm.sourceid());
if(id.gnss == 3 && g_svstats[id].oldBeidouMessage.sow >= 0 && g_svstats[id].oldBeidouMessage.sqrtA != 0) {
double freq = 1561.098;
if(nmm.rfd().sigid() != 0)
freq = 1207.140;
auto res = doDoppler(nmm.rfd().rcvtow(), g_srcpos[nmm.sourceid()].pos, g_svstats[id].oldBeidouMessage, freq * 1000000);
// the magic 14 is because 'rcvtow()' is in GPS/Galileo TOW
// but BeiDou operates with 14 leap seconds less than GPS/Galileo
auto res = doDoppler(nmm.rfd().rcvtow()-14, g_srcpos[nmm.sourceid()].pos, g_svstats[id].oldBeidouMessage, freq * 1000000);
if(isnan(res.preddop)) {
cerr<<"Problem with doppler calculation for C"<<id.sv<<": "<<endl;
@ -1768,7 +1865,19 @@ try
idb.addValue(id, "delta_hz", nmm.rfd().doppler() - res.preddop);
auto corr = getHzCorrection(t, nmm.sourceid(), id.gnss, id.sigid, g_svstats);
if(corr) {
idb.addValue(id, "delta_hz_cor", nmm.rfd().doppler() - res.preddop - (1561.098/1575.42) * (*corr));
idb.addValue(id, "delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr));
Point sat;
getCoordinates(nmm.rfd().rcvtow(), g_svstats[id].oldBeidouMessage, &sat);
idb.addValue(id, 1000000000.0*t, "correlator",
{{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)},
{"delta_hz", nmm.rfd().doppler() - res.preddop},
{"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}
}, nmm.sourceid());
}
}
}
@ -1779,25 +1888,135 @@ try
auto res = doDoppler(nmm.rfd().rcvtow(), g_srcpos[nmm.sourceid()].pos, g_svstats[id].liveIOD(),freqMHZ * 1000000);
idb.addValue(id, nanoTime(0, nmm.rfd().rcvwn(), nmm.rfd().rcvtow()), "orbit",
{{"preddop", res.preddop},
{"radvel", res.radvel}});
Point sat;
getCoordinates(nmm.rfd().rcvtow(), g_svstats[id].liveIOD(), &sat);
time_t t = utcFromGPS(nmm.rfd().rcvwn(), nmm.rfd().rcvtow());
// idb.addValueObserver((int)nmm.sourceid(), "orbit",
// {{"preddop", res.preddop},
// {"radvel", res.radvel}}, t, id);
if(t - g_svstats[id].perrecv[nmm.sourceid()].deltaHzTime > 10) { // only replace after 10 seconds
if(t - g_svstats[id].perrecv[nmm.sourceid()].deltaHzTime > 10) { // only replace after 5 seconds
g_svstats[id].perrecv[nmm.sourceid()].deltaHz = nmm.rfd().doppler() - res.preddop;
g_svstats[id].perrecv[nmm.sourceid()].deltaHzTime = t;
idb.addValue(id, "delta_hz", nmm.rfd().doppler() - res.preddop);
auto corr = getHzCorrection(t, nmm.sourceid(), id.gnss, id.sigid, g_svstats);
if(corr) {
idb.addValue(id, "delta_hz_cor", nmm.rfd().doppler() - res.preddop - *corr);
idb.addValue(id, "delta_hz_cor", nmm.rfd().doppler() - res.preddop - *corr, nmm.sourceid());
idb.addValue(id, 1000000000.0*t, "correlator",
{{"delta_hz_cor", nmm.rfd().doppler() - res.preddop - (*corr)},
{"delta_hz", nmm.rfd().doppler() - res.preddop},
{"hz", nmm.rfd().doppler()},
{"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}
}, nmm.sourceid());
}
}
}
}
else if(nmm.type()== NavMonMessage::ObserverDetailsType) {
auto& o = g_srcpos[nmm.sourceid()];
o.serialno = nmm.od().serialno();
o.swversion = nmm.od().swversion();
o.hwversion = nmm.od().hwversion();
o.mods = nmm.od().modules();
if(nmm.od().has_clockoffsetns())
o.clockOffsetNS = nmm.od().clockoffsetns();
else
o.clockOffsetNS = -1;
if(nmm.od().has_clockoffsetdriftns())
o.clockOffsetDriftNS = nmm.od().clockoffsetdriftns();
else
o.clockOffsetDriftNS = -1;
if(nmm.od().has_clockaccuracyns())
o.clockAccuracyNS = nmm.od().clockaccuracyns();
else
o.clockAccuracyNS = -1;
if(nmm.od().has_freqaccuracyps())
o.freqAccuracyPS = nmm.od().freqaccuracyps();
else
o.freqAccuracyPS = -1;
if(nmm.od().has_uptime())
o.uptime = nmm.od().uptime();
else
o.uptime = -1;
if(nmm.od().has_recvgithash())
o.githash = nmm.od().recvgithash();
else
o.githash.clear();
o.vendor = nmm.od().vendor();
o.owner = nmm.od().owner();
o.remark = nmm.od().remark();
idb.addValueObserver(nmm.sourceid(), "observer_details", {
{"clock_offset_ns", o.clockOffsetNS},
{"clock_drift_ns", o.clockOffsetDriftNS},
{"clock_acc_ns", o.clockAccuracyNS},
{"freq_acc_ps", o.freqAccuracyPS},
{"uptime", o.uptime}
},
nmm.localutcseconds());
}
else if(nmm.type()== NavMonMessage::DebuggingType) {
auto ret = parseTrkMeas(basic_string<uint8_t>((const uint8_t*)nmm.dm().payload().c_str(), nmm.dm().payload().size()));
for(const auto& tss : ret) {
SatID id{tss.gnss, tss.sv, tss.gnss == 2 ? 1 : 0};
if(g_svstats[id].completeIOD()) {
double freqMHZ = 1575.42;
double tsat = ldexp(1.0* tss.tr, -32) /1000.0;
auto res = doDoppler(tsat, g_srcpos[nmm.sourceid()].pos, g_svstats[id].liveIOD(),freqMHZ * 1000000);
idb.addValueObserver((int)nmm.sourceid(), "orbit",
{{"preddop", res.preddop},
{"radvel", res.radvel}}, (time_t)nmm.localutcseconds(), id);
// cout << " preddop "<<res.preddop <<" meas "<<tss.dopplerHz <<" delta " << (tss.dopplerHz - res.preddop);
double t = utcFromGPS(latestWN(0, g_svstats), tsat);
if(t - g_svstats[id].perrecv[nmm.sourceid()].deltaHzTime > 5) { // only replace after 5 seconds
g_svstats[id].perrecv[nmm.sourceid()].deltaHz = tss.dopplerHz - res.preddop;
g_svstats[id].perrecv[nmm.sourceid()].deltaHzTime = t;
idb.addValue(id, "delta_hz", tss.dopplerHz - res.preddop);
auto corr = getHzCorrection(t, nmm.sourceid(), id.gnss, id.sigid, g_svstats);
if(corr) {
Point sat;
getCoordinates(tsat, g_svstats[id].liveIOD(), &sat);
idb.addValue(id, "delta_hz_cor", tss.dopplerHz - res.preddop - *corr, nmm.sourceid());
idb.addValue(id, 1000000000.0*t, "correlator",
{{"delta_hz_cor", tss.dopplerHz - res.preddop - *corr},
{"delta_hz", tss.dopplerHz - res.preddop},
{"hz", tss.dopplerHz},
{"elevation", getElevationDeg(sat, g_srcpos[nmm.sourceid()].pos)},
{"prres", g_svstats[id].perrecv[nmm.sourceid()].prres},
{"qi", g_svstats[id].perrecv[nmm.sourceid()].qi}
}, nmm.sourceid());
}
}
}
// cout<<endl;
}
}
else if(nmm.type()== NavMonMessage::GPSInavType) {
if(nmm.gpsi().sigid()) {
cout<<"ignoring sigid "<<nmm.gpsi().sigid()<<" for GPS "<<nmm.gpsi().gnsssv()<<endl;
@ -1821,11 +2040,11 @@ try
idb.addValue(id, "ura", svstat.ura);
if(oldsvstat.af0 && oldsvstat.ura != svstat.ura) { // XX find better way to check
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" wn "<<svstat.wn <<" GPS "<<id.sv <<"@"<<id.sigid<<" change in URA ["<< humanUra(oldsvstat.ura) <<"] -> [" << humanUra(svstat.ura)<<"] "<<(int)svstat.ura<<", lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" src "<<nmm.sourceid()<<" wn "<<svstat.wn <<" GPS "<<id.sv <<"@"<<id.sigid<<" change in URA ["<< humanUra(oldsvstat.ura) <<"] -> [" << humanUra(svstat.ura)<<"] "<<(int)svstat.ura<<", lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
}
if(oldsvstat.af0 && oldsvstat.gpshealth != svstat.gpshealth) { // XX find better way to check
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" wn "<<svstat.wn <<" GPS "<<id.sv <<"@"<<id.sigid<<" change in health ["<< (int)oldsvstat.gpshealth <<"] -> [" << (int)svstat.gpshealth <<"], lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" src " <<nmm.sourceid()<<" wn "<<svstat.wn <<" GPS "<<id.sv <<"@"<<id.sigid<<" change in health ["<< (int)oldsvstat.gpshealth <<"] -> [" << (int)svstat.gpshealth <<"], lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
}
@ -1843,7 +2062,7 @@ try
}
else if(frame==2) {
if(oldsvstat.gpshealth != svstat.gpshealth) {
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" GPS "<<id.sv <<"@"<<id.sigid<<" change in health: ["<< (int)oldsvstat.gpshealth<<"] -> ["<< (int)svstat.gpshealth<<"] , lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" src "<<nmm.sourceid()<<" GPS "<<id.sv <<"@"<<id.sigid<<" change in health: ["<< (int)oldsvstat.gpshealth<<"] -> ["<< (int)svstat.gpshealth<<"] , lastseen "<<ephAge(oldsvstat.tow, svstat.tow)/3600.0 <<" hours"<<endl;
}
idb.addValue(id, "gpshealth", g_svstats[id].gpshealth);
}
@ -1866,6 +2085,7 @@ try
g_svstats[id].wn += 2048;
}
else if(nmm.type()== NavMonMessage::BeidouInavTypeD1) {
try {
SatID id{nmm.bid1().gnssid(), nmm.bid1().gnsssv(), nmm.bid1().sigid()};
g_svstats[id].perrecv[nmm.sourceid()].t = nmm.localutcseconds();
@ -1977,6 +2197,9 @@ try
g_dtLSBeidou = bm.deltaTLS;
// cout<<"Beidou leap seconds: "<<g_dtLSBeidou<<endl;
}
}catch(std::exception& e) {
cerr<<"Beidou: "<<e.what()<<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()));
@ -2007,6 +2230,7 @@ try
// cout<<"Glonass now: "<<humanTime(glotime + 820368000) << " n4 "<<(int)gm.n4<<" NT "<<(int)gm.NT<< " wn "<<svstat.wn <<" tow " <<svstat.tow<<endl;
}
else if(strno == 2) {
idb.addValue(id, "glohealth", gm.Bn);
if(oldgm.Bn != gm.Bn) {
cout<<humanTime(id.gnss, svstat.wn, svstat.tow)<<" n4 "<< (int)gm.n4<<" NT " << (int)gm.NT<<" GLONASS R"<<id.sv<<"@"<<id.sigid<<" health changed from "<<(int)oldgm.Bn <<" to "<< (int)gm.Bn <<endl;

View File

@ -40,7 +40,7 @@ int main(int argc, char **argv)
{
MiniCurl mc;
MiniCurl::MiniCurlHeaders mch;
string dbname("galileo3");
string dbname("galileo");
string url="http://127.0.0.1:8086/query?db="+dbname+"&epoch=s&q=";
string period="time > now() - 1w";

125
trkmeas.cc 100644
View File

@ -0,0 +1,125 @@
#include <string>
#include "fmt/format.h"
#include "fmt/printf.h"
#include <vector>
#include <map>
#include <iostream>
#include "ubx.hh"
using namespace std;
/* TRK-MEAS
2 U1 Number of channels tracked
?
8 U4 Poweron counter milliseconds
12 U4 "session time counter" milliseconds
104 + n*56 + 0 U1 Channel (?)
104 + n*56 + 1 U1 Quality indicator 0 idle, 1 search, 2 acquired, 3 unsusable, 4 code lock, 5/6/7 carrier lock)
+ 2 ?
+ 3 ?
104 + n*56 + 4 U1 GNSS (?)
104 + n*56 + 5 U1 Satellite ID
?
104 + n*56 + 7 U1 GLONASS Frequency
104 + n*56 + 8 U1 Tracking status
?
+ 16 U1 Code lock "count"
+ 17 U1 Phase lock "count"
?
+ 20 U2 256*dB
?
104 + n*56 + 24 I8 Transmission time, in units of 2^-32 milliseconds (?) // ts=I8(p+24)*P2_32/1000.0;
+ 32 I8 "ADR" in units of 2^-32, plus 0.5 if 'bit' 0x40 is set in tracking status
+ 40 I4 Doppler in units of 2^-10 deca-hertz (?) // I4(p+40)*P2_10*10.0;
+ 44 ?
..
+ 56
*/
extern "C" {
int ubxdecrypt(const unsigned char crypto[16], unsigned char plaintext[16]) __attribute__((weak));
int ubxdecrypt(const unsigned char crypto[16], unsigned char plaintext[16])
{
return -1;
}
}
vector<TrkSatStat> parseTrkMeas(std::basic_string_view<uint8_t> payload)
{
uint8_t plainchunk[16];
std::basic_string<uint8_t> plaintext;
/*
cerr<<"payload.size(): "<<payload.size()<<", mod "<<(payload.size()-4)%16<<", " << payload[2]+256*payload[3]<<": ";
for(int n=0; n < 4; ++n)
cerr<<fmt::sprintf("%02x ", (unsigned int) payload[n]);
cerr<<endl;
*/
int msgsize = (payload[2]+256*payload[3]) - 6;
vector<TrkSatStat> ret;
for(unsigned int n=4 ; n < payload.size(); n+=16) {
if(ubxdecrypt(&payload[n], plainchunk) < 0)
return ret;
plaintext.append(plainchunk, plainchunk+16);
}
// cerr<<"msgsize: "<<msgsize<<", assumed SVs: "<<(int)plaintext[8]<<", per "<<(msgsize-104.0)/(int)plaintext[8]<<endl;
int64_t maxtr=0;
for(int n = 0 ; n < plaintext[8]; ++n) {
int offset = 110+n*56;
int64_t tr;
memcpy(&tr, &plaintext[offset+24], 8);
if(tr > maxtr)
maxtr = tr;
}
for(int n = 0 ; n < plaintext[8]; ++n) {
int offset = 110+n*56;
int32_t rdoppler;
int64_t tr;
memcpy(&rdoppler, &plaintext[offset+40], 4);
memcpy(&tr, &plaintext[offset+24], 8);
int gnssid = plaintext[offset+4];
int sv = plaintext[offset+5];
int trkstat = plaintext[offset+8];
double doppler = ldexp(1.0*rdoppler,-10)*10;
int plcount = (unsigned int)plaintext[offset+17];
/*
cerr<<" gnssid " << gnssid <<" sv "<< sv;
cerr<<" qi "<<(unsigned int)plaintext[offset+1] <<" c "<<(unsigned int)plaintext[offset];
cerr<<" ? "<<(unsigned int)plaintext[offset+2] << " ? " <<(unsigned int)plaintext[offset+3];
cerr<<" trkstat " << trkstat<<" db " << (unsigned int) plaintext[offset+21] << " doppler "<<
doppler << "Hz tr " << tr<< " cl-count " << (unsigned int)plaintext[offset+16]<< " pl-count " << plcount;
cerr<<" tau " << ldexp((tr - maxtr), -32)/1000.0;
cerr<<" pr " << 3e5*ldexp((tr - maxtr), -32)/1000.0;
cerr<<endl;
*/
if((gnssid== 0 || gnssid == 2) && plcount > 2) {
struct TrkSatStat tss;
tss.sv = sv;
tss.gnss = gnssid;
tss.dopplerHz = doppler;
tss.tr = tr;
ret.push_back(tss);
}
}
/*
for(int n = 0 ; n < plaintext[8]; ++n) {
int offset = 110+n*56;
cerr<<" gnssid " << (unsigned int)plaintext[offset+4]<<" sv "<<(unsigned int) plaintext[offset+5]<<endl;
for(int j = 0; j < 56; ++j) {
cerr<< fmt::sprintf("%02x ", (unsigned int)plaintext[offset+j]);
if((j % 16)==15)
cerr<<endl;
}
cerr<<endl;
}
*/
return ret;
}

11
ubx.hh
View File

@ -1,3 +1,4 @@
#pragma once
#include <string>
#include <vector>
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str);
@ -11,3 +12,13 @@ std::basic_string<uint8_t> getGlonassFromSFRBXMsg(std::basic_string_view<uint8_t
std::basic_string<uint8_t> getBeidouFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
std::basic_string<uint8_t> getSBASFromSFRBXMsg(std::basic_string_view<uint8_t> msg);
struct CRCMismatch{};
struct TrkSatStat
{
int gnss;
int sv;
double dopplerHz;
uint64_t tr;
};
std::vector<TrkSatStat> parseTrkMeas(std::basic_string_view<uint8_t> payload);

View File

@ -756,6 +756,23 @@ int main(int argc, char** argv)
cerr<<humanTimeNow()<<" Got nack on F9P GNSS setting"<<endl;
exit(-1);
}
/* VALSET
0x20 91 02 32 =
*/
msg = buildUbxMessage(0x06, 0x8a, {0x00, 0x01, 0x00, 0x00,
0x07, 0x00, 0x91, 0x20, 1,
0x32, 0x02, 0x91, 0x20, 1});
if(sendAndWaitForUBXAckNack(fd, 2, msg, 0x06, 0x8a)) { // msg cfg F9P
if (doDEBUG) { cerr<<humanTimeNow()<<" Got ack on F9P UART1 setting"<<endl; }
}
else {
cerr<<humanTimeNow()<<" Got nack on F9P UART1 setting"<<endl;
exit(-1);
}
}
if(doSBAS) {