2026年1月15日 星期四

RFID Super Tag 協議模擬器

RFID Super Tag 協議模擬器

Super Tag 協議(通常指一種改進型的時分多址或樹狀抗碰撞協議)的核心概念是「標籤主導」與「快速識別」。在這種模式下,標籤不僅僅是等待查詢,還會根據內部的計數器或隨機分配的 Slot 進行主動回應,而 Reader 則根據回應情況發送確認訊號。

在進階的 Super Tag 模擬中,我們通常會加入動態時槽調整標籤靜默機制


協議邏輯說明

  1. 標籤主導 (Tag-Driven):在此模擬中,每個標籤會根據內部的隨機邏輯決定是否在目前的時間窗發送訊號,這模擬了 Super Tag 協議中標籤自主競爭通道的特性。

  2. 狀態監控

    • 黃色 (Sending):標籤嘗試與 Reader 溝通。

    • 綠色 (Identified):Reader 成功解析標籤 ID 並發送 Ack 訊號,標籤進入靜默(Quiet)狀態。

    • 紅色 (Collision):多個標籤同時說話,訊號相互干擾。

import tkinter as tk

from tkinter import ttk

import random

import time

from threading import Thread, Event


class SuperTagSimulator:

    def __init__(self, root):

        self.root = root

        self.root.title("RFID Super Tag Protocol (標籤主導協議) 模擬器")

        

        # 模擬標籤群 (包含 UID 與初始計數器)

        self.tag_data = [

            {"uid": "ST-101", "state": "Idle"},

            {"uid": "ST-205", "state": "Idle"},

            {"uid": "ST-309", "state": "Idle"},

            {"uid": "ST-412", "state": "Idle"},

            {"uid": "ST-555", "state": "Idle"}

        ]

        

        # 控制旗標

        self.is_running = False

        self.stop_requested = False

        self.pause_event = Event()

        self.pause_event.set()

        

        self.setup_ui()


    def setup_ui(self):

        # 頂部狀態列

        self.header = tk.Frame(self.root, bg="#34495e", pady=10)

        self.header.pack(fill="x")

        self.lbl_info = tk.Label(self.header, text="Super Tag 系統就緒", font=("Arial", 12, "bold"), fg="white", bg="#34495e")

        self.lbl_info.pack()


        # 標籤視覺化區域

        self.canvas = tk.Canvas(self.root, width=700, height=150, bg="#ecf0f1")

        self.canvas.pack(pady=10)

        self.tag_visuals = {}

        for i, tag in enumerate(self.tag_data):

            x = 80 + (i * 130)

            rect = self.canvas.create_rectangle(x-40, 40, x+40, 90, fill="white", outline="#7f8c8d", width=2)

            txt = self.canvas.create_text(x, 65, text=tag["uid"], font=("Arial", 9, "bold"))

            status_txt = self.canvas.create_text(x, 105, text="IDLE", fill="#7f8c8d")

            self.tag_visuals[tag["uid"]] = {"rect": rect, "status": status_txt}


        # 按鈕控制區

        btn_frame = tk.Frame(self.root)

        btn_frame.pack(pady=10)


        self.btn_run = tk.Button(btn_frame, text="▶ 啟動程序", command=self.start_sim, bg="#27ae60", fg="white", width=12)

        self.btn_run.pack(side=tk.LEFT, padx=5)


        self.btn_pause = tk.Button(btn_frame, text="⏸ 暫停", command=self.toggle_pause, state=tk.DISABLED, width=12)

        self.btn_pause.pack(side=tk.LEFT, padx=5)


        self.btn_stop = tk.Button(btn_frame, text="⏹ 結束", command=self.terminate_sim, state=tk.DISABLED, bg="#c0392b", fg="white", width=12)

        self.btn_stop.pack(side=tk.LEFT, padx=5)


        # 傳輸紀錄表

        self.tree = ttk.Treeview(self.root, columns=("Time", "Tag", "Action", "Result"), show="headings", height=8)

        self.tree.heading("Time", text="時標")

        self.tree.heading("Tag", text="發送標籤")

        self.tree.heading("Action", text="通訊動作")

        self.tree.heading("Result", text="結果")

        self.tree.column("Time", width=80)

        self.tree.pack(fill="both", padx=10, pady=10)


    def toggle_pause(self):

        if not self.is_running: return

        if self.pause_event.is_set():

            self.pause_event.clear()

            self.btn_pause.config(text="▶ 恢復", bg="#f1c40f")

            self.lbl_info.config(text="系統已暫停")

        else:

            self.pause_event.set()

            self.btn_pause.config(text="⏸ 暫停", bg="SystemButtonFace")

            self.lbl_info.config(text="系統運行中...")


    def terminate_sim(self):

        self.stop_requested = True

        self.pause_event.set() # 確保解開暫停鎖以便退出


    def start_sim(self):

        self.is_running = True

        self.stop_requested = False

        self.pause_event.set()

        self.btn_run.config(state=tk.DISABLED)

        self.btn_pause.config(state=tk.NORMAL)

        self.btn_stop.config(state=tk.NORMAL)

        for item in self.tree.get_children(): self.tree.delete(item)

        

        Thread(target=self.protocol_logic, daemon=True).start()


    def protocol_logic(self):

        self.root.after(0, lambda: self.lbl_info.config(text="正在同步所有標籤..."))

        time.sleep(1)


        # 模擬 Super Tag 的多輪辨識

        unidentified_tags = [t["uid"] for t in self.tag_data]

        

        while unidentified_tags and not self.stop_requested:

            self.pause_event.wait()

            

            # 隨機選擇本輪嘗試發送的標籤(模擬機率碰撞)

            # 在 Super Tag 協議中,標籤會根據內部的 Slot Counter 決定是否發送

            attempting_tags = [uid for uid in unidentified_tags if random.random() > 0.4]

            

            timestamp = time.strftime("%H:%M:%S")

            

            if not attempting_tags:

                self.log_event(timestamp, "N/A", "Channel Sensing", "Idle (空閒)")

                time.sleep(0.8)

                continue


            # 視覺化:所有嘗試發送的標籤變色

            for uid in attempting_tags:

                self.update_tag_ui(uid, "#f1c40f", "SENDING...")


            time.sleep(1) # 模擬傳輸延遲

            

            if len(attempting_tags) == 1:

                # 成功辨識

                success_uid = attempting_tags[0]

                unidentified_tags.remove(success_uid)

                self.log_event(timestamp, success_uid, "Identification", "SUCCESS (辨識成功)")

                self.update_tag_ui(success_uid, "#2ecc71", "IDENTIFIED")

                # 成功後標籤進入靜默狀態 (Sleep)

            else:

                # 發生碰撞

                self.log_event(timestamp, "Multiple", "Collision", f"{len(attempting_tags)} Tags Clash!")

                for uid in attempting_tags:

                    self.update_tag_ui(uid, "#e74c3c", "COLLISION")

            

            time.sleep(1.2)

            # 重置未辨識標籤的顏色

            for uid in unidentified_tags:

                self.update_tag_ui(uid, "white", "IDLE")


        final_msg = "所有標籤辨識完成!" if not self.stop_requested else "模擬已結束"

        self.root.after(0, lambda: self.lbl_info.config(text=final_msg))

        self.reset_controls()


    def update_tag_ui(self, uid, color, status):

        def update():

            self.canvas.itemconfig(self.tag_visuals[uid]["rect"], fill=color)

            self.canvas.itemconfig(self.tag_visuals[uid]["status"], text=status, fill="#2c3e50" if color != "white" else "#7f8c8d")

        self.root.after(0, update)


    def log_event(self, ts, tag, action, res):

        self.root.after(0, lambda: self.tree.insert("", 0, values=(ts, tag, action, res)))


    def reset_controls(self):

        self.is_running = False

        self.btn_run.config(state=tk.NORMAL)

        self.btn_pause.config(state=tk.DISABLED, text="⏸ 暫停", bg="SystemButtonFace")

        self.btn_stop.config(state=tk.DISABLED)


