mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 18:32:02 +00:00
feat: Allow millisecond offset for timeturner
Co-authored-by: aider (gemini/gemini-2.5-pro-preview-05-06) <aider@aider.chat>
This commit is contained in:
parent
a12ee88b9b
commit
c712014bb9
6 changed files with 25 additions and 8 deletions
|
|
@ -303,7 +303,7 @@ mod tests {
|
||||||
let new_config_json = serde_json::json!({
|
let new_config_json = serde_json::json!({
|
||||||
"hardwareOffsetMs": 55,
|
"hardwareOffsetMs": 55,
|
||||||
"defaultNudgeMs": 2,
|
"defaultNudgeMs": 2,
|
||||||
"timeturnerOffset": { "hours": 1, "minutes": 2, "seconds": 3, "frames": 4 }
|
"timeturnerOffset": { "hours": 1, "minutes": 2, "seconds": 3, "frames": 4, "milliseconds": 5 }
|
||||||
});
|
});
|
||||||
|
|
||||||
let req = test::TestRequest::post()
|
let req = test::TestRequest::post()
|
||||||
|
|
@ -315,15 +315,18 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(resp.hardware_offset_ms, 55);
|
assert_eq!(resp.hardware_offset_ms, 55);
|
||||||
assert_eq!(resp.timeturner_offset.hours, 1);
|
assert_eq!(resp.timeturner_offset.hours, 1);
|
||||||
|
assert_eq!(resp.timeturner_offset.milliseconds, 5);
|
||||||
let final_config = app_state.config.lock().unwrap();
|
let final_config = app_state.config.lock().unwrap();
|
||||||
assert_eq!(final_config.hardware_offset_ms, 55);
|
assert_eq!(final_config.hardware_offset_ms, 55);
|
||||||
assert_eq!(final_config.timeturner_offset.hours, 1);
|
assert_eq!(final_config.timeturner_offset.hours, 1);
|
||||||
|
assert_eq!(final_config.timeturner_offset.milliseconds, 5);
|
||||||
|
|
||||||
// Test that the file was written
|
// Test that the file was written
|
||||||
assert!(fs::metadata(config_path).is_ok());
|
assert!(fs::metadata(config_path).is_ok());
|
||||||
let contents = fs::read_to_string(config_path).unwrap();
|
let contents = fs::read_to_string(config_path).unwrap();
|
||||||
assert!(contents.contains("hardwareOffsetMs: 55"));
|
assert!(contents.contains("hardwareOffsetMs: 55"));
|
||||||
assert!(contents.contains("hours: 1"));
|
assert!(contents.contains("hours: 1"));
|
||||||
|
assert!(contents.contains("milliseconds: 5"));
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
let _ = fs::remove_file(config_path);
|
let _ = fs::remove_file(config_path);
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,17 @@ pub struct TimeturnerOffset {
|
||||||
pub minutes: i64,
|
pub minutes: i64,
|
||||||
pub seconds: i64,
|
pub seconds: i64,
|
||||||
pub frames: i64,
|
pub frames: i64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub milliseconds: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeturnerOffset {
|
impl TimeturnerOffset {
|
||||||
pub fn is_active(&self) -> bool {
|
pub fn is_active(&self) -> bool {
|
||||||
self.hours != 0 || self.minutes != 0 || self.seconds != 0 || self.frames != 0
|
self.hours != 0
|
||||||
|
|| self.minutes != 0
|
||||||
|
|| self.seconds != 0
|
||||||
|
|| self.frames != 0
|
||||||
|
|| self.milliseconds != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ timeturnerOffset:
|
||||||
minutes: 0
|
minutes: 0
|
||||||
seconds: 0
|
seconds: 0
|
||||||
frames: 0
|
frames: 0
|
||||||
|
milliseconds: 0
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
/// If no `config.yml` exists alongside the binary, write out the default.
|
/// If no `config.yml` exists alongside the binary, write out the default.
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ pub fn calculate_target_time(frame: &LtcFrame, config: &Config) -> DateTime<Loca
|
||||||
+ ChronoDuration::seconds(offset.seconds);
|
+ ChronoDuration::seconds(offset.seconds);
|
||||||
// Frame offset needs to be converted to milliseconds
|
// Frame offset needs to be converted to milliseconds
|
||||||
let frame_offset_ms = (offset.frames as f64 / frame.frame_rate * 1000.0).round() as i64;
|
let frame_offset_ms = (offset.frames as f64 / frame.frame_rate * 1000.0).round() as i64;
|
||||||
dt_local + ChronoDuration::milliseconds(frame_offset_ms)
|
dt_local + ChronoDuration::milliseconds(frame_offset_ms + offset.milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_sync(frame: &LtcFrame, config: &Config) -> Result<String, ()> {
|
pub fn trigger_sync(frame: &LtcFrame, config: &Config) -> Result<String, ()> {
|
||||||
|
|
@ -177,6 +177,7 @@ mod tests {
|
||||||
minutes: 5,
|
minutes: 5,
|
||||||
seconds: 10,
|
seconds: 10,
|
||||||
frames: 12, // 12 frames at 25fps is 480ms
|
frames: 12, // 12 frames at 25fps is 480ms
|
||||||
|
milliseconds: 20,
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_time = calculate_target_time(&frame, &config);
|
let target_time = calculate_target_time(&frame, &config);
|
||||||
|
|
@ -184,8 +185,8 @@ mod tests {
|
||||||
assert_eq!(target_time.hour(), 11);
|
assert_eq!(target_time.hour(), 11);
|
||||||
assert_eq!(target_time.minute(), 25);
|
assert_eq!(target_time.minute(), 25);
|
||||||
assert_eq!(target_time.second(), 40);
|
assert_eq!(target_time.second(), 40);
|
||||||
// 480ms
|
// 480ms + 20ms = 500ms
|
||||||
assert_eq!(target_time.nanosecond(), 480_000_000);
|
assert_eq!(target_time.nanosecond(), 500_000_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -197,14 +198,15 @@ mod tests {
|
||||||
minutes: -5,
|
minutes: -5,
|
||||||
seconds: -10,
|
seconds: -10,
|
||||||
frames: -12, // -480ms
|
frames: -12, // -480ms
|
||||||
|
milliseconds: -80,
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_time = calculate_target_time(&frame, &config);
|
let target_time = calculate_target_time(&frame, &config);
|
||||||
|
|
||||||
assert_eq!(target_time.hour(), 9);
|
assert_eq!(target_time.hour(), 9);
|
||||||
assert_eq!(target_time.minute(), 15);
|
assert_eq!(target_time.minute(), 15);
|
||||||
assert_eq!(target_time.second(), 20);
|
assert_eq!(target_time.second(), 19);
|
||||||
assert_eq!(target_time.nanosecond(), 0);
|
assert_eq!(target_time.nanosecond(), 920_000_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
<input type="number" id="hw-offset" name="hw-offset">
|
<input type="number" id="hw-offset" name="hw-offset">
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label>Timeturner Offset:, HH:MM:SS:f</label>
|
<label>Timeturner Offset: HH:MM:SS:f.ms</label>
|
||||||
<input type="number" id="offset-h" placeholder="H">
|
<input type="number" id="offset-h" placeholder="H">
|
||||||
<label>:</label>
|
<label>:</label>
|
||||||
<input type="number" id="offset-m" placeholder="M">
|
<input type="number" id="offset-m" placeholder="M">
|
||||||
|
|
@ -58,6 +58,8 @@
|
||||||
<input type="number" id="offset-s" placeholder="S">
|
<input type="number" id="offset-s" placeholder="S">
|
||||||
<label>.</label>
|
<label>.</label>
|
||||||
<input type="number" id="offset-f" placeholder="F">
|
<input type="number" id="offset-f" placeholder="F">
|
||||||
|
<label>.</label>
|
||||||
|
<input type="number" id="offset-ms" placeholder="ms">
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<button id="save-config">Save Config</button>
|
<button id="save-config">Save Config</button>
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
m: document.getElementById('offset-m'),
|
m: document.getElementById('offset-m'),
|
||||||
s: document.getElementById('offset-s'),
|
s: document.getElementById('offset-s'),
|
||||||
f: document.getElementById('offset-f'),
|
f: document.getElementById('offset-f'),
|
||||||
|
ms: document.getElementById('offset-ms'),
|
||||||
};
|
};
|
||||||
const saveConfigButton = document.getElementById('save-config');
|
const saveConfigButton = document.getElementById('save-config');
|
||||||
const manualSyncButton = document.getElementById('manual-sync');
|
const manualSyncButton = document.getElementById('manual-sync');
|
||||||
|
|
@ -84,6 +85,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
offsetInputs.m.value = data.timeturnerOffset.minutes;
|
offsetInputs.m.value = data.timeturnerOffset.minutes;
|
||||||
offsetInputs.s.value = data.timeturnerOffset.seconds;
|
offsetInputs.s.value = data.timeturnerOffset.seconds;
|
||||||
offsetInputs.f.value = data.timeturnerOffset.frames;
|
offsetInputs.f.value = data.timeturnerOffset.frames;
|
||||||
|
offsetInputs.ms.value = data.timeturnerOffset.milliseconds || 0;
|
||||||
nudgeValueInput.value = data.defaultNudgeMs;
|
nudgeValueInput.value = data.defaultNudgeMs;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching config:', error);
|
console.error('Error fetching config:', error);
|
||||||
|
|
@ -99,6 +101,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
minutes: parseInt(offsetInputs.m.value, 10) || 0,
|
minutes: parseInt(offsetInputs.m.value, 10) || 0,
|
||||||
seconds: parseInt(offsetInputs.s.value, 10) || 0,
|
seconds: parseInt(offsetInputs.s.value, 10) || 0,
|
||||||
frames: parseInt(offsetInputs.f.value, 10) || 0,
|
frames: parseInt(offsetInputs.f.value, 10) || 0,
|
||||||
|
milliseconds: parseInt(offsetInputs.ms.value, 10) || 0,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue