diff --git a/src/decoders/trx-vdes/src/lib.rs b/src/decoders/trx-vdes/src/lib.rs index aed2038..76a6251 100644 --- a/src/decoders/trx-vdes/src/lib.rs +++ b/src/decoders/trx-vdes/src/lib.rs @@ -20,9 +20,8 @@ use num_complex::Complex; use trx_core::decode::VdesMessage; const VDES_SYMBOL_RATE: f32 = 76_800.0; -const MIN_BURST_MS: f32 = 1.5; -const BURST_END_MS: f32 = 0.35; -const MAX_BURST_MS: f32 = 45.0; +const MIN_BURST_MS: f32 = 2.0; +const BURST_END_MS: f32 = 0.4; const MIN_BURST_SYMBOLS: usize = 64; const TER_MCS1_100_BURST_SYMBOLS: usize = 1_984; const TER_MCS1_100_RAMP_SYMBOLS: usize = 32; @@ -36,10 +35,10 @@ const TER_MCS1_100_SYNC_BITS: &[u8; TER_MCS1_100_SYNC_SYMBOLS] = b"1111110011010 const PI4_QPSK_DIBITS: [u8; 4] = [0b00, 0b01, 0b11, 0b10]; const MIN_SYNC_CANDIDATE_SCORE: f32 = 0.20; const MIN_SYNC_PARSE_SCORE: f32 = 0.50; -const BURST_TRIGGER_NOISE_MULT: f32 = 3.0; -const BURST_TRIGGER_FLOOR: f32 = 1.0e-10; -const BURST_SUSTAIN_NOISE_MULT: f32 = 1.15; -const BURST_SUSTAIN_FLOOR: f32 = 1.0e-11; +const BURST_TRIGGER_NOISE_MULT: f32 = 8.0; +const BURST_TRIGGER_FLOOR: f32 = 2.0e-4; +const BURST_SUSTAIN_NOISE_MULT: f32 = 3.0; +const BURST_SUSTAIN_FLOOR: f32 = 1.2e-4; #[derive(Debug, Clone)] pub struct VdesDecoder { @@ -74,8 +73,6 @@ impl VdesDecoder { ((self.sample_rate * (MIN_BURST_MS / 1000.0)).round() as usize).max(16); let quiet_limit = ((self.sample_rate * (BURST_END_MS / 1000.0)).round() as u32).max(4); - let max_burst_samples = - ((self.sample_rate * (MAX_BURST_MS / 1000.0)).round() as usize).max(min_burst_samples); for &sample in samples { let power = sample.norm_sqr(); @@ -99,16 +96,6 @@ impl VdesDecoder { self.quiet_run = 0; } - if self.burst_samples.len() >= max_burst_samples { - if let Some(msg) = self.finalize_burst(channel) { - out.push(msg); - } - self.in_burst = false; - self.quiet_run = 0; - self.burst_samples.clear(); - continue; - } - if self.quiet_run >= quiet_limit { if self.burst_samples.len() >= min_burst_samples { if let Some(msg) = self.finalize_burst(channel) { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index 2a2b578..36c5c40 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -3139,10 +3139,11 @@ let aprsRadioPath = null; const stationMarkers = new Map(); const locatorMarkers = new Map(); const mapMarkers = new Set(); -const mapFilter = { ais: true, aprs: true, ft8: true, wspr: true }; +const mapFilter = { ais: true, vdes: true, aprs: true, ft8: true, wspr: true }; const APRS_TRACK_MAX_POINTS = 64; const AIS_TRACK_MAX_POINTS = 64; const aisMarkers = new Map(); +const vdesMarkers = new Map(); let selectedAisTrackMmsi = null; function syncAprsReceiverMarker() { @@ -3205,6 +3206,17 @@ window.clearMapMarkersByType = function(type) { return; } + if (type === "vdes") { + vdesMarkers.forEach((entry) => { + if (entry && entry.marker) { + if (aprsMap && aprsMap.hasLayer(entry.marker)) entry.marker.removeFrom(aprsMap); + mapMarkers.delete(entry.marker); + } + }); + vdesMarkers.clear(); + return; + } + if (type === "ft8" || type === "wspr") { const prefix = `${type}:`; for (const [key, entry] of locatorMarkers.entries()) { @@ -3311,6 +3323,19 @@ function initAprsMap() { { className: "aprs-radio-path", weight: 2, interactive: false } ).addTo(aprsMap); } + return; + } + + if (marker._vdesKey) { + const entry = vdesMarkers.get(String(marker._vdesKey)); + if (!entry || !entry.msg) return; + e.popup.setContent(buildVdesPopupHtml(entry.msg)); + if (serverLat != null && serverLon != null) { + aprsRadioPath = L.polyline( + [[serverLat, serverLon], [ll.lat, ll.lng]], + { className: "aprs-radio-path", weight: 2, interactive: false } + ).addTo(aprsMap); + } } }); @@ -3334,6 +3359,7 @@ function initAprsMap() { applyMapFilter(); const aisFilter = document.getElementById("map-filter-ais"); + const vdesFilter = document.getElementById("map-filter-vdes"); const aprsFilter = document.getElementById("map-filter-aprs"); const ft8Filter = document.getElementById("map-filter-ft8"); const wsprFilter = document.getElementById("map-filter-wspr"); @@ -3349,6 +3375,12 @@ function initAprsMap() { } }); } + if (vdesFilter) { + vdesFilter.addEventListener("change", () => { + mapFilter.vdes = vdesFilter.checked; + applyMapFilter(); + }); + } if (aprsFilter) { aprsFilter.addEventListener("change", () => { mapFilter.aprs = aprsFilter.checked; @@ -3528,6 +3560,43 @@ function buildAisPopupHtml(msg) { ``; } +function buildVdesPopupHtml(msg) { + const age = formatTimeAgo(msg?.ts_ms); + const distKm = (serverLat != null && serverLon != null && msg?.lat != null && msg?.lon != null) + ? haversineKm(serverLat, serverLon, msg.lat, msg.lon) + : null; + const distStr = distKm != null + ? (distKm < 1 ? `${Math.round(distKm * 1000)} m` : `${distKm.toFixed(1)} km`) + : null; + const meta = [ + age, + distStr, + msg?.message_label ? escapeMapHtml(msg.message_label) : null, + Number.isFinite(msg?.link_id) ? `LID ${Number(msg.link_id)}` : null, + ].filter(Boolean).join(" · "); + let rows = ""; + if (distStr) rows += `