821 lines
27 KiB
C++
821 lines
27 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>
|
|
#include <thread>
|
|
#include <signal.h>
|
|
#include "ext/powerblog/h2o-pp.hh"
|
|
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;
|
|
}
|
|
|
|
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 */
|
|
}
|
|
|
|
void setbitu(unsigned char *buff, int pos, int len, unsigned int data)
|
|
{
|
|
unsigned int mask=1u<<(len-1);
|
|
int i;
|
|
if (len<=0||32<len) return;
|
|
for (i=pos;i<pos+len;i++,mask>>=1) {
|
|
if (data&mask) buff[i/8]|=1u<<(7-i%8); else buff[i/8]&=~(1u<<(7-i%8));
|
|
}
|
|
}
|
|
static const unsigned int tbl_CRC24Q[]={
|
|
0x000000,0x864CFB,0x8AD50D,0x0C99F6,0x93E6E1,0x15AA1A,0x1933EC,0x9F7F17,
|
|
0xA18139,0x27CDC2,0x2B5434,0xAD18CF,0x3267D8,0xB42B23,0xB8B2D5,0x3EFE2E,
|
|
0xC54E89,0x430272,0x4F9B84,0xC9D77F,0x56A868,0xD0E493,0xDC7D65,0x5A319E,
|
|
0x64CFB0,0xE2834B,0xEE1ABD,0x685646,0xF72951,0x7165AA,0x7DFC5C,0xFBB0A7,
|
|
0x0CD1E9,0x8A9D12,0x8604E4,0x00481F,0x9F3708,0x197BF3,0x15E205,0x93AEFE,
|
|
0xAD50D0,0x2B1C2B,0x2785DD,0xA1C926,0x3EB631,0xB8FACA,0xB4633C,0x322FC7,
|
|
0xC99F60,0x4FD39B,0x434A6D,0xC50696,0x5A7981,0xDC357A,0xD0AC8C,0x56E077,
|
|
0x681E59,0xEE52A2,0xE2CB54,0x6487AF,0xFBF8B8,0x7DB443,0x712DB5,0xF7614E,
|
|
0x19A3D2,0x9FEF29,0x9376DF,0x153A24,0x8A4533,0x0C09C8,0x00903E,0x86DCC5,
|
|
0xB822EB,0x3E6E10,0x32F7E6,0xB4BB1D,0x2BC40A,0xAD88F1,0xA11107,0x275DFC,
|
|
0xDCED5B,0x5AA1A0,0x563856,0xD074AD,0x4F0BBA,0xC94741,0xC5DEB7,0x43924C,
|
|
0x7D6C62,0xFB2099,0xF7B96F,0x71F594,0xEE8A83,0x68C678,0x645F8E,0xE21375,
|
|
0x15723B,0x933EC0,0x9FA736,0x19EBCD,0x8694DA,0x00D821,0x0C41D7,0x8A0D2C,
|
|
0xB4F302,0x32BFF9,0x3E260F,0xB86AF4,0x2715E3,0xA15918,0xADC0EE,0x2B8C15,
|
|
0xD03CB2,0x567049,0x5AE9BF,0xDCA544,0x43DA53,0xC596A8,0xC90F5E,0x4F43A5,
|
|
0x71BD8B,0xF7F170,0xFB6886,0x7D247D,0xE25B6A,0x641791,0x688E67,0xEEC29C,
|
|
0x3347A4,0xB50B5F,0xB992A9,0x3FDE52,0xA0A145,0x26EDBE,0x2A7448,0xAC38B3,
|
|
0x92C69D,0x148A66,0x181390,0x9E5F6B,0x01207C,0x876C87,0x8BF571,0x0DB98A,
|
|
0xF6092D,0x7045D6,0x7CDC20,0xFA90DB,0x65EFCC,0xE3A337,0xEF3AC1,0x69763A,
|
|
0x578814,0xD1C4EF,0xDD5D19,0x5B11E2,0xC46EF5,0x42220E,0x4EBBF8,0xC8F703,
|
|
0x3F964D,0xB9DAB6,0xB54340,0x330FBB,0xAC70AC,0x2A3C57,0x26A5A1,0xA0E95A,
|
|
0x9E1774,0x185B8F,0x14C279,0x928E82,0x0DF195,0x8BBD6E,0x872498,0x016863,
|
|
0xFAD8C4,0x7C943F,0x700DC9,0xF64132,0x693E25,0xEF72DE,0xE3EB28,0x65A7D3,
|
|
0x5B59FD,0xDD1506,0xD18CF0,0x57C00B,0xC8BF1C,0x4EF3E7,0x426A11,0xC426EA,
|
|
0x2AE476,0xACA88D,0xA0317B,0x267D80,0xB90297,0x3F4E6C,0x33D79A,0xB59B61,
|
|
0x8B654F,0x0D29B4,0x01B042,0x87FCB9,0x1883AE,0x9ECF55,0x9256A3,0x141A58,
|
|
0xEFAAFF,0x69E604,0x657FF2,0xE33309,0x7C4C1E,0xFA00E5,0xF69913,0x70D5E8,
|
|
0x4E2BC6,0xC8673D,0xC4FECB,0x42B230,0xDDCD27,0x5B81DC,0x57182A,0xD154D1,
|
|
0x26359F,0xA07964,0xACE092,0x2AAC69,0xB5D37E,0x339F85,0x3F0673,0xB94A88,
|
|
0x87B4A6,0x01F85D,0x0D61AB,0x8B2D50,0x145247,0x921EBC,0x9E874A,0x18CBB1,
|
|
0xE37B16,0x6537ED,0x69AE1B,0xEFE2E0,0x709DF7,0xF6D10C,0xFA48FA,0x7C0401,
|
|
0x42FA2F,0xC4B6D4,0xC82F22,0x4E63D9,0xD11CCE,0x575035,0x5BC9C3,0xDD8538
|
|
};
|
|
|
|
|
|
/* crc-24q parity --------------------------------------------------------------
|
|
* compute crc-24q parity for sbas, rtcm3
|
|
* args : unsigned char *buff I data
|
|
* int len I data length (bytes)
|
|
* return : crc-24Q parity
|
|
* notes : see reference [2] A.4.3.3 Parity
|
|
*-----------------------------------------------------------------------------*/
|
|
unsigned int rtk_crc24q(const unsigned char *buff, int len)
|
|
{
|
|
unsigned int crc=0;
|
|
int i;
|
|
|
|
for (i=0;i<len;i++) crc=((crc<<8)&0xFFFFFF)^tbl_CRC24Q[(crc>>16)^buff[i]];
|
|
return crc;
|
|
}
|
|
|
|
string humanSisa(uint8_t sisa)
|
|
{
|
|
unsigned int sval = sisa;
|
|
if(sisa < 50)
|
|
return std::to_string(sval)+" cm";
|
|
if(sisa < 75)
|
|
return std::to_string(50 + 2* (sval-50))+" cm";
|
|
if(sisa < 100)
|
|
return std::to_string(100 + 4*(sval-75))+" cm";
|
|
if(sisa < 125)
|
|
return std::to_string(200 + 16*(sval-100))+" cm";
|
|
if(sisa < 255)
|
|
return "SPARE";
|
|
return "NO SIS AVAILABLE";
|
|
}
|
|
|
|
|
|
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;
|
|
uint32_t e, sqrtA;
|
|
int32_t m0, omega0, i0, omega, idot, omegadot, deltan;
|
|
|
|
int16_t cuc{0}, cus{0}, crc{0}, crs{0}, cic{0}, cis{0};
|
|
|
|
uint8_t sisa;
|
|
|
|
uint32_t wn{0}, tow{0};
|
|
bool complete() const
|
|
{
|
|
return words[1] && words[2] && words[3] && words[4];
|
|
}
|
|
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 = getbitu(&page[0], 16, 14);
|
|
m0 = getbits(&page[0], 30, 32);
|
|
e = getbitu(&page[0], 62, 32);
|
|
sqrtA = getbitu(&page[0], 94, 32);
|
|
}
|
|
else if(wtype == 2) {
|
|
omega0 = getbits(&page[0], 16, 32);
|
|
i0 = getbits(&page[0], 48, 32);
|
|
omega = getbits(&page[0], 80, 32);
|
|
idot = getbits(&page[0], 112, 14);
|
|
}
|
|
else if(wtype == 3) {
|
|
omegadot = getbits(&page[0], 16, 24);
|
|
deltan = getbits(&page[0], 40, 16);
|
|
cuc = getbits(&page[0], 56, 16);
|
|
cus = getbits(&page[0], 72, 16);
|
|
crc = getbits(&page[0], 88, 16);
|
|
crs = getbits(&page[0], 104, 16);
|
|
sisa = getbitu(&page[0], 120, 8);
|
|
}
|
|
else if(wtype == 4) {
|
|
cic = getbits(&page[0], 22, 16);
|
|
cis = getbits(&page[0], 38, 16);
|
|
/*
|
|
t0c = getbitu(&page[0], 54, 14);
|
|
af0 = getbitu(&page[0], 68, 31);
|
|
af1 = getbitu(&page[0], 99, 21);
|
|
af2 = getbitu(&page[0], 120, 6);
|
|
*/
|
|
}
|
|
|
|
}
|
|
|
|
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}, t0t{0}, wn0t{0};
|
|
int16_t el{-1}, db{-1};
|
|
map<int, SVIOD> iods;
|
|
void addWord(std::basic_string_view<uint8_t> page);
|
|
bool completeIOD() const;
|
|
uint16_t getIOD() const;
|
|
SVIOD liveIOD() const;
|
|
|
|
pair<int,SVIOD> prevIOD{-1, SVIOD()};
|
|
void clearPrev()
|
|
{
|
|
prevIOD.first = -1;
|
|
}
|
|
};
|
|
|
|
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 == 0) {
|
|
if(getbitu(&page[0], 6,2) == 2) {
|
|
wn = getbitu(&page[0], 96, 12);
|
|
tow = getbitu(&page[0], 108, 20);
|
|
}
|
|
|
|
}
|
|
else if(wtype >=1 && wtype <= 4) { // ephemeris
|
|
uint16_t iod = getbitu(&page[0], 6, 10);
|
|
iods[iod].addWord(page);
|
|
|
|
if(iods[iod].complete()) {
|
|
for(const auto& i : iods) {
|
|
if(i.first != iod && i.second.complete())
|
|
prevIOD=i;
|
|
}
|
|
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;
|
|
}
|
|
else if(wtype == 6) {
|
|
a0 = getbits(&page[0], 6, 32);
|
|
a1 = getbits(&page[0], 38, 24);
|
|
t0t = getbitu(&page[0], 70, 8);
|
|
wn0t = getbits(&page[0], 78, 8);
|
|
|
|
}
|
|
}
|
|
|
|
double todeg(double rad)
|
|
{
|
|
return 360 * rad/(2*M_PI);
|
|
}
|
|
|
|
void getCoordinates(int wn, int tow, const SVIOD& iod, double* x, double* y, double* z, bool quiet=true)
|
|
{
|
|
// here goes
|
|
|
|
constexpr double mu = 3.986004418 * pow(10.0, 14.0);
|
|
constexpr double omegaE = 7.2921151467 * pow(10.0, -5);
|
|
|
|
double sqrtA = 1.0*iod.sqrtA / (1ULL<<19);
|
|
double deltan = M_PI * 1.0*iod.deltan / (1ULL<<43);
|
|
double t0e = 60.0*iod.t0e;
|
|
double m0 = M_PI * 1.0*iod.m0 / (1ULL<<31);
|
|
double e = 1.0*iod.e / (1ULL<<33);
|
|
double omega = M_PI * 1.0*iod.omega / (1ULL<<31);
|
|
|
|
double cuc = 1.0*iod.cuc / (1ULL<<29);
|
|
double cus = 1.0*iod.cus / (1ULL<<29);
|
|
|
|
double crc = 1.0*iod.crc / (1ULL<<5);
|
|
double crs = 1.0*iod.crs / (1ULL<<5);
|
|
|
|
double cic = 1.0*iod.cic / (1ULL<<29);
|
|
double cis = 1.0*iod.cis / (1ULL<<29);
|
|
|
|
double idot = M_PI * 1.0*iod.idot / (1ULL<<43);
|
|
double i0 = M_PI * 1.0*iod.i0 / (1ULL << 31);
|
|
|
|
double Omegadot = M_PI * 1.0*iod.omegadot / (1ULL << 43);
|
|
double Omega0 = M_PI * 1.0*iod.omega0 / (1ULL << 31);
|
|
|
|
// NO IOD BEYOND THIS POINT!
|
|
if(!quiet) {
|
|
cout << "sqrtA = "<< sqrtA << endl;
|
|
cout << "deltan = "<< deltan << endl;
|
|
cout << "t0e = "<< t0e << endl;
|
|
cout << "m0 = "<< m0 << " ("<<todeg(m0)<<")"<<endl;
|
|
cout << "e = "<< e << endl;
|
|
cout << "omega = " << omega << " ("<<todeg(omega)<<")"<<endl;
|
|
cout << "idot = " << idot <<endl;
|
|
cout << "i0 = " << i0 << " ("<<todeg(i0)<<")"<<endl;
|
|
|
|
cout << "cuc = " << cuc << endl;
|
|
cout << "cus = " << cus << endl;
|
|
|
|
cout << "crc = " << crc << endl;
|
|
cout << "crs = " << crs << endl;
|
|
|
|
cout << "cic = " << cic << endl;
|
|
cout << "cis = " << cis << endl;
|
|
|
|
cout << "Omega0 = " << Omega0 << " ("<<todeg(Omega0)<<")"<<endl;
|
|
cout << "Omegadot = " << Omegadot << " ("<<todeg(Omegadot)<< ")"<<endl;
|
|
|
|
}
|
|
|
|
double A = pow(sqrtA, 2.0);
|
|
double A3 = pow(sqrtA, 6.0);
|
|
|
|
double n0 = sqrt(mu/A3);
|
|
double tk = tow - t0e; // in seconds, ignores WN!
|
|
|
|
double n = n0 + deltan;
|
|
if(!quiet)
|
|
cout <<"tk: "<<tk<<", n0: "<<n0<<", deltan: "<<deltan<<", n: "<<n<<endl;
|
|
|
|
double M = m0 + n * tk;
|
|
if(!quiet)
|
|
cout << " M = m0 + n * tk = "<<m0 << " + " << n << " * " << tk <<endl;
|
|
double E = M;
|
|
for(int k =0 ; k < 10; ++k) {
|
|
if(!quiet)
|
|
cout<<"M = "<<M<<", E = "<< E << endl;
|
|
E = M + e * sin(E);
|
|
}
|
|
|
|
// M = E - e * sin(E) -> E = M + e * sin(E)
|
|
|
|
|
|
double nu = atan( ((sqrt(1-e*e) * sin(E)) / (1 - e * cos(E)) ) /
|
|
((cos(E) - e)/ (1-e*cos(E)))
|
|
);
|
|
|
|
double nu1 = atan( (sqrt(1-e*e) * sin(E)) /
|
|
(cos(E) - e)
|
|
);
|
|
|
|
double nu2 = M + e*2*sin(M) +
|
|
e *e * (5.0/4.0) * sin(2*M) -
|
|
e*e*e * (0.25*sin(M) - (13.0/12.0)*sin(3*M));
|
|
|
|
double nu3 = 2* atan( sqrt((1+e)/(1-e)) * tan(E/2));
|
|
|
|
if(!quiet) {
|
|
cout <<" nu sis: "<<nu<< " / +pi = " << nu +M_PI << endl;
|
|
cout <<" nu ?: "<<nu1<< " / +pi = " << nu1 +M_PI << endl;
|
|
cout <<" nu fourier/esa: "<<nu2<< " / +pi = "<<nu2+M_PI << endl;
|
|
cout <<" nu wikipedia: "<<nu3<< " / +pi = " <<nu3 +M_PI << endl;
|
|
}
|
|
|
|
nu = nu2;
|
|
|
|
// https://en.wikipedia.org/wiki/True_anomaly is good
|
|
|
|
double psi = nu + omega;
|
|
if(!quiet) {
|
|
cout<<"psi = nu + omega = " << nu <<" + "<<omega<< " = " << psi << "\n";
|
|
}
|
|
|
|
|
|
double deltau = cus * sin(2*psi) + cuc * cos(2*psi);
|
|
double deltar = crs * sin(2*psi) + crc * cos(2*psi);
|
|
double deltai = cis * sin(2*psi) + cic * cos(2*psi);
|
|
|
|
double u = psi + deltau;
|
|
|
|
double r = A * (1- e * cos(E)) + deltar;
|
|
|
|
|
|
|
|
|
|
double xprime = r*cos(u), yprime = r*sin(u);
|
|
if(!quiet) {
|
|
cout<<"u = psi + deltau = "<< psi <<" + " << deltau << " = "<<u<<"\n";
|
|
cout << "calculated r = "<< r << " (" << (r/1000.0) <<"km)"<<endl;
|
|
cout << "xprime: "<<xprime<<", yprime: "<<yprime<<endl;
|
|
}
|
|
double Omega = Omega0 + (Omegadot - omegaE)*tk - omegaE * t0e;
|
|
double i = i0 + deltai + idot * tk;
|
|
*x = xprime * cos(Omega) - yprime * cos(i) * sin(Omega);
|
|
*y = xprime * sin(Omega) + yprime * cos(i) * cos(Omega);
|
|
*z = yprime * sin(i);
|
|
|
|
if(!quiet)
|
|
cout << sqrt( (*x)*(*x) + (*y)*(*y) + (*z)*(*z)) << " calculated r "<<endl;
|
|
|
|
}
|
|
|
|
|
|
std::map<int, SVStat> g_svstats;
|
|
|
|
std::string humanTime(int wn, int tow)
|
|
{
|
|
time_t t = 935280000 + wn * 7*86400 + tow;
|
|
struct tm tm;
|
|
gmtime_r(&t, &tm);
|
|
|
|
char buffer[80];
|
|
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %T %z", &tm);
|
|
return buffer;
|
|
}
|
|
|
|
int* g_tow, *g_wn;
|
|
|
|
int main(int argc, char** argv)
|
|
try
|
|
{
|
|
signal(SIGPIPE, SIG_IGN);
|
|
H2OWebserver h2s("galmon");
|
|
|
|
int tow=0, wn=0;
|
|
g_tow = &tow;
|
|
g_wn = &wn;
|
|
|
|
h2s.addHandler("/svs", [](auto handler, auto req) {
|
|
nlohmann::json ret = nlohmann::json::object();
|
|
for(const auto& s: g_svstats)
|
|
if(s.second.completeIOD()) {
|
|
nlohmann::json item = nlohmann::json::object();
|
|
|
|
item["iod"]=s.second.getIOD();
|
|
item["sisa"]=humanSisa(s.second.liveIOD().sisa);
|
|
item["a0"]=s.second.a0;
|
|
item["a1"]=s.second.a1;
|
|
item["e5bdvs"]=s.second.e5bdvs;
|
|
item["e1bdvs"]=s.second.e1bdvs;
|
|
item["e5bhs"]=s.second.e5bhs;
|
|
item["e1bhs"]=s.second.e1bhs;
|
|
item["elev"]=s.second.el;
|
|
item["db"]=s.second.db;
|
|
item["eph-age-m"] = (*g_tow - 60*s.second.liveIOD().t0e)/60.0;
|
|
item["last-seen-s"] = s.second.tow ? (*g_tow - s.second.tow) : -1;
|
|
|
|
/* Our location:
|
|
X : 3922.505 km
|
|
Y : 290.116 km
|
|
Z : 5004.189 km
|
|
*/
|
|
|
|
double ourx = 3922.505 * 1000;
|
|
double oury = 290.116 * 1000;
|
|
double ourz = 5004.189 * 1000;
|
|
double x, y, z;
|
|
// cout<<"For sv " <<s.first<<" at "<<humanTime(*g_wn, *g_tow)<<": \n";
|
|
getCoordinates(*g_wn, *g_tow, s.second.liveIOD(), &x, &y, &z);
|
|
|
|
double dx = x-ourx, dy = y-oury, dz = z-ourz;
|
|
|
|
/* https://gis.stackexchange.com/questions/58923/calculating-view-angle
|
|
to calculate elevation:
|
|
Cos(elevation) = (x*dx + y*dy + z*dz) / Sqrt((x^2+y^2+z^2)*(dx^2+dy^2+dz^2))
|
|
Obtain its principal inverse cosine. Subtract this from 90 degrees if you want
|
|
the angle of view relative to a nominal horizon. This is the "elevation."
|
|
NOTE! x = you on the ground!
|
|
*/
|
|
double elev = acos ( (ourx*dx + oury*dy + ourz*dz) / (sqrt(ourx*ourx + oury*oury + ourz*ourz) * sqrt(dx*dx + dy*dy + dz*dz)));
|
|
|
|
double deg = 180.0* (elev/M_PI);
|
|
|
|
item["calc-elev"] = 90 - deg;
|
|
/*
|
|
cout<<s.first<<" calc elev radians "<< elev << ", deg "<< deg <<" calc-elev "<<(90-deg)<<", from ublox "<< s.second.el<<endl <<std::fixed<<" (" << ourx << ", "<< oury <<", "<<ourz<<") -> ("
|
|
<< x << ", "<< y <<", "<< z<<") " << sqrt(ourx*ourx + oury*oury + ourz*ourz) <<" - " << sqrt(x*x+y*y+z*z) <<endl;
|
|
*/
|
|
item["x"]=x;
|
|
item["y"]=y;
|
|
item["z"]=z;
|
|
item["wn"]=*g_wn;
|
|
item["tow"]=*g_tow;
|
|
ret[std::to_string(s.first)] = item;
|
|
}
|
|
return ret;
|
|
});
|
|
h2s.addDirectory("/", argc > 2 ? argv[2] : "./html/");
|
|
|
|
int port = argc > 1 ? atoi(argv[1]) : 29599;
|
|
std::thread ws([&h2s, port]() {
|
|
auto actx = h2s.addContext();
|
|
h2s.addListener(ComboAddress("::", port), actx);
|
|
cout<<"Listening on port "<< port <<endl;
|
|
h2s.runLoop();
|
|
});
|
|
ws.detach();
|
|
|
|
|
|
// 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;
|
|
ofstream gstutc("gstutc.csv");
|
|
gstutc << "timestamp sv tow t0t age rawshift nsecshift a0 a1" <<endl;
|
|
ofstream gstgps("gstgps.csv");
|
|
gstgps << "timestamp sv tow t0g age rawshift nsecshift a0g a1g" <<endl;
|
|
|
|
ofstream sisacsv("sisa.csv");
|
|
|
|
sisacsv << "timestamp sv sisa"<<endl;
|
|
|
|
|
|
string line;
|
|
for(;;) {
|
|
auto c = getUint8();
|
|
if(c != 0xb5) {
|
|
line.append(1, c);
|
|
// cout << (char)c;
|
|
/*
|
|
msgs num svs sv el az db sv el az db sv el az db sv el az db gnssid
|
|
$GAGSV,3, 1, 09, 02,31,073,34,07,84,296,42,08,40,081,37,12,03,340, ,0*7A
|
|
$GAGSV,3, 2, 09, 13,07,191,16,25,11,023, ,26,36,235,33,30,21,129,38,0*75
|
|
$GAGSV,3, 3, 09, 33,34,300,38,0*42
|
|
*/
|
|
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) {
|
|
g_svstats[atoi(strs[n].c_str())].el = atoi(strs[n+1].c_str());
|
|
if(g_svstats[atoi(strs[n].c_str())].el >= 0)
|
|
g_svstats[atoi(strs[n].c_str())].db = atoi(strs[n+3].c_str());
|
|
else
|
|
g_svstats[atoi(strs[n].c_str())].db = -1;
|
|
|
|
/*
|
|
cout<<"sv "<<atoi(strs[n].c_str())
|
|
<< " el "<< atoi(strs[n+1].c_str())
|
|
<< " db " << atoi(strs[n+3].c_str())
|
|
<<endl;
|
|
*/
|
|
}
|
|
}
|
|
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 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]);
|
|
|
|
|
|
/* test crc (4(pad) + 114 + 82 bits) */
|
|
unsigned char crc_buff[26]={0};
|
|
unsigned int i,j;
|
|
for (i=0,j= 4;i<15;i++,j+=8) setbitu(crc_buff,j,8,getbitu(payload.c_str() ,i*8,8));
|
|
for (i=0,j=118;i<11;i++,j+=8) setbitu(crc_buff,j,8,getbitu(payload.c_str()+16,i*8,8));
|
|
if (rtk_crc24q(crc_buff,25) != getbitu(payload.c_str()+16,82,24)) {
|
|
cout << "CRC mismatch, " << rtk_crc24q(crc_buff, 25) << " != " << getbitu(payload.c_str()+16,82,24) <<endl;
|
|
continue;
|
|
}
|
|
|
|
// for(auto& c : payload)
|
|
// fmt::printf("%02x ", c);
|
|
|
|
// cout<<"\n";
|
|
|
|
std::basic_string<uint8_t> inav;
|
|
|
|
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);
|
|
|
|
unsigned int wtype = getbitu(&inav[0], 0, 6);
|
|
|
|
g_svstats[sv].addWord(inav);
|
|
if(!wtype) {
|
|
if(getbitu(&inav[0], 6,2) == 2) {
|
|
wn = getbitu(&inav[0], 96, 12);
|
|
tow = getbitu(&inav[0], 108, 20);
|
|
}
|
|
}
|
|
else 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);
|
|
}
|
|
else if(wtype == 3) {
|
|
// unsigned int sisa = getbitu(&inav[0], 120, 8);
|
|
}
|
|
else
|
|
;
|
|
}
|
|
else if(wtype == 5) {
|
|
tow = getbitu(&inav[0], 85, 20);
|
|
wn = getbitu(&inav[0], 73, 12);
|
|
}
|
|
else if(wtype == 6) { // GST-UTC
|
|
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) {
|
|
|
|
int age = dw * 7 * 86400 + tow - t0t * 3600;
|
|
// a0 = 2^-30 s, a1 = 2^-50 s/s
|
|
|
|
int shift = a0 * (1U<<20) + a1 * age; // in 2^-50 seconds units
|
|
time_t t = 935280000 + wn * 7*86400 + tow;
|
|
gstutc << t << " " << sv <<" " << tow << " " << (t0t * 3600) << " "<< age <<" " <<shift << " " << shift * pow(2, -50) * 1000000000 << " " << a0 <<" " << a1 << "\n";
|
|
|
|
cout<<humanTime(wn, tow)<<" sv "<<sv<< " GST-UTC6, a0="+to_string(a0)+", a1="+to_string(a1)+", age="+to_string(age/3600)+"h, dw="+to_string(dw)
|
|
+", wn0t="+to_string(wn0t)+", wn8="+to_string(wn&0xff)+"\n";
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
else
|
|
wk.emitLine(sv, "GST-UTC6, a0="+to_string(a0)+", a1="+to_string(a1));
|
|
*/
|
|
|
|
}
|
|
else if(wtype == 10) { // GSTT GPS
|
|
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) {
|
|
|
|
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
|
|
time_t t = 935280000 + wn * 7*86400 + tow;
|
|
gstgps << t << " " << sv <<" " << tow << " " << (t0g * 3600) <<" " << age <<" " <<shift << " " << shift * pow(2, -50) * 1000000000 << " " << a0g <<" " << a1g << "\n";
|
|
|
|
cout<<humanTime(wn, tow)<<" sv "<<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))+"\n";
|
|
}
|
|
}
|
|
|
|
for(auto& ent : g_svstats) {
|
|
// fmt::printf("%2d\t", ent.first);
|
|
if(ent.second.completeIOD() && ent.second.prevIOD.first >= 0) {
|
|
time_t t = 935280000 + wn * 7*86400 + tow;
|
|
sisacsv << t <<" " << ent.first << " " << (unsigned int) ent.second.liveIOD().sisa << "\n";
|
|
cout << t <<" " << ent.first << " " << (unsigned int) ent.second.liveIOD().sisa << "\n";
|
|
if(ent.second.liveIOD().sisa != ent.second.prevIOD.second.sisa) {
|
|
int age = tow - ent.second.prevIOD.second.t0e * 60;
|
|
cout<<humanTime(wn, tow)<<" sv "<<ent.first<<" changed sisa from "<<(unsigned int) ent.second.prevIOD.second.sisa<<" ("<<
|
|
humanSisa(ent.second.prevIOD.second.sisa)<<") to " << (unsigned int)ent.second.liveIOD().sisa << " ("<<
|
|
humanSisa(ent.second.liveIOD().sisa)<<"), lastseen = "<< (age/3600.0) <<"h"<<endl;
|
|
}
|
|
ent.second.clearPrev();
|
|
}
|
|
|
|
if(0 && ent.second.completeIOD() && ent.second.prevIOD.first >= 0) {
|
|
double x, y, z;
|
|
double oldx, oldy, oldz;
|
|
getCoordinates(wn, tow, ent.second.liveIOD(), &x, &y, &z);
|
|
cout << ent.first << ": iod= "<<ent.second.getIOD()<<" "<< x/1000.0 << ", "<< y/1000.0 <<", "<<z/1000.0<<endl;
|
|
|
|
cout<<"OLD: \n";
|
|
getCoordinates(wn, tow, ent.second.prevIOD.second, &oldx, &oldy, &oldz);
|
|
cout << ent.first << ": iod= "<<ent.second.prevIOD.first<<" "<< oldx/1000.0 << ", "<< oldy/1000.0 <<", "<<oldz/1000.0<<endl;
|
|
|
|
double hours = ((ent.second.liveIOD().t0e - ent.second.prevIOD.second.t0e)/60.0);
|
|
cout<<ent.first<<" discontinuity "<< hours<<"h old: "<<sqrt((x-oldx)*(x-oldx) + (y-oldy)*(y-oldy) + (z-oldz)*(z-oldz)) <<endl;
|
|
|
|
|
|
if(hours < 2) {
|
|
ofstream orbitcsv("orbit."+to_string(ent.first)+"."+to_string(ent.second.prevIOD.first)+"-"+to_string(ent.second.getIOD())+".csv");
|
|
|
|
orbitcsv << "timestamp x y z oldx oldy oldz\n";
|
|
orbitcsv << fixed;
|
|
for(int offset = -7200; offset < 7200; offset += 30) {
|
|
int t = ent.second.liveIOD().t0e * 60 + offset;
|
|
getCoordinates(wn, t, ent.second.liveIOD(), &x, &y, &z, false);
|
|
getCoordinates(wn, t, ent.second.prevIOD.second, &oldx, &oldy, &oldz);
|
|
time_t posix = 935280000 + wn * 7*86400 + t;
|
|
orbitcsv << posix <<" "
|
|
<<x<<" " <<y<<" "<<z<<" "
|
|
<<oldx<<" " <<oldy<<" "<<oldz<<"\n";
|
|
}
|
|
}
|
|
ent.second.clearPrev();
|
|
}
|
|
|
|
}
|
|
|
|
// 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)
|
|
{}
|