BPSK 調變
頂部為參數輸入區,您可以設定載波頻率 (Fc)、位元速率 (Rb) 和總位元數。Fc 必須遠大於 Rb 才能清晰地看出調變效果(建議 Fc >= 10 * Rb)。
程式將在 GUI 中顯示三個子圖:
基帶訊號: 顯示原始的 NRZ 數位電平。
載波訊號: 顯示純粹的餘弦波載波。
BPSK 調變訊號: 顯示相位隨著輸入資料的 0 和 1 而發生 180° 變化的最終訊號。
基帶訊號: 原始的二進位資料(1 或 0)被轉換為 NRZ 形式(非歸零碼),即將 1 映射為 A(例如 +1),將 0 映射為 -A(例如 -1)。
BPSK 調變: 簡單地將 NRZ 基帶訊號與載波訊號相乘:
S(t) = d(t) cos(2*pi*F_c t)
當 d(t)=+1 時,輸出 cos(2*pi *F_c *t) 。
當 $d(t)=-1 時,輸出 -cos(2*pi*F_c*t) = cos(2 *pi* F_c*t + pi) (相位偏移 $180 °)。
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
import matplotlib.font_manager as fm
# --- Matplotlib 中文字體設定 ---
# 設置中文字體以解決圖表標題和標籤亂碼問題。
# 這裡嘗試使用多個常見中文黑體,如果您的系統有這些字體,中文即可正常顯示。
try:
# 優先使用微軟正黑體,若無則使用SimHei,最後嘗試Arial Unicode MS
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei', 'SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False # 解決負號顯示為方塊的問題
except:
# 失敗時不作處理,依賴系統預設字體
pass
# -----------------------------------
class BPSKSimulator(tk.Tk):
def __init__(self):
super().__init__()
self.title("BPSK (二進位相位偏移調變) 模擬器")
self.geometry("800x650")
# 預設參數
self.default_bit_rate = 10 # 位元速率 (Rb)
self.default_carrier_freq = 100 # 載波頻率 (Fc)
self.total_bits = 5
self.create_widgets()
def create_widgets(self):
# --- 參數輸入框架 ---
param_frame = ttk.LabelFrame(self, text="調變參數設置", padding="10")
param_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=5)
# 載波頻率
ttk.Label(param_frame, text="載波頻率 (Hz, Fc):").grid(row=0, column=0, padx=5, pady=5, sticky='w')
self.fc_entry = ttk.Entry(param_frame, width=10)
self.fc_entry.insert(0, str(self.default_carrier_freq))
self.fc_entry.grid(row=0, column=1, padx=5, pady=5, sticky='w')
# 位元速率
ttk.Label(param_frame, text="位元速率 (bits/s, Rb):").grid(row=0, column=2, padx=5, pady=5, sticky='w')
self.rb_entry = ttk.Entry(param_frame, width=10)
self.rb_entry.insert(0, str(self.default_bit_rate))
self.rb_entry.grid(row=0, column=3, padx=5, pady=5, sticky='w')
# 總位元數
ttk.Label(param_frame, text="總位元數:").grid(row=1, column=0, padx=5, pady=5, sticky='w')
self.bits_entry = ttk.Entry(param_frame, width=10)
self.bits_entry.insert(0, str(self.total_bits))
self.bits_entry.grid(row=1, column=1, padx=5, pady=5, sticky='w')
# 隨機資料按鈕
ttk.Button(param_frame, text="生成隨機資料並調變", command=self.run_simulation).grid(row=2, column=0, columnspan=4, pady=10)
# 顯示當前資料序列
self.data_label = ttk.Label(param_frame, text="當前資料序列: N/A", font=('Arial', 10, 'italic'))
self.data_label.grid(row=3, column=0, columnspan=4, pady=5)
# --- 視覺化框架 (Matplotlib) ---
plot_frame = ttk.Frame(self)
plot_frame.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True, padx=10, pady=5)
# 創建 3 個子圖的 Matplotlib 圖表
self.fig, self.axes = plt.subplots(3, 1, figsize=(7, 5), sharex=True)
plt.subplots_adjust(hspace=0.5)
self.canvas = FigureCanvasTkAgg(self.fig, master=plot_frame)
self.canvas_widget = self.canvas.get_tk_widget()
self.canvas_widget.pack(fill=tk.BOTH, expand=True)
def run_simulation(self):
"""從 GUI 獲取參數,執行 BPSK 調變並繪製結果。"""
try:
Rb = float(self.rb_entry.get())
Fc = float(self.fc_entry.get())
num_bits = int(self.bits_entry.get())
if Rb <= 0 or Fc <= 0 or num_bits <= 0:
raise ValueError("所有參數必須為正數。")
except ValueError as e:
messagebox.showerror("輸入錯誤", f"請輸入有效的數值:{e}")
return
# 1. 產生隨機二進位資料
data_bits = np.random.randint(0, 2, num_bits)
data_str = "".join(map(str, data_bits))
self.data_label.config(text=f"當前資料序列: {data_str}")
# 2. BPSK 調變
t, data_signal, carrier_signal, bpsk_signal = self.bpsk_modulation(data_bits, Rb, Fc)
# 3. 繪製結果
self.plot_signals(t, data_signal, carrier_signal, bpsk_signal, data_bits)
def bpsk_modulation(self, data_bits, Rb, Fc):
"""執行 BPSK 調變的邏輯。"""
# 採樣率 (必須遠大於 Fc 和 Rb)
Fs = 20 * Fc # 採樣頻率是載波頻率的 20 倍
if Fs < 100 * Rb:
Fs = 100 * Rb # 確保有足夠的採樣點
T_bit = 1 / Rb # 位元週期
T_total = len(data_bits) * T_bit # 總時間
# 時間向量
t = np.arange(0, T_total, 1/Fs)
# 1. 產生 NRZ 數位基帶訊號 (將 0 映射為 -1,1 映射為 +1)
data_nrz = 2 * data_bits - 1
data_signal = np.zeros_like(t)
for i, bit in enumerate(data_nrz):
start_sample = int(i * T_bit * Fs)
end_sample = int((i + 1) * T_bit * Fs)
data_signal[start_sample:end_sample] = bit
# 2. 產生載波訊號 (Carrier Signal)
carrier_signal = np.cos(2 * np.pi * Fc * t)
# 3. 產生 BPSK 訊號
# BPSK = 基帶訊號 * 載波訊號
bpsk_signal = data_signal * carrier_signal
return t, data_signal, carrier_signal, bpsk_signal
def plot_signals(self, t, data_signal, carrier_signal, bpsk_signal, data_bits):
"""繪製三個訊號波形。"""
# 清除舊圖
for ax in self.axes:
ax.clear()
# --- 繪製基帶訊號 ---
self.axes[0].plot(t, data_signal, 'r-')
self.axes[0].set_title(f"1. 基帶訊號 (NRZ, {len(data_bits)} bits)", fontsize=10)
self.axes[0].set_ylabel("振幅", fontsize=8)
self.axes[0].grid(True, linestyle='--')
self.axes[0].set_yticks([-1, 0, 1])
# --- 繪製載波訊號 ---
self.axes[1].plot(t, carrier_signal, 'b-')
self.axes[1].set_title(f"2. 載波訊號 (Fc={self.fc_entry.get()} Hz)", fontsize=10)
self.axes[1].set_ylabel("振幅", fontsize=8)
self.axes[1].grid(True, linestyle='--')
# --- 繪製 BPSK 訊號 ---
self.axes[2].plot(t, bpsk_signal, 'g-')
self.axes[2].set_title("3. BPSK 調變訊號", fontsize=10)
self.axes[2].set_xlabel("時間 (秒)", fontsize=8)
self.axes[2].set_ylabel("振幅", fontsize=8)
self.axes[2].grid(True, linestyle='--')
# 重新繪製 canvas
self.fig.tight_layout()
self.canvas.draw()
if __name__ == "__main__":
app = BPSKSimulator()
app.mainloop()

沒有留言:
張貼留言