if __name__ == "__main__":

    root = tk.Tk()

    app = SuperTagSimulator(root)

    root.mainloop()

 

Super Tag 協議中,最重要的效能指標是 「吞吐量與碰撞率的關係」。當標籤數量增加時,碰撞會呈指數級成長。

  • 碰撞與成功率的拉鋸

    • 在模擬開始時,標籤數量多(未辨識者多),碰撞率 (Collision) 會頻繁上升。

    • 隨著標籤一個個被「辨識成功」並轉為綠色,競爭者變少,成功率 (Success) 會趨於穩定。

  • 效能視覺化

    • 左上角的長條圖會動態伸縮。如果紅色部分(Collision)太高,代表協議的背離避讓機制(Backoff)需要調整。



  • import tkinter as tk

    from tkinter import ttk

    import random

    import time

    from threading import Thread, Event


    class SuperTagProSimulator:

        def __init__(self, root):

            self.root = root

            self.root.title("RFID Super Tag 效能分析模擬器")

            

            # 標籤資料

            self.tags = [{"uid": f"TAG-{i:02d}", "identified": False} for i in range(12)]

            

            # 統計數據

            self.stats = {"Success": 0, "Collision": 0, "Idle": 0}

            self.is_running = False

            self.is_paused = False

            self.stop_requested = False

            self.pause_event = Event()

            self.pause_event.set()

            

            self.setup_ui()


        def setup_ui(self):

            # 頂部:圖表統計區 (使用 Canvas 自行繪製簡易長條圖)

            graph_frame = tk.LabelFrame(self.root, text="即時效能統計 (Real-time Stats)", padx=10, pady=10)

            graph_frame.pack(fill="x", padx=10, pady=5)

            

            self.stat_canvas = tk.Canvas(graph_frame, width=400, height=120, bg="#f8f9fa")

            self.stat_canvas.pack(side=tk.LEFT)

            self.bars = {}

            self.create_chart()


            # 中間:標籤狀態網格

            tag_frame = tk.LabelFrame(self.root, text="標籤場景狀態")

            tag_frame.pack(fill="both", padx=10, pady=5)

            self.tag_labels = {}

            

            grid_container = tk.Frame(tag_frame)

            grid_container.pack(pady=5)

            for i, tag in enumerate(self.tags):

                lbl = tk.Label(grid_container, text=tag["uid"], width=8, relief="groove", bg="white")

                lbl.grid(row=i//6, column=i%6, padx=2, pady=2)

                self.tag_labels[tag["uid"]] = lbl


            # 按鈕控制區

            ctrl_frame = tk.Frame(self.root)

            ctrl_frame.pack(pady=10)


            self.btn_run = tk.Button(ctrl_frame, text="▶ 開始分析", command=self.start_sim, bg="#27ae60", fg="white", width=10)

            self.btn_run.pack(side=tk.LEFT, padx=5)


            self.btn_pause = tk.Button(ctrl_frame, text="⏸ 暫停", command=self.toggle_pause, state=tk.DISABLED, width=10)

            self.btn_pause.pack(side=tk.LEFT, padx=5)


            self.btn_stop = tk.Button(ctrl_frame, text="⏹ 結束", command=self.terminate_sim, state=tk.DISABLED, bg="#c0392b", fg="white", width=10)

            self.btn_stop.pack(side=tk.LEFT, padx=5)


            # 底部狀態列

            self.lbl_summary = tk.Label(self.root, text="準備就緒", font=("Arial", 10, "italic"))

            self.lbl_summary.pack(pady=5)


        def create_chart(self):

            # 建立長條圖基礎結構

            colors = {"Success": "#2ecc71", "Collision": "#e74c3c", "Idle": "#95a5a6"}

            for i, (key, color) in enumerate(colors.items()):

                x = 50 + (i * 100)

                bar = self.stat_canvas.create_rectangle(x, 100, x+40, 100, fill=color)

                txt = self.stat_canvas.create_text(x+20, 110, text=key)

                val = self.stat_canvas.create_text(x+20, 90, text="0")

                self.bars[key] = {"rect": bar, "val_txt": val}


        def update_chart(self):

            total = sum(self.stats.values())

            if total == 0: return

            

            for key, value in self.stats.items():

                height = (value / total) * 80  # 最大高度 80 像素

                x0, _, x1, _ = self.stat_canvas.coords(self.bars[key]["rect"])

                self.stat_canvas.coords(self.bars[key]["rect"], x0, 100-height, x1, 100)

                self.stat_canvas.itemconfig(self.bars[key]["val_txt"], text=str(value))


        def toggle_pause(self):

            if self.is_paused:

                self.is_paused = False

                self.pause_event.set()

                self.btn_pause.config(text="⏸ 暫停")

            else:

                self.is_paused = True

                self.pause_event.clear()

                self.btn_pause.config(text="▶ 恢復")


        def terminate_sim(self):

            self.stop_requested = True

            self.pause_event.set()


        def start_sim(self):

            self.is_running = True

            self.stop_requested = False

            self.is_paused = False

            self.pause_event.set()

            self.stats = {"Success": 0, "Collision": 0, "Idle": 0}

            for t in self.tags: t["identified"] = False

            

            self.btn_run.config(state=tk.DISABLED)

            self.btn_pause.config(state=tk.NORMAL)

            self.btn_stop.config(state=tk.NORMAL)

            

            Thread(target=self.logic_loop, daemon=True).start()


        def logic_loop(self):

            while not self.stop_requested and any(not t["identified"] for t in self.tags):

                self.pause_event.wait()

                

                # 模擬 Super Tag 隨機存取機率

                active_tags = [t for t in self.tags if not t["identified"] and random.random() < 0.2]

                

                # 更新所有標籤 UI 為初始色

                self.root.after(0, self.reset_labels)

                time.sleep(0.3)


                if len(active_tags) == 0:

                    self.stats["Idle"] += 1

                elif len(active_tags) == 1:

                    self.stats["Success"] += 1

                    tag = active_tags[0]

                    tag["identified"] = True

                    self.root.after(0, lambda uid=tag["uid"]: self.tag_labels[uid].config(bg="#2ecc71", fg="white"))

                else:

                    self.stats["Collision"] += 1

                    for tag in active_tags:

                        self.root.after(0, lambda uid=tag["uid"]: self.tag_labels[uid].config(bg="#e74c3c", fg="white"))


                self.root.after(0, self.update_chart)

                self.root.after(0, lambda: self.lbl_summary.config(text=f"已辨識: {sum(t['identified'] for t in self.tags)} / {len(self.tags)}"))

                time.sleep(0.7)


            self.is_running = False

            self.root.after(0, self.reset_buttons)


        def reset_labels(self):

            for tag in self.tags:

                if not tag["identified"]:

                    self.tag_labels[tag["uid"]].config(bg="white", fg="black")


        def reset_buttons(self):

            self.btn_run.config(state=tk.NORMAL)

            self.btn_pause.config(state=tk.DISABLED)

            self.btn_stop.config(state=tk.DISABLED)


    if __name__ == "__main__":

        root = tk.Tk()

        app = SuperTagProSimulator(root)

        root.mainloop()


    沒有留言:

    張貼留言

    RFID 系統的Stream Cipher(串流加密)

      RFID 系統的Stream Cipher(串流加密) 在 RFID 系統中,由於標籤(Tag)的運算資源有限,通常會採用 Stream Cipher(串流加密) 。其原理是利用一個「金鑰(Key)」與「初始向量(IV)」生成一段無限長的偽隨機位元流(Key Stream),...