Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
misc/
output/
output/
telemetry.log
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
58 changes: 52 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@
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

# Defaults
tele_IP_addr = "0.0.0.0"
tele_port = 5607

logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
Expand Down Expand Up @@ -126,20 +130,22 @@ 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

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()
Expand Down Expand Up @@ -248,8 +254,10 @@ def __init__(self):
super().__init__()
self.setWindowTitle("FH5 Telemetry")
self.setStyleSheet("background-color: #121212; color: #808080;")
self.setWindowIcon(QIcon("./img/logo.ico"))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added icon to program



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)

Expand Down Expand Up @@ -300,6 +308,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)
Expand Down Expand Up @@ -363,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
Expand All @@ -386,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
Expand Down Expand Up @@ -514,6 +542,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()
Expand Down Expand Up @@ -565,4 +611,4 @@ def closeEvent(self, event):
app = QApplication(sys.argv)
window = ForzaTelemetryApp()
window.showMaximized()
sys.exit(app.exec())
sys.exit(app.exec())