mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-16 06:52:40 +00:00
gps "fiddling" v2 and some debug log fixes
This commit is contained in:
@@ -764,6 +764,19 @@ config ZEPHCORE_REPEATER_GPS_INTERVAL_SEC
|
||||
overrides it with "set gps duty <sec>" (persisted to flash).
|
||||
Companions default to ZEPHCORE_GPS_POLL_INTERVAL_SEC (300s) instead.
|
||||
|
||||
config ZEPHCORE_GPS_NMEA_DUMP
|
||||
bool "Log every NMEA sentence received from the GNSS module"
|
||||
help
|
||||
Print each sentence the GNSS driver parses, plus everything else the
|
||||
module emits, to the console. For diagnosing a receiver that reports
|
||||
no satellites: it distinguishes a module that is talking normally
|
||||
while it searches the sky from one that is silent, at the wrong baud,
|
||||
or has had its NMEA output disabled — cases that are otherwise
|
||||
indistinguishable from the application.
|
||||
|
||||
Verbose: at 1 Hz with multi-GNSS this is a continuous stream, and on
|
||||
a repeater it shares the console with the CLI. Diagnostic builds only.
|
||||
|
||||
config ZEPHCORE_GPS_NAV_MODE
|
||||
int "GNSS navigation dynamic model (CASIC $PCAS11)"
|
||||
range -1 7
|
||||
|
||||
@@ -147,17 +147,18 @@ get gps diag # read it back
|
||||
Sample reply:
|
||||
|
||||
```
|
||||
> diag=on cfg=uart age=910s mod=URANUS5 sent=12/336B sys=G3/R4/E0/B3/?0
|
||||
> diag=on cfg=uart age=910s rx=120 mod=URANUS5 sent=12/336B sys=G3/R4/E0/B3/?0
|
||||
```
|
||||
|
||||
- `rx=` NMEA sentences the driver has parsed. **Check this first** — it is the only field that cannot be misread. Non-zero means the module is alive, at the right baud, and talking, so anything still wrong is signal or antenna. Zero means nothing is arriving at all, and no antenna work will help
|
||||
- `cfg=` which path ran — `uart` (raw PMTK+PCAS+UBX), `api` (driver GNSS API), `blind` (neither available), or `never-run`
|
||||
- `mod=` module identification captured from the `$GPTXT` reply to a version query, or `no-reply`
|
||||
- `mod=` module identification, from a CASIC `$GPTXT` version reply or a u-blox `$PUBX` poll response, or `no-reply`. Only an explicit software-version token is accepted as an identity — TXT sentences also carry warnings, and a warning reported as an identity is worse than no answer
|
||||
- `sent=` commands/bytes written to the module (UART path), or `sys_ret=`/`rate_ret=` return codes (API path)
|
||||
- `sys=` tracked satellites per constellation from GSV talker IDs: **G**PS / GLONASS (**R**) / Galileo (**E**) / **B**eiDou / other. A constellation that stops reporting for 30 s decays to zero rather than showing a stale count
|
||||
|
||||
`sys=` totalling more than `sats=` in `get gps` is expected, not a discrepancy: GSV counts satellites **tracked**, GGA counts satellites **used in the fix solution**.
|
||||
|
||||
**`mod=` is the TX-path proof.** Everything else on this transport is written blind, so a module that hears nothing looks exactly like one that hears everything and ignores it. A version string means the module received a command and answered. `mod=no-reply` alongside a healthy `sats=` in `get gps` means the receive direction works but the module is not hearing us — wiring or pin assignment, not configuration.
|
||||
**`rx=` first, then `mod=`.** `rx=` is the only field that cannot be misread: non-zero means the module is alive, at the right baud and talking, so anything still wrong is signal or antenna; zero means nothing is arriving at all. `mod=` then tells you whether the module *heard* us — everything on this transport is written blind, so a module that hears nothing looks exactly like one that hears everything and ignores it. `mod=no-reply` with `rx=` climbing means the receive direction works but our transmit does not reach it: wiring or pin assignment, not configuration.
|
||||
|
||||
**`sent=` proves transmission, not acceptance.** Only `sys=` shows what the module actually did. A module still running its factory or previously saved configuration reports `G` non-zero with the rest at `0`. Note `B0` is expected on u-blox M8 (BeiDou is deliberately disabled — only three major constellations can run concurrently), and `?0` is normal outside Japan (QZSS is regional).
|
||||
|
||||
|
||||
@@ -494,6 +494,14 @@ static bool gps_diag_on = false;
|
||||
* MCU's TX line reaches the module at all. */
|
||||
extern "C" int zephcore_gnss_version_get(char *buf, size_t len) __attribute__((weak));
|
||||
|
||||
/* Count of NMEA sentences the GNSS driver has parsed. This is the one signal
|
||||
* in the whole diagnostic that cannot be misread: no satellites, a module
|
||||
* silenced by a disabled output protocol, and a module stranded at the wrong
|
||||
* baud all look identical otherwise. Non-zero means the module is alive and
|
||||
* talking at our baud, so anything still wrong is signal; zero means nothing
|
||||
* is arriving and no antenna work will change that. */
|
||||
extern "C" uint32_t zephcore_gnss_rx_count(void) __attribute__((weak));
|
||||
|
||||
/* True only while gps_configure_via_uart() is running (see gps_uart_send). */
|
||||
static bool gps_cfg_counting = false;
|
||||
|
||||
@@ -1429,8 +1437,23 @@ static const uint8_t pmtk_standby[] = "$PMTK161,0*28\r\n";
|
||||
* version=0, reserved[3]=0,
|
||||
* duration=0x00000000 (infinite),
|
||||
* flags=0x00000006 (backup + force),
|
||||
* wakeupSources=0x00000020 (UART RX)
|
||||
* Module stops all output and draws ~7µA. Wakes on any UART RX byte. */
|
||||
* wakeupSources=0x00000028 (uartrx bit3 | extint0 bit5)
|
||||
*
|
||||
* THE WAKE SOURCE BIT IS LOAD-BEARING. wakeupSources bit 3 is uartrx; bit 5
|
||||
* is extint0. This frame previously sent 0x20 — extint0 only — with a comment
|
||||
* claiming that was "UART RX (bit 5)". It is not. EXTINT is not routed to the
|
||||
* WisBlock connector on the RAK12500 (RAK's datasheet: only UART/I2C, 1PPS,
|
||||
* RESET, VDD and GND are connected), so the module was told to sleep forever
|
||||
* with a wake source that can never be asserted. duration=0 means infinite,
|
||||
* so it never came back: no NMEA at any baud, and no I2C either, because
|
||||
* backup mode powers down the DDC interface too. Only a physical power cycle
|
||||
* recovered it — a reboot does not, since the 3V3_S rail stays up.
|
||||
* Confirmed on hardware: reseat -> module answers -> one fix -> first standby
|
||||
* -> gone again.
|
||||
*
|
||||
* 0x28 sets both, so a board that does wire EXTINT keeps that path as well.
|
||||
* Module stops all output and draws ~20µA (ZOE-M8Q at 3V, datasheet Table 13;
|
||||
* the 15µA hardware-backup figure needs VCC removed entirely). */
|
||||
static const uint8_t ubx_pmreq_backup[] = {
|
||||
0xB5, 0x62, /* UBX sync chars */
|
||||
0x02, 0x41, /* Class: RXM, ID: PMREQ */
|
||||
@@ -1440,9 +1463,9 @@ static const uint8_t ubx_pmreq_backup[] = {
|
||||
0x00, 0x00, 0x00, /* reserved1[3] */
|
||||
0x00, 0x00, 0x00, 0x00, /* duration: 0 = infinite */
|
||||
0x06, 0x00, 0x00, 0x00, /* flags: backup(0x02) | force(0x04) */
|
||||
0x20, 0x00, 0x00, 0x00, /* wakeupSources: UART RX (bit 5) */
|
||||
0x28, 0x00, 0x00, 0x00, /* wakeupSources: uartrx(bit3) | extint0(bit5) */
|
||||
/* Checksum (Fletcher-8 over class..payload) */
|
||||
0x79, 0xCB
|
||||
0x81, 0xEB
|
||||
};
|
||||
|
||||
/* Put GPS module into software sleep (for boards without GPIO power control).
|
||||
@@ -2259,6 +2282,16 @@ void gps_get_diag_report(char *buf, size_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Sentences parsed from the module. Print this before anything else
|
||||
* that could be misinterpreted — rx=0 makes every other field moot. */
|
||||
if (zephcore_gnss_rx_count != NULL) {
|
||||
n += (size_t)snprintf(buf + n, len - n, " rx=%u",
|
||||
(unsigned)zephcore_gnss_rx_count());
|
||||
if (n >= len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Module identity, when the driver could obtain it. Present means the
|
||||
* module answered us, i.e. the TX path is good; absent on a driver that
|
||||
* asks is a strong hint the module never hears our configuration. */
|
||||
|
||||
@@ -589,7 +589,20 @@ void LoRaRadioBase::startReceive()
|
||||
atomic_set(&_in_recv_mode, 1);
|
||||
return;
|
||||
}
|
||||
if (ret != -ENOSYS) {
|
||||
if (ret == -EBUSY) {
|
||||
/* A concurrent TX owns the chip. Not the
|
||||
* CAD-busy case: when the LBT branch of
|
||||
* send_async restores RX in-driver it
|
||||
* leaves the chip in RX, and the driver's
|
||||
* idempotent fast-path (patch 0003)
|
||||
* re-arms duty cycle from there — AGC
|
||||
* reset included — rather than refusing.
|
||||
* So -EBUSY here means the radio is
|
||||
* genuinely mid-transmit; the fall-through
|
||||
* to lora_recv_async will fail the same
|
||||
* way and report it. */
|
||||
LOG_DBG("rxduty: busy (TX in progress) — continuous RX");
|
||||
} else if (ret != -ENOSYS) {
|
||||
LOG_ERR("lora_recv_duty_cycle failed: %d", ret);
|
||||
}
|
||||
/* Fall through to continuous RX */
|
||||
@@ -684,7 +697,16 @@ bool LoRaRadioBase::startSendRaw(const uint8_t *bytes, int len)
|
||||
|
||||
int ret = hwSendAsync(_tx_buf, (uint32_t)len, &_tx_signal);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("hwSendAsync failed: %d", ret);
|
||||
if (ret == -EBUSY) {
|
||||
/* LBT refused the transmit because the channel is busy
|
||||
* — the designed outcome, not a fault. The dispatcher
|
||||
* re-queues and retries. On a busy site this fires
|
||||
* constantly, and at ERR it buries real faults and
|
||||
* makes a healthy repeater look broken. */
|
||||
LOG_DBG("hwSendAsync: channel busy (LBT), re-queuing");
|
||||
} else {
|
||||
LOG_ERR("hwSendAsync failed: %d", ret);
|
||||
}
|
||||
_board->onAfterTransmit();
|
||||
atomic_set(&_tx_active, 0);
|
||||
/* startReceive() is safe to call here regardless of failure
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# GPS diagnostic overlay — dumps the raw NMEA stream to the USB console.
|
||||
#
|
||||
# For a receiver that reports no satellites. From the application's point of
|
||||
# view a module with no sky view, a module silenced by a disabled output
|
||||
# protocol, and a module stranded at a baud rate saved by a previous host all
|
||||
# look identical: zero satellites, no fix. Seeing the actual bytes separates
|
||||
# them in seconds.
|
||||
#
|
||||
# west build -b rak3401_1watt zephcore --pristine -- \
|
||||
# -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/gps_debug.conf"
|
||||
#
|
||||
# Then watch the USB serial port. Expect one burst per second:
|
||||
# NMEA: $GNRMC,... NMEA: $GNGGA,... NMEA: $GPGSV,...
|
||||
#
|
||||
# Sentences flowing -> module is alive at 9600; any remaining
|
||||
# problem is signal or antenna.
|
||||
# Nothing at all -> module is silent, at another baud, or not
|
||||
# connected. Check "get gps diag" (rx=0 confirms).
|
||||
# Garbled/partial lines -> wrong baud rate.
|
||||
#
|
||||
# Deliberately NOT boards/common/debug.conf: that routes logging to SEGGER RTT
|
||||
# only, which needs a debug probe. This sends it to the console UART, which on
|
||||
# these boards is the USB CDC port already used by the CLI.
|
||||
|
||||
# Logging to the console rather than RTT
|
||||
CONFIG_LOG=y
|
||||
CONFIG_LOG_BACKEND_UART=y
|
||||
CONFIG_LOG_BACKEND_RTT=n
|
||||
CONFIG_USE_SEGGER_RTT=n
|
||||
|
||||
# The GNSS driver logs the sentence dump and the module version at INF
|
||||
CONFIG_GNSS_LOG_LEVEL_INF=y
|
||||
CONFIG_ZEPHCORE_GPS_LOG_LEVEL_INF=y
|
||||
|
||||
# Dump every NMEA sentence received
|
||||
CONFIG_ZEPHCORE_GPS_NMEA_DUMP=y
|
||||
@@ -1052,7 +1052,7 @@ index 30243ba5dc7..f0794661515 100644
|
||||
if (ret < 0) {
|
||||
sx126x_set_sleep(dev);
|
||||
k_mutex_unlock(&data->lock);
|
||||
@@ -1083,14 +1718,671 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
@@ -1083,14 +1718,733 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1576,6 +1576,91 @@ index 30243ba5dc7..f0794661515 100644
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Explicit timing only. The window sizing (detection budget,
|
||||
+ * datasheet Tpreamble+Theader <= 2*rxPeriod+sleepPeriod rule, TCXO
|
||||
+ * transition accounting) is owned by the caller -- the adapter layer
|
||||
+ * knows the mesh preamble policy, the driver just programs periods.
|
||||
+ * Validated before we claim the state so a malformed request cannot
|
||||
+ * leave the chip half-configured. */
|
||||
+ if (K_TIMEOUT_EQ(rx_period, K_FOREVER) ||
|
||||
+ K_TIMEOUT_EQ(sleep_period, K_FOREVER)) {
|
||||
+ LOG_ERR("recv_duty_cycle: explicit rx/sleep periods required");
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Convert us to 15.625us steps */
|
||||
+ uint32_t rx_time = (k_ticks_to_us_ceil32(rx_period.ticks) * 64) / 1000;
|
||||
+ uint32_t sleep_time = (k_ticks_to_us_ceil32(sleep_period.ticks) * 64) / 1000;
|
||||
+
|
||||
+ if (rx_time < 64) rx_time = 64;
|
||||
+ if (sleep_time < 64) sleep_time = 64;
|
||||
+
|
||||
+ /* Idempotent fast-path: the chip is already in RX.
|
||||
+ *
|
||||
+ * The usual way here is the LBT branch of sx126x_lora_send_async: on
|
||||
+ * CAD-busy it calls sx126x_restart_rx() and returns -EBUSY, leaving
|
||||
+ * state == RX. Failing the CAS below would then report -EBUSY to a
|
||||
+ * caller whose only honest reading of it is "a concurrent TX owns the
|
||||
+ * chip". Worse, it is a one-way door: an entry that fell back to
|
||||
+ * continuous RX (any error path below that returns before
|
||||
+ * rx_duty_cycle_enabled is set) could never re-arm, because every
|
||||
+ * later attempt hits the same failed CAS -- the node quietly listens
|
||||
+ * at full RX current until it is rebooted.
|
||||
+ *
|
||||
+ * sx126x_restart_rx() runs the identical entry sequence to the one
|
||||
+ * below (Calibrate(ALL) AGC reset, CalibrateImage, DIO2 RF switch,
|
||||
+ * StopTimerOnPreamble, RX-gain retention, SetRxDutyCycle), so re-arming
|
||||
+ * through it gives up nothing. When the chip is already duty cycling
|
||||
+ * on these exact periods there is nothing to re-arm and only the
|
||||
+ * callback is refreshed: no SPI traffic, no ~5 ms calibration, no
|
||||
+ * dropped RX window. */
|
||||
+ if (atomic_get(&data->state) == SX126X_STATE_RX) {
|
||||
+ const bool rearm = !data->rx_duty_cycle_enabled ||
|
||||
+ data->dc_rx_time != rx_time ||
|
||||
+ data->dc_sleep_time != sleep_time;
|
||||
+
|
||||
+ data->rx_cb = cb;
|
||||
+ data->rx_cb_user_data = user_data;
|
||||
+ atomic_inc(&data->rx_cb_gen);
|
||||
+
|
||||
+ if (!rearm) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ data->dc_rx_time = rx_time;
|
||||
+ data->dc_sleep_time = sleep_time;
|
||||
+ data->rx_duty_cycle_enabled = true;
|
||||
+
|
||||
+ /* Mid-cycle the chip may be in its duty-cycle sleep phase with
|
||||
+ * BUSY asserted; talking to it then stalls the HAL in
|
||||
+ * wait_busy(). Poke CS first, same as the cancel path above. */
|
||||
+ if (sx126x_hal_is_busy(dev)) {
|
||||
+ sx126x_hal_wakeup(dev);
|
||||
+ }
|
||||
+
|
||||
+ ret = sx126x_restart_rx(dev, data);
|
||||
+ if (ret < 0) {
|
||||
+ /* Hand the chip back un-owned so the caller's
|
||||
+ * continuous-RX fallback performs a full clean entry
|
||||
+ * rather than fast-pathing onto a radio we have just
|
||||
+ * failed to program. */
|
||||
+ data->rx_duty_cycle_enabled = false;
|
||||
+ data->rx_cb = NULL;
|
||||
+ data->rx_cb_user_data = NULL;
|
||||
+ atomic_inc(&data->rx_cb_gen);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ LOG_DBG("recv_duty_cycle: re-armed in place rx=%uus sleep=%uus",
|
||||
+ (rx_time * 1000) / 64, (sleep_time * 1000) / 64);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_RX)) {
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ return -EBUSY;
|
||||
@@ -1649,31 +1734,8 @@ index 30243ba5dc7..f0794661515 100644
|
||||
+
|
||||
+ sx126x_set_rf_path(dev, true, false);
|
||||
+
|
||||
+ uint32_t rx_time, sleep_time;
|
||||
+
|
||||
+ /* Explicit timing only. The window sizing (detection budget,
|
||||
+ * datasheet Tpreamble+Theader <= 2*rxPeriod+sleepPeriod rule, TCXO
|
||||
+ * transition accounting) is owned by the caller -- the adapter layer
|
||||
+ * knows the mesh preamble policy, the driver just programs periods. */
|
||||
+ if (K_TIMEOUT_EQ(rx_period, K_FOREVER) ||
|
||||
+ K_TIMEOUT_EQ(sleep_period, K_FOREVER)) {
|
||||
+ LOG_ERR("recv_duty_cycle: explicit rx/sleep periods required");
|
||||
+ data->rx_cb = NULL;
|
||||
+ atomic_inc(&data->rx_cb_gen);
|
||||
+ sx126x_set_sleep(dev);
|
||||
+ k_mutex_unlock(&data->lock);
|
||||
+ atomic_set(&data->state, SX126X_REST_STATE);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Convert us to 15.625us steps */
|
||||
+ rx_time = (k_ticks_to_us_ceil32(rx_period.ticks) * 64) / 1000;
|
||||
+ sleep_time = (k_ticks_to_us_ceil32(sleep_period.ticks) * 64) / 1000;
|
||||
+
|
||||
+ if (rx_time < 64) rx_time = 64;
|
||||
+ if (sleep_time < 64) sleep_time = 64;
|
||||
+
|
||||
+ /* Store for restart_rx to re-use */
|
||||
+ /* Store for restart_rx to re-use (periods validated and converted at
|
||||
+ * function entry, before the state was claimed). */
|
||||
+ data->dc_rx_time = rx_time;
|
||||
+ data->dc_sleep_time = sleep_time;
|
||||
+ data->rx_duty_cycle_enabled = true;
|
||||
|
||||
@@ -43,7 +43,7 @@ index c5d09261da5..94ff2848b88 100644
|
||||
+
|
||||
endif
|
||||
diff --git a/drivers/gnss/gnss_luatos_air530z.c b/drivers/gnss/gnss_luatos_air530z.c
|
||||
index 74708edbd62..7fc5e4768f1 100644
|
||||
index 74708edbd62..82320abc125 100644
|
||||
--- a/drivers/gnss/gnss_luatos_air530z.c
|
||||
+++ b/drivers/gnss/gnss_luatos_air530z.c
|
||||
@@ -9,7 +9,6 @@
|
||||
@@ -132,7 +132,7 @@ index 74708edbd62..7fc5e4768f1 100644
|
||||
MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(init_script, init_script_cmds, NULL, 5);
|
||||
|
||||
struct gnss_luatos_air530z_config {
|
||||
@@ -73,12 +131,57 @@ struct gnss_luatos_air530z_data {
|
||||
@@ -73,12 +131,81 @@ struct gnss_luatos_air530z_data {
|
||||
struct k_sem lock;
|
||||
};
|
||||
|
||||
@@ -177,10 +177,36 @@ index 74708edbd62..7fc5e4768f1 100644
|
||||
+ return (int)strlen(buf);
|
||||
+}
|
||||
+#endif /* CONFIG_GNSS_LUATOS_AIR530Z_VERSION_QUERY */
|
||||
+
|
||||
+/* Raw sentence counter — see the equivalent note in gnss_nmea_generic.c.
|
||||
+ * Non-zero means the module is alive, at the right baud, and emitting
|
||||
+ * parseable NMEA; zero means nothing is arriving at all. */
|
||||
+static uint32_t air530z_rx_sentences;
|
||||
+
|
||||
+static void air530z_gga_cb(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ air530z_rx_sentences++;
|
||||
+ gnss_nmea0183_match_gga_callback(chat, argv, argc, user_data);
|
||||
+}
|
||||
+
|
||||
+static void air530z_rmc_cb(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ air530z_rx_sentences++;
|
||||
+ gnss_nmea0183_match_rmc_callback(chat, argv, argc, user_data);
|
||||
+}
|
||||
+
|
||||
+__weak uint32_t zephcore_gnss_rx_count(void)
|
||||
+{
|
||||
+ return air530z_rx_sentences;
|
||||
+}
|
||||
+
|
||||
MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
|
||||
- MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
|
||||
- MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
|
||||
+ MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", air530z_gga_cb),
|
||||
+ MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", air530z_rmc_cb),
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
|
||||
#endif
|
||||
@@ -190,7 +216,7 @@ index 74708edbd62..7fc5e4768f1 100644
|
||||
);
|
||||
|
||||
static void luatos_air530z_lock(const struct device *dev)
|
||||
@@ -217,54 +320,11 @@ static int gnss_luatos_air530z_init(const struct device *dev)
|
||||
@@ -217,54 +344,11 @@ static int gnss_luatos_air530z_init(const struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -250,7 +276,7 @@ index 74708edbd62..7fc5e4768f1 100644
|
||||
|
||||
static int luatos_air530z_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
|
||||
{
|
||||
@@ -356,10 +416,8 @@ static DEVICE_API(gnss, gnss_api) = {
|
||||
@@ -356,10 +440,8 @@ static DEVICE_API(gnss, gnss_api) = {
|
||||
.dynamic_separators_buf = {',', '*'}, \
|
||||
}; \
|
||||
\
|
||||
@@ -282,10 +308,10 @@ index d4e7b502e39..c8e8f2da2d2 100644
|
||||
*sv_system = GNSS_SYSTEM_GPS;
|
||||
break;
|
||||
diff --git a/drivers/gnss/gnss_nmea_generic.c b/drivers/gnss/gnss_nmea_generic.c
|
||||
index bef7989828d..5e5a90716fb 100644
|
||||
index bef7989828d..2042c85a2ee 100644
|
||||
--- a/drivers/gnss/gnss_nmea_generic.c
|
||||
+++ b/drivers/gnss/gnss_nmea_generic.c
|
||||
@@ -50,12 +50,83 @@ struct gnss_nmea_generic_data {
|
||||
@@ -50,11 +50,163 @@ struct gnss_nmea_generic_data {
|
||||
uint8_t *chat_argv[CHAT_ARGV_SZ];
|
||||
};
|
||||
|
||||
@@ -307,8 +333,7 @@ index bef7989828d..5e5a90716fb 100644
|
||||
+ ARG_UNUSED(chat);
|
||||
+ ARG_UNUSED(user_data);
|
||||
+
|
||||
+ /* Prefer an explicit software-version token; otherwise keep the last
|
||||
+ * text field, which is where u-blox put their boot banner. */
|
||||
+ /* Only an explicit software-version token counts as an identity. */
|
||||
+ for (uint16_t i = 1; i < argc; i++) {
|
||||
+ if (strncmp(argv[i], "SW=", 3) == 0) {
|
||||
+ strncpy(nmea_generic_version, argv[i] + 3,
|
||||
@@ -319,10 +344,12 @@ index bef7989828d..5e5a90716fb 100644
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (argc > 4 && nmea_generic_version[0] == '\0') {
|
||||
+ strncpy(nmea_generic_version, argv[4], sizeof(nmea_generic_version) - 1);
|
||||
+ nmea_generic_version[sizeof(nmea_generic_version) - 1] = '\0';
|
||||
+ }
|
||||
+ /* Deliberately NO generic fallback to "keep the last text field".
|
||||
+ * TXT sentences also carry warnings and errors — a module complaining
|
||||
+ * about a sentence it did not understand was being captured and then
|
||||
+ * reported as though it were the module's identity. A diagnostic that
|
||||
+ * invents an answer is worse than one admitting it has none, so an
|
||||
+ * unrecognised TXT is ignored and the field reads as no-reply. */
|
||||
+}
|
||||
+
|
||||
+/* u-blox reply to a $PUBX poll. u-blox ignore the CASIC $PCAS06 query, so
|
||||
@@ -357,15 +384,96 @@ index bef7989828d..5e5a90716fb 100644
|
||||
+ buf[len - 1] = '\0';
|
||||
+ return (int)strlen(buf);
|
||||
+}
|
||||
+
|
||||
+/* Raw sentence dump for field diagnosis (CONFIG_ZEPHCORE_GPS_NMEA_DUMP).
|
||||
+ *
|
||||
+ * Reconstructs the sentence from the parsed argv and logs it. Enabled only
|
||||
+ * for deliberate debugging: at 1 Hz with multi-GNSS this is a lot of console
|
||||
+ * traffic, and on a repeater the console is shared with the CLI.
|
||||
+ *
|
||||
+ * Matching is first-match-wins, so the ordinary GGA/RMC/GSV matches never
|
||||
+ * reach a catch-all placed after them — hence the logging lives in the
|
||||
+ * wrappers as well as in the catch-all, which then covers everything else the
|
||||
+ * module emits (TXT banners, PUBX replies, sentences we do not parse).
|
||||
+ */
|
||||
+#ifdef CONFIG_ZEPHCORE_GPS_NMEA_DUMP
|
||||
+static void nmea_generic_dump(char **argv, uint16_t argc)
|
||||
+{
|
||||
+ char line[128];
|
||||
+ size_t n = 0;
|
||||
+
|
||||
+ for (uint16_t i = 0; i < argc && n < sizeof(line) - 2; i++) {
|
||||
+ n += (size_t)snprintk(&line[n], sizeof(line) - n, "%s%s",
|
||||
+ (i == 0) ? "" : ",", argv[i]);
|
||||
+ }
|
||||
+ LOG_INF("NMEA: %s", line);
|
||||
+}
|
||||
+
|
||||
+static void nmea_generic_catchall_cb(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ ARG_UNUSED(chat);
|
||||
+ ARG_UNUSED(user_data);
|
||||
+ nmea_generic_dump(argv, argc);
|
||||
+}
|
||||
+#else
|
||||
+#define nmea_generic_dump(argv, argc) do { } while (0)
|
||||
+#endif
|
||||
+
|
||||
+/* Raw sentence counter — the only unambiguous "is the module talking to us"
|
||||
+ * signal available.
|
||||
+ *
|
||||
+ * Everything else can be misread. No satellites, a module mute because its
|
||||
+ * output protocol was disabled, and a module stranded at the wrong baud all
|
||||
+ * present identically to the application: zero satellites and no fix. Even
|
||||
+ * the position callback cannot tell them apart, because the NMEA matcher only
|
||||
+ * publishes once GGA and RMC agree on a valid UTC — so a receiver that is
|
||||
+ * healthily emitting NMEA while it searches the sky publishes nothing at all.
|
||||
+ *
|
||||
+ * Counting parsed sentences separates the cases outright: non-zero means the
|
||||
+ * module is alive, at the right baud, and emitting NMEA we can parse, so any
|
||||
+ * remaining problem is signal. Zero means nothing is arriving and no amount
|
||||
+ * of antenna work will help.
|
||||
+ */
|
||||
+static uint32_t nmea_rx_sentences;
|
||||
+
|
||||
+static void nmea_generic_gga_cb(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ nmea_rx_sentences++;
|
||||
+ nmea_generic_dump(argv, argc);
|
||||
+ gnss_nmea0183_match_gga_callback(chat, argv, argc, user_data);
|
||||
+}
|
||||
+
|
||||
+static void nmea_generic_rmc_cb(struct modem_chat *chat, char **argv, uint16_t argc,
|
||||
+ void *user_data)
|
||||
+{
|
||||
+ nmea_rx_sentences++;
|
||||
+ nmea_generic_dump(argv, argc);
|
||||
+ gnss_nmea0183_match_rmc_callback(chat, argv, argc, user_data);
|
||||
+}
|
||||
+
|
||||
+/* Exported for application-level diagnostics. Weak so boards building another
|
||||
+ * GNSS driver that also exports it still link. */
|
||||
+__weak uint32_t zephcore_gnss_rx_count(void)
|
||||
+{
|
||||
+ return nmea_rx_sentences;
|
||||
+}
|
||||
+
|
||||
MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
|
||||
- MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
|
||||
- MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
|
||||
+ MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", nmea_generic_gga_cb),
|
||||
+ MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", nmea_generic_rmc_cb),
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
|
||||
#endif
|
||||
+#endif
|
||||
+ MODEM_CHAT_MATCH_WILDCARD("$??TXT,", ",*", nmea_generic_txt_callback),
|
||||
+ MODEM_CHAT_MATCH("$PUBX", ",*", nmea_generic_pubx_callback),
|
||||
+#ifdef CONFIG_ZEPHCORE_GPS_NMEA_DUMP
|
||||
+ /* Must stay LAST: a zero-length match matches every line, so anything
|
||||
+ * after it would be unreachable. */
|
||||
+ MODEM_CHAT_MATCH("", ",*", nmea_generic_catchall_cb),
|
||||
#endif
|
||||
);
|
||||
|
||||
static int gnss_nmea_generic_resume(const struct device *dev)
|
||||
|
||||
@@ -511,7 +511,12 @@ void Dispatcher::checkSend()
|
||||
bool success = _radio->startSendRaw(raw, len);
|
||||
if (!success) {
|
||||
uint32_t retry = getCADFailRetryDelay();
|
||||
LOG_ERR("checkSend: startSendRaw failed! re-queuing delay=%u", retry);
|
||||
/* Almost always LBT refusing a busy channel, which
|
||||
* is the designed outcome — the packet is
|
||||
* re-queued below and retried. At ERR this fires
|
||||
* continuously on a busy site and buries real
|
||||
* faults. */
|
||||
LOG_DBG("checkSend: startSendRaw refused, re-queuing delay=%u", retry);
|
||||
logTxFail(outbound, outbound->getRawLength());
|
||||
_mgr->queueOutbound(outbound, outbound_priority, futureMillis((int)retry));
|
||||
outbound = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user