Thursday, September 28, 2023

TKINTER PYTHON: AMPLITUDE, FREQUENCY, AND PHASE MODULATION

 This is the product of BALIGE ACADEMY TEAM: Vivian Siahaan and Rismon Hasiholan Sianipar.

SEMANGAT BELAJAR dan HORAS!!!


BALIGE CITY, NORTH SUMATERA


SUPPORT OUR YOUTUBE CHANNEL BY SUBSCRIBING:






FULL SOURCE CODE 4.0 (AMPLITUDE AND FREQUENCY MODULATION):

#main_program.py
import tkinter as tk
from main_form import Main_Form
from plot_utils import Plot_Utils

class Main_Program():
    def __init__(self, root):
        self.initialize(root)

    def initialize(self, root):
        self.root = root
        width = 1560
        height = 450
        self.root.geometry(f"{width}x{height}")
        self.root.title("START FROM FROM SCRATCH SIGNAL AND IMAGE PROCESSING WITH TKINTER")

        #Creates necessary objects
        self.obj_main_form = Main_Form()
        self.obj_plot_utils = Plot_Utils()
        
        #Places widgets in root
        self.obj_main_form.add_widgets(self.root)  

        #Binds events
        self.binds_event()
                
    def binds_event(self):
        self.obj_plot_utils.binds_simple_sinusoidal(self.obj_main_form)
        self.obj_plot_utils.binds_modulation(self.obj_main_form)
         
if __name__ == "__main__":
    root = tk.Tk()
    app = Main_Program(root)
    root.mainloop()

#main_form.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class Main_Form:
    def add_widgets(self, root):        
        #Adds menu
        self.add_menu(root)

        #Adds canvasses
        self.add_canvas(root)

    def add_menu(self, root):
        self.menu_bar = tk.Menu(root)
        
        #Creates a sinusoidal menu
        self.sinusoidal_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.sinusoidal_menu.add_command(label="Simple Sinusoidal")
        self.sinusoidal_menu.add_command(label="Two Sinusoidals")
        self.sinusoidal_menu.add_command(label="More Two Sinusoidals")      
        self.menu_bar.add_cascade(label="Sinusoidal Signals", menu=self.sinusoidal_menu)      

        #Creates a modulation menu
        self.modulation = tk.Menu(self.menu_bar, tearoff=0)
        self.modulation.add_command(label="Amplitude Modulation")
        self.modulation.add_command(label="Frequency Modulation")     
        self.menu_bar.add_cascade(label="Modulation", menu=self.modulation)  
        
        root.config(menu=self.menu_bar)    

    def add_canvas(self, root):
        #Menambahkan canvas1 widget pada root untuk menampilkan hasil
        self.figure1 = Figure(figsize=(15.2, 4.2), dpi=100)
        self.figure1.patch.set_facecolor('gray')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=root)
        self.canvas1.get_tk_widget().grid(row=0, column=1, columnspan=1, 
            rowspan=25, padx=5, pady=5, sticky="n")


#form2.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from signal_utils import Signal_Utils

class Form2:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        self.signal_utils = Signal_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="START")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="END")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="STEP")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")

        self.label4 = tk.Label(master, text="FREQUENCY 1")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="FREQUENCY 2")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create an entry widget and place it in the first row, first column
        self.entry_start = tk.Entry(master, width=20, bg="cyan")
        self.entry_start.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_start.insert(0, "0")
        self.entry_start.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_end = tk.Entry(master, width=20, bg="cyan")
        self.entry_end.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_end.insert(0, "1")
        self.entry_end.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_step = tk.Entry(master, width=20, bg="cyan")
        self.entry_step.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_step.insert(0, "0.001")
        self.entry_step.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_freq1 = tk.Entry(master, width=20, bg="cyan")
        self.entry_freq1.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_freq1.insert(0, "7")
        self.entry_freq1.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_freq2 = tk.Entry(master, width=20, bg="cyan")
        self.entry_freq2.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_freq2.insert(0, "5")
        self.entry_freq2.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.7, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=10, column=0, padx=5, pady=1, sticky="n")

    def read_and_check_input(self, start, end, step, freq1, freq2):        
        try:
            start = float(self.entry_start.get())
        except ValueError:
            start = 0
            self.entry_start.delete(0, tk.END)
            self.entry_start.insert(0, "0")

        try:
            end = float(self.entry_end.get())
        except ValueError:
            end = 1
            self.entry_end.delete(0, tk.END)
            self.entry_end.insert(0, "1")

        try:
            step = float(self.entry_step.get())
        except ValueError:
            step = 0.001
            self.entry_step.delete(0, tk.END)
            self.entry_step.insert(0, "0.001")

        try:
            freq1 = float(self.entry_freq1.get())
        except ValueError:
            freq1 = 10
            self.entry_freq1.delete(0, tk.END)
            self.entry_freq1.insert(0, "10")

        try:
            freq2 = float(self.entry_freq2.get())
        except ValueError:
            freq2 = 5
            self.entry_freq2.delete(0, tk.END)
            self.entry_freq2.insert(0, "5")

        return start, end, step, freq1, freq2

    def plot_graph(self):
        start = self.entry_start.get()
        end = self.entry_end.get()
        step = self.entry_step.get()
        freq1 = self.entry_freq1.get()
        freq2 = self.entry_freq2.get()

        start, end, step, freq1, freq2 = self.read_and_check_input(start, end, 
                step, freq1, freq2)
        self.signal_utils.more_two_sines(start, end, step, freq1, 
                freq2, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()

        
if __name__ == "__main__":
    window = tk.Tk()
    Form2(window)
    window.mainloop()

#form1.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class Form1:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds canvasses
        self.add_canvas(self.window)

    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=0, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(15, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        
if __name__ == "__main__":
    window = tk.Tk()
    Form1(window)
    window.mainloop()

#signal_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class Signal_Utils:
    def __init__(self):
        pass

    def simple_sine(self, figure, canvas):
        figure.clear()        
        start = 0
        end = 1
        step = 0.001
        x = np.arange(float(start), float(end), float(step))
        Fs = 10
        y = np.sin(2*np.pi*float(Fs)*x)
        
        ax = figure.add_subplot(1,1,1)
        ax.plot(x, y, linewidth=5.0, color='red')
        ax.set_ylabel('Amplitude', color='blue')
        ax.set_xlabel('Time (second)', color='blue')
        ax.set_title(f'Simple Sinusoidal Graph (Frequency = {Fs} Hz)')
        ax.set_facecolor('#F0F0F0')
        ax.grid(True)
        figure.tight_layout()
        canvas.draw()
        
    def two_sines(self, figure, canvas):
        figure.clear()
        
        start = 0
        end = 1
        step = 0.001
        x = np.arange(float(start), float(end), float(step))
        Fs1 = 10
        Fs2 = 6
        y1 = np.sin(2*np.pi*float(Fs1)*x)
        y2 = np.cos(2*np.pi*float(Fs2)*x)
        
        ax1 = figure.add_subplot(2,2,1)
        ax1.plot(x, y1, linewidth=3.0, color='red', label=f'Sin(2*pi*{Fs1}*x)')
        ax1.plot(x, y2, linewidth=3.0, color='black', label=f'Cos(2*pi*{Fs2}*x)')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(f'Sin(2*pi*{Fs1}*x) and Cos(2*pi*{Fs2}*x)')
        ax1.set_facecolor('#F0F0F0')
        ax1.legend()
        ax1.grid(True)        

        ax2 = figure.add_subplot(2,2,2)
        ax2.plot(x, y1*y2, linewidth=3.0, color='green')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(f'Sin(2*pi*{Fs1}*x) * Cos(2*pi*{Fs2}*x)')
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)

        ax3 = figure.add_subplot(2,2,3)
        ax3.plot(x, y1 + y2, linewidth=3.0, color='brown')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(f'Sin(2*pi*{Fs1}*x) + Cos(2*pi*{Fs2}*x)')
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)        
        
        ax4 = figure.add_subplot(2,2,4)
        ax4.plot(x, y1 - y2, linewidth=3.0, color='blue')
        ax4.set_ylabel('Amplitude', color='blue')
        ax4.set_xlabel('Time (second)', color='blue')
        ax4.set_title(f'Sin(2*pi*{Fs1}*x) - Cos(2*pi*{Fs2}*x)')
        ax4.set_facecolor('#F0F0F0')
        ax4.grid(True) 
        
        figure.tight_layout()
        canvas.draw()  
        
    def more_two_sines(self, start=0, end=1, step=0.001, Fs1=7, Fs2=5, figure=None, canvas=None):
        figure.clear()

        x = np.arange(float(start), float(end), float(step))

        y1 = np.sin(2*np.pi*float(Fs1)*x)
        y2 = np.cos(2*np.pi*float(Fs2)*x)
        
        ax1 = figure.add_subplot(2,2,1)
        ax1.plot(x, y1, linewidth=3.0, color='red', label=f'Sin(2*pi*{Fs1}*x)')
        ax1.plot(x, y2, linewidth=3.0, color='black', label=f'Cos(2*pi*{Fs2}*x)')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(f'Sin(2*pi*{Fs1}*x) and Cos(2*pi*{Fs2}*x)')
        ax1.set_facecolor('#F0F0F0')
        ax1.legend()
        ax1.grid(True)        

        ax2 = figure.add_subplot(2,2,2)
        ax2.plot(x, y1*y2, linewidth=3.0, color='green')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(f'Sin(2*pi*{Fs1}*x) * Cos(2*pi*{Fs2}*x)')
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)

        ax3 = figure.add_subplot(2,2,3)
        ax3.plot(x, y1 + y2, linewidth=3.0, color='brown')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(f'Sin(2*pi*{Fs1}*x) + Cos(2*pi*{Fs2}*x)')
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)        
        
        ax4 = figure.add_subplot(2,2,4)
        ax4.plot(x, y1 - y2, linewidth=3.0, color='blue')
        ax4.set_ylabel('Amplitude', color='blue')
        ax4.set_xlabel('Time (second)', color='blue')
        ax4.set_title(f'Sin(2*pi*{Fs1}*x) - Cos(2*pi*{Fs2}*x)')
        ax4.set_facecolor('#F0F0F0')
        ax4.grid(True) 
        
        figure.tight_layout()
        canvas.draw()                
      

