strand_cam_bui_types/lib.rs
1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Type definitions for the Strand Camera Browser User Interface (BUI) system.
5//!
6//! This crate provides core data structures used in the Strand Camera ecosystem
7//! for recording path management and clock synchronization between different
8//! timing sources. These types are shared between the camera backend and the
9//! web-based user interface.
10//!
11//! ## Core Types
12//!
13//! - [`RecordingPath`]: Manages file paths with timestamps and size tracking
14//! - [`ClockModel`]: Linear clock synchronization between different time sources
15//!
16//! ## Features
17//!
18//! - Serialization support via serde for network communication
19//! - UTC timestamp tracking for recording sessions
20//! - Clock drift compensation for multi-camera synchronization
21
22// Copyright 2020-2023 Andrew D. Straw.
23//
24// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
25// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT
26// or http://opensource.org/licenses/MIT>, at your option. This file may not be
27// copied, modified, or distributed except according to those terms.
28
29#![warn(missing_docs)]
30
31use serde::{Deserialize, Serialize};
32
33/// Path to a recording file with associated metadata and timing information.
34///
35/// This structure tracks recording file paths along with when recording started
36/// and optionally the current file size. It's used throughout the Strand Camera
37/// system to manage active recordings for various file formats (MP4, FMF, UFMF, CSV).
38///
39/// The start time is automatically set to the current UTC time when created,
40/// providing a timestamp for when the recording session began.
41///
42/// # Examples
43///
44/// ```rust
45/// use strand_cam_bui_types::RecordingPath;
46///
47/// // Create a new recording path
48/// let recording = RecordingPath::new("/path/to/video.mp4".to_string());
49/// println!("Recording started at: {}", recording.start_time());
50/// ```
51#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
52pub struct RecordingPath {
53 /// The filesystem path to the recording file
54 path: String,
55 /// UTC timestamp when recording started
56 start_time: chrono::DateTime<chrono::Utc>,
57 /// Current size of the recording file in bytes (if known)
58 current_size_bytes: Option<usize>,
59}
60
61impl RecordingPath {
62 /// Creates a new recording path with the current UTC time as the start time.
63 ///
64 /// # Arguments
65 ///
66 /// * `path` - The filesystem path to the recording file
67 ///
68 /// # Returns
69 ///
70 /// A new [`RecordingPath`] instance with the current UTC time as the start time
71 /// and no file size information.
72 ///
73 /// # Examples
74 ///
75 /// ```rust
76 /// use strand_cam_bui_types::RecordingPath;
77 ///
78 /// let recording = RecordingPath::new("/path/to/video.mp4".to_string());
79 /// ```
80 pub fn new(path: String) -> Self {
81 let start_time = chrono::Utc::now();
82 RecordingPath::from_path_and_time(path, start_time)
83 }
84
85 /// Creates a new recording path with a specific start time.
86 ///
87 /// This method is useful when you need to recreate a recording path from
88 /// stored data or when you want to specify an exact start time.
89 ///
90 /// # Arguments
91 ///
92 /// * `path` - The filesystem path to the recording file
93 /// * `start_time` - The UTC timestamp when recording started
94 ///
95 /// # Returns
96 ///
97 /// A new [`RecordingPath`] instance with the specified path and start time.
98 ///
99 /// # Examples
100 ///
101 /// ```rust
102 /// use strand_cam_bui_types::RecordingPath;
103 /// use chrono::{DateTime, Utc};
104 ///
105 /// let start_time = Utc::now();
106 /// let recording = RecordingPath::from_path_and_time(
107 /// "/path/to/video.mp4".to_string(),
108 /// start_time
109 /// );
110 /// ```
111 pub fn from_path_and_time(path: String, start_time: chrono::DateTime<chrono::Utc>) -> Self {
112 Self {
113 path,
114 start_time,
115 current_size_bytes: None,
116 }
117 }
118
119 /// Returns the filesystem path to the recording file.
120 ///
121 /// # Returns
122 ///
123 /// A clone of the recording file path.
124 ///
125 /// # Examples
126 ///
127 /// ```rust
128 /// use strand_cam_bui_types::RecordingPath;
129 ///
130 /// let recording = RecordingPath::new("/path/to/video.mp4".to_string());
131 /// assert_eq!(recording.path(), "/path/to/video.mp4");
132 /// ```
133 pub fn path(&self) -> String {
134 self.path.clone()
135 }
136
137 /// Returns the UTC timestamp when recording started.
138 ///
139 /// # Returns
140 ///
141 /// The UTC timestamp when this recording session began.
142 ///
143 /// # Examples
144 ///
145 /// ```rust
146 /// use strand_cam_bui_types::RecordingPath;
147 ///
148 /// let recording = RecordingPath::new("/path/to/video.mp4".to_string());
149 /// println!("Recording started at: {}", recording.start_time());
150 /// ```
151 pub fn start_time(&self) -> chrono::DateTime<chrono::Utc> {
152 self.start_time
153 }
154
155 /// Returns the current size of the recording file in bytes, if known.
156 ///
157 /// This value is optional and may not be available for all recording types
158 /// or during certain phases of recording.
159 ///
160 /// # Returns
161 ///
162 /// The current file size in bytes, or `None` if not available.
163 ///
164 /// # Examples
165 ///
166 /// ```rust
167 /// use strand_cam_bui_types::RecordingPath;
168 ///
169 /// let recording = RecordingPath::new("/path/to/video.mp4".to_string());
170 /// match recording.current_size_bytes() {
171 /// Some(size) => println!("Recording size: {} bytes", size),
172 /// None => println!("Recording size unknown"),
173 /// }
174 /// ```
175 pub fn current_size_bytes(&self) -> Option<usize> {
176 self.current_size_bytes
177 }
178
179 /// Updates the current size of the recording file.
180 ///
181 /// This method is typically called by the recording system to update
182 /// the file size as data is written to disk.
183 ///
184 /// # Arguments
185 ///
186 /// * `size` - The new file size in bytes, or `None` to clear the size information
187 ///
188 /// # Examples
189 ///
190 /// ```rust
191 /// use strand_cam_bui_types::RecordingPath;
192 ///
193 /// let mut recording = RecordingPath::new("/path/to/video.mp4".to_string());
194 /// recording.set_current_size_bytes(Some(1024));
195 /// assert_eq!(recording.current_size_bytes(), Some(1024));
196 /// ```
197 pub fn set_current_size_bytes(&mut self, size: Option<usize>) {
198 self.current_size_bytes = size;
199 }
200}
201
202/// Linear clock synchronization model for multi-camera systems.
203///
204/// This structure implements a linear transformation to synchronize timestamps
205/// between different clock sources (e.g., camera hardware clocks vs. host system clock).
206/// The transformation follows the equation: `host_time = gain * device_time + offset`.
207///
208/// The clock model is essential for multi-camera synchronization in the Strand Camera
209/// system, allowing timestamps from different sources to be aligned to a common
210/// time reference.
211///
212/// # Mathematical Model
213///
214/// The linear relationship is: **t_host = gain × t_device + offset**
215///
216/// - `gain`: Clock rate ratio (typically close to 1.0)
217/// - `offset`: Time offset between clock sources
218/// - `residuals`: Sum of squared residuals from the linear fit
219/// - `n_measurements`: Number of data points used to compute the model
220///
221/// # Examples
222///
223/// ```rust
224/// use strand_cam_bui_types::ClockModel;
225///
226/// // Create a clock model with typical values
227/// let clock_model = ClockModel {
228/// gain: 1.000001, // Slightly faster device clock
229/// offset: -1234567.89, // Device clock started earlier
230/// residuals: 0.001, // Good fit quality
231/// n_measurements: 100, // Based on 100 sync points
232/// };
233///
234/// // Convert device timestamp to host timestamp
235/// let device_time = 1000.0;
236/// let host_time = clock_model.gain * device_time + clock_model.offset;
237/// ```
238#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
239pub struct ClockModel {
240 /// Clock rate ratio between device and host clocks.
241 ///
242 /// This represents how fast the device clock runs relative to the host clock.
243 /// A value of 1.0 means identical rates, > 1.0 means the device clock runs faster,
244 /// and < 1.0 means it runs slower.
245 pub gain: f64,
246
247 /// Time offset between device and host clocks.
248 ///
249 /// This is the constant offset needed to align the two time sources.
250 /// The offset accounts for differences in when the clocks were started
251 /// and any systematic time differences.
252 pub offset: f64,
253
254 /// Sum of squared residuals from the linear regression fit.
255 ///
256 /// This value indicates the quality of the linear fit - smaller values
257 /// indicate better synchronization. It's computed during the least-squares
258 /// fitting process used to determine the gain and offset parameters.
259 pub residuals: f64,
260
261 /// Number of timestamp measurements used to compute this model.
262 ///
263 /// More measurements typically lead to better model accuracy.
264 /// The synchronization system collects timestamp pairs over time
265 /// to build a robust clock model.
266 pub n_measurements: u64,
267}