mirror of
https://github.com/cjfranko/NTP-Timeturner.git
synced 2025-11-08 18:32:02 +00:00
Update timeturner.py
This commit is contained in:
parent
38c15897ae
commit
c9d0bf0937
1 changed files with 41 additions and 20 deletions
|
|
@ -4,12 +4,23 @@ import serial
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
|
||||||
SERIAL_PORT = "/dev/ttyUSB0"
|
|
||||||
BAUD_RATE = 115200
|
BAUD_RATE = 115200
|
||||||
|
|
||||||
line_regex = re.compile(r"\[(LOCK|FREE)\]\s+(\d{2}:\d{2}:\d{2}[:;]\d{2})\s+\|\s+([\d.]+)fps")
|
line_regex = re.compile(r"\[(LOCK|FREE)\]\s+(\d{2}:\d{2}:\d{2}[:;]\d{2})\s+\|\s+([\d.]+)fps")
|
||||||
|
|
||||||
|
def find_serial_port():
|
||||||
|
candidates = sorted(glob.glob('/dev/ttyACM*') + glob.glob('/dev/ttyUSB*'))
|
||||||
|
for port in candidates:
|
||||||
|
try:
|
||||||
|
s = serial.Serial(port, BAUD_RATE, timeout=1)
|
||||||
|
s.close()
|
||||||
|
return port
|
||||||
|
except serial.SerialException:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
def parse_ltc_line(line):
|
def parse_ltc_line(line):
|
||||||
match = line_regex.match(line.strip())
|
match = line_regex.match(line.strip())
|
||||||
if not match:
|
if not match:
|
||||||
|
|
@ -32,12 +43,26 @@ def run_curses(stdscr):
|
||||||
curses.start_color()
|
curses.start_color()
|
||||||
curses.use_default_colors()
|
curses.use_default_colors()
|
||||||
|
|
||||||
curses.init_pair(1, curses.COLOR_GREEN, -1) # LOCK
|
curses.init_pair(1, curses.COLOR_GREEN, -1)
|
||||||
curses.init_pair(2, curses.COLOR_YELLOW, -1) # FREE
|
curses.init_pair(2, curses.COLOR_YELLOW, -1)
|
||||||
curses.init_pair(3, curses.COLOR_RED, -1) # LOST
|
curses.init_pair(3, curses.COLOR_RED, -1)
|
||||||
curses.init_pair(4, curses.COLOR_CYAN, -1) # Offset
|
curses.init_pair(4, curses.COLOR_CYAN, -1)
|
||||||
|
|
||||||
|
serial_port = find_serial_port()
|
||||||
|
if not serial_port:
|
||||||
|
stdscr.addstr(0, 0, "❌ Could not find Teensy serial port (ACM/USB).")
|
||||||
|
stdscr.refresh()
|
||||||
|
time.sleep(3)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
ser = serial.Serial(serial_port, BAUD_RATE, timeout=1)
|
||||||
|
except Exception as e:
|
||||||
|
stdscr.addstr(0, 0, f"❌ Failed to open {serial_port}: {e}")
|
||||||
|
stdscr.refresh()
|
||||||
|
time.sleep(3)
|
||||||
|
return
|
||||||
|
|
||||||
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
|
|
||||||
lock_count = 0
|
lock_count = 0
|
||||||
free_count = 0
|
free_count = 0
|
||||||
last_ltc_dt = None
|
last_ltc_dt = None
|
||||||
|
|
@ -64,7 +89,6 @@ def run_curses(stdscr):
|
||||||
|
|
||||||
if sync_requested and not syncing:
|
if sync_requested and not syncing:
|
||||||
syncing = True
|
syncing = True
|
||||||
# Snap to this LTC frame's HH:MM:SS (no frames)
|
|
||||||
new_time = last_ltc_dt.strftime("%H:%M:%S")
|
new_time = last_ltc_dt.strftime("%H:%M:%S")
|
||||||
try:
|
try:
|
||||||
subprocess.run(["sudo", "date", "-s", new_time], check=True)
|
subprocess.run(["sudo", "date", "-s", new_time], check=True)
|
||||||
|
|
@ -74,7 +98,6 @@ def run_curses(stdscr):
|
||||||
sync_requested = False
|
sync_requested = False
|
||||||
syncing = False
|
syncing = False
|
||||||
|
|
||||||
# Calculate offset
|
|
||||||
if last_ltc_dt:
|
if last_ltc_dt:
|
||||||
sys_time = now.replace(microsecond=0)
|
sys_time = now.replace(microsecond=0)
|
||||||
offset = (sys_time - last_ltc_dt).total_seconds()
|
offset = (sys_time - last_ltc_dt).total_seconds()
|
||||||
|
|
@ -84,11 +107,10 @@ def run_curses(stdscr):
|
||||||
else:
|
else:
|
||||||
offset_str = "n/a"
|
offset_str = "n/a"
|
||||||
|
|
||||||
# Draw UI
|
|
||||||
stdscr.erase()
|
stdscr.erase()
|
||||||
stdscr.addstr(0, 0, "NTP Timeturner v0.5")
|
stdscr.addstr(0, 0, "NTP Timeturner v0.5")
|
||||||
stdscr.addstr(2, 0, "LTC Status : ")
|
stdscr.addstr(1, 0, f"Using Serial Port: {serial_port}")
|
||||||
|
stdscr.addstr(3, 0, "LTC Status : ")
|
||||||
if last_status == "LOCK":
|
if last_status == "LOCK":
|
||||||
stdscr.addstr("LOCK", curses.color_pair(1))
|
stdscr.addstr("LOCK", curses.color_pair(1))
|
||||||
elif last_status == "FREE":
|
elif last_status == "FREE":
|
||||||
|
|
@ -96,21 +118,20 @@ def run_curses(stdscr):
|
||||||
else:
|
else:
|
||||||
stdscr.addstr("LOST", curses.color_pair(3))
|
stdscr.addstr("LOST", curses.color_pair(3))
|
||||||
|
|
||||||
stdscr.addstr(3, 0, f"LTC Timecode : {last_ltc_dt.strftime('%H:%M:%S') if last_ltc_dt else 'n/a'}")
|
stdscr.addstr(4, 0, f"LTC Timecode : {last_ltc_dt.strftime('%H:%M:%S') if last_ltc_dt else 'n/a'}")
|
||||||
stdscr.addstr(4, 0, f"Frame Rate : {frame_rate:.2f}fps")
|
stdscr.addstr(5, 0, f"Frame Rate : {frame_rate:.2f}fps")
|
||||||
stdscr.addstr(5, 0, f"System Clock : {now.strftime('%H:%M:%S.%f')[:-3]}")
|
stdscr.addstr(6, 0, f"System Clock : {now.strftime('%H:%M:%S.%f')[:-3]}")
|
||||||
stdscr.addstr(6, 0, "Sync Offset : ")
|
stdscr.addstr(7, 0, "Sync Offset : ")
|
||||||
stdscr.addstr(offset_str, curses.color_pair(4))
|
stdscr.addstr(offset_str, curses.color_pair(4))
|
||||||
stdscr.addstr(7, 0, f"Lock Ratio : {lock_count} LOCK / {free_count} FREE")
|
stdscr.addstr(8, 0, f"Lock Ratio : {lock_count} LOCK / {free_count} FREE")
|
||||||
stdscr.addstr(9, 0, "[S] Set system clock to LTC [Ctrl+C] Quit")
|
stdscr.addstr(10, 0, "[S] Set system clock to LTC [Ctrl+C] Quit")
|
||||||
|
|
||||||
if 'sync_feedback' in locals():
|
if 'sync_feedback' in locals():
|
||||||
stdscr.addstr(11, 0, sync_feedback[:curses.COLS - 1])
|
stdscr.addstr(12, 0, sync_feedback[:curses.COLS - 1])
|
||||||
del sync_feedback
|
del sync_feedback
|
||||||
|
|
||||||
stdscr.refresh()
|
stdscr.refresh()
|
||||||
|
|
||||||
# Handle input
|
|
||||||
stdscr.nodelay(True)
|
stdscr.nodelay(True)
|
||||||
try:
|
try:
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue