emdashes begone (#10847)

This commit is contained in:
Tom
2026-07-01 19:01:27 -05:00
committed by GitHub
co-authored by GitHub
parent dee94e0758
commit 3becaf2d95
276 changed files with 1795 additions and 1793 deletions
+10 -10
View File
@@ -89,7 +89,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
assert(r);
#ifdef MESHTASTIC_ENCRYPTED_STORAGE
// While storage is locked, drop every admin payload both local and
// While storage is locked, drop every admin payload - both local and
// remote (PKC, mesh-relayed). Lockdown unlock is the prerequisite for
// any admin operation: operators must authenticate via lockdown_auth
// first. The lockdown_auth path itself is handled synchronously in
@@ -100,9 +100,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
// operator has even unlocked it.
// Only gate when lockdown is ACTIVE. A lockdown-capable build that hasn't
// been provisioned (or was disabled) is not unlocked either, but must
// still serve admin normally so check isLockdownActive() first.
// still serve admin normally - so check isLockdownActive() first.
if (EncryptedStorage::isLockdownActive() && !EncryptedStorage::isUnlocked()) {
LOG_WARN("AdminModule: dropping admin payload storage locked");
LOG_WARN("AdminModule: dropping admin payload - storage locked");
return handled;
}
#endif
@@ -136,7 +136,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
// local packets into the remote-PKC key check.
//
// Under MESHTASTIC_PHONEAPI_ACCESS_CONTROL, the per-connection auth
// gate lives in PhoneAPI::handleToRadioPacket any local admin
// gate lives in PhoneAPI::handleToRadioPacket - any local admin
// payload other than lockdown_auth is dropped there if the
// originating connection is unauthorized. By the time we reach
// this branch the connection has already proven the passphrase,
@@ -168,7 +168,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
// Note: PKC admin does NOT automatically authorize the
// originating local PhoneAPI connection for content
// redaction purposes. PKC and the per-connection lockdown
// auth slot are independent gates operators using PKC
// auth slot are independent gates - operators using PKC
// admin from a local app should still send lockdown_auth
// separately to unlock the redacted FromRadio stream.
// (The previous auto-authorize path read a shared
@@ -217,7 +217,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#ifdef MESHTASTIC_ENCRYPTED_STORAGE
// lockdown_auth is handled synchronously in
// PhoneAPI::handleToRadioPacket see handleLockdownAuthInline. A
// PhoneAPI::handleToRadioPacket - see handleLockdownAuthInline. A
// packet should not normally reach AdminModule under that flag set,
// but if it ever does (e.g. injected via a non-PhoneAPI path), drop
// it silently rather than leaking a partial response.
@@ -301,7 +301,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
break;
}
// Hardware supports 2.4 GHz apply the config.
// Hardware supports 2.4 GHz - apply the config.
// Fail closed: null instance is treated as incapable.
if (RadioLibInterface::instance && RadioLibInterface::instance->wideLora()) {
LOG_DEBUG("LORA_24 requested, radio hardware supports 2.4 GHz, applying");
@@ -493,7 +493,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
saveChanges(SEGMENT_NODEDATABASE, false);
if (screen)
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
} else if (mp.from == 0) { // local request from the phone tell the user why it didn't take
} else if (mp.from == 0) { // local request from the phone - tell the user why it didn't take
sendWarning(NodeDB::PROTECTED_CAP_WARN_FMT, "favorite", r->set_favorite_node, MAX_NUM_NODES - 2);
} else {
LOG_WARN("Remote set_favorite_node for 0x%08x refused: protected-node cap", r->set_favorite_node);
@@ -522,7 +522,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
if (nodeDB->setProtectedFlag(node, NODEINFO_BITFIELD_IS_IGNORED_MASK, true)) {
nodeDB->eraseNodeSatellites(node->num);
saveChanges(SEGMENT_NODEDATABASE, false);
} else if (mp.from == 0) { // local request from the phone tell the user why it didn't take
} else if (mp.from == 0) { // local request from the phone - tell the user why it didn't take
sendWarning(NodeDB::PROTECTED_CAP_WARN_FMT, "ignore", r->set_ignored_node, MAX_NUM_NODES - 2);
} else {
LOG_WARN("Remote set_ignored_node for 0x%08x refused: protected-node cap", r->set_ignored_node);
@@ -1271,7 +1271,7 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
moduleConfig.has_mesh_beacon = true;
moduleConfig.mesh_beacon = beaconCfg;
shouldReboot = false;
// Payload content changed invalidate the broadcaster's cache.
// Payload content changed - invalidate the broadcaster's cache.
if (meshBeaconBroadcastModule)
meshBeaconBroadcastModule->invalidateCache();
break;
+1 -1
View File
@@ -6,6 +6,6 @@ AtakPluginModule::AtakPluginModule() : SinglePortModule("atak", meshtastic_PortN
ProcessMessage AtakPluginModule::handleReceived(const meshtastic_MeshPacket &mp)
{
(void)mp; // Passthrough no processing needed, apps handle compression/decompression
(void)mp; // Passthrough - no processing needed, apps handle compression/decompression
return ProcessMessage::CONTINUE;
}
+2 -2
View File
@@ -1369,7 +1369,7 @@ int32_t CannedMessageModule::runOnce()
case INPUT_BROKER_RIGHT:
break;
default:
// Only insert ASCII printable characters (32126)
// Only insert ASCII printable characters (32-126)
if (this->payload >= 32 && this->payload <= 126) {
requestFocus();
if (this->cursor == this->freetext.length()) {
@@ -2128,7 +2128,7 @@ static float getSnrLimit(meshtastic_Config_LoRaConfig_ModemPreset preset)
}
}
// Return Good/Fair/Bad label and set 15 bars based on SNR and RSSI
// Return Good/Fair/Bad label and set 1-5 bars based on SNR and RSSI
static const char *getSignalGrade(float snr, int32_t rssi, float snrLimit, int &bars)
{
// 5-bar logic: strength inside Good/Fair/Bad category
+4 -4
View File
@@ -23,7 +23,7 @@ constexpr uint32_t RUN_INTERVAL_MS = 5 * 60 * 1000UL; // Emit micro-summary ever
// Persistence
// Note: this only needs incrementing if the published arrangement changes. For testing purposes, or prior to widespread release,
// it can stay the same even if the internal layout changes.
constexpr uint32_t HISTOGRAM_STATE_MAGIC = 0x48535432; // 'HST2' layout v2
constexpr uint32_t HISTOGRAM_STATE_MAGIC = 0x48535432; // 'HST2' - layout v2
constexpr uint8_t HISTOGRAM_STATE_VERSION = 1;
constexpr const char *HISTOGRAM_STATE_FILE = "/prefs/hopScalingState.bin";
@@ -282,7 +282,7 @@ void HopScalingModule::rollHour()
}
// 2. Walk scaled hop buckets to produce a hop-limit recommendation.
// effectiveMin: walk threshold first hop whose cumulative count reaches this.
// effectiveMin: walk threshold - first hop whose cumulative count reaches this.
// effectiveMax: ceiling on the one-hop extension check with GENEROUS politeness.
const uint16_t effectiveMin = TARGET_AFFECTED_NODES;
const uint16_t effectiveMax = MAX_TARGET_NODES;
@@ -351,7 +351,7 @@ void HopScalingModule::rollHour()
// 5. Tick down the hold counter; once it reaches zero, halve filteringDenominator toward
// samplingDenominator once per rollHour() (= once per hour) rather than a single jump:
// avoids a sudden large change in the hop-walk count when samplingDenominator cascaded
// down significantly during the hold period. No new hold is placed on each step the
// down significantly during the hold period. No new hold is placed on each step - the
// 13-roll hold already guaranteed that re-admitted nodes have full seenHoursAgo history;
// further pacing is provided naturally by the 1-step-per-hour rate. denominatorHistory
// is updated automatically by the shift at the top of rollHour(), so no backfill here.
@@ -405,7 +405,7 @@ void HopScalingModule::trimIfNeeded()
// down yet) are left untouched: eviction at samplingDenominator retains exactly those
// entries, so the old higher gate remains accurate for those historical hours.
// Slots below the new value must be raised because the eviction removed entries that
// had been admitted at the looser old gate the remaining entries represent a 1/N
// had been admitted at the looser old gate - the remaining entries represent a 1/N
// subsample where N is the new filteringDenominator, not the old smaller value.
for (uint8_t h = 0; h < 13; h++)
denominatorHistory[h] = std::max(denominatorHistory[h], filteringDenominator);
+18 -18
View File
@@ -16,13 +16,13 @@
*
* Memory layout: 512 bytes total (128 entries × 4 bytes/entry, no padding)
* - 16-bit XOR-fold hash of node ID
* - 3-bit hops away (07)
* - 3-bit hops away (0-7)
* - 13-bit hourly seen bitmap
* All three fields are packed into a single 32-bit Record; sizeof(Record) == 4.
*
* Sampling:
* - A node is added only when passesFilter(hashNodeId(nodeId), samplingDenominator),
* i.e. (hash16(nodeId) & (samplingDenominator 1)) == 0 (hash-space subsample, not raw ID)
* i.e. (hash16(nodeId) & (samplingDenominator - 1)) == 0 (hash-space subsample, not raw ID)
* - samplingDenominator starts at 1 (sample all), doubles when the list exceeds FILL_HIGH_PCT
* - filteringDenominator tracks samplingDenominator upward immediately but does not drop back
* down until FILTER_DENOM_HOLD_MS (13 h) have elapsed since the last scale-up
@@ -72,18 +72,18 @@ class HopScalingModule : private concurrency::OSThread
// This value is deliberately equal to the seenHoursAgo window (13 hours / 13 bits).
// Invariant: every entry that existed when a scale-up fired had seenHoursAgo != 0 at
// that moment (trimIfNeeded() evicts stale entries before doubling the denominator),
// so it remains seenInLast13h for at most 13 more rollHour() calls exactly the
// so it remains seenInLast13h for at most 13 more rollHour() calls - exactly the
// hold duration. That means entries from the scale-up event keep counts.total above
// the scale-down threshold for the entire hold period under normal (active) mesh
// conditions. On a genuinely quieting mesh the scale-down CAN fire before the hold
// expires each firing halves samplingDenominator but filteringDenominator stays
// expires - each firing halves samplingDenominator but filteringDenominator stays
// elevated, so the hop recommendation correctly stays conservative (MAX_HOP) while
// the cascade runs. The cascade is bounded at DENOM_MIN (7 halvings from DENOM_MAX);
// when the hold finally expires, step 5 of rollHour() halves filteringDenominator
// once per hour (rather than jumping directly to samplingDenominator) until the two
// converge, giving the hop-walk a gradual, 1-step-per-hour descent.
static constexpr uint32_t FILTER_DENOM_HOLD_MS = 13UL * 60UL * 60UL * 1000UL; // 13 h (documentation only)
// Number of rollHour() calls the hold spans equals the seenHoursAgo window width.
// Number of rollHour() calls the hold spans - equals the seenHoursAgo window width.
// filteringDenomHoldRollsRemaining is initialised to this value on scale-up and
// decremented once per rollHour(); step-down begins when it reaches zero.
static constexpr uint8_t FILTER_DENOM_HOLD_ROLLS = 13u;
@@ -132,15 +132,15 @@ class HopScalingModule : private concurrency::OSThread
struct MeshTrendStats {
/// Estimated node count per hour slot (h=0 is the just-completed hour, h=12 is 12 h ago).
uint16_t scaledPerHour[13] = {};
/// Nodes heard only this hour with no prior bitmap history indicates new arrivals.
/// Nodes heard only this hour with no prior bitmap history - indicates new arrivals.
uint16_t newThisHour = 0;
/// Nodes heard this hour that also appeared in at least one older hour stable regulars.
/// Nodes heard this hour that also appeared in at least one older hour - stable regulars.
uint16_t returningThisHour = 0;
/// Nodes heard last hour but silent this hour potential departures.
/// Nodes heard last hour but silent this hour - potential departures.
uint16_t lapsedSinceLastHour = 0;
/// Nodes absent from the last 4 hours but still present in some older hour (513 h) quieting down.
/// Nodes absent from the last 4 hours but still present in some older hour (5-13 h) - quieting down.
uint16_t olderThan4h = 0;
/// Nodes whose only remaining history is the 13th hour (bit 12 only) about to age out entirely.
/// Nodes whose only remaining history is the 13th hour (bit 12 only) - about to age out entirely.
uint16_t agingOut = 0;
};
@@ -257,11 +257,11 @@ class HopScalingModule : private concurrency::OSThread
//
// Two separate denominators control two distinct gates:
//
// samplingDenominator admission gate. A node is added/updated only when
// samplingDenominator - admission gate. A node is added/updated only when
// passesFilter(hash, samplingDenominator). Lower value = more permissive =
// more nodes enter = represents recent mesh state.
//
// filteringDenominator counting gate. The hop-walk tally in rollHour() only
// filteringDenominator - counting gate. The hop-walk tally in rollHour() only
// counts entries that pass passesFilter(hash, filteringDenominator). It moves
// up with samplingDenominator immediately (scale-up) but is held at the
// elevated value for FILTER_DENOM_HOLD_MS (13 h) after any scale-up before it
@@ -272,20 +272,20 @@ class HopScalingModule : private concurrency::OSThread
// that also pass F is exactly D/F. Therefore:
// raw_count × F = (total × D/F) × F = total × D
// The population estimate is the same whether we count with D or with F.
// The hold period is not about accuracy it is about stability: it prevents the
// The hold period is not about accuracy - it is about stability: it prevents the
// hop recommendation from reacting to recently-admitted nodes that have not yet
// accumulated enough seenHoursAgo history to be statistically reliable.
//
// denominatorHistory[h] the filteringDenominator used to both gate and scale
// denominatorHistory[h] - the filteringDenominator used to both gate and scale
// hourlyRaw[h]. Invariant: denominatorHistory[h] always equals the
// filteringDenominator that was active when seenHoursAgo bit h was set.
// rollHour() advances the array at the very start (before the tally loop), then
// gates hourlyRaw[h] per-slot by denominatorHistory[h] each slot's raw count
// gates hourlyRaw[h] per-slot by denominatorHistory[h] - each slot's raw count
// and multiplier are therefore always consistent, even when filteringDenominator
// changes between rolls (e.g. hold expiry). On scale-up (trimIfNeeded()), the
// entire array is backfilled uniformly with the new filteringDenominator to
// preserve the invariant retroactively for all 13 slots. Initialised to
// DENOM_MIN (1); scaledPerHour slots that draw from a 1 entry are unscaled
// DENOM_MIN (1); scaledPerHour slots that draw from a 1 entry are unscaled -
// correct for a fresh instance with no prior history.
// -----------------------------------------------------------------------
uint8_t samplingDenominator = DENOM_MIN;
@@ -321,7 +321,7 @@ class HopScalingModule : private concurrency::OSThread
// Record field semantics:
// nodeHash → XOR-fold of full 32-bit node ID to 16 bits
// hops_away → hop distance (07)
// hops_away → hop distance (0-7)
// seenHoursAgo → 13-bit per-hour seen bitmap
// bit 0 = seen in the current / most-recent hour
// bit 12 = seen 12 hours ago
@@ -338,7 +338,7 @@ class HopScalingModule : private concurrency::OSThread
static bool passesFilter(uint16_t nodeHash, uint8_t denom) { return (nodeHash & static_cast<uint16_t>(denom - 1u)) == 0u; }
public:
// Clock public so tests can share the same timebase via HopScalingModule::s_testNowMs
// Clock - public so tests can share the same timebase via HopScalingModule::s_testNowMs
#ifdef PIO_UNIT_TESTING
static uint32_t nowMs() { return s_testNowMs; }
#else
+17 -17
View File
@@ -109,13 +109,13 @@ bool MeshBeaconModule::beaconTxConfigInvalid(const meshtastic_MeshPacket *p)
meshtastic_Config_LoRaConfig_ModemPreset preset;
meshtastic_Config_LoRaConfig_RegionCode sidecarRegion = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
if (!getTargetRadioSettings(p, &preset, nullptr, nullptr, &sidecarRegion))
return false; // not a beacon-switch packet nothing to validate, normal traffic unaffected
return false; // not a beacon-switch packet - nothing to validate, normal traffic unaffected
const meshtastic_Config_LoRaConfig_RegionCode region =
(sidecarRegion != meshtastic_Config_LoRaConfig_RegionCode_UNSET) ? sidecarRegion : config.lora.region;
// An unlicensed node must never key up on a ham-only (licensed-only) region. The reverse is
// allowed: a licensed (ham) node may operate in a non-ham region and the switch only touches
// allowed: a licensed (ham) node may operate in a non-ham region - and the switch only touches
// preset/region/channel, never owner.is_licensed, so it cannot deactivate licensed mode.
const RegionInfo *r = getRegion(region);
if (r && r->profile->licensedOnly && !owner.is_licensed)
@@ -156,7 +156,7 @@ bool MeshBeaconModule::reconfigureForBeaconTX(RadioInterface *iface, meshtastic_
// heuristic both missed cases (a channel name/PSK swap that left preset/slot/region unchanged would
// never be restored) and fired falsely (a legitimate non-beacon channel edit would be reverted on
// the next TX). With the flag the restore fires for ANY field we changed and only when we changed
// it including on TX-failure paths, which route through this same restore call.
// it - including on TX-failure paths, which route through this same restore call.
static bool radioSwitched = false;
meshtastic_ChannelSettings *primaryCh = &channels.getByIndex(channels.getPrimaryIndex()).settings;
@@ -193,7 +193,7 @@ bool MeshBeaconModule::reconfigureForBeaconTX(RadioInterface *iface, meshtastic_
targetRegion == config.lora.region && !channelDiffers(targetChannel))
return false;
// Guard: never key up on an invalid target config bad preset for the region, or an
// Guard: never key up on an invalid target config - bad preset for the region, or an
// unlicensed node keying up on a ham-only region. Refuse the switch here so we never
// transmit on it; the radio driver drops the packet outright (see RadioLibInterface,
// beaconTxConfigInvalid) rather than letting it fall through onto the current config.
@@ -265,7 +265,7 @@ void MeshBeaconBroadcastModule::rebuildCache()
beacon.has_offer_channel = true;
beacon.offer_channel = bcfg.broadcast_offer_channel;
// PSK is included intentionally: this beacon is a public join-invitation.
// The offered channel is not secret the PSK here is a convenience token,
// The offered channel is not secret - the PSK here is a convenience token,
// not a security boundary. Operators who want a private channel must
// distribute the PSK out-of-band and leave offer_channel unset.
}
@@ -273,7 +273,7 @@ void MeshBeaconBroadcastModule::rebuildCache()
beacon.offer_preset = bcfg.broadcast_offer_preset;
beacon.offer_region = bcfg.broadcast_offer_region;
// Note: an empty config legitimately encodes to 0 bytes, and pb_encode_to_bytes can't distinguish
// that from a (here effectively impossible buffer is max-sized) failure, so we always clear the
// that from a (here effectively impossible - buffer is max-sized) failure, so we always clear the
// dirty flag. The combined send is gated on payloadCacheSize > 0, so an empty payload is never TX'd.
payloadCacheSize = (pb_size_t)pb_encode_to_bytes(payloadCache, sizeof(payloadCache), &meshtastic_MeshBeacon_msg, &beacon);
payloadCacheDirty = false;
@@ -291,7 +291,7 @@ void MeshBeaconBroadcastModule::sendBeaconPacket(meshtastic_MeshPacket *p, mesht
}
// perhapsEncode() keys encryption (and the channel-hash hint) off the PRIMARY channel slot, and
// the radio-thread channel switch only happens AFTER encryption so a beacon on an override
// the radio-thread channel switch only happens AFTER encryption - so a beacon on an override
// channel would otherwise be encrypted with the PRIMARY PSK, not the beacon channel's. Install the
// beacon channel into the primary slot for the synchronous duration of send(), then restore.
// Meshtastic threading is cooperative (no preemption between the swap and restore).
@@ -326,13 +326,13 @@ void MeshBeaconBroadcastModule::sendBeacon()
// broadcast_send_as_node: commented out pending further review.
// Spoof notes preserved for when this is re-enabled:
// broadcast_send_as_node overrides the source NodeNum. NOTE: this is a *node-ID* spoof
// only it rewrites the 'from' field but does NOT forge any signature. Once 'from' is
// only - it rewrites the 'from' field but does NOT forge any signature. Once 'from' is
// not us, the packet is no longer isFromUs(), so Router::perhapsEncode() skips XEdDSA
// signing and receivers get an unsigned packet attributed to another node.
// When broadcast_send_as_node == 0 the beacon is genuinely from us and Router::perhapsEncode()
// signs it under the same XEdDSA broadcast policy as normal channel messages.
// When broadcast_send_as_node rewrites p->from, perhapsEncode() sees isFromUs()=false and
// skips setting has_bitfield must be set explicitly so receivers can classify hop_start
// skips setting has_bitfield - must be set explicitly so receivers can classify hop_start
// correctly and so ok_to_mqtt is honoured on the spoofed packet.
// if (bcfg.broadcast_send_as_node != 0) {
// p->from = bcfg.broadcast_send_as_node;
@@ -347,8 +347,8 @@ void MeshBeaconBroadcastModule::sendBeacon()
// ── Packet type decisions ────────────────────────────────────────────────
//
// FLAG_LEGACY_SPLIT: when both text and offer are present, send TWO packets A)
// MESH_BEACON_APP (offer only) and B) TEXT_MESSAGE_APP (text only) both on the SAME beacon
// FLAG_LEGACY_SPLIT: when both text and offer are present, send TWO packets - A)
// MESH_BEACON_APP (offer only) and B) TEXT_MESSAGE_APP (text only) - both on the SAME beacon
// radio settings, so nodes that only decode TEXT_MESSAGE_APP still receive the text. Otherwise a
// single packet is sent (offer-only, text-only, or the combined offer+text path).
//
@@ -361,7 +361,7 @@ void MeshBeaconBroadcastModule::sendBeacon()
const bool sendTextOnly = splitBoth || (!hasRadioContent && hasText);
const bool sendCombined = !legacySplit && hasRadioContent && hasText;
// Build offer payload once shared across all targets.
// Build offer payload once - shared across all targets.
uint8_t offerBuf[meshtastic_MeshBeacon_size] = {};
pb_size_t offerSize = 0;
if (sendOfferOnly) {
@@ -399,7 +399,7 @@ void MeshBeaconBroadcastModule::sendBeacon()
// Dedup state: the beacon payload is identical across targets, so two targets that resolve to
// the same effective radio config (preset + resolved region + channel) would just re-broadcast
// the same packet wasted airtime and a redundant radio switch each. We skip the later one.
// the same packet - wasted airtime and a redundant radio switch each. We skip the later one.
// Keyed on the *resolved* values so an explicit "current region" dedups against an UNSET one.
EffTarget sent[4];
meshtastic_Config_LoRaConfig_RegionCode sentRegion[4];
@@ -422,7 +422,7 @@ void MeshBeaconBroadcastModule::sendBeacon()
tgt.preset = bt.has_preset ? bt.preset : config.lora.modem_preset;
tgt.region = bt.region;
// Resolve the channel from the device's channel table by index. A slot is only usable
// if it is actually configured (has a name or PSK its key is needed to encrypt). An
// if it is actually configured (has a name or PSK - its key is needed to encrypt). An
// out-of-range index, or a blank slot, falls back to the default channel for the target
// preset (see beaconChannelSettings), exactly as an unset channel_index would.
tgt.has_channel = false;
@@ -585,14 +585,14 @@ bool MeshBeaconListenerModule::handleReceivedProtobuf(const meshtastic_MeshPacke
// NOTE: we deliberately do NOT unwrap the text into a synthesized TEXT_MESSAGE_APP for the
// phone. The original MESH_BEACON_APP packet already flows to the client (we return CONTINUE),
// so a beacon-aware client renders `message` directly injecting a copy would only duplicate
// so a beacon-aware client renders `message` directly - injecting a copy would only duplicate
// it. Broadcasters that need non-beacon-aware clients to see the text use FLAG_LEGACY_SPLIT,
// which sends a real TEXT_MESSAGE_APP over RF. We also do not fire EVENT_RECEIVED_MSG: a beacon
// is an advisory broadcast, not a personal message, and must not wake the device from sleep.
if (hasText)
LOG_INFO("Beacon: received from 0x%08x: '%.40s'", mp.from, b->message);
// Cache any offer for the client app never auto-applied.
// Cache any offer for the client app - never auto-applied.
if (hasOfferContent) {
lastReceivedOffer.valid = true;
lastReceivedOffer.sender = mp.from;
@@ -602,7 +602,7 @@ bool MeshBeaconListenerModule::handleReceivedProtobuf(const meshtastic_MeshPacke
lastReceivedOffer.region = b->offer_region;
lastReceivedOffer.preset = b->offer_preset;
lastReceivedOffer.received_at =
getValidTime(RTCQualityFromNet); // 0 if no RTC fix yet consumers must not treat 0 as valid
getValidTime(RTCQualityFromNet); // 0 if no RTC fix yet - consumers must not treat 0 as valid
LOG_INFO("Beacon: stored offer from 0x%08x (preset=%d)", mp.from, b->offer_preset);
}
+3 -3
View File
@@ -133,9 +133,9 @@ extern MeshBeaconBroadcastModule *meshBeaconBroadcastModule;
/**
* Listener: receives MESH_BEACON_APP packets and caches any offered channel/preset for the client
* app to retrieve. It does NOT unwrap the text into a separate message the original beacon packet
* app to retrieve. It does NOT unwrap the text into a separate message - the original beacon packet
* already reaches the client (handler returns CONTINUE), which reads `message` from it directly.
* Does NOT auto-apply offered settings client app must do so explicitly.
* Does NOT auto-apply offered settings - client app must do so explicitly.
* Active only when the FLAG_LISTEN_ENABLED bit is set in moduleConfig.mesh_beacon.flags.
*/
class MeshBeaconListenerModule : public ProtobufModule<meshtastic_MeshBeacon>, public Observable<const meshtastic_MeshPacket *>
@@ -153,7 +153,7 @@ class MeshBeaconListenerModule : public ProtobufModule<meshtastic_MeshBeacon>, p
uint32_t received_at;
};
// Last received offer accessible to admin/API for client app retrieval.
// Last received offer - accessible to admin/API for client app retrieval.
static BeaconOffer lastReceivedOffer;
protected:
+3 -3
View File
@@ -339,7 +339,7 @@ meshtastic_MeshPacket *PositionModule::allocAtakPli()
strncpy(takPacket.device_callsign, owner.long_name, sizeof(takPacket.device_callsign) - 1);
takPacket.device_callsign[sizeof(takPacket.device_callsign) - 1] = '\0';
// CoT uid ATAK drops PLI entities with empty uid; derive stable "!<nodenum>" id.
// CoT uid - ATAK drops PLI entities with empty uid; derive stable "!<nodenum>" id.
snprintf(takPacket.uid, sizeof(takPacket.uid), "!%08x", nodeDB->getNodeNum());
// Encode TAKPacketV2 protobuf, leaving room for flags byte prefix
@@ -577,7 +577,7 @@ void PositionModule::sendLostAndFoundText()
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
p->want_ack = false;
if (written < 0) {
// snprintf encoding error send an empty payload rather than uninitialized bytes.
// snprintf encoding error - send an empty payload rather than uninitialized bytes.
p->decoded.payload.size = 0;
} else {
// Clamp to buffer capacity (snprintf returns "would-have-written" which can exceed the buffer).
@@ -598,7 +598,7 @@ static inline void computeImpreciseLatLon(int32_t inLat, int32_t inLon, uint8_t
// Build mask for top 'precisionBits' bits of a 32-bit unsigned field
const uint32_t mask = (precisionBits == 32) ? UINT32_MAX : (UINT32_MAX << (32 - precisionBits));
// Note: latitude_i/longitude_i are stored as signed 32-bit in meshtastic code but
// the bitmask logic used previously operated as unsignedpreserve that behavior by
// the bitmask logic used previously operated as unsigned-preserve that behavior by
// casting to uint32_t for masking, then back to int32_t.
uint32_t lat_u = static_cast<uint32_t>(inLat) & mask;
uint32_t lon_u = static_cast<uint32_t>(inLon) & mask;
+1 -1
View File
@@ -69,7 +69,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
// precision grid cell, so re-sending would be a duplicate that traffic management dedups
// downstream anyway. Used to hold stationary broadcasts to a 12h floor. useConfiguredPrecision
// gauges movement at our own configured (unclamped) precision rather than the on-wire
// (public-clamped) precision trackers report finer movement.
// (public-clamped) precision - trackers report finer movement.
bool positionUnchangedSinceLastSend(const meshtastic_PositionLite &selfPos, bool useConfiguredPrecision);
meshtastic_MeshPacket *allocAtakPli();
void trySetRtc(meshtastic_Position p, bool isLocal, bool forceUpdate = false);
+3 -3
View File
@@ -59,14 +59,14 @@ static bool replybotRateLimited(uint32_t from, uint32_t cooldownMs)
return false;
}
}
// No entry found insert new sender into the ring
// No entry found - insert new sender into the ring
replybotCooldown[replybotCooldownIdx].from = from;
replybotCooldown[replybotCooldownIdx].lastMs = now;
replybotCooldownIdx = (replybotCooldownIdx + 1) % REPLYBOT_COOLDOWN_SLOTS;
return false;
}
// Constructor registers a single text port and marks the module promiscuous
// Constructor - registers a single text port and marks the module promiscuous
// so that broadcast messages on the primary channel are visible.
ReplyBotModule::ReplyBotModule() : SinglePortModule("replybot", meshtastic_PortNum_TEXT_MESSAGE_APP)
{
@@ -122,7 +122,7 @@ ProcessMessage ReplyBotModule::handleReceived(const meshtastic_MeshPacket &mp)
return ProcessMessage::CONTINUE;
}
// Compute hop count indicator if the relay_node is nonzero we know
// Compute hop count indicator - if the relay_node is nonzero we know
// there was at least one relay. Some firmware builds support a hop_start
// field which could be used for more accurate counts, but here we use
// the available relay_node flag only.
+1 -1
View File
@@ -17,7 +17,7 @@
// NEOPIXEL_STATUS_POWER_PIN (required to enable the power/charge pixel)
// NEOPIXEL_STATUS_POWER_COLOR (optional, default red 0xFF0000)
// NEOPIXEL_STATUS_PAIRING_PIN / _COLOR (default blue 0x0000FF)
// Each pixel is a standalone 1-LED strand on its own GPIO this mirrors how
// Each pixel is a standalone 1-LED strand on its own GPIO - this mirrors how
// boards like the LilyGo T-Echo-Card expose three independent WS2812s.
#if defined(NEOPIXEL_STATUS_POWER_PIN) || defined(NEOPIXEL_STATUS_PAIRING_PIN)
#include <Adafruit_NeoPixel.h>
@@ -436,7 +436,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
bool isCooldownOver = (now - lastAlertTime > 60000);
if (isOwnTelemetry && bannerMsg && isCooldownOver) {
LOG_INFO("drawFrame: IAQ %d (own) showing banner: %s", m.iaq, bannerMsg);
LOG_INFO("drawFrame: IAQ %d (own) - showing banner: %s", m.iaq, bannerMsg);
screen->showSimpleBanner(bannerMsg, 3000);
// Only buzz if IAQ is over 200
+1 -1
View File
@@ -331,7 +331,7 @@ void TraceRouteModule::maybeSetNextHop(NodeNum target, uint8_t nextHopByte)
#if HAS_TRAFFIC_MANAGEMENT
// Mirror into the TMM overflow cache. Traceroute is the highest-confidence
// source (full known route), and this captures the target even when it isn't
// in the hot NodeDB same rationale as the ACK-confirmed path in NextHopRouter.
// in the hot NodeDB - same rationale as the ACK-confirmed path in NextHopRouter.
if (trafficManagementModule)
trafficManagementModule->setNextHop(target, nextHopByte);
#endif
+21 -21
View File
@@ -227,7 +227,7 @@ void TrafficManagementModule::resetStats()
void TrafficManagementModule::recordRouterHopPreserved()
{
// router_preserve_hops: not suitable right now removed from config until
// router_preserve_hops: not suitable right now - removed from config until
// the right heuristic for when to preserve vs. exhaust is clearer.
(void)stats.router_hops_preserved;
}
@@ -280,7 +280,7 @@ int TrafficManagementModule::peekCachedRole(NodeNum node)
* One linear pass tracks the match, the first empty slot, and the eviction
* victim. When the cache is full, the victim is the stalest entry (largest
* of its three relative timestamps is smallest), preferring entries without
* a next_hop hint those hints are the long-tail routing state the cache
* a next_hop hint - those hints are the long-tail routing state the cache
* exists to keep, and the maintenance sweep never ages them out.
*
* @param node NodeNum to find or create
@@ -288,7 +288,7 @@ int TrafficManagementModule::peekCachedRole(NodeNum node)
* @return Pointer to entry, or nullptr if the cache is unavailable
*/
// Sender-role resolution for the position hot path. The tier-3 cache is authoritative
// here and is kept fresh by updateCachedRoleFromNodeInfo() i.e. updated at the same
// here and is kept fresh by updateCachedRoleFromNodeInfo() - i.e. updated at the same
// time NodeDB learns a role, not re-derived on every packet. We only fall back to a
// NodeDB scan (tiers 1+2) the first time we start tracking a node, to seed the cache so
// a resident special-role node is correct from its very first position. Thereafter the
@@ -308,8 +308,8 @@ meshtastic_Config_DeviceConfig_Role TrafficManagementModule::resolveSenderRole(N
return static_cast<meshtastic_Config_DeviceConfig_Role>(entry->getCachedRole());
}
// Refresh the tier-3 role cache from an observed NodeInfo the same event that updates
// NodeDB's role so role changes (including demotion back to CLIENT) are picked up
// Refresh the tier-3 role cache from an observed NodeInfo - the same event that updates
// NodeDB's role - so role changes (including demotion back to CLIENT) are picked up
// without scanning NodeDB on the position hot path. Role is read straight from the
// packet's User payload (authoritative regardless of module ordering). Only updates nodes
// we already track (findEntry, no create) so NodeInfo from non-position nodes can't pollute
@@ -537,7 +537,7 @@ void TrafficManagementModule::cacheNodeInfoPacket(const meshtastic_MeshPacket &m
//
// A routing hint store. The byte is the last byte of the NodeNum to use as next
// hop to reach `dest`. It is written ONLY from NextHopRouter's ACK-confirmed
// decision (a bidirectionally-verified relay) never inferred one-way from
// decision (a bidirectionally-verified relay) - never inferred one-way from
// relayed traffic. The TMM cache holds confirmed next-hops that have aged out of
// the hot NodeDB (NodeInfoLite), and NextHopRouter::getNextHop() consults it as a
// fallback after the hot store.
@@ -593,7 +593,7 @@ bool TrafficManagementModule::preloadNextHopsFromNodeDB()
{
#if TRAFFIC_MANAGEMENT_CACHE_SIZE > 0
if (!cache || !nodeDB)
return false; // prerequisites not ready yet caller should retry on a later pass
return false; // prerequisites not ready yet - caller should retry on a later pass
uint16_t seeded = 0;
concurrency::LockGuard guard(&cacheLock);
@@ -675,7 +675,7 @@ uint8_t TrafficManagementModule::computePositionFingerprint(int32_t lat_truncate
// 0 is the "no position seen" sentinel for pos_fingerprint, so a real position that happens to
// hash to 0 must not collide with it (otherwise its duplicates would never dedup). Remap 0 -> 0xFF,
// mirroring NodeDB::getLastByteOfNodeNum()'s 0 -> 0xFF idiom. Cost: the 0x00 bucket merges into
// 0xFF (one extra collision in 256 negligible; the fingerprint already collides every 16 cells).
// 0xFF (one extra collision in 256 - negligible; the fingerprint already collides every 16 cells).
return fp ? fp : 0xFF;
}
@@ -715,7 +715,7 @@ ProcessMessage TrafficManagementModule::handleReceived(const meshtastic_MeshPack
logAction("drop", &mp, "unknown");
incrementStat(&stats.unknown_packet_drops);
ignoreRequest = true; // Suppress NAK for want_response packets
return ProcessMessage::STOP; // Consumed will not be rebroadcast
return ProcessMessage::STOP; // Consumed - will not be rebroadcast
}
}
return ProcessMessage::CONTINUE;
@@ -746,7 +746,7 @@ ProcessMessage TrafficManagementModule::handleReceived(const meshtastic_MeshPack
logAction("respond", &mp, "nodeinfo-cache");
incrementStat(&stats.nodeinfo_cache_hits);
ignoreRequest = true; // We responded; suppress default NAK
return ProcessMessage::STOP; // Consumed request will not be forwarded
return ProcessMessage::STOP; // Consumed - request will not be forwarded
}
}
@@ -765,7 +765,7 @@ ProcessMessage TrafficManagementModule::handleReceived(const meshtastic_MeshPack
logAction("drop", &mp, "position-dedup");
incrementStat(&stats.position_dedup_drops);
ignoreRequest = true; // Suppress NAK
return ProcessMessage::STOP; // Consumed duplicate will not be rebroadcast
return ProcessMessage::STOP; // Consumed - duplicate will not be rebroadcast
}
}
}
@@ -782,7 +782,7 @@ ProcessMessage TrafficManagementModule::handleReceived(const meshtastic_MeshPack
logAction("drop", &mp, "rate-limit");
incrementStat(&stats.rate_limit_drops);
ignoreRequest = true; // Suppress NAK
return ProcessMessage::STOP; // Consumed throttled packet will not be rebroadcast
return ProcessMessage::STOP; // Consumed - throttled packet will not be rebroadcast
}
}
}
@@ -803,7 +803,7 @@ void TrafficManagementModule::alterReceived(meshtastic_MeshPacket &mp)
return;
// exhaust_hop_telemetry / exhaust_hop_position / router_preserve_hops:
// not suitable right now the right heuristics for when to exhaust or
// not suitable right now - the right heuristics for when to exhaust or
// preserve hops need more field data before we expose them as config knobs.
// exhaustRequested stays false; perhapsRebroadcast() behaves normally.
@@ -816,8 +816,8 @@ void TrafficManagementModule::alterReceived(meshtastic_MeshPacket &mp)
// ceiling. Guards against forwarding more-precise coordinates than the
// channel is intended to carry (e.g. a LongFast channel set to 13-bit /
// ~1.5 km). chanPrec==0 means position sharing is disabled on the channel;
// skip not our job to zero positions on relay.
// Ham mode (owner.is_licensed) is exempt. Lost-and-found is NOT exempt its relayed
// skip - not our job to zero positions on relay.
// Ham mode (owner.is_licensed) is exempt. Lost-and-found is NOT exempt - its relayed
// positions get the same precision clamp as any node.
// Compile USERPREFS_TMM_APPLY_TO_PRIVATE_CHANNELS to extend to private channels.
if (!owner.is_licensed && isPosition && isBroadcast(mp.to)) {
@@ -878,7 +878,7 @@ int32_t TrafficManagementModule::runOnce()
const uint8_t rateTtlTicks = static_cast<uint8_t>(
std::min(static_cast<uint32_t>(15), (rateWindowMs > 0 ? rateWindowMs * 2 : 24 * kRateTimeTickMs) / kRateTimeTickMs));
// unknown: fixed 12-tick TTL (12 min 4 ticks past the 5-min default window)
// unknown: fixed 12-tick TTL (12 min - 4 ticks past the 5-min default window)
const uint8_t unknownTtlTicks = 12;
const uint8_t nowPosTick = currentPosTick();
@@ -978,7 +978,7 @@ bool TrafficManagementModule::shouldDropPosition(const meshtastic_MeshPacket *p,
if (!pos->has_latitude_i || !pos->has_longitude_i)
return false;
// Precision is driven by the channel's own position_precision ceiling the same
// Precision is driven by the channel's own position_precision ceiling - the same
// grid the channel uses for broadcast. Falls back to the firmware default (19-bit,
// ~90m cells) when the channel has no precision configured (chanPrec == 0).
const uint32_t chanPrec = getPositionPrecisionForChannel(p->channel);
@@ -990,7 +990,7 @@ bool TrafficManagementModule::shouldDropPosition(const meshtastic_MeshPacket *p,
const uint8_t fingerprint = computePositionFingerprint(lat_truncated, lon_truncated, precision);
// Drop gate uses the RAW configured interval: 0 means "dedup disabled" (the
// contract documented below). The 12h default is only for resolution/TTL
// sizing (constructor / runOnce), not for deciding whether to drop feeding
// sizing (constructor / runOnce), not for deciding whether to drop - feeding
// the default here would silently turn the 0-disables-dedup contract off.
uint32_t minIntervalMs = secsToMs(moduleConfig.traffic_management.position_min_interval_secs);
@@ -1003,12 +1003,12 @@ bool TrafficManagementModule::shouldDropPosition(const meshtastic_MeshPacket *p,
// Role exceptions keyed on the originating node's advertised role, resolved across
// all three tiers (hot store → warm store → TMM live cache). The position path is
// the one place that needs sender-role, and it also keeps tier 3 warm so the
// exception survives the node aging out of both NodeDB stores important in the
// exception survives the node aging out of both NodeDB stores - important in the
// common dedup-only config, where isRateLimited()'s role write never runs.
const meshtastic_Config_DeviceConfig_Role role = resolveSenderRole(p->from, entry, isNew);
if (role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) {
// Lost-and-found may refresh a duplicate position at most every ~15 min (cap, never
// lengthens; quantised to ~2 dedup ticks). Only when dedup is active never tighten
// lengthens; quantised to ~2 dedup ticks). Only when dedup is active - never tighten
// past an operator who disabled it (0).
const uint32_t lostFoundCapMs = secsToMs(default_traffic_mgmt_lost_and_found_position_min_interval_secs);
if (minIntervalMs != 0 && minIntervalMs > lostFoundCapMs)
@@ -1253,7 +1253,7 @@ bool TrafficManagementModule::shouldDropUnknown(const meshtastic_MeshPacket *p,
}
// Increment counter, saturating at 63 (6-bit field max). With threshold
// capped at 60, a saturated reading always exceeds the limit no special
// capped at 60, a saturated reading always exceeds the limit - no special
// already-saturated edge case needed.
const uint8_t cur = entry->getUnknownCount();
if (cur < 0x3F)
+5 -5
View File
@@ -33,7 +33,7 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
TrafficManagementModule();
~TrafficManagementModule();
// Singleton no copying or moving
// Singleton - no copying or moving
TrafficManagementModule(const TrafficManagementModule &) = delete;
TrafficManagementModule &operator=(const TrafficManagementModule &) = delete;
@@ -116,10 +116,10 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
// unknown active: getUnknownCount() != 0 (low 6 bits only)
//
// next_hop: routing hint written only from ACK-confirmed NextHopRouter decisions.
// No TTL keeps the slot alive across maintenance sweeps.
// No TTL - keeps the slot alive across maintenance sweeps.
//
#if _meshtastic_Config_DeviceConfig_Role_MAX > 15
#warning "Device role enum max exceeds 15 TMM 4-bit role cache (rate_count[7:6]/unknown_count[7:6]) will truncate new values"
#warning "Device role enum max exceeds 15 - TMM 4-bit role cache (rate_count[7:6]/unknown_count[7:6]) will truncate new values"
#endif
struct __attribute__((packed)) UnifiedCacheEntry {
NodeNum node;
@@ -152,7 +152,7 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
// =========================================================================
//
// Plain array, linear scan (same idiom as WarmNodeStore). A lookup walks at
// most cacheSize() × 10 B microseconds at LoRa packet rates, not worth a
// most cacheSize() × 10 B - microseconds at LoRa packet rates, not worth a
// hash table. Insertion on a full cache evicts the stalest entry,
// preferring entries without a next_hop hint (those are the long-tail
// routing state this cache exists to keep).
@@ -270,7 +270,7 @@ class TrafficManagementModule : public MeshModule, private concurrency::OSThread
// Resolve a sender's advertised device role for the position hot path. The tier-3
// cache (this entry's getCachedRole) is authoritative and is kept fresh by
// updateCachedRoleFromNodeInfo() updated when NodeDB learns a role, not re-derived
// updateCachedRoleFromNodeInfo() - updated when NodeDB learns a role, not re-derived
// per packet. Only on first tracking (isNew) do we scan NodeDB (hot store → warm
// store, via getNodeRole) to seed the cache, so a resident special-role node is
// correct from its first position; after that the read is O(1) and survives the node