Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,20 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
} else {
strcpy(reply, "gps provider not found");
}
} else if (strcmp(command, "gps diag") == 0) {
LocationProvider * l = _sensors->getLocationProvider();
if (l != NULL) {
const char* setting = _sensors->getSettingByKey("gps");
bool requested = setting != NULL && strcmp(setting, "1") == 0;
int prefix_len = snprintf(reply, 160, "req:%u ", requested ? 1U : 0U);
if (prefix_len < 0 || prefix_len >= 160) {
reply[159] = 0;
} else {
l->formatDiagnostics(reply + prefix_len, 160 - prefix_len);
}
} else {
strcpy(reply, "gps provider not found");
}
} else if (memcmp(command, "gps setloc", 10) == 0) {
_prefs->node_lat = _sensors->node_lat;
_prefs->node_lon = _sensors->node_lon;
Expand Down
12 changes: 12 additions & 0 deletions src/helpers/sensors/LocationProvider.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "Mesh.h"
#include <stddef.h>
#include <stdio.h>


class LocationProvider {
Expand All @@ -22,4 +24,14 @@ class LocationProvider {
virtual void stop() = 0;
virtual void loop() = 0;
virtual bool isEnabled() = 0;

// Format compact diagnostics in the caller-provided buffer. Providers that
// have more information (for example UART counters) can override this.
virtual void formatDiagnostics(char* out, size_t out_size) {
if (out_size == 0) return;
snprintf(out, out_size, "en:%u sat:%ld fix:%u",
isEnabled() ? 1U : 0U,
satellitesCount(),
isValid() ? 1U : 0U);
}
};
84 changes: 83 additions & 1 deletion src/helpers/sensors/MicroNMEALocationProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <MicroNMEA.h>
#include <RTClib.h>
#include <helpers/RefCountedDigitalPin.h>
#include <string.h>

#ifndef GPS_EN
#ifdef PIN_GPS_EN
Expand Down Expand Up @@ -51,6 +52,32 @@ class MicroNMEALocationProvider : public LocationProvider {
unsigned long _last_time_sync = 0;
static const unsigned long TIME_SYNC_INTERVAL = 1800000; // Re-sync every 30 minutes

// Observation-only diagnostics. These fields do not change GPS power,
// timing, reset behaviour, or parser input.
uint32_t _uart_bytes = 0;
uint32_t _nmea_checksum_ok = 0;
uint32_t _nmea_checksum_bad = 0;
uint32_t _begin_calls = 0;
uint32_t _stop_calls = 0;
unsigned long _last_uart_ms = 0;
unsigned long _last_nmea_ms = 0;
unsigned long _last_fix_ms = 0;
bool _uart_seen = false;
bool _nmea_seen = false;
bool _fix_seen = false;

static uint32_t ageMs(unsigned long timestamp) {
return (uint32_t)(millis() - timestamp);
}

static void formatAge(char* out, size_t out_size, bool seen, unsigned long timestamp) {
if (!seen) {
snprintf(out, out_size, "never");
} else {
snprintf(out, out_size, "%lu", (unsigned long)ageMs(timestamp));
}
}

public :
MicroNMEALocationProvider(Stream& ser, mesh::RTCClock* clock = NULL, int pin_reset = GPS_RESET, int pin_en = GPS_EN,RefCountedDigitalPin* peripher_power=NULL) :
nmea(_nmeaBuffer, sizeof(_nmeaBuffer)), _clock(clock), _gps_serial(&ser), _peripher_power(peripher_power), _pin_reset(pin_reset), _pin_en(pin_en) {
Expand All @@ -76,6 +103,7 @@ public :
}

void begin() override {
_begin_calls++;
claim();
if (_pin_en != -1) {
digitalWrite(_pin_en, GPS_EN_ACTIVE);
Expand All @@ -94,6 +122,7 @@ public :
}

void stop() override {
_stop_calls++;
if (_pin_en != -1) {
digitalWrite(_pin_en, !GPS_EN_ACTIVE);
}
Expand Down Expand Up @@ -137,10 +166,36 @@ public :

while (_gps_serial->available()) {
char c = _gps_serial->read();
_uart_bytes++;
_last_uart_ms = millis();
_uart_seen = true;
#ifdef GPS_NMEA_DEBUG
Serial.print(c);
#endif
nmea.process(c);
bool parsed = nmea.process(c);

// MicroNMEA leaves the completed sentence in its buffer after the
// first line terminator. CRLF therefore counts once: CR observes the
// sentence, then LF clears the now-empty buffer.
if ((c == '\0' || c == '\r' || c == '\n') && nmea.getSentence()[0] != '\0') {
if (MicroNMEA::testChecksum(nmea.getSentence())) {
_nmea_checksum_ok++;
_last_nmea_ms = millis();
_nmea_seen = true;

// Refresh fix age only when a newly parsed GGA/RMC sentence
// carries a valid fix. Other valid NMEA sentences must not
// make an old position look fresh.
const char* message_id = nmea.getMessageID();
if (parsed && isValid() &&
(strcmp(message_id, "GGA") == 0 || strcmp(message_id, "RMC") == 0)) {
_last_fix_ms = millis();
_fix_seen = true;
}
} else {
_nmea_checksum_bad++;
}
}
}

if (!isValid()) time_valid = 0;
Expand All @@ -163,4 +218,31 @@ public :
}
}
}

void formatDiagnostics(char* out, size_t out_size) override {
if (out_size == 0) return;

char uart_age[11];
char nmea_age[11];
char fix_age[11];
formatAge(uart_age, sizeof(uart_age), _uart_seen, _last_uart_ms);
formatAge(nmea_age, sizeof(nmea_age), _nmea_seen, _last_nmea_ms);
formatAge(fix_age, sizeof(fix_age), _fix_seen, _last_fix_ms);

// Compact enough for CommonCLI's 160-byte reply buffer, including the
// "req:" prefix added by CommonCLI.
snprintf(out, out_size,
"en:%u ub:%lu ua:%s ok:%lu bad:%lu na:%s sat:%ld fix:%u fa:%s bc:%lu sc:%lu",
isEnabled() ? 1U : 0U,
(unsigned long)_uart_bytes,
uart_age,
(unsigned long)_nmea_checksum_ok,
(unsigned long)_nmea_checksum_bad,
nmea_age,
satellitesCount(),
isValid() ? 1U : 0U,
fix_age,
(unsigned long)_begin_calls,
(unsigned long)_stop_calls);
}
};