diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index 3b664f8..d7456c2 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -1872,6 +1872,12 @@ void case 3: // leg/body state + stability { Mech__StateUpdateRecord *record = (Mech__StateUpdateRecord *)message; + if (getenv("BT_GAITEV")) + DEBUG_STREAM << "[t3rx] legState=" << record->legState + << " reset=" << record->legResetLatch + << " myLegState=" << (int)legStateAlarm.GetLevel() + << " myLegFrm=" << legAnimation.currentFrame + << " spd=" << record->speedDemand << "\n" << std::flush; { // CLOCK GUARD (keyboard-skip fix): the base reader stamps lastUpdate=Now() // on EVERY record ('HACK' per SIMULATE.cpp:276). Non-pose records carry no diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index ff1a406..08b8e71 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -2124,6 +2124,41 @@ void legCycleSpeed = 0.0f; (void)AdvanceLegAnimation(dt); // joints only; travel = DeadReckon + // GAIT-EVENNESS probe (BT_GAITEV, 1s stats): the metric of the VISIBLE + // speed-change glitch -- per-frame leg-clip advance regularity + leg-state + // transition (flap) rate on the peer. Position dead-reckons smoothly + // regardless; the churn shows in the LEGS. + if (getenv("BT_GAITEV")) + { + static float s_gvPrevF = -1.0f, s_gvAcc = 0.0f, s_gvSum = 0.0f, s_gvMax = 0.0f; + static int s_gvN = 0, s_gvBack = 0, s_gvTrans = 0, s_gvPrevSt = -1; + static float s_gvDemPrev = 0.0f; static int s_gvDemChg = 0; + const float gf = (float)legAnimation.currentFrame; + const int gs = (int)legStateAlarm.GetLevel(); + if (s_gvPrevF >= 0.0f) + { + float df = gf - s_gvPrevF; + if (df < -5.0f) df += 20.0f; // clip wrap heal (approx) + const float adf = (df < 0.0f) ? -df : df; + s_gvSum += adf; if (adf > s_gvMax) s_gvMax = adf; + if (df < 0.0f) s_gvBack++; + s_gvN++; + } + if (s_gvPrevSt >= 0 && gs != s_gvPrevSt) s_gvTrans++; + if ((float)bodyTargetSpeed != s_gvDemPrev) { s_gvDemChg++; s_gvDemPrev = (float)bodyTargetSpeed; } + s_gvPrevF = gf; s_gvPrevSt = gs; s_gvAcc += dt; + if (s_gvAcc >= 1.0f && s_gvN > 0) + { + DEBUG_STREAM << "[gaitev] frmAvg=" << (s_gvSum / s_gvN) + << " frmMax=" << s_gvMax + << " back=" << s_gvBack << "/" << s_gvN + << " stateFlips/s=" << s_gvTrans + << " demChanges/s=" << s_gvDemChg + << " state=" << gs << "\n" << std::flush; + s_gvAcc = 0.0f; s_gvSum = 0.0f; s_gvMax = 0.0f; s_gvN = 0; s_gvBack = 0; s_gvTrans = 0; s_gvDemChg = 0; + } + } + // TURN-STEP rate probe (BT_TRNRATE, 0.5s): body rotation rate vs trn clip advance. if (getenv("BT_TRNRATE")) { @@ -2522,6 +2557,35 @@ void : (r < 1.0f ? sAutoDrive * r : sAutoDrive); } } + // BT_DRIVE_SWEEP=: triangle-sweep the FORCED throttle + // 0.2..0.9 -- sweeps the ACTUAL drive velocity AND (via mechmppr's + // forced key_throttle) the mapper demand together, the true + // keyboard accel/decel regime. BT_FORCE_STEP=1 quantizes it to + // the RIO Ranger 0.05 grid (the authentic stepped input shape). + { + static float s_dsP = -1.0f, s_dsClock = 0.0f; + static int s_dsStep = -1; + if (s_dsP < 0.0f) + { + const char *v = getenv("BT_DRIVE_SWEEP"); + s_dsP = v ? (float)atof(v) : 0.0f; + s_dsStep = getenv("BT_FORCE_STEP") ? 1 : 0; + } + if (s_dsP > 0.0f) + { + s_dsClock += dt; + float ph = fmodf(s_dsClock, 2.0f * s_dsP) / s_dsP; + float tri = (ph < 1.0f) ? ph : (2.0f - ph); + // BT_DRIVE_SWEEP0=1: sweep 0.0..0.7 (through STOP each cycle -- the + // stand<->walk gauntlet: walk-entry, came-to-rest sends, type-3 stomps). + static int s_dsZero = -1; + if (s_dsZero < 0) s_dsZero = getenv("BT_DRIVE_SWEEP0") ? 1 : 0; + float thr = s_dsZero ? (0.7f * tri) : (0.2f + 0.7f * tri); + if (s_dsStep) + thr = floorf(thr / 0.05f + 0.5f) * 0.05f; + gBTDrive.forcedThrottle = thr; + } + } } else if (sGotoDrive) { @@ -2580,8 +2644,37 @@ void else sStick = 0.0f; } - gBTDrive.throttle = sLever; - gBTDrive.turn = sStick; + // RIO-GRID PUBLISH (authentic input shaping; decomp workflow w0odszxro): + // the pod's analog lever/stick reached the mapper through the RIO Ranger + // (800 ADC counts, 0.05 deadband -- L4RIO.cpp:801), so the DEMAND the 1995 + // machinery consumed moved in coarse, settled STEPS with silence between + // them -- one type-2 speed record per step, and each gait threshold + // (standSpeed etc., zero hysteresis) crossed ONCE per maneuver. Our + // keyboard integrator sweeps CONTINUOUSLY (kLeverRate every frame of a + // key-hold): ~86 records per accel, demand DWELLING on the knife edges -> + // transition-clip churn -- the speed-change peer glitch. Publish on the + // Ranger's own 0.05 grid: internal lever feel unchanged, published demand + // steps like the real hardware (a bucket holds ~4 frames at kLeverRate, so + // the send gate self-closes between steps). Also kills -epsilon reverse + // publishes from rest (bucket rounds to 0 until the lever passes -0.025; + // authentic reverse is a BUTTON sign-flip, never a sweep through zero). + // DEFAULT: CONTINUOUS publish (authentic -- the pod lever was analog, + // 800-count ADC ~= continuous; empirical A/B found NO gait/position + // difference between swept and stepped demand after the clock-guard + + // heading-integrator fixes, so the grid gains nothing and makes the + // DRIVE step unauthentically). BT_GRID_LEVER=1 keeps the 0.05-grid + // variant for experiments. + static const int s_gridLever = getenv("BT_GRID_LEVER") ? 1 : 0; + if (s_gridLever) + { + gBTDrive.throttle = floorf(sLever / 0.05f + 0.5f) * 0.05f; + gBTDrive.turn = floorf(sStick / 0.05f + 0.5f) * 0.05f; + } + else + { + gBTDrive.throttle = sLever; + gBTDrive.turn = sStick; + } } // DEATH GATE (task #52): a DESTROYED mech takes no pilot input -- kill diff --git a/game/reconstructed/mechmppr.cpp b/game/reconstructed/mechmppr.cpp index 91199c1..6b946a2 100644 --- a/game/reconstructed/mechmppr.cpp +++ b/game/reconstructed/mechmppr.cpp @@ -665,6 +665,13 @@ void float ph = fmodf(s_swpClock, 2.0f * s_swpP) / s_swpP; // 0..2 float tri = (ph < 1.0f) ? ph : (2.0f - ph); // 0..1..0 key_throttle = 0.2f + 0.7f * tri; + // BT_FORCE_STEP=1: quantize the swept throttle to the RIO + // Ranger 0.05 grid (the authentic ADC/deadband step) -- the + // stepped-vs-continuous A/B for the speed-change glitch. + static int s_swpStep = -1; + if (s_swpStep < 0) s_swpStep = getenv("BT_FORCE_STEP") ? 1 : 0; + if (s_swpStep) + key_throttle = floorf(key_throttle / 0.05f + 0.5f) * 0.05f; } } // BT_FORCE_FLIP=: invert the forced throttle after t sim-seconds