space-data-module-sdk 0.8.12 → 0.8.13

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.
@@ -0,0 +1,312 @@
1
+ /* ===========================================================================
2
+ * GENERATED FILE — DO NOT EDIT.
3
+ *
4
+ * Source of truth : schemas/orbpro/Propagator.fbs
5
+ * Generator : scripts/generate-propagator-abi.mjs
6
+ * Drift gate : scripts/check-propagator-abi.mjs (runs in `npm test`)
7
+ * Contract : docs/propagator-abi.md
8
+ *
9
+ * Edit the .fbs and regenerate. A hand edit here is erased by the next run and
10
+ * failed by the gate in between — which is the point: this file existing in
11
+ * five hand-maintained copies is the drift that
12
+ * graph/findings/official-harness-shapes.md §3 forbids.
13
+ * ===========================================================================
14
+ */
15
+
16
+ #ifndef ORBPRO_PROPAGATOR_ABI_H
17
+ #define ORBPRO_PROPAGATOR_ABI_H
18
+
19
+ #include <stdint.h>
20
+ #include <stddef.h> /* offsetof */
21
+
22
+ /* The ABI locks below must compile in both C and C++ — first-party
23
+ * modules are C++ (sgp4_plugin.cpp, poly_coverage_module.cpp) while the
24
+ * reference module and the header's own examples are C. */
25
+ #if defined(__cplusplus)
26
+ #define ORBPRO_ABI_STATIC_ASSERT(cond, msg) static_assert(cond, msg)
27
+ #else
28
+ #define ORBPRO_ABI_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
29
+ #endif
30
+
31
+ #ifdef __cplusplus
32
+ extern "C" {
33
+ #endif
34
+
35
+ /* ========================================================================= */
36
+ /* ReferenceFrame */
37
+ /* ========================================================================= */
38
+
39
+ /** ReferenceFrame */
40
+ typedef enum {
41
+ ORBPRO_FRAME_TEME = 0,
42
+ ORBPRO_FRAME_J2000 = 1,
43
+ ORBPRO_FRAME_ICRF = 2,
44
+ ORBPRO_FRAME_ECEF = 3,
45
+ ORBPRO_FRAME_MCI = 4,
46
+ ORBPRO_FRAME_MCMF = 5,
47
+ } OrbProReferenceFrame;
48
+
49
+ /* ========================================================================= */
50
+ /* StateFlags */
51
+ /* ========================================================================= */
52
+
53
+ /**
54
+ * StateVector.flags is a BITFIELD carrying any OR-combination of these, so it
55
+ * is declared `uint` on the struct rather than typed to this enum. The C
56
+ * enumerators are generated from here anyway — they are the contract.
57
+ */
58
+ typedef enum {
59
+ ORBPRO_STATE_NONE = 0,
60
+ ORBPRO_STATE_VALID = 1,
61
+ ORBPRO_STATE_IN_ECLIPSE = 2,
62
+ ORBPRO_STATE_DECAYED = 4,
63
+ ORBPRO_STATE_MANEUVERING = 8,
64
+ ORBPRO_STATE_EXTRAPOLATED = 16,
65
+ ORBPRO_STATE_HAS_COVARIANCE = 32,
66
+ } OrbProStateFlags;
67
+
68
+ /* ========================================================================= */
69
+ /* OrbProStateVector — 64 bytes, 8-byte aligned */
70
+ /* ========================================================================= */
71
+
72
+ /**
73
+ * Orbital state vector — 64 bytes, 8-byte aligned. Mirrors
74
+ * `OrbProStateVector` in orbpro-integration/sdk/include/orbpro_propagator.h
75
+ * byte for byte:
76
+ *
77
+ * 0 8 epoch (Julian date, float64)
78
+ * 8 24 position (METERS)
79
+ * 32 24 velocity (METERS/SECOND)
80
+ * 56 1 reference_frame (ubyte)
81
+ * 57 3 padding, MUST be zero
82
+ * 60 4 flags (uint32)
83
+ *
84
+ * NORMATIVE UNITS: position METERS, velocity METERS/SECOND. There is no km
85
+ * variant and no host-side conversion — the engine hands these straight to
86
+ * Cesium Cartesian3, whose unit is metres, and both shipped propagators emit
87
+ * meters. The C header used to declare `reference_frame` as a uint32 at
88
+ * offset 56, wire-identical to this ubyte+padding only by little-endian
89
+ * accident; it now declares ubyte + 3 reserved so the two agree by
90
+ * construction.
91
+ *
92
+ * Ruling: graph/findings/official-harness-shapes.md §4.1 / §4.2
93
+ *
94
+ * Binary layout (derived from the IDL, not hand-written):
95
+ *
96
+ * Offset Size Field
97
+ * ------ ---- -----------------------------------------
98
+ * 0 8 epoch
99
+ * 8 24 position
100
+ * 32 24 velocity
101
+ * 56 1 reference_frame
102
+ * 57 3 (alignment padding — MUST be written as zero)
103
+ * 60 4 flags
104
+ */
105
+ typedef struct {
106
+ double epoch; /**< Julian date of this state. */
107
+ double position[3]; /**< Position [x, y, z] in METERS. */
108
+ double velocity[3]; /**< Velocity [vx, vy, vz] in METERS PER SECOND. */
109
+ uint8_t reference_frame; /**< Reference frame of position/velocity. One byte; the three bytes that follow are alignment padding and MUST be written as zero. */
110
+ uint8_t _reserved[3]; /**< Alignment padding at offset 57. MUST be 0. */
111
+ uint32_t flags; /**< StateFlags bitfield. */
112
+ } OrbProStateVector;
113
+
114
+ ORBPRO_ABI_STATIC_ASSERT(sizeof(OrbProStateVector) == 64,
115
+ "OrbProStateVector must be 64 bytes");
116
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProStateVector, epoch) == 0,
117
+ "OrbProStateVector.epoch must be at offset 0");
118
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProStateVector, position) == 8,
119
+ "OrbProStateVector.position must be at offset 8");
120
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProStateVector, velocity) == 32,
121
+ "OrbProStateVector.velocity must be at offset 32");
122
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProStateVector, reference_frame) == 56,
123
+ "OrbProStateVector.reference_frame must be at offset 56");
124
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProStateVector, flags) == 60,
125
+ "OrbProStateVector.flags must be at offset 60");
126
+
127
+ /**
128
+ * Zero an entire OrbProStateVector, INCLUDING its alignment padding.
129
+ * Start every write here — see the note above about scratch buffers.
130
+ */
131
+ static inline void orbpro_state_init(OrbProStateVector* value) {
132
+ for (size_t i = 0; i < sizeof(*value); ++i) {
133
+ ((unsigned char*)value)[i] = 0;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Set OrbProStateVector.reference_frame AND clear the 3 padding byte(s)
139
+ * that follow it. USE THIS instead of assigning the field directly.
140
+ */
141
+ static inline void orbpro_state_set_reference_frame(OrbProStateVector* value, OrbProReferenceFrame v) {
142
+ value->reference_frame = (uint8_t)v;
143
+ ((unsigned char*)value)[57] = 0;
144
+ ((unsigned char*)value)[58] = 0;
145
+ ((unsigned char*)value)[59] = 0;
146
+ }
147
+
148
+ /* ========================================================================= */
149
+ /* OrbProOrbitalElements — 64 bytes, 8-byte aligned */
150
+ /* ========================================================================= */
151
+
152
+ /**
153
+ * Keplerian orbital elements — the OPTIONAL initialization input accepted by
154
+ * `plugin_init_elements`. 64 bytes, 8-byte aligned.
155
+ *
156
+ * UNITS NOTE: `semi_major_axis` is KILOMETRES. That is deliberate and it is
157
+ * NOT an inconsistency with StateVector's metres: this is an INPUT element
158
+ * set, not an output state vector, and the two are different structs on
159
+ * different sides of the call. Do not "unify" them — see the normative units
160
+ * block in the generated C header.
161
+ *
162
+ * Binary layout (derived from the IDL, not hand-written):
163
+ *
164
+ * Offset Size Field
165
+ * ------ ---- -----------------------------------------
166
+ * 0 8 semi_major_axis
167
+ * 8 8 eccentricity
168
+ * 16 8 inclination
169
+ * 24 8 raan
170
+ * 32 8 arg_periapsis
171
+ * 40 8 true_anomaly
172
+ * 48 8 epoch
173
+ * 56 8 reserved
174
+ */
175
+ typedef struct {
176
+ double semi_major_axis; /**< Semi-major axis in KILOMETRES. */
177
+ double eccentricity; /**< Orbital eccentricity (0 = circular, <1 = ellipse). */
178
+ double inclination; /**< Inclination in RADIANS. */
179
+ double raan; /**< Right ascension of the ascending node in RADIANS. */
180
+ double arg_periapsis; /**< Argument of periapsis in RADIANS. */
181
+ double true_anomaly; /**< True anomaly in RADIANS. */
182
+ double epoch; /**< Epoch as a Julian date. */
183
+ double reserved; /**< Reserved, MUST be written as 0. */
184
+ } OrbProOrbitalElements;
185
+
186
+ ORBPRO_ABI_STATIC_ASSERT(sizeof(OrbProOrbitalElements) == 64,
187
+ "OrbProOrbitalElements must be 64 bytes");
188
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, semi_major_axis) == 0,
189
+ "OrbProOrbitalElements.semi_major_axis must be at offset 0");
190
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, eccentricity) == 8,
191
+ "OrbProOrbitalElements.eccentricity must be at offset 8");
192
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, inclination) == 16,
193
+ "OrbProOrbitalElements.inclination must be at offset 16");
194
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, raan) == 24,
195
+ "OrbProOrbitalElements.raan must be at offset 24");
196
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, arg_periapsis) == 32,
197
+ "OrbProOrbitalElements.arg_periapsis must be at offset 32");
198
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, true_anomaly) == 40,
199
+ "OrbProOrbitalElements.true_anomaly must be at offset 40");
200
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, epoch) == 48,
201
+ "OrbProOrbitalElements.epoch must be at offset 48");
202
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOrbitalElements, reserved) == 56,
203
+ "OrbProOrbitalElements.reserved must be at offset 56");
204
+
205
+ /**
206
+ * Zero an entire OrbProOrbitalElements, INCLUDING its alignment padding.
207
+ * Start every write here — see the note above about scratch buffers.
208
+ */
209
+ static inline void orbpro_elements_init(OrbProOrbitalElements* value) {
210
+ for (size_t i = 0; i < sizeof(*value); ++i) {
211
+ ((unsigned char*)value)[i] = 0;
212
+ }
213
+ }
214
+
215
+ /* ========================================================================= */
216
+ /* OrbProOMMRecord — 88 bytes, 8-byte aligned */
217
+ /* ========================================================================= */
218
+
219
+ /**
220
+ * Binary OMM record — the mean-element ingest struct. 88 bytes, 8-byte
221
+ * aligned.
222
+ *
223
+ * !! THIS STRUCT IS ALSO AN ON-DISK FORMAT !!
224
+ * -----------------------------------------------------------------------
225
+ * It crosses the ABI (`plugin_init_omm`, `plugin_entity_add_omm`) AND is
226
+ * persisted verbatim as a SQLite BLOB by the first-party SGP4 module
227
+ * (`sgp4_plugin.cpp`, `sqlite3_bind_blob(..., &omm, sizeof(OrbProOMMRecord), ...)`).
228
+ * Until W1.1 it carried NO size or offset lock anywhere in the stack, so the
229
+ * layout that every stored blob depends on was held only by the field order
230
+ * of one hand-written C struct in one module.
231
+ *
232
+ * The layout declared here is that layout, EXACTLY as it has been written to
233
+ * disk — the four trailing padding bytes at offset 84 included. This is a
234
+ * description of the wire as it already exists, not a redesign of it; the
235
+ * generated locks now pin it. Migrating the format is explicitly out of
236
+ * scope and would invalidate every stored blob.
237
+ *
238
+ * UNITS: angles in DEGREES, mean motion in REV/DAY, bstar in 1/earth-radii.
239
+ * These are the SDS $OMM units, carried through unconverted.
240
+ *
241
+ * Binary layout (derived from the IDL, not hand-written):
242
+ *
243
+ * Offset Size Field
244
+ * ------ ---- -----------------------------------------
245
+ * 0 8 epoch_jd
246
+ * 8 8 mean_motion
247
+ * 16 8 eccentricity
248
+ * 24 8 inclination
249
+ * 32 8 ra_of_asc_node
250
+ * 40 8 arg_of_pericenter
251
+ * 48 8 mean_anomaly
252
+ * 56 8 bstar
253
+ * 64 8 mean_motion_dot
254
+ * 72 8 mean_motion_ddot
255
+ * 80 4 norad_cat_id
256
+ * 84 4 (alignment padding — MUST be written as zero)
257
+ */
258
+ typedef struct {
259
+ double epoch_jd; /**< Epoch as a Julian date (callers convert ISO 8601 -> JD). */
260
+ double mean_motion; /**< Mean motion in REV/DAY. */
261
+ double eccentricity; /**< Eccentricity (unitless). */
262
+ double inclination; /**< Inclination in DEGREES. */
263
+ double ra_of_asc_node; /**< Right ascension of the ascending node in DEGREES. */
264
+ double arg_of_pericenter; /**< Argument of pericenter in DEGREES. */
265
+ double mean_anomaly; /**< Mean anomaly in DEGREES. */
266
+ double bstar; /**< B* drag term in 1/earth-radii. */
267
+ double mean_motion_dot; /**< First derivative of mean motion, REV/DAY^2. */
268
+ double mean_motion_ddot; /**< Second derivative of mean motion, REV/DAY^3. */
269
+ uint32_t norad_cat_id; /**< NORAD catalog number — the identity authority for this record. */
270
+ uint8_t _reserved[4]; /**< Alignment padding at offset 84. MUST be 0. */
271
+ } OrbProOMMRecord;
272
+
273
+ ORBPRO_ABI_STATIC_ASSERT(sizeof(OrbProOMMRecord) == 88,
274
+ "OrbProOMMRecord must be 88 bytes");
275
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, epoch_jd) == 0,
276
+ "OrbProOMMRecord.epoch_jd must be at offset 0");
277
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, mean_motion) == 8,
278
+ "OrbProOMMRecord.mean_motion must be at offset 8");
279
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, eccentricity) == 16,
280
+ "OrbProOMMRecord.eccentricity must be at offset 16");
281
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, inclination) == 24,
282
+ "OrbProOMMRecord.inclination must be at offset 24");
283
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, ra_of_asc_node) == 32,
284
+ "OrbProOMMRecord.ra_of_asc_node must be at offset 32");
285
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, arg_of_pericenter) == 40,
286
+ "OrbProOMMRecord.arg_of_pericenter must be at offset 40");
287
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, mean_anomaly) == 48,
288
+ "OrbProOMMRecord.mean_anomaly must be at offset 48");
289
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, bstar) == 56,
290
+ "OrbProOMMRecord.bstar must be at offset 56");
291
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, mean_motion_dot) == 64,
292
+ "OrbProOMMRecord.mean_motion_dot must be at offset 64");
293
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, mean_motion_ddot) == 72,
294
+ "OrbProOMMRecord.mean_motion_ddot must be at offset 72");
295
+ ORBPRO_ABI_STATIC_ASSERT(offsetof(OrbProOMMRecord, norad_cat_id) == 80,
296
+ "OrbProOMMRecord.norad_cat_id must be at offset 80");
297
+
298
+ /**
299
+ * Zero an entire OrbProOMMRecord, INCLUDING its alignment padding.
300
+ * Start every write here — see the note above about scratch buffers.
301
+ */
302
+ static inline void orbpro_omm_init(OrbProOMMRecord* value) {
303
+ for (size_t i = 0; i < sizeof(*value); ++i) {
304
+ ((unsigned char*)value)[i] = 0;
305
+ }
306
+ }
307
+
308
+ #ifdef __cplusplus
309
+ } /* extern "C" */
310
+ #endif
311
+
312
+ #endif /* ORBPRO_PROPAGATOR_ABI_H */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "space-data-module-sdk",
3
- "version": "0.8.12",
3
+ "version": "0.8.13",
4
4
  "description": "Module SDK for building, validating, signing, and deploying WebAssembly modules on the Space Data Network.",
