more bits

pull/1/head
bert hubert 2019-08-06 12:57:06 +02:00
parent 5f559da18b
commit 1b03c0941d
10 changed files with 937 additions and 236 deletions

View File

@ -1,6 +1,6 @@
CXXFLAGS:= -std=gnu++17 -Wall -O3 -MMD -MP -ggdb -fno-omit-frame-pointer -Iext/fmt-5.2.1/include/ -Iext/powerblog/ext/simplesocket -Iext/powerblog/ext/
PROGRAMS = ubxparse ubxdisplay minread
PROGRAMS = ubxparse ubxdisplay minread ubxtool
all: $(PROGRAMS)
@ -11,9 +11,12 @@ clean:
H2OPP=ext/powerblog/h2o-pp.o
SIMPLESOCKETS=ext/powerblog/ext/simplesocket/swrappers.o ext/powerblog/ext/simplesocket/sclasses.o ext/powerblog/ext/simplesocket/comboaddress.o
ubxparse: ubxparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS)
g++ -std=gnu++17 $^ -o $@ -pthread -lncurses -L/usr/local/lib -lh2o-evloop -lssl -lcrypto -lz
ubxparse: ubxparse.o ext/fmt-5.2.1/src/format.o $(H2OPP) $(SIMPLESOCKETS) minicurl.o ubx.o bits.o
g++ -std=gnu++17 $^ -o $@ -pthread -lncurses -L/usr/local/lib -lh2o-evloop -lssl -lcrypto -lz -lcurl
ubxdisplay: ubxdisplay.o ext/fmt-5.2.1/src/format.o
g++ -std=gnu++17 $^ -o $@ -pthread -lncurses
ubxtool: ubxtool.o ubx.o ext/fmt-5.2.1/src/format.o
g++ -std=gnu++17 $^ -o $@

77
bits.cc 100644
View File

@ -0,0 +1,77 @@
#include "bits.hh"
/* 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;
}

7
bits.hh 100644
View File

@ -0,0 +1,7 @@
#pragma once
/* lovingly lifted from RTK */
unsigned int getbitu(const unsigned char *buff, int pos, int len);
int getbits(const unsigned char *buff, int pos, int len);
void setbitu(unsigned char *buff, int pos, int len, unsigned int data);
unsigned int rtk_crc24q(const unsigned char *buff, int len);

View File

@ -1,5 +1,7 @@
var repeat;
moment.relativeTimeThreshold('m', 120);
function maketable(str, arr)
{
var table=d3.select(str);
@ -12,7 +14,7 @@ function maketable(str, arr)
enter().
append("tr");
var columns = ["sv", "iod", "eph-age-m", "sisa", "e1bhs", "e1bdvs", "e5bhs", "e5bdvs", "a0", "a1", "elev", "calc-elev", "db", "last-seen-s"];
var columns = ["sv", "iod", "eph-age-m", "sisa", "e1bhs", "e1bdvs", "e5bhs", "e5bdvs", "a0", "a1","a0g", "a1g", "calc-elev", "db", "last-seen-s"];
// append the header row
thead.append("tr")
@ -27,7 +29,7 @@ function maketable(str, arr)
return columns.map(function(column) {
var ret={};
ret.column = column;
ret.color="white";
ret.color=null;
if(column == "eph-age-m") {
var b = moment.duration(row[column], 'm');
ret.value = b.humanize();

180
minicurl.cc 100644
View File

@ -0,0 +1,180 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 powerdns.com bv
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "minicurl.hh"
#include <curl/curl.h>
#include <stdexcept>
#include <vector>
#include <boost/format.hpp> // XXX
void MiniCurl::init()
{
static std::atomic_flag s_init = ATOMIC_FLAG_INIT;
if (s_init.test_and_set())
return;
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
if (code != 0) {
throw std::runtime_error("Error initializing libcurl");
}
}
MiniCurl::MiniCurl(const string& useragent)
{
d_curl = curl_easy_init();
if (d_curl == nullptr) {
throw std::runtime_error("Error creating a MiniCurl session");
}
curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str());
}
MiniCurl::~MiniCurl()
{
// NEEDS TO CLEAN HOSTLIST
curl_easy_cleanup(d_curl);
}
size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
MiniCurl* us = (MiniCurl*)userdata;
us->d_data.append(ptr, size*nmemb);
return size*nmemb;
}
using namespace std;
static string extractHostFromURL(const std::string& url)
{
auto pos = url.find("://");
if(pos == string::npos)
throw std::runtime_error("Can't find host part of '"+url+"'");
pos += 3;
auto endpos = url.find('/', pos);
if(endpos == string::npos)
return url.substr(pos);
return url.substr(pos, endpos-pos);
}
void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
{
if(rem) {
struct curl_slist *hostlist = nullptr; // THIS SHOULD BE FREED
// url = http://hostname.enzo/url
string host4=extractHostFromURL(str);
// doest the host contain port indication
std::size_t found = host4.find(':');
vector<uint16_t> ports{80, 443};
if (found != std::string::npos) {
int port = std::stoi(host4.substr(found + 1));
if (port <= 0 || port > 65535)
throw std::overflow_error("Invalid port number");
ports = {(uint16_t)port};
host4 = host4.substr(0, found);
}
for (const auto& port : ports) {
string hcode = boost::str(boost::format("%s:%u:%s") % host4 % port % rem->toString());
hostlist = curl_slist_append(hostlist, hcode.c_str());
}
curl_easy_setopt(d_curl, CURLOPT_RESOLVE, hostlist);
}
if(src) {
curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
}
curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true);
/* only allow HTTP and HTTPS */
curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true);
curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str());
curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 10L);
clearHeaders();
d_data.clear();
}
std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
{
setupURL(str, rem, src);
auto res = curl_easy_perform(d_curl);
long http_code = 0;
curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
if(res != CURLE_OK || http_code != 200) {
throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
}
std::string ret=d_data;
d_data.clear();
return ret;
}
std::string MiniCurl::postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers)
{
setupURL(str);
setHeaders(headers);
curl_easy_setopt(d_curl, CURLOPT_POSTFIELDSIZE, postdata.size());
curl_easy_setopt(d_curl, CURLOPT_POSTFIELDS, postdata.c_str());
auto res = curl_easy_perform(d_curl);
long http_code = 0;
curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
if(res != CURLE_OK)
throw std::runtime_error("Unable to post URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
std::string ret=d_data;
d_data.clear();
return ret;
}
void MiniCurl::clearHeaders()
{
if (d_curl) {
curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, NULL);
if (d_header_list != nullptr) {
curl_slist_free_all(d_header_list);
d_header_list = nullptr;
}
}
}
void MiniCurl::setHeaders(const MiniCurlHeaders& headers)
{
if (d_curl) {
for (auto& header : headers) {
std::stringstream header_ss;
header_ss << header.first << ": " << header.second;
d_header_list = curl_slist_append(d_header_list, header_ss.str().c_str());
}
curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, d_header_list);
}
}

