#define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ubx.hh" #include "navmon.hh" #include #include #include #include "fmt/format.h" #include "fmt/printf.h" #include "bits.hh" #include "galileo.hh" #include #include "navmon.pb.h" #include "gps.hh" #include "glonass.hh" #include "beidou.hh" #include "CLI/CLI.hpp" #include #include #include "comboaddress.hh" #include "swrappers.hh" #include "sclasses.hh" #include "nmmsender.hh" #include "version.hh" static char program[]="ubxtool"; bool doDEBUG{false}; bool doLOGFILE{false}; bool doVERSION{false}; struct timespec g_gnssutc; uint16_t g_galwn; using namespace std; uint16_t g_srcid{0}; int g_fixtype{-1}; double g_speed{-1}; extern const char* g_gitHash; static int getBaudrate(int baud) { if(baud==115200) return B115200; else if(baud==57600) return B57600; else if(baud==38400) return B38400; else if(baud==19200) return B19200; else if(baud==9600) return B9600; else throw std::runtime_error("Unknown baudrate "+to_string(baud)); } static int getBaudrateFromSymbol(int baud) { if(baud==B115200) return 115200; else if(baud==B57600) return 57600; else if(baud==B38400) return 38400; else if(baud==B19200) return 19200; else if(baud==B9600) return 9600; else throw std::runtime_error("Unknown baudrate symbol "+to_string(baud)); } static int g_baudval; /* inav schedule: 1) Find plausible start time of next cycle Current cycle: TOW - (TOW%30) Next cycle: TOW - (TOW%30) + 30 E1: t n w 0 1: 2 tow % 30 == 1 2 2: 4 tow % 30 == 3 4 3: 6 TOW 5 -> set startTow, startTowFresh 6 4: 7/9 7 8 5: 8/10 9 10 6: 0 WN/TOW 11 12 7: 0 WN/TOW 13 14 8: 0 WN/TOW 15 16 9: 0 WN/TOW 17 18 10: 0 WN/TOW 19 20 11: 1 21 22 12: 3 23 24 13: 5 WN/TOW 25 26 14: 0 WN/TOW 27 28 15: 0 WN/TOW 29 E5b-1: t n w 0 1: 1 (2/2) tow % 30 == 0 2 2: 3 tow % 30 == 2 4 3: 5 WN/TOW 4 -> set startTow, startTowFresh 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: 2 22 12: 4 24 13: 6 TOW 26 14: 0 WN/TOW 28 15: 1 (1/2) WN/TOW */ class UBXMessage { public: struct BadChecksum{}; explicit UBXMessage(basic_string_view src) { d_raw = src; if(d_raw.size() < 6) throw std::runtime_error("Partial UBX message"); uint16_t csum = calcUbxChecksum(getClass(), getType(), d_raw.substr(6, d_raw.size()-8)); if(csum != d_raw.at(d_raw.size()-2) + 256*d_raw.at(d_raw.size()-1)) throw BadChecksum(); } uint8_t getClass() const { return d_raw.at(2); } uint8_t getType() const { return d_raw.at(3); } std::basic_string getPayload() const { return d_raw.substr(6, d_raw.size()-8); } std::basic_string d_raw; }; bool g_fromFile{false}; #if !defined(O_LARGEFILE) #define O_LARGEFILE 0 #endif std::pair getUBXMessage(int fd, double* timeout) { static int logfile=0; if (doLOGFILE) { if(!logfile && !g_fromFile) { logfile = open("./logfile", O_WRONLY|O_CREAT|O_APPEND|O_LARGEFILE, 0600); if(!logfile) throw std::runtime_error("Failed to open logfile for writing"); } } uint8_t marker[2]={0}; for(;;) { marker[0] = marker[1]; int res = readn2Timeout(fd, marker+1, 1, timeout); if(res < 0) { cerr<<"Readn2Timeout failed: "< msg; msg.append(marker, 2); // 0,1 uint8_t b[4]; readn2Timeout(fd, b, 4, timeout); msg.append(b, 4); // class, type, len1, len2 uint16_t len = b[2] + 256*b[3]; // if (doDEBUG) { cerr< msg, uint8_t ubxClass, uint8_t ubxType) { for(int n=3; n; --n) { writen2(fd, &msg[0], msg.size()); try { return waitForUBX(fd, seconds, ubxClass, ubxType); } catch(...) { if(n==1) throw; cerr<<"Retransmit"< msg, uint8_t ubxClass, uint8_t ubxType) { for(int n=3; n; --n) { writen2(fd, &msg[0], msg.size()); try { return waitForUBXAckNack(fd, seconds, ubxClass, ubxType); } catch(...) { if(n==1) throw; cerr<<"Retransmit"< payload; if(version9) { payload= basic_string({ubxClass, ubxType, rate}); } else { if(port > 6) throw std::runtime_error("Port number out of range (>6)"); payload.assign({ubxClass, ubxType, 0, 0, 0, 0, 0, 0}); payload[2+ port]=rate; } auto msg = buildUbxMessage(0x06, 0x01, payload); if(sendAndWaitForUBXAckNack(fd, 2, msg, 0x06, 0x01)) return; else throw std::runtime_error("Got NACK enabling UBX message "+to_string((int)ubxClass)+" "+to_string((int)ubxType)); } catch(TimeoutError& te) { if (doDEBUG) { cerr< payload) { return fmt::sprintf("%02x%02x%02x%02x%02x", payload[4], payload[5], payload[6], payload[7], payload[8]); } // these are four structs to capture Ublox F9P time offset stats namespace { struct TIMEGAL { uint32_t itow; uint32_t galTow; int32_t fGalTow; int16_t galWno; int8_t leapS; uint8_t valid; uint32_t tAcc; } __attribute__((packed)); struct TIMEBDS { uint32_t itow; uint32_t sow; int32_t fSow; int16_t week; int8_t leapS; uint8_t valid; uint32_t tAcc; } __attribute__((packed)); struct TIMEGLO { uint32_t itow; uint32_t tod; int32_t fTod; uint16_t nT; uint8_t n4; uint8_t valid; uint32_t tAcc; } __attribute__((packed)); struct TIMEGPS { uint32_t itow; int32_t ftow; int16_t week; int8_t leapS; uint8_t valid; uint32_t tAcc; } __attribute__((packed)); } // ubxtool device srcid int main(int argc, char** argv) { auto starttime = std::chrono::steady_clock::now(); GOOGLE_PROTOBUF_VERIFY_VERSION; CLI::App app(program); bool doGPS{true}, doGalileo{true}, doGlonass{false}, doBeidou{false}, doReset{false}, doWait{true}, doRTSCTS{true}, doSBAS{false}; bool doFakeFix{false}; bool doKeepNMEA{false}; bool doSTDOUT=false; #ifdef OpenBSD doRTSCTS = false; #endif vector destinations; string portName; int ubxport=3; int baudrate=0; int newbaudrate=0; unsigned int fuzzPositionMeters=0; string owner; string remark; bool doCompress=true; app.add_option("--destination,-d", destinations, "Send output to this IPv4/v6 address"); app.add_flag("--wait", doWait, "Wait a bit, do not try to read init messages"); // app.add_flag("--compress,-z", doCompress, "Use compressed protocol for network transmission"); app.add_flag("--reset", doReset, "Reset UBX device"); app.add_flag("--beidou,-c", doBeidou, "Enable BeiDou reception"); app.add_flag("--gps,-g", doGPS, "Enable GPS reception"); app.add_flag("--glonass,-r", doGlonass, "Enable Glonass reception"); app.add_flag("--galileo,-e", doGalileo, "Enable Galileo reception"); app.add_flag("--sbas,-s", doSBAS, "Enable SBAS (EGNOS/WAAS/etc) reception"); app.add_option("--rtscts", doRTSCTS, "Set hardware handshaking"); app.add_flag("--stdout", doSTDOUT, "Emit output to stdout"); auto pn = app.add_option("--port,-p", portName, "Device or file to read serial from"); app.add_option("--station", g_srcid, "Station id"); app.add_option("--ubxport,-u", ubxport, "UBX port to enable messages on (usb=3)"); app.add_option("--baud,-b", baudrate, "Baudrate for serial connection"); app.add_option("--newbaud,-n", newbaudrate, "Attempt to change to this baudrate for serial connection"); app.add_flag("--keep-nmea,-k", doKeepNMEA, "Don't disable NMEA"); app.add_flag("--fake-fix", doFakeFix, "Inject locally generated fake fix data"); app.add_option("--fuzz-position,-f", fuzzPositionMeters, "Fuzz position by this many meters"); app.add_option("--owner,-o", owner, "Name/handle/nick of owner/operator"); app.add_option("--remark", remark, "Remark for this station"); int surveyMinSeconds = 0; int surveyMinCM = 0; bool doSurveyReset=false; app.add_option("--survey-min-seconds", surveyMinSeconds, "Survey minimally this amount of seconds"); app.add_option("--survey-min-cm", surveyMinCM, "Survey until accuracy is better (lower) than this setting"); app.add_flag("--survey-reset", doSurveyReset, "Reset the Surveyed-in state"); app.add_flag("--debug", doDEBUG, "Display debug information"); app.add_flag("--logfile", doLOGFILE, "Create logfile"); app.add_flag("--version", doVERSION, "show program version and copyright"); try { app.parse(argc, argv); } catch(const CLI::Error &e) { return app.exit(e); } if(doVERSION) { showVersion(program, g_gitHash); exit(0); } if(! *pn) { cerr<<"you must provide the --port"< msg; if(doReset) { if (doDEBUG) { cerr< msg; if(version9) { cmd = 0x8a; msg = buildUbxMessage(0x06, cmd, {0x00, 0x01, 0x00, 0x00, 0x01,0x00,0x03,0x20, 1, // survey in mode // min survey time: 0x10,0x00,0x03,0x40, ptrSeconds[0], ptrSeconds[1], ptrSeconds[2], ptrSeconds[3], 0x11,0x00,0x03,0x40, ptrCent[0], ptrCent[1], ptrCent[2], ptrCent[3] }); } else { minCentimetersVal /= 10; cmd = 0x3d; msg = buildUbxMessage(0x06, cmd, { 1,0,0,0, // survey-in, res, flag1, flag2 0,0,0,0, // x 0,0,0,0, // y 0,0,0,0, // z 0,0,0,0, // fixed position accuracy ptrSeconds[0], ptrSeconds[1], ptrSeconds[2], ptrSeconds[3], ptrCent[0], ptrCent[1], ptrCent[2], ptrCent[3] }); } cerr<>8) & 0xFF), (unsigned char)((actbaud>>16) & 0xFF), (unsigned char)((actbaud>>24) & 0xFF), // in in out out // 0x01 = ublox, 0x02 = nmea 0x03,0x00,outproto,0x00, // flags res res 0x00,0x00,0x00,0x00 }); } else { // port res tx-ready res res res res res res res res in in out out res res res res msg = buildUbxMessage(0x06, 0x00, {(unsigned char)(ubxport),0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,outproto,0x00,0x00,0x00,0x00,0x00}); } if(newbaudrate && newbaudrate != baudrate) { for(int n=0; n < 3; ++n) { cerr< itows; if(doGlonass) itows.push_back(glo.itow); if(doGalileo) itows.push_back(gal.itow); if(doBeidou) itows.push_back(bds.itow); if(doGPS) itows.push_back(gps.itow); if(itows.empty()) return; if(itows[0] == 0) return; for(const auto& itow : itows) if(itow != itows[0]) return; NavMonMessage nmm; nmm.set_sourceid(g_srcid); nmm.set_localutcseconds(g_gnssutc.tv_sec); nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec); nmm.set_type(NavMonMessage::TimeOffsetType); nmm.mutable_to()->set_itow(gps.itow); NavMonMessage::GNSSOffset* no; if(doGPS) { no = nmm.mutable_to()->add_offsets(); no->set_gnssid(0); no->set_offsetns(gps.ftow); no->set_tacc(gps.tAcc); no->set_tow(gps.itow); // this is for consistency no->set_leaps(gps.leapS); no->set_wn(gps.week); no->set_valid(gps.valid); } if(doGalileo) { no = nmm.mutable_to()->add_offsets(); no->set_gnssid(2); no->set_offsetns(gal.fGalTow); no->set_tacc(gal.tAcc); no->set_leaps(gal.leapS); no->set_wn(gal.galWno); no->set_valid(gal.valid); no->set_tow(gal.galTow); } if(doBeidou) { no = nmm.mutable_to()->add_offsets(); no->set_gnssid(3); no->set_offsetns(bds.fSow); no->set_tacc(bds.tAcc); no->set_leaps(bds.leapS); no->set_wn(bds.week); no->set_valid(bds.valid); no->set_tow(bds.sow); } if(doGlonass) { no = nmm.mutable_to()->add_offsets(); no->set_gnssid(6); no->set_offsetns(glo.fTod); no->set_tacc(glo.tAcc); no->set_nt(glo.nT); no->set_n4(glo.n4); no->set_valid(glo.valid); no->set_tow(glo.tod); } ns.emitNMM(nmm); gal.itow = 0; gps.itow = 0; glo.itow = 0; bds.itow = 0; } } tstate; tstate.doGPS = doGPS; tstate.doGalileo = doGalileo; tstate.doGlonass = doGlonass; tstate.doBeidou = doBeidou; for(;;) { try { auto [msg, timestamp] = getUBXMessage(fd, nullptr); (void)timestamp; auto payload = msg.getPayload(); if(msg.getClass() == 0x01 && msg.getType() == 0x07) { // UBX-NAV-PVT struct PVT { uint32_t itow; uint16_t year; uint8_t month; // jan = 1 uint8_t day; uint8_t hour; // 24 uint8_t min; uint8_t sec; uint8_t valid; uint32_t tAcc; int32_t nano; uint8_t fixtype; uint8_t flags; uint8_t flags2; uint8_t numsv; // 1e-7 mm mm int32_t lon, lat, height, hMSL; // mm mm uint32_t hAcc, vAcc; // mm/s mm/s mm/s mm/s int32_t velN, velE, velD, gSpeed; // millimeters } __attribute__((packed)); PVT pvt; memcpy(&pvt, &payload[0], sizeof(pvt)); // cerr << "Ground speed: "<set_x(3924698.1158); nmm.mutable_op()->set_y(301124.8036); nmm.mutable_op()->set_z(5001904.9952); nmm.mutable_op()->set_acc(3.14); ns.emitNMM( nmm); } } if(msg.getClass() == 0x27 && msg.getType() == 0x03) { // serial serialno = format_serial(payload); if(doDEBUG) cerr<set_gnssid(gnssid); nmm.mutable_rfd()->set_gnsssv(sv); nmm.mutable_rfd()->set_sigid(sigid); nmm.mutable_rfd()->set_rcvtow(rcvTow); nmm.mutable_rfd()->set_rcvwn(rcvWn); nmm.mutable_rfd()->set_doppler(doppler); nmm.mutable_rfd()->set_carrierphase(cpMes); nmm.mutable_rfd()->set_pseudorange(prMes); nmm.mutable_rfd()->set_prstd(ldexp(0.01, prStddev)); nmm.mutable_rfd()->set_dostd(ldexp(0.002, doStddev)); nmm.mutable_rfd()->set_cpstd(cpStddev*0.4); nmm.mutable_rfd()->set_locktimems(locktimems); nmm.mutable_rfd()->set_cno(cno); nmm.mutable_rfd()->set_prvalid(trkStat & 1); nmm.mutable_rfd()->set_cpvalid(trkStat & 2); nmm.mutable_rfd()->set_halfcycvalid(trkStat & 4); nmm.mutable_rfd()->set_subhalfcyc(trkStat & 8); nmm.mutable_rfd()->set_clkreset(clkReset); ns.emitNMM( nmm); } } else if(msg.getClass() == 0x01 && msg.getType() == 0x01) { // POSECF struct pos { uint32_t iTOW; int32_t ecefX; int32_t ecefY; int32_t ecefZ; uint32_t pAcc; }; pos p; memcpy(&p, payload.c_str(), sizeof(pos)); if(fuzzPositionMeters) { p.ecefX -= (p.ecefX % (fuzzPositionMeters*100)); p.ecefY -= (p.ecefY % (fuzzPositionMeters*100)); p.ecefZ -= (p.ecefZ % (fuzzPositionMeters*100)); } /* if (doDEBUG) { cerr<set_x(p.ecefX /100.0); nmm.mutable_op()->set_y(p.ecefY /100.0); nmm.mutable_op()->set_z(p.ecefZ /100.0); nmm.mutable_op()->set_acc(p.pAcc /100.0); if(g_speed >= 0.0) nmm.mutable_op()->set_groundspeed(g_speed); ns.emitNMM( nmm); } else if(msg.getClass() == 2 && msg.getType() == 0x13) { // SFRBX // order: 2, 4, 6, 7/9, 8/10, 0, 0, 0, 0, 0, 1, 3, 5, 0, 0 // * * * * * * * // tow try { pair id = make_pair(payload[0], payload[1]); int sigid = payload[2]; static set> svseen; static time_t lastStat; svseen.insert({id.first, id.second, payload[2]}); if(time(0)- lastStat > 30) { cerr<(s)<<","<(s)<<"@"<(s)<<" "; } cerr<set_gnsswn(wn); // XXX this sucks nmm.mutable_gpsi()->set_sigid(sigid); nmm.mutable_gpsi()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted nmm.mutable_gpsi()->set_gnssid(id.first); nmm.mutable_gpsi()->set_gnsssv(id.second); nmm.mutable_gpsi()->set_contents(string((char*)gpsframe.c_str(), gpsframe.size())); ns.emitNMM( nmm); continue; } if(id.first == 0 && sigid) { // new GPS auto cnav = getGPSFromSFRBXMsg(payload); static int wn, tow; int type = getbitu(&cnav[0], 14, 6); tow = 6 * getbitu(&cnav[0], 20, 17) - 12; if(type == 10) { wn = getbitu(&cnav[0], 38, 13); } if(!wn) continue; // can't file this yet NavMonMessage nmm; nmm.set_type(NavMonMessage::GPSCnavType); nmm.set_localutcseconds(g_gnssutc.tv_sec); nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec); nmm.set_sourceid(g_srcid); nmm.mutable_gpsc()->set_gnsswn(wn); // XXX this sucks nmm.mutable_gpsc()->set_sigid(sigid); nmm.mutable_gpsc()->set_gnsstow(tow); // "with 6 second increments" -- needs to be adjusted nmm.mutable_gpsc()->set_gnssid(id.first); nmm.mutable_gpsc()->set_gnsssv(id.second); nmm.mutable_gpsc()->set_contents(string((char*)cnav.c_str(), cnav.size())); ns.emitNMM( nmm); } else if(id.first ==2) { // GALILEO basic_string reserved1, reserved2, sar, spare, crc; auto inav = getInavFromSFRBXMsg(payload, reserved1, reserved2, sar, spare, crc); unsigned int wtype = getbitu(&inav[0], 0, 6); uint32_t satTOW; int msgTOW{0}; if(getTOWFromInav(inav, &satTOW, &g_galwn)) { // 0, 6, 5 // if (doDEBUG) { cerr<set_gnsswn(g_galwn); nmm.mutable_gi()->set_gnsstow(msgTOW); nmm.mutable_gi()->set_gnssid(id.first); nmm.mutable_gi()->set_gnsssv(id.second); nmm.mutable_gi()->set_sigid(sigid); nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size()); nmm.mutable_gi()->set_reserved1((const char*)&reserved1[0], reserved1.size()); nmm.mutable_gi()->set_reserved2((const char*)&reserved2[0], reserved2.size()); nmm.mutable_gi()->set_sar((const char*) &sar[0], sar.size()); nmm.mutable_gi()->set_crc((const char*) &crc[0], crc.size()); nmm.mutable_gi()->set_spare((const char*)&spare[0], spare.size()); ns.emitNMM( nmm); } else if(id.first==3) { auto gstr = getGlonassFromSFRBXMsg(payload); auto cond = getCondensedBeidouMessage(gstr); static map bms; auto& bm = bms[id.second]; uint8_t pageno; bm.parse(cond, &pageno); if(bm.wn < 0) { if (doDEBUG) { cerr< 5) { // this **HARDCODES** that C01,02,03,04,05 emit D2 messages! nmm.set_type(NavMonMessage::BeidouInavTypeD1); nmm.mutable_bid1()->set_gnsswn(bm.wn); // only sent in word 1!! nmm.mutable_bid1()->set_gnsstow(bm.sow); nmm.mutable_bid1()->set_gnssid(id.first); nmm.mutable_bid1()->set_gnsssv(id.second); nmm.mutable_bid1()->set_sigid(sigid); nmm.mutable_bid1()->set_contents(string((char*)gstr.c_str(), gstr.size())); ns.emitNMM( nmm); } else { // not sending this: we can't even get the week number right! /* nmm.set_type(NavMonMessage::BeidouInavTypeD2); nmm.mutable_bid2()->set_gnsswn(bm.wn); nmm.mutable_bid2()->set_gnsstow(bm.sow); nmm.mutable_bid2()->set_gnssid(id.first); nmm.mutable_bid2()->set_gnsssv(id.second); nmm.mutable_bid2()->set_sigid(sigid); nmm.mutable_bid2()->set_contents(string((char*)gstr.c_str(), gstr.size())); */ } continue; } else if(id.first==6) { // if (doDEBUG) { cerr< gms; GlonassMessage& gm = gms[id.second]; int strno = gm.parse(gstr); */ if(id.second != 255) { NavMonMessage nmm; nmm.set_localutcseconds(g_gnssutc.tv_sec); nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec); nmm.set_sourceid(g_srcid); nmm.set_type(NavMonMessage::GlonassInavType); nmm.mutable_gloi()->set_freq(payload[3]); nmm.mutable_gloi()->set_gnssid(id.first); nmm.mutable_gloi()->set_gnsssv(id.second); nmm.mutable_gloi()->set_sigid(sigid); nmm.mutable_gloi()->set_contents(string((char*)gstr.c_str(), gstr.size())); ns.emitNMM( nmm); } } else if(id.first == 1) {// SBAS /* if (doDEBUG) { cerr<set_gnssid(id.first); nmm.mutable_sbm()->set_gnsssv(id.second); nmm.mutable_sbm()->set_contents(string((char*)sbas.c_str(), sbas.size())); ns.emitNMM( nmm); } else ; // if (doDEBUG) { cerr<set_gnssid(gnssid); nmm.mutable_rd()->set_gnsssv(sv); nmm.mutable_rd()->set_db(db); nmm.mutable_rd()->set_el(el); nmm.mutable_rd()->set_azi(azi); nmm.mutable_rd()->set_prres(*((int16_t*)(payload.c_str()+ 14 +12*n)) *0.1); uint32_t status; memcpy(&status, &payload[16+12*n], 4); nmm.mutable_rd()->set_qi(status & 7); nmm.mutable_rd()->set_used(status & 8); /* if (doDEBUG) { cerr<> 8)&7); cerr<<" eph-avail " << !!(status & (1<<11)); cerr<<" alm-avail " << !!(status & (1<<12)); cerr<set_gnssid(gnssid); nmm.mutable_rd()->set_gnsssv(sv); nmm.mutable_rd()->set_db(db); nmm.mutable_rd()->set_prres(*((int16_t*)(payload.c_str()+ 12 +16*n)) *0.1); // ENDIANISM nmm.mutable_rd()->set_sigid(sigid); nmm.mutable_rd()->set_el(0); nmm.mutable_rd()->set_azi(0); nmm.mutable_rd()->set_qi(qi); nmm.mutable_rd()->set_used(sigflags & 8); ns.emitNMM( nmm); } } else if(msg.getClass() == 1 && msg.getType() == 0x30) { // UBX-NAV-SVINFO } else if(msg.getClass() == 1 && msg.getType() == 0x22) { // UBX-NAV-CLOCK struct NavClock { uint32_t iTowMS; int32_t clkBNS; int32_t clkDNS; uint32_t tAcc; uint32_t fAcc; } nc; memcpy(&nc, &payload[0], sizeof(nc)); // cerr<<"Clock offset "<< nc.clkBNS<<" nanoseconds, drift "<< nc.clkDNS<<" nanoseconds/second, accuracy " << nc.tAcc<<" ns, frequency accuracy "<set_vendor("Ublox"); nmm.mutable_od()->set_hwversion(hwversion); nmm.mutable_od()->set_swversion(swversion); nmm.mutable_od()->set_serialno(serialno); nmm.mutable_od()->set_modules(mods); nmm.mutable_od()->set_clockoffsetns(nc.clkBNS); nmm.mutable_od()->set_clockoffsetdriftns(nc.clkDNS); nmm.mutable_od()->set_clockaccuracyns(nc.tAcc); nmm.mutable_od()->set_freqaccuracyps(nc.fAcc); nmm.mutable_od()->set_owner(owner); nmm.mutable_od()->set_remark(remark); nmm.mutable_od()->set_recvgithash(g_gitHash); nmm.mutable_od()->set_uptime(std::chrono::duration_cast(std::chrono::steady_clock::now()-starttime).count()); ns.emitNMM( nmm); } else if(msg.getClass() == 0x02 && msg.getType() == 0x14) { // UBX-RXM-MEASX // if (doDEBUG) { cerr<set_gnssid(2); // Galileo only for now nmm.mutable_sr()->set_gnsssv(payload[2]); nmm.mutable_sr()->set_sigid(1); // nmm.mutable_sr()->set_type(payload[1]); nmm.mutable_sr()->set_identifier(string((char*)payload.c_str()+4, 8)); nmm.mutable_sr()->set_code(payload[12]); nmm.mutable_sr()->set_params(string((char*)payload.c_str()+13, payload.size()-14)); string hexstring; for(int n = 0; n < 15; ++n) hexstring+=fmt::sprintf("%x", (int)getbitu(payload.c_str(), 36 + 4*n, 4)); ns.emitNMM(nmm); } else if(msg.getClass()==39 && msg.getType()==0) { NavMonMessage nmm; nmm.set_sourceid(g_srcid); nmm.set_localutcseconds(g_gnssutc.tv_sec); nmm.set_localutcnanoseconds(g_gnssutc.tv_nsec); nmm.set_type(NavMonMessage::DebuggingType); nmm.mutable_dm()->set_type(0); nmm.mutable_dm()->set_payload(string((char*)&payload[0], payload.size())); ns.emitNMM( nmm); } else if(msg.getClass() == 0x01 && msg.getType() == 0x3B) { // UBX-NAV-SVIN struct NavSin { uint8_t ver; uint8_t res[3]; uint32_t iTow; uint32_t dur; int32_t meanXCM, meanYCM, meanZCM; int8_t meanXHP, meanYHP, meanZHP; uint8_t res2; int32_t meanAcc; int32_t obs; int8_t valid; int8_t active; uint8_t res3[2]; } __attribute__((packed)); NavSin NS; static NavSin lastNS; if(payload.size() != sizeof(NS)) { cerr<<"Wrong NAV-SVIN message size, skipping"<(latlonh)<<" lon "<< get<1>(latlonh) << " h " << get<2>(latlonh) << endl; } lastNS = NS; } else if(msg.getClass() == 0x0d && msg.getType() == 0x04) { // UBX-TIM-SVIN struct TimSin { uint32_t dur; int32_t meanXCM, meanYCM, meanZCM; uint32_t meanVar; uint32_t obs; int8_t valid; int8_t active; uint8_t res3[2]; } __attribute__((packed)); TimSin TS; static TimSin lastTS; if(payload.size() != sizeof(TS)) { cerr<<"Wrong NAV-SVIN message size, skipping"<set_noiseperms(mhw.noisePerMS); nmm.mutable_ujs()->set_agccnt(mhw.agcCnt); nmm.mutable_ujs()->set_flags(mhw.flags); nmm.mutable_ujs()->set_jamind(mhw.jamInd); ns.emitNMM(nmm); } else if(msg.getClass() == 0x01 && msg.getType() == 0x25) { // UBX-NAV-TIMEGAL memcpy(&tstate.gal, &payload[0], sizeof(TIMEGAL)); // cerr << "TIMEGAL itow: "<