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#
- 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.
Read_Xplane_DR/Read_B738_DR: snapshot of the inputs of §2.2;plane_on_ground;sim_period.Annunciators(§4.7): firstAnnun_timers(all count-down timers of §3.2 except those counted inline), then the lights — using the bus states of the previous frame — and lastB738_battery_disch_annun(discharge detection, used by the lights in the next frame).Switches(§4.3): guard animation and guard-closing actions.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).B738_gpu_power(§4.8) — only in Zibo's 2 Hz slow loop: a timer accumulatessim_period; when it exceeds 0.5 s it is reset and, if the sim is not paused andfirst_timer> 2 s,B738_gpu_powerruns (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-zerobtb2:= 1. Ifac_power_bus[0]= 0 and OK(BTB1):ac_power_bus[0]:=tie, and iftie≠ 0btb1:= 1. If both closed: cross-fail. - BUS TRANSFER OFF (X-Plane
cross_tie= 0) orbpcu_status≠ 1: if both buses are on the same non-zero source,btbk:= OK(BTBk) for both; otherwisebtbk:= 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); thenbtbk:= 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, orautopilot/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-Planegenerator_apu_on= 1: same. (sim/cockpit/electrical/generator_apu_onis X-Plane's legacy alias of the APU generator switch.)
Engine generators (first_timer = seconds since load):
first_timer≤ 6 s:genk_available:= X-Planegenerator_on[k-1](trust the loaded state).- later, per side k (index i = k−1), on a working copy
gof X-Planegenerator_on:- Zibo failure GENk = 1 and X-Plane
rel_genera{i}≠ 6 →g[i]:= 0, writegenerator_on[i]:= 0,rel_genera{i}:= 6. - Zibo failure GENk ≠ 1 and X-Plane
rel_genera{i}= 6 →g[i]:= 0, writegenerator_on[i]:= 0, Zibo failure GENk := 1. - 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. genk_available:= (eng_N2[k]≥ 49.5 andrel_genera{i}≠ 6); if not availableg[i]:= 0.- If
gdiffers fromgenerator_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).
- Zibo failure GENk = 1 and X-Plane
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): |
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_on0); 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#
- 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.
- 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). - Warm start (engines running):
ac_sel_power_busstarts at 9 and only the GPU path initialises it, so with X-Plane starting withgenerator_on= 1, Zibo would show SOURCE OFF 1/2 until the crew presses GEN 1/2 ON — unlessSystems_load(situation files) restores it. Not verified (the oracle cannot run engines). generator_apu_onvsAPU_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.- 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. - SCI contract labels:
ELEC_DC_METER_POSenum names do not match Zibo's index meaning (index 3 is TR 1, 6 is TEST). Recommend renaming toSTBY_PWR, BAT_BUS, BAT, TR1, TR2, TR3, TEST(index unchanged), after checking the LevelUp knob's printed labels. - 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_BATread swapped datarefs;SRC_IDG*_RDYshould readgen1/2_available; DU/ISFD/CDU_PWRsynth expressions are not Zibo's gates (§6.2). Not changed here. - 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 whethergpu_availablecomes from the EFB only or also from MSFSA:EXTERNAL POWER AVAILABLE. - 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.
- Display content formats (
display_unitsvalues 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. - Six-pack / master caution recall semantics are shared by all systems; only the ELEC fault vector is specified.
- 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):
- §4.4 step 17: with the switched hot battery bus dead, only
down_timer := 2depends onwarm;warm,up_timer,init_mcdu:= 0 andmcdu_timer:= 30 are unconditional (corrected above). 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 inElectric_busas described in §4.4.- 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_dnfrom 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. - 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/test1);bright_test_dn: TEST → BRT (test 0, six-pack history cleared), BRT → DIM (switch 14 := 0.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. - Oracle host: a command begun and not ended is not re-fired by a second begin (scripts must use
onceor begin/end pairs);avionics_onread 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).