mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-14 07:21:30 +10:00
Adds the Traffic Management module (TMM) plus the NodeDB/warm-store and next-hop foundations it builds on: - Unified per-node cache (flat array, 8-bit relative ticks) shared by all features; role-aware throttles for tracker / lost-and-found. - Position deduplication: drop unchanged position rebroadcasts within a configurable interval; precision driven off the channel ceiling (clamped to the public-key max on well-known channels). Enabled by default at 11h. - Per-node rate limiting and unknown-packet filtering (config-driven; a non-zero companion field enables each feature -- no bool toggles). - NodeInfo direct response from cache with role-based hop clamps. - Persistent next-hop overflow store: confirmed hops have no TTL, are seeded from NodeInfoLite at boot, and survive hot-store eviction. - Three-tier sender-role resolution (hot NodeInfoLite -> warm store -> TMM cache). Role is cached write-time (seeded on first track, refreshed from NodeInfo), pins its cache entry like a next-hop hint, and is evicted last. - Warm store caches device role + protected category across reboot/eviction. - PositionModule stationary floor for tracker / lost-and-found. - PSRAM gating for warm/satellite/TMM cache sizes; STM32WL excluded. Protobufs: TrafficManagementConfig trimmed to the five uint32 fields actually used; submodule repointed to protobufs develop. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
95 lines
3.3 KiB
C++
95 lines
3.3 KiB
C++
#include "PositionPrecision.h"
|
|
#include "Channels.h"
|
|
#include "mesh-pb-constants.h"
|
|
|
|
#include <Arduino.h>
|
|
|
|
uint32_t getPositionPrecisionForChannel(const meshtastic_Channel &channel)
|
|
{
|
|
if (channel.settings.has_module_settings) {
|
|
return channel.settings.module_settings.position_precision;
|
|
}
|
|
// No module settings: fail closed. A PRIMARY channel used to default to 32
|
|
// here, leaking an exact position on a sharing-disabled channel. See #10509.
|
|
return 0;
|
|
}
|
|
|
|
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex)
|
|
{
|
|
const meshtastic_Channel &ch = channels.getByIndex(channelIndex);
|
|
if (ch.role == meshtastic_Channel_Role_DISABLED)
|
|
return 0;
|
|
uint32_t precision = getPositionPrecisionForChannel(ch);
|
|
|
|
// Never send a precise position on a publicly-decryptable channel (key check is gated on > ceiling).
|
|
if (precision > MAX_POSITION_PRECISION_PUBLIC_KEY && channels.usesPublicKey(channelIndex)) {
|
|
precision = MAX_POSITION_PRECISION_PUBLIC_KEY;
|
|
}
|
|
return precision;
|
|
}
|
|
|
|
int32_t truncateCoordinate(int32_t coordinate, uint32_t precision)
|
|
{
|
|
if (precision == 0 || precision >= 32)
|
|
return coordinate;
|
|
|
|
uint32_t coordinateBits = static_cast<uint32_t>(coordinate);
|
|
uint32_t truncated = coordinateBits & (UINT32_MAX << (32 - precision));
|
|
|
|
// Use the middle of the possible location, not the low edge of the bucket.
|
|
truncated += (1UL << (31 - precision));
|
|
|
|
return static_cast<int32_t>(truncated);
|
|
}
|
|
|
|
int32_t truncateCoordinate(int32_t coordinate, uint8_t precision)
|
|
{
|
|
return truncateCoordinate(coordinate, static_cast<uint32_t>(precision));
|
|
}
|
|
|
|
void applyPositionPrecision(meshtastic_Position &position, uint32_t precision)
|
|
{
|
|
if (precision == 0) {
|
|
uint32_t time = position.time;
|
|
position = meshtastic_Position_init_default;
|
|
position.time = time;
|
|
return;
|
|
}
|
|
|
|
uint32_t effectivePrecision = precision > 32 ? 32 : precision;
|
|
position.precision_bits = effectivePrecision;
|
|
|
|
if (effectivePrecision < 32) {
|
|
position.latitude_i = truncateCoordinate(position.latitude_i, effectivePrecision);
|
|
position.longitude_i = truncateCoordinate(position.longitude_i, effectivePrecision);
|
|
}
|
|
}
|
|
|
|
bool applyPositionPrecision(meshtastic_MeshPacket &packet, uint32_t precision)
|
|
{
|
|
if (packet.which_payload_variant != meshtastic_MeshPacket_decoded_tag ||
|
|
packet.decoded.portnum != meshtastic_PortNum_POSITION_APP) {
|
|
return true;
|
|
}
|
|
|
|
meshtastic_Position position = meshtastic_Position_init_default;
|
|
if (!pb_decode_from_bytes(packet.decoded.payload.bytes, packet.decoded.payload.size, &meshtastic_Position_msg, &position)) {
|
|
return false;
|
|
}
|
|
|
|
applyPositionPrecision(position, precision);
|
|
packet.decoded.payload.size = pb_encode_to_bytes(packet.decoded.payload.bytes, sizeof(packet.decoded.payload.bytes),
|
|
&meshtastic_Position_msg, &position);
|
|
return true;
|
|
}
|
|
|
|
bool applyPositionPrecisionForChannel(meshtastic_MeshPacket &packet, uint8_t channelIndex)
|
|
{
|
|
if (packet.which_payload_variant != meshtastic_MeshPacket_decoded_tag ||
|
|
packet.decoded.portnum != meshtastic_PortNum_POSITION_APP) {
|
|
return true;
|
|
}
|
|
|
|
return applyPositionPrecision(packet, getPositionPrecisionForChannel(channelIndex));
|
|
}
|