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,82 @@
|
|
|
1
|
+
# Performance and S&C Analysis Report
|
|
2
|
+
|
|
3
|
+
**Aircraft:** AeroLine AT-78 (example transport configuration, 150-seat class)
|
|
4
|
+
**Assessment basis:** FAR/CS Part 25 performance context (25.121/25.125/25.181) and MIL-STD-1797A mode criteria by analogy (aircraft class III (large transport, MIL-STD-1797A classing); flight phase categories B and C (cruise and approach)).
|
|
5
|
+
**Status:** draft-for-review
|
|
6
|
+
|
|
7
|
+
## 1. Mission performance
|
|
8
|
+
|
|
9
|
+
**Cruise point:** FL350 (ISA -54 degC), M0.78, V = 231.3 m/s (450 kt TAS), MTOW. CL = 0.603, CD = 0.0351, L/D = 17.2; (L/D)max = 17.5 at CL* = 0.74. Input basis: MTOW 78,000 kg, cruise altitude FL350, M0.78, clean configuration, example class data.
|
|
10
|
+
|
|
11
|
+
- **Still-air range (Breguet):** R = (V/(TSFC*g))*(L/D)*ln(W0/W1) = **6,492 km (3,506 nm)** burning 17,160 kg over the cruise segment (78,000 -> 60,840 kg). Breguet assumptions stated: constant L/D = 17.2, constant TSFC = 1.55e-05 kg/(N s), still air, cruise configuration.
|
|
12
|
+
- **Loiter endurance (Breguet):** E = (1/(TSFC*g))*(L/D)*ln(W0/W1) = **1.56 h** on 3,000 kg hold fuel at L/D = 17.2.
|
|
13
|
+
- **Specific air range:** SAR = V*(L/D)/(TSFC*W) = **335.0 m/kg** (0.335 km/kg); 5,000 km sector fuel ~ 14,925 kg. SAR curve (same weight, M = 0.74/0.78/0.82): 0.74 M -> 0.323 km/kg (L/D 17.4), 0.78 M -> 0.335 km/kg (L/D 17.2), 0.82 M -> 0.344 km/kg (L/D 16.8).
|
|
14
|
+
- **Climb (MTOW, clean, all engines):** ROC(SL) = (T-D)V/W = **17.7 m/s (3,491 ft/min)** at 250 KCAS; ROC(FL350) = **6.50 m/s (1,280 ft/min)** (climb thrust scaled by sigma^0.7); time to climb SL->FL350 at mean ROC = **14.7 min**. ROC at FL410 = 3.19 m/s (628 ft/min); linear-lapse ceiling estimate ~45.9k ft (indicative; nonlinear refinement is an open item).
|
|
15
|
+
- **Takeoff (SL ISA, MTOW):** Vs = 141 kt; V_LOF = V2 = 1.2*Vs = 169 kt; estimated ground roll Sg = 1.44*W^2/(g*rho*S*CLmax*(T-mu*W)) = **1,499 m** (mu = 0.03, CLmax = 1.9). The FAR 25.113 takeoff distance demonstration (airborne segment to 35 ft, 1.15 factor) is a flight-test determination - open item for the takeoff leaf build.
|
|
16
|
+
- **Landing (SL ISA, MLW):** Vs = 112 kt, Vref = 1.3*Vs = 145 kt, touchdown = 138 kt; air distance over 50 ft 365 m + ground roll 511 m = 877 m actual; certified field length (FAR 25.125, x1.67) = **1,464 m**.
|
|
17
|
+
- **Glide (engine-out reference):** best glide L/D = 17.5 at 227 kt EAS; from FL350: glide range 187 km, sink 6.6 m/s, 27 min to sea level.
|
|
18
|
+
- **Turn (25 deg bank, cruise, MTOW):** n = 1/cos(phi) = 1.103; turn rate 1.13 deg/s; radius 11.7 km; sustained (yes: D_turn = 49,148 N vs cruise available 58,000 N).
|
|
19
|
+
- **Wind effects (enroute):** 40 kt headwind lowers groundspeed 231 -> 211 m/s (6.00 h -> 6.59 h on 5,000 km).
|
|
20
|
+
|
|
21
|
+
## 2. Safety performance
|
|
22
|
+
|
|
23
|
+
- **OEI second segment (FAR 25.121(b), one engine inoperative at V2, gear up):** T_oei = 110,000 N, L/D = 10.8, gradient = (T_oei - D)/W = **5.13%** vs the 2-engine minimum of **2.4%** -> meets.
|
|
24
|
+
- **Energy height / time to climb:** cruise energy height h_e = h + V^2/2g = **13,396 m** (44k ft), kinetic term 2,728 m of zoom-climb reserve; specific excess power equals ROC (steady unaccelerated) by identity.
|
|
25
|
+
- **Wind / windshear effects:** enroute headwind case above (wind-effects leaf). Windshear assessment needs a shear profile (windshear-analysis leaf) - **open item**.
|
|
26
|
+
|
|
27
|
+
## 3. Rotorcraft
|
|
28
|
+
|
|
29
|
+
Not applicable to this example (fixed-wing transport). For a rotary-wing vehicle the rotorcraft performance leaves (hover OGE/IGE, forward flight, autorotation, tail-rotor sizing, vertical climb) replace this section.
|
|
30
|
+
|
|
31
|
+
## 4. Stability
|
|
32
|
+
|
|
33
|
+
- **Longitudinal static stability:** stick-fixed neutral point h_np = h_ac_w + V_h*(a_t/a_w)*(1-d_eps/d_alpha) = 0.25 + 0.862*0.72*0.55 = **0.591 MAC** (V_h = 0.862). Static margin SM = h_np - h_cg: **fwd CG 27.1%**, **aft CG 15.1%** (minimum margin band 0.05 MAC; positive SM = stable).
|
|
34
|
+
- **Directional stability (build):** V_v = 0.051; Cn_beta(vt) = eta*V_v*a_vt*(1+k_s) = 0.179; total Cn_beta = **0.120/rad** (fuselage term -0.059) - directionally stable (Cn_beta > 0).
|
|
35
|
+
- **Lateral stability (build):** dihedral term Cl_beta(gamma) = -CL*Gamma = -0.055 at cruise CL; total Cl_beta = **-0.080/rad** (remainder -0.025, class data) - laterally stable (Cl_beta < 0).
|
|
36
|
+
- **Trim (linear model):** cruise CL_trim = 0.603, alpha_trim = 6.3 deg, elevator to trim -3.0 deg (limit +/-25 deg); approach CL_trim = 1.420, elevator to trim -14.0 deg. Linear-model values; the stabilizer/elevator split and flap Cm increments are trim-analysis + AVL stage items (open item).
|
|
37
|
+
|
|
38
|
+
## 5. Dynamic modes
|
|
39
|
+
|
|
40
|
+
| Mode | Condition | Frequency | Damping | Requirement | Result |
|
|
41
|
+
|---|---|---|---|---|---|
|
|
42
|
+
| short period | cruise FL350 aft CG | 1.10 rad/s (0.17 Hz) | zeta = 0.31 | FAR 25.181 heavily damped (zeta >= 0.3 band); MIL-STD-1797A cat B L1 zeta 0.30-2.00, freq floor 2.0 rad/s (n_alpha 9.1 g/rad, table-clamped) | damping L1, freq-limited -> Level 2 by analogy (FAR heavy damping met) |
|
|
43
|
+
| short period | cruise FL350 fwd CG | 1.44 rad/s | zeta = 0.24 | as above | damping below L2 floor 0.25 -> Level 3 by analogy; fwd-CG cruise damping open item |
|
|
44
|
+
| short period | approach aft CG | 0.77 rad/s | zeta = 0.57 | FAR 25.181; cat C L1 zeta 0.30-2.00, freq floor 2.0 rad/s | damping L1 -> Level 2 by analogy (frequency-limited) |
|
|
45
|
+
| short period | approach fwd CG | 0.96 rad/s | zeta = 0.46 | as above | Level 2 by analogy |
|
|
46
|
+
| phugoid | cruise FL350 | 0.0600 rad/s (T = 105 s) | zeta = 0.041 | FAR 25.181 not growing (zeta > 0); 1797A L1 zeta >= 0.04 | Level 1 (t_half 281 s, 2.7 cycles) |
|
|
47
|
+
| phugoid | approach | 0.1855 rad/s (T = 34 s) | zeta = 0.085 | as above | Level 1 |
|
|
48
|
+
| dutch roll | cruise FL350 | 0.92 rad/s (T = 6.8 s) | zeta = 0.099 (zeta*omega = 0.090) | FAR 25.181 positively damped; 1797A cat B L1 zeta >= 0.08 & zeta*omega >= 0.15 | stable (FAR ok); Level 2 by analogy - yaw damper need (GNC boundary) |
|
|
49
|
+
| dutch roll | approach | 0.54 rad/s (T = 11.7 s) | zeta = 0.187 (product 0.101) | 1797A cat C L1 as above | Level 2 by analogy |
|
|
50
|
+
| roll subsidence | cruise FL350 | tau = 1.35 s | - | 1797A cat B L1 tau <= 1.4 s | Level 1 |
|
|
51
|
+
| roll subsidence | approach | tau = 1.30 s | - | 1797A cat C L1 tau <= 1.0 s | Level 2 |
|
|
52
|
+
| spiral | cruise FL350 | lambda = -0.00365 1/s | - | 1797A L1 T2 >= 12 s (cat B); stable root passes | convergent, Level 1 |
|
|
53
|
+
| spiral | approach | lambda = -0.01128 1/s | - | 1797A L1 T2 >= 20 s (cat C) | convergent, Level 1 |
|
|
54
|
+
|
|
55
|
+
Short-period/phugoid frequency separation (omega_sp/omega_ph): 18x cruise, 4.1x approach (>= 5x ideal for the two-timescale split; well separated at cruise, tighter at approach - a full 4-DOF longitudinal root solve is an open item).
|
|
56
|
+
|
|
57
|
+
## 6. Control effectiveness
|
|
58
|
+
|
|
59
|
+
- Elevator authority for trim: cruise -3.0 deg, approach -14.0 deg of +/-25 deg limits (linear model; margins acceptable, verify in the AVL/6DOF stage - open item).
|
|
60
|
+
- Aileron reversal: requires wing torsional stiffness and aileron geometry (aileron-reversal leaf) - **open item**.
|
|
61
|
+
- Deep stall: example has a low-mounted tailplane; deep-stall analysis (deep-stall-analysis leaf) remains a verification item for the stall-characteristics campaign - open item.
|
|
62
|
+
- Spin: no spin demonstration is required for the FAR Part 25 transport category; the spin-recovery leaf applies to Part 23-class vehicles.
|
|
63
|
+
|
|
64
|
+
## 7. Handling qualities
|
|
65
|
+
|
|
66
|
+
- Mode-based assessment (MIL-STD-1797A summary by analogy, class III): longitudinal modes damped at the aft-CG design point (zeta_sp 0.57 approach, 0.31 cruise); Dutch roll positively damped but below the Level 1 product criterion -> production fitment of a yaw damper is expected (SAS design is the GNC role boundary).
|
|
67
|
+
- FAR 25.181 context: short period heavily damped (zeta >= 0.3 band met at the aft CG), phugoid not growing in amplitude (zeta > 0) - satisfied at the analyzed conditions; formal compliance is a flight-test demonstration, not an analytic finding (boundary).
|
|
68
|
+
- Cooper-Harper ratings are pilot-in-the-loop observations: they are NOT assigned analytically. The cooper-harper-rating leaf structures the evaluation campaign (simulator/flight) - open item. PIO/pitch-bandwidth criteria likewise require the closed-loop response (6DOF + pilot model) - open item.
|
|
69
|
+
|
|
70
|
+
## 8. Simulation
|
|
71
|
+
|
|
72
|
+
- Trajectory evidence in this report is the analytic energy/climb set of sections 1-2 (specific excess power = rate of climb identity, energy height, time-to-climb at mean ROC).
|
|
73
|
+
- Point-mass trajectory and 6-DOF simulation runs (point-mass-trajectory / six-dof-simulation leaves, JSBSim class tools) plus the AVL derivative build (stability-derivatives-avl leaf) are the deeper stages that replace the class-range derivative data - open items with the input basis recorded above.
|
|
74
|
+
|
|
75
|
+
## 9. Conclusions and open items
|
|
76
|
+
|
|
77
|
+
- Performance vs requirement: computed still-air range 6,492 km, cruise ROC 17.7 m/s at SL, takeoff ground roll 1,499 m, certified landing field 1,464 m - all in the class range for a 150-seat twin-engine transport at the stated assumptions (Breguet assumptions for range, example thrust ratings, dry runway).
|
|
78
|
+
- S&C vs requirement: static margin 15.1% (aft CG) positive; short-period damping meets the FAR 25.181 heavy-damping band at the aft-CG design point (zeta 0.57 approach / 0.31 cruise); all modes stable.
|
|
79
|
+
- Open items: (1) replace example class-range inputs with project data; (2) AVL derivative build and wind-tunnel/flight-data correlation; (3) takeoff distance/accelerate-stop demonstration inputs (takeoff leaf); (4) windshear profile analysis; (5) 6DOF/JSBSim trajectory and closed-loop handling checks; (6) stabilizer scheduling and elevator authority verification; (7) fwd-CG cruise pitch damping margin; (8) yaw damper authority (GNC role).
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
*Generated by Aero Agent Roles flight-mechanics-engineer core (2026-09-05). Every number above carries its stated input basis (mass, CG, altitude, configuration, assumptions). DRAFT for human flight-mechanics review - engineering assessment, not an approval or certification finding.*
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Test flight_mechanics_core: the executable engine of the role.
|
|
3
|
+
|
|
4
|
+
Proves the role can DO its job standalone (no AeroSkills needed): the
|
|
5
|
+
domain rules produce REAL numbers (range, ROC, takeoff, static margin,
|
|
6
|
+
short-period damping) that are physically plausible, the builder produces
|
|
7
|
+
the full report model, the evidence gates pass, and the markdown
|
|
8
|
+
deliverable carries every gate marker.
|
|
9
|
+
"""
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import unittest
|
|
13
|
+
|
|
14
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
15
|
+
"..", "core"))
|
|
16
|
+
from flight_mechanics_core import ( # noqa: E402
|
|
17
|
+
ExampleTransport, analyze_vehicle, assess_dutch_roll,
|
|
18
|
+
assess_phugoid, assess_roll_mode, assess_short_period, assess_spiral,
|
|
19
|
+
breguet_range, build_report, c_m_alpha_from_sm, check_report,
|
|
20
|
+
check_report_markdown, cl_beta_dihedral, cn_beta_vertical_tail,
|
|
21
|
+
elevator_to_trim, example_report_markdown, example_vehicle,
|
|
22
|
+
far_25_121_minimum, isa, jet_endurance, landing_ground_roll, neutral_point,
|
|
23
|
+
oei_gradient_pct, oei_thrust, phugoid_damping, render_report_markdown,
|
|
24
|
+
roll_time_constant, short_period_damping, short_period_frequency,
|
|
25
|
+
specific_air_range, spiral_eigenvalue, spiral_stability_parameter,
|
|
26
|
+
stall_speed, static_margin, takeoff_ground_roll,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
G = 9.80665
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestDomainRules(unittest.TestCase):
|
|
33
|
+
"""Real formulas from the bound flight-mechanics leaves."""
|
|
34
|
+
|
|
35
|
+
def test_isa_atmosphere(self):
|
|
36
|
+
sl = isa(0.0)
|
|
37
|
+
self.assertAlmostEqual(sl["rho"], 1.225, places=3)
|
|
38
|
+
self.assertAlmostEqual(sl["p"], 101325.0, delta=1.0)
|
|
39
|
+
self.assertAlmostEqual(sl["sigma"], 1.0, places=6)
|
|
40
|
+
fl350 = isa(10668.0)
|
|
41
|
+
self.assertTrue(0.35 < fl350["rho"] < 0.41, fl350["rho"])
|
|
42
|
+
self.assertAlmostEqual(fl350["T"], 218.81, delta=0.5)
|
|
43
|
+
self.assertAlmostEqual(fl350["a"] * 0.78, 231.3, delta=2.0)
|
|
44
|
+
|
|
45
|
+
def test_breguet_range_hand(self):
|
|
46
|
+
# R = (V/(TSFC*g))*(L/D)*ln(m0/m1) in meters
|
|
47
|
+
r = breguet_range(231.3, 1.55e-5, 17.2, 78000.0, 60840.0)
|
|
48
|
+
expect = (231.3 / (1.55e-5 * G)) * 17.2 * \
|
|
49
|
+
__import__("math").log(78000.0 / 60840.0)
|
|
50
|
+
self.assertAlmostEqual(r, expect, places=1)
|
|
51
|
+
self.assertGreater(r / 1000.0, 5000.0) # multi-thousand km class
|
|
52
|
+
|
|
53
|
+
def test_breguet_rejects_no_fuel(self):
|
|
54
|
+
with self.assertRaises(ValueError):
|
|
55
|
+
breguet_range(231.3, 1.55e-5, 17.2, 78000.0, 78000.0)
|
|
56
|
+
|
|
57
|
+
def test_jet_endurance_hours(self):
|
|
58
|
+
e = jet_endurance(1.55e-5, 17.2, 62000.0 * G, 59000.0 * G)
|
|
59
|
+
self.assertGreater(e, 3000.0) # ~1.5 h loiter
|
|
60
|
+
self.assertLess(e, 20000.0)
|
|
61
|
+
|
|
62
|
+
def test_specific_air_range_units(self):
|
|
63
|
+
sar = specific_air_range(231.3, 1.55e-5, 78000.0 * G, 17.2)
|
|
64
|
+
self.assertTrue(100.0 < sar < 1000.0) # ~0.3 km/kg
|
|
65
|
+
|
|
66
|
+
def test_takeoff_rules(self):
|
|
67
|
+
vs = stall_speed(6119.0, 1.225, 1.9)
|
|
68
|
+
self.assertAlmostEqual(vs, 72.5, delta=1.0) # ~141 kt
|
|
69
|
+
sg = takeoff_ground_roll(78000.0 * G, 125.0, 220000.0, 1.225, 1.9)
|
|
70
|
+
self.assertTrue(1000.0 < sg < 2500.0, sg) # realistic ground roll
|
|
71
|
+
with self.assertRaises(ValueError):
|
|
72
|
+
takeoff_ground_roll(78000.0 * G, 125.0, 1000.0, 1.225, 1.9)
|
|
73
|
+
|
|
74
|
+
def test_landing_certified_factor(self):
|
|
75
|
+
res = analyze_vehicle(example_vehicle())
|
|
76
|
+
self.assertAlmostEqual(res["land_certified"],
|
|
77
|
+
res["land_actual"] * 1.67, places=3)
|
|
78
|
+
self.assertTrue(1000.0 < res["land_certified"] < 3000.0)
|
|
79
|
+
|
|
80
|
+
def test_oei_far_25_121(self):
|
|
81
|
+
self.assertEqual(far_25_121_minimum("second-segment", 2), 2.4)
|
|
82
|
+
self.assertEqual(far_25_121_minimum("approach", 2), 2.1)
|
|
83
|
+
self.assertEqual(far_25_121_minimum("landing", 4), 3.2)
|
|
84
|
+
t = oei_thrust(220000.0, 2, 1)
|
|
85
|
+
self.assertEqual(t, 110000.0)
|
|
86
|
+
res = analyze_vehicle(example_vehicle())
|
|
87
|
+
self.assertGreater(res["oei"]["gradient_pct"],
|
|
88
|
+
res["oei"]["min_pct"]) # meets 25.121(b)
|
|
89
|
+
|
|
90
|
+
def test_neutral_point_and_margin(self):
|
|
91
|
+
np_ = neutral_point(0.25, 0.862, 0.72, 0.45)
|
|
92
|
+
self.assertAlmostEqual(np_, 0.591, places=3)
|
|
93
|
+
self.assertAlmostEqual(static_margin(np_, 0.44), 0.1515, places=3)
|
|
94
|
+
with self.assertRaises(ValueError):
|
|
95
|
+
static_margin(1.2, 0.3)
|
|
96
|
+
|
|
97
|
+
def test_cm_alpha_sign(self):
|
|
98
|
+
self.assertAlmostEqual(c_m_alpha_from_sm(5.5, 0.1515), -0.833, places=3)
|
|
99
|
+
self.assertLess(c_m_alpha_from_sm(5.5, 0.15), 0.0)
|
|
100
|
+
|
|
101
|
+
def test_lateral_stability_build(self):
|
|
102
|
+
clb = cl_beta_dihedral(0.603, 5.2)
|
|
103
|
+
self.assertAlmostEqual(clb, -0.0547, places=3) # -CL*Gamma
|
|
104
|
+
cnb_vt = cn_beta_vertical_tail(0.95, 0.0512626, 3.4, 0.08)
|
|
105
|
+
self.assertAlmostEqual(cnb_vt, 0.1788, places=3)
|
|
106
|
+
aero = analyze_vehicle(example_vehicle())["aero"]
|
|
107
|
+
self.assertGreater(aero["cn_beta"], 0.0) # directionally stable
|
|
108
|
+
self.assertLess(aero["cl_beta"], 0.0) # laterally stable
|
|
109
|
+
|
|
110
|
+
def test_short_period_hand_identity(self):
|
|
111
|
+
# short period from the dynamic-stability leaf formulas
|
|
112
|
+
qbar = 0.5 * 0.37949 * 231.3 ** 2
|
|
113
|
+
za = -(qbar * 125.0 * 5.5) / 78000.0
|
|
114
|
+
cm_a = c_m_alpha_from_sm(5.5, 0.1515)
|
|
115
|
+
ma = qbar * 125.0 * 3.4916 * cm_a / 3.4e6
|
|
116
|
+
mq = qbar * 125.0 * 3.4916 ** 2 * (-30.0) / (2 * 231.3 * 3.4e6)
|
|
117
|
+
om = short_period_frequency(za, ma, mq, 231.3)
|
|
118
|
+
zeta = short_period_damping(za, ma, mq, 231.3)
|
|
119
|
+
self.assertTrue(0.9 < om < 1.4, om)
|
|
120
|
+
self.assertTrue(0.2 < zeta < 0.45, zeta)
|
|
121
|
+
|
|
122
|
+
def test_phugoid_damping_small(self):
|
|
123
|
+
z = phugoid_damping(17.2)
|
|
124
|
+
self.assertAlmostEqual(z, 1.0 / (1.41421356 * 17.2), places=5)
|
|
125
|
+
self.assertGreater(z, 0.0)
|
|
126
|
+
|
|
127
|
+
def test_spiral_roll_dutch_roll_helpers(self):
|
|
128
|
+
# stable spiral criterion term > 0 -> convergent
|
|
129
|
+
self.assertGreater(spiral_stability_parameter(-1.8, 0.23, 0.84,
|
|
130
|
+
-0.14), 0.0)
|
|
131
|
+
lam = spiral_eigenvalue(-1.8, 0.23, 0.84, -0.14, -0.74, 231.3)
|
|
132
|
+
self.assertLess(lam, 0.0) # convergent spiral
|
|
133
|
+
with self.assertRaises(ValueError):
|
|
134
|
+
roll_time_constant(0.5) # L_p must be < 0
|
|
135
|
+
self.assertAlmostEqual(roll_time_constant(-0.74), 1.351, places=3)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TestFlyingQualitiesAssessors(unittest.TestCase):
|
|
139
|
+
"""Level criteria as summarized by the mil-std-1797a leaf."""
|
|
140
|
+
|
|
141
|
+
def test_short_period_level1(self):
|
|
142
|
+
r = assess_short_period(0.50, 3.5, "A", "III", 1.0)
|
|
143
|
+
self.assertEqual(r["level"], 1)
|
|
144
|
+
|
|
145
|
+
def test_short_period_frequency_degrade(self):
|
|
146
|
+
r = assess_short_period(0.50, 2.0, "A", "III", 3.0) # floor 6.0
|
|
147
|
+
self.assertEqual(r["level"], 2)
|
|
148
|
+
|
|
149
|
+
def test_short_period_band_boundary(self):
|
|
150
|
+
r = assess_short_period(0.31, 2.0, "B", "III", 1.0)
|
|
151
|
+
self.assertEqual(r["level"], 1) # cat B floor 0.30
|
|
152
|
+
|
|
153
|
+
def test_dutch_roll_levels(self):
|
|
154
|
+
self.assertEqual(assess_dutch_roll(0.1, 1.6, "B", "III"), 1)
|
|
155
|
+
self.assertEqual(assess_dutch_roll(0.1, 0.5, "B", "III"), 2)
|
|
156
|
+
self.assertEqual(assess_dutch_roll(-0.01, 0.5, "B", "III"), 3)
|
|
157
|
+
|
|
158
|
+
def test_phugoid_spiral_roll_levels(self):
|
|
159
|
+
self.assertEqual(assess_phugoid(0.041), 1)
|
|
160
|
+
self.assertEqual(assess_phugoid(0.02), 2)
|
|
161
|
+
self.assertEqual(assess_phugoid(-0.01), 3)
|
|
162
|
+
self.assertEqual(assess_spiral(-0.004, "B"), 1)
|
|
163
|
+
self.assertEqual(assess_roll_mode(1.3, "B"), 1)
|
|
164
|
+
self.assertEqual(assess_roll_mode(1.3, "C"), 2)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class TestAnalysisAndBuilder(unittest.TestCase):
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def setUpClass(cls):
|
|
171
|
+
cls.vehicle = example_vehicle()
|
|
172
|
+
cls.results = analyze_vehicle(cls.vehicle)
|
|
173
|
+
cls.model = build_report(cls.vehicle, cls.results)
|
|
174
|
+
|
|
175
|
+
def test_example_transport_realistic_class(self):
|
|
176
|
+
v = self.vehicle
|
|
177
|
+
self.assertTrue(100.0 < v.s < 200.0)
|
|
178
|
+
self.assertTrue(60000.0 < v.m_mtow < 100000.0)
|
|
179
|
+
self.assertTrue(0.25 < v.thrust_total_to / v.w_mtow < 0.35)
|
|
180
|
+
|
|
181
|
+
def test_range_number(self):
|
|
182
|
+
self.assertTrue(5500.0 < self.results["range_km"] < 8000.0,
|
|
183
|
+
self.results["range_km"])
|
|
184
|
+
|
|
185
|
+
def test_climb_roc_number(self):
|
|
186
|
+
self.assertTrue(10.0 < self.results["roc_sl"] < 30.0)
|
|
187
|
+
self.assertTrue(2.0 < self.results["roc_350"] < 12.0)
|
|
188
|
+
|
|
189
|
+
def test_takeoff_number(self):
|
|
190
|
+
self.assertTrue(1000.0 < self.results["to_ground_roll"] < 2500.0)
|
|
191
|
+
|
|
192
|
+
def test_static_margins(self):
|
|
193
|
+
sm_f = self.results["aero"]["sm_fwd"]
|
|
194
|
+
sm_a = self.results["aero"]["sm_aft"]
|
|
195
|
+
self.assertGreater(sm_f, sm_a)
|
|
196
|
+
self.assertGreater(sm_a, 0.05) # above the minimum margin band
|
|
197
|
+
|
|
198
|
+
def test_short_period_numbers(self):
|
|
199
|
+
ca = self.results["sp_cruise_aft"]
|
|
200
|
+
aa = self.results["sp_appr_aft"]
|
|
201
|
+
self.assertTrue(0.8 < ca["omega_sp"] < 2.0)
|
|
202
|
+
self.assertTrue(0.25 < ca["zeta_sp"] < 0.5)
|
|
203
|
+
self.assertTrue(0.5 < aa["omega_sp"] < 1.5)
|
|
204
|
+
self.assertTrue(0.4 < aa["zeta_sp"] < 0.9)
|
|
205
|
+
# aft CG short period damping exceeds the FAR heavy-damping band
|
|
206
|
+
self.assertGreaterEqual(aa["zeta_sp"], 0.30)
|
|
207
|
+
|
|
208
|
+
def test_modes_all_stable(self):
|
|
209
|
+
self.assertGreater(self.results["phugoid_cruise"]["zeta"], 0.0)
|
|
210
|
+
self.assertGreater(self.results["lat_cruise"]["zeta_dr"], 0.0)
|
|
211
|
+
self.assertLess(self.results["lat_cruise"]["lambda_spiral"], 0.0)
|
|
212
|
+
self.assertGreater(self.results["lat_appr"]["zeta_dr"], 0.0)
|
|
213
|
+
|
|
214
|
+
def test_derivative_signs(self):
|
|
215
|
+
sp = self.results["sp_cruise_aft"]
|
|
216
|
+
self.assertLess(sp["za"], 0.0) # Z_alpha negative
|
|
217
|
+
self.assertLess(sp["ma"], 0.0) # M_alpha negative (stable)
|
|
218
|
+
self.assertLess(sp["mq"], 0.0) # M_q negative (damping)
|
|
219
|
+
la = self.results["lat_cruise"]
|
|
220
|
+
self.assertGreater(la["nb"], 0.0) # yaw stiffness
|
|
221
|
+
self.assertLess(la["lp"], 0.0) # roll damping
|
|
222
|
+
|
|
223
|
+
def test_elevator_trim_within_authority(self):
|
|
224
|
+
tr = self.results["trim_cruise"]
|
|
225
|
+
ta = self.results["trim_appr"]
|
|
226
|
+
lim = self.vehicle.elevator_limit_deg
|
|
227
|
+
self.assertLess(abs(tr["de_deg"]), lim)
|
|
228
|
+
self.assertLess(abs(ta["de_deg"]), lim)
|
|
229
|
+
|
|
230
|
+
def test_model_has_all_sections(self):
|
|
231
|
+
self.assertEqual(self.model["document_type"],
|
|
232
|
+
"Performance and S&C Analysis Report")
|
|
233
|
+
self.assertEqual(self.model["status"], "draft-for-review")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class TestGatesAndDeliverable(unittest.TestCase):
|
|
237
|
+
|
|
238
|
+
def test_core_gates_all_pass(self):
|
|
239
|
+
r = analyze_vehicle(example_vehicle())
|
|
240
|
+
gates = check_report(r)
|
|
241
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
242
|
+
|
|
243
|
+
def test_markdown_gates_all_pass(self):
|
|
244
|
+
md = example_report_markdown()
|
|
245
|
+
gates = check_report_markdown(md)
|
|
246
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
247
|
+
|
|
248
|
+
def test_rendered_sections(self):
|
|
249
|
+
md = example_report_markdown()
|
|
250
|
+
for sec in ["## 1. Mission performance", "## 4. Stability",
|
|
251
|
+
"## 5. Dynamic modes", "## 7. Handling qualities",
|
|
252
|
+
"## 9. Conclusions"]:
|
|
253
|
+
self.assertIn(sec, md)
|
|
254
|
+
self.assertIn("not an approval", md.lower())
|
|
255
|
+
self.assertIn("draft", md.lower())
|
|
256
|
+
|
|
257
|
+
def test_template_zero_blanks(self):
|
|
258
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
259
|
+
tmpl = os.path.join(here, "..", "templates",
|
|
260
|
+
"perf-sc-report-template.md")
|
|
261
|
+
with open(tmpl) as f:
|
|
262
|
+
text = f.read()
|
|
263
|
+
self.assertNotIn("___", text)
|
|
264
|
+
self.assertIn("AeroLine AT-78", text)
|
|
265
|
+
|
|
266
|
+
def test_standalone_no_skills_repo(self):
|
|
267
|
+
os.environ["AEROSKILLS_DEV"] = "/nonexistent"
|
|
268
|
+
v = example_vehicle()
|
|
269
|
+
r = analyze_vehicle(v)
|
|
270
|
+
md = render_report_markdown(build_report(v, r))
|
|
271
|
+
self.assertGreater(len(md), 5000)
|
|
272
|
+
self.assertTrue(check_report(r)["all_pass"])
|
|
273
|
+
self.assertTrue(check_report_markdown(md)["all_pass"])
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
if __name__ == "__main__":
|
|
277
|
+
unittest.main()
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Role test: flight-mechanics-engineer."""
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
ROLES_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
|
|
8
|
+
os.path.abspath(__file__)))))
|
|
9
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
10
|
+
HAS_SKILLS = os.path.isdir(os.path.join(AEROSKILLS, "skills"))
|
|
11
|
+
ROLE_DIR = os.path.join(ROLES_REPO, "roles", "flight-mechanics-engineer")
|
|
12
|
+
TEMPLATE = os.path.join(ROLE_DIR, "templates", "perf-sc-report-template.md")
|
|
13
|
+
|
|
14
|
+
EXPECTED_BOUND = [
|
|
15
|
+
"flight-mechanics/performance/breguet-range",
|
|
16
|
+
"flight-mechanics/performance/breguet-endurance",
|
|
17
|
+
"flight-mechanics/performance/specific-range",
|
|
18
|
+
"flight-mechanics/performance/climb-performance",
|
|
19
|
+
"flight-mechanics/performance/descent-performance",
|
|
20
|
+
"flight-mechanics/performance/glide-performance",
|
|
21
|
+
"flight-mechanics/performance/takeoff-performance",
|
|
22
|
+
"flight-mechanics/performance/landing-performance",
|
|
23
|
+
"flight-mechanics/performance/turn-performance",
|
|
24
|
+
"flight-mechanics/performance/thrust-required",
|
|
25
|
+
"flight-mechanics/performance/oei-climb-gradient",
|
|
26
|
+
"flight-mechanics/performance/energy-height",
|
|
27
|
+
"flight-mechanics/performance/speed-stability",
|
|
28
|
+
"flight-mechanics/performance/wind-effects",
|
|
29
|
+
"flight-mechanics/performance/windshear-analysis",
|
|
30
|
+
"flight-mechanics/performance/rotorcraft-hover-performance",
|
|
31
|
+
"flight-mechanics/performance/rotorcraft-forward-flight-performance",
|
|
32
|
+
"flight-mechanics/performance/rotorcraft-blade-element-hover-performance",
|
|
33
|
+
"flight-mechanics/performance/rotorcraft-autorotative-descent",
|
|
34
|
+
"flight-mechanics/performance/rotorcraft-tail-rotor-sizing",
|
|
35
|
+
"flight-mechanics/performance/rotorcraft-vertical-climb-performance",
|
|
36
|
+
"flight-mechanics/stability-control/longitudinal-stability",
|
|
37
|
+
"flight-mechanics/stability-control/lateral-directional-stability",
|
|
38
|
+
"flight-mechanics/stability-control/dynamic-stability",
|
|
39
|
+
"flight-mechanics/stability-control/short-period-mode-analysis",
|
|
40
|
+
"flight-mechanics/stability-control/phugoid-mode-analysis",
|
|
41
|
+
"flight-mechanics/stability-control/trim-analysis",
|
|
42
|
+
"flight-mechanics/stability-control/stability-derivatives-avl",
|
|
43
|
+
"flight-mechanics/stability-control/control-surface-effectiveness",
|
|
44
|
+
"flight-mechanics/stability-control/aileron-reversal",
|
|
45
|
+
"flight-mechanics/stability-control/deep-stall-analysis",
|
|
46
|
+
"flight-mechanics/stability-control/spin-recovery",
|
|
47
|
+
"flight-mechanics/handling-qualities/cooper-harper-rating",
|
|
48
|
+
"flight-mechanics/handling-qualities/mil-std-1797a",
|
|
49
|
+
"flight-mechanics/handling-qualities/pilot-induced-oscillation",
|
|
50
|
+
"flight-mechanics/handling-qualities/pitch-bandwidth-criteria",
|
|
51
|
+
"flight-mechanics/flight-dynamics-sim/point-mass-trajectory",
|
|
52
|
+
"flight-mechanics/flight-dynamics-sim/six-dof-simulation"
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
STAGES = [
|
|
56
|
+
"1. Range/endurance",
|
|
57
|
+
"2. Mission performance",
|
|
58
|
+
"3. Safety performance",
|
|
59
|
+
"4. Rotorcraft performance",
|
|
60
|
+
"5. Static stability",
|
|
61
|
+
"6. Dynamic stability",
|
|
62
|
+
"7. Control effectiveness",
|
|
63
|
+
"8. Handling qualities",
|
|
64
|
+
"9. Simulation",
|
|
65
|
+
"10. Report"
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def role_text():
|
|
70
|
+
p = os.path.join(ROLE_DIR, "ROLE.md")
|
|
71
|
+
if not os.path.exists(p):
|
|
72
|
+
return None
|
|
73
|
+
with open(p) as f:
|
|
74
|
+
return f.read()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TestFlightMechanicsEngineerRole(unittest.TestCase):
|
|
78
|
+
|
|
79
|
+
def test_bound_skills_resolve(self):
|
|
80
|
+
if not HAS_SKILLS:
|
|
81
|
+
self.skipTest("skills checkout absent (cross-repo dev check)")
|
|
82
|
+
for leaf in EXPECTED_BOUND:
|
|
83
|
+
self.assertTrue(
|
|
84
|
+
os.path.exists(os.path.join(AEROSKILLS, "skills", leaf, "SKILL.md")),
|
|
85
|
+
f"bound skill missing: {leaf}")
|
|
86
|
+
|
|
87
|
+
def test_stages_ordered(self):
|
|
88
|
+
role = role_text()
|
|
89
|
+
self.assertIsNotNone(role)
|
|
90
|
+
wf = role[role.find("## Workflow"):role.find("## Evidence gates")]
|
|
91
|
+
idx = [wf.find(s) for s in STAGES]
|
|
92
|
+
self.assertTrue(all(i >= 0 for i in idx), "stage missing in workflow")
|
|
93
|
+
self.assertEqual(idx, sorted(idx), "stage order not deterministic")
|
|
94
|
+
|
|
95
|
+
def test_template_present(self):
|
|
96
|
+
self.assertTrue(os.path.exists(TEMPLATE), "deliverable template missing")
|
|
97
|
+
|
|
98
|
+
def test_template_is_filled_deliverable(self):
|
|
99
|
+
"""100% standard: the template IS a complete worked example report
|
|
100
|
+
(zero blank ___, all sections, gate markers)."""
|
|
101
|
+
with open(TEMPLATE) as f:
|
|
102
|
+
text = f.read()
|
|
103
|
+
self.assertNotIn("___", text)
|
|
104
|
+
for n in range(1, 10):
|
|
105
|
+
self.assertTrue(
|
|
106
|
+
re.search(rf"^## {n}\. ", text, re.M),
|
|
107
|
+
f"report section {n} missing")
|
|
108
|
+
low = text.lower()
|
|
109
|
+
self.assertIn("draft", low)
|
|
110
|
+
self.assertIn("not an approval", low)
|
|
111
|
+
self.assertIn("aeroline at-78", low)
|
|
112
|
+
# key computed numbers present (range, ROC, static margin, damping)
|
|
113
|
+
self.assertRegex(text, r"\d[\d,]*\s*km")
|
|
114
|
+
self.assertRegex(text, r"static margin[^\n]*\d+\.\d+%")
|
|
115
|
+
self.assertRegex(text, r"zeta\s*=\s*\d+\.\d+")
|
|
116
|
+
|
|
117
|
+
def test_core_engine_exists(self):
|
|
118
|
+
"""100% standard: every role ships an executable core + cli."""
|
|
119
|
+
for f in ["core", "cli.py"]:
|
|
120
|
+
self.assertTrue(os.path.exists(os.path.join(ROLE_DIR, f)),
|
|
121
|
+
f"{f} missing - role is not executable")
|
|
122
|
+
|
|
123
|
+
def test_core_test_file_exists(self):
|
|
124
|
+
core_test = os.path.join(ROLE_DIR, "tests",
|
|
125
|
+
"test_flight_mechanics_core.py")
|
|
126
|
+
self.assertTrue(os.path.exists(core_test),
|
|
127
|
+
"core test file missing")
|
|
128
|
+
|
|
129
|
+
def test_boundaries(self):
|
|
130
|
+
role = role_text()
|
|
131
|
+
self.assertIsNotNone(role)
|
|
132
|
+
for phrase in ["sign_off_required: true", "forbidden"]:
|
|
133
|
+
self.assertIn(phrase, role)
|
|
134
|
+
|
|
135
|
+
def test_sources(self):
|
|
136
|
+
self.assertTrue(os.path.exists(os.path.join(ROLE_DIR, "SOURCES.md")),
|
|
137
|
+
"SOURCES.md missing")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
unittest.main()
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: role
|
|
3
|
+
name: flight-test-engineer
|
|
4
|
+
title: "Flight Test Engineer"
|
|
5
|
+
status: draft
|
|
6
|
+
domain: flight-test-operations
|
|
7
|
+
deliverable_type: "flight test plan + envelope expansion report"
|
|
8
|
+
standards_bound:
|
|
9
|
+
- id: far-25
|
|
10
|
+
tier: TIER-1
|
|
11
|
+
reference-only: false
|
|
12
|
+
- id: mil-std-1797a
|
|
13
|
+
tier: TIER-1
|
|
14
|
+
reference-only: false
|
|
15
|
+
skills_bound:
|
|
16
|
+
- flight-test-operations/planning/flight-test-safety
|
|
17
|
+
- flight-test-operations/envelope/envelope-expansion
|
|
18
|
+
- flight-test-operations/envelope/v-speeds
|
|
19
|
+
- flight-test-operations/envelope/load-factor-envelope
|
|
20
|
+
- flight-test-operations/envelope/stall-characteristics-testing
|
|
21
|
+
- flight-test-operations/envelope/high-angle-of-attack-testing
|
|
22
|
+
- flight-test-operations/envelope/buffet-boundary-testing
|
|
23
|
+
- flight-test-operations/envelope/spin-testing
|
|
24
|
+
- flight-test-operations/envelope/icing-flight-test
|
|
25
|
+
- flight-test-operations/envelope/vmc-determination
|
|
26
|
+
- flight-test-operations/envelope/flight-loads-survey
|
|
27
|
+
- flight-test-operations/envelope/structural-coupling-test
|
|
28
|
+
- flight-test-operations/flutter/ground-vibration-testing
|
|
29
|
+
- flight-test-operations/flutter/flight-vibration-survey
|
|
30
|
+
- flight-test-operations/flutter/flutter-testing
|
|
31
|
+
- flight-test-operations/flutter/limit-cycle-oscillation
|
|
32
|
+
- flight-test-operations/performance/accelerate-stop-distance
|
|
33
|
+
- flight-test-operations/performance/climb-performance-flight-test
|
|
34
|
+
- flight-test-operations/performance/cruise-performance-flight-test
|
|
35
|
+
- flight-test-operations/performance/engine-failure-takeoff-flight-test
|
|
36
|
+
- flight-test-operations/performance/engine-flight-test
|
|
37
|
+
tools_allowed: [stdlib, offline-file-processing]
|
|
38
|
+
forbidden:
|
|
39
|
+
- "clear an envelope"
|
|
40
|
+
- "declare airworthiness"
|
|
41
|
+
- "authorize flight"
|
|
42
|
+
- "reproduce proprietary standard text"
|
|
43
|
+
sign_off_required: true
|
|
44
|
+
license: Apache-2.0
|
|
45
|
+
compatibility: "agentskills.io ROLE.md; any SKILL.md host"
|
|
46
|
+
metadata:
|
|
47
|
+
version: 0.2.0
|
|
48
|
+
author: Aero Agent Roles
|
|
49
|
+
skills_release: "v1.3.0+"
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Flight Test Engineer
|
|
53
|
+
|
|
54
|
+
## Role identity
|
|
55
|
+
|
|
56
|
+
Owns the flight test campaign: plan safe, structured envelope expansion
|
|
57
|
+
and performance flights; define the test points, instrumentation,
|
|
58
|
+
build-up approach, and safety limits; reduce and report the data with
|
|
59
|
+
clear PASS/FAIL against requirements. Use when a vehicle must prove its
|
|
60
|
+
flight characteristics in the air (envelope, stall, flutter, V-speeds,
|
|
61
|
+
performance). Do NOT use for ground certification workflow, design
|
|
62
|
+
analysis (aero/structures), or control law design.
|
|
63
|
+
|
|
64
|
+
## Deliverable contract
|
|
65
|
+
|
|
66
|
+
1. **Flight test plan** - objectives, build-up blocks, test points,
|
|
67
|
+
instrumentation, safety limits, crew/airspace, data criteria.
|
|
68
|
+
2. **Envelope expansion report** - results per test (V-speeds, stall,
|
|
69
|
+
flutter, loads, icing, VMC), limits expanded with evidence, findings.
|
|
70
|
+
3. **Safety assessment** - risks + mitigations per test block.
|
|
71
|
+
|
|
72
|
+
## Workflow (stages -> bound skills)
|
|
73
|
+
|
|
74
|
+
| Stage | Loads skill | Produces |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| 1. Safety planning | flight-test-safety | risk assessment, limits, go/no-go |
|
|
77
|
+
| 2. Ground vibration | ground-vibration-testing | GVT results, mode clearance |
|
|
78
|
+
| 3. Flutter build-up | flight-vibration-survey + flutter-testing + limit-cycle-oscillation | flutter clearance per point |
|
|
79
|
+
| 4. V-speeds | v-speeds + vmc-determination | V-speeds determination |
|
|
80
|
+
| 5. Envelope expansion | envelope-expansion + load-factor-envelope | build-up block results |
|
|
81
|
+
| 6. Stall/alpha | stall-characteristics-testing + high-angle-of-attack-testing | stall margins |
|
|
82
|
+
| 7. Buffet/loads | buffet-boundary-testing + flight-loads-survey | buffet boundary, loads survey |
|
|
83
|
+
| 8. Spin/icing | spin-testing + icing-flight-test | spin/icing results |
|
|
84
|
+
| 9. Structural coupling | structural-coupling-test | coupling check |
|
|
85
|
+
| 10. Performance | accelerate-stop-distance + climb-performance-flight-test + cruise-performance-flight-test + engine-failure-takeoff-flight-test + engine-flight-test | performance results |
|
|
86
|
+
| 11. Report | (all above) | the test plan + expansion report |
|
|
87
|
+
|
|
88
|
+
## Evidence gates
|
|
89
|
+
|
|
90
|
+
- Stage 1 done = every test block has a stated limit + abort criteria.
|
|
91
|
+
- Stage 3 done = flutter tested to the planned build-up with no LCO
|
|
92
|
+
beyond limits.
|
|
93
|
+
- FINAL = each envelope limit change traces to a flight data point; no
|
|
94
|
+
clearance claimed beyond the data.
|
|
95
|
+
|
|
96
|
+
## Boundary / forbidden
|
|
97
|
+
|
|
98
|
+
- NEVER clear an envelope, declare airworthiness, or authorize flight.
|
|
99
|
+
The flight test CONDUCTOR / authority does that.
|
|
100
|
+
- Reports state "data supports expanding to X pending human sign-off",
|
|
101
|
+
never "envelope cleared". Every deliverable carries the explicit
|
|
102
|
+
"not a clearance" marker.
|
|
103
|
+
- Icing/spin results are test-specific; never generalize beyond the
|
|
104
|
+
tested configuration.
|
|
105
|
+
|
|
106
|
+
## Verification
|
|
107
|
+
|
|
108
|
+
The role runs STANDALONE: `core/flight_test_core.py` is an executable
|
|
109
|
+
engine that computes V-speeds (vref = 1.3*vs0, v2 = 1.2*vs1, vr =
|
|
110
|
+
1.1*vs1), the maneuvering speed VA = VS*sqrt(n_max), stall warning and
|
|
111
|
+
recovery checks, the flutter build-up with the 0.03 minimum damping and
|
|
112
|
+
1.2 x V_D margin, the altitude/Mach grid, the load factor envelope,
|
|
113
|
+
climb gradient check, and the safety/go-no-go register; BUILDS the
|
|
114
|
+
flight test plan + envelope expansion report; and gate-checks the
|
|
115
|
+
deliverable. No AeroSkills checkout required.
|
|
116
|
+
|
|
117
|
+
Run the role:
|
|
118
|
+
```bash
|
|
119
|
+
python3 cli.py build --out plan.md # worked-example aircraft
|
|
120
|
+
python3 cli.py check --file plan.md # gate-check a plan/report
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Tests:
|
|
124
|
+
- tests/test_flight_test_engineer_core.py (24 tests): domain rules +
|
|
125
|
+
builder + gates + standalone (no skills repo)
|
|
126
|
+
- tests/test_role_flight_test_engineer.py: bound-skill resolution
|
|
127
|
+
(skips if the skills repo is absent) + workflow + template filled +
|
|
128
|
+
executable core + boundaries
|
|
129
|
+
|
|
130
|
+
Bound skills in Aero Agent Skills deepen individual stages when the
|
|
131
|
+
library is present (safety, envelope expansion, flutter, stall,
|
|
132
|
+
performance methodology); the core engine does not depend on them.
|
|
133
|
+
|
|
134
|
+
## Compliance
|
|
135
|
+
|
|
136
|
+
- far-25 + mil-std-1797a context. Data references summary-not-copy.
|
|
137
|
+
- SOURCES.md records standards referenced.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# SOURCES.md - Flight Test Engineer
|
|
2
|
+
|
|
3
|
+
Standards and documents this role references. Summary-not-copy: see
|
|
4
|
+
[STANDARDS.md](../../STANDARDS.md) for the full rule and purchase links.
|
|
5
|
+
|
|
6
|
+
| Source | Role use | Gated |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| 14 CFR Part 25 (FAR-25, public domain) | V-speed factors (25.107/25.125 context), flutter margin and damping (25.629), stall warning and recovery (25.203/25.207), maneuvering speed (25.335), climb gradient context (25.121) | false |
|
|
9
|
+
| EASA CS-25 (free download) | same context as FAR-25 for European basis | false |
|
|
10
|
+
| MIL-STD-1797A | handling qualities evaluation during flight test | false |
|
|
11
|
+
| FAA Flight Test Guide (AC) | accepted flight test methodology | false |
|
|
12
|
+
| Kimberlin, Flight Testing of Fixed-Wing Aircraft | flight test build-up methods (book) | true |
|
|
13
|
+
| Ward, Introduction to Flight Test Engineering | methods (book) | true |
|
|
14
|
+
| ISA atmosphere (public physics) | altitude/Mach grid reduction (sigma, speed of sound) | false |
|
|
15
|
+
|
|
16
|
+
The flight test plan and envelope expansion report in `templates/` and
|
|
17
|
+
the numbers produced by `core/flight_test_core.py` come from the domain
|
|
18
|
+
rules encoded in the bound Aero Agent Skills flight-test-operations
|
|
19
|
+
leaves (v-speeds, envelope-expansion, flutter-testing,
|
|
20
|
+
stall-characteristics-testing, climb-performance-flight-test,
|
|
21
|
+
flight-test-safety, flight-test-planning), which themselves paraphrase
|
|
22
|
+
the FAR/CS-25 context and common flight test practice. Summary-not-copy
|
|
23
|
+
per STANDARDS.md; no proprietary text is reproduced. Worked-example
|
|
24
|
+
flight data in the template is explicitly labeled simulated.
|