Technical reference

Electrical system (737-800): specification for the rewrite

docs/specs/electrics.md

Status: M2 draft, 2026-10-31. 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 and the Lua, and from oracle runs; no decompiled code is reproduced.

Notation: Z: = Zibo dataref (laminar/B738/...), X: = X-Plane dataref/command (sim/...), SCI: = stable cockpit interface state/control (data/sci/contract). "OK(i)" means electrical failure item i is not active (see §5.4).

1 Scope and sources#

Covers the 737-800 AC/DC power distribution as Zibo computes it: batteries and standby power, the BAT / STBY PWR / GRD PWR / GEN / APU GEN / BUS TRANSFER / DRIVE DISCONNECT / CAB-UTIL / IFE switches, source selection and bus tie breakers, TRUs, static inverter, ground power availability, the DC/AC meters, the electrical annunciators, and the power gates other systems and the display units use. The APU itself (start sequence, EGT, apu_bus_enable timing) is the APU spec; here only how the APU generator is switched onto the buses and when it is "available".

Where the logic is. Electrical logic is entirely in zibomod (C++). The Lua scripts compute none of it; they only (a) connect/disconnect ground power from the EFB, (b) hold the failure array, and (c) read bus states for sounds, lights and the glareshield.

zibomod function Called from (order in the frame, §4) Electrical content
MainFlightLoopCallback X-Plane flight loop frame period sim_period, plane_on_ground, N2 copy, call order
Read_Xplane_DR, Read_B738_DR start of frame copies of X-Plane / Zibo datarefs into globals (§2.2)
Annunciators → Annun_timers (first) and B738_battery_disch_annun (last) before Systems all electrical lights, timers of warm-ups, discharge detection
Switches after Annunciators guard animation; closing a guard forces BAT ON / STBY AUTO / BUS TRANSFER AUTO
Systems → Electric_bus (first call inside Systems) after Switches the whole bus network (§4.4)
Systems (rest) after Electric_bus APU-generator availability and drop-out, engine-generator availability and drive disconnect, meters, display brightness gates, display blanking
B738_gpu_power near the end of the frame ground power availability (gpu_available), GPU volts override
CMD_battery_up/dn, CMD_standby_bat_lft/rgt/on/off, CMD_gpu_up/dn, CMD_gen1/2_up/dn, CMD_apu_gen1/2_up/dn, CMD_dc/ac_power_knob_up/dn, CMD_drive_disconnect1/2(_off), CMD_cab_util_toggle, CMD_ife_pass_seat_toggle, CMD_button_switch_cover_NN, CMD_batt_full_off, CMD_batt_std_full_off, CMD_master_caution_button1/2 command handlers (run when the command fires, between frames) switch positions and the source-selection latches
Systems_start_flight, Switches_start_flight, Annunciators_start_flight flight start / reload initial switch positions and latch values (§3.3)
Systems_load / Systems_save situation save/load persist all of the above (not needed for M3)
failure(x) helper "item not failed": returns true when the failure value is 0 or 2
Lua Content
B738.tablet/B738.tablet.lua laminar/B738/gpu_toggle (EFB ground services): toggles laminar/B738/gpu_hide (0 = GPU cart present) only on the ground and below 0.08 m/s ground speed; flight-start visibility of the GPU (§3.3); electric-failure page (indices, §5.4)
B738.a_fms/B738.a_fms.lua creates laminar/B738/failure/electric (float[70]) and laminar/B738/fms/engine_no_running_state (cold & dark vs. turnaround preference)
B738.zz.sim_cockpit, B738.a_AXP_4_FMOD_logic, B738.glaresheild, LU_737NG.sound readers only (*_status, gpu_available, main_bus1/2, generator_on, …)

Datarefs and commands named in this document were taken from the registration code (Register_systemsDR, Register_annunciateDR, Register_systemsCMD, Xplane_datarefs, Register_Own_B738DR): each Zibo dataref maps to one global through its accessor; names below are the dataref names, not the globals, except for internal state (§3). Sources for measured values: oracle runs in §8 (xphost/test/sci/elec_spec_*.json).

2 Inputs#

2.1 Cockpit controls#

