Repository navigation
Conversation
FieldOfViewParams gains an optional `interpolate` flag (default false, so existing step-sector masks and catalog JSON keep today's behaviour). When true, each ElevationMask entry is a vertex at (startAz, minEl) joined linearly to the next vertex, wrapping at 360 deg; stopAz is ignored. This matches SODA's piecewise-linear horizon mask semantics. - Vertices are sorted once in the constructor; getMinElevation does a binary search and never sorts or allocates. - New pure maskMinElevation(masks, az, interpolate, floorEl) is the single implementation, shared with KeepTrack's legacy sensor path. - Effective floor is max(minElevation, mask(az)). - Validation for interpolated masks: 2 to 72 vertices, 0 <= az < 360, 0 <= el <= 89, unique azimuths; throws ValidationError. - serialize() and toString() carry the flag.
The local eslint/tsup DTS failures were a stale node_modules. After rm -rf node_modules && pnpm install --frozen-lockfile, eslint and the tsup DTS build both pass unchanged, so eslint stays (no biome switch). - tsconfig.base.json and test/integration/tsconfig.json: types ["jest", "node"] -> ["node", "vitest/globals"]. 157 of 164 test files use describe/it/expect as globals (vitest globals: true), so the vitest/globals types are required. - Remove @types/jest from devDependencies and the lockfile. - Fix the 12 tsc errors in src unit tests that surfaced: jest fail() -> expect.fail(), missing required name on TransmitterParams / ReceiverParams, zero-valued Degrees casts on Radians parameters, mockImplementation() arity, Vector3D unit casts. - tsconfig.json excludes test/integration, which has its own tsconfig resolving "ootk" to dist. That project still reports 99 pre-existing type errors (unit-brand drift, e.g. Degrees passed to Radians fields); not addressed here. Fresh worktree at this tree (git worktree add <scratch> HEAD): pnpm install --frozen-lockfile: exit 0, Done in 10.2s pnpm lint: exit 0, 0 errors, 35 warnings (same as before) pnpm build: exit 0, ESM/CJS/DTS build success pnpm exec tsc --noEmit -p tsconfig.json: exit 0, 0 errors pnpm test: exit 0, 138 passed | 1 skipped files, 2230 passed | 20 skipped (the skipped file needs the gitignored Sgp4Prop*.wasm binaries)
contains() now checks range, the global minimum elevation, the cone, and only then the azimuth mask (skipped entirely when there are no masks). The checks are conjunctive and the mask never drops below minElevation, so results are unchanged. containsDirection() never consults the mask, and VisualizationHelpers uses getMinElevation to clamp mesh points, not as a containment test, so neither needed the reorder. FieldOfView.mask.test.ts gains a hand-derived boolean table on a masked cone (outside the cone above the mask, inside the cone below the mask, below the global floor, on the mask boundary, behind, out of range). It passes against both the old and the new ordering. Timed loop, not committed: 72-vertex interpolated mask, cone boresight az 90 el 30 half-angle 20, 1e6 seeded random RAE points with 90% outside the cone, 3 warm-up passes, median of 7, tsx on Node 22, three runs each: before: 92.0 / 92.0 / 94.9 ms after: 63.4 / 63.1 / 62.2 ms
SwathGeometry gives the per-sample frame (geodetic up, along, right), the WGS84 local radius and horizon half-angle, swath width to FOV conversion, ray/ellipsoid hits, horizon-clamped cross-track edges (plus an allocation-free variant for per-frame use), the sub-point daylight test and the lit/unlit run splitter with shared boundary samples.
Inventory of `tsc --noEmit -p test/integration/tsconfig.json` against the built dist before this series (99 errors): | File | Unit | Null | Missing field | Other | Total | | ------------------------ | ---- | ---- | ------------- | ----- | ----- | | lambert.test.ts | 25 | 15 | 0 | 0 | 40 | | orbital-elements.test.ts | 24 | 0 | 0 | 0 | 24 | | maneuvers.test.ts | 12 | 0 | 0 | 0 | 12 | | doppler.test.ts | 0 | 6 | 0 | 0 | 6 | | observations.test.ts | 0 | 6 | 0 | 0 | 6 | | sensor-fov.test.ts | 0 | 0 | 3 | 3 | 6 | | iod.test.ts | 0 | 4 | 0 | 0 | 4 | | moon.test.ts | 0 | 0 | 0 | 1 | 1 | | Total | 61 | 31 | 3 | 4 | 99 | Unit: Degrees passed as Radians, plain number passed as Kilometers or KilometersPerSecond. Null: T | null or T | undefined used unchecked. Missing field: object literal uses a FieldOfViewParams key that no longer exists. Other: string id where number is typed (3), instantiating an abstract class (1). This commit: - lib/assert.ts: add expectNotNull, a real assertion (not.toBeNull and toBeDefined) that narrows T | null. toBeDefined alone passes on null, so the old checks on LambertIOD.estimate and friends were vacuous. - lambert: Vector3D<Kilometers>(n, n, n) becomes Vector3D(n as Kilometers, ...) on literals already in km; circular speed sqrt(mu / r1) is km/s by construction. estimate() and Satellite.lla() results go through expectNotNull. The multi-rev loop keeps its `if (solution)` because nRev 1 and 2 may have no solution. "should handle close positions" now requires a non-null solution (it was toBeDefined, which a null passed). - iod, doppler: estimate(), applyDoppler() and dopplerFactor() results go through expectNotNull. No expected values changed; all tests in these files pass. Remaining integration tsc errors after this commit: 49.
- maneuvers, orbital-elements: ClassicalElements takes Radians, but the tests passed degree literals branded as Degrees (inclination 28.5, argPerigee 270 and so on were read as radians). Inputs are now `(n * DEG2RAD) as Radians`; zeros are `0 as Radians`. Every assertion in these tests depends only on semimajor axis, eccentricity, period, or radius at true anomaly 0, which do not depend on the angles, so no expected value changed. - observations: RadecTopocentric/RadecGeocentric.range is Kilometers | undefined and GroundStation.rae() is nullable; both go through expectNotNull before use. - moon: GroundObject is abstract; the observer is now a GroundStation with the same lat, lon, alt and name. - sensor-fov: the PhasedArrayRadar fixtures used the removed min/max azimuth and elevation FieldOfView keys and a string id. id is now 1 (typed number). The old window (az 0-360, el 10-85) becomes the hemisphere form of the current cone API: halfAngle 80 about the zenith boresight default with minElevation 10. The 85 deg upper limit has no cone equivalent and is dropped. Before this, halfAngle was undefined at runtime, so the cone was NaN; the assertions (minElevation 10, maxRange 5556, canObserve returns a boolean, getRae is defined) are unchanged and pass. Previously-wrong tests (expected value changed): none. The unit fixes changed inputs that no assertion reads. TODO(api) casts: none. RadecGeocentric.fromDegrees (rates typed Degrees) is not called by any integration test. Integration tsc errors: 99 before this series, 0 after.
- package.json: add `typecheck` (tsc -p tsconfig.json) and `typecheck:integration` (tsc -p test/integration/tsconfig.json, which resolves ootk to dist). `test:integration` now runs build, then typecheck:integration, then vitest. - ci.yml: the test-integration job (the one that downloads the dist artifact) runs typecheck:integration before test:integration:only. Gates, fresh worktree at this commit (pnpm install --frozen-lockfile first): pnpm lint: exit 0 (0 errors, 35 warnings) pnpm build: exit 0 pnpm exec tsc --noEmit -p tsconfig.json: exit 0 pnpm typecheck:integration: exit 0 pnpm test: exit 0 (2255 passed, 20 skipped of 2276) pnpm test:integration:only: exit 0 (291 passed, 3 skipped of 294) `pnpm test` has a pre-existing intermittent failure under the full parallel run (a vitest forks worker exits unexpectedly, or ConjunctionAssessment.highfidelity throws "y2.add is not a function" in RungeKuttaAdaptive). It reproduces at f4eaa92 (1 of 3 runs) and the file passes 3 of 3 runs alone. Not addressed here.
Vitest defaults to one fork per logical core, which is 31 workers on the 32-thread Windows dev box. That initial burst can exceed what Windows will commit, and forks die intermittently with V8 "Zone Allocation failed - process out of memory". An intermittent ootk worker crash matched this; a 30-run investigation found no cause in the code itself. Cap maxWorkers at min(12, availableParallelism - 1) in both the unit and integration configs (the integration config does not inherit the root one). This is the same cap the keeptrack-space parent applies in its vitest.config.ts for the same crash. CI runners (2-4 cores) are unaffected.
Fixes the silently wrong numbers found in the 2026-10-13 QA deep dive across operations, time, transforms, covariance, sgp4, objects, orbit determination, conjunction, sensor and parsers, and moves the qa proof tests into the module __tests__ folders as permanent regression tests. BREAKING CHANGES: mag2db is renamed pow2db and Lambert posigrade is renamed shortWay (no aliases); cappedScreeningCovarianceFromTle returns RIC variances in R, I, C order and its caps are sigma caps; chanPc2D is corrected and the secondary covariance is rotated into the primary RIC frame; estimateRcs returns m2 and estimateVmagFromRcs uses the corrected constant (1 m2 is about +7.9); at boresight elevation 90 the roll angle is measured from the boresight azimuth (FieldOfView.migrateZenithRollFromV7 converts v7 configs); fovFromSwathWidth returns NaN past the horizon; Satellite and Tle apogee/perigee are altitudes above 6378.137 km; sccNum5/sccNum6 keep leading zeros and sccNum6 is 6 wide; CDM parse/export is in metres; ScreeningFilter takes a shell pad in km (useInclinationFilter and shellsOverlapWithInclination removed); Matrix.inverse throws SingularMatrixError; FieldOfView requires halfAngle; Beacon.getNextTransmission returns the next window only. See CHANGELOG for the migration section.
…act snapshots The pre-push suite failed on a 4e-12 km last-ulp difference in driftDegreesToSma: x ** (1 / 3) is not bit-stable across V8 JIT tiers. Math.cbrt is, and the three tests now check to 1 mm instead of snapshotting every digit.
…e with location and dispersion helpers
…al normal integral, analytic Pc bound and absolute error floor normalIntervalProbability_ lost precision to erfc cancellation on chords much narrower than sigma, so the 1e-12 relative tolerance was unreachable and the adaptive rule bisected to depth 30 (one Pc of 2.9e-7 took 259 s). Narrow intervals now integrate the density with one Kronrod rule; an analytic upper bound returns 0 below PC_NEGLIGIBLE (1e-30); panels stop at an absolute error floor of 1e-16 of the running total.
…one in final windows and floor flat-valley sigma
…or near-circular targets
A fixed FOV from one altitude makes the ground width follow the height above the surface, which changes by about 21 km between the equator and the poles on a near-circular orbit. This re-solves the look half-angle at each position on the local WGS84 radius, so a published swath width holds at every latitude.
… standard magnitude
…h valid checksums
…itest dev tooling)
…t like its neighbours
|
🎉 This PR is included in version 7.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request adds a type-checking step for integration tests to the continuous integration workflow. This ensures that type errors in integration tests are caught earlier in the CI process.
Type-check integration testsstep to.github/workflows/ci.ymlto runpnpm run typecheck:integrationbefore executing integration tests.