mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(hud): carry rounded DMS seconds into the minutes (#1339)
Carry rounded HUD coordinate seconds into minutes and degrees. Contribution by @rudycelekli.
This commit is contained in:
1 parent
2a17491c8b
commit
a15fdd3134
5 files changed
+123
-6
No files matched your search
@@ -0,0 +1,3 @@
|
||||
- Carry HUD LAT/LON seconds that round up to 60.00" into the minutes and
|
||||
degrees, so longitude -122.35 reads `122°21'00.00"W` instead of
|
||||
`122°20'60.00"W` (rudycelekli, #1339).
|
||||
@@ -4,6 +4,12 @@ Docker Compose mounts the checkout with the shared SELinux `:z` label so
|
||||
containers can read it on enforcing SELinux hosts. The separate image-owned
|
||||
`node_modules` volume and loopback-only port publishing remain unchanged.
|
||||
|
||||
The HUD's LAT/LON degrees-minutes-seconds readout rounds the coordinate once, to
|
||||
hundredths of a second, and then splits it. A value within 0.005" of the next
|
||||
whole minute carries into the minutes and degrees, so `-122.35` reads
|
||||
`122°21'00.00"W` rather than `122°20'60.00"W`. The field widths and hemisphere
|
||||
letters are unchanged.
|
||||
|
||||
Aircraft detection candidates reuse normalized display text until callsign or
|
||||
registration changes, including in-place enrichment. Each candidate resolves its
|
||||
fleet model once per collection. Positions, model ownership and tracked-aircraft
|
||||
|
||||
@@ -1087,5 +1087,6 @@
|
||||
"scripts/qa-status-badges.mjs",
|
||||
"src/dockerTrustedPeers.test.mjs",
|
||||
"src/cameraGlobeRotation.test.mjs",
|
||||
"src/voice/globeResetConfirmation.test.mjs"
|
||||
"src/voice/globeResetConfirmation.test.mjs",
|
||||
"src/hudDms.test.mjs"
|
||||
]
|
||||
+6
-5
@@ -574,11 +574,12 @@ export class IntelHUD {
|
||||
* @returns {string} Formatted DMS string, e.g. `"38°53'23.10"N"`.
|
||||
*/
|
||||
_toDMS(decimal, type) {
|
||||
const abs = Math.abs(decimal);
|
||||
const deg = Math.floor(abs);
|
||||
const minFloat = (abs - deg) * 60;
|
||||
const min = Math.floor(minFloat);
|
||||
const sec = ((minFloat - min) * 60).toFixed(2);
|
||||
// Round once, in hundredths of a second, so a value that rounds up to
|
||||
// 60.00" carries into the minutes and degrees instead of printing 60.
|
||||
const hundredths = Math.round(Math.abs(decimal) * 360000);
|
||||
const deg = Math.floor(hundredths / 360000);
|
||||
const min = Math.floor(hundredths / 6000) % 60;
|
||||
const sec = ((hundredths % 6000) / 100).toFixed(2);
|
||||
|
||||
let dir;
|
||||
if (type === 'lat') dir = decimal >= 0 ? 'N' : 'S';
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// The HUD's LAT/LON readout in degrees-minutes-seconds. Seconds are printed to
|
||||
// two decimals, so a value within 0.005" of the next whole minute rounds up.
|
||||
// That carry must reach the minutes (and degrees): `122°20'60.00"W` is not a
|
||||
// coordinate. Ordinary decimal inputs land there through binary floating
|
||||
// point: 122.35 - 122 is 0.34999999999999432, so -122.35 read 20'60.00".
|
||||
//
|
||||
// hud.js imports `mgrs`, a CommonJS package whose named exports Node's ESM
|
||||
// loader cannot see, so this installs the same one-specifier stub as
|
||||
// hudAltitudeDatum.test.mjs before importing it.
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { registerHooks } from 'node:module';
|
||||
|
||||
const MGRS_STUB_URL = 'gev-test-stub:mgrs';
|
||||
registerHooks({
|
||||
resolve(specifier, context, next) {
|
||||
if (specifier === 'mgrs') return { url: MGRS_STUB_URL, shortCircuit: true };
|
||||
return next(specifier, context);
|
||||
},
|
||||
load(url, context, next) {
|
||||
if (url === MGRS_STUB_URL) {
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
source:
|
||||
'export function forward() { return "10SEG55776339"; }\nexport default { forward };\n',
|
||||
};
|
||||
}
|
||||
return next(url, context);
|
||||
},
|
||||
});
|
||||
|
||||
const { IntelHUD } = await import('./hud.js');
|
||||
|
||||
const toDMS = (decimal, type) => IntelHUD.prototype._toDMS(decimal, type);
|
||||
|
||||
test('a rounded-up second carries into the minutes', () => {
|
||||
assert.equal(toDMS(-122.35, 'lon'), `122°21'00.00"W`);
|
||||
assert.equal(toDMS(139.7, 'lon'), `139°42'00.00"E`);
|
||||
assert.equal(toDMS(37.0166666, 'lat'), `37°01'00.00"N`);
|
||||
});
|
||||
|
||||
test('a carry through 59 minutes reaches the degrees', () => {
|
||||
assert.equal(toDMS(38.999999, 'lat'), `39°00'00.00"N`);
|
||||
assert.equal(toDMS(-0.999999, 'lat'), `01°00'00.00"S`);
|
||||
assert.equal(toDMS(179.999999, 'lon'), `180°00'00.00"E`);
|
||||
});
|
||||
|
||||
test('ordinary values keep their existing format', () => {
|
||||
assert.equal(toDMS(38.889722, 'lat'), `38°53'23.00"N`);
|
||||
assert.equal(toDMS(-77.035278, 'lon'), `077°02'07.00"W`);
|
||||
assert.equal(toDMS(51.5, 'lat'), `51°30'00.00"N`);
|
||||
assert.equal(toDMS(-33.8568, 'lat'), `33°51'24.48"S`);
|
||||
assert.equal(toDMS(0, 'lon'), `000°00'00.00"E`);
|
||||
assert.equal(toDMS(90, 'lat'), `90°00'00.00"N`);
|
||||
});
|
||||
|
||||
test('the telemetry tick paints the carried value into both readouts', () => {
|
||||
const elements = new Map(
|
||||
[
|
||||
'hud-alt',
|
||||
'hud-summary',
|
||||
'hud-mgrs',
|
||||
'hud-latlon',
|
||||
'hud-bottom-line',
|
||||
'hud-gsd',
|
||||
'hud-coll',
|
||||
'hud-ona',
|
||||
'hud-mode',
|
||||
].map((id) => [id, { textContent: '' }]),
|
||||
);
|
||||
const previousDocument = globalThis.document;
|
||||
globalThis.document = {
|
||||
getElementById: (id) => elements.get(id) ?? null,
|
||||
querySelectorAll: () => [],
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
};
|
||||
const viewer = {
|
||||
camera: {
|
||||
pitch: -Math.PI / 2,
|
||||
positionCartographic: {
|
||||
latitude: (37.75 * Math.PI) / 180,
|
||||
longitude: (-122.35 * Math.PI) / 180,
|
||||
height: 1500,
|
||||
},
|
||||
computeViewRectangle: () => undefined,
|
||||
moveStart: { addEventListener() {}, removeEventListener() {} },
|
||||
moveEnd: { addEventListener() {}, removeEventListener() {} },
|
||||
},
|
||||
};
|
||||
let hud;
|
||||
try {
|
||||
hud = new IntelHUD(viewer);
|
||||
hud._updateCameraData();
|
||||
const latlon = elements.get('hud-latlon').textContent;
|
||||
const bottom = elements.get('hud-bottom-line').textContent;
|
||||
assert.equal(latlon, `37°45'00.00"N 122°21'00.00"W`);
|
||||
assert.match(bottom, /LON: 122°21'00\.00"W/);
|
||||
assert.doesNotMatch(`${latlon} ${bottom}`, /60\.00"/);
|
||||
} finally {
|
||||
hud?.destroy();
|
||||
if (previousDocument === undefined) delete globalThis.document;
|
||||
else globalThis.document = previousDocument;
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user