-
-
Notifications
You must be signed in to change notification settings - Fork 951
Add adaptive congestion control for backbone repeaters #1938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
wbijen
wants to merge
1
commit into
meshcore-dev:dev
Choose a base branch
from
wbijen:feature/adaptive-congestion-control
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| #pragma once | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| // --- Congestion control compile-time defines --- | ||
| // Override any of these via platformio.ini build_flags, e.g.: | ||
| // -D GROUP_FLOOD_MAX=16 -D BUSY_ONSET=0.20f | ||
|
|
||
| #ifndef BUSY_ONSET | ||
| #define BUSY_ONSET 0.15f // busy level below which no throttling occurs | ||
| #endif | ||
| #ifndef BUSY_WINDOW_MS | ||
| #define BUSY_WINDOW_MS 60000 // tumbling window for busy calculation (ms) | ||
| #endif | ||
|
|
||
| #ifndef GROUP_FLOOD_MAX | ||
| #define GROUP_FLOOD_MAX 8 // max group reach when quiet. 0 = no group forwarding. | ||
| #endif | ||
| #ifndef GROUP_FLOOD_MID | ||
| #define GROUP_FLOOD_MID 5 // knee point at moderate congestion | ||
| #endif | ||
| #ifndef GROUP_FLOOD_FLOOR | ||
| #define GROUP_FLOOD_FLOOR 2 // minimum group reach under extreme congestion | ||
| #endif | ||
|
|
||
| #ifndef ADVERT_FLOOD_MAX | ||
| #define ADVERT_FLOOD_MAX 8 // max advert reach when quiet. 0 = no advert forwarding. | ||
| #endif | ||
| #ifndef ADVERT_FLOOD_MID | ||
| #define ADVERT_FLOOD_MID 4 // knee point at moderate congestion | ||
| #endif | ||
| #ifndef ADVERT_FLOOD_FLOOR | ||
| #define ADVERT_FLOOD_FLOOR 1 // minimum advert reach under extreme congestion | ||
| #endif | ||
|
|
||
| // Busy score component weights (must sum to 1.0) | ||
| #ifndef BUSY_WEIGHT_AIRTIME | ||
| #define BUSY_WEIGHT_AIRTIME 0.5f // tx+rx airtime fraction of window | ||
| #endif | ||
| #ifndef BUSY_WEIGHT_QUEUE | ||
| #define BUSY_WEIGHT_QUEUE 0.3f // tx queue fill level | ||
| #endif | ||
| #ifndef BUSY_WEIGHT_DUP | ||
| #define BUSY_WEIGHT_DUP 0.2f // flood duplicate ratio | ||
| #endif | ||
|
|
||
| // TX delay scaling: delay multiplier = 1 + busy * BUSY_TX_DELAY_SCALE | ||
| // At busy=0: 1× (unchanged), at busy=1: (1+scale)× wider jitter window | ||
| #ifndef BUSY_TX_DELAY_SCALE | ||
| #define BUSY_TX_DELAY_SCALE 2.0f // busy=1 gives 3× wider retransmit window | ||
| #endif | ||
|
|
||
| /** | ||
| * \brief Piecewise-linear ramp: dead zone + two-segment curve through ceiling → mid → floor. | ||
| * Returns effective hop limit for a given busy score. | ||
| */ | ||
| inline uint8_t getEffectiveFloodMax(float busy, uint8_t ceiling, uint8_t mid, uint8_t floor) { | ||
| if (ceiling == 0) return 0; // type disabled | ||
| if (mid > ceiling) mid = ceiling; // guard against misconfigured overrides | ||
| if (floor > mid) floor = mid; | ||
| if (busy <= BUSY_ONSET) return ceiling; | ||
| if (busy >= 1.0f) return floor; | ||
|
|
||
| float t = (busy - BUSY_ONSET) / (1.0f - BUSY_ONSET); // normalize active zone to [0,1] | ||
| if (t <= 0.5f) { | ||
| float s = t / 0.5f; // [0,1] within first half | ||
| return mid + (uint8_t)((1.0f - s) * (ceiling - mid)); | ||
| } else { | ||
| float s = (t - 0.5f) / 0.5f; // [0,1] within second half | ||
| return floor + (uint8_t)((1.0f - s) * (mid - floor)); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * \brief Lightweight busy score tracker. Computes busy ∈ [0,1] over a rolling window | ||
| * from airtime ratio, queue depth, and flood duplicate ratio. | ||
| * Call update() from loop(). Recomputes every BUSY_WINDOW_MS (tumbling window). | ||
| * All counters come from existing Dispatcher/SimpleMeshTables. | ||
| */ | ||
| struct BusyTracker { | ||
| float busy; // composite score [0,1] | ||
| float airtime_ratio; // tx+rx airtime / window | ||
| float queue_ratio; // queue depth / capacity | ||
| float dup_ratio; // flood dups / flood recv | ||
|
|
||
| // snapshot of counters at start of window | ||
| unsigned long window_start; | ||
| unsigned long prev_tx_air; | ||
| unsigned long prev_rx_air; | ||
| uint32_t prev_flood_recv; | ||
| uint32_t prev_flood_dups; | ||
|
|
||
| void reset(unsigned long now_ms, | ||
| unsigned long tx_air, unsigned long rx_air, | ||
| uint32_t flood_recv, uint32_t flood_dups) { | ||
| busy = 0; | ||
| airtime_ratio = queue_ratio = dup_ratio = 0; | ||
| window_start = now_ms; | ||
| prev_tx_air = tx_air; | ||
| prev_rx_air = rx_air; | ||
| prev_flood_recv = flood_recv; | ||
| prev_flood_dups = flood_dups; | ||
| } | ||
|
|
||
| void update(unsigned long now_ms, | ||
| unsigned long tx_air, unsigned long rx_air, | ||
| int queue_count, int queue_capacity, | ||
| uint32_t flood_recv, uint32_t flood_dups) { | ||
| unsigned long elapsed = now_ms - window_start; | ||
| if (elapsed < BUSY_WINDOW_MS) return; // not yet time | ||
|
|
||
| // airtime deltas — detect counter wrap (~49 days) and skip window | ||
| unsigned long delta_tx = tx_air - prev_tx_air; | ||
| unsigned long delta_rx = rx_air - prev_rx_air; | ||
| if (delta_tx > elapsed || delta_rx > elapsed) { | ||
| // counter wrapped — re-baseline and keep previous busy score | ||
| prev_tx_air = tx_air; | ||
| prev_rx_air = rx_air; | ||
| prev_flood_recv = flood_recv; | ||
| prev_flood_dups = flood_dups; | ||
| window_start = now_ms; | ||
| return; | ||
| } | ||
|
|
||
| // airtime component: fraction of window spent transmitting or receiving | ||
| airtime_ratio = (float)(delta_tx + delta_rx) / (float)elapsed; | ||
| if (airtime_ratio > 1.0f) airtime_ratio = 1.0f; | ||
|
|
||
| // queue component: instantaneous depth / capacity | ||
| queue_ratio = (queue_capacity > 0) ? (float)queue_count / (float)queue_capacity : 0; | ||
| if (queue_ratio > 1.0f) queue_ratio = 1.0f; | ||
|
|
||
| // duplicate component: flood dups / flood recv | ||
| uint32_t delta_recv = flood_recv - prev_flood_recv; | ||
| uint32_t delta_dups = flood_dups - prev_flood_dups; | ||
| dup_ratio = (delta_recv > 0) ? (float)delta_dups / (float)delta_recv : 0; | ||
| if (dup_ratio > 1.0f) dup_ratio = 1.0f; | ||
|
|
||
| // weighted composite | ||
| busy = airtime_ratio * BUSY_WEIGHT_AIRTIME + queue_ratio * BUSY_WEIGHT_QUEUE + dup_ratio * BUSY_WEIGHT_DUP; | ||
| if (busy > 1.0f) busy = 1.0f; | ||
|
|
||
| // slide window | ||
| prev_tx_air = tx_air; | ||
| prev_rx_air = rx_air; | ||
| prev_flood_recv = flood_recv; | ||
| prev_flood_dups = flood_dups; | ||
| window_start = now_ms; | ||
| } | ||
| }; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could a max based on
min(_prefs->flood_max, getEffectiveFloodMax(...))be used, to adhere to a configured lower value?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lowest now wins, fixed and pushed! thanks.
It now has GROUP_FLOOD_MAX and ADVERT_FLOOD_MAX configured at 8. Do you think that value makes sense or you want me to set it to 64 so by default there is no change in behavior at quiet times?