* Protobufs * Wire up MEDIUM_TURBO modem preset MEDIUM_TURBO (500 kHz, SF9, CR 4/5) already existed in the protobuf enum but was never wired into firmware, so selecting it silently fell through to the LONG_FAST default and rendered an "Invalid" display name. Add its bw/sf/cr mapping (modemPresetToParams), display name (MediumTurbo/MedT), PRESETS_STD membership (standard regions only — 500 kHz does not fit EU868's 250 kHz band, so it stays out of PRESETS_EU_868 and is rejected/clamped there), and the MEDIUM SNR-grading bucket. Includes positive coverage in test_radio, EU868-reject + US-accept coverage in test_admin_radio and test_mesh_beacon, the STD preset count 9->10, an extended fuzz range, and the client-spec doc. * Address review feedback on MEDIUM_TURBO tests - test_mesh_beacon: assert has_mesh_beacon before checking the invalid preset was cleared, so the EU868-cleared test can't pass on a dropped message (matches the existing SHORT_TURBO test). - test_fuzz_packets: draw modem presets from _ModemPreset_ARRAYSIZE instead of a hard-coded 17 so the fuzz range tracks future enum additions automatically.
297 lines
9.2 KiB
C++
297 lines
9.2 KiB
C++
#pragma once
|
|
|
|
#include "MemoryPool.h"
|
|
#include "MeshTypes.h"
|
|
#include "PointerQueue.h"
|
|
#include "configuration.h"
|
|
#include "detect/LoRaRadioType.h"
|
|
|
|
// Sentinel marking the end of a modem preset array. Declared `const` rather
|
|
// than `constexpr` because the cast from 0xFF to the enum is out-of-range and
|
|
// therefore not a valid constant expression on Clang 16+ (Apple Clang on
|
|
// macOS). The value is only ever compared at runtime, so static-init is fine.
|
|
static const meshtastic_Config_LoRaConfig_ModemPreset MODEM_PRESET_END =
|
|
static_cast<meshtastic_Config_LoRaConfig_ModemPreset>(0xFF);
|
|
|
|
#define PRESET(name) meshtastic_Config_LoRaConfig_ModemPreset_##name
|
|
|
|
// Override slot magic numbers for RegionInfo.overrideSlot
|
|
#define OVERRIDE_SLOT_DEFAULT_CHANNEL_HASH 0 // Use hash of primary channel name
|
|
#define OVERRIDE_SLOT_PRESET_HASH -1 // Use hash of preset name instead
|
|
// Positive values (1-32767) are explicit slot numbers
|
|
|
|
// Region profile: bundles the preset list with regulatory parameters shared across regions
|
|
struct RegionProfile {
|
|
const meshtastic_Config_LoRaConfig_ModemPreset *presets; // sentinel-terminated
|
|
float spacing; // gaps between radio channels
|
|
float padding; // padding at each side of the "operating channel"
|
|
bool audioPermitted;
|
|
bool licensedOnly; // a region profile for licensed operators only
|
|
int8_t textThrottle; // throttle for text - future expansion
|
|
int8_t positionThrottle; // throttle for location data - future expansion
|
|
int8_t telemetryThrottle; // throttle for telemetry - future expansion
|
|
};
|
|
|
|
/**
|
|
* Get the effective duty cycle for the current region based on device role.
|
|
* For EU_866, returns 10% for fixed devices (ROUTER, ROUTER_LATE) and 2.5% for mobile devices.
|
|
* For other regions, returns the standard duty cycle.
|
|
*/
|
|
extern float getEffectiveDutyCycle();
|
|
|
|
extern const RegionProfile PROFILE_STD;
|
|
extern const RegionProfile PROFILE_EU868;
|
|
extern const RegionProfile PROFILE_UNDEF;
|
|
extern const RegionProfile PROFILE_LITE;
|
|
extern const RegionProfile PROFILE_NARROW;
|
|
extern const RegionProfile PROFILE_HAM_20KHZ;
|
|
extern const RegionProfile PROFILE_HAM_100KHZ;
|
|
|
|
// Map from old region names to new region enums
|
|
struct RegionInfo {
|
|
meshtastic_Config_LoRaConfig_RegionCode code;
|
|
float freqStart;
|
|
float freqEnd;
|
|
float dutyCycle; // modified by getEffectiveDutyCycle
|
|
uint8_t powerLimit; // Or zero for not set
|
|
bool freqSwitching;
|
|
bool wideLora;
|
|
const RegionProfile *profile;
|
|
meshtastic_Config_LoRaConfig_ModemPreset defaultPreset;
|
|
int16_t overrideSlot; // per-region override slot for frequency selection
|
|
// Magic values: 0 = use channel name hash, -1 = use preset name hash, >0 = explicit slot
|
|
const char *name; // EU433 etc
|
|
|
|
// Preset accessors (delegate through profile)
|
|
meshtastic_Config_LoRaConfig_ModemPreset getDefaultPreset() const { return defaultPreset; }
|
|
const meshtastic_Config_LoRaConfig_ModemPreset *getAvailablePresets() const { return profile->presets; }
|
|
bool supportsPreset(meshtastic_Config_LoRaConfig_ModemPreset preset) const
|
|
{
|
|
for (size_t i = 0; profile->presets[i] != MODEM_PRESET_END; i++) {
|
|
if (profile->presets[i] == preset)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
size_t getNumPresets() const
|
|
{
|
|
size_t n = 0;
|
|
while (profile->presets[n] != MODEM_PRESET_END)
|
|
n++;
|
|
return n;
|
|
}
|
|
};
|
|
|
|
extern const RegionInfo regions[];
|
|
extern const RegionInfo *myRegion;
|
|
|
|
extern void initRegion();
|
|
extern const RegionInfo *getRegion(meshtastic_Config_LoRaConfig_RegionCode code);
|
|
|
|
// Fill `map` with the region->valid-preset table, grouped so regions sharing a
|
|
// preset list reference the same group. Sent to clients during want_config so
|
|
// their UI can block illegal region+preset combinations.
|
|
extern void getRegionPresetMap(meshtastic_LoRaRegionPresetMap &map);
|
|
|
|
// Valid LoRa spread factor range and defaults
|
|
constexpr uint8_t LORA_SF_MIN = 5;
|
|
constexpr uint8_t LORA_SF_MAX = 12;
|
|
constexpr uint8_t LORA_SF_DEFAULT = 11; // LONG_FAST default
|
|
|
|
// Valid LoRa coding rate range and default
|
|
constexpr uint8_t LORA_CR_MIN = 4;
|
|
constexpr uint8_t LORA_CR_MAX = 8;
|
|
constexpr uint8_t LORA_CR_DEFAULT = 5; // LONG_FAST default
|
|
|
|
// Default bandwidth in kHz (LONG_FAST)
|
|
constexpr float LORA_BW_DEFAULT_KHZ = 250.0f;
|
|
|
|
/// Clamp spread factor to the valid LoRa range [5, 12].
|
|
/// Out-of-range values (including 0 from unset preset mode) return LORA_SF_DEFAULT.
|
|
static inline uint8_t clampSpreadFactor(uint8_t sf)
|
|
{
|
|
// We check for RF95 radios that are incompatible with Spreading Factors 5 and 6.
|
|
if (radioType == RF95_RADIO && (sf == 5 || sf == 6))
|
|
return LORA_SF_DEFAULT;
|
|
|
|
if (sf < LORA_SF_MIN || sf > LORA_SF_MAX)
|
|
return LORA_SF_DEFAULT;
|
|
return sf;
|
|
}
|
|
|
|
/// Clamp coding rate to the valid LoRa range [4, 8].
|
|
/// Out-of-range values return LORA_CR_DEFAULT.
|
|
static inline uint8_t clampCodingRate(uint8_t cr)
|
|
{
|
|
if (cr < LORA_CR_MIN || cr > LORA_CR_MAX)
|
|
return LORA_CR_DEFAULT;
|
|
return cr;
|
|
}
|
|
|
|
/// Ensure bandwidth is positive. Non-positive values return LORA_BW_DEFAULT_KHZ.
|
|
static inline float clampBandwidthKHz(float bwKHz)
|
|
{
|
|
if (bwKHz <= 0.0f)
|
|
return LORA_BW_DEFAULT_KHZ;
|
|
return bwKHz;
|
|
}
|
|
|
|
static inline float bwCodeToKHz(uint16_t bwCode)
|
|
{
|
|
if (bwCode == 8)
|
|
return 7.8f;
|
|
if (bwCode == 10)
|
|
return 10.4f;
|
|
if (bwCode == 16)
|
|
return 15.6f;
|
|
if (bwCode == 21)
|
|
return 20.8f;
|
|
if (bwCode == 31)
|
|
return 31.25f;
|
|
if (bwCode == 42)
|
|
return 41.7f;
|
|
if (bwCode == 62)
|
|
return 62.5f;
|
|
if (bwCode == 200)
|
|
return 203.125f;
|
|
if (bwCode == 400)
|
|
return 406.25f;
|
|
if (bwCode == 800)
|
|
return 812.5f;
|
|
if (bwCode == 1600)
|
|
return 1625.0f;
|
|
return (float)bwCode;
|
|
}
|
|
|
|
static inline uint16_t bwKHzToCode(float bwKHz)
|
|
{
|
|
if (bwKHz > 7.7f && bwKHz < 7.9f)
|
|
return 8;
|
|
if (bwKHz > 10.3f && bwKHz < 10.5f)
|
|
return 10;
|
|
if (bwKHz > 15.5f && bwKHz < 15.7f)
|
|
return 16;
|
|
if (bwKHz > 20.7f && bwKHz < 20.9f)
|
|
return 21;
|
|
if (bwKHz > 31.24f && bwKHz < 31.26f)
|
|
return 31;
|
|
if (bwKHz > 41.6f && bwKHz < 41.8f)
|
|
return 42;
|
|
if (bwKHz > 62.49f && bwKHz < 62.51f)
|
|
return 62;
|
|
if (bwKHz > 203.12f && bwKHz < 203.13f)
|
|
return 200;
|
|
if (bwKHz > 406.24f && bwKHz < 406.26f)
|
|
return 400;
|
|
if (bwKHz > 812.49f && bwKHz < 812.51f)
|
|
return 800;
|
|
if (bwKHz > 1624.99f && bwKHz < 1625.01f)
|
|
return 1600;
|
|
return (uint16_t)(bwKHz + 0.5f);
|
|
}
|
|
|
|
/// Clamp a bandwidth *code* (the on-wire config.lora.bandwidth value) to a usable value.
|
|
/// A code of 0 (proto default / unset) returns the default bandwidth's code; any other value
|
|
/// is returned unchanged (RadioLib validates the exact discrete bandwidth downstream).
|
|
static inline uint16_t clampBandwidthCode(uint16_t bwCode)
|
|
{
|
|
if (bwCode == 0)
|
|
return bwKHzToCode(LORA_BW_DEFAULT_KHZ);
|
|
return bwCode;
|
|
}
|
|
|
|
static inline void modemPresetToParams(meshtastic_Config_LoRaConfig_ModemPreset preset, bool wideLora, float &bwKHz, uint8_t &sf,
|
|
uint8_t &cr)
|
|
{
|
|
switch (preset) {
|
|
case PRESET(SHORT_TURBO):
|
|
bwKHz = wideLora ? 1625.0f : 500.0f;
|
|
cr = 5;
|
|
sf = 7;
|
|
break;
|
|
case PRESET(SHORT_FAST):
|
|
bwKHz = wideLora ? 812.5f : 250.0f;
|
|
cr = 5;
|
|
sf = 7;
|
|
break;
|
|
case PRESET(SHORT_SLOW):
|
|
bwKHz = wideLora ? 812.5f : 250.0f;
|
|
cr = 5;
|
|
sf = 8;
|
|
break;
|
|
case PRESET(MEDIUM_FAST):
|
|
bwKHz = wideLora ? 812.5f : 250.0f;
|
|
cr = 5;
|
|
sf = 9;
|
|
break;
|
|
case PRESET(MEDIUM_SLOW):
|
|
bwKHz = wideLora ? 812.5f : 250.0f;
|
|
cr = 5;
|
|
sf = 10;
|
|
break;
|
|
case PRESET(MEDIUM_TURBO):
|
|
bwKHz = wideLora ? 1625.0f : 500.0f;
|
|
cr = 5;
|
|
sf = 9;
|
|
break;
|
|
case PRESET(LONG_TURBO):
|
|
bwKHz = wideLora ? 1625.0f : 500.0f;
|
|
cr = 8;
|
|
sf = 11;
|
|
break;
|
|
case PRESET(LONG_MODERATE):
|
|
bwKHz = wideLora ? 406.25f : 125.0f;
|
|
cr = 8;
|
|
sf = 11;
|
|
break;
|
|
case PRESET(LONG_SLOW):
|
|
bwKHz = wideLora ? 406.25f : 125.0f;
|
|
cr = 8;
|
|
sf = 12;
|
|
break;
|
|
case PRESET(LITE_FAST):
|
|
bwKHz = 125;
|
|
cr = 5;
|
|
sf = 9;
|
|
break;
|
|
case PRESET(LITE_SLOW):
|
|
bwKHz = 125;
|
|
cr = 5;
|
|
sf = 10;
|
|
break;
|
|
case PRESET(NARROW_FAST):
|
|
bwKHz = 62.5f;
|
|
cr = 6;
|
|
sf = 7;
|
|
break;
|
|
case PRESET(NARROW_SLOW):
|
|
bwKHz = 62.5f;
|
|
cr = 6;
|
|
sf = 8;
|
|
break;
|
|
case PRESET(TINY_FAST):
|
|
bwKHz = 15.6f;
|
|
cr = 5;
|
|
sf = 7;
|
|
break;
|
|
case PRESET(TINY_SLOW):
|
|
bwKHz = 15.6f;
|
|
cr = 6;
|
|
sf = 8;
|
|
break;
|
|
default: // LONG_FAST (or illegal)
|
|
bwKHz = wideLora ? 812.5f : 250.0f;
|
|
cr = 5;
|
|
sf = 11;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline float modemPresetToBwKHz(meshtastic_Config_LoRaConfig_ModemPreset preset, bool wideLora)
|
|
{
|
|
float bwKHz = 0;
|
|
uint8_t sf = 0;
|
|
uint8_t cr = 0;
|
|
modemPresetToParams(preset, wideLora, bwKHz, sf, cr);
|
|
return bwKHz;
|
|
} |