#plot_utils.py
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import *
import seaborn as sns
import numpy as np 
import pandas as pd
from form1 import Form1
from form2 import Form2
from form_am import Form_AM
from form_fm import Form_FM
from signal_utils import Signal_Utils
from am_utils import AM_Utils
from fm_utils import FM_Utils

class Plot_Utils:
    def __init__(self):
        self.signal_utils = Signal_Utils()
        self.am_utils = AM_Utils()
        self.fm_utils = FM_Utils()

    def plot_simple_sine(self):
        win = tk.Toplevel()
        form1 = Form1(win)
        win.title("Simple Sinusoidal Signal")
        self.signal_utils.simple_sine(form1.figure1, form1.canvas1)

    def plot_two_sines(self):
        win = tk.Toplevel()
        form1 = Form1(win)
        win.title("Two Simple Sinusoidal Signals")
        self.signal_utils.two_sines(form1.figure1, form1.canvas1)

    def plot_more_two_sines(self):
        win = tk.Toplevel()
        win.title("MANIPULATING SINUSOIDAL SIGNALS")
        form2 = Form2(win)
        start = float(form2.entry_start.get())
        end = float(form2.entry_end.get())
        step = float(form2.entry_step.get())
        freq1 = float(form2.entry_freq1.get())
        freq2 = float(form2.entry_freq2.get())
        self.signal_utils.more_two_sines(start, end, step, freq1, freq2, form2.figure1, form2.canvas1)
        
    def binds_simple_sinusoidal(self, window):     
        window.sinusoidal_menu.entryconfigure("Simple Sinusoidal",
            command =lambda:self.plot_simple_sine()) 
        
        window.sinusoidal_menu.entryconfigure("Two Sinusoidals",
            command =lambda:self.plot_two_sines())         

        window.sinusoidal_menu.entryconfigure("More Two Sinusoidals",
            command =lambda:self.plot_more_two_sines())  

    def plot_amplitude_modulation(self):
        win = tk.Toplevel()
        win.title("AMPLITUDE MODULATION")
        form_am = Form_AM(win)
        form_am.plot_fft_am()
        form_am.plot_graph()    

    def plot_frequency_modulation(self):
        win = tk.Toplevel()
        win.title("FREQUENCY MODULATION")
        form_fm = Form_FM(win)
        form_fm.plot_fft_fm()
        form_fm.plot_graph()
        
    def binds_modulation(self, window):     
        window.modulation.entryconfigure("Amplitude Modulation",
            command =lambda:self.plot_amplitude_modulation())         
        
        window.modulation.entryconfigure("Frequency Modulation",
            command =lambda:self.plot_frequency_modulation())      


#form_am.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from form1 import Form1

class Form_AM:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        self.am_utils = AM_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="MODULATING AMPLITUDE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="MODULATING FREQUENCY")
        self.label6.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")

        self.label7 = tk.Label(master, text="IDX MOD:", font=("Helvetica", 10))
        self.label7.grid(row=15, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="cyan")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="cyan")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="cyan")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="cyan")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_am = tk.Entry(master, width=20, bg="cyan")
        self.entry_am.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_am.insert(0, "1")
        self.entry_am.bind('<Return>', self.plot_graph_event)
        
        self.entry_fm = tk.Entry(master, width=20, bg="cyan")
        self.entry_fm.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fm.insert(0, "5")
        self.entry_fm.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=12, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_am)
        self.btn2.grid(row=13, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_am_db)
        self.btn3.grid(row=14, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, fs, T, ac, fc, am, fm):        
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            amp_mod = float(am)
        except ValueError:
            amp_mod = 1
            self.entry_am.delete(0, tk.END)
            self.entry_am.insert(0, "1")
            
        try:
            freq_mod = float(fm)
        except ValueError:
            freq_mod = 5
            self.entry_fm.delete(0, tk.END)
            self.entry_fm.insert(0, "5")
            
        return fsampling, periode, amp_carrier, freq_carrier, amp_mod, freq_mod

    def plot_graph(self):
        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()
        
        #Change text in label7 for index modulation
        miu=float(amp_mod)/float(amp_carrier)
        self.label7.config(text=f"IDX MOD: {miu:.2f}", font=("Helvetica", 12, "bold"))

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}'
        title2 = f'Modulating Signal, frequency = {Fm}, Amplitude = {Am}'
        title3 = "Amplitude Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             am_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_am(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            am_signal, "FFT of AM Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_am_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            am_signal, "Decible Magnitude of AM Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_AM(window)
    window.mainloop()

