Browse Source

update sdr-tcp client-gui and add new commands

Vyacheslav Vinokurov 5 days ago
parent
commit
1f239d26f3
2 changed files with 237 additions and 5 deletions
  1. 207 0
      sdr-tcp/client-gui.py
  2. 30 5
      sdr-tcp/server-simulator.py

+ 207 - 0
sdr-tcp/client-gui.py

@@ -0,0 +1,207 @@
+import numpy as np
+from matplotlib.figure import Figure
+import socket
+import msgpack
+from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QHBoxLayout, QMessageBox
+from PySide6.QtWidgets import QDialog, QFormLayout, QLineEdit, QLabel, QComboBox, QSpinBox, QToolButton, QFileDialog
+from PySide6.QtCore import QTimer
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
+from PySide6.QtWidgets import QSizePolicy
+import json
+
+class MplCanvas(FigureCanvas):
+    def __init__(self, parent=None, width=5, height=4, dpi=100):
+        self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor='grey')
+        super().__init__(self.fig)
+        self.setParent(parent)
+        self.axes = self.fig.add_subplot(111)
+        # Построим простой график
+        self.axes.plot([0, 1, 2, 3], [10, 1, 20, 3])
+        self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
+
+class SDRClient(QMainWindow):
+    def __init__(self):
+        super().__init__()
+        # Параметры подключения к серверу
+        self.server_ip = '127.0.0.1'
+        self.server_port = 5003
+        self.sock = None
+        # Параметры для отрисовки
+        self.channel_data = {
+            'time': np.array([]),
+            'channel_g': np.array([]),
+            'channel_rf': np.array([]),
+            'channel_adc_ttl': np.array([]),
+            'channel_rf_ttl': np.array([])
+        }
+
+        # Настройка окна
+        self.setWindowTitle("SDR Client")
+        self.setGeometry(100, 100, 1280, 720)
+        self.central_widget = QWidget()
+        self.setCentralWidget(self.central_widget)
+
+        self.layout = QHBoxLayout()
+
+        self.plot_layout = QVBoxLayout()
+        self.menu_layout = QVBoxLayout()
+        self.layout.addLayout(self.menu_layout)
+        self.layout.addLayout(self.plot_layout)
+
+        self.central_widget.setLayout(self.layout)
+
+        self.command_layout = QHBoxLayout()
+        self.plot_layout.addLayout(self.command_layout)
+        
+        self.add_event_button = QPushButton("Add Event")
+        self.command_layout.addWidget(self.add_event_button)
+        self.add_rfwave_button = QPushButton("Add RF Wave")
+        self.command_layout.addWidget(self.add_rfwave_button)
+        self.run_event_list_button = QPushButton("Run Event List")
+        self.command_layout.addWidget(self.run_event_list_button)
+        self.clear_event_list_button = QPushButton("Clear Event List")
+        self.command_layout.addWidget(self.clear_event_list_button)
+
+        self.plot_canvas = MplCanvas(self, width=5, height=4, dpi=100)
+        #self.plot_canvas.setSizePolicy(self.plot_widget.sizePolicy())
+        self.plot_layout.addWidget(self.plot_canvas, stretch=1)
+
+        toolbar = NavigationToolbar(self.plot_canvas, self)
+        self.plot_layout.addWidget(toolbar, stretch=1)
+
+        self.connect_button = QPushButton("Connect to SDR")
+        self.connect_button.setMinimumSize(200, 50)
+        self.disconnect_button = QPushButton("Disconnect")
+        self.disconnect_button.setMinimumSize(200, 50)
+        self.settings_button = QPushButton("Settings")
+        self.settings_button.setMinimumSize(200, 50)
+        self.recv_sim_data_button = QPushButton("Receive Sim Data")
+        self.recv_sim_data_button.setMinimumSize(200, 50)
+
+        self.menu_layout.addWidget(self.connect_button)
+        self.menu_layout.addWidget(self.disconnect_button)
+        self.menu_layout.addWidget(self.settings_button)
+        self.menu_layout.addWidget(self.recv_sim_data_button)
+
+        self.connect_button.clicked.connect(self.connect_to_server)
+        self.disconnect_button.clicked.connect(self.disconnect_from_server)
+        self.add_event_button.clicked.connect(self.add_event_dialog)
+
+    def connect_to_server(self):
+        try:
+            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.sock.connect((self.server_ip, self.server_port))
+            print(f"Connected to SDR server at {self.server_ip}:{self.server_port}")
+        except Exception as e:
+            print(f"Failed to connect to server: {e}")
+            QMessageBox.critical(self, "Connection Error", f"Failed to connect to server: {e}")
+            self.sock = None
+
+    def disconnect_from_server(self):
+        if self.sock:
+            self.sock.close()
+            self.sock = None
+            QMessageBox.information(self, "Disconnected", "Successfully disconnected from SDR server")
+            print("Disconnected from SDR server")
+
+    def add_event_dialog(self):
+        #if not self.sock:
+        #    QMessageBox.warning(self, "Not Connected", "Please connect to the SDR server first")
+        #    return False
+        add_event_dialog = QDialog(self)
+        add_event_dialog.setWindowTitle("Add Event")
+        layout = QFormLayout()
+
+        labels = ["Start Time (ticks)", "Duration (ticks)", "Waveform Type", "time_front_rf_ttl (ticks)", "time_back_rf_ttl (ticks)", "time_front_adc_ttl (ticks)", "time_back_adc_ttl (ticks)", "time_start_rf (ticks)", "time_end_rf (ticks)", "rf_freq (Hz)"]
+        self.event_inputs = {}
+        for label in labels:
+            if label == "Waveform Type":
+                combo = QComboBox()
+                combo.addItems(["sine", "square", "triangle"])
+                layout.addRow(QLabel(label), combo)
+                self.event_inputs[label] = combo
+            else:
+                line_edit = QLineEdit()
+                layout.addRow(QLabel(label), line_edit)
+                self.event_inputs[label] = line_edit
+        
+        submit_button = QPushButton("Submit")
+        layout.addWidget(submit_button)
+
+        load_event_list_button = QPushButton("Load Event List")
+        layout.addWidget(load_event_list_button)
+
+        submit_button.clicked.connect(self.submit_event)
+        load_event_list_button.clicked.connect(self.load_event_list)
+
+        add_event_dialog.setLayout(layout)
+        add_event_dialog.exec()
+
+    def submit_event(self):
+        try:
+            event_data = {
+                "time": int(self.event_inputs["Start Time (ticks)"].text()),
+                "duration": int(self.event_inputs["Duration (ticks)"].text()),
+                "wave": self.event_inputs["Waveform Type"].currentText(),
+                "time_front_rf_trigger": int(self.event_inputs["time_front_rf_ttl (ticks)"].text()),
+                "time_back_rf_trigger": int(self.event_inputs["time_back_rf_ttl (ticks)"].text()),
+                "time_front_adc_trigger": int(self.event_inputs["time_front_adc_ttl (ticks)"].text()),
+                "time_back_adc_trigger": int(self.event_inputs["time_back_adc_ttl (ticks)"].text()),
+                "rf_start": int(self.event_inputs["time_start_rf (ticks)"].text()),
+                "rf_end": int(self.event_inputs["time_end_rf (ticks)"].text()),
+                "carrier_freq": float(self.event_inputs["rf_freq (Hz)"].text())
+            }
+            msg = msgpack.packb({"command": 0x10, "data": event_data})
+            self.sock.sendall(msg)
+            print("Event added successfully")
+            QMessageBox.information(self, "Success", "Event added successfully")
+            self.add_event_dialog.close()
+        except Exception as e:
+            print(f"Failed to add event: {e}")
+            QMessageBox.critical(self, "Error", f"Failed to add event: {e}")
+
+    def load_event_list(self):
+        file_dialog = QFileDialog(self)
+        file_dialog.setNameFilter("JSON Files (*.json)")
+        if file_dialog.exec():
+            selected_file = file_dialog.selectedFiles()[0]
+            try:
+                with open(selected_file, 'r') as f:
+                    event_list = json.load(f)
+                for event_data in event_list:
+                    msg = {
+                        "magic": 0xAA,
+                        "cmd": 0x10,
+                        "time": event_data["time"],
+                        "duration": event_data["duration"],
+                        "wave": event_data["wave"],
+                        "time_front_rf_trigger": event_data["time_front_rf_trigger"],
+                        "time_back_rf_trigger": event_data["time_back_rf_trigger"],
+                        "time_front_adc_trigger": event_data["time_front_adc_trigger"],
+                        "time_back_adc_trigger": event_data["time_back_adc_trigger"],
+                        "rf_start": event_data["rf_start"],
+                        "rf_end": event_data["rf_end"],
+                        "carrier_freq": event_data["carrier_freq"]
+                    }
+                    packed_msg = msgpack.packb(msg, use_bin_type=True)
+                    self.sock.sendall(packed_msg)
+                    resp = self.sock.recv(1024)
+                    if not resp:
+                        raise Exception("No response from server")
+                    unpacked_resp = msgpack.unpackb(resp, raw=False)
+                    if unpacked_resp["cmd"] != 0xFE and unpacked_resp["code"] != 0x00000010:
+                        raise Exception(f"Server error: {unpacked_resp['message']}")
+                print("Event list loaded successfully")
+                QMessageBox.information(self, "Success", "Event list loaded successfully")
+                self.add_event_dialog.close()
+            except Exception as e:
+                print(f"Failed to load event list: {e}")
+                QMessageBox.critical(self, "Error", f"Failed to load event list: {e}")
+
+app = QApplication([])
+client = SDRClient()
+client.show()
+#plt.ion()  # Enable interactive mode for plotting
+#plt.show()
+app.exec()

