space-data-module-sdk 0.8.14 → 0.8.16

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 (125) hide show
  1. package/bin/space-data-module.js +91 -0
  2. package/docs/_shell/consumer-assets.html +5 -0
  3. package/docs/browser-wasmedge-isomorphic.html +261 -0
  4. package/docs/byo-wasm-quickstart.html +226 -0
  5. package/docs/byo-wasm-quickstart.md +204 -0
  6. package/docs/conformance.html +156 -0
  7. package/docs/conformance.md +92 -0
  8. package/docs/emception-shared-memory-verdict.md +209 -0
  9. package/docs/events-abi.html +313 -0
  10. package/docs/events-abi.md +530 -0
  11. package/docs/families/analytics.html +178 -0
  12. package/docs/families/analytics.md +104 -0
  13. package/docs/families/attitude.html +157 -0
  14. package/docs/families/attitude.md +36 -0
  15. package/docs/families/behavior.html +157 -0
  16. package/docs/families/behavior.md +36 -0
  17. package/docs/families/breakup.html +157 -0
  18. package/docs/families/breakup.md +36 -0
  19. package/docs/families/conjunction.html +186 -0
  20. package/docs/families/conjunction.md +121 -0
  21. package/docs/families/data-source.html +318 -0
  22. package/docs/families/effects.html +157 -0
  23. package/docs/families/effects.md +36 -0
  24. package/docs/families/environment.html +170 -0
  25. package/docs/families/environment.md +73 -0
  26. package/docs/families/estimation.html +190 -0
  27. package/docs/families/estimation.md +128 -0
  28. package/docs/families/gnc.html +157 -0
  29. package/docs/families/gnc.md +36 -0
  30. package/docs/families/maneuver.html +209 -0
  31. package/docs/families/maneuver.md +206 -0
  32. package/docs/families/obstruction.html +191 -0
  33. package/docs/families/obstruction.md +96 -0
  34. package/docs/families/propagator.html +333 -0
  35. package/docs/families/propulsion.html +157 -0
  36. package/docs/families/propulsion.md +36 -0
  37. package/docs/families/reentry.html +157 -0
  38. package/docs/families/reentry.md +36 -0
  39. package/docs/families/rf.html +193 -0
  40. package/docs/families/rf.md +136 -0
  41. package/docs/families/scheduler.html +157 -0
  42. package/docs/families/scheduler.md +36 -0
  43. package/docs/families/sensor.html +157 -0
  44. package/docs/families/sensor.md +36 -0
  45. package/docs/families/signature.html +157 -0
  46. package/docs/families/signature.md +36 -0
  47. package/docs/flatsql-host-contract.html +176 -0
  48. package/docs/flatsql-streaming-standard.html +375 -0
  49. package/docs/gpu-module-abi.html +233 -0
  50. package/docs/harness-family-matrix.md +135 -0
  51. package/docs/index.html +216 -85
  52. package/docs/isomorphic-pthreads.html +228 -0
  53. package/docs/isomorphic-pthreads.md +84 -0
  54. package/docs/isomorphic-sdn-runtime-plan.html +274 -0
  55. package/docs/language-runtime-matrix.html +182 -0
  56. package/docs/llms.txt +86 -0
  57. package/docs/module-bundle-runtime-plan.html +146 -0
  58. package/docs/module-publication-standard.html +503 -0
  59. package/docs/node-red-default-node-parity.html +201 -0
  60. package/docs/propagator-abi.md +73 -21
  61. package/docs/protect-and-sign.html +181 -0
  62. package/docs/protect-and-sign.md +120 -0
  63. package/docs/protocol-installation.html +296 -0
  64. package/docs/publication-submission.html +158 -0
  65. package/docs/publication-submission.md +78 -0
  66. package/docs/secrets-capability.html +206 -0
  67. package/docs/styles.css +860 -167
  68. package/docs/testing-harness.html +408 -0
  69. package/docs/tri-runtime-parity-gate.html +182 -0
  70. package/docs/tri-runtime-parity.html +203 -0
  71. package/include/orbpro/orbpro_event_runner.h +951 -0
  72. package/include/orbpro/orbpro_events_abi.h +565 -0
  73. package/include/orbpro/orbpro_propagator_abi.h +17 -0
  74. package/package.json +15 -5
  75. package/schemas/orbpro/Events.fbs +501 -0
  76. package/schemas/orbpro/Propagator.fbs +72 -9
  77. package/schemas/orbpro/reference-frame-crosswalk.json +108 -0
  78. package/schemas/orbpro/reference-frame.lock.json +98 -0
  79. package/src/browser.js +13 -0
  80. package/src/conformance/abiDriver.js +225 -0
  81. package/src/conformance/index.js +151 -0
  82. package/src/conformance/propagatorSuite.js +526 -0
  83. package/src/conformance/selfTest.js +275 -0
  84. package/src/conformance/selfTestCorpus.js +68 -0
  85. package/src/conformance/twoBodyReference.js +132 -0
  86. package/src/generated/orbpro/events-abi.js +206 -0
  87. package/src/generated/orbpro/events-abi.ts +319 -0
  88. package/src/generated/orbpro/propagator/reference-frame.js +17 -0
  89. package/src/generated/orbpro/propagator/reference-frame.ts +91 -1
  90. package/src/generated/orbpro/propagator-abi.js +17 -0
  91. package/src/generated/orbpro/propagator-abi.ts +17 -0
  92. package/src/generated/spacedatastandards/plg/CCT.d.ts +94 -0
  93. package/src/generated/spacedatastandards/plg/CCT.d.ts.map +1 -0
  94. package/src/generated/spacedatastandards/plg/CCT.js +208 -0
  95. package/src/generated/spacedatastandards/plg/CCT.ts +281 -0
  96. package/src/generated/spacedatastandards/plg/CCTCategory.d.ts +98 -0
  97. package/src/generated/spacedatastandards/plg/CCTCategory.d.ts.map +1 -0
  98. package/src/generated/spacedatastandards/plg/CCTCategory.js +177 -0
  99. package/src/generated/spacedatastandards/plg/CCTCategory.ts +269 -0
  100. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts +54 -0
  101. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.d.ts.map +1 -0
  102. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.js +106 -0
  103. package/src/generated/spacedatastandards/plg/CCTCategoryRollup.ts +144 -0
  104. package/src/generated/spacedatastandards/plg/PLG.d.ts +46 -2
  105. package/src/generated/spacedatastandards/plg/PLG.d.ts.map +1 -1
  106. package/src/generated/spacedatastandards/plg/PLG.js +78 -5
  107. package/src/generated/spacedatastandards/plg/PLG.ts +90 -5
  108. package/src/generated/spacedatastandards/plg/capabilityClass.d.ts +181 -0
  109. package/src/generated/spacedatastandards/plg/capabilityClass.d.ts.map +1 -0
  110. package/src/generated/spacedatastandards/plg/capabilityClass.js +183 -0
  111. package/src/generated/spacedatastandards/plg/capabilityClass.ts +208 -0
  112. package/src/generated/spacedatastandards/plg/main.d.ts +4 -0
  113. package/src/generated/spacedatastandards/plg/main.d.ts.map +1 -1
  114. package/src/generated/spacedatastandards/plg/main.js +4 -0
  115. package/src/generated/spacedatastandards/plg/main.ts +4 -0
  116. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts +40 -2
  117. package/src/generated/spacedatastandards/plg/pluginCategory.d.ts.map +1 -1
  118. package/src/generated/spacedatastandards/plg/pluginCategory.js +40 -2
  119. package/src/generated/spacedatastandards/plg/pluginCategory.ts +40 -2
  120. package/src/host/browserModuleHarness.js +8 -0
  121. package/src/host/wasiThreadHost.js +189 -14
  122. package/src/index.d.ts +18 -1
  123. package/src/testing/isomorphicHarness.js +233 -0
  124. package/src/transport/index.js +16 -0
  125. package/src/transport/records.js +1015 -3
