1

What does this wizard actually do? Does it create dead zones and store them in the registry? Does it map raw data to upper and lower bounds somewhere?

Is this exclusively a software calibration or does it send data back to the device?

I'm wondering if the output of this wizard can be identified so that calibration can be made available from within my application.

2
  • I can find no info on this but willing to bet it is a combo of software and registry.
    – Moab
    Sep 26, 2019 at 21:54
  • 1
    Not an answer, but it would seem like "common sense" that it would map a particular "output level" from the controller to whatever commands are being sent by Windows itself. Likewise, it seems unlikely any data would be sent back to the controller. Sep 26, 2019 at 22:43

2 Answers 2

1

From my recent exploration of the Windows built-in joystick controller calibration tool, I can confirm the following behaviour on a PS4 joystick (results should be similar for other controllers):

  • After going through the calibration wizard, the following registries are written with the calibrated data, HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput\<DEVICE_ID>\Calibration\0\Type\Axes\<NUM>, where <NUM> is the axis number.

  • For a PS4 joystick, Windows reads six axis and therefore six registry key values are created. The numbering of the axes are as follows:

    • 0 (x) --> Left analog stick horizontal motion.
    • 1 (y) --> Left analog stick vertical motion.
    • 2 (z) --> Right analog horizontal motion.
    • 3 (Rx) --> L2 trigger.
    • 4 (Ry) --> R2 trigger.
    • 5 (Rz) --> Right analog vertical motion.
  • The format of the registry keys is Calibration mapped to a 12 byte binary value (encoded as <MIN> <MID> <MAX>). For instance, my Axis 0 (x) has value: <00 00 00 00> <80 00 00 00> <ff 00 00 00> which translates to

    • Minimum Axis 0 value = <00 00 00 00> = 0 (decimal)
    • Middle Axis 0 value = <80 00 00 00> = 128 (decimal)
    • Maximum Axis 0 value <ff 00 00 00> = 255 (decimal)
  • The calibration that Windows does is convert the physical values into a calibrated range (min, mid, max values from above). For instance, suppose my defective left analog stick reads from 10-100 for horizontal movement (x-axis) when it should be range from 0-255. Then I can set the min/max values to 10 and 100 respectively to calibrate for the defective analog stick.

  • There doesn't seem to be a specific setting for deadzones, so I assume this is an implementation detail left for the application above (e.g. For a game, defined as part of game logic code).

  • Since the calibration settings are stored in registry, calibration is not persistent across different machines.

As for your particular case, you might want to consider using the some Windows API to read the values (e.g. XInput, UWP API). As a bonus, here is some of my buggy that reads controller input using Windows XInput API.

#pragma comment(lib,"XInput.lib")
#pragma comment(lib,"Xinput9_1_0.lib")

#include <iostream>
#include <roapi.h>
#include <Xinput.h>


XINPUT_STATE fetchAConnectedJoystick() 
{
    DWORD dwResult;    
    XINPUT_STATE state;

    for (DWORD i=0; i < XUSER_MAX_COUNT; i++)
    {
        ZeroMemory(&state, sizeof(XINPUT_STATE));

        // Simply get the state of the controller from XInput.
        dwResult = XInputGetState(i, &state);

        // Controller is connected
        if(dwResult == ERROR_SUCCESS)
        {
            return state;
        }
    }

    std::exit;
}


void printLeftAnalogStickReadings(XINPUT_STATE state) 
{
    float LX = state.Gamepad.sThumbLX;
    float LY = state.Gamepad.sThumbLY;
    std::cout << "(X=" << LX << ", Y=" << LY << ")\n";
}


int computeAdjustedMagnitude(XINPUT_STATE state) 
{
    int INPUT_DEADZONE = 42;

    float LX = state.Gamepad.sThumbLX;
    float LY = state.Gamepad.sThumbLY;

    float magnitude = sqrt(LX*LX + LY*LY);              // Determine how far the controller is pushed

    // Determine the direction the controller is pushed
    float normalizedLX = LX / magnitude;
    float normalizedLY = LY / magnitude;

    if (magnitude > INPUT_DEADZONE)                     // Check if the controller is outside a circular dead zone
    {
        if (magnitude > 32767) magnitude = 32767;       // Clip the magnitude at its expected maximum value
        magnitude -= INPUT_DEADZONE;                    // Adjust magnitude relative to the end of the dead zone

    }
    else                                                // If the controller is in the deadzone zero out the magnitude
    {
        magnitude = 0.0;
    }
    return magnitude;
}


int main()
{
    XINPUT_STATE state;
    int sleepDuration = 100;                            // Milliseconds
    int adjustedMag = 0;

    while (true) {
        state = fetchAConnectedJoystick();
        printLeftAnalogStickReadings(state);
//      adjustedMag = computeAdjustedMagnitude(state);
//      std::this_thread::sleep_for(std::chrono::milliseconds(sleepDuration));
    }
}
1
0

I believe the calibration information is stored in the registry at:

HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\DirectInput\<DEVICE>\Calibration

I don't know what the format of the entries are but it isn't human-readable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.