2026年1月21日 星期三

BPSK (Binary Phase Shift Keying) 調變與 NRZ-L 編碼。

BPSK (Binary Phase Shift Keying) 調變與 NRZ-L 編碼 

ISO 14443-B 標準中,從標籤(PICC)到讀取器(PCD)的反向傳輸使用 847 kHz 副載波 (Subcarrier),並採用 BPSK (Binary Phase Shift Keying) 調變與 NRZ-L 編碼。

核心原理

  1. NRZ-L 編碼:邏輯 '1' 與 '0' 分別對應不同的電平(通常在調變中對應不同的相位)。

  2. BPSK 調變

    • 當數據為 '0' 時,副載波相位為 0

    • 當數據為 '1' 時,副載波相位翻轉為 180 (相位切換)。

  3. 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()


    沒有留言:

    張貼留言

    經由MQTT協定的2個WOKWI ESP32 雙向通訊 (ESP32 to ESP32 MQTT Communication )

     經由MQTT協定的2個WOKWI ESP32 雙向通訊  (ESP32  to ESP32 MQTT Communication ) 使用兩個 ESP32 建立一個遠端控制系統。 MQTT Broker: mqtt-dashboard.com Topic (主題): ale...