+ 30 - 5
sdr-tcp/server-simulator.py

@@ -17,7 +17,6 @@ class ServerSimulator:
         self.data = {}
         self.simview = {}
         #Simulation parameters
-        self.freq = 3e6
         self.level = 20000
         #32bit Codes
         self.errCode = 0x00000000
@@ -28,6 +27,8 @@ class ServerSimulator:
            0x20: self.getRfwaveTable,
            0x30: self.runEventList,
            0x40: self.sendSimViewData,
+           0x60: self.clearEventList,
+           0x70: self.setFrequency,
            0x00: self.disconnect
         }
 
@@ -36,6 +37,7 @@ class ServerSimulator:
         self.sock.listen(1)
         self.conn, addr = self.sock.accept()
         print(f"New connection from {addr}")
+        return True
 
     def disconnect(self):
         self.conn.close()
@@ -43,7 +45,7 @@ class ServerSimulator:
     def getErrorMsg(self, errCode, errString):
         error = {
             "magic": self.magic,
-            "cmd": b'\xFF',
+            "cmd": 0xFF,
             "code": errCode,
             "message": errString
             }
@@ -52,7 +54,7 @@ class ServerSimulator:
     def getStatusMsg(self, statCode, statString):
         status = {
             "magic": self.magic,
-            "cmd": b'\xFE',
+            "cmd": 0xFE,
             "code": statCode,
             "message": statString
         }