@@ -0,0 +1,501 @@
1
+ include "Propagator.fbs";
2
+
3
+ namespace orbpro.events;
4
+
5
+ // =============================================================================
6
+ // THE EVENT-LOCATOR 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 WASM
10
+ // boundary between a third-party event-locator module and its consumer. The C
11
+ // header, the TypeScript byte-offset bindings and the size/offset locks are all
12
+ // GENERATED from these declarations:
13
+ //
14
+ // node scripts/generate-events-abi.mjs # regenerate
15
+ // node scripts/check-events-abi.mjs # the drift gate (runs in npm test)
16
+ //
17
+ // The `abi`, `abi_c_name`, `abi_c_prefix` and `abi_c_helper_prefix` attributes
18
+ // are declared in Propagator.fbs, which this file includes. `ReferenceFrame`
19
+ // and `StateVector` come from there too, and are NOT redeclared: five
20
+ // hand-vendored copies of one struct is the drift the generated-ABI lane
21
+ // exists to end.
22
+ //
23
+ // Contract document: docs/events-abi.md
24
+ // Family page: docs/families/analytics.md (event-location sub-harness)
25
+ // Ruling: Janus AMEND 2026-08-30 (gmat-06-parameter-catalog-and-event-locators)
26
+ //
27
+ // -----------------------------------------------------------------------------
28
+ // ONE ABI, N LOCATORS
29
+ // -----------------------------------------------------------------------------
30
+ // Eclipse, station contact, sensor intrusion, apsides, node crossings and a
31
+ // propagate-to-condition stop are the SAME thing: a vector of scalar event
32
+ // functions g_i(t) whose sign changes are refined to roots. A locator supplies
33
+ // the g vector and its description. It does NOT supply the root finder, the
34
+ // bracketing scan, the occurrence counting or the interval pairing — those are
35
+ // the RUNNER (include/orbpro/orbpro_event_runner.h), which is identical in
36
+ // every locator and is what makes "adding a locator needs no runner change"
37
+ // a structural fact rather than an aspiration.
38
+ //
39
+ // -----------------------------------------------------------------------------
40
+ // THE STATE SOURCE IS PULLED, NEVER IMPORTED
41
+ // -----------------------------------------------------------------------------
42
+ // A locator needs states at epochs only it can choose (Brent picks the next
43
+ // abscissa from the last three). It does NOT import a host function to get
44
+ // them. The runner INVERTS the call: `plugin_event_next` reports the epochs it
45
+ // wants, the consumer evaluates them through whatever propagator is wired to
46
+ // the port, and `plugin_event_supply` feeds the states back.
47
+ //
48
+ // This is deliberate and it is the whole reason no new host capability is
49
+ // needed for this family:
50
+ //
51
+ // * The propagator stays a PORT. Any module implementing the propagator ABI
52
+ // serves any locator, with no name of a provider anywhere in this schema.
53
+ // * There is no runtime detection and no new import, so the browser,
54
+ // native-WasmEdge and Docker-WasmEdge lanes are byte-identical by
55
+ // construction — the consumer shuttles buffers and decides nothing.
56
+ // * The refinement stays INSIDE the guest. A host-side root finder would be
57
+ // physics in JavaScript, and its arithmetic would differ per lane.
58
+ //
59
+ // -----------------------------------------------------------------------------
60
+ // EPOCHS ARE SPLIT, AND THAT IS NOT DECORATION
61
+ // -----------------------------------------------------------------------------
62
+ // Every epoch in this ABI is a PAIR: an exactly-representable Julian day plus
63
+ // an offset in SECONDS. A Julian date carried in one float64 resolves to
64
+ // ulp(2460000.5) = 2^-31 d = 4.02e-5 s — forty microseconds — so a single-double
65
+ // JD CANNOT express a stop epoch to 1e-6 s, and a locator that reported one
66
+ // would be reporting rounding noise. The split pair resolves 1.5e-11 s.
67
+ //
68
+ // `OrbProStateVector.epoch` is a single float64 and is FROZEN (64 bytes, on the
69
+ // wire, in the field). It is therefore the state SOURCE's resolution that
70
+ // bounds a stop epoch, not this ABI's. That resolution is DECLARED
71
+ // (`EventLocatorConfig.stateEpochResolutionSeconds`), the runner clamps its
72
+ // tolerance to it, and a root that hit the clamp is reported
73
+ // `EPOCH_RESOLUTION_LIMITED` rather than claiming a precision nothing measured.
74
+
75
+ /// The refinement algorithm the runner uses on a bracketed root. All three are
76
+ /// bracketing methods: a root that is bracketed stays bracketed, so a locator
77
+ /// can never report a root outside the interval that produced it.
78
+ enum RootMethod : ubyte (abi, abi_c_name: "OrbProRootMethod", abi_c_prefix: "ORBPRO_ROOT_") {
79
+ /// Brent's method (inverse quadratic interpolation with bisection fallback).
80
+ /// The default: superlinear on smooth g, and it cannot diverge.
81
+ BRENT = 0,
82
+ /// Plain bisection. Slower, and chosen only when g is known to be noisy or
83
+ /// piecewise — it is the one method whose iteration count is a function of
84
+ /// the bracket width alone, which makes it the reproducibility fallback.
85
+ BISECTION = 1,
86
+ /// Illinois (modified regula falsi). Retained because it is the method the
87
+ /// legacy JavaScript eclipse scan used, so a like-for-like comparison
88
+ /// against the pre-module answer is possible without changing the method.
89
+ ILLINOIS = 2
90
+ }
91
+
92
+ /// Which way g crosses zero. SIGNED on purpose: a crossing qualifies when
93
+ /// `direction * (g_after - g_before) > 0`, so the filter is arithmetic rather
94
+ /// than a branch table, and ANY = 0 falls out of the same expression.
95
+ enum CrossingDirection : byte (abi, abi_c_name: "OrbProCrossingDirection", abi_c_prefix: "ORBPRO_CROSSING_") {
96
+ /// g decreasing through zero — an interval OPENS for the usual
97
+ /// "g > 0 means outside" convention (eclipse entry, AOS, FOV entry).
98
+ FALLING = -1,
99
+ /// Either direction.
100
+ ANY = 0,
101
+ /// g increasing through zero — an interval CLOSES (eclipse exit, LOS).
102
+ RISING = 1
103
+ }
104
+
105
+ /// The sign of a scalar at an interval endpoint. Its only job is to make an
106
+ /// interval that is ALREADY OPEN at the scan start representable: a
107
+ /// crossing-only report loses that interval entirely, which is the classic
108
+ /// event-scan defect (the pass you were already inside when the scan began).
109
+ enum Sign : byte (abi, abi_c_name: "OrbProSign", abi_c_prefix: "ORBPRO_SIGN_") {
110
+ NEGATIVE = -1,
111
+ ZERO = 0,
112
+ POSITIVE = 1
113
+ }
114
+
115
+ /// How a refinement ended. NEVER folded into the returned root: a root that
116
+ /// stopped on the iteration cap and a root that converged are different
117
+ /// answers, and a consumer that cannot tell them apart will publish the first
118
+ /// as if it were the second.
119
+ enum RootStatus : ubyte (abi, abi_c_name: "OrbProRootStatus", abi_c_prefix: "ORBPRO_ROOT_STATUS_") {
120
+ /// |g| <= value_tolerance, or the bracket narrowed below epoch_tolerance.
121
+ CONVERGED = 0,
122
+ /// The iteration cap was reached with the bracket still wider than the
123
+ /// tolerance. The returned epoch is the best bracket midpoint.
124
+ MAX_ITERATIONS = 1,
125
+ /// g did not change sign across a bracket that the scan said it did — the
126
+ /// state source returned inconsistent values for the same epoch, or g is
127
+ /// not a function of epoch alone.
128
+ FLAT_BRACKET = 2,
129
+ /// g jumped across zero without passing through it within the epoch
130
+ /// tolerance: a discontinuity, not a root. Reported, never refined away.
131
+ DISCONTINUOUS = 3,
132
+ /// The scan hit `EventInterval.max_events` or `max_evaluations` and stopped
133
+ /// early. Everything reported is real; the list is not complete.
134
+ TRUNCATED = 4,
135
+ /// The bracket narrowed to the state source's declared epoch resolution
136
+ /// before it reached `epoch_tolerance_seconds`. The root is correct to that
137
+ /// resolution and NO FINER, and this is how the ABI says so instead of
138
+ /// reporting the tolerance it was asked for.
139
+ EPOCH_RESOLUTION_LIMITED = 5
140
+ }
141
+
142
+ /// EventHit.flags and EventInterval.flags are BITFIELDS carrying any
143
+ /// OR-combination of these, so both are declared `uint` on their structs
144
+ /// rather than typed to this enum. The C enumerators are generated from here
145
+ /// anyway — they are the contract.
146
+ enum EventFlags : uint (abi, abi_c_name: "OrbProEventFlags", abi_c_prefix: "ORBPRO_EVENT_") {
147
+ NONE = 0,
148
+ /// The component's interval was already open at the interval start, so this
149
+ /// hit is a CLOSING crossing whose opening epoch is before the scan.
150
+ INTERVAL_OPEN_AT_START = 1,
151
+ /// The component's interval is still open at the interval end.
152
+ INTERVAL_OPEN_AT_END = 2,
153
+ /// g touched zero and returned on the same side — a grazing event. Reported
154
+ /// as a hit with this flag rather than silently dropped, because a grazing
155
+ /// eclipse and no eclipse are different answers.
156
+ TANGENTIAL = 4,
157
+ /// The root sits within one epoch tolerance of an interval endpoint, so the
158
+ /// bracket is one-sided and the consumer should widen the interval to
159
+ /// confirm it.
160
+ ON_SCAN_BOUNDARY = 8,
161
+ /// The scan ran backward in time (`stop` earlier than `start`). Hits are
162
+ /// still reported in SCAN order, which is decreasing epoch.
163
+ BACKWARD = 16,
164
+ /// This hit satisfied a component declared `isStopCondition` — it is the
165
+ /// propagate-to-condition answer, not merely an observed crossing.
166
+ STOP_CONDITION = 32
167
+ }
168
+
169
+ /// Named negative return codes for every export in this family. A locator that
170
+ /// answers -1 for everything is unconformable: the consumer cannot place the
171
+ /// failure on the degradation ladder, so it cannot decide between retrying,
172
+ /// widening the interval, and refusing. Codes -1..-6 are deliberately the SAME
173
+ /// numbers and the same meanings as the propagator family's
174
+ /// (docs/propagator-abi.md §Error codes); the events-specific codes start at
175
+ /// -20 so no consumer can confuse the two tables by value.
176
+ enum EventError : int (abi, abi_c_name: "OrbProEventError", abi_c_prefix: "ORBPRO_EVENT_E_") {
177
+ /// The runner asked for a phase that does not exist. Internal; never
178
+ /// returned by a conformant locator.
179
+ INTERNAL = -25,
180
+ /// `plugin_event_supply` was called with a count that is not
181
+ /// `epoch_count * object_count` from the last `plugin_event_next`.
182
+ SUPPLY_COUNT_MISMATCH = -24,
183
+ /// `plugin_event_supply` was called when no request was outstanding, or
184
+ /// `plugin_event_next` twice without an intervening supply.
185
+ PROTOCOL_ORDER = -23,
186
+ /// A caller buffer was too small for the answer. The required element count
187
+ /// is available from the matching `*_count` export; nothing is truncated
188
+ /// silently.
189
+ BUFFER_TOO_SMALL = -22,
190
+ /// The configuration named a component, parameter or object this locator
191
+ /// does not implement. The name is reported through the description, never
192
+ /// coerced to a neighbour.
193
+ UNKNOWN_COMPONENT = -21,
194
+ /// `plugin_event_begin` has not been called, or the previous scan finished.
195
+ NOT_STARTED = -20,
196
+ /// A state, epoch or parameter is outside the domain where this locator's
197
+ /// geometry is defined (a station below the surface, e < 0, a cone
198
+ /// half-angle outside (0, pi)).
199
+ UNPHYSICAL = -6,
200
+ /// A refinement ended without converging AND the caller asked for strict
201
+ /// convergence. Ordinarily non-convergence is reported per hit in
202
+ /// `EventHit.status`, not as a call failure.
203
+ NOT_CONVERGED = -5,
204
+ /// A pointer, count or policy value is malformed (NaN tolerance, zero scan
205
+ /// step, `max_iterations` of 0).
206
+ BAD_INPUT = -4,
207
+ /// A required output pointer was null.
208
+ NULL_OUTPUT = -3,
209
+ /// An object index is outside `[0, object_count)`.
210
+ BAD_OBJECT_INDEX = -2,
211
+ /// `plugin_event_configure` has not been called, or it failed.
212
+ NOT_CONFIGURED = -1,
213
+ OK = 0
214
+ }
215
+
216
+ /// Whether a component's g is safe to refine, and with which method. A locator
217
+ /// that lies here produces roots that look converged and are not, which is why
218
+ /// it is declared per component rather than assumed for the family.
219
+ enum ComponentContinuity : ubyte (abi, abi_c_name: "OrbProComponentContinuity", abi_c_prefix: "ORBPRO_CONTINUITY_") {
220
+ /// g is C1 in epoch. Brent is valid.
221
+ SMOOTH = 0,
222
+ /// g is continuous but its derivative jumps (a piecewise mask, a
223
+ /// multi-body minimum). Bracketing methods are valid; interpolation is not
224
+ /// guaranteed to help, and the runner falls back to bisection.
225
+ PIECEWISE = 1,
226
+ /// g steps between levels and has no root in the analytic sense (a discrete
227
+ /// count, an enumerated state). The runner reports the STEP epoch to within
228
+ /// the scan step and marks it DISCONTINUOUS; it never refines it.
229
+ DISCRETE = 2
230
+ }
231
+
232
+ // =============================================================================
233
+ // THE ABI STRUCTS
234
+ // =============================================================================
235
+
236
+ /// The root-refinement policy — the SAME parameters for every locator, which
237
+ /// is what makes two locators' epochs comparable. 32 bytes, 8-byte aligned.
238
+ ///
239
+ /// A zeroed policy is NOT a default: `orbpro_root_policy_init` zeroes it and
240
+ /// `plugin_event_begin` refuses a zero `scan_step_seconds` with `BAD_INPUT`.
241
+ /// A silent default step is how a scan misses every event shorter than it.
242
+ struct RootPolicy (abi, abi_c_name: "OrbProRootPolicy", abi_c_helper_prefix: "orbpro_root_policy_") {
243
+ /// Coarse bracketing step, SECONDS, always positive. The runner applies the
244
+ /// interval's direction. This is the ONLY thing that decides which events
245
+ /// are found; the refinement decides only how precisely.
246
+ scan_step_seconds:double;
247
+ /// Convergence bar on the bracket width, SECONDS. Clamped up to the state
248
+ /// source's declared epoch resolution — see `EPOCH_RESOLUTION_LIMITED`.
249
+ epoch_tolerance_seconds:double;
250
+ /// Convergence bar on |g|, in the component's own declared unit. 0 disables
251
+ /// the value test and converges on the bracket alone.
252
+ value_tolerance:double;
253
+ /// Hard cap on refinement iterations for ONE root. Reaching it is reported
254
+ /// as `MAX_ITERATIONS`, never as a converged root.
255
+ max_iterations:uint;
256
+ /// Refinement algorithm.
257
+ method:RootMethod;
258
+ }
259
+
260
+ /// The interval to scan, and the filter applied to what is found. 56 bytes,
261
+ /// 8-byte aligned.
262
+ ///
263
+ /// EPOCHS ARE SPLIT PAIRS: `*_jd_day` is an exactly-representable Julian day
264
+ /// (an integer, or an integer + 0.5) and `*_seconds` is the offset from it in
265
+ /// seconds. Do not collapse them into one double before comparing — that is
266
+ /// the forty-microsecond quantization this pair exists to avoid.
267
+ ///
268
+ /// BACKWARD PROPAGATION IS NOT A MODE. It is `stop` earlier than `start`. The
269
+ /// runner steps negatively, the consumer's propagator is asked for earlier
270
+ /// epochs, and every hit carries `BACKWARD`. There is no second code path to
271
+ /// keep in agreement with the first.
272
+ struct EventInterval (abi, abi_c_name: "OrbProEventInterval", abi_c_helper_prefix: "orbpro_event_interval_") {
273
+ /// Scan start: exactly-representable Julian day.
274
+ start_jd_day:double;
275
+ /// Scan start: seconds from `start_jd_day`.
276
+ start_seconds:double;
277
+ /// Scan end: exactly-representable Julian day. May be EARLIER than the
278
+ /// start — that is backward propagation.
279
+ stop_jd_day:double;
280
+ /// Scan end: seconds from `stop_jd_day`.
281
+ stop_seconds:double;
282
+ /// Stop after this many qualifying hits. 0 = unbounded (bounded only by the
283
+ /// caller's hit buffer and by `max_evaluations`).
284
+ max_events:uint;
285
+ /// Hard cap on g evaluations for the whole scan, so a pathological
286
+ /// configuration terminates with `TRUNCATED` instead of running forever.
287
+ /// 0 = unbounded.
288
+ max_evaluations:uint;
289
+ /// Report only the Nth qualifying crossing, 1-based. 0 = report all. This is
290
+ /// GMAT's "which occurrence" on a stopping condition, and it counts
291
+ /// crossings that pass the direction filter, not raw sign changes.
292
+ occurrence:uint;
293
+ /// Restrict the scan to one component index. 0xFFFFFFFF = every component.
294
+ component:uint;
295
+ /// EventFlags bitfield. Input flags are advisory; the runner ORs its own
296
+ /// findings into each hit's flags.
297
+ flags:uint;
298
+ /// Crossing-direction filter applied to every component.
299
+ direction:CrossingDirection;
300
+ }
301
+
302
+ /// One refined crossing. 48 bytes, 8-byte aligned.
303
+ ///
304
+ /// A HIT IS A CROSSING, NOT AN INTERVAL. Apsides and node crossings are
305
+ /// instants; eclipse and contact are intervals. Emitting crossings and pairing
306
+ /// them by direction is total over both, whereas an interval-shaped hit puts a
307
+ /// sentinel in half the family. Pairing is deterministic and is specified in
308
+ /// docs/events-abi.md §Interval pairing; `EventScanSummary` carries the
309
+ /// endpoint signs that make the pairing total.
310
+ struct EventHit (abi, abi_c_name: "OrbProEventHit", abi_c_helper_prefix: "orbpro_event_hit_") {
311
+ /// Refined root: exactly-representable Julian day.
312
+ epoch_jd_day:double;
313
+ /// Refined root: seconds from `epoch_jd_day`.
314
+ epoch_seconds:double;
315
+ /// g at the reported root, in the component's declared unit. This is the
316
+ /// RESIDUAL and it is evidence: a "converged" root with a residual far
317
+ /// outside `value_tolerance` is a defect the consumer can see.
318
+ value:double;
319
+ /// Which component crossed.
320
+ component:uint;
321
+ /// Refinement iterations actually taken.
322
+ iterations:uint;
323
+ /// g evaluations spent on this root, scan samples included.
324
+ evaluations:uint;
325
+ /// EventFlags bitfield.
326
+ flags:uint;
327
+ /// The direction g crossed in.
328
+ direction:CrossingDirection;
329
+ /// How the refinement ended.
330
+ status:RootStatus;
331
+ }
332
+
333
+ /// What one component did across the whole interval. 32 bytes, 8-byte aligned.
334
+ ///
335
+ /// This is what makes a crossing list interpretable. Without the endpoint
336
+ /// signs, "no hits" is ambiguous between "never in eclipse" and "in eclipse the
337
+ /// entire time", and those are opposite answers.
338
+ struct EventScanSummary (abi, abi_c_name: "OrbProEventScanSummary", abi_c_helper_prefix: "orbpro_event_scan_summary_") {
339
+ /// g at the interval start, in the component's declared unit.
340
+ initial_value:double;
341
+ /// g at the interval end.
342
+ final_value:double;
343
+ /// Which component this summarizes.
344
+ component:uint;
345
+ /// Qualifying crossings found for this component.
346
+ crossing_count:uint;
347
+ /// g evaluations spent on this component.
348
+ evaluation_count:uint;
349
+ /// Sign of g at the interval start.
350
+ initial_sign:Sign;
351
+ /// Sign of g at the interval end.
352
+ final_sign:Sign;
353
+ }
354
+
355
+ /// What the locator wants evaluated next — the guest-to-consumer half of the
356
+ /// pull protocol. 16 bytes, 4-byte aligned.
357
+ ///
358
+ /// The consumer reads this, propagates the requested epochs through whatever
359
+ /// module is wired to the propagator port, and returns the states through
360
+ /// `plugin_event_supply`. The consumer chooses NOTHING: not the epochs, not
361
+ /// the order, not when the scan ends.
362
+ struct EventStateRequest (abi, abi_c_name: "OrbProEventStateRequest", abi_c_helper_prefix: "orbpro_event_state_request_") {
363
+ /// How many epochs were written to the caller's epoch buffer. The buffer
364
+ /// holds `2 * epoch_count` doubles: (jd_day, seconds) pairs, in scan order.
365
+ epoch_count:uint;
366
+ /// How many objects each epoch must be evaluated for. Every configured
367
+ /// object is evaluated at EVERY requested epoch — that is what makes a
368
+ /// multi-spacecraft (Formation) stop synchronized by construction rather
369
+ /// than by the consumer remembering to keep the epochs aligned.
370
+ object_count:uint;
371
+ /// The frame the returned states must be expressed in. An
372
+ /// `OrbProReferenceFrame` value; the consumer converts through the frames
373
+ /// port if its propagator emits another frame, and a consumer that cannot
374
+ /// must fail loudly rather than supply a differently-framed state.
375
+ reference_frame:orbpro.propagator.ReferenceFrame;
376
+ /// EventFlags bitfield describing the phase this request belongs to
377
+ /// (a coarse scan batch versus a single refinement abscissa).
378
+ flags:uint;
379
+ }
380
+
381
+ // =============================================================================
382
+ // THE WIRE TABLES
383
+ // =============================================================================
384
+ // Tables are messages, not ABI. Anything with a string or a variable-length
385
+ // vector lives here and crosses as a FlatBuffer through `plugin_event_describe`
386
+ // and `plugin_event_configure`, never as a raw struct.
387
+
388
+ /// One scalar event function the locator offers. `name` is the key a consumer
389
+ /// resolves through the SDS parameter-catalog record; this schema deliberately
390
+ /// does NOT carry a parameter roster of its own, because a second roster is a
391
+ /// second source of truth.
392
+ table EventComponent {
393
+ /// Index used by every ABI struct in this file. Dense, 0-based, stable for
394
+ /// the lifetime of one configuration.
395
+ componentId:uint = 0;
396
+ /// The catalog parameter or locator-defined component name.
397
+ name:string;
398
+ /// The unit `value`, `goalValue` and `valueTolerance` are expressed in.
399
+ unit:string;
400
+ /// Capability-class description. Never a vendor or organization name.
401
+ description:string;
402
+ /// For a stop condition, g = parameter - goalValue. For a pure locator
403
+ /// component (an eclipse shadow function) the goal is 0 and g is the
404
+ /// geometry itself.
405
+ goalValue:double = 0.0;
406
+ /// Direction filter for this component, ANDed with the interval's.
407
+ direction:orbpro.events.CrossingDirection = ANY;
408
+ /// 1-based occurrence for this component. 0 = every occurrence.
409
+ occurrence:uint = 0;
410
+ /// True when this component is a propagate-to-condition stop rather than an
411
+ /// observation. Hits from it carry `STOP_CONDITION`.
412
+ isStopCondition:bool = false;
413
+ /// Which configured object this component is about.
414
+ objectIndex:uint = 0;
415
+ /// The second object for a relative component (the station, the occulting
416
+ /// body, the observed body). Meaningful only when `usesTargetObject`.
417
+ targetObjectIndex:uint = 0;
418
+ /// Whether `targetObjectIndex` is used at all.
419
+ usesTargetObject:bool = false;
420
+ /// The frame this component's geometry is evaluated in.
421
+ referenceFrame:orbpro.propagator.ReferenceFrame = TEME;
422
+ /// Continuity class, which decides whether interpolation is admissible.
423
+ continuity:orbpro.events.ComponentContinuity = SMOOTH;
424
+ }
425
+
426
+ /// A named scalar or string the locator declares it accepts. This is how a
427
+ /// locator is configured WITHOUT a schema per locator: the keys are published
428
+ /// in the description, so the configuration is self-describing rather than
429
+ /// free-form.
430
+ table EventParameter {
431
+ key:string;
432
+ value:double = 0.0;
433
+ text:string;
434
+ }
435
+
436
+ /// What `plugin_event_describe` returns. A consumer reads this BEFORE
437
+ /// configuring, so a generic runner can present any locator's controls without
438
+ /// knowing the locator.
439
+ table EventLocatorDescription {
440
+ /// Capability class of the locator (shadow-crossing, station-access,
441
+ /// field-of-view-intrusion, orbit-apsis, node-crossing, parameter-stop).
442
+ /// Capability classes only; never a vendor or organization name.
443
+ locatorClass:string;
444
+ /// The locator module's own version string.
445
+ locatorVersion:string;
446
+ /// Objects this locator needs states for, in the order
447
+ /// `plugin_event_supply` expects them.
448
+ objectCount:uint = 0;
449
+ /// Every component this locator can offer, before configuration narrows it.
450
+ components:[EventComponent];
451
+ /// The parameter keys this locator accepts, with their defaults.
452
+ acceptedParameters:[EventParameter];
453
+ /// The policy the locator recommends for its own geometry. A consumer may
454
+ /// override it; a consumer that does NOT set one gets this.
455
+ defaultPolicy:orbpro.events.RootPolicy;
456
+ /// The largest scan step, SECONDS, at which this locator claims not to miss
457
+ /// an event. 0 means the locator declines to bound it, which a conformance
458
+ /// kit records as a named gap.
459
+ maxSafeScanStepSeconds:double = 0.0;
460
+ }
461
+
462
+ /// What `plugin_event_configure` accepts.
463
+ table EventLocatorConfig {
464
+ /// Must match the description's `locatorClass`, so a configuration written
465
+ /// for one locator cannot be silently accepted by another.
466
+ locatorClass:string;
467
+ /// Objects the consumer will supply states for.
468
+ objectCount:uint = 0;
469
+ /// The components to evaluate, in the order their indices will be used.
470
+ components:[EventComponent];
471
+ /// Locator-specific settings, keyed by names from `acceptedParameters`. An
472
+ /// unrecognized key is `UNKNOWN_COMPONENT`, never ignored.
473
+ parameters:[EventParameter];
474
+ /// The refinement policy for this scan.
475
+ policy:orbpro.events.RootPolicy;
476
+ /// The state source's epoch resolution in SECONDS — the smallest epoch
477
+ /// difference at which the consumer's propagator returns a different state.
478
+ /// For a propagator whose ABI takes a single float64 Julian date this is
479
+ /// ulp(jd) * 86400, about 4.02e-5 s at a 2026 epoch. The runner clamps
480
+ /// `epoch_tolerance_seconds` up to it and reports
481
+ /// `EPOCH_RESOLUTION_LIMITED`. 0 means "not declared", and the runner then
482
+ /// trusts the tolerance it was given — which is the only case where a
483
+ /// reported epoch can be finer than anything that was measured.
484
+ stateEpochResolutionSeconds:double = 0.0;
485
+ }
486
+
487
+ /// The scan result as a record, for a consumer that wants the answer as a
488
+ /// message rather than through the struct exports. The struct exports remain
489
+ /// authoritative; this is a projection of them, and the conformance kit asserts
490
+ /// the two agree exactly.
491
+ table EventScanResult {
492
+ hits:[orbpro.events.EventHit];
493
+ summaries:[orbpro.events.EventScanSummary];
494
+ evaluationCount:uint = 0;
495
+ /// The worst status of any hit, so a consumer can gate on one field.
496
+ status:orbpro.events.RootStatus = CONVERGED;
497
+ /// The clamped tolerance actually used, SECONDS.
498
+ effectiveEpochToleranceSeconds:double = 0.0;
499
+ }
500
+
501
+ root_type EventLocatorDescription;
@@ -41,31 +41,94 @@ attribute "abi_c_prefix";
41
41
  /// enum field. Writing these by hand is how padding bytes go stale.
