Skip to content

Commit eb5a204

Browse files
committed
added SNR lines for neighborInfo and direct routes in map
1 parent 864f807 commit eb5a204

File tree

3 files changed

+144
-1
lines changed

3 files changed

+144
-1
lines changed

src/core/stores/deviceStore.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface Device {
5252
activePage: Page;
5353
activeNode: number;
5454
waypoints: Protobuf.Mesh.Waypoint[];
55+
neighborInfo: Map<number, Protobuf.Mesh.NeighborInfo>;
5556
// currentMetrics: Protobuf.DeviceMetrics;
5657
pendingSettingsChanges: boolean;
5758
messageDraft: string;
@@ -76,6 +77,10 @@ export interface Device {
7677
setActivePage: (page: Page) => void;
7778
setActiveNode: (node: number) => void;
7879
setPendingSettingsChanges: (state: boolean) => void;
80+
setNeighborInfo: (
81+
nodeId: number,
82+
neighborInfo: Protobuf.Mesh.NeighborInfo,
83+
) => void;
7984
addChannel: (channel: Protobuf.Channel.Channel) => void;
8085
addWaypoint: (waypoint: Protobuf.Mesh.Waypoint) => void;
8186
addNodeInfo: (nodeInfo: Protobuf.Mesh.NodeInfo) => void;
@@ -151,6 +156,20 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
151156
},
152157
pendingSettingsChanges: false,
153158
messageDraft: "",
159+
neighborInfo: new Map<number, Protobuf.Mesh.NeighborInfo>(),
160+
setNeighborInfo: (
161+
nodeId: number,
162+
neighborInfo: Protobuf.Mesh.NeighborInfo,
163+
) => {
164+
set(
165+
produce<DeviceState>((draft) => {
166+
const device = draft.devices.get(id);
167+
if (device) {
168+
device.neighborInfo.set(nodeId, neighborInfo);
169+
}
170+
}),
171+
);
172+
},
154173

155174
setStatus: (status: Types.DeviceStatusEnum) => {
156175
set(

src/core/subscriptions.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const subscribeAll = (
55
device: Device,
66
connection: Types.ConnectionType,
77
) => {
8+
// Set device as a global variable for debugging
89
let myNodeNum = 0;
910

1011
// onLogEvent
@@ -87,6 +88,7 @@ export const subscribeAll = (
8788
});
8889

8990
connection.events.onTraceRoutePacket.subscribe((traceRoutePacket) => {
91+
console.log("Trace Route Packet", traceRoutePacket);
9092
device.addTraceRoute({
9193
...traceRoutePacket,
9294
});
@@ -103,4 +105,8 @@ export const subscribeAll = (
103105
time: meshPacket.rxTime,
104106
});
105107
});
108+
109+
connection.events.onNeighborInfoPacket.subscribe((neighborInfo) => {
110+
device.setNeighborInfo(neighborInfo.from, neighborInfo.data);
111+
});
106112
};

src/pages/Map/index.tsx

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,35 @@ import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
1212
import {
1313
AttributionControl,
1414
GeolocateControl,
15+
Layer,
1516
Marker,
1617
NavigationControl,
1718
Popup,
1819
ScaleControl,
20+
Source,
1921
useMap,
2022
} from "react-map-gl";
2123
import MapGl from "react-map-gl/maplibre";
2224

25+
// taken from android client these probably should be moved into a shared file
26+
const SNR_GOOD_THRESHOLD = -7;
27+
const SNR_FAIR_THRESHOLD = -15;
28+
const RSSI_GOOD_THRESHOLD = -115;
29+
const RSSI_FAIR_THRESHOLD = -126;
30+
const LINE_GOOD_COLOR = "#00ff00";
31+
const LINE_FAIR_COLOR = "#ffe600";
32+
const LINE_BAD_COLOR = "#f7931a";
33+
34+
const getSignalColor = (snr: number, rssi?: number) => {
35+
if (snr > SNR_GOOD_THRESHOLD && (rssi == null || rssi > RSSI_GOOD_THRESHOLD))
36+
return LINE_GOOD_COLOR;
37+
if (snr > SNR_FAIR_THRESHOLD && (rssi == null || rssi > RSSI_FAIR_THRESHOLD))
38+
return LINE_FAIR_COLOR;
39+
return LINE_BAD_COLOR;
40+
};
41+
42+
const DIRECT_NODE_TIMEOUT = 60 * 20; // 60 seconds * ? minutes
43+
2344
type NodePosition = {
2445
latitude: number;
2546
longitude: number;
@@ -33,8 +54,72 @@ const convertToLatLng = (position: {
3354
longitude: (position.longitudeI ?? 0) / 1e7,
3455
});
3556

57+
const generateNeighborLines = (
58+
nodes: {
59+
node: Protobuf.Mesh.NodeInfo;
60+
neighborInfo: Protobuf.Mesh.NeighborInfo;
61+
}[],
62+
) => {
63+
const features = [];
64+
for (const { node, neighborInfo } of nodes) {
65+
const start = convertToLatLng(node.position);
66+
if (!neighborInfo) continue;
67+
for (const neighbor of neighborInfo.neighbors) {
68+
const toNode = nodes.find((n) => n.node.num === neighbor.nodeId)?.node;
69+
if (!toNode) continue;
70+
const end = convertToLatLng(toNode.position);
71+
features.push({
72+
type: "Feature",
73+
geometry: {
74+
type: "LineString",
75+
coordinates: [
76+
[start.longitude, start.latitude],
77+
[end.longitude, end.latitude],
78+
],
79+
},
80+
properties: {
81+
color: getSignalColor(neighbor.snr),
82+
},
83+
});
84+
}
85+
}
86+
87+
return {
88+
type: "FeatureCollection",
89+
features,
90+
};
91+
};
92+
const generateDirectLines = (nodes: Protobuf.Mesh.NodeInfo[]) => {
93+
const features = [];
94+
for (const node of nodes) {
95+
if (!node.position) continue;
96+
if (node.hopsAway > 0) continue;
97+
if (Date.now() / 1000 - node.lastHeard > DIRECT_NODE_TIMEOUT) continue;
98+
const start = convertToLatLng(node.position);
99+
const selfNode = nodes.find((n) => n.isFavorite);
100+
const end = convertToLatLng(selfNode.position);
101+
features.push({
102+
type: "Feature",
103+
geometry: {
104+
type: "LineString",
105+
coordinates: [
106+
[start.longitude, start.latitude],
107+
[end.longitude, end.latitude],
108+
],
109+
},
110+
properties: {
111+
color: getSignalColor(node.snr),
112+
},
113+
});
114+
}
115+
116+
return {
117+
type: "FeatureCollection",
118+
features,
119+
};
120+
};
36121
const MapPage = (): JSX.Element => {
37-
const { nodes, waypoints } = useDevice();
122+
const { nodes, waypoints, neighborInfo } = useDevice();
38123
const currentTheme = useTheme();
39124
const { default: map } = useMap();
40125

@@ -131,6 +216,19 @@ const MapPage = (): JSX.Element => {
131216
[validNodes, handleMarkerClick],
132217
);
133218

219+
const neighborLines = useMemo(() => {
220+
return generateNeighborLines(
221+
validNodes.map((vn) => ({
222+
node: vn,
223+
neighborInfo: neighborInfo.get(vn.num),
224+
})),
225+
);
226+
}, [validNodes, neighborInfo]);
227+
228+
const directLines = useMemo(
229+
() => generateDirectLines(validNodes),
230+
[validNodes],
231+
);
134232
useEffect(() => {
135233
map?.on("load", () => {
136234
getMapBounds();
@@ -185,6 +283,26 @@ const MapPage = (): JSX.Element => {
185283
</Marker>
186284
))}
187285
{markers}
286+
<Source id="neighbor-lines" type="geojson" data={neighborLines}>
287+
<Layer
288+
id="neighborLineLayer"
289+
type="line"
290+
paint={{
291+
"line-color": ["get", "color"],
292+
"line-width": 2,
293+
}}
294+
/>
295+
</Source>
296+
<Source id="direct-lines" type="geojson" data={directLines}>
297+
<Layer
298+
id="directLineLayer"
299+
type="line"
300+
paint={{
301+
"line-color": ["get", "color"],
302+
"line-width": 4,
303+
}}
304+
/>
305+
</Source>
188306
{selectedNode ? (
189307
<Popup
190308
anchor="top"

0 commit comments

Comments
 (0)