Skip to content

Fix/noise floor ratchet, adapt noise to the real floor with recovery in both directions#2933

Open
usrflo wants to merge 3 commits into
meshcore-dev:devfrom
usrflo:fix/noise-floor-ratchet
Open

Fix/noise floor ratchet, adapt noise to the real floor with recovery in both directions#2933
usrflo wants to merge 3 commits into
meshcore-dev:devfrom
usrflo:fix/noise-floor-ratchet

Conversation

@usrflo

@usrflo usrflo commented Jul 12, 2026

Copy link
Copy Markdown

Symptom

On long-running nodes (I saw it with an ufo integration branch, which combines feature/repeated-sending-2 + feature/quiet-dwell), direct-packet resends and dwell-gated TX get deferred or suppressed even on a quiet channel.
The radio's reported noise_floor drifts to the -120 clamp and never recovers, so the RSSI-margin LBT checks (isResendChannelActive, isChannelNoisy, isChannelActive with interference_threshold) stay permanently over-sensitive — every send looks like it collides with noise.

Root cause

RadioLibWrapper::loop() calibrated _noise_floor from a 64-sample block, but only accepted samples that satisfied

rssi < _noise_floor + SAMPLING_THRESHOLD   // SAMPLING_THRESHOLD = 14

That filter is a one-way ratchet: it admits ever-lower samples but rejects anything above the current floor, so the block mean can only move down. Over time it walks to the -120 lower clamp and sticks there. The only thing that reset it was resetAGC setting _noise_floor = 0 — but resetAGC is gated on agc_reset_interval, which defaults to 0 (off), and forcing 0 would anyway open a brief permissive LBT window (margin = RSSI − 0) until the next block completes.

The fix

