fale

 avatar
unknown
python
2 years ago
12 kB
6
Indexable
import sys
import PyQt5.QtWidgets
import pyqtgraph as pg
import numpy as np
from scipy.io import wavfile
from scipy.fft import fft
import csv


class SignalGenerator:
    def __init__(self, sampling_rate, duration):
        self.sampling_rate = sampling_rate
        self.duration = duration

    def generate_sine(self, frequency, amplitude):
        t = np.linspace(0, self.duration, int(self.sampling_rate * self.duration), endpoint=False)
        signal = amplitude * np.sin(2 * np.pi * frequency * t)
        return t, signal

    def generate_square(self, frequency, amplitude):
        t = np.linspace(0, self.duration, int(self.sampling_rate * self.duration), endpoint=False)
        signal = amplitude * np.sign(np.sin(2 * np.pi * frequency * t))
        return t, signal

    def generate_sawtooth(self, frequency, amplitude):
        t = np.linspace(0, self.duration, int(self.sampling_rate * self.duration), endpoint=False)
        signal = amplitude * (2 * (frequency * t - np.floor(0.5 + frequency * t)))
        return t, signal

    def generate_triangle(self, frequency, amplitude):
        t = np.linspace(0, self.duration, int(self.sampling_rate * self.duration), endpoint=False)
        signal = amplitude * np.abs((2 * (frequency * t - np.floor(0.5 + frequency * t))) - 1)
        return t, signal

    def generate_white_noise(self, amplitude):
        signal = amplitude * np.random.uniform(-1, 1, int(self.sampling_rate * self.duration))
        t = np.linspace(0, self.duration, int(self.sampling_rate * self.duration), endpoint=False)
        return t, signal