Zibo commands (all act on the command's begin phase, phase 0, unless stated) and the SCI control that drives them (data/sci/contract/10_elec.json, profile elec.json, generic controls in 90_cockpit.json).

SCI control Zibo command(s) Zibo state written Behaviour
ELEC_BAT (guard ELEC_BAT_GRD) laminar/B738/switch/battery_dn / battery_up Z:electric/battery_pos 1 / 0 _dn sets ON only if OFF; _up sets OFF only if ON. No guard check in the handler (the SCI layer blocks while the guard is closed).
ELEC_BAT_GRD laminar/B738/button_switch_cover02 guard target 2 toggles 0↔1 Guard position Z:button_switch/cover_position[2] animates to the target (§4.3); when it is below 0.3 BAT is forced ON.
ELEC_STBY_PWR (guard ELEC_STBY_PWR_GRD) laminar/B738/switch/standby_bat_left / _right; also standby_bat_on (→ AUTO) and standby_bat_off (→ OFF) Z:electric/standby_bat_pos −1 BAT / 0 OFF / 1 AUTO left: AUTO→OFF→BAT; right: BAT→OFF→AUTO (one step per command).
ELEC_STBY_PWR_GRD laminar/B738/button_switch_cover03 guard target 3 Guard below 0.3 and STBY not AUTO → Zibo fires its own standby_bat_on (STBY → AUTO).
ELEC_DC_METER laminar/B738/knob/dc_power_up / _dn Z:knob/dc_power 0…6 One step per command, clamped 0…6. Moving into/out of 3,4,5 also fires X-Plane sim/electrical/dc_volt_lft/rgt (no effect on Zibo's own values). Every accepted step restarts the DC-panel warm-up (0.2 s, §4.6).
ELEC_AC_METER laminar/B738/knob/ac_power_up / _dn Z:knob/ac_power 0…6 Same, AC panel warm-up (0.2 s).
ELEC_GPU (spring) laminar/B738/toggle_switch/gpu_dn (ON) / gpu_up (OFF) Z:electrical/gpu_pos 1 / −1 while held, 0 on release (phase 2) ON/OFF action on begin (§4.2).
ELEC_GEN1, ELEC_GEN2 (spring) laminar/B738/toggle_switch/gen1_dn/gen1_up, gen2_dn/gen2_up Z:electrical/gen1_pos, gen2_pos ±1 / 0 §4.2.
ELEC_APUGEN1, ELEC_APUGEN2 (spring) laminar/B738/toggle_switch/apu_gen1_dn/_up, apu_gen2_dn/_up Z:electrical/apu_gen1_pos, apu_gen2_pos ±1 / 0 §4.2.
ELEC_BUS_TRANS X-Plane sim/electrical/cross_tie_on / cross_tie_off (no Zibo command) X:cockpit2/electrical/cross_tie 1 AUTO / 0 OFF Modelled by Zibo (it reads cross_tie, §4.4). While the guard cover_position[6] is < 0.3 Zibo writes cross_tie = 1 every frame, so OFF is only possible with the guard open (oracle Q19–Q23). SCI_IMPLEMENTATION.md §3 ("not modelled") is wrong on this point.
BUS_TRANSFER_COVER (generic) laminar/B738/button_switch_cover06 guard target 6 as above
ELEC_DRIVE1_DISC, ELEC_DRIVE2_DISC laminar/B738/one_way_switch/drive_disconnect1 / drive_disconnect1_off (and 2) Z:one_way_switch/drive_disconnect1_pos 1 / 0 Position only; the effect is in §4.5 (needs start lever ≥ 0.9).
DRIVE_DISCONNECT_COVER_1, DRIVE_DISCONNECT_2_COVER button_switch_cover04 / 05 guard targets 4/5 Not checked by the drive handlers; closing them forces nothing.
ELEC_CAB_UTIL, ELEC_IFE laminar/B738/autopilot/cab_util_toggle, ife_pass_seat_toggle Z:toggle_switch/cab_util_pos, ife_pass_seat_pos 0/1 Toggle.
APU_SW laminar/B738/spring_toggle_switch/APU_start_pos_up/dn Z:spring_toggle_switch/APU_start_pos APU spec; here only apu_bus_enable is consumed.
LIGHTS_TEST_BRIGHTNESS_CONTROL laminar/B738/toggle_switch/bright_test_up/dn Z:annunciator/test (=1 in TEST) Lights test forces all electrical lights to the panel brightness (§4.7).
MASTER_CAUTION_ACCEPT(_2) laminar/B738/push_button/master_caution1/2 clears the six-pack latches §4.7.
(none: EFB) laminar/B738/gpu_toggle (Lua) Z:gpu_hide §4.8.
(none: maintenance) laminar/B738/push_button/batt_full_off, batt_std_full_off opens guard 2 (or 3), sets BAT OFF (or STBY OFF) and X-Plane battery_on[1] (or [2]) = 0 Utility commands.

2.2 Simulator and other-system inputs#

Input (X-Plane / Zibo name) Use Notes / rewrite source
X:flightmodel2/gear/on_ground[0..2] plane_on_ground = any wheel on ground MSFS SIM ON GROUND / gear contact
frame time sim_period = time since last frame, capped at 0.1 s (0.01 s on the very first frame) rewrite's own dt with the same cap
X:cockpit2/electrical/battery_voltage_actual_volts[0] hot battery bus = volts > 10 X-Plane battery model (§7)
X:cockpit2/electrical/battery_voltage_indicated_volts[1], [2] DC meter BAT BUS/BAT ([1]), STBY PWR ([2]) §7
X:cockpit2/electrical/battery_amps[0..2] BAT DISCHARGE detection, DC meter BAT amps §7
X:cockpit2/electrical/bus_load_amps[0..1] DC meter TR amps §7
X:cockpit2/electrical/generator_amps[0..1], APU_generator_amps AC meter amps; APU GEN OFF BUS §7
X:cockpit/electrical/gpu_amps input to the AP "lost AC" detector §7
X:cockpit2/annunciators/generator_off[0..1] GEN OFF BUS lights §7
X:cockpit2/electrical/generator_on[0..1] engine generator connected (switch state that Zibo commands) §7 — X-Plane command semantics
X:cockpit/electrical/gpu_on ground power connected (Zibo issues GPU_on/off) §7
X:cockpit2/electrical/APU_generator_on, X:cockpit/electrical/generator_apu_on APU generator connected Zibo writes the first; reads both
X:cockpit2/electrical/cross_tie BUS TRANSFER switch §2.1
X:cockpit2/electrical/bus_volts[0..1] only for internal DC-bus volts (not exported) can be ignored
X:operation/failures/rel_esys, rel_esys2 value 6 = bus 1 / bus 2 failed (multiplies XFR/DC 1/2) MSFS: none; keep as failure inputs
X:operation/failures/rel_genera0, rel_genera1 6 = generator failed written by Zibo too (§4.5)
X:flightmodel2/engines/N2_percent[0..1] (copied to eng_N2_idx) generator available (≥ 49.5), DRIVE light (< 13) engine spec (Zibo engine model output)
Z:engine/mixture_ratio1/2 (start levers) drive disconnect effective at ≥ 0.9 engine spec
X:cockpit2/electrical/APU_N1_percent, X:cockpit/engine/APU_running via apu_bus_enable (APU spec)
Z:electrical/apu_bus_enable APU generator available APU spec computes it (§4.5 summary)
X:flightmodel2/lights/generic_lights_brightness_ratio[13], [14] annunciator brightness §7 — 0 in the oracle
X:graphics/scenery/sun_pitch_degrees, X:version/xplane_internal_version day/night brightness factor §5.2
X:cockpit2/gauges/indicators/radio_altimeter_height_ft_pilot TR UNIT logic in flight (≥ 10 ft) MSFS RADIO HEIGHT
Z:autopilot/autoland_status, Z:ap/glideslope_status, gls_gs_status, lpv_gs_status (per master side) DC bus tie opens on approach (§4.4) AFDS spec
Z:fms/engine_no_running_state, X:operation/prefs/startup_running flight-start state (§3.3)
Z:guarded_covers 1 = all guards held open (EFB option)
Z:failure/electric[0..23] Zibo electrical failures (§5.4) Lua-owned array
ground-power availability inputs: Z:gpu_hide, Z:gpu_test, Z:fms/gpu_test_enable, Z:sam_enable, Z:jetway_power, Z:near_apt_icao, Z:autogate_gpu, Z:gp_cable_connect, parking brake, X:flightmodel2/engines/engine_is_burning_fuel[1], beacon §4.8

3 State#

3.1 Latched source selection (the core of the model)#

Name (Zibo global) Type Meaning
ac_power_bus[k], k = 0 (XFR 1), 1 (XFR 2) int actual source of transfer bus k: 0 none, 1 GEN 1, 2 GEN 2, 3 APU, 4 GPU
ac_sel_power_bus[k] int, initial 9 source last selected by the crew for bus k (same codes, 9 = none). SOURCE OFF k = actual ≠ selected. Only the command handlers write it (and flight start).
ac_power_bus_tie int source on the tie bus: 3 if X-Plane APU generator is on, else 4 if X-Plane GPU is on, else 0 (recomputed each frame)
takeoff_apu_powered bool on the ground: both buses on APU; kept in the air until that stops (§4.4 step 1)
tru3_via_bus 0/1/2 which transfer bus feeds TRU 3 (0 = TRU 3 off)
bpcu_status 0/1 BPCU has power: gpu_available ∨ batbus_status ∨ dc_bus1_status (previous frame's values)
fail_bus1, fail_bus2 1/0 0 when X-Plane bus failure rel_esys/rel_esys2 = 6

3.2 Timers and latches#

Name Set / reset Effect
dir_batbus_after_loss, dc_std_after_loss, ac_std_after_loss (initial 1) 1 while the bus is powered (timer reloaded to 2 s); after the bus has been off for 2 s → 0 STANDBY PWR OFF light
init_ac_source 0 while DC STBY off (timer 0.2 s); 1 after DC STBY powered 0.2 s gates TRANSFER BUS OFF, SOURCE OFF, GEN OFF BUS, APU GEN OFF BUS
warm_up_disp / warm_up_disp2 (+ up/down timers) §4.6: 32 s warm-up after DC STBY (captain/upper) or DC 2 (FO/lower) DU brightness gate, Z:display/warm_up_disp(2)
init_mcdu1/2 (timer 30 s) §4.6 Z:electric/mcdu1_on, mcdu2_on
warm_up_electric_panel / 2 (timers 4 s / 5 s; 0.2 s after a knob step) §4.6 Z:dc_pnl_warm_up, Z:ac_pnl_warm_up
init_heat, init_heat2 (4.7 s after DC 1 / DC 2) used by window/probe heat
isfd_init_time (96 s) / warm_up_isfd §4.6 Z:display/isfd_init_time
TMR_pow4_timer1/2 (240 s) → Z:electric/ac_tns_bus1_pow4, _pow4 2 §4.6 "transfer bus powered for 4 min" (stall-warning test etc.)
TMR_Amp5Exceed (95 s), TMR_Amp15Exceed (3.5 s), TMR_Amp100Exceed (2 s) → batt_discharge5/15/100 → batt_discharge §4.7 BAT DISCHARGE
guard positions Z:button_switch/cover_position[0..10] and targets §4.3
elec_fail_ann, elec_fail_ann_rst[20], six_pack_elec_fail_0[20] §4.7 ELEC six-pack
freq_ac_mode0..5, volts_ac_mode1..5 drawn once at flight start: uniform 395–405 Hz / 112–118 V meter readings (§4.6)
first_timer seconds since load; generator logic differs until > 6 s (§4.5)

3.3 Initial values at flight start#

Switches_start_flight and Systems_start_flight (X-Plane startup_running = 0 is "cold", 1 is "engines running"):

Item Cold & dark (startup_running=0 and engine_no_running_state=0) Turnaround (startup_running=0, engine_no_running_state=1) Engines running (startup_running=1)
BAT OFF, guard target open (cover 2, 3, 9 positions = 1) ON, guards closed ON, guards closed
STBY PWR AUTO; guard 3 target closed (it animates shut in the first frames) AUTO AUTO
X-Plane battery_on[0], [2] 1 1 1
X-Plane battery_on[1] 0 1 1
cross_tie 1 1 1
DRIVE DISC switches, APU GEN switch, apu_power_bus1/2 0 0 0
GPU hidden (Lua sets gpu_hide = 1) shown; if visible: gpu_available = 1, X-Plane gpu_on = 1, both buses selected and on GPU (ac_sel = 4, ac_power_bus = 4 if BTB OK) hidden
warm-ups, isfd_init_time not warm (DC panel timer 4 s, AC 5 s, ISFD 96 s) warm (all 1) warm (all 1)
init_mcdu1/2 and timers 0 0 0
fail_bus1/2 1 1 1
AC meter random values drawn drawn drawn

Then one evaluation of the source-resolution part of §4.4 is run.

4 Per-frame logic in execution order#

Conventions: OK(X) = Zibo failure item X not active (§5.4); booleans are 0/1 floats as in Zibo; "X-Plane cmd" means Zibo fires that X-Plane command once (its effect is §7); XFRk = ac_tnsbus{k}_status. Values computed in a frame are read by later steps of the same frame and by earlier steps of the next frame — the order matters for one-frame-exact parity.

4.1 Frame order#

  1. Command handlers of commands that fired since the last frame (§4.2). X-Plane runs them outside the flight loop, i.e. before step 2 of the next frame.
  2. Read_Xplane_DR / Read_B738_DR: snapshot of the inputs of §2.2; plane_on_ground; sim_period.
  3. Annunciators (§4.7): first Annun_timers (all count-down timers of §3.2 except those counted inline), then the lights — using the bus states of the previous frame — and last B738_battery_disch_annun (discharge detection, used by the lights in the next frame).
  4. Switches (§4.3): guard animation and guard-closing actions.
  5. Systems: Electric_bus (§4.4) first, then APU-generator availability/drop-out and engine-generator availability (§4.5), meters (§4.6), display gates (§4.9).
  6. B738_gpu_power (§4.8) — only in Zibo's 2 Hz slow loop: a timer accumulates sim_period; when it exceeds 0.5 s it is reset and, if the sim is not paused and first_timer > 2 s, B738_gpu_power runs (together with other slow tasks). Ground-power availability therefore changes up to 0.5 s after its cause.

4.2 Switch actions (command handlers)#

All act on the begin phase. Spring switches set their position to +1 (ON, _dn) or −1 (OFF, _up) on begin and back to 0 on end.

GRD PWR ON (gpu_dn): if gpu_available = 1 and bpcu_status = 1 and OK(BPCU): X-Plane GPU_on, generator_1_off, generator_2_off; X-Plane APU_generator_on := 0; apu_power_bus1 = apu_power_bus2 := 0; ac_sel := {4, 4}; ac_power_bus[0] := 4·OK(BTB1), ac_power_bus[1] := 4·OK(BTB2). Otherwise nothing but the position. Battery switch position is not required (oracle R5: ground power connects with BAT OFF).

GRD PWR OFF (gpu_up): X-Plane GPU_off; for each bus currently on GPU, ac_sel[k] := 9.

GEN 1 ON (gen1_dn): if gen1_available = 1 and the battery bus (dir_batbus_status) = 1: remember whether bus 2 is on APU / on GPU; X-Plane generator_1_on; ac_sel[0] := 1; ac_power_bus[0] := 1; apu_power_bus1 := 0; if bus 2 is not on APU: X-Plane APU_generator_on := 0 and apu_power_bus2 := 0; if bus 2 is not on GPU: X-Plane GPU_off. GEN 2 ON is the mirror image (code 2, generator_2_on) but its condition is gen2_available = 1 and DC STBY (dc_stdbus_status) = 1 — the asymmetry is Zibo's and must be kept.

GEN k OFF (genk_up): X-Plane generator_k_off; if ac_power_bus[k] is this generator, ac_sel[k] := 9.

APU GEN k ON (apu_genk_dn): if apu_bus_enable = 1 and APU-generator failure value ≠ 1: if X-Plane APU_generator_on = 0 set it to 1 and apu_power_bus_k := 1 (only in that case); ac_sel[k] := 3; ac_power_bus[k] := 3·OK(BTBk); X-Plane generator_k_off (k = this side) and GPU_off. APU GEN k OFF (apu_genk_up): apu_power_bus_k := 0; if the other bus is not selected to APU (ac_sel[other] ≠ 3): X-Plane APU_generator_on := 0, apu_power_bus_other := 0 and, if the other bus is on APU, ac_sel[other] := 9. Then if bus k is on APU, ac_sel[k] := 9. Consequence (oracle Q28): with both APU GENs on, switching APU GEN 1 OFF leaves bus 1 on the APU (apu_power_bus1 comes back the next frame through §4.4 step 5) and only lights SOURCE OFF 1.

BAT: battery_dn sets battery_pos := 1 if it was 0; battery_up := 0 if it was 1. STBY PWR: standby_bat_left: 1→0, 0→−1; standby_bat_right: −1→0, 0→1; standby_bat_on := 1; standby_bat_off := 0. Meters: dc_power_up/dn, ac_power_up/dn step 0…6; each accepted step sets the panel's warm-up flag and dataref to 0 and its timer to 0.2 s. Guards button_switch_coverNN toggle target NN between 0 and 1. DRIVE DISC: drive_disconnectk := 1, drive_disconnectk_off := 0. CAB/UTIL, IFE: toggle 0↔1. Master caution (§4.7).

4.3 Guards (Switches)#

If guarded_covers = 1 every guard position and target is held at 1 (open). Otherwise, each frame, for guards 0…10: pos += sim_period · 20 · (target − pos) without overshooting the target, clamped to 0…1 (helper "Anim_value": it snaps to the target once the value is within 0.01 of a 0/1 limit and the target is within 0.001 of that limit). At 30 fps a closing guard reads 0.333 then 0.111, so the actions below happen on the second frame after the command. Then:

  • guard 2 (BAT) < 0.3 and BAT ≠ ON → battery_pos := 1;
  • guard 3 (STBY PWR) < 0.3 and STBY ≠ AUTO → Zibo fires laminar/B738/switch/standby_bat_on (STBY := AUTO);
  • guard 6 (BUS TRANSFER) < 0.3 and X-Plane cross_tie ≠ 1 → cross_tie := 1;
  • (non-electrical: 7 pax oxygen → 0, 8 ELT → 0, 9 emergency exit lights → ON, 10 nose steering → 1).

4.4 Bus network (Electric_bus)#

Step 1 — take-off on APU. On the ground, takeoff_apu_powered := both buses on APU (code 3). In the air, if it is set and (a bus is no longer on APU, or X-Plane APU_generator_on = 0, or apu_bus_enable = 0): clear it, then for each side whose generator is available — side 1 needs gen1_available and the battery bus, side 2 needs gen2_available and DC STBY (same asymmetry as §4.2) — perform the GEN k ON action of §4.2 without changing ac_sel or the switch position (so SOURCE OFF stays lit until the crew selects).

Step 2 — fail_busk := 0 if X-Plane rel_esys/rel_esys2 = 6, else 1.

Step 3 — battery switch to X-Plane. BAT OFF: X-Plane battery_on[1] := 0, generator_apu_on := 0, APU_generator_on := 0, apu_power_bus1/2 := 0; and if X-Plane GPU is off, ac_sel[k] := 9 for each side whose X-Plane generator switch is off. BAT ON: battery_on[1] := 1, inverter_on[0] := 1. (Zibo writes these X-Plane arrays only when the value changes.)

Step 4 — BPCU. bpcu_status := gpu_available ∨ batbus_status ∨ dc_bus1_status (values from the previous frame). If BPCU failed or not powered and X-Plane GPU is on → X-Plane GPU_off.

Step 5 — source per transfer bus (side 1 shown; side 2 swaps 1↔2 and uses BTB2):

if X-Plane generator_on[0] = 1:      ac_power_bus[0] = 1
elif X-Plane gpu_on = 1:             ac_power_bus[0] = 4
else switch ac_power_bus[0]:
    1 (own gen)  -> 0
    2 (gen 2)    -> keep only while X-Plane generator_on[1] = 1, else 0
    3 (APU)      -> if X-Plane APU_generator_on = 0: 0
                    else ac_power_bus[0] = 3*OK(BTB1); apu_power_bus1 = OK(BTB1)
    4 (GPU)      -> 0

Step 6 — if neither bus is on APU: X-Plane APU_generator_on := 0 and apu_power_bus1/2 := 0; otherwise apu_power_bus_k := 0 for a bus not on APU. If neither bus is on GPU and X-Plane GPU is on → GPU_off.

Step 7 — ac_power_bus[k] := ac_power_bus[k] · OK(XFRk) · fail_busk (integer).

Step 8 — tie-bus source tie := 3 if X-Plane APU_generator_on, else 4 if X-Plane gpu_on, else 0.

Step 9 — bus tie breakers. btb1, btb2, dctie, trutie start at 0. "Cross-fail" below means: ac_power_bus[0] := ac_power_bus[0]·OK(XFR2)·fail_bus2 and ac_power_bus[1] := ac_power_bus[1]·OK(XFR1)·fail_bus1 (note the crossed factors: with both BTBs closed, bus 1 is multiplied by bus 2's health and vice versa. Combined with step 7 and the source copy below, a failed transfer bus 2 with BUS TRANSFER AUTO ends with bus 2 powered and bus 1 dark — oracle-verified, elec_spec_sources.json step 13; reproduce as is).

  • BPCU failed: if tie = 0 and OK(BTB2): tie := ac_power_bus[1], and if that is non-zero btb2 := 1. If ac_power_bus[0] = 0 and OK(BTB1): ac_power_bus[0] := tie, and if tie ≠ 0 btb1 := 1. If both closed: cross-fail.
  • BUS TRANSFER OFF (X-Plane cross_tie = 0) or bpcu_status ≠ 1: if both buses are on the same non-zero source, btbk := OK(BTBk) for both; otherwise btbk := OK(BTBk) only for a bus whose source is APU or GPU. If both closed: cross-fail. No source is copied between sides.
  • BUS TRANSFER AUTO and BPCU powered:
    • tie = 0 (no APU generator, no GPU): if exactly one bus has a source, and OK(BTB1) and OK(BTB2), the dead bus takes the same source code (one engine generator powers both buses); then btbk := OK(BTBk). If both buses have the same non-zero source, btbk := OK(BTBk) and, if both closed, cross-fail.
    • else if one bus is dead and the other is on the tie source: the dead bus takes it (if both BTBs are OK); btbk := OK(BTBk). This is how APU GEN 1 alone powers both buses (oracle Q26).
    • else: the BUS TRANSFER OFF rule above.
    • dctie := 1, except 0 while (glideslope captured — ap/glideslope_status = 2 — or GLS or LPV glidepath captured on the master FCC side, or autopilot/autoland_status = 1).
    • trutie := 1 if bus 2 is dead.
  • Outputs Z:electric/ac_cross_tie = btb1, ac_cross_tie2 = btb2, dc_cross_tie = dctie, tru_cross_tie = trutie; then, if both BTBs are closed, cross-fail once more (idempotent).

Step 10 — in the air, clear takeoff_apu_powered if the buses are no longer both on APU.

Step 11 — AC: ac_tnsbus{k}_status := (ac_power_bus[k] ≠ 0). TRUs: tru1 := OK(TR1)·XFR1; tru2 := OK(TR2)·XFR2; if trutie = 0 or the TRU3 relay failed: tru3 := OK(TR3)·XFR2 (fed from bus 2) else tru3 := OK(TR3)·XFR1 (fed from bus 1); tru3_via_bus = 2 / 1, 0 when tru3 = 0. Ground service: if gpu_available: ac_gnd_service_busk := OK(GSVCk) (powered by a connected GPU cart even if GRD PWR is not switched on — oracle R3), else OK(GSVCk)·XFRk. dc_gnd_service is never written (always 0). Main buses: CAB/UTIL OFF → main_bus1/2 and main_bus := 0; ON → main_busk := OK(MAINk)·XFRk·OK(BPCU), main_bus := main_bus1·main_bus2. IFE OFF → ife_pass_bus1/2 := 0, ON → := XFR1 / XFR2.

Step 12 — battery buses (dataref names are Zibo's; their meaning follows the failure items they use):

hot      = OK(HOTBAT) * (X-Plane battery_voltage_actual_volts[0] > 10)    -> hot_batbus_status
swhot    = OK(SWHOTBAT) * (BAT switch == ON ? hot : 0)                     -> batbus_status   (switched hot battery bus)
s        = STBY BAT  : hot
           STBY OFF  : swhot ? hot : 0
           STBY AUTO : swhot ? (tru3 ? 1 : hot) : 0
batbus   = OK(BATBUS) * s * hot                                            -> dir_batbus_status (battery bus)

So the battery bus is powered with BAT OFF when STBY PWR is at BAT, and is never powered when the hot battery bus is dead even if TRU 3 is running.

Step 13 — DC 1 / DC 2. d1 := OK(DC1)·tru1·fail_bus1; d2 := OK(DC2)·fail_bus2 if tru2 or tru3, else 0.

  • dctie = 0: DC 1 powered = d1; DC 2 powered = TRU 3 running (tru3_via_bus ≠ 0) — TRU 2 alone does not power DC 2 in this branch (Zibo quirk; only visible with a TRU 3 failure).
  • dctie = 1: a := OK(DC2)·d1·fail_bus2, b := OK(DC1)·d2·fail_bus1; DC 1 powered = DC 2 powered = a ∨ b. dc_bus1_status := OK(DC1)·(DC 1 powered)·fail_bus1; dc_bus2_status := OK(DC2)·(DC 2 powered)·fail_bus2.

Step 14 — standby buses (static inverter = AC STBY from battery):

STBY BAT : dcs = hot ; acs = hot
STBY OFF : dcs = 0   ; acs = 0
STBY AUTO: tr  = tru1 or (dctie and tru2) or (dctie and tru3)
           if swhot == 0: dcs = tr ; acs = XFR1
           else:          dcs = tr ; acs = XFR1
                          if not (acs and dcs): dcs = hot ; acs = hot     # both fall back to the battery together
X-Plane battery_on[2] := dcs ; X-Plane inverter_on[1] := acs   (on change)
dc_stdbus_status = OK(DCSTBY)*dcs ; ac_stdbus_status = OK(ACSTBY)*acs

Step 15 — STANDBY PWR OFF latches: for each of battery bus, DC STBY, AC STBY: powered → latch 1 and its timer := 2 s; unpowered → timer counts down by sim_period and the latch goes to 0 when the timer is 0. (The first frame with the timer at 0 clears the latch, so the light comes on 2.0–2.1 s after the loss — oracle: lit at +63 frames, not at +59.)

Step 16 — Z:electric/cross_tie := btb1·btb2 if the hot battery bus is powered, else 0.

Step 17 — warm-ups and CDU boot (two independent sides: side A uses DC STBY with warm_up_disp, init_mcdu1; side B uses DC 2 with warm_up_disp2, init_mcdu2):

if swhot == 0:                       # switched hot battery bus dead: both sides
    if warm: down_timer = 2
    warm = 0 ; up_timer = 0 ; init_mcdu = 0 ; mcdu_timer = 30     # (M3: only down_timer depends on warm)
else for each side:
    if side bus == 0:
        if warm: down_timer = 2
        warm = 0 ; up_timer = 0 ; init_mcdu = 0
        mcdu_timer = (down_timer == 0) ? 30 : 0.1    # short interruption (<2 s): CDU back almost at once
    else:
        if not warm and down_timer != 0: down_timer = 0 ; up_timer = 0.1   # short interruption: quick re-warm
        if up_timer == 0 and not warm:   up_timer = 32
        if mcdu_timer == 0: init_mcdu = 1 else mcdu_timer -= sim_period (not below 0)

up_timer/down_timer are counted down in Annun_timers (next frame); warm := 1 when up_timer reaches 0. Outputs Z:display/warm_up_disp, warm_up_disp2, Z:electric/mcdu1_on, mcdu2_on. Oracle (P2–P5): battery ON → mcdu1_on = 1 and warm_up_disp = 1 between 2 s and 42 s (timers 30 s / 32 s).

Step 18 — electrical panel warm-up: DC STBY off → both panel flags 0, DC timer 4 s, AC timer 5 s. DC STBY on → if a knob position changed since last frame and its panel was warm: flag 0, timer 0.2 s. Timers counted in Annun_timers; flag := 1 at 0.

Step 19 — init_ac_source: DC STBY off → 0 and timer 0.2 s; on → counts down, then 1. Step 20 — init_heat/init_heat2: DC 1 / DC 2 off → 0, timer 4.7 s; on → 1 after the timer. Step 21 — ISFD: battery bus off → warm_up_isfd 0, time 96 s; on and not warm → time −= sim_period; below 0 → 0 and warm. Output Z:display/isfd_init_time. Step 22 — 4-minute flags: ac_tns_bus{k}_pow4 = 0: XFRk on → start a 240 s timer if not running (counted in Annun_timers, flag := 1 at 0); XFRk off → stop the timer. Flag = 1 and XFRk off → stop timer, flag := 0. Step 23 — X-Plane avionics_on := batbus_status (on change).

4.5 Generator availability and drop-out (Systems, after Electric_bus)#

APU generator (apu_bus_enable, computed by the APU logic — see the APU spec): goes to 1 two seconds after the APU is running and stabilised (APU N1 > 94 %, EGT in its window); goes to 0 when the APU stops or shuts down, and whenever both batbus_status and dc_bus2_status are 0. Then:

  • if the APU-generator failure value = 1: X-Plane APU_generator_on := 0, apu_power_bus1/2 := 0;
  • else if (neither bus on APU or apu_bus_enable = 0) and X-Plane generator_apu_on = 1: same. (sim/cockpit/electrical/generator_apu_on is X-Plane's legacy alias of the APU generator switch.)

Engine generators (first_timer = seconds since load):

  • first_timer ≤ 6 s: genk_available := X-Plane generator_on[k-1] (trust the loaded state).
  • later, per side k (index i = k−1), on a working copy g of X-Plane generator_on:
    1. Zibo failure GENk = 1 and X-Plane rel_genera{i} ≠ 6 → g[i] := 0, write generator_on[i] := 0, rel_genera{i} := 6.
    2. Zibo failure GENk ≠ 1 and X-Plane rel_genera{i} = 6 → g[i] := 0, write generator_on[i] := 0, Zibo failure GENk := 1.
    3. Drive disconnect: start lever k ≥ 0.9 and DRIVE DISC k switch = 1 and rel_genera{i} ≠ 6 → g[i] := 0, generator_on[i] := 0, rel_genera{i} := 6, Zibo failure GENk := 1 (permanent until the failure is cleared from the EFB). With the start lever at CUTOFF the switch does nothing.
    4. genk_available := (eng_N2[k] ≥ 49.5 and rel_genera{i} ≠ 6); if not available g[i] := 0.
    5. If g differs from generator_on, write it (this is how a generator drops off when its engine spools down: the next frame's §4.4 step 5 sees the X-Plane switch off).

4.6 Meters (Systems)#

AC meter (Z:knob/ac_power):

Pos Label ac_freq_value ac_volt_value ac_amp_value
0 STBY PWR X-Plane GPU on: unchanged; else v = AC STBY · battery_voltage_indicated_volts[2]: 0 if v = 0, freq_ac_mode0 if v > 0 Rescale(0…28 V → 0…115 V) of x, where x = 29 if X-Plane GPU on, else clamp(v, 0, 28) (so 115 V on ground power, 100.6 V on a 24.5 V battery) unchanged
1 GRD PWR gpu_available ? freq_ac_mode1 : 0 gpu_available ? volts_ac_mode1 : 0 unchanged
2 GEN 1 gen1_available ? freq_ac_mode2 : 0 … volts_ac_mode2 : 0 clamp(generator_amps[0], −99, 99) : 0
3 APU GEN apu_bus_enable ? freq_ac_mode3 : 0 … volts_ac_mode3 : 0 clamp(APU_generator_amps, −99, 99) : 0
4 GEN 2 gen2_available ? freq_ac_mode4 : 0 … volts_ac_mode4 : 0 clamp(generator_amps[1], −99, 99) : 0
5 INV X-Plane inverter_on[0] ? freq_ac_mode5 : 0 … volts_ac_mode5 : 0 unchanged
6 TEST unchanged (last values stay) unchanged unchanged

DC meter (Z:knob/dc_power):

Pos Label (Zibo logic) dc_volt_value dc_amp_value
0 STBY PWR 29 if X-Plane GPU on, else AC STBY · battery_voltage_indicated_volts[2] 0
1 BAT BUS 29 if X-Plane GPU on, else battery_voltage_indicated_volts[1] 0
2 BAT as 1 clamp(battery_amps[2] + battery_amps[1], −99, 99)
3 TR 1 29 · tru1 clamp(tru1 · bus_load_amps[0], −99, 99)
4 TR 2 29 · tru2 clamp(tru2 · bus_load_amps[1], −99, 99)
5 TR 3 0 if TRU 3 off, else 29 · tru3 clamp(tru3 · bus_load_amps[via−1], −99, 99) (0 if off)
6 TEST unchanged unchanged

Note the SCI enum ELEC_DC_METER_POS = STBY_PWR, BAT_BUS, BAT, AUX_BAT, TR1, TR2, TR3 does not match Zibo's logic from index 3 on (Zibo: 3 = TR1 … 5 = TR3, 6 = TEST/hold). The enum labels are only names; the index is what the model animates, so keep Zibo's index semantics and fix the labels (§9).

Panel visibility: Z:ac_pnl_warm_up := DC STBY ? AC-panel flag : 0; Z:dc_pnl_warm_up := DC-panel flag when DC STBY is powered, left unchanged when it is not (Zibo quirk; the flag itself is 0 then, so the next powered frame shows 0).

4.7 Annunciators (Annunciators, before Systems)#

Brightness factors (all lights below are multiplied by one of them):

day  = 1 ; if X-Plane version >= 12.0:
          sun pitch >= 35 -> 1 ; 0..35 -> Rescale(0->0.1, 35->1) ; -5..0 -> Rescale(-5->0.05, 0->0.1) ; < -5 -> 0.05
B    = battery_bus * X-Plane generic_lights_brightness_ratio[14] * day     # normal annunciators

generic_lights_brightness_ratio[14] is X-Plane's output for the generic light Zibo drives for the annunciator DIM/BRT switch (§7); the oracle leaves it 0, so every light below reads 0 in the oracle unless a script sets that dataref (the elec_spec_* scripts set it to 1 and the sun pitch to 40°).

Light (Zibo dataref annunciator/…) Condition (raw) Value
bat_discharge batt_discharge (previous frame, below) ·B
tr_unit (XFR1 ∧ ¬TR1) ∨ T, where with BUS TRANSFER OFF or BPCU unpowered/failed T = XFR2 ∧ (¬TR2 ∨ ¬TR3); with BUS TRANSFER AUTO and BPCU powered T = (XFR1 ∧ ¬TR3) ∨ (XFR2 ∧ ¬TR2) when RA < 10 ft, and T = XFR1 ∧ ¬TR3 ∧ XFR2 ∧ ¬TR2 when RA ≥ 10 ft ·B
trans_bus_off1/2 ¬XFR1 / ¬XFR2 ·B·init_ac_source
drive1/2 X-Plane rel_genera{0/1} = 6 or engine N2 < 13 % ·B
ground_power_avail gpu_available = 1 ·day only (not gated by any bus; lit in cold & dark when the GPU is connected)
apu_gen_off_bus X-Plane APU_generator_amps ≤ 0 ∧ apu_bus_enable = 1 ·B·init_ac_source
gen_off_bus1/2 X-Plane annunciators/generator_off[0/1] ·B·init_ac_source
source_off1/2 ac_power_bus[k] ≠ ac_sel_power_bus[k] ·B·init_ac_source
standby_pwr_off any of the three latches of §4.4 step 15 is 0 ·B
elec never (always 0; ELEC light not modelled) 0, but B in lights test
six_pack_elec ELEC six-pack latch (below) ·B

Lights test (Z:annunciator/test = 1): all of the above (and every other annunciator) := B. Six-pack ELEC: a fault vector of 20 entries, of which Zibo fills 8: [0] SOURCE OFF 1, [1] SOURCE OFF 2, [2] TRANSFER BUS OFF 1, [3] TRANSFER BUS OFF 2, [4] BAT DISCHARGE, [5] DRIVE 1, [6] DRIVE 2, [7] TR UNIT (raw conditions, without the brightness or init_ac_source factors). Generic six-pack latch shared with the other systems: an entry rising 0→1 sets elec_fail_ann and marks it; when every marked entry has gone back to 0 the latch clears; MASTER CAUTION (master_caution1/2) clears the latch and all marks (a fault still present re-lights only on a new rising edge — recall/master-caution details belong to the annunciator spec). Oracle Q2: battery ON in cold & dark → ELEC six-pack and MASTER CAUTION lit (DRIVE, TRANSFER BUS OFF and SOURCE OFF entries rise).

Battery discharge (B738_battery_disch_annun, end of Annunciators):

a = battery_amps[1] + battery_amps[2]
if BUS TRANSFER AUTO and bpcu_status == 1 and OK(BPCU): a += battery_amps[0]
if a > -5:  discharge5 = discharge15 = discharge100 = 0 ; stop all three timers
elif a > -15:  start the 95 s timer if it is not running
elif a > -100: start the 3.5 s timer if it is not running
else:          start the 2 s timer if it is not running
if a > -15: discharge15 = discharge100 = 0
if a > -100: discharge100 = 0
batt_discharge = discharge5 or discharge15 or discharge100

The timers run in Annun_timers; each sets its flag when it reaches 0. Timers keep running when a moves between bands (only a > −5 stops them). Oracle Q13–Q15 (amps forced to −20): light after 3.5 s (off at 3.33 s, on at 3.67 s); back to −8 A → off within a frame.

AP "lost AC" (Z:electric/lost_ac_ap_a/_b, read by the AFDS): after first_timer > 6 s, per side, 1 for the frame in which "GPU amps < 1" changed, or that side's "APU amps < 1 or bus not on APU" changed, or X-Plane generator_off[k] changed; a GPU change sets both sides. Inputs are X-Plane amps (§7).

4.8 Ground power availability (B738_gpu_power, Lua gpu_toggle)#

EFB "GPU" (laminar/B738/gpu_toggle): if the cart is shown, hide it; if hidden, show it only when a wheel is on the ground and ground speed < 0.08 m/s. Then each frame:

keep = (gpu_test == 0) and (gpu_test_enable != 0 or on ground)
if gpu_test != 0: gpu_test = 0
if not keep and gpu_hide == 0: gpu_hide = 1          # cart vanishes in the air (or on a GPU test request)
src = sam_enable ? jetway_power : -1
if not OK(BPCU):          gpu_available = 0 ; reset autogate timers
elif not on ground:       gpu_available = 0 ; X-Plane gpu_on = 0
elif gpu_hide == 0:       gpu_available = 1                      # EFB cart
elif src == 0:            gpu_available = 0 ; gpu_on = 0          # SAM jetway without power
elif src == 1:            gpu_available = 1                      # SAM jetway power
elif near_apt_icao empty or autogate_gpu == 0: gpu_available = 0 ; gpu_on = 0
else: AutoGate logic (15 s connect/disconnect timers, parking brake, engine 2, beacon) — §9
if X-Plane override_GPU_volts exists: set it to 1 and GPU_generator_volts = available ? 28.5 : 0

Runs at 2 Hz (§4.1), so gpu_available follows the EFB toggle 0–0.5 s later (oracle: 3 to 30 frames). Removing the cart while it powers the aircraft drops both buses (gpu_available 0, X-Plane gpu_on 0, SOURCE OFF + TRANSFER BUS OFF lit).

4.9 Power gates for displays and other systems#

Zibo has no single "powered" flag per device; each consumer tests a bus. What the displays use (Systems):

Device Content shown (Z:display_units/* ≥ 0, −1 = blank) Brightness gate (multiplies the knob brightness into X-Plane instrument_brightness)
Captain outboard/inboard DU (odu_cpt, idu_cpt) XFR2 ∨ battery bus (depending on the DU selector, the inboard shows ND/ENG/PFD/MFD formats) DC STBY · warm_up_disp (and 0 if the switched hot battery bus is off)
Upper DU (udu) always 0 or −1 by the LOWER DU selector only (never blanked by power) DC STBY · warm_up_disp
FO outboard/inboard DU (odu_fo, idu_fo) XFR2 DC 2 · warm_up_disp2 (0 if switched hot battery bus off)
Lower DU (ldu) XFR2 DC 2 · warm_up_disp2
CDU 1 / CDU 2 — DC STBY · mcdu1_on / DC 2 · mcdu2_on (index 10 / 11)
Display tint Z:electric/tint_dspl_capt / _fo — 0.72 · DC STBY / 0.72 · DC 2 (0 if switched hot battery bus off)
Annunciators — battery bus · DIM/BRT · day factor (§4.7)
ISFD — battery bus, after isfd_init_time (96 s)
Electrical meters — dc_pnl_warm_up, ac_pnl_warm_up (DC STBY + 4 s / 5 s)

Other consumers (non-exhaustive, from cross references): hydraulics (XFR, DC), fuel pumps (XFR1/2 — and they add 4–6 A each to X-Plane plugin_bus_load_amps), anti-ice/window heat (XFR, DC 1/2, init_heat), lighting (panel brightness: battery bus or XFR2 · knob), APU (switched hot battery bus, DC 2), AFDS/MCP (DC 1/2, lost_ac_ap_a/b), flight controls, GPWS, transponder/TCAS tests (XFR1), pressurisation (battery bus, DC 1/2, XFR), FMS (mcdu1_on, mcdu2_on), Lua glareshield/sounds (*_status, main_bus1/2). The SCI _PWR states should be derived from these Zibo gates, see §6.

5 Constants, tables, timings#

5.1 Source codes#

ac_power_bus / ac_sel_power_bus: 0 none, 1 GEN 1, 2 GEN 2, 3 APU, 4 GPU, 9 nothing selected (selection only; initial value of both selections).

5.2 Thresholds#

Constant Value Where
hot battery bus alive battery actual volts > 10 V §4.4 step 12
generator available engine N2 ≥ 49.5 % (and no generator failure) §4.5
DRIVE light engine N2 < 13 % or generator failure §4.7
drive disconnect effective start lever (mixture_ratio) ≥ 0.9 §4.5
generator logic "load" phase first_timer ≤ 6 s §4.5
TR UNIT air/ground switch radio height 10 ft §4.7
guard closed guard position < 0.3 §4.3
guard animation rate 20 /s (exponential, Anim_value) §4.3
GPU cart allowed on ground, ground speed < 0.08 m/s §4.8
GPU volts (X-Plane override) 28.5 V when available, else 0 §4.8
meter: DC volts with GPU or a TRU 29 V §4.6
meter: AC STBY volts scale 0…28 V → 0…115 V, input clamped to 0…28; 29 V (GPU) → 115 V §4.6
meter: random AC values frequencies uniform in [395, 405] Hz (6 values), volts uniform in [112, 118] V (5 values), drawn once per flight start (rand() % 100000 · 1e-5) §4.6
meter amps clamp ±99 A §4.6
frame period cap 0.1 s (first frame 0.01 s) §4.1
DU tint 0.72 §4.9
day factor X-Plane ≥ 12: sun pitch ≥ 35° → 1; 0…35° → 0.1…1 linear; −5…0° → 0.05…0.1; < −5° → 0.05 §4.7
annunciator DIM generic light 14 switch 0.5 (BRT and TEST: 1) §7

5.3 Timers#

Timer Value Start / effect
STANDBY PWR OFF delay 2 s after loss of battery bus, DC STBY or AC STBY
init_ac_source 0.2 s after DC STBY powered; gates TRANSFER BUS / SOURCE / GEN / APU GEN OFF lights
DU warm-up (warm_up_disp, warm_up_disp2) 32 s after DC STBY / DC 2 (with switched hot battery bus)
DU warm-down memory 2 s an interruption shorter than this re-warms in 0.1 s
CDU boot (mcdu1_on, mcdu2_on) 30 s (0.1 s after a short interruption) after DC STBY / DC 2
electrical panel warm-up DC 4 s, AC 5 s (cold & dark start and after DC STBY loss); 0.2 s after a knob step
init_heat / init_heat2 4.7 s after DC 1 / DC 2
ISFD initialisation 96 s after battery bus
transfer bus "4 min" flags 240 s after XFR 1 / XFR 2
BAT DISCHARGE > 5 A for 95 s; > 15 A for 3.5 s; > 100 A for 2 s (discharge current) §4.7
AutoGate GPU connect/disconnect 15 s §4.8
APU generator available 2 s after APU stabilised (APU spec)

5.4 Failure items (laminar/B738/failure/electric[i], Lua array of 70, EFB failure page)#

failure(v) is true ("OK") when v = 0 or v = 2; v = 1 is "failed" (the GEN/APU-GEN checks compare with 1 directly).

i Item i Item i Item
0 AC transfer bus 1 (XFR1) 8 DC switched hot battery bus 16 TR 3
1 AC transfer bus 2 (XFR2) 9 DC battery bus 17 IDG 1 / generator 1
2 AC main bus 1 10 DC bus 1 18 IDG 2 / generator 2
3 AC main bus 2 11 DC bus 2 19 APU generator
4 AC standby bus 12 DC ground service bus (unused by the logic) 20 BPCU
5 AC ground service bus 1 13 DC standby bus 21 relay BTB 1
6 AC ground service bus 2 14 TR 1 22 relay BTB 2
7 DC hot battery bus 15 TR 2 23 relay TRU 3 (TR 3 cross-feed)

X-Plane failures also read: rel_esys/rel_esys2 (6 = transfer-side 1/2 down) and rel_genera0/1 (6 = generator failed). Default for the rewrite: all 0.

6 Outputs#

6.1 Zibo datarefs written by the electrical logic#

Buses (laminar/B738/electric/…, 0/1 floats): hot_batbus_status, batbus_status (switched hot battery bus), dir_batbus_status (battery bus), dc_bus1_status, dc_bus2_status, dc_stdbus_status, ac_stdbus_status, ac_tnsbus1_status, ac_tnsbus2_status, main_bus1, main_bus2, main_bus, ac_gnd_service_bus1/2, dc_gnd_service (always 0), tru1_status, tru2_status, tru3_status, ac_cross_tie, ac_cross_tie2, dc_cross_tie, tru_cross_tie, cross_tie, ife_pass_bus1/2, mcdu1_on, mcdu2_on, ac_tns_bus1_pow4, ac_tns_bus2_pow4, gen1_available, gen2_available, lost_ac_ap_a/_b, tint_dspl_capt/_fo; laminar/B738/electrical/apu_power_bus1/2; laminar/B738/gpu_available. Switches: electric/battery_pos, electric/standby_bat_pos, electrical/gpu_pos, gen1_pos, gen2_pos, apu_gen1_pos, apu_gen2_pos, one_way_switch/drive_disconnect1_pos/2_pos, toggle_switch/cab_util_pos, ife_pass_seat_pos, knob/dc_power, knob/ac_power, button_switch/cover_position[0..10]. Meters: dc_volt_value, dc_amp_value, ac_volt_value, ac_amp_value, ac_freq_value, dc_pnl_warm_up, ac_pnl_warm_up. Lights (annunciator/…): bat_discharge, tr_unit, elec, standby_pwr_off, ground_power_avail, drive1/2, trans_bus_off1/2, source_off1/2, gen_off_bus1/2, apu_gen_off_bus, six_pack_elec (and the master caution it feeds). Display gates: display_units/odu_cpt, idu_cpt, udu, ldu, idu_fo, odu_fo, display/warm_up_disp, warm_up_disp2, display/isfd_init_time. X-Plane datarefs/commands Zibo writes: battery_on[0..2], inverter_on[0..1], generator_on[0..1], APU_generator_on, generator_apu_on, cross_tie, avionics_on, rel_genera0/1, GPU_generator_volts + override, commands GPU_on/off, generator_1/2_on/off (§7).

6.2 SCI states (data/sci/contract/10_elec.json) and their source in Zibo#

SCI state Zibo source today (profile elec.json) Comment / what the rewrite must output
ELEC_BAT_POS, ELEC_STBY_PWR_POS, ELEC_GPU_POS, ELEC_GEN1/2_POS, ELEC_APUGEN1/2_POS, ELEC_DRIVE1/2_DISC_POS, ELEC_CAB_UTIL_POS, ELEC_IFE_POS, ELEC_DC_METER_POS, ELEC_AC_METER_POS, APU_SW_POS switch datarefs of §6.1 (maps −1/0/1 → 0/1/2) as is
ELEC_BAT_GRD, ELEC_STBY_PWR_GRD cover_position[2], [3] > 0.5 as is (guard 6 = generic BUTTON_SWITCH_COVER_POSITION_6)
ELEC_BUS_TRANS_POS shadow (profile says "not modelled") wrong: read X-Plane cross_tie (the rewrite's own BUS TRANSFER state, §2.1); drive with cross-tie on/off; forced AUTO while guard 6 is closed
SRC_GPU_AVAIL gpu_available ok
SRC_APU_GEN_RDY electrical/apu_bus_enable ok
SRC_IDG1_RDY / _2 N2 > 55 % Zibo's own is electric/gen1_available (N2 ≥ 49.5, failures) — use it
BUS_HOT_BAT hot_batbus_status ok
BUS_SW_HOT_BAT dir_batbus_status swapped: Zibo's switched hot battery bus is batbus_status
BUS_BAT batbus_status swapped: Zibo's battery bus is dir_batbus_status (differs when STBY PWR = BAT with BAT OFF: battery bus powered)
BUS_DC1, BUS_DC2, BUS_DC_STBY, BUS_AC_STBY, BUS_XFR1/2, BUS_MAIN1/2, BUS_GND_SVC1/2 the corresponding *_status ok
ELEC_POWERED, ELEC_AC_POWERED, ELEC_STATE synthesized expressions not Zibo; keep the expressions, computed from the rewrite's buses and ac_power_bus (GPU/APU/ENG/MIXED)
DU_CPT_OUTBD_PWR, DU_CPT_INBD_PWR, DU_UPPER_PWR BUS_AC_STBY ∧ display_units ≥ 0 Zibo's gate is switched hot battery bus ∧ DC STBY ∧ warm_up_disp (32 s warm-up) ∧ display_units ≥ 0. Differs: BAT OFF on ground power (Zibo dark, SCI lit), first 32 s after power-up (Zibo dark)
DU_LOWER_PWR, DU_FO_INBD_PWR, DU_FO_OUTBD_PWR BUS_XFR2 ∧ display_units ≥ 0 Zibo: switched hot battery bus ∧ DC 2 ∧ warm_up_disp2 ∧ display_units ≥ 0
CDU1_PWR / CDU2_PWR BUS_XFR1 / BUS_XFR2 Zibo's brightness gate: DC STBY · mcdu1_on / DC 2 · mcdu2_on (30 s boot); whether the FMS Lua draws a boot page before that is an open question (§9)
ISFD_PWR BUS_DC_STBY Zibo: battery bus (dir_batbus_status); attitude valid after 96 s (isfd_init_time)
MCP_PWR, EFIS_*_PWR, RADIO*_PWR, CLOCK_PWR, ELEC_METER_PWR synthesized Zibo: MCP lights need (DC1 ∨ DC2 ∨ DC STBY) ∧ switched hot battery bus; meters: DC STBY + warm-up flags; others: keep the synthesis until their specs exist
ELEC_DC_VOLTS, ELEC_AC_VOLTS dc_volt_value, ac_volt_value ok
ANN_BAT_DISCHARGE, ANN_TR_UNIT, ANN_ELEC, ANN_STBY_PWR_OFF, ANN_GRD_PWR_AVAIL, ANN_DRIVE1/2, ANN_TRANS_BUS_OFF1/2, ANN_SOURCE_OFF1/2, ANN_GEN_OFF_BUS1/2, ANN_APU_GEN_OFF_BUS, ANN_SIXPACK_ELEC the annunciator/* datarefs (brightness already applied) ok; the profile's fallback synth expressions are not Zibo's (e.g. GRD POWER AVAILABLE is not bus-gated in Zibo, STBY PWR OFF has a 2 s delay and includes the battery bus, SOURCE OFF is selected≠actual, DRIVE is N2 < 13)

7 X-Plane-provided behaviour the rewrite must supply#

Zibo reads or commands these; X-Plane (not Zibo) computes them. The oracle host emulates only a few (xphost/src/xpsim.cpp), so oracle values for them are not reference values.

# X-Plane behaviour What X-Plane does (as far as determinable) Oracle host Rewrite: provide / MSFS candidate
1 Battery voltage battery_voltage_actual_volts[0], _indicated_volts[1..2] Battery model from the ACF (2 batteries, _battery_watt_hr_max 1152 Wh): 24 V on battery, rising toward bus voltage (28 V) when charged; falls as the charge is used. Zibo only needs > 10 V for the hot battery bus and shows the value on the DC meter. fixed 24.5 V, 28 V when its model has AC; never discharges (its own 30-min drain only drops volts when its model says so) own battery model in WASM (state of charge integrated from load; volts from charge). MSFS A:ELECTRICAL BATTERY VOLTAGE:n, A:ELECTRICAL BATTERY ESTIMATED CAPACITY PCT:n if the MSFS electrical system is used
2 Battery current battery_amps[0..2] (negative = discharge) from bus loads when batteries feed buses; positive when charging always 0 → BAT DISCHARGE never lights own model: discharge current = load on battery-fed buses (incl. Zibo's plugin_bus_load_amps additions). MSFS A:ELECTRICAL BATTERY LOAD:n
3 Bus load bus_load_amps[0..1] sum of the loads on X-Plane buses 0/1 0 own load estimate; MSFS A:ELECTRICAL MAIN BUS AMPS / circuit amps
4 Engine generator switch generator_on[k] set by sim/electrical/generator_k_on/off command sets the switch; generator produces when its engine turns above the ACF cut-in and is not failed commands not implemented → generator_on stays 0, engine generators can never power a bus in the oracle own state set by the commands of §4.2; forward to MSFS only if MSFS-side loads need it (K:TOGGLE_ALTERNATORn / A:GENERAL ENG MASTER ALTERNATOR:n)
5 Generator current generator_amps[k], light annunciators/generator_off[k] amps = load carried by that generator; generator_off = generator switch off or not producing 0 / 0 → GEN OFF BUS never lights generator_off[k] = ¬(generator_on[k] ∧ N2 above cut-in ∧ not failed) — Zibo already forces the switch off below 49.5 % N2, so ¬generator_on[k] is a good first approximation; amps from own load model
6 Ground power gpu_on via GPU_on/off; gpu_amps command sets the connection; with Zibo's volts override the GPU only supplies power when GPU_generator_volts > 0 GPU_on/off implemented; amps 0 own state; MSFS A:EXTERNAL POWER ON, K:SET_EXTERNAL_POWER / K:TOGGLE_EXTERNAL_POWER if MSFS loads need it
7 APU generator APU_generator_on (and legacy alias generator_apu_on), APU_generator_amps switch; amps > 0 when the APU generator carries load (APU running, connected) the two datarefs are separate in the host (alias not reproduced), amps 0 → APU GEN OFF BUS stays lit with the APU on the buses treat both names as one variable; APU gen amps = load share when a bus is on the APU (> 0). MSFS A:APU GENERATOR SWITCH, A:APU GENERATOR ACTIVE
8 BUS TRANSFER cross_tie via sim/electrical/cross_tie_on/off switch state (X-Plane also ties its own buses) commands implemented own state
9 battery_on[0..2], inverter_on[0..1], avionics_on switch states that power X-Plane's own buses/avionics avionics_on is overwritten by the host from its synthesized model (e.g. 1 with BAT OFF on ground power, where Zibo writes 0) keep internally; avionics_on (= switched hot battery bus) → K:AVIONICS_MASTER_SET if MSFS radios/avionics are used
10 Failures rel_esys, rel_esys2, rel_genera0/1 X-Plane failure system (6 = failed) 0 internal failure table, default 0
11 generic_lights_brightness_ratio[14] (and [13]) brightness of generic light 14 = its switch value (Zibo writes switch [14] = 1 for BRT/TEST, 0.5 for DIM) when the light's bus is powered 0 → every bus-gated electrical light reads 0 in the oracle ratio = switch value (1 / 0.5); initial value: BRT (1) — verify in X-Plane
12 Sun pitch sun_pitch_degrees, xplane_internal_version sun elevation; version ≥ 12 selects the day factor ~0° (day factor 0.1) solar elevation from E:ZULU TIME and position (no direct SimVar known); version: always the X-Plane 12 branch
13 Ground/air, RA, ground speed, N2, start levers flight model settable A:SIM ON GROUND, A:RADIO HEIGHT, A:GROUND VELOCITY; N2 and start levers from the engine module
14 plugin_bus_load_amps X-Plane adds it to the bus load (battery drain) ignored include in own load model (fuel pumps add 4–6 A)

The MSFS electrical system (systems.cfg) is not the model: the WASM module computes the buses and only mirrors what MSFS-side consumers need (external power, battery master, avionics master, alternators). Decide in M3 whether MSFS lights/avionics are left permanently powered or driven from the WASM buses.

7.1 The oracle host's synthesized model compared with Zibo#

xphost/src/sci.cpp (Sci::updateElec) feeds X-Plane quantities to the plugins and provides SCI fallbacks. It is not Zibo; where it differs:

Topic Host model Zibo
Source latching own latch from SCI switch-position edges (GPU/APU/IDG per side) X-Plane switch states (generator_on, gpu_on, APU_generator_on) + ac_power_bus/ac_sel_power_bus with failures and BTB relays (§4.4)
APU GEN with GPU on APU takes the side and also the other side if that side was on GPU APU GEN k fires GPU_off: GPU leaves both sides; the other side gets the APU through the BTBs only via the tie logic; SOURCE OFF lights on the side still selected to GPU
APU GEN k OFF while the other APU GEN is on side k loses its source (repowered by cross-feed if BUS TRANSFER AUTO) side k stays on the APU (apu_power_bus_k returns), SOURCE OFF k
Engine generators IDG ready = N2 > 55 % ∧ DRIVE switch at NORM; reconnects when the switch returns available at N2 ≥ 49.5 %; DRIVE DISC only with start lever ≥ 0.9 and then permanent (failure)
Cross-feed XFRk from the other side's source if BUS TRANSFER AUTO and that source is not the GPU §4.4 step 9 (GPU also cross-feeds through the tie when one side is dead)
TRU 3 / DC buses TRU3 = XFR2 ∨ (AUTO ∧ XFR1); DC1 = TR1 ∨ (AUTO ∧ TR3); DC2 = TR2 ∨ TR3 §4.4 steps 11, 13 (DC tie opens on approach; DC 2 needs TRU 3 when the DC tie is open)
Battery bus naming BUS_BAT = BAT ON ∧ (hot ∨ DC1 ∨ DC2); BUS_SW_HOT_BAT = hot ∧ BAT ON battery bus depends on STBY PWR (BAT position powers it with BAT OFF); profile reads the two Zibo datarefs swapped (§6.2)
Standby buses at STBY BAT need BAT ON powered from the hot battery bus regardless of BAT
Standby buses at AUTO with BAT ON DC STBY = DC1 or battery; AC STBY = XFR1 or battery, independently both fall back to the battery together when either XFR1 or the TR source is missing
Ground service buses XFR ∨ (GPU available ∧ a ELEC_GND_SVC_POS switch) GPU cart connected powers them; no switch
Main buses XFR ∧ CAB/UTIL XFR ∧ CAB/UTIL ∧ BPCU (and failures)
Battery charge drains in ~30 min on battery, recharges in 1 h on AC; battery volts 24.5/28 V, 0 when "empty" from X-Plane's battery model
Lights (profile synth) simple bus expressions §4.7: delays, latches, BPCU, X-Plane amps/annunciators
X-Plane values written bus_volts[0..5], battery_voltage_*, avionics_on from the synthesized buses, every frame Zibo reads bus_volts only for internal values; writes avionics_on itself (the host overwrites it)
Not emulated generator_k_on/off commands, generator/APU/battery amps, generator_off annunciators, generator_apu_on alias, generic light brightness all used by Zibo (§7)

8 Differential test scenarios#

Harness rules: dt = 1/30 s; both sides get identical sim inputs; the X-Plane quantities of §7 that the oracle does not compute are injected identically into both (oracle: "set"; rewrite: SimIO); compare the Zibo datarefs of §6.1 by name (the SCI layer maps them), exactly for 0/1 values and positions, ±1 frame for timer edges, ±0.01 for brightness products, and meter random values only as "0 / inside [395,405] Hz / inside [112,118] V". The slow-loop phase of §4.1 (GPU availability, 0.5 s) must be aligned: start the accumulator at load like Zibo and compare from the same first frame.

Oracle scripts (both pass, lu737host --aircraft <dir> --cold --sci-test <script>):

  • xphost/test/sci/elec_spec_battery.json (22 checks) — cold & dark; BAT ON (buses within one frame; DRIVE 1/2, ELEC six-pack, TRANSFER BUS OFF, SOURCE OFF lit); CDU boot at 30 s and DU warm-up at 32 s; STBY PWR OFF (buses at once, STANDBY PWR OFF light at 2.0 s, not at 1.9 s); STBY BAT with BAT OFF (battery bus and standby buses stay powered, avionics_on 0); guard closing sets STBY AUTO and BAT ON on the second frame; BAT DISCHARGE at 3.5 s with −20 A injected, out at −8 A; LIGHTS TEST.
  • xphost/test/sci/elec_spec_sources.json (25 checks) — GPU cart (2 Hz update; GRD POWER AVAILABLE lit with BAT OFF; ground service buses; BPCU); GRD PWR ON with BAT OFF (all AC/DC, standby from TR 1/XFR 1, DUs never warm, CDU never boots); BAT ON clears SOURCE OFF; BUS TRANSFER forced AUTO under its guard, OFF with the guard open (DC and TRU 3 ties open); GRD PWR OFF (SOURCE OFF, standby on battery); cart removed while powering; APU start; APU GEN 1 alone powers both buses (SOURCE OFF 2); APU GEN 2; APU GEN 1 OFF keeps bus 1 on the APU; GRD PWR takes over from the APU; APU GEN OFF BUS follows injected APU amps; TR 3 failure (TR UNIT; DC 2 lost with BUS TRANSFER OFF); XFR 2 failure (sides swapped with AUTO).

Further scenarios for the differential suite (event sequence → values to compare):

# Sequence Compare
D1 Cold & dark, then every control of §2.1 swept through all positions with no power (SCI events) positions, guards; all buses/lights stay 0; ground_power_avail 0
D2 BAT ON, DC meter 0→6→0 and AC meter 0→6→0, 1 s per position; inject battery indicated volts 24.0/24.5 dc_volt_value, ac_volt_value (0…28→0…115 scaling), dc/ac_pnl_warm_up (0.2 s blank per step, 4 s / 5 s after power-up), position 6 holds the previous values
D3 GPU + BAT, then BAT OFF (guard open) batbus/dir_batbus 0, DC/AC STBY 1, warm_up_disp → 0, mcdu1_on → 0, avionics_on 0, DU brightness gates 0
D4 Short power interruption: BAT only, STBY OFF for 1 s then AUTO; and for 3 s then AUTO 1 s: warm_up_disp back after 0.1 s, mcdu1_on after 0.1 s; 3 s: full 32 s / 30 s
D5 Engine generators (inject: engine N2 per side, X-Plane generator_on semantics of §7 item 4, generator_off per item 5): N2 1 = 60 %, GEN 1 ON (needs battery bus) → both buses on GEN 1 (BUS TRANSFER AUTO, no APU/GPU); GEN 2 ON (needs DC STBY) → own; N2 1 → 40 % gen1_available, X-Plane generator_on[0] forced 0, bus 1 recovered from GEN 2 via BTBs next frame, SOURCE OFF 1, GEN OFF BUS 1, DRIVE 1 below 13 %
D6 DRIVE DISC 1 with start lever CUTOFF, then at IDLE (≥ 0.9) no effect / permanent generator 1 failure (rel_genera0 6, failure[17] 1, DRIVE 1)
D7 Take-off on APU: both buses on APU, GEN switches off, N2 both 80 %; lift-off (on-ground 0); APU stopped (apu_bus_enable → 0) automatic GEN 1 / GEN 2 connection (side 2 only if DC STBY), ac_sel unchanged → SOURCE OFF 1/2 lit
D8 Approach: GPU-free flight on two generators, glideslope captured (ap/glideslope_status = 2 on the master side) dc_cross_tie 0 while captured; TR UNIT in-air rule (RA ≥ 10 ft) with a TR 2 failure
D9 Failures one at a time (indices 0–23, value 1) on GPU+BAT and on battery only all bus datarefs; BPCU failure (index 20) case of §4.4 step 9; ground-service and main-bus items
D10 Battery discharge bands: inject −6 A for 100 s, −20 A for 4 s, −120 A for 3 s, back to +10 A bat_discharge at 95 s / 3.5 s / 2 s; cleared at > −5 A
D11 Lights: BRT/DIM/TEST (bright_test_dn twice → DIM: inject generic ratio 0.5) and sun pitch −10°, 0°, 20°, 40° every electrical light value = condition × 0.5/1 × day factor; GRD POWER AVAILABLE only × day factor
D12 Load at flight start in the three states of §3.3 (cold & dark, turnaround, engines running) initial switch positions, guards, GPU state, warm-ups, ac_sel/ac_power_bus

9 Open questions and uncertainties#

  1. X-Plane battery, generator and amp models (§7 items 1–5, 7) are not reproduced by the oracle and could not be measured here. Exact battery voltage curve, charge rate, and generator cut-in of X-Plane 12 for this ACF (2 batteries, 1152 Wh, 3 buses, 2 generators, 1 inverter) are unknown; the rewrite's model will differ numerically (DC meter volts, BAT DISCHARGE timing in long battery-only phases). Needs an X-Plane recording if numerical parity matters.
  2. Generic light 14 initial value (annunciator BRT) at load and whether X-Plane applies a bus check or a fade to generic_lights_brightness_ratio[14]: not determined. Assumed ratio = switch value (1 BRT, 0.5 DIM).
  3. Warm start (engines running): ac_sel_power_bus starts at 9 and only the GPU path initialises it, so with X-Plane starting with generator_on = 1, Zibo would show SOURCE OFF 1/2 until the crew presses GEN 1/2 ON — unless Systems_load (situation files) restores it. Not verified (the oracle cannot run engines).
  4. generator_apu_on vs APU_generator_on: assumed to be one X-Plane variable (legacy alias). In the host they are separate, so the APU-generator drop-out of §4.5 never triggers in the oracle.
  5. CDU screens: Zibo's brightness gate for CDU 1/2 is DC STBY · mcdu1_on (DC 2 · mcdu2_on), i.e. dark for 30 s; whether the FMS Lua shows anything before (the profile's comment says a boot page from the transfer bus) belongs to the FMS/CDU spec.
  6. SCI contract labels: ELEC_DC_METER_POS enum names do not match Zibo's index meaning (index 3 is TR 1, 6 is TEST). Recommend renaming to STBY_PWR, BAT_BUS, BAT, TR1, TR2, TR3, TEST (index unchanged), after checking the LevelUp knob's printed labels.
  7. SCI profile errors found (for whoever maintains data/sci/profiles/zibo_2S1_50/elec.json): BUS TRANSFER is modelled (not a shadow state); BUS_BAT/BUS_SW_HOT_BAT read swapped datarefs; SRC_IDG*_RDY should read gen1/2_available; DU/ISFD/CDU _PWR synth expressions are not Zibo's gates (§6.2). Not changed here.
  8. AutoGate / SAM jetway ground power (§4.8): only the branch structure was read (15 s timers, parking brake, engine 2 not running, beacon off, gp_cable_connect); not tested. MSFS has its own jetway/GPU services — decide in M3 whether gpu_available comes from the EFB only or also from MSFS A:EXTERNAL POWER AVAILABLE.
  9. Anim_value snapping: the decompiled helper's exit path (when the value is already at a 0/1 limit) was not fully resolved; the guard timings above (action on the second frame, fully closed within ~10 frames) are oracle-verified, the exact snap frame is not.
  10. Display content formats (display_units values 0–3, DU selector and LOWER DU logic) are only summarised; the display spec (M4) owns them. Captain DUs blank only when both XFR 2 and the battery bus are dead.
  11. Six-pack / master caution recall semantics are shared by all systems; only the ELEC fault vector is specified.
  12. Unread corners of Electric_bus: Ghidra reported two unreachable blocks; the behaviour above is from the reachable code and was checked against the oracle where the oracle can exercise it (battery, standby, GPU, APU generator, bus transfer, TR 3 and XFR 2 failures). Engine-generator paths (§4.4 step 1, §4.5) are from the code only.

10 Corrections and details from the M3 implementation (2026-11-01)#

Resolved with tools/re/decompile.py while implementing wasm/src/core/elec and checked against the oracle (frame-by-frame trace, xphost/test/sci/elec_trace.json):

  1. §4.4 step 17: with the switched hot battery bus dead, only down_timer := 2 depends on warm; warm, up_timer, init_mcdu := 0 and mcdu_timer := 30 are unconditional (corrected above).
  2. Annun_timers: every count-down timer runs while it is non-zero (t -= sim_period, clamped at 0) and sets its flag in the frame it reaches 0 (warm-ups, panel warm-ups, BAT DISCHARGE bands, 4-minute flags); the DU warm-down timers have no flag. The STANDBY PWR OFF, init_ac_source, init_heat, ISFD and CDU timers run inline in Electric_bus as described in §4.4.
  3. Six-pack latch (§4.7): the edge loop covers entries 0–17: equal to the previous value and 0 → unmark; rising → latch := 1 and mark; falling → unmark and remember "a fault fell". If a fault fell, every entry still active is marked again; if any entry changed and none is marked, the latch clears. six_pack_elec = latch · B, and = B during the lights test or the six-pack recall (capt_6_pack_pos/fo_6_pack_pos = 1). MASTER CAUTION (master_caution1/2, only when its button position is 0) clears the latch and the marks. Leaving the lights test (bright_test_dn from TEST) clears the stored previous values of all twelve six-pack fault vectors, so faults still present latch again on the next frame. All entries are already active in cold & dark, so the latch is set from the first frame and shows as soon as the battery bus powers the lights.
  4. LIGHTS TEST switch (toggle_switch/bright_test): −1 DIM, 0 BRT, 1 TEST. bright_test_up: DIM → BRT (generic light switch 14 := 1), BRT → TEST (annunciator/test 1); bright_test_dn: TEST → BRT (test 0, six-pack history cleared), BRT → DIM (switch 14 := 0.5).
  5. §4.4 step 9, BUS TRANSFER AUTO with no tie source: when exactly one bus has a source, the dead bus copies it only if both BTBs are OK, but btbk := OK(BTBk) is set in any case; no cross-fail in that branch (only the final one). Same in the tie-source branch.
  6. Oracle host: a command begun and not ended is not re-fired by a second begin (scripts must use once or begin/end pairs); avionics_on read in the oracle is the host's own value (§7 item 9), so it is compared only where host and Zibo agree.

What the rewrite supplies for §7 (deterministic, Electrics::xplaneModel; every value can be overridden through elec::Inputs): battery volts 24.5 V, 28 V while a transfer bus is powered (as the oracle; no drain); battery amps 0 (as the oracle: BAT DISCHARGE only with injected currents); generator_off[k] = ¬generator_on[k] (X-Plane semantics; the oracle has 0); generator / APU generator / GPU amps 45 A per transfer bus they carry (APU GEN OFF BUS goes out when the APU powers a bus; the oracle has 0); TR bus load 25 A per powered DC bus; bus_volts[0] = 28 V with AC, battery volts on battery, 0 without DC. Day factor: the MSFS build uses 1 (no sun elevation input yet); annunciator brightness follows the BRT/DIM switch (1 / 0.5).

The differential tests (wasm/test/sci_script_runner.cpp, part of wasm/build.sh test) run elec_spec_battery, elec_spec_sources, elec_cold_start and elec_degraded on the rewrite with the same checks as the oracle (APU-owned steps skipped until the APU subsystem exists) and compare the 53 values of elec_trace.json on every one of its 2597 frames with the oracle's trace (wasm/test/data/elec_trace_oracle.txt): no mismatch, one one-frame shift (DC meter volts following the X-Plane battery volts). elec_cold_start / elec_degraded now give the DU and CDU power checks 1100 frames: Zibo's gates include the 32 s DU warm-up and the 30 s CDU boot (§6.2), the profile's synthesis does not; the BUS TRANSFER sweep runs with its guard open (Zibo holds AUTO under the closed guard).