lower sf fixes

This commit is contained in:
liquidraver
2026-09-17 13:14:37 +02:00
parent 1c2c942849
commit a78b4ae2ea
2 changed files with 43 additions and 40 deletions
+15 -7
View File
@@ -940,32 +940,40 @@ uint32_t LoRaRadioBase::getEstAirtimeFor(int len_bytes)
* drifts with it — reported RX airtime, the TX airtime and duty-cycle
* budget that now derive from it, and outbound_expiry. Upstream cannot
* drift this way because it asks the radio (getTimeOnAir()); these
* accessors are our equivalent. bw is read directly rather than via
* getActiveBandwidthKHzX10(), whose fixed-point rounding would cost
* precision at 31.25 kHz. */
* accessors are our equivalent. Resolve bandwidth through the same
* enum as buildModemConfig(): a saved 62 or 62.5 both program 62.5 kHz,
* and a rounded setting must not change the airtime calculation. */
uint8_t sf = getActiveSpreadingFactor();
float bw = _has_radio_override ? _override_bw
: (_prefs ? _prefs->bw : (float)LoRaConfig::BANDWIDTH);
uint8_t cr_val = getActiveCodingRate();
if (sf < 6) sf = 6;
uint8_t min_sf = _loramac_node ? 6 : 5;
if (sf < min_sf) sf = min_sf;
if (sf > 12) sf = 12;
if (bw < 7.0f) bw = 125.0f;
bw = bandwidth_to_hz(bw_khz_to_enum((uint16_t)bw)) / 1000.0f;
if (cr_val < 5) cr_val = 5;
if (cr_val > 8) cr_val = 8;
float t_sym = (float)(1 << sf) / (bw * 1000.0f);
float t_preamble = (preambleLengthForSF(sf) + 4.25f) * t_sym;
/* SX126x/LR11xx/LR20xx add two synchronization symbols at SF5/6
* and carry eight more header bits there. SX127x uses the older
* format. See Semtech's lr11xx_radio_get_lora_time_on_air_numerator.
* Treating SF5 as SF6 charged 397 ms for a 126-byte SF5/BW62.5 CR4/8
* packet whose Semtech airtime is 237 ms (confirmed by TX timing). */
float fine_sync = (!_loramac_node && sf <= 6) ? 1.0f : 0.0f;
float t_preamble = (preambleLengthForSF(sf) + 4.25f + 2.0f * fine_sync) * t_sym;
/* LDRO threshold must track the SX126x driver's should_enable_ldro()
* exactly (symbol time > 16.38 ms) so this estimate's DE matches the
* hardware's DE on every SF/BW pair. The old `sf >= 11` was only
* correct at BW 125 kHz and diverged on every other bandwidth. */
float de = (t_sym > 0.01638f) ? 1.0f : 0.0f;
float num = 8.0f * len_bytes - 4.0f * sf + 28.0f + 16.0f;
float num = 8.0f * len_bytes - 4.0f * sf + 28.0f + 16.0f - 8.0f * fine_sync;
float den = 4.0f * (sf - 2.0f * de);
if (den < 1.0f) den = 4.0f;
float n_payload = 8.0f + fmaxf(ceilf(num / den) * (cr_val - 4 + 4), 0.0f);
float n_payload = 8.0f + fmaxf(ceilf(num / den) * cr_val, 0.0f);
float t_payload = n_payload * t_sym;
return (uint32_t)((t_preamble + t_payload) * 1000.0f);
@@ -283,19 +283,7 @@ static lr11xx_system_tcxo_supply_voltage_t get_tcxo_voltage(uint16_t mv)
/* Get kHz value from Zephyr BW enum — needed for duty cycle timing */
static float bw_enum_to_khz(enum lora_signal_bandwidth bw)
{
switch (bw) {
case BW_7_KHZ: return 7.81f;
case BW_10_KHZ: return 10.42f;
case BW_15_KHZ: return 15.63f;
case BW_20_KHZ: return 20.83f;
case BW_31_KHZ: return 31.25f;
case BW_41_KHZ: return 41.67f;
case BW_62_KHZ: return 62.5f;
case BW_125_KHZ: return 125.0f;
case BW_250_KHZ: return 250.0f;
case BW_500_KHZ: return 500.0f;
default: return 125.0f;
}
return lr11xx_radio_get_lora_bw_in_hz(bw_enum_to_lr11xx(bw)) / 1000.0f;
}
/* ── Hardware reset (BUSY stuck recovery) ───────────────────────────── */
@@ -352,6 +340,14 @@ static void lr11xx_hardware_reset(struct lr11xx_data *data,
/* ── Apply modem configuration ──────────────────────────────────────── */
/* Share the LDRO decision between the hardware and its airtime estimate. */
static uint8_t lr11xx_ldro(const struct lora_modem_config *mc)
{
uint32_t bw_hz = lr11xx_radio_get_lora_bw_in_hz(bw_enum_to_lr11xx(mc->bandwidth));
uint32_t symbol_time_us = ((1U << (uint8_t)mc->datarate) * 1000000U) / bw_hz;
return symbol_time_us > 16380;
}
static void lr11xx_apply_modem_config(struct lr11xx_data *data,
const struct lr11xx_config *cfg,
bool tx_mode)
@@ -361,16 +357,11 @@ static void lr11xx_apply_modem_config(struct lr11xx_data *data,
lr11xx_radio_set_rf_freq(ctx, mc->frequency);
/* LDRO must be enabled when symbol time > 16.38ms (SF11+/BW125 etc) */
uint32_t bw_hz = (uint32_t)(bw_enum_to_khz(mc->bandwidth) * 1000.0f);
uint32_t symbol_time_us = ((1U << (uint8_t)mc->datarate) * 1000000U) / bw_hz;
uint8_t ldro = (symbol_time_us > 16380) ? 1 : 0;
lr11xx_radio_mod_params_lora_t mod = {
.sf = (lr11xx_radio_lora_sf_t)mc->datarate,
.bw = bw_enum_to_lr11xx(mc->bandwidth),
.cr = cr_enum_to_lr11xx(mc->coding_rate),
.ldro = ldro,
.ldro = lr11xx_ldro(mc),
};
lr11xx_radio_set_lora_mod_params(ctx, &mod);
@@ -1088,20 +1079,24 @@ static uint32_t lr11xx_lora_airtime(const struct device *dev,
struct lr11xx_data *data = dev->data;
struct lora_modem_config *mc = &data->modem_cfg;
uint8_t sf = (uint8_t)mc->datarate;
float bw = bw_enum_to_khz(mc->bandwidth) * 1000.0f;
uint8_t cr = (uint8_t)mc->coding_rate + 4;
float ts = (float)(1 << sf) / bw;
int de = (sf >= 11 && bw <= 125000.0f) ? 1 : 0;
float n_payload = 8.0f + fmaxf(
ceilf((8.0f * data_len - 4.0f * sf + 28.0f + 16.0f) /
(4.0f * (sf - 2.0f * de))) * cr,
0.0f);
float t_preamble = (mc->preamble_len + 4.25f) * ts;
float t_payload = n_payload * ts;
return (uint32_t)((t_preamble + t_payload) * 1000.0f);
/* Use Semtech's calculation, including SF5/6 synchronization and the
* actual LDRO/CRC configuration. The old SF>=11 && BW<=125 shortcut
* underestimated SF10/BW62.5 and SF12/BW250, including the TX watchdog
* budgets derived from this API. */
lr11xx_radio_mod_params_lora_t mod = {
.sf = (lr11xx_radio_lora_sf_t)mc->datarate,
.bw = bw_enum_to_lr11xx(mc->bandwidth),
.cr = cr_enum_to_lr11xx(mc->coding_rate),
.ldro = lr11xx_ldro(mc),
};
lr11xx_radio_pkt_params_lora_t pkt = {
.preamble_len_in_symb = mc->preamble_len,
.header_type = LR11XX_RADIO_LORA_PKT_EXPLICIT,
.pld_len_in_bytes = (uint8_t)data_len,
.crc = mc->packet_crc_disable ? LR11XX_RADIO_LORA_CRC_OFF
: LR11XX_RADIO_LORA_CRC_ON,
};
return lr11xx_radio_get_lora_time_on_air_in_ms(&pkt, &mod);
}
/* Forward declaration — needed by LBT in send_async */