From 50e9e18060b638b6d3291ccab3a02f5e781c14bb Mon Sep 17 00:00:00 2001 From: Logan Simonsen Date: Thu, 26 Feb 2026 17:08:11 +1300 Subject: [PATCH 1/4] Added IP & Port changing Added option to change the port and IP used in the GUI --- main.py | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index b6ae5c5..f0fe705 100644 --- a/main.py +++ b/main.py @@ -49,6 +49,10 @@ from struct import unpack import csv +# Defaults +tele_IP_addr = "0.0.0.0" +tele_port = 5607 + logging.basicConfig( level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s", @@ -126,13 +130,13 @@ def to_dict(self): return {prop: getattr(self, prop) for prop in self.get_props()} class TelemetryReceiver(QObject): + global tele_IP_addr, tele_port data_received = Signal(dict) log_message = Signal(str) - def __init__(self, ip="0.0.0.0", port=5607): + + def __init__(self): super().__init__() - self.ip = ip - self.port = port self._running = False self.sock = None self.thread = None @@ -140,6 +144,8 @@ def __init__(self, ip="0.0.0.0", port=5607): def start(self): if self._running: return + self.ip = tele_IP_addr + self.port = tele_port self._running = True self.thread = threading.Thread(target=self._listen_loop, daemon=True) self.thread.start() @@ -249,7 +255,7 @@ def __init__(self): self.setWindowTitle("FH5 Telemetry") self.setStyleSheet("background-color: #121212; color: #808080;") - self.receiver = TelemetryReceiver(ip="0.0.0.0", port=5607) + self.receiver = TelemetryReceiver() self.receiver.data_received.connect(self.buffer_data) self.receiver.log_message.connect(self.log) @@ -300,6 +306,22 @@ def __init__(self): controls.addStretch() + + # Ip / Port Changer + ip_input = QLabel("IP:Port") + ip_input.setAlignment(Qt.AlignLeft) + controls.addWidget(ip_input) + self.changeIP = QLineEdit("127.0.0.1:5500") + self.changeIP.setFixedWidth(100) + self.changeIP.setPlaceholderText("IP:Port") + controls.addWidget(self.changeIP) + + self.btn_changeIP = QPushButton("Apply") + self.btn_changeIP.clicked.connect(self.update_IP_port) + controls.addWidget(self.btn_changeIP) + + controls.addStretch() + self.btn_clear_logs = QPushButton("Clear Logs") self.btn_clear_logs.clicked.connect(self.log_panel.clear) controls.addWidget(self.btn_clear_logs) @@ -514,6 +536,24 @@ def stop(self): chart.data.clear() chart.add_values([]) + def update_IP_port(self, ): + global tele_IP_addr, tele_port + try: + ip_port = self.changeIP.text().split(":") + if len(ip_port) != 2: + raise ValueError("Invalid format. Use IP:Port") + print(f"IP port input: {ip_port}") + tele_IP_addr = ip_port[0] + tele_port = int(ip_port[1]) + log.info(f"Telemetry IP/Port changed to {tele_IP_addr}:{tele_port}") + self.log(f"Telemetry IP/Port changed to {tele_IP_addr}:{tele_port}") + if self.receiver._running: + self.receiver.stop() + self.receiver.start() + except Exception as e: + log.error(f"Failed to change IP/Port: {e}") + self.log(f"Failed to change IP/Port: {e}") + def toggle_logging(self, checked): if checked: self._start_logging() @@ -565,4 +605,4 @@ def closeEvent(self, event): app = QApplication(sys.argv) window = ForzaTelemetryApp() window.showMaximized() - sys.exit(app.exec()) \ No newline at end of file + sys.exit(app.exec()) From 9d4cf08ce39e269e88871f8cbaa4203b505f97ef Mon Sep 17 00:00:00 2001 From: Logan Simonsen Date: Thu, 26 Feb 2026 17:09:39 +1300 Subject: [PATCH 2/4] Update main.py --- main.py | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index b6ae5c5..f0a2b74 100644 --- a/main.py +++ b/main.py @@ -49,6 +49,10 @@ from struct import unpack import csv +# Defaults +tele_IP_addr = "0.0.0.0" +tele_port = 5607 + logging.basicConfig( level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s", @@ -126,13 +130,13 @@ def to_dict(self): return {prop: getattr(self, prop) for prop in self.get_props()} class TelemetryReceiver(QObject): + global tele_IP_addr, tele_port data_received = Signal(dict) log_message = Signal(str) - def __init__(self, ip="0.0.0.0", port=5607): + + def __init__(self): super().__init__() - self.ip = ip - self.port = port self._running = False self.sock = None self.thread = None @@ -140,6 +144,8 @@ def __init__(self, ip="0.0.0.0", port=5607): def start(self): if self._running: return + self.ip = tele_IP_addr + self.port = tele_port self._running = True self.thread = threading.Thread(target=self._listen_loop, daemon=True) self.thread.start() @@ -249,7 +255,7 @@ def __init__(self): self.setWindowTitle("FH5 Telemetry") self.setStyleSheet("background-color: #121212; color: #808080;") - self.receiver = TelemetryReceiver(ip="0.0.0.0", port=5607) + self.receiver = TelemetryReceiver() self.receiver.data_received.connect(self.buffer_data) self.receiver.log_message.connect(self.log) @@ -300,6 +306,22 @@ def __init__(self): controls.addStretch() + + # Ip / Port Changer + ip_input = QLabel("IP:Port") + ip_input.setAlignment(Qt.AlignLeft) + controls.addWidget(ip_input) + self.changeIP = QLineEdit("127.0.0.1:5500") + self.changeIP.setFixedWidth(100) + self.changeIP.setPlaceholderText("IP:Port") + controls.addWidget(self.changeIP) + + self.btn_changeIP = QPushButton("Apply") + self.btn_changeIP.clicked.connect(self.update_IP_port) + controls.addWidget(self.btn_changeIP) + + controls.addStretch() + self.btn_clear_logs = QPushButton("Clear Logs") self.btn_clear_logs.clicked.connect(self.log_panel.clear) controls.addWidget(self.btn_clear_logs) @@ -514,6 +536,24 @@ def stop(self): chart.data.clear() chart.add_values([]) + def update_IP_port(self, ): + global tele_IP_addr, tele_port + try: + ip_port = self.changeIP.text().split(":") + if len(ip_port) != 2: + raise ValueError("Invalid format. Use IP:Port") + print(f"IP port input: {ip_port}") + tele_IP_addr = ip_port[0] + tele_port = int(ip_port[1]) + log.info(f"Telemetry IP/Port changed to {tele_IP_addr}:{tele_port}") + self.log(f"Telemetry IP/Port changed to {tele_IP_addr}:{tele_port}") + if self.receiver._running: + self.receiver.stop() + self.receiver.start() + except Exception as e: + log.error(f"Failed to change IP/Port: {e}") + self.log(f"Failed to change IP/Port: {e}") + def toggle_logging(self, checked): if checked: self._start_logging() From da6766314d7d8cf1020c712f42fb382622d3c9a3 Mon Sep 17 00:00:00 2001 From: Logan Simonsen Date: Thu, 26 Feb 2026 17:47:03 +1300 Subject: [PATCH 3/4] Updated readme to relfect new changes + added icon to window --- .gitignore | 3 ++- README.md | 3 +-- main.py | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1104fc6..527dc4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ misc/ -output/ \ No newline at end of file +output/ +telemetry.log \ No newline at end of file diff --git a/README.md b/README.md index 896718d..6b53b4b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - 📊 **Live Graphs**: Real-time charts for engine, suspension, input, etc. - 💾 **CSV Logging**: Save sessions to file for playback and later review. - ⏪ **Replay Mode**: Open saved CSV files to view logged data with graph control. -- 🔧 **User Controls**: Adjustable sample count, start/stop, open/clear logs, etc. +- 🔧 **User Controls**: Adjustable sample count, start/stop, open/clear logs, change IP/port, etc. ## 📷 Preview @@ -96,5 +96,4 @@ See [`LICENSE`](./LICENSE) for full details. - Torque conversion (not entirely accurate right now) - Fix for speed, suspension, and velocity charts (jittering at idle/standstill) -- Input to choose port? - UI Improvements \ No newline at end of file diff --git a/main.py b/main.py index f0fe705..0d9965e 100644 --- a/main.py +++ b/main.py @@ -45,7 +45,7 @@ QTabWidget, QLabel, QTextEdit, QLineEdit, QGridLayout, QSlider, QFileDialog ) from PySide6.QtCharts import QChart, QChartView, QLineSeries, QValueAxis -from PySide6.QtGui import QPainter +from PySide6.QtGui import QPainter, QIcon from struct import unpack import csv @@ -254,6 +254,8 @@ def __init__(self): super().__init__() self.setWindowTitle("FH5 Telemetry") self.setStyleSheet("background-color: #121212; color: #808080;") + self.setWindowIcon(QIcon("./img/logo.ico")) + self.receiver = TelemetryReceiver() self.receiver.data_received.connect(self.buffer_data) From e8ee1746d727a4d5f2bd85dff891ceac676ce4b4 Mon Sep 17 00:00:00 2001 From: Logan Simonsen Date: Thu, 26 Feb 2026 21:45:06 +1300 Subject: [PATCH 4/4] Update main.py --- main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main.py b/main.py index 0d9965e..e2b0f7e 100644 --- a/main.py +++ b/main.py @@ -387,6 +387,9 @@ def _setup_charts(self): def buffer_data(self, data: dict): self.sample_count += 1 + # 4 decimal places had jitter in suspension data, 3 seems stable. + CALC_PRECISION = 3 + def scale_controls(val): return val * 100 / 255 def norm_steer(val): return val * 100 / 127 def to_mph(val): return val * 2.23694 @@ -410,6 +413,7 @@ def clamp_zero(val): return max(val, 0) for key, val in data.items(): if key in self.data_buffer and isinstance(val, (int, float)): + val = round(val, CALC_PRECISION) fixed_val = patch_map[key](val) if key in patch_map else val self.data_buffer[key].append(fixed_val) raw_snapshot[key] = fixed_val