55
minicurl.hh 100644
View File

@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2018-2019 powerdns.com bv
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <string>
#include <curl/curl.h>
#include "comboaddress.hh"
#include <map>
#include <atomic>
#include <stdexcept>
using std::string;
// turns out 'CURL' is currently typedef for void which means we can't easily forward declare it
class MiniCurl
{
public:
using MiniCurlHeaders = std::map<std::string, std::string>;
static void init();
MiniCurl(const string& useragent="MiniCurl/0.0");
~MiniCurl();
MiniCurl& operator=(const MiniCurl&) = delete;
std::string getURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
std::string postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers);
private:
CURL *d_curl;
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);
std::string d_data;
struct curl_slist* d_header_list = nullptr;
void setupURL(const std::string& str, const ComboAddress* rem=0, const ComboAddress* src=0);
void setHeaders(const MiniCurlHeaders& headers);
void clearHeaders();
};

55
ubx.cc 100644
View File

@ -0,0 +1,55 @@
#include <iostream>
#include "ubx.hh"
#include "fmt/format.h"
#include "fmt/printf.h"
using namespace std;
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;
}
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& lst)
{
std::basic_string<uint8_t> str;
for(const auto& a : lst)
str.append(1, a);
return buildUbxMessage(ubxClass, ubxType, str);
}
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str)
{
// 0xb5 0x62 class id len1 len2 payload cka ckb
std::basic_string<uint8_t> msg;
msg.append(1, 0xb5);
msg.append(1, 0x62);
msg.append(1, ubxClass); // CFG
msg.append(1, ubxType); // MSG
msg.append(1, str.size()); // len1
msg.append(1, str.size()/256); // len2
for(unsigned int n= 0 ; n < str.size(); ++n)
msg.append(1, str[n]);
uint16_t csum = calcUbxChecksum(0x06, 0x01, msg.substr(6));
msg.append(1, csum&0xff);
msg.append(1, csum>>8);
return msg;
}

6
ubx.hh 100644
View File

@ -0,0 +1,6 @@
#include <string>
#include <vector>
uint16_t calcUbxChecksum(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str);
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, std::basic_string_view<uint8_t> str);
std::basic_string<uint8_t> buildUbxMessage(uint8_t ubxClass, uint8_t ubxType, const std::initializer_list<uint8_t>& str);

View File

