import sys
import socket
import struct
import numpy as np
from PyQt5 import QtWidgets, QtCore
import pyqtgraph as pg

class ADCPlotter(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("ADC 波形與 FFT 顯示器")
        self.setGeometry(100, 100, 1000, 750)

        # 中央介面
        self.central_widget = QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QtWidgets.QVBoxLayout(self.central_widget)

        self.status_label = QtWidgets.QLabel("尚未連線")
        self.layout.addWidget(self.status_label)

        # 控制按鈕
        self.connect_button = QtWidgets.QPushButton("連線")
        self.connect_button.clicked.connect(self.connect_to_board)
        self.layout.addWidget(self.connect_button)

        self.Single_collection_button = QtWidgets.QPushButton("Single Acquisition")
        self.Single_collection_button.clicked.connect(self.Single_collection)
        self.layout.addWidget(self.Single_collection_button)

        self.Realtime_Acquisition_button = QtWidgets.QPushButton("Real-time Acquisition")
        self.Realtime_Acquisition_button.clicked.connect(self.start_realtime_acquisition)
        self.layout.addWidget(self.Realtime_Acquisition_button)

        self.stop_button = QtWidgets.QPushButton("停止")
        self.stop_button.clicked.connect(self.stop_realtime_acquisition)
        self.layout.addWidget(self.stop_button)

        # FFT 範圍設定區
        freq_range_layout = QtWidgets.QHBoxLayout()
        self.start_freq_input = QtWidgets.QLineEdit("20000")
        self.stop_freq_input = QtWidgets.QLineEdit("10000000")
        self.apply_freq_button = QtWidgets.QPushButton("套用 FFT 範圍")
        self.apply_freq_button.clicked.connect(self.update_fft_range)
        freq_range_layout.addWidget(QtWidgets.QLabel("Start (Hz):"))
        freq_range_layout.addWidget(self.start_freq_input)
        freq_range_layout.addWidget(QtWidgets.QLabel("Stop (Hz):"))
        freq_range_layout.addWidget(self.stop_freq_input)
        freq_range_layout.addWidget(self.apply_freq_button)
        self.layout.addLayout(freq_range_layout)

        # 圖形顯示區
        self.plot_widget = pg.GraphicsLayoutWidget()

        # --- ADC 波形圖 ---
        self.plot_time = self.plot_widget.addPlot(title="ADC 波形圖")
        self.plot_time.setYRange(-0.5, 0.5)
        self.plot_time.showGrid(x=True, y=True)
        self.plot_time.setLabel('left', 'Voltage', units='V')
        self.plot_time.setLabel('bottom', 'Time', units='µs')

        # ✅ 固定 X 軸範圍：0~50 µs，每 5 µs 一格
        self.plot_time.setXRange(0, 50, padding=0)
        ticks = [(i, f"{i} µs") for i in range(0, 51, 5)]
        self.plot_time.getAxis('bottom').setTicks([ticks])

        self.time_curve = self.plot_time.plot(pen='y')

        # --- FFT 圖 ---
        self.plot_widget.nextRow()
        self.plot_fft = self.plot_widget.addPlot(title="FFT 頻譜圖")
        self.plot_fft.setLogMode(x=True, y=False)
        self.plot_fft.showGrid(x=True, y=True)
        self.plot_fft.setLabel('bottom', 'Frequency', units='Hz')
        self.plot_fft.setLabel('left', 'Magnitude')
        self.fft_curve = self.plot_fft.plot(pen='c')

        self.layout.addWidget(self.plot_widget)

        # 網路參數
        self.host_ip = '192.168.0.3'
        self.host_port = 6102
        self.board_ip = '192.168.0.2'
        self.board_port = 5000

        # ADC 參數
        self.sample_num = 16384
        self.adc_width = 12
        self.voltage_range = 2
        self.sampling_rate = 50_000_000  # 50 MHz
        self.lsb = (2 * self.voltage_range) / (2 ** self.adc_width)

        # FFT 範圍預設
        self.fft_freq_start = 20000
        self.fft_freq_stop = 10000000

        # Timer
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.Single_collection)

        self.sock = None

    def connect_to_board(self):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.sock.bind((self.host_ip, self.host_port))
            self.sock.settimeout(2.0)
            self.status_label.setText("連線成功 🟢")
        except Exception as e:
            self.status_label.setText(f"錯誤: {e}")
            self.sock = None

    def Single_collection(self):
        if not self.sock:
            self.status_label.setText("錯誤：尚未連線")
            return

        try:
            hex_command = '55A50200004000F055A50100000001F055A50300000000F055A50000000000F0'
            cmd_bytes = bytes.fromhex(hex_command)
            self.sock.sendto(cmd_bytes, (self.board_ip, self.board_port))
            self.status_label.setText("已發送指令，等待資料...")

            data = b''
            while len(data) < self.sample_num * 2:
                packet, _ = self.sock.recvfrom(4096)
                data += packet

            self.status_label.setText("接收完成，顯示波形與 FFT")
            self.process_data(data)

        except Exception as e:
            self.status_label.setText(f"錯誤: {e}")
            self.timer.stop()

    def start_realtime_acquisition(self):
        if self.sock:
            self.timer.start(500)
            self.status_label.setText("連續采集中...")
        else:
            self.status_label.setText("請先連線")

    def stop_realtime_acquisition(self):
        self.timer.stop()
        self.status_label.setText("已停止連續采集")

    def update_fft_range(self):
        try:
            start = int(self.start_freq_input.text())
            stop = int(self.stop_freq_input.text())
            if 0 < start < stop:
                self.fft_freq_start = start
                self.fft_freq_stop = stop
                self.status_label.setText(f"FFT 範圍已套用：{start} ~ {stop} Hz")
            else:
                self.status_label.setText("錯誤：起始頻率必須小於終止頻率")
        except ValueError:
            self.status_label.setText("錯誤：請輸入正確數字")

    def process_data(self, raw_data):
        data = np.frombuffer(raw_data, dtype=np.uint16).astype(np.int16)
        data[data >= 2 ** (self.adc_width - 1)] -= 2 ** self.adc_width
        voltage = data * self.lsb

        # 時域波形：X 軸為微秒
        time_axis = np.arange(len(voltage)) / self.sampling_rate * 1e6
        self.time_curve.setData(time_axis, voltage)

        # FFT
        voltage -= np.mean(voltage)
        N = len(voltage)
        fft_data = np.fft.fft(voltage * np.hanning(N))
        freqs = np.fft.fftfreq(N, 1 / self.sampling_rate)

        fft_mag = 2.0 / N * np.abs(fft_data[:N // 2])
        freqs = freqs[:N // 2]

        mask = (freqs >= self.fft_freq_start) & (freqs <= self.fft_freq_stop)
        self.fft_curve.setData(freqs[mask], fft_mag[mask])

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = ADCPlotter()
    window.show()
    sys.exit(app.exec_())
