@sailingrotevista/rotevista-dash 7.0.30 → 7.0.32
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 +24 -15
- package/index.js +7 -2
- package/package.json +1 -1
- package/utils.js +7 -6
package/app.js
CHANGED
|
@@ -323,6 +323,11 @@ function getSourcePriorityScore(sourceName) {
|
|
|
323
323
|
}
|
|
324
324
|
|
|
325
325
|
function processIncomingData(path, val, source, timeMs) {
|
|
326
|
+
// 0. Filtro di validità assoluta: ignora all'istante pacchetti vuoti, null o non numerici
|
|
327
|
+
if (val === null || val === undefined || (typeof val === 'number' && isNaN(val))) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
326
331
|
// 1. Filtro anti-spike vento (> 100 nodi / 51.44 m/s)
|
|
327
332
|
if ((path === "environment.wind.speedApparent" || path === "environment.wind.speedTrue") && val > 51.44) {
|
|
328
333
|
return;
|
|
@@ -348,19 +353,22 @@ function processIncomingData(path, val, source, timeMs) {
|
|
|
348
353
|
} else {
|
|
349
354
|
const currentLock = sourceLocks[path];
|
|
350
355
|
const isSameSource = (currentLock.label === source);
|
|
351
|
-
|
|
356
|
+
// Tolleranza 5s: ignora il GPS Victron durante le micro-pause di Yacht Device per evitare imprecisioni
|
|
357
|
+
const isLockExpired = (now - currentLock.lastSeen > 5000);
|
|
352
358
|
const hasHigherPriority = (score > currentLock.score);
|
|
359
|
+
// Switch immediato tra apparati principali di pari priorità (es. YD e AP) se inattivi da > 1.5s
|
|
360
|
+
const isSamePriorityStale = (score === currentLock.score && (now - currentLock.lastSeen > 1500));
|
|
353
361
|
|
|
354
362
|
if (isSameSource) {
|
|
355
363
|
// Stessa sorgente: aggiorna il timestamp e mantiene il blocco
|
|
356
364
|
currentLock.lastSeen = now;
|
|
357
365
|
currentLock.score = score;
|
|
358
|
-
} else if (isLockExpired ||
|
|
359
|
-
//
|
|
366
|
+
} else if (hasHigherPriority || isLockExpired || isSamePriorityStale) {
|
|
367
|
+
// Passa a sorgenti secondarie (Victron) solo se Yacht Device è spento da > 5s
|
|
360
368
|
sourceLocks[path] = { label: source, score: score, lastSeen: now };
|
|
361
369
|
console.log(`🔌 [Smart Lock] Path "${path}" switched to: ${source} (Score: ${score})`);
|
|
362
370
|
} else {
|
|
363
|
-
//
|
|
371
|
+
// Scarta sorgenti imprecise di servizio mentre Yacht Device è la sorgente principale attiva
|
|
364
372
|
return;
|
|
365
373
|
}
|
|
366
374
|
}
|
|
@@ -410,19 +418,21 @@ function processIncomingData(path, val, source, timeMs) {
|
|
|
410
418
|
|
|
411
419
|
// --- GESTIONE PRUA VERA / MAGNETICA CON AUTODIVIAZIONE ---
|
|
412
420
|
if (path === "navigation.headingTrue") {
|
|
421
|
+
store.timestamps["navigation.headingTrueNative"] = now; // Marca la presenza di un sensore nativo di prua vera
|
|
422
|
+
store.timestamps["navigation.headingTrue"] = now;
|
|
413
423
|
safePush(store.smoothBuf.hdg, val, now);
|
|
414
424
|
safePush(store.longBuf.hdg, val, now);
|
|
415
425
|
}
|
|
416
426
|
else if (path === "navigation.headingMagnetic") {
|
|
417
|
-
// Se non
|
|
418
|
-
const
|
|
419
|
-
if (!
|
|
427
|
+
// Se non c'è una prua vera NATIVA negli ultimi 5 secondi, converte e registra ad ogni secondo la prua magnetica
|
|
428
|
+
const hasNativeTrueHdg = store.timestamps["navigation.headingTrueNative"] && (now - store.timestamps["navigation.headingTrueNative"] < 5000);
|
|
429
|
+
if (!hasNativeTrueHdg) {
|
|
420
430
|
const variation = store.raw["navigation.magneticVariation"] || 0; // Legge la declinazione magnetica del GPS
|
|
421
431
|
const calculatedTrueHdg = (val + variation + 2 * Math.PI) % (2 * Math.PI);
|
|
422
432
|
|
|
423
|
-
//
|
|
433
|
+
// Registra il valore calcolato mantenendo attivo il watchdog senza bloccare i successivi pacchetti magnetici
|
|
424
434
|
store.raw["navigation.headingTrue"] = calculatedTrueHdg;
|
|
425
|
-
store.timestamps["navigation.headingTrue"] = now;
|
|
435
|
+
store.timestamps["navigation.headingTrue"] = now;
|
|
426
436
|
safePush(store.smoothBuf.hdg, calculatedTrueHdg, now);
|
|
427
437
|
safePush(store.longBuf.hdg, calculatedTrueHdg, now);
|
|
428
438
|
}
|
|
@@ -795,16 +805,15 @@ function startDisplayLoop() {
|
|
|
795
805
|
}
|
|
796
806
|
|
|
797
807
|
// --- SLOW TIER (Salvataggio stato ogni 10 secondi) ---
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
808
|
+
if (lastAvgUIUpdate++ % 10 === 0) {
|
|
809
|
+
saveDashboardState();
|
|
810
|
+
}
|
|
801
811
|
|
|
802
|
-
|
|
812
|
+
// AGGIORNAMENTO FLUIDO MEDIE UI (Esecuzione a 1Hz costante ad ogni secondo)
|
|
803
813
|
const baseThreshold = CONFIG.averaging.stabilityThreshold || 0.95;
|
|
804
814
|
const breakout = CONFIG.averaging.stabilityBreakout || 15;
|
|
805
815
|
|
|
806
|
-
// CALCOLO DIFFERENZIALE: Tolleranza del vento proporzionale alla sensibilità di base
|
|
807
|
-
// Rilassiamo la soglia del vento di 0.13 rispetto alla prua (es. se la prua richiede 0.95, il vento richiede 0.82)
|
|
816
|
+
// CALCOLO DIFFERENZIALE: Tolleranza del vento proporzionale alla sensibilità di base
|
|
808
817
|
const windThreshold = Math.max(0.60, baseThreshold - 0.13);
|
|
809
818
|
|
|
810
819
|
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averaging.longWindow * 2, false, now, baseThreshold, breakout);
|
package/index.js
CHANGED
|
@@ -38,6 +38,7 @@ module.exports = function (app) {
|
|
|
38
38
|
// Memoria temporale per rilevare la presenza di sensori nativi sul Cerbo
|
|
39
39
|
let lastNativeTwsTime = 0;
|
|
40
40
|
let lastNativeTwdTime = 0;
|
|
41
|
+
let lastNativeHeadingTrueTime = 0; // Traccia la presenza di bussola vera nativa
|
|
41
42
|
|
|
42
43
|
// Monitoraggio dello scorrere dei blocchi da 30 minuti
|
|
43
44
|
let lastFrozen30mSlot = 0;
|
|
@@ -248,10 +249,14 @@ module.exports = function (app) {
|
|
|
248
249
|
lastNativeTwsTime = now; // Rilevato TWS nativo della centralina!
|
|
249
250
|
manageHistory('tws', smoothedVal * 1.94384);
|
|
250
251
|
}
|
|
252
|
+
else if (path === 'navigation.headingTrue') {
|
|
253
|
+
lastNativeHeadingTrueTime = now; // Rilevata prua vera nativa dalla rete NMEA
|
|
254
|
+
}
|
|
251
255
|
// --- DECODIFICA PRUA MAGNETICA SERVER-SIDE ---
|
|
252
256
|
else if (path === 'navigation.headingMagnetic') {
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
// Converte in prua vera solo se non c'è una bussola vera nativa attiva negli ultimi 5s
|
|
258
|
+
const hasNativeTrueHdg = (now - lastNativeHeadingTrueTime < 5000);
|
|
259
|
+
if (!hasNativeTrueHdg) {
|
|
255
260
|
const variation = raw['navigation.magneticVariation'] || 0;
|
|
256
261
|
raw['navigation.headingTrue'] = (smoothedVal + variation + 2 * Math.PI) % (2 * Math.PI);
|
|
257
262
|
}
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -36,10 +36,9 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false, now
|
|
|
36
36
|
const len = bufferArray.length;
|
|
37
37
|
if (len === 0) return null;
|
|
38
38
|
|
|
39
|
-
// RIFERIMENTO TEMPORALE IMMUNE DA DESINCRONIZZAZIONE
|
|
40
|
-
// Usiamo come 'ora attuale' l'ultimo timestamp presente nel buffer stesso,
|
|
41
|
-
// garantendo che la finestra di 2s sia sempre calcolata sui dati NMEA reali arrivati.
|
|
39
|
+
// RIFERIMENTO TEMPORALE IMMUNE DA DESINCRONIZZAZIONE (Usa l'ultimo dato del buffer)
|
|
42
40
|
const referenceTime = bufferArray[len - 1].time;
|
|
41
|
+
|
|
43
42
|
let sSin = 0, sCos = 0, count = 0;
|
|
44
43
|
let newestTime = 0, oldestTime = 0;
|
|
45
44
|
|
|
@@ -54,7 +53,7 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false, now
|
|
|
54
53
|
|
|
55
54
|
for (let i = len - 1; i >= 0; i--) {
|
|
56
55
|
const item = bufferArray[i];
|
|
57
|
-
if ((referenceTime - item.time) > windowMs) break;
|
|
56
|
+
if ((referenceTime - item.time) > windowMs) break;
|
|
58
57
|
|
|
59
58
|
let diffRad = Math.atan2(Math.sin(item.val - pilotRad), Math.cos(item.val - pilotRad));
|
|
60
59
|
let finalSin, finalCos;
|
|
@@ -82,12 +81,14 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false, now
|
|
|
82
81
|
const R = Math.hypot(sSin, sCos) / count;
|
|
83
82
|
const avgRad = Math.atan2(sSin, sCos);
|
|
84
83
|
const finalVal = signed ? avgRad : (avgRad + Math.PI * 2) % (Math.PI * 2);
|
|
85
|
-
const historyDuration = (count > 2) ? (newestTime - oldestTime) : 0;
|
|
86
84
|
const safeR = Math.max(R, 1e-9);
|
|
87
85
|
|
|
86
|
+
// Stabilità valida se ci sono almeno 2 campioni nel buffer e la varianza radiale supera la soglia
|
|
87
|
+
const isStable = (count >= 2) && (R > stabilityThreshold);
|
|
88
|
+
|
|
88
89
|
return {
|
|
89
90
|
val: finalVal,
|
|
90
|
-
stable:
|
|
91
|
+
stable: isStable,
|
|
91
92
|
dev: (R < 1) ? Math.round(Math.sqrt(-2 * Math.log(safeR)) * (180 / Math.PI)) : 0,
|
|
92
93
|
samples: count
|
|
93
94
|
};
|