Skip to content

Commit ae869b4

Browse files
authored
Merge pull request #38 from MrAlders0n/copilot/implement-distance-based-ping-filtering
Add 25m distance filtering and 150km Ottawa geofence to wardriver pings
2 parents 9b43735 + 57227f8 commit ae869b4

1 file changed

Lines changed: 93 additions & 1 deletion

File tree

content/wardrive.js

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,51 @@ const MESHMAPPER_API_URL = "https://yow.meshmapper.net/wardriving-api.php";
2727
const MESHMAPPER_API_KEY = "59C7754DABDF5C11CA5F5D8368F89";
2828
const MESHMAPPER_DEFAULT_WHO = "GOME-WarDriver"; // Default identifier
2929

30+
// GPS Enhancement Configuration
31+
const MIN_PING_DISTANCE_M = 25; // Minimum distance between pings in meters
32+
const OTTAWA_CENTER_LAT = 45.4215; // Ottawa center (Parliament Hill) latitude
33+
const OTTAWA_CENTER_LON = -75.6972; // Ottawa center (Parliament Hill) longitude
34+
const OTTAWA_GEOFENCE_RADIUS_KM = 150; // Service area radius in kilometers (covers greater Ottawa region)
35+
36+
// ---- GPS Distance Calculation (Haversine Formula) ----
37+
const EARTH_RADIUS_METERS = 6371000; // Earth's mean radius in meters
38+
const DEG_TO_RAD = Math.PI / 180; // Conversion factor from degrees to radians
39+
40+
/**
41+
* Calculate the great-circle distance between two GPS coordinates using the Haversine formula.
42+
* @param {number} lat1 - Latitude of first point in degrees
43+
* @param {number} lon1 - Longitude of first point in degrees
44+
* @param {number} lat2 - Latitude of second point in degrees
45+
* @param {number} lon2 - Longitude of second point in degrees
46+
* @returns {number} Distance in meters
47+
*/
48+
function calculateDistance(lat1, lon1, lat2, lon2) {
49+
const φ1 = lat1 * DEG_TO_RAD;
50+
const φ2 = lat2 * DEG_TO_RAD;
51+
const Δφ = (lat2 - lat1) * DEG_TO_RAD;
52+
const Δλ = (lon2 - lon1) * DEG_TO_RAD;
53+
54+
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
55+
Math.cos(φ1) * Math.cos(φ2) *
56+
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
57+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
58+
59+
return EARTH_RADIUS_METERS * c; // Distance in meters
60+
}
61+
62+
/**
63+
* Check if current location is within the Ottawa geofence.
64+
* @param {number} lat - Current latitude
65+
* @param {number} lon - Current longitude
66+
* @returns {object} { inBounds: boolean, distanceKm: number }
67+
*/
68+
function checkGeofence(lat, lon) {
69+
const distanceM = calculateDistance(lat, lon, OTTAWA_CENTER_LAT, OTTAWA_CENTER_LON);
70+
const distanceKm = distanceM / 1000;
71+
const inBounds = distanceKm <= OTTAWA_GEOFENCE_RADIUS_KM;
72+
return { inBounds, distanceKm };
73+
}
74+
3075
// ---- DOM refs (from index.html; unchanged except the two new selectors) ----
3176
const $ = (id) => document.getElementById(id);
3277
const statusEl = $("status");
@@ -64,7 +109,8 @@ const state = {
64109
autoCountdownTimer: null, // Timer for auto-ping countdown display
65110
nextAutoPingTime: null, // Timestamp when next auto-ping will occur
66111
apiCountdownTimer: null, // Timer for API post countdown display
67-
apiPostTime: null // Timestamp when API post will occur
112+
apiPostTime: null, // Timestamp when API post will occur
113+
lastPingLocation: null // { lat, lon, tsMs } - location of last successful ping for distance filtering
68114
};
69115

70116
// ---- UI helpers ----
@@ -555,11 +601,56 @@ async function sendPing(manual = false) {
555601
}
556602
}
557603

604+
// Check geofence: ensure we're within the Ottawa service area
605+
const geofenceCheck = checkGeofence(lat, lon);
606+
if (!geofenceCheck.inBounds) {
607+
const msg = `Location outside service area (${geofenceCheck.distanceKm.toFixed(1)}km from Ottawa)`;
608+
console.log(`Geofence check failed: ${msg}`);
609+
setStatus(msg, "text-red-300");
610+
611+
// In auto mode, schedule next ping to keep checking location
612+
if (!manual && state.running) {
613+
scheduleNextAutoPing();
614+
}
615+
return;
616+
}
617+
618+
// Check distance from last ping: skip if too close (within 25m)
619+
if (state.lastPingLocation) {
620+
const distanceFromLastPing = calculateDistance(
621+
lat, lon,
622+
state.lastPingLocation.lat, state.lastPingLocation.lon
623+
);
624+
625+
console.log(`Distance from last ping: ${distanceFromLastPing.toFixed(1)}m`);
626+
627+
if (distanceFromLastPing < MIN_PING_DISTANCE_M) {
628+
const remainingDistance = MIN_PING_DISTANCE_M - distanceFromLastPing;
629+
const msg = `Ping skipped, too close to last ping (${distanceFromLastPing.toFixed(1)}m away). Need ${remainingDistance.toFixed(1)}m more to ping`;
630+
console.log(msg);
631+
setStatus(msg, "text-amber-300");
632+
633+
// In auto mode, schedule next ping to keep checking distance
634+
if (!manual && state.running) {
635+
scheduleNextAutoPing();
636+
}
637+
return;
638+
}
639+
}
640+
558641
const payload = buildPayload(lat, lon);
559642

560643
const ch = await ensureChannel();
561644
await state.connection.sendChannelTextMessage(ch.channelIdx, payload);
562645

646+
// Store this location as the last successful ping location for distance filtering
647+
state.lastPingLocation = {
648+
lat,
649+
lon,
650+
tsMs: Date.now()
651+
};
652+
console.log(`Last ping location updated: ${lat.toFixed(5)}, ${lon.toFixed(5)}`);
653+
563654
// Start cooldown period after successful ping
564655
startCooldown();
565656

@@ -765,6 +856,7 @@ async function connect() {
765856
state.cooldownEndTime = null;
766857

767858
state.lastFix = null;
859+
state.lastPingLocation = null; // Clear last ping location on disconnect
768860
state.gpsState = "idle";
769861
updateGpsUi();
770862
});

0 commit comments

Comments
 (0)