縱向冗餘檢查法(LRC, Longitudinal Redundancy Check)
縱向冗餘檢查法(LRC, Longitudinal Redundancy Check)是一種簡單的錯誤檢查方式,通常應用於工業通訊(如 Modbus ASCII)。它的原理是將所有資料位元組進行 XOR(互斥或) 運算。
產生器 (Generator) 與 檢查器 (Checker) 功能的 LRC 進階演示程式。
在「檢查器」模式下,如果最後計算出的結果為 00,則代表資料傳輸正確;若非 00 則代表資料有誤。
LRC 原理解析與操作說明
二進制 XOR (互斥或) 運算:
LRC 的核心就是對每一個位元組進行二進制運算。
0 ⊕ 0 = 0
1 ⊕ 1 = 0
0 ⊕ 1 = 1
1 ⊕ 0 = 1
當我們將一連串的位元組互相 XOR 時,得到的結果就是 LRC 值。
為什麼要用 LRC?
它比簡單的奇偶校驗強大,可以偵測出單一位元組內多個位元的錯誤。
在 Modbus ASCII 傳輸中,LRC 被放在封包的結尾。
如何使用檢查器?
當接收端收到資料時,將所有資料位元組(含 LRC)再次全部 XOR 運算。
如果結果為 00,則表示傳輸過程中沒有發生單一位元錯誤。
程式功能:
16進制轉換:輸入
52 A3等字串,程式自動將其解析為數值。二進制可視化:在執行下一步時,畫面會同時顯示 Hex 值與 8-bit 二進制值,方便觀察 XOR 的變化。
逐步演示:透過「下一步」按鈕,您可以觀察 LRC 如何從第一個位元組累積到最後一個。
1. 產生器模式 (Generator)
用途:當你要發送資料時,計算該附加上去的校驗碼。
步驟:輸入資料(如
52 A3 BD) 點擊下一步直到完畢 程式會告訴你最終的 LRC(例如48)。結果:你實際發送的內容應為
52 A3 BD 48。
2. 檢查器模式 (Checker)
用途:當你收到一串資料,想確認它對不對。
步驟:輸入收到的完整序列(包含最後一個 LRC 位元組,例如
52 A3 BD 48)。判定:如果資料完全正確,最後一步的結果一定是 00。
LRC 的數學美感
LRC 利用了 XOR 的對稱性質。如果你對一組數字 A, B, C 做 XOR 得到 L = A ⊕ B⊕ C ,那麼將 L 本身也加入運算時:
這就是為什麼檢查器模式下,正確資料的結果必然為零的原因。
import tkinter as tk
from tkinter import messagebox, ttk
class LRCAdvancedApp:
def __init__(self, root):
self.root = root
self.root.title("LRC 縱向冗餘檢查器 (產生/檢查)")
self.root.geometry("700x750")
# 狀態變數
self.data_bytes = []
self.current_step = -1
self.current_lrc = 0
self.create_widgets()
def create_widgets(self):
# 模式選擇區
mode_frame = tk.Frame(self.root, pady=10)
mode_frame.pack()
tk.Label(mode_frame, text="選擇模式:", font=("Arial", 10, "bold")).pack(side=tk.LEFT)
self.mode_var = tk.StringVar(value="generator")
tk.Radiobutton(mode_frame, text="產生器 (計算LRC)", variable=self.mode_var, value="generator").pack(side=tk.LEFT, padx=10)
tk.Radiobutton(mode_frame, text="檢查器 (驗證資料)", variable=self.mode_var, value="checker").pack(side=tk.LEFT, padx=10)
# 輸入區
input_frame = tk.Frame(self.root, pady=10)
input_frame.pack()
tk.Label(input_frame, text="輸入 16 進制序列:", font=("Arial", 10)).pack(anchor=tk.W)
self.ent_hex = tk.Entry(input_frame, font=("Courier New", 12), width=40)
self.ent_hex.insert(0, "52 A3 BD 89 C6")
self.ent_hex.pack(pady=5)
tk.Label(input_frame, text="(檢查器模式請包含最後一個 LRC 位元組)", fg="gray", font=("Arial", 9)).pack()
# 控制按鈕
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="初始化重置", command=self.reset_lrc, bg="#f0f0f0", width=15).pack(side=tk.LEFT, padx=5)
self.btn_next = tk.Button(btn_frame, text="執行下一步 >>", command=self.next_step,
state=tk.DISABLED, bg="#4CAF50", fg="white", font=("Arial", 10, "bold"), width=15)
self.btn_next.pack(side=tk.LEFT, padx=5)
# 顯示區
self.txt_display = tk.Text(self.root, font=("Courier New", 11), height=22, padx=15, pady=15, bg="#fafafa")
self.txt_display.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)
# 最終狀態指示燈
self.lbl_status = tk.Label(self.root, text="準備就緒", font=("Arial", 14, "bold"), fg="gray")
self.lbl_status.pack(pady=15)
def reset_lrc(self):
hex_str = self.ent_hex.get().strip()
try:
self.data_bytes = [int(x, 16) for x in hex_str.split()]
if not self.data_bytes: raise ValueError
except:
messagebox.showerror("錯誤", "16 進制格式錯誤。範例: 52 A3 BD")
return
self.current_step = 0
self.current_lrc = 0
self.btn_next.config(state=tk.NORMAL)
mode_text = "產生器模式 (計算最後的 LRC)" if self.mode_var.get() == "generator" else "檢查器模式 (結果應為 00)"
self.lbl_status.config(text=f"正在執行: {mode_text}", fg="#333")
self.txt_display.delete(1.0, tk.END)
self.txt_display.insert(tk.END, f"模式: {mode_text}\n")
self.txt_display.insert(tk.END, f"載入資料: {' '.join([f'{b:02X}' for b in self.data_bytes])}\n")
self.txt_display.insert(tk.END, "-"*50 + "\n")
def next_step(self):
if self.current_step < len(self.data_bytes):
byte_val = self.data_bytes[self.current_step]
old_lrc = self.current_lrc
self.current_lrc ^= byte_val
# 詳細顯示
self.txt_display.insert(tk.END, f"【步驟 {self.current_step + 1}】\n")
self.txt_display.insert(tk.END, f" 上一次 LRC: {old_lrc:02X} ({old_lrc:08b})\n")
self.txt_display.insert(tk.END, f" XOR 資料: {byte_val:02X} ({byte_val:08b})\n")
self.txt_display.insert(tk.END, f" =目前結果: {self.current_lrc:02X} ({self.current_lrc:08b})\n")
self.txt_display.insert(tk.END, "-"*35 + "\n")
self.txt_display.see(tk.END)
self.current_step += 1
# 檢查是否結束
if self.current_step == len(self.data_bytes):
self.btn_next.config(state=tk.DISABLED)
self.finalize_result()
def finalize_result(self):
mode = self.mode_var.get()
final_val = self.current_lrc
if mode == "generator":
self.lbl_status.config(text=f"產生完成!LRC = {final_val:02X}", fg="blue")
self.txt_display.insert(tk.END, f"\n計算結束。建議傳輸序列: \n{' '.join([f'{b:02X}' for b in self.data_bytes])} {final_val:02X}")
else:
if final_val == 0:
self.lbl_status.config(text="檢查通過 (PASS): 結果為 00", fg="green")
self.txt_display.insert(tk.END, "\n[驗證成功] 所有位元組 XOR 結果為 0,資料正確。")
else:
self.lbl_status.config(text=f"檢查失敗 (FAIL): 結果 {final_val:02X}", fg="red")
self.txt_display.insert(tk.END, f"\n[驗證失敗] 結果不為 0,資料在傳輸中可能已受損。")
if __name__ == "__main__":
root = tk.Tk()
app = LRCAdvancedApp(root)
root.mainloop()


沒有留言:
張貼留言