#am_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class AM_Utils:
    def __init__(self):
        pass

    def calculate_amplitude_modulation(self, Fs, T, Ac, Fc, Am, Fm):
        # Define time line
        t = np.arange(0, T, 1/Fs)

        # Carrier signal
        carrier_wave = Ac*np.cos(2*np.pi*Fc*t)

        # Modulating signal
        modulating_wave = Am*np.cos(2*np.pi*Fm*t)

        # Amplitude modulation
        miu = Am/Ac
        am_signal = Ac*(1 + miu*np.cos(2*np.pi*Fm*t)) * np.cos(2*np.pi*Fc*t)
        
        return t, carrier_wave, modulating_wave, am_signal
        
    def time_domain(self, t, carrier_wave, title1, modulating_wave, title2,
                             am_signal, title3, figure=None, canvas=None):
        figure.clear()   

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(t, carrier_wave, linewidth=3.0, color='red')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(title1)
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(t, modulating_wave, linewidth=3.0, color='black')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)        
        
        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(t, am_signal, linewidth=3.0, color='blue')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)         

        figure.tight_layout()
        canvas.draw()  

    def fft_signal(self, Fs, signal):
        # Perform FFT
        fft_result = np.fft.fft(signal)
        freqs = np.fft.fftfreq(len(signal), 1/Fs)
        
        # Keep only positive frequencies
        pos_freqs = freqs[:len(freqs)//2]
        pos_fft_result = fft_result[:len(fft_result)//2]

        return pos_freqs, pos_fft_result

    def fft_db(self, Fs, signal):
        # Apply Hamming window
        windowed_signal = signal * np.hamming(len(signal))
    
        # Perform FFT and converts its magnitude to decibel
        fft_result = np.fft.fft(windowed_signal)
    
        # Handle cases where fft_result is zero
        magnitude_dB = np.zeros_like(fft_result)
        non_zero_indices = np.abs(fft_result) > 0.01
        magnitude_dB[non_zero_indices] = 20*np.log10(np.abs(fft_result[non_zero_indices]))
    
        freqs = np.fft.fftfreq(len(signal), 1/Fs)

        # Keep only positive frequencies
        pos_freqs = freqs[:len(freqs)//2]
        pos_magnitude_dB = magnitude_dB[:len(magnitude_dB)//2]

        return pos_freqs, pos_magnitude_dB
    
    def plot_fft_signal(self, Fs, signal1, title1, signal2, title2, signal3, title3, figure=None, canvas=None):
        figure.clear()   

        #Calculate FFT
        freqs1, fft_result1 = self.fft_signal(Fs, signal1)
        freqs2, fft_result2 = self.fft_signal(Fs, signal2)
        freqs3, fft_result3 = self.fft_signal(Fs, signal3) 

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(freqs1, np.abs(fft_result1), linewidth=2.0, color='red')
        ax1.set_ylabel('Magnitude', color='blue')
        ax1.set_xlabel('Frequency (Hz)', color='blue')
        ax1.set_title(title1)
        ax1.set_xticks(np.arange(min(freqs1), max(freqs1)+1, step=100))
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(freqs2, np.abs(fft_result2), linewidth=2.0, color='black')
        ax2.set_ylabel('Magnitude', color='blue')
        ax2.set_xlabel('Frequency (Hz)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.set_xticks(np.arange(min(freqs1), max(freqs2)+1, step=100))
        ax2.grid(True)        

        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(freqs3, np.abs(fft_result3), linewidth=2.0, color='blue')
        ax3.set_ylabel('Magnitude', color='blue')
        ax3.set_xlabel('Frequency (Hz)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.set_xticks(np.arange(min(freqs1), max(freqs3)+1, step=100))
        ax3.grid(True)
                
        figure.tight_layout()
        canvas.draw()  

    def plot_fft_db(self, Fs, signal1, title1, signal2, title2, signal3, title3, figure=None, canvas=None):
        figure.clear()   

        #Calculate FFT
        freqs1, fft_result1 = self.fft_db(Fs, signal1)
        freqs2, fft_result2 = self.fft_db(Fs, signal2)
        freqs3, fft_result3 = self.fft_db(Fs, signal3) 

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(freqs1, np.abs(fft_result1), linewidth=2.0, color='red')
        ax1.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax1.set_xlabel('Frequency (Hz)', color='blue')
        ax1.set_title(title1)
        ax1.set_xticks(np.arange(min(freqs1), max(freqs1)+1, step=100))
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(freqs2, np.abs(fft_result2), linewidth=2.0, color='black')
        ax2.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax2.set_xlabel('Frequency (Hz)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.set_xticks(np.arange(min(freqs1), max(freqs2)+1, step=100))
        ax2.grid(True)        

        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(freqs3, np.abs(fft_result3), linewidth=2.0, color='blue')
        ax3.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax3.set_xlabel('Frequency (Hz)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.set_xticks(np.arange(min(freqs1), max(freqs3)+1, step=100))
        ax3.grid(True)
                
        figure.tight_layout()
        canvas.draw()
        
        
#fm_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class FM_Utils:
    def __init__(self):
        pass

    def calculate_frequency_modulation(self, beta, Fs, T, Ac, Fc, Am, Fm):
        # Defines time line
        t = np.arange(0, T, 1/Fs)

        # Carrier signal
        carrier_wave = Ac*np.sin(2*np.pi*Fc*t)

        # Modulating signal
        modulating_wave = Am*np.sin(2*np.pi*Fm*t)

        # Frequency modulation
        #beta is modulation index
        fm_signal = Ac*np.sin(2*np.pi*Fc*t + beta*modulating_wave)  
        
        return t, carrier_wave, modulating_wave, fm_signal

#form_fm.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from fm_utils import FM_Utils
from form1 import Form1

class Form_FM:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        #Creates neccesary objects
        self.am_utils = AM_Utils()
        self.fm_utils = FM_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="MODULATING AMPLITUDE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="MODULATING FREQUENCY")
        self.label6.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")

        self.label7 = tk.Label(master, text="IDX MODULATION:")
        self.label7.grid(row=12, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="cyan")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="cyan")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="cyan")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="cyan")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_am = tk.Entry(master, width=20, bg="cyan")
        self.entry_am.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_am.insert(0, "1")
        self.entry_am.bind('<Return>', self.plot_graph_event)
        
        self.entry_fm = tk.Entry(master, width=20, bg="cyan")
        self.entry_fm.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fm.insert(0, "5")
        self.entry_fm.bind('<Return>', self.plot_graph_event)

        self.entry_idx_mod = tk.Entry(master, width=20, bg="cyan")
        self.entry_idx_mod.grid(row=13, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_idx_mod.insert(0, "5")
        self.entry_idx_mod.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=14, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_fm)
        self.btn2.grid(row=15, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_fm_db)
        self.btn3.grid(row=16, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, beta, fs, T, ac, fc, am, fm):  
        try:
            idx_mod = float(beta)
        except ValueError:
            idx_mod = 5
            self.entry_idx_mod.delete(0, tk.END)
            self.entry_idx_mod.insert(0, "5")
            
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            amp_mod = float(am)
        except ValueError:
            amp_mod = 1
            self.entry_am.delete(0, tk.END)
            self.entry_am.insert(0, "1")
            
        try:
            freq_mod = float(fm)
        except ValueError:
            freq_mod = 5
            self.entry_fm.delete(0, tk.END)
            self.entry_fm.insert(0, "5")
            
        return idx_mod, fsampling, periode, amp_carrier, freq_carrier, amp_mod, freq_mod

    def plot_graph(self):
        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()        

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs frequency modulation
        t, carrier_wave, modulating_wave, fm_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}'
        title2 = f'Modulating Signal, frequency = {Fm}, Amplitude = {Am}'
        title3 = "Frequency Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             fm_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_fm(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs frequency modulation
        t, carrier_wave, modulating_wave, am_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            am_signal, "FFT of FM Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_fm_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            am_signal, "Decible Magnitude of FM Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_FM(window)
    window.mainloop()        


FULL SOURCE CODE 5.0 (PHASE MODULATION):



#main_program.py
import tkinter as tk
from main_form import Main_Form
from plot_utils import Plot_Utils

class Main_Program():
    def __init__(self, root):
        self.initialize(root)

    def initialize(self, root):
        self.root = root
        width = 1560
        height = 450
        self.root.geometry(f"{width}x{height}")
        self.root.title("START FROM FROM SCRATCH SIGNAL AND IMAGE PROCESSING WITH TKINTER")

        #Creates necessary objects
        self.obj_main_form = Main_Form()
        self.obj_plot_utils = Plot_Utils()
        
        #Places widgets in root
        self.obj_main_form.add_widgets(self.root)  

        #Binds events
        self.binds_event()
                
    def binds_event(self):
        self.obj_plot_utils.binds_simple_sinusoidal(self.obj_main_form)
        self.obj_plot_utils.binds_modulation(self.obj_main_form)
         
if __name__ == "__main__":
    root = tk.Tk()
    app = Main_Program(root)
    root.mainloop()

#main_form.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class Main_Form:
    def add_widgets(self, root):        
        #Adds menu
        self.add_menu(root)

        #Adds canvasses
        self.add_canvas(root)

    def add_menu(self, root):
        self.menu_bar = tk.Menu(root)
        
        #Creates a sinusoidal menu
        self.sinusoidal_menu = tk.Menu(self.menu_bar, tearoff=0)
        self.sinusoidal_menu.add_command(label="Simple Sinusoidal")
        self.sinusoidal_menu.add_command(label="Two Sinusoidals")
        self.sinusoidal_menu.add_command(label="More Two Sinusoidals")      
        self.menu_bar.add_cascade(label="Sinusoidal Signals", menu=self.sinusoidal_menu)      

        #Creates a modulation menu
        self.modulation = tk.Menu(self.menu_bar, tearoff=0)
        self.modulation.add_command(label="Amplitude Modulation")
        self.modulation.add_command(label="Frequency Modulation") 
        self.modulation.add_command(label="Phase Modulation")
        self.menu_bar.add_cascade(label="Modulation", menu=self.modulation)  
        
        root.config(menu=self.menu_bar)    

    def add_canvas(self, root):
        #Menambahkan canvas1 widget pada root untuk menampilkan hasil
        self.figure1 = Figure(figsize=(15.2, 4.2), dpi=100)
        self.figure1.patch.set_facecolor('gray')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=root)
        self.canvas1.get_tk_widget().grid(row=0, column=1, columnspan=1, 
            rowspan=25, padx=5, pady=5, sticky="n")


#form1.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

class Form1:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds canvasses
        self.add_canvas(self.window)

    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=0, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(15, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        
if __name__ == "__main__":
    window = tk.Tk()
    Form1(window)
    window.mainloop()

#form2.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from signal_utils import Signal_Utils

class Form2:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        self.signal_utils = Signal_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="START")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="END")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="STEP")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")

        self.label4 = tk.Label(master, text="FREQUENCY 1")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="FREQUENCY 2")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create an entry widget and place it in the first row, first column
        self.entry_start = tk.Entry(master, width=20, bg="cyan")
        self.entry_start.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_start.insert(0, "0")
        self.entry_start.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_end = tk.Entry(master, width=20, bg="cyan")
        self.entry_end.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_end.insert(0, "1")
        self.entry_end.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_step = tk.Entry(master, width=20, bg="cyan")
        self.entry_step.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_step.insert(0, "0.001")
        self.entry_step.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_freq1 = tk.Entry(master, width=20, bg="cyan")
        self.entry_freq1.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_freq1.insert(0, "7")
        self.entry_freq1.bind('<Return>', self.plot_graph_event)

        # Create an entry widget and place it in the first row, first column
        self.entry_freq2 = tk.Entry(master, width=20, bg="cyan")
        self.entry_freq2.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_freq2.insert(0, "5")
        self.entry_freq2.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.7, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=10, column=0, padx=5, pady=1, sticky="n")

    def read_and_check_input(self, start, end, step, freq1, freq2):        
        try:
            start = float(self.entry_start.get())
        except ValueError:
            start = 0
            self.entry_start.delete(0, tk.END)
            self.entry_start.insert(0, "0")

        try:
            end = float(self.entry_end.get())
        except ValueError:
            end = 1
            self.entry_end.delete(0, tk.END)
            self.entry_end.insert(0, "1")

        try:
            step = float(self.entry_step.get())
        except ValueError:
            step = 0.001
            self.entry_step.delete(0, tk.END)
            self.entry_step.insert(0, "0.001")

        try:
            freq1 = float(self.entry_freq1.get())
        except ValueError:
            freq1 = 10
            self.entry_freq1.delete(0, tk.END)
            self.entry_freq1.insert(0, "10")

        try:
            freq2 = float(self.entry_freq2.get())
        except ValueError:
            freq2 = 5
            self.entry_freq2.delete(0, tk.END)
            self.entry_freq2.insert(0, "5")

        return start, end, step, freq1, freq2

    def plot_graph(self):
        start = self.entry_start.get()
        end = self.entry_end.get()
        step = self.entry_step.get()
        freq1 = self.entry_freq1.get()
        freq2 = self.entry_freq2.get()

        start, end, step, freq1, freq2 = self.read_and_check_input(start, end, 
                step, freq1, freq2)
        self.signal_utils.more_two_sines(start, end, step, freq1, 
                freq2, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()

        
if __name__ == "__main__":
    window = tk.Tk()
    Form2(window)
    window.mainloop()

#signal_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class Signal_Utils:
    def __init__(self):
        pass

    def simple_sine(self, figure, canvas):
        figure.clear()        
        start = 0
        end = 1
        step = 0.001
        x = np.arange(float(start), float(end), float(step))
        Fs = 10
        y = np.sin(2*np.pi*float(Fs)*x)
        
        ax = figure.add_subplot(1,1,1)
        ax.plot(x, y, linewidth=5.0, color='red')
        ax.set_ylabel('Amplitude', color='blue')
        ax.set_xlabel('Time (second)', color='blue')
        ax.set_title(f'Simple Sinusoidal Graph (Frequency = {Fs} Hz)')
        ax.set_facecolor('#F0F0F0')
        ax.grid(True)
        figure.tight_layout()
        canvas.draw()
        
    def two_sines(self, figure, canvas):
        figure.clear()
        
        start = 0
        end = 1
        step = 0.001
        x = np.arange(float(start), float(end), float(step))
        Fs1 = 10
        Fs2 = 6
        y1 = np.sin(2*np.pi*float(Fs1)*x)
        y2 = np.cos(2*np.pi*float(Fs2)*x)
        
        ax1 = figure.add_subplot(2,2,1)
        ax1.plot(x, y1, linewidth=3.0, color='red', label=f'Sin(2*pi*{Fs1}*x)')
        ax1.plot(x, y2, linewidth=3.0, color='black', label=f'Cos(2*pi*{Fs2}*x)')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(f'Sin(2*pi*{Fs1}*x) and Cos(2*pi*{Fs2}*x)')
        ax1.set_facecolor('#F0F0F0')
        ax1.legend()
        ax1.grid(True)        

        ax2 = figure.add_subplot(2,2,2)
        ax2.plot(x, y1*y2, linewidth=3.0, color='green')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(f'Sin(2*pi*{Fs1}*x) * Cos(2*pi*{Fs2}*x)')
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)

        ax3 = figure.add_subplot(2,2,3)
        ax3.plot(x, y1 + y2, linewidth=3.0, color='brown')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(f'Sin(2*pi*{Fs1}*x) + Cos(2*pi*{Fs2}*x)')
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)        
        
        ax4 = figure.add_subplot(2,2,4)
        ax4.plot(x, y1 - y2, linewidth=3.0, color='blue')
        ax4.set_ylabel('Amplitude', color='blue')
        ax4.set_xlabel('Time (second)', color='blue')
        ax4.set_title(f'Sin(2*pi*{Fs1}*x) - Cos(2*pi*{Fs2}*x)')
        ax4.set_facecolor('#F0F0F0')
        ax4.grid(True) 
        
        figure.tight_layout()
        canvas.draw()  
        
    def more_two_sines(self, start=0, end=1, step=0.001, Fs1=7, Fs2=5, figure=None, canvas=None):
        figure.clear()

        x = np.arange(float(start), float(end), float(step))

        y1 = np.sin(2*np.pi*float(Fs1)*x)
        y2 = np.cos(2*np.pi*float(Fs2)*x)
        
        ax1 = figure.add_subplot(2,2,1)
        ax1.plot(x, y1, linewidth=3.0, color='red', label=f'Sin(2*pi*{Fs1}*x)')
        ax1.plot(x, y2, linewidth=3.0, color='black', label=f'Cos(2*pi*{Fs2}*x)')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(f'Sin(2*pi*{Fs1}*x) and Cos(2*pi*{Fs2}*x)')
        ax1.set_facecolor('#F0F0F0')
        ax1.legend()
        ax1.grid(True)        

        ax2 = figure.add_subplot(2,2,2)
        ax2.plot(x, y1*y2, linewidth=3.0, color='green')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(f'Sin(2*pi*{Fs1}*x) * Cos(2*pi*{Fs2}*x)')
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)

        ax3 = figure.add_subplot(2,2,3)
        ax3.plot(x, y1 + y2, linewidth=3.0, color='brown')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(f'Sin(2*pi*{Fs1}*x) + Cos(2*pi*{Fs2}*x)')
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)        
        
        ax4 = figure.add_subplot(2,2,4)
        ax4.plot(x, y1 - y2, linewidth=3.0, color='blue')
        ax4.set_ylabel('Amplitude', color='blue')
        ax4.set_xlabel('Time (second)', color='blue')
        ax4.set_title(f'Sin(2*pi*{Fs1}*x) - Cos(2*pi*{Fs2}*x)')
        ax4.set_facecolor('#F0F0F0')
        ax4.grid(True) 
        
        figure.tight_layout()
        canvas.draw()                
      
