Skip to main content

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}