Document direct retry CLI

This commit is contained in:
mikecarper committed 2026-06-15 17:42:53 -07:00
1 parent 765ec1447b
commit cb5e2e7c3d
4 files changed
+255 -7

No files matched your search

+224
View File
@@ -950,6 +950,230 @@ region save
- Enables flooding for all child regions automatically
- Useful for global networks with specific regional rules
---
### Direct Retry
Direct retry resends direct-routed packets when the downstream echo is not heard. It applies to direct messages and TRACE packets. It does not change ACK handling.
#### View or change direct retry state
**Usage:**
- `get direct.retry`
- `set direct.retry <state>`
**Parameters:**
- `state`: `on`|`off`
**Default:** `on`
**Examples:**
```
get direct.retry
set direct.retry on
set direct.retry off
```
---
#### View or apply a direct retry preset
**Usage:**
- `get retry.preset`
- `set retry.preset <preset>`
**Parameters:**
- `preset`: `infra`|`rooftop`|`mobile`
**Notes:**
- `infra`: fewer, slower retries for stable fixed infrastructure.
- `rooftop`: default long retry window for weak rooftop links.
- `mobile`: long retry count with shorter spacing for moving or changing links.
- Changing `direct.retry.count`, `direct.retry.base`, `direct.retry.step`, or `direct.retry.margin` makes the preset report as `custom`.
**Examples:**
```
get retry.preset
set retry.preset infra
set retry.preset rooftop
set retry.preset mobile
```
---
#### View or change direct retry count
**Usage:**
- `get direct.retry.count`
- `set direct.retry.count <count>`
**Parameters:**
- `count`: Maximum retry attempts after the original send, from `1` to `15`.
**Default:** `15` with the `rooftop` preset
**Examples:**
```
get direct.retry.count
set direct.retry.count 1
set direct.retry.count 4
set direct.retry.count 15
```
---
#### View or change direct retry base delay
**Usage:**
- `get direct.retry.base`
- `set direct.retry.base <ms>`
**Parameters:**
- `ms`: First retry wait in milliseconds, from `10` to `5000`.
**Default:** `175` with the `rooftop` preset
**Explanation:**
- The first retry waits `base` milliseconds after the failed echo window.
- Larger values reduce channel pressure and give slow repeaters more time.
- Smaller values recover faster but create tighter retry bursts.
**Examples:**
```
get direct.retry.base
set direct.retry.base 175
set direct.retry.base 275
set direct.retry.base 500
```
---
#### View or change direct retry step delay
**Usage:**
- `get direct.retry.step`
- `set direct.retry.step <ms>`
**Parameters:**
- `ms`: Extra milliseconds added for each subsequent retry, from `0` to `5000`.
**Default:** `100` with the `rooftop` preset
**Explanation:**
- Retry delay is `base + attempt_index * step`.
- With `base=175` and `step=100`, retries wait about `175`, `275`, `375`, `475` ms, and so on.
- `step=0` keeps every retry at the same delay.
- Larger steps spread retries over time and are safer on busy channels.
**Examples:**
```
get direct.retry.step
set direct.retry.step 0
set direct.retry.step 50
set direct.retry.step 100
set direct.retry.step 250
```
---
#### View or change direct retry SNR margin
**Usage:**
- `get direct.retry.margin`
- `set direct.retry.margin <snr_db>`
**Parameters:**
- `snr_db`: Extra SNR margin above the SF receive floor, from `0` to `40`.
**Default:** `5.00` with the `rooftop` preset
**Notes:**
- Unknown repeaters are still retried.
- Known repeaters below the receive floor plus this margin are skipped.
- Failed attempts lower the recent repeater SNR estimate by `0.25 dB`.
**Examples:**
```
get direct.retry.margin
set direct.retry.margin 0
set direct.retry.margin 2.5
set direct.retry.margin 5
set direct.retry.margin 10
```
---
#### View or change adaptive direct retry coding rate
**Usage:**
- `get direct.retry.cr`
- `set direct.retry.cr off`
- `set direct.retry.cr <cr4_min>,<cr5_min>,<cr7_min>,<cr8_max>`
**Parameters:**
- `cr4_min`: Minimum SNR in dB to retry at CR4.
- `cr5_min`: Minimum SNR in dB to retry at CR5.
- `cr7_min`: Minimum SNR in dB to retry at CR7.
- `cr8_max`: Maximum SNR in dB that forces CR8.
**Default:** `10.00,7.50,2.50,2.50`
**Explanation:**
- Higher SNR uses faster coding rates.
- Lower SNR uses more robust coding rates.
- CR6 is intentionally skipped.
- `off` disables per-packet retry CR overrides and uses the current radio CR.
- Unknown repeaters start at `+2.00 dB` for adaptive CR selection.
- A failed unknown repeater is seeded at `+1.75 dB`.
- Each later failure lowers the SNR estimate by `0.25 dB`.
**Examples:**
```
get direct.retry.cr
set direct.retry.cr off
set direct.retry.cr 10.0,7.5,2.5,2.5
set direct.retry.cr 12.0,8.0,4.0,1.0
set direct.retry.cr 8.0,5.0,1.5,0
set direct.retry.cr 6.0,3.0,0,-2.0
set direct.retry.cr 20.0,12.0,6.0,2.0
set direct.retry.cr 4.0,2.0,0,-4.0
```
**Example profiles:**
- Conservative weak-link profile:
```
set direct.retry.cr 12.0,8.0,4.0,1.0
```
- Balanced rooftop profile:
```
set direct.retry.cr 10.0,7.5,2.5,2.5
```
- Faster strong-link profile:
```
set direct.retry.cr 6.0,3.0,0,-2.0
```
- Very cautious noisy-link profile:
```
set direct.retry.cr 20.0,12.0,6.0,2.0
```
---
#### View, seed, or clear the recent repeater table
**Usage:**
- `get recent.repeater`
- `get recent.repeater <page>`
- `get recent.repeater page <page>`
- `set recent.repeater <prefix> <snr_db>`
- `clear recent.repeater`
**Parameters:**
- `prefix`: Repeater path-hash prefix as hex.
- `snr_db`: SNR in dB.
- `page`: 1-based result page.
**Examples:**
```
get recent.repeater
get recent.repeater 2
get recent.repeater page 3
set recent.repeater A1B2C3 8.5
set recent.repeater 71CE82 -3.25
clear recent.repeater
```
---
### GPS (When GPS support is compiled in)
+6 -1
View File
@@ -575,7 +575,8 @@ uint8_t MyMesh::getDirectRetryCodingRateForSNR(int8_t snr_x4) const {
if (snr_x4 >= _prefs.direct_retry_cr4_snr_x4) return 4;
if (snr_x4 >= _prefs.direct_retry_cr5_snr_x4) return 5;
if (snr_x4 >= _prefs.direct_retry_cr7_snr_x4) return 7;
return 8;
if (snr_x4 <= _prefs.direct_retry_cr8_snr_x4) return 8;
return 7;
}
uint8_t MyMesh::getDirectRetryPreset() const {
@@ -592,6 +593,9 @@ uint32_t MyMesh::getDirectRetryAttemptStepMillis() const {
bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const {
(void)packet;
if (!_prefs.direct_retry_enabled) {
return false;
}
if (next_hop_hash == NULL || next_hop_hash_len == 0) {
return true;
}
@@ -1108,6 +1112,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT;
_prefs.direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT;
_prefs.direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT;
_prefs.direct_retry_enabled = 1;
_prefs.direct_retry_cr_enabled = 1;
_prefs.direct_retry_prefs_magic[0] = DIRECT_RETRY_PREFS_MAGIC_0;
_prefs.direct_retry_prefs_magic[1] = DIRECT_RETRY_PREFS_MAGIC_1;
+24 -6
View File
@@ -101,6 +101,7 @@ static void setDefaultDirectRetryPrefs(NodePrefs* prefs) {
prefs->direct_retry_cr5_snr_x4 = DIRECT_RETRY_CR5_MIN_SNR_X4_DEFAULT;
prefs->direct_retry_cr7_snr_x4 = DIRECT_RETRY_CR7_MIN_SNR_X4_DEFAULT;
prefs->direct_retry_cr8_snr_x4 = DIRECT_RETRY_CR8_MAX_SNR_X4_DEFAULT;
prefs->direct_retry_enabled = 1;
markDirectRetryPrefsValid(prefs);
}
@@ -209,9 +210,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304
file.read((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305
file.read((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306
file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307
file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308
// next: 310
file.read((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307
file.read((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308
file.read((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309
// next: 311
// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
@@ -253,6 +255,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
_prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, 10, 5000);
_prefs->direct_retry_step_ms = constrain(_prefs->direct_retry_step_ms, 0, 5000);
_prefs->direct_retry_snr_margin_x4 = constrain(_prefs->direct_retry_snr_margin_x4, 0, 160);
_prefs->direct_retry_enabled = constrain(_prefs->direct_retry_enabled, 0, 1);
_prefs->direct_retry_cr_enabled = constrain(_prefs->direct_retry_cr_enabled, 0, 1);
file.close();
@@ -329,9 +332,10 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->direct_retry_cr5_snr_x4, sizeof(_prefs->direct_retry_cr5_snr_x4)); // 304
file.write((uint8_t *)&_prefs->direct_retry_cr7_snr_x4, sizeof(_prefs->direct_retry_cr7_snr_x4)); // 305
file.write((uint8_t *)&_prefs->direct_retry_cr8_snr_x4, sizeof(_prefs->direct_retry_cr8_snr_x4)); // 306
file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 307
file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 308
// next: 310
file.write((uint8_t *)&_prefs->direct_retry_enabled, sizeof(_prefs->direct_retry_enabled)); // 307
file.write((uint8_t *)&_prefs->direct_retry_cr_enabled, sizeof(_prefs->direct_retry_cr_enabled)); // 308
file.write((uint8_t *)&_prefs->direct_retry_prefs_magic, sizeof(_prefs->direct_retry_prefs_magic)); // 309
// next: 311
file.close();
}
@@ -833,6 +837,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "Error, must be infra, rooftop, or mobile");
}
} else if (memcmp(config, "direct.retry ", 13) == 0) {
if (memcmp(&config[13], "on", 2) == 0) {
_prefs->direct_retry_enabled = 1;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(&config[13], "off", 3) == 0) {
_prefs->direct_retry_enabled = 0;
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
}
} else if (memcmp(config, "direct.retry.margin ", 20) == 0) {
if (!looksNumeric(&config[20])) {
strcpy(reply, "Error, must be 0-40 dB");
@@ -1106,6 +1122,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
} else if (memcmp(config, "retry.preset", 12) == 0) {
sprintf(reply, "> %s", retryPresetName(_prefs->retry_preset));
} else if (memcmp(config, "direct.retry", 12) == 0 && (config[12] == 0 || config[12] == ' ')) {
sprintf(reply, "> %s", _prefs->direct_retry_enabled ? "on" : "off");
} else if (memcmp(config, "direct.retry.margin", 19) == 0) {
char margin[12];
formatSnrDbX4(margin, sizeof(margin), _prefs->direct_retry_snr_margin_x4);
+1
View File
@@ -102,6 +102,7 @@ struct NodePrefs { // persisted to file
int8_t direct_retry_cr5_snr_x4;
int8_t direct_retry_cr7_snr_x4;
int8_t direct_retry_cr8_snr_x4;
uint8_t direct_retry_enabled;
uint8_t direct_retry_cr_enabled;
uint8_t direct_retry_prefs_magic[2];
};