#plot_utils.py
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import *
import seaborn as sns
import numpy as np 
import pandas as pd
from form1 import Form1
from form2 import Form2
from form_am import Form_AM
from form_fm import Form_FM
from form_pm import Form_PM
from signal_utils import Signal_Utils

class Plot_Utils:
    def __init__(self):
        self.signal_utils = Signal_Utils()

    def plot_simple_sine(self):
        win = tk.Toplevel()
        form1 = Form1(win)
        win.title("Simple Sinusoidal Signal")
        self.signal_utils.simple_sine(form1.figure1, form1.canvas1)

    def plot_two_sines(self):
        win = tk.Toplevel()
        form1 = Form1(win)
        win.title("Two Simple Sinusoidal Signals")
        self.signal_utils.two_sines(form1.figure1, form1.canvas1)

    def plot_more_two_sines(self):
        win = tk.Toplevel()
        win.title("MANIPULATING SINUSOIDAL SIGNALS")
        form2 = Form2(win)
        start = float(form2.entry_start.get())
        end = float(form2.entry_end.get())
        step = float(form2.entry_step.get())
        freq1 = float(form2.entry_freq1.get())
        freq2 = float(form2.entry_freq2.get())
        self.signal_utils.more_two_sines(start, end, step, freq1, freq2, form2.figure1, form2.canvas1)
        
    def binds_simple_sinusoidal(self, window):     
        window.sinusoidal_menu.entryconfigure("Simple Sinusoidal",
            command =lambda:self.plot_simple_sine()) 
        
        window.sinusoidal_menu.entryconfigure("Two Sinusoidals",
            command =lambda:self.plot_two_sines())         

        window.sinusoidal_menu.entryconfigure("More Two Sinusoidals",
            command =lambda:self.plot_more_two_sines())  

    def plot_amplitude_modulation(self):
        win = tk.Toplevel()
        win.title("AMPLITUDE MODULATION")
        form_am = Form_AM(win)
        form_am.plot_fft_am()
        form_am.plot_graph()    

    def plot_frequency_modulation(self):
        win = tk.Toplevel()
        win.title("FREQUENCY MODULATION")
        form_fm = Form_FM(win)
        form_fm.plot_fft_fm()
        form_fm.plot_graph()

    def plot_phase_modulation(self):
        win = tk.Toplevel()
        win.title("PHASE MODULATION")
        form_pm = Form_PM(win)
        form_pm.plot_fft_pm()
        form_pm.plot_graph()
        
    def binds_modulation(self, window):     
        window.modulation.entryconfigure("Amplitude Modulation",
            command =lambda:self.plot_amplitude_modulation())         
        
        window.modulation.entryconfigure("Frequency Modulation",
            command =lambda:self.plot_frequency_modulation())      

        window.modulation.entryconfigure("Phase Modulation",
            command =lambda:self.plot_phase_modulation()) 

