Scaffold workspace, shared types and requirements spec
Cargo workspace with core/ble/fit/probe crates. crates/core/src/types.rs is the fixed contract between the BLE layer, ride engine and UI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
//! Virtual speed from measured power (§5.7 of REQUIREMENTS.md).
|
||||
//!
|
||||
//! The app owns the physics rather than trusting the trainer's reported speed
|
||||
//! (FR-7.1). This makes ride behaviour reproducible in tests, independent of
|
||||
//! the trainer's internal mass assumptions, and is a prerequisite for virtual
|
||||
//! gearing later.
|
||||
//!
|
||||
//! Per tick:
|
||||
//! ```text
|
||||
//! F_propulsive = (P × drivetrain_efficiency) / max(v, v_min)
|
||||
//! F_gravity = m × g × sin(atan(gradient))
|
||||
//! F_rolling = m × g × Crr × cos(atan(gradient))
|
||||
//! F_aero = ½ × ρ × CdA × v²
|
||||
//! a = (F_propulsive − F_gravity − F_rolling − F_aero) / m
|
||||
//! v += a × Δt (clamped at ≥ 0)
|
||||
//! ```
|
||||
|
||||
use crate::types::RiderConfig;
|
||||
|
||||
pub const GRAVITY: f32 = 9.80665;
|
||||
|
||||
/// Speed floor used to keep `P / v` finite at a standstill. Also the speed
|
||||
/// below which the rider is considered stopped.
|
||||
pub const MIN_SPEED_MPS: f32 = 0.5;
|
||||
|
||||
/// Evolving physical state of the virtual rider.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct PhysicsState {
|
||||
/// Virtual speed, metres per second.
|
||||
pub speed_mps: f32,
|
||||
/// Virtual distance travelled, metres.
|
||||
pub distance_m: f64,
|
||||
/// Cumulative elevation gained, metres.
|
||||
pub elevation_gain_m: f32,
|
||||
}
|
||||
|
||||
impl PhysicsState {
|
||||
/// Advance the simulation by `dt` seconds under `power_w` at `gradient_pct`.
|
||||
///
|
||||
/// Must model inertia (FR-7.3) — speed accelerates toward equilibrium
|
||||
/// rather than snapping to it — and must never produce negative speed,
|
||||
/// NaN, or unbounded values for any finite input.
|
||||
pub fn step(&mut self, power_w: f32, gradient_pct: f32, cfg: &RiderConfig, dt: f32) {
|
||||
let _ = (power_w, gradient_pct, cfg, dt);
|
||||
todo!("implemented in crates/core/src/physics.rs — see AGENT task A")
|
||||
}
|
||||
|
||||
pub fn speed_kph(&self) -> f32 {
|
||||
self.speed_mps * 3.6
|
||||
}
|
||||
|
||||
pub fn is_moving(&self) -> bool {
|
||||
self.speed_mps > MIN_SPEED_MPS
|
||||
}
|
||||
}
|
||||
|
||||
/// Steady-state speed for a given power and gradient — the speed at which
|
||||
/// propulsive and resistive forces balance. Useful for tests and for sanity
|
||||
/// checks on the resistance curve later.
|
||||
pub fn equilibrium_speed_mps(power_w: f32, gradient_pct: f32, cfg: &RiderConfig) -> f32 {
|
||||
let _ = (power_w, gradient_pct, cfg);
|
||||
todo!("implemented in crates/core/src/physics.rs — see AGENT task A")
|
||||
}
|
||||
Reference in New Issue
Block a user