Files
roro9stack/src/services/gnss_service.h
T
twislaandClaude Opus 5.5 b7aed8e91c F1 steps 2-6: the Storage App, its viewers, and Maintenance moved in (#3)
The Storage App browses the SD card: folders first with sizes and dates,
three sorts, one item at a time with a clipboard (c, x, v), rename, delete
after counting what's inside, new folder, details. A listing holds 256
entries and says when a folder has more.

FileOps does the card's work for the App and the console alike, one
operation at a time on the storage task in turns of about 150 ms, so Logs
and Captures are still written during a long copy. A copy shows progress,
can be cancelled (what it wrote is taken back) and compares sizes after.
The read-only rules are checked there: the firmware's top-level folders,
/gemini/cache, and files being written (a Track, a Capture, an upload,
today's IRC Logs). A listing reads the folder straight from FatFs: through
the Arduino File, 329 entries took over two seconds.

Viewers by type: text read a screen at a time whatever the file's size
(logs open at the end), a hex dump, a Capture's packets as the LoRa Scanner
lists them, a Track's summary, and an Update File checked as an install
would check it, without writing anything. Tab shows any file as hex or text.

Settings > Storage is gone: usage, Storage Clean-up and Erase are the App's
Maintenance, behind a warning. The Storage Warning points there.

The Clock sets the system time whatever its source, so files are dated
correctly with a GNSS Fix alone (Q137).

Console: cp, mv, mkdir, du, cancel; rm takes folders and follows the rules;
ls shows dates; Debug Builds get `sd fill`.

424 host tests. Checked on the device: docs/milestones/F1.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 08:45:43 +02:00

90 lines
3.7 KiB
C++

#pragma once
#include <Print.h>
#include <string>
#include "event_bus.h"
#include "nmea_parser.h"
#include "service.h"
#include "services/clock_service.h"
#include "services/storage_service.h"
#include "settings.h"
#include "track.h"
namespace roro {
// The GNSS receiver on the Cap LoRa-1262 (see CONTEXT.md and docs/milestones/M2.md): reads its
// NMEA from the main loop's tick (about 450 bytes/s, so a 512-byte UART buffer covers a second),
// holds the current Fix, and sets the clock from it. Settings → GNSS switches it on and off.
class GnssService : public Service {
public:
static constexpr int kRxPin = 15, kTxPin = 13; // measured: M2 step 1
static constexpr uint32_t kBaud = 115200;
static constexpr uint32_t kSilentAfterMs = 3000; // no sentence for this long: receiver silent
static constexpr uint32_t kClockEveryMs = 600000; // refresh the clock from the Fix this often
// Off (Q58, measured): "$PCAS12,<s>" puts the receiver in standby for that long, and any
// command wakes it. Standby is renewed while off, in case the duration is clamped.
static constexpr uint32_t kStandbyRenewMs = 3600000;
GnssService(const Settings& settings, ClockService& clock, StorageService& storage, EventBus& bus);
const char* name() const override { return "gnss"; }
uint32_t tickIntervalMs() const override { return 50; }
void start() override;
void stop() override;
void tick(uint32_t nowMs) override;
bool on() const { return active_; }
// Standby while the LoRa radio listens, when the user chose that (issue #20): the receiver
// raises the radio's noise floor by 8 dB while it runs. Never asked for during a Track.
void holdForLora(bool hold) { held_ = hold; }
bool heldForLora() const { return held_ && !active_; }
bool receiving(uint32_t nowMs) const { return active_ && lastLineMs_ && nowMs - lastLineMs_ < kSilentAfterMs; }
const gnss::GnssState& state() const { return parser_.state(); }
uint32_t onSinceMs() const { return openedMs_; } // when the receiver was last woken
// Debug aids (Q68): raw sentences to the console, a status report, and sending a command
// ("PCAS10,0" becomes "$PCAS10,0*1C\r\n").
void setEcho(bool echo) { echo_ = echo; }
void printStatus(Print& out, uint32_t nowMs) const;
bool send(const std::string& body);
// Tracks (Q63): GPX on the SD card, a point every 5 s once moved 5 m, recorded in the
// background whatever App is open. startTrack() returns why it can't, or "".
std::string startTrack(uint32_t nowMs);
void stopTrack();
bool tracking() const { return !trackPath_.empty(); }
const std::string& trackPath() const { return trackPath_; }
int trackPoints() const { return trackPoints_; }
uint32_t trackStartMs() const { return trackStartMs_; }
void restart(uint32_t nowMs) {
close();
open(nowMs);
}
private:
void open(uint32_t nowMs); // the UART; the receiver's state follows the setting in tick()
void close();
void wake(uint32_t nowMs);
void recordPoint(uint32_t nowMs);
void closeUnfinishedTracks(); // on the storage task
void notify(const std::string& text);
void standby(uint32_t nowMs);
const Settings& settings_;
ClockService& clock_;
StorageService& storage_;
EventBus& bus_;
gnss::NmeaParser parser_;
bool open_ = false, active_ = false, echo_ = false, held_ = false;
uint32_t bytes_ = 0, standbyMs_ = 0;
uint32_t openedMs_ = 0, lastLineMs_ = 0, firstFixMs_ = 0, clockSetMs_ = 0;
gnss::FixType lastFix_ = gnss::FixType::None;
std::string trackPath_;
int trackPoints_ = 0;
uint32_t trackStartMs_ = 0;
gnss::TrackRule trackRule_;
};
} // namespace roro