#form_am.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from form1 import Form1

class Form_AM:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        self.am_utils = AM_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="MODULATING AMPLITUDE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="MODULATING FREQUENCY")
        self.label6.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")

        self.label7 = tk.Label(master, text="IDX MOD:", font=("Helvetica", 10))
        self.label7.grid(row=15, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="cyan")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="cyan")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="cyan")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="cyan")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_am = tk.Entry(master, width=20, bg="cyan")
        self.entry_am.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_am.insert(0, "1")
        self.entry_am.bind('<Return>', self.plot_graph_event)
        
        self.entry_fm = tk.Entry(master, width=20, bg="cyan")
        self.entry_fm.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fm.insert(0, "5")
        self.entry_fm.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=12, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_am)
        self.btn2.grid(row=13, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_am_db)
        self.btn3.grid(row=14, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, fs, T, ac, fc, am, fm):        
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            amp_mod = float(am)
        except ValueError:
            amp_mod = 1
            self.entry_am.delete(0, tk.END)
            self.entry_am.insert(0, "1")
            
        try:
            freq_mod = float(fm)
        except ValueError:
            freq_mod = 5
            self.entry_fm.delete(0, tk.END)
            self.entry_fm.insert(0, "5")
            
        return fsampling, periode, amp_carrier, freq_carrier, amp_mod, freq_mod

    def plot_graph(self):
        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()
        
        #Change text in label7 for index modulation
        miu=float(amp_mod)/float(amp_carrier)
        self.label7.config(text=f"IDX MOD: {miu:.2f}", font=("Helvetica", 12, "bold"))

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}'
        title2 = f'Modulating Signal, frequency = {Fm}, Amplitude = {Am}'
        title3 = "Amplitude Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             am_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_am(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            am_signal, "FFT of AM Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_am_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("AMPLITUDE MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads AM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs amplitude modulation
        t, carrier_wave, modulating_wave, am_signal = self.am_utils.calculate_amplitude_modulation(Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            am_signal, "Decible Magnitude of AM Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_AM(window)
    window.mainloop()

#am_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class AM_Utils:
    def __init__(self):
        pass

    def calculate_amplitude_modulation(self, Fs, T, Ac, Fc, Am, Fm):
        # Define time line
        t = np.arange(0, T, 1/Fs)

        # Carrier signal
        carrier_wave = Ac*np.cos(2*np.pi*Fc*t)

        # Modulating signal
        modulating_wave = Am*np.cos(2*np.pi*Fm*t)

        # Amplitude modulation
        miu = Am/Ac
        am_signal = Ac*(1 + miu*np.cos(2*np.pi*Fm*t)) * np.cos(2*np.pi*Fc*t)
        
        return t, carrier_wave, modulating_wave, am_signal
        
    def time_domain(self, t, carrier_wave, title1, modulating_wave, title2,
                             am_signal, title3, figure=None, canvas=None):
        figure.clear()   

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(t, carrier_wave, linewidth=3.0, color='red')
        ax1.set_ylabel('Amplitude', color='blue')
        ax1.set_xlabel('Time (second)', color='blue')
        ax1.set_title(title1)
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(t, modulating_wave, linewidth=3.0, color='black')
        ax2.set_ylabel('Amplitude', color='blue')
        ax2.set_xlabel('Time (second)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.grid(True)        
        
        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(t, am_signal, linewidth=3.0, color='blue')
        ax3.set_ylabel('Amplitude', color='blue')
        ax3.set_xlabel('Time (second)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.grid(True)         

        figure.tight_layout()
        canvas.draw()  

    def fft_signal(self, Fs, signal):
        # Perform FFT
        fft_result = np.fft.fft(signal)
        freqs = np.fft.fftfreq(len(signal), 1/Fs)
        
        # Keep only positive frequencies
        pos_freqs = freqs[:len(freqs)//2]
        pos_fft_result = fft_result[:len(fft_result)//2]

        return pos_freqs, pos_fft_result

    def fft_db(self, Fs, signal):
        # Apply Hamming window
        windowed_signal = signal * np.hamming(len(signal))
    
        # Perform FFT and converts its magnitude to decibel
        fft_result = np.fft.fft(windowed_signal)
    
        # Handle cases where fft_result is zero
        magnitude_dB = np.zeros_like(fft_result)
        non_zero_indices = np.abs(fft_result) > 0.01
        magnitude_dB[non_zero_indices] = 20*np.log10(np.abs(fft_result[non_zero_indices]))
    
        freqs = np.fft.fftfreq(len(signal), 1/Fs)

        # Keep only positive frequencies
        pos_freqs = freqs[:len(freqs)//2]
        pos_magnitude_dB = magnitude_dB[:len(magnitude_dB)//2]

        return pos_freqs, pos_magnitude_dB
    
    def plot_fft_signal(self, Fs, signal1, title1, signal2, title2, signal3, title3, figure=None, canvas=None):
        figure.clear()   

        #Calculate FFT
        freqs1, fft_result1 = self.fft_signal(Fs, signal1)
        freqs2, fft_result2 = self.fft_signal(Fs, signal2)
        freqs3, fft_result3 = self.fft_signal(Fs, signal3) 

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(freqs1, np.abs(fft_result1), linewidth=2.0, color='red')
        ax1.set_ylabel('Magnitude', color='blue')
        ax1.set_xlabel('Frequency (Hz)', color='blue')
        ax1.set_title(title1)
        ax1.set_xticks(np.arange(min(freqs1), max(freqs1)+1, step=100))
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(freqs2, np.abs(fft_result2), linewidth=2.0, color='black')
        ax2.set_ylabel('Magnitude', color='blue')
        ax2.set_xlabel('Frequency (Hz)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.set_xticks(np.arange(min(freqs1), max(freqs2)+1, step=100))
        ax2.grid(True)        

        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(freqs3, np.abs(fft_result3), linewidth=2.0, color='blue')
        ax3.set_ylabel('Magnitude', color='blue')
        ax3.set_xlabel('Frequency (Hz)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.set_xticks(np.arange(min(freqs1), max(freqs3)+1, step=100))
        ax3.grid(True)
                
        figure.tight_layout()
        canvas.draw()  

    def plot_fft_db(self, Fs, signal1, title1, signal2, title2, signal3, title3, figure=None, canvas=None):
        figure.clear()   

        #Calculate FFT
        freqs1, fft_result1 = self.fft_db(Fs, signal1)
        freqs2, fft_result2 = self.fft_db(Fs, signal2)
        freqs3, fft_result3 = self.fft_db(Fs, signal3) 

        ax1 = figure.add_subplot(3,1,1)
        ax1.plot(freqs1, np.abs(fft_result1), linewidth=2.0, color='red')
        ax1.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax1.set_xlabel('Frequency (Hz)', color='blue')
        ax1.set_title(title1)
        ax1.set_xticks(np.arange(min(freqs1), max(freqs1)+1, step=100))
        ax1.set_facecolor('#F0F0F0')
        ax1.grid(True)
         
        ax2 = figure.add_subplot(3,1,2)
        ax2.plot(freqs2, np.abs(fft_result2), linewidth=2.0, color='black')
        ax2.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax2.set_xlabel('Frequency (Hz)', color='blue')
        ax2.set_title(title2)
        ax2.set_facecolor('#F0F0F0')
        ax2.set_xticks(np.arange(min(freqs1), max(freqs2)+1, step=100))
        ax2.grid(True)        

        ax3 = figure.add_subplot(3,1,3)
        ax3.plot(freqs3, np.abs(fft_result3), linewidth=2.0, color='blue')
        ax3.set_ylabel('Magnitude Spectrum (dB)', color='blue')
        ax3.set_xlabel('Frequency (Hz)', color='blue')
        ax3.set_title(title3)
        ax3.set_facecolor('#F0F0F0')
        ax3.set_xticks(np.arange(min(freqs1), max(freqs3)+1, step=100))
        ax3.grid(True)
                
        figure.tight_layout()
        canvas.draw()

#fm_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class FM_Utils:
    def __init__(self):
        pass

    def calculate_frequency_modulation(self, beta, Fs, T, Ac, Fc, Am, Fm):
        # Defines time line
        t = np.arange(0, T, 1/Fs)

        # Carrier signal
        carrier_wave = Ac*np.sin(2*np.pi*Fc*t)

        # Modulating signal
        modulating_wave = Am*np.sin(2*np.pi*Fm*t)

        # Frequency modulation
        #beta is modulation index
        fm_signal = Ac*np.sin(2*np.pi*Fc*t + beta*modulating_wave)  
        
        return t, carrier_wave, modulating_wave, fm_signal

#form_fm.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from fm_utils import FM_Utils
from form1 import Form1

class Form_FM:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        #Creates neccesary objects
        self.am_utils = AM_Utils()
        self.fm_utils = FM_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="MODULATING AMPLITUDE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="MODULATING FREQUENCY")
        self.label6.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")

        self.label7 = tk.Label(master, text="IDX MODULATION:")
        self.label7.grid(row=12, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="cyan")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="cyan")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="cyan")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="cyan")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_am = tk.Entry(master, width=20, bg="cyan")
        self.entry_am.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_am.insert(0, "1")
        self.entry_am.bind('<Return>', self.plot_graph_event)
        
        self.entry_fm = tk.Entry(master, width=20, bg="cyan")
        self.entry_fm.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fm.insert(0, "5")
        self.entry_fm.bind('<Return>', self.plot_graph_event)

        self.entry_idx_mod = tk.Entry(master, width=20, bg="cyan")
        self.entry_idx_mod.grid(row=13, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_idx_mod.insert(0, "5")
        self.entry_idx_mod.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=14, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_fm)
        self.btn2.grid(row=15, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_fm_db)
        self.btn3.grid(row=16, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, beta, fs, T, ac, fc, am, fm):  
        try:
            idx_mod = float(beta)
        except ValueError:
            idx_mod = 5
            self.entry_idx_mod.delete(0, tk.END)
            self.entry_idx_mod.insert(0, "5")
            
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            amp_mod = float(am)
        except ValueError:
            amp_mod = 1
            self.entry_am.delete(0, tk.END)
            self.entry_am.insert(0, "1")
            
        try:
            freq_mod = float(fm)
        except ValueError:
            freq_mod = 5
            self.entry_fm.delete(0, tk.END)
            self.entry_fm.insert(0, "5")
            
        return idx_mod, fsampling, periode, amp_carrier, freq_carrier, amp_mod, freq_mod

    def plot_graph(self):
        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()        

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs frequency modulation
        t, carrier_wave, modulating_wave, fm_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}'
        title2 = f'Modulating Signal, frequency = {Fm}, Amplitude = {Am}'
        title3 = "Frequency Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             fm_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_fm(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("FREQUENCY MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs frequency modulation
        t, carrier_wave, modulating_wave, fm_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)


        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            fm_signal, "FFT of FM Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_fm_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("FREQUENCY MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads FM params
        idx_mod = self.entry_idx_mod.get()
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get()

        beta, Fs, T, Ac, Fc, Am, Fm = self.read_and_check_input(idx_mod, fsampling, periode, amp_carrier,
                freq_carrier, amp_mod, freq_mod)
        
        # Performs frequency modulation
        t, carrier_wave, modulating_wave, fm_signal = self.fm_utils.calculate_frequency_modulation(beta, Fs, T, Ac, Fc, Am, Fm)

        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            fm_signal, "Decible Magnitude of FM Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_FM(window)
    window.mainloop()

#pm_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class PM_Utils:
    def __init__(self):
        pass

    def calculate_phase_modulation(self, Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m):
        # Defines time line
        t = np.arange(0, T, 1/Fs)

        # Carrier signal
        carrier_wave = Ac*np.sin(2*np.pi*Fc*t+ phi_c)

        # Modulating signal
        modulating_wave = Am*np.sin(2*np.pi*Fm*t + phi_m)

        # Phase modulation
        pm_signal = Ac*np.sin(Fc*2*np.pi*t + phi_c + Am*np.sin(Fm*2*np.pi*t+phi_m)) 
        
        return t, carrier_wave, modulating_wave, pm_signal

#form_pm.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from pm_utils import PM_Utils
from form1 import Form1

class Form_PM:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        #Creates neccesary objects
        self.am_utils = AM_Utils()
        self.pm_utils = PM_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="CARRIER PHASE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label5 = tk.Label(master, text="MODULATING AMPLITUDE")
        self.label5.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="MODULATING FREQUENCY")
        self.label6.grid(row=12, column=0, padx=5, pady=0.1, sticky="w")

        self.label7 = tk.Label(master, text="MODULATING PHASE:")
        self.label7.grid(row=14, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="cyan")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="cyan")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="cyan")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="cyan")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_pc = tk.Entry(master, width=20, bg="cyan")
        self.entry_pc.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_pc.insert(0, "0")
        self.entry_pc.bind('<Return>', self.plot_graph_event)
        
        self.entry_am = tk.Entry(master, width=20, bg="cyan")
        self.entry_am.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_am.insert(0, "1")
        self.entry_am.bind('<Return>', self.plot_graph_event)
        
        self.entry_fm = tk.Entry(master, width=20, bg="cyan")
        self.entry_fm.grid(row=13, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fm.insert(0, "5")
        self.entry_fm.bind('<Return>', self.plot_graph_event)

        self.entry_pm = tk.Entry(master, width=20, bg="cyan")
        self.entry_pm.grid(row=15, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_pm.insert(0, "0")
        self.entry_pm.bind('<Return>', self.plot_graph_event)
        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=16, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_pm)
        self.btn2.grid(row=17, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_pm_db)
        self.btn3.grid(row=18, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, fs, T, ac, fc, pc, am, fm, pm):             
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            phi_c = float(pc)
        except ValueError:
            phi_c = 0
            self.entry_pc.delete(0, tk.END)
            self.entry_pc.insert(0, "0")
            
        try:
            amp_mod = float(am)
        except ValueError:
            amp_mod = 1
            self.entry_am.delete(0, tk.END)
            self.entry_am.insert(0, "1")
            
        try:
            freq_mod = float(fm)
        except ValueError:
            freq_mod = 5
            self.entry_fm.delete(0, tk.END)
            self.entry_fm.insert(0, "5")
        try:
            phi_m = float(pm)
        except ValueError:
            phi_m = 0
            self.entry_pm.delete(0, tk.END)
            self.entry_pm.insert(0, "0")
            
        return fsampling, periode, amp_carrier, freq_carrier, phi_c, amp_mod, freq_mod, phi_m

    def plot_graph(self):
        #Reads PM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get() 
        phase_mod = self.entry_pm.get()

        Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, amp_mod, freq_mod, phase_mod)
        
        # Performs phase modulation
        t, carrier_wave, modulating_wave, fm_signal = \
            self.pm_utils.calculate_phase_modulation(Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}, Phase = {phi_c}'
        title2 = f'Modulating Signal, frequency = {Fm}, Amplitude = {Am}, Phase = {phi_m}'
        title3 = "Phase Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             fm_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_pm(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("PHASE MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads PM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get() 
        phase_mod = self.entry_pm.get()

        Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, amp_mod, freq_mod, phase_mod)
        
        # Performs phase modulation
        t, carrier_wave, modulating_wave, pm_signal = \
            self.pm_utils.calculate_phase_modulation(Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m)

        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            pm_signal, "FFT of Phase-Modulated Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_pm_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("PHASE MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads PM params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        amp_mod = self.entry_am.get()
        freq_mod = self.entry_fm.get() 
        phase_mod = self.entry_pm.get()

        Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, amp_mod, freq_mod, phase_mod)
        
        # Performs phase modulation
        t, carrier_wave, modulating_wave, pm_signal = \
            self.pm_utils.calculate_phase_modulation(Fs, T, Ac, Fc, phi_c, Am, Fm, phi_m)
            
        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            pm_signal, "Decible Magnitude of Phase-Modulated Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_PM(window)
    window.mainloop()