5
5
  "type": "module",
6
6
  "types": "./src/index.d.ts",
@@ -29,6 +29,8 @@
29
29
  "types": "./src/licensing/index.d.ts",
30
30
  "default": "./src/licensing/index.js"
31
31
  },
32
+ "./include/*": "./include/*",
33
+ "./generated/propagator-abi": "./src/generated/orbpro/propagator-abi.js",
32
34
  "./compiler": "./src/compiler/index.js",
33
35
  "./compiler/emception": "./src/compiler/emception.js",
34
36
  "./bundle": "./src/bundle/index.js",
@@ -75,6 +77,7 @@
75
77
  "files": [
76
78
  "bin/",
77
79
  "docs/",
80
+ "include/",
78
81
  "parity/",
79
82
  "schemas/",
80
83
  "src/",
@@ -98,6 +101,8 @@
98
101
  "check:nav": "node scripts/check-sdn-stack-nav.mjs module-sdk docs/index.html docs/styles.css",
99
102
  "check:compliance": "node ./bin/space-data-module.js check --repo-root .",
100
103
  "check:generated-bindings": "node scripts/check-generated-bindings.mjs",
104
+ "check:propagator-abi": "node scripts/check-propagator-abi.mjs",
105
+ "generate:propagator-abi": "node scripts/generate-propagator-abi.mjs",
101
106
  "generate:vectors": "node ./examples/single-file-bundle/generate-vectors.mjs",
102
107
  "prepublishOnly": "npm test && npm run check:compliance"
103
108
  },
