///|
/// Per-axis affine transform for sensor gains and offsets.
pub struct AxisTransform {
gain_x : Double
gain_y : Double
gain_z : Double
offset_x : Double
offset_y : Double
offset_z : Double
} derive(Debug, Eq)
///|
pub fn AxisTransform::new(
gain_x : Double,
gain_y : Double,
gain_z : Double,
offset_x : Double,
offset_y : Double,
offset_z : Double,
) -> AxisTransform {
{ gain_x, gain_y, gain_z, offset_x, offset_y, offset_z }
}
///|
pub fn AxisTransform::identity() -> AxisTransform {
AxisTransform::new(1.0, 1.0, 1.0, 0.0, 0.0, 0.0)
}
///|
pub fn transform_force_sample(
sample : ForceSample,
transform : AxisTransform,
) -> ForceSample {
ForceSample::new(
sample.timestamp,
Vec3::new(
(sample.force.x - transform.offset_x) * transform.gain_x,
(sample.force.y - transform.offset_y) * transform.gain_y,
(sample.force.z - transform.offset_z) * transform.gain_z,
),
sample.moment,
sample.cop,
)
}
///|
/// Applies an affine transform while preserving moments, COP, and timestamps.
pub fn transform_trial_axes(
trial : ForceTrial,
transform : AxisTransform,
) -> ForceTrial {
let samples : Array[ForceSample] = Array::new(capacity=trial.samples.length())
for sample in trial.samples {
samples.push(transform_force_sample(sample, transform))
}
ForceTrial::new(trial.metadata, samples)
}
///|
pub fn compute_force_magnitude_series(trial : ForceTrial) -> Array[Double] {
let result : Array[Double] = Array::new(capacity=trial.samples.length())
for sample in trial.samples {
result.push(sample.force.magnitude())
}
result
}
///|
/// Projects each force vector onto a normalized axis; a zero axis gives zero.
pub fn project_force_series(trial : ForceTrial, axis : Vec3) -> Array[Double] {
let magnitude = axis.magnitude()
let result : Array[Double] = Array::new(capacity=trial.samples.length())
if magnitude <= 0.000001 || magnitude.is_nan() || magnitude.is_inf() {
for _sample in trial.samples {
result.push(0.0)
}
return result
}
let normalized = axis.scale(1.0 / magnitude)
for sample in trial.samples {
result.push(sample.force.dot(normalized))
}
result
}
///|
pub struct ForceMagnitudeSummary {
count : Int
mean_magnitude_n : Double
rms_magnitude_n : Double
peak_magnitude_n : Double
p95_magnitude_n : Double
} derive(Debug, Eq)
///|
pub fn summarize_force_magnitude(
values : Array[Double],
) -> ForceMagnitudeSummary {
let summary = summarize_signal(values)
{
count: summary.count,
mean_magnitude_n: summary.mean,
rms_magnitude_n: summary.rms,
peak_magnitude_n: summary.maximum,
p95_magnitude_n: quantile(values, 0.95),
}
}