42
42
  attribute "abi_c_helper_prefix";
43
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.
44
+ // !! FIVE INCOMPATIBLE `ReferenceFrame` VOCABULARIES CROSS THIS SEAM, PLUS AN
45
+ // !! AXIS-TYPE VOCABULARY THAT IS NOT A ReferenceFrame AT ALL !!
46
+ // This is the `orbpro.propagator` one, and it is the ONE that crosses the ABI.
47
+ // The NUMERIC values below are NOT interchangeable with any of the others;
48
+ // translate by NAMED TOKEN at every seam.
47
49
  //
48
- // (A) orbpro.propagator (THIS) TEME=0 J2000=1 ICRF=2 ECEF=3 MCI=4 MCMF=5
50
+ // (A) orbpro.propagator (THIS) TEME=0 J2000=1 ICRF=2 ECEF=3 MCI=4 MCMF=5,
51
+ // then the appended axis roster at 6+
49
52
  // (B) orbpro.plugins ECI=0 ECEF=1 TEME=2 ICRF=3
50
53
  // (PropagatorState.fbs / "PRST" — the wire the compiled sgp4, hpop,
51
54
  // conjunction-assessment and sensor-shader modules already emit)
52
- // (C) Cesium.ReferenceFrame FIXED=0 INERTIAL=1
55
+ // (C) OrbPro engine ReferenceFrame.js — NINE members, not two:
56
+ // FIXED=0 INERTIAL=1 TEME=2 VVLH=3 ENU=4 NED=5 NEU=6 RIC=7 LVLH=8
57
+ // (verified on OrbPro 417320c5c5. The long-standing claim in this block
58
+ // that it was "Cesium.ReferenceFrame FIXED=0 INERTIAL=1" was stale, and
59
+ // stale in the direction that mattered: it hid seven live members.)
53
60
  // (D) ConjunctionCommon.fbs ECI=1
