# SDL: Increased precision for PS4 sensor data conversion

**URL:** <https://discourse.libsdl.org/t/sdl-increased-precision-for-ps4-sensor-data-conversion/43347>\
**Category:** SDL Commits\
**Created:** [March 27, 2023, 9:32pm UTC](https://discourse.libsdl.org/t/sdl-increased-precision-for-ps4-sensor-data-conversion/43347 "2023-03-27T21:32:02Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SDLPushPostBot](https://discourse.libsdl.org/user_avatar/discourse.libsdl.org/sdlpushpostbot/32/4370_2.png) [@SDLPushPostBot](https://discourse.libsdl.org/u/SDLPushPostBot)\
**Post date:** [March 27, 2023, 9:32pm UTC](https://discourse.libsdl.org/t/sdl-increased-precision-for-ps4-sensor-data-conversion/43347/1 "2023-03-27T21:32:03Z")

</div>

> <https://github.com/libsdl-org/SDL/commit/3340864786314d77e04a0dd2bf1ebffbc43bfc31>

```diff
From 3340864786314d77e04a0dd2bf1ebffbc43bfc31 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 27 Mar 2023 14:21:05 -0700
Subject: [PATCH] Increased precision for PS4 sensor data conversion

---
 src/joystick/hidapi/SDL_hidapi_ps4.c | 111 ++++++++++++++++++---------
 1 file changed, 75 insertions(+), 36 deletions(-)

diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index e589b8b9859b..5bd2bdda0710 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -38,8 +38,6 @@
 /* Define this if you want to log calibration data */
 /*#define DEBUG_PS4_CALIBRATION*/
 
-#define GYRO_RES_PER_DEGREE 1024.0f
-#define ACCEL_RES_PER_G 8192.0f
 #define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500
 
 #define LOAD16(A, B) (Sint16)((Uint16)(A) | (((Uint16)(B)) << 8))
@@ -118,7 +116,7 @@ typedef struct
 typedef struct
 {
     Sint16 bias;
- float sensitivity;
+ float scale;
 } IMUCalibrationData;
 
 typedef struct
@@ -145,6 +143,10 @@ typedef struct
     Uint8 led_red;
     Uint8 led_green;
     Uint8 led_blue;
+ Uint16 gyro_numerator;
+ Uint16 gyro_denominator;
+ Uint16 accel_numerator;
+ Uint16 accel_denominator;
     Uint64 sensor_ticks;
     Uint16 last_tick;
     Uint16 valid_crc_packets; /* wrapping counter */
@@ -243,6 +245,11 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
     }
     ctx->device = device;
 
+ ctx->gyro_numerator = 1;
+ ctx->gyro_denominator = 16;
+ ctx->accel_numerator = 1;
+ ctx->accel_denominator = 8192;
+
     device->context = ctx;
 
     if (device->serial && SDL_strlen(device->serial) == 12) {
@@ -314,6 +321,10 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
     if (size == 48 && data[2] == 0x27) {
         Uint8 capabilities = data[4];
         Uint8 device_type = data[5];
+ Uint16 gyro_numerator = LOAD16(data[10], data[11]);
+ Uint16 gyro_denominator = LOAD16(data[12], data[13]);
+ Uint16 accel_numerator = LOAD16(data[14], data[15]);
+ Uint16 accel_denominator = LOAD16(data[16], data[17]);
 
 #ifdef DEBUG_PS4_PROTOCOL
         HIDAPI_DumpPacket("PS4 capabilities: size = %d", data, size);
@@ -357,6 +368,15 @@ static SDL_bool HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
             joystick_type = SDL_JOYSTICK_TYPE_UNKNOWN;
             break;
         }
+
+ if (gyro_numerator && gyro_denominator) {
+ ctx->gyro_numerator = gyro_numerator;
+ ctx->gyro_denominator = gyro_denominator;
+ }
+ if (accel_numerator && accel_denominator) {
+ ctx->accel_numerator = accel_numerator;
+ ctx->accel_denominator = accel_denominator;
+ }
     } else if (device->vendor_id == USB_VENDOR_SONY) {
         ctx->official_controller = SDL_TRUE;
         ctx->sensors_supported = SDL_TRUE;
@@ -411,7 +431,7 @@ static int HIDAPI_DriverPS4_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_
     return -1;
 }
 
-static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
+static SDL_bool HIDAPI_DriverPS4_LoadOfficialCalibrationData(SDL_HIDAPI_Device *device)
 {
     SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
     int i, tries, size;
@@ -422,7 +442,7 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
 #ifdef DEBUG_PS4_CALIBRATION
         SDL_Log("Not an official controller, ignoring calibration\n");
 #endif
- return;
+ return SDL_FALSE;
     }
 
     for (tries = 0; tries < 5; ++tries) {
@@ -432,7 +452,7 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
 #ifdef DEBUG_PS4_CALIBRATION
             SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
 #endif
- return;
+ return SDL_FALSE;
         }
 
         if (device->is_bluetooth) {
@@ -441,7 +461,7 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
 #ifdef DEBUG_PS4_CALIBRATION
                 SDL_Log("Short read of calibration data: %d, ignoring calibration\n", size);
 #endif
- return;
+ return SDL_FALSE;
             }
         }
 
@@ -471,6 +491,7 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
         Sint16 sAccZPlus, sAccZMinus;
 
         float flNumerator;
+ float flDenominator;
         Sint16 sRange2g;
 
 #ifdef DEBUG_PS4_CALIBRATION
@@ -507,36 +528,44 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
         sAccZPlus = LOAD16(data[31], data[32]);
         sAccZMinus = LOAD16(data[33], data[34]);
 
- flNumerator = (sGyroSpeedPlus + sGyroSpeedMinus) * GYRO_RES_PER_DEGREE;
- ctx->calibration[0].bias = sGyroPitchBias;
- ctx->calibration[0].sensitivity = flNumerator / (sGyroPitchPlus - sGyroPitchMinus);
+ flNumerator = (float)(sGyroSpeedPlus + sGyroSpeedMinus) * ctx->gyro_denominator / ctx->gyro_numerator;
+ flDenominator = (float)(SDL_abs(sGyroPitchPlus - sGyroPitchBias) + SDL_abs(sGyroPitchMinus - sGyroPitchBias));
+ if (flDenominator != 0.0f) {
+ ctx->calibration[0].bias = sGyroPitchBias;
+ ctx->calibration[0].scale = flNumerator / flDenominator;
+ }
 
- ctx->calibration[1].bias = sGyroYawBias;
- ctx->calibration[1].sensitivity = flNumerator / (sGyroYawPlus - sGyroYawMinus);
+ flDenominator = (float)(SDL_abs(sGyroYawPlus - sGyroYawBias) + SDL_abs(sGyroYawMinus - sGyroYawBias));
+ if (flDenominator != 0.0f) {
+ ctx->calibration[1].bias = sGyroYawBias;
+ ctx->calibration[1].scale = flNumerator / flDenominator;
+ }
 
- ctx->calibration[2].bias = sGyroRollBias;
- ctx->calibration[2].sensitivity = flNumerator / (sGyroRollPlus - sGyroRollMinus);
+ flDenominator = (float)(SDL_abs(sGyroRollPlus - sGyroRollBias) + SDL_abs(sGyroRollMinus - sGyroRollBias));
+ if (flDenominator != 0.0f) {
+ ctx->calibration[2].bias = sGyroRollBias;
+ ctx->calibration[2].scale = flNumerator / flDenominator;
+ }
 
         sRange2g = sAccXPlus - sAccXMinus;
         ctx->calibration[3].bias = sAccXPlus - sRange2g / 2;
- ctx->calibration[3].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
+ ctx->calibration[3].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g;
 
         sRange2g = sAccYPlus - sAccYMinus;
         ctx->calibration[4].bias = sAccYPlus - sRange2g / 2;
- ctx->calibration[4].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
+ ctx->calibration[4].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g;
 
         sRange2g = sAccZPlus - sAccZMinus;
         ctx->calibration[5].bias = sAccZPlus - sRange2g / 2;
- ctx->calibration[5].sensitivity = 2.0f * ACCEL_RES_PER_G / (float)sRange2g;
+ ctx->calibration[5].scale = (2.0f * ctx->accel_denominator / ctx->accel_numerator) / sRange2g;
 
         ctx->hardware_calibration = SDL_TRUE;
         for (i = 0; i < 6; ++i) {
- float divisor = (i < 3 ? 64.0f : 1.0f);
 #ifdef DEBUG_PS4_CALIBRATION
- SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].sensitivity);
+ SDL_Log("calibration[%d] bias = %d, sensitivity = %f\n", i, ctx->calibration[i].bias, ctx->calibration[i].scale);
 #endif
             /* Some controllers have a bad calibration */
- if ((SDL_abs(ctx->calibration[i].bias) > 1024) || (SDL_fabs(1.0f - ctx->calibration[i].sensitivity / divisor) > 0.5f)) {
+ if (SDL_abs(ctx->calibration[i].bias) > 1024 || SDL_fabs(1.0f - ctx->calibration[i].scale) > 0.5f) {
 #ifdef DEBUG_PS4_CALIBRATION
                 SDL_Log("invalid calibration, ignoring\n");
 #endif
@@ -548,29 +577,39 @@ static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
         SDL_Log("Calibration data not available\n");
 #endif
     }
+ return ctx->hardware_calibration;
 }
 
-static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value)
+static void HIDAPI_DriverPS4_LoadCalibrationData(SDL_HIDAPI_Device *device)
 {
- float result;
-
- if (ctx->hardware_calibration) {
- IMUCalibrationData *calibration = &ctx->calibration[index];
+ SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
+ int i;
 
- result = (value - calibration->bias) * calibration->sensitivity;
- } else if (index < 3) {
- result = value * 64.f;
- } else {
- result = value;
+ if (!HIDAPI_DriverPS4_LoadOfficialCalibrationData(device)) {
+ for (i = 0; i < SDL_arraysize(ctx->calibration); ++i) {
+ ctx->calibration[i].bias = 0;
+ ctx->calibration[i].scale = 1.0f;
+ }
     }
 
- /* Convert the raw data to the units expected by SDL */
- if (index < 3) {
- result = (result / GYRO_RES_PER_DEGREE) * SDL_PI_F / 180.0f;
- } else {
- result = (result / ACCEL_RES_PER_G) * SDL_STANDARD_GRAVITY;
+ /* Scale the raw data to the units expected by SDL */
+ for (i = 0; i < SDL_arraysize(ctx->calibration); ++i) {
+ double scale = ctx->calibration[i].scale;
+
+ if (i < 3) {
+ scale *= ((double)ctx->gyro_numerator / ctx->gyro_denominator) * SDL_PI_D / 180.0;
+ } else {
+ scale *= ((double)ctx->accel_numerator / ctx->accel_denominator) * SDL_STANDARD_GRAVITY;
+ }
+ ctx->calibration[i].scale = (float)scale;
     }
- return result;
+}
+
+static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, int index, Sint16 value)
+{
+ IMUCalibrationData *calibration = &ctx->calibration[index];
+
+ return ((float)value - calibration->bias) * calibration->scale;
 }
 
 static int HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device)

```