FULL SOURCE CODE 6.0: AMPLITUDE-SHIFT KEYING (ASK) MODULATION



DOWNLOAD FULL SOURCE CODE


FULL SOURCE CODE 7.0: FREQUENCY-SHIFT KEYING (FSK) MODULATION

#fsk_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class FSK_Utils:
    def __init__(self):
        pass

    def generate_random_binary_array(self, length):
        # Generate a random binary array of specified length
        return np.random.randint(2, size=length)

    def calculate_fsk_modulation(self, Fs, T, Ac, Fc, phi_c, samples_per_bit):
        # Defines time line
        t = np.arange(0, T, 1/Fs)
        
        bit_arr_size = int(2*Fs/samples_per_bit)
        bit_arr = self.generate_random_binary_array(bit_arr_size)
        
        modulating_wave = np.repeat(bit_arr, samples_per_bit)[:len(t)]

       # Ensures the lengths of modulating_wave and t match
        if len(modulating_wave) < len(t):
            modulating_wave = np.pad(modulating_wave, (0, len(t) - len(modulating_wave)))
        
        # Carrier signal
        carrier_wave = Ac*np.sin(2*np.pi*Fc*t+ phi_c)

        #Frequency-shift keying modulated signal   
        freq_sk = Fc*(1 + modulating_wave)
        fsk_signal = Ac*np.sin(2*np.pi*freq_sk*t+ phi_c)
        
        return t, carrier_wave, modulating_wave, fsk_signal

