Milestone M2. Status: draft 2026-11-01 (bleed/duct/packs/isolation/APU bleed and pressurization complete; temperatures partial, §9). Subject: Zibo 2.S1.50 (zibomod lin_x64 + XLua) as run by
the oracle (xphost/out/linux/lu737host). Everything below is described in our own words from the
decompiled zibomod (Ghidra, tools/re/decompile.py), the disassembly where the decompiler lost
arguments or constants, the Lua, the SCI contract/profile and oracle runs. No decompiled code is
reproduced; constants, tables, timings and conditions are Zibo's.
Notation as in electrics.md and engine_start_and_dynamics.md: Z: = Zibo dataref
(laminar/B738/...), X: = X-Plane dataref/command (sim/...), SCI: = stable cockpit interface
state/control. Global names in code are the symbol names in zibomod.xpl so a reader can find
the original. dt = sim_period (frame time clamped to (0, 0.1] s, see the engine spec,
App. A). Index i: 0 = left/engine 1, 1 = right/engine 2. Helper functions (Rescale, Limit,
Anim_value, Anim_linear_value2, B738_set_animation_rate, B738_set_anim_time,
generate_rnd) have the exact semantics of the engine spec §5.1; ann_bright is the
annunciator brightness factor of electrics.md §4.7 (dir_batbus × generic_brightness_ratio[14] × dim), ann_bright_nodim the same without the DIM factor.
1 Scope and sources#
In scope
- Bleed sources: engine 5th/9th stage pressures, HP (9th-stage) valve, PRSOV (engine bleed valve), precooler and duct temperature, bleed trip; APU bleed (APU "establish" warm-up, load control valve, start-valve deficit), APU bleed valve; ground air cart (ASU, EFB).
- Distribution: isolation valve (CLOSE/AUTO/OPEN), leak/load sharing between sources, duct pressure L/R (real and gauge), DUAL BLEED, X-Plane bleed mode.
- Packs: OFF/AUTO/HIGH switches, pack valves, pack flow (into the cabin model), PACK lights; APU load demand of the packs. Recirculation fans and trim air switches (state only, see below).
- Pressurization: mode selector AUTO/ALTN/MAN, controller power and failures, FLT ALT and LAND ALT
selectors, the automatic schedule (ground / take-off / climb / cruise / descent), outflow
valve (auto and manual), cabin altitude / rate / differential pressure integration, ram air
doors, AUTO FAIL / OFF SCHED DESCENT / ALTN / MANUAL lights, cabin altitude warning,
fuselage over-pressure failure,
pressurization_dashdisplay blanking. - Anti-ice (bleed side): engine cowl valves, wing anti-ice valves including the ground logic and lift-off trip-off, their lights and the X-Plane cowl-heat output.
Covered only partly (see §9): cabin/duct temperature model and zone-temperature lights
(selectors and targets are given, the per-zone thermal model is not), WING-BODY OVERHEAT
(only the test path exists in Zibo), de_ice() (wing/engine ice removal, X-Plane ice ratios),
window/probe heat (electrical; not pneumatic), the -700 variant air panel (*_700 datarefs).
Not modelled by Zibo 2.S1.50 (nothing to reproduce): the FLT/GND switch (no command and
no dataref exist; Zibo derives ground/flight from plane_on_ground and engine N1/N2),
recirculation fans and trim air (switch positions only: no logic reads them except state
save/load and cold-start initialisation), cabin temperature effect of trim air, pack trip /
pack overheat (the PACK light comes only from AC power loss with the switch on, or an X-Plane
failure), duct overheat.
Sources (zibomod function → section): Systems (bleed/duct/packs/valves, §4.3–4.6; start
valves and ASU annunciation are in the engine spec §4.11), pressure_5th, pressure_9th (§5.2),
Pressurisation_system (called from Systems, §4.7), update_press_mode (§4.7.1),
Pressurization (X-Plane bleed actuators, §6.3), Anti_ice (§4.2), Annunciators (§4.8),
Systems_start_flight, Anti_ice_start_flight (§3.3), command handlers CMD_bleed_air_1/2/apu,
CMD_l/r_pack_up/dn, CMD_iso_valve_up/dn, CMD_l/r_recirc_fan, CMD_trim_air,
CMD_air_valve_lft/rgt, CMD_air_valve_ctrl_lft/rgt, CMD_flt_alt_press_up/dn,
CMD_land_alt_press_up/dn, CMD_trip_reset, CMD_duct_ovht_test, CMD_eng1/2_heat,
CMD_wing_heat, CMD_air_temp_ctrl_lft/rgt (§4.1); Xplane_datarefs/Read_Xplane_DR and the
Register_*DR functions for dataref names. Dead code, not called anywhere:
calc_target_cabin_alt, press_calc_volume/pressure/weight/density/speed (do not port).
Lua: B738.a_fms owns Z:failure/pressurisation (array[70], EFB failure page);
B738.tablet owns Z:engine/engine_air_start (EFB ground air cart). SCI: data/sci/contract/90_cockpit.json
and the Zibo profile 90_cockpit.json (generated names, all kind: raw, value = Zibo's raw
value).
2 Inputs#
2.1 Cockpit controls (SCI control → Zibo command → Zibo state)#
All are generic SCI controls; the SCI state carries Zibo's raw value.
| SCI control (actions) | Zibo command(s) | Zibo state (SCI state) | Behaviour |
|---|---|---|---|
ENGINE_1_BLEED_AIR, ENGINE_2_BLEED_AIR (PRESS) |
toggle_switch/bleed_air_1, _2 |
toggle_switch/bleed_air_1_pos, _2_pos (TOGGLE_SWITCH_BLEED_AIR_1_POS, _2_POS) 0 OFF / 1 ON |
toggle on command begin |
APU_BLEED_AIR (PRESS) |
toggle_switch/bleed_air_apu |
toggle_switch/bleed_air_apu_pos (TOGGLE_SWITCH_BLEED_AIR_APU_POS) |
toggle |
L_PACK, R_PACK (INC/DEC) |
toggle_switch/l_pack_up/dn, r_pack_up/dn |
air/l_pack_pos, r_pack_pos (AIR_L_PACK_POS, AIR_R_PACK_POS) 0 OFF / 1 AUTO / 2 HIGH |
_up = toward OFF, _dn = toward HIGH, one step per begin |
ISOLATION_VALVE (INC/DEC) |
toggle_switch/iso_valve_up/dn |
air/isolation_valve_pos (AIR_ISOLATION_VALVE_POS) 0 CLOSE / 1 AUTO / 2 OPEN |
_up toward CLOSE |
LEFT_RECIRC_FAN, RIGHT_RECIRC_FAN (PRESS) |
toggle_switch/l_recirc_fan, r_recirc_fan |
air/l_recirc_fan_pos, r_recirc_fan_pos |
toggle; no effect |
TRIM_AIR (PRESS) |
toggle_switch/trim_air |
air/trim_air_pos |
toggle; no effect |
BLEED_TRIP_RESET (PRESS) |
push_button/bleed_trip_reset → handler CMD_trip_reset |
push_button/trip_reset |
§4.1.2 |
DUCT_OVERHEAT (PRESS) |
push_button/duct_ovht_test |
push_button/duct_ovht_test_pos (PUSH_BUTTON_DUCT_OVHT_TEST_POS) |
1 while held (begin/end) |
AIR_VALVE_CONTROL (INC/DEC) |
toggle_switch/air_valve_ctrl_right/left |
toggle_switch/air_valve_ctrl (TOGGLE_SWITCH_AIR_VALVE_CTRL) 0 AUTO / 1 ALTN / 2 MAN |
one step per begin, then update_press_mode() immediately (§4.7.1) |
AIR_VALVE_MANUAL (INC/DEC, spring) |
toggle_switch/air_valve_manual_right/left |
toggle_switch/air_valve_manual (TOGGLE_SWITCH_AIR_VALVE_MANUAL) −1 CLOSE / 0 / +1 OPEN |
−1 (left) or +1 (right) while held, 0 on end |
FLT_ALT_PRESSURIZATION (INC/DEC) |
knob/flt_alt_press_up/dn |
X-Plane X:cockpit/pressure/max_allowable_altitude (SCI SIM_COCKPIT2_PRESSURIZATION_ACTUATORS_MAX_ALLOWABLE_ALTITUDE_FT, see §7) |
±500 ft, §4.1.3 |
LAND_ALT_PRESSURIZATION (INC/DEC) |
knob/land_alt_press_up/dn |
pressurization/knobs/landing_alt (PRESSURIZATION_KNOBS_LANDING_ALT) |
±50 ft, §4.1.3 |
CONT_CABIN_TEMP, FORWARD_CABIN_TEMP, AFT_CABIN_TEMP (INC/DEC) |
dataref drive (+/−0.05, 0…1) | air/cont_cab_temp/rheostat, fwd_…, aft_… |
§4.9 |
AIR_TEMP_SORCE_2 (INC/DEC) |
toggle_switch/air_temp_source_right/left |
toggle_switch/air_temp_source (TOGGLE_SWITCH_AIR_TEMP_SOURCE) 0…6 |
one step per begin; display only |
CABIN_TEMP, PASSENGER_CABIN_TEMP, AIR_TEMP_SORCE |
-700 panel variants (*_700) |
AIR_*_700 |
not used on the -800 |
ENGINE_1_ANTI_ICE, ENGINE_2_ANTI_ICE (PRESS) |
toggle_switch/eng1_heat, eng2_heat |
ice/eng1_heat_pos, eng2_heat_pos (ICE_ENG1_HEAT_POS, ICE_ENG2_HEAT_POS) |
toggle (any value ≠ 0 → 0) |
WING_ANTI_ICE (PRESS) |
toggle_switch/wing_heat |
ice/wing_heat_pos (ICE_WING_HEAT_POS) |
toggle; switching ON on the ground also sets wing_heat_ground_set = 1, OFF clears it |
CAPTAIN_SIX_PACK, FIRST_OFFICER_SIX_PACK |
recall | – | annunciator spec |
No SCI control exists for FLT/GND (not modelled by Zibo, §1). There is no SCI state for
Z:annunciator/autofail; the AUTO FAIL light of the model is driven by another name or not at
all (open question §9).
2.2 Simulator inputs (X-Plane → MSFS source; see §7 for the recommendation)#
| Zibo global | X-Plane dataref | Use |
|---|---|---|
simDR_pressure_altitude (p0) |
X:flightmodel2/position/pressure_altitude |
pressurization (ambient) |
elevation_ft |
computed every frame from X:weather/barometer_current_inhg: (1 − (inHg/29.92)^0.190284) · 145366.45 (correction, apu.md §2.2: on XP12 the barometer comes from X:weather/aircraft/barometer_current_pas; the oracle's APU psi stays 0 with only the inHg dataref set) |
bleed pressures, APU psi, precooler, trip reset |
simDR_OAT, simDR_true_TAT, simDR_TAT |
X:cockpit2/temperature/outside_air_temp_degc and TAT datarefs |
duct temperature, APU establish time, cabin temperature init |
simDR_fpm |
aircraft vertical speed (fpm) | cabin rate in cruise climb |
simDR_flaps_ratio, simDR_ground_speed |
flap ratio, ground speed (kt) | ram doors, pack HIGH flow |
plane_on_ground, simDR_on_ground[3], simDR_radio_height_pilot_ft |
gear on ground, RA | pressurization mode, wing anti-ice |
simDR_apu_status |
X:cockpit2/electrical/APU_N1_percent |
APU bleed |
simDR_apu_run |
X:cockpit/engine/APU_running |
APU duct enable (APU spec) |
simDR_engine_on[2] |
X-Plane engine running flags | engine cowl valve |
simDR_throttle_used_ratio[2] |
throttle used | wing anti-ice lift-off logic |
simDR_rel_eng_ice[2] |
X:flightmodel/failures/inlet_ice_per_engine |
COWL ANTI-ICE light |
simDR_bus_amps[2] |
X-Plane bus amps | APU EGT load (APU spec) |
simDR_startup_running |
X-Plane "start with engines running" | flight-start state |
simDR_bleed_trip_off_annun[2] |
X:cockpit/warnings/annunciators/bleed_air_fail |
trip if == 6 (never true, §7) |
simDR_wing_body_ovht_annun |
X:cockpit/warnings/annunciators/hvac |
WING-BODY OVHT if == 1 |
simDR_failures[153] |
X:operation/failures/failures (enum, 6 = failed) |
PACK lights if == 6 |
simDR_press_max_alt |
X:cockpit/pressure/max_allowable_altitude |
FLT ALT (X-Plane only stores it) |
2.3 Other subsystems (Zibo globals)#
| Global | From | Use |
|---|---|---|
B738DR_dc_bus1/2_status, B738DR_ac_tnsbus1/2_status, B738DR_batbus_status, B738DR_dir_batbus_status |
electrics | valve power, controllers, gauge, lights |
eng_N1_idx[2], eng_N2_idx[2] |
engine (Zibo N1/N2 %) | bleed pressure, pressurization mode |
B738DR_engine1/2_starter_valve (first-order 0…1), B738DR_start_valve1/2 |
engine spec §4.11.7 (previous frame) | duct load, PRSOV close, APU deficit |
B738DR_apu_start_switch_position, apu_duct_enable |
APU spec (apu_duct_enable = 1 two seconds after the APU is running and stable, cleared when it stops or shuts down) |
APU bleed valve |
B738DR_engine01/02_fire_ext_switch_pos_arm, B738DR_apu_fire_ext_switch_pos_arm |
fire | close bleed / APU bleed valve when the handle is pulled |
B738DR_engine_air_start |
Lua Z:engine/engine_air_start (EFB ASU) |
ground air |
B738DR_thrust1/2_leveler |
thrust levers | wing anti-ice ground logic |
door ratios (door_ratio_*), B738DR_capt/fo_window_ratio, b737_variant, B738DR_73x |
doors | cabin leak |
B738DR_pressurisation_failure[0..15] |
Lua Z:failure/pressurisation (EFB) |
§5.6 |
B738DR_brightness_level, B738DR_lights_test |
annunciators | DUAL BLEED light |
B738DR_high_alt_ldg, B738DR_high_alt_ldg_on |
high-altitude landing option | cabin altitude warning threshold |
loaded_flight, rst_failure_timer, loaded_flight_timer |
flight load / failure reset | gate of Pressurisation_system |
3 State#
3.1 Persistent state (per flight)#
| State | Meaning |
|---|---|
reg_5th[2] (bool) |
5th-stage regulation latch (HP valve closed) |
B738DR_engine1/2_bleed_air_9th_valve_pos |
HP valve 0…1 |
B738DR_engine1/2_bleed_air_valve_pos |
PRSOV 0…1 |
B738DR_precooler1/2_valve_pos (Z:air/engN_precooler_valve_pos) |
precooler control valve 0…1 |
duct_temp_L/R |
duct temperature °C (−35…350) |
eng1_leak, eng2_leak, apu_leak, asu_leak |
load (psi) attributed to each source, computed this frame, used next frame |
apu_establish_time |
APU bleed warm-up 0…1 |
bleedAir_apu_psi |
raw APU bleed pressure available (before the load valve) |
air_apu_load |
APU load-control factor 0…1 |
apu_mode, both_packs_old, TMR_apu_idle |
APU load-control mode and timer |
ext_air_duct |
ASU pressure 0…48 psi |
B738DR_apu/lpack/rpack/isolation_bleed_air_valve_pos |
valves 0…1 |
B738DR_real_duct_pressure_L/R, B738DR_duct_pressure_L/R |
duct pressure (real; gauge) |
bleed_trip_off_annun1/2 |
bleed trip latch |
pack_mode, dual_bleed, apu_protection_disabled |
bleed mode for X-Plane, DUAL BLEED |
l_pack_coef, r_pack_coef |
pack flow factor 0…1.82 |
l_pack_no_ac, r_pack_no_ac, ac_tnsbus1/2_status_old2 |
PACK light latch on AC loss |
press_mode, press_mode_status |
controller in use (0 none, 1 AUTO, 2 ALTN, 3 MAN), power-up ramp |
B738DR_pressurization_mode2 |
schedule phase: 0 ground, 1 take-off, 2 climb, 3 cruise, 4 descent |
to_timer, off_timer |
1.5 s timers for ground ↔ take-off |
mem_landing_alt, mem_landing_alt2, mem_last_alt, mem_cruise_alt, calc_cabin_alt, press_max_alt_old, press_max_alt_old2, land_alt_knob_old2 |
schedule memory |
target_cabin_alt, target_diff_press, tgt_outflow_valve, allow_control, cabin_pressure_diff_old |
controller |
off_sched_desc |
OFF SCHED DESCENT latch |
real_cabin_altitude_actual, real_cabin_vvi, real_cabin_pressure_diff |
cabin state (the model's truth) |
B738DR_outflow_valve |
outflow valve 0 closed … 1 open |
fuselage_depress_tmr |
over-pressure timer 0…240 |
ram_door_tgt, B738DR_ram_door1/2 |
ram air doors |
B738DR_eng_inlet_valve[2], B738DR_wing_anti_ice_valve[2] |
anti-ice valves 0…1 |
lift_on, wing_heat_enable_left/right, wing_heat_ground_set, wai_overheat_left/right, wai_overheat_timer_left/right |
wing anti-ice logic |
rndm_max_apu_bleed_psi, rndm_max_eng1/2_bleed_psi |
random per flight (generate_rnd) |
FLT ALT (X:cockpit/pressure/max_allowable_altitude) and LAND ALT (Z:pressurization/knobs/landing_alt)
are selector values; in the rewrite both are owned state.
3.2 Constants in writable memory#
bleed_valve_min_act_press = 8 psi (initialised data, never written): minimum duct pressure for
the wing anti-ice valve. apu_protection_disabled starts at 1.
3.3 Initial values at flight start (Systems_start_flight, Anti_ice_start_flight)#
- Always:
rndm_max_apu_bleed_psi = generate_rnd(45, 46),rndm_max_eng1_bleed_psi = generate_rnd(45, 46),rndm_max_eng2 = eng1 + generate_rnd(0, 4) − 2(the engine values are not used by the code described here).apu_establish_time = 0.B738DR_air_temp_source = 1, zone rheostats 0.5,-700controls 0.5/0. X-Planerel_APU_pressfailure cleared. - Pressurization (always):
X:operation/override/override_pressurization = 1(X-Plane's own pressurization model is switched off; it is also set again inSystems), all controller state above = 0 (press_mode = 0,mode2 = 0,allow_control = 0, timers 0, memories 0),B738DR_outflow_valve = 0, ram doors 0. If on the ground with "engines running" start, or a cold start: cabin altitude =psi_to_ft(ft_to_psi(elevation_ft))(= field pressure altitude), differential pressure 0, cabin rate 0. The displayed andreal_*cabin values are copied from these. - Cold & dark (
simDR_startup_running == 0): bleed 1 and 2 switches ON, APU bleed OFF, right recirc fan ON, left recirc fan ON on the -800 (OFF on -600/-700/73x variants), trim air OFF. IfB738DR_engine_no_running_state == 0(normal cold start): packs OFF, isolation OPEN; otherwise packs AUTO, isolation AUTO with the isolation valve at 1 (GPU connected). Cabin temperatures = TAT (min 10 °C). - Engines running (
simDR_startup_running == 1): packs AUTO, isolation AUTO, isolation valve position 0, bleeds ON, APU bleed OFF, recirc as above, trim air ON, cabin temperatures 25/27/26/28/27, pack temperatures 27/28. - Anti-ice: engine and wing switches OFF, all valves 0,
lift_on = 0,wing_heat_enable_* = 1,wing_heat_ground_set = 0.
4 Per-frame logic in execution order#
Frame skeleton (engine spec App. A.4): Starter (step 7, reads B738DR_real_duct_pressure_L/R,
APU and isolation valve positions of the previous frame) → … → Annunciators (step 11) →
Anti_ice (step 12) → … → Systems (step 19: §4.3–4.7 in this order, then the temperature model
§4.9) → … → Pressurization (A.6.2, X-Plane bleed actuators, §6.3). Everything listed below
runs only when X-Plane is not paused and not in replay. Command handlers (§4.1) run when X-Plane
delivers the command, i.e. before the flight loop of that frame.
4.1 Command handlers#
4.1.1 Switches: as in §2.1. Toggles act on command begin only; the pack and isolation
selectors move one step per begin and stop at the ends; air_valve_manual is −1/+1 while the
command is held and 0 at the end; air_valve_ctrl steps and calls update_press_mode() and
copies press_mode to Z:pressurization_mode immediately.
4.1.2 Bleed trip reset (on begin, only if the button was released before):
button = 1; if a trip is latched on either side, for each side: reset (latch = 0 and
X:operation/failures/rel_bleed_air_lft/rgt = 0) unless the X-Plane bleed annunciator of that
side is 6, or (elevation_ft > 35000 and that wing anti-ice valve > 0.95), or that engine's
air_engine_psi > 220, or duct_temp > 254. Button = 0 on end.
4.1.3 FLT ALT / LAND ALT knobs: on begin: FLT ALT = Limit(roundUp(FLT ALT ± 500, 500), −1000, 42000) (written to X-Plane's max_allowable_altitude); LAND ALT = Limit(roundUp(LAND ALT ± 50, 50), −1000, 13600). While held: after 1 s (CMD_duration accumulates dt while ≤ 1 s) one more
step whenever CMD_duration2 has exceeded 0.01 s (it accumulates dt and resets after each
step) — i.e. one step every second frame at 30 fps. roundUpToIncrement(x, n) rounds up to the
next multiple of n (open question §9 for negative values).
4.2 Anti_ice (bleed part)#
# engine cowl valves
for i: tgt = (eng_heat_pos[i] == 1 and simDR_engine_on[i] == 1 and dc_bus[i+1] powered) ? 1 : 0
eng_inlet_valve[i] = Anim_value(eng_inlet_valve[i], tgt, 0, 1, 1.5)
cowl_on[i] = eng_inlet_valve[i] > 0.95
if cowl_on differs from X:cockpit2/ice/ice_inlet_heat_on_per_engine → write it (int[2])
# lift-off detection and wing anti-ice switch trip-off
if lift_on == 0: if all three gear off the ground and RA > 10 ft: lift_on = 1
elif lift_on == 1:
if plane_on_ground: lift_on = 0
elif throttle_used[0] > 0.5 or throttle_used[1] > 0.5:
if wing_heat_pos == 1 and wing_heat_ground_set == 1: command wing_heat (switch → OFF)
wing_heat_ground_set = 0
wing_heat_enable_left = wing_heat_enable_right = 1
# ground logic, per side (left: thrust1, N1[0]; right: thrust2, N1[1])
if plane_on_ground:
if thrust_lever <= 0.5:
enable = 1
if N1 <= 41.3: if timer == 0: overheat = 0
else: timer -= 0.25·dt; if timer < 0: overheat = 0, timer = 0
elif overheat == 0: timer += dt; if timer > 10: overheat = 1, timer = 10
else: timer -= 0.25·dt; if timer < 0: overheat = 0, timer = 0
else: enable = 0, overheat = 0, timer = 0
else: overheat = 0, timer = 0 (both sides)
# wing valves (left uses real_duct_L, right real_duct_R)
if wing_heat_pos == 0 or ac_tnsbus1 unpowered: tgtL = tgtR = 0
else: tgt = 1 unless duct < 8 (bleed_valve_min_act_press) or enable == 0 or overheat
if ac_tnsbus1 == 1: wing_anti_ice_valve[i] = Anim_value(·, tgt, 0, 1, 1.8) (else frozen)
Z:ice/wing_l_heat_on = valveL > 0.95 and real_duct_L >= 18 (R likewise)
X:cockpit2/ice/ice_surfce_heat_left_on/right_on forced to 0 (written when ≠ 0)
The rest of Anti_ice (window heat, probe heat) is not pneumatic.
4.3 Systems: bleed sources#
Runs right after Electric_bus. All *_leak values are those of the previous frame (§4.5).
4.3.1 APU bleed available
t_est = 500 ; tgt = 0
if APU_N1 > 90: tgt = 1 ; t_est = Rescale(−30, 180, 40, 60, Limit(TAT, −30, 40))
apu_establish_time = B738_set_animation_rate(apu_establish_time, tgt, 0, 1, t_est)
P = rndm_max_apu_bleed_psi · sin(Rescale(10000, 90°, 80000, 180°, elevation_ft)) − apu_leak ; P >= 0
if (start_valve1 > 0.1 or start_valve2 > 0.1) and apu_establish_time < 1:
P = max(0, P − (1 − apu_establish_time) · 25)
bleedAir_apu_psi (raw) = Rescale(0, 0, 100, P, APU_N1)
(t_est is seconds for the full 0→1 ramp: 180 s at −30 °C, 103 s at 15 °C, 60 s at 40 °C;
the decay without APU takes 500 s.)
4.3.2 Engine bleed, per engine (engine i, N1 = eng_N1_idx[i], leak = eng_i_leak)
p5 = max(0, pressure_5th(elevation_ft, N1) − leak) → Z:air/engineN_5th_psi
p9 = max(0, pressure_9th(elevation_ft, N1) − leak) → Z:air/engineN_9th_psi
hp_tgt = 0
if p9 >= 8 − leak and p9 < 110 and not reg_5th[i] and p9 != 0:
hp_tgt = Limit((38 − hp·p9)/p9 + hp, 0, 1) (regulates hp·p9 to 38 psi)
unless failure HP valve stuck: hp = Anim_value(hp, hp_tgt, 0, 1, 3)
bleed_ok = bleed_switch > 0.95 and fire handle not pulled and engineN_starter_valve == 0
and dc_bus[i+1] powered and trip latch == 0
if not bleed_ok or p5 < 8 − leak: reg_5th[i] = false
elif p5 >= 38: reg_5th[i] = true
elif p5 <= 34: reg_5th[i] = false (34…38: hold)
air_engine_psi[i] = max(p5, hp·p9)
prsov_tgt = (bleed_ok and air_engine_psi >= 8 − leak and air_engine_psi != 0)
? Limit((50 − real_duct[i]) / air_engine_psi + prsov, 0, 1) : 0
unless failure PRSOV stuck: prsov = Anim_value(prsov, prsov_tgt, 0, 1, 3)
Note: prsov_tgt uses the duct pressure of its own side (real_duct_L for engine 1,
real_duct_R for engine 2), of the previous frame.
4.3.3 Precooler and duct temperature, per engine
k = Rescale(0, 13300, 41000, 7565, elevation_ft) (precooler capacity)
m = 0.0314 · sqrt(max(0, (air_engine_psi + leak + Rescale(0,0,1,10, wing_ai_valve[i])) · 6894.75729) / 0.601)
T_src = OAT ; rate = 0.04
if N1 > 15: rate = 0.9 ; T_src = 580
if p5 >= 32: T_src = Rescale(0, 340, 1, 580, hp[i]) ; rate = Rescale(0, 0.45, 1, 0.9, hp[0])
T_tgt = (m > 0) ? T_src − precooler·k / (m · 1.04328) : T_src
duct_temp[i] = B738_set_animation_rate(duct_temp[i], T_tgt, −35, 350, rate)
pc_tgt = (air_engine_psi >= 8 − leak and k > 0) ? Limit(m·1.04328·(T_src − 214)/k, 0, 1) : 1
unless failure precooler stuck: precooler = Anim_value(precooler, pc_tgt, 0, 1, 2)
Zibo quirk to keep: the rate for engine 2 is interpolated from engine 1's HP valve (hp[0]).
4.3.4 Ground air (ASU): if engine_air_start == 1: target 48 psi, else ext_air_duct = 0
immediately; ext_air_duct = Anim_linear_value2(ext_air_duct, target, 0, 48, 6) (6 psi/s,
8 s to full).
4.3.5 APU load control (pack demand on the APU)
starter_demand = max(engine1_starter_valve, iso_valve · engine2_starter_valve)
both_off = l_pack_pos == 0 and r_pack_pos == 0 (code 0)
both_auto = l_pack_pos == 1 and r_pack_pos == 1 (code 1; otherwise code 2)
if starter_demand > 0 or apu_bleed_switch == 0 or a pack is HIGH: load_tgt = 1, apu_mode = 0, TMR_apu_idle = 0
elif both_auto: apu_mode = 1 (TMR_apu_idle = 0 when the code changed)
elif both_off: apu_mode = 2 (TMR_apu_idle = 0 when the code changed)
else: load_tgt = 1, apu_mode = 3, TMR_apu_idle = 0
both_packs_old = code
apu_mode 1: TMR_apu_idle += dt while < 15 ;
load_tgt = 1 if bleedAir_apu_psi − apu_leak < 0.1 or TMR < 2.5 ; else (21 + apu_leak)/bleedAir_apu_psi
apu_mode 2: TMR as above ; if bleedAir_apu_psi − apu_leak < 0.1: load_tgt = 1
elif bleedAir_apu_psi != 0: (38 if TMR < 2 ; 33 if TMR < 9.5 ; else 21) + apu_leak, / bleedAir_apu_psi
(load_tgt stays 0 when bleedAir_apu_psi == 0)
air_apu_load = Anim_linear_value2(air_apu_load, load_tgt, 0, 1, 0.2 if rising/equal else 0.06)
apu_out = max(0, air_apu_load · bleedAir_apu_psi)
apu_load_egt, apu_fuel_flow: APU spec (uses apu_valve · B738DR_bleedAir_apu_psi of the previous frame)
B738DR_bleedAir_apu_psi = min(41, apu_out) → Z:air/apu_psi
4.4 Duct pressures#
s1 = prsov[0] · air_engine_psi[0] → Z:air/engine1_psi s2 = prsov[1] · air_engine_psi[1] → Z:air/engine2_psi
sa = apu_valve · min(41, apu_out) sx = ext_air_duct
loadL = 4·lpack_valve + 8·wing_ai_valve[0] + 4·eng_inlet_valve[0] + 10·engine1_starter_valve
loadR = 4·rpack_valve + 8·wing_ai_valve[1] + 4·eng_inlet_valve[1] + 10·engine2_starter_valve
(each term is Rescale(0,0,1,w,valve); the starter term (w = 5) is added twice)
srcL = max(s1, sa) ; srcR = max(s2, sx) ; srcAll = max(s1, sa, s2, sx)
iso = isolation valve position (actual, previous frame)
ductL_tgt = Rescale(0, srcL, 1, srcAll, iso) ; ductR_tgt = Rescale(0, srcR, 1, srcAll, iso)
if ac_tnsbus2 powered: duct_pressure_L/R (gauge) = Anim_value(·, tgt, 0, 80, 4)
real_duct_pressure_L/R = Anim_value(·, tgt, 0, 80, 4) (always)
The gauge (Z:indicators/duct_press_L/R, SCI INDICATORS_DUCT_PRESS_L/R) freezes when AC
transfer bus 2 is unpowered; the start, anti-ice and pack logic use the real_ values.
4.5 Load sharing (leaks for the next frame)#
L = s1 + sa ; R = s2 + sx ; A = L + R ; loadA = loadL + loadR
isolated: e1 = s1/L·loadL, a = sa/L·loadL (0 if L == 0) ; e2 = s2/R·loadR, x = sx/R·loadR (0 if R == 0)
combined: E1 = s1/A·loadA, Ap = sa/A·loadA, E2 = s2/A·loadA, X = sx/A·loadA (0 if A == 0)
eng1_leak = Rescale(0, e1, 1, E1, iso) + cowl_on[0] (cowl_on is the int 0/1 sent to X-Plane)
eng2_leak = Rescale(0, e2, 1, E2, iso) + cowl_on[1]
apu_leak = 1.5 · Rescale(0, a, 1, Ap, iso) + Rescale(0, 5·sv1, 1, 5·(sv1 + sv2), iso) (sv = engineN_starter_valve)
asu_leak = Rescale(0, x, 1, X, iso) (computed, not used anywhere)
Consequences: the ASU pressure is never reduced by load; the APU pressure drops with packs, wing anti-ice and especially open start valves (up to 10 psi of extra APU leak per starter); engine bleed pressure drops by its share of the load (§4.3.2 subtracts it from p5/p9).
4.6 Bleed mode, DUAL BLEED, valve targets and motion#
apu_on = apu_valve > 0.05 and apu_bleed_switch == 1
(b1, b2 = bleed switch positions; values other than exactly 0/1 match no row)
b1 b2 apu_on → pack_mode, dual_bleed
0 0 0 → 0, 0 1 0 0 → 1, 0 0 0 1 → 4, 0 0 1 0 → 3, 0
1 0 1 → (apu_protection_disabled == 0 ? 1 : 5), 1
0 1 1 → 5 ; dual_bleed = iso_valve > 0.1 ; if dual_bleed and apu_protection_disabled == 0: pack_mode = 3
1 1 0 → 2, 0 1 1 1 → (apu_protection_disabled == 0 ? 2 : 5), 1
X:cockpit2/pressurization/actuators/bleed_air_mode = pack_mode (written on change)
Z:annunciator/dual_bleed = lights_test ? brightness_level : dual_bleed · (apu_valve > 0.95) · brightness_level
apu_protection_disabled = 1 ; if dual_bleed: = 0 if real_duct_L > B738DR_apu_psi + 5,
else (if real_duct_L < B738DR_apu_psi + 1) stays 1
(apu_protection_disabled therefore only matters through the previous-frame value used above.)
Valve targets:
apu_tgt = 0 if failure APU-bleed-fail, APU fire handle pulled, APU switch OFF, or apu_duct_enable == 0
else 1 if apu_bleed_switch == 1 and bleedAir_apu_psi (raw) > 8, else 0
lpack_tgt = l_pack_pos > 0.95 and real_duct_L >= 4 and batbus powered (R: real_duct_R)
iso_tgt = AUTO: 1 if any of bleed1, bleed2, lpack, rpack switches <= 0.95, else 0 ; CLOSE: 0 ; OPEN: 1
apu_valve, lpack_valve, rpack_valve = Anim_value(·, tgt, 0, 1, 10) unless the matching failure (stuck)
iso_valve = Anim_value(·, iso_tgt, 0, 1, 10) only if ac_tnsbus1 == 1 and not failed
APU fault side effect: with failure 15 (APU bleed) X-Plane rel_APU_press is set to 6 once;
APU MAINT light: APU spec.
4.7 Pressurisation_system (inside Systems)#
Runs only if loaded_flight == 0, rst_failure_timer == 0 and loaded_flight_timer == 0;
otherwise the X-Plane cabin datarefs are rewritten from the real_* values (hold).
4.7.1 Preliminaries
ram_door_tgt: on ground → 1 ; airborne: flaps == 0 → 0 ; flaps > 0: GS < 123 → 1, GS > 136 → 0 (hold between)
ram_door1 = B738_set_animation_rate(·, tgt, 0, 1, 10) if ac_tnsbus1 powered ; ram_door2 likewise on ac_tnsbus2
copy real_cabin_vvi / real_cabin_pressure_diff to X-Plane if different
update_press_mode():
AUTO: DC1 → 1, else DC2 → 2, else 0 ; ALTN: DC2 → 2 else 0 ; MAN: dir_batbus → 3 else 0
press_mode_status = B738_set_animation_rate(status, press_mode != 0, 0, 1, 1)
if press_mode_status != 1: press_mode = 0 (1 s power-up delay)
pressurization_dash = (DC1 and DC2 unpowered) or (failure 9 and failure 10)
p0 = pressure altitude ; p_alt = Limit(p0, 0, 50000) ; P_amb = ft_to_psi(p0) ; P_cab = ft_to_psi(real_cabin_alt)
Z:air/cabin_psi = P_cab
4.7.2 Pack inflow (cabin altitude decrease, fpm)
F = Rescale(2.55, 1600, 14.7, 2200, P_amb) (max pack effect)
k = 1 below 25000 ; 25000…33000: 1…2 ; 33000…36000: 2 ; 36000…41000: 2…1.8 (by p_alt)
eN = Rescale(34, 1.54·k, 85, 1, N1[N]) (engine idle boost)
ok1 = Rescale(0, 0, 2, 1, e1 · Z:air/engine1_psi − P_cab) ok2 likewise
okA = Rescale(0, 0, 2, 1, apu_valve · B738DR_bleedAir_apu_psi − P_cab)
left = Rescale(0, F·ok1 + 0.5·F·okA, 1, F·ok1 + 0.5·F·okA + F·ok2, iso_tgt)
right = Rescale(0, F·ok2, 1, F·ok1 + 0.5·F·okA, iso_tgt)
(iso_tgt = isolation valve *target*; the right side with the valve open gets engine 1 + APU, not engine 2: keep)
l_req = HIGH: 1.82·lpack_valve ; AUTO: 1.45·lpack_valve if (airborne, other pack OFF, flaps 0)
or (on ground, both engine bleed switches OFF), else lpack_valve ; OFF: 0 (R likewise)
l_pack_coef = B738_set_animation_rate(·, l_req, 0, 1.82, 3)
inflow = l_pack_coef · Limit(−left·lpack_valve, −F, 0) + r_pack_coef · Limit(−right·rpack_valve, −F, 0)
cab1 = real_cabin_alt + inflow/60 · dt ; P1 = ft_to_psi(cab1)
4.7.3 Leakage and outflow capacity
doors = sum of the 12 door ratios (+2 emergency exits on variant 4, + both cockpit windows if B738DR_73x > 0)
leak0 = 600000 if doors > 1 ; 360000 if 0 < doors <= 1 or failure 0 ; else 0
leak = 1100 + leak0 + (failure 1 ? 12000 : 0) + 225·|P1 − P_amb|
cap = 5500 + 250·|P1 − P_amb|
if P1 < P_amb: leak, cap negative
4.7.4 Schedule (mode2). A controller is "failed for this mode" if (failure 9 and press_mode 1)
or (failure 10 and press_mode 1 or 2). On the ground off_sched_desc = 0.
press_mode == 0:target_cabin_alt = 0,mode2 = 0, timers 0 → skip to 4.7.6.- controller failed: timers 0,
tgt_outflow = outflow(frozen), target 0,mode2 = 0→ 4.7.6. - On the ground:
mode2 == 0: if (N1 > 60 and N2 > 89) on either engine for > 1.5 s (to_timer, reset when not):mode2 = 1,mem_landing_alt = p0,calc_cabin_alt = Rescale(0, 0, 28000, 8000, Limit(FLT ALT, 0, 28000)),press_max_alt_old = 0; when not:mem_last_alt = 0.mode2 == 1: if (N1 < 50 and N2 < 84) on either engine for > 1.5 s (off_timer):mode2 = 0,mem_last_alt = 0,calc_cabin_alt = 0.mode22/3/4 (landed):mode2 = 1,mem_landing_alt = p0, timers andmem_last_alt0.
- Airborne with
mode2 == 0:mode2 = 2,mem_landing_alt = 0,press_max_alt_old = 0,mem_last_alt = p0. - Then:
mode2 == 1and airborne → 2 (mem_last_alt = p0).mode2 == 2(climb):mem_cruise_alt = FLT ALT; ifft_to_psi(p0) >= ft_to_psi(FLT ALT) + 0.25(more than 0.25 psi below FLT ALT): if alsoft_to_psi(p0) > ft_to_psi(mem_last_alt) + 0.25(descended 0.25 psi from the highest altitude): descent before FLT ALT →mode2 = 4,mem_landing_alt2 = mem_landing_alt,calc_cabin_alt = cabin alt,land_alt_knob_old2 = LAND ALT,off_sched_desc = 1,mem_cruise_alt = p0. Else (within 0.25 psi of FLT ALT or above) →mode2 = 3,press_max_alt_old = FLT ALT,target_diff_press = Limit(ft_to_psi(target_cabin_alt) − ft_to_psi(FLT ALT), 0, max_diff_psi(FLT ALT)).mode2 == 3(cruise): FLT ALT increased (vs. last frame) →mode2 = 2,mem_last_alt = p0. Still 3 and the aircraft is more than 0.25 psi below both FLT ALT andmem_last_alt→mode2 = 4,mem_landing_alt2 = LAND ALT,calc_cabin_alt = cabin alt,mem_cruise_alt = p0.mode2 == 4(descent): ifp0 >= FLT ALTor within 0.25 psi of it →mode2 = 3(memories = FLT ALT / current cabin,target_cabin_alt = cabin alt,mem_last_alt = p0,target_diff_pressas above with the cabin altitude,off_sched_desc = 0); elifmem_last_alt < p0(climbing above the highest altitude) →mode2 = 2,mem_cruise_alt = FLT ALT,off_sched_desc = 0.
4.7.5 Targets and outflow command
mode2 2 and 3 (identical):
dmax = 7.45 (FLT ALT <= 28000) ; 7.8 (<= 37000) ; 8.35 (above)
top = psi_to_ft(ft_to_psi(FLT ALT) − 0.25)
calc_cabin_alt = max(0, mem_landing_alt, psi_to_ft(dmax + ft_to_psi(Limit(FLT ALT, 0, 41000))))
target_cabin = Rescale(mem_landing_alt, mem_landing_alt, top, calc_cabin_alt, max(mem_last_alt, p0))
target_diff = Limit(ft_to_psi(target_cabin) − ft_to_psi(FLT ALT), 0, dmax)
if ft_to_psi(target_cabin) − P_amb > target_diff: target_cabin = psi_to_ft(target_diff + P_amb)
target_cabin = min(target_cabin, 8000)
mode2 0: tgt_outflow = 1, target_diff = 8.35, target_cabin = p0
mode2 1: target_cabin = psi_to_ft(ft_to_psi(mem_landing_alt) + 0.1), target_diff = 8.35
mode2 4: target_diff = max_diff_psi(p0) ; LAND ALT changed → mem_landing_alt2 = LAND ALT
target_cabin = Rescale(mem_landing_alt2, mem_landing_alt2, mem_cruise_alt, calc_cabin_alt, min(p0, mem_cruise_alt))
mem_last_alt = max(mem_last_alt, p0)
if press_mode < 3 (AUTO or ALTN):
if mode2 == 0 and the differential pressure crossed or touched zero since last frame
(old >= 0 >= new or old <= 0 <= new): allow_control = 0
if allow_control:
tc = target_cabin ; if mode2 == 3 and p0 > mem_cruise_alt: tc += p0 − mem_cruise_alt
if ft_to_psi(tc) − P_amb < 0: tc = p0 ; elif > target_diff: tc = psi_to_ft(target_diff + P_amb)
R = 700 (350 if mode2 <= 1) ; band = 0.05·R
rate = mode2 3 and p0 > mem_cruise_alt: aircraft V/S
mode2 0: (real diff >= 0) ? R : −R
else: Rescale(−band, −R, band, R, tc − real_cabin_alt)
need = rate − (inflow + leak)
tgt_outflow = (need and cap have the same sign) ? Limit(need / cap, 0, 1) : 0
if real_cabin_alt > 14500: tgt_outflow = 0
allow_control is set to 1 at the end of every frame with mode2 != 0; so on the ground the
controller is switched off once the cabin has equalised, and the outflow valve then stays where
it is (fully open in practice).
4.7.6 Outflow valve, integration, outputs
if failure 2 (outflow stuck): valve unchanged
elif press_mode == 3 (MAN): valve −= 0.05·dt if manual == −1 ; += 0.05·dt if +1 (0…1) ; timers 0
else: valve moves toward tgt_outflow at 0.05/s (20 s full travel)
B738DR_outflow_valve = Limit(valve, 0, 1) ; X:cockpit/pressure/outflow_valve = same
v = (valve·cap + leak)/60 · dt ; cab2 = cab1 + v
if v > 0 and cab2 >= p_alt: cab2 = p_alt ; if v < 0 and cab2 <= p_alt: cab2 = p_alt
if ft_to_psi(cab2) − P_amb > 9.1: cab2 = psi_to_ft(P_amb + 9.1)
if ft_to_psi(cab2) − P_amb < −1: cab2 = psi_to_ft(P_amb − 1)
vvi = Anim_value(vvi, Limit((cab2 − real_cabin_alt)/dt · 60, −4000, 4000), −4000, 4000, 4)
cabin_alt = cab2 ; diff = Limit(ft_to_psi(cab2) − P_amb, −0.2, 10.2)
→ X:cockpit/pressure/cabin_vvi_actual_fpm, cabin_altitude_actual_ft, cabin_pressure_differential_psi ; real_* = these
displayed (Z:cabin_vvi, cabin_altitude_actual, cabin_pressure_diff) = Anim_value toward the real
values with k = 16000 / 100 / 30 (i.e. immediate) when dir_batbus powered, toward 0 otherwise
Z:pressurization_mode = press_mode ; Z:cabin_alt = real cabin altitude
over-pressure: diff > 9.2 → fuselage_depress_tmr = B738_set_animation_rate(tmr, 240, 0, 240, Rescale(9.2, 1, 10, 0.1, Limit(diff, 9.2, 10)))
else decays to 0 at 1 unit/s ; tmr == 240 → failure 0 = 1 (rapid depressurisation)
4.8 Annunciators (Annunciators, before Systems: previous-frame values)#
Light (Z:annunciator/…) |
Condition (× ann_bright unless noted) |
|---|---|
bleed_trip_1/2 |
latch set if X-Plane bleed_air_fail[i] == 6, or air_engine_psi[i] > 220, or duct_temp[i] > 254; cleared only by TRIP RESET (§4.1.2) |
wing_body_ovht_left/right (both the same) |
X-Plane annunciators/hvac == 1 or wing_body_ovht_test == 1 (OVHT TEST: 3 s timer started while the button is held; the countdown is in Annun_timers, §9) |
pack_left |
X-Plane failure[153] == 6 or l_pack_no_ac (latched when AC transfer bus 2 drops while the L pack switch is not OFF; cleared when the bus returns) |
pack_right |
same with r_pack_no_ac and AC transfer bus 1 |
dual_bleed |
§4.6 (uses brightness_level/lights_test, computed in Systems) |
cabin_alt |
real cabin altitude > 10000 ft (> 14000 ft when the high-altitude landing option is selected and on) |
off_sched_descent |
off_sched_desc |
altn_press |
(mode 1 and exactly one of failures 9/10) or (mode 2 and not failure 10) |
manual_press |
selector MAN |
autofail |
(AUTO and mode ≠ 1) or (ALTN and mode ≠ 2) or mode 0 or diff > 8.75 or cabin > 15800 or |
ram_door_open1/2 |
door 0.05…0.95 → 1, > 0.95 → 0.3, else 0 |
cowl_ice_on_0/1 (COWL VALVE OPEN) |
valve in transit (0.05 < v ≤ 0.95) → 1 ; v > 0.95 → 0.3 ; then disagreement → 1: (switch ON and v ≤ 0.95) or (switch OFF and v ≥ 0.05). _annun variant uses ann_bright_nodim and 0.5 instead of partial values |
wing_ice_on_L/R (L/R VALVE OPEN) |
same pattern with the wing switch and valve L/R |
cowl_ice_0/1 (COWL ANTI-ICE) |
X-Plane inlet ice ratio > 0.055 and cowl heat on |
Z:eicas/eng1_tai, eng2_tai |
1 if the inlet-valve/switch disagreement latch (8 s timer, Annun_timers) is set, else 0 (Zibo's "0.5 when dim" branch compares a float 0.3 to the double 0.3 and is never taken) |
All these lights are forced to the test brightness by the annunciator light test (annunciator spec). The six-pack AIR COND / ANTI-ICE / OVERHEAD recall logic belongs to the annunciator spec.
4.9 Air conditioning temperatures (partial)#
Targets (−800): CONT CAB rheostat 0 → 24 °C, exactly 0.5 → 23 °C, else Rescale(0, 18, 1, 28, r);
FWD: 0 → 18, 0.5 → 24, else the same rescale; AFT: 0 → 18 (Zibo tests 0 twice, so its "25 °C"
branch is dead), else the rescale. If both pack valves > 0.1 or the ASU is connected, the pack
and zone temperatures follow these targets with B738_set_animation_rate (pack temperature
toward the lowest zone target, −30…60 °C; zones ±1 °C from the cont-cab target); the remainder
of the model (one pack, no packs, the outputs Z:zone_temp, Z:cabin_temp, zone temperature
lights) is not specified yet (§9). It has no effect on the pneumatics or pressurization.
5 Constants, tables, timings#
5.1 Pressure conversions (exact)#
ft_to_psi(h) = 1013.25 · (1 − 6.87535e−6 · h)^5.2561 · 0.014503774(computed in double, result float).psi_to_ft(p) = p > 0 ? (1 − 10^(log10(p · 68.9475729 / 1013.25) / 5.2558797)) / 6.8755856e−6 : 0. Note the exponents differ (5.2561 vs 5.2558797; 6.87535e−6 vs 6.8755856e−6): round trips are not exact; keep both.max_diff_psi(h): 7.45 if h ≤ 28000, 7.8 if h ≤ 37000, else 8.35.roundUpToIncrement(x, n): round up to a multiple of n (used by the knobs).
5.2 Engine stage pressures pressure_5th/9th(alt, N1)#
P = N1 > lo(alt) ? (N1 − lo) · S / (hi − lo) : 0 (no upper clamp), lo/hi linearly
interpolated between the rows (alt ≤ 0 uses the first row, > 41000 the last):
| alt ft | 5th lo | 5th hi | 9th lo | 9th hi |
|---|---|---|---|---|
| 0 | 21.143 | 54 | 7 | 25 |
| 10000 | 24.952 | 60 | 10.5 | 33 |
| 22000 | 37.143 | 70 | 21.125 | 38 |
| 31000 | 42.048 | 76 | 25 | 43 |
| 37000 | 55.714 | 82 | 25 | 52 |
| 41000 | 60.524 | 89 | 31.125 | 57 |
S = 46 psi (5th), 36 psi (9th). Example sea level, N1 20 %: 5th 0, 9th 26 psi → duct ≈ 26 psi.
5.3 Thresholds and rates#
| Item | Value |
|---|---|
| HP valve regulation / 5th-stage latch | 38 psi ; latch on ≥ 38, off ≤ 34 ; 9th valid 8 − leak … 110 |
| PRSOV regulation | duct 50 psi ; minimum source 8 − leak ; switch "on" > 0.95 |
| Valve rates (Anim_value k) | HP, PRSOV 3 ; precooler 2 ; APU, packs, isolation 10 ; cowl 1.5 ; wing 1.8 |
| Duct pressure | Anim_value k = 4, 0…80 psi |
| APU bleed | max 45…46 psi (random) × altitude factor ; output cap 41 psi ; valve needs raw > 8 ; idle regulation 21/33/38 psi + leak ; load ramps 0.2/s up, 0.06/s down |
| APU start deficit | −25 psi × (1 − establish) while a start valve > 0.1 |
| ASU | 48 psi, 6 psi/s |
| Pack valve | duct ≥ 4 psi, battery bus |
| Pack flow factor | AUTO 1, AUTO high-flow 1.45, HIGH 1.82, ramp 3 s per unit |
| Wing anti-ice | duct ≥ 8 to open ; "heat on" ≥ 18 psi and valve > 0.95 ; ground overheat after 10 s at N1 > 41.3 % (recovers at 0.25 s/s) ; thrust lever > 0.5 on ground closes |
| Bleed trip | > 220 psi or > 254 °C |
| Duct temperature | source 580 °C (340 with HP closed) ; precooler setpoint 214 °C ; capacity 13300 (SL) … 7565 (41000 ft) |
| Pressurization | rate limit 700 fpm (350 on ground/take-off), proportional band 5 % ; outflow 0.05/s ; leak 1100 fpm + 225/psi ; outflow capacity 5500 fpm + 250/psi ; cabin rate ±4000 ; diff −1…9.1 psi (displayed −0.2…10.2) ; take-off pre-pressurisation 0.1 psi ; max cabin 8000 ft ; outflow forced closed above 14500 ft |
| Mode timers | take-off detect N1 > 60 and N2 > 89 for 1.5 s ; ground N1 < 50 and N2 < 84 for 1.5 s ; mode switch thresholds 0.25 psi |
| Controller power-up | 1 s |
| Knobs | FLT ALT −1000…42000 step 500 ; LAND ALT −1000…13600 step 50 ; auto-repeat after 1 s |
| Over-pressure | > 9.2 psi: 240 s at 9.2 … 24 s at 10 psi → failure 0 |
| Ram doors | 10 s travel ; open on ground, or flaps out and GS < 123 kt ; closed flaps up or GS > 136 kt |
5.4 Pressurisation failure items (Z:failure/pressurisation[i], value 1 = failed)#
Meaning inferred from the code that reads them (the EFB page text was not checked, §9): 0 fuselage leak (rapid, 360000 fpm), 1 slow leak (12000 fpm), 2 outflow valve stuck, 3 L pack valve stuck, 4 R pack valve stuck, 5 isolation valve stuck, 6 APU bleed valve stuck, 7 engine 1 PRSOV stuck, 8 engine 2 PRSOV stuck, 9 AUTO controller 1, 10 controller 2 (ALTN), 11/12 HP valve 1/2 stuck, 13/14 precooler 1/2 stuck, 15 APU bleed failed (valve closed, APU MAINT).
6 Outputs#
6.1 Zibo datarefs written (read by Lua, the model, the displays)#
Dataref (laminar/B738/…) |
Global | SCI state |
|---|---|---|
indicators/duct_press_L/R |
B738DR_duct_pressure_L/R |
INDICATORS_DUCT_PRESS_L/R |
indicators/real_duct_press_L/R |
B738DR_real_duct_pressure_L/R |
– (engine start input) |
air/engine1_psi, engine2_psi, engineN_5th_psi, engineN_9th_psi, engineN_9th_valve, engN_precooler_valve_pos, apu_psi, cabin_psi |
bleed globals | – |
air/engine1/bleed_valve_pos, engine2/…, apu/…, l_pack/…, r_pack/…, isolation/bleed_valve_pos |
valve positions | – |
air/engine1/starter_valve, engine2/… |
starter valves (engine spec) | – |
toggle_switch/bleed_air_1_pos, _2_pos, _apu_pos, air/l_pack_pos, r_pack_pos, isolation_valve_pos, l/r_recirc_fan_pos, trim_air_pos, toggle_switch/air_valve_ctrl, air_valve_manual, air_temp_source, pressurization/knobs/landing_alt, air/*_cab_temp/rheostat |
switch states | the …_POS / raw SCI states of §2.1 |
push_button/trip_reset, push_button/duct_ovht_test_pos |
buttons | PUSH_BUTTON_DUCT_OVHT_TEST_POS |
annunciator/dual_bleed, bleed_trip_1/2, wing_body_ovht_left/right, pack_left/right, cabin_alt, off_sched_descent, altn_press, manual_press, autofail, ram_door_open1/2(_annun), cowl_ice_0/1, cowl_ice_on_0/1(_annun), wing_ice_on_L/R(_annun) |
§4.8 | ANNUNCIATOR_* of the same name (no SCI state for autofail, ram_door_*, *_annun) |
outflow_valve, outflow_valve_dig |
outflow 0…1 | OUTFLOW_VALVE |
cabin_vvi, cabin_alt, cabin_altitude_actual, cabin_pressure_diff |
displayed cabin values | – (the model reads the X-Plane ones, §6.2) |
pressurization_mode, pressurization_mode2, pressurization_dash, ram_door1/2 |
§4.7 | DIGITAL_PRESS_PANEL is Lua-side (§9) |
system/eng_inlet_valve[2], system/wing_anti_ice_valve[2], ice/wing_l/r_heat_on, eicas/eng1/2_tai |
anti-ice | – |
ice/eng1_heat_pos, eng2_heat_pos, wing_heat_pos |
switches | ICE_*_HEAT_POS |
engine/engine_air_starter |
ASU start annunciation (engine spec §4.11.5) | – |
6.2 X-Plane datarefs written (and what they mean for MSFS)#
| X-Plane dataref | Written by | MSFS meaning |
|---|---|---|
X:cockpit/pressure/cabin_altitude_actual_ft, cabin_pressure_differential_psi, cabin_vvi_actual_fpm, outflow_valve |
§4.7.6 | these are the SCI states SIM_COCKPIT2_PRESSURIZATION_INDICATORS_CABIN_ALTITUDE_FT, …_PRESSURE_DIFFENTIAL_PSI, …_CABIN_VVI_FPM (X-Plane aliases cockpit ↔ cockpit2): the rewrite outputs its own cabin values there |
X:cockpit/pressure/max_allowable_altitude |
FLT ALT knob | SCI SIM_COCKPIT2_PRESSURIZATION_ACTUATORS_MAX_ALLOWABLE_ALTITUDE_FT; rewrite state |
X:operation/override/override_pressurization = 1 |
flight start, Systems |
none (the rewrite simply owns pressurization) |
X:cockpit2/pressurization/actuators/bleed_air_mode |
§4.6 | none |
X:cockpit2/bleedair/actuators/* (Pressurization()): pack_center = 0, engine_bleed_sov[2] = bleed switches ≠ 0, apu_bleed = APU valve > 0.05 and switch ON, gpu_bleed = ASU, pack_left/right = switch ≠ OFF, isol_valve_left/right (open unless all four switches on with isolation AUTO, or OPEN; with the ASU connected the right one is always 1; with APU bleed the left one is always 1) |
A.6.2 | none: only X-Plane's own bleed model reads them, and nothing in Zibo reads that model back |
X:cockpit2/ice/ice_inlet_heat_on_per_engine[2] |
§4.2 | engine anti-ice to the sim (ice removal) |
X:cockpit2/ice/ice_surfce_heat_left_on/right_on |
forced 0 | Zibo removes wing ice itself (de_ice, §9) |
X:operation/failures/rel_bleed_air_lft/rgt = 0 |
trip reset | none |
X:operation/failures/rel_APU_press = 6 |
failure 15 | none |
6.3 Values the engine start uses (contract with the engine spec)#
real_duct_press_L/R (§4.4), isolation/bleed_valve_pos, apu/bleed_valve_pos, ext_air_duct,
apu_establish_time, engine_air_start, l/r_pack/bleed_valve_pos (ASU start annunciation), all
from the previous frame (Starter runs before Systems). The starter needs ≥ 10.5 psi to turn,
full motoring at 21.6 psi (engine spec §4.8). Measured on the oracle (§8): ASU 48 psi on both
ducts; APU at sea level, isolation open: packs OFF 41 → 38 → 33 → 21 psi (load schedule, 0.06/s
down-ramp), packs AUTO 33 psi (21 + APU leak 1.5 × pack load 8). Start valve opened with both
packs AUTO about 60 s after the APU came up: the raw APU pressure (≈ 45.5 − leaks − 25 × (1 −
establish)) falls below 8 psi, the APU bleed valve target goes to 0 and the duct collapses to
≈ 5 psi — the starter does not turn (< 10.5 psi). Zibo therefore requires packs OFF and/or an
established APU (≈ 100 s above 90 % at 15 °C) for an APU-bleed start; the rewrite must reproduce
this, including the APU valve closing.
7 X-Plane-provided behaviour to supply, with a recommendation#
Key finding: Zibo computes the complete pneumatic and pressurization model itself. X-Plane's
pressurization model is disabled (override_pressurization = 1) and Zibo writes the cabin
altitude, rate, differential and outflow valve into X-Plane's datarefs; X-Plane's bleed-air model
is only fed (bleedair/actuators/*, bleed_air_mode) and never read back. The rewrite therefore
needs no MSFS pressurization or bleed model. What it needs from the simulator:
| Input | X-Plane | MSFS SimVar (recommended) |
|---|---|---|
Pressure altitude p0 |
flightmodel2/position/pressure_altitude |
PRESSURE ALTITUDE (ft) |
Ambient pressure for elevation_ft |
weather/barometer_current_inhg |
AMBIENT PRESSURE (inHg); compute elevation_ft with Zibo's formula |
| OAT / TAT | cockpit2/temperature/outside_air_temp_degc, TAT |
AMBIENT TEMPERATURE, TOTAL AIR TEMPERATURE (°C) |
| Vertical speed | simDR_fpm |
VERTICAL SPEED (ft/min) |
| Ground speed, flaps, on-ground, RA | as listed in §2.2 | GROUND VELOCITY, Zibo flap ratio (flight controls), SIM ON GROUND / gear contact, PLANE ALT ABOVE GROUND MINUS CG / RA |
| Engine running | simDR_engine_on |
engine spec (ENG COMBUSTION) |
| APU N1, running | APU_N1_percent, APU_running |
APU spec (APU PCT RPM) |
| Doors | door ratios | INTERACTIVE POINT OPEN:n / exit open ratio (doors spec) |
| Inlet ice (COWL ANTI-ICE light) | flightmodel/failures/inlet_ice_per_engine |
ENG ANTI ICE-independent: STRUCTURAL ICE PCT / engine icing SimVar if available; else 0 (light never on) |
Failure inputs (bleed_air_fail, hvac, failures[153]) |
X-Plane failure system | none in MSFS: constant 0. bleed_air_fail == 6 never occurs in X-Plane either (the annunciator is 0/1), so the condition is dead |
Outputs to MSFS (recommendation): engine anti-ice → K:ANTI_ICE_SET_ENG1/2 from
cowl_on[i] (so MSFS inlet icing is removed like X-Plane's); wing anti-ice → MSFS structural
de-ice from wing_l/r_heat_on (only if MSFS icing is enabled; Zibo's own de_ice replaces
X-Plane's wing heat). MSFS PRESSURIZATION * SimVars are not used (no need to drive them);
if the aircraft.cfg has a pressurization section, the MSFS cabin model runs in parallel without
effect on the systems.
The oracle host has no X-Plane atmosphere (engine spec): scripts must set
sim/weather/barometer_current_inhg (elevation_ft), sim/flightmodel2/position/pressure_altitude
and the temperatures themselves.
8 Differential test scenarios#
Compare at fixed dt on the SCI states and the Z:/X: values of §6. Tolerances: valve
positions and pressures 1e−4 relative (float vs double in ft_to_psi), random values
(rndm_max_apu_bleed_psi) seeded identically or fed from the oracle.
- Ground air (oracle script
xphost/test/sci/pneu_spec_ground_air.json, 21/21 pass): cold & dark (bleeds ON, APU bleed OFF, packs OFF, isolation OPEN, isolation valve 0 until AC transfer bus 1 is powered), battery + GPU → isolation valve opens; ASU → both real ducts and the gauge 48 psi; packs AUTO → both pack valves open, ASU pressure unchanged; isolation AUTO with bleeds ON and packs AUTO → valve closes, left duct 0, L pack valve closes (< 4 psi), right side 48; L pack OFF → isolation reopens, left duct 48; ASU removed → ducts 0, R pack valve closes. - APU bleed with packs OFF / AUTO (oracle script
xphost/test/sci/pneu_spec_apu_bleed.json, 17/17 pass; APU started as inelec_cold_start.json, fuel set so the APU lights): APU bleed ON → valve opens onceapu_duct_enableis set; packs OFF: APU psi 41 → 33 → 21, both ducts equal (isolation open); packs AUTO: 33 psi on both ducts; engine 1 start switch GRD ~60 s after APU start: APU valve closes, ducts ≈ 5 psi (printed, §6.3). - Engine start on APU bleed (with the engine spec): start valve open → APU load valve full open, PRSOV of the starting engine closed, duct pressure drop by the starter load.
- Engine bleed after start: N1 idle → 9th-stage regulation (HP valve), PRSOV regulating to 50 psi duct, DUAL BLEED with APU bleed ON and bleed 1 ON, isolation AUTO closing when all four switches are on.
- Pressurization schedule (needs pressure altitude/ambient pressure as inputs over time): take-off (N1 > 60, N2 > 89 for 1.5 s → mode 1, 0.1 psi pre-pressurisation), climb to FLT ALT (mode 2 → 3), cruise, descent (mode 4 toward LAND ALT), landing (mode 1 → 0, outflow opens); descent before FLT ALT → OFF SCHED DESCENT; FLT ALT raised in cruise → mode 2.
- Modes and failures: ALTN (DC bus 1 off), MAN with the manual switch, controller failures 9/10 (ALTN/AUTO FAIL lights), door open (600000 fpm leak), over-pressure timer.
- Anti-ice: cowl valve with engine running and DC bus; wing anti-ice on the ground (thrust lever > 0.5 closes; N1 > 41.3 % for 10 s → overheat close), lift-off trip-off of a switch set on the ground; VALVE OPEN light brightness sequence.
8a Implementation notes (M3c, build 6)#
The bleed part (§4.2–4.6, §4.8 bleed lights) is implemented in wasm/src/core/pneu/; the APU bleed
(§4.3.1, §4.3.5, APU valve of §4.6) stays in the APU subsystem. Scenarios 1 and 2 of §8 pass on the
rewrite with the same checks; their printed values agree until the start valve opens in scenario 2,
where the APU valve cycles chaotically (the phase depends on Zibo's random maximum APU pressure).
Checked against the disassembly: pressure_5th/9th (row breakpoints and (N1 − lo)·(S/(hi − lo))),
ft_to_psi, psi_to_ft (bit for bit, wasm/test/test_engine_zibo.cpp). The demand of the APU load
control uses the first-order engineN_starter_valve, the warm-up deficit B738DR_start_valveN
(§4.3.1, §4.3.5). For the engine start: engine 1 can only motor from ground air through the
isolation valve (src2 = iso·ext·29.6), and the isolation valve does not move without AC transfer
bus 1 (battery only: the left duct stays empty). Pressurization (§4.7) is the next step.
9 Open questions and what is not covered yet#
- Not covered: cabin/zone temperature model beyond the targets (§4.9;
SystemsafterPressurisation_system), zone temperature lights,de_ice()(wing/engine ice removal and the X-Plane ice ratios),Annun_timers(WING-BODY OVHT test timer, inlet disagreement 8 s timer), window/probe heat, the -700 air panel (*_700), the Lua side of the pressurization panel display (DIGITAL_PRESS_PANEL, FLT/LAND ALT digits), the six-pack recall,Systems_load/save(situation save/restore of the pneumatic state), replay (Systems_replay). roundUpToIncrementfor negative values (FLT ALT/LAND ALT below 0): read its body before porting.- FLT ALT initial value: X-Plane's
max_allowable_altitudecomes from the .acf/X-Plane; measure on the oracle (and whether the host aliasessim/cockpit/pressure/max_allowable_altitudewith the cockpit2 dataref the SCI state reads). - The SCI contract has no state for
annunciator/autofail(nor for the RAM DOOR lights); check which names the cockpit model uses for these lights. - X-Plane failure index 153 (PACK lights): identify which X-Plane failure it is; constant 0 in MSFS.
- Failure item names (§5.4) are inferred from the code; confirm against the EFB failure page strings
in
B738.a_fms. - The doubled starter load term (§4.4, confirmed in the disassembly: the Rescale result is added to itself) and the asymmetric isolation-valve pack supply (§4.7.2) are reproduced as found; they look like Zibo slips but must stay for parity.
- Decompiler caveats checked against the disassembly: double-precision constants (0.1, 0.25, 0.5,
0.95, 1.54, 1.45, 1.82, 9.1, 9.2, 0.055) and the
std::absarguments of §4.7.3. TheSystemsdecompilation used for §4.3–4.6 was produced before initialised-data globals were excluded from constant folding; the two places affected (apu_protection_disabled,bleed_valve_min_act_press) were re-read from the disassembly. A re-check of the whole of §4.3–4.6 against a clean decompilation is advisable before implementation.
10 Implementation (build 31): pressurization#
§4.7 and the pressurization commands are zibomod's own code translated by tools/re/gen_press.sh
(wasm/src/core/pneu/press_gen.*, class Press in press.h), the §4.8 pressurization lights in press.cpp,
wired in wasm/src/core/systems_press.cpp. Oracle: xphost/test/sci/press_trace.json (host call hooks around
Systems → Pressurisation_system), golden wasm/test/data/press_trace_oracle.txt.gz, test
press_replay_matches_oracle. Doors and windows are closed (not modelled), the high-altitude-landing option is off.