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);
}
});