#form_fsk.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from fsk_utils import FSK_Utils
from form1 import Form1

class Form_FSK:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        #Creates neccesary objects
        self.am_utils = AM_Utils()
        self.fsk_utils = FSK_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="CARRIER PHASE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label5 = tk.Label(master, text="SAMPLES PER BIT")
        self.label5.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="lightblue")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="lightblue")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="lightblue")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="lightblue")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_pc = tk.Entry(master, width=20, bg="lightblue")
        self.entry_pc.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_pc.insert(0, "0")
        self.entry_pc.bind('<Return>', self.plot_graph_event)
        
        self.entry_spb = tk.Entry(master, width=20, bg="lightblue")
        self.entry_spb.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_spb.insert(0, "50")
        self.entry_spb.bind('<Return>', self.plot_graph_event)

        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=12, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_fsk)
        self.btn2.grid(row=13, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_fsk_db)
        self.btn3.grid(row=14, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, fs, T, ac, fc, pc, N):             
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            phi_c = float(pc)
        except ValueError:
            phi_c = 0
            self.entry_pc.delete(0, tk.END)
            self.entry_pc.insert(0, "0")
            
        try:
            spb = int(N)
        except ValueError:
            spb = 50
            self.entry_length.delete(0, tk.END)
            self.entry_length.insert(0, "50")
            
        return fsampling, periode, amp_carrier, freq_carrier, phi_c, spb

    def plot_graph(self):
        #Reads FSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs FSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.fsk_utils.calculate_fsk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}, Phase = {phi_c}'
        title2 = f'Modulating Signal, samples per bit = {samples_per_bit}'
        title3 = "FSK Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             ask_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_fsk(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("FSK MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads FSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs FSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.fsk_utils.calculate_fsk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
            
        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            ask_signal, "FFT of ASK-Modulated Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_fsk_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("FSK MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads FSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs FSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.fsk_utils.calculate_fsk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
            
        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            ask_signal, "Decible Magnitude of ASK-Modulated Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_FSK(window)
    window.mainloop()


DOWNLOAD FULL SOURCE CODE VERSION 7.0


FULL SOURCE CODE 8.0: PHASE-SHIFT KEYING (PSK) MODULATION


#psk_utils.py
import matplotlib.pyplot as plt
import numpy as np 

class PSK_Utils:
    def __init__(self):
        pass

    def generate_random_binary_array(self, length):
        # Generate a random binary array of specified length
        return np.random.randint(2, size=length)

    def calculate_psk_modulation(self, Fs, T, Ac, Fc, phi_c, samples_per_bit):
        # Defines time line
        t = np.arange(0, T, 1/Fs)
        
        bit_arr_size = int(2*Fs/samples_per_bit)
        bit_arr = self.generate_random_binary_array(bit_arr_size)
        
        modulating_wave = np.repeat(bit_arr, samples_per_bit)[:len(t)]

       # Ensures the lengths of modulating_wave and t match
        if len(modulating_wave) < len(t):
            modulating_wave = np.pad(modulating_wave, (0, len(t) - len(modulating_wave)))
        
        # Carrier signal
        carrier_wave = Ac*np.sin(2*np.pi*Fc*t+ phi_c)

        #Phase-shift keying modulated signal 
        #Shifts 180 degree
        psk_signal = Ac * np.sin(2*np.pi*Fc*t + phi_c + np.pi * modulating_wave)
        
        return t, carrier_wave, modulating_wave, psk_signal


