PageSourceSearch

https://droneskycheck-d0136.web.app/js/fleet/flight-replay/wind-diagnostics-data-adapter.js?v=__VERSION__

js droneskycheck-d0136.web.app collected 2026-10-03 12:23:50 UTC 10,082 bytes, 257 lines download raw bytes

1(function () {
2  "use strict";
3
4  const CARDINALS = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"];
5  const SEGMENTS = {
6    hover: { color: "#2f80ed", label: "Stazionario" },
7    straightFlight: { color: "#27ae60", label: "Volo rettilineo" },
8    turn: { color: "#f2994a", label: "Virata" },
9    takeoff: { color: "#9b51e0", label: "Decollo" },
10    landing: { color: "#eb5757", label: "Atterraggio" },
11    unknown: { color: "#7f8fa6", label: "Non classificato" }
12  };
13
14  function toNumber(value) {
15    if (value === null || value === undefined || value === "") return null;
16    const number = Number(value);
17    return Number.isFinite(number) ? number : null;
18  }
19
20  function normalizeDirection(value) {
21    const direction = toNumber(value);
22    if (direction === null) return null;
23    return ((direction % 360) + 360) % 360;
24  }
25
26  function getDirectionToDeg(directionFromDeg) {
27    const direction = normalizeDirection(directionFromDeg);
28    return direction === null ? null : normalizeDirection(direction + 180);
29  }
30
31  function getCardinal(value) {
32    const direction = normalizeDirection(value);
33    if (direction === null) return null;
34    return CARDINALS[Math.round(direction / 22.5) % 16];
35  }
36
37  function getCircularDistanceDeg(left, right) {
38    const start = normalizeDirection(left);
39    const end = normalizeDirection(right);
40    if (start === null || end === null) return null;
41    return Math.abs(((((end - start) + 540) % 360) - 180));
42  }
43
44  function clamp(value, min, max) {
45    const number = toNumber(value);
46    if (number === null) return null;
47    return Math.max(min, Math.min(max, number));
48  }
49
50  function getAgreementPct(directionDifferenceDeg, speedDifferenceKmh) {
51    const directionDifference = toNumber(directionDifferenceDeg);
52    const speedDifference = toNumber(speedDifferenceKmh);
53    if (directionDifference === null || speedDifference === null) return null;
54    const directionScore = clamp(1 - (directionDifference / 90), 0, 1);
55    const speedScore = clamp(1 - (speedDifference / 20), 0, 1);
56    if (directionScore === null || speedScore === null) return null;
57    return Math.round(((directionScore * 0.62) + (speedScore * 0.38)) * 100);
58  }
59
60  function normalizeEstimate(wind = {}) {
61    if (!wind || typeof wind !== "object") return null;
62    const speed = toNumber(wind?.speed ?? wind?.speedKmh);
63    const rawDirectionToDeg = normalizeDirection(wind?.directionToDeg);
64    const finalFrom = normalizeDirection(wind?.directionFromDeg) ??
65      (rawDirectionToDeg === null ? null : normalizeDirection(rawDirectionToDeg + 180));
66    const directionToDeg = finalFrom === null ? rawDirectionToDeg : getDirectionToDeg(finalFrom);
67    if (speed === null || finalFrom === null || directionToDeg === null) return null;
68    return {
69      cardinalFrom: wind.cardinalFrom || getCardinal(finalFrom),
70      cardinalTo: wind.cardinalTo || getCardinal(directionToDeg),
71      confidence: toNumber(wind.confidence),
72      debug: wind.debug || null,
73      directionFromDeg: finalFrom,
74      directionToDeg,
75      speedKmh: speed
76    };
77  }
78
79  function normalizeHistoricalWind(wind = {}) {
80    return normalizeEstimate({
81      cardinalFrom: wind.cardinalFrom10m,
82      cardinalTo: wind.cardinalTo10m,
83      directionFromDeg: wind.directionFromDeg10m,
84      directionToDeg: wind.directionToDeg10m,
85      speed: wind.speedKmh10m
86    });
87  }
88
89  function getSegmentType(point = {}) {
90    return point.wind?.instant?.debug?.segmentType ||
91      point.wind?.debug?.segmentType ||
92      "unknown";
93  }
94
95  function getSegmentInfo(segmentType) {
96    return SEGMENTS[segmentType] || SEGMENTS.unknown;
97  }
98
99  function getWindowDebug(windowed) {
100    return windowed?.debug || {};
101  }
102
103  function getMedian(values = []) {
104    const numbers = values.map(toNumber).filter(value => value !== null).sort((left, right) => left - right);
105    if (!numbers.length) return null;
106    const middle = Math.floor(numbers.length / 2);
107    return numbers.length % 2 ? numbers[middle] : (numbers[middle - 1] + numbers[middle]) / 2;
108  }
109
110  function getLocalRows(rows = [], index = 0, radius = 2) {
111    return rows.slice(Math.max(0, index - radius), Math.min(rows.length, index + radius + 1));
112  }
113
114  function markOutliers(rows = []) {
115    rows.forEach((row, index) => {
116      const reasons = [];
117      const localRows = getLocalRows(rows, index).filter(candidate => candidate !== row);
118      const localSpeedMedian = getMedian(localRows.map(candidate => candidate.active?.speedKmh));
119      const speed = toNumber(row.active?.speedKmh);
120
121      if (speed !== null && localSpeedMedian !== null && Math.abs(speed - localSpeedMedian) >= 18) {
122        reasons.push("velocita incompatibile con i campioni vicini");
123      }
124
125      const previous = rows[index - 1];
126      const next = rows[index + 1];
127      const direction = toNumber(row.active?.directionFromDeg);
128      const previousJump = previous && direction !== null ? getCircularDistanceDeg(previous.active?.directionFromDeg, direction) : null;
129      const nextJump = next && direction !== null ? getCircularDistanceDeg(direction, next.active?.directionFromDeg) : null;
130      if ((previousJump !== null && previousJump >= 95) || (nextJump !== null && nextJump >= 95)) {
131        reasons.push("salto improvviso della direzione");
132      }
133
134      if (row.confidencePct !== null && row.confidencePct < 28) {
135        reasons.push("confidence molto bassa");
136      }
137
138      if (row.directionalDispersionDeg !== null && row.directionalDispersionDeg >= 75) {
139        reasons.push("dispersione elevata");
140      }
141
142      row.isOutlier = reasons.length > 0;
143      row.outlierReasons = reasons;
144      row.outlierSeverity = reasons.length >= 3 ? "high" : reasons.length >= 2 ? "medium" : reasons.length ? "low" : "none";
145    });
146    return rows;
147  }
148
149  function create(options = {}) {
150    const weatherService = options.weatherService || window.DSC_FlightWeatherService?.create?.();
151
152    async function load(points = []) {
153      let dataset = null;
154      try {
155        dataset = await weatherService?.loadFlightWeather?.(points);
156      } catch (error) {
157        dataset = { error, status: "failed" };
158      }
159      return build(points, dataset);
160    }
161
162    function build(points = [], dataset = null) {
163      const safePoints = Array.isArray(points) ? points : [];
164      const durationSec = Math.max(...safePoints.map(point => toNumber(point.elapsedSec) || 0), 1);
165      const rows = safePoints.map((point, index) => {
166        const date = weatherService?.getReplayDate?.(point, safePoints);
167        const weather = date && dataset ? weatherService?.getWeatherAt?.(dataset, date) : null;
168        const historical = weather?.wind ? normalizeHistoricalWind(weather.wind) : null;
169        const instant = normalizeEstimate(point.wind?.instant || point.wind);
170        const windowed = normalizeEstimate(point.wind?.windowed);
171        const solver = normalizeEstimate(point.wind?.solver);
172        const active = solver || windowed || instant;
173        const segmentType = getSegmentType(point);
174        const solverDebug = solver?.debug || {};
175        const windowDebug = getWindowDebug(windowed);
176        const solverConfidence = clamp(solver?.confidence, 0, 1);
177        const solverConfidencePct = solverConfidence === null
178          ? null
179          : Math.round(solverConfidence * 100);
180        const solverResidualError = toNumber(solverDebug.residualError);
181        const solverFit = solverResidualError === null
182          ? null
183          : clamp(1 - (solverResidualError / 1.8), 0, 1);
184        const solverFitPct = solverResidualError === null
185          ? null
186          : Math.round(solverFit * 100);
187        const speedDifferenceKmh = historical && active
188          ? Math.abs((historical.speedKmh || 0) - (active.speedKmh || 0))
189          : null;
190        const solverHistoricalDirectionDiffDeg = historical && solver
191          ? getCircularDistanceDeg(historical.directionFromDeg, solver.directionFromDeg)
192          : null;
193        const solverHistoricalSpeedDiffKmh = historical && solver
194          ? Math.abs((historical.speedKmh || 0) - (solver.speedKmh || 0))
195          : null;
196        const solverHistoricalAgreementPct = getAgreementPct(
197          solverHistoricalDirectionDiffDeg,
198          solverHistoricalSpeedDiffKmh
199        );
200
201        return {
202          active,
203          altitudeM: toNumber(point.altitude),
204          confidencePct: active?.confidence === null || active?.confidence === undefined ? null : Math.round(Math.max(0, Math.min(1, active.confidence)) * 100),
205          date,
206          directionalDispersionDeg: toNumber(windowDebug.directionalDispersionDeg),
207          durationSec,
208          elapsedSec: toNumber(point.elapsedSec) || 0,
209          historical,
210          index,
211          instant,
212          minimumDirectionDifferenceDeg: historical && active ? getCircularDistanceDeg(historical.directionFromDeg, active.directionFromDeg) : null,
213          point,
214          sampleCount: safePoints.length,
215          samplesUsed: toNumber(windowDebug.window?.samplesUsed),
216          segmentColor: getSegmentInfo(segmentType).color,
217          segmentLabel: getSegmentInfo(segmentType).label,
218          segmentType,
219          solver,
220          solverConfidencePct,
221          solverCost: toNumber(solverDebug.solverCost),
222          solverFitPct,
223          solverHistoricalAgreementPct,
224          solverHistoricalDirectionDiffDeg,
225          solverHistoricalSpeedDiffKmh,
226          solverResidualError,
227          solverStateId: solverDebug.stateId || null,
228          speedDifferenceKmh,
229          windowDurationSec: toNumber(windowDebug.window?.durationSec),
230          windowed
231        };
232      });
233      markOutliers(rows);
234
235      return {
236        dataset,
237        durationSec,
238        rows,
239        segments: SEGMENTS
240      };
241    }
242
243    return {
244      build,
245      getCardinal,
246      getCircularDistanceDeg,
247      load,
248      normalizeDirection,
249      normalizeEstimate
250    };
251  }
252
253  window.DSC_WindDiagnosticsDataAdapter = {
254    create,
255    segments: SEGMENTS
256  };
257})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.