forked from NaxdyOrg/NaxGCC-FW
feat(config, stick): implement more calibration logic
This commit is contained in:
parent
5408d37560
commit
2d7239c04a
6 changed files with 489 additions and 121 deletions
|
@ -62,6 +62,7 @@ debug = 2
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
incremental = false
|
incremental = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
lto = "fat"
|
||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
|
|
||||||
# cargo build/run --release
|
# cargo build/run --release
|
||||||
|
|
253
src/config.rs
253
src/config.rs
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
use core::f32::consts::PI;
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
use defmt::{info, warn, Format};
|
use defmt::{error, info, warn, Format};
|
||||||
use embassy_rp::{
|
use embassy_rp::{
|
||||||
flash::{Async, Flash, ERASE_SIZE},
|
flash::{Async, Flash, ERASE_SIZE},
|
||||||
peripherals::FLASH,
|
peripherals::FLASH,
|
||||||
|
@ -12,16 +12,38 @@ use embassy_rp::{
|
||||||
use packed_struct::{derive::PackedStruct, PackedStruct};
|
use packed_struct::{derive::PackedStruct, PackedStruct};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
helpers::{PackedFloat, ToPackedFloatArray},
|
helpers::{PackedFloat, ToPackedFloatArray, XyValuePair},
|
||||||
stick::{NotchStatus, NO_OF_CALIBRATION_POINTS, NO_OF_NOTCHES},
|
input::{read_ext_adc, Stick, StickAxis, SPI_ACS_SHARED, SPI_CCS_SHARED, SPI_SHARED},
|
||||||
|
stick::{NotchStatus, NO_OF_ADJ_NOTCHES, NO_OF_CALIBRATION_POINTS, NO_OF_NOTCHES},
|
||||||
ADDR_OFFSET, FLASH_SIZE,
|
ADDR_OFFSET, FLASH_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pubsub::Subscriber};
|
use embassy_sync::{
|
||||||
|
blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex},
|
||||||
|
pubsub::Subscriber,
|
||||||
|
signal::Signal,
|
||||||
|
};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
|
|
||||||
use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
|
use crate::{gcc_hid::GcReport, input::CHANNEL_GCC_STATE};
|
||||||
|
|
||||||
|
/// Whether we are currently calibrating the sticks. Updates are dispatched when the status changes.
|
||||||
|
/// Initial status is assumed to be false.
|
||||||
|
pub static SIGNAL_IS_CALIBRATING: Signal<CriticalSectionRawMutex, bool> = Signal::new();
|
||||||
|
|
||||||
|
/// Dispatched when we want to override the GCC state for a short amount of time.
|
||||||
|
pub static SIGNAL_OVERRIDE_GCC_STATE: Signal<CriticalSectionRawMutex, OverrideGcReportInstruction> =
|
||||||
|
Signal::new();
|
||||||
|
|
||||||
|
/// Struct used for overriding the GCC state for a given amount of
|
||||||
|
/// time, useful for providing feedback to the user e.g. if we just entered
|
||||||
|
/// a certain mode.
|
||||||
|
#[derive(Default, Debug, Clone, Format)]
|
||||||
|
pub struct OverrideGcReportInstruction {
|
||||||
|
pub report: GcReport,
|
||||||
|
pub duration_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
const CONFIG_MODE_ENTRY_COMBO: [AwaitableButtons; 4] = [
|
const CONFIG_MODE_ENTRY_COMBO: [AwaitableButtons; 4] = [
|
||||||
AwaitableButtons::Start,
|
AwaitableButtons::Start,
|
||||||
AwaitableButtons::A,
|
AwaitableButtons::A,
|
||||||
|
@ -29,6 +51,20 @@ const CONFIG_MODE_ENTRY_COMBO: [AwaitableButtons; 4] = [
|
||||||
AwaitableButtons::Y,
|
AwaitableButtons::Y,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const LSTICK_CALIBRATION_COMBO: [AwaitableButtons; 4] = [
|
||||||
|
AwaitableButtons::A,
|
||||||
|
AwaitableButtons::X,
|
||||||
|
AwaitableButtons::Y,
|
||||||
|
AwaitableButtons::L,
|
||||||
|
];
|
||||||
|
|
||||||
|
const RSTICK_CALIBRATION_COMBO: [AwaitableButtons; 4] = [
|
||||||
|
AwaitableButtons::A,
|
||||||
|
AwaitableButtons::X,
|
||||||
|
AwaitableButtons::Y,
|
||||||
|
AwaitableButtons::R,
|
||||||
|
];
|
||||||
|
|
||||||
/// This doesn't need to be super fast, since it's only used
|
/// This doesn't need to be super fast, since it's only used
|
||||||
/// in config mode.
|
/// in config mode.
|
||||||
const BUTTON_POLL_INTERVAL_MILLIS: u64 = 20;
|
const BUTTON_POLL_INTERVAL_MILLIS: u64 = 20;
|
||||||
|
@ -96,7 +132,7 @@ const DEFAULT_CAL_POINTS_Y: [f32; NO_OF_CALIBRATION_POINTS] = [
|
||||||
0.3000802760,0.3008482317
|
0.3000802760,0.3008482317
|
||||||
];
|
];
|
||||||
|
|
||||||
const DEFAULT_ANGLES: [f32; NO_OF_NOTCHES] = [
|
pub const DEFAULT_ANGLES: [f32; NO_OF_NOTCHES] = [
|
||||||
0.,
|
0.,
|
||||||
PI / 8.0,
|
PI / 8.0,
|
||||||
PI * 2. / 8.,
|
PI * 2. / 8.,
|
||||||
|
@ -150,9 +186,9 @@ pub struct StickConfig {
|
||||||
#[packed_field(size_bits = "8")]
|
#[packed_field(size_bits = "8")]
|
||||||
pub y_smoothing: u8,
|
pub y_smoothing: u8,
|
||||||
#[packed_field(element_size_bytes = "4")]
|
#[packed_field(element_size_bytes = "4")]
|
||||||
pub temp_cal_points_x: [PackedFloat; 32],
|
pub cal_points_x: [PackedFloat; 32],
|
||||||
#[packed_field(element_size_bytes = "4")]
|
#[packed_field(element_size_bytes = "4")]
|
||||||
pub temp_cal_points_y: [PackedFloat; 32],
|
pub cal_points_y: [PackedFloat; 32],
|
||||||
#[packed_field(element_size_bytes = "4")]
|
#[packed_field(element_size_bytes = "4")]
|
||||||
pub angles: [PackedFloat; 16],
|
pub angles: [PackedFloat; 16],
|
||||||
}
|
}
|
||||||
|
@ -168,8 +204,8 @@ impl Default for StickConfig {
|
||||||
y_smoothing: 0,
|
y_smoothing: 0,
|
||||||
cardinal_snapping: 0,
|
cardinal_snapping: 0,
|
||||||
analog_scaler: 100,
|
analog_scaler: 100,
|
||||||
temp_cal_points_x: *DEFAULT_CAL_POINTS_X.to_packed_float_array(),
|
cal_points_x: *DEFAULT_CAL_POINTS_X.to_packed_float_array(),
|
||||||
temp_cal_points_y: *DEFAULT_CAL_POINTS_Y.to_packed_float_array(),
|
cal_points_y: *DEFAULT_CAL_POINTS_Y.to_packed_float_array(),
|
||||||
angles: *DEFAULT_ANGLES.to_packed_float_array(),
|
angles: *DEFAULT_ANGLES.to_packed_float_array(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,16 +291,17 @@ trait WaitForButtonPress {
|
||||||
) -> usize;
|
) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
|
impl<'a, T: RawMutex, const I: usize, const J: usize, const K: usize> WaitForButtonPress
|
||||||
for Subscriber<'a, CriticalSectionRawMutex, GcReport, I, J, K>
|
for Subscriber<'a, T, GcReport, I, J, K>
|
||||||
{
|
{
|
||||||
async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
|
async fn wait_for_button_press(&mut self, button_to_wait_for: &AwaitableButtons) {
|
||||||
loop {
|
loop {
|
||||||
if match self.next_message_pure().await {
|
let report = self.next_message_pure().await;
|
||||||
report => is_awaitable_button_pressed(&report, button_to_wait_for),
|
|
||||||
} {
|
if is_awaitable_button_pressed(&report, button_to_wait_for) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,13 +311,15 @@ impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
|
||||||
buttons_to_wait_for: &[AwaitableButtons; N],
|
buttons_to_wait_for: &[AwaitableButtons; N],
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
if match self.next_message_pure().await {
|
let report = self.next_message_pure().await;
|
||||||
report => buttons_to_wait_for
|
|
||||||
.iter()
|
if buttons_to_wait_for
|
||||||
.all(|button| is_awaitable_button_pressed(&report, button)),
|
.iter()
|
||||||
} {
|
.all(|button| is_awaitable_button_pressed(&report, button))
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,15 +329,14 @@ impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
|
||||||
buttons_to_wait_for: &[AwaitableButtons; N],
|
buttons_to_wait_for: &[AwaitableButtons; N],
|
||||||
) -> AwaitableButtons {
|
) -> AwaitableButtons {
|
||||||
loop {
|
loop {
|
||||||
match self.next_message_pure().await {
|
let report = self.next_message_pure().await;
|
||||||
report => {
|
|
||||||
for button in buttons_to_wait_for {
|
for button in buttons_to_wait_for {
|
||||||
if is_awaitable_button_pressed(&report, button) {
|
if is_awaitable_button_pressed(&report, button) {
|
||||||
return *button;
|
return *button;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,18 +346,17 @@ impl<'a, const I: usize, const J: usize, const K: usize> WaitForButtonPress
|
||||||
buttons_to_wait_for: &[[AwaitableButtons; N]; M],
|
buttons_to_wait_for: &[[AwaitableButtons; N]; M],
|
||||||
) -> usize {
|
) -> usize {
|
||||||
loop {
|
loop {
|
||||||
match self.next_message_pure().await {
|
let report = self.next_message_pure().await;
|
||||||
report => {
|
|
||||||
for (i, buttons) in buttons_to_wait_for.iter().enumerate() {
|
for (i, buttons) in buttons_to_wait_for.iter().enumerate() {
|
||||||
if buttons
|
if buttons
|
||||||
.iter()
|
.iter()
|
||||||
.all(|button| is_awaitable_button_pressed(&report, button))
|
.all(|button| is_awaitable_button_pressed(&report, button))
|
||||||
{
|
{
|
||||||
return i;
|
return i;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
Timer::after_millis(BUTTON_POLL_INTERVAL_MILLIS).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,18 +373,156 @@ fn is_awaitable_button_pressed(report: &GcReport, button_to_wait_for: &Awaitable
|
||||||
AwaitableButtons::Left => report.buttons_1.dpad_left,
|
AwaitableButtons::Left => report.buttons_1.dpad_left,
|
||||||
AwaitableButtons::Right => report.buttons_1.dpad_right,
|
AwaitableButtons::Right => report.buttons_1.dpad_right,
|
||||||
AwaitableButtons::Start => report.buttons_2.button_start,
|
AwaitableButtons::Start => report.buttons_2.button_start,
|
||||||
AwaitableButtons::L => report.buttons_2.button_l,
|
AwaitableButtons::L => report.buttons_2.button_l || report.trigger_l > 10,
|
||||||
AwaitableButtons::R => report.buttons_2.button_r,
|
AwaitableButtons::R => report.buttons_2.button_r || report.trigger_r > 10,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Format)]
|
||||||
|
struct StickCalibrationProcess<'a> {
|
||||||
|
which_stick: Stick,
|
||||||
|
calibration_step: u8,
|
||||||
|
gcc_config: &'a mut ControllerConfig,
|
||||||
|
cal_points: [XyValuePair<f32>; NO_OF_CALIBRATION_POINTS],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StickCalibrationProcess<'a> {
|
||||||
|
pub fn new(gcc_config: &'a mut ControllerConfig, which_stick: Stick) -> Self {
|
||||||
|
Self {
|
||||||
|
which_stick,
|
||||||
|
calibration_step: 0,
|
||||||
|
gcc_config,
|
||||||
|
cal_points: [XyValuePair::default(); NO_OF_CALIBRATION_POINTS],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn calibration_advance(&mut self) {
|
||||||
|
let mut spi_unlocked = SPI_SHARED.lock().await;
|
||||||
|
let mut spi_acs_unlocked = SPI_ACS_SHARED.lock().await;
|
||||||
|
let mut spi_ccs_unlocked = SPI_CCS_SHARED.lock().await;
|
||||||
|
|
||||||
|
let spi = spi_unlocked.as_mut().unwrap();
|
||||||
|
let spi_acs = spi_acs_unlocked.as_mut().unwrap();
|
||||||
|
let spi_ccs = spi_ccs_unlocked.as_mut().unwrap();
|
||||||
|
|
||||||
|
if self.calibration_step < NO_OF_CALIBRATION_POINTS as u8 {
|
||||||
|
let mut x: f32 = 0.;
|
||||||
|
let mut y: f32 = 0.;
|
||||||
|
|
||||||
|
for _ in 0..128 {
|
||||||
|
x += read_ext_adc(self.which_stick, StickAxis::XAxis, spi, spi_acs, spi_ccs) as f32
|
||||||
|
/ 4096.0;
|
||||||
|
y += read_ext_adc(self.which_stick, StickAxis::YAxis, spi, spi_acs, spi_ccs) as f32
|
||||||
|
/ 4096.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
x /= 128.;
|
||||||
|
y /= 128.;
|
||||||
|
|
||||||
|
self.cal_points[self.calibration_step as usize] = XyValuePair { x, y };
|
||||||
|
}
|
||||||
|
|
||||||
|
self.calibration_step += 1;
|
||||||
|
|
||||||
|
// TODO: phob does something related to undo here
|
||||||
|
|
||||||
|
if self.calibration_step == NO_OF_CALIBRATION_POINTS as u8 {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.calibration_step >= NO_OF_CALIBRATION_POINTS as u8 + NO_OF_ADJ_NOTCHES as u8 {
|
||||||
|
let stick_config = match self.which_stick {
|
||||||
|
Stick::ControlStick => &mut self.gcc_config.astick_config,
|
||||||
|
Stick::CStick => &mut self.gcc_config.cstick_config,
|
||||||
|
};
|
||||||
|
|
||||||
|
stick_config.cal_points_x = self.cal_points.map(|p| p.x.into());
|
||||||
|
stick_config.cal_points_y = self.cal_points.map(|p| p.y.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn calibrate_stick(&mut self) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn configuration_main_loop<
|
||||||
|
'a,
|
||||||
|
M: RawMutex,
|
||||||
|
const C: usize,
|
||||||
|
const S: usize,
|
||||||
|
const P: usize,
|
||||||
|
>(
|
||||||
|
current_config: &ControllerConfig,
|
||||||
|
mut flash: &mut Flash<'static, FLASH, Async, FLASH_SIZE>,
|
||||||
|
gcc_subscriber: &mut Subscriber<'a, M, GcReport, C, S, P>,
|
||||||
|
) {
|
||||||
|
let mut final_config = current_config.clone();
|
||||||
|
let config_options = [LSTICK_CALIBRATION_COMBO, RSTICK_CALIBRATION_COMBO];
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
match gcc_subscriber
|
||||||
|
.wait_and_filter_simultaneous_button_presses(&config_options)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
selection => match selection {
|
||||||
|
1 => {
|
||||||
|
StickCalibrationProcess::new(&mut final_config, Stick::ControlStick)
|
||||||
|
.calibrate_stick()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
StickCalibrationProcess::new(&mut final_config, Stick::CStick)
|
||||||
|
.calibrate_stick()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Invalid selection: {}", selection);
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
final_config.write_to_flash(&mut flash).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn config_task() {
|
pub async fn config_task(
|
||||||
|
current_config: ControllerConfig,
|
||||||
|
mut flash: Flash<'static, FLASH, Async, FLASH_SIZE>,
|
||||||
|
) {
|
||||||
let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
|
let mut gcc_subscriber = CHANNEL_GCC_STATE.subscriber().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
gcc_subscriber
|
gcc_subscriber
|
||||||
.wait_for_simultaneous_button_presses(&CONFIG_MODE_ENTRY_COMBO)
|
.wait_for_simultaneous_button_presses(&CONFIG_MODE_ENTRY_COMBO)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
info!("Entering config mode.");
|
||||||
|
|
||||||
|
SIGNAL_OVERRIDE_GCC_STATE.signal(OverrideGcReportInstruction {
|
||||||
|
report: match GcReport::default() {
|
||||||
|
mut a => {
|
||||||
|
a.trigger_r = 255;
|
||||||
|
a.trigger_l = 255;
|
||||||
|
a.buttons_2.button_l = true;
|
||||||
|
a.buttons_2.button_r = true;
|
||||||
|
a.buttons_1.button_x = true;
|
||||||
|
a.buttons_1.button_y = true;
|
||||||
|
a.buttons_1.button_a = true;
|
||||||
|
a.stick_x = 127;
|
||||||
|
a.stick_y = 127;
|
||||||
|
a.cstick_x = 127;
|
||||||
|
a.cstick_y = 127;
|
||||||
|
a
|
||||||
|
}
|
||||||
|
},
|
||||||
|
duration_ms: 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
configuration_main_loop(¤t_config, &mut flash, &mut gcc_subscriber).await;
|
||||||
|
|
||||||
|
info!("Exiting config mode.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,12 @@ impl Deref for PackedFloat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<f32> for PackedFloat {
|
||||||
|
fn from(f: f32) -> Self {
|
||||||
|
Self(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Format, Default, Copy)]
|
#[derive(Debug, Clone, Format, Default, Copy)]
|
||||||
pub struct XyValuePair<T> {
|
pub struct XyValuePair<T> {
|
||||||
pub x: T,
|
pub x: T,
|
||||||
|
|
128
src/input.rs
128
src/input.rs
|
@ -5,19 +5,22 @@ use embassy_rp::{
|
||||||
gpio::{AnyPin, Input, Output, Pin},
|
gpio::{AnyPin, Input, Output, Pin},
|
||||||
peripherals::{
|
peripherals::{
|
||||||
FLASH, PIN_10, PIN_11, PIN_16, PIN_17, PIN_18, PIN_19, PIN_20, PIN_21, PIN_22, PIN_23,
|
FLASH, PIN_10, PIN_11, PIN_16, PIN_17, PIN_18, PIN_19, PIN_20, PIN_21, PIN_22, PIN_23,
|
||||||
PIN_24, PIN_5, PIN_8, PIN_9, PWM_CH4, PWM_CH6, SPI0,
|
PIN_24, PIN_5, PIN_8, PIN_9, PWM_CH4, PWM_CH6, SPI0, SPI1,
|
||||||
},
|
},
|
||||||
pwm::Pwm,
|
pwm::Pwm,
|
||||||
spi::Spi,
|
spi::{Blocking, Spi},
|
||||||
};
|
};
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
blocking_mutex::raw::CriticalSectionRawMutex, pubsub::PubSubChannel, signal::Signal,
|
blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex, ThreadModeRawMutex},
|
||||||
|
mutex::Mutex,
|
||||||
|
pubsub::PubSubChannel,
|
||||||
|
signal::Signal,
|
||||||
};
|
};
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use libm::{fmaxf, fminf};
|
use libm::{fmaxf, fminf};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::ControllerConfig,
|
config::{ControllerConfig, SIGNAL_IS_CALIBRATING, SIGNAL_OVERRIDE_GCC_STATE},
|
||||||
filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS},
|
filter::{run_waveshaping, FilterGains, KalmanState, WaveshapingValues, FILTER_GAINS},
|
||||||
gcc_hid::GcReport,
|
gcc_hid::GcReport,
|
||||||
helpers::XyValuePair,
|
helpers::XyValuePair,
|
||||||
|
@ -30,7 +33,17 @@ pub static CHANNEL_GCC_STATE: PubSubChannel<CriticalSectionRawMutex, GcReport, 1
|
||||||
PubSubChannel::new();
|
PubSubChannel::new();
|
||||||
|
|
||||||
/// Used to send the stick state from the stick task to the main input task
|
/// Used to send the stick state from the stick task to the main input task
|
||||||
static STICK_SIGNAL: Signal<CriticalSectionRawMutex, StickState> = Signal::new();
|
static SIGNAL_STICK_STATE: Signal<CriticalSectionRawMutex, StickState> = Signal::new();
|
||||||
|
|
||||||
|
/// Used to send the raw stick values for the calibration task
|
||||||
|
static SIGNAL_RAW_STICK_VALUES: Signal<CriticalSectionRawMutex, RawStickValues> = Signal::new();
|
||||||
|
|
||||||
|
pub static SPI_SHARED: Mutex<ThreadModeRawMutex, Option<Spi<'static, SPI0, Blocking>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
pub static SPI_ACS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
pub static SPI_CCS_SHARED: Mutex<ThreadModeRawMutex, Option<Output<'static, AnyPin>>> =
|
||||||
|
Mutex::new(None);
|
||||||
|
|
||||||
const STICK_HYST_VAL: f32 = 0.3;
|
const STICK_HYST_VAL: f32 = 0.3;
|
||||||
const FLOAT_ORIGIN: f32 = 127.5;
|
const FLOAT_ORIGIN: f32 = 127.5;
|
||||||
|
@ -61,7 +74,7 @@ struct RawStickValues {
|
||||||
c_unfiltered: XyValuePair<f32>,
|
c_unfiltered: XyValuePair<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Debug, Clone, Format, Copy)]
|
||||||
pub enum Stick {
|
pub enum Stick {
|
||||||
ControlStick,
|
ControlStick,
|
||||||
CStick,
|
CStick,
|
||||||
|
@ -74,7 +87,13 @@ pub enum StickAxis {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link_section = ".time_critical.read_ext_adc"]
|
#[link_section = ".time_critical.read_ext_adc"]
|
||||||
fn read_ext_adc<'a, Acs: Pin, Ccs: Pin, I: embassy_rp::spi::Instance, M: embassy_rp::spi::Mode>(
|
pub fn read_ext_adc<
|
||||||
|
'a,
|
||||||
|
Acs: Pin,
|
||||||
|
Ccs: Pin,
|
||||||
|
I: embassy_rp::spi::Instance,
|
||||||
|
M: embassy_rp::spi::Mode,
|
||||||
|
>(
|
||||||
which_stick: Stick,
|
which_stick: Stick,
|
||||||
which_axis: StickAxis,
|
which_axis: StickAxis,
|
||||||
spi: &mut Spi<'a, I, M>,
|
spi: &mut Spi<'a, I, M>,
|
||||||
|
@ -110,16 +129,7 @@ fn read_ext_adc<'a, Acs: Pin, Ccs: Pin, I: embassy_rp::spi::Instance, M: embassy
|
||||||
/// Gets the average stick state over a 1ms interval in a non-blocking fashion.
|
/// Gets the average stick state over a 1ms interval in a non-blocking fashion.
|
||||||
/// Will wait until end_time is reached before continuing after reading the ADCs.
|
/// Will wait until end_time is reached before continuing after reading the ADCs.
|
||||||
#[link_section = ".time_critical.update_stick_states"]
|
#[link_section = ".time_critical.update_stick_states"]
|
||||||
async fn update_stick_states<
|
async fn update_stick_states(
|
||||||
'a,
|
|
||||||
Acs: Pin,
|
|
||||||
Ccs: Pin,
|
|
||||||
I: embassy_rp::spi::Instance,
|
|
||||||
M: embassy_rp::spi::Mode,
|
|
||||||
>(
|
|
||||||
mut spi: &mut Spi<'a, I, M>,
|
|
||||||
mut spi_acs: &mut Output<'a, Acs>,
|
|
||||||
mut spi_ccs: &mut Output<'a, Ccs>,
|
|
||||||
current_stick_state: &StickState,
|
current_stick_state: &StickState,
|
||||||
controlstick_params: &StickParams,
|
controlstick_params: &StickParams,
|
||||||
cstick_params: &StickParams,
|
cstick_params: &StickParams,
|
||||||
|
@ -130,6 +140,7 @@ async fn update_stick_states<
|
||||||
old_stick_pos: &mut StickPositions,
|
old_stick_pos: &mut StickPositions,
|
||||||
raw_stick_values: &mut RawStickValues,
|
raw_stick_values: &mut RawStickValues,
|
||||||
kalman_state: &mut KalmanState,
|
kalman_state: &mut KalmanState,
|
||||||
|
is_calibrating: bool,
|
||||||
) -> StickState {
|
) -> StickState {
|
||||||
let mut adc_count = 0u32;
|
let mut adc_count = 0u32;
|
||||||
let mut ax_sum = 0u32;
|
let mut ax_sum = 0u32;
|
||||||
|
@ -139,39 +150,23 @@ async fn update_stick_states<
|
||||||
|
|
||||||
let end_time = Instant::now() + Duration::from_micros(300); // this seems kinda magic, and it is, but
|
let end_time = Instant::now() + Duration::from_micros(300); // this seems kinda magic, and it is, but
|
||||||
|
|
||||||
|
let mut spi_unlocked = SPI_SHARED.lock().await;
|
||||||
|
let mut spi_acs_unlocked = SPI_ACS_SHARED.lock().await;
|
||||||
|
let mut spi_ccs_unlocked = SPI_CCS_SHARED.lock().await;
|
||||||
|
|
||||||
|
let spi = spi_unlocked.as_mut().unwrap();
|
||||||
|
let spi_acs = spi_acs_unlocked.as_mut().unwrap();
|
||||||
|
let spi_ccs = spi_ccs_unlocked.as_mut().unwrap();
|
||||||
|
|
||||||
// "do-while at home"
|
// "do-while at home"
|
||||||
while {
|
while {
|
||||||
let loop_start = Instant::now();
|
let loop_start = Instant::now();
|
||||||
|
|
||||||
adc_count += 1;
|
adc_count += 1;
|
||||||
ax_sum += read_ext_adc(
|
ax_sum += read_ext_adc(Stick::ControlStick, StickAxis::XAxis, spi, spi_acs, spi_ccs) as u32;
|
||||||
Stick::ControlStick,
|
ay_sum += read_ext_adc(Stick::ControlStick, StickAxis::YAxis, spi, spi_acs, spi_ccs) as u32;
|
||||||
StickAxis::XAxis,
|
cx_sum += read_ext_adc(Stick::CStick, StickAxis::XAxis, spi, spi_acs, spi_ccs) as u32;
|
||||||
&mut spi,
|
cy_sum += read_ext_adc(Stick::CStick, StickAxis::YAxis, spi, spi_acs, spi_ccs) as u32;
|
||||||
&mut spi_acs,
|
|
||||||
&mut spi_ccs,
|
|
||||||
) as u32;
|
|
||||||
ay_sum += read_ext_adc(
|
|
||||||
Stick::ControlStick,
|
|
||||||
StickAxis::YAxis,
|
|
||||||
&mut spi,
|
|
||||||
&mut spi_acs,
|
|
||||||
&mut spi_ccs,
|
|
||||||
) as u32;
|
|
||||||
cx_sum += read_ext_adc(
|
|
||||||
Stick::CStick,
|
|
||||||
StickAxis::XAxis,
|
|
||||||
&mut spi,
|
|
||||||
&mut spi_acs,
|
|
||||||
&mut spi_ccs,
|
|
||||||
) as u32;
|
|
||||||
cy_sum += read_ext_adc(
|
|
||||||
Stick::CStick,
|
|
||||||
StickAxis::YAxis,
|
|
||||||
&mut spi,
|
|
||||||
&mut spi_acs,
|
|
||||||
&mut spi_ccs,
|
|
||||||
) as u32;
|
|
||||||
|
|
||||||
let loop_end = Instant::now();
|
let loop_end = Instant::now();
|
||||||
loop_end < end_time - (loop_end - loop_start)
|
loop_end < end_time - (loop_end - loop_start)
|
||||||
|
@ -265,7 +260,7 @@ async fn update_stick_states<
|
||||||
controlstick_params,
|
controlstick_params,
|
||||||
controller_config,
|
controller_config,
|
||||||
Stick::ControlStick,
|
Stick::ControlStick,
|
||||||
false,
|
is_calibrating,
|
||||||
) {
|
) {
|
||||||
(x, y) => XyValuePair { x, y },
|
(x, y) => XyValuePair { x, y },
|
||||||
};
|
};
|
||||||
|
@ -275,7 +270,7 @@ async fn update_stick_states<
|
||||||
cstick_params,
|
cstick_params,
|
||||||
controller_config,
|
controller_config,
|
||||||
Stick::CStick,
|
Stick::CStick,
|
||||||
false,
|
is_calibrating,
|
||||||
) {
|
) {
|
||||||
(x, y) => XyValuePair { x, y },
|
(x, y) => XyValuePair { x, y },
|
||||||
};
|
};
|
||||||
|
@ -285,7 +280,7 @@ async fn update_stick_states<
|
||||||
controlstick_params,
|
controlstick_params,
|
||||||
controller_config,
|
controller_config,
|
||||||
Stick::ControlStick,
|
Stick::ControlStick,
|
||||||
false,
|
is_calibrating,
|
||||||
) {
|
) {
|
||||||
(x, y) => XyValuePair { x, y },
|
(x, y) => XyValuePair { x, y },
|
||||||
};
|
};
|
||||||
|
@ -295,7 +290,7 @@ async fn update_stick_states<
|
||||||
cstick_params,
|
cstick_params,
|
||||||
controller_config,
|
controller_config,
|
||||||
Stick::CStick,
|
Stick::CStick,
|
||||||
false,
|
is_calibrating,
|
||||||
) {
|
) {
|
||||||
(x, y) => XyValuePair { x, y },
|
(x, y) => XyValuePair { x, y },
|
||||||
};
|
};
|
||||||
|
@ -440,16 +435,19 @@ pub async fn update_button_state_task(
|
||||||
);
|
);
|
||||||
|
|
||||||
// not every loop pass is going to update the stick state
|
// not every loop pass is going to update the stick state
|
||||||
match STICK_SIGNAL.try_take() {
|
if let Some(stick_state) = SIGNAL_STICK_STATE.try_take() {
|
||||||
Some(stick_state) => {
|
gcc_state.stick_x = stick_state.ax;
|
||||||
gcc_state.stick_x = stick_state.ax;
|
gcc_state.stick_y = stick_state.ay;
|
||||||
gcc_state.stick_y = stick_state.ay;
|
gcc_state.cstick_x = stick_state.cx;
|
||||||
gcc_state.cstick_x = stick_state.cx;
|
gcc_state.cstick_y = stick_state.cy;
|
||||||
gcc_state.cstick_y = stick_state.cy;
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for a gcc state override (usually coming from the config task)
|
||||||
|
if let Some(override_gcc_state) = SIGNAL_OVERRIDE_GCC_STATE.try_take() {
|
||||||
|
gcc_publisher.publish_immediate(override_gcc_state.report);
|
||||||
|
Timer::after_millis(override_gcc_state.duration_ms).await;
|
||||||
|
};
|
||||||
|
|
||||||
gcc_publisher.publish_immediate(gcc_state);
|
gcc_publisher.publish_immediate(gcc_state);
|
||||||
|
|
||||||
// give other tasks a chance to do something
|
// give other tasks a chance to do something
|
||||||
|
@ -459,6 +457,8 @@ pub async fn update_button_state_task(
|
||||||
|
|
||||||
/// Task responsible for updating the stick states.
|
/// Task responsible for updating the stick states.
|
||||||
/// Publishes the result to STICK_SIGNAL.
|
/// Publishes the result to STICK_SIGNAL.
|
||||||
|
///
|
||||||
|
/// Has to run on core0 because it makes use of SPI0.
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn update_stick_states_task(
|
pub async fn update_stick_states_task(
|
||||||
mut spi: Spi<'static, SPI0, embassy_rp::spi::Blocking>,
|
mut spi: Spi<'static, SPI0, embassy_rp::spi::Blocking>,
|
||||||
|
@ -490,13 +490,12 @@ pub async fn update_stick_states_task(
|
||||||
// the time at which the current loop iteration should end
|
// the time at which the current loop iteration should end
|
||||||
let mut end_time = Instant::now() + Duration::from_micros(1000);
|
let mut end_time = Instant::now() + Duration::from_micros(1000);
|
||||||
|
|
||||||
|
let mut is_calibrating = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let timer = Timer::at(end_time);
|
let timer = Timer::at(end_time);
|
||||||
|
|
||||||
current_stick_state = update_stick_states(
|
current_stick_state = update_stick_states(
|
||||||
&mut spi,
|
|
||||||
&mut spi_acs,
|
|
||||||
&mut spi_ccs,
|
|
||||||
¤t_stick_state,
|
¤t_stick_state,
|
||||||
&controlstick_params,
|
&controlstick_params,
|
||||||
&cstick_params,
|
&cstick_params,
|
||||||
|
@ -507,22 +506,27 @@ pub async fn update_stick_states_task(
|
||||||
&mut old_stick_pos,
|
&mut old_stick_pos,
|
||||||
&mut raw_stick_values,
|
&mut raw_stick_values,
|
||||||
&mut kalman_state,
|
&mut kalman_state,
|
||||||
|
is_calibrating,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
timer.await;
|
timer.await;
|
||||||
end_time += Duration::from_micros(1000);
|
end_time += Duration::from_micros(1000);
|
||||||
|
|
||||||
|
if let Some(new_calibrating) = SIGNAL_IS_CALIBRATING.try_take() {
|
||||||
|
is_calibrating = new_calibrating;
|
||||||
|
}
|
||||||
|
|
||||||
match Instant::now() {
|
match Instant::now() {
|
||||||
n => {
|
n => {
|
||||||
match (n - last_loop_time).as_micros() {
|
match (n - last_loop_time).as_micros() {
|
||||||
a if a > 1100 => debug!("Loop took {} us", a),
|
a if a > 1999 => debug!("Loop took {} us", a),
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
last_loop_time = n;
|
last_loop_time = n;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
STICK_SIGNAL.signal(current_stick_state.clone());
|
SIGNAL_STICK_STATE.signal(current_stick_state.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,14 @@ fn main() -> ! {
|
||||||
// pwm_brake.set_counter(255);
|
// pwm_brake.set_counter(255);
|
||||||
|
|
||||||
executor0.run(|spawner| {
|
executor0.run(|spawner| {
|
||||||
spawner.spawn(config_task()).unwrap();
|
// Config task has to run on core0 because it reads and writes to flash.
|
||||||
|
spawner
|
||||||
|
.spawn(config_task(controller_config.clone(), flash))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Stick loop has to run on core0 because it makes use of SPI0.
|
||||||
|
// Perhaps in the future we can rewire the board to have it make use of SPI1 instead.
|
||||||
|
// This way it could be the sole task running on core1, and everything else could happen on core0.
|
||||||
spawner
|
spawner
|
||||||
.spawn(update_stick_states_task(
|
.spawn(update_stick_states_task(
|
||||||
spi,
|
spi,
|
||||||
|
|
213
src/stick.rs
213
src/stick.rs
|
@ -6,7 +6,7 @@ use defmt::{debug, Format};
|
||||||
use libm::{atan2f, cosf, fabs, fabsf, roundf, sinf, sqrtf};
|
use libm::{atan2f, cosf, fabs, fabsf, roundf, sinf, sqrtf};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{ControllerConfig, StickConfig, DEFAULT_NOTCH_STATUS},
|
config::{ControllerConfig, StickConfig, DEFAULT_ANGLES, DEFAULT_NOTCH_STATUS},
|
||||||
helpers::{ToRegularArray, XyValuePair},
|
helpers::{ToRegularArray, XyValuePair},
|
||||||
input::Stick,
|
input::Stick,
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
const FIT_ORDER: usize = 3;
|
const FIT_ORDER: usize = 3;
|
||||||
const NUM_COEFFS: usize = FIT_ORDER + 1;
|
const NUM_COEFFS: usize = FIT_ORDER + 1;
|
||||||
pub const NO_OF_NOTCHES: usize = 16;
|
pub const NO_OF_NOTCHES: usize = 16;
|
||||||
const NO_OF_ADJ_NOTCHES: usize = 12;
|
pub const NO_OF_ADJ_NOTCHES: usize = 12;
|
||||||
pub const NO_OF_CALIBRATION_POINTS: usize = 32;
|
pub const NO_OF_CALIBRATION_POINTS: usize = 32;
|
||||||
const MAX_ORDER: usize = 20;
|
const MAX_ORDER: usize = 20;
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ impl StickParams {
|
||||||
/// Generate StickParams structs for the sticks, returned as a tuple of (analog_stick, c_stick)
|
/// Generate StickParams structs for the sticks, returned as a tuple of (analog_stick, c_stick)
|
||||||
pub fn from_stick_config(stick_config: &StickConfig) -> Self {
|
pub fn from_stick_config(stick_config: &StickConfig) -> Self {
|
||||||
let cleaned_cal_points = CleanedCalibrationPoints::from_temp_calibration_points(
|
let cleaned_cal_points = CleanedCalibrationPoints::from_temp_calibration_points(
|
||||||
&stick_config.temp_cal_points_x.to_regular_array(),
|
stick_config.cal_points_x.to_regular_array(),
|
||||||
&stick_config.temp_cal_points_y.to_regular_array(),
|
stick_config.cal_points_y.to_regular_array(),
|
||||||
&stick_config.angles.to_regular_array(),
|
stick_config.angles.to_regular_array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let linearized_cal = LinearizedCalibration::from_calibration_points(&cleaned_cal_points);
|
let linearized_cal = LinearizedCalibration::from_calibration_points(&cleaned_cal_points);
|
||||||
|
@ -68,7 +68,7 @@ impl StickParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Format, Copy)]
|
#[derive(Clone, Debug, Format, Copy, Eq, PartialEq)]
|
||||||
pub enum NotchStatus {
|
pub enum NotchStatus {
|
||||||
TertInactive,
|
TertInactive,
|
||||||
TertActive,
|
TertActive,
|
||||||
|
@ -387,6 +387,190 @@ impl NotchCalibration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AppliedCalibration {
|
||||||
|
stick_params: StickParams,
|
||||||
|
cleaned_calibration: CleanedCalibrationPoints,
|
||||||
|
notch_angles: [f32; NO_OF_NOTCHES],
|
||||||
|
measured_notch_angles: [f32; NO_OF_NOTCHES],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppliedCalibration {
|
||||||
|
pub fn from_points(
|
||||||
|
cal_points_x: &[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
cal_points_y: &[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
stick_config: &StickConfig,
|
||||||
|
which_stick: Stick,
|
||||||
|
) -> Self {
|
||||||
|
let mut stick_params = StickParams::from_stick_config(stick_config);
|
||||||
|
|
||||||
|
let angles = stick_config.angles;
|
||||||
|
|
||||||
|
let (stripped_cal_points_x, stripped_cal_points_y) =
|
||||||
|
strip_cal_points(cal_points_x, cal_points_y);
|
||||||
|
|
||||||
|
let stripped_cleaned_calibration = CleanedCalibrationPoints::from_temp_calibration_points(
|
||||||
|
&stripped_cal_points_x,
|
||||||
|
&stripped_cal_points_y,
|
||||||
|
&DEFAULT_ANGLES,
|
||||||
|
);
|
||||||
|
|
||||||
|
let linearized_calibration =
|
||||||
|
LinearizedCalibration::from_calibration_points(&stripped_cleaned_calibration);
|
||||||
|
|
||||||
|
stick_params.fit_coeffs = XyValuePair {
|
||||||
|
x: linearized_calibration.fit_coeffs.x.map(|e| e as f32),
|
||||||
|
y: linearized_calibration.fit_coeffs.y.map(|e| e as f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
let notch_calibration = NotchCalibration::from_cleaned_and_linearized_calibration(
|
||||||
|
&stripped_cleaned_calibration,
|
||||||
|
&linearized_calibration,
|
||||||
|
);
|
||||||
|
|
||||||
|
stick_params.affine_coeffs = notch_calibration.affine_coeffs;
|
||||||
|
stick_params.boundary_angles = notch_calibration.boundary_angles;
|
||||||
|
|
||||||
|
let original_cleaned_calibration = CleanedCalibrationPoints::from_temp_calibration_points(
|
||||||
|
cal_points_x,
|
||||||
|
cal_points_y,
|
||||||
|
&DEFAULT_ANGLES,
|
||||||
|
);
|
||||||
|
|
||||||
|
let (transformed_cal_points_x, transformed_cal_points_y) = transform_cal_points(
|
||||||
|
&original_cleaned_calibration.cleaned_points.x,
|
||||||
|
&original_cleaned_calibration.cleaned_points.y,
|
||||||
|
&stick_params,
|
||||||
|
stick_config,
|
||||||
|
);
|
||||||
|
|
||||||
|
let measured_notch_angles =
|
||||||
|
compute_stick_angles(&transformed_cal_points_x, &transformed_cal_points_y);
|
||||||
|
|
||||||
|
let cleaned_with_measured_notch_angles =
|
||||||
|
CleanedCalibrationPoints::from_temp_calibration_points(
|
||||||
|
cal_points_x,
|
||||||
|
cal_points_y,
|
||||||
|
&measured_notch_angles,
|
||||||
|
);
|
||||||
|
|
||||||
|
let cleaned_notch_angles = clean_notches(
|
||||||
|
&measured_notch_angles,
|
||||||
|
&cleaned_with_measured_notch_angles.notch_status,
|
||||||
|
);
|
||||||
|
|
||||||
|
let cleaned_full = CleanedCalibrationPoints::from_temp_calibration_points(
|
||||||
|
cal_points_x,
|
||||||
|
cal_points_y,
|
||||||
|
&cleaned_notch_angles,
|
||||||
|
);
|
||||||
|
|
||||||
|
let linearized_full = LinearizedCalibration::from_calibration_points(&cleaned_full);
|
||||||
|
|
||||||
|
stick_params.fit_coeffs = XyValuePair {
|
||||||
|
x: linearized_full.fit_coeffs.x.map(|e| e as f32),
|
||||||
|
y: linearized_full.fit_coeffs.y.map(|e| e as f32),
|
||||||
|
};
|
||||||
|
|
||||||
|
let notch_calibrate_full = NotchCalibration::from_cleaned_and_linearized_calibration(
|
||||||
|
&cleaned_full,
|
||||||
|
&linearized_full,
|
||||||
|
);
|
||||||
|
|
||||||
|
stick_params.affine_coeffs = notch_calibrate_full.affine_coeffs;
|
||||||
|
stick_params.boundary_angles = notch_calibrate_full.boundary_angles;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
stick_params,
|
||||||
|
measured_notch_angles,
|
||||||
|
notch_angles: cleaned_notch_angles,
|
||||||
|
cleaned_calibration: cleaned_full,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets notches to measured values if absent.
|
||||||
|
fn clean_notches(
|
||||||
|
measured_notch_angles: &[f32; NO_OF_NOTCHES],
|
||||||
|
notch_status: &[NotchStatus; NO_OF_NOTCHES],
|
||||||
|
) -> [f32; NO_OF_NOTCHES] {
|
||||||
|
let mut out = [0f32; NO_OF_NOTCHES];
|
||||||
|
|
||||||
|
for i in 0..NO_OF_NOTCHES {
|
||||||
|
if notch_status[i] == NotchStatus::TertInactive {
|
||||||
|
out[i] = measured_notch_angles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn angle_on_sphere(x: f32, y: f32) -> f32 {
|
||||||
|
let xx = sinf(x * MAX_STICK_ANGLE / 100.) * cosf(y * MAX_STICK_ANGLE / 100.);
|
||||||
|
let yy = cosf(x * MAX_STICK_ANGLE / 100.) * sinf(y * MAX_STICK_ANGLE / 100.);
|
||||||
|
match atan2f(yy, xx) {
|
||||||
|
a if a < 0. => a + 2. * PI,
|
||||||
|
a => a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_stick_angles(
|
||||||
|
x_in: &[f32; NO_OF_NOTCHES + 1],
|
||||||
|
y_in: &[f32; NO_OF_NOTCHES + 1],
|
||||||
|
) -> [f32; NO_OF_NOTCHES] {
|
||||||
|
let mut angles = [0f32; NO_OF_NOTCHES];
|
||||||
|
|
||||||
|
for i in 0..NO_OF_NOTCHES {
|
||||||
|
if i % 2 == 0 {
|
||||||
|
angles[i] = DEFAULT_ANGLES[i];
|
||||||
|
} else {
|
||||||
|
angles[i] = angle_on_sphere(x_in[i], y_in[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angles
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transform_cal_points(
|
||||||
|
cal_points_x: &[f32; NO_OF_NOTCHES + 1],
|
||||||
|
cal_points_y: &[f32; NO_OF_NOTCHES + 1],
|
||||||
|
stick_params: &StickParams,
|
||||||
|
stick_config: &StickConfig,
|
||||||
|
) -> ([f32; NO_OF_NOTCHES + 1], [f32; NO_OF_NOTCHES + 1]) {
|
||||||
|
let mut transformed_points_x = [0f32; NO_OF_NOTCHES + 1];
|
||||||
|
let mut transformed_points_y = [0f32; NO_OF_NOTCHES + 1];
|
||||||
|
|
||||||
|
for i in 0..NO_OF_NOTCHES + 1 {
|
||||||
|
let x = linearize(cal_points_x[i], &stick_params.fit_coeffs.x);
|
||||||
|
let y = linearize(cal_points_y[i], &stick_params.fit_coeffs.y);
|
||||||
|
let (out_x, out_y) = notch_remap(x, y, stick_params, stick_config, true);
|
||||||
|
transformed_points_x[i] = out_x;
|
||||||
|
transformed_points_y[i] = out_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
(transformed_points_x, transformed_points_y)
|
||||||
|
}
|
||||||
|
/// Removes the notches from un-cleaned cal points
|
||||||
|
/// so we can get the original values of the notches after the affine transform.
|
||||||
|
fn strip_cal_points(
|
||||||
|
cal_points_x: &[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
cal_points_y: &[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
) -> (
|
||||||
|
[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
[f32; NO_OF_CALIBRATION_POINTS],
|
||||||
|
) {
|
||||||
|
let mut stripped_points_x = [0f32; NO_OF_CALIBRATION_POINTS];
|
||||||
|
let mut stripped_points_y = [0f32; NO_OF_CALIBRATION_POINTS];
|
||||||
|
for i in 0..NO_OF_CALIBRATION_POINTS {
|
||||||
|
(stripped_points_x[i], stripped_points_y[i]) = if (i + 1) % 4 == 0 {
|
||||||
|
(cal_points_x[0], cal_points_y[0])
|
||||||
|
} else {
|
||||||
|
(cal_points_x[i], cal_points_y[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(stripped_points_x, stripped_points_y)
|
||||||
|
}
|
||||||
|
|
||||||
fn inverse(in_mat: &[[f32; 3]; 3]) -> [[f32; 3]; 3] {
|
fn inverse(in_mat: &[[f32; 3]; 3]) -> [[f32; 3]; 3] {
|
||||||
let mut out_mat = [[0f32; 3]; 3];
|
let mut out_mat = [[0f32; 3]; 3];
|
||||||
|
|
||||||
|
@ -561,7 +745,7 @@ fn fit_curve<const N: usize, const NCOEFFS: usize>(
|
||||||
|
|
||||||
/// Compute the stick x/y coordinates from a given angle.
|
/// Compute the stick x/y coordinates from a given angle.
|
||||||
/// The stick moves spherically, so it requires 3D trigonometry.
|
/// The stick moves spherically, so it requires 3D trigonometry.
|
||||||
fn calc_stick_values(angle: f32) -> (f32, f32) {
|
pub fn calc_stick_values(angle: f32) -> (f32, f32) {
|
||||||
let x =
|
let x =
|
||||||
100. * atan2f(sinf(MAX_STICK_ANGLE) * cosf(angle), cosf(MAX_STICK_ANGLE)) / MAX_STICK_ANGLE;
|
100. * atan2f(sinf(MAX_STICK_ANGLE) * cosf(angle), cosf(MAX_STICK_ANGLE)) / MAX_STICK_ANGLE;
|
||||||
let y =
|
let y =
|
||||||
|
@ -571,7 +755,7 @@ fn calc_stick_values(angle: f32) -> (f32, f32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link_section = ".time_critical.linearize"]
|
#[link_section = ".time_critical.linearize"]
|
||||||
pub fn linearize(point: f32, coefficients: &[f32; 4]) -> f32 {
|
pub fn linearize(point: f32, coefficients: &[f32; NUM_COEFFS]) -> f32 {
|
||||||
coefficients[0] * (point * point * point)
|
coefficients[0] * (point * point * point)
|
||||||
+ coefficients[1] * (point * point)
|
+ coefficients[1] * (point * point)
|
||||||
+ coefficients[2] * point
|
+ coefficients[2] * point
|
||||||
|
@ -583,8 +767,7 @@ pub fn notch_remap(
|
||||||
x_in: f32,
|
x_in: f32,
|
||||||
y_in: f32,
|
y_in: f32,
|
||||||
stick_params: &StickParams,
|
stick_params: &StickParams,
|
||||||
controller_config: &ControllerConfig,
|
stick_config: &StickConfig,
|
||||||
which_stick: Stick,
|
|
||||||
is_calibrating: bool,
|
is_calibrating: bool,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
//determine the angle between the x unit vector and the current position vector
|
//determine the angle between the x unit vector and the current position vector
|
||||||
|
@ -606,10 +789,7 @@ pub fn notch_remap(
|
||||||
NO_OF_NOTCHES - 1
|
NO_OF_NOTCHES - 1
|
||||||
};
|
};
|
||||||
|
|
||||||
let stick_scale = match which_stick {
|
let stick_scale = stick_config.analog_scaler as f32 / 100.;
|
||||||
Stick::ControlStick => controller_config.astick_config.analog_scaler as f32 / 100.,
|
|
||||||
Stick::CStick => controller_config.cstick_config.analog_scaler as f32 / 100.,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut x_out = stick_scale
|
let mut x_out = stick_scale
|
||||||
* (stick_params.affine_coeffs[region][0] * x_in
|
* (stick_params.affine_coeffs[region][0] * x_in
|
||||||
|
@ -619,11 +799,6 @@ pub fn notch_remap(
|
||||||
+ stick_params.affine_coeffs[region][3] * y_in);
|
+ stick_params.affine_coeffs[region][3] * y_in);
|
||||||
|
|
||||||
if !is_calibrating {
|
if !is_calibrating {
|
||||||
let stick_config = match which_stick {
|
|
||||||
Stick::ControlStick => &controller_config.astick_config,
|
|
||||||
Stick::CStick => &controller_config.cstick_config,
|
|
||||||
};
|
|
||||||
|
|
||||||
if stick_config.cardinal_snapping > 0 {
|
if stick_config.cardinal_snapping > 0 {
|
||||||
if fabsf(x_out) < stick_config.cardinal_snapping as f32 + 0.5 && fabsf(y_out) >= 79.5 {
|
if fabsf(x_out) < stick_config.cardinal_snapping as f32 + 0.5 && fabsf(y_out) >= 79.5 {
|
||||||
x_out = 0.;
|
x_out = 0.;
|
||||||
|
|
Loading…
Reference in a new issue