Files
BT411/context/pod-hardware.md
T
arcattackandClaude Fable 5 1271d3bc76 Torso elevation (pitch aim) wired: the pod stick's Y axis lives
The mechs could always tilt their aim up/down -- Torso models the full
vertical axis (currentElevation, rate, VerticalLimitTop/Bottom,
recenter) and EVERY 1995 control mode routes stickPosition.y into
Torso::SetAnalogElevationAxis -- but the desktop bridge hard-zeroed
stick Y, so the axis was dead on a keyboard rig.  The one pod control
the remap left unwired.

- btinput: JoystickY axis -> elevTarget/elevActive/elevAbsolute
- mech4 shim: sElev integrator (same walk/spring model as the twist;
  X recenters pitch too via gBTElevRecenter)
- mechmppr bridge: feeds stickPosition.y every bridged frame (the old
  unconditional zero removed); both mode branches covered
- CONTROLS.MAP (+ numpad profile + compiled default): R/F = aim
  up/down, pad LeftStickY = elevation (was unused)
- torso.hpp: CurrentElevation()/ElevationVelocity() accessors (diag)
- [mppr] trace gains stickY (note: the trace reads AFTER the next
  frame's device push re-zeroes the stick -- input flows regardless)

Verified live: R held -> torso elevation climbs at the authored rate
and clamps at 0.349066 rad = exactly 20.0 deg (the Blackhawk's
VerticalLimitTop); release holds the aim.  The eyepoint correctly
stays level -- pitch aims the GUNS and reads on the HUD's vertical
elevation tape, as in the pod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:44:22 -05:00

9.4 KiB
Raw Blame History

id, title, status, source_sections, related_topics, key_terms
id title status source_sections related_topics key_terms
pod-hardware Pod Hardware — the fixed target (7 monitors, D3D9, RIO) established PROGRESS_LOG.md §3; the PLATFORM PROFILE + GAUGE DEV-COMPOSITE notes
gauges-hud
rendering
project-overview
pod
RIO
MFD
IG-board
SVGA16

Pod Hardware (the fixed target)

The port must run on (a) a dev box and (b) the fixed arcade pod. The pod is a hard constraint that bounds the graphics API. Full detail: docs/PROGRESS_LOG.md §3.

Displays

  • 2 video cards → 7 monitors: main 3D view 800×600; radar 640×480; five monochrome MFDs driven as one 1280×480 horizontally-spanned surface. [T1]
  • Requires old NVIDIA drivers for the MFD horizontal spanning — a hard constraint that bounds the graphics API → Target Direct3D 9 (lowest common denominator that runs on old-driver pods AND modern cards; one codepath). NOT Vulkan/D3D11+. CUDA is irrelevant (compute, not display). [T1]

Cockpit I/O (RIO)

Joystick X/Y, throttle, pedals, buttons over serial COM (L4RIO, L4SERIAL). Must be remapped to keyboard/gamepad on a dev box; real wiring is a pod bring-up task (Phase 8). The engine is a PUSH model — LBE4ControlsManager groups are fed by all devices (RIO on the pod, DirectInput on dev); the MechControlsMapper interprets them (locomotion). [T2]

The button space + lamps [T0, L4CTRL.h enum]

buttonGroup addresses 0x00-0x47: AuxLowerRight/Left 1-8 (0x00-0x0F), Secondary1-12 (0x10-0x1B), AuxUpperCenter/Left/Right 1-8 (0x20-0x37 — the preset/eng "display eng data" banks; 0x30-0x37 = the target HOTBOX, pilot select), icom/door 0x39-0x3C, Panic 0x3D (the config-mode lamp), Throttle1 0x3F (throttle-head = REVERSE THRUST), joystick cluster 0x40-0x47 (Main trigger 0x40, hat 0x41-0x44, Pinky/ThumbLow/ThumbHigh 0x45-0x47 — the four MAPPABLE fire buttons). "Lamp buttons" are literal: physical illuminated pushbuttons in panels around the screens; the RIO LampRequest protocol lights them (MakeLinkedLamp), reports burned-out bulbs (RIODeadLamps failure page, per-lamp names). A lit button = active. Keypads are NOT buttons: keyboardGroup[KeyboardPilot/ KeyboardExternal] carry key VALUES ('0'-'9','A'-'F', L4CTRL.cpp:2526). RIO event convention: press = buttonGroup[a].Update(a+1, modeMask) (mask saved), release = Update(-a-1, savedMask) (L4CTRL.cpp:2470-2520).

Desktop input remap — CONTROLS.MAP + XInput (2026-07-18) [T2 live]

game/reconstructed/btinput.cpp + content/CONTROLS.MAP (WASD-classic default, compiled-in twin; content/CONTROLS_NUMPAD.MAP = the corrected community numpad profile). Grammar: key <name> button <addr>|axis <axis> deflect|rate <n>|keypad pilot|external <0-15>|pckey <char>|action <name>; pad <button> same targets; padaxis <src> axis ... [invert] [deadzone d] [rate n]. Axes: Throttle (lever, rate-walks + sticks), LeftPedal/RightPedal/Pedals (turn, springs), JoystickX (torso twist), JoystickY (torso ELEVATION — pitch aim; every 1995 control mode routes it to Torso::SetAnalogElevationAxis; keys R/F + pad LeftStickY in the default map; verified to the authentic VerticalLimitTop clamp, 20° on the Blackhawk; aims the GUNS — reads on the HUD elevation tape, the eyepoint stays level). Emissions mirror the RIO conventions above exactly; the four fire buttons (0x40/45/46/47) publish as levels to the bring-up fire channels instead of buttonGroup (avoids double-fire once the streamed ChooseButton mappings are exercised). XInput loads dynamically (xinput1_4 → 9_1_0; disconnected-pad probing rate-limited to 1 Hz). Keys claimed by a binding are SUPPRESSED from the legacy WM_CHAR/KEYUP feed (BTInputSuppressKey, hook at L4CTRL.cpp keyboard read) — this ended the historic DOUBLE-DISPATCH: 'w' drove AND selected pilot 0; F5's key-up value 0x74 aliased to the 't' hotkey; letter key-ups (uppercase) fed the developer fake-event dispatcher. Unbound keys keep their authentic 1995 dispatcher meaning (reachable from any key via pckey). The old dual-use 'V' (view toggle + look-behind) is split: V = ViewToggle, B = LookBehind. Verified live: bindings load, W/NumPad8 drive (spd rises), X all-stop, aux-button emission ([input] 0x2f PRESS), suppression both directions ('r' delivered / 'w' swallowed), full-keyspace WM_CHAR+WM_KEYUP fuzz survived. Diag: BT_INPUT_LOG.

The pod throttle is ANALOG-CONTINUOUS, not notched (verified end-to-end) [T1]

The authentic pod throttle path — traced 2026-07 (task #50 throttle-fidelity question):

  1. Hardware → RIO: serial AnalogReply packet → Ranger("Throttle", 0, 800, .05) — raw counts 0800, 5% deadband, auto-ranging offset, output a CONTINUOUS Scalar; the sign is inverted ("Throttle counts BACKWARDS", engine/MUNGA_L4/L4RIO.cpp:1374-1377, Ranger @L4RIO.cpp:461-701). No quantization/notching anywhere in Ranger::Update.
  2. Manager: LBE4ControlsManager::ExecutescalarGroup[ScalarThrottle].Update(&rioPointer-> Throttle, mode_mask) on every AnalogReply (L4CTRL.cpp:1379-1382). ScalarThrottle = index 0 → manager+0x24 (scalarGroup base 0x24, 0x20/entry; buttonGroup base 0x1c0, keyboardGroup 0x160).
  3. Streamed .CTL mapping (the "handled elsewhere"): MechRIOMapper's ctor @004d266c binds NO throttle — the bind comes from the type-19 ControlMappingStream resource named "L4" (child of the per-mech type-6 ControlMappingsList; installed by BTL4APP MakeViewpointEntity via CreateStreamedMappings @0047703c). BTL4.RES "L4" record [1]: Scalar Throttle → subsystemID 0 (ControlsMapper slot), DirectMapping, attr 4, mask 0xffffffff. Attr 4 = "ThrottlePosition" @ mapper+0x11c (binary IndexEntry table @0050efd8: id 4 → offset 0x11d-1 = 0x11c, name "ThrottlePosition" @0050f28f). Reverse = record [2]: Button Throttle1 (0x3F, on the throttle handle) → attr 6 ReverseThrust@0x124. Turn = pedals value-bound in the ctor (manager+0x44/+0x64 → mapper+0x1b4/+0x1b8).
  4. Interpretation: L4MechControlsMapper::InterpretControls @004d196c applies the ONLY software detent — snap to 1.0 when |t1.0| ≤ 0.05 — then MechControlsMapper::InterpretControls @004afd10 computes speedDemand@0x128 = maxSpeed(mech+0x34c) × throttlePosition(0x11c) × scale(mech+0x5c0) (forward) or maxSpeed × throttlePosition (reverse flag 0x124).

Consequence: the original pod produced a continuously varying speedDemand while the lever moved (updated per serial AnalogReply, NOT per render frame). There are NO "5 throttle notches" anywhere in the software path. (Keyboard keys '1'-'5' in @004d1bf0 set controls-manager MODE masks; the '+'/'-' pair steps a [0,5] value that drives pow(2,x) into mech+0x404 = HUD zoom 1×–32× — neither is a speed setting.) The mech's throttleState@0x4a4 writer remains un-exported (likely in the 0x4a9b5a0x4ab188 gap) — open-questions.

MECHANICAL-notch hypothesis (2026-07-16) [T4, hardware]: the "5 speeds" pod lore may still be true — as detents in the throttle QUADRANT hardware, invisible to the software (the pot reads continuous counts regardless of where the lever mechanically rests). Independent corroboration from the gait math (locomotion): the gait SM has NO stable state for a demand between the walk cap (walkStrideLength@0x534) and the run engage speed (reverseSpeedMax@0x538, the walk→run transition clip's exit speed) — a demand PARKED in that band hunts walk→shift-up→shift-down forever, firing the authored EngineShiftFwd/Rev sounds each swing (binary-verified, all-authentic data: Blackhawk band = demand 22.0230.87 = throttle 3650%). A design like that only ships if the hardware discourages parking in the band. Port accommodation: the keyboard lever snaps out of the dead band AT REST (mech4.cpp "GAIT DETENT"; sweeping through while held stays continuous = authentic moving lever). Ask Nick: did the pod throttle quadrant have mechanical detents (how many / positions)? — open-questions.

Multi-surface gauge path (intact, pod-only by default)

The pod multi-surface path EXISTS and is intact: DPLRenderer::FindBestAdapterIndices (multi- adapter selector, honors PRIMGAUGE/SECGAUGE/MFDGAUGE/SPANDISABLE), SVGA16::BuildWindows (a fullscreen D3D device + window per gauge/MFD surface, MFD span = width×2), L4GaugeRenderer (gated on L4GAUGE). content/SETENV.BAT is the authentic pod env preset (L4CONTROLS=RIO,KEYBOARD; L4GAUGE=640x480x16; L4PLASMA=com2). [T2]

Platform profile switch

-platform pod|dev (or BT_PLATFORM env; default DEV) selects a runtime env preset WITHOUT forking the codepath. DEV = keyboard + single 800×600 window. POD = RIO input (keyboard fallback off serial) + multi-surface gauges (from SETENV.BAT/L4GAUGE on real hardware). -platform pod does NOT auto-enable L4GAUGE (each surface needs its own fullscreen device on the pod's 2 cards). Off-pod, the 6 MFD surfaces render in a dev window via BT_DEV_GAUGES (gauges-hud). [T2]

MFD surface model

All cockpit surfaces are bit-plane MASKS over ONE shared SVGA16 pixelBuffer: sec=palette low byte; Heat=0x4000, Mfd2=0x0400, Comm=0x8000, Mfd1=0x0100, Mfd3=0x1000; Eng1-3 = engineering-mode alt planes; overlay=0x00C0 (shares the sec surface). See gauges-hud. [T2]

Key Relationships