Public Access
The GNSS Service reads the receiver (UART 15/13, 115200, 1 KB buffer) from its 50 ms tick and feeds the NMEA parser. With a Fix it sets the clock (GNSS is the most trusted TimeSource) and refreshes it every 10 min. Settings gets GNSS On/Off and the coordinate format (Q64). Off puts the receiver in standby with $PCAS12,65535, renewed hourly; On wakes it with a hot start, $PCAS10,0. Both measured on the device: the output stops within a second, and a command wakes it within a second. Commands: gnss status (Fix, satellites per constellation, bytes and sentences), gnss nmea on|off, gnss send <sentence>, gnss restart. The probe is gone; never drive GPIO 15 (the receiver's output): the first probe's swapped-pin attempt silenced it until a power cycle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
83 lines
2.9 KiB
C++
83 lines
2.9 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#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);
|
|
|
|
// Called with every complete line fed in, valid or not (`gnss nmea on` echoes them).
|
|
std::function<void(const std::string&)> onLine;
|
|
|
|
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
|