Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Analog Smoothing to thumbsticks #1122

Merged
merged 2 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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