Dies ist eine alte Version des Dokuments!
MeshCore RX Delay Calculator
What is RX Delay?
RX Delay (rxdelay / rx_delay_base) is a MeshCore repeater setting that delays processing of flood-routed packets based on how good the received signal is (the packet score).
Purpose: MeshCore uses „first packet wins“ for route discovery. Without RX delay, a repeater might forward a packet from a weak, distant hop before a stronger, better path arrives. RX delay gives better signals a head start by holding weaker copies longer in an internal queue.
Only affects flood routes. Direct-route packets are processed immediately.
Default: rxdelay = 0 (disabled). Recommended minimum for repeaters is often 2 or higher, depending on neighbor density.
Limits (firmware):
- Delay < 50 ms → packet processed immediately (no queue)
- Delay > 32 000 ms → capped at 32 seconds
rxdelayCLI range: 0 … 20 (0 = off)
Sources: MyMesh.cpp, Issue #2064, Issue #2123
The Formula
/** * MeshCore RX delay (milliseconds) * * @param {number} rxDelayBase - CLI value "rxdelay" (0 = disabled, typical 2–10) * @param {number} score - packet score 0.0 … 1.0 (from SNR + packet length) * @param {number} airtimeMs - estimated on-air time of the packet in ms * @returns {number} delay in ms (can be negative → treated as immediate) */ function calcRxDelay(rxDelayBase, score, airtimeMs) { if (rxDelayBase <= 0) return 0; return (Math.pow(rxDelayBase, 0.85 - score) - 1) * airtimeMs; }
Excel equivalent (same formula as in your spreadsheet):
=(rx_delay_base ^ (0.85 - score) - 1) * airtime
Your spreadsheet uses airtime = 150 ms as a fixed example. In firmware, airtime comes from getEstAirtimeFor(packet_length) and depends on SF, bandwidth, and payload size.
Packet Score (0.0 – 1.0)
The score is not raw SNR. It is computed in packetScoreInt() from SNR, spreading factor, and packet length:
score = clamp(0, 1, ((SNR - SNR_threshold[SF]) / 10) * (1 - packet_len / 256))
Interpretation:
- score = 1.0 → excellent signal, well above decoding threshold, short packet
- score = 0.5 → moderate margin above threshold
- score = 0.0 → at or below SNR threshold (should not decode reliably)
SNR thresholds per SF (approx.): SF7 −7.5 dB, SF8 −10, SF9 −12.5, SF10 −15, SF11 −17.5, SF12 −20.
Example: SF10, SNR = −5 dB, 64-byte packet → score ≈ 1) / 10) × (1 − 64/256) = 0.75
Reading the Table
Your Excel sheet layout:
| rx_delay_base score | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1.0 |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 102 | 85 | 70 | 55 | 41 | 28 | 16 | 5 | −5 | −15 |
| … | … | … | … | … | … | … | … | … | … | … |
- Rows =
rx_delay_base(1 … 19 in your sheet; CLI allows up to 20) - Columns = packet score (0.1 … 1.0)
- Cells = delay in milliseconds (for airtime = 150 ms)
Key behaviors:
rx_delay_base = 1→ always 0 ms delay (feature effectively off)rx_delay_basebetween 0 and 1 → inverted behavior: weak signals delayed less than strong ones (avoid!)- Higher
rx_delay_base→ longer delays overall → stronger collision/backoff spreading - Higher score (better signal) → shorter delay → better paths propagate first
Interactive Calculator
Paste the block below into a DokuWiki page. Requires HTML embed enabled (htmlok in config, or the htmlok plugin). Wrap in <html>…</html> if your wiki needs it.
<html> <style>
#mc-rxdelay { font-family: sans-serif; max-width: 960px; }
#mc-rxdelay table { border-collapse: collapse; width: 100%; font-size: 13px; }
#mc-rxdelay th, #mc-rxdelay td { border: 1px solid #ccc; padding: 4px 6px; text-align: right; }
#mc-rxdelay th { background: #eef; position: sticky; top: 0; }
#mc-rxdelay td.row-hdr { background: #f5f5f5; font-weight: bold; text-align: center; }
#mc-rxdelay .neg { color: #888; }
#mc-rxdelay .imm { background: #e8f5e9; }
#mc-rxdelay .queued { background: #fff8e1; }
#mc-rxdelay .controls { margin: 12px 0; display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
#mc-rxdelay .controls label { display: flex; align-items: center; gap: 6px; }
#mc-rxdelay .result-box { background: #f0f7ff; border: 1px solid #9cf; padding: 10px; margin: 12px 0; border-radius: 4px; }
</style>
<div id=„mc-rxdelay“>
<div class="controls"> <label>Airtime (ms): <input type="number" id="mc-airtime" value="150" min="1" max="5000" step="1"></label> <label>Highlight rx_delay_base: <input type="number" id="mc-highlight-rx" value="2" min="0" max="20" step="0.1"></label> <label>Highlight score: <input type="number" id="mc-highlight-score" value="0.5" min="0" max="1" step="0.05"></label> <button type="button" onclick="mcRxDelayUpdate()">Recalculate</button> </div>
<div class="result-box" id="mc-single-result"></div>
<div style="overflow-x:auto; max-height: 480px; overflow-y: auto;">
<table id="mc-rxdelay-table">
<thead></thead>
<tbody></tbody>
</table>
</div>
<p style="font-size:12px;color:#666;margin-top:8px;"> Green = <50 ms (processed immediately) · Yellow = queued · Grey italic = negative (also immediate) </p>
</div>
<script type=„text/javascript“> /* MeshCore RX delay – matches Excel „rxdelay calc.xlsx“ and firmware MyMesh::calcRxDelay */
var MC_RX_SCORES = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]; var MC_RX_BASES = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]; var MC_RX_THRESHOLD_MS = 50; var MC_RX_MAX_MS = 32000;
function mcCalcRxDelay(rxDelayBase, score, airtimeMs) {
if (rxDelayBase <= 0) return 0; return (Math.pow(rxDelayBase, 0.85 - score) - 1) * airtimeMs;
}
/ Optional: compute packet score from SNR (SF7–SF12), matching RadioLibWrappers.cpp */ function mcPacketScore(snr, sf, packetLen) { var thresholds = [-7.5, -10, -12.5, -15, -17.5, -20]; if (sf < 7 || sf > 12) return 0; if (snr < thresholds[sf - 7]) return 0; var rate = (snr - thresholds[sf - 7]) / 10.0; var penalty = 1 - (packetLen / 256.0); return Math.max(0, Math.min(1, rate * penalty)); } / Firmware-applied delay (clamp + threshold) */ function mcEffectiveRxDelay(rxDelayBase, score, airtimeMs) {
var raw = mcCalcRxDelay(rxDelayBase, score, airtimeMs); if (raw <MC_RX_THRESHOLD_MS) return 0; return Math.min(Math.round(raw), MC_RX_MAX_MS);
}
function mcRxDelayUpdate() {
var airtime = parseFloat(document.getElementById('mc-airtime').value) || 150;
var hlRx = parseFloat(document.getElementById('mc-highlight-rx').value) || 0;
var hlScore = parseFloat(document.getElementById('mc-highlight-score').value) || 0;
var raw = mcCalcRxDelay(hlRx, hlScore, airtime);
var eff = mcEffectiveRxDelay(hlRx, hlScore, airtime);
document.getElementById('mc-single-result').innerHTML =
'<strong>Single value:</strong> rx_delay_base=' + hlRx +
', score=' + hlScore + ', airtime=' + airtime + ' ms<br>' +
'Formula: (' + hlRx + '<sup>(0.85 − ' + hlScore + ')</sup> − 1) × ' + airtime +
' = <strong>' + raw.toFixed(1) + ' ms</strong> (raw)<br>' +
'Firmware effective delay: <strong>' + eff + ' ms</strong>' +
(raw <MC_RX_THRESHOLD_MS ? ' (below 50 ms threshold → immediate)' : '');
var thead = '<tr><th>rx_delay_base \\ score</th>';
for (var s = 0; s <MC_RX_SCORES.length; s++) {
thead += '<th>' + MC_RX_SCORES[s].toFixed(1) + '</th>';
}
thead += '</tr>';
document.querySelector('#mc-rxdelay-table thead').innerHTML = thead;
var tbody = '';
for (var r = 0; r <MC_RX_BASES.length; r++) {
var rx = MC_RX_BASES[r];
var rowClass = (Math.abs(rx - hlRx) <0.01) ? ' style="outline:2px solid #39f;"' : '';
tbody += '<tr' + rowClass + '><td class="row-hdr">' + rx + '</td>';
for (var c = 0; c <MC_RX_SCORES.length; c++) {
var sc = MC_RX_SCORES[c];
var val = mcCalcRxDelay(rx, sc, airtime);
var cls = '';
if (val <0) cls = 'neg';
else if (val <MC_RX_THRESHOLD_MS) cls = 'imm';
else cls = 'queued';
var hi = (Math.abs(rx - hlRx) <0.01 && Math.abs(sc - hlScore) <0.001) ? ' outline:2px solid #f90;' : '';
tbody += '<td class="' + cls + '" style="' + hi + '">' +
(val <0 ? '<span class="neg">' + val.toFixed(0) + '</span>' : val.toFixed(0)) +
'</td>';
}
tbody += '</tr>';
}
document.querySelector('#mc-rxdelay-table tbody').innerHTML = tbody;
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', mcRxDelayUpdate);
} else {
mcRxDelayUpdate();
} </script> </html>
SNR → Score Helper (optional second widget)
<html> <style>
#mc-score-calc { font-family: sans-serif; max-width: 480px; }
#mc-score-calc .controls { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 8px; }
#mc-score-calc .out { background: #f5f5f5; padding: 8px; border-radius: 4px; }
</style> <div id=„mc-score-calc“>
<p><strong>Estimate packet score from SNR</strong></p> <div class="controls"> <label>SNR (dB): <input type="number" id="mc-snr" value="-5" step="0.5"></label> <label>SF: <select id="mc-sf"><option>7</option><option>8</option><option>9</option><option selected>10</option><option>11</option><option>12</option></select></label> <label>Packet len: <input type="number" id="mc-plen" value="64" min="1" max="256"></label> <button type="button" onclick="mcScoreUpdate()">Calc</button> </div> <div class="out" id="mc-score-out"></div>
</div> <script type=„text/javascript“> function mcScoreUpdate() {
var snr = parseFloat(document.getElementById('mc-snr').value);
var sf = parseInt(document.getElementById('mc-sf').value, 10);
var pl = parseInt(document.getElementById('mc-plen').value, 10);
var score = mcPacketScore(snr, sf, pl);
document.getElementById('mc-score-out').innerHTML =
'Score = <strong>' + score.toFixed(3) + '</strong> — use this in the table above.';
} mcScoreUpdate(); </script> </html>
Practical Tuning Guide
- Start with
rxdelay 2on repeaters in busy areas; increase if you still see duplicate flood collisions. - Never use 0 < rxdelay < 1 — it reverses the logic (bad paths win).
rxdelay 1gives zero delay for all scores (same as off).- Compare delays for your typical packet airtime: a 300 ms airtime doubles all table values.
- Newer firmware may autotune delays from neighbor count (CLI:
get autotune/set autotune on). - RX delay is complementary to TX delay (
txdelay): TX delay spreads transmissions, RX delay spreads reception/processing of floods.
CLI Reference
get rxdelay # show current value (float, 0 = off) set rxdelay 2 # enable with base 2
Companion radio stores the value internally as millis × 1000; repeater/room/sensor use the float directly.