aero-agent-roles 1.0.0
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.
- package/NOTICE +18 -0
- package/README.md +49 -0
- package/bin/aero-agent-roles.js +127 -0
- package/lib/catalog.js +97 -0
- package/lib/install.js +48 -0
- package/lib/mcp.js +199 -0
- package/manifest.json +516 -0
- package/package.json +44 -0
- package/roles/aerodynamics-engineer/ROLE.md +153 -0
- package/roles/aerodynamics-engineer/SOURCES.md +12 -0
- package/roles/aerodynamics-engineer/cli.py +143 -0
- package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
- package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
- package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
- package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
- package/roles/aircraft-design-engineer/ROLE.md +138 -0
- package/roles/aircraft-design-engineer/SOURCES.md +12 -0
- package/roles/aircraft-design-engineer/cli.py +152 -0
- package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
- package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
- package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
- package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
- package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
- package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
- package/roles/airworthiness-compliance-engineer/cli.py +146 -0
- package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
- package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
- package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
- package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
- package/roles/as9100-quality-auditor/ROLE.md +128 -0
- package/roles/as9100-quality-auditor/SOURCES.md +16 -0
- package/roles/as9100-quality-auditor/cli.py +136 -0
- package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
- package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
- package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
- package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
- package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
- package/roles/data-bus-avionics-engineer/cli.py +311 -0
- package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
- package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
- package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
- package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
- package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
- package/roles/do160-environmental-engineer/ROLE.md +136 -0
- package/roles/do160-environmental-engineer/SOURCES.md +30 -0
- package/roles/do160-environmental-engineer/cli.py +147 -0
- package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
- package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
- package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
- package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
- package/roles/do178c-cert-engineer/ROLE.md +120 -0
- package/roles/do178c-cert-engineer/SOURCES.md +16 -0
- package/roles/do178c-cert-engineer/cli.py +129 -0
- package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
- package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
- package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
- package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
- package/roles/do254-hardware-engineer/ROLE.md +127 -0
- package/roles/do254-hardware-engineer/SOURCES.md +19 -0
- package/roles/do254-hardware-engineer/cli.py +308 -0
- package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
- package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
- package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
- package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
- package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
- package/roles/engineering-analysis-engineer/ROLE.md +154 -0
- package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
- package/roles/engineering-analysis-engineer/cli.py +151 -0
- package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
- package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
- package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
- package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
- package/roles/flight-management-engineer/ROLE.md +160 -0
- package/roles/flight-management-engineer/SOURCES.md +30 -0
- package/roles/flight-management-engineer/cli.py +258 -0
- package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
- package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
- package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
- package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
- package/roles/flight-mechanics-engineer/ROLE.md +162 -0
- package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
- package/roles/flight-mechanics-engineer/cli.py +149 -0
- package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
- package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
- package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
- package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
- package/roles/flight-test-engineer/ROLE.md +137 -0
- package/roles/flight-test-engineer/SOURCES.md +24 -0
- package/roles/flight-test-engineer/cli.py +135 -0
- package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
- package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
- package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
- package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
- package/roles/flight-test-performance-engineer/ROLE.md +165 -0
- package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
- package/roles/flight-test-performance-engineer/cli.py +319 -0
- package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
- package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
- package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
- package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
- package/roles/gnc-engineer/ROLE.md +141 -0
- package/roles/gnc-engineer/SOURCES.md +35 -0
- package/roles/gnc-engineer/cli.py +141 -0
- package/roles/gnc-engineer/core/gnc_core.py +1074 -0
- package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
- package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
- package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
- package/roles/guidance-engineer/ROLE.md +156 -0
- package/roles/guidance-engineer/SOURCES.md +43 -0
- package/roles/guidance-engineer/cli.py +280 -0
- package/roles/guidance-engineer/core/guidance_core.py +820 -0
- package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
- package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
- package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
- package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
- package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
- package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
- package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
- package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
- package/roles/ndt-engineer/ROLE.md +157 -0
- package/roles/ndt-engineer/SOURCES.md +18 -0
- package/roles/ndt-engineer/cli.py +316 -0
- package/roles/ndt-engineer/core/ndt_core.py +1590 -0
- package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
- package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
- package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
- package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
- package/roles/numerical-analysis-engineer/ROLE.md +158 -0
- package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
- package/roles/numerical-analysis-engineer/cli.py +414 -0
- package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
- package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
- package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
- package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
- package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
- package/roles/propulsion-engineer/ROLE.md +163 -0
- package/roles/propulsion-engineer/SOURCES.md +15 -0
- package/roles/propulsion-engineer/cli.py +168 -0
- package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
- package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
- package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
- package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
- package/roles/space-systems-engineer/ROLE.md +161 -0
- package/roles/space-systems-engineer/SOURCES.md +49 -0
- package/roles/space-systems-engineer/cli.py +173 -0
- package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
- package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
- package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
- package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
- package/roles/state-estimation-engineer/ROLE.md +154 -0
- package/roles/state-estimation-engineer/SOURCES.md +27 -0
- package/roles/state-estimation-engineer/cli.py +326 -0
- package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
- package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
- package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
- package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
- package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
- package/roles/structures-loads-engineer/ROLE.md +171 -0
- package/roles/structures-loads-engineer/SOURCES.md +44 -0
- package/roles/structures-loads-engineer/cli.py +219 -0
- package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
- package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
- package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
- package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
- package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""cli.py - run the State Estimation Engineer role.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 cli.py build [--out <file.md>] [--dt <s>] [--steps <n>]
|
|
6
|
+
[--q <m2/s3>] [--r <m2>] [--alpha <a>]
|
|
7
|
+
[--bundle] [--no-dispatch] [--profile <p.json>]
|
|
8
|
+
# build the Navigation State Estimator Design Report
|
|
9
|
+
# --bundle also emit evidence/{model,gates,provenance}.json
|
|
10
|
+
# (docs/PROTOCOL.md v1)
|
|
11
|
+
# --profile apply a program/customer profile header
|
|
12
|
+
python3 cli.py check --file <file.md> # gate-check an existing report
|
|
13
|
+
|
|
14
|
+
The role ENGINE (core/state_estimation_core.py) does the work standalone:
|
|
15
|
+
complementary-filter attitude design, constant-velocity Kalman recursion,
|
|
16
|
+
alpha-beta (Benedict-Bordner/Kalata) gains, EKF linearization, RTS
|
|
17
|
+
smoothing, observability, and the evidence gates. When AeroSkills is
|
|
18
|
+
present (AEROSKILLS_DEV or ~/AeroSkills), the CLI dispatches the bound
|
|
19
|
+
estimation-filtering leaf logic files and cross-checks identical
|
|
20
|
+
computations (alpha-beta gains, Kalman recursion, EKF Jacobian, NEES,
|
|
21
|
+
effective sample size); agreement is recorded in provenance.json.
|
|
22
|
+
"""
|
|
23
|
+
import argparse
|
|
24
|
+
import importlib.util
|
|
25
|
+
import json
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
30
|
+
"core"))
|
|
31
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
32
|
+
"..", "..", "scripts"))
|
|
33
|
+
import state_estimation_core as core # noqa: E402
|
|
34
|
+
import evidence # noqa: E402
|
|
35
|
+
|
|
36
|
+
ROLE_SLUG = "state-estimation-engineer"
|
|
37
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
38
|
+
SKILLS_RELEASE = "v1.3.0+"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _load_logic(leaf: str):
|
|
42
|
+
"""Import the first *_logic.py under an AeroSkills leaf (or None)."""
|
|
43
|
+
logic_dir = os.path.join(AEROSKILLS, "skills", leaf, "scripts")
|
|
44
|
+
if not os.path.isdir(logic_dir):
|
|
45
|
+
return None
|
|
46
|
+
for lf in sorted(f for f in os.listdir(logic_dir)
|
|
47
|
+
if f.endswith("_logic.py")):
|
|
48
|
+
spec = importlib.util.spec_from_file_location(
|
|
49
|
+
"role_dispatch", os.path.join(logic_dir, lf))
|
|
50
|
+
mod = importlib.util.module_from_spec(spec)
|
|
51
|
+
try:
|
|
52
|
+
spec.loader.exec_module(mod)
|
|
53
|
+
return mod, lf
|
|
54
|
+
except Exception:
|
|
55
|
+
continue
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _dispatch_row(leaf, item, model, spec) -> dict | None:
|
|
60
|
+
"""Run one bound-leaf computation and compare with the core value.
|
|
61
|
+
|
|
62
|
+
spec: {function, skill_kwargs(item)->kwargs, select(result)->float,
|
|
63
|
+
core(item, model)->float, note} -- select turns the leaf
|
|
64
|
+
return value into the comparable scalar.
|
|
65
|
+
"""
|
|
66
|
+
loaded = _load_logic(leaf)
|
|
67
|
+
if loaded is None:
|
|
68
|
+
return None
|
|
69
|
+
mod, logic_file = loaded
|
|
70
|
+
if not hasattr(mod, spec["function"]):
|
|
71
|
+
return None
|
|
72
|
+
try:
|
|
73
|
+
skill_val = spec["select"](
|
|
74
|
+
getattr(mod, spec["function"])(**spec["skill_kwargs"](item)))
|
|
75
|
+
core_val = spec["core"](item, model)
|
|
76
|
+
delta = abs(float(core_val) - float(skill_val))
|
|
77
|
+
return {
|
|
78
|
+
"leaf": leaf,
|
|
79
|
+
"skill_md": "SKILL.md",
|
|
80
|
+
"logic_file": "scripts/" + logic_file,
|
|
81
|
+
"function": spec["function"],
|
|
82
|
+
"dispatched": True,
|
|
83
|
+
"cross_check_point": spec["note"](item),
|
|
84
|
+
"core_value": round(float(core_val), 9),
|
|
85
|
+
"skill_value": round(float(skill_val), 9),
|
|
86
|
+
"delta": round(delta, 12),
|
|
87
|
+
"agrees": delta <= spec.get("tolerance", 1e-6),
|
|
88
|
+
"tolerance": spec.get("tolerance", 1e-6),
|
|
89
|
+
"skills_release": SKILLS_RELEASE,
|
|
90
|
+
}
|
|
91
|
+
except Exception as e: # noqa: BLE001 - dispatch must never crash build
|
|
92
|
+
return {"leaf": leaf, "function": spec["function"],
|
|
93
|
+
"dispatched": True, "error": str(e)[:150],
|
|
94
|
+
"agrees": False}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def dispatch_crosschecks(item, model) -> list:
|
|
98
|
+
"""Cross-check the core against all bound leaves with shared math."""
|
|
99
|
+
|
|
100
|
+
def ab_leaf(lam):
|
|
101
|
+
return {"leaf": "gnc-autonomy/estimation-filtering/alpha-beta-filter",
|
|
102
|
+
"function": "gains_from_tracking_index",
|
|
103
|
+
"skill_kwargs": lambda it: {"tracking_index": it.tracking_index()},
|
|
104
|
+
"select": lambda v: v["alpha"],
|
|
105
|
+
"core": lambda it, m: core.kalata_gains(it.tracking_index())[
|
|
106
|
+
"alpha"],
|
|
107
|
+
"note": lambda it: "Kalata alpha from lambda=%.6f"
|
|
108
|
+
% it.tracking_index(),
|
|
109
|
+
"tolerance": 1e-6}
|
|
110
|
+
|
|
111
|
+
def beta_bb(lam):
|
|
112
|
+
return {"leaf": "gnc-autonomy/estimation-filtering/alpha-beta-filter",
|
|
113
|
+
"function": "steady_state_gains",
|
|
114
|
+
"skill_kwargs": lambda it: {"alpha": it.alpha},
|
|
115
|
+
"select": lambda v: v["beta"],
|
|
116
|
+
"core": lambda it, m: core.steady_state_beta(it.alpha),
|
|
117
|
+
"note": lambda it: "Benedict-Bordner beta at alpha=%.3f"
|
|
118
|
+
% it.alpha,
|
|
119
|
+
"tolerance": 1e-6}
|
|
120
|
+
|
|
121
|
+
def kf_final(lam):
|
|
122
|
+
return {"leaf": "gnc-autonomy/estimation-filtering/rts-smoother",
|
|
123
|
+
"function": "forward_kalman",
|
|
124
|
+
"skill_kwargs": lambda it: {
|
|
125
|
+
"measurements": it.measurement_sequence(), "dt": it.dt,
|
|
126
|
+
"q": it.process_noise_intensity, "r": it.meas_variance,
|
|
127
|
+
"x0": [0.0, 0.0],
|
|
128
|
+
"p0": [[it.initial_pos_var, 0.0],
|
|
129
|
+
[0.0, it.initial_vel_var]]},
|
|
130
|
+
"select": lambda v: v[-1]["P_filt"][0][0],
|
|
131
|
+
"core": lambda it, m: m["kalman"]["final"]["pos_var_after"],
|
|
132
|
+
"note": lambda it: "final position variance, CV Kalman "
|
|
133
|
+
"recursion (step %d)" % it.horizon_steps,
|
|
134
|
+
"tolerance": 1e-6}
|
|
135
|
+
|
|
136
|
+
def ekf_h(lam):
|
|
137
|
+
op = [float(v) for v in item.ekf_op_point]
|
|
138
|
+
return {"leaf":
|
|
139
|
+
"gnc-autonomy/estimation-filtering/extended-kalman-filter",
|
|
140
|
+
"function": "jacobian_h",
|
|
141
|
+
"skill_kwargs": lambda it: {
|
|
142
|
+
"h": _skill_bearing_range, "x": op},
|
|
143
|
+
"select": lambda v: v[0][0],
|
|
144
|
+
"core": lambda it, m: m["ekf"]["H"][0][0],
|
|
145
|
+
"note": lambda it: "EKF Jacobian H[0][0] at the operating "
|
|
146
|
+
"point (range row)",
|
|
147
|
+
"tolerance": 1e-6}
|
|
148
|
+
|
|
149
|
+
def nees_chk(lam):
|
|
150
|
+
op = [float(v) for v in item.ekf_op_point]
|
|
151
|
+
p2 = [[item.initial_pos_var, 0.0], [0.0, item.initial_pos_var]]
|
|
152
|
+
return {"leaf":
|
|
153
|
+
"gnc-autonomy/estimation-filtering/unscented-kalman-filter",
|
|
154
|
+
"function": "nees",
|
|
155
|
+
"skill_kwargs": lambda it: {"x_est": op[:2], "p_est": p2,
|
|
156
|
+
"x_true": [op[0] - 5.0,
|
|
157
|
+
op[1] + 2.0]},
|
|
158
|
+
"select": lambda v: float(v),
|
|
159
|
+
"core": lambda it, m: core.nees(op[:2], p2,
|
|
160
|
+
[op[0] - 5.0, op[1] + 2.0]),
|
|
161
|
+
"note": lambda it: "NEES consistency sample on the "
|
|
162
|
+
"position subspace",
|
|
163
|
+
"tolerance": 1e-6}
|
|
164
|
+
|
|
165
|
+
def ess_chk(lam):
|
|
166
|
+
w = [1.0 / item.particle_count] * item.particle_count
|
|
167
|
+
return {"leaf": "gnc-autonomy/estimation-filtering/particle-filter",
|
|
168
|
+
"function": "effective_sample_size",
|
|
169
|
+
"skill_kwargs": lambda it: {"weights": w},
|
|
170
|
+
"select": lambda v: float(v),
|
|
171
|
+
"core": lambda it, m: core.effective_sample_size(w),
|
|
172
|
+
"note": lambda it: "ESS of a uniform %d-particle ensemble"
|
|
173
|
+
% it.particle_count,
|
|
174
|
+
"tolerance": 1e-9}
|
|
175
|
+
|
|
176
|
+
specs = [beta_bb(item), ab_leaf(item), kf_final(item), ekf_h(item),
|
|
177
|
+
nees_chk(item), ess_chk(item)]
|
|
178
|
+
rows = []
|
|
179
|
+
for s in specs:
|
|
180
|
+
row = _dispatch_row(s["leaf"], item, model, s)
|
|
181
|
+
if row:
|
|
182
|
+
rows.append(row)
|
|
183
|
+
return rows
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _skill_bearing_range(x):
|
|
187
|
+
"""Bearing/range measurement used only for the dispatch comparison
|
|
188
|
+
(same model as the extended-kalman-filter leaf)."""
|
|
189
|
+
import math
|
|
190
|
+
return [math.hypot(x[0], x[1]), math.atan2(x[1], x[0])]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _provenance(item, model, dispatch_rows):
|
|
194
|
+
return {
|
|
195
|
+
"role": ROLE_SLUG,
|
|
196
|
+
"core": {
|
|
197
|
+
"file": "core/state_estimation_core.py",
|
|
198
|
+
"functions": ["run_cv_kalman", "kalata_gains",
|
|
199
|
+
"steady_state_beta", "ekf_correct", "nees",
|
|
200
|
+
"effective_sample_size", "steady_state_verdict",
|
|
201
|
+
"rts_smooth", "observability_rank"],
|
|
202
|
+
"version": "0.1.0",
|
|
203
|
+
},
|
|
204
|
+
"skills": dispatch_rows,
|
|
205
|
+
"cross_checked": bool(dispatch_rows) and all(
|
|
206
|
+
r.get("dispatched") and not r.get("error") and r.get("agrees")
|
|
207
|
+
for r in dispatch_rows),
|
|
208
|
+
"disclaimer": "DRAFT for human review. Not an approval document.",
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def cmd_build(args):
|
|
213
|
+
item = core.example_item()
|
|
214
|
+
if args.dt:
|
|
215
|
+
item.dt = float(args.dt)
|
|
216
|
+
if args.steps:
|
|
217
|
+
item.horizon_steps = int(args.steps)
|
|
218
|
+
if args.q:
|
|
219
|
+
item.process_noise_intensity = float(args.q)
|
|
220
|
+
if args.r:
|
|
221
|
+
item.meas_variance = float(args.r)
|
|
222
|
+
if args.alpha:
|
|
223
|
+
item.alpha = float(args.alpha)
|
|
224
|
+
model = core.build_report(item)
|
|
225
|
+
md = core.render_report_markdown(model)
|
|
226
|
+
|
|
227
|
+
profile = None
|
|
228
|
+
try:
|
|
229
|
+
profile = evidence.load_profile(args.profile)
|
|
230
|
+
except ValueError as e:
|
|
231
|
+
print(f"error: bad profile: {e}")
|
|
232
|
+
return 1
|
|
233
|
+
if profile:
|
|
234
|
+
model["profile"] = {
|
|
235
|
+
"customer": profile.get("customer"),
|
|
236
|
+
"program": profile.get("program"),
|
|
237
|
+
"basis": profile.get("basis"),
|
|
238
|
+
"authority": profile.get("authority"),
|
|
239
|
+
"der": profile.get("der"),
|
|
240
|
+
"document_prefix": profile.get("document_prefix"),
|
|
241
|
+
"revision": profile.get("revision"),
|
|
242
|
+
}
|
|
243
|
+
md = evidence.profile_header_block(profile) + md
|
|
244
|
+
|
|
245
|
+
gates = core.check_report(model)
|
|
246
|
+
|
|
247
|
+
dispatch_rows = []
|
|
248
|
+
if not args.no_dispatch:
|
|
249
|
+
dispatch_rows = dispatch_crosschecks(item, model)
|
|
250
|
+
|
|
251
|
+
if args.out:
|
|
252
|
+
with open(args.out, "w") as f:
|
|
253
|
+
f.write(md if md.endswith("\n") else md + "\n")
|
|
254
|
+
print(f"built Navigation State Estimator Design Report -> {args.out}")
|
|
255
|
+
print(f"estimator class: {model['estimator_class']}")
|
|
256
|
+
print(f"kalman final gain: {model['kalman']['final']['gain']} "
|
|
257
|
+
f"pos var: {model['kalman']['final']['pos_var_after']:.4f} m^2")
|
|
258
|
+
print(f"gates: all_pass={gates['all_pass']} {gates}")
|
|
259
|
+
if args.bundle:
|
|
260
|
+
prov = _provenance(item, model, dispatch_rows)
|
|
261
|
+
paths = evidence.write_bundle(
|
|
262
|
+
args.out, ROLE_SLUG,
|
|
263
|
+
"Navigation State Estimator Design Report", model, gates,
|
|
264
|
+
prov)
|
|
265
|
+
print(f"bundle: model={paths['model']}")
|
|
266
|
+
print(f" gates={paths['gates']}")
|
|
267
|
+
print(f" provenance={paths['provenance']}")
|
|
268
|
+
for row in dispatch_rows:
|
|
269
|
+
if "error" in row:
|
|
270
|
+
print(f"cross-check: {row['leaf']} error: {row['error']}")
|
|
271
|
+
else:
|
|
272
|
+
print(f"cross-check: {row['leaf']} "
|
|
273
|
+
f"{row['function']} core={row['core_value']} "
|
|
274
|
+
f"skill={row['skill_value']} "
|
|
275
|
+
f"delta={row['delta']} agrees={row['agrees']}")
|
|
276
|
+
if not dispatch_rows:
|
|
277
|
+
print("cross-check: not dispatched (no AeroSkills logic)")
|
|
278
|
+
else:
|
|
279
|
+
print(md)
|
|
280
|
+
return 0 if gates["all_pass"] else 1
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def cmd_check(args):
|
|
284
|
+
if not os.path.exists(args.file):
|
|
285
|
+
print(f"file not found: {args.file}")
|
|
286
|
+
return 1
|
|
287
|
+
md = open(args.file).read()
|
|
288
|
+
gates = core.check_report_markdown(md)
|
|
289
|
+
for name, ok in gates.items():
|
|
290
|
+
if name != "all_pass":
|
|
291
|
+
print(f"{'PASS' if ok else 'FAIL'} {name}")
|
|
292
|
+
print(f"RESULT: {'PASS' if gates['all_pass'] else 'FAIL'}")
|
|
293
|
+
return 0 if gates["all_pass"] else 1
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def main():
|
|
297
|
+
p = argparse.ArgumentParser(
|
|
298
|
+
description="State Estimation Engineer role")
|
|
299
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
300
|
+
|
|
301
|
+
b = sub.add_parser("build")
|
|
302
|
+
b.add_argument("--out", default="", help="output file (default: stdout)")
|
|
303
|
+
b.add_argument("--dt", default="", help="nav update interval in s")
|
|
304
|
+
b.add_argument("--steps", default="", help="design horizon in steps")
|
|
305
|
+
b.add_argument("--q", default="", help="process noise intensity m^2/s^3")
|
|
306
|
+
b.add_argument("--r", default="", help="measurement variance m^2")
|
|
307
|
+
b.add_argument("--alpha", default="", help="alpha-beta smoothing factor")
|
|
308
|
+
b.add_argument("--bundle", action="store_true",
|
|
309
|
+
help="emit evidence/{model,gates,provenance}.json")
|
|
310
|
+
b.add_argument("--no-dispatch", action="store_true",
|
|
311
|
+
help="skip dispatching bound AeroSkills logic")
|
|
312
|
+
b.add_argument("--profile", default="",
|
|
313
|
+
help="program profile JSON (docs/PROFILE-SCHEMA.md)")
|
|
314
|
+
b.set_defaults(fn=cmd_build)
|
|
315
|
+
|
|
316
|
+
c = sub.add_parser("check")
|
|
317
|
+
c.add_argument("--file", required=True,
|
|
318
|
+
help="report markdown to gate-check")
|
|
319
|
+
c.set_defaults(fn=cmd_check)
|
|
320
|
+
|
|
321
|
+
args = p.parse_args()
|
|
322
|
+
sys.exit(args.fn(args))
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
if __name__ == "__main__":
|
|
326
|
+
main()
|