BPSK (Binary Phase Shift Keying) 調變與 NRZ-L 編碼
在 ISO 14443-B 標準中,從標籤(PICC)到讀取器(PCD)的反向傳輸使用 847 kHz 副載波 (Subcarrier),並採用 BPSK (Binary Phase Shift Keying) 調變與 NRZ-L 編碼。
核心原理
NRZ-L 編碼:邏輯 '1' 與 '0' 分別對應不同的電平(通常在調變中對應不同的相位)。
BPSK 調變:
當數據為 '0' 時,副載波相位為 0 。
當數據為 '1' 時,副載波相位翻轉為 180 (相位切換)。
847 kHz 副載波:這是由 13.56 MHz / 16 得到的頻率。
BPSK 理輯實作:
我們使用相位偏移來實作調變:
np.cos(2 * np.pi * f * t + phase)。當數據是
0時,phase = 0。當數據是
1時,phase = π (180°)。在波形圖中,你會發現在 Bit 切換(例如從 0 變 1)的地方,正弦波的連續性會被打斷,產生一個明顯的「尖點」或翻轉,這就是相位調變的特徵。
頻率與速率:
副載波頻率固定為 847.5 kHz。
位元速率設定為 106 kbps(這是 ISO 14443-B 最基礎的速率)。這意味著每個位元週期內包含約 8 個副載波週期 (847.5 / 106 ≈ 8 )。
在實際的 ISO 14443-B 物理層中,847 kHz 的副載波並不是獨立存在的,它是透過「負載調變」(Load Modulation)掛載在 13.56 MHz 的載波 之上。
當標籤(PICC)切換其內部負載時,13.56 MHz 載波的振幅會隨著 847 kHz 的頻率產生微小的變化。這在頻譜上看起來像是距離中心頻率 847 kHz 的邊帶(Sidebands)。
第一層 (NRZ Data):原始數位資料(0 與 1)。
第二層 (BPSK Subcarrier):847 kHz 的副載波,根據資料切換相位。
第三層 (Load Modulation):將 BPSK 訊號調變至 13.56 MHz 載波上。
import tkinter as tk
from tkinter import messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class ISO14443B_Full_Simulator:
def __init__(self, root):
self.root = root
self.root.title("ISO 14443-B 完整負載調變模擬器")
# UI 佈局
control_frame = tk.Frame(root)
control_frame.pack(side=tk.TOP, fill=tk.X, padx=10, pady=10)
tk.Label(control_frame, text="Bit Stream (PICC to PCD):").pack(side=tk.LEFT)
self.bit_entry = tk.Entry(control_frame, width=20)
self.bit_entry.insert(0, "1011")
self.bit_entry.pack(side=tk.LEFT, padx=5)
self.btn_plot = tk.Button(control_frame, text="生成三級波形", command=self.plot_all, bg="#fff3cd")
self.btn_plot.pack(side=tk.LEFT, padx=5)
# 建立三個子圖
self.fig, (self.ax1, self.ax2, self.ax3) = plt.subplots(3, 1, figsize=(9, 8), sharex=True)
self.canvas = FigureCanvasTkAgg(self.fig, master=root)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
def plot_all(self):
bits_str = self.bit_entry.get().strip()
if not all(b in '01' for b in bits_str) or not bits_str:
messagebox.showerror("錯誤", "請輸入二進位位元流")
return
bits = [int(b) for b in bits_str]
# 為了觀察方便,我們縮小頻率比例但保持物理意義
# 實際: 13.56MHz / 847kHz = 16 倍關係
f_sub = 847500 # 847.5 kHz
f_carrier = f_sub * 8 # 模擬載波 (簡化為8倍以利肉眼觀察波形)
bit_rate = 106000 # 106 kbps
fs = 2000 # 取樣率
t = np.linspace(0, len(bits), len(bits) * fs)
# 1. NRZ 訊號
nrz_signal = np.repeat(bits, fs)
# 2. BPSK 副載波 (847 kHz)
phases = np.repeat([0 if b == 0 else np.pi for b in bits], fs)
subcarrier = np.cos(2 * np.pi * (f_sub/bit_rate) * t + phases)
# 3. 負載調變 (13.56 MHz 載波 + 副載波調變)
# 調變深度通常很淺 (例如 10%)
mod_depth = 0.15
carrier_1356 = np.sin(2 * np.pi * (f_carrier/bit_rate) * t)
# 負載調變公式:Carrier * (1 + depth * subcarrier)
load_mod_signal = carrier_1356 * (1 + mod_depth * subcarrier)
# --- 繪圖 ---
for ax in [self.ax1, self.ax2, self.ax3]: ax.clear()
# 波形 1: NRZ
self.ax1.step(t, nrz_signal, where='post', color='red')
self.ax1.set_title("1. PICC Data (NRZ-L)")
self.ax1.set_ylim(-0.2, 1.2)
# 波形 2: BPSK Subcarrier
self.ax2.plot(t, subcarrier, color='blue', linewidth=0.8)
self.ax2.set_title("2. BPSK Subcarrier (847.5 kHz)")
self.ax2.legend(["Phase 0°/180°"], loc='lower right', fontsize='x-small')
# 波形 3: 13.56MHz Carrier with Load Modulation
self.ax3.plot(t, load_mod_signal, color='green', linewidth=0.5)
self.ax3.set_title("3. Load Modulation (13.56 MHz Carrier)")
self.ax3.set_xlabel("Bit Index")
self.ax3.legend(["ASK-modulated Carrier"], loc='lower right', fontsize='x-small')
self.fig.tight_layout()
self.canvas.draw()
if __name__ == "__main__":
root = tk.Tk()
root.geometry("1000x850")
app = ISO14443B_Full_Simulator(root)
app.plot_all()
root.mainloop()

沒有留言:
張貼留言