@@ -97,6 +99,7 @@ class ServerSimulator:
             "start_time": data["time"],
             "duration": data["duration"],
             "wave": data["wave"],
+            "carrier_freq": data["carrier_freq"],
             "time_front_rf_trigger": data["time_front_rf_trigger"],
             "time_back_rf_trigger": data["time_back_rf"],
             "rf_start": data["rf_start"],
@@ -165,6 +168,7 @@ class ServerSimulator:
             return False
         self.events.append(event)
         print(f"Added event: {event}")
+        self.conn.sendall(self.getStatusMsg(0x00000010, "Event added successfully"))
         return True
 
     def getRfwaveTable(self, data):
@@ -278,7 +282,7 @@ class ServerSimulator:
                 interp_wave_q = interp_func_q(rf_time).astype(np.int16)
                 interp_wave_i = interp_func_i(rf_time).astype(np.int16)
 
-            channel_rf[event["rf_start"]:event["rf_end"]+1] = np.floor((interp_wave_q * np.cos(2 * np.pi * self.freq * rf_time * 8e-9) - interp_wave_i * np.sin(2 * np.pi * self.freq * rf_time * 8e-9)) / np.sqrt(2)).astype(np.int16)
+            channel_rf[event["rf_start"]:event["rf_end"]+1] = np.floor((interp_wave_q * np.cos(2 * np.pi * event["carrier_freq"] * rf_time * 8e-9) - interp_wave_i * np.sin(2 * np.pi * event["carrier_freq"] * rf_time * 8e-9)) / np.sqrt(2)).astype(np.int16)
             channel_rf_ttl[event["time_front_rf_trigger"]:event["time_back_rf_trigger"]+1] = 1
             channel_adc_ttl[event["time_front_adc_trigger"]:event["time_back_adc_trigger"]+1] = 1
             channel_g[event["start_time"]:(event["start_time"]+event["duration"]+1)] = 0
@@ -327,4 +331,25 @@ class ServerSimulator:
                     return False
         print("Simulation data sent successfully")
         self.conn.sendall(self.getStatusMsg(0x00000044, "All simulation data packets sent"))
-        return True
+        return True
+    
+    def clearEventList(self, data):
+        self.events = []
+        self.conn.sendall(self.getStatusMsg(0x00000064, "Event list cleared"))
+        return True
+    
+    def setFrequency(self, data):
+        if "frequency" not in data:
+            print("Missing frequency parameter")
+            self.errCode = 0x00000020 # Missing parameter
+            self.conn.sendall(self.getErrorMsg(self.errCode, "Missing frequency parameter"))
+            return False
+        self.freq = data["frequency"]
+        self.conn.sendall(self.getStatusMsg(0x00000071, f"Frequency set to {self.freq} Hz"))
+        return True
+    
+server = ServerSimulator()
+
+while server.connect('127.0.0.1', 5003):
+    while server.acceptCommand():
+        pass