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 b6ae5c5..52563f7 100644 --- a/main.py +++ b/main.py @@ -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 = "192.168.1.9" +tele_port = 5500 + 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() @@ -236,11 +242,11 @@ def add_values(self, vals): self.series.replace(points) if self.data: - mn, mx = min(self.data), max(self.data) - if mn == mx: - mn -= 0.1 - mx += 0.1 - self.axis_y.setRange(mn, mx) + minimum, maximum = min(self.data), max(self.data) + if minimum == maximum: + minimum -= 0.1 + maximum += 0.1 + self.axis_y.setRange(minimum, maximum) self.axis_x.setRange(0, len(self.data)) class ForzaTelemetryApp(QWidget): @@ -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")) + - 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 +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("") + 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) @@ -362,13 +386,28 @@ def _setup_charts(self): def buffer_data(self, data: dict): self.sample_count += 1 + imperial_toggle = False # Set to False to show speed in metric instead of imperial def scale_controls(val): return val * 100 / 255 def norm_steer(val): return val * 100 / 127 - def to_mph(val): return val * 2.23694 - def to_hp(val): return val / 745.7 + def to_mph(val): + if imperial_toggle: + return val * 2.23694 # MPH + else: + return val * 3.6 # KPH + def to_hp(val): + if imperial_toggle: + return val / 745.7 # HP + else: + return val / 1000 # KW def clamp_zero(val): return max(val, 0) - + def to_psi(val): + if imperial_toggle: + return val # PSI + else: + return val / 14.504 # BAR + + patch_map = { 'accel': scale_controls, 'brake': scale_controls, @@ -378,7 +417,7 @@ def clamp_zero(val): return max(val, 0) 'speed': to_mph, 'power': to_hp, 'torque': clamp_zero, - 'boost': clamp_zero + 'boost': to_psi, } raw_snapshot = {} @@ -514,6 +553,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 +622,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())