#define _LARGEFILE64_SOURCE #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" struct timespec g_gstutc; uint16_t g_wn; using namespace std; uint16_t g_srcid{2}; #define BAUDRATE B115200 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; } /* inav schedule: 1) Find plausible start time of next cycle Current cycle: TOW - (TOW%30) Next cycle: TOW - (TOW%30) + 30 t n w 0 1: 2 wn % 30 == 0 2 2: 4 wn % 30 == 2 4 3: 6 WN/TOW 4 -> set startTow, startTowFresh 6 4: 7/9 8 5: 8/10 10 6: 0 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 WN/TOW 26 14: 0 WN/TOW 28 15: 0 WN/TOW */ /* 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 = "< 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) { static int logfile; 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 = readn2(fd, marker+1, 1); if(res < 0) throw EofException(); // cerr<<"marker now: "<< (int)marker[0]<<" " <<(int)marker[1]< msg; msg.append(marker, 2); // 0,1 uint8_t b[4]; readn2(fd, b, 4); msg.append(b, 4); // class, type, len1, len2 uint16_t len = b[2] + 256*b[3]; // cerr<<"Got class "<<(int)msg[2]<<" type "<<(int)msg[3]<<", len = "< seconds) { throw TimeoutError(); } if(msg.getClass() != 5 || !(msg.getType() == 0 || msg.getType() == 1)) { cerr<<"Got: "<<(int)msg.getClass() << " " <<(int)msg.getType() <<" while waiting for ack/nack of " << ubxClass<<" "< serial; bool doGPS{true}, doGalileo{true}, doGlonass{false}, doBeidou{true}, doReset{false}, doWait{false}, doRTSCTS{true}, doSBAS{false}; #ifdef OpenBSD doRTSCTS = false; #endif app.add_option("serial", serial, "Serial"); app.add_flag("--wait", doWait, "Wait a bit, do not try to read init messages"); 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"); try { app.parse(argc, argv); } catch(const CLI::Error &e) { return app.exit(e); } if(serial.size() != 2) { cout< msg; if(doReset) { cerr<<"Sending a soft reset"<, struct timeval> lasttv, tv; int curCycleTOW{-1}; // means invalid cerr<<"Entering main loop"< %.4f or %d:%f\n", tm.tm_hour, tm.tm_min, seconds, satutc, timegm(&tm), pvt.nano/1000.0); if(!g_fromFile) { // struct tm ourtime; // time_t ourt = timestamp.tv_sec; // gmtime_r(&ourt, &ourtime); // //double ourutc = ourt + timestamp.tv_usec/1000000.0; // seconds = ourtime.tm_sec + timestamp.tv_usec/1000000.0; // fmt::fprintf(stderr, "Our UTC : %02d:%02d:%06.4f -> %.4f or %d:%f -> delta = %.4fs\n", tm.tm_hour, tm.tm_min, seconds, ourutc, timestamp.tv_sec, 1.0*timestamp.tv_usec, ourutc - satutc); } } else if(msg.getClass() == 0x02 && msg.getType() == 0x15) { // RAWX // cerr<<"Got "<<(int)payload[11] <<" measurements "<set_gnssid(gnssid); nmm.mutable_rfd()->set_gnsssv(sv); 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); emitNMM(1, 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)); /* cerr<<"Position: ("<< p.ecefX / 100000.0<<", " << p.ecefY / 100000.0<<", " << p.ecefZ / 100000.0<<") +- "<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_acccm(p.pAcc /100.0); emitNMM(1, 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]); static set> svseen; static time_t lastStat; svseen.insert(id); if(time(0)- lastStat > 30) { cerr<<"src "<set_gnsswn(wn); // XXX this sucks 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())); emitNMM(1, nmm); continue; } else if(id.first ==2) { auto inav = getInavFromSFRBXMsg(payload); unsigned int wtype = getbitu(&inav[0], 0, 6); tv[id] = timestamp; // cerr<<"gnssid "<set_gnsswn(g_wn); nmm.mutable_gi()->set_gnsstow(msgTOW); nmm.mutable_gi()->set_gnssid(id.first); nmm.mutable_gi()->set_gnsssv(id.second); nmm.mutable_gi()->set_contents((const char*)&inav[0], inav.size()); emitNMM(1, 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) { cerr<<"BeiDou C"< 5) { // this **HARDCODES** that C01,02,03,04,05 emit D2 messages! nmm.set_type(NavMonMessage::BeidouInavTypeD1); nmm.mutable_bid1()->set_gnsswn(bm.wn); 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_contents(string((char*)gstr.c_str(), gstr.size())); } else { 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_contents(string((char*)gstr.c_str(), gstr.size())); } emitNMM(1, nmm); continue; } else if(id.first==6) { // cerr<<"SFRBX from GLONASS "< gms; GlonassMessage& gm = gms[id.second]; int strno = gm.parse(gstr); */ if(id.second != 255) { NavMonMessage nmm; nmm.set_localutcseconds(g_gstutc.tv_sec); nmm.set_localutcnanoseconds(g_gstutc.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_contents(string((char*)gstr.c_str(), gstr.size())); emitNMM(1, nmm); } } else cerr<<"SFRBX from unsupported GNSSID "< %d:%d, delta=%d\n", payload[0], payload[1], wtype, lasttv[id].tv_sec, lasttv[id].tv_usec, tv[id].tv_sec, tv[id].tv_usec, tv[id].tv_usec - lasttv[id].tv_usec); } #endif lasttv[id]=tv[id]; } catch(CRCMismatch& cm) { cerr<<"Had CRC mismatch!"<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); emitNMM(1, nmm); } } // writen2(1, payload.d_raw.c_str(),msg.d_raw.size()); } catch(UBXMessage::BadChecksum &e) { cerr<<"Bad UBX checksum, skipping message"<