From ee4b68096325975a3f574cf8146300f73bb7f947 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Mon, 12 Jan 2026 21:23:49 -0500 Subject: [PATCH 01/17] Add carpeater filter UI and functionality to ignore specific repeaters --- content/wardrive.js | 126 +++++++++++++++++++++++++++++++++++++++++++- index.html | 15 ++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index f3af332..404ad53 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -328,6 +328,7 @@ const ADVERT_HEADER = 0x11; // Header byte for ADVERT packets // RX Packet Filter Configuration const MAX_RX_PATH_LENGTH = 9; // Maximum path length for RX packets (drop if exceeded to filter corrupted packets) +const MAX_RX_RSSI_THRESHOLD = -30; // Maximum RSSI (dBm) for RX packets (drop if ≥ -30 to filter "carpeater" - extremely close/interfering repeaters) const RX_ALLOWED_CHANNELS = ['#wardriving', 'Public', '#testing', '#ottawa']; // Allowed channels for RX wardriving (Public uses fixed key, hashtag channels use SHA-256 derivation) const RX_PRINTABLE_THRESHOLD = 0.80; // Minimum printable character ratio for GRP_TXT (80%) @@ -516,6 +517,8 @@ const txLogState = { const rxLogState = { entries: [], // Array of parsed RX log entries dropCount: 0, // Count of dropped/filtered packets + carpeaterIgnoreDropCount: 0, // User-specified repeater drops (silent) + carpeaterRssiDropCount: 0, // RSSI failsafe drops (logged) isExpanded: false, autoScroll: true, maxEntries: 100 // Limit to prevent memory issues @@ -2246,6 +2249,23 @@ function getExternalAntennaSetting() { return checkedAntenna ? checkedAntenna.value : ""; } +/** + * Get carpeater ignore settings from UI + * @returns {{enabled: boolean, repeaterId: string|null}} Settings object + */ +function getCarpeaterIgnoreSettings() { + const enabled = document.getElementById('carpeaterFilterEnabled')?.checked || false; + const idInput = document.getElementById('carpeaterIdInput')?.value?.trim()?.toLowerCase() || ''; + + // Validate hex format (2 chars, 00-FF) + const isValidHex = /^[0-9a-f]{2}$/.test(idInput); + + return { + enabled: enabled && isValidHex, + repeaterId: (enabled && isValidHex) ? idInput : null + }; +} + function buildPayload(lat, lon) { const coordsStr = `${lat.toFixed(5)}, ${lon.toFixed(5)}`; const power = getCurrentPowerSetting(); @@ -3417,7 +3437,15 @@ async function validateRxPacket(metadata) { } debugLog(`[RX FILTER] ✓ Path length OK (${metadata.pathLength} ≤ ${MAX_RX_PATH_LENGTH})`); - // VALIDATION 2: Check packet type (only ADVERT and GRP_TXT) + // VALIDATION 2: Check RSSI (Carpeater filter - drop extremely strong signals from OTHER repeaters) + // Note: User-specified carpeater is already filtered earlier in handleTxLogging/handleRxLogging + if (metadata.rssi >= MAX_RX_RSSI_THRESHOLD) { + debugLog(`[RX FILTER] ❌ DROPPED: RSSI too strong (${metadata.rssi} ≥ ${MAX_RX_RSSI_THRESHOLD}) - possible carpeater (RSSI failsafe)`); + return { valid: false, reason: 'carpeater-rssi' }; + } + debugLog(`[RX FILTER] ✓ RSSI OK (${metadata.rssi} < ${MAX_RX_RSSI_THRESHOLD})`); + + // VALIDATION 3: Check packet type (only ADVERT and GRP_TXT) if (metadata.header === CHANNEL_GROUP_TEXT_HEADER) { debugLog(`[RX FILTER] Packet type: GRP_TXT (0x15)`); @@ -3614,6 +3642,16 @@ async function handleTxLogging(metadata, data) { const firstHopId = metadata.firstHop; const pathHex = firstHopId.toString(16).padStart(2, '0'); + // Check if this repeater is the user-specified carpeater to ignore + const carpeaterSettings = getCarpeaterIgnoreSettings(); + if (carpeaterSettings.enabled && pathHex === carpeaterSettings.repeaterId) { + rxLogState.dropCount++; + rxLogState.carpeaterIgnoreDropCount++; + updateRxLogSummary(); + debugLog(`[PING] ❌ Dropped: Repeater ${pathHex} is user-specified carpeater (ignore)`); + return; + } + debugLog(`[PING] Repeater echo accepted: first_hop=${pathHex}, SNR=${metadata.snr}, full_path_length=${metadata.pathLength}`); // Check if we already have this path @@ -3788,6 +3826,13 @@ async function handleRxLogging(metadata, data) { const validation = await validateRxPacket(metadata); if (!validation.valid) { rxLogState.dropCount++; + + // Special handling for RSSI failsafe carpeater drops (not user-specified drops) + if (validation.reason === 'carpeater-rssi') { + rxLogState.carpeaterRssiDropCount++; + updateCarpeaterRssiErrorLog(); + } + updateRxLogSummary(); const rawHex = Array.from(metadata.raw).map(b => b.toString(16).padStart(2, '0').toUpperCase()).join(' '); debugLog(`[RX LOG] ❌ Packet dropped: ${validation.reason}`); @@ -3799,6 +3844,16 @@ async function handleRxLogging(metadata, data) { const lastHopId = metadata.lastHop; const repeaterId = lastHopId.toString(16).padStart(2, '0'); + // Check if this repeater is the user-specified carpeater to ignore + const carpeaterSettings = getCarpeaterIgnoreSettings(); + if (carpeaterSettings.enabled && repeaterId === carpeaterSettings.repeaterId) { + rxLogState.dropCount++; + rxLogState.carpeaterIgnoreDropCount++; + updateRxLogSummary(); + debugLog(`[RX LOG] ❌ Dropped: Repeater ${repeaterId} is user-specified carpeater (ignore)`); + return; + } + debugLog(`[RX LOG] Packet heard via last hop: ${repeaterId}, SNR=${metadata.snr}, path_length=${metadata.pathLength}`); debugLog(`[RX LOG] ✅ Packet validated and passed filter`); @@ -4816,6 +4871,34 @@ function addErrorLogEntry(message, source = null) { debugLog(`[ERROR LOG] Added entry: ${message.substring(0, 50)}${message.length > 50 ? '...' : ''}`); } +/** + * Update or add carpeater error log entry with current drop count + * This creates a persistent error entry that updates in place rather than creating multiple entries + */ +function updateCarpeaterErrorLog() { + const message = `Possible Carpeater Detected, Dropped ${rxLogState.carpeaterDropCount} packet${rxLogState.carpeaterDropCount !== 1 ? 's' : ''}`; + const source = 'RX FILTER'; + + // Find existing carpeater entry by checking if message starts with "Possible Carpeater Detected" + const existingIndex = errorLogState.entries.findIndex(entry => + entry.source === source && entry.message.startsWith('Possible Carpeater Detected') + ); + + if (existingIndex !== -1) { + // Update existing entry + errorLogState.entries[existingIndex].message = message; + errorLogState.entries[existingIndex].timestamp = new Date().toISOString(); + debugLog(`[ERROR LOG] Updated carpeater entry: ${rxLogState.carpeaterDropCount} drops`); + + // Full re-render to update the displayed message + renderErrorLogEntries(true); + updateErrorLogSummary(); + } else { + // Create new entry (first carpeater detection) + addErrorLogEntry(message, source); + } +} + // ---- CSV Export Functions ---- /** @@ -5751,6 +5834,8 @@ async function connect() { rxLogState.entries = []; rxLogState.dropCount = 0; + rxLogState.carpeaterIgnoreDropCount = 0; + rxLogState.carpeaterRssiDropCount = 0; renderRxLogEntries(true); updateRxLogSummary(); @@ -6637,6 +6722,45 @@ export async function onLoad() { }); }); + // Carpeater filter checkbox event listener + const carpeaterCheckbox = document.getElementById('carpeaterFilterEnabled'); + const carpeaterInputContainer = document.getElementById('carpeaterIdInputContainer'); + const carpeaterInput = document.getElementById('carpeaterIdInput'); + + if (carpeaterCheckbox && carpeaterInputContainer && carpeaterInput) { + // Load saved settings from localStorage + const savedEnabled = localStorage.getItem('carpeaterFilterEnabled') === 'true'; + const savedId = localStorage.getItem('carpeaterRepeaterId') || ''; + + carpeaterCheckbox.checked = savedEnabled; + carpeaterInput.value = savedId; + carpeaterInputContainer.classList.toggle('hidden', !savedEnabled); + + // Checkbox toggle event + carpeaterCheckbox.addEventListener('change', (e) => { + const isEnabled = e.target.checked; + carpeaterInputContainer.classList.toggle('hidden', !isEnabled); + localStorage.setItem('carpeaterFilterEnabled', isEnabled); + debugLog(`[SETTINGS] Carpeater filter ${isEnabled ? 'enabled' : 'disabled'}`); + }); + + // Input validation and save event + carpeaterInput.addEventListener('input', (e) => { + const value = e.target.value.toLowerCase(); + const isValid = /^[0-9a-f]{0,2}$/.test(value); + + if (isValid) { + e.target.value = value; + if (value.length === 2) { + localStorage.setItem('carpeaterRepeaterId', value); + debugLog(`[SETTINGS] Carpeater repeater ID set to: ${value}`); + } + } else { + e.target.value = value.slice(0, -1); // Remove invalid character + } + }); + } + // Session Log event listener if (txLogSummaryBar) { txLogSummaryBar.addEventListener("click", () => { diff --git a/index.html b/index.html index 9d19af0..3143bb5 100644 --- a/index.html +++ b/index.html @@ -162,6 +162,21 @@