@ -13,6 +13,11 @@
#include <thread>
#include <signal.h>
#include "ext/powerblog/h2o-pp.hh"
#include "minicurl.hh"
#include <time.h>
#include "ubx.hh"
#include "bits.hh"
using namespace std;
struct EofException{};
@ -36,83 +41,29 @@ uint16_t getUint16()
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 */
}
int g_tow, g_wn, g_dtLS{18};
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
};
/* inav schedule:
t n w
0 1: 2 wn % 30 == 0
2 2: 4 wn % 30 == 2
4 3: 6
6 4: 7/9
8 5: 8/10
10 6: 0 WN/TOW
12 7: 0 WN/TOW
14 8: 0 WN/TOW
16 9: 0 WN/TOW
18 10: 0 WN/TOW
20 11: 1
22 12: 3
24 13: 5
26 14: 0 WN/TOW
28 15: 0 WN/TOW
*/
/* 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;
@ -130,24 +81,6 @@ string humanSisa(uint8_t sisa)
}
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
{
@ -157,6 +90,9 @@ struct SVIOD
int32_t m0, omega0, i0, omega, idot, omegadot, deltan;
int16_t cuc{0}, cus{0}, crc{0}, crs{0}, cic{0}, cis{0};
uint16_t t0c; // clock epoch
int32_t af0, af1;
int8_t af2;
uint8_t sisa;
@ -196,11 +132,14 @@ void SVIOD::addWord(std::basic_string_view<uint8_t> page)
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);
af0 = getbits(&page[0], 68, 31);
af1 = getbits(&page[0], 99, 21);
af2 = getbits(&page[0], 120, 6);
/*
cout<<(int) t0c << " " <<(int) af0 <<" " <<(int) af1 <<" " <<(int) af2<<endl;
cout<<(int) t0c*60 << " " << (((double) af0) / (1ULL<<34))*1000000 <<" usec " << (((double) af1)/(1ULL << 46))*1000000000000 <<" ps/s"<<endl;
*/
}
@ -209,12 +148,20 @@ void SVIOD::addWord(std::basic_string_view<uint8_t> page)
struct SVStat
{
uint8_t e5bhs{0}, e1bhs{0};
uint16_t ai0{0};
int16_t ai1{0}, ai2{0};
bool sf1{0}, sf2{0}, sf3{0}, sf4{0}, sf5{0};
int BGDE1E5a{0}, BGDE1E5b{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};
int32_t a0{0}, a1{0}, t0t{0}, wn0t{0};
int32_t a0g{0}, a1g{0}, t0g{0}, wn0g{0};
int8_t dtLS{0}, dtLSF{0};
uint16_t wnLSF{0};
uint8_t dn; // leap second day number
int16_t el{-1}, azi{-1}, db{-1};
map<int, SVIOD> iods;
void addWord(std::basic_string_view<uint8_t> page);
bool completeIOD() const;
@ -255,13 +202,11 @@ 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);
g_wn = wn = getbitu(&page[0], 96, 12);
g_tow = tow = getbitu(&page[0], 108, 20);
}
}
else if(wtype >=1 && wtype <= 4) { // ephemeris
uint16_t iod = getbitu(&page[0], 6, 10);
@ -278,20 +223,44 @@ void SVStat::addWord(std::basic_string_view<uint8_t> page)
}
}
else if(wtype==5) { // disturbance, health, time
ai0 = getbitu(&page[0], 6, 11);
ai1 = getbits(&page[0], 17, 11); // ai1 & 2 are signed, 0 not
ai2 = getbits(&page[0], 28, 14);
sf1 = getbitu(&page[0], 42, 1);
sf2 = getbitu(&page[0], 43, 1);
sf3 = getbitu(&page[0], 44, 1);
sf4 = getbitu(&page[0], 45, 1);
sf5 = getbitu(&page[0], 46, 1);
BGDE1E5a = getbits(&page[0], 47, 10);
BGDE1E5b = getbits(&page[0], 57, 10);
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;
g_wn = wn = getbitu(&page[0], 73, 12);
g_tow = tow = getbitu(&page[0], 85, 20);
}
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);
dtLS = getbits(&page[0], 62, 8);
t0t = getbitu(&page[0], 70, 8);
wn0t = getbitu(&page[0], 78, 8);
wnLSF = getbitu(&page[0], 86, 8);
dn = getbitu(&page[0], 94, 3);
dtLSF = getbits(&page[0], 97, 8);
// cout<<(int) dtLS << " " <<(int) wnLSF<<" " <<(int) dn <<" " <<(int) dtLSF<<endl;
g_tow = tow = getbitu(&page[0], 105, 20);
}
else if(wtype == 10) { // GSTT GPS
a0g = getbits(&page[0], 86, 16);
a1g = getbits(&page[0], 102, 12);
t0g = getbitu(&page[0], 114, 8);
wn0g = getbitu(&page[0], 122, 6);
}
}
@ -308,26 +277,26 @@ void getCoordinates(int wn, int tow, const SVIOD& iod, double* x, double* y, dou
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 deltan = M_PI * 1.0*iod.deltan / (1LL<<43);
double t0e = 60.0*iod.t0e;
double m0 = M_PI * 1.0*iod.m0 / (1ULL<<31);
double m0 = M_PI * 1.0*iod.m0 / (1LL<<31);
double e = 1.0*iod.e / (1ULL<<33);
double omega = M_PI * 1.0*iod.omega / (1ULL<<31);
double omega = M_PI * 1.0*iod.omega / (1LL<<31);
double cuc = 1.0*iod.cuc / (1ULL<<29);
double cus = 1.0*iod.cus / (1ULL<<29);
double cuc = 1.0*iod.cuc / (1LL<<29);
double cus = 1.0*iod.cus / (1LL<<29);
double crc = 1.0*iod.crc / (1ULL<<5);
double crs = 1.0*iod.crs / (1ULL<<5);
double crc = 1.0*iod.crc / (1LL<<5);
double crs = 1.0*iod.crs / (1LL<<5);
double cic = 1.0*iod.cic / (1ULL<<29);
double cis = 1.0*iod.cis / (1ULL<<29);
double cic = 1.0*iod.cic / (1LL<<29);
double cis = 1.0*iod.cis / (1LL<<29);
double idot = M_PI * 1.0*iod.idot / (1ULL<<43);
double i0 = M_PI * 1.0*iod.i0 / (1ULL << 31);
double idot = M_PI * 1.0*iod.idot / (1LL<<43);
double i0 = M_PI * 1.0*iod.i0 / (1LL << 31);
double Omegadot = M_PI * 1.0*iod.omegadot / (1ULL << 43);
double Omega0 = M_PI * 1.0*iod.omega0 / (1ULL << 31);
double Omegadot = M_PI * 1.0*iod.omegadot / (1LL << 43);
double Omega0 = M_PI * 1.0*iod.omega0 / (1LL << 31);
// NO IOD BEYOND THIS POINT!
if(!quiet) {
@ -377,28 +346,34 @@ void getCoordinates(int wn, int tow, const SVIOD& iod, double* x, double* y, dou
// 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 corr = e*e*e*e * (103*sin(4*M) - 44*sin(2*M)) / 96.0 +
e*e*e*e*e * (1097*sin(5*M) - 645*sin(3*M) + 50 *sin(M))/960.0 +
e*e*e*e*e*e * (1223*sin(6*M) - 902*sin(4*M) + 85 *sin(2*M))/960.0;
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;
double nu1 = atan( ((sqrt(1-e*e) * sin(E)) / (1 - e * cos(E)) ) /
((cos(E) - e)/ (1-e*cos(E)))
);
double nu2 = atan( (sqrt(1-e*e) * sin(E)) /
(cos(E) - e)
);
double nu3 = 2* atan( sqrt((1+e)/(1-e)) * tan(E/2));
cout << "e: "<<e<<", M: "<< M<<endl;
cout <<" nu sis: "<<nu1<< " / +pi = " << nu1 +M_PI << endl;
cout <<" nu ?: "<<nu2<< " / +pi = " << nu2 +M_PI << endl;
cout <<" nu fourier/esa: "<<nu2<< " + " << corr <<" = " << nu2 + corr<<endl;
cout <<" nu wikipedia: "<<nu3<< " / +pi = " <<nu3 +M_PI << endl;
}
nu = nu2;
double nu = nu2 + corr;
// https://en.wikipedia.org/wiki/True_anomaly is good
@ -439,9 +414,74 @@ void getCoordinates(int wn, int tow, const SVIOD& iod, double* x, double* y, dou
std::map<int, SVStat> g_svstats;
uint64_t nanoTime(int wn, int tow)
{
return 1000000000ULL*(935280000 + wn * 7*86400 + tow - g_dtLS); // Leap!!
}
uint64_t utcFromGST(int wn, int tow)
{
return (935280000 + wn * 7*86400 + tow - g_dtLS); // Leap!!
}
struct InfluxPusher
{
explicit InfluxPusher(std::string_view dbname) : d_dbname(dbname)
{
}
void addValue( const pair<int,SVStat>& ent, string_view name, auto value)
{
d_buffer+= string(name) +",sv=" +to_string(ent.first)+" value="+to_string(value)+
" "+to_string(nanoTime(ent.second.wn, ent.second.tow))+"\n";
checkSend();
}
// note: this version trusts the system-wide g_wn, g_tow ovre the local one
void addValue(int sv, string_view name, auto value)
{
d_buffer+= string(name) +",sv=" +to_string(sv) + " value="+to_string(value)+" "+
to_string(nanoTime(g_wn, g_tow))+"\n";
checkSend();
}
void checkSend()
{
if(d_buffer.size() > 1000000 || (time(0) - d_lastsent) > 10) {
string buffer;
buffer.swap(d_buffer);
// thread t([buffer,this]() {
doSend(buffer);
// });
// t.detach();
d_lastsent=time(0);
}
}
void doSend(std::string buffer)
{
MiniCurl mc;
MiniCurl::MiniCurlHeaders mch;
if(!buffer.empty()) {
mc.postURL("http://127.0.0.1:8086/write?db="+d_dbname, buffer, mch);
}
}
~InfluxPusher()
{
doSend(d_buffer);
}
std::string d_buffer;
time_t d_lastsent{0};
string d_dbname;
};
/* The GST start epoch is defined as 13 seconds before midnight between 21st August and
22nd August 1999, i.e. GST was equal to 13 seconds at 22nd August 1999 00:00:00 UTC. */
std::string humanTime(int wn, int tow)
{
time_t t = 935280000 + wn * 7*86400 + tow;
time_t t = utcFromGST(wn, tow);
struct tm tm;
gmtime_r(&t, &tm);
@ -450,17 +490,68 @@ std::string humanTime(int wn, int tow)
return buffer;
}
int* g_tow, *g_wn;
/* | t0e tow | - > tow - t0e, <3.5 days, so ok
| t0e tow | -> tow - t0e > 3.5 days, so
7*86400 - tow + t0e
| tow t0e | -> 7*86400 - tow + t0e > 3.5 days, so
tow - t0e (negative age)
| tow t0e | -> 7*86400 - tow + t0e < 3.5 days, ok
*/
int ephAge(int tow, int t0e)
{
unsigned int diff;
unsigned int halfweek = 0.5*7*86400;
if(t0e < tow) {
diff = tow - t0e;
if(diff < halfweek)
return diff;
else
return (7*86400 - tow) + t0e;
}
else { // "t0e in future"
diff = 7*86400 - t0e + tow;
if(diff < halfweek)
return diff;
else
return tow - t0e; // in the future, negative age
}
}
int main(int argc, char** argv)
try
{
signal(SIGPIPE, SIG_IGN);
InfluxPusher idb(argc > 3 ? argv[3] : "galileo");
MiniCurl::init();
H2OWebserver h2s("galmon");
int tow=0, wn=0;
g_tow = &tow;
g_wn = &wn;
h2s.addHandler("/global", [](auto handler, auto req) {
nlohmann::json ret = nlohmann::json::object();
ret["leap-seconds"] = g_dtLS;
map<int, int> utcstats;
for(const auto& s: g_svstats) {
int dw = (uint8_t)g_wn - s.second.wn0t;
int age = dw * 7 * 86400 + g_tow - s.second.t0t * 3600;
utcstats[age]=s.first;
}
if(utcstats.empty()) {
ret["utc-offset"]=nullptr;
}
else {
int sv = utcstats.begin()->second; // freshest SV
long shift = g_svstats[sv].a0 * (1LL<<20) + g_svstats[sv].a1 * utcstats.begin()->first; // in 2^-50 seconds units
ret["utc-offset-ns"] = 1.073741824*ldexp(shift, -20);
ret["leap-second-planned"] = (g_svstats[sv].dtLSF != g_svstats[sv].dtLS);
}
return ret;
});
h2s.addHandler("/svs", [](auto handler, auto req) {
nlohmann::json ret = nlohmann::json::object();
@ -472,14 +563,22 @@ try
item["sisa"]=humanSisa(s.second.liveIOD().sisa);
item["a0"]=s.second.a0;
item["a1"]=s.second.a1;
item["dtLS"]=s.second.dtLS;
item["a0g"]=s.second.a0g;
item["a1g"]=s.second.a1g;
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;
item["eph-age-m"] = ephAge(g_tow, 60*s.second.liveIOD().t0e)/60.0;
item["last-seen-s"] = s.second.tow ? (7*86400*(g_wn - s.second.wn) + (int)g_tow - (int)s.second.tow) : -1;
item["af0"] = s.second.liveIOD().af0;
item["af1"] = s.second.liveIOD().af1;
item["af2"] = (int)s.second.liveIOD().af2;
item["t0c"] = s.second.liveIOD().t0c;
/* Our location:
X : 3922.505 km
@ -491,8 +590,8 @@ Z : 5004.189 km
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);
// 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;
@ -515,8 +614,8 @@ Z : 5004.189 km
item["x"]=x;
item["y"]=y;
item["z"]=z;
item["wn"]=*g_wn;
item["tow"]=*g_tow;
item["wn"] = s.second.wn;
item["tow"] = s.second.tow;
ret[std::to_string(s.first)] = item;
}
return ret;
@ -533,7 +632,6 @@ Z : 5004.189 km
ws.detach();
// unsigned int tow=0, wn=0;
ofstream csv("iod.csv");
ofstream csv2("toe.csv");
csv<<"timestamp sv iod sisa"<<endl;
@ -546,9 +644,13 @@ Z : 5004.189 km
ofstream sisacsv("sisa.csv");
sisacsv << "timestamp sv sisa"<<endl;
ofstream clockcsv("clock.csv");
clockcsv <<"timestamp sv af0 af1 af2 t0c age offset"<<endl;
string line;
time_t lastDisplay = time(0);
try {
for(;;) {
auto c = getUint8();
if(c != 0xb5) {
@ -565,18 +667,20 @@ Z : 5004.189 km
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;
*/
int sv = atoi(strs[n].c_str());
if(sv < 37) {
g_svstats[sv].el = atoi(strs[n+1].c_str());
g_svstats[sv].azi = atoi(strs[n+2].c_str());
if(g_svstats[sv].el >= 0)
g_svstats[sv].db = atoi(strs[n+3].c_str());
else
g_svstats[sv].db = -1;
idb.addValue(sv, "db", g_svstats[sv].db);
idb.addValue(sv, "elev", g_svstats[sv].el);
idb.addValue(sv, "azi", g_svstats[sv].azi);
}
}
}
line.clear();
@ -588,7 +692,12 @@ Z : 5004.189 km
ungetc(c, stdin); // might be 0xb5
continue;
}
if(lastDisplay != time(0)) {
lastDisplay = time(0);
if(g_wn && g_tow) {
cout<<"UTC from satellite: "<<humanTime(g_wn, g_tow)<<", delta: "<<lastDisplay - utcFromGST(g_wn, g_tow) << endl;
}
}
// if we are here, just had ubx header
uint8_t ubxClass = getUint8();
@ -604,7 +713,7 @@ Z : 5004.189 km
uint16_t ubxChecksum = getUint16();
if(ubxChecksum != calcUbxChecksum(ubxClass, ubxType, msg)) {
cout<<"Checksum: "<<ubxChecksum<< ", calculated: "<<
cout<<humanTime(g_wn, g_tow)<<": len = "<<msgLen<<", sv ? "<<(int)msg[2]<<", checksum: "<<ubxChecksum<< ", calculated: "<<
calcUbxChecksum(ubxClass, ubxType, msg)<<"\n";
}
@ -631,13 +740,12 @@ Z : 5004.189 km
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];
if(msg[0] == 2) {
// cout<<" word "<< (int)(msg[11] & (~(64+128)));
}
// cout<<"\n";
if(msg[0] != 2) // version field
if(msg[0] != 2) // galileo
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";
@ -680,15 +788,19 @@ Z : 5004.189 km
unsigned int wtype = getbitu(&inav[0], 0, 6);
g_svstats[sv].addWord(inav);
if(g_svstats[sv].e1bhs || g_svstats[sv].e5bhs || g_svstats[sv].e1bdvs || g_svstats[sv].e5bdvs) {
if(sv != 18 && sv != 14)
cout << "sv "<<sv<<" health: " << g_svstats[sv].e1bhs <<" " << g_svstats[sv].e5bhs <<" " << g_svstats[sv].e1bdvs <<" "<< g_svstats[sv].e5bdvs <<endl;
}
if(!wtype) {
if(getbitu(&inav[0], 6,2) == 2) {
wn = getbitu(&inav[0], 96, 12);
tow = getbitu(&inav[0], 108, 20);
// 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) {
if(wtype == 1 && g_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);
@ -696,101 +808,142 @@ Z : 5004.189 km
else if(wtype == 3) {
// unsigned int sisa = getbitu(&inav[0], 120, 8);
}
else if(wtype == 4) {
uint16_t iod = getbitu(&inav[0], 6, 10);
idb.addValue(sv, "af0", g_svstats[sv].iods[iod].af0);
idb.addValue(sv, "af1", g_svstats[sv].iods[iod].af1);
idb.addValue(sv, "af2", g_svstats[sv].iods[iod].af2);
idb.addValue(sv, "t0c", g_svstats[sv].iods[iod].t0c);
double age = ephAge(g_tow, g_svstats[sv].iods[iod].t0c * 60);
/*
cout<<"Atomic age "<<age<<", g_tow: "<<g_tow<<", t0c*60 = "<<g_svstats[sv].iods[iod].t0c * 60<<endl;
cout<<"af0: "<<g_svstats[sv].iods[iod].af0<<", af1: "<<g_svstats[sv].iods[iod].af1 << ", af2: "<<(int)g_svstats[sv].iods[iod].af2<<endl;
*/
double offset = ldexp(1000.0*(1.0*g_svstats[sv].iods[iod].af0 + ldexp(age*g_svstats[sv].iods[iod].af1, -12)), -34);
// XXX what units is this in then? milliseconds?
/*
cout <<"Atomic offset: "<<offset<<endl;
cout << 1.0*g_svstats[sv].iods[iod].af0/(1LL<<34) << endl;
cout << age * (g_svstats[sv].iods[iod].af1/(1LL<<46)) << endl;
*/
idb.addValue(sv, "atomic_offset_ns", 1000000.0*offset);
}
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;
idb.addValue(sv, "ai0", g_svstats[sv].ai0);
idb.addValue(sv, "ai1", g_svstats[sv].ai1);
idb.addValue(sv, "ai2", g_svstats[sv].ai2);
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";
idb.addValue(sv, "sf1", g_svstats[sv].sf1);
idb.addValue(sv, "sf2", g_svstats[sv].sf2);
idb.addValue(sv, "sf3", g_svstats[sv].sf3);
idb.addValue(sv, "sf4", g_svstats[sv].sf4);
idb.addValue(sv, "sf5", g_svstats[sv].sf5);
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";
}
idb.addValue(sv, "BGDE1E5a", g_svstats[sv].BGDE1E5a);
idb.addValue(sv, "BGDE1E5b", g_svstats[sv].BGDE1E5b);
/*
else
wk.emitLine(sv, "GST-UTC6, a0="+to_string(a0)+", a1="+to_string(a1));
*/
}
else if(wtype == 6) { // GST-UTC
idb.addValue(sv, "a0", g_svstats[sv].a0);
idb.addValue(sv, "a1", g_svstats[sv].a1);
g_dtLS = g_svstats[sv].dtLS;
int dw = (uint8_t)g_wn - g_svstats[sv].wn0t;
if(g_tow && g_wn) {
int age = dw * 7 * 86400 + g_tow - g_svstats[sv].t0t * 3600;
// a0 = 2^-30 s, a1 = 2^-50 s/s
long shift = g_svstats[sv].a0 * (1LL<<20) + g_svstats[sv].a1 * age; // in 2^-50 seconds units
time_t t = utcFromGST(g_wn, g_tow);
gstutc << t << " " << sv <<" " << g_tow << " " << (g_svstats[sv].t0t * 3600) << " "<< age <<" " <<shift << " " << shift * pow(2, -50) * 1000000000 << " " << g_svstats[sv].a0 <<" " << g_svstats[sv].a1 << "\n";
// 2^-30
idb.addValue(sv, "utc_diff_ns", 1.073741824*ldexp(shift, -20));
// cout<<humanTime(g_wn, g_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(g_wn&0xff)+"\n";
}
}
else if(wtype == 10) { // GSTT GPS
idb.addValue(sv, "a0g", g_svstats[sv].a0g);
idb.addValue(sv, "a1g", g_svstats[sv].a1g);
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;
int dw = (((uint8_t)g_wn)&(1+2+4+8+16+32)) - wn0g;
if(tow && wn) {
if(g_tow && g_wn) {
int age = tow - t0g * 3600;
int age = g_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";
time_t t = utcFromGST(g_wn, g_tow);
gstgps << t << " " << sv <<" " << g_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";
// cout<<humanTime(g_wn, g_tow)<<" sv "<<sv<< " GST-GPS, a0g="+to_string(a0g)+", a1g="+to_string(a1g)+", t0g="+to_string(t0g)+", age="+to_string(g_tow/3600-t0g)+"h, dw="+to_string(dw)
// +", wn0g="+to_string(wn0g)+", wn6="+to_string(g_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";
time_t t = utcFromGST(g_wn, g_tow);
sisacsv << t <<" " << ent.first << " " << (unsigned int) ent.second.liveIOD().sisa << endl;
// cout << t <<" " << ent.first << " " << (unsigned int) ent.second.liveIOD().sisa << "\n";
double clockage = ephAge(g_tow, ent.second.liveIOD().t0c * 60);
double offset = 1.0*ent.second.liveIOD().af0/(1LL<<34) + clockage * ent.second.liveIOD().af1/(1LL<<46);
clockcsv << t << " " << ent.first<<" " << ent.second.liveIOD().af0 << " " << ent.second.liveIOD().af1 <<" " << (int)ent.second.liveIOD().af2 <<" " << 935280000 + g_wn *7*86400 + ent.second.liveIOD().t0c * 60 << " " << clockage << " " << offset<<endl;
int ephage = ephAge(g_tow, ent.second.prevIOD.second.t0e * 60);
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<<" ("<<
cout<<humanTime(g_wn, g_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;
humanSisa(ent.second.liveIOD().sisa)<<"), lastseen = "<< (ephage/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);
getCoordinates(g_wn, g_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);
getCoordinates(g_wn, g_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;
double disco = sqrt((x-oldx)*(x-oldx) + (y-oldy)*(y-oldy) + (z-oldz)*(z-oldz));
cout<<ent.first<<" discontinuity "<< hours<<"h old: "<< disco <<endl;
idb.addValue(sv, "iod-actual", ent.second.getIOD());
idb.addValue(sv, "iod-hours", hours);
if(hours < 4)
idb.addValue(sv, "eph-disco", disco);
if(hours < 2) {
if(0 && 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;
getCoordinates(g_wn, t, ent.second.liveIOD(), &x, &y, &z, false);
getCoordinates(g_wn, t, ent.second.prevIOD.second, &oldx, &oldy, &oldz);
time_t posix = utcFromGST(g_wn, t);
orbitcsv << posix <<" "
<<x<<" " <<y<<" "<<z<<" "
<<oldx<<" " <<oldy<<" "<<oldz<<"\n";
@ -798,23 +951,38 @@ Z : 5004.189 km
}
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;
}
}
else if (ubxClass == 1 && ubxType == 0x35) { // UBX-NAV-SAT
cout<< "Info for "<<(int) msg[5]<<" svs: \n";
for(unsigned int n = 0 ; n < msg[5]; ++n) {
cout << " "<<(msg[8+12*n] ? 'E' : 'G') << (int)msg[9+12*n] <<" db=";
cout << (int)msg[10+12*n]<<" elev="<<(int)(char)msg[11+12*n]<<" azi=";
cout << ((int)msg[13+12*n]*256 + msg[12+12*n])<<" pr="<< *((int16_t*)(msg.c_str()+ 14 +12*n)) *0.1 << " signal="<< ((int)(msg[16+12*n])&7) << " used="<< (msg[16+12*n]&8);
fmt::printf(" | %02x %02x %02x %02x", (int)msg[16+12*n], (int)msg[17+12*n],
(int)msg[18+12*n], (int)msg[19+12*n]);
cout<<"\n";
}
cout<<"\n";
*/
cout<<endl;
}
else if (ubxClass == 4) { // info
fmt::printf("Log level %d: %.*s\n", (int)ubxType, msg.size(), (char*)msg.c_str());
}
else if (ubxClass == 6 && ubxType == 1) { // msg rate
fmt::printf("Rate for class %02x type %02x: %d %d %d %d %d %d\n",
(int)msg[0], (int)msg[1], (int) msg[2], (int) msg[3],(int) msg[4],
(int) msg[5],(int) msg[6],(int) msg[7]);
}
else
cout << "ubxClass: "<<(unsigned int)ubxClass<<", ubxType: "<<(unsigned int)ubxType<<", size="<<msg.size()<<endl;
}
}
catch(EofException& e)
{}
}
catch(EofException& e)
{}
}
catch(std::exception& e)
{
cerr<<"Exiting because of fatal error "<<e.what()<<endl;
}

148
ubxtool.cc 100644
View File

@ -0,0 +1,148 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <string>
#include <stdint.h>
#include "ubx.hh"
#include <iostream>
#include <string.h>
using namespace std;
#define BAUDRATE B921600
#define MODEMDEVICE "/dev/ttyACM0"
namespace {
struct EofException{};
}
size_t readn2(int fd, void* buffer, size_t len)
{
size_t pos=0;
ssize_t res;
for(;;) {
res = read(fd, (char*)buffer + pos, len - pos);
if(res == 0)
throw EofException();
if(res < 0) {
throw runtime_error("failed in readn2: "+string(strerror(errno)));
}
pos+=(size_t)res;
if(pos == len)
break;
}
return len;
}
static uint8_t getUint8(int fd)
{
uint8_t c;
if(readn2(fd, &c, 1) != sizeof(c))
throw EofException();
return c;
}
static uint16_t getUint16(int fd)
{
uint8_t c[2];
if(readn2(fd, &c, sizeof(c)) != sizeof(c))
throw EofException();
return c[0] + 256*c[1];
}
size_t writen2(int fd, const void *buf, size_t count)
{
const char *ptr = (char*)buf;
const char *eptr = ptr + count;
ssize_t res;
while(ptr != eptr) {
res = ::write(fd, ptr, eptr - ptr);
if(res < 0) {
throw runtime_error("failed in writen2: "+string(strerror(errno)));
}
else if (res == 0)
throw EofException();
ptr += (size_t) res;
}
return count;
}
int main(int argc, char** argv)
{
int fd, res;
struct termios oldtio,newtio;
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); exit(-1); }
tcgetattr(fd,&oldtio); /* save current port settings */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 5; /* blocking read until 5 chars received */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
usleep(500000);
std::basic_string<uint8_t> msg = buildUbxMessage(0x06, 0x01, {0x02, 89});
writen2(fd, msg.c_str(), msg.size());
msg = buildUbxMessage(0x06, 0x01, {0x02, 89, 0, 0, 0, 1, 0, 0});
writen2(fd, msg.c_str(), msg.size());
msg = buildUbxMessage(0x06, 0x01, {0x02, 89});
writen2(fd, msg.c_str(), msg.size());
/* goal: isolate UBX messages, ignore everyting else.
The challenge is that we might sometimes hit the 0xb5 0x62 marker
in the middle of a message, which will cause us to possibly jump over valid messages */
uint8_t marker[2]={0};
for(;;) {
marker[0] = marker[1];
res = readn2(fd, marker+1, 1);
if(res < 0)
break;
// cerr<<"marker now: "<< (int)marker[0]<<" " <<(int)marker[1]<<endl;
if(marker[0]==0xb5 && marker[1]==0x62) { // bingo
basic_string<uint8_t> msg;
msg.append(marker, 2);
msg.append(1, getUint8(fd)); // class
msg.append(1, getUint8(fd)); // type
uint16_t len = getUint16(fd);
msg.append((uint8_t*)&len, 2);
cerr<<"Got class "<<(int)msg[0]<<" type "<<(int)msg[1]<<", len = "<<len<<endl;
uint8_t buffer[len+2];
res=readn2(fd, buffer, len+2);
msg.append(buffer, len+2); // checksum
writen2(1, msg.c_str(),msg.size());
marker[0]=marker[1]=0;
}
}
tcsetattr(fd,TCSANOW,&oldtio);
}