61
+ // (E) foundation/frames `AxisType` (space-data-network-modules) — an
62
+ // ORIENTATION RULE roster, mirroring SDS `rfmAxisType`. It is not a
63
+ // ReferenceFrame and must never be numerically equated with one: a
64
+ // coordinate system is an axis set AND an origin, and this enum carries
65
+ // no origin.
54
66
  //
55
67
  // ECI==0, TEME==0 and FIXED==0 all collide, and ECEF is 1 in (B) but 3 here.
56
68
  // (B)'s values are frozen by compiled WASM artifacts in the field, so
57
69
  // 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.
70
+ // graph/tasks/sdk-reference-frame-enum-unification.md, which also owns the
71
+ // (A)-vs-(C) inversion at 0/1. The reference pattern for a correct seam is
72
+ // propagator.hpop/index.js createSourceFromState.
60
73
  //
61
- // Ruling: graph/findings/official-harness-shapes.md §4.2 / §8.2
74
+ // The crosswalk between all of these is DATA, in
75
+ // schemas/orbpro/reference-frame-crosswalk.json, and
76
+ // scripts/check-reference-frame-uniqueness.mjs holds it to this enum.
77
+ //
78
+ // Ruling: graph/findings/official-harness-shapes.md §4.2 / §8.2; Janus consult
79
+ // 2026-08-30 (gmat-08-frames-and-state-representations).
80
+ //
81
+ // VALUES 0-5 ARE FROZEN AND ARE NEVER RENUMBERED. New members are APPENDED.
82
+ // ECEF/MCI/MCMF are body-bound legacy instances of the generic pair at 11/12 —
83
+ // ECEF is BODY_FIXED about the Earth, MCI is BODY_INERTIAL about Mars, MCMF is
84
+ // BODY_FIXED about Mars. They are retained, and the generic members are NOT a
85
+ // second spelling of them.
62
86
  enum ReferenceFrame : ubyte (abi, abi_c_name: "OrbProReferenceFrame", abi_c_prefix: "ORBPRO_FRAME_") {
63
87
  TEME = 0,
64
88
  J2000 = 1,
65
89
  ICRF = 2,
66
90
  ECEF = 3,
67
91
  MCI = 4,
68
- MCMF = 5
92
+ MCMF = 5,
93
+ /// Mean ecliptic and equinox of J2000. (The mean EQUATOR and equinox of
94
+ /// J2000 is J2000 = 1 and is not repeated here.)
95
+ MJ2000EC = 6,
96
+ /// Mean equator of date.
97
+ MOD = 7,
98
+ /// True equator of date.
99
+ TOD = 8,
100
+ /// Mean ecliptic of date.
101
+ MOE = 9,
102
+ /// True ecliptic of date.
103
+ TOE = 10,
104
+ /// Axes rotating with a named body, per its published rotation elements.
105
+ BODY_FIXED = 11,
106
+ /// Non-rotating axes on a named body's equator and prime meridian.
107
+ BODY_INERTIAL = 12,
108
+ /// Axes built from the relative geometry of two named objects. The three
109
+ /// live RTN triads — RIC, RSW and RTN — are THIS member with an axes spec,
110
+ /// not three members; so are LVLH and VVLH.
111
+ OBJECT_REFERENCED = 13,
112
+ /// Axes built by aligning one vector and constraining a second.
113
+ LOCAL_ALIGNED_CONSTRAINED = 14,
114
+ /// A named body's equatorial plane at the epoch.
115
+ EQUATOR = 15,
116
+ /// Solar-ecliptic magnetospheric (GSE).
117
+ GSE = 16,
118
+ /// Solar magnetospheric (GSM).
119
+ GSM = 17,
120
+ /// Local horizon axes at a surface site.
121
+ TOPOCENTRIC = 18,
122
+ /// Axes fixed by a body's spin axis and its direction to the Sun.
123
+ BODY_SPIN_SUN = 19,
124
+ /// Axes declared by a loaded ephemeris/orientation kernel.
125
+ SPICE_DEFINED = 20,
126
+ /// LEGACY, retained and NAMED rather than left implicit: mean equator of
127
+ /// date on the IAU-76/FK5 theory. Differs from MOD at the milliarcsecond
128
+ /// level; the two are not interchangeable.
129
+ MOD_FK5 = 21,
130
+ /// LEGACY, retained and NAMED: true equator of date on IAU-76/FK5.
131
+ TOD_FK5 = 22
69
132
  }
70
133
 
71
134
  /// StateVector.flags is a BITFIELD carrying any OR-combination of these, so it