Public Access
lib/gnss: RMC, GGA, GSA and GSV into one GnssState: Fix type, position, altitude, speed, course, HDOP, UTC time (trusted only with a Fix) and the satellites in view across constellations. GSV sequences are kept per constellation and signal band and merged per satellite with the stronger SNR; GSA's system ID marks which satellites are used. 16 tests, using lines captured from the device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
79 lines
2.7 KiB
C++
79 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace roro::gnss {
|
|
|
|
enum class Constellation : uint8_t { Unknown, Gps, Glonass, Galileo, BeiDou, Qzss, Navic };
|
|
const char* constellationName(Constellation c);
|
|
|
|
enum class FixType : uint8_t { None, TwoD, ThreeD };
|
|
|
|
struct Satellite {
|
|
Constellation system = Constellation::Unknown;
|
|
int prn = 0;
|
|
int elevation = 0; // degrees above the horizon
|
|
int azimuth = 0; // degrees from north, clockwise
|
|
int snr = -1; // dB-Hz; -1 when not tracked
|
|
bool used = false; // part of the current Fix
|
|
};
|
|
|
|
// Everything the receiver has said, as of the last sentence (see CONTEXT.md: Fix).
|
|
struct GnssState {
|
|
FixType fix = FixType::None;
|
|
bool positionValid = false;
|
|
double latitude = 0, longitude = 0; // degrees, south and west negative
|
|
bool altitudeValid = false;
|
|
float altitudeM = 0; // above mean sea level
|
|
float speedKmh = 0;
|
|
bool courseValid = false;
|
|
float courseDeg = 0;
|
|
int satellitesUsed = 0;
|
|
float hdop = 99.9f;
|
|
// UTC, trusted only with a Fix: the receiver reports a time without one, from its own clock.
|
|
bool timeValid = false;
|
|
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
|
|
std::vector<Satellite> satellites; // in view, all constellations, one entry per satellite
|
|
|
|
int64_t utcSeconds() const; // seconds since 1970-01-01 UTC; meaningful when timeValid
|
|
};
|
|
|
|
// NMEA 0183 (4.10 style, as the Cap's AT6668 sends it): RMC, GGA, GSA and GSV, with the
|
|
// constellation taken from the talker ID or GSA's system ID field. Host-tested; no hardware here.
|
|
class NmeaParser {
|
|
public:
|
|
// Raw bytes from the UART, in any chunks.
|
|
void feed(const char* data, size_t len);
|
|
// One sentence without its line end. False if malformed or its checksum is wrong.
|
|
bool sentence(const std::string& line);
|
|
|
|
const GnssState& state() const { return state_; }
|
|
uint32_t goodSentences() const { return good_; }
|
|
uint32_t badSentences() const { return bad_; }
|
|
|
|
private:
|
|
using Fields = std::vector<std::string>;
|
|
void rmc(const Fields& f);
|
|
void gga(const Fields& f);
|
|
void gsa(const Fields& f, Constellation talker);
|
|
void gsv(const Fields& f, Constellation talker);
|
|
void rebuildSatellites();
|
|
|
|
static constexpr size_t kMaxLine = 120;
|
|
std::string line_;
|
|
bool overflow_ = false;
|
|
uint32_t good_ = 0, bad_ = 0;
|
|
GnssState state_;
|
|
// GSV sequences per (constellation, signal): pending until their last message.
|
|
std::map<std::pair<int, int>, std::vector<Satellite>> pending_, inView_;
|
|
std::map<int, std::set<int>> used_; // per constellation, from GSA
|
|
};
|
|
|
|
} // namespace roro::gnss
|