Skip to content

Commit a6d8259

Browse files
MiguelPuntoEsclaude
andcommitted
test: pin frames outputs against an independent implementation
Round-trip tests cancel a consistently-applied sign error, the NAD83 test ran at the 2010.0 parameter epoch where the rate terms vanish, and the published-offset test sat at the origin where rotations contribute nothing — so a future sign or convention regression was undetectable. Added: absolute expected vectors computed with a separate Python implementation (NAD83 from EPSG:8970's raw Coordinate-Frame rotations, negated independently) at off-epoch dates with all terms active, a NAD83 secular-drift check (~1.8 cm/yr at Boulder), and the magnitude band tightened from 1.0–3.0 m to 1.4–1.7 m around the true 1.52 m. Verified by mutation: rate-sign, table-sign and formula-sign flips now each fail 1–4 tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 73c2212 commit a6d8259

1 file changed

Lines changed: 52 additions & 9 deletions

File tree

test/frames.test.ts

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const DELF: [number, number, number] = [
1414
3924687.7039, 301132.7618, 5001910.7712,
1515
];
1616

17+
/** Boulder-ish CONUS point for the NAD83 tests. */
18+
const BOULDER: [number, number, number] = [-1288398.0, -4721696.0, 4078625.0];
19+
1720
const dist = (
1821
a: readonly [number, number, number],
1922
b: readonly [number, number, number]
@@ -148,15 +151,55 @@ describe('transformFrame', () => {
148151
expect(d).toBeLessThan(0.8);
149152
});
150153

151-
it('NAD83(2011) offset from ITRF2014 is 1–2 m (CONUS)', () => {
152-
// Boulder-ish point; the NAD83 frame offset is ~2.2 m in total.
153-
const boulder: [number, number, number] = [
154-
-1288398.0, -4721696.0, 4078625.0,
155-
];
156-
const nad = transformFrame(boulder, 'ITRF2014', 'NAD83(2011)', 2010.0);
157-
const d = dist(nad, boulder);
158-
expect(d).toBeGreaterThan(1.0);
159-
expect(d).toBeLessThan(3.0);
154+
it('NAD83(2011) offset from ITRF2014 is ~1.52 m at Boulder (2010.0)', () => {
155+
const nad = transformFrame(BOULDER, 'ITRF2014', 'NAD83(2011)', 2010.0);
156+
const d = dist(nad, BOULDER);
157+
expect(d).toBeGreaterThan(1.4);
158+
expect(d).toBeLessThan(1.7);
159+
});
160+
161+
it('NAD83(2011) rates produce plate-scale secular motion (2010→2020)', () => {
162+
// At the 2010.0 parameter epoch the rate terms vanish, so this pair
163+
// of epochs is what actually exercises the CF→PV rate negation: a
164+
// NAD83-fixed point drifts through ITRF2014 with the North American
165+
// plate, ~1.8 cm/yr at Boulder.
166+
const at2010 = transformFrame(BOULDER, 'ITRF2014', 'NAD83(2011)', 2010.0);
167+
const at2020 = transformFrame(BOULDER, 'ITRF2014', 'NAD83(2011)', 2020.0);
168+
const drift = dist(at2010, at2020);
169+
expect(drift).toBeGreaterThan(0.1);
170+
expect(drift).toBeLessThan(0.25);
171+
});
172+
});
173+
174+
describe('pinned vectors (independent implementation)', () => {
175+
// Expected outputs computed with a separate Python implementation of
176+
// the time-dependent Helmert formula, fed the published parameters —
177+
// for NAD83 starting from EPSG:8970's RAW Coordinate-Frame rotations
178+
// with the CF→PV negation performed independently. Epochs are chosen
179+
// off the parameter epochs so every rate term is active, and the
180+
// points are far from the origin so every rotation term is active.
181+
// Round-trip tests cancel a consistently-applied sign error; these
182+
// pins do not.
183+
184+
it('ITRF2020→ITRF93 at 2020.0 (full rotation vector + all rates)', () => {
185+
const out = transformFrame(DELF, 'ITRF2020', 'ITRF93', 2020.0);
186+
expect(out[0]).toBeCloseTo(3924687.5144, 4);
187+
expect(out[1]).toBeCloseTo(301132.88, 4);
188+
expect(out[2]).toBeCloseTo(5001910.8085, 4);
189+
});
190+
191+
it('ITRF2014→NAD83(2011) at 2020.0 (CF→PV negation, rates active)', () => {
192+
const out = transformFrame(BOULDER, 'ITRF2014', 'NAD83(2011)', 2020.0);
193+
expect(out[0]).toBeCloseTo(-1288397.0668, 4);
194+
expect(out[1]).toBeCloseTo(-4721697.3057, 4);
195+
expect(out[2]).toBeCloseTo(4078625.1209, 4);
196+
});
197+
198+
it('ITRF2020→ETRF2000 at 2024.0 (two-edge path through ITRF2014)', () => {
199+
const out = transformFrame(DELF, 'ITRF2020', 'ETRF2000', 2024.0);
200+
expect(out[0]).toBeCloseTo(3924688.2277, 4);
201+
expect(out[1]).toBeCloseTo(301132.2184, 4);
202+
expect(out[2]).toBeCloseTo(5001910.3677, 4);
160203
});
161204
});
162205

0 commit comments

Comments
 (0)