mirror of
https://github.com/meshtastic/firmware.git
synced 2026-03-28 03:53:13 -04:00
Scaling tweaks (#9653)
* refactor: update throttling factor calculation and add unit tests for scaling behavior * refactor: adjust throttling factor calculation for improved accuracy in different configurations * Update src/mesh/Default.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/mesh/Default.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * refactor: enhance throttling factor calculation and introduce pow_of_2 utility function * refactor: improve expected ms calculation in unit tests for Default::getConfiguredOrDefaultMsScaled * refactor: improve scaling logic for routers and sensors in computeExpectedMs function --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#include <MeshRadio.h>
|
||||
#include <NodeDB.h>
|
||||
#include <RadioInterface.h>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <meshUtils.h>
|
||||
#define ONE_DAY 24 * 60 * 60
|
||||
@@ -63,25 +66,26 @@ class Default
|
||||
if (numOnlineNodes <= 40) {
|
||||
return 1.0;
|
||||
} else {
|
||||
float throttlingFactor = 0.075;
|
||||
if (config.lora.use_preset && config.lora.modem_preset == meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW)
|
||||
throttlingFactor = 0.04;
|
||||
else if (config.lora.use_preset && config.lora.modem_preset == meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST)
|
||||
throttlingFactor = 0.02;
|
||||
else if (config.lora.use_preset &&
|
||||
IS_ONE_OF(config.lora.modem_preset, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW))
|
||||
throttlingFactor = 0.01;
|
||||
// Get bandwidth in kHz - convert from code if not using preset
|
||||
float bwKHz =
|
||||
config.lora.use_preset ? modemPresetToBwKHz(config.lora.modem_preset, false) : bwCodeToKHz(config.lora.bandwidth);
|
||||
|
||||
// throttlingFactor = 2^SF / (BW_in_kHz * scaling_divisor)
|
||||
// With scaling_divisor=100:
|
||||
// In SF11 and BW=250khz (longfast), this gives 0.08192 rather than the original 0.075
|
||||
// In SF10 and BW=250khz (mediumslow), this gives 0.04096 rather than the original 0.04
|
||||
// In SF9 and BW=250khz (mediumfast), this gives 0.02048 rather than the original 0.02
|
||||
// In SF7 and BW=250khz (shortfast), this gives 0.00512 rather than the original 0.01
|
||||
float throttlingFactor = static_cast<float>(pow_of_2(config.lora.spread_factor)) / (bwKHz * 100.0f);
|
||||
|
||||
#if USERPREFS_EVENT_MODE
|
||||
// If we are in event mode, scale down the throttling factor
|
||||
throttlingFactor = 0.04;
|
||||
// If we are in event mode, scale down the throttling factor by 4
|
||||
throttlingFactor = static_cast<float>(pow_of_2(config.lora.spread_factor)) / (bwKHz * 25.0f);
|
||||
#endif
|
||||
|
||||
// Scaling up traffic based on number of nodes over 40
|
||||
int nodesOverForty = (numOnlineNodes - 40);
|
||||
return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by 0.075 (default)
|
||||
return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by throttle factor
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "configuration.h"
|
||||
#include "detect/LoRaRadioType.h"
|
||||
#include "main.h"
|
||||
#include "meshUtils.h" // for pow_of_2
|
||||
#include "sleep.h"
|
||||
#include <assert.h>
|
||||
#include <pb_decode.h>
|
||||
@@ -31,12 +32,6 @@
|
||||
#include "STM32WLE5JCInterface.h"
|
||||
#endif
|
||||
|
||||
// Calculate 2^n without calling pow()
|
||||
uint32_t pow_of_2(uint32_t n)
|
||||
{
|
||||
return 1 << n;
|
||||
}
|
||||
|
||||
#define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching, wide_lora) \
|
||||
{ \
|
||||
meshtastic_Config_LoRaConfig_RegionCode_##name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, \
|
||||
|
||||
@@ -38,4 +38,10 @@ const std::string vformat(const char *const zcFormat, ...);
|
||||
// Get actual string length for nanopb char array fields.
|
||||
size_t pb_string_length(const char *str, size_t max_len);
|
||||
|
||||
/// Calculate 2^n without calling pow() - used for spreading factor and other calculations
|
||||
inline uint32_t pow_of_2(uint32_t n)
|
||||
{
|
||||
return 1 << n;
|
||||
}
|
||||
|
||||
#define IS_ONE_OF(item, ...) isOneOf(item, sizeof((int[]){__VA_ARGS__}) / sizeof(int), __VA_ARGS__)
|
||||
|
||||
109
test/test_default/test_main.cpp
Normal file
109
test/test_default/test_main.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
// Unit tests for Default::getConfiguredOrDefaultMsScaled
|
||||
#include "Default.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "TestUtil.h"
|
||||
#include "meshUtils.h"
|
||||
#include <unity.h>
|
||||
|
||||
// Helper to compute expected ms using same logic as Default::congestionScalingCoefficient
|
||||
static uint32_t computeExpectedMs(uint32_t defaultSeconds, uint32_t numOnlineNodes)
|
||||
{
|
||||
uint32_t baseMs = Default::getConfiguredOrDefaultMs(0, defaultSeconds);
|
||||
|
||||
// Routers don't scale
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
|
||||
return baseMs;
|
||||
}
|
||||
|
||||
// Sensors and trackers don't scale
|
||||
if ((config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR) ||
|
||||
(config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER)) {
|
||||
return baseMs;
|
||||
}
|
||||
|
||||
if (numOnlineNodes <= 40) {
|
||||
return baseMs;
|
||||
}
|
||||
|
||||
float bwKHz =
|
||||
config.lora.use_preset ? modemPresetToBwKHz(config.lora.modem_preset, false) : bwCodeToKHz(config.lora.bandwidth);
|
||||
|
||||
uint8_t sf = config.lora.spread_factor;
|
||||
if (sf < 7)
|
||||
sf = 7;
|
||||
else if (sf > 12)
|
||||
sf = 12;
|
||||
|
||||
float throttlingFactor = static_cast<float>(pow_of_2(sf)) / (bwKHz * 100.0f);
|
||||
#if USERPREFS_EVENT_MODE
|
||||
throttlingFactor = static_cast<float>(pow_of_2(sf)) / (bwKHz * 25.0f);
|
||||
#endif
|
||||
|
||||
int nodesOverForty = (numOnlineNodes - 40);
|
||||
float coeff = 1.0f + (nodesOverForty * throttlingFactor);
|
||||
return static_cast<uint32_t>(baseMs * coeff + 0.5f);
|
||||
}
|
||||
|
||||
void test_router_no_scaling()
|
||||
{
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_ROUTER;
|
||||
// set some sane lora config so bootstrap paths are deterministic
|
||||
config.lora.use_preset = false;
|
||||
config.lora.spread_factor = 9;
|
||||
config.lora.bandwidth = 250;
|
||||
|
||||
uint32_t res = Default::getConfiguredOrDefaultMsScaled(0, 60, 100);
|
||||
uint32_t expected = computeExpectedMs(60, 100);
|
||||
TEST_ASSERT_EQUAL_UINT32(expected, res);
|
||||
}
|
||||
|
||||
void test_client_below_threshold()
|
||||
{
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
config.lora.use_preset = false;
|
||||
config.lora.spread_factor = 9;
|
||||
config.lora.bandwidth = 250;
|
||||
|
||||
uint32_t res = Default::getConfiguredOrDefaultMsScaled(0, 60, 40);
|
||||
uint32_t expected = computeExpectedMs(60, 40);
|
||||
TEST_ASSERT_EQUAL_UINT32(expected, res);
|
||||
}
|
||||
|
||||
void test_client_default_preset_scaling()
|
||||
{
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
config.lora.use_preset = false;
|
||||
config.lora.spread_factor = 9; // SF9
|
||||
config.lora.bandwidth = 250; // 250 kHz
|
||||
|
||||
uint32_t res = Default::getConfiguredOrDefaultMsScaled(0, 60, 50);
|
||||
uint32_t expected = computeExpectedMs(60, 50); // nodesOverForty = 10
|
||||
TEST_ASSERT_EQUAL_UINT32(expected, res);
|
||||
}
|
||||
|
||||
void test_client_medium_fast_preset_scaling()
|
||||
{
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
config.lora.use_preset = true;
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
|
||||
// nodesOverForty = 30 -> test with nodes=70
|
||||
uint32_t res = Default::getConfiguredOrDefaultMsScaled(0, 60, 70);
|
||||
uint32_t expected = computeExpectedMs(60, 70);
|
||||
// Allow ±1 ms tolerance for floating-point rounding
|
||||
TEST_ASSERT_INT_WITHIN(1, expected, res);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Small delay to match other test mains
|
||||
delay(10);
|
||||
initializeTestEnvironment();
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_router_no_scaling);
|
||||
RUN_TEST(test_client_below_threshold);
|
||||
RUN_TEST(test_client_default_preset_scaling);
|
||||
RUN_TEST(test_client_medium_fast_preset_scaling);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
Reference in New Issue
Block a user