pub enum DspStatus {
V80(DspStatus80),
V46(DspStatus46),
}Expand description
DSP status response. Type 0xAA, SRC=0x40.
Two formats keyed by payload[0] (version byte):
0x80(128): Gen1 Mevo+, 129 bytes. Fully decoded.0x46(70): Gen2 Mevo, 71 bytes. Field layout differs; raw bytes preserved.
Variants§
V80(DspStatus80)
Gen1 Mevo+ (version 0x80, 129 bytes). All fields decoded.
V46(DspStatus46)
Gen2 Mevo (version 0x46, 71 bytes). Raw payload preserved until field mapping is done.
Implementations§
Source§impl DspStatus
impl DspStatus
pub fn decode(payload: &[u8]) -> Result<Self>
Sourcepub fn battery_percent(&self) -> u8
pub fn battery_percent(&self) -> u8
Battery percentage (0-100). Returns 0 for Gen2 (field offsets TBD).
Examples found in repository?
examples/event_loop.rs (line 125)
109fn print_handshake(dsp: &DspSync, avr: &AvrSync, pi: &PiSync) {
110 println!("--- Handshake complete ---");
111 println!(
112 " DSP: type=0x{:02X} pcb={}",
113 dsp.hw_info.dsp_type, dsp.hw_info.pcb
114 );
115 println!(" DSP info: {}", dsp.dev_info.text);
116 println!(" AVR info: {}", avr.dev_info.text);
117 println!(
118 " AVR tilt={:.1}° roll={:.1}°",
119 avr.status.tilt, -avr.status.roll
120 );
121 println!(" PI info: {}", pi.dev_info.text);
122 println!(" SSID: {} password: {}", pi.ssid, pi.password);
123 println!(
124 " Battery: {}% {}",
125 dsp.status.battery_percent(),
126 if dsp.status.external_power() {
127 "(plugged in)"
128 } else {
129 ""
130 },
131 );
132}
133
134// ---------------------------------------------------------------------------
135// Main
136// ---------------------------------------------------------------------------
137
138fn main() {
139 if let Err(e) = run() {
140 eprintln!("error: {e}");
141 process::exit(1);
142 }
143}
144
145fn send_action(conn: &mut BinaryConnection<std::net::TcpStream>, action: Action) -> Result<(), ConnError> {
146 seq::send_action(conn, action)
147}
148
149fn run() -> Result<(), ConnError> {
150 // 1. Connect
151 let addr: SocketAddr = DEFAULT_ADDR.parse().unwrap();
152 println!("Connecting to {addr}...");
153 let mut conn = BinaryConnection::connect_timeout(&addr, Duration::from_secs(5))?;
154 println!("Connected.");
155
156 // Wire-level trace callbacks.
157 conn.set_on_send(|cmd, dest| println!("\n>> {:?} [{}]", cmd, cmd.debug_hex(dest)));
158 conn.set_on_recv(|env| println!("\n<< {env:?}"));
159
160 // 2. Handshake (blocking via drive())
161 println!("DSP sync...");
162 let dsp = seq::sync_dsp(&mut conn)?;
163 println!("AVR sync...");
164 let avr = seq::sync_avr(&mut conn)?;
165 println!("PI sync...");
166 let pi = seq::sync_pi(&mut conn)?;
167 print_handshake(&dsp, &avr, &pi);
168
169 // 3. Configure
170 let settings = default_avr_settings();
171 println!(
172 "Configuring... mode=Outdoor ball=RCT range={}mm height={}mm",
173 settings.radar_cal.range_mm, settings.radar_cal.height_mm,
174 );
175 seq::configure_avr(&mut conn, &settings)?;
176 seq::configure_camera(&mut conn, &default_cam_config())?;
177
178 // 4. Arm
179 seq::arm(&mut conn)?;
180 println!("=== ARMED — waiting for shots ===");
181
182 // 5. Non-blocking event loop
183 conn.stream_mut()
184 .set_read_timeout(Some(Duration::from_millis(1)))?;
185 let mut last_keepalive = Instant::now();
186 let mut shot: Option<ShotSequencer> = None;
187
188 loop {
189 if let Some(env) = conn.recv()? {
190 // If a shot sequence is active, feed it first.
191 if let Some(ref mut s) = shot {
192 let actions = s.feed(&env);
193 for a in actions {
194 send_action(&mut conn, a)?;
195 }
196 if s.is_complete() {
197 let data = shot.take().unwrap().into_result();
198 print_shot(&data);
199 continue;
200 }
201 }
202
203 // Handle messages not consumed by the shot sequencer.
204 match &env.message {
205 Message::ShotText(st) if st.is_processed() && shot.is_none() => {
206 println!(" Shot processed — starting shot sequence...");
207 let (s, actions) = ShotSequencer::new();
208 for a in actions {
209 send_action(&mut conn, a)?;
210 }
211 shot = Some(s);
212 }
213
214 // --- Flight result (primary) ---
215 Message::FlightResult(r) => {
216 println!(" Flight result (shot #{}):", r.total);
217 println!(
218 " Ball speed: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
219 ms_to_mph(r.launch_speed),
220 r.launch_elevation,
221 r.launch_azimuth,
222 );
223 println!(
224 " Carry: {:.1} yd Total: {:.1} yd Height: {:.1} ft",
225 m_to_yd(r.carry_distance),
226 m_to_yd(r.total_distance),
227 m_to_ft(r.max_height),
228 );
229 println!(
230 " Backspin: {} rpm Sidespin: {} rpm",
231 r.backspin_rpm, r.sidespin_rpm,
232 );
233 println!(
234 " Club speed: {:.1} mph Path: {:.1}° AoA: {:.1}°",
235 ms_to_mph(r.clubhead_speed),
236 r.club_strike_direction,
237 r.club_attack_angle,
238 );
239 println!(
240 " Face: {:.1}° Loft: {:.1}°",
241 r.club_face_angle, r.club_effective_loft,
242 );
243 }
244
245 // --- Flight result v1 (early) ---
246 Message::FlightResultV1(r) => {
247 println!(" Flight result v1 (shot #{}):", r.total);
248 println!(
249 " Ball: {:.1} mph Club: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
250 ms_to_mph(r.ball_velocity),
251 ms_to_mph(r.club_velocity),
252 r.elevation,
253 r.azimuth,
254 );
255 println!(
256 " Dist: {:.1} yd Height: {:.1} ft Backspin: {} rpm",
257 m_to_yd(r.distance),
258 m_to_ft(r.height),
259 r.backspin_rpm,
260 );
261 }
262
263 // --- Club result ---
264 Message::ClubResult(r) => {
265 println!(" Club result:");
266 println!(
267 " Club speed: {:.1} mph (pre) / {:.1} mph (post) Smash: {:.2}",
268 ms_to_mph(r.pre_club_speed),
269 ms_to_mph(r.post_club_speed),
270 r.smash_factor,
271 );
272 println!(
273 " Path: {:.1}° Face: {:.1}° AoA: {:.1}° Loft: {:.1}°",
274 r.strike_direction, r.face_angle, r.attack_angle, r.dynamic_loft,
275 );
276 println!(
277 " Low point: {:.1} in Club height: {:.1} in",
278 m_to_in(r.club_offset),
279 m_to_in(r.club_height),
280 );
281 }
282
283 // --- Spin result ---
284 Message::SpinResult(r) => {
285 println!(" Spin result:");
286 println!(
287 " Total spin: {} rpm (PM final) Axis: {:.1}° Method: {}",
288 r.pm_spin_final, r.spin_axis, r.spin_method,
289 );
290 println!(
291 " AM: {} rpm PM: {} rpm Launch: {} rpm AOD: {} rpm PLL: {} rpm",
292 r.am_spin, r.pm_spin, r.launch_spin, r.aod_spin, r.pll_spin,
293 );
294 }
295
296 // --- Speed profile ---
297 Message::SpeedProfile(r) => {
298 let peak = r.speeds.iter().copied().fold(0.0_f64, f64::max);
299 println!(
300 " Speed profile: {} samples ({}+{}) peak: {:.1} mph",
301 r.speeds.len(),
302 r.num_pre,
303 r.num_post,
304 ms_to_mph(peak),
305 );
306 }
307
308 // --- Tracking status ---
309 Message::TrackingStatus(r) => {
310 println!(
311 " Tracking: iter={} quality={} prc_count={} trigger_idx={}",
312 r.processing_iteration,
313 r.result_quality,
314 r.prc_tracking_count,
315 r.trigger_idx,
316 );
317 }
318
319 // --- PRC data ---
320 Message::PrcData(r) => {
321 println!(" PRC: seq={} points={}", r.sequence, r.points.len());
322 }
323
324 // --- Club PRC data ---
325 Message::ClubPrc(r) => {
326 println!(" Club PRC: points={}", r.points.len());
327 }
328
329 // --- Camera image available ---
330 Message::CamImageAvail(r) => {
331 println!(
332 " Camera: streaming={} fusion={} video={}",
333 r.streaming_available, r.fusion_available, r.video_available,
334 );
335 }
336
337 // --- DspStatus from keepalive ---
338 Message::DspStatus(s) => {
339 println!(
340 "[keepalive] battery={}%{} temp={:.1}°C",
341 s.battery_percent(),
342 if s.external_power() {
343 " (plugged in)"
344 } else {
345 ""
346 },
347 s.temperature_c(),
348 );
349 }
350 Message::AvrStatus(s) => {
351 println!("[keepalive] tilt={:.1}° roll={:.1}°", s.tilt, -s.roll);
352 }
353
354 // --- Config ack / noise ---
355 Message::ConfigAck(_) | Message::PiStatus(_) => {}
356
357 // --- Everything else: debug line is enough ---
358 _ => {}
359 }
360 }
361
362 // Heartbeats always fire — even during shot sequencing.
363 if last_keepalive.elapsed() >= Duration::from_secs(1) {
364 for a in seq::keepalive_actions() {
365 send_action(&mut conn, a)?;
366 }
367 last_keepalive = Instant::now();
368 }
369 }
370}Sourcepub fn external_power(&self) -> bool
pub fn external_power(&self) -> bool
External power connected. Returns false for Gen2 (field offsets TBD).
Examples found in repository?
examples/event_loop.rs (line 126)
109fn print_handshake(dsp: &DspSync, avr: &AvrSync, pi: &PiSync) {
110 println!("--- Handshake complete ---");
111 println!(
112 " DSP: type=0x{:02X} pcb={}",
113 dsp.hw_info.dsp_type, dsp.hw_info.pcb
114 );
115 println!(" DSP info: {}", dsp.dev_info.text);
116 println!(" AVR info: {}", avr.dev_info.text);
117 println!(
118 " AVR tilt={:.1}° roll={:.1}°",
119 avr.status.tilt, -avr.status.roll
120 );
121 println!(" PI info: {}", pi.dev_info.text);
122 println!(" SSID: {} password: {}", pi.ssid, pi.password);
123 println!(
124 " Battery: {}% {}",
125 dsp.status.battery_percent(),
126 if dsp.status.external_power() {
127 "(plugged in)"
128 } else {
129 ""
130 },
131 );
132}
133
134// ---------------------------------------------------------------------------
135// Main
136// ---------------------------------------------------------------------------
137
138fn main() {
139 if let Err(e) = run() {
140 eprintln!("error: {e}");
141 process::exit(1);
142 }
143}
144
145fn send_action(conn: &mut BinaryConnection<std::net::TcpStream>, action: Action) -> Result<(), ConnError> {
146 seq::send_action(conn, action)
147}
148
149fn run() -> Result<(), ConnError> {
150 // 1. Connect
151 let addr: SocketAddr = DEFAULT_ADDR.parse().unwrap();
152 println!("Connecting to {addr}...");
153 let mut conn = BinaryConnection::connect_timeout(&addr, Duration::from_secs(5))?;
154 println!("Connected.");
155
156 // Wire-level trace callbacks.
157 conn.set_on_send(|cmd, dest| println!("\n>> {:?} [{}]", cmd, cmd.debug_hex(dest)));
158 conn.set_on_recv(|env| println!("\n<< {env:?}"));
159
160 // 2. Handshake (blocking via drive())
161 println!("DSP sync...");
162 let dsp = seq::sync_dsp(&mut conn)?;
163 println!("AVR sync...");
164 let avr = seq::sync_avr(&mut conn)?;
165 println!("PI sync...");
166 let pi = seq::sync_pi(&mut conn)?;
167 print_handshake(&dsp, &avr, &pi);
168
169 // 3. Configure
170 let settings = default_avr_settings();
171 println!(
172 "Configuring... mode=Outdoor ball=RCT range={}mm height={}mm",
173 settings.radar_cal.range_mm, settings.radar_cal.height_mm,
174 );
175 seq::configure_avr(&mut conn, &settings)?;
176 seq::configure_camera(&mut conn, &default_cam_config())?;
177
178 // 4. Arm
179 seq::arm(&mut conn)?;
180 println!("=== ARMED — waiting for shots ===");
181
182 // 5. Non-blocking event loop
183 conn.stream_mut()
184 .set_read_timeout(Some(Duration::from_millis(1)))?;
185 let mut last_keepalive = Instant::now();
186 let mut shot: Option<ShotSequencer> = None;
187
188 loop {
189 if let Some(env) = conn.recv()? {
190 // If a shot sequence is active, feed it first.
191 if let Some(ref mut s) = shot {
192 let actions = s.feed(&env);
193 for a in actions {
194 send_action(&mut conn, a)?;
195 }
196 if s.is_complete() {
197 let data = shot.take().unwrap().into_result();
198 print_shot(&data);
199 continue;
200 }
201 }
202
203 // Handle messages not consumed by the shot sequencer.
204 match &env.message {
205 Message::ShotText(st) if st.is_processed() && shot.is_none() => {
206 println!(" Shot processed — starting shot sequence...");
207 let (s, actions) = ShotSequencer::new();
208 for a in actions {
209 send_action(&mut conn, a)?;
210 }
211 shot = Some(s);
212 }
213
214 // --- Flight result (primary) ---
215 Message::FlightResult(r) => {
216 println!(" Flight result (shot #{}):", r.total);
217 println!(
218 " Ball speed: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
219 ms_to_mph(r.launch_speed),
220 r.launch_elevation,
221 r.launch_azimuth,
222 );
223 println!(
224 " Carry: {:.1} yd Total: {:.1} yd Height: {:.1} ft",
225 m_to_yd(r.carry_distance),
226 m_to_yd(r.total_distance),
227 m_to_ft(r.max_height),
228 );
229 println!(
230 " Backspin: {} rpm Sidespin: {} rpm",
231 r.backspin_rpm, r.sidespin_rpm,
232 );
233 println!(
234 " Club speed: {:.1} mph Path: {:.1}° AoA: {:.1}°",
235 ms_to_mph(r.clubhead_speed),
236 r.club_strike_direction,
237 r.club_attack_angle,
238 );
239 println!(
240 " Face: {:.1}° Loft: {:.1}°",
241 r.club_face_angle, r.club_effective_loft,
242 );
243 }
244
245 // --- Flight result v1 (early) ---
246 Message::FlightResultV1(r) => {
247 println!(" Flight result v1 (shot #{}):", r.total);
248 println!(
249 " Ball: {:.1} mph Club: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
250 ms_to_mph(r.ball_velocity),
251 ms_to_mph(r.club_velocity),
252 r.elevation,
253 r.azimuth,
254 );
255 println!(
256 " Dist: {:.1} yd Height: {:.1} ft Backspin: {} rpm",
257 m_to_yd(r.distance),
258 m_to_ft(r.height),
259 r.backspin_rpm,
260 );
261 }
262
263 // --- Club result ---
264 Message::ClubResult(r) => {
265 println!(" Club result:");
266 println!(
267 " Club speed: {:.1} mph (pre) / {:.1} mph (post) Smash: {:.2}",
268 ms_to_mph(r.pre_club_speed),
269 ms_to_mph(r.post_club_speed),
270 r.smash_factor,
271 );
272 println!(
273 " Path: {:.1}° Face: {:.1}° AoA: {:.1}° Loft: {:.1}°",
274 r.strike_direction, r.face_angle, r.attack_angle, r.dynamic_loft,
275 );
276 println!(
277 " Low point: {:.1} in Club height: {:.1} in",
278 m_to_in(r.club_offset),
279 m_to_in(r.club_height),
280 );
281 }
282
283 // --- Spin result ---
284 Message::SpinResult(r) => {
285 println!(" Spin result:");
286 println!(
287 " Total spin: {} rpm (PM final) Axis: {:.1}° Method: {}",
288 r.pm_spin_final, r.spin_axis, r.spin_method,
289 );
290 println!(
291 " AM: {} rpm PM: {} rpm Launch: {} rpm AOD: {} rpm PLL: {} rpm",
292 r.am_spin, r.pm_spin, r.launch_spin, r.aod_spin, r.pll_spin,
293 );
294 }
295
296 // --- Speed profile ---
297 Message::SpeedProfile(r) => {
298 let peak = r.speeds.iter().copied().fold(0.0_f64, f64::max);
299 println!(
300 " Speed profile: {} samples ({}+{}) peak: {:.1} mph",
301 r.speeds.len(),
302 r.num_pre,
303 r.num_post,
304 ms_to_mph(peak),
305 );
306 }
307
308 // --- Tracking status ---
309 Message::TrackingStatus(r) => {
310 println!(
311 " Tracking: iter={} quality={} prc_count={} trigger_idx={}",
312 r.processing_iteration,
313 r.result_quality,
314 r.prc_tracking_count,
315 r.trigger_idx,
316 );
317 }
318
319 // --- PRC data ---
320 Message::PrcData(r) => {
321 println!(" PRC: seq={} points={}", r.sequence, r.points.len());
322 }
323
324 // --- Club PRC data ---
325 Message::ClubPrc(r) => {
326 println!(" Club PRC: points={}", r.points.len());
327 }
328
329 // --- Camera image available ---
330 Message::CamImageAvail(r) => {
331 println!(
332 " Camera: streaming={} fusion={} video={}",
333 r.streaming_available, r.fusion_available, r.video_available,
334 );
335 }
336
337 // --- DspStatus from keepalive ---
338 Message::DspStatus(s) => {
339 println!(
340 "[keepalive] battery={}%{} temp={:.1}°C",
341 s.battery_percent(),
342 if s.external_power() {
343 " (plugged in)"
344 } else {
345 ""
346 },
347 s.temperature_c(),
348 );
349 }
350 Message::AvrStatus(s) => {
351 println!("[keepalive] tilt={:.1}° roll={:.1}°", s.tilt, -s.roll);
352 }
353
354 // --- Config ack / noise ---
355 Message::ConfigAck(_) | Message::PiStatus(_) => {}
356
357 // --- Everything else: debug line is enough ---
358 _ => {}
359 }
360 }
361
362 // Heartbeats always fire — even during shot sequencing.
363 if last_keepalive.elapsed() >= Duration::from_secs(1) {
364 for a in seq::keepalive_actions() {
365 send_action(&mut conn, a)?;
366 }
367 last_keepalive = Instant::now();
368 }
369 }
370}Sourcepub fn temperature_c(&self) -> f64
pub fn temperature_c(&self) -> f64
Temperature in degrees C. Returns 0.0 for Gen2 (field offsets TBD).
Examples found in repository?
examples/event_loop.rs (line 347)
149fn run() -> Result<(), ConnError> {
150 // 1. Connect
151 let addr: SocketAddr = DEFAULT_ADDR.parse().unwrap();
152 println!("Connecting to {addr}...");
153 let mut conn = BinaryConnection::connect_timeout(&addr, Duration::from_secs(5))?;
154 println!("Connected.");
155
156 // Wire-level trace callbacks.
157 conn.set_on_send(|cmd, dest| println!("\n>> {:?} [{}]", cmd, cmd.debug_hex(dest)));
158 conn.set_on_recv(|env| println!("\n<< {env:?}"));
159
160 // 2. Handshake (blocking via drive())
161 println!("DSP sync...");
162 let dsp = seq::sync_dsp(&mut conn)?;
163 println!("AVR sync...");
164 let avr = seq::sync_avr(&mut conn)?;
165 println!("PI sync...");
166 let pi = seq::sync_pi(&mut conn)?;
167 print_handshake(&dsp, &avr, &pi);
168
169 // 3. Configure
170 let settings = default_avr_settings();
171 println!(
172 "Configuring... mode=Outdoor ball=RCT range={}mm height={}mm",
173 settings.radar_cal.range_mm, settings.radar_cal.height_mm,
174 );
175 seq::configure_avr(&mut conn, &settings)?;
176 seq::configure_camera(&mut conn, &default_cam_config())?;
177
178 // 4. Arm
179 seq::arm(&mut conn)?;
180 println!("=== ARMED — waiting for shots ===");
181
182 // 5. Non-blocking event loop
183 conn.stream_mut()
184 .set_read_timeout(Some(Duration::from_millis(1)))?;
185 let mut last_keepalive = Instant::now();
186 let mut shot: Option<ShotSequencer> = None;
187
188 loop {
189 if let Some(env) = conn.recv()? {
190 // If a shot sequence is active, feed it first.
191 if let Some(ref mut s) = shot {
192 let actions = s.feed(&env);
193 for a in actions {
194 send_action(&mut conn, a)?;
195 }
196 if s.is_complete() {
197 let data = shot.take().unwrap().into_result();
198 print_shot(&data);
199 continue;
200 }
201 }
202
203 // Handle messages not consumed by the shot sequencer.
204 match &env.message {
205 Message::ShotText(st) if st.is_processed() && shot.is_none() => {
206 println!(" Shot processed — starting shot sequence...");
207 let (s, actions) = ShotSequencer::new();
208 for a in actions {
209 send_action(&mut conn, a)?;
210 }
211 shot = Some(s);
212 }
213
214 // --- Flight result (primary) ---
215 Message::FlightResult(r) => {
216 println!(" Flight result (shot #{}):", r.total);
217 println!(
218 " Ball speed: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
219 ms_to_mph(r.launch_speed),
220 r.launch_elevation,
221 r.launch_azimuth,
222 );
223 println!(
224 " Carry: {:.1} yd Total: {:.1} yd Height: {:.1} ft",
225 m_to_yd(r.carry_distance),
226 m_to_yd(r.total_distance),
227 m_to_ft(r.max_height),
228 );
229 println!(
230 " Backspin: {} rpm Sidespin: {} rpm",
231 r.backspin_rpm, r.sidespin_rpm,
232 );
233 println!(
234 " Club speed: {:.1} mph Path: {:.1}° AoA: {:.1}°",
235 ms_to_mph(r.clubhead_speed),
236 r.club_strike_direction,
237 r.club_attack_angle,
238 );
239 println!(
240 " Face: {:.1}° Loft: {:.1}°",
241 r.club_face_angle, r.club_effective_loft,
242 );
243 }
244
245 // --- Flight result v1 (early) ---
246 Message::FlightResultV1(r) => {
247 println!(" Flight result v1 (shot #{}):", r.total);
248 println!(
249 " Ball: {:.1} mph Club: {:.1} mph VLA: {:.1}° HLA: {:.1}°",
250 ms_to_mph(r.ball_velocity),
251 ms_to_mph(r.club_velocity),
252 r.elevation,
253 r.azimuth,
254 );
255 println!(
256 " Dist: {:.1} yd Height: {:.1} ft Backspin: {} rpm",
257 m_to_yd(r.distance),
258 m_to_ft(r.height),
259 r.backspin_rpm,
260 );
261 }
262
263 // --- Club result ---
264 Message::ClubResult(r) => {
265 println!(" Club result:");
266 println!(
267 " Club speed: {:.1} mph (pre) / {:.1} mph (post) Smash: {:.2}",
268 ms_to_mph(r.pre_club_speed),
269 ms_to_mph(r.post_club_speed),
270 r.smash_factor,
271 );
272 println!(
273 " Path: {:.1}° Face: {:.1}° AoA: {:.1}° Loft: {:.1}°",
274 r.strike_direction, r.face_angle, r.attack_angle, r.dynamic_loft,
275 );
276 println!(
277 " Low point: {:.1} in Club height: {:.1} in",
278 m_to_in(r.club_offset),
279 m_to_in(r.club_height),
280 );
281 }
282
283 // --- Spin result ---
284 Message::SpinResult(r) => {
285 println!(" Spin result:");
286 println!(
287 " Total spin: {} rpm (PM final) Axis: {:.1}° Method: {}",
288 r.pm_spin_final, r.spin_axis, r.spin_method,
289 );
290 println!(
291 " AM: {} rpm PM: {} rpm Launch: {} rpm AOD: {} rpm PLL: {} rpm",
292 r.am_spin, r.pm_spin, r.launch_spin, r.aod_spin, r.pll_spin,
293 );
294 }
295
296 // --- Speed profile ---
297 Message::SpeedProfile(r) => {
298 let peak = r.speeds.iter().copied().fold(0.0_f64, f64::max);
299 println!(
300 " Speed profile: {} samples ({}+{}) peak: {:.1} mph",
301 r.speeds.len(),
302 r.num_pre,
303 r.num_post,
304 ms_to_mph(peak),
305 );
306 }
307
308 // --- Tracking status ---
309 Message::TrackingStatus(r) => {
310 println!(
311 " Tracking: iter={} quality={} prc_count={} trigger_idx={}",
312 r.processing_iteration,
313 r.result_quality,
314 r.prc_tracking_count,
315 r.trigger_idx,
316 );
317 }
318
319 // --- PRC data ---
320 Message::PrcData(r) => {
321 println!(" PRC: seq={} points={}", r.sequence, r.points.len());
322 }
323
324 // --- Club PRC data ---
325 Message::ClubPrc(r) => {
326 println!(" Club PRC: points={}", r.points.len());
327 }
328
329 // --- Camera image available ---
330 Message::CamImageAvail(r) => {
331 println!(
332 " Camera: streaming={} fusion={} video={}",
333 r.streaming_available, r.fusion_available, r.video_available,
334 );
335 }
336
337 // --- DspStatus from keepalive ---
338 Message::DspStatus(s) => {
339 println!(
340 "[keepalive] battery={}%{} temp={:.1}°C",
341 s.battery_percent(),
342 if s.external_power() {
343 " (plugged in)"
344 } else {
345 ""
346 },
347 s.temperature_c(),
348 );
349 }
350 Message::AvrStatus(s) => {
351 println!("[keepalive] tilt={:.1}° roll={:.1}°", s.tilt, -s.roll);
352 }
353
354 // --- Config ack / noise ---
355 Message::ConfigAck(_) | Message::PiStatus(_) => {}
356
357 // --- Everything else: debug line is enough ---
358 _ => {}
359 }
360 }
361
362 // Heartbeats always fire — even during shot sequencing.
363 if last_keepalive.elapsed() >= Duration::from_secs(1) {
364 for a in seq::keepalive_actions() {
365 send_action(&mut conn, a)?;
366 }
367 last_keepalive = Instant::now();
368 }
369 }
370}Trait Implementations§
Auto Trait Implementations§
impl Freeze for DspStatus
impl RefUnwindSafe for DspStatus
impl Send for DspStatus
impl Sync for DspStatus
impl Unpin for DspStatus
impl UnsafeUnpin for DspStatus
impl UnwindSafe for DspStatus
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more