#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#
# SPDX-License-Identifier: GPL-3.0
#
# GNU Radio Python Flow Graph
# Title: FHSS Modulator and Demodulator
# Author: Jack L
# GNU Radio version: 3.10.8.0

from PyQt5 import Qt
from gnuradio import qtgui
from PyQt5.QtCore import QObject, pyqtSlot
from gnuradio import analog
import math
from gnuradio import audio
from gnuradio import blocks
from gnuradio import filter
from gnuradio.filter import firdes
from gnuradio import gr
from gnuradio.fft import window
import sys
import signal
from PyQt5 import Qt
from argparse import ArgumentParser
from gnuradio.eng_arg import eng_float, intx
from gnuradio import eng_notation
from gnuradio.qtgui import Range, RangeWidget
from PyQt5 import QtCore
import random



class FHSSfinal(gr.top_block, Qt.QWidget):

    def __init__(self):
        gr.top_block.__init__(self, "FHSS Modulator and Demodulator", catch_exceptions=True)
        Qt.QWidget.__init__(self)
        self.setWindowTitle("FHSS Modulator and Demodulator")
        qtgui.util.check_set_qss()
        try:
            self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc'))
        except BaseException as exc:
            print(f"Qt GUI: Could not set Icon: {str(exc)}", file=sys.stderr)
        self.top_scroll_layout = Qt.QVBoxLayout()
        self.setLayout(self.top_scroll_layout)
        self.top_scroll = Qt.QScrollArea()
        self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame)
        self.top_scroll_layout.addWidget(self.top_scroll)
        self.top_scroll.setWidgetResizable(True)
        self.top_widget = Qt.QWidget()
        self.top_scroll.setWidget(self.top_widget)
        self.top_layout = Qt.QVBoxLayout(self.top_widget)
        self.top_grid_layout = Qt.QGridLayout()
        self.top_layout.addLayout(self.top_grid_layout)

        self.settings = Qt.QSettings("GNU Radio", "FHSSfinal")

        try:
            geometry = self.settings.value("geometry")
            if geometry:
                self.restoreGeometry(geometry)
        except BaseException as exc:
            print(f"Qt GUI: Could not restore geometry: {str(exc)}", file=sys.stderr)

        ##################################################
        # Variables
        ##################################################
        self.samp_rate = samp_rate = 48000
        self.Sensitivity = Sensitivity = 2
        self.NoiseCutOff = NoiseCutOff = 3000
        self.NoiseAmp = NoiseAmp = 0
        self.Interpolation = Interpolation = 10
        self.Channel = Channel = 1
        self.CarrierFreq = CarrierFreq = 20000

        ##################################################
        # Blocks
        ##################################################

        self._NoiseAmp_range = Range(0, 40, 0.2, 0, 200)
        self._NoiseAmp_win = RangeWidget(self._NoiseAmp_range, self.set_NoiseAmp, "Noise Amplitude", "counter_slider", float, QtCore.Qt.Horizontal)
        self.top_layout.addWidget(self._NoiseAmp_win)
        # Create the options list
        self._Channel_options = [1, 2, 3, 4, 5, 6, 7, 8, 9]
        # Create the labels list
        self._Channel_labels = ['Channel-A 20kHz', 'Channel-B 40kHz', 'Channel-C 60kHz', 'Channel-D 80kHz', 'Channel-E 100kHz', 'Channel-F 120.0kHz', 'Channel-G 140kHz', 'Channel-H 160kHz', 'Channel-I 180kHz']
        # Create the combo box
        self._Channel_tool_bar = Qt.QToolBar(self)
        self._Channel_tool_bar.addWidget(Qt.QLabel("Channel" + ": "))
        self._Channel_combo_box = Qt.QComboBox()
        self._Channel_tool_bar.addWidget(self._Channel_combo_box)
        for _label in self._Channel_labels: self._Channel_combo_box.addItem(_label)
        self._Channel_callback = lambda i: Qt.QMetaObject.invokeMethod(self._Channel_combo_box, "setCurrentIndex", Qt.Q_ARG("int", self._Channel_options.index(i)))
        self._Channel_callback(self.Channel)
        self._Channel_combo_box.currentIndexChanged.connect(
            lambda i: self.set_Channel(self._Channel_options[i]))
        # Create the radio buttons
        self.top_layout.addWidget(self._Channel_tool_bar)
        self.low_pass_filter_2 = filter.fir_filter_fff(
            1,
            firdes.low_pass(
                1,
                (samp_rate*Interpolation),
                50000,
                100,
                window.WIN_HAMMING,
                6.76))
        self.low_pass_filter_0 = filter.fir_filter_ccf(
            1,
            firdes.low_pass(
                1,
                (samp_rate*Interpolation),
                NoiseCutOff,
                200,
                window.WIN_HAMMING,
                6.76))
        self.blocks_wavfile_source_0 = blocks.wavfile_source('C:\\Users\\ISA06016497\\Music\\Guitar_Test_Sound.wav', True)
        self.blocks_multiply_xx_1 = blocks.multiply_vcc(1)
        self.blocks_multiply_xx_0_1 = blocks.multiply_vcc(1)
        self.blocks_multiply_xx_0_0_0 = blocks.multiply_vcc(1)
        self.blocks_multiply_xx_0_0 = blocks.multiply_vcc(1)
        self.blocks_multiply_xx_0 = blocks.multiply_vcc(1)
        self.blocks_conjugate_cc_0_0_0 = blocks.conjugate_cc()
        self.blocks_conjugate_cc_0_0 = blocks.conjugate_cc()
        self.blocks_add_xx_0 = blocks.add_vcc(1)
        self.audio_sink_0 = audio.sink(samp_rate, '', True)
        self.analog_sig_source_x_0_1_0 = analog.sig_source_c((samp_rate*Interpolation), analog.GR_COS_WAVE, (Channel*CarrierFreq), 1, 0, 0)
        self.analog_sig_source_x_0_1 = analog.sig_source_c((samp_rate*Interpolation), analog.GR_COS_WAVE, (Channel*CarrierFreq), 1, 0, 0)
        self.analog_sig_source_x_0_0_0 = analog.sig_source_c((samp_rate*Interpolation), analog.GR_COS_WAVE, CarrierFreq, NoiseAmp, 0, 0)
        self.analog_sig_source_x_0_0 = analog.sig_source_c((samp_rate*Interpolation), analog.GR_COS_WAVE, CarrierFreq, 1, 0, 0)
        self.analog_sig_source_x_0 = analog.sig_source_c((samp_rate*Interpolation), analog.GR_COS_WAVE, CarrierFreq, 1, 0, 0)
        self.analog_quadrature_demod_cf_0 = analog.quadrature_demod_cf((1/Sensitivity))
        self.analog_phase_modulator_fc_0 = analog.phase_modulator_fc(Sensitivity)
        self.analog_noise_source_x_0 = analog.noise_source_c(analog.GR_GAUSSIAN, 1, 0)


        ##################################################
        # Connections
        ##################################################
        self.connect((self.analog_noise_source_x_0, 0), (self.low_pass_filter_0, 0))
        self.connect((self.analog_phase_modulator_fc_0, 0), (self.blocks_multiply_xx_0, 1))
        self.connect((self.analog_quadrature_demod_cf_0, 0), (self.audio_sink_0, 0))
        self.connect((self.analog_sig_source_x_0, 0), (self.blocks_multiply_xx_0, 0))
        self.connect((self.analog_sig_source_x_0_0, 0), (self.blocks_conjugate_cc_0_0, 0))
        self.connect((self.analog_sig_source_x_0_0_0, 0), (self.blocks_multiply_xx_1, 0))
        self.connect((self.analog_sig_source_x_0_1, 0), (self.blocks_multiply_xx_0_0, 0))
        self.connect((self.analog_sig_source_x_0_1_0, 0), (self.blocks_conjugate_cc_0_0_0, 0))
        self.connect((self.blocks_add_xx_0, 0), (self.blocks_multiply_xx_0_0_0, 1))
        self.connect((self.blocks_conjugate_cc_0_0, 0), (self.blocks_multiply_xx_0_1, 0))
        self.connect((self.blocks_conjugate_cc_0_0_0, 0), (self.blocks_multiply_xx_0_0_0, 0))
        self.connect((self.blocks_multiply_xx_0, 0), (self.blocks_multiply_xx_0_0, 1))
        self.connect((self.blocks_multiply_xx_0_0, 0), (self.blocks_add_xx_0, 0))
        self.connect((self.blocks_multiply_xx_0_0_0, 0), (self.blocks_multiply_xx_0_1, 1))
        self.connect((self.blocks_multiply_xx_0_1, 0), (self.analog_quadrature_demod_cf_0, 0))
        self.connect((self.blocks_multiply_xx_1, 0), (self.blocks_add_xx_0, 1))
        self.connect((self.blocks_wavfile_source_0, 0), (self.low_pass_filter_2, 0))
        self.connect((self.low_pass_filter_0, 0), (self.blocks_multiply_xx_1, 1))
        self.connect((self.low_pass_filter_2, 0), (self.analog_phase_modulator_fc_0, 0))


    def closeEvent(self, event):
        self.settings = Qt.QSettings("GNU Radio", "FHSSfinal")
        self.settings.setValue("geometry", self.saveGeometry())
        self.stop()
        self.wait()

        event.accept()

    def get_samp_rate(self):
        return self.samp_rate

    def set_samp_rate(self, samp_rate):
        self.samp_rate = samp_rate
        self.analog_sig_source_x_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_0_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_1.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_1_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.low_pass_filter_0.set_taps(firdes.low_pass(1, (self.samp_rate*self.Interpolation), self.NoiseCutOff, 200, window.WIN_HAMMING, 6.76))
        self.low_pass_filter_2.set_taps(firdes.low_pass(1, (self.samp_rate*self.Interpolation), 50000, 100, window.WIN_HAMMING, 6.76))

    def get_Sensitivity(self):
        return self.Sensitivity

    def set_Sensitivity(self, Sensitivity):
        self.Sensitivity = Sensitivity
        self.analog_phase_modulator_fc_0.set_sensitivity(self.Sensitivity)
        self.analog_quadrature_demod_cf_0.set_gain((1/self.Sensitivity))

    def get_NoiseCutOff(self):
        return self.NoiseCutOff

    def set_NoiseCutOff(self, NoiseCutOff):
        self.NoiseCutOff = NoiseCutOff
        self.low_pass_filter_0.set_taps(firdes.low_pass(1, (self.samp_rate*self.Interpolation), self.NoiseCutOff, 200, window.WIN_HAMMING, 6.76))

    def get_NoiseAmp(self):
        return self.NoiseAmp

    def set_NoiseAmp(self, NoiseAmp):
        self.NoiseAmp = NoiseAmp
        self.analog_sig_source_x_0_0_0.set_amplitude(self.NoiseAmp)

    def get_Interpolation(self):
        return self.Interpolation

    def set_Interpolation(self, Interpolation):
        self.Interpolation = Interpolation
        self.analog_sig_source_x_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_0_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_1.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.analog_sig_source_x_0_1_0.set_sampling_freq((self.samp_rate*self.Interpolation))
        self.low_pass_filter_0.set_taps(firdes.low_pass(1, (self.samp_rate*self.Interpolation), self.NoiseCutOff, 200, window.WIN_HAMMING, 6.76))
        self.low_pass_filter_2.set_taps(firdes.low_pass(1, (self.samp_rate*self.Interpolation), 50000, 100, window.WIN_HAMMING, 6.76))

    def get_Channel(self):
        return self.Channel

    def set_Channel(self, Channel):
        self.Channel = Channel
        self._Channel_callback(self.Channel)
        self.analog_sig_source_x_0_1.set_frequency((self.Channel*self.CarrierFreq))
        self.analog_sig_source_x_0_1_0.set_frequency((self.Channel*self.CarrierFreq))

    def get_CarrierFreq(self):
        return self.CarrierFreq

    def set_CarrierFreq(self, CarrierFreq):
        self.CarrierFreq = CarrierFreq
        self.analog_sig_source_x_0.set_frequency(self.CarrierFreq)
        self.analog_sig_source_x_0_0.set_frequency(self.CarrierFreq)
        self.analog_sig_source_x_0_0_0.set_frequency(self.CarrierFreq)
        self.analog_sig_source_x_0_1.set_frequency((self.Channel*self.CarrierFreq))
        self.analog_sig_source_x_0_1_0.set_frequency((self.Channel*self.CarrierFreq))




def main(top_block_cls=FHSSfinal, options=None):

    qapp = Qt.QApplication(sys.argv)

    tb = top_block_cls()

    tb.start()

    tb.show()

    def sig_handler(sig=None, frame=None):
        tb.stop()
        tb.wait()

        Qt.QApplication.quit()

    signal.signal(signal.SIGINT, sig_handler)
    signal.signal(signal.SIGTERM, sig_handler)

    timer = Qt.QTimer()
    timer.start(500)
    timer.timeout.connect(lambda: None)

    qapp.exec_()

if __name__ == '__main__':
    main()
