Update timeturner.py

This commit is contained in:
Chris Frankland-Wright 2025-07-08 15:34:35 +01:00 committed by GitHub
parent 6bceabd1a3
commit 393998a521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -26,11 +26,8 @@ latest_line = None
latest_timestamp = None latest_timestamp = None
line_lock = Lock() line_lock = Lock()
# Rolling jitter calculation # Sync Jitter buffer
jitter_samples = deque(maxlen=25) offset_buffer = deque(maxlen=50)
last_jitter_update = time.time()
displayed_jitter_ms = None
displayed_jitter_frames = None
# Load config # Load config
if os.path.exists(CONFIG_FILE): if os.path.exists(CONFIG_FILE):
@ -74,8 +71,6 @@ def serial_reader(port, baud):
latest_timestamp = datetime.datetime.now() latest_timestamp = datetime.datetime.now()
def run_curses(stdscr): def run_curses(stdscr):
global displayed_jitter_ms, displayed_jitter_frames, last_jitter_update
curses.curs_set(0) curses.curs_set(0)
stdscr.nodelay(True) stdscr.nodelay(True)
stdscr.timeout(100) stdscr.timeout(100)
@ -90,9 +85,7 @@ def run_curses(stdscr):
lock_count = 0 lock_count = 0
free_count = 0 free_count = 0
last_displayed_frame = ""
sync_allowed = False sync_allowed = False
last_frame = None
while True: while True:
now = datetime.datetime.now() now = datetime.datetime.now()
@ -119,8 +112,8 @@ def run_curses(stdscr):
stdscr.addstr(5, 2, f"LTC Timecode : {timecode_str}") stdscr.addstr(5, 2, f"LTC Timecode : {timecode_str}")
stdscr.addstr(6, 2, f"Frame Rate : {parsed['fps']:.2f}fps") stdscr.addstr(6, 2, f"Frame Rate : {parsed['fps']:.2f}fps")
# Approximate LTC timecode as a datetime # Generate LTC datetime object
ltc_time = timestamp.replace( ltc_time = datetime.datetime.now().replace(
hour=parsed["hours"], hour=parsed["hours"],
minute=parsed["minutes"], minute=parsed["minutes"],
second=parsed["seconds"], second=parsed["seconds"],
@ -131,36 +124,34 @@ def run_curses(stdscr):
system_time = datetime.datetime.now() system_time = datetime.datetime.now()
stdscr.addstr(7, 2, f"System Clock : {system_time.strftime('%H:%M:%S.%f')[:-3]}") stdscr.addstr(7, 2, f"System Clock : {system_time.strftime('%H:%M:%S.%f')[:-3]}")
# Only calculate jitter if LOCKED # Calculate Sync Jitter
if lock_state == "LOCK": if lock_state == "LOCK":
offset_ms = (system_time - ltc_time).total_seconds() * 1000 - hardware_offset_ms offset_ms = (system_time - ltc_time).total_seconds() * 1000 - hardware_offset_ms
jitter_samples.append(offset_ms) offset_buffer.append(offset_ms)
if offset_buffer:
if time.time() - last_jitter_update >= 1.0: avg_offset = sum(offset_buffer) / len(offset_buffer)
avg_offset = sum(jitter_samples) / len(jitter_samples)
displayed_jitter_ms = avg_offset
frame_error = round((avg_offset / 1000) * parsed["fps"]) frame_error = round((avg_offset / 1000) * parsed["fps"])
displayed_jitter_frames = frame_error stdscr.addstr(8, 2, f"Sync Jitter : {avg_offset:+.0f} ms ({frame_error:+} frames)",
last_jitter_update = time.time() curses.color_pair(0 if abs(avg_offset) < 5 else 3))
else:
stdscr.addstr(8, 2, f"Sync Jitter : --")
else:
stdscr.addstr(8, 2, f"Sync Jitter : -- (FREE mode)", curses.color_pair(3))
if displayed_jitter_ms is not None: # Timecode Match (HH:MM:SS only)
stdscr.addstr(8, 2, f"Sync Jitter : {displayed_jitter_ms:+.0f} ms ({displayed_jitter_frames:+} frames)", ltc_time_str = ltc_time.strftime('%H:%M:%S')
curses.color_pair(0 if abs(displayed_jitter_ms) < 5 else 3)) sys_time_str = system_time.strftime('%H:%M:%S')
if lock_state == "LOCK":
# Compare timecode match if ltc_time_str == sys_time_str:
ltc_str = ltc_time.strftime('%H:%M:%S')
sys_str = system_time.strftime('%H:%M:%S')
if ltc_str == sys_str and parsed["frames"] == int((system_time.microsecond / 1_000_000) * parsed["fps"]):
stdscr.addstr(9, 2, "Timecode Match: MATCHED", curses.color_pair(2)) stdscr.addstr(9, 2, "Timecode Match: MATCHED", curses.color_pair(2))
else: else:
stdscr.addstr(9, 2, "Timecode Match: OUT OF SYNC", curses.color_pair(3)) stdscr.addstr(9, 2, "Timecode Match: OUT OF SYNC", curses.color_pair(3))
sync_allowed = True
else: else:
stdscr.addstr(8, 2, f"Sync Jitter : -- (FREE mode)", curses.color_pair(3)) stdscr.addstr(9, 2, "Timecode Match: UNKNOWN", curses.color_pair(3))
stdscr.addstr(9, 2, f"Timecode Match: UNKNOWN", curses.color_pair(3))
sync_allowed = False
sync_allowed = lock_state == "LOCK"
# Lock Ratio
total = lock_count + free_count total = lock_count + free_count
if total > 0: if total > 0:
lock_ratio = (lock_count / total) * 100 lock_ratio = (lock_count / total) * 100
@ -172,7 +163,8 @@ def run_curses(stdscr):
key = stdscr.getch() key = stdscr.getch()
if key in (ord('s'), ord('S')) and parsed and sync_allowed: if key in (ord('s'), ord('S')) and parsed and sync_allowed:
os.system(f"sudo date -s \"{timecode_str.replace(':', ':')}\"") clock_str = f"{parsed['hours']:02}:{parsed['minutes']:02}:{parsed['seconds']:02}"
os.system(f"sudo date -s \"{clock_str}\"")
stdscr.refresh() stdscr.refresh()
time.sleep(0.05) time.sleep(0.05)