ASK (振幅鍵移) FSK (頻率鍵移)PSK (相位鍵移)
一個結合 tkinter 與 matplotlib 的數位調變(Digital Modulation)模擬程式。
為了實現這個功能,我們需要用到三個主要的庫:
tkinter: 建立視窗介面。
numpy: 進行訊號的數學運算。
matplotlib: 將波形繪製成圖表並嵌入到視窗中。
1. 安裝必要套件
若尚未安裝,請執行:
pip install numpy matplotlib
2. 數位調變模擬程式碼
這個程式允許你輸入二進制序列(例如 10110),並自定義載波的震幅 (Amplitude) 與 頻率 (Frequency)。
3. 功能解析
ASK (振幅鍵移):
當位元為 1 時,輸出載波波形;當位元為 0 時,輸出為 0。公式:s(t) = A ▪️ bit▪️sin(2pi f1 t)。
FSK (頻率鍵移):
當位元為 1 時使用頻率 f1,位元為 0 時切換到另一個頻率f2。這在無線電通訊中非常常見。
PSK (相位鍵移):
頻率與震幅保持不變。當位元從 1 變為 0 時,波形的相位會旋轉 180°(反相),這在現代數位通訊(如 Wi-Fi、4G)中是更進階調變(如 QAM)的基礎。
操作提示:
您可以修改「數位序列」來觀察不同長度的訊號。
將 FSK 第二頻率 設得比 f1 大很多,可以更明顯地看出頻率密度的變化。
載波頻率 建議設在
5到20之間,視覺效果最好。
import tkinter as tk
from tkinter import ttk, messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class ModulationApp:
def __init__(self, root):
self.root = root
self.root.title("數位調變模擬器 (ASK, FSK, PSK)")
self.root.geometry("1000x700")
self.create_widgets()
self.plot_initial()
def create_widgets(self):
# --- 左側參數控制區 ---
control_frame = tk.LabelFrame(self.root, text="參數設定", padx=10, pady=10)
control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
# 二進制序列
tk.Label(control_frame, text="數位序列 (例如 1011):").pack(anchor=tk.W)
self.entry_bits = tk.Entry(control_frame)
self.entry_bits.insert(0, "10110")
self.entry_bits.pack(fill=tk.X, pady=5)
# 震幅
tk.Label(control_frame, text="載波震幅 (A):").pack(anchor=tk.W)
self.entry_amp = tk.Entry(control_frame)
self.entry_amp.insert(0, "1")
self.entry_amp.pack(fill=tk.X, pady=5)
# 頻率
tk.Label(control_frame, text="載波頻率 (f1, Hz):").pack(anchor=tk.W)
self.entry_freq1 = tk.Entry(control_frame)
self.entry_freq1.insert(0, "5")
self.entry_freq1.pack(fill=tk.X, pady=5)
# FSK 專用頻率
tk.Label(control_frame, text="FSK 第二頻率 (f2, Hz):").pack(anchor=tk.W)
self.entry_freq2 = tk.Entry(control_frame)
self.entry_freq2.insert(0, "12")
self.entry_freq2.pack(fill=tk.X, pady=5)
# 更新按鈕
tk.Button(control_frame, text="生成波形", command=self.update_plot,
bg="#4CAF50", fg="white", font=('Arial', 10, 'bold')).pack(fill=tk.X, pady=20)
# --- 右側繪圖區 ---
self.plot_frame = tk.Frame(self.root, bg="white")
self.plot_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)
def plot_initial(self):
self.fig, (self.ax_bit, self.ax_ask, self.ax_fsk, self.ax_psk) = plt.subplots(4, 1, figsize=(8, 10), constrained_layout=True)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
self.update_plot()
def update_plot(self):
try:
# 獲取輸入
bits_str = self.entry_bits.get().strip()
bits = [int(b) for b in bits_str if b in '01']
A = float(self.entry_amp.get())
f1 = float(self.entry_freq1.get())
f2 = float(self.entry_freq2.get())
if not bits:
raise ValueError("請輸入有效的 0 與 1 序列")
# 時間軸設定
t_bit = np.linspace(0, 1, 100) # 每一個位元的細分時間
t_total = np.array([])
bit_signal = np.array([])
ask_signal = np.array([])
fsk_signal = np.array([])
psk_signal = np.array([])
for i, bit in enumerate(bits):
t = t_bit + i
t_total = np.concatenate([t_total, t])
# 原始數位訊號
bit_signal = np.concatenate([bit_signal, np.full_like(t, bit)])
# ASK: 1 有震幅, 0 無震幅
ask_signal = np.concatenate([ask_signal, A * bit * np.sin(2 * np.pi * f1 * t)])
# FSK: 1 使用 f1, 0 使用 f2
freq = f1 if bit == 1 else f2
fsk_signal = np.concatenate([fsk_signal, A * np.sin(2 * np.pi * freq * t)])
# PSK: 1 使用 0 相位, 0 使用 180 度反相 (相位差 pi)
phase = 0 if bit == 1 else np.pi
psk_signal = np.concatenate([psk_signal, A * np.sin(2 * np.pi * f1 * t + phase)])
# 清除舊圖並繪製新圖
for ax in [self.ax_bit, self.ax_ask, self.ax_fsk, self.ax_psk]: ax.clear()
self.ax_bit.step(t_total, bit_signal, where='post', color='black', lw=2)
self.ax_bit.set_title(f"Original Bits: {bits_str}")
self.ax_bit.set_ylim(-0.2, 1.2)
self.ax_ask.plot(t_total, ask_signal, color='blue')
self.ax_ask.set_title("ASK (Amplitude Shift Keying)")
self.ax_fsk.plot(t_total, fsk_signal, color='red')
self.ax_fsk.set_title("FSK (Frequency Shift Keying)")
self.ax_psk.plot(t_total, psk_signal, color='green')
self.ax_psk.set_title("PSK (Phase Shift Keying)")
for ax in [self.ax_bit, self.ax_ask, self.ax_fsk, self.ax_psk]:
ax.grid(True, alpha=0.3)
self.canvas.draw()
except Exception as e:
messagebox.showerror("輸入錯誤", f"請確認數值格式是否正確!\n{e}")
if __name__ == "__main__":
root = tk.Tk()
app = ModulationApp(root)
root.mainloop()

沒有留言:
張貼留言