From ca9e5ff5c06ae4f06de2daa322cf06715962fe4a Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Thu, 13 Aug 2026 19:03:41 -0500 Subject: [PATCH] #173 FINAL: port cadence defect FIXED -- the velocity ring now samples at the pod's 28Hz by default (BT_MYO_RING_HZ overrides). Measured [T2, six curves m173v2]: at ~59fps the |a| operand carried 64-66% gait-aliasing noise = drive heat 2x the machine's; at 28Hz + the authored loop-5 boost (detent 50 receipt-proven, flow 0.909) a vulture holds FULL supercharge 54-57 u/s indefinitely at stable Tm~1500 -- the machine's valve lever worked, the manual's Super Charged print was honest, witnesses 7-0 (Draco/Oracle/Elengil + the user's eyes beat the solver AND the first bench). Balanced seek-4 still cooks authentically (~18s); wrap trap stands; display pixel-verified in sync. BT_VALVE5 bench hook added (receipted detent presses). Post-promotion smoke: boosted vulture stable ~1500 dipping under. Gotcha 32 (sampled-history operands at render cadence; a validated model EXTRAPOLATES, only the bench INTERPOLATES); cadence census -> BTSimClock migration checklist in open-questions (gyro bounce timescale next, footstep smoothers, doppler; heatLoad was already pod-clocked #119 = the reference pattern). Guide 2B rewritten with the confession + the measured fix --- context/open-questions.md | 36 ++++++++- context/reconstruction-gotchas.md | 23 ++++++ context/subsystems.md | 42 +++++++++++ docs/WEAPONS_FIELD_GUIDE.txt | 36 +++++---- game/reconstructed/mech4.cpp | 117 ++++++++++++++++++++++++++++- scratchpad/night17/m173v2_bench.sh | 47 ++++++++++++ scratchpad/night17/m173v_bench.sh | 65 ++++++++++++++++ scratchpad/night17/m173v_check.py | 68 +++++++++++++++++ 8 files changed, 415 insertions(+), 19 deletions(-) create mode 100644 scratchpad/night17/m173v2_bench.sh create mode 100644 scratchpad/night17/m173v_bench.sh create mode 100644 scratchpad/night17/m173v_check.py diff --git a/context/open-questions.md b/context/open-questions.md index aecddea..83272ef 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -1233,7 +1233,18 @@ consistent with internals-only, see #103). Logs: `scratchpad/night16/` + Oracle (x2 spawns, AFC50/Zanin Neko); once ABSENT for an entire fatal leak (5-6 voice repetitions, no flash on any MFD, died ~9:38PM CT -- the match's only death, receipt findable in night16). Distinct from the closed #135: this is the SET-edge / first-flash arming path. -- **#173 -- chicken-walker seek-4 myomer overheat** (see [[subsystems]] night-16 note). +- **#173 -- chicken-walker seek-4 myomer overheat: ⚑ SETTLED AUTHENTIC (2026-08-13, three + lanes, see [[subsystems]] #173 blocks).** Bench matrix reproduced Elengil exactly (vulture + seek-4: degT in 13.9 s, governor-pinned eq ~1801, loop-5 bar full-scale ~15-20 s, sheds + −10/s only at standstill); the equilibrium is a chassis-blind governor thermostat — the + port's avatar (same 143-kph speed class) cooks identically, and the manual prints the big + walkers even FASTER (MadCat 175/220) than the port runs them. Counter-play measured: one + loop-5 valve press sustains drive 0.68 vs 0.54. TWO residuals stay open: (1) [T3] the + accel-term |a| ring is frame-cadence-sensitive (|a|≈14-20 at 59 fps on a straight flat + cruise = gait surge; pod 28 Hz would read ~2-3x smaller → pod eq likely ~1200-1500, softer + governor — if calibration is wanted, fix the |a| feed cadence, never the byte-exact + constants); (2) locomotion-lane: port per-chassis top speeds undershoot the manual prints + (thor 87 vs 143 N print) — correcting them raises heat further. - **#165 ROOT-CAUSED + FIXED + BENCHED (2026-08-13, the 4-agent hunt):** gotcha SS30 -- the binary's ONE destroyed cell was split into four port stores and the crit-lottery kill path (MechWeapon::TakeDamage) wrote none the gate reads. Fixed (chain to MechSubsystem::TakeDamage, @@ -1253,3 +1264,26 @@ consistent with internals-only, see #103). Logs: `scratchpad/night16/` + Oracle wanted for the pair-leak confirmation. - Nanook lost the coolant MFD glass window, recovered only by config edit -> #76 comment (in-game layout-reset recovery path). + + +## Cadence census -- the shared 28Hz sim-clock migration checklist (2026-08-13) [T1/T2] +Census of sampled-history-at-render-cadence sites (gotcha 32; full table in the census +workflow journal wf_d875938f + classifier verdicts). DONE: velocity ring default-28Hz +(mech4.cpp, HIGH, the #173 fix -- myomer heat + instability/sway inherit); heatLoad already +pod-clocked (#119, the reference pattern). REMAINING for the BTSimClock migration (one +shared 28Hz tick source; tick-native sites subscribe; dt-native physics -- Mover/missile ODE +-- stays on real dt): +- MED gyro eye/body spring-damper integrators (gyro.cpp:503-575): per-tick position step, NO + dt, damping carries last-frame state -- cockpit bounce timescale runs 2.14x the machine's. + Needs an impulse-response bench (28 vs 60) before/after. +- LOW footstep AudioControlSmoother windows (AUDCMP.cpp:952-977): authored N=30/15 sized for + 28Hz; halved at 60fps + noisy operand. Clock the watcher-poll intake. +- LOW doppler operand (AUDLOC.cpp:439-505 reads UNSMOOTHED worldLinearVelocity frame + derivative): per-stride pitch warble on nearby movers. A/B-able by ear. +- LOW published localVelocity 0.25s exponential smoothing (mech4.cpp:5578-5613): re-base to + the binary's own 15-sample@28Hz filter once the shared clock exists. +- DEFERRED dead-reckon divergence (@_DAT_004be050) -- only matters if the Missile entity + revives. ANNOTATE-ONLY: Simulate telemetry FilteredScalars (dormant). +- Productionize the ring's 16-slot dt-accumulator registry (mech4.cpp) to the heat.cpp map + pattern (degrades gracefully to per-frame push on overflow today; fine for one mission's + mech count, sloppy long-term). diff --git a/context/reconstruction-gotchas.md b/context/reconstruction-gotchas.md index 2ec5c50..2bfdf92 100644 --- a/context/reconstruction-gotchas.md +++ b/context/reconstruction-gotchas.md @@ -1064,3 +1064,26 @@ passes every reader-side bench. **Bench rule (the #165 lesson, second applicatio week):** prove the bench detects the bug BEFORE trusting its pass -- the negative control here (fix stashed) scored 147 cascades where the fixed build scores 1. Bench: BT_ZONE_HAMMER=[,] (mech4.cpp) + scratchpad/night16/cascade_bench.sh. + + +## 32. SAMPLED-HISTORY OPERANDS AT RENDER CADENCE: rate-scaling amounts cannot un-noise a signal (#173 flip, 2026-08-13) +The port's #137 pattern (term x dt x 28) correctly rate-normalizes per-tick heat AMOUNTS -- but +OPERANDS COMPUTED FROM SAMPLED HISTORY (rings, last-frame deltas, moving averages, per-tick +integrators carrying state) still sampled at RENDER cadence where the pod's board sampled at +28 Hz. The killer case: the AccelerationLastFrame velocity ring pushed per render frame read +|a| 17-26 of pure gait-aliasing noise on a flat straight cruise -- 64-66% of myomer drive-heat +generation was PHANTOM; at 28 Hz sampling the noise drops to 24-32% and generation HALVES at +equal speed. Consequence: every fast-heavy chassis was over-punished ~2x; the authored loop-5 +valve boost genuinely sustains FULL supercharge seek-4 at 28 Hz (bench m173v2 leg G: 54-57 u/s +held, Tm stable ~1500) -- the machine's lever worked and the manual's Super Charged print was +honest. Witnesses (Draco/Oracle/Elengil + the user's own eyes) beat the validated-solver +extrapolation AND the first bench: 7-0 this week. +**Rules:** (1) any binary value derived from per-tick sampled history must be sampled on the +BINARY'S clock -- accumulate render dt, take one sample per 1/28 s measuring over the full +elapsed window (the heat.cpp #119 heatLoad clock is the reference implementation; the ring +default-28Hz in mech4.cpp is the second). (2) Rate-scaling the consuming term is NOT +sufficient -- it scales amounts, not signal quality. (3) A validated model EXTRAPOLATES; only +the bench INTERPOLATES -- never print "sustains indefinitely" from a solver row the bench +never flew (the #173 verdict flipped twice on exactly this). Census of the whole class (11 +rows, 2 HIGH fixed, migration checklist for the shared 28 Hz sim-clock): open-questions.md +SSCadence-census + the wf_d875938f journal. diff --git a/context/subsystems.md b/context/subsystems.md index c418ab5..eaec83c 100644 --- a/context/subsystems.md +++ b/context/subsystems.md @@ -475,3 +475,45 @@ edge — this is the gap that would reconcile Oracle's "ran 182 kph sustained" p If field calibration is ever wanted, the knob is the |a| feed's cadence (sample the ring at 28 Hz), NOT the formula constants (byte-exact, #137). Also note the CLIMB term is inert in the port (g=0, EnvironmentZone unwired) — the pod added heat on slopes that we do not. + +**#173 DECISIVE VALVE-BOOST + CADENCE EXPERIMENT (2026-08-13 night17; +`scratchpad/night17/m173v_bench.sh`/`m173v2_bench.sh`/`m173v_check.py`, logs +`content/m173_vul_[ABCEFG]*.log`, captures `content/vlv_*.png`) — the residual above is now +MEASURED and the verdict sharpens:** +- **Valve delivery PORT-FAITHFUL, receipt-PROVEN [T2 live / T1 operands]**: the `BT_VALVE5=N` + hook (mech4.cpp, env-gated) presses Condenser5's MoveValve N times; every landed detent is + receipted (`[valve] Condenser5 valve -> detent 5/50/0` + the zero-sum redistribute lines: + share 0.5 → 0.909091 → 0, others 0.1 → 0.0182 → 0.2, exactly @0049f788's flow=valve/total). + C5 responds as flow dictates (cruise Tc5: balanced 1396 → detent-50 712 → CLOSED 1902 pegged). +- **The bottleneck SHIFTS to the valve-independent stage [T1+T2]**: myomer→Condenser5 + conduction measures 4.1–5.0e4 heat-units/K of dT in EVERY run (balanced/boosted/closed, + both cadences) — constant because @004ad9ec's exponent reads the CALLER's own + flowScale@+0x15C (the myomer's, pinned 1.0), never the condenser's valve share [T1 raw + pseudocode]. The solver's 2.7x boosted whole-chain throughput is CONFIRMED live (4.8e7/s + through the chain at 60fps detent 50) but its "sustains under degT 1000" corollary is + WRONG at any cadence: sub-1000 Tm caps stage-1 flux at ~3e7/s < supercharge generation, so + the governor always engages; the boost buys SPEED at a lower pinned Tm, never sub-1000 Tm. +- **The cadence residual is REAL and ~2x [T2 measured]** (BT_MYO_RING_HZ=28 knob landed in + mech4.cpp — cadence only, constants untouched): at ~59fps the |a| gait-surge noise carries + 64–66% of steady seek-4 generation (|a|≈17–26 flat grass); at 28Hz it drops to 24–32% + (|a|≈6–7) and generation at equal speed HALVES. The six curves (solo vulture seek-4, t1000 / + equilibrium / held v): 60fps bal 12.3s/1800/28 · 60fps d50 12.3s/1675/44 · 60fps CLOSED + 13.9s/NO-EQ-1936-still-climbing/11.7 (drive 0.21, C5 pegged 1902 — the wrap-trap signature) + · 28Hz bal 18.3s/~1710/39–41 · **28Hz d50 22.5s/~1500 STABLE/54–57 = FULL supercharge speed + sustained indefinitely** (equilibrium sits on the soft governor shoulder, zero speed cost; + 500 under failT). **The machine's loop-5 boost genuinely sustained unrestricted seek-4 — the + manual's Super Charged print is vindicated at pod cadence; the port at ~59fps over-punishes + by ~2x. Severity verdict flips to PORT CADENCE DEFECT (fix = 28Hz ring sampling; default + promotion = owners' decision).** Balance verdict unchanged: balanced-valve seek-4 cooks at + BOTH cadences (authentic, manual-warned). +- **Field "3–4s to governor" ≠ spawn preheat [T2]**: field-shaped run (autofire at scenery + from frame 0, no pre-settle) still takes 15.9s to degT from cold; GenD/loop-5 preheat is + real but slow (GenD 666 by 29s). The 3–4s feel is a WARM-START baseline (T≥800 → <3s) plus + the deep 59fps governor making the derate obvious. +- **Heat-MFD valve slider IN SYNC [T2 pixel-verified]**: the vertNormalSlider renders + coolantFlowScale@0x15C — the SAME cell the flow model consumes (single-cell design, no + split possible); measured bar fractions 0.20/0.51/0.90/0.04 for receipt shares + 0.167/0.5/0.909/0.0 (perfectly linear; `vlv_*.png`). Detent 50 (≈90% up) and detent 0 + (bottom) are clearly distinguishable; the residual legibility trap is CLOSED-vs-STARVED + (share 0 vs 0.018–0.1 ≈ 1–2px apart) and the silent 4th-press wrap — "maxed and couldn't + shed" field reports match the run-C wrap signature, not a display desync. diff --git a/docs/WEAPONS_FIELD_GUIDE.txt b/docs/WEAPONS_FIELD_GUIDE.txt index ec7c393..2eb927e 100644 --- a/docs/WEAPONS_FIELD_GUIDE.txt +++ b/docs/WEAPONS_FIELD_GUIDE.txt @@ -121,9 +121,9 @@ THE ORIGINAL'S BEHAVIOR (audited to the byte this week) in this machine. The fast-heavy Clan walkers pay the most. - Gears 1 through 3 make IDENTICAL heat: seek-3 is free speed. Seek-4 roughly doubles drive heat AND raises your top speed -- - a sprint gear. Cruising it on a heavy walker crosses myomer - degradation in about 14 seconds and the governor drags you to a - crawl. A same-weight humanoid at the same speed cooks the same: + a sprint gear. Cruising it on a heavy walker without the boost + crosses myomer degradation in under twenty seconds and the + governor bites. A same-weight humanoid at the same speed cooks the same: it is physics, not a chicken-walker curse. - The machine gave you the tool: BOOST COOLANT LOOP 5 (Generator D / Myomers). One press visibly buys your speed back; a full boost @@ -133,20 +133,28 @@ THE ORIGINAL'S BEHAVIOR (audited to the byte this week) wrap. Two presses, never three. - Standing still sheds heat fast (~10 degrees a second). -OUR PORT TODAY - All of the above, byte-faithful -- and our walkers actually run a - little SLOWER than the machine's printed speeds, so if anything the - pods cooked slightly faster than we do. One open sliver: our - governor may bite a touch harder than you remember (a frame-timing - nuance under study), which the question below settles. +OUR PORT TODAY -- AND A CONFESSION YOUR REPORTS FORCED + You were right that something was off. Our build measured your + mech's acceleration at modern frame rates, and at those rates the + measurement picks up walking-gait jitter as phantom acceleration -- + nearly TWO-THIRDS of your drive heat on a flat cruise was noise + that the original's electronics (sampling at their own slower + clock) never saw. The port was punishing heavy walkers roughly + TWICE as hard as the machine did. -CHANGING IN THE NEXT BUILD - Nothing mechanical. +CHANGING IN THE NEXT BUILD (the fix, measured) + Heat sensing now samples on the original's clock. Result on the + bench: a Vulture at seek-4 WITH the loop-5 boost holds FULL + supercharge speed indefinitely, running hot but stable -- the + valve trick works exactly as the machine intended, and the + manual's printed Super Charged speed is real. WITHOUT the boost, + seek-4 still cooks you (that part is the machine's own balance, + and it stays). The wrap trap also stays: two presses, never three. WE NEED YOUR MEMORY - - At seek-4 on a Vulture or MadCat -- balanced valves, flat ground - -- did your speed sag within ~15 seconds, or could you hold - supercharge speed sustained? + - Next build: boost loop 5, hold seek-4 on a big walker. Does the + sustained supercharge match how the machine felt? Your yes + closes the oldest heat complaint on the books. - Did the loop-5 valve boost trick live in era pilot lore, or are we rediscovering it? diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 2e5a6fc..9ed2609 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -7835,6 +7835,57 @@ void gBTValveKey = (s_vtFrame >= 600 && s_vtFrame < 610) ? 1 : 0; } + // #173 decisive experiment (BT_VALVE5=N): press Condenser5's MoveValve + // exactly N times, spaced BT_VALVE5_SPACING frames (default 30) apart, + // starting at frame BT_VALVE5_AT (default 300) -- walks the detent + // cycle 1->5->50->0 (@4ae464) a CONTROLLED number of steps on the + // MYOMER loop. Dispatches the same ReceiverDataMessageOf + // press payload the pod's engineering-screen aux button delivers; the + // handler's unconditional [valve] receipt proves each landed detent. + // Self-contained (does NOT ride gBTValveKey, so it cannot collide with + // the BT_VALVE_TEST edge detector). Env-gated, off by default. + if ((Entity *)this == application->GetViewpointEntity() + && getenv("BT_VALVE5")) + { + static int s_v5Frame = 0; + static int s_v5Sent = 0; + ++s_v5Frame; + int want = atoi(getenv("BT_VALVE5")); + const char *atEnv = getenv("BT_VALVE5_AT"); + const char *spEnv = getenv("BT_VALVE5_SPACING"); + int startAt = (atEnv != 0) ? atoi(atEnv) : 300; + int spacing = (spEnv != 0) ? atoi(spEnv) : 30; + if (spacing < 2) spacing = 2; // edge needs a gap + if (s_v5Sent < want + && s_v5Frame >= startAt + s_v5Sent * spacing) + { + Subsystem *condenser = 0; + for (int s = 1; s < GetSubsystemCount(); ++s) + { + Subsystem *sub = GetSubsystem(s); + if (sub != 0 && (int)sub->GetClassID() == 0xBBD // Condenser + && sub->GetName() != 0 + && stricmp(sub->GetName(), "Condenser5") == 0) + { + condenser = sub; + break; + } + } + if (condenser != 0) + { + ++s_v5Sent; + DEBUG_STREAM << "[valve-tx] BT_VALVE5 press " << s_v5Sent + << "/" << want << " -> " << condenser->GetName() + << " (frame " << s_v5Frame << ")\n" << std::flush; + ReceiverDataMessageOf msg( + 4 /*Condenser::MoveValveMessageID*/, + sizeof(ReceiverDataMessageOf), + (ControlsButton)1 /*press*/); + condenser->Dispatch(&msg); + } + } + } + // Gitea #6 scripted verify (BT_VIEWCYCLE_TEST=): pulse one // secondary-schematic cycle (Damage -> Critical -> Heat -> Damage) at // the given frame and every 300 frames after -- with @@ -8695,15 +8746,72 @@ void // footstep smoother fed (fed per-stride only, it took 10-20 s to warm // from fill=0: the late-footsteps bug). { + // #173 RING CADENCE -- DEFAULT 28 Hz (promoted 2026-08-13, user call, + // six-curve bench m173v2): push velocity-ring samples at the pod's + // BOARD cadence (28 Hz nominal [T1]) instead of once per render + // frame. The ring / mean-derivative STRUCTURE is untouched + // (byte-faithful); the only change is the sampling clock, which on + // the pod WAS the frame clock (28 Hz) and here is ~60. MEASURED + // [T2]: at ~59fps the |a| operand carries 64-66% ALIASING NOISE + // (|a| 17-26 on flat straight grass); at 28 Hz it drops to 24-32% + // and drive-heat generation HALVES at equal speed -- the port was + // over-punishing every fast-heavy chassis ~2x. With the authored + // loop-5 boost, 28 Hz sustains FULL supercharge speed indefinitely + // at a stable Tm ~1500 (leg G: 54-57 u/s held) -- the machine's + // valve lever worked, and the manual's printed Super Charged stat + // was honest. BT_MYO_RING_HZ= overrides for bracketing + // (=0 or negative restores raw render-cadence sampling). + int ringPush = 1; + Scalar ringDt = dt; + { + static Scalar s_ringHz = -2.0f; + if (s_ringHz < -1.0f) + { + const char *rh = getenv("BT_MYO_RING_HZ"); + s_ringHz = (rh != 0 && *rh != '\0') ? (Scalar)atof(rh) : 28.0f; + } + if (s_ringHz > 0.0f) + { + // per-instance dt accumulator (tiny fixed registry -- bench + // scaffolding; a solo bench has one mech, MP a handful) + static const void *s_raKey[16]; + static Scalar s_raAcc[16]; + int slot = -1, freeSlot = -1; + for (int k = 0; k < 16; ++k) + { + if (s_raKey[k] == (const void *)this) { slot = k; break; } + if (s_raKey[k] == 0 && freeSlot < 0) freeSlot = k; + } + if (slot < 0 && freeSlot >= 0) + { + slot = freeSlot; + s_raKey[slot] = (const void *)this; + s_raAcc[slot] = 0.0f; + } + if (slot >= 0) + { + s_raAcc[slot] += dt; + if (s_raAcc[slot] < 1.0f / s_ringHz) + ringPush = 0; + else + { + ringDt = s_raAcc[slot]; + s_raAcc[slot] = 0.0f; + } + } + } + } + if (ringPush) + { Scalar rdx = localOrigin.linearPosition.x - accelPrevPos.x; Scalar rdy = localOrigin.linearPosition.y - accelPrevPos.y; Scalar rdz = localOrigin.linearPosition.z - accelPrevPos.z; accelPrevPos = localOrigin.linearPosition; - if (dt > 1.0e-4f) + if (ringDt > 1.0e-4f) { - velRingFwd[velRingCursor] = (Scalar)sqrtf(rdx * rdx + rdz * rdz) / dt; - velRingVert[velRingCursor] = rdy / dt; - velRingDt[velRingCursor] = dt; + velRingFwd[velRingCursor] = (Scalar)sqrtf(rdx * rdx + rdz * rdz) / ringDt; + velRingVert[velRingCursor] = rdy / ringDt; + velRingDt[velRingCursor] = ringDt; velRingCursor = (velRingCursor + 1) % 15; } Scalar fwdMean = 0.0f, vertMean = 0.0f, dtMean = 0.0f; @@ -8723,6 +8831,7 @@ void -((fwdMean - accelPrevFwdMean) / dtMean)); accelPrevFwdMean = fwdMean; accelPrevVertMean = vertMean; + } } // (AUDIO_FIDELITY F7) latch the incoming-missile report accumulated by diff --git a/scratchpad/night17/m173v2_bench.sh b/scratchpad/night17/m173v2_bench.sh new file mode 100644 index 0000000..92f3d10 --- /dev/null +++ b/scratchpad/night17/m173v2_bench.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# ========================================================================= +# #173 wave 2 -- the field-feel gap (3-4s to governor vs bench 12-13s) and +# the 28Hz cadence residual, decisive runs: +# E) FIELD-SHAPED spawn: no pre-settle, weapons charging + THROTTLED +# autofire at scenery from the first frame, sprint at seek-4 -- +# time-to-degradation + Condenser5/GeneratorD preheat at motion start. +# F) 28Hz ring cadence (BT_MYO_RING_HZ=28), balanced valves -- does the +# |a| gait-surge term collapse and t1000 stretch? +# G) 28Hz ring cadence + PROVEN detent 50 on Condenser5 (BT_VALVE5=2) -- +# THE FLIP TEST: does boosted seek-4 land UNDER degradation 1000? +# Same rig as m173v_bench.sh (solo expert vulture, grass/day, seek-4 forced, +# full autodrive, 0.12 turn), 165s each. +# ========================================================================= +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +taskkill //F //IM btl4.exe > /dev/null 2>&1 +sleep 2 + +run_one () { # run_one [extra env pairs...] + local TAG=$1 SECS=$2; shift 2 + bt_expert_egg MP.EGG M173V.EGG + sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=vulture/" M173V.EGG + local LOG=m173_${TAG}.log + rm -f "$LOG" + ( export BT_MYO_LOG=1 BT_HEAT_LOG=1 BT_SPEC_LOG=1 BT_MYOMERS_LOG=1 BT_VALVE_LOG=1 + export BT_FORCE_SEEK=3 BT_AUTODRIVE=1.0 BT_FORCE_TURN=0.12 + for kv in "$@"; do export "$kv"; done + bt_launch "$LOG" M173V.EGG 0x03 ) + sleep "$SECS" + bt_kill_ours + sleep 3 + taskkill //F //IM btl4.exe > /dev/null 2>&1 + sleep 2 +} + +run_one vul_E_field 165 BT_AUTOFIRE=1 BT_FIRE_AT_ICON=1 BT_AF_PERIOD=4 BT_FIRE_LOG=1 +run_one vul_F_28bal 165 BT_MYO_RING_HZ=28 +run_one vul_G_28d50 165 BT_MYO_RING_HZ=28 BT_VALVE5=2 + +echo "=================== m173v wave-2 RUNS DONE ===================" +for f in m173_vul_E_field.log m173_vul_F_28bal.log m173_vul_G_28d50.log; do + echo "--- $f" + grep -ac "myotemp" "$f" 2>/dev/null + grep -a "valve-tx\|valve -> detent" "$f" 2>/dev/null +done diff --git a/scratchpad/night17/m173v_bench.sh b/scratchpad/night17/m173v_bench.sh new file mode 100644 index 0000000..3b424b5 --- /dev/null +++ b/scratchpad/night17/m173v_bench.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# ========================================================================= +# #173 DECISIVE EXPERIMENT -- does boosting coolant loop 5's valve sustain +# seek-4 on a Vulture, as the equilibrium solver claims, or does it still +# cook as the field reported ("loop 5 maxed and couldn't shed")? +# +# Rig: the m173 measurement pattern (night16 myo173.sh) -- solo expert +# vulture, grass/day, BT_FORCE_SEEK=3 (seek-4) + BT_AUTODRIVE=1.0 + +# BT_FORCE_TURN=0.12 (stays on-map near top speed), myomer/condenser temp +# receipts under the heat-log envs. +# +# Valve control: the NEW BT_VALVE5=N hook (mech4.cpp) presses Condenser5's +# MoveValve N times, spaced 30 frames, starting frame 300. Every press is +# receipted by the handler's unconditional "[valve] Condenser5 valve -> +# detent D" line, and BT_VALVE_LOG receipts the zero-sum share redistribute +# ("[valve] condenser#5 valveState=V flow=F (total=T)") -- the landed +# detent is PROVEN, not assumed. +# +# Three measurement runs, ~150s sustained seek-4 cruise each, identical +# otherwise: +# A) balanced (no presses) -- expected: degradation ~14s, eq ~1800 +# B) BT_VALVE5=2 (detent 1->5->50) -- THE QUESTION: stays under 1000? +# C) BT_VALVE5=3 (wrap to detent 0) -- expected WORSE than A (share 0) +# Plus one visual run: +# D) idle mech, 3 presses spaced 600 frames, BT_SHOT_EVERY=90 -- captures +# the Heat MFD valve slider at each detent 1/5/50/0 (indicator audit). +# ========================================================================= +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +taskkill //F //IM btl4.exe > /dev/null 2>&1 +sleep 2 +rm -f vlv_*.png + +run_one () { # run_one [extra env pairs...] + local TAG=$1 SECS=$2; shift 2 + bt_expert_egg MP.EGG M173V.EGG + sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=vulture/" M173V.EGG + local LOG=m173_${TAG}.log + rm -f "$LOG" + ( export BT_MYO_LOG=1 BT_HEAT_LOG=1 BT_SPEC_LOG=1 BT_MYOMERS_LOG=1 BT_VALVE_LOG=1 + for kv in "$@"; do export "$kv"; done + bt_launch "$LOG" M173V.EGG 0x03 ) + sleep "$SECS" + bt_kill_ours + sleep 3 + taskkill //F //IM btl4.exe > /dev/null 2>&1 + sleep 2 +} + +DRIVE="BT_FORCE_SEEK=3 BT_AUTODRIVE=1.0 BT_FORCE_TURN=0.12" + +run_one vul_A_bal 165 $DRIVE +run_one vul_B_d50 165 $DRIVE BT_VALVE5=2 +run_one vul_C_d0 165 $DRIVE BT_VALVE5=3 +run_one vul_D_shot 100 BT_VALVE5=3 BT_VALVE5_AT=900 BT_VALVE5_SPACING=600 \ + BT_SHOT_EVERY=90 BT_SHOT_PREFIX=vlv + +echo "=================== m173v RUNS DONE ===================" +for f in m173_vul_A_bal.log m173_vul_B_d50.log m173_vul_C_d0.log m173_vul_D_shot.log; do + echo "--- $f" + grep -ac "myotemp" "$f" 2>/dev/null + grep -a "\[valve" "$f" 2>/dev/null | head -40 +done +ls vlv_*.png 2>/dev/null | head diff --git a/scratchpad/night17/m173v_check.py b/scratchpad/night17/m173v_check.py new file mode 100644 index 0000000..0417d86 --- /dev/null +++ b/scratchpad/night17/m173v_check.py @@ -0,0 +1,68 @@ +# #173 valve experiment digest -- per run: valve receipts (PROOF of detent), +# time-to-degradation-1000, equilibrium T, held speed, C5/bank finals. +import re, sys, statistics as st + +def digest(path): + myotemp = [] # (t, T) + myoheat = [] # (v, a, dt) + myo = [] # speedEffect samples in file order + c5 = []; bank = [] + presses = [] # ([valve-tx] BT_VALVE5 press lines) + detents = [] # ([valve] CondenserN valve -> detent D lines) + redist = [] # ([valve] condenser#N valveState=V flow=F (total=T)) + for ln in open(path, encoding='latin-1', errors='replace'): + m = re.search(r"\[myotemp\] t=([\d.eE+-]+) T=([\d.eE+-]+)", ln) + if m: myotemp.append((float(m.group(1)), float(m.group(2)))); continue + m = re.search(r"\[myoheat\] v=([\d.eE+-]+) a=([\d.eE+-]+) m=[\d.eE+-]+ g=[\d.eE+-]+ ratio=[\d.eE+-]+ dmgGain=[\d.eE+-]+ dt=([\d.eE+-]+)", ln) + if m: myoheat.append(tuple(map(float, m.groups()))); continue + m = re.search(r"\[myo\] Myomers.* speed=([\d.eE+-]+).* gear=(\d+)", ln) + if m: myo.append((float(m.group(1)), int(m.group(2)))); continue + m = re.search(r"\[heat-t\] (\S+)( \(bank\))? T=([\d.eE+-]+)", ln) + if m: + name, isbank, T = m.group(1), m.group(2), float(m.group(3)) + if name == 'Condenser5': c5.append(T) + elif isbank or name == 'HeatSink': bank.append(T) + continue + if '[valve-tx] BT_VALVE5' in ln: presses.append(ln.strip()); continue + m = re.search(r"\[valve\] (Condenser\d+) valve -> detent (\d+)", ln) + if m: detents.append((m.group(1), int(m.group(2)))); continue + m = re.search(r"\[valve\] condenser#(\d) valveState=(\d+) flow=([\d.eE+-]+) \(total=(\d+)\)", ln) + if m: redist.append(tuple(int(x) if i != 2 else float(x) for i, x in enumerate(m.groups()))); continue + + name = path.split('m173_')[-1].replace('.log', '') + print("=" * 78) + print("RUN %s" % name) + if presses: + for p in presses: print(" " + p) + if detents: + print(" detent walk: " + " -> ".join("%s:%d" % d for d in detents)) + # final redistribute state (last 6 lines) + if redist: + last6 = redist[-6:] + print(" final shares: " + " ".join("#%d v=%d f=%.4f" % (n, v, f) for (n, v, f, t) in last6) + + " (total=%d)" % last6[-1][3]) + if not myotemp: + print(" NO MYOTEMP DATA"); return + t_end = myotemp[-1][0] + t1000 = next((t for t, T in myotemp if T >= 1000), None) + t2000 = next((t for t, T in myotemp if T >= 2000), None) + Tpk = max(T for _, T in myotemp) + late = [T for t, T in myotemp if t > t_end - 40.0] + k = max(2, len(myotemp) // 5) + dT = (myotemp[-1][1] - myotemp[-k][1]) / max(1e-6, (myotemp[-1][0] - myotemp[-k][0])) + # pair myoheat with myotemp by index (printed in lockstep) + n = min(len(myoheat), len(myotemp)) + vlate = [myoheat[i][0] for i in range(n) if myotemp[i][0] > t_end - 40.0] + vpk = max(x[0] for x in myoheat) if myoheat else 0.0 + selate = [s for s, g in myo[-20:]] + gears = set(g for s, g in myo) + print(" t1000=%s t2000=%s Tpeak=%.0f eqT(last40s)=%.0f endSlope=%+.2f/s (window %.1fs)" + % ("%.1fs" % t1000 if t1000 else "NEVER", "%.1fs" % t2000 if t2000 else "never", + Tpk, st.mean(late), dT, t_end)) + print(" vPeak=%.1f vHeld(last40s)=%.1f u/s speedEffect(last~20 samples)=%.2f gear(s)=%s" + % (vpk, st.mean(vlate) if vlate else 0.0, st.mean(selate) if selate else 0.0, sorted(gears))) + print(" Condenser5 end T=%.0f bank end T=%.0f" % (c5[-1] if c5 else 0, bank[-1] if bank else 0)) + +if __name__ == '__main__': + for f in sys.argv[1:]: + digest(f)