Diag: speed-change investigation -- authentic analog throttle CONFIRMED, harnesses + gait probes (task #50)
Decomp workflow w0odszxro settled the speed model: the pod throttle was ANALOG- CONTINUOUS (RIO Ranger, 800 ADC counts, 0.05 deadband; sole detent = snap-to-1.0 within 0.05 @004d196c). '5 speeds' is FALSE -- the 1-5 keys are MFD mode pages; the 0..5 stepper is HUD/radar zoom. BONUS: throttleState@0x4a4 is a MISNOMER -- binary census proves it is the fall-contact surface material cache (0..7, init 2=Concrete), written only at knockdown; rename pending. Empirical rule-outs (autonomous drive-sweep harness, this commit): - Record density: with the clock guard + incremental heading in, a per-frame continuous demand sweep measures IDENTICAL to constant throttle on position evenness ([repljit]), render heading ([rendhdg]), and gait cadence ([gaitev]). - Type-3 stomps: ~0 fire in sustained sweeps (only at launch) -- not the driver. - The 0.05-grid keyboard publish experiment is retained env-gated OFF (BT_GRID_LEVER) -- the ADC was ~continuous, so the grid is NOT authentic and gains nothing measurable; default publish stays continuous (authentic). New tooling: BT_DRIVE_SWEEP[0] (forced-drive triangle sweep, optional through- zero), BT_FORCE_STEP (0.05-grid variant), BT_GAITEV (per-frame leg-clip advance + state-flip + demand-change stats), [t3rx] (type-3 stomp trace). STATUS: user still reports visible speed-change glitches in interactive play; all harness metrics saturate at baseline -- next step is probes ON the user's interactive session (their eyes + instruments on the same run). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
93456be051
commit
2c6db6a2de
@@ -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=<period>: 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
|
||||
|
||||
Reference in New Issue
Block a user