Status: M2 draft, 2026-11-01. Subject: Zibo 2.S1.50 (zibomod lin_x64 + XLua scripts) as run by the
oracle (xphost/out/linux/lu737host). Everything here is described in our own words from reading the
decompiled zibomod, the Lua and oracle runs; no decompiled code is reproduced.
Notation as in electrics.md: Z: = Zibo dataref (laminar/B738/...), X: = X-Plane dataref/command
(sim/...), SCI: = stable cockpit interface state/control (data/sci/contract). Rescale(x1→y1, x2→y2, v)
is Zibo's clamped linear interpolation (outside [x1,x2] the end value), Limit(v, lo, hi) a clamp, and the
three animation helpers are defined in §5.1. dt = sim_period (frame time).
Key findings
- Zibo does not spool the APU. It runs an APU controller (start sequencing, EGT, LOW OIL / FAULT /
MAINT / OVERSPEED logic, generator and bleed availability, door, cool-down, fuel burn) around
X-Plane's APU, which it only commands (
X:electrical/APU_startheld 1 s, thenAPU_on;APU_off) and reads (X:cockpit2/electrical/APU_N1_percent,X:cockpit/engine/APU_running). - The "START only works after ~5–6 s at ON" observation (SCI_IMPLEMENTATION §3) is a start delay, not a
rejection. On START release Zibo waits
T_startbefore callingAPU_start;T_startis 17.5 s at TAT 15 °C / sea level, but is divided by 8 (2.2 s) when the APU LOW OIL PRESSURE light is already lit — and that light comes on 5.5 s after the switch reaches ON. A START given after 4 s at ON therefore callsAPU_start~17.5–21 s later, outside the 15 s window that was used for the measurement. Oracle:xphost/test/sci/apu_spec_delay_fault.json. The SCI profile's 7 s gate on START is not needed in the rewrite. - Zibo refuses to start when X-Plane's APU N1 is already ≥ 7 % (this is the "MSFS APU N1 ~20 % → Zibo never starts" effect of the old host). The rewrite must own the APU N1 (§7).
- APU generator availability (
Z:electrical/apu_bus_enable, →SCI:SRC_APU_GEN_RDY) comes 2 s after the APU has settled: EGT (Zibo's own) between 46 and 52·load, X-Plane running, N1 > 94 % — about 25 s after N1 reaches 100 % with the emulated spool of §8.
1 Scope and sources#
In scope: APU switch OFF/ON/START, start availability and sequence, LOW OIL PRESSURE / FAULT / MAINT /
OVERSPEED lights and the APU six-pack inputs, Zibo's APU EGT and the overhead EGT gauge, APU door,
cool-down and shutdown, fuel availability and fuel burn, APU bleed supply pressure and APU bleed valve,
APU generator availability (apu_bus_enable), APU failures and fire hooks, and the X-Plane APU the
rewrite must replace. Out of scope (other specs): switching the APU generator onto the buses, APU GEN OFF
BUS light, AC meter (electrics.md §4.2, §4.4, §4.5, §4.6, §4.7); duct pressures, packs, isolation valve,
apu_leak (pneumatics spec); engine start air (engine start spec); fire detection/bottles in general and the
master-caution latch mechanism (fire / annunciator specs; the APU-specific parts are summarised here).
| Source | Function / script | APU content |
|---|---|---|
zibomod |
Systems (block after the fuel-transfer code) |
start availability, switch-state machine, start delay, EGT, enable timers, failures, fuel-pump status, abort, shutdown, door, EGT gauge, light memories |
Systems (early, after Electric_bus) |
apu_establish_time, APU bleed supply pressure, APU bleed demand (apu_mode), EGT load factor, APU fuel flow, APU bleed valve target/position |
|
Annun_timers (start of Annunciators) |
count-down timers: start delay → APU_start, start-stop (1 s) → APU_on, LOW OIL (5.5 s), normal (8.5 s), cool-down, 5-min; fuel burn |
|
Annunciators |
lights apu_low_oil, apu_fault, maint_apu, over_speed_apu; APU six-pack inputs; APU fire light |
|
CMD_apu_starter_switch_up/dn, CMD_bleed_air_apu |
switch handlers | |
Fire, manage_eng_failure |
fire handle, extinguishing, X-Plane failure hooks | |
Pressurization |
X:cockpit2/bleedair/actuators/apu_bleed |
|
Lighting |
adds apu_start_load to both AC bus loads (electrics) |
|
Systems_start_flight, Systems_load/_save, MainFlightLoopCallback (panel-state string) |
initial values, situation restore | |
| Lua | B738.zz.sim_cockpit |
Z:switches/apu_start (0/1/2) convenience dataref → switch commands |
B738.tablet |
EFB failure pages: Z:failure/engine[18..22] (APU), failure/electric[19], failure/pressurisation[6, 15] |
|
B738.a_AXP_4_FMOD_logic |
sound only (APU bleed when N1 > 80 %): not in phase 1 | |
| acf | 737_80NG.acf |
_APU_spoolup_time 53, _APU_spooldn_time 18, _APU_cooldown_time 7, _APU_door_time 9, _APU_ff_kgh 0, _APU_fuel_src 0, _has_APU_bleed_air/_generator/_switch 1, _red_hi_N1_apu 105 |
| oracle | xphost/test/sci/apu_spec_start.json, apu_spec_delay_fault.json, apu_trace.json (frame trace) |
pass (27 + 12 checks); the rewrite passes the same scripts and matches the trace frame by frame (wasm/test/sci_script_runner.cpp) |
Frame order (whole plugin, see electrics.md §4.1): command handlers → reads → engines/starter →
Annunciators (Annun_timers first, then lights from the previous frame's APU indicator values) →
Fire → Switches → Lighting → Systems (Electric_bus, then the APU parts in the order of §4) →
Pressurization → manage_failure → manage_eng_failure.
2 Inputs#
2.1 Cockpit controls#
| SCI control | Zibo command (phase) | Effect |
|---|---|---|
APU_SW INC (to ON / START), APU_SW RELEASE from START |
Z:spring_toggle_switch/APU_start_pos_dn begin; end |
§4.1 |
APU_SW DEC |
…/APU_start_pos_up begin |
ON → OFF only |
APU_BLEED_AIR PRESS |
Z:toggle_switch/bleed_air_apu |
bleed_air_apu_pos 0 ↔ 1 |
ELEC_APUGEN1/2 |
Z:toggle_switch/apu_gen1/2_dn/up |
electrics.md §4.2 (needs apu_bus_enable) |
APU_FIRE_HANDLE_ARM PRESS, FIRE_HANDLE_DISCHARGE_3 INC/DEC |
Z:fire/apu/ext_switch_arm, …/ext_switch_L/_R |
fire handle pulled (Z:fire/apu/ext_switch/pos_arm, animated 0..1 at rate 10) and turned (pos_disch −1..1) |
| (hardware) | Z:switches/apu_start written 0/1/2 |
Lua relays a change into the switch commands: from OFF: 1 → dn once, 2 → dn once + dn held; from ON: 0 → up once, 2 → dn held; from START: 0 → release dn + up once, 1 → release dn |
2.2 Simulator and other-system inputs#
| Input | Use |
|---|---|
X:cockpit2/electrical/APU_N1_percent (N1) |
everything (§7: rewrite provides it) |
X:cockpit/engine/APU_running (RUN, 0/1) |
everything (§7) |
X:cockpit2/electrical/APU_generator_amps |
APU GEN OFF BUS (electrics) |
Z:electric/batbus_status |
APU controller power; start condition; abort when 0 |
Z:electric/dc_bus2_status |
keeps apu_bus_enable when the battery bus is off |
X:weather/aircraft/temperature_leadingedge_deg_c (XP12; XP11: X:weather/temperature_le_c) = TAT |
start delay, EGT targets, establish time |
X:flightmodel/position/elevation (m, elev_m) |
fuel availability (7620 m), start delay altitude factor (×3.281 → ft), fuel flow (×3.28) |
X:weather/aircraft/barometer_current_pas (XP12, Pa; as inHg p = Pa/3386.39) → alt_p = (1 − (p/29.92)^0.190284)·145366.45 ft |
APU bleed pressure. Correction (M3, oracle apu_trace.json): Zibo 2.S1.50 on XP12 reads the Pa dataref; with only barometer_current_inhg set, apu_psi_raw stays 0 and the bleed valve never opens |
X:flightmodel/weight/m_fuel[0..2] (L, C, R kg), X:cockpit2/fuel/fuel_tank_pump_on[0..2] |
fuel availability and burn |
Z:annunciator/low_fuel_press_l1/l2/c1/c2/r1/r2, Z:knob/cross_feed… (cross_feed_selector_knob), tank_c1_status |
APU fuel-pump status (fuel spec computes them) |
Z:…/l_pack_pos, r_pack_pos (0 OFF / 1 AUTO / 2 HIGH), engine starter valves, isolation valve, start_valve1/2, apu_leak |
APU bleed demand and pressure (pneumatics/engine-start specs) |
ac_power_bus[k] (3 = APU), X:cockpit2/electrical/bus_load_amps[k] |
EGT load factor (electrics) |
Z:failure/engine[18..22], failure/electric[19], failure/pressurisation[6], [15] |
§4.8 |
X:operation/failures/rel_apu_fire, rel_apu, rel_APU_press |
fire state, APU failed (read back) |
X:operation/prefs/startup_running |
initial switch positions (§3.2) |
3 State#
3.1 Variables#
| Name (Zibo global) | Type / values | Meaning |
|---|---|---|
sw = Z:spring_toggle_switch/APU_start_pos |
0 OFF, 1 ON, 2 START | switch |
sw_old |
0/1/2 | switch value at the end of the previous APU block (also set to 1 by the START-release handler) |
start_active |
0 idle, 1 start delay, 2 X-Plane starting, 3 running/established | start state machine |
shutdown_active |
0, 1 cool-down, 2 send APU_off |
shutdown state machine |
cool_idle |
0/1 | cool-down in progress |
start_avail, fuel_pump_status, fuel_tank_status |
0/1 | fuel/start availability (tank = previous frame's pump) |
T_start, T_start_stop, T_low_oil, T_normal, T_cool, T_5min, T_temp_init |
s, count down to 0 | timers (§5.3) |
start_meter |
s, counts up | time since START without reaching "running" |
bus_enable_tmr, duct_enable_tmr |
s | 2 s delays |
Z:electrical/apu_bus_enable |
0/1 | APU generator available |
duct_enable |
0/1 | APU bleed allowed |
egt (apu_temp) |
0..100 (≈ °C/10) | Zibo's APU EGT |
egt_tgt (apu_temp_target) |
persisted target (used by one branch, §4.6) | |
Z:electrical/apu_temp (gauge) |
0..100 | EGT needle (OBJ: 0..100 → 0..168°) |
temp_init |
0..3 | EGT-gauge power-up sweep state |
door_tgt, Z:electrical/apu_door |
0/1, 0..1 | APU inlet door |
run_old |
0/1 | RUN of the previous frame |
hot_start |
0/1 | only non-zero after a situation restore (§3.2) |
establish (apu_establish_time) |
0..1 | APU thermally established |
fault/maint/overspeed/low_oil × {raw, ind, tmr, tmr2} |
light logic (§4.7) | |
apu_mode, T_idle, both_packs_old, air_load |
bleed demand (§4.9) | |
load_egt, apu_ff |
EGT load factor, fuel flow kg/s | |
max_psi (rndm_max_apu_bleed_psi) |
uniform [45, 46] | drawn at flight start |
Z:air/apu/bleed_valve_pos, Z:air/apu_psi |
0..1, psi | bleed valve, APU bleed pressure |
T_fuel |
s | fuel-burn accumulator |
3.2 Initial values#
- Flight start (
Systems_start_flight):sw= 0,bleed_air_apu_pos= 0, APU GEN switches 0, X-Plane APU generator off,apu_power_bus1/2= 0 (both for cold-and-dark and engines-running starts);start_active,door_tgt,start_avail,fuel_pump_status,establish,cool_idle,hot_start,apu_mode,T_fuel, all lightind= 0;X:rel_APU_press:= 0;max_psidrawn;.datainitial valuesfuel_tank_status= 1,T_temp_init= 1.5. On the very first frameegt= max(0, TAT/10). - Situation restore (
Systems_load):start_active,cool_idle,hot_start,T_cool,T_start,T_normal,establish, switch positions andX:APU_runningare restored (Zibo writesRUN!). The rewrite restores the same fields; withhot_start= 1 the EGT window check of §4.5 is skipped. - Panel-state string (Zibo's saved switch positions): APU character '0' →
sw0; '1' →sw1, X-PlaneAPU_on,T_low_oil= 5.5; anything else →sw2.
4 Per-frame logic in execution order#
4.1 Switch handlers (between frames)#
APU_start_pos_up(begin): ifsw= 1 → 0. (No effect from START.)APU_start_pos_dnbegin: 0 → 1, 1 → 2.APU_start_pos_dnend (release from START): ifsw= 2:sw:= 1, then ifbatbus∧shutdown_active= 0 ∧start_active= 0 ∧RUN= 0: { ifstart_avail= 1:start_active:= 1; ifT_low_oil= 0 ∧low_oil_raw= 0:T_low_oil:= 5.5;sw_old:= 1 }. (Holding START has no other effect; the release is what starts the APU.)bleed_air_apu: togglesbleed_air_apu_pos.
4.2 Annun_timers (start of Annunciators)#
Each timer: if ≠ 0, subtract dt, floor at 0, and act when it reaches 0 in this frame:
T_cool→cool_idle:= 0.T_start_stop→ X-PlaneAPU_startend, thenAPU_ononce.T_start→ ifstart_avail= 1: X-PlaneAPU_startbegin,start_active:= 2,shutdown_active:= 0,T_start_stop:= 1; elsestart_active:= 0,T_start_stop:= 0.T_low_oil→ ifN1< 40:low_oil_raw:= 1.T_normal→start_active:= 3.T_5min→ (only counts down).- Fuel burn (§4.12).
4.3 Early part of Systems (after Electric_bus)#
- Established time: if
N1> 90: target 1 overRescale(−30→180 s, 40→60 s, Limit(TAT,−30,40))(102.9 s at 15 °C), else target 0 over 500 s;establishmoves linearly (set_animation_rate, §5.1). - APU bleed supply pressure:
base= max(0,max_psi·sin(Rescale(10000→90°, 80000→180°, alt_p)) −apu_leak); if (start_valve1> 0.1 ∨start_valve2> 0.1) ∧establish< 1:base−= (1 −establish)·25, floor 0;apu_psi_raw(bleedAir_apu_psi) =Rescale(0→0, 100→base, N1). - Demand and output pressure (§4.9), EGT load factor
load_egt(§4.10), fuel flowapu_ff(§4.12). - APU bleed valve (§4.9).
4.4 Start availability, light-memory gates (main APU block of Systems)#
- If
sw= 0 ∨batbus= 0: gates G = false; ifbatbus= 0 all fourindandtmr:= 0; for each light:tmr:= 0 ifind= 0, else count down. Otherwise: G = true and all fourtmr:= 300. T_amb= max(0, TAT/10).start_avail:= 1 iff fire handlepos_arm= 0 ∧batbus= 1 ∧failure/pressurisation[15]≠ 1 ∧N1< 7 ∧ ( (elev_m< 7620 ∧ L tank > 0) ∨ (fuel_pump_status= 1 ∧fuel_tank_status= 1) ).- If
batbus= 0: allraw,ind:= 0; fault/overspeed/low-oiltmr2:= 0.
4.5 Switch-state machine (first matching branch)#
sw= 0: ifsw_old≠ 0 ∧low_oil_ind∧ no oil failure (engine[21],[22]≠ 1):low_oil_ind:= 0 (LOW OIL goes out at once). Allraw:=ind;tmr2:= 0. IfRUN= 1: ifshutdown_active= 0:shutdown_active:= 1 and, ifgauge≤ 25:cool_idle:= 0,T_cool:= 0, elsecool_idle:= 1 andT_cool:= 58 if it was 0. IfRUN= 0:shutdown_active,cool_idle,T_cool:= 0. Thenapu_bus_enable, both enable timers,duct_enable,T_low_oil,hot_start,start_active,T_start,T_start_stop:= 0.sw= 1 ∧sw_old= 2 (START released, seen inSystems): ifbatbus∧shutdown_active= 0 ∧start_active= 0 ∧RUN= 0:start_active:= 1 ifstart_avail;T_low_oil:= 5.5 if it is 0 andlow_oil_raw= 0. (Duplicate of the handler path; normally the handler already did it.)sw= 1 ∧RUN∧shutdown_active≠ 0 (ON again during cool-down):start_active:= 3,shutdown_active,cool_idle,T_cool,hot_start:= 0.sw= 1 ∧start_active= 0 ∧batbus∧shutdown_active= 0 ∧RUN= 0 ∧T_low_oil= 0 ∧low_oil_raw= 0:T_low_oil:= 5.5 (this is what lights LOW OIL 5.5 s after selecting ON).
Then sw_old := sw; if start_active < 3: apu_bus_enable, both enable timers, duct_enable := 0.
4.6 Start phases and EGT while starting/running#
start_active= 1 ∧start_avail: ifT_start= 0:T_start=Rescale(0→28, 60→14, Limit(TAT+30, 0, 60))·Rescale(0→1, 28000→1.3, Limit(elev_m·3.281, 0, 28000)); iflow_oil_raw= 0:T_low_oil:= 5.5, elseT_start/= 8. Always:shutdown_active,cool_idle,T_cool,hot_start:= 0. (egtis not updated in this state.)- = 2:
egt_tgt= max(egt,N1);egt:= AnimValue(egt→egt_tgt, 0..100, rate 8); ifegt> 95 ∧T_normal= 0:T_normal:= 8.5.T_cool,cool_idle,hot_start:= 0. LOW OIL clear rule (below). - = 3 ∧
RUN:egt_tgt=Rescale(0→47, 4→51, Limit(T_amb, 0, 4)); ifegt_tgt≤egt∨egt≥ 46:egt:= AnimValue(→load_egt·egt_tgt, rate 0.2) else AnimValue(→egt_tgt, rate 0.5). Ifhot_start= 0: when 46 <egt< 52·load_egt∧RUN= 1 ∧N1> 94: startbus_enable_tmr= 2 (ifapu_bus_enable= 0 and the timer is 0) andduct_enable_tmr= 2 (ifduct_enable= 0 and timer 0). Ifhot_start= 1: start both timers unconditionally. LOW OIL clear rule;T_cool,cool_idle:= 0. - LOW OIL clear rule (states 2 and 3):
egt> 40 ∧RUN= 1 ∧N1> 40 →low_oil_raw:= 0. - If
batbus= 0 ∧dc_bus2= 0:apu_bus_enable:= 0, timer 0.bus_enable_tmrcounts down; at ≤ 0:apu_bus_enable:= 1.
4.7 Failures, start load, fuel status, FAULT#
- MAINT raw := 1 if (
apu_bus_enable= 1 ∧failure/electric[19]= 1) or (establish= 1 ∧failure/pressurisation[15]= 1). (MAINT raw is otherwise only cleared throughraw := indwith the switch OFF / battery off.) Ifpressurisation[15]= 1:X:rel_APU_press:= 6. - OVERSPEED (
engine[20]= 1): ifRUN= 1 ∧N1> 99:overspeed_raw:= 1,tmr2:= 30, abort; else ifRUN= 0 ∧tmr2= 0:overspeed_raw:= 0. - LOW OIL PRESSURE failure (
engine[22]= 1): if (RUN= 0 ∧start_active= 0 ∧shutdown_active= 0) ∨T_low_oil≠ 0:low_oil_raw:= 0 when itstmr2= 0; elselow_oil_raw:= 1 and abort ifRUN∧N1> 99. - LOW OIL QUANTITY failure (
engine[21]= 1) ∧sw≠ 0:low_oil_raw:= 1; abort ifRUN∧N1> 99. Z:electric/apu_start_load= 60 whileT_start≠ 0, else 0 (A, added to both AC bus loads).fuel_pump_status:= 1 if any of: (L1 or L2 low-pressure light out) ∧ L > 0; C1 light out ∧ C > 100 kg (crossfeed closed) or (C1 or C2 out) ∧ C > 100 (crossfeed open); (crossfeed open only) (R1 or R2 out) ∧ R > 0;elev_m< 7620 ∧ L > 0;RUN= 1 ∧ L > 0. If 0 → abort.- Start meter: if
start_active= 0: 0; else ifstart_active< 3 ∨RUN= 0: if ≤ 120 add dt, else {fault_raw:= 1,fault_tmr2:= 30 unless (start_active= 3 ∧run_old= 1); abort }; else 0. - If
start_active= 3 ∧run_old= 1 ∧RUN= 0 (APU stopped by itself):fault_raw:= 1,tmr2:= 30; else ifstart_active= 0 ∧shutdown_active= 0 ∧fault_tmr2= 0:fault_raw:= 0. run_old:=RUN;tmr2of fault/low-oil/overspeed count down;fuel_tank_status:=fuel_pump_status.
4.8 Abort, shutdown, door, enables#
- Abort if any "abort" above, or fire handle
pos_arm= 1, orbatbus= 0:shutdown_active,cool_idle,T_cool,start_active,hot_start,T_start,T_start_stop,T_low_oil:= 0;low_oil_raw:= 0 ifstart_meter> 120 orbatbus= 0;apu_bus_enable, its timer,door_tgt:= 0; ifRUN= 1: X-PlaneAPU_off. - Shutdown: if
shutdown_active= 1 ∧cool_idle= 0 → 2; else if = 2 → 0, X-PlaneAPU_off,hot_start:= 0. (SoAPU_offgoes out one frame after the cool-down ends, or two frames after OFF when the gauge EGT was ≤ 25.) door_tgt:= 0 ifRUN= 0 ∧start_active= 0, else 1.- If
shutdown_active≠ 0:start_active,hot_start,T_start,T_start_stop,T_low_oil,low_oil_raw,apu_bus_enable, its timer := 0. Ifstart_active≠ 0:shutdown_active,cool_idle,T_cool:= 0. - If
RUN= 0 ∨shutdown_active≠ 0:duct_enable, its timer := 0; elseduct_enable_tmrcounts down, at ≤ 0duct_enable:= 1. - If
sw≠ 0:T_5min:= 300.
Note: switching OFF while start_active = 2 and X-Plane not yet running resets start_active but sends no
APU_off; X-Plane keeps spooling (it got APU_on) until RUN = 1, when the OFF branch starts a normal
cool-down. Reproduce as is.
4.9 APU bleed (demand, pressure, valve, X-Plane actuator)#
starter = max(engine-1 starter valve, isolation valve · engine-2 starter valve). Pack combination:
both OFF → code 0, both AUTO → 1, else 2. Mode (first match): starter > 0 ∨ APU bleed switch OFF ∨ a pack
HIGH → mode 0 (factor 1, T_idle := 0); both AUTO → mode 1; both OFF → mode 2 (T_idle := 0 when the
pack code changed); otherwise mode 3 (factor 1, T_idle := 0). In modes 1/2 T_idle += dt up to 15 s and,
if apu_psi_raw − apu_leak ≥ 0.1 (else factor 1):
- mode 1: factor 1 for
T_idle< 2.5 s, then (21 +apu_leak)/apu_psi_raw; - mode 2: (38 + leak)/raw for < 2 s, (33 + leak)/raw for < 9.5 s, then (21 + leak)/raw.
air_load moves linearly toward the factor (0..1) at 0.2/s up, 0.06/s down.
Z:air/apu_psi = min(41, max(0, air_load·apu_psi_raw)) (the pneumatics spec uses it times the valve).
Valve target 1 iff APU bleed switch ON ∧ apu_psi_raw > 8 (bleed_valve_min_act_press) and none of:
pressurisation[15] = 1, fire handle pos_arm = 1, sw = 0, duct_enable = 0. Position:
AnimValue(→ target, 0..1, rate 10) unless failure/pressurisation[6] = 1 (valve frozen).
In Pressurization (after Systems): X:cockpit2/bleedair/actuators/apu_bleed = (valve > 0.05 ∧ switch ON).
Downstream users: duct pressures/packs/dual bleed (pneumatics), engine start air uses
valve · establish (engine start spec).
4.10 EGT load factor#
load_bleed = Rescale(16→1, max_psi→1.14, v) if v = valve·apu_psi ≥ 16, else 1;
load_elec = 1 + Σ over transfer buses on APU (ac_power_bus[k] = 3) of Rescale(0→0, 100→0.07, Limit(bus_load_amps[k], 0, 100));
load_egt = max(load_bleed, load_elec).
4.11 EGT when not starting, gauge, light outputs#
egt when start_active = 0 (AV(rate) = AnimValue to the stated target, 0..100):
sw= 0:shutdown_active= 0 → toward the persistedegt_tgt, rate 0.008 ifegt≤ 21 else 0.15; = 1 →egt_tgt:=Rescale(0→47, 4→51, Limit(T_amb,0,4)), rate 0.5; = 2 →egt_tgt:=T_amb, rates 0.008/0.15.sw≠ 0:shutdown_active= 0 → iftemp_init= 0 ∧T_temp_init= 0:temp_init:= 1;egt_tgt:=T_amb, rates 0.008/0.15; = 1 and = 2 as above.
T_temp_init: reset to 1.5 (and temp_init := 0) when batbus = 0 or sw = 0, else counts down;
batbus = 0 also zeroes T_5min. Door: apu_door moves linearly toward door_tgt, full travel 6 s.
Gauge (first match): batbus = 0 ∨ T_5min = 0 → AV(0, 8); start_active > 1 ∨ shutdown_active ≠ 0 ∨
sw = 0 → AV(egt, 8); temp_init 0 → AV(egt, 3); 1 → AV(egt + 20, 8), → 2 once within 5 of it;
2 → AV(0, 7), → 3 below 1.5; 3 → AV(egt, 3). (Power-up sweep of the needle ~1.5 s after ON.)
Light memories: for each of FAULT, MAINT, OVERSPEED, LOW OIL: if G: ind := raw; else if tmr = 0:
ind := 0 (so FAULT/MAINT/OVERSPEED stay lit up to 300 s after OFF; LOW OIL is cleared on OFF, §4.5).
Annunciators (next frame), with B = battery bus · generic_lights_brightness_ratio[14] · day factor
(electrics.md §4.7; lights test overrides as there):
Z:annunciator/apu_low_oil = low_oil_ind·B, apu_fault = fault_ind·B, maint_apu = maint_ind·B,
over_speed_apu = overspeed_ind·B. APU six-pack inputs (rising edge latches six_pack_apu, master-caution
reset clears; mechanism in the annunciator spec): FAULT ind, MAINT ind, OVERSPEED ind, LOW OIL ind ∧
engine[22], LOW OIL ind ∧ engine[21] (a LOW OIL light without an oil failure does not trigger it).
apu_gen_off_bus: electrics.md §4.7.
4.12 Fuel flow and burn#
ff0 = Rescale(0→225, 35000→105, elev_m·3.28) lb/h · 0.45359 / 3600 (kg/s);
apu_ff = Rescale(1→ff0, 1.14→1.15·ff0, load_egt). T_fuel += dt; when > 1 s, if RUN = 1 burn
b = T_fuel·apu_ff, then T_fuel := 0:
- no tank pump on (
fuel_tank_pump_on[0]+[1]+[2]= 0): L −= b; - crossfeed closed: if
tank_c1_status= 1 ∧ C pump on ∧ C > 100: take from C down to 100 kg, the rest from L if the L pump is on; else L −= b if the L pump is on; - crossfeed open: if C pump on ∧ C > 100: take from C down to 100, the excess as below; else if L pump on ∧
tank_c1_status= 1: as below with b; else L −= b if L pump on and R −= b if R pump on. "As below" = take from the heavier wing tank up to the L/R difference, the remainder half from each. - floor L, R at 0; write
X:m_fuel[0..2]. (X-Plane's own APU burns nothing:_APU_ff_kgh= 0.)
4.13 Fire and X-Plane failure hooks#
manage_eng_failure:engine[18]= 1 →X:rel_apu_fire:= 6;engine[19]= 1 →X:rel_apu:= 6 (X-Plane then fails the APU: no start / it stops → FAULT via §4.7 7/8).Fire: handle discharged (pos_disch< −0.95 or > 0.95) →fire/apu/fire_ext:= 1,was_fire:= 1,X:rel_apu:= 6 (APU unusable for the rest of the flight unless failures are reset); circuit L/R flag; bottlefire/apu/ext_bottle/psifalls 100 psi/s while the amount discharged accumulates; whenrel_apu_fire= 6 and the discharged amount > random(400..1000) (drawn when the fire starts) the fire is cleared; burning 600 s → cockpit smoke.- Lights:
apu_fire= fire (or fire test)·B_fire,apu_bottle_discharge= bottle empty, extinguisher circuit test,fire_apu_det_inopon FAULT test (fire spec). - Handle pulled (
pos_arm= 1): no start, abort (APU_off), bleed valve closed.
5 Constants, tables, timings#
5.1 Helpers#
AnimValue(cur → tgt, lo..hi, k): c = clamp(cur); if tgt ≥ hi − 0.001 ∧ c ≥ hi − 0.01 → hi; if tgt ≤ lo + 0.001 ∧ c ≤ lo + 0.01 → lo; else c + (tgt − c)·k·dt without overshoot, clamped (first order, time constant 1/k).AnimLinear(cur → tgt, lo..hi, r)(Anim_linear_value2): same end snaps, moves r·dt toward tgt.set_animation_rate(cur → tgt, lo..hi, T): same snaps, moves dt/T toward tgt (T ≤ 0 → 1).
5.2 Thresholds#
| Value | Use |
|---|---|
| N1 < 7 % | start available |
| 7620 m | below: gravity/DC feed from L tank enough to start |
N1 < 40 % at T_low_oil expiry |
LOW OIL raw |
egt > 40 ∧ N1 > 40 ∧ RUN |
LOW OIL clears |
egt > 95 |
starts T_normal |
46 < egt < 52·load_egt, N1 > 94, RUN |
generator/duct enable timers |
| EGT target 47..51 (TAT 0..40 °C) | running EGT (×load_egt ≤ 1.14) |
| gauge ≤ 25 at OFF | no cool-down |
| N1 > 99 | overspeed / oil failures cause shutdown |
| N1 > 90 | establish ramps up |
apu_psi_raw > 8 psi |
bleed valve may open; output clamp 41 psi; demand 38/33/21 psi |
max_psi U[45, 46] |
|
| fuel C > 100 kg | centre tank usable for APU |
5.3 Timers#
| Timer | Value |
|---|---|
T_low_oil |
5.5 s |
T_start |
Rescale(0→28, 60→14, TAT+30) × Rescale(0→1, 28000→1.3, alt ft); ÷ 8 if LOW OIL raw set. TAT 15 °C, SL: 17.5 s / 2.19 s; TAT 0: 21 s / 2.63 s |
APU_start held |
1 s (T_start_stop), then APU_on |
T_normal |
8.5 s after egt > 95 |
| generator / duct enable | 2 s |
| start meter | 120 s → FAULT; tmr2 30 s |
T_cool |
58 s |
light memory tmr, T_5min |
300 s |
T_temp_init |
1.5 s |
| door | 6 s full travel |
establish |
180 s (−30 °C) … 60 s (40 °C), 500 s back |
| fuel burn | applied each > 1 s; 225 lb/h SL … 105 lb/h at 35 000 ft, +15 % at full load |
5.4 Failure items#
| Item | Effect |
|---|---|
engine[18] APU FIRE |
X:rel_apu_fire = 6 → fire |
engine[19] APU STARTING/RUNNING |
X:rel_apu = 6 → X-Plane APU dead → FAULT |
engine[20] OVERSPEED |
OVERSPEED light + shutdown at N1 > 99 |
engine[21] LOW OIL QUANTITY |
LOW OIL whenever switch ≠ OFF; shutdown at N1 > 99 |
engine[22] LOW OIL PRESSURE |
LOW OIL while starting/running; shutdown at N1 > 99 |
electric[19] APU GENERATOR |
MAINT when available; no APU GEN (electrics) |
pressurisation[6] APU bleed valve |
valve frozen |
pressurisation[15] APU pressurization |
no start, valve closed, MAINT when established, X:rel_APU_press = 6 |
6 Outputs#
6.1 Zibo datarefs#
spring_toggle_switch/APU_start_pos, toggle_switch/bleed_air_apu_pos, electrical/apu_temp (gauge),
electrical/apu_door, electrical/apu_bus_enable, electric/apu_start_load, air/apu_psi,
air/apu/bleed_valve_pos, annunciator/apu_low_oil, apu_fault, maint_apu, over_speed_apu,
six_pack_apu, apu_fire, apu_bottle_discharge, extinguisher_circuit_annun_apu, fire_apu_det_inop,
fire/apu/fire, fire_ext, was_fire, ext_bottle/psi, ext_switch/pos_arm, pos_disch,
light/spill/ratio/apu_avail_lights_spill[3] (= apu_gen_off_bus), …extinguisher_*_spill_apu[3].
X-Plane writes: commands APU_start (begin/end), APU_on, APU_off; rel_apu, rel_apu_fire,
rel_APU_press; cockpit2/bleedair/actuators/apu_bleed; m_fuel[0..2]; cockpit/engine/APU_running (restore only).
6.2 SCI states#
| SCI state | Source |
|---|---|
APU_SW_POS (OFF, ON, START) |
APU_start_pos |
SRC_APU_RUNNING |
profile: N1 > 95 (keep; it is an SCI convenience, Zibo has no such flag) |
SRC_APU_GEN_RDY |
apu_bus_enable |
ANN_APU_LOW_OIL, ANN_APU_FAULT, ANN_APU_MAINT, ANN_APU_OVERSPEED, ANN_SIXPACK_APU, ANN_APU_GEN_OFF_BUS |
the annunciator datarefs above |
ELECTRICAL_APU_TEMP, ELECTRICAL_APU_DOOR |
apu_temp (gauge), apu_door |
TOGGLE_SWITCH_BLEED_AIR_APU_POS |
bleed_air_apu_pos |
FIRE_APU_EXT_SWITCH_POS_ARM/_DISCH, ANNUNCIATOR_APU_FIRE, …_APU_BOTTLE_DISCHARGE, …_EXTINGUISHER_CIRCUIT_ANNUN_APU |
fire datarefs |
Profile note: the APU_SW gate (T_APU_SW_POS >= 7) and the x_apu_start effect fallback exist only
because the old host did not model X-Plane's APU; the rewrite must not delay START or start the APU itself.
7 X-Plane-provided behaviour to supply, with a recommendation#
What X-Plane provides and Zibo consumes: the APU N1 trajectory, the APU_running flag, response to
APU_start/APU_on/APU_off and to rel_apu (failed), generator amps (electrics), its own APU bleed
and generator (Zibo overrides/does not use them: bleed pressure, EGT, availability and door are Zibo's).
X-Plane 12 parameters for this aircraft (acf): spool-up 53 s, spool-down 18 s, cool-down 7 s, door 9 s,
fuel flow 0. As understood from X-Plane's model (not verifiable in the oracle): the starter switch is
START while APU_start is held and ON after; the inlet door opens first (9 s); N1 rises to 100 % in about
the spool-up time; APU_running is set when the APU becomes self-sustaining; after APU_off the APU runs
the cool-down time, then spools down in ~18 s, APU_running clears and the door closes. X-Plane needs DC
power to start and stops the APU when it fails (rel_apu = 6).
Requirements Zibo puts on this model (parity-relevant): N1 < 7 % until Zibo calls APU_start; RUN = 1 and
N1 > 94 % reached within 120 s of the START release (else FAULT); RUN must not drop while running (else
FAULT); N1 ≥ 40 % with RUN = 1 to clear LOW OIL; APU_off must stop it (RUN → 0) or Zibo keeps
door_tgt = 1.
MSFS candidates: SimVars APU PCT RPM, APU PCT STARTER, APU SWITCH, APU GENERATOR SWITCH,
APU GENERATOR ACTIVE, APU VOLTS, APU ON FIRE DETECTED, APU BLEED PRESSURE RECEIVED BY ENGINE; events
APU_STARTER, APU_OFF_SWITCH, APU_GENERATOR_SWITCH_TOGGLE/SET, APU_BLEED_AIR_SOURCE_TOGGLE/SET.
Recommendation: model the APU spool in wasm/src/core (a small ApuSpool behind the same interface
Zibo's controller uses: start_begin/end, on, off, failed → N1, running, door_xp), and keep
the MSFS APU off. Reasons: (1) the MSFS APU's spool rate comes from the package cfg and is unrelated to
X-Plane's 53/18/7/9 s; (2) anything else that starts it (a default binding, a state load) pushes N1 ≥ 7 %
and Zibo then never starts; (3) MSFS's APU would burn fuel and feed MSFS's electrical/bleed while Zibo
already burns APU fuel itself and owns the buses; (4) a core model is deterministic and runs in the native
differential tests. Proposed model (to be checked against an X-Plane capture, §9): on APU_start begin:
door opens over 9 s; N1 then rises linearly 0→100 in 53 s; running from N1 ≥ 50 % (assumption); on
APU_off: hold 7 s, N1 falls linearly 100→0 in 18 s, running := 0 below the same threshold, door closes
over 9 s; failed → as APU_off without cool-down and no restart. MSFS APU events may later be mirrored
only for exterior effects/sound (phase 2).
For the differential tests, both sides receive the same injected N1/RUN sequence (as the oracle
scripts do), so the Zibo controller is compared exactly and the spool model is tested separately.
8 Differential test scenarios#
Harness: dt = 1/30 s; inputs as in the oracle scripts (ground, L/R 3000 kg, barometer 29.92 inHg, TAT
15 °C on both TAT datarefs, generic_lights_brightness_ratio[14] = 1, sun pitch 40°); X-Plane APU
quantities injected identically. Compare by name: switch, start_active-visible outputs
(apu_start_load), commands sent (APU_start begin/end, APU_on, APU_off) with frame numbers (±1 frame),
light values exactly, apu_bus_enable edge ±1 frame, apu_temp/apu_door/apu_psi/valve within 1e-3.
| # | Sequence | Expected (oracle) |
|---|---|---|
| S1 | apu_spec_start.json: BAT; ON; START after 7 s; N1 +10 %/3 s, RUN from 60 %; N1 100; APU bleed ON (packs off); OFF |
LOW OIL at 5.5 s after ON; start load 60 for 2.19 s then APU_start begin, APU_on 1 s later; door 1 within 6 s; EGT = N1 during start; LOW OIL out at N1 60 + RUN; EGT 100 for ~8.5 s, then decays to ~48.5; apu_bus_enable 19–25 s after N1 100; GEN OFF BUS lit; bleed valve 1, apu_bleed 1, apu_psi 41 → 33 → 21; OFF: bus enable 0 at once, valve 0, APU_off 58 s later; door 0 after RUN 0 (27 checks, passes) |
| S2 | apu_spec_delay_fault.json part 1: ON 2 s, START |
start load 60 for 17.5 s; LOW OIL 5.5 s after START (timer re-armed) |
| S3 | same, no N1 | FAULT at 120 s after START, LOW OIL cleared, six-pack APU lit, door closes; FAULT still lit 10 s after OFF (12 checks, passes) |
| S4 | S1 to running, BAT OFF | abort: APU_off, all lights 0, gauge → 0 at rate 8 |
| S5 | OFF during start (state 2, RUN 0), then inject RUN 1 | no APU_off until RUN 1; then cool-down 58 s |
| S6 | running, OFF with gauge ≤ 25 (inject low EGT via restore or cold APU) | APU_off within 2 frames |
| S7 | failures: electric[19]; pressurisation[15] (start unavailable; MAINT after establish); engine[20], [21], [22] at N1 100 |
MAINT / OVERSPEED / LOW OIL, shutdown, six-pack inputs as §4.11 |
| S8 | fire: engine[18]; pull handle; discharge |
APU fire light, abort, rel_apu 6, extinguish after random amount |
| S9 | elev_m 8000, all pumps off |
START gives no start (start_avail 0); pumps on → start |
| S10 | ON, observe gauge 0–3 s | needle sweep to egt+20 and back from 1.5 s |
| S11 | running 120 s with/without bleed and APU GEN load, various tank-pump/crossfeed settings | fuel removed per §4.12 |
| S12 | ON during cool-down | start_active 3, cool-down cancelled, bus enable returns after 2 s |
| S13 | TAT −30/40 °C, 28 000 ft | T_start 28 s·1.3 / 14 s; EGT target 47/51 |
9 Open questions#
- X-Plane's actual APU trajectory (N1 shape, door gating,
APU_runningthreshold, cool-down behaviour afterAPU_off) cannot be measured in the oracle. Needed: one capture in X-Plane 12 with the aircraft (DataRefTool, 10 Hz:APU_N1_percent,APU_running,cockpit2/electrical/APU_door,APU_starter_switch) for a start and a shutdown. Until then §7's model is a proposal. - Does X-Plane's own 7 s cool-down add to Zibo's 58 s (total run after OFF ≈ 65 s + 18 s spool-down)?
apu_leakand the duct-pressure side of the APU bleed are left to the pneumatics spec; S1's bleed numbers assume no leak (isolation valve, packs off).- Whether
SRC_APU_RUNNINGshould follow Zibo'sRUNinstead ofN1 > 95(contract semantics). M3: kept at N1 > 95 (profile); X-Plane'sRUNis published as the diagnosticL:LU737_APU_RUNNING. - The fuel-burn crossfeed-open branch uses
tank_c1_statusin a way that looks unintended (it gates the wing balancing); reproduce literally unless the owner decides otherwise. - MSFS: confirm the package cfg defines no active APU (or that leaving it off has no side effects such as MSFS's own APU-generator bus feed).