Replace the ratcheted block mean with the median of the 64-sample block:

  • Accept every idle sample (!isReceivingPacket()) — no downward bias.
  • Sort the 64 samples and take the median (mean of the two middle values). The median rejects transient interference spikes in both directions and recovers upward as well as down.
  • Write _noise_floor only after a full block is collected. The previous value stays valid while the next block is sampled — no reset-to-0, hence no permissive LBT window during reconvergence.
  • Clamp the result to -120 (lower bound of the radio's RSSI range).
  • resetAGC() no longer touches _noise_floor; it only discards the in-progress block (the analog frontend was just reset, so queued samples are stale). _noise_floor itself is left in place because the median estimator no longer needs the hard reset that the ratchet did.

SAMPLING_THRESHOLD is removed (it only fed the ratchet filter).
NUM_NOISE_FLOOR_SAMPLES (64) moves to the header so the sample buffer can be a member array.

chart-noise-floor

usrflo and others added 3 commits July 11, 2026 12:10
The noise-floor calibration sampled only RSSI values below the current
floor + threshold, a one-way ratchet: it accepted ever-lower samples but
never recovered upward, so _noise_floor drifted to the -120 clamp and
stayed there. That left the RSSI-margin LBT (isChannelActive with
interference_threshold, plus isResendChannelActive / isChannelNoisy on
the feature branches that consume _noise_floor) permanently over-sensitive
— resends and dwell-gated TX deferred even on a quiet channel.

Replace the ratcheted block mean with the median of the 64-sample block:
- accepts every idle (!isReceivingPacket) sample — no downward bias;
- median rejects transient interference spikes (high and low outliers) and
  recovers in BOTH directions;
- _noise_floor is written only after a full block, so the previous value
  stays valid while the next block is sampled — no reset-to-0 and thus no
  permissive LBT window (margin = RSSI - 0) during reconvergence.

resetAGC no longer forces _noise_floor = 0 (the stuck-ratchet workaround);
it only discards the in-progress block so a fresh one is measured after the
analog frontend reset.

Verified: Heltec_v3_repeater firmware build (compiles RadioLibWrappers.cpp
against real RadioLib).

Co-Authored-By: Claude <noreply@anthropic.com>
Documents the ratchet-to-median fix on fix/noise-floor-ratchet: symptom,
root cause (one-way ratchet drift to -120), the median-of-64 replacement,
files touched, build verification note (sim does not compile
RadioLibWrappers.cpp; verified via Heltec_v3_repeater), and merge intent.

Co-Authored-By: Claude <noreply@anthropic.com>
@usrflo

usrflo commented Jul 15, 2026

Copy link
Copy Markdown
Author

Problem Proof on dev Branch, _noise_floor = -120 trap, Case C

Patch noise-floor-provocation-C.patch demonstrates on the dev branch that if _noise_floor ever drops to -120, it does not recover on its own - resulting in the RSSI-Margin-LBT becoming permanently hypersensitive. Recovery is only possible via a reboot or resetAGC() (which forces _noise_floor = 0).

  // [TEMP PROVOCATION C ] After ~20 s of healthy operation, seed
  // _noise_floor = -120 to demonstrate the unrecoverable trap. With the floor at -120 the
  // filter cutoff becomes -106; ambient idle RSSI (~-92) lies ABOVE that, so every sample is
  // rejected, the block never completes, and _noise_floor stays stuck at -120 until resetAGC().
  // The 20 s lead-in lets the healthy baseline (floor ~ -92, rejected=0) print first.

$ pio device monitor --baud 115200 --port /dev/meshhtv3
--- Terminal on /dev/meshhtv3 | 115200 8-N-1
--- Available filters and text transformations: debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
DEBUG: floor-sample: floor=-92 cutoff=-78 accepted=1/64 rejected=0 [9056 ms]
DEBUG: noise_floor = -92  [9069 ms]  (block: 64 accepted, 0 rejected above cutoff -78)
DEBUG: floor-sample: floor=-92 cutoff=-78 accepted=1/64 rejected=0 [11057 ms]
DEBUG: noise_floor = -92  [11070 ms]  (block: 64 accepted, 0 rejected above cutoff -78)
DEBUG: floor-sample: floor=-92 cutoff=-78 accepted=1/64 rejected=0 [13058 ms]
DEBUG: noise_floor = -93  [13071 ms]  (block: 64 accepted, 0 rejected above cutoff -78)
DEBUG: floor-sample: floor=-93 cutoff=-79 accepted=1/64 rejected=0 [15059 ms]
DEBUG: noise_floor = -92  [15072 ms]  (block: 64 accepted, 0 rejected above cutoff -79)
DEBUG: floor-sample: floor=-92 cutoff=-78 accepted=1/64 rejected=0 [17060 ms]
DEBUG: noise_floor = -93  [17073 ms]  (block: 64 accepted, 0 rejected above cutoff -78)
10:52:48 - 15/5/2024 U: TX, len=119 (type=4, route=D, payload_len=117)
DEBUG: floor-sample: floor=-93 cutoff=-79 accepted=1/64 rejected=0 [19878 ms]
DEBUG: noise_floor = -97  [19891 ms]  (block: 64 accepted, 0 rejected above cutoff -79)
DEBUG: >>> PROVOCATION C: seeded _noise_floor=-120 (cutoff -106), block reset @ [20000 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=4245 [20878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=9099 [21878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=13953 [22878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=19041 [23878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=24150 [24878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=29260 [25878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=34369 [26878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=39478 [27878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=44587 [28878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=49697 [29878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=54806 [30878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=59915 [31878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=65024 [32878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=4598 [33878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=9707 [34878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=14816 [35878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=19925 [36878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=25035 [37878 ms]
DEBUG: floor-sample: floor=-120 cutoff=-106 accepted=0/64 rejected=30144 [38878 ms]

@usrflo

usrflo commented Jul 15, 2026

Copy link
Copy Markdown
Author

Problem Proof on dev Branch, Drifting Floor, Case B

Patch noise-floor-provocation-B.patch demonstrates the one-way drift of the noise floor on the dev branch, provoked by a reduced threshold.

From 20 s — Drift (threshold = 2): cutoff = floor + 2 slips into the noise; each block censors the upper portion → floor slides monotonically downward, rejected count rises, cutoff follows.
Finally, a stall: as soon as the floor slips below the lower edge of the actual noise, even low-value samples are discarded → accepted count freezes, no more noise_floor lines appear — the same stall as in C, occurring no later than the -120 clamp.

What B demonstrates (vs. C)

Variant	Demonstrates	Mechanism
C	The trap: once at -120 → permanently stuck	Floor set directly to -120
B	The drift: the one-sided bias accumulates block by block, accelerates, and leads to a stall	Floor slides downward past the actual noise floor

Together, they cover: cause (B) and symptom (C).

$ pio device monitor --baud 115200 --port /dev/meshhtv3
--- Terminal on /dev/meshhtv3 | 115200 8-N-1
--- Available filters and text transformations: debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
DEBUG: floor-sample: floor=-88 cutoff=-74 accepted=1/64 rejected=0 [9053 ms]
DEBUG: noise_floor = -90  [9066 ms]  (block: 64 accepted, 0 rejected above cutoff -74)
DEBUG: floor-sample: floor=-90 cutoff=-76 accepted=1/64 rejected=0 [11054 ms]
DEBUG: noise_floor = -90  [11067 ms]  (block: 64 accepted, 0 rejected above cutoff -76)
DEBUG: floor-sample: floor=-90 cutoff=-76 accepted=1/64 rejected=0 [13055 ms]
DEBUG: noise_floor = -90  [13068 ms]  (block: 64 accepted, 0 rejected above cutoff -76)
DEBUG: floor-sample: floor=-90 cutoff=-76 accepted=1/64 rejected=0 [15056 ms]
DEBUG: noise_floor = -90  [15069 ms]  (block: 64 accepted, 0 rejected above cutoff -76)
DEBUG: floor-sample: floor=-90 cutoff=-76 accepted=1/64 rejected=0 [17057 ms]
DEBUG: noise_floor = -90  [17070 ms]  (block: 64 accepted, 0 rejected above cutoff -76)
10:55:13 - 15/5/2024 U: TX, len=119 (type=4, route=D, payload_len=117)
DEBUG: floor-sample: floor=-90 cutoff=-76 accepted=1/64 rejected=0 [19874 ms]
DEBUG: noise_floor = -94  [19888 ms]  (block: 64 accepted, 0 rejected above cutoff -76)
DEBUG: >>> PROVOCATION B: sampling threshold 14 -> 2 (cutoff into noise) @ [20000 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=1 [21059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=4748 [22059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=9498 [23059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=14496 [24059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=19494 [25059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=24493 [26059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=29491 [27059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=34490 [28059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=39488 [29059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=44487 [30059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=49485 [31059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=54484 [32059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=59482 [33059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=64481 [34059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=3943 [35059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=8942 [36059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=13940 [37059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=18939 [38059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=23937 [39059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=28936 [40059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=33934 [41059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=38933 [42059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=0/64 rejected=43931 [43059 ms]
10:55:37 - 15/5/2024 U RAW: 0A401C1D87C180408ADCD9E340B6B7C6AB67E184AC1F
10:55:37 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=11 RSSI=-8 score=1000 time=304 hash=FA52762105A668D3 [1D -> 1C]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=6/64 rejected=47830 [44059 ms]
10:55:38 - 15/5/2024 U RAW: 0E01B7BEFC9BE7003F
10:55:38 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-26 score=1000 time=242 hash=965BF82987750713
10:55:38 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=26/64 rejected=51142 [45059 ms]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=26/64 rejected=56140 [46059 ms]
10:55:40 - 15/5/2024 U RAW: 0A401C1DC08C688ADD169EBA8C73C8E24AFC42760CBA
10:55:40 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=11 RSSI=-8 score=1000 time=304 hash=5CBE8F7ACDE3FA62 [1D -> 1C]
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=32/64 rejected=60038 [47059 ms]
10:55:41 - 15/5/2024 U RAW: 0E01B7A0F5625500B7
10:55:41 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-26 score=1000 time=242 hash=9DFA572F7C6B9C07
10:55:41 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-94 cutoff=-92 accepted=52/64 rejected=63351 [48059 ms]
10:55:42 - 15/5/2024 U RAW: 0A401C1DF0DA76AC77CB8167C6D7C98E53617530A6BD
10:55:42 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=11 RSSI=-8 score=1000 time=304 hash=AC33E9424DBBD29B [1D -> 1C]
DEBUG: noise_floor = -104  [48340 ms]  (block: 64 accepted, 63652 rejected above cutoff -92)
10:55:42 - 15/5/2024 U RAW: 0E01B773D0F84E0043
10:55:42 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-26 score=1000 time=242 hash=D68792B891BDCB64
10:55:42 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=1 [49073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=4998 [50073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=9997 [51073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=14995 [52073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=19993 [53073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=24992 [54073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=29990 [55073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=34988 [56073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=39987 [57073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=44985 [58073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=49983 [59073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=54982 [60073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=59980 [61073 ms]
DEBUG: floor-sample: floor=-104 cutoff=-102 accepted=0/64 rejected=64978 [62073 ms]

@usrflo

usrflo commented Jul 15, 2026

Copy link
Copy Markdown
Author

Proof of fix/noise-floor-ratchet (PR 2933), _noise_floor = -120 trap, Case C

Patch fix-noise-floor-ratchet-C.patch demonstrates the noise floor doesn't drop permanently if once lowered using the changes from PR 2933.

$ pio device monitor --baud 115200 --port /dev/meshhtv3
--- Terminal on /dev/meshhtv3 | 115200 8-N-1
--- Available filters and text transformations: debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
DEBUG: floor-sample: floor=-91 accepted=0/64 [5057 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [5070 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [7058 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [7071 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [9059 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [9072 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [11060 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [11073 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [13061 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [13074 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [15062 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-90] [15075 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [17063 ms]
DEBUG: noise_floor = -91 (median) [mean=-91 min=-92 max=-91] [17076 ms]
10:52:48 - 15/5/2024 U: TX, len=119 (type=4, route=D, payload_len=117)
DEBUG: floor-sample: floor=-91 accepted=0/64 [19881 ms]
DEBUG: noise_floor = -91 (median) [mean=-95 min=-127 max=-90] [19894 ms]
DEBUG: >>> PROVOCATION C: seeded _noise_floor=-120 (expect recovery on next block) @ [20000 ms]
DEBUG: floor-sample: floor=-120 accepted=0/64 [21065 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-92 max=-90] [21078 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [23066 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [23078 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [25067 ms]
DEBUG: noise_floor = -91 (median) [mean=-90 min=-91 max=-90] [25079 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [27068 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-90] [27080 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [29069 ms]

@usrflo

usrflo commented Jul 15, 2026

Copy link
Copy Markdown
Author

Proof of fix/noise-floor-ratchet (PR 2933), Drifting Floor, Case B

Patch fix-noise-floor-ratchet-B.patch demonstrates the noise floor doesn't drift one-sided when using the changes from PR 2933.

$ pio device monitor --baud 115200 --port /dev/meshhtv3
--- Terminal on /dev/meshhtv3 | 115200 8-N-1
--- Available filters and text transformations: debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
DEBUG: RX Boosted Gain Mode: Enabled
DEBUG: noise_floor = -90 (median) [mean=-89 min=-90 max=-89] [3100 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [5051 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-90] [5064 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [7052 ms]
DEBUG: noise_floor = -90 (median) [mean=-89 min=-90 max=-89] [7065 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [9053 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-89] [9066 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [11054 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-89] [11067 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [13055 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-89] [13068 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [15056 ms]
DEBUG: noise_floor = -90 (median) [mean=-90 min=-91 max=-90] [15069 ms]
10:54:32 - 15/5/2024 U RAW: 0A401C1DE928A5B89399ED9A0B15B35CD9A441B1E010
10:54:32 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=12 RSSI=-32 score=1000 time=304 hash=18A4BA9D9F519DA1 [1D -> 1C]
DEBUG: floor-sample: floor=-90 accepted=0/64 [17057 ms]
DEBUG: noise_floor = -77 (median) [mean=-60 min=-90 max=-24] [17070 ms]
10:54:33 - 15/5/2024 U RAW: 0E01B7CABB1460004F
10:54:33 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=11 RSSI=-24 score=1000 time=242 hash=73FC3496B9E94CAF
10:54:33 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
10:54:34 - 15/5/2024 U: TX, len=119 (type=4, route=D, payload_len=117)
DEBUG: floor-sample: floor=-77 accepted=0/64 [19875 ms]
DEBUG: noise_floor = -91 (median) [mean=-95 min=-127 max=-90] [19889 ms]
DEBUG: >>> PROVOCATION B: injecting -120 outliers (median must hold, mean drifts) @ [21059 ms]
DEBUG: floor-sample: floor=-91 accepted=0/64 [21059 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-89] [21082 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [23060 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-89] [23073 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [25061 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-90] [25074 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [27062 ms]
DEBUG: noise_floor = -22 (median) [mean=-46 min=-120 max=-22] [27075 ms]
10:54:43 - 15/5/2024 U RAW: 0A401C1DAFA16FE21459BDF35391087461C1C5AD6A62
10:54:43 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=12 RSSI=-17 score=1000 time=304 hash=EF5F9157ACE0F8DF [1D -> 1C]
10:54:43 - 15/5/2024 U RAW: 0E01B7311E6F0E00AB
10:54:43 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-24 score=1000 time=242 hash=E56135797E5E17ED
10:54:43 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-22 accepted=0/64 [29063 ms]
DEBUG: noise_floor = -92 (median) [mean=-98 min=-120 max=-91] [29076 ms]
10:54:46 - 15/5/2024 U RAW: 0A401C1D8733BC5DE52436078BC247C0E25B66320CD7
10:54:46 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=13 RSSI=0 score=1000 time=304 hash=143B9E7FD6936FAD [1D -> 1C]
DEBUG: floor-sample: floor=-92 accepted=0/64 [31064 ms]
DEBUG: noise_floor = -21 (median) [mean=-45 min=-120 max=-21] [31077 ms]
10:54:47 - 15/5/2024 U RAW: 0E01B7E8EF1A600083
10:54:47 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-22 score=1000 time=242 hash=ECBA20C7317FA361
10:54:47 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-21 accepted=0/64 [33065 ms]
DEBUG: noise_floor = -94 (median) [mean=-100 min=-120 max=-92] [33078 ms]
10:54:49 - 15/5/2024 U RAW: 0A401C1D738E54D75C913FD11DD6BB449CF77630A871
10:54:49 - 15/5/2024 U: RX, len=22 (type=2, route=D, payload_len=20) SNR=12 RSSI=0 score=1000 time=304 hash=275292B1E4896AA4 [1D -> 1C]
10:54:49 - 15/5/2024 U RAW: 0E01B7900F99500089
10:54:49 - 15/5/2024 U: RX, len=9 (type=3, route=D, payload_len=6) SNR=12 RSSI=-23 score=1000 time=242 hash=2290DE9D91996D60
10:54:49 - 15/5/2024 U: TX, len=8 (type=3, route=D, payload_len=6)
DEBUG: floor-sample: floor=-94 accepted=0/64 [35066 ms]
DEBUG: noise_floor = -94 (median) [mean=-100 min=-120 max=-93] [35079 ms]
DEBUG: floor-sample: floor=-94 accepted=0/64 [37067 ms]
DEBUG: noise_floor = -94 (median) [mean=-100 min=-120 max=-93] [37080 ms]
DEBUG: floor-sample: floor=-94 accepted=0/64 [39068 ms]
DEBUG: noise_floor = -94 (median) [mean=-100 min=-120 max=-93] [39081 ms]
DEBUG: floor-sample: floor=-94 accepted=0/64 [41069 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-89] [41082 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [43070 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-89] [43083 ms]
DEBUG: floor-sample: floor=-90 accepted=0/64 [45071 ms]
DEBUG: noise_floor = -90 (median) [mean=-97 min=-120 max=-88] [45084 ms]

@usrflo

usrflo commented Jul 15, 2026

Copy link
Copy Markdown
Author

Overall picture: 4 test/debug patches (bug ↔ solution)

Above you can find 4 patches that demonstrate a) the problem b) the bugfix of this pull request.

Branch Case B (Drift) Case C (Trap)
dev (Bug) noise-floor-provocation-B.patch → floor slips → stuck noise-floor-provocation-C.patch → permanent stuck
fix/noise-floor-ratchet (solution) fix-noise-floor-ratchet-B.patch → median holds fix-noise-floor-ratchet-C.patch → recovery

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant