Skip to content

Commit

Permalink
Add Analog Smoothing to thumbsticks (#1122)
Browse files Browse the repository at this point in the history
* initial commit

* fix web, better precision
  • Loading branch information
NickGuyver authored Sep 13, 2024
1 parent 6b7552c commit 1848523
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 2 deletions.
9 changes: 9 additions & 0 deletions headers/addons/analog.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
#define AUTO_CALIBRATE_ENABLED 0
#endif

#ifndef ANALOG_SMOOTHING_ENABLED
#define ANALOG_SMOOTHING_ENABLED 0
#endif

#ifndef SMOOTHING_FACTOR
#define SMOOTHING_FACTOR 5
#endif

// Analog Module Name
#define AnalogName "Analog"

Expand All @@ -78,6 +86,7 @@ class AnalogInput : public GPAddon {
uint16_t adc_2_y_center = 0;

static float readPin(int pin, uint16_t center, bool autoCalibrate);
static float emaCalculation(float ema_value, float smoothing_factor, float ema_previous);
static uint16_t map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max);
static float magnitudeCalculation(float x, float y, float& x_magnitude, float& y_magnitude);
static void radialDeadzone(float& x, float& y, float in_deadzone, float out_deadzone, float x_magnitude, float y_magnitude, float magnitude, bool circularity);
Expand Down
4 changes: 4 additions & 0 deletions headers/gamepad/GamepadState.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ struct GamepadState
uint16_t ry {GAMEPAD_JOYSTICK_MID};
uint8_t lt {0};
uint8_t rt {0};
float ema_1_x {GAMEPAD_JOYSTICK_MID};
float ema_1_y {GAMEPAD_JOYSTICK_MID};
float ema_2_x {GAMEPAD_JOYSTICK_MID};
float ema_2_y {GAMEPAD_JOYSTICK_MID};
};

// Convert the horizontal GamepadState dpad axis value into an analog value
Expand Down
2 changes: 2 additions & 0 deletions proto/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ message AnalogOptions
optional InvertMode analogAdc2Invert = 11;
optional bool auto_calibrate = 12;
optional uint32 outer_deadzone = 13;
optional bool analog_smoothing = 14;
optional float smoothing_factor = 15;
}

message TurboOptions
Expand Down
25 changes: 23 additions & 2 deletions src/addons/analog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ void AnalogInput::process()
float magnitude_1 = 0.0f;
float magnitude_2 = 0.0f;

bool ema_option = analogOptions.analog_smoothing;
float ema_smoothing = analogOptions.smoothing_factor / 1000.0f;

struct adc_pair
{
int x_pin;
Expand All @@ -77,6 +80,8 @@ void AnalogInput::process()
float& y_magnitude;
InvertMode analog_invert;
DpadMode analog_dpad;
float& x_ema;
float& y_ema;
};

adc_pair adc_pairs[num_adc_pairs] =
Expand All @@ -86,14 +91,16 @@ void AnalogInput::process()
adc_1_x_center, adc_1_y_center,
magnitude_1, x_magnitude_1, y_magnitude_1,
analogOptions.analogAdc1Invert,
analogOptions.analogAdc1Mode},
analogOptions.analogAdc1Mode,
gamepad->state.ema_1_x, gamepad->state.ema_1_y},

{analogOptions.analogAdc2PinX, analogOptions.analogAdc2PinY,
adc_2_x, adc_2_y,
adc_2_x_center, adc_2_y_center,
magnitude_2, x_magnitude_2, y_magnitude_2,
analogOptions.analogAdc2Invert,
analogOptions.analogAdc2Mode}
analogOptions.analogAdc2Mode,
gamepad->state.ema_2_x, gamepad->state.ema_2_y}
};

for(size_t i = 0; i < num_adc_pairs; i++) {
Expand All @@ -106,6 +113,11 @@ void AnalogInput::process()

adc_pairs[i].x_value = ANALOG_MAX - adc_pairs[i].x_value;
}

if (ema_option) {
adc_pairs[i].x_value = emaCalculation(adc_pairs[i].x_value, ema_smoothing, adc_pairs[i].x_ema);
adc_pairs[i].x_ema = adc_pairs[i].x_value;
}
}
if (isValidPin(adc_pairs[i].y_pin)) {
adc_select_input(adc_pairs[i].y_pin - ADC_PIN_OFFSET);
Expand All @@ -116,6 +128,11 @@ void AnalogInput::process()

adc_pairs[i].y_value = ANALOG_MAX - adc_pairs[i].y_value;
}

if (ema_option) {
adc_pairs[i].y_value = emaCalculation(adc_pairs[i].y_value, ema_smoothing, adc_pairs[i].y_ema);
adc_pairs[i].y_ema = adc_pairs[i].y_value;
}
}

if (in_deadzone >= 0.0f || analogOptions.forced_circularity == true) {
Expand Down Expand Up @@ -171,6 +188,10 @@ float AnalogInput::readPin(int pin, uint16_t center, bool autoCalibrate) {
return ((float)adc_calibrated) / ADC_MAX;
}

float AnalogInput::emaCalculation(float ema_value, float smoothing_factor, float ema_previous) {
return (smoothing_factor * ema_value) + ((1.0f - smoothing_factor) * ema_previous);
}

uint16_t AnalogInput::map(uint16_t x, uint16_t in_min, uint16_t in_max, uint16_t out_min, uint16_t out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Expand Down
2 changes: 2 additions & 0 deletions src/config_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ void ConfigUtils::initUnsetPropertiesWithDefaults(Config& config)
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, inner_deadzone, DEFAULT_INNER_DEADZONE);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, outer_deadzone, DEFAULT_OUTER_DEADZONE);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, auto_calibrate, !!AUTO_CALIBRATE_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, analog_smoothing, !!ANALOG_SMOOTHING_ENABLED);
INIT_UNSET_PROPERTY(config.addonOptions.analogOptions, smoothing_factor, !!SMOOTHING_FACTOR);

// addonOptions.turboOptions
INIT_UNSET_PROPERTY(config.addonOptions.turboOptions, enabled, !!TURBO_ENABLED);
Expand Down
4 changes: 4 additions & 0 deletions src/configs/webconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,8 @@ std::string setAddonOptions()
docToValue(analogOptions.inner_deadzone, doc, "inner_deadzone");
docToValue(analogOptions.outer_deadzone, doc, "outer_deadzone");
docToValue(analogOptions.auto_calibrate, doc, "auto_calibrate");
docToValue(analogOptions.analog_smoothing, doc, "analog_smoothing");
docToValue(analogOptions.smoothing_factor, doc, "smoothing_factor");
docToValue(analogOptions.enabled, doc, "AnalogInputEnabled");

BootselButtonOptions& bootselButtonOptions = Storage::getInstance().getAddonOptions().bootselButtonOptions;
Expand Down Expand Up @@ -1820,6 +1822,8 @@ std::string getAddonOptions()
writeDoc(doc, "inner_deadzone", analogOptions.inner_deadzone);
writeDoc(doc, "outer_deadzone", analogOptions.outer_deadzone);
writeDoc(doc, "auto_calibrate", analogOptions.auto_calibrate);
writeDoc(doc, "analog_smoothing", analogOptions.analog_smoothing);
writeDoc(doc, "smoothing_factor", analogOptions.smoothing_factor);
writeDoc(doc, "AnalogInputEnabled", analogOptions.enabled);

const BootselButtonOptions& bootselButtonOptions = Storage::getInstance().getAddonOptions().bootselButtonOptions;
Expand Down
2 changes: 2 additions & 0 deletions www/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ app.get('/api/getAddonsOptions', (req, res) => {
inner_deadzone: 5,
outer_deadzone: 95,
auto_calibrate: 0,
analog_smoothing: 0,
smoothing_factor: 5,
bootselButtonMap: 0,
buzzerPin: -1,
buzzerEnablePin: -1,
Expand Down
36 changes: 36 additions & 0 deletions www/src/Addons/Analog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export const analogScheme = {
.number()
.label('Auto Calibration')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
analog_smoothing: yup
.number()
.label('Analog Smoothing')
.validateRangeWhenValue('AnalogInputEnabled', 0, 1),
smoothing_factor: yup
.number()
.label('Smoothing Factor')
.validateRangeWhenValue('AnalogInputEnabled', 0, 100),
};

export const analogState = {
Expand All @@ -89,6 +97,8 @@ export const analogState = {
inner_deadzone: 5,
outer_deadzone: 95,
auto_calibrate: 0,
analog_smoothing: 0,
smoothing_factor: 5,
};

const Analog = ({ values, errors, handleChange, handleCheckbox }) => {
Expand Down Expand Up @@ -279,6 +289,32 @@ const Analog = ({ values, errors, handleChange, handleCheckbox }) => {
handleChange(e);
}}
/>
<FormCheck
label={t('AddonsConfig:analog-smoothing')}
type="switch"
id="Analog_smoothing"
className="col-sm-3 ms-2"
isInvalid={false}
checked={Boolean(values.analog_smoothing)}
onChange={(e) => {
handleCheckbox('analog_smoothing', values);
handleChange(e);
}}
/>
<FormControl
hidden={!values.analog_smoothing}
type="number"
label={t('AddonsConfig:smoothing-factor')}
name="smoothing_factor"
className="form-control-sm"
groupClassName="col-sm-3 mb-3"
value={values.smoothing_factor}
error={errors.smoothing_factor}
isInvalid={errors.smoothing_factor}
onChange={handleChange}
min={0}
max={100}
/>
</Row>
</div>
<FormCheck
Expand Down
2 changes: 2 additions & 0 deletions www/src/Locales/en/AddonsConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export default {
'inner-deadzone-size': 'Inner Deadzone Size (%)',
'outer-deadzone-size': 'Outer Deadzone Size (%)',
'analog-auto-calibrate': 'Auto Calibration',
'analog-smoothing': 'Analog Smoothing',
'smoothing-factor': 'Smoothing Factor',
'turbo-header-text': 'Turbo',
'turbo-button-pin-label': 'Turbo Pin',
'turbo-led-pin-label': 'Turbo LED Pin',
Expand Down

0 comments on commit 1848523

Please sign in to comment.