Skip to content

Commit d4b0426

Browse files
half2mecujomalainey
authored andcommitted
Add Speed & Cadence Profile
1 parent e9fa40d commit d4b0426

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

ant/src/plus/profiles/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
// except according to those terms.
88

99
pub mod heart_rate;
10+
pub mod speed_and_cadence;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use derive_new::new;
2+
use packed_struct::derive::PackedStruct;
3+
4+
pub const DEVICE_TYPE: u8 = 121;
5+
6+
#[derive(PackedStruct, new, PartialEq, Copy, Clone, Debug)]
7+
#[packed_struct(endian = "lsb")]
8+
pub struct SpeedAndCadence {
9+
/// Time of the last valid bike cadence event (1/1024 sec)
10+
pub cadence_event_time: u16,
11+
12+
/// Total number of pedal revolutions
13+
pub cadence_revolution_count: u16,
14+
15+
/// Time of the last valid bike speed event (1/1024 sec)
16+
pub speed_event_time: u16,
17+
18+
/// Total number of wheel revolutions
19+
pub speed_revolution_count: u16,
20+
}
21+
22+
impl SpeedAndCadence {
23+
/// Calculates the average cadence (rpm)
24+
pub fn cadence(a: SpeedAndCadence, b: SpeedAndCadence) -> Option<f32> {
25+
let time_delta = b.cadence_event_time.wrapping_sub(a.cadence_event_time);
26+
if time_delta == 0 {
27+
return None;
28+
}
29+
let rev_delta = b
30+
.cadence_revolution_count
31+
.wrapping_sub(a.cadence_revolution_count);
32+
Some((rev_delta as f32) * 1024.0 * 60.0 / (time_delta as f32))
33+
}
34+
35+
/// Calculates the number of wheel revolutions
36+
pub fn wheel_revolutions(a: SpeedAndCadence, b: SpeedAndCadence) -> Option<u16> {
37+
let time_delta = b.speed_event_time.wrapping_sub(a.speed_event_time);
38+
if time_delta == 0 {
39+
return None;
40+
}
41+
Some(
42+
b.speed_revolution_count
43+
.wrapping_sub(a.speed_revolution_count),
44+
)
45+
}
46+
47+
/// Calculates the distance (m) covered between two messages
48+
pub fn distance(a: SpeedAndCadence, b: SpeedAndCadence, circumference: f32) -> Option<f32> {
49+
if let Some(revs) = Self::wheel_revolutions(a, b) {
50+
return Some(revs as f32 * circumference);
51+
}
52+
None
53+
}
54+
55+
/// Calculates average speed in revolutions per sec (useful when circumference is not known)
56+
pub fn speed_revs_per_sec(a: SpeedAndCadence, b: SpeedAndCadence) -> Option<f32> {
57+
if let Some(revs) = Self::wheel_revolutions(a, b) {
58+
let time_delta = b.speed_event_time.wrapping_sub(a.speed_event_time);
59+
return Some(revs as f32 * 1024.0 / time_delta as f32);
60+
}
61+
None
62+
}
63+
64+
/// Calculates average speed (m/s)
65+
pub fn speed(a: SpeedAndCadence, b: SpeedAndCadence, circumference: f32) -> Option<f32> {
66+
if let Some(speed) = Self::speed_revs_per_sec(a, b) {
67+
return Some(speed * circumference);
68+
}
69+
None
70+
}
71+
}
72+
73+
#[cfg(test)]
74+
mod tests {
75+
use super::*;
76+
use packed_struct::PackedStruct;
77+
78+
#[test]
79+
fn unpack() {
80+
let raw = [0x09, 0x91, 0xd5, 0x08, 0xd7, 0x90, 0x42, 0x1b];
81+
let foo = SpeedAndCadence::unpack(&raw).unwrap();
82+
assert_eq!(foo.cadence_event_time, 37129);
83+
assert_eq!(foo.cadence_revolution_count, 2261);
84+
assert_eq!(foo.speed_event_time, 37079);
85+
assert_eq!(foo.speed_revolution_count, 6978);
86+
}
87+
88+
#[test]
89+
fn cadence() {
90+
// If the timer hasn't changed we should return None
91+
let a = SpeedAndCadence::new(0, 0, 0, 0);
92+
assert_eq!(SpeedAndCadence::cadence(a, a), None);
93+
94+
let a = SpeedAndCadence::new(0, 0, 0, 0);
95+
let b = SpeedAndCadence::new(1024, 1, 0, 0);
96+
assert!((SpeedAndCadence::cadence(a, b).unwrap() - 60.0).abs() <= f32::EPSILON);
97+
98+
// test counter roll-over
99+
let a = SpeedAndCadence::new(u16::MAX, u16::MAX, 0, 0);
100+
let b = SpeedAndCadence::new(1023, 0, 0, 0);
101+
assert!((SpeedAndCadence::cadence(a, b).unwrap() - 60.0).abs() <= f32::EPSILON);
102+
}
103+
104+
#[test]
105+
fn speed() {
106+
// If the timer hasn't changed we should return None
107+
let a = SpeedAndCadence::new(0, 0, 0, 0);
108+
assert_eq!(SpeedAndCadence::speed(a, a, 1.0), None);
109+
110+
let a = SpeedAndCadence::new(0, 0, 0, 0);
111+
let b = SpeedAndCadence::new(0, 0, 1024, 1);
112+
assert!((SpeedAndCadence::speed(a, b, 1.0).unwrap() - 1.0).abs() <= f32::EPSILON);
113+
114+
// test counter roll-over
115+
let a = SpeedAndCadence::new(0, 0, u16::MAX, u16::MAX);
116+
let b = SpeedAndCadence::new(0, 0, 1023, 0);
117+
assert!((SpeedAndCadence::speed(a, b, 1.0).unwrap() - 1.0).abs() <= f32::EPSILON);
118+
}
119+
}

0 commit comments

Comments
 (0)