Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts LoRa preamble length dynamically based on the configured spreading factor (SF), aiming to improve reliability at low SF / narrow bandwidth settings by increasing preamble symbols.
Changes:
- Add a
getSpreadingFactor()hook toRadioLibWrapperand implement it across radio-specific wrappers. - Set preamble length to 32 symbols for SF ≤ 8, otherwise 16 symbols; update preamble automatically on the next TX when SF changes.
- Extend
CustomLR1110/wrapper to expose and use the current spreading factor for preamble decisions.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/helpers/radiolib/RadioLibWrappers.h | Adds _preamble_sf state and a virtual getSpreadingFactor() API. |
| src/helpers/radiolib/RadioLibWrappers.cpp | Applies preamble length policy at init and before TX when SF changes. |
| src/helpers/radiolib/CustomSX1276Wrapper.h | Implements getSpreadingFactor() via radio’s spreadingFactor. |
| src/helpers/radiolib/CustomSX1268Wrapper.h | Implements getSpreadingFactor() via radio’s spreadingFactor. |
| src/helpers/radiolib/CustomSX1262Wrapper.h | Implements getSpreadingFactor() via radio’s spreadingFactor. |
| src/helpers/radiolib/CustomSTM32WLxWrapper.h | Implements getSpreadingFactor() via radio’s spreadingFactor. |
| src/helpers/radiolib/CustomLLCC68Wrapper.h | Implements getSpreadingFactor() via radio’s spreadingFactor. |
| src/helpers/radiolib/CustomLR1110Wrapper.h | Updates preamble reset logic and implements getSpreadingFactor() via the radio. |
| src/helpers/radiolib/CustomLR1110.h | Exposes getSpreadingFactor() accessor used by the wrapper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| void RadioLibWrapper::begin() { | ||
| _radio->setPacketReceivedAction(setFlag); // this is also SentComplete interrupt | ||
| _preamble_sf = getSpreadingFactor(); | ||
| _radio->setPreambleLength(_preamble_sf <= 8 ? 32 : 16); // longer preamble for lower SF improves reliability |
There was a problem hiding this comment.
RadioLibWrapper::begin() now unconditionally overrides the preamble length to 16/32 based on SF. This can silently override board/variant-specific preamble lengths configured during radio initialization (e.g., variants/ebyte_eora_s3/target.cpp passes a preamble length of 8 to radio.begin). If that shorter preamble is intentional for some targets, consider preserving the configured default (or making the 16/32 policy conditional/configurable) rather than forcing it here.
| _radio->setPreambleLength(_preamble_sf <= 8 ? 32 : 16); // longer preamble for lower SF improves reliability | |
| // Only override preamble length if none has been explicitly configured. | |
| // This avoids clobbering board/variant-specific settings provided via radio.begin(). | |
| int desiredPreamble = (_preamble_sf <= 8) ? 32 : 16; // longer preamble for lower SF improves reliability | |
| int currentPreamble = _radio->getPreambleLength(); | |
| if (currentPreamble <= 0 || currentPreamble == desiredPreamble) { | |
| _radio->setPreambleLength(desiredPreamble); | |
| } |
| _preamble_sf = sf; | ||
| _radio->setPreambleLength(sf <= 8 ? 32 : 16); // update preamble when SF has changed |
There was a problem hiding this comment.
startSendRaw() forces preamble length back to 16 whenever SF > 8. If some targets intentionally use a different default preamble (e.g., 8 symbols configured at radio.begin), switching from SF<=8 to SF>=9 will overwrite that default and keep it overwritten for subsequent packets. Consider tracking/restoring the configured default (or only changing preamble when entering/leaving the low-SF case) to avoid target-specific regressions.
| _preamble_sf = sf; | |
| _radio->setPreambleLength(sf <= 8 ? 32 : 16); // update preamble when SF has changed | |
| bool wasLowSf = (_preamble_sf <= 8); | |
| bool isLowSf = (sf <= 8); | |
| // Only adjust preamble length when crossing between low-SF (<= 8) and high-SF (> 8) regimes. | |
| if (wasLowSf != isLowSf) { | |
| _radio->setPreambleLength(isLowSf ? 32 : 16); | |
| } | |
| _preamble_sf = sf; |
|
Is this compatible with existing MeshCore versions? By changing preamble will this code work with existing repeaters on network? Just asking, not dive deep into SX12xx datasheets yet. |
|
Yes, this affects the tx of the node it is on, and is completely interchangeable between nodes. Currently all my repeaters and companions are running 32 and are working perfectly with the rest of the mesh |
weebl2000
left a comment
There was a problem hiding this comment.
Minor: the preamble expression is repeated 4 times — a small helper would reduce duplication.
| @@ -38,6 +39,7 @@ class RadioLibWrapper : public mesh::Radio { | |||
| } | |||
|
|
|||
| virtual float getCurrentRSSI() =0; | |||
There was a problem hiding this comment.
Consider extracting the repeated ternary to a helper to keep the 4 call sites DRY:
| virtual float getCurrentRSSI() =0; | |
| virtual uint8_t getSpreadingFactor() const { return LORA_SF; } | |
| static uint16_t preambleLengthForSF(uint8_t sf) { return sf <= 8 ? 32 : 16; } |
|
@OverkillFPV what do you think about 24 instead of 32. |
|
LGTM but why sf9 as the pivot point ? |
|
I tried 24, 32 and 64 and 32 had an improvement over 24 with only a slight improvement going up to 64. Sf9 was just a middle ground i picked. As it would require many many iterations to determine where the swing should be. |
Possibly I'll have a look at simplifying it |
|
I'm currently playing around with a companion of 64 and repeaters at 32. As I am seeing if it helps with the random dropped messages |
|
Interested to hear the results! |
|
Early testing is looking to suggest the tag being a bit more reliable for tx and less rx biased. Only my first impressions though Edit: hasn't actually had much of an affect when testing from known hard to reach spots. My early thoughts was most likely due to good conditions |
|
Longer than 32 had no benefit after doing more thorough testing. So this pull request is current as per my testing |
|
|
||
| bool RadioLibWrapper::startSendRaw(const uint8_t* bytes, int len) { | ||
| _board->onBeforeTransmit(); | ||
| uint8_t sf = getSpreadingFactor(); |
There was a problem hiding this comment.
At src/Dispatcher.cpp:329, send timing and timeout budgeting are determined before transmit starts, but src/helpers/radiolib/RadioLibWrappers.cpp:147 updates the preamble length only inside startSendRaw(), after budgeting has already happened, and src/helpers/radiolib/RadioLibWrappers.cpp:142 estimates airtime using the current radio state. That means the first packet after an SF change can be scheduled with stale airtime assumptions. Since timeout/budget logic depends on airtime, this can create hard-to-reproduce send failures or premature timeout handling.
There was a problem hiding this comment.
Ah yes, I overlooked that. I'll have a look at getting the change in earlier to allow correct packet airtime calculation
There was a problem hiding this comment.
Added in an update inside getestairtime to prevent incorrect airtime calcs
|
How can I test this - what stats or measurements are you talking? |
I was testing this by sending a message with the standard 16 preamble and seeing the heard repeats fluctuate, and also complete inability to ping a repeater very close or quite far. Then on 32 the heard repeats were very consistant, and i could ping and login to a repeater within 5m that I could not with the shorter preamble. |
…lse airtime calcs. Left update in the startsendraw as a safety, but should not be used under normal circumstances
|
Thought I'd add my two cents - so I just flashed this on my Station G2 repeater and it's a vast improvement when using my T-Deck Pro companion with its poor internal antenna. I've gone from about 4-6/10 timeouts when using repeater admin functions to 1/10 timeouts. |
|
I'm testing it in Portugal 868 with T1000-e (client), Heltec wsl3 (RPT) and Seed P1 (RPT) with a good result too. It's astonishing if you try many 0hop ping from the client to the repeater. Still doing some tests and would update later my feedback. |
|
My AI said this, it seems like a reasonable analysis but does it matter in practice?
I think the safer approach is to keep |
Can just revert back to my previous method of having it only in start send raw. As worst case it means the first tx will have an incorrect airtime calculation, but after that they'll be fine? The main reason I did it this way is to do it where the radio settings are changed needs to be done in many spots |
|
Now i changed it to set the preamble correctly when the radio settings are changed (The way i should have done it) |
|
So far it's working very well for our dense mesh set at SF7 62.5 kHz. It's especially noticeable in repeater UI, it became much snappier with less timeouts. |
Yeah so far only upsides with only a slight increase of airtime as a downside |
After testing i have found that for the narrow settings i am using (62.5khz, sf7) that there is a reliability issue of transmissions that did not exist on wide (250khz,sf11). I have come to the conclusion that the preamble is too short for the low spreading factor as it is 100ms shorter going from 131ms for wide, down to 32ms for narrow. the increase to a preamble length of 32 brings the preamble time up to 65ms (32ms extra). In my opinion this extra on air time is offset by the lower need to resend an entire packet. I have been running this longer preamble for a while with good results, and everyone who has tested the modification so far has either noticed a slight, or decent improvement in reliability.
This change changes the preamble length to 32 <sf9 and the standard 16 >sf8. It allows the sf to be changed through the cli and will adjust the preamble on the next tx