space-data-module-sdk 0.8.16 → 0.8.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/bin/space-data-module.js +24 -6
  2. package/docs/browser-wasmedge-isomorphic.html +1 -1
  3. package/docs/byo-wasm-quickstart.html +1 -1
  4. package/docs/conformance.html +10 -4
  5. package/docs/conformance.md +20 -4
  6. package/docs/events-abi.html +1 -1
  7. package/docs/families/analytics.html +1 -1
  8. package/docs/families/attitude.html +1 -1
  9. package/docs/families/behavior.html +1 -1
  10. package/docs/families/breakup.html +1 -1
  11. package/docs/families/conjunction.html +1 -1
  12. package/docs/families/data-source.html +7 -2
  13. package/docs/families/effects.html +1 -1
  14. package/docs/families/environment.html +1 -1
  15. package/docs/families/estimation.html +56 -54
  16. package/docs/families/estimation.md +155 -125
  17. package/docs/families/gnc.html +1 -1
  18. package/docs/families/maneuver.html +1 -1
  19. package/docs/families/obstruction.html +1 -1
  20. package/docs/families/propagator.html +16 -2
  21. package/docs/families/propulsion.html +1 -1
  22. package/docs/families/reentry.html +1 -1
  23. package/docs/families/rf.html +1 -1
  24. package/docs/families/scheduler.html +1 -1
  25. package/docs/families/sensor.html +1 -1
  26. package/docs/families/signature.html +1 -1
  27. package/docs/flatsql-host-contract.html +1 -1
  28. package/docs/flatsql-streaming-standard.html +1 -1
  29. package/docs/gpu-module-abi.html +1 -1
  30. package/docs/harness-family-matrix.md +7 -7
  31. package/docs/index.html +2 -2
  32. package/docs/isomorphic-pthreads.html +1 -1
  33. package/docs/isomorphic-sdn-runtime-plan.html +1 -1
  34. package/docs/language-runtime-matrix.html +1 -1
  35. package/docs/llms.txt +1 -1
  36. package/docs/module-bundle-runtime-plan.html +1 -1
  37. package/docs/module-publication-standard.html +1 -1
  38. package/docs/node-red-default-node-parity.html +1 -1
  39. package/docs/propagator-abi.md +52 -0
  40. package/docs/protect-and-sign.html +1 -1
  41. package/docs/protocol-installation.html +1 -1
  42. package/docs/provider-access-abi.md +25 -0
  43. package/docs/publication-submission.html +1 -1
  44. package/docs/secrets-capability.html +1 -1
  45. package/docs/testing-harness.html +1 -1
  46. package/docs/tri-runtime-parity-gate.html +1 -1
  47. package/docs/tri-runtime-parity.html +1 -1
  48. package/include/orbpro/orbpro_estimation_abi.h +883 -0
  49. package/include/orbpro/orbpro_propagator_abi.h +58 -0
  50. package/package.json +4 -2
  51. package/schemas/orbpro/Estimation.fbs +244 -0
  52. package/schemas/orbpro/Propagator.fbs +64 -0
  53. package/src/conformance/estimationReference.js +81 -0
  54. package/src/conformance/estimationSuite.js +134 -0
  55. package/src/conformance/index.js +39 -2
  56. package/src/generated/orbpro/propagator-abi.js +21 -0
  57. package/src/generated/orbpro/propagator-abi.ts +50 -0
  58. package/src/standards/sharedCatalog.js +7 -0
  59. package/templates/estimation-module/README.md +24 -0
  60. package/templates/estimation-module/build.mjs +32 -0
  61. package/templates/estimation-module/package.json +12 -0
  62. package/templates/estimation-module/plugin-manifest.json +34 -0
  63. package/templates/estimation-module/src/reference_estimation.cpp +53 -0
@@ -19,6 +19,8 @@ import path from "node:path";
19
19
 
20
20
  import { loadPropagatorArtifact } from "./abiDriver.js";
21
21
  import { computeVerdict, runPropagatorSuite } from "./propagatorSuite.js";