#form_psk.py
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from am_utils import AM_Utils
from psk_utils import PSK_Utils
from form1 import Form1

class Form_PSK:
    def __init__(self, window):
        self.window = window
        width = 1520
        height = 760
        self.window.geometry(f"{width}x{height}")
    
        #Adds label widgets
        self.add_labels(self.window)
        
        #Adds entry widgets
        self.add_entries(self.window)
        
        #Adds canvasses
        self.add_canvas(self.window)
        
        #Adds button
        self.add_button(self.window)
        
        #Creates neccesary objects
        self.am_utils = AM_Utils()
        self.psk_utils = PSK_Utils()

    def add_labels(self, master):
        # Create two labels
        self.label1 = tk.Label(master, text="SAMPLING FREQUENCY")
        self.label1.grid(row=0, column=0, padx=5, pady=0.1, sticky="w")

        self.label2 = tk.Label(master, text="TIME PERIODE")
        self.label2.grid(row=2, column=0, padx=5, pady=0.1, sticky="w")

        self.label3 = tk.Label(master, text="CARRIER AMPLITUDE")
        self.label3.grid(row=4, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label4 = tk.Label(master, text="CARRIER FREQUENCY")
        self.label4.grid(row=6, column=0, padx=5, pady=0.1, sticky="w")

        self.label5 = tk.Label(master, text="CARRIER PHASE")
        self.label5.grid(row=8, column=0, padx=5, pady=0.1, sticky="w")
        
        self.label6 = tk.Label(master, text="SAMPLES PER BIT")
        self.label6.grid(row=10, column=0, padx=5, pady=0.1, sticky="w")
        
    def add_entries(self, master):
        # Create entry widgets
        self.entry_fs = tk.Entry(master, width=20, bg="lightblue")
        self.entry_fs.grid(row=1, column=0, padx=5, pady=0.1, sticky="n")  
        self.entry_fs.insert(0, "1000")
        self.entry_fs.bind('<Return>', self.plot_graph_event)

        self.entry_periode = tk.Entry(master, width=20, bg="lightblue")
        self.entry_periode.grid(row=3, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_periode.insert(0, "1")
        self.entry_periode.bind('<Return>', self.plot_graph_event)

        self.entry_ac = tk.Entry(master, width=20, bg="lightblue")
        self.entry_ac.grid(row=5, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_ac.insert(0, "1")
        self.entry_ac.bind('<Return>', self.plot_graph_event)
        
        self.entry_fc = tk.Entry(master, width=20, bg="lightblue")
        self.entry_fc.grid(row=7, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_fc.insert(0, "50")
        self.entry_fc.bind('<Return>', self.plot_graph_event)

        self.entry_pc = tk.Entry(master, width=20, bg="lightblue")
        self.entry_pc.grid(row=9, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_pc.insert(0, "0")
        self.entry_pc.bind('<Return>', self.plot_graph_event)
        
        self.entry_spb = tk.Entry(master, width=20, bg="lightblue")
        self.entry_spb.grid(row=11, column=0, padx=5, pady=0.1, sticky="n") 
        self.entry_spb.insert(0, "50")
        self.entry_spb.bind('<Return>', self.plot_graph_event)

        
    def add_canvas(self, master):    
        # Create a frame for canvas1 with a border
        frame1 = ttk.Frame(master, borderwidth=3, relief="groove")
        frame1.grid(row=0, column=1, columnspan=1, rowspan=25, padx=5, pady=5, sticky="n")

        # Adds canvas1 widget to frame1
        self.figure1 = Figure(figsize=(13.2, 7.4), dpi=100)
        self.figure1.patch.set_facecolor('#F0F0F0')
        self.canvas1 = FigureCanvasTkAgg(self.figure1, master=frame1)
        self.canvas1.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def add_button(self, master):
        #Adds button
        self.btn1 = tk.Button(master, height=2, width=17, text="PLOT GRAPH", 
            command=self.plot_graph)
        self.btn1.grid(row=12, column=0, padx=5, pady=1, sticky="n")

        self.btn2 = tk.Button(master, height=2, width=17, text="PLOT FFT", 
            command=self.plot_fft_psk)
        self.btn2.grid(row=13, column=0, padx=5, pady=1, sticky="n")

        self.btn3 = tk.Button(master, height=2, width=17, text="PLOT DB", 
            command=self.plot_fft_psk_db)
        self.btn3.grid(row=14, column=0, padx=5, pady=1, sticky="n")
        
    def read_and_check_input(self, fs, T, ac, fc, pc, N):             
        try:
            fsampling = float(fs)
        except ValueError:
            fsampling = 1000
            self.entry_fs.delete(0, tk.END)
            self.entry_fs.insert(0, "1000")

        try:
            periode = float(T)
        except ValueError:
            periode = 1
            self.entry_periode.delete(0, tk.END)
            self.entry_periode.insert(0, "1")

        try:
            amp_carrier = float(ac)
        except ValueError:
            amp_carrier = 1
            self.entry_ac.delete(0, tk.END)
            self.entry_ac.insert(0, "1")
            
        try:
            freq_carrier = float(fc)
        except ValueError:
            freq_carrier = 50
            self.entry_fc.delete(0, tk.END)
            self.entry_fc.insert(0, "50")

        try:
            phi_c = float(pc)
        except ValueError:
            phi_c = 0
            self.entry_pc.delete(0, tk.END)
            self.entry_pc.insert(0, "0")
            
        try:
            spb = int(N)
        except ValueError:
            spb = 50
            self.entry_length.delete(0, tk.END)
            self.entry_length.insert(0, "50")
            
        return fsampling, periode, amp_carrier, freq_carrier, phi_c, spb

    def plot_graph(self):
        #Reads PSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs PSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.psk_utils.calculate_psk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
        
        title1 = f'Carrier Signal, frequency = {Fc}, Amplitude = {Ac}, Phase = {phi_c}'
        title2 = f'Modulating Signal, samples per bit = {samples_per_bit}'
        title3 = "PSK Modulated Signal"
        self.am_utils.time_domain(t, carrier_wave, title1, modulating_wave, title2,
                             ask_signal, title3, self.figure1, self.canvas1)

    def plot_graph_event(self, event):
        self.plot_graph()
        
    def plot_fft_psk(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("PSK MODULATION (FREQUENCY-DOMAIN)")
        form1 = Form1(win2)      

        #Reads PSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs FSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.psk_utils.calculate_psk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
            
        self.am_utils.plot_fft_signal(Fs, carrier_wave, \
            "FFT of Carrier Signal", modulating_wave, "FFT of Modulating Signal", 
            ask_signal, "FFT of PSK-Modulated Signal", form1.figure1, form1.canvas1)         
            
    def plot_fft_psk_db(self):
        #Plots FFT of signals in form1
        win2 = tk.Toplevel()
        win2.title("PSK MODULATION (FREQUENCY-DOMAIN) with DECIBLE MAGNITUDE")
        form1 = Form1(win2)      

        #Reads PSK params
        fsampling = self.entry_fs.get()
        periode = self.entry_periode.get()
        amp_carrier = self.entry_ac.get()
        freq_carrier = self.entry_fc.get()
        phase_carrier = self.entry_pc.get()
        spb = self.entry_spb.get()

        Fs, T, Ac, Fc, phi_c, samples_per_bit = self.read_and_check_input(fsampling, 
                periode, amp_carrier, freq_carrier, phase_carrier, spb)
        
        # Performs PSK modulation
        t, carrier_wave, modulating_wave, ask_signal = \
            self.psk_utils.calculate_psk_modulation(Fs, T, Ac, Fc, phi_c, samples_per_bit)
            
        self.am_utils.plot_fft_db(Fs, carrier_wave, \
            "Decible Magnitude of Carrier Signal", modulating_wave, "Decible Magnitude of Modulating Signal", 
            ask_signal, "Decible Magnitude of PSK-Modulated Signal", form1.figure1, form1.canvas1)  

        
if __name__ == "__main__":
    window = tk.Tk()
    Form_PSK(window)
    window.mainloop()


DOWNLOAD FULL SOURCE CODE VERSION 8.0