From 19505d794c200f96ff97001a939aed3fc6598ddb Mon Sep 17 00:00:00 2001 From: Sassa NF Date: Fri, 18 Apr 2025 19:58:37 +0100 Subject: [PATCH] Plot multiple foxes --- web_app/compass/index.html | 129 ++++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 25 deletions(-) diff --git a/web_app/compass/index.html b/web_app/compass/index.html index f6350c2..10365a2 100644 --- a/web_app/compass/index.html +++ b/web_app/compass/index.html @@ -175,24 +175,62 @@ } let lineCoef = 2.5; + + var minf = -1, maxf = -1; + dataPoints.forEach(({ spectrum }) => { + if (!spectrum) return; + + spectrum.forEach(({ F }) => { + if (minf < 0 || minf > F) minf = F; + maxf = Math.max(maxf, F); + }); + }); + // Draw data points - dataPoints.forEach(({ angle, rssi }) => { + dataPoints.forEach(({ angle, rssi, t0, spectrum }) => { + if (!currentPoint.spectrum || !spectrum) return; + const rad = (angle * Math.PI) / 180; //const length = (120 + rssi) / (radius / 2) * radius; - var length = ((120 - 90) + (rssi + 90)) * lineCoef; // Scale RSSI to fit within radar - if (length > radius) { - length = radius; - } - //console.log("Length: " + length); - const x = centerX + length * Math.sin(rad + angleOffset); - const y = centerY - length * Math.cos(rad + angleOffset); + if (minf == maxf) { + const length = Math.max(radius - 10, ((120 - 90) + (rssi + 90)) * lineCoef); // Scale RSSI to fit within radar - ctx.beginPath(); - ctx.moveTo(centerX, centerY); - ctx.lineTo(x, y); - ctx.strokeStyle = "red"; - ctx.lineWidth = 2; - ctx.stroke(); + //console.log("Length: " + length); + const x = centerX + length * Math.sin(rad + angleOffset); + const y = centerY - length * Math.cos(rad + angleOffset); + + ctx.beginPath(); + ctx.moveTo(centerX, centerY); + ctx.lineTo(x, y); + // calculating linear decay factor: + // for 5000 < dt < 45000 - linear decay from 1.0 to 0.5 + // for dt outside those - flat 1.0, or flat 0.5 + const colorDecay = 1 - Math.min(Math.max((currentPoint.t0 - t0 - 5000) / (2 * 40000), 0), 0.5); + ctx.strokeStyle = `rgba(${Math.trunc(255 * colorDecay)}, ${Math.trunc((1 - colorDecay) * 255)}, ${Math.trunc((1 - colorDecay) * 255)}, 1)`; + ctx.lineWidth = 2; + ctx.stroke(); + } else { + // now, here's radical new view: + // colour: the redder it is, the more recent the reading is + // intensity: the more intense it is, the higher the RSSI + // radius: the distance from centre represents frequency for which the reading was made + // angle: direction + + spectrum.forEach(({ F, R }) => { + const length = (F - minf) / (maxf - minf) * (radius - 20 - 10) + 20; + + ctx.beginPath(); + ctx.arc(centerX, centerY, length, rad + angleOffset - 0.5 * Math.PI / 180 - Math.PI / 2, rad + angleOffset + 0.5 * Math.PI / 180 - Math.PI / 2, false); + // calculating linear decay factor: + // for 5000 < dt < 45000 - linear decay from 1.0 to 0.5 + // for dt outside those - flat 1.0, or flat 0.5 + const colorDecay = 1 - Math.min(Math.max((currentPoint.t0 - t0 - 5000) / (2 * 40000), 0), 0.5); + const colorIntensity = 1 + Math.max(-1, Math.min(0, R + 20) / (70 - 20)); + ctx.strokeStyle = `rgba(${Math.trunc(255 * colorDecay)}, ${Math.trunc((1 - colorDecay) * 255)}, ${Math.trunc((1 - colorDecay) * 255)}, ${colorIntensity})`; + ctx.lineWidth = 5; + ctx.stroke(); + }); + } }); const maxPoint = dataPoints.reduce((max, point) => (point.rssi > max.rssi ? point : max), { angle: 0, rssi: -120 }); @@ -351,12 +389,40 @@ document.body.appendChild(promptDiv); } - function simulateRssi(fox) { - if (fox > 35) { - return -90; + function angleDiff(a, b) { + a = (a + 360) % 360; + b = (b + 360) % 360; + if (b < a) { + a += b; + b = a - b; + a -= b; } - return Math.cos((Math.PI / 2) * fox / 35) * 40 - Math.random() * 3 - 90; + return b - a > 180 ? a + 360 - b : b - a; + } + + function simulateRssi(foxes, h_angle) { + const rssis = foxes.map(({ angle, f_mhz, rssi }, i) => { + const fox = angleDiff(angle, h_angle); + return { + f_mhz: f_mhz, + rssi: Math.max(-90, (fox > 35 ? 0 : Math.cos((Math.PI / 2) * fox / 35) * (rssi + 90)) - Math.random() * 3 - 90) + }; + }); + + rssis.sort((a, b) => a.f_mhz - b.f_mhz); + + return rssis.reduce((v, fox) => { + if (v.length == 0 || v[v.length - 1].f_mhz != fox.f_mhz) { + v.push(fox); + return v; + } + + const prev = v[v.length - 1]; + prev.rssi = Math.log(Math.exp(prev.rssi) + Math.exp(fox.rssi)); + + return v; + }, []); } // Parse Bluetooth data @@ -392,8 +458,9 @@ const heading = ((headingMax + headingMin + 720 + (headingMax - headingMin > 180 ? 360 : 0)) / 2) % 360; const rssi = spectrum[0]["R"]; - dataPoints[Math.trunc(heading)] = { angle: heading, rssi: rssi }; - currentPoint = { angle: heading, rssi: rssi }; + const t0 = Date.now(); + dataPoints[Math.trunc(heading)] = { angle: heading, rssi: rssi, t0: t0, spectrum: spectrum }; + currentPoint = { angle: heading, rssi: rssi, t0: t0, spectrum: spectrum }; //if (dataPoints.length > 50) dataPoints.shift(); // Keep only the last 50 points headingDisplay.textContent = `${heading.toFixed(1)}°`; rssiDisplay.textContent = `${rssi.toFixed(1)} dBm`; @@ -446,15 +513,27 @@ dataPoints[i] = { angle: i, rssi: -120 }; } - (function simulate(fox, prevAngle, prevRssi) { + const foxes = [ + { angle: Math.random() * 360, f_mhz: 240, rssi: -60 }, + { angle: Math.random() * 360, f_mhz: 320, rssi: -45 }, + { angle: Math.random() * 360, f_mhz: 120, rssi: -20 }, + { angle: Math.random() * 360, f_mhz: 120, rssi: -35 } + ]; + (function simulate(foxes, prevAngle, prevRssi) { if (!isSimulating) return; const angle = (prevAngle - 3 + Math.random() * 7.5) % 360; // bias slightly to scan on - const rssi = simulateRssi(Math.abs(angle - fox)); + const spectrum = simulateRssi(foxes, angle); - const data = "RSSI_HEADING: '{H:" + angle + ",RSSI:" + rssi + "}'" + const data = spectrum.length == 1 ? "RSSI_HEADING: '{H:" + angle + ",RSSI:" + spectrum[0].rssi + "}'" : JSON.stringify({ + SCAN_RESULT: { + Hmin: Math.min(prevAngle, angle), + Hmax: Math.max(prevAngle, angle), + Spectrum: spectrum.map(({ f_mhz, rssi }) => ({ F: f_mhz, R: rssi })) + } + }); parseBTData(data); // test actual BT data processing - setTimeout(simulate, 100, fox, angle, rssi); - })(Math.random() * 360, Math.random() * 360, -70 + Math.random() * 30); + setTimeout(simulate, 100, foxes, angle, rssi); + })(foxes, Math.random() * 360, -70 + Math.random() * 30); } });