Phantom node fix perhaps (#11271)

* trying to fix phantom nodes

* fix comment spam

* oops - missed one
This commit is contained in:
Tom
2026-07-28 18:32:18 +00:00
committed by GitHub
co-authored by GitHub
parent a8623a60c5
commit 2c57a17124
20 changed files with 120 additions and 26 deletions
+28 -3
View File
@@ -202,7 +202,9 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
if (ostream) {
const auto *vec = static_cast<const std::vector<meshtastic_NodeInfoLite> *>(iter->pData);
for (auto item : *vec) {
item.snr_q4 = (int32_t)(item.snr * 4.0f);
// Round rather than truncate: truncation wiped any |SNR| < 0.25 dB to exactly
// 0, which collided with the "never stored" sentinel below.
item.snr_q4 = (int32_t)lroundf(item.snr * 4.0f);
item.snr = 0.0f;
if (!pb_encode_tag_for_field(ostream, iter))
return false;
@@ -214,8 +216,14 @@ bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostre
meshtastic_NodeInfoLite node = meshtastic_NodeInfoLite_init_zero;
auto *vec = static_cast<std::vector<meshtastic_NodeInfoLite> *>(iter->pData);
if (pb_decode(istream, meshtastic_NodeInfoLite_fields, &node)) {
if (node.snr_q4)
// snr_q4 = 0 is byte-identical to "field never written" but 0 dB is valid.
// NODEINFO_BITFIELD_HAS_SNR_MASK disambiguates going forward; legacy
// records (bit clear) treat this as unknown.
if (nodeInfoLiteHasSnr(&node)) {
node.snr = node.snr_q4 / 4.0f;
} else if (node.snr_q4) {
node.snr = node.snr_q4 / 4.0f;
}
node.snr_q4 = 0;
vec->push_back(node);
}
@@ -3624,8 +3632,20 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
info->last_heard = mp.rx_time;
if (mp.rx_snr)
// Gate on the packet actually having been received over our own radio, not on rx_snr being
// truthy, because 0 dB is valid. TRANSPORT_LORA is set only on the real over-the-air RX path
// (RadioInterface.cpp); it excludes TRANSPORT_INTERNAL and TRANSPORT_MQTT, while still accepting
// an MQTT-origin packet that a gateway rebroadcasts onto LoRa - we genuinely measured that one
// ourselves. Mirrors hop histogram below.
// Belt-and-braces: also require has_rx_rssi, which every genuine RF-reception site sets
// unconditionally alongside rx_snr - unlike PhoneAPI's replay packets, which set TRANSPORT_LORA
// too (so the client treats restored history as if heard over the air) but never has_rx_rssi.
// Replay packets don't reach updateFrom() today; this check guards against a future change that
// routes them back through this path silently recording a replayed rx_snr as a fresh measurement.
if (mp.transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA && mp.has_rx_rssi) {
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
nodeInfoLiteSetBit(info, NODEINFO_BITFIELD_HAS_SNR_MASK, true);
}
nodeInfoLiteSetBit(info, NODEINFO_BITFIELD_VIA_MQTT_MASK,
mp.via_mqtt); // Store if we received this packet via MQTT
@@ -3637,6 +3657,11 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
// inflate the local mesh-size estimate with non-RF nodes (and they usually carry
// hop_start==0, landing in the hop-0 bucket that pulls the recommendation lowest), so
// exclude via_mqtt too.
//
// The std::max clamp below is deliberate, not a bug: Counting an unproven-but-real neighbor as 0 hops is the
// conservative direction. This intentionally does not agree with the `hopsAway >= 0`
// gate below, which rejects the same -1 rather than storing it as a fabricated 0 -
// the histogram and the stored hops_away serve different purposes.
if (mp.transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA && !mp.via_mqtt &&
hopScalingModule) {
uint8_t hopCount = std::max(int8_t(0), getHopsAway(mp));