From e5bb38a178a370eb926424cbedc47b95462e64af Mon Sep 17 00:00:00 2001 From: landwhich Date: Wed, 13 May 2026 21:59:13 -0400 Subject: [PATCH 1/8] changes needed for proper manual publishing --- src/components/panels/AntennaControlPanel.tsx | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/components/panels/AntennaControlPanel.tsx b/src/components/panels/AntennaControlPanel.tsx index 080247b..39a36a4 100644 --- a/src/components/panels/AntennaControlPanel.tsx +++ b/src/components/panels/AntennaControlPanel.tsx @@ -6,39 +6,48 @@ import { useROS } from '@/ros/ROSContext'; const AntennaControlPanel: React.FC = () => { const { ros } = useROS(); - const [disabled, setDisabled] = useState(false); + const [enabled, setEnabled] = useState(false); const [leftHeld, setLeftHeld] = useState(false); const [rightHeld, setRightHeld] = useState(false); - const topicRef = useRef(null); + const antStatusTopicRef = useRef(null); + const antValTopicRef = useRef(null); const intervalRef = useRef(null); // Create/cleanup topic when ROS connection changes useEffect(() => { if (!ros) { - topicRef.current = null; + antValTopicRef.current = null; return; } - topicRef.current = new ROSLIB.Topic({ + antValTopicRef.current = new ROSLIB.Topic({ ros, - name: '/antenna_control', + name: '/antenna/manual_value', messageType: 'std_msgs/Float32', }); + antStatusTopicRef.current = new ROSLIB.Topic({ + ros, + name: '/antenna/manual', + messageType: 'std_msgs/Bool', + }); + return () => { try { - topicRef.current?.unadvertise(); + antStatusTopicRef.current?.unadvertise(); + antValTopicRef.current?.unadvertise(); } catch { // ignore } - topicRef.current = null; + antStatusTopicRef.current = null; + antValTopicRef.current = null; }; }, [ros]); // Determine what value should be published right now const computeValue = () => { - if (disabled) return 0.0; + if (enabled) return 0.0; if (leftHeld && !rightHeld) return -0.5; if (rightHeld && !leftHeld) return 0.5; return 0.0; // neither held OR both held @@ -46,7 +55,7 @@ const AntennaControlPanel: React.FC = () => { // Start/stop the 100ms publish loop useEffect(() => { - if (!ros || !topicRef.current) return; + if (!ros || !antValTopicRef.current || !antStatusTopicRef.current) return; // Clear any previous loop if (intervalRef.current) { @@ -57,7 +66,9 @@ const AntennaControlPanel: React.FC = () => { // Publish immediately, then every 100ms const publishNow = () => { const value = computeValue(); - topicRef.current?.publish(new ROSLIB.Message({ data: value })); + const status = enabled; + antValTopicRef.current?.publish(new ROSLIB.Message({ data: value })); + antStatusTopicRef.current?.publish(new ROSLIB.Message({ data: status })); }; publishNow(); @@ -70,23 +81,23 @@ const AntennaControlPanel: React.FC = () => { } }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ros, disabled, leftHeld, rightHeld]); + }, [ros, enabled, leftHeld, rightHeld]); // If user disables controls, clear held state so it goes to 0 cleanly useEffect(() => { - if (disabled) { + if (enabled) { setLeftHeld(false); setRightHeld(false); } - }, [disabled]); + }, [enabled]); const setHeld = (side: 'left' | 'right', held: boolean) => { - if (disabled) return; + if (enabled) return; if (side === 'left') setLeftHeld(held); else setRightHeld(held); }; - const btnDisabled = disabled || !ros; + const btnDisabled = enabled || !ros; return (
@@ -121,8 +132,8 @@ const AntennaControlPanel: React.FC = () => { From 0c0c8106062121a5377958c1d8d3459783a86e61 Mon Sep 17 00:00:00 2001 From: landwhich Date: Wed, 13 May 2026 22:24:15 -0400 Subject: [PATCH 2/8] updated values --- src/components/panels/AntennaControlPanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/panels/AntennaControlPanel.tsx b/src/components/panels/AntennaControlPanel.tsx index 39a36a4..a16a931 100644 --- a/src/components/panels/AntennaControlPanel.tsx +++ b/src/components/panels/AntennaControlPanel.tsx @@ -48,8 +48,8 @@ const AntennaControlPanel: React.FC = () => { // Determine what value should be published right now const computeValue = () => { if (enabled) return 0.0; - if (leftHeld && !rightHeld) return -0.5; - if (rightHeld && !leftHeld) return 0.5; + if (leftHeld && !rightHeld) return -0.125; + if (rightHeld && !leftHeld) return 0.125; return 0.0; // neither held OR both held }; From 3a8822b44e7c8401283111ca30496aa5639bd1d8 Mon Sep 17 00:00:00 2001 From: Landwhich <156349813+Landwhich@users.noreply.github.com> Date: Fri, 24 Jul 2026 22:21:42 -0400 Subject: [PATCH 3/8] fixed wtv I was doing before --- src/components/BreadCrumbTrail.tsx | 57 +++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/components/BreadCrumbTrail.tsx b/src/components/BreadCrumbTrail.tsx index 6345b2f..157081a 100644 --- a/src/components/BreadCrumbTrail.tsx +++ b/src/components/BreadCrumbTrail.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react'; import { Polyline, Marker, Popup } from 'react-leaflet'; import { useROS } from '@/ros/ROSContext'; -import { useWaypoints } from '@/contexts/WaypointContext'; +import { useWaypoints, LatLngTuple } from '@/contexts/WaypointContext'; import ROSLIB from 'roslib'; import L from 'leaflet' @@ -18,6 +18,8 @@ const BreadcrumbTrail: React.FC = () => { const [breadcrumbs, setBreadcrumbs] = useState([]); const [paused, setPaused] = useState(false); const [lastFix, setLastFix] = useState(null); + const [antennaLoc, setAntennaLoc] = useState([0, 0]); + const [antennaHead, setAntennaHead] = useState(0); useEffect(() => { if (!ros) return; @@ -28,6 +30,18 @@ const BreadcrumbTrail: React.FC = () => { messageType: 'sensor_msgs/NavSatFix', }); + const antennaFixTopic = new ROSLIB.Topic({ + ros, + name: '/base_station/fix', + messageType: 'sensor_msgs/NavSatFix', + }); + + const antennaBearingTopic = new ROSLIB.Topic({ + ros, + name: '/antenna/tracker_bearing', + messageType: 'std_msgs/Float32', + }); + const handleFix = (message: any) => { if (paused) return; // Assuming the /fix message contains 'latitude' and 'longitude' @@ -40,9 +54,25 @@ const BreadcrumbTrail: React.FC = () => { setLastFix(newFix); }; + const handleAntennaFix = (message: any) => { + // Assuming the /fix message contains 'latitude' and 'longitude' + const { latitude, longitude } = message; + setAntennaLoc([latitude, longitude]); + }; + + const handleAntennaBearing = (message: any) => { + // Assuming the message contains float32 + const angle = message.data * 360; + setAntennaHead(angle); + }; + fixTopic.subscribe(handleFix); + antennaFixTopic.subscribe(handleAntennaFix); + antennaBearingTopic.subscribe(handleAntennaBearing); return () => { fixTopic.unsubscribe(handleFix); + antennaFixTopic.unsubscribe(handleAntennaFix); + antennaBearingTopic.unsubscribe(handleAntennaBearing); }; }, [ros, paused]); @@ -107,17 +137,17 @@ const BreadcrumbTrail: React.FC = () => { maxWidth: '300px', }} > -
+
GPS Fix Status
-
+
ROS Connection:{' '} {connectionStatus}
{lastFix ? ( -
+
Last Fix:
{/* TODO: Is this enough percision? */} @@ -128,15 +158,26 @@ const BreadcrumbTrail: React.FC = () => { Time: {new Date(lastFix.timestamp).toLocaleTimeString()}
) : ( -
No fix data received yet.
+
No fix data received yet.
)} -
+
Total Fixes: {breadcrumbs.length}
-
+
Total Distance: {totalDistance.toFixed(2)} km
-
+
+ Antenna Location: +
+ {/* TODO: Is this enough percision? */} + Lat: {antennaLoc[0].toFixed(6)} +
+ Lon: {antennaLoc[1].toFixed(6)} +
+
+ Antenna Heading: {antennaHead.toFixed(1)}° +
+
Antenna Location: -
+
{/* TODO: Is this enough percision? */} Lat: {antennaLoc[0].toFixed(6)}
From 45a2f334111a03026f6d3a8f4ba41a55556eff03 Mon Sep 17 00:00:00 2001 From: Nathaniel Hargrave Date: Tue, 29 Sep 2026 21:45:07 -0400 Subject: [PATCH 5/8] Move antenna information to antenna control panel --- src/components/BreadCrumbTrail.tsx | 75 +++---------- src/components/panels/AntennaControlPanel.tsx | 103 ++++++++++++++++-- 2 files changed, 108 insertions(+), 70 deletions(-) diff --git a/src/components/BreadCrumbTrail.tsx b/src/components/BreadCrumbTrail.tsx index 1d2d582..508df84 100644 --- a/src/components/BreadCrumbTrail.tsx +++ b/src/components/BreadCrumbTrail.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { Circle, Polyline, useMap } from 'react-leaflet'; import { useROS } from '@/ros/ROSContext'; -import { useWaypoints, LatLngTuple } from '@/contexts/WaypointContext'; +import { useWaypoints } from '@/contexts/WaypointContext'; import ROSLIB from 'roslib'; interface Breadcrumb { @@ -17,6 +17,22 @@ type BreadcrumbTrailProps = { downloadPNG: Function, } +// cool thing for calculating distance on a sphere +export const haversineDistance = ( + [lat1, lon1]: [number, number], + [lat2, lon2]: [number, number] +): number => { + const toRad = (x: number) => (x * Math.PI) / 180; + const R = 6371; + const dLat = toRad(lat2 - lat1); + const dLon = toRad(lon2 - lon1); + const a = + Math.sin(dLat / 2) ** 2 + + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2; + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return R * c; +}; + const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { const map = useMap(); const { ros, connectionStatus } = useROS(); @@ -24,8 +40,6 @@ const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { const [breadcrumbs, setBreadcrumbs] = useState([]); const [paused, setPaused] = useState(false); const [lastFix, setLastFix] = useState(null); - const [antennaLoc, setAntennaLoc] = useState([0, 0]); - const [antennaHead, setAntennaHead] = useState(0); const hasRecenteredRef = useRef(false); useEffect(() => { @@ -37,18 +51,6 @@ const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { messageType: 'sensor_msgs/NavSatFix', }); - const antennaFixTopic = new ROSLIB.Topic({ - ros, - name: '/base_station/fix', - messageType: 'sensor_msgs/NavSatFix', - }); - - const antennaBearingTopic = new ROSLIB.Topic({ - ros, - name: '/antenna/tracker_bearing', - messageType: 'std_msgs/Float32', - }); - const handleFix = (message: any) => { if (paused) return; @@ -79,25 +81,9 @@ const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { } }; - const handleAntennaFix = (message: any) => { - // Assuming the /fix message contains 'latitude' and 'longitude' - const { latitude, longitude } = message; - setAntennaLoc([latitude, longitude]); - }; - - const handleAntennaBearing = (message: any) => { - // Assuming the message contains float32 - const angle = message.data * 360; - setAntennaHead(angle); - }; - fixTopic.subscribe(handleFix); - antennaFixTopic.subscribe(handleAntennaFix); - antennaBearingTopic.subscribe(handleAntennaBearing); return () => { fixTopic.unsubscribe(handleFix); - antennaFixTopic.unsubscribe(handleAntennaFix); - antennaBearingTopic.unsubscribe(handleAntennaBearing); }; }, [ros, paused, map]); @@ -106,22 +92,6 @@ const BreadcrumbTrail: React.FC = ({ downloadPNG }) => { setLastFix(null); }; - // cool thing for calculating distance on a sphere - const haversineDistance = ( - [lat1, lon1]: [number, number], - [lat2, lon2]: [number, number] - ): number => { - const toRad = (x: number) => (x * Math.PI) / 180; - const R = 6371; // radius of my nutz in kilometers - const dLat = toRad(lat2 - lat1); - const dLon = toRad(lon2 - lon1); - const a = - Math.sin(dLat / 2) ** 2 + - Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2; - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - return R * c; - }; - const computeDistance = (): number => { if (breadcrumbs.length < 2) return 0; let total = 0; @@ -212,17 +182,6 @@ const BreadcrumbTrail: React.FC = ({ downloadPNG }) => {
Total Distance: {totalDistance.toFixed(2)} km
-
- Antenna Location: -
- {/* TODO: Is this enough percision? */} - Lat: {antennaLoc[0].toFixed(6)} -
- Lon: {antennaLoc[1].toFixed(6)} -
-
- Antenna Heading: {antennaHead.toFixed(1)}° -
- - - +
+ +
+
+ Pointing: +
+ Delta: {bearingDelta}° +
+ Bearing: {bearing.toFixed(1)}° +
+ Target Bearing: {targetBearing.toFixed(1)}° +
+
+ Location: +
+ Distance to Rover: {(haversineDistance(antennaLoc, roverLoc) * 1000).toFixed(2)} m +
+ Lat: {antennaLoc[0].toFixed(6)} +
+ Lon: {antennaLoc[1].toFixed(6)} +