feat(mesh): Meshtastic RSSI/SNR + peer-location map wiring (backlog #14/#15, part 1)
Backend: parse_mesh_packet now decodes MeshPacket.rx_snr (field 8, float) and rx_rssi (field 12, int32), and a new POSITION_APP branch decodes Position. latitude_i/longitude_i (fields 1/2, sfixed32) -- all field numbers confirmed against the canonical meshtastic/protobufs mesh.proto, not guessed. Threaded through ParsedContact -> refresh_contacts -> MeshPeer (mirroring how pkc_capable was wired for #17), so mesh.peers now surfaces real rssi/snr/lat/ lon instead of always-null. Fixed a real bug found along the way: update_node_info's unconditional contact replace would have silently wiped any already-tracked signal/position data on the next NodeInfo packet -- now preserves it. Frontend: mesh.ts's updateNodePositionsFromPeers() feeds real position data into the SAME nodePositions map MeshMap.vue already renders from (parallel to the existing Coordinate/Alert-message path) -- MeshMap.vue itself needed zero changes, it was already built for this. 105/105 mesh tests pass (4 new: rx_snr/rx_rssi decode, position decode + incomplete-field handling, full packet_to_inbound_frame integration). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
dfca007949
commit
02b6b52a8c
@@ -38,6 +38,10 @@ export interface MeshPeer {
|
||||
hops: number
|
||||
last_advert?: number
|
||||
reachable?: boolean
|
||||
/** Last known position (degrees), from a Meshtastic POSITION_APP broadcast.
|
||||
* Absent until the peer has shared one. */
|
||||
lat?: number | null
|
||||
lon?: number | null
|
||||
}
|
||||
|
||||
export interface MeshChannel {
|
||||
@@ -215,6 +219,7 @@ export const useMeshStore = defineStore('mesh', () => {
|
||||
method: 'mesh.peers',
|
||||
})
|
||||
peers.value = res.peers
|
||||
updateNodePositionsFromPeers(res.peers)
|
||||
} catch (err: unknown) {
|
||||
error.value = err instanceof Error ? err.message : 'Failed to fetch mesh peers'
|
||||
}
|
||||
@@ -298,6 +303,27 @@ export const useMeshStore = defineStore('mesh', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** Feed real Meshtastic POSITION_APP broadcasts into the same map the
|
||||
* manually-shared Coordinate/Alert messages already populate — MeshMap.vue
|
||||
* needs no changes, it already renders from `nodePositions`. Uses
|
||||
* `last_heard` as the freshness timestamp, same comparison pattern as
|
||||
* `updateNodePositionsFromMessages`, so a peer position never clobbers a
|
||||
* more-recently-shared coordinate message and vice versa. */
|
||||
function updateNodePositionsFromPeers(peerList: MeshPeer[]) {
|
||||
for (const peer of peerList) {
|
||||
if (typeof peer.lat !== 'number' || typeof peer.lon !== 'number') continue
|
||||
const existing = nodePositions.value.get(peer.contact_id)
|
||||
if (!existing || peer.last_heard > existing.timestamp) {
|
||||
nodePositions.value.set(peer.contact_id, {
|
||||
lat: peer.lat,
|
||||
lng: peer.lon,
|
||||
label: peer.advert_name,
|
||||
timestamp: peer.last_heard,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNodePositions(): Map<number, NodePosition> {
|
||||
return nodePositions.value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user