use ph_haptics::{DEFAULT_ERM_PROFILE, DEFAULT_LRA_PROFILE};
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct Profile {
pub(crate) name: String,
pub(crate) motor: Motor,
pub(crate) kick_ms: u16,
pub(crate) kick_level: f32,
pub(crate) min_level: f32,
pub(crate) max_level: f32,
pub(crate) gamma: Option<String>,
pub(crate) ramp_step_ms: u16,
pub(crate) min_dt_ms: u16,
pub(crate) duty_step: f32,
}
impl Profile {
pub(crate) fn default_erm_named(name: &str) -> Self {
let base = DEFAULT_ERM_PROFILE;
Self {
name: name.to_owned(),
motor: Motor::Erm,
kick_ms: base.kick_ms,
kick_level: frac_u8_to_percent(base.kick_frac),
min_level: frac_u8_to_percent(base.min_run_frac),
max_level: frac_u8_to_percent(base.max_frac),
gamma: None,
ramp_step_ms: base.ramp_step_ms,
min_dt_ms: base.min_dt_ms,
duty_step: frac_u8_to_percent(base.duty_step),
}
}
pub(crate) fn default_lra_named(name: &str) -> Self {
let base = DEFAULT_LRA_PROFILE;
Self {
name: name.to_owned(),
motor: Motor::Lra,
kick_ms: base.kick_ms,
kick_level: frac_u8_to_percent(base.kick_frac),
min_level: frac_u8_to_percent(base.min_run_frac),
max_level: frac_u8_to_percent(base.max_frac),
gamma: None,
ramp_step_ms: base.ramp_step_ms,
min_dt_ms: base.min_dt_ms,
duty_step: frac_u8_to_percent(base.duty_step),
}
}
pub(crate) fn percent_to_frac_u8(pct: f32) -> u8 {
(pct / 100.0 * 255.0).round() as u8
}
}
fn frac_u8_to_percent(frac: u8) -> f32 {
frac as f32 / 255.0 * 100.0
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Motor {
Erm,
Lra,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum LoopMode {
Once,
Forever,
Count(u32),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum Rounding {
Nearest,
Floor,
Ceil,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum CurveRef {
Named(String),
Gamma,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Inst {
Ramp {
line: u32,
duration_ms: u32,
from: u16,
to: u16,
curve: CurveRef,
step: Option<u16>,
rounding: Option<Rounding>,
min_dt_ms: Option<u32>,
lra_hz: Option<u16>,
lra_hz_to: Option<u16>,
},
Hold {
line: u32,
duration_ms: u32,
level: u16,
lra_hz: Option<u16>,
},
Pause {
line: u32,
duration_ms: u32,
},
}
impl Inst {
pub(crate) fn line(&self) -> u32 {
match self {
Self::Ramp { line, .. } | Self::Hold { line, .. } | Self::Pause { line, .. } => *line,
}
}
pub(crate) fn duration_ms(&self) -> u32 {
match self {
Self::Ramp { duration_ms, .. }
| Self::Hold { duration_ms, .. }
| Self::Pause { duration_ms, .. } => *duration_ms,
}
}
pub(crate) fn lra_option(&self) -> Option<&'static str> {
match self {
Self::Ramp {
lra_hz, lra_hz_to, ..
} => {
if lra_hz.is_some() {
Some("lra_hz")
} else if lra_hz_to.is_some() {
Some("lra_hz_to")
} else {
None
}
}
Self::Hold { lra_hz, .. } => lra_hz.is_some().then_some("lra_hz"),
Self::Pause { .. } => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Haptic {
pub(crate) line: u32,
pub(crate) name: String,
pub(crate) motor: Motor,
pub(crate) loop_mode: LoopMode,
pub(crate) profile: Option<String>,
pub(crate) instructions: Vec<Inst>,
}