2

I am trying to develop a gui to receive data over udp with rate of 72Mbit/s. I am using the pyqtgraph to plot the data of 10 channels. but after receiving some packets, the widow freezes and it seems like some packets are lost.

I am using this code:

import sys
import collections
import pyqtgraph as pg
from PyQt5.QtCore import QThread, pyqtSignal, QTimer, QCoreApplication, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication, QVBoxLayout, QWidget, QLabel 
import socket

def convert_hex_to_bin(hex_string):
    return bin(int(hex_string, 16))[2:].zfill(len(hex_string) * 4)

class UdpThread(QThread):
    raw_data_received = pyqtSignal(bytes)

    def __init__(self, parent=None, port=8080):
        super().__init__(parent)
        self.port = port
        self.running = True

    def run(self):
        UDP_IP = "0.0.0.0"
        MAX_UDP_PAYLOAD = 1035
        try:
            udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            udp_socket.bind((UDP_IP, self.port))
            while self.running:
                data, _ = udp_socket.recvfrom(MAX_UDP_PAYLOAD)
                if data:
                    self.raw_data_received.emit(data)
                self.msleep(10)
        except socket.error as e:
            print(f"Socket error: {e}")
        finally:
            if 'udp_socket' in locals():
                udp_socket.close()

    def stop(self):
        self.running = False
        try:
            dummy_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            dummy_socket.sendto(b'stop', ('127.0.0.1', self.port))
            dummy_socket.close()
        except socket.error:
            pass
        self.wait()

class DataProcessor(QObject):
    data_parsed = pyqtSignal(int, list)

    def process_data(self, datagram_data):
        
        try:
            data_pack = datagram_data.hex()
            data_online = data_pack[16:-6]
            
            # --- Process Block 1 ---
            block1 = data_online[:1024]
            metadata_data1 = block1[:14]
            metadata_bin1 = convert_hex_to_bin(metadata_data1)
            channel1 = int(metadata_bin1[-7:], 2)
            block1_data = block1[14:-2]
            result_list1 = [int(block1_data[j:j+6], 16) for j in range(0, len(block1_data), 6)]
            self.data_parsed.emit(channel1, result_list1)
            
            # --- Process Block 2 ---
            block2 = data_online[1024:]
            metadata_data2 = block2[:14]
            metadata_bin2 = convert_hex_to_bin(metadata_data2)
            channel2 = int(metadata_bin2[-7:], 2)
            block2_data = block2[14:-2]
            result_list2 = [int(block2_data[j:j+6], 16) for j in range(0, len(block2_data), 6)]
            self.data_parsed.emit(channel2, result_list2)

        except (ValueError, IndexError) as e:
            print(f"Error parsing data: {e}")

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Data Logger")
        self.setGeometry(100, 100, 1200, 800) 
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        self.plot_widget = pg.PlotWidget()
        main_layout.addWidget(self.plot_widget)
        self.plot_widget.addLegend()
        self.plot_widget.showGrid(x=True, y=True, alpha=0.5)

        self.num_channels = 10 
        self.channel_colors = ['#00BFFF', '#FF4500', '#32CD32', '#FFD700', '#9370DB', '#808080', '#00AAFF', '#FF8000', '#32AA32', '#FFA700']
        
        self.data_buffers = {i: collections.deque() for i in range(1, self.num_channels + 1)} 
        self.curves = {i: self.plot_widget.plot(pen=pg.mkPen(self.channel_colors[i-1], width=2, style = pg.QtCore.Qt.DashLine), name=f"Channel {i}")
                       for i in range(1, self.num_channels + 1)}

        self.udp_thread = UdpThread()
        self.processor_thread = QThread()
        self.data_processor = DataProcessor()
        
        self.data_processor.moveToThread(self.processor_thread)
        self.processor_thread.start()
        
        self.udp_thread.raw_data_received.connect(self.data_processor.process_data)
        self.data_processor.data_parsed.connect(self.update_plot)
        self.udp_thread.start()

    def update_plot_view(self):
        max_len = max(len(buf) for buf in self.data_buffers.values())
        if max_len == 0:
            max_len = 1000 

        view_window_size = 1000 

        x_offset_val = (self.x_offset_slider.value() - 50) / 100.0 * view_window_size 
        x_div_val = (100 - self.x_div_slider.value()) / 100.0 * view_window_size + 100 

        x_max = max_len
        x_min = max(0, x_max - x_div_val)
        
        self.plot_widget.setXRange(x_min + x_offset_val, x_max + x_offset_val, padding=0)

    def update_plot(self, channel, data):
        if channel in self.data_buffers: 
            self.data_buffers[channel].extend(data) 

            self.curves[channel].setData(list(self.data_buffers[channel]))
            self.update_plot_view()
    
    def closeEvent(self, event):
        print("Stopping UDP listener thread...")
        self.udp_thread.stop()
        self.processor_thread.quit()
        self.processor_thread.wait()
        super().closeEvent(event)
        
if __name__ == '__main__':
    app = QCoreApplication.instance()
    if app is None:
        app = QApplication(sys.argv)
    
    main_win = MainWindow()
    main_win.show()
    
    sys.exit(app.exec_()) 

but this code leads to packet losses after a short time, that is, the first 60-70 packets are received correctly without any losses, but the following packets may be lost. Can anyone help me to troubleshoot this issue? Or is there any better options to develop a gui for high rate data? Thank you

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.