Settings< + +
+ +
+ + +
+ +

Enable to ignore all packets from a specific repeater. RSSI failsafe (≥ -30 dBm) remains active for other repeaters.

+
+
From efdff749e8b45b568b873484a6d707a6a1779e13 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Mon, 12 Jan 2026 21:27:17 -0500 Subject: [PATCH 02/17] Enhance carpeater filter UI with additional information modal and improved styling --- index.html | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index 3143bb5..dc0cb35 100644 --- a/index.html +++ b/index.html @@ -163,18 +163,19 @@

Settings<

-
- -
- - +
+ +
+ +
- @@ -409,5 +410,37 @@

Override Auto

+ + + From 9e8bad84e6812d0d28077e065d7a6251bded3ec3 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Mon, 12 Jan 2026 21:33:09 -0500 Subject: [PATCH 03/17] Refactor carpeater filter UI: enhance checkbox styling and input behavior --- content/wardrive.js | 12 ++++++++---- index.html | 21 +++++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index 404ad53..f271b0d 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -6724,24 +6724,28 @@ export async function onLoad() { // Carpeater filter checkbox event listener const carpeaterCheckbox = document.getElementById('carpeaterFilterEnabled'); - const carpeaterInputContainer = document.getElementById('carpeaterIdInputContainer'); const carpeaterInput = document.getElementById('carpeaterIdInput'); - if (carpeaterCheckbox && carpeaterInputContainer && carpeaterInput) { + if (carpeaterCheckbox && carpeaterInput) { // Load saved settings from localStorage const savedEnabled = localStorage.getItem('carpeaterFilterEnabled') === 'true'; const savedId = localStorage.getItem('carpeaterRepeaterId') || ''; carpeaterCheckbox.checked = savedEnabled; carpeaterInput.value = savedId; - carpeaterInputContainer.classList.toggle('hidden', !savedEnabled); + carpeaterInput.disabled = !savedEnabled; // Enable/disable input based on checkbox // Checkbox toggle event carpeaterCheckbox.addEventListener('change', (e) => { const isEnabled = e.target.checked; - carpeaterInputContainer.classList.toggle('hidden', !isEnabled); + carpeaterInput.disabled = !isEnabled; localStorage.setItem('carpeaterFilterEnabled', isEnabled); debugLog(`[SETTINGS] Carpeater filter ${isEnabled ? 'enabled' : 'disabled'}`); + + // Focus input when enabled + if (isEnabled) { + carpeaterInput.focus(); + } }); // Input validation and save event diff --git a/index.html b/index.html index dc0cb35..fa06fbe 100644 --- a/index.html +++ b/index.html @@ -167,14 +167,19 @@

Settings< -
- - -
- From 8a05f00403eb7cabaa33dd90f329bf3df877fd3e Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Mon, 12 Jan 2026 21:39:43 -0500 Subject: [PATCH 04/17] Refactor carpeater filter UI: update label text and improve checkbox SVG visibility --- index.html | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index fa06fbe..4e73a2c 100644 --- a/index.html +++ b/index.html @@ -164,22 +164,21 @@

Settings<
- +
- + + Info
From 103aedacd233f2b44f06dc99af41a5bfbadb357a Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Mon, 12 Jan 2026 21:48:00 -0500 Subject: [PATCH 05/17] Enhance carpeater filter UI: improve checkmark visibility on load and toggle --- content/wardrive.js | 11 +++++++++++ index.html | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index f271b0d..a9fc376 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -6735,6 +6735,12 @@ export async function onLoad() { carpeaterInput.value = savedId; carpeaterInput.disabled = !savedEnabled; // Enable/disable input based on checkbox + // Update checkmark visibility on load + const checkmarkSvg = document.querySelector('.checkmark-svg'); + if (checkmarkSvg) { + checkmarkSvg.style.opacity = savedEnabled ? '1' : '0'; + } + // Checkbox toggle event carpeaterCheckbox.addEventListener('change', (e) => { const isEnabled = e.target.checked; @@ -6742,6 +6748,11 @@ export async function onLoad() { localStorage.setItem('carpeaterFilterEnabled', isEnabled); debugLog(`[SETTINGS] Carpeater filter ${isEnabled ? 'enabled' : 'disabled'}`); + // Update checkmark visibility + if (checkmarkSvg) { + checkmarkSvg.style.opacity = isEnabled ? '1' : '0'; + } + // Focus input when enabled if (isEnabled) { carpeaterInput.focus(); diff --git a/index.html b/index.html index 4e73a2c..d41f547 100644 --- a/index.html +++ b/index.html @@ -169,8 +169,8 @@

Settings<