@sailingrotevista/rotevista-dash 3.0.5 → 3.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +91 -19
- package/index.js +8 -2
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -8,8 +8,8 @@ let CONFIG = {
|
|
|
8
8
|
smoothWindow: 2000,
|
|
9
9
|
longWindow: 30000,
|
|
10
10
|
stabilityTolerance: 2000,
|
|
11
|
-
stabilityThreshold: 0.
|
|
12
|
-
minSpeed:
|
|
11
|
+
stabilityThreshold: 0.93,
|
|
12
|
+
minSpeed: 2
|
|
13
13
|
},
|
|
14
14
|
graphs: { reef1: 15.0, reef2: 20.0, historyMinutes: 5, samples: 60 },
|
|
15
15
|
scales: {
|
|
@@ -94,10 +94,22 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false) {
|
|
|
94
94
|
if (validData.length === 0) return null;
|
|
95
95
|
let sSin = 0, sCos = 0;
|
|
96
96
|
validData.forEach(item => { sSin += Math.sin(item.val); sCos += Math.cos(item.val); });
|
|
97
|
+
|
|
97
98
|
let R = Math.sqrt(sSin * sSin + sCos * sCos) / validData.length;
|
|
98
99
|
let isStable = (validData.length > 2) && (validData[validData.length - 1].time - validData[0].time >= windowMs - CONFIG.averages.stabilityTolerance) && (R > CONFIG.averages.stabilityThreshold);
|
|
99
100
|
let avgRad = Math.atan2(sSin, sCos);
|
|
100
|
-
|
|
101
|
+
|
|
102
|
+
// Calcolo deviazione standard (scarto) in gradi
|
|
103
|
+
let deviation = 0;
|
|
104
|
+
if (R < 1 && R > 0) {
|
|
105
|
+
deviation = Math.round(Math.sqrt(-2 * Math.log(R)) * (180 / Math.PI));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
val: signed ? avgRad : (avgRad + 2 * Math.PI) % (2 * Math.PI),
|
|
110
|
+
stable: isStable,
|
|
111
|
+
dev: deviation
|
|
112
|
+
};
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
function playBingBing() {
|
|
@@ -311,29 +323,89 @@ function startDisplayLoop() {
|
|
|
311
323
|
if (tick % 3 === 0) {
|
|
312
324
|
// Utilizziamo CONFIG.averages.longWindow (che ora è 30000ms)
|
|
313
325
|
// In questo modo, se cambi il tempo su Signal K, la dashboard si aggiorna da sola.
|
|
314
|
-
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averages.longWindow, false)
|
|
326
|
+
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averages.longWindow * 2, false)
|
|
315
327
|
cObj = getCircularAverageFromBuffer(store.longBuf.cog, CONFIG.averages.longWindow, false),
|
|
316
328
|
awObj = getCircularAverageFromBuffer(store.longBuf.awa, CONFIG.averages.longWindow, true),
|
|
317
329
|
twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.averages.longWindow, true),
|
|
318
330
|
twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.averages.longWindow, false);
|
|
319
331
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
332
|
+
const upUI = (el, obj, instantRaw, isCompass = false) => {
|
|
333
|
+
if (!obj || obj.val === null || instantRaw === undefined) {
|
|
334
|
+
el.innerHTML = "---°";
|
|
335
|
+
el.classList.remove('unstable-data');
|
|
336
|
+
} else {
|
|
337
|
+
let valDeg = Math.round(radToDeg(obj.val));
|
|
338
|
+
let mainVal = (isCompass ? ((valDeg + 360) % 360).toString().padStart(3, '0') : valDeg) + "°";
|
|
339
|
+
|
|
340
|
+
// Mostriamo lo scarto medio (±)
|
|
341
|
+
let devDisplay = (obj.dev > 1 && obj.dev < 90) ?
|
|
342
|
+
`<span style="font-size: 0.35em; opacity: 0.5; margin-left: 4px; vertical-align: middle;">±${obj.dev}</span>` : "";
|
|
343
|
+
|
|
344
|
+
el.innerHTML = mainVal + devDisplay;
|
|
345
|
+
|
|
346
|
+
// --- LOGICA ALLARME ISTANTANEA (ANTI-RITARDO) ---
|
|
347
|
+
// Calcoliamo la differenza tra istantaneo e media (con gestione giro bussola)
|
|
348
|
+
let instantDeg = radToDeg(instantRaw);
|
|
349
|
+
let diff = Math.abs((instantDeg - radToDeg(obj.val) + 540) % 360 - 180);
|
|
350
|
+
|
|
351
|
+
// Lampeggia se:
|
|
352
|
+
// 1. La statistica R è bassa (obj.stable è false)
|
|
353
|
+
// 2. Lo scarto medio è alto (> 15°)
|
|
354
|
+
// 3. C'è un salto improvviso tra istantaneo e media (> 15°)
|
|
355
|
+
if (isNavigating && (!obj.stable || obj.dev > 15 || diff > 15)) {
|
|
356
|
+
el.classList.add('unstable-data');
|
|
357
|
+
} else {
|
|
358
|
+
el.classList.remove('unstable-data');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
// Passiamo: (elemento UI, oggetto media, valore istantaneo dal sensore, è una bussola?)
|
|
363
|
+
upUI(ui.hdg, hObj, store.raw["navigation.headingTrue"], true);
|
|
364
|
+
upUI(ui.cog, cObj, store.raw["navigation.courseOverGroundTrue"], true);
|
|
365
|
+
upUI(ui.awaAvg, awObj, store.raw["environment.wind.angleApparent"], false);
|
|
366
|
+
upUI(ui.twaAvg, twObj, store.raw["environment.wind.angleTrueWater"], false);
|
|
367
|
+
upUI(ui.twdAvg, twdObj, store.raw["environment.wind.directionTrue"], true);
|
|
328
368
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const tackCogDeg = radToDeg((cObj.val + twObj.val * 2 + Math.PI * 2) % (Math.PI * 2));
|
|
334
|
-
|
|
369
|
+
// --- CALCOLO E VALIDAZIONE TACK ---
|
|
370
|
+
if (hObj && twObj) {
|
|
371
|
+
// Calcoliamo i valori teorici
|
|
372
|
+
const tackHdgDeg = radToDeg((hObj.val + twObj.val * 2 + Math.PI * 2) % (Math.PI * 2));
|
|
373
|
+
const tackCogDeg = cObj ? radToDeg((cObj.val + twObj.val * 2 + Math.PI * 2) % (Math.PI * 2)) : null;
|
|
374
|
+
|
|
375
|
+
// Condizione di instabilità tecnica (durante la manovra)
|
|
376
|
+
const isTackUnstable = !hObj.stable || !twObj.stable || hObj.dev > 15 || twObj.dev > 15;
|
|
377
|
+
|
|
378
|
+
// --- GESTIONE INTERFACCIA TACK HDG ---
|
|
379
|
+
if (!isNavigating) {
|
|
380
|
+
// Caso 1: Barca ferma -> Trattini fissi
|
|
381
|
+
ui.tackHdg.innerHTML = "---°";
|
|
382
|
+
ui.tackHdg.classList.remove('unstable-data');
|
|
383
|
+
} else if (isTackUnstable) {
|
|
384
|
+
// Caso 2: Manovra in corso -> Trattini lampeggianti
|
|
385
|
+
ui.tackHdg.innerHTML = "---°";
|
|
386
|
+
ui.tackHdg.classList.add('unstable-data');
|
|
387
|
+
} else {
|
|
388
|
+
// Caso 3: Navigazione stabile -> Mostra valore
|
|
389
|
+
ui.tackHdg.innerHTML = `${Math.round((tackHdgDeg + 360) % 360).toString().padStart(3, '0')}°`;
|
|
390
|
+
ui.tackHdg.classList.remove('unstable-data');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// --- GESTIONE INTERFACCIA TACK COG ---
|
|
394
|
+
if (cObj) {
|
|
395
|
+
const isCogTackUnstable = !cObj.stable || !twObj.stable || cObj.dev > 15 || twObj.dev > 15;
|
|
396
|
+
|
|
397
|
+
if (!isNavigating) {
|
|
398
|
+
ui.tackCog.innerHTML = "---°";
|
|
399
|
+
ui.tackCog.classList.remove('unstable-data');
|
|
400
|
+
} else if (isCogTackUnstable) {
|
|
401
|
+
ui.tackCog.innerHTML = "---°";
|
|
402
|
+
ui.tackCog.classList.add('unstable-data');
|
|
403
|
+
} else {
|
|
404
|
+
ui.tackCog.innerHTML = `${Math.round((tackCogDeg + 360) % 360).toString().padStart(3, '0')}°`;
|
|
405
|
+
ui.tackCog.classList.remove('unstable-data');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
335
408
|
}
|
|
336
|
-
}
|
|
337
409
|
const smHdg = getCircularAverageFromBuffer(store.smoothBuf.hdg, 2000, false), smTwd = getCircularAverageFromBuffer(store.smoothBuf.twd, 2000, false);
|
|
338
410
|
if (smHdg && smTwd) {
|
|
339
411
|
curWindCompassRot = getShortestRotation(curWindCompassRot, radToDeg(smTwd.val)); ui.twdArrow.setAttribute('transform', `rotate(${curWindCompassRot}, 20, 20)`);
|
package/index.js
CHANGED
|
@@ -66,17 +66,23 @@ module.exports = function (app) {
|
|
|
66
66
|
description: "Time buffer for 'MEAN' values. Larger windows produce smoother numbers but increase the 'Unstable' (orange) alerts during maneuvers or in gusty conditions, as data coherence decreases over time.",
|
|
67
67
|
default: 30000
|
|
68
68
|
},
|
|
69
|
-
|
|
69
|
+
smoothWindow: {
|
|
70
70
|
type: 'number',
|
|
71
71
|
title: 'Pointer Smoothing Window (ms)',
|
|
72
72
|
description: "Buffer for gauge needles and pointers. Removes sensor jitter while maintaining real-time responsiveness.",
|
|
73
73
|
default: 2000
|
|
74
74
|
},
|
|
75
|
-
|
|
75
|
+
minSpeed: {
|
|
76
76
|
type: 'number',
|
|
77
77
|
title: 'Min Speed for Stability (knots)',
|
|
78
78
|
description: "SOG threshold below which stability alerts (blinking orange) are suppressed to avoid GPS noise while docked.",
|
|
79
79
|
default: 0.5
|
|
80
|
+
},
|
|
81
|
+
stabilityThreshold: {
|
|
82
|
+
type: 'number',
|
|
83
|
+
title: 'Stability Sensitivity (R value: 0.7 - 0.98)',
|
|
84
|
+
description: "Determines when the number blinks orange. 0.95 = very sensitive (blinks with small movements), 0.85 = standard (balanced for sea), 0.75 = sturdy (blinks only in very rough conditions).",
|
|
85
|
+
default: 0.93
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
},
|