Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion src/cockpitMath.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,55 @@ export function cockpitGroundSafeHeight(
*/
export function cockpitAltitudeDisplayFt(altitudeM, onGround) {
if (onGround === true) return 0;
return Number.isFinite(altitudeM) ? altitudeM * 3.28084 : null;
return Number.isFinite(altitudeM) ? altitudeM / 0.3048 : null;
}

/** Round to clean IEEE-754 decimal precision without float representation noise. */
function cleanPrecision(value, decimals = 6) {
const factor = 10 ** decimals;
return Math.round(value * factor) / factor;
}

/** Convert Celsius to Fahrenheit with NaN/non-finite guard and IEEE-754 precision smoothing. */
export function celsiusToFahrenheit(c) {
if (!Number.isFinite(c)) return null;
return cleanPrecision((c * 9) / 5 + 32);
}

/** Convert Fahrenheit to Celsius with NaN/non-finite guard and IEEE-754 precision smoothing. */
export function fahrenheitToCelsius(f) {
if (!Number.isFinite(f)) return null;
return cleanPrecision(((f - 32) * 5) / 9);
}

/** Convert Celsius to Kelvin, clamping unphysical temperatures below absolute zero. */
export function celsiusToKelvin(c) {
if (!Number.isFinite(c)) return null;
return cleanPrecision(Math.max(0, c + 273.15));
}

/** Convert hectopascals (hPa) to inches of mercury (inHg). */
export function hpaToInHg(hpa) {
if (!Number.isFinite(hpa) || hpa < 0) return null;
return cleanPrecision(hpa * 0.029529983071445, 4);
}

/** Convert inches of mercury (inHg) to hectopascals (hPa). */
export function inHgToHpa(inHg) {
if (!Number.isFinite(inHg) || inHg < 0) return null;
return cleanPrecision(inHg / 0.029529983071445, 2);
}

/** Convert knots to meters per second. */
export function knotsToMps(knots) {
if (!Number.isFinite(knots) || knots < 0) return null;
return cleanPrecision(knots * 0.514444);
}

/** Convert meters per second to knots. */
export function mpsToKnots(mps) {
if (!Number.isFinite(mps) || mps < 0) return null;
return cleanPrecision(mps / 0.514444);
}

/** Format the cockpit Context scope without overstating installation coverage. */
Expand Down
25 changes: 25 additions & 0 deletions src/cockpitMath.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import {
bearingBetweenCoordinates,
cockpitAnchorCorrectionStep,
cockpitAltitudeDisplayFt,
celsiusToFahrenheit,
fahrenheitToCelsius,
celsiusToKelvin,
hpaToInHg,
inHgToHpa,
knotsToMps,
mpsToKnots,
cockpitGroundSafeHeight,
cockpitSurfaceWaitExpired,
cockpitUiUpdateDue,
Expand Down Expand Up @@ -364,3 +371,21 @@ test('tracked-identity resolution falls back to layer precedence', () => {
assert.equal(resolveTrackedAircraftInfo({ trackedId: 'flights:aaa077' }), null);
assert.equal(resolveTrackedAircraftInfo(), null);
});

test('telemetry unit conversions maintain exact precision and guard invalid values', () => {
// Exact foot conversion avoids 0.00032 drift on FL100
assert.equal(cockpitAltitudeDisplayFt(3048, false), 10000);

// Temperature conversions
assert.equal(celsiusToFahrenheit(0), 32);
assert.equal(celsiusToFahrenheit(100), 212);
assert.equal(fahrenheitToCelsius(212), 100);
assert.equal(celsiusToKelvin(-300), 0); // clamped at absolute zero
assert.equal(celsiusToFahrenheit(Number.NaN), null);

// Pressure and speed round-trip accuracy
assert.equal(inHgToHpa(hpaToInHg(1013.25)), 1013.25);
assert.equal(hpaToInHg(-5), null);
assert.ok(Math.abs(mpsToKnots(knotsToMps(250)) - 250) < 1e-5);
assert.equal(knotsToMps(-1), null);
});
Loading