#96: FIX -- normalise the myomer kinetic heat term to a reference frame rate
The previous commit only instrumented. This is the behavioural change.
The binary's drive-heat integrator (FUN_004b8d18) multiplies its climb and accel
terms by the time slice but NOT the kinetic one:
heat += ratio^2 * (1+dmg) * [ (1-accEff)*|vy|*m*g*dt
+ (1-velEff)*(0.5*m*|v|^2) <-- no dt
+ (1-accEff)*|v|*|a|*m*dt ]
A per-TICK energy is a constant on a fixed-frame 1995 machine and a variable on a
modern PC: heat/second scales with frame rate, so two players on different
hardware -- or one player in a heavy scene versus an empty one -- get different
heat from identical throttle. That is a defect independent of what the pod's
rate actually was.
The kinetic term is now multiplied by (dt * kPodFrameHz), which is exactly 1.0
when dt == 1/30 and makes the result machine-independent. This is the one
deliberate divergence from a verbatim transcription of @004b8d18, and it is
marked as such in the source.
Measured, Owens at seek 4 flat out:
before crossed degradeT(1000) at ~27s, peaked 1213, settled ~710
after peak 1007, settled ~525
i.e. roughly halved at our measured ~59Hz, which is the direction the players
asked for ("too much IMO", "def need to tone down the myomer heat some").
⚠ CALIBRATION IS NOT PROVEN. 30Hz is the i860 BOARD frame ([T1],
rendering.md:177); the HOST simulation rate is not separately established, and no
fixed-timestep constant exists in the image (searched 1/15..1/60 and 30/60 Hz --
the arcade computed dt from a clock, as we do). So the reference rate is the
best-documented value, not a proven one. BT_MYO_HZ overrides it with no rebuild
so it can be calibrated by feel.
Precedent for the bug class, already logged in the KB: "trail density is
frame-rate-dependent (2/frame @60fps = 2x pod density)" (rendering.md:222).
NOT settled: whether the per-chassis ordering matches player memory (a Thor
sustaining seek 4 where a light chicken-walker cannot). Post-fix benches put the
Thor HOTTER (crosses at 40s vs the Owens at 47s), but those runs are BT_GOTO
path-driven and the acceleration term is large and path-dependent, so they are
too noisy to conclude from. The cooling data being identical across chassis IS
solid (measured, previous commit).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9dd7d48b41
commit
ee079083a3
@@ -745,10 +745,38 @@ void Myomers::MyomersDriveHeat(Scalar time_slice)
|
||||
// accTerm = m |v| |a| dt (acceleration power)
|
||||
Scalar work = mass * (velMag * velMag) * 0.5f; // dot(v,v)*0.5 (_DAT_004b8ee4)
|
||||
|
||||
// The three terms, kept separate for diagnosis (#96 "myomers run too hot"):
|
||||
// two are POWER (x dt), one is an ENERGY added per TICK.
|
||||
// The three terms (#96 "myomers run too hot"). Two are POWER (x dt); the
|
||||
// kinetic one is an ENERGY the binary adds once per TICK with NO dt
|
||||
// (FUN_004b8d18: param_2 multiplies the climb and accel terms only).
|
||||
//
|
||||
// FRAME-RATE NORMALISATION. A per-tick energy makes heat/SECOND scale with
|
||||
// the tick rate, which on a 1995 fixed-frame machine is a constant and on a
|
||||
// modern PC is not: two players on different hardware, or the same player in
|
||||
// a heavy scene, get different heat from identical throttle. Measured here
|
||||
// at ~59Hz the myomers cross their degradation threshold in ~27s of seek-4
|
||||
// cruising; at the pod's documented 30Hz board frame that is ~54s.
|
||||
//
|
||||
// So the term is rescaled to the reference frame rate: multiply by
|
||||
// dt * kPodFrameHz, which is 1.0 exactly when dt == 1/30 and makes the heat
|
||||
// identical on every machine. This is the ONE deliberate divergence from a
|
||||
// verbatim transcription of @004b8d18, and it exists because the binary's
|
||||
// value depends on a constant our port does not have.
|
||||
//
|
||||
// ⚠ 30Hz is the i860 board frame ([T1], rendering.md) -- the HOST simulation
|
||||
// rate is not separately established, so treat this as the reference, not as
|
||||
// proven. BT_MYO_HZ overrides it without a rebuild so the pod veterans can
|
||||
// calibrate it by feel.
|
||||
static const Scalar kPodFrameHz = 30.0f;
|
||||
static Scalar s_hz = -1.0f;
|
||||
if (s_hz < 0.0f)
|
||||
{
|
||||
const char *hv = getenv("BT_MYO_HZ");
|
||||
s_hz = (hv && *hv) ? (Scalar)atof(hv) : kPodFrameHz;
|
||||
if (s_hz <= 0.0f) s_hz = kPodFrameHz;
|
||||
}
|
||||
|
||||
Scalar termClimb = velComplement * vy * mass * gravity * time_slice;
|
||||
Scalar termKinetic = workComplement * work; // <-- no dt, per the binary
|
||||
Scalar termKinetic = workComplement * work * (time_slice * s_hz); // rate-normalised
|
||||
Scalar termAccel = velComplement * velMag * accMag * mass * time_slice;
|
||||
Scalar gain = ratio * ratio * damageGain;
|
||||
|
||||
@@ -757,9 +785,17 @@ void Myomers::MyomersDriveHeat(Scalar time_slice)
|
||||
if (getenv("BT_MYO_LOG"))
|
||||
{
|
||||
static int s_n = 0;
|
||||
static Scalar s_elapsed = 0.0f;
|
||||
s_elapsed += time_slice; // wall clock since the sim started
|
||||
if ((s_n++ % 120) == 0)
|
||||
{
|
||||
Scalar sum = termClimb + termKinetic + termAccel;
|
||||
// T and the clock: how long does seek 4 actually take to cook the
|
||||
// myomers? (#96 -- the players' claim is "under a minute".)
|
||||
DEBUG_STREAM << "[myotemp] t=" << s_elapsed
|
||||
<< " T=" << currentTemperature
|
||||
<< " degradeT=" << degradationTemperature
|
||||
<< " failT=" << failureTemperature << "\n" << std::flush;
|
||||
DEBUG_STREAM << "[myoheat] v=" << velMag << " a=" << accMag
|
||||
<< " m=" << mass << " g=" << gravity
|
||||
<< " ratio=" << ratio << " dmgGain=" << damageGain
|
||||
|
||||
Reference in New Issue
Block a user