#wykres transformaty furiera
    def plot_fourier_transform(self, signal, title, plot_widget):
        N = len(signal)
        T = 1 / self.sampling_rate
        yf = fft(signal)
        xf = np.fft.fftfreq(N, T)[:N // 2]
        plot_widget.plot(xf, 2.0 / N * np.abs(yf[0:N // 2]))
        plot_widget.setTitle(title)
        plot_widget.setLabel('bottom', 'Częstotliwość (Hz)')
        plot_widget.setLabel('left', 'Amplituda')
        plot_widget.setXRange(0, 3000)
        plot_widget.setYRange(0, 0.0125)
        plot_widget.getAxis('bottom').setTicks([[(i, str(i)) for i in range(0, 3001, 500)]])
        plot_widget.getAxis('left').setTicks([[(i, str(i)) for i in np.arange(0, 0.0126, 0.0025)]])
        plot_widget.show()
#zapisywanie do wav i csv
    def save_to_wav(self, t, signal, filename):
        if signal is not None:
            scaled_data = np.int16(signal / np.max(np.abs(signal)) * 32767)
            scaled_data = scaled_data.flatten()  # Spłaszcz dane, jeśli są wielowymiarowe
            wavfile.write(filename, self.sampling_rate, scaled_data)

    def save_fourier_to_csv(self, signal, filename):
        if signal is not None:
            N = len(signal)
            T = 1 / self.sampling_rate
            yf = fft(signal)
            xf = np.fft.fftfreq(N, T)[:N // 2]

            with open(filename, 'w', newline='') as csvfile:
                csvwriter = csv.writer(csvfile)
                csvwriter.writerow(['Częstotliwość', 'Amplituda'])
                for freq, amp in zip(xf, 2.0 / N * np.abs(yf[0:N // 2])):
                    csvwriter.writerow(
                        [freq.item(), amp.item()])  # Użyj item() do pobrania wartości z jednoelementowej tablicy NumPy


class SignalGeneratorGUI(PyQt5.QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()

        self.generator = SignalGenerator(44100, 5)
        self.t = None  # Add this line to store time data
        self.signal = None  # Add this line to store signal data

        self.central_widget = PyQt5.QtWidgets.QWidget()
        self.setCentralWidget(self.central_widget)

        self.init_ui()

    def init_ui(self):
        # Layouts
        main_layout = PyQt5.QtWidgets.QHBoxLayout()

        # Left-side layout for signal options
        options_layout = PyQt5.QtWidgets.QVBoxLayout()
#wybor fali
        # Signal Type
        signal_type_label = PyQt5.QtWidgets.QLabel("Typ sygnału:")
        self.signal_type_combo = PyQt5.QtWidgets.QComboBox()
        self.signal_type_combo.addItems(['Sine', 'Square', 'Triangle', 'Sawtooth', 'White Noise'])
        options_layout.addWidget(signal_type_label)
        options_layout.addWidget(self.signal_type_combo)
#podawanie parametrow spinboxy
        # Sampling Rate
        sampling_rate_label = PyQt5.QtWidgets.QLabel("Częstotliwość próbkowania (Hz):")
        self.sampling_rate_spin = PyQt5.QtWidgets.QSpinBox()
        self.sampling_rate_spin.setValue(44100)
        self.sampling_rate_spin.setRange(0, 100000)
        self.sampling_rate_spin.setSingleStep(1)
        options_layout.addWidget(sampling_rate_label)
        options_layout.addWidget(self.sampling_rate_spin)

        # Duration
        duration_label = PyQt5.QtWidgets.QLabel("Długość trwania (s):")
        self.duration_spin = PyQt5.QtWidgets.QSpinBox()
        self.duration_spin.setValue(0)
        self.duration_spin.setRange(0, 100000)
        self.duration_spin.setSingleStep(1)
        options_layout.addWidget(duration_label)
        options_layout.addWidget(self.duration_spin)

        # Frequency
        frequency_label = PyQt5.QtWidgets.QLabel("Częstotliwość (Hz):")
        self.frequency_double_spin = PyQt5.QtWidgets.QDoubleSpinBox()
        self.frequency_double_spin.setValue(0)
        self.frequency_double_spin.setRange(0, 100000)
        self.frequency_double_spin.setSingleStep(1)
        options_layout.addWidget(frequency_label)
        options_layout.addWidget(self.frequency_double_spin)

        # Amplitude
        amplitude_label = PyQt5.QtWidgets.QLabel("Amplituda:")
        self.amplitude_double_spin = PyQt5.QtWidgets.QDoubleSpinBox()
        self.amplitude_double_spin.setValue(0)
        self.amplitude_double_spin.setRange(0, 100000)
        self.amplitude_double_spin.setSingleStep(1)
        options_layout.addWidget(amplitude_label)
        options_layout.addWidget(self.amplitude_double_spin)

        # Generate Button
        generate_button = PyQt5.QtWidgets.QPushButton("Generuj sygnał")
        generate_button.clicked.connect(self.generate_signal)
        options_layout.addWidget(generate_button)

        # Connect SpinBox valueChanged signals
        self.sampling_rate_spin.valueChanged.connect(self.update_signal)
        self.duration_spin.valueChanged.connect(self.update_signal)
        self.frequency_double_spin.valueChanged.connect(self.update_signal)
        self.amplitude_double_spin.valueChanged.connect(self.update_signal)

        # Right-side layout for plots and tables
        plots_layout = PyQt5.QtWidgets.QHBoxLayout()
#tabelka z czasem i amplituda a powinno byc z czasem i zbiorem wartosci
        # Table for time domain
        self.table = PyQt5.QtWidgets.QTableWidget()
        self.table.setColumnCount(2)
        self.table.setHorizontalHeaderLabels(['Czas', 'Zbiór wartości'])
        plots_layout.addWidget(self.table)

        # Middle layout for plots
        middle_layout = PyQt5.QtWidgets.QVBoxLayout()

        # Plot
        self.plot_widget = pg.PlotWidget()
        self.plot_widget.setLabel('bottom', 'Czas (s)')
        self.plot_widget.setLabel('left', 'Amplituda')
        middle_layout.addWidget(self.plot_widget)

        # Fourier Transform Plot
        self.fourier_plot_widget = pg.PlotWidget()
        self.fourier_plot_widget.setLabel('bottom', 'Częstotliwość (Hz)')
        self.fourier_plot_widget.setLabel('left', 'Amplituda')
        middle_layout.addWidget(self.fourier_plot_widget)

        plots_layout.addLayout(middle_layout)

        main_layout.addLayout(options_layout)
        main_layout.addLayout(plots_layout)
#pryzciski do zapisywania do wav i csv
        # Menu Bar
        menubar = self.menuBar()
        file_menu = menubar.addMenu('Plik')

        # Save to WAV
        save_wav_action = PyQt5.QtWidgets.QAction('Zapisz do WAV', self)
        save_wav_action.triggered.connect(self.save_to_wav)
        file_menu.addAction(save_wav_action)

        # Save to CSV
        save_csv_action = PyQt5.QtWidgets.QAction('Zapisz do CSV', self)
        save_csv_action.triggered.connect(self.save_to_csv)
        file_menu.addAction(save_csv_action)

        self.central_widget.setLayout(main_layout)
        self.setWindowTitle("Generator Sygnałów")
        self.show()

    def update_signal(self):
        # Funkcja wywoływana po zmianie wartości w spinboxach
        if self.t is not None and self.signal is not None and len(self.t) > 0 and len(self.signal) > 0:
            # Aktualizacja Table
            self.table.setRowCount(len(self.t))
            for i in range(len(self.t)):
                self.table.setItem(i, 0, PyQt5.QtWidgets.QTableWidgetItem(str(self.t[i])))
                self.table.setItem(i, 1, PyQt5.QtWidgets.QTableWidgetItem(str(self.signal[i])))

            # Aktualizacja Plot
            self.plot_widget.clear()
            self.plot_widget.plot(self.t, self.signal, pen='b')

            # Aktualizacja Fourier Transform Plot
            self.fourier_plot_widget.clear()
            self.generator.plot_fourier_transform(self.signal, 'Fourier Transform', self.fourier_plot_widget)

    def generate_signal(self):
        # Generowanie sygnału na podstawie aktualnych wartości spinboxów
        signal_type = self.signal_type_combo.currentText()
        frequency = self.frequency_double_spin.value()
        amplitude = self.amplitude_double_spin.value()
        duration = self.duration_spin.value()
        sampling_rate = self.sampling_rate_spin.value()

        self.t, self.signal = [], []  # Reset danych

        if signal_type == 'Sine':
            self.t, self.signal = self.generator.generate_sine(frequency, amplitude)
        elif signal_type == 'Square':
            self.t, self.signal = self.generator.generate_square(frequency, amplitude)
        elif signal_type == 'Triangle':
            self.t, self.signal = self.generator.generate_triangle(frequency, amplitude)
        elif signal_type == 'Sawtooth':
            self.t, self.signal = self.generator.generate_sawtooth(frequency, amplitude)
        elif signal_type == 'White Noise':
            self.t, self.signal = self.generator.generate_white_noise(amplitude)

        self.update_signal()  # Wywołanie funkcji aktualizującej po wygenerowaniu nowych danych

        t, signal = self.generator.generate_sine(frequency, amplitude)  # Default to sine for simplicity

        # Update Table
        self.table.setRowCount(len(self.t))
        for i in range(len(self.t)):
            self.table.setItem(i, 0, PyQt5.QtWidgets.QTableWidgetItem(str(self.t[i])))
            self.table.setItem(i, 1, PyQt5.QtWidgets.QTableWidgetItem(str(self.signal[i])))

        # Update Plot
        self.plot_widget.clear()
        self.plot_widget.plot(self.t, self.signal, pen='b')

        # Update Fourier Transform Plot
        self.fourier_plot_widget.clear()
        self.generator.plot_fourier_transform(self.signal, 'Fourier Transform', self.fourier_plot_widget)

    def save_to_wav(self):
        filename, _ = PyQt5.QtWidgets.QFileDialog.getSaveFileName(self, 'Zapisz do pliku WAV', '',
                                                                  'Wave Files (*.wav);;All Files (*)')
        if filename:
            self.generator.save_to_wav(self.t, self.signal, filename)

    def save_to_csv(self):
        filename, _ = PyQt5.QtWidgets.QFileDialog.getSaveFileName(self, 'Zapisz do pliku CSV', '',
                                                                  'CSV Files (*.csv);;All Files (*)')
        if filename:
            self.generator.save_fourier_to_csv(self.signal, filename)


if __name__ == '__main__':
    app = PyQt5.QtWidgets.QApplication(sys.argv)
    window = SignalGeneratorGUI()
    sys.exit(app.exec_())
Editor is loading...
Leave a Comment