22
+ import { runEstimationSuite } from "./estimationSuite.js";
23
+ import { makeEstimationReferenceEvidence } from "./estimationReference.js";
22
24
 
23
25
  export { ErrorCode, REQUIRED_ABI_EXPORTS } from "./abiDriver.js";
24
26
  export {
@@ -34,14 +36,14 @@ export { runPropagatorSelfTest, formatSelfTestReport } from "./selfTest.js";
34
36
  * never coerced — the ANALYSIS fallback was the namespace corruption the
35
37
  * finding killed.
36
38
  */
37
- export const CONFORMANCE_FAMILIES = Object.freeze(["propagator"]);
39
+ export const CONFORMANCE_FAMILIES = Object.freeze(["estimation", "propagator"]);
38
40
 
39
41
  export class UnknownConformanceFamilyError extends Error {
40
42
  constructor(family) {
41
43
  super(
42
44
  `no conformance kit for family "${family}" — kits exist for: ` +
43
45
  `${CONFORMANCE_FAMILIES.join(", ")}. A family with no kit can never be CORE ` +
44
- "(finding §5); maneuver is Wave 2 (EXPERIMENTAL), OD is deferred by ruling.",
46
+ "(finding §5); maneuver remains Wave 2.",
45
47
  );
46
48
  this.name = "UnknownConformanceFamilyError";
47
49
  this.family = family;
@@ -93,6 +95,23 @@ export async function runConformance(options) {
93
95
  if (!CONFORMANCE_FAMILIES.includes(family)) {
94
96
  throw new UnknownConformanceFamilyError(options.family);
95
97
  }
98
+ if (family === "estimation") {
99
+ const evidence = options.evidence ?? JSON.parse(
100
+ await fs.readFile(path.resolve(options.evidencePath), "utf8"),
101
+ );
102
+ const evaluated = runEstimationSuite(evidence);
103
+ let artifact = evidence.artifact ?? null;
104
+ if (options.artifactPath) {
105
+ const artifactPath = path.resolve(options.artifactPath);
106
+ const artifactBytes = await fs.readFile(artifactPath);
107
+ artifact = {
108
+ path: artifactPath,
109
+ sha256: crypto.createHash("sha256").update(artifactBytes).digest("hex"),
110
+ };
111
+ }
112
+ return { family, artifact, corpus: null, evidence: options.evidencePath ?? null, ...evaluated };
113
+ }
114
+
96
115
  const artifactPath = path.resolve(options.artifactPath);
97
116
  const artifactBytes = await fs.readFile(artifactPath);
98
117
  const artifactSha256 = crypto
@@ -129,6 +148,24 @@ export async function runConformance(options) {
129
148
  };
130
149
  }
131
150
 
151
+ export { runEstimationSuite } from "./estimationSuite.js";
152
+ export { makeEstimationReferenceEvidence } from "./estimationReference.js";
153
+
154
+ export async function runEstimationSelfTest() {
155
+ const controls = [];
156
+ for (const [id, mutate] of [
157
+ ["runtime-divergence", (e) => { e.runtime.threads["8"] = "different"; }],
158
+ ["ekf-ukf-alias", (e) => { e.filter.ekfUkfCovarianceDifference = 0; }],
159
+ ["covariance-placeholder", (e) => { e.invariants.actualOcmCovariance = false; }],
160
+ ]) {
161
+ const evidence = makeEstimationReferenceEvidence();
162
+ mutate(evidence);
163
+ const report = runEstimationSuite(evidence);
164
+ controls.push({ id, caught: report.verdict === "FAIL" });
165
+ }
166
+ return { ok: controls.every((control) => control.caught), controls };
167
+ }
168
+
132
169
  export function formatConformanceReport(report) {
133
170
  const lines = [];
134
171
  lines.push(`conformance ${report.family} — ${report.verdict}`);
@@ -47,6 +47,27 @@ export const StateFlags = Object.freeze({
47
47
  HAS_COVARIANCE: 32,
48
48
  });
49
49
 
50
+ export const ErrorCode = Object.freeze({
51
+ OK: 0,
52
+ NOT_INITIALIZED: -1,
53
+ BAD_ENTITY_INDEX: -2,
54
+ NULL_OUTPUT: -3,
55
+ BAD_INPUT: -4,
56
+ NOT_CONVERGED: -5,
57
+ UNPHYSICAL: -6,
58
+ UNSUPPORTED_FORMAT: -7,
59
+ EPOCH_OUT_OF_RANGE: -8,
60
+ });
61
+
62
+ export const EphemerisFormat = Object.freeze({
63
+ AUTO: 0,
64
+ CCSDS_OEM_KVN: 1,
65
+ SPK_DAF: 2,
66
+ CODE500: 3,
67
+ STK_EPHEMERIS: 4,
68
+ SP3_D: 5,
69
+ });
70
+
50
71
  export const ORBPRO_STATE_VECTOR = Object.freeze({
51
72
  name: "StateVector",
52
73
  cName: "OrbProStateVector",
@@ -53,6 +53,56 @@ export enum StateFlags {
53
53
  HAS_COVARIANCE = 32,
54
54
  }
55
55
 
56
+ /**
57
+ * The negative return codes every propagator export shares.
58
+ *
59
+ * These were documented in docs/propagator-abi.md and declared NOWHERE, so
60
+ * every module hand-typed them as literals and the contract lived only in
61
+ * prose. A propagator that returns -1 for everything is unconformable — the
62
+ * host cannot tell a bad entity index from an uninitialized module, so it
63
+ * cannot place the failure on the degradation ladder — and the cheapest way
64
+ * to end up there is having no names to return.
65
+ *
66
+ * VALUES ARE FROZEN. New classes are appended downward.
67
+ */
68
+ export enum ErrorCode {
69
+ OK = 0,
70
+ NOT_INITIALIZED = -1,
71
+ BAD_ENTITY_INDEX = -2,
72
+ NULL_OUTPUT = -3,
73
+ BAD_INPUT = -4,
74
+ NOT_CONVERGED = -5,
75
+ UNPHYSICAL = -6,
76
+ UNSUPPORTED_FORMAT = -7,
77
+ EPOCH_OUT_OF_RANGE = -8,
78
+ }
79
+
80
+ /**
81
+ * The container an ephemeris-driven propagator was handed on
82
+ * `plugin_init_ephemeris`. A format is DATA, not identity: one ephemeris
83
+ * provider reads every member below behind this discriminator rather than
84
+ * shipping one artifact per container, because the interpolation, the time
85
+ * scale handling and the frame rotation are shared and splitting them
86
+ * re-creates the five-mirror drift this file exists to end.
87
+ *
88
+ * AUTO is the honest default: every container here is self-identifying in its
89
+ * first bytes (`DAF/SPK ` / `NAIF/DAF`, the OEM version keyword, the STK
90
+ * `stk.v.` banner, the SP3 `#d` line, the Code-500 fixed header block), so a
91
+ * caller that does not know what it fetched says so rather than guessing. A
92
+ * module that cannot identify the bytes returns UNSUPPORTED_FORMAT; it never
93
+ * falls back to a default container.
94
+ *
95
+ * VALUES ARE FROZEN AND ARE NEVER RENUMBERED. New containers are APPENDED.
96
+ */
97
+ export enum EphemerisFormat {
98
+ AUTO = 0,
99
+ CCSDS_OEM_KVN = 1,
100
+ SPK_DAF = 2,
101
+ CODE500 = 3,
102
+ STK_EPHEMERIS = 4,
103
+ SP3_D = 5,
104
+ }
105
+
56
106
  /** One field's placement inside an ABI struct. */
57
107
  export interface AbiField {
58
108
  readonly offset: number;
@@ -219,6 +219,13 @@ const sharedModuleCatalogEntries = [
219
219
  fileIdentifier: "PRST",
220
220
  source: "orbpro-propagator",
221
221
  },
222
+ {
223
+ schemaCode: "ESTIMATION",
224
+ schemaName: "Estimation.fbs",
225
+ fileIdentifier: "$EST",
226
+ rootTypeName: "EstimationEnvelope",
227
+ source: "orbpro-estimation",
228
+ },
222
229
  {
223
230
  schemaCode: "ACCESSWINDOWREQUEST",
224
231
  schemaName: "AccessWindowRequest.fbs",
@@ -0,0 +1,24 @@
1
+ # Estimation reference module
2
+
3
+ This is the force-model-blind reference implementation for the fixed
4
+ estimation ABI. It demonstrates one covariance-form position update using an
5
+ `OrbProEstimationPropagatorSample` supplied by the caller. It never propagates
6
+ an orbit and never names a provider: production estimators obtain state and
7
+ STM samples from the caller-selected `plugin_propagate` /
8
+ `plugin_compute_stm` port.
9
+
10
+ The reference export is:
11
+
12
+ ```c
13
+ int32_t orbpro_estimation_reference_position_update(
14
+ const OrbProEstimationConfig*,
15
+ const OrbProEstimationObservation*,
16
+ const OrbProEstimationPropagatorSample*,
17
+ OrbProEstimationState*);
18
+ ```
19
+
20
+ It accepts only a three-lane `POSITION_VECTOR` observation. That narrow shape
21
+ is deliberate: the reference proves ABI layout, covariance update, typed
22
+ refusal and deterministic bytes. The estimation conformance kit adjudicates
23
+ full modules against Orekit, Vallado, media, Monte-Carlo and runtime evidence.
24
+ It is not a second JS physics implementation.
@@ -0,0 +1,32 @@
1
+ import fs from "node:fs/promises";
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import { compileModuleFromSource } from "space-data-module-sdk/compiler";
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const root = fileURLToPath(new URL(".", import.meta.url));
10
+ const manifest = JSON.parse(await fs.readFile(path.join(root, "plugin-manifest.json"), "utf8"));
11
+ const source = await fs.readFile(path.join(root, "src/reference_estimation.cpp"), "utf8");
12
+ const propagatorHeader = await fs.readFile(
13
+ require.resolve("space-data-module-sdk/include/orbpro/orbpro_propagator_abi.h"), "utf8");
14
+ const estimationHeader = (await fs.readFile(
15
+ require.resolve("space-data-module-sdk/include/orbpro/orbpro_estimation_abi.h"), "utf8"))
16
+ .replace('#include "orbpro/orbpro_propagator_abi.h"', "");
17
+ const translationUnit = source
18
+ .replace('#include "orbpro/orbpro_propagator_abi.h"', propagatorHeader)
19
+ .replace('#include "orbpro/orbpro_estimation_abi.h"', estimationHeader);
20
+ const outputPath = path.join(root, "dist/isomorphic/module.wasm");
21
+ await fs.rm(path.join(root, "dist"), { recursive: true, force: true });
22
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
23
+ const result = await compileModuleFromSource({
24
+ manifest,
25
+ sourceCode: translationUnit,
26
+ language: "c++",
27
+ outputPath,
28
+ threadModel: manifest.threadModel,
29
+ });
30
+ if (!result.report?.ok) throw new Error(JSON.stringify(result.report?.issues ?? [], null, 2));
31
+ await fs.copyFile(path.join(root, "plugin-manifest.json"), path.join(root, "dist/plugin-manifest.json"));
32
+ console.log(`Built ${path.relative(root, outputPath)} with ${result.compiler}`);
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "space-data-network-module-estimation-reference",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "node build.mjs"
8
+ },
9
+ "dependencies": {
10
+ "space-data-module-sdk": "^0.8.16"
11
+ }
12
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "pluginId": "com.orbpro.estimation.reference",
3
+ "name": "Estimation ABI Reference",
4
+ "version": "0.1.0",
5
+ "description": "Force-model-blind covariance update reference for the ratified estimation ABI.",
6
+ "pluginFamily": "analysis",
7
+ "capabilities": [],
8
+ "externalInterfaces": [],
9
+ "invokeSurfaces": ["direct"],
10
+ "runtimeTargets": ["browser", "wasmedge"],
11
+ "threadModel": "wasi-sequential",
12
+ "sequentialJustification": {
13
+ "kind": "inherently-sequential-algorithm",
14
+ "detail": "One six-state covariance update is order-dependent and small; callers parallelize independent tracks. The artifact still uses the sanctioned wasm32-wasip1-threads compiler lane."
15
+ },
16
+ "methods": [
17
+ {
18
+ "methodId": "abi_probe",
19
+ "displayName": "ABI Probe",
20
+ "description": "Returns success after the artifact has instantiated; estimation data uses the fixed direct export documented in README.md.",
21
+ "inputPorts": [],
22
+ "outputPorts": [],
23
+ "maxBatch": 1,
24
+ "drainPolicy": "single-shot"
25
+ }
26
+ ],
27
+ "schemasUsed": [
28
+ {"schemaName": "Estimation.fbs", "fileIdentifier": "$EST", "rootTypeName": "EstimationEnvelope"}
29
+ ],
30
+ "buildArtifacts": [
31
+ {"artifactId": "estimation-reference-isomorphic", "kind": "wasm", "path": "dist/isomorphic/module.wasm", "target": "browser,wasmedge"}
32
+ ],
33
+ "abiVersion": 1
34
+ }
@@ -0,0 +1,53 @@
1
+ #include "orbpro/orbpro_propagator_abi.h"
2
+ #include "orbpro/orbpro_estimation_abi.h"
3
+
4
+ #include <cmath>
5
+ #include <cstdint>
6
+
7
+ #if defined(__wasm__)
8
+ #define ESTIMATION_EXPORT(name) __attribute__((export_name(name))) extern "C"
9
+ #else
10
+ #define ESTIMATION_EXPORT(name) extern "C"
11
+ #endif
12
+
13
+ extern "C" int abi_probe(void) { return 0; }
14
+
15
+ ESTIMATION_EXPORT("orbpro_estimation_reference_position_update")
16
+ int32_t orbpro_estimation_reference_position_update(
17
+ const OrbProEstimationConfig* config,
18
+ const OrbProEstimationObservation* observation,
19
+ const OrbProEstimationPropagatorSample* sample,
20
+ OrbProEstimationState* output) {
21
+ if (config == nullptr || observation == nullptr || sample == nullptr || output == nullptr) {
22
+ return ORBPRO_ESTIMATION_NULL_OUTPUT;
23
+ }
24
+ if (observation->kind != ORBPRO_MEASUREMENT_POSITION_VECTOR ||
25
+ observation->value_count != 3) {
26
+ return ORBPRO_ESTIMATION_BAD_INPUT;
27
+ }
28
+
29
+ orbpro_estimation_state_init(output);
30
+ output->epoch[0] = sample->epoch[0];
31
+ output->epoch[1] = sample->epoch[1];
32
+ for (int axis = 0; axis < 6; ++axis) {
33
+ output->state[axis] = sample->state[axis];
34
+ for (int column = 0; column < 6; ++column) {
35
+ output->covariance[axis * 6 + column] = config->initial_covariance[axis * 6 + column];
36
+ }
37
+ }
38
+ for (int axis = 0; axis < 3; ++axis) {
39
+ const double prior = output->covariance[axis * 6 + axis];
40
+ const double sigma = observation->sigma[axis];
41
+ if (!(prior > 0.0) || !(sigma > 0.0) || !std::isfinite(sigma)) {
42
+ return ORBPRO_ESTIMATION_BAD_INPUT;
43
+ }
44
+ const double measurement_variance = sigma * sigma;
45
+ const double gain = prior / (prior + measurement_variance);
46
+ output->state[axis] += gain * (observation->value[axis] - output->state[axis]);
47
+ output->covariance[axis * 6 + axis] = (1.0 - gain) * prior;
48
+ }
49
+ output->accepted_count = 1;
50
+ output->converged = 1;
51
+ output->estimator = ORBPRO_ESTIMATOR_EXTENDED_KALMAN_FILTER;
52
+ return ORBPRO_ESTIMATION_OK;
53
+ }