From 34616d1b009e666cbb6fb17af11dc967ec0f8957 Mon Sep 17 00:00:00 2001 From: Aubrey Hesselgren Date: Tue, 22 Jul 2025 11:01:57 -0700 Subject: [PATCH] A little more tidying. Better notes around how the absolute maximum threshold was arrived at. --- test/gamepadutils.c | 25 +++++++++++-------------- test/gamepadutils.h | 7 +++++-- test/testcontroller.c | 18 ++++++++---------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/test/gamepadutils.c b/test/gamepadutils.c index de2a6fffc9..21206b071f 100644 --- a/test/gamepadutils.c +++ b/test/gamepadutils.c @@ -236,7 +236,7 @@ void DrawGyroDebugAxes(SDL_Renderer *renderer, const Quaternion *orientation, co SDL_RenderLine(renderer, origin_screen.x, origin_screen.y, up_screen.x, up_screen.y); SDL_SetRenderDrawColor(renderer, GYRO_COLOR_BLUE); SDL_RenderLine(renderer, origin_screen.x, origin_screen.y, back_screen.x, back_screen.y); - + /* Restore current color */ SDL_SetRenderDrawColor(renderer, r, g, b, a); } @@ -1053,7 +1053,7 @@ GyroDisplay *CreateGyroDisplay(SDL_Renderer *renderer) ctx->next_reported_sensor_time = 0; ctx->current_calibration_phase = GYRO_CALIBRATION_PHASE_OFF; ctx->calibration_phase_progress_fraction = 0.0f; /* [0..1] */ - ctx->accelerometer_noise_sq = 0.0f; + ctx->accelerometer_noise_sq = 0.0f; ctx->accelerometer_noise_tolerance_sq = ACCELEROMETER_NOISE_THRESHOLD; /* Will be overwritten but this avoids divide by zero. */ ctx->reset_gyro_button = CreateGamepadButton(renderer, "Reset View"); ctx->calibrate_gyro_button = CreateGamepadButton(renderer, "Recalibrate Drift"); @@ -1678,7 +1678,6 @@ void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad) SDLTest_DrawString(ctx->renderer, x + center - SDL_strlen(text) * FONT_CHARACTER_SIZE, y, text); SDL_snprintf(text, sizeof(text), "[%.2f,%.2f,%.2f]%s/s", ctx->gyro_data[0] * RAD_TO_DEG, ctx->gyro_data[1] * RAD_TO_DEG, ctx->gyro_data[2] * RAD_TO_DEG, DEGREE_UTF8); SDLTest_DrawString(ctx->renderer, x + center + 2.0f, y, text); - /* Display the testcontroller tool's evaluation of drift. This is also useful to get an average rate of turn in calibrated turntable tests. */ if (ctx->gyro_drift_correction_data[0] != 0.0f && ctx->gyro_drift_correction_data[2] != 0.0f && ctx->gyro_drift_correction_data[2] != 0.0f ) @@ -1758,7 +1757,6 @@ void RenderGyroDriftCalibrationButton(GyroDisplay *ctx, GamepadDisplay *gamepad_ const float new_line_height = gamepad_display->button_height + 2.0f; GamepadButton *start_calibration_button = GetGyroCalibrateButton(ctx); - /* Show the recalibration progress bar. */ float recalibrate_button_width = GetGamepadButtonLabelWidth(start_calibration_button) + 2 * BUTTON_PADDING; SDL_FRect recalibrate_button_area; @@ -1784,14 +1782,12 @@ void RenderGyroDriftCalibrationButton(GyroDisplay *ctx, GamepadDisplay *gamepad_ SetGamepadButtonLabel(start_calibration_button, label_text); SetGamepadButtonArea(start_calibration_button, &recalibrate_button_area); - RenderGamepadButton(start_calibration_button); + RenderGamepadButton(start_calibration_button); bool bExtremeNoise = ctx->accelerometer_noise_sq > ACCELEROMETER_MAX_NOISE_G_SQ; /* Explicit warning message if we detect too much movement */ if (ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_OFF) { - - if (bExtremeNoise) - { + if (bExtremeNoise) { SDL_strlcpy(label_text, "GamePad Must Be Still", sizeof(label_text)); SDLTest_DrawString(ctx->renderer, recalibrate_button_area.x, recalibrate_button_area.y + recalibrate_button_area.h + new_line_height, label_text); SDL_strlcpy(label_text, "Place GamePad On Table", sizeof(label_text)); @@ -1799,12 +1795,14 @@ void RenderGyroDriftCalibrationButton(GyroDisplay *ctx, GamepadDisplay *gamepad_ } } - if (ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_NOISE_PROFILING - || ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_DRIFT_PROFILING) + if (ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_NOISE_PROFILING || + ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_DRIFT_PROFILING) { float flAbsoluteNoiseFraction = SDL_clamp(ctx->accelerometer_noise_sq / ACCELEROMETER_MAX_NOISE_G_SQ, 0.0f, 1.0f); float flAbsoluteToleranceFraction = SDL_clamp(ctx->accelerometer_noise_tolerance_sq / ACCELEROMETER_MAX_NOISE_G_SQ, 0.0f, 1.0f); - float flRelativeNoiseFraction = SDL_clamp(ctx->accelerometer_noise_sq / ctx->accelerometer_noise_tolerance_sq, 0.0f, 1.0f); + + float flMaxNoiseForThisPhase = ctx->current_calibration_phase == GYRO_CALIBRATION_PHASE_NOISE_PROFILING ? ACCELEROMETER_MAX_NOISE_G_SQ : ctx->accelerometer_noise_tolerance_sq; + float flRelativeNoiseFraction = SDL_clamp(ctx->accelerometer_noise_sq / flMaxNoiseForThisPhase, 0.0f, 1.0f); float noise_bar_height = gamepad_display->button_height; SDL_FRect noise_bar_rect; @@ -1813,7 +1811,7 @@ void RenderGyroDriftCalibrationButton(GyroDisplay *ctx, GamepadDisplay *gamepad_ noise_bar_rect.w = recalibrate_button_area.w; noise_bar_rect.h = noise_bar_height; - SDL_snprintf(label_text, sizeof(label_text), "Noise Tolerance: %3.3fG ", SDL_sqrtf(ctx->accelerometer_noise_tolerance_sq) ); + SDL_snprintf(label_text, sizeof(label_text), "Accelerometer Noise Tolerance: %3.3fG ", SDL_sqrtf(ctx->accelerometer_noise_tolerance_sq) ); SDLTest_DrawString(ctx->renderer, recalibrate_button_area.x, recalibrate_button_area.y + recalibrate_button_area.h + new_line_height * 2, label_text); /* Adjust the noise bar rectangle based on the accelerometer noise value */ @@ -1837,7 +1835,7 @@ void RenderGyroDriftCalibrationButton(GyroDisplay *ctx, GamepadDisplay *gamepad_ tolerance_bar_rect.w = tolerance_bar_fill_width; tolerance_bar_rect.h = noise_bar_height; - SDL_SetRenderDrawColor(ctx->renderer, 128, 128, 0, 255); + SDL_SetRenderDrawColor(ctx->renderer, 128, 128, 0, 255); SDL_RenderRect(ctx->renderer, &tolerance_bar_rect); /* draw the tolerance rectangle */ SDL_SetRenderDrawColor(ctx->renderer, 100, 100, 100, 255); /* gray box */ @@ -1985,7 +1983,6 @@ void RenderGyroDisplay(GyroDisplay *ctx, GamepadDisplay *gamepadElements, SDL_Ga float bottom = RenderEulerReadout(ctx, gamepadElements); RenderGyroGizmo(ctx, gamepad, bottom); } - SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a); } diff --git a/test/gamepadutils.h b/test/gamepadutils.h index c829c376e1..19eeefcff5 100644 --- a/test/gamepadutils.h +++ b/test/gamepadutils.h @@ -143,9 +143,12 @@ extern void DestroyGamepadButton(GamepadButton *ctx); /* Gyro element Display */ -/* This is used as the initial noise tolernace threshold. It's set very close to zero to avoid divide by zero while we're evaluating the noise profile. Each controller may have a very different noise profile.*/ +/* This is used as the initial noise tolerance threshold. It's set very close to zero to avoid divide by zero while we're evaluating the noise profile. Each controller may have a very different noise profile.*/ #define ACCELEROMETER_NOISE_THRESHOLD 1e-6f -#define ACCELEROMETER_MAX_NOISE_G_SQ ( 0.125f * 0.125f ) +/* The value below is based on observation of a Dualshock controller. Of all gamepads observed, the Dualshock (PS4) tends to have one of the noisiest accelerometers. Increase this threshold if a controller is failing to pass the noise profiling stage while stationary on a table. */ +#define ACCELEROMETER_MAX_NOISE_G 0.075f +#define ACCELEROMETER_MAX_NOISE_G_SQ (ACCELEROMETER_MAX_NOISE_G * ACCELEROMETER_MAX_NOISE_G) + /* Gyro Calibration Phases */ typedef enum { diff --git a/test/testcontroller.c b/test/testcontroller.c index 2c41e01d8f..6b0fdea27f 100644 --- a/test/testcontroller.c +++ b/test/testcontroller.c @@ -211,7 +211,7 @@ void ResetGyroOrientation(IMUState *imustate) } /* More time = more accurate drift correction*/ -#define SDL_GAMEPAD_IMU_NOISE_SETTLING_PERIOD_NS (1 * SDL_NS_PER_SECOND) +#define SDL_GAMEPAD_IMU_NOISE_SETTLING_PERIOD_NS ( SDL_NS_PER_SECOND / 2) #define SDL_GAMEPAD_IMU_NOISE_EVALUATION_PERIOD_NS (4 * SDL_NS_PER_SECOND) #define SDL_GAMEPAD_IMU_NOISE_PROFILING_PHASE_DURATION_NS (SDL_GAMEPAD_IMU_NOISE_SETTLING_PERIOD_NS + SDL_GAMEPAD_IMU_NOISE_EVALUATION_PERIOD_NS) #define SDL_GAMEPAD_IMU_CALIBRATION_PHASE_DURATION_NS (5 * SDL_NS_PER_SECOND) @@ -222,12 +222,11 @@ void ResetGyroOrientation(IMUState *imustate) void CalibrationPhase_NoiseProfiling(IMUState *imustate) { /* If we have really large movement (i.e. greater than a fraction of G), then we want to start noise evaluation over. The frontend will warn the user to put down the controller. */ - const float flAbsoluteMaxAccelerationG = 0.125f; - if (imustate->accelerometer_length_squared > (flAbsoluteMaxAccelerationG * flAbsoluteMaxAccelerationG) ) { + if (imustate->accelerometer_length_squared > ACCELEROMETER_MAX_NOISE_G_SQ) { BeginNoiseCalibrationPhase(imustate); return; } - + Uint64 now = SDL_GetTicksNS(); Uint64 delta_ns = now - imustate->calibration_phase_start_time_ticks_ns; @@ -1433,7 +1432,7 @@ static void HandleGamepadGyroEvent(SDL_Event *event) /* Two strategies for evaluating polling rate - one based on a fixed packet count, and one using a fixed time window. * Smaller values in either will give you a more responsive polling rate estimate, but this may fluctuate more. * Larger values in either will give you a more stable average but they will require more time to evaluate. - * Generally, wired connections tend to give much more stable + * Generally, wired connections tend to give much more stable */ /* #define SDL_USE_FIXED_PACKET_COUNT_FOR_ESTIMATION */ #define SDL_GAMEPAD_IMU_MIN_POLLING_RATE_ESTIMATION_COUNT 2048 @@ -1479,7 +1478,7 @@ static void UpdateGamepadOrientation( Uint64 delta_time_ns ) static void HandleGamepadSensorEvent( SDL_Event* event ) { if (!controller) - return; + return; if (controller->id != event->gsensor.which) return; @@ -1504,7 +1503,7 @@ static void HandleGamepadSensorEvent( SDL_Event* event ) /* Show how far we are through the current phase. When off, just default to zero progress */ Uint64 now = SDL_GetTicksNS(); - float duration = 0.0f; + Uint64 duration = 0; if (controller->imu_state->calibration_phase == GYRO_CALIBRATION_PHASE_NOISE_PROFILING) { duration = SDL_GAMEPAD_IMU_NOISE_PROFILING_PHASE_DURATION_NS; } else if (controller->imu_state->calibration_phase == GYRO_CALIBRATION_PHASE_DRIFT_PROFILING) { @@ -1512,7 +1511,7 @@ static void HandleGamepadSensorEvent( SDL_Event* event ) } Uint64 delta_ns = now - controller->imu_state->calibration_phase_start_time_ticks_ns; - float drift_calibration_progress_frac = duration > 0.0f ? ((float)delta_ns / (float)duration) : 0.0f; + float drift_calibration_progress_fraction = duration > 0.0f ? ((float)delta_ns / (float)duration) : 0.0f; int reported_polling_rate_hz = sensorTimeStampDelta_ns > 0 ? (int)(SDL_NS_PER_SECOND / sensorTimeStampDelta_ns) : 0; @@ -1524,10 +1523,9 @@ static void HandleGamepadSensorEvent( SDL_Event* event ) reported_polling_rate_hz, controller->imu_state->imu_estimated_sensor_rate, controller->imu_state->calibration_phase, - drift_calibration_progress_frac, + drift_calibration_progress_fraction, controller->imu_state->accelerometer_length_squared, controller->imu_state->accelerometer_tolerance_squared - ); /* Also show the gyro correction next to the gyro speed - this is useful in turntable tests as you can use a turntable to calibrate for drift, and that drift correction is functionally the same as the turn table speed (ignoring drift) */