Refine sample delivery force UI and mapping
This commit is contained in:
@@ -199,7 +199,7 @@ pub async fn serial_connect(
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!("serial task exited with error: {error}");
|
||||
log::error!("serial task exited with error: {error}");
|
||||
}
|
||||
|
||||
let manager = task_app.state::<SerialConnectionState>();
|
||||
|
||||
@@ -62,7 +62,6 @@ impl Codec<TestFrame> for TestCodec {
|
||||
continue;
|
||||
}
|
||||
let dts = elapsed_millis(session_started_at);
|
||||
println!("dts_ms: {dts}");
|
||||
frames.push(TestFrame {
|
||||
header: [0xAA, 0x55],
|
||||
cmd: cmd,
|
||||
@@ -244,11 +243,10 @@ mod tests {
|
||||
fn test_read_csv_basic() -> anyhow::Result<()> {
|
||||
let mut rdr = Reader::from_path("recording_20260329_125238.csv")?;
|
||||
let headers = rdr.headers()?;
|
||||
println!("headers: {:?}", headers);
|
||||
|
||||
for result in rdr.records() {
|
||||
let record = result?;
|
||||
println!("record: {:?}", record);
|
||||
let _ = record;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -17,6 +17,7 @@ const POST_STABLE_MS: f32 = 200.0;
|
||||
const POST_STABLE_THRESH: f32 = 0.1;
|
||||
const COP_LPF_ALPHA: f32 = 0.25;
|
||||
const EPSILON: f32 = 1e-8;
|
||||
const DISPLAY_ANGLE_OFFSET_DEG: f32 = -90.0;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct PztSpatialAnalysis {
|
||||
@@ -359,10 +360,7 @@ impl PztProcessor {
|
||||
|
||||
fn compute_vector_angle(x: f32, y: f32) -> (f32, f32) {
|
||||
let magnitude = (x * x + y * y).sqrt();
|
||||
let mut angle = y.atan2(x + EPSILON).to_degrees();
|
||||
if angle < 0.0 {
|
||||
angle += 360.0;
|
||||
}
|
||||
let angle = (y.atan2(x + EPSILON).to_degrees() + DISPLAY_ANGLE_OFFSET_DEG).rem_euclid(360.0);
|
||||
(angle, magnitude)
|
||||
}
|
||||
|
||||
@@ -481,7 +479,7 @@ mod tests {
|
||||
assert!(analysis.contact_active);
|
||||
assert!(analysis.reportable);
|
||||
assert!(analysis.magnitude > 0.0);
|
||||
assert!(analysis.angle_deg <= 45.0 || analysis.angle_deg >= 315.0);
|
||||
assert!(analysis.angle_deg >= 225.0 && analysis.angle_deg <= 315.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -358,88 +358,23 @@ fn build_display_values(
|
||||
spatial_force: Option<HudSpatialForce>,
|
||||
) -> Option<Vec<i32>> {
|
||||
let summary = values.iter().copied().sum::<i32>();
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
let g1 = raw_to_g1(summary as u32);
|
||||
println!("raw_to_g1 map: {g1}");
|
||||
}
|
||||
chart_state.record_spatial_force(spatial_force);
|
||||
|
||||
match ad_to_x(summary as f64) {
|
||||
Some(x) => {
|
||||
if x <= MIN_DISPLAY_FORCE {
|
||||
let zero_values = vec![0; values.len()];
|
||||
chart_state.record_summary(0.0);
|
||||
chart_state.record_pressure_matrix(&zero_values);
|
||||
return Some(vec![0]);
|
||||
}
|
||||
|
||||
chart_state.record_pressure_matrix(values);
|
||||
chart_state.record_summary(x as f32);
|
||||
Some(vec![x.round() as i32])
|
||||
}
|
||||
None => {
|
||||
chart_state.record_pressure_matrix(values);
|
||||
chart_state.record_summary(MAX_DISPLAY_FORCE as f32);
|
||||
Some(vec![MAX_DISPLAY_FORCE.round() as i32])
|
||||
}
|
||||
let x = raw_to_g1(summary as u32).min(MAX_DISPLAY_FORCE);
|
||||
if x <= MIN_DISPLAY_FORCE {
|
||||
let zero_values = vec![0; values.len()];
|
||||
chart_state.record_summary(0.0);
|
||||
chart_state.record_pressure_matrix(&zero_values);
|
||||
return Some(vec![0]);
|
||||
}
|
||||
|
||||
chart_state.record_pressure_matrix(values);
|
||||
chart_state.record_summary(x as f32);
|
||||
Some(vec![x.round() as i32])
|
||||
}
|
||||
|
||||
const MIN_DISPLAY_FORCE: f64 = 0.1;
|
||||
const MAX_DISPLAY_FORCE: f64 = 25.6;
|
||||
const QUADRATIC_A: f64 = -375.2;
|
||||
const QUADRATIC_B: f64 = 25880.0;
|
||||
const QUADRATIC_C: f64 = 52150.0;
|
||||
|
||||
fn ad_to_x(ad: f64) -> Option<f64> {
|
||||
const CUBIC_LIMIT: f64 = 6.57;
|
||||
const EPSILON: f64 = 0.000_001;
|
||||
|
||||
let cubic_min = ad_from_cubic_x(0.0);
|
||||
if ad <= cubic_min {
|
||||
return Some(0.0);
|
||||
}
|
||||
|
||||
let cubic_threshold = ad_from_cubic_x(CUBIC_LIMIT);
|
||||
if ad <= cubic_threshold {
|
||||
return Some(solve_monotonic_ad(ad, 0.0, CUBIC_LIMIT, ad_from_cubic_x));
|
||||
}
|
||||
|
||||
let quadratic_vertex = -QUADRATIC_B / (2.0 * QUADRATIC_A);
|
||||
let quadratic_max = ad_from_quadratic_x(quadratic_vertex);
|
||||
if ad - EPSILON > quadratic_max {
|
||||
return Some(MAX_DISPLAY_FORCE);
|
||||
}
|
||||
|
||||
Some(solve_monotonic_ad(
|
||||
ad,
|
||||
CUBIC_LIMIT,
|
||||
quadratic_vertex,
|
||||
ad_from_quadratic_x,
|
||||
))
|
||||
}
|
||||
|
||||
fn solve_monotonic_ad(ad: f64, mut low: f64, mut high: f64, f: fn(f64) -> f64) -> f64 {
|
||||
for _ in 0..80 {
|
||||
let mid = (low + high) / 2.0;
|
||||
if f(mid) < ad {
|
||||
low = mid;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
|
||||
(low + high) / 2.0
|
||||
}
|
||||
|
||||
fn ad_from_cubic_x(x: f64) -> f64 {
|
||||
-5.732 * x.powi(3) - 131.5 * x.powi(2) + 31980.0 * x + 13490.0
|
||||
}
|
||||
|
||||
fn ad_from_quadratic_x(x: f64) -> f64 {
|
||||
QUADRATIC_A * x.powi(2) + QUADRATIC_B * x + QUADRATIC_C
|
||||
}
|
||||
|
||||
#[cfg(feature = "devkit")]
|
||||
fn push_devkit_frame(app: &AppHandle, values: &[i32], dts_ms: u64, resultant_force: f64) {
|
||||
@@ -493,8 +428,6 @@ fn infer_matrix_shape(len: usize) -> (u32, u32) {
|
||||
(best.0 as u32, best.1 as u32)
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
#[allow(dead_code)]
|
||||
fn raw_to_g1(raw: u32) -> f64 {
|
||||
const X: [u32; 12] = [
|
||||
0, 20829, 102371, 132956, 165568, 182033, 217263, 263098, 283747, 365120, 410556, 477190
|
||||
|
||||
@@ -41,9 +41,7 @@
|
||||
"template": "nsis/installer.nsi"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
"resources/je-skin-devkit-server.exe"
|
||||
]
|
||||
"resources": []
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
|
||||
Reference in New Issue
Block a user