@@ -7,6 +7,24 @@ include "TypedArenaBuffer.fbs";
7
7
  namespace orbpro.manifest;
8
8
 
9
9
  /// Canonical plugin family.
10
+ ///
11
+ /// APPEND-ONLY. Ordinals are wire and MUST never be renumbered or reused.
12
+ ///
13
+ /// This enum is a PROJECTION of the authoritative SDS vocabulary
14
+ /// `pluginCategory` (spacedatastandards.org `schema/PLG/main.fbs`). SDS is the
15
+ /// source of truth for what families exist; this enum is what the SDK code
16
+ /// dispatches on. The two DO NOT share ordinals — they diverge from index 5
17
+ /// onward (SDK `COMMS = 5`, SDS `EW = 5`) — so the SDK↔SDS mapping is BY NAME,
18
+ /// declared explicitly in `src/manifest/normalize.js`
19
+ /// (`sdsPluginCategoryByFamily`). Never convert one to the other numerically.
20
+ ///
21
+ /// Unknown family strings used to be silently coerced to `ANALYSIS`, which
22
+ /// mislabelled 19 first-party modules and made family-typed resolution work
23
+ /// for propagators only. `normalizePluginFamily()` now REFUSES an unknown
24
+ /// family, naming it and the valid vocabulary.
25
+ ///
26
+ /// Ruling: graph/findings/official-harness-shapes.md §4.7 / §8.3
27
+ /// Task: graph/tasks/harness-w0-immediate-fixes.md (W0.3)
10
28
  enum PluginFamily : ubyte {
11
29
  SENSOR = 0,
12
30
  PROPAGATOR = 1,
@@ -18,7 +36,34 @@ enum PluginFamily : ubyte {
18
36
  SDF = 7,
19
37
  INFRASTRUCTURE = 8,
20
38
  FLOW = 9,
21
- BRIDGE = 10
39
+ BRIDGE = 10,
40
+ /// Maneuver planning, targeting and trajectory optimization.
41
+ /// Projects onto SDS `pluginCategory.Maneuver`.
42
+ MANEUVER = 11,
43
+ /// Orbit determination: fitting an orbit to observations/ephemerides.
44
+ /// SDS has NO dedicated member yet — this projects onto
45
+ /// `pluginCategory.Analysis` until Themis mints one. That gap is filed as
46
+ /// graph/tasks/sds-plugin-category-projection-gaps.md.
47
+ ORBIT_DETERMINATION = 12,
48
+ /// Foundational math / utility library module (frames, time, numerics,
49
+ /// attitude, splines). Projects onto SDS `pluginCategory.Foundation`.
50
+ FOUNDATION = 13,
51
+ /// Parses raw upstream bytes into canonical SDS records.
52
+ /// Projects onto SDS `pluginCategory.Parser`.
53
+ PARSER = 14,
54
+ /// Validates records (integrity, physical bounds, continuity).
55
+ /// Projects onto SDS `pluginCategory.Validator`.
56
+ VALIDATOR = 15,
57
+ /// Exports records to external formats (CSV, etc.).
58
+ /// Projects onto SDS `pluginCategory.Exporter`.
59
+ EXPORTER = 16,
60
+ /// Publication: PNM signing + pub/sub announcement.
61
+ /// Projects onto SDS `pluginCategory.Publisher`.
62
+ PUBLISHER = 17,
63
+ /// Astrodynamics simulation / dynamics modelling.
64
+ /// Projects onto SDS `pluginCategory.Basilisk` (a legacy vocabulary key
65
+ /// retained there for wire compatibility).
66
+ BASILISK = 18
22
67
  }
23
68
 
24
69
  /// Host capability requested by a plugin.
@@ -2,7 +2,64 @@ include "BaseTypes.fbs";
2
2
 
3
3
  namespace orbpro.propagator;
4
4
 
5
- enum ReferenceFrame : ubyte {
5
+ // =============================================================================
6
+ // THE PROPAGATOR ABI LIVES HERE. THIS FILE IS THE SINGLE SOURCE OF TRUTH.
7
+ // =============================================================================
8
+ //
9
+ // Every struct below marked `(abi)` is a BINARY CONTRACT that crosses the
10
+ // WASM boundary between a third-party propagator module and the OrbPro engine.
11
+ // The C header, the TypeScript byte-offset bindings and the size/offset locks
12
+ // are all GENERATED from these declarations:
13
+ //
14
+ // node scripts/generate-propagator-abi.mjs # regenerate
15
+ // node scripts/check-propagator-abi.mjs # the drift gate (runs in npm test)
16
+ //
17
+ // Do NOT hand-write a mirror of these structs anywhere. Five hand-vendored
18
+ // copies of `StateVector` is what this lane exists to end, and the units
19
+ // contradiction W0.1 fixed (km in the field comments, METERS in the layout
20
+ // block three lines below) lived for months precisely because there was no
21
+ // mechanical link between the IDL and the header.
22
+ //
23
+ // Contract document: docs/propagator-abi.md
24
+ // Ruling: graph/findings/official-harness-shapes.md §3 / §7 SHIP-1
25
+ // Task: graph/tasks/harness-w1-propagator-abi-and-reference.md (W1.1)
26
+
27
+ /// Marks a struct or enum as part of the binary propagator ABI. Generated
28
+ /// artifacts are derived from exactly the declarations carrying this.
29
+ attribute "abi";
30
+
31
+ /// The C type name a generated ABI declaration takes. Part of the contract:
32
+ /// third-party sources say `OrbProStateVector`, not `orbpro_propagator_StateVector`.
33
+ attribute "abi_c_name";
34
+
35
+ /// The C enumerator prefix for a generated ABI enum (ReferenceFrame's members
36
+ /// are `ORBPRO_FRAME_*`, StateFlags' are `ORBPRO_STATE_*`).
37
+ attribute "abi_c_prefix";
38
+
39
+ /// The C helper-function prefix for a generated ABI struct: the generator emits
40
+ /// `<prefix>init()` and a padding-clearing `<prefix>set_<field>()` for every
41
+ /// enum field. Writing these by hand is how padding bytes go stale.
42
+ attribute "abi_c_helper_prefix";
43
+
44
+ // !! FOUR INCOMPATIBLE `ReferenceFrame` VOCABULARIES CROSS THIS SEAM !!
45
+ // This is the `orbpro.propagator` one. Its NUMERIC values are NOT
46
+ // interchangeable with the others; translate by NAMED TOKEN at every seam.
47
+ //
48
+ // (A) orbpro.propagator (THIS) TEME=0 J2000=1 ICRF=2 ECEF=3 MCI=4 MCMF=5
49
+ // (B) orbpro.plugins ECI=0 ECEF=1 TEME=2 ICRF=3
50
+ // (PropagatorState.fbs / "PRST" — the wire the compiled sgp4, hpop,
51
+ // conjunction-assessment and sensor-shader modules already emit)
52
+ // (C) Cesium.ReferenceFrame FIXED=0 INERTIAL=1
53
+ // (D) ConjunctionCommon.fbs ECI=1
54
+ //
55
+ // ECI==0, TEME==0 and FIXED==0 all collide, and ECEF is 1 in (B) but 3 here.
56
+ // (B)'s values are frozen by compiled WASM artifacts in the field, so
57
+ // collapsing (A) and (B) is a wire break, not a Wave-0 edit — see
58
+ // graph/tasks/sdk-reference-frame-enum-unification.md. The reference pattern
59
+ // for a correct seam is propagator.hpop/index.js createSourceFromState.
60
+ //
61
+ // Ruling: graph/findings/official-harness-shapes.md §4.2 / §8.2
62
+ enum ReferenceFrame : ubyte (abi, abi_c_name: "OrbProReferenceFrame", abi_c_prefix: "ORBPRO_FRAME_") {
6
63
  TEME = 0,
7
64
  J2000 = 1,
8
65
  ICRF = 2,
@@ -11,7 +68,10 @@ enum ReferenceFrame : ubyte {
11
68
  MCMF = 5
12
69
  }
13
70
 
14
- enum StateFlags : uint {
71
+ /// StateVector.flags is a BITFIELD carrying any OR-combination of these, so it
72
+ /// is declared `uint` on the struct rather than typed to this enum. The C
73
+ /// enumerators are generated from here anyway — they are the contract.
74
+ enum StateFlags : uint (abi, abi_c_name: "OrbProStateFlags", abi_c_prefix: "ORBPRO_STATE_") {
15
75
  NONE = 0,
16
76
  VALID = 1,
17
77
  IN_ECLIPSE = 2,
@@ -42,14 +102,112 @@ enum PropagatorSourceKind : ubyte {
42
102
  SDP8 = 12
43
103
  }
44
104
 
45
- struct StateVector {
105
+ /// Orbital state vector — 64 bytes, 8-byte aligned. Mirrors
106
+ /// `OrbProStateVector` in orbpro-integration/sdk/include/orbpro_propagator.h
107
+ /// byte for byte:
108
+ ///
109
+ /// 0 8 epoch (Julian date, float64)
110
+ /// 8 24 position (METERS)
111
+ /// 32 24 velocity (METERS/SECOND)
112
+ /// 56 1 reference_frame (ubyte)
113
+ /// 57 3 padding, MUST be zero
114
+ /// 60 4 flags (uint32)
115
+ ///
116
+ /// NORMATIVE UNITS: position METERS, velocity METERS/SECOND. There is no km
117
+ /// variant and no host-side conversion — the engine hands these straight to
118
+ /// Cesium Cartesian3, whose unit is metres, and both shipped propagators emit
119
+ /// meters. The C header used to declare `reference_frame` as a uint32 at
120
+ /// offset 56, wire-identical to this ubyte+padding only by little-endian
121
+ /// accident; it now declares ubyte + 3 reserved so the two agree by
122
+ /// construction.
123
+ ///
124
+ /// Ruling: graph/findings/official-harness-shapes.md §4.1 / §4.2
125
+ struct StateVector (abi, abi_c_name: "OrbProStateVector", abi_c_helper_prefix: "orbpro_state_") {
126
+ /// Julian date of this state.
46
127
  epoch:double;
128
+ /// Position [x, y, z] in METERS.
47
129
  position:orbpro.Vec3;
130
+ /// Velocity [vx, vy, vz] in METERS PER SECOND.
48
131
  velocity:orbpro.Vec3;
132
+ /// Reference frame of position/velocity. One byte; the three bytes that
133
+ /// follow are alignment padding and MUST be written as zero.
49
134
  reference_frame:ReferenceFrame;
135
+ /// StateFlags bitfield.
50
136
  flags:uint;
51
137
  }
52
138
 
139
+ /// Keplerian orbital elements — the OPTIONAL initialization input accepted by
140
+ /// `plugin_init_elements`. 64 bytes, 8-byte aligned.
141
+ ///
142
+ /// UNITS NOTE: `semi_major_axis` is KILOMETRES. That is deliberate and it is
143
+ /// NOT an inconsistency with StateVector's metres: this is an INPUT element
144
+ /// set, not an output state vector, and the two are different structs on
145
+ /// different sides of the call. Do not "unify" them — see the normative units
146
+ /// block in the generated C header.
147
+ struct OrbitalElements (abi, abi_c_name: "OrbProOrbitalElements", abi_c_helper_prefix: "orbpro_elements_") {
148
+ /// Semi-major axis in KILOMETRES.
149
+ semi_major_axis:double;
150
+ /// Orbital eccentricity (0 = circular, <1 = ellipse).
151
+ eccentricity:double;
152
+ /// Inclination in RADIANS.
153
+ inclination:double;
154
+ /// Right ascension of the ascending node in RADIANS.
155
+ raan:double;
156
+ /// Argument of periapsis in RADIANS.
157
+ arg_periapsis:double;
158
+ /// True anomaly in RADIANS.
159
+ true_anomaly:double;
160
+ /// Epoch as a Julian date.
161
+ epoch:double;
162
+ /// Reserved, MUST be written as 0.
163
+ reserved:double;
164
+ }
165
+
166
+ /// Binary OMM record — the mean-element ingest struct. 88 bytes, 8-byte
167
+ /// aligned.
168
+ ///
169
+ /// !! THIS STRUCT IS ALSO AN ON-DISK FORMAT !!
170
+ /// -----------------------------------------------------------------------
171
+ /// It crosses the ABI (`plugin_init_omm`, `plugin_entity_add_omm`) AND is
172
+ /// persisted verbatim as a SQLite BLOB by the first-party SGP4 module
173
+ /// (`sgp4_plugin.cpp`, `sqlite3_bind_blob(..., &omm, sizeof(OrbProOMMRecord), ...)`).
174
+ /// Until W1.1 it carried NO size or offset lock anywhere in the stack, so the
175
+ /// layout that every stored blob depends on was held only by the field order
176
+ /// of one hand-written C struct in one module.
177
+ ///
178
+ /// The layout declared here is that layout, EXACTLY as it has been written to
179
+ /// disk — the four trailing padding bytes at offset 84 included. This is a
180
+ /// description of the wire as it already exists, not a redesign of it; the
181
+ /// generated locks now pin it. Migrating the format is explicitly out of
182
+ /// scope and would invalidate every stored blob.
183
+ ///
184
+ /// UNITS: angles in DEGREES, mean motion in REV/DAY, bstar in 1/earth-radii.
185
+ /// These are the SDS $OMM units, carried through unconverted.
186
+ struct OMMRecord (abi, abi_c_name: "OrbProOMMRecord", abi_c_helper_prefix: "orbpro_omm_") {
187
+ /// Epoch as a Julian date (callers convert ISO 8601 -> JD).
188
+ epoch_jd:double;
189
+ /// Mean motion in REV/DAY.
190
+ mean_motion:double;
191
+ /// Eccentricity (unitless).
192
+ eccentricity:double;
193
+ /// Inclination in DEGREES.
194
+ inclination:double;
195
+ /// Right ascension of the ascending node in DEGREES.
196
+ ra_of_asc_node:double;
197
+ /// Argument of pericenter in DEGREES.
198
+ arg_of_pericenter:double;
199
+ /// Mean anomaly in DEGREES.
200
+ mean_anomaly:double;
201
+ /// B* drag term in 1/earth-radii.
202
+ bstar:double;
203
+ /// First derivative of mean motion, REV/DAY^2.
204
+ mean_motion_dot:double;
205
+ /// Second derivative of mean motion, REV/DAY^3.
206
+ mean_motion_ddot:double;
207
+ /// NORAD catalog number — the identity authority for this record.
208
+ norad_cat_id:uint;
209
+ }
210
+
53
211
  table PropagatorDescribeSourcesBatchRequest {
54
212
  catalogHandle:uint = 0;
55
213
  sourceHandles:[uint];
@@ -11,6 +11,7 @@ export {
11
11
  CONTENT_HASH_SIZE as SIGNATURE_STATEMENT_CONTENT_HASH_SIZE,
12
12
  DOMAIN_MODULE_PUBLICATION_V1,
13
13
  DOMAIN_UPDATE_MANIFEST_V1,
14
+ DOMAIN_UPDATE_SIGNAL_V1,
14
15
  SignatureDomainError,
15
16
  describe as describeSignatureDomain,
16
17
  domains as registeredSignatureDomains,
@@ -61,6 +61,24 @@ export const DOMAIN_MODULE_PUBLICATION_V1 = "SDN-MODULE-PUBLICATION-V1";
61
61
  */
62
62
  export const DOMAIN_UPDATE_MANIFEST_V1 = "SDN-UPDATE-MANIFEST-V1";
63
63
 
64
+ /**
65
+ * An UPDATE SIGNAL: the small pub/sub nudge a publisher emits after an artifact
66
+ * is on the update feed, telling the fleet that a new version exists and where
67
+ * to fetch it (owner ruling 2026-08-09, "pushing an update signal to all
68
+ * installs to upgrade in place").
69
+ *
70
+ * A separate domain from {@link DOMAIN_UPDATE_MANIFEST_V1} even though the same
71
+ * bonded key signs both, and the separation is load-bearing in one direction: a
72
+ * signal is a POINTER, a manifest is an AUTHORIZATION. A signal signature is
73
+ * cheap, frequent and broadcast to anyone listening, so if the two shared a
74
+ * preimage space one could be replayed as authorization for bytes. The signal
75
+ * grants nothing — everything it names is re-fetched and re-verified against the
76
+ * signed manifest before a byte is swapped. Registered here so the JS registry
77
+ * still mirrors the node's exactly; a module verifier must REFUSE it, which the
78
+ * shared vectors pin.
79
+ */
80
+ export const DOMAIN_UPDATE_SIGNAL_V1 = "SDN-UPDATE-SIGNAL-V1";
81
+
64
82
  /**
65
83
  * The CLOSED set of statement domains this SDK will verify. The map value is a
66
84
  * short human description: this table is the answer to "what can the bonded
@@ -76,6 +94,10 @@ const REGISTRY = new Map([
76
94
  DOMAIN_UPDATE_MANIFEST_V1,
77
95
  "update manifest, canonical manifest SHA-256 (reserved; no producer yet)",
78
96
  ],
97
+ [
98
+ DOMAIN_UPDATE_SIGNAL_V1,
99
+ "update signal, canonical signal SHA-256 (advisory pub/sub nudge; authorizes nothing)",
100
+ ],
79
101
  ]);
80
102
 
81
103
  const domainTextEncoder = new TextEncoder();