Qualify optional 64-symbol fast preamble
This commit is contained in:
1 parent
ba70c5b3c6
commit
da74d7de22
7 files changed
+129
-35
No files matched your search
+4
-3
@@ -250,9 +250,10 @@ a trusted LAN.
|
||||
The WebConfig **Advanced** card exposes device power saving on WiFi Companion,
|
||||
repeater, and room-server builds. It also exposes RX power saving on radio chips
|
||||
that support receive duty cycling. RXPS can select continuous receive, levels
|
||||
1-10, automatic or explicit 16/32-symbol preambles, or manual receive/sleep
|
||||
windows. The WebConfig **WiFi** card exposes WiFi modem power saving. All three
|
||||
settings are persisted across reboots.
|
||||
1-10, automatic or explicit 16/32-symbol timing assumptions, or manual
|
||||
receive/sleep windows. The timing assumption does not change the radio's actual
|
||||
wire preamble. The WebConfig **WiFi** card exposes WiFi modem power saving. All
|
||||
three settings are persisted across reboots.
|
||||
|
||||
The three settings are independent. A WiFi Companion keeps its transports
|
||||
available while device power saving reduces CPU and GPS idle power. An
|
||||
|
||||
+17
-2
@@ -742,7 +742,18 @@ On SX1262+TCXO boards, the tested fast-setting boundaries are:
|
||||
|
||||
SF7/BW500, SF6/BW250, and SF5/BW125 are timing-equivalent because each has a
|
||||
256 us LoRa symbol. SF5/BW250 and SF5/BW500 use `continuous-fast` even with
|
||||
preamble 32.
|
||||
preamble 32 in the normal build.
|
||||
|
||||
The table's effective preamble is the length RXPS uses for its timing window.
|
||||
The radio's actual transmitted preamble is a firmware property, not a
|
||||
`radio.rxps` preference. Normal firmware transmits 32 symbols at SF5-SF8 and
|
||||
16 symbols at SF9-SF12. A controlled build can set
|
||||
`-D LORA_FAST_PREAMBLE=64`; this changes the actual SF5-SF8 wire preamble and
|
||||
lets SF5/BW250 duty-cycle at effective level 8 (`1252 / 6424 us`). That profile
|
||||
passed a 16/16 on-air qualification at 909.950 MHz. SF5/BW500 remains too fast.
|
||||
Use the 64-symbol option only when every possible sender to an RXPS receiver
|
||||
uses the longer preamble; treating a 32-symbol sender as if it sent 64 would
|
||||
create a receive gap.
|
||||
|
||||
---
|
||||
|
||||
@@ -798,7 +809,8 @@ Station G2/G3 targets default to `off`.
|
||||
**Parameters:**
|
||||
- `rx_us`, `sleep_us`: Receive and sleep durations in microseconds (`1000`-`30000000`).
|
||||
- `level`: A power-saving level from `1` (most conservative) to `10` (least power saving).
|
||||
- `preamble`: LoRa preamble length in symbols; `16` or `32`.
|
||||
- `preamble`: RXPS timing assumption in symbols; `16` or `32`. This does not
|
||||
change the radio's actual transmitted preamble.
|
||||
- `state`: `on` or `off`.
|
||||
|
||||
**Notes:**
|
||||
@@ -809,6 +821,9 @@ Station G2/G3 targets default to `off`.
|
||||
- `on` and `conservative` select level `1` with a 16-symbol preamble; `balanced` selects level `5` with a 16-symbol preamble.
|
||||
- Fresh Cascade-profile builds start with RXPS on at level `8` and a 16-symbol preamble. Saved operator settings still take precedence after an upgrade.
|
||||
- Level-based settings automatically recalculate their timings when the spreading factor or bandwidth changes. Custom `<rx_us> <sleep_us>` timings remain fixed.
|
||||
- `get radio.rxps` reports both the saved timing assumption and any effective
|
||||
preamble/level selected for the current tuple. A 64-symbol effective preamble
|
||||
appears only in a firmware build whose actual fast preamble is 64.
|
||||
- The selected mode is applied immediately, persisted, and restored after reboot.
|
||||
|
||||
---
|
||||
|
||||
+17
-10
@@ -107,14 +107,16 @@ get radio.rxps
|
||||
set radio.rxps {off|on|level 1-10 [preamble 16|32]|rx_us sleep_us}
|
||||
```
|
||||
Shows or changes LoRa receive duty cycling on supported radios. Fresh Cascade
|
||||
builds select level 8 with a 16-symbol preamble. A configured level is the
|
||||
minimum: when faster SF/BW settings shorten the timing window, firmware raises
|
||||
the effective level only as far as needed, up to level 10. For SF5-SF8 it can
|
||||
then use the radio's actual 32-symbol transmit preamble if no level is safe
|
||||
under the legacy 16-symbol assumption. The configured values remain unchanged,
|
||||
so every radio change recalculates from the saved minimum. A slower tuple
|
||||
returns to that exact level when it is safe; otherwise firmware keeps using the
|
||||
smallest safe effective level.
|
||||
builds select level 8 with a 16-symbol timing assumption. The `preamble`
|
||||
argument controls the RXPS calculation; it does not change the physical wire
|
||||
preamble. A configured level is the minimum: when faster SF/BW settings shorten
|
||||
the timing window, firmware raises the effective level only as far as needed,
|
||||
up to level 10. For SF5-SF8 the normal build can then use the radio's actual
|
||||
32-symbol transmit preamble if no level is safe under the legacy 16-symbol
|
||||
assumption. The configured values remain unchanged, so every radio change
|
||||
recalculates from the saved minimum. A slower tuple returns to that exact level
|
||||
when it is safe; otherwise firmware keeps using the smallest safe effective
|
||||
level.
|
||||
|
||||
If neither adjustment leaves enough time for the radio to wake, RXPS stays
|
||||
logically enabled but receives continuously instead of rejecting the radio
|
||||
@@ -135,8 +137,13 @@ RXPS boundary profiles (CR does not change the RXPS preamble timing):
|
||||
| 5 | 125 | 32 | 7 | 2731 / 6101 us |
|
||||
| 5 | 62.5 | 16 | 10 | 4096 / 6272 us |
|
||||
|
||||
SF5/BW250 and SF5/BW500 remain too fast for a true TCXO duty cycle even with
|
||||
preamble 32, so they use `continuous-fast`.
|
||||
SF5/BW250 and SF5/BW500 remain too fast for a true TCXO duty cycle in the
|
||||
normal 32-symbol build, so they use `continuous-fast`. A controlled build with
|
||||
`-D LORA_FAST_PREAMBLE=64` changes the actual SF5-SF8 wire preamble. SF5/BW250
|
||||
then uses effective level 8 and `1252 / 6424 us`; that combination passed a
|
||||
16/16 on-air qualification at 909.950 MHz. SF5/BW500 still uses
|
||||
`continuous-fast`. Deploy a longer physical preamble consistently across all
|
||||
senders that may reach an RXPS receiver.
|
||||
|
||||
```
|
||||
get wifi.powersave
|
||||
|
||||
@@ -6,6 +6,17 @@
|
||||
#define RX_POWERSAVING_MIN_PERIOD_US 1000UL
|
||||
#define RX_POWERSAVING_MAX_PERIOD_US 30000000UL
|
||||
|
||||
// Actual transmit preamble used by MeshCore at SF5-SF8. Keep 32 as the wire
|
||||
// compatible default; controlled qualification builds can select 64 to test
|
||||
// faster RX duty-cycle profiles without pretending the sender transmits a
|
||||
// longer preamble than it really does.
|
||||
#ifndef LORA_FAST_PREAMBLE
|
||||
#define LORA_FAST_PREAMBLE 32
|
||||
#endif
|
||||
#if LORA_FAST_PREAMBLE != 32 && LORA_FAST_PREAMBLE != 64
|
||||
#error "LORA_FAST_PREAMBLE must be 32 or 64"
|
||||
#endif
|
||||
|
||||
// A level-derived timing pair equal to this value means the requested LoRa
|
||||
// tuple is too fast for a safe duty cycle. Keep the operator's RXPS preference
|
||||
// enabled, but receive continuously until a later SF/BW change produces usable
|
||||
@@ -95,7 +106,11 @@ inline bool canStartRxPowerSavingDutyCycle(uint32_t rx_us, uint32_t sleep_us,
|
||||
// Must stay in sync with RadioLibWrapper::preambleLengthForSF() (the value the
|
||||
// radio actually transmits); kept local here because CommonCLI is radio-agnostic.
|
||||
inline uint8_t rxPowerSavingPreambleForSF(uint8_t sf) {
|
||||
return sf <= 8 ? 32 : 16;
|
||||
return sf <= 8 ? LORA_FAST_PREAMBLE : 16;
|
||||
}
|
||||
|
||||
inline bool isRxPowerSavingPreamble(uint8_t preamble) {
|
||||
return preamble == 16 || preamble == 32 || preamble == 64;
|
||||
}
|
||||
|
||||
inline bool isNumeric(const char *sp) {
|
||||
@@ -115,7 +130,7 @@ inline uint32_t ceilPositiveFloat(float value) {
|
||||
inline bool calcRxPowerSavingLevel(uint8_t level, uint8_t sf, float bw, uint8_t preamble, uint32_t *rx_us,
|
||||
uint32_t *sleep_us) {
|
||||
if (!rx_us || !sleep_us || level < 1 || level > 10 || sf < 5 || sf > 12
|
||||
|| bw <= 0.0f || (preamble != 16 && preamble != 32)) {
|
||||
|| bw <= 0.0f || !isRxPowerSavingPreamble(preamble)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -162,19 +177,19 @@ inline bool calcRxPowerSavingLevelAtOrAbove(
|
||||
uint8_t *effective_preamble = nullptr) {
|
||||
if (!rx_us || !sleep_us || requested_level < 1 || requested_level > 10
|
||||
|| sf < 5 || sf > 12 || bw <= 0.0f
|
||||
|| (preamble != 16 && preamble != 32)) {
|
||||
|| !isRxPowerSavingPreamble(preamble)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep the configured/legacy preamble assumption if any level can satisfy
|
||||
// it. For SF5-SF8 the radio already transmits 32 symbols, so a second pass
|
||||
// may use that real preamble length when 16 symbols cannot cover the TCXO
|
||||
// transition at a faster tuple.
|
||||
const bool can_try_32 = preamble == 16
|
||||
&& rxPowerSavingPreambleForSF(sf) == 32;
|
||||
const uint8_t pass_count = can_try_32 ? 2 : 1;
|
||||
// it. A second pass may use the radio's real, longer transmit preamble when
|
||||
// the configured assumption cannot cover the transition at a faster tuple.
|
||||
const uint8_t transmitted_preamble = rxPowerSavingPreambleForSF(sf);
|
||||
const bool can_try_transmitted = transmitted_preamble > preamble;
|
||||
const uint8_t pass_count = can_try_transmitted ? 2 : 1;
|
||||
for (uint8_t pass = 0; pass < pass_count; pass++) {
|
||||
const uint8_t candidate_preamble = pass == 0 ? preamble : 32;
|
||||
const uint8_t candidate_preamble = pass == 0
|
||||
? preamble : transmitted_preamble;
|
||||
for (uint8_t level = requested_level; level <= 10; level++) {
|
||||
const float symbol_us = (1000.0f * (float)(1UL << sf)) / bw;
|
||||
const float amount = (float)(level - 1) / 9.0f;
|
||||
|
||||
@@ -172,7 +172,9 @@ public:
|
||||
|
||||
virtual float getCurrentRSSI() =0;
|
||||
virtual uint8_t getSpreadingFactor() const { return LORA_SF; }
|
||||
static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; }
|
||||
static uint16_t preambleLengthForSF(uint8_t sf) {
|
||||
return rxPowerSavingPreambleForSF(sf);
|
||||
}
|
||||
bool updatePreamble(uint8_t sf) {
|
||||
if (_radio->setPreambleLength(preambleLengthForSF(sf)) != RADIOLIB_ERR_NONE) return false;
|
||||
_preamble_sf = sf;
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ does not reflect the GoogleTest count -- run the built binary directly
|
||||
| `test_packet_manager` | `src/Packet.cpp`, `src/Dispatcher.cpp`, `src/helpers/StaticPoolPacketManager.cpp` | truncated-packet rejection, unavailable-radio behavior, scoped RX-delay replacement, queue/CAD scheduling, and staged radio/TX recovery |
|
||||
| `test_persistent_store_format` | `src/helpers/PersistentStoreFormat.h` | contact-page headers and CRCs, dirty-page state, stable slot allocation, and bounded resumable legacy migration across power loss |
|
||||
| `test_power_management` | `src/helpers/PowerManagementUtils.h` | median filtering of a brownout outlier and valid-reading requirements for the boot lock |
|
||||
| `test_rx_power_saving` | `src/helpers/radiolib/RXPowerSaving.h` | level-derived timing, automatic level/preamble selection, equivalent SF7/BW500, SF6/BW250, and SF5/BW125 profiles, SF5/BW62.5 with preamble 16, SF5/BW250/BW500 continuous-RX fallback, automatic duty-cycle resume, and SX1262 TCXO timing thresholds |
|
||||
| `test_rx_power_saving` | `src/helpers/radiolib/RXPowerSaving.h` | level-derived timing, automatic level/preamble selection, equivalent SF7/BW500, SF6/BW250, and SF5/BW125 profiles, SF5/BW62.5 with preamble 16, normal-preamble SF5/BW250/BW500 continuous-RX fallback, opt-in 64-symbol SF5/BW250 level-8 timing, automatic duty-cycle resume, and SX1262 TCXO timing thresholds |
|
||||
| `test_region_names` | `src/helpers/RegionNameUtils.h` | canonical public-region markers while preserving distinct private and differently named regions |
|
||||
| `test_routing_policy` | `src/helpers/RoutingPolicy.h` | scoped/unscoped flood hop limits and selection of direct, path-return, mirrored-scope, default-scope, or unscoped replies |
|
||||
| `test_rs232_uart` | `src/helpers/bridges/RS232UartUtils.h` | stopping the active UART peripheral before reassigning its pins |
|
||||
|
||||
@@ -64,8 +64,8 @@ TEST(RxPowerSaving, RequestedLevelIsRaisedOnlyToRequiredMinimum) {
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 7, 500.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(7U, effective_level);
|
||||
EXPECT_EQ(32U, effective_preamble);
|
||||
EXPECT_EQ(LORA_FAST_PREAMBLE == 64 ? 3U : 7U, effective_level);
|
||||
EXPECT_EQ((uint8_t)LORA_FAST_PREAMBLE, effective_preamble);
|
||||
EXPECT_TRUE(canStartRxPowerSavingDutyCycle(
|
||||
rx_us, sleep_us, sx1262_tcxo_transition_us));
|
||||
|
||||
@@ -73,7 +73,7 @@ TEST(RxPowerSaving, RequestedLevelIsRaisedOnlyToRequiredMinimum) {
|
||||
8, 7, 500.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(8U, effective_level);
|
||||
EXPECT_EQ(32U, effective_preamble);
|
||||
EXPECT_EQ((uint8_t)LORA_FAST_PREAMBLE, effective_preamble);
|
||||
EXPECT_TRUE(canStartRxPowerSavingDutyCycle(
|
||||
rx_us, sleep_us, sx1262_tcxo_transition_us));
|
||||
}
|
||||
@@ -93,7 +93,7 @@ TEST(RxPowerSaving, Sf5Bw500StillFallsBackWithPreamble32) {
|
||||
EXPECT_TRUE(rxPowerSavingUsesContinuousFallback(rx_us, sleep_us));
|
||||
}
|
||||
|
||||
TEST(RxPowerSaving, Sf5Bw250StillFallsBackWithPreamble32OnTcxoRadio) {
|
||||
TEST(RxPowerSaving, Sf5Bw250NeedsLongerTransmittedPreambleOnTcxoRadio) {
|
||||
constexpr uint32_t sx1262_tcxo_transition_us = 6000;
|
||||
uint32_t rx_us = 0;
|
||||
uint32_t sleep_us = 0;
|
||||
@@ -112,11 +112,55 @@ TEST(RxPowerSaving, Sf5Bw250StillFallsBackWithPreamble32OnTcxoRadio) {
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 5, 250.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
#if LORA_FAST_PREAMBLE == 64
|
||||
EXPECT_EQ(8U, effective_level);
|
||||
EXPECT_EQ(64U, effective_preamble);
|
||||
EXPECT_EQ(1252U, rx_us);
|
||||
EXPECT_EQ(6424U, sleep_us);
|
||||
EXPECT_FALSE(rxPowerSavingUsesContinuousFallback(rx_us, sleep_us));
|
||||
#else
|
||||
EXPECT_EQ(0U, effective_level);
|
||||
EXPECT_EQ(0U, effective_preamble);
|
||||
EXPECT_TRUE(rxPowerSavingUsesContinuousFallback(rx_us, sleep_us));
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RxPowerSaving, Sf5Bw250UsesPreamble64AtEffectiveLevel8) {
|
||||
constexpr uint32_t sx1262_tcxo_transition_us = 6000;
|
||||
uint32_t rx_us = 0;
|
||||
uint32_t sleep_us = 0;
|
||||
uint8_t effective_level = 0;
|
||||
uint8_t effective_preamble = 0;
|
||||
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 5, 250.0f, 64, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(8U, effective_level);
|
||||
EXPECT_EQ(64U, effective_preamble);
|
||||
EXPECT_EQ(1252U, rx_us);
|
||||
EXPECT_EQ(6424U, sleep_us);
|
||||
EXPECT_TRUE(canStartRxPowerSavingDutyCycle(
|
||||
rx_us, sleep_us, sx1262_tcxo_transition_us));
|
||||
}
|
||||
|
||||
#if LORA_FAST_PREAMBLE == 64
|
||||
TEST(RxPowerSaving, FastPreamble64BuildSelectsItAutomatically) {
|
||||
constexpr uint32_t sx1262_tcxo_transition_us = 6000;
|
||||
uint32_t rx_us = 0;
|
||||
uint32_t sleep_us = 0;
|
||||
uint8_t effective_level = 0;
|
||||
uint8_t effective_preamble = 0;
|
||||
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 5, 250.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(8U, effective_level);
|
||||
EXPECT_EQ(64U, effective_preamble);
|
||||
EXPECT_EQ(1252U, rx_us);
|
||||
EXPECT_EQ(6424U, sleep_us);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(RxPowerSaving, Sf5Bw125UsesPreamble32AtEffectiveLevel7) {
|
||||
constexpr uint32_t sx1262_tcxo_transition_us = 6000;
|
||||
uint32_t rx_us = 0;
|
||||
@@ -127,10 +171,15 @@ TEST(RxPowerSaving, Sf5Bw125UsesPreamble32AtEffectiveLevel7) {
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 5, 125.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(7U, effective_level);
|
||||
EXPECT_EQ(32U, effective_preamble);
|
||||
EXPECT_EQ(LORA_FAST_PREAMBLE == 64 ? 3U : 7U, effective_level);
|
||||
EXPECT_EQ((uint8_t)LORA_FAST_PREAMBLE, effective_preamble);
|
||||
#if LORA_FAST_PREAMBLE == 64
|
||||
EXPECT_EQ(3641U, rx_us);
|
||||
EXPECT_EQ(6414U, sleep_us);
|
||||
#else
|
||||
EXPECT_EQ(2731U, rx_us);
|
||||
EXPECT_EQ(6101U, sleep_us);
|
||||
#endif
|
||||
EXPECT_TRUE(canStartRxPowerSavingDutyCycle(
|
||||
rx_us, sleep_us, sx1262_tcxo_transition_us));
|
||||
}
|
||||
@@ -145,10 +194,15 @@ TEST(RxPowerSaving, Sf6Bw250UsesPreamble32AtEffectiveLevel7) {
|
||||
ASSERT_TRUE(calcRxPowerSavingLevelAtOrAbove(
|
||||
1, 6, 250.0f, 16, sx1262_tcxo_transition_us,
|
||||
&rx_us, &sleep_us, &effective_level, &effective_preamble));
|
||||
EXPECT_EQ(7U, effective_level);
|
||||
EXPECT_EQ(32U, effective_preamble);
|
||||
EXPECT_EQ(LORA_FAST_PREAMBLE == 64 ? 3U : 7U, effective_level);
|
||||
EXPECT_EQ((uint8_t)LORA_FAST_PREAMBLE, effective_preamble);
|
||||
#if LORA_FAST_PREAMBLE == 64
|
||||
EXPECT_EQ(3641U, rx_us);
|
||||
EXPECT_EQ(6414U, sleep_us);
|
||||
#else
|
||||
EXPECT_EQ(2731U, rx_us);
|
||||
EXPECT_EQ(6101U, sleep_us);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(RxPowerSaving, Sf5Bw62Point5UsesPreamble16AtEffectiveLevel10) {
|
||||
|
||||
Reference in new issue
Block a user