468 lines
13 KiB
C++
468 lines
13 KiB
C++
#include <stdio.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <arpa/inet.h>
|
|
#include "fmt/format.h"
|
|
#include "fmt/printf.h"
|
|
#include <fstream>
|
|
#include <map>
|
|
#include <bitset>
|
|
#include <curses.h>
|
|
#include <vector>
|
|
#include <boost/algorithm/string.hpp>
|
|
using namespace std;
|
|
|
|
|
|
struct EofException{};
|
|
|
|
uint8_t getUint8()
|
|
{
|
|
int c;
|
|
c = fgetc(stdin);
|
|
if(c == -1)
|
|
throw EofException();
|
|
return (uint8_t) c;
|
|
}
|
|
|
|
uint16_t getUint16()
|
|
{
|
|
uint16_t ret;
|
|
int res = fread(&ret, 1, sizeof(ret), stdin);
|
|
if(res != sizeof(ret))
|
|
throw EofException();
|
|
// ret = ntohs(ret);
|
|
return ret;
|
|
}
|
|
|
|
/* lovingly lifted from RTKLIB */
|
|
unsigned int getbitu(const unsigned char *buff, int pos, int len)
|
|
{
|
|
unsigned int bits=0;
|
|
int i;
|
|
for (i=pos;i<pos+len;i++) bits=(bits<<1)+((buff[i/8]>>(7-i%8))&1u);
|
|
return bits;
|
|
}
|
|
|
|
extern int getbits(const unsigned char *buff, int pos, int len)
|
|
{
|
|
unsigned int bits=getbitu(buff,pos,len);
|
|
if (len<=0||32<=len||!(bits&(1u<<(len-1)))) return (int)bits;
|
|
return (int)(bits|(~0u<<len)); /* extend sign */
|
|
}
|
|
|
|
|
|
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str)
|
|
{
|
|
uint8_t CK_A = 0, CK_B = 0;
|
|
|
|
auto update = [&CK_A, &CK_B](uint8_t c) {
|
|
CK_A = CK_A + c;
|
|
CK_B = CK_B + CK_A;
|
|
};
|
|
update(ubxClass);
|
|
update(ubxType);
|
|
uint16_t len = str.size();
|
|
update(((uint8_t*)&len)[0]);
|
|
update(((uint8_t*)&len)[1]);
|
|
for(unsigned int I=0; I < str.size(); I++) {
|
|
update(str[I]);
|
|
}
|
|
return (CK_B << 8) + CK_A;
|
|
}
|
|
|
|
struct SVIOD
|
|
{
|
|
std::bitset<32> words;
|
|
uint32_t t0e;
|
|
uint8_t sisa;
|
|
bool complete() const
|
|
{
|
|
return words[1] && words[3];
|
|
}
|
|
void addWord(std::basic_string_view<uint8_t> page);
|
|
};
|
|
|
|
void SVIOD::addWord(std::basic_string_view<uint8_t> page)
|
|
{
|
|
uint8_t wtype = getbitu(&page[0], 0, 6);
|
|
words[wtype]=true;
|
|
if(wtype == 1) {
|
|
t0e = 60*getbitu(&page[0], 16, 14);
|
|
}
|
|
else if(wtype == 3) {
|
|
sisa = getbitu(&page[0], 120, 8);
|
|
}
|
|
}
|
|
|
|
struct SVStat
|
|
{
|
|
uint8_t e5bhs{0}, e1bhs{0};
|
|
bool e5bdvs{false}, e1bdvs{false};
|
|
bool disturb1{false}, disturb2{false}, disturb3{false}, disturb4{false}, disturb5{false};
|
|
uint16_t wn{0};
|
|
uint32_t tow{0}; // "last seen"
|
|
uint32_t a0{0}, a1{0};
|
|
int el{0}, db{0};
|
|
map<int, SVIOD> iods;
|
|
void addWord(std::basic_string_view<uint8_t> page);
|
|
bool completeIOD() const;
|
|
uint16_t getIOD() const;
|
|
SVIOD liveIOD() const;
|
|
};
|
|
|
|
bool SVStat::completeIOD() const
|
|
{
|
|
for(const auto& iod : iods)
|
|
if(iod.second.complete())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
uint16_t SVStat::getIOD() const
|
|
{
|
|
for(const auto& iod : iods)
|
|
if(iod.second.complete())
|
|
return iod.first;
|
|
throw std::runtime_error("Asked for unknown IOD");
|
|
}
|
|
|
|
SVIOD SVStat::liveIOD() const
|
|
{
|
|
if(auto iter = iods.find(getIOD()); iter != iods.end())
|
|
return iter->second;
|
|
throw std::runtime_error("Asked for unknown IOD");
|
|
}
|
|
|
|
void SVStat::addWord(std::basic_string_view<uint8_t> page)
|
|
{
|
|
uint8_t wtype = getbitu(&page[0], 0, 6);
|
|
if(wtype >=1 && wtype <= 4) { // ephemeris
|
|
uint16_t iod = getbitu(&page[0], 6, 10);
|
|
iods[iod].addWord(page);
|
|
if(iods[iod].complete()) {
|
|
SVIOD latest = iods[iod];
|
|
iods.clear();
|
|
iods[iod] = latest;
|
|
}
|
|
}
|
|
else if(wtype==5) { // disturbance, health, time
|
|
e5bhs = getbitu(&page[0], 67, 2);
|
|
e1bhs = getbitu(&page[0], 69, 2);
|
|
e5bdvs = getbitu(&page[0], 71, 1);
|
|
e1bdvs = getbitu(&page[0], 72, 1);
|
|
wn = getbitu(&page[0], 73, 12);
|
|
tow = getbitu(&page[0], 85, 20);
|
|
// cout<<"Setting tow to "<<tow<<endl;
|
|
}
|
|
|
|
}
|
|
|
|
struct WinKeeper
|
|
{
|
|
WinKeeper();
|
|
struct Window
|
|
{
|
|
WINDOW *header, *text;
|
|
int sv;
|
|
void setHeader(std::string_view line)
|
|
{
|
|
wclear(header);
|
|
wmove(header, 0, 0);
|
|
wattron(header, A_BOLD | A_UNDERLINE);
|
|
wprintw(header, "%s", &line[0]);
|
|
wattroff(header, A_BOLD | A_UNDERLINE);
|
|
wrefresh(header);
|
|
}
|
|
void setStatus(std::string_view line)
|
|
{
|
|
wmove(header, 1, 0);
|
|
wattron(header, A_BOLD);
|
|
wprintw(header, "%s", &line[0]);
|
|
wattroff(header, A_BOLD);
|
|
wrefresh(header);
|
|
}
|
|
|
|
void emitLine(std::string_view line)
|
|
{
|
|
wprintw(text, "%s\n", &line[0]);
|
|
wrefresh(text);
|
|
}
|
|
};
|
|
vector<Window> d_windows;
|
|
static int d_h, d_w;
|
|
void emitLine(int sv, std::string_view line);
|
|
void setStatus(int sv, std::string_view line);
|
|
};
|
|
|
|
int WinKeeper::d_h;
|
|
int WinKeeper::d_w;
|
|
|
|
WinKeeper::WinKeeper()
|
|
{
|
|
initscr();
|
|
getmaxyx(stdscr, d_h, d_w);
|
|
|
|
int winwidth = d_w / 7;
|
|
for(int n=0; n < 8 ; ++n) {
|
|
d_windows.push_back({
|
|
newwin(3, winwidth, 0, n*(winwidth+2)),
|
|
newwin(d_h-3, winwidth, 3, n*(winwidth+2)),
|
|
0});
|
|
scrollok(d_windows[n].text, 1);
|
|
}
|
|
};
|
|
|
|
void WinKeeper::emitLine(int sv, std::string_view line)
|
|
{
|
|
for(auto& w: d_windows) {
|
|
if(w.sv == sv) {
|
|
w.emitLine(line);
|
|
return;
|
|
}
|
|
}
|
|
// nothing matched
|
|
for(auto& w: d_windows) {
|
|
if(!w.sv) {
|
|
w.sv = sv;
|
|
w.setHeader(std::to_string(sv));
|
|
w.emitLine(line);
|
|
return;
|
|
}
|
|
}
|
|
throw std::runtime_error("Ran out of windows searching for sv "+std::to_string(sv));
|
|
}
|
|
|
|
void WinKeeper::setStatus(int sv, std::string_view line)
|
|
{
|
|
for(auto& w: d_windows) {
|
|
if(w.sv == sv) {
|
|
w.setStatus(line);
|
|
return;
|
|
}
|
|
}
|
|
// nothing matched
|
|
for(auto& w: d_windows) {
|
|
if(!w.sv) {
|
|
w.sv = sv;
|
|
w.setHeader(std::to_string(sv));
|
|
w.setStatus(line);
|
|
return;
|
|
}
|
|
}
|
|
throw std::runtime_error("Ran out of windows searching for sv "+std::to_string(sv));
|
|
}
|
|
|
|
|
|
std::map<int, SVStat> g_svstats;
|
|
|
|
int main()
|
|
try
|
|
{
|
|
WinKeeper wk;
|
|
|
|
// unsigned int tow=0, wn=0;
|
|
ofstream csv("iod.csv");
|
|
ofstream csv2("toe.csv");
|
|
csv<<"timestamp sv iod sisa"<<endl;
|
|
csv2<<"timestamp sv tow toe"<<endl;
|
|
int tow=0, wn=0;
|
|
string line;
|
|
for(;;) {
|
|
auto c = getUint8();
|
|
if(c != 0xb5) {
|
|
// cout << (char)c;
|
|
line.append(1,c);
|
|
if(c=='\n') {
|
|
if(line.rfind("$GAGSV", 0)==0) {
|
|
vector<string> strs;
|
|
boost::split(strs,line,boost::is_any_of(","));
|
|
for(unsigned int n=4; n + 4 < strs.size(); n += 4) {
|
|
int sv = atoi(strs[n].c_str());
|
|
|
|
g_svstats[sv].el = atoi(strs[n+1].c_str());
|
|
g_svstats[sv].db = atoi(strs[n+3].c_str());
|
|
|
|
}
|
|
}
|
|
line.clear();
|
|
}
|
|
continue;
|
|
}
|
|
c = getUint8();
|
|
if(c != 0x62) {
|
|
ungetc(c, stdin); // might be 0xb5
|
|
continue;
|
|
}
|
|
|
|
// if we are here, just had ubx header
|
|
|
|
uint8_t ubxClass = getUint8();
|
|
uint8_t ubxType = getUint8();
|
|
uint16_t msgLen = getUint16();
|
|
|
|
// cout <<"Had an ubx message of class "<<(int) ubxClass <<" and type "<< (int) ubxType << " of " << msgLen <<" bytes"<<endl;
|
|
|
|
std::basic_string<uint8_t> msg;
|
|
msg.reserve(msgLen);
|
|
for(int n=0; n < msgLen; ++n)
|
|
msg.append(1, getUint8());
|
|
|
|
uint16_t ubxChecksum = getUint16();
|
|
if(ubxChecksum != calcUbxChecksum(ubxClass, ubxType, msg)) {
|
|
cout<<"Checksum: "<<ubxChecksum<< ", calculated: "<<
|
|
calcUbxChecksum(ubxClass, ubxType, msg)<<"\n";
|
|
}
|
|
|
|
if(ubxClass == 2 && ubxType == 89) { // SAR
|
|
string hexstring;
|
|
for(int n = 0; n < 15; ++n)
|
|
hexstring+=fmt::format("%x", (int)getbitu(msg.c_str(), 36 + 4*n, 4));
|
|
|
|
int sv = (int)msg[2];
|
|
wk.emitLine(sv, "SAR "+hexstring);
|
|
// cout<<"SAR: sv = "<< (int)msg[2] <<" ";
|
|
// for(int n=4; n < 12; ++n)
|
|
// fmt::printf("%02x", (int)msg[n]);
|
|
|
|
// for(int n = 0; n < 15; ++n)
|
|
// fmt::printf("%x", (int)getbitu(msg.c_str(), 36 + 4*n, 4));
|
|
|
|
// cout << " Type: "<< (int) msg[12] <<"\n";
|
|
// cout<<"Parameter: (len = "<<msg.length()<<") ";
|
|
// for(unsigned int n = 13; n < msg.length(); ++n)
|
|
// fmt::printf("%02x ", (int)msg[n]);
|
|
// cout<<"\n";
|
|
}
|
|
if(ubxClass == 2 && ubxType == 19) { //UBX-RXM-SFRBX
|
|
// cout<<"SFRBX GNSSID "<< (int)msg[0]<<", SV "<< (int)msg[1];
|
|
// cout<<" words "<< (int)msg[4]<<", version "<< (int)msg[6];
|
|
// cout<<"\n";
|
|
if(msg[0] != 2) // version field
|
|
continue;
|
|
// 7 is reserved
|
|
|
|
// cout << ((msg[8]&128) ? "Even " : "Odd ");
|
|
// cout << ((msg[8]&64) ? "Alert " : "Nominal ");
|
|
unsigned int wtype = (int)(msg[11] & (~(64+128)));
|
|
unsigned int sv = (int)msg[1];
|
|
// cout << "Word type "<< (int)(msg[11] & (~(64+128))) <<" SV " << (int)msg[1]<<"\n";
|
|
// for(unsigned int n = 8; n < msg.size() ; ++n) {
|
|
// fmt::printf("%02x ", msg[n]);
|
|
// }
|
|
// cout<<"\n";
|
|
std::basic_string<uint8_t> payload;
|
|
for(unsigned int i = 0 ; i < (msg.size() - 8) / 4; ++i)
|
|
for(int j=1; j <= 4; ++j)
|
|
payload.append(1, msg[8 + (i+1) * 4 -j]);
|
|
|
|
// for(auto& c : payload)
|
|
// fmt::printf("%02x ", c);
|
|
|
|
// cout<<"\n";
|
|
|
|
std::basic_string<uint8_t> inav;
|
|
unsigned int i,j;
|
|
for (i=0,j=2; i<14; i++, j+=8)
|
|
inav.append(1, (unsigned char)getbitu(payload.c_str() ,j,8));
|
|
for (i=0,j=2; i< 2; i++, j+=8)
|
|
inav.append(1, (unsigned char)getbitu(payload.c_str()+16,j,8));
|
|
|
|
// cout<<"inav for "<<wtype<<" for sv "<<sv<<": ";
|
|
// for(auto& c : inav)
|
|
// fmt::printf("%02x ", c);
|
|
|
|
g_svstats[sv].addWord(inav);
|
|
if(wtype >=1 && wtype <= 4) { // ephemeris
|
|
uint16_t iod = getbitu(&inav[0], 6, 10);
|
|
if(wtype == 1 && tow) {
|
|
int t0e = 60*getbitu(&inav[0], 16, 14);
|
|
int age = (tow - t0e)/60;
|
|
uint32_t e = getbitu(&inav[0], 6+10+14+32, 32);
|
|
wk.emitLine(sv, "Eph" +std::to_string(wtype)+", e=" + to_string(e)+", age="+to_string(age)+"m" );
|
|
}
|
|
else if(wtype == 3) {
|
|
unsigned int sisa = getbitu(&inav[0], 120, 8);
|
|
wk.emitLine(sv, "Eph3, iod="+to_string(iod)+", sisa="+to_string(sisa));
|
|
}
|
|
else
|
|
wk.emitLine(sv, "Eph" +std::to_string(wtype)+", iod=" + to_string(iod));
|
|
}
|
|
else if(!wtype) {
|
|
wk.emitLine(sv, ".");
|
|
if(getbitu(&inav[0], 6,2) == 2) {
|
|
wn = getbitu(&inav[0], 96, 12);
|
|
tow = getbitu(&inav[0], 108, 20);
|
|
}
|
|
}
|
|
else if(wtype == 5) {
|
|
string out="IonoBGDHealth ";
|
|
for(int n=0; n <5; ++n)
|
|
out.append(1, getbitu(&inav[0], 42+n, 1) ? '1' : '0');
|
|
out+=" ";
|
|
out += to_string(getbitu(&inav[0], 67,2))+to_string(getbitu(&inav[0], 69,2))+to_string(getbitu(&inav[0], 70,1))+to_string(getbitu(&inav[0], 71,1));
|
|
wk.emitLine(sv, out);
|
|
wk.setStatus(sv, "Hlth: "+std::to_string(getbitu(&inav[0], 67, 2)) +", el="+to_string(g_svstats[sv].el)+", db="+to_string(g_svstats[sv].db) );
|
|
tow = getbitu(&inav[0], 85, 20);
|
|
wn = getbitu(&inav[0], 73, 12);
|
|
}
|
|
else if(wtype == 6) {
|
|
int a0 = getbits(&inav[0], 6, 32);
|
|
int a1 = getbits(&inav[0], 38, 24);
|
|
int t0t = getbitu(&inav[0], 70, 8);
|
|
uint8_t wn0t = getbits(&inav[0], 78, 8);
|
|
int dw = (uint8_t)wn - wn0t;
|
|
if(tow && wn)
|
|
wk.emitLine(sv, "GST-UTC6, a0="+to_string(a0)+", a1="+to_string(a1)+", age="+to_string(tow/3600-t0t)+"h, dw="+to_string(dw)
|
|
+", wn0t="+to_string(wn0t)+", wn8="+to_string(wn&0xff));
|
|
else
|
|
wk.emitLine(sv, "GST-UTC6, a0="+to_string(a0)+", a1="+to_string(a1));
|
|
|
|
}
|
|
else if(wtype >= 7 && wtype <= 9) {
|
|
uint16_t ioda = getbitu(&inav[0], 6, 4);
|
|
wk.emitLine(sv, "Alm"+to_string(wtype)+" IODa="+to_string(ioda));
|
|
}
|
|
else if(wtype == 10) {
|
|
int a0g = getbits(&inav[0], 86, 16);
|
|
int a1g = getbits(&inav[0], 102, 12);
|
|
int t0g = getbitu(&inav[0], 114, 8);
|
|
uint8_t wn0g = getbitu(&inav[0], 122, 6);
|
|
int dw = (((uint8_t)wn)&(1+2+4+8+16+32)) - wn0g;
|
|
|
|
if(tow && wn) {
|
|
time_t t = 935280000 + wn * 7*86400 + tow;
|
|
struct tm tm;
|
|
gmtime_r(&t, &tm);
|
|
|
|
int age = tow - t0g * 3600;
|
|
// a0g = 2^-32 s, a1 = 2^-50 s/s
|
|
|
|
// int shift = a0g * (1U<<16) + a1g * age; // in 2^-51 seconds units
|
|
|
|
wk.emitLine(sv, "GST-GPS, a0g="+to_string(a0g)+", a1g="+to_string(a1g)+", t0g="+to_string(t0g)+", age="+to_string(tow/3600-t0g)+"h, dw="+to_string(dw)
|
|
+", wn0g="+to_string(wn0g)+", wn6="+to_string(wn&(1+2+4+8+16+32)));
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
wk.emitLine(sv, "Word "+std::to_string(wtype));
|
|
|
|
// time_t t = 935280000 + wn * 7*86400 + tow;
|
|
/*
|
|
for(const auto& ent : g_svstats) {
|
|
// 12 iod t0e
|
|
fmt::printf("%2d\t", ent.first);
|
|
if(ent.second.completeIOD()) {
|
|
cout << ent.second.getIOD() << "\t" << ( ent.second.tow - ent.second.liveIOD().t0e ) << "\t" << (unsigned int)ent.second.liveIOD().sisa;
|
|
}
|
|
cout<<"\n";
|
|
}
|
|
cout<<"\n";
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
catch(EofException& e)
|
|
{}
|