@sailingrotevista/rotevista-dash 7.0.22 → 7.0.23
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 +142 -52
- package/charts.js +36 -10
- package/index.js +16 -8
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -216,16 +216,25 @@ function checkDepthAlarm(m) {
|
|
|
216
216
|
function computeTrueWind() {
|
|
217
217
|
const aws = store.raw["environment.wind.speedApparent"], awa = store.raw["environment.wind.angleApparent"];
|
|
218
218
|
const stw = store.raw["navigation.speedThroughWater"], sog = store.raw["navigation.speedOverGround"] || 0;
|
|
219
|
-
const hdg = store.raw["navigation.headingTrue"] || 0
|
|
219
|
+
const hdg = store.raw["navigation.headingTrue"] || 0; // Il COG qui non serve più per il calcolo stabile
|
|
220
220
|
if (aws === undefined || awa === undefined) return;
|
|
221
221
|
|
|
222
|
-
// Usiamo il tempo esatto di arrivo del pacchetto del vento per la coerenza dei buffer
|
|
223
222
|
const now = store.timestamps["environment.wind.speedApparent"] || Date.now();
|
|
224
223
|
|
|
225
|
-
//
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
224
|
+
// RILEVAMENTO "LOG BLOCCATO" (Fouled Paddlewheel)
|
|
225
|
+
const hasFreshStw = store.timestamps["navigation.speedThroughWater"] && (now - store.timestamps["navigation.speedThroughWater"] < 15000);
|
|
226
|
+
let speedRef = 0;
|
|
227
|
+
|
|
228
|
+
if (hasFreshStw && stw !== undefined) {
|
|
229
|
+
// Se la barca naviga a GPS (> 1.5 nodi) ma l'elichetta legge quasi zero (< 0.5 nodi), il log è sporco/bloccato.
|
|
230
|
+
if (sog > 0.77 && stw < 0.25) {
|
|
231
|
+
speedRef = sog; // Forza il backup su SOG ignorando lo "0" fittizio del log
|
|
232
|
+
} else {
|
|
233
|
+
speedRef = stw;
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
speedRef = sog;
|
|
237
|
+
}
|
|
229
238
|
|
|
230
239
|
// ==========================================================================
|
|
231
240
|
// 1. GESTIONE TWS (NATIVO vs FALLBACK)
|
|
@@ -234,47 +243,51 @@ function computeTrueWind() {
|
|
|
234
243
|
let tws_water = 0;
|
|
235
244
|
|
|
236
245
|
if (hasNativeTws) {
|
|
237
|
-
// Se la barca invia il TWS nativo, usiamo direttamente quello
|
|
238
246
|
tws_water = store.raw["environment.wind.speedTrue"] ? msToKts(store.raw["environment.wind.speedTrue"]) : 0;
|
|
239
247
|
} else {
|
|
240
|
-
// Altrimenti eseguiamo il calcolo vettoriale tattico utilizzando la velocità di riferimento (STW o SOG)
|
|
241
248
|
tws_water = Math.sqrt(aws * aws + speedRef * speedRef - 2 * aws * speedRef * Math.cos(awa));
|
|
242
249
|
store.raw["environment.wind.speedTrue"] = tws_water;
|
|
243
250
|
}
|
|
244
251
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
+
let twa = 0;
|
|
253
|
+
// Verifica se riceviamo un TWA nativo valido ed efficiente negli ultimi 5 secondi
|
|
254
|
+
const hasNativeTwa = store.timestamps["environment.wind.angleTrueWater"] && (Date.now() - store.timestamps["environment.wind.angleTrueWater"] < 5000);
|
|
255
|
+
|
|
256
|
+
if (hasNativeTwa && store.raw["environment.wind.angleTrueWater"] !== undefined) {
|
|
257
|
+
// "Native First": diamo la precedenza assoluta al valore calcolato dalla centralina
|
|
258
|
+
twa = store.raw["environment.wind.angleTrueWater"];
|
|
259
|
+
} else {
|
|
260
|
+
// "Fallback Second": se manca il dato nativo, eseguiamo il calcolo vettoriale a mano
|
|
261
|
+
if (tws_water > 0.05) {
|
|
262
|
+
twa = Math.atan2(aws * Math.sin(awa), aws * Math.cos(awa) - speedRef);
|
|
263
|
+
store.raw["environment.wind.angleTrueWater"] = twa;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Salviamo e allineiamo i buffer delle medie se abbiamo un angolo valido (nativo o calcolato)
|
|
268
|
+
if (tws_water > 0.05 || hasNativeTwa) {
|
|
252
269
|
safePush(store.smoothBuf.twa, twa, now);
|
|
253
270
|
safePush(store.longBuf.twa, twa, now);
|
|
254
|
-
|
|
255
|
-
|
|
271
|
+
|
|
272
|
+
// Salviamo l'angolo apparente solo se il relativo sensore fisico è effettivamente attivo
|
|
273
|
+
if (awa !== undefined) {
|
|
274
|
+
safePush(store.smoothBuf.awa, awa, now);
|
|
275
|
+
safePush(store.longBuf.awa, awa, now);
|
|
276
|
+
}
|
|
256
277
|
}
|
|
257
278
|
|
|
258
279
|
// ==========================================================================
|
|
259
|
-
// 2. GESTIONE TWD (NATIVO vs FALLBACK)
|
|
280
|
+
// 2. GESTIONE TWD (NATIVO vs FALLBACK MATEMATICO STABILE)
|
|
260
281
|
// ==========================================================================
|
|
261
282
|
const hasNativeTwd = store.timestamps["environment.wind.directionTrue"] && (Date.now() - store.timestamps["environment.wind.directionTrue"] < 5000);
|
|
262
283
|
|
|
263
|
-
if (hasNativeTwd) {
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const tws_ground = Math.sqrt(tw_ground_x * tw_ground_x + tw_ground_y * tw_ground_y);
|
|
271
|
-
|
|
272
|
-
if (tws_ground > 0.05) {
|
|
273
|
-
let twd = (hdg + Math.atan2(tw_ground_y, tw_ground_x) + 2 * Math.PI) % (2 * Math.PI);
|
|
274
|
-
store.raw["environment.wind.directionTrue"] = twd;
|
|
275
|
-
safePush(store.smoothBuf.twd, twd, now);
|
|
276
|
-
safePush(store.longBuf.twd, twd, now); // Alimenta la bussola meteo strategica!
|
|
277
|
-
}
|
|
284
|
+
if (!hasNativeTwd && tws_water > 0.05) {
|
|
285
|
+
// Calcolo TWD stabile e immune dal rollio: Prua + TWA
|
|
286
|
+
// Elimina i sobbalzi legati al brandeggio del COG e dello scarroccio fasullo
|
|
287
|
+
let twd = (hdg + twa + 2 * Math.PI) % (2 * Math.PI);
|
|
288
|
+
store.raw["environment.wind.directionTrue"] = twd;
|
|
289
|
+
safePush(store.smoothBuf.twd, twd, now);
|
|
290
|
+
safePush(store.longBuf.twd, twd, now);
|
|
278
291
|
}
|
|
279
292
|
}
|
|
280
293
|
|
|
@@ -387,6 +400,12 @@ function processIncomingData(path, val, source, timeMs) {
|
|
|
387
400
|
let speedVal = (val && typeof val === 'object' && val.val !== undefined) ? val.val : val;
|
|
388
401
|
store.raw["environment.wind.speedTrue"] = speedVal;
|
|
389
402
|
}
|
|
403
|
+
|
|
404
|
+
// INTERCETTAZIONE TWA NATIVO (Angolo Vento Reale pronto all'uso)
|
|
405
|
+
if (path === "environment.wind.angleTrueWater") {
|
|
406
|
+
let angleVal = (val && typeof val === 'object' && val.val !== undefined) ? val.val : val;
|
|
407
|
+
store.raw["environment.wind.angleTrueWater"] = angleVal;
|
|
408
|
+
}
|
|
390
409
|
|
|
391
410
|
// --- GESTIONE PRUA VERA / MAGNETICA CON AUTODIVIAZIONE ---
|
|
392
411
|
if (path === "navigation.headingTrue") {
|
|
@@ -665,7 +684,16 @@ function startDisplayLoop() {
|
|
|
665
684
|
|
|
666
685
|
// --- LOGICA SOG / VMG ---
|
|
667
686
|
if (store.raw["navigation.speedOverGround"] !== undefined) {
|
|
668
|
-
|
|
687
|
+
// RILEVAMENTO "LOG BLOCCATO" PER IL CALCOLO VMG
|
|
688
|
+
const hasFreshStw = store.timestamps["navigation.speedThroughWater"] && (now - store.timestamps["navigation.speedThroughWater"] < 15000);
|
|
689
|
+
let speedRefKts = sogKts; // Fallback predefinito su GPS
|
|
690
|
+
|
|
691
|
+
if (hasFreshStw) {
|
|
692
|
+
// Se viaggiamo a più di 1.5 nodi (GPS) ma il log segna meno di 0.5 nodi, usa il SOG come backup
|
|
693
|
+
speedRefKts = (sogKts > 1.5 && stwKts < 0.5) ? sogKts : stwKts;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const vmgVal = Math.abs(speedRefKts * Math.cos(store.raw["environment.wind.angleTrueWater"] || 0));
|
|
669
697
|
manageHistory('vmg', vmgVal);
|
|
670
698
|
manageHistory('sog', sogKts);
|
|
671
699
|
|
|
@@ -679,13 +707,22 @@ function startDisplayLoop() {
|
|
|
679
707
|
if (labelSogVmg) labelSogVmg.textContent = 'SOG';
|
|
680
708
|
|
|
681
709
|
if (isNavigating) {
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
710
|
+
const hasFreshStw = store.timestamps["navigation.speedThroughWater"] && (now - store.timestamps["navigation.speedThroughWater"] < 15000);
|
|
711
|
+
// Rileviamo se il log è plausibilmente sporco o bloccato (SOG in movimento, STW a zero)
|
|
712
|
+
const isFouledLog = (sogKts > 1.5 && stwKts < 0.5);
|
|
713
|
+
|
|
714
|
+
// Se non abbiamo un log affidabile, è impossibile calcolare la corrente. Evitiamo falsi colori tattici.
|
|
715
|
+
if (!hasFreshStw || isFouledLog) {
|
|
716
|
+
ui.sog.style.removeProperty('color');
|
|
717
|
+
} else {
|
|
718
|
+
const lastSog = store.histories.sog.length > 0 ? store.histories.sog[store.histories.sog.length - 1].val : sogKts;
|
|
719
|
+
const lastStw = store.histories.stw.length > 0 ? store.histories.stw[store.histories.stw.length - 1].val : stwKts;
|
|
720
|
+
const drift = lastSog - lastStw;
|
|
721
|
+
|
|
722
|
+
if (drift < -0.3) ui.sog.style.setProperty('color', '#ff3b30', 'important'); // Contro
|
|
723
|
+
else if (drift > 0.3) ui.sog.style.setProperty('color', '#00C851', 'important'); // Favore
|
|
724
|
+
else ui.sog.style.setProperty('color', '#ffbb33', 'important'); // Neutro SOG
|
|
725
|
+
}
|
|
689
726
|
} else {
|
|
690
727
|
ui.sog.style.removeProperty('color');
|
|
691
728
|
}
|
|
@@ -902,23 +939,74 @@ async function fetchServerHistory() {
|
|
|
902
939
|
const data = await response.json();
|
|
903
940
|
|
|
904
941
|
if (data && typeof data === 'object') {
|
|
942
|
+
// 1. RILEVAMENTO DEL DELTA DI COERENZA TEMPORALE
|
|
943
|
+
// Troviamo il timestamp più recente in assoluto presente nei dati storici del server
|
|
944
|
+
let maxServerTime = 0;
|
|
945
|
+
for (let key in data) {
|
|
946
|
+
if (store.histories[key] !== undefined && Array.isArray(data[key]) && data[key].length > 0) {
|
|
947
|
+
const lastPoint = data[key][data[key].length - 1];
|
|
948
|
+
if (lastPoint && lastPoint.time > maxServerTime) {
|
|
949
|
+
maxServerTime = lastPoint.time;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// Calcoliamo lo sfasamento in millisecondi rispetto all'orologio del client (MacBook/Tablet)
|
|
955
|
+
const nowMac = Date.now();
|
|
956
|
+
const timeDelta = (maxServerTime > 0) ? (nowMac - maxServerTime) : 0;
|
|
957
|
+
|
|
958
|
+
if (Math.abs(timeDelta) > 500) {
|
|
959
|
+
console.log(`⏱️ [Clock Sync] Rilevato scostamento di ${(timeDelta/1000).toFixed(1)}s rispetto a einstein. Calibrazione attiva.`);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// 2. TRASLAZIONE DEI DATI STORICI
|
|
963
|
+
// Spostiamo nel tempo tutti i punti passati per incollarli millimetricamente al "now" del client
|
|
905
964
|
for (let key in data) {
|
|
906
965
|
if (store.histories[key] !== undefined) {
|
|
907
|
-
|
|
966
|
+
if (Array.isArray(data[key])) {
|
|
967
|
+
store.histories[key] = data[key].map(p => ({
|
|
968
|
+
val: p.val,
|
|
969
|
+
time: p.time + timeDelta, // Sposta il punto nel tempo per allinearlo al Mac
|
|
970
|
+
min: p.min !== undefined ? p.min : undefined,
|
|
971
|
+
max: p.max !== undefined ? p.max : undefined
|
|
972
|
+
}));
|
|
973
|
+
} else {
|
|
974
|
+
store.histories[key] = data[key];
|
|
975
|
+
}
|
|
908
976
|
}
|
|
909
977
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
if (data.
|
|
913
|
-
|
|
914
|
-
|
|
978
|
+
|
|
979
|
+
// Sincronizza i dati specifici del radar storici, previsionali e i buffer minuto per minuto (applicando il Delta)
|
|
980
|
+
if (data.windRadarSlots) {
|
|
981
|
+
store.windRadarSlots = data.windRadarSlots.map(s => ({
|
|
982
|
+
...s,
|
|
983
|
+
timestamp: s.timestamp + timeDelta
|
|
984
|
+
}));
|
|
985
|
+
}
|
|
986
|
+
if (data.futureForecast) {
|
|
987
|
+
store.futureForecast = {
|
|
988
|
+
...data.futureForecast,
|
|
989
|
+
timestamp: data.futureForecast.timestamp + timeDelta
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
if (data.twd) {
|
|
993
|
+
store.twdMinuteBuffer = data.twd.map(p => ({
|
|
994
|
+
...p,
|
|
995
|
+
time: p.time + timeDelta
|
|
996
|
+
}));
|
|
997
|
+
}
|
|
998
|
+
if (data.tws) {
|
|
999
|
+
store.twsMinuteBuffer = data.tws.map(p => ({
|
|
1000
|
+
...p,
|
|
1001
|
+
time: p.time + timeDelta
|
|
1002
|
+
}));
|
|
1003
|
+
}
|
|
915
1004
|
|
|
916
1005
|
// --- SILLABAZIONE STRATEGICA DELLA BUSSOLA METEO (TWD) ---
|
|
917
|
-
// Se il server ci invia lo storico del TWD, lo inseriamo calcolando i seni e coseni per i vettori
|
|
918
1006
|
if (data.twd && data.twd.length > 0) {
|
|
919
1007
|
store.longBuf.twd = data.twd.map(p => ({
|
|
920
1008
|
val: p.val,
|
|
921
|
-
time: p.time,
|
|
1009
|
+
time: p.time + timeDelta, // Allinea il tempo strategico del radar
|
|
922
1010
|
sin: Math.sin(p.val),
|
|
923
1011
|
cos: Math.cos(p.val)
|
|
924
1012
|
}));
|
|
@@ -928,7 +1016,7 @@ async function fetchServerHistory() {
|
|
|
928
1016
|
}
|
|
929
1017
|
} catch (err) {
|
|
930
1018
|
console.warn("⚠️ Impossibile caricare lo storico dal server. Utilizzo dati vuoti/simulati.");
|
|
931
|
-
throw err;
|
|
1019
|
+
throw err;
|
|
932
1020
|
}
|
|
933
1021
|
}
|
|
934
1022
|
|
|
@@ -1176,6 +1264,7 @@ function connect() {
|
|
|
1176
1264
|
{ path: "environment.wind.speedApparent", minPeriod: 333 }, // AWS a 3 Hz
|
|
1177
1265
|
{ path: "environment.wind.angleApparent", minPeriod: 333 }, // AWA a 3 Hz
|
|
1178
1266
|
{ path: "environment.wind.speedTrue", minPeriod: 333 }, // TWS (Nativo) a 3 Hz
|
|
1267
|
+
{ path: "environment.wind.angleTrueWater", minPeriod: 333 }, // TWA (Nativo) a 3 Hz (AGGIUNTO)
|
|
1179
1268
|
{ path: "environment.wind.directionTrue", minPeriod: 333 } // TWD (Nativo) a 3 Hz
|
|
1180
1269
|
]
|
|
1181
1270
|
};
|
|
@@ -1331,11 +1420,12 @@ async function init() {
|
|
|
1331
1420
|
startDisplayLoop();
|
|
1332
1421
|
connect(); // Si collegherà in tempo reale al WebSocket reale della barca
|
|
1333
1422
|
|
|
1334
|
-
// 5. POLL LENTO (15 secondi): aggiorna i dati radar in background
|
|
1423
|
+
// 5. POLL LENTO (15 secondi): aggiorna i dati radar in background SOLO se lo strumento attivo è il radar,
|
|
1424
|
+
// evitando di sovraccaricare la rete ed eliminando il salto visivo di reset dei grafici a 1Hz
|
|
1335
1425
|
setInterval(async () => {
|
|
1336
1426
|
try {
|
|
1337
|
-
await fetchServerHistory();
|
|
1338
1427
|
if (activeInstrument === 'radar') {
|
|
1428
|
+
await fetchServerHistory();
|
|
1339
1429
|
renderRadar();
|
|
1340
1430
|
}
|
|
1341
1431
|
} catch (err) {
|
package/charts.js
CHANGED
|
@@ -19,7 +19,9 @@ function calculateScale(type, data, mode) {
|
|
|
19
19
|
|
|
20
20
|
if (!store.herculesScales) store.herculesScales = {};
|
|
21
21
|
if (!store.herculesScales[type]) {
|
|
22
|
-
|
|
22
|
+
// Inizializzazione a NULL dello stato di partenza per consentire un allineamento istantaneo
|
|
23
|
+
// e perfettamente centrato al primo doppio clic
|
|
24
|
+
store.herculesScales[type] = { min: null, max: null };
|
|
23
25
|
}
|
|
24
26
|
let currentScale = store.herculesScales[type];
|
|
25
27
|
|
|
@@ -65,9 +67,10 @@ function calculateScale(type, data, mode) {
|
|
|
65
67
|
targetMax = targetMin + 4;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
currentScale.
|
|
70
|
+
// Gestione dell'allineamento iniziale o dello scostamento dei limiti della scala
|
|
71
|
+
if (currentScale.min === null || currentVal < currentScale.min || currentVal > currentScale.max) {
|
|
72
|
+
currentScale.min = currentScale.min === null ? targetMin : Math.min(currentScale.min, targetMin);
|
|
73
|
+
currentScale.max = currentScale.max === null ? targetMax : Math.max(currentScale.max, targetMax);
|
|
71
74
|
} else {
|
|
72
75
|
const allStableInTarget = recentVals.every(val => val >= targetMin && val <= targetMax);
|
|
73
76
|
if (allStableInTarget) {
|
|
@@ -90,8 +93,25 @@ function calculateScale(type, data, mode) {
|
|
|
90
93
|
const oneThirdCount = Math.max(1, Math.floor(data.length / 3));
|
|
91
94
|
const recentThirdData = data.slice(-oneThirdCount);
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
let localMin = Math.min(...recentThirdData);
|
|
97
|
+
let localMax = Math.max(...recentThirdData);
|
|
98
|
+
|
|
99
|
+
// SMART MOTORING DETECTION (Specifica per i grafici di velocità: STW, SOG, VMG)
|
|
100
|
+
const isSpeedType = (type === 'stw' || type === 'sog' || type === 'vmg');
|
|
101
|
+
if (isSpeedType && data.length > oneThirdCount) {
|
|
102
|
+
const olderData = data.slice(0, data.length - oneThirdCount);
|
|
103
|
+
const olderMin = Math.min(...olderData);
|
|
104
|
+
const olderMax = Math.max(...olderData);
|
|
105
|
+
const olderRange = olderMax - olderMin; // Variazione nello storico passato
|
|
106
|
+
|
|
107
|
+
// Se lo storico passato non era piatto (range > 0.2 nodi) oppure eravamo fermi/all'ancora (< 1.5 nodi),
|
|
108
|
+
// significa che il passato è rilevante per il trend. Includiamo il passato nel calcolo della scala Y.
|
|
109
|
+
// Se invece era piatto (motore), lo ignoriamo per zoomare subito sulla vela attuale.
|
|
110
|
+
if (olderRange > 0.2 || olderMin < 0.77) {
|
|
111
|
+
localMin = Math.min(localMin, olderMin);
|
|
112
|
+
localMax = Math.max(localMax, olderMax);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
95
115
|
|
|
96
116
|
let targetMin = Math.max(0, Math.floor(localMin / roundStep) * roundStep);
|
|
97
117
|
let targetMax = Math.ceil(localMax / roundStep) * roundStep;
|
|
@@ -100,9 +120,10 @@ function calculateScale(type, data, mode) {
|
|
|
100
120
|
targetMax = targetMin + roundStep;
|
|
101
121
|
}
|
|
102
122
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
currentScale.
|
|
123
|
+
// Gestione dell'allineamento iniziale o dello scostamento dei limiti della scala
|
|
124
|
+
if (currentScale.min === null || currentVal < currentScale.min || currentVal > currentScale.max) {
|
|
125
|
+
currentScale.min = currentScale.min === null ? targetMin : Math.min(currentScale.min, targetMin);
|
|
126
|
+
currentScale.max = currentScale.max === null ? targetMax : Math.max(currentScale.max, targetMax);
|
|
106
127
|
} else {
|
|
107
128
|
const allStableInTarget = recentThirdData.every(val => val >= targetMin && val <= targetMax);
|
|
108
129
|
if (allStableInTarget) {
|
|
@@ -256,7 +277,12 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
256
277
|
gradientStops += `<stop offset="${offset1}%" stop-color="${props.color}" stop-opacity="${props.opacity}" />`;
|
|
257
278
|
gradientStops += `<stop offset="${offset2}%" stop-color="${props.color}" stop-opacity="${props.opacity}" />`;
|
|
258
279
|
|
|
259
|
-
|
|
280
|
+
// CALCOLO ELASTICO DELL'INTERRUZIONE: Tollera buchi storici fino a 90 secondi (es. i 30s di einstein)
|
|
281
|
+
// per permettere di disegnare linee solide e continue anche se mostrate in una timeline corta e veloce
|
|
282
|
+
const maxAllowedGap = Math.max(expectedInterval * 2.5, 90000);
|
|
283
|
+
const isHistoryGap = deltaTime > maxAllowedGap;
|
|
284
|
+
|
|
285
|
+
if (isHistoryGap) {
|
|
260
286
|
if (started) {
|
|
261
287
|
areaPath += `L ${x1} ${h} `;
|
|
262
288
|
started = false;
|
package/index.js
CHANGED
|
@@ -277,32 +277,40 @@ module.exports = function (app) {
|
|
|
277
277
|
const stw = raw["navigation.speedThroughWater"];
|
|
278
278
|
const sog = raw["navigation.speedOverGround"] || 0;
|
|
279
279
|
const hdg = raw["navigation.headingTrue"];
|
|
280
|
-
const cog = raw["navigation.courseOverGroundTrue"] || 0;
|
|
281
280
|
|
|
282
281
|
if (aws !== undefined && awa !== undefined) {
|
|
283
282
|
const awsKts = aws * 1.94384;
|
|
284
283
|
|
|
285
|
-
//
|
|
284
|
+
// RILEVAMENTO "LOG BLOCCATO" SUL SERVER
|
|
286
285
|
const hasStw = lastPathProcessTimes["navigation.speedThroughWater"] && (now - lastPathProcessTimes["navigation.speedThroughWater"] < 15000);
|
|
287
|
-
|
|
288
|
-
|
|
286
|
+
let speedRef = 0;
|
|
287
|
+
|
|
288
|
+
if (hasStw && stw !== undefined) {
|
|
289
|
+
if (sog > 0.77 && stw < 0.25) {
|
|
290
|
+
speedRef = sog; // Log sporco: forza il SOG
|
|
291
|
+
} else {
|
|
292
|
+
speedRef = stw;
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
speedRef = sog;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const speedKtsRef = speedRef * 1.94384;
|
|
289
299
|
|
|
290
300
|
const tw_water_x = awsKts * Math.cos(awa) - speedKtsRef;
|
|
291
301
|
const tw_water_y = awsKts * Math.sin(awa);
|
|
292
302
|
|
|
293
|
-
//
|
|
303
|
+
// Calcolo TWS
|
|
294
304
|
if (now - lastNativeTwsTime > 5000) {
|
|
295
305
|
const tws = Math.sqrt(tw_water_x * tw_water_x + tw_water_y * tw_water_y);
|
|
296
306
|
manageHistory('tws', tws);
|
|
297
307
|
}
|
|
298
308
|
|
|
299
309
|
const twa = Math.atan2(tw_water_y, tw_water_x);
|
|
300
|
-
|
|
301
|
-
// La VMG viene calcolata usando la velocità di riferimento (STW reale o SOG di fallback)
|
|
302
310
|
const vmg = Math.abs(speedKtsRef * Math.cos(twa));
|
|
303
311
|
manageHistory('vmg', vmg);
|
|
304
312
|
|
|
305
|
-
//
|
|
313
|
+
// Calcolo TWD stabile (Prua + TWA)
|
|
306
314
|
if (hdg !== undefined && (now - lastNativeTwdTime > 5000)) {
|
|
307
315
|
const twd = (hdg + twa + 2 * Math.PI) % (2 * Math.PI);
|
|
308
316
|
manageHistory('twd', twd);
|