八木天線(Yagi-Uda Antenna)的計算工具
設計一個八木天線(Yagi-Uda Antenna)的計算工具,首先需要理解其基本結構:
反射器 (Reflector):通常只有 1 節,長度最長。
驅動元件 (Driven Element / Folded Dipole):饋電點,長度約為 $0.5 \lambda$。
引向器 (Directors):可有多節,長度較短,用於增強方向性。
它會根據輸入的頻率與節數,自動計算並顯示每一節的長度以及節與節之間的間距(基於標準 DL6WU 演算法模型)。
八木天線原理圖解
程式設計重點說明:
動態節數計算:
長度公式與縮減係數 (Velocity Factor):
反射器 (Reflector):約 0.495λ。它是最長的,負責將電波反射回去。
驅動元件 (Driven Element):約 0.473 λ。這是天線接收或發射信號的核心。
引向器 (Directors):約 0.44λ 左右。在多節設計中,引向器通常會越往末端越短一點點,以增加寬頻響應。
間距 (Spacing):
如何使用:
執行程式後,輸入您想要的頻率(例如 433 代表 433 MHz 的無線電頻段)。
輸入節數,例如 10。
點擊「開始計算」,表格會立即列出從第一節到第十節的精確長度(公釐)與累計距離。
import tkinter as tk
from tkinter import ttk, messagebox
class YagiCalculatorApp:
def __init__(self, root):
self.root = root
self.root.title("八木天線 (Yagi-Uda) 設計工具")
self.root.geometry("700x650")
# 標題
tk.Label(self.root, text="Yagi-Uda Antenna Designer", font=("Arial", 16, "bold")).pack(pady=10)
# 輸入區域
input_frame = tk.LabelFrame(self.root, text=" 輸入設計參數 ", padx=10, pady=10)
input_frame.pack(fill="x", padx=20, pady=5)
tk.Label(input_frame, text="中心頻率 (MHz):").grid(row=0, column=0, sticky="w")
self.entry_freq = tk.Entry(input_frame)
self.entry_freq.insert(0, "433.0")
self.entry_freq.grid(row=0, column=1, padx=5, pady=5)
tk.Label(input_frame, text="總節數 (至少 3 節):").grid(row=1, column=0, sticky="w")
self.entry_elements = tk.Entry(input_frame)
self.entry_elements.insert(0, "5")
self.entry_elements.grid(row=1, column=1, padx=5, pady=5)
# 按鈕
self.btn_calc = tk.Button(input_frame, text="開始計算", command=self.calculate_yagi, bg="#4CAF50", fg="white", width=15)
self.btn_calc.grid(row=2, column=0, columnspan=2, pady=10)
# 結果顯示區域
result_frame = tk.Frame(self.root)
result_frame.pack(fill="both", expand=True, padx=20, pady=10)
# 左側列表:顯示長度與間距
columns = ("element", "length", "spacing", "total_dist")
self.tree = ttk.Treeview(result_frame, columns=columns, show="headings")
self.tree.heading("element", text="元件名稱")
self.tree.heading("length", text="長度 (mm)")
self.tree.heading("spacing", text="與前一節距離 (mm)")
self.tree.heading("total_dist", text="累計距離 (mm)")
# 設定欄寬
self.tree.column("element", width=120, anchor="center")
self.tree.column("length", width=120, anchor="center")
self.tree.column("spacing", width=120, anchor="center")
self.tree.column("total_dist", width=120, anchor="center")
self.tree.pack(side="left", fill="both", expand=True)
# 捲軸
scrollbar = ttk.Scrollbar(result_frame, orient="vertical", command=self.tree.yview)
self.tree.configure(yscroll=scrollbar.set)
scrollbar.pack(side="right", fill="y")
def calculate_yagi(self):
try:
freq = float(self.entry_freq.get())
num_elements = int(self.entry_elements.get())
if num_elements < 3:
messagebox.showwarning("輸入錯誤", "八木天線至少需要 3 節(反射器、驅動元件、引向器)。")
return
# 清除舊數據
for i in self.tree.get_children():
self.tree.delete(i)
# 波長 λ (mm)
wavelength = 299792 / freq # c / f
# 1. 反射器 (Reflector)
refl_len = wavelength * 0.495
self.tree.insert("", "end", values=("反射器 (Refl)", f"{refl_len:.2f}", "0.00", "0.00"))
# 2. 驅動元件 (Driven Element)
driven_len = wavelength * 0.473
driven_space = wavelength * 0.125
self.tree.insert("", "end", values=("驅動元件 (DE)", f"{driven_len:.2f}", f"{driven_space:.2f}", f"{driven_space:.2f}"))
# 3. 引向器 (Directors)
total_dist = driven_space
num_directors = num_elements - 2
for i in range(1, num_directors + 1):
# 簡單 DL6WU 模型估算:引向器長度約為 0.44λ,間距約為 0.2λ
dir_len = wavelength * (0.450 - (i * 0.005)) # 越往後越短
dir_space = wavelength * 0.2
total_dist += dir_space
self.tree.insert("", "end", values=(f"引向器 D{i}", f"{dir_len:.2f}", f"{dir_space:.2f}", f"{total_dist:.2f}"))
except ValueError:
messagebox.showerror("錯誤", "請輸入有效的數字格式。")
if __name__ == "__main__":
root = tk.Tk()
app = YagiCalculatorApp(root)
root.mainloop()
import tkinter as tk
from tkinter import ttk, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
import platform
# --- 設定 Matplotlib 中文字體,解決 Glyph 錯誤 ---
def set_mpl_chinese_font():
system = platform.system()
try:
if system == "Windows":
matplotlib.rc('font', family='Microsoft JhengHei')
elif system == "Darwin":
matplotlib.rc('font', family='Arial Unicode MS')
else:
matplotlib.rc('font', family='Noto Sans CJK JP')
matplotlib.rcParams['axes.unicode_minus'] = False
except:
pass # 如果找不到字體,則使用預設值避免程式崩潰
set_mpl_chinese_font()
class YagiCalculatorApp:
def __init__(self, root):
self.root = root
self.root.title("八木天線 (Yagi-Uda) 專業設計工具")
self.root.geometry("1100x700")
# 主容器
main_frame = tk.Frame(self.root, padx=10, pady=10)
main_frame.pack(fill="both", expand=True)
# --- 左側控制區 ---
control_frame = tk.Frame(main_frame, width=400)
control_frame.pack(side="left", fill="y", padx=(0, 10))
tk.Label(control_frame, text="設計參數輸入", font=("Arial", 14, "bold")).pack(pady=10)
input_frame = tk.LabelFrame(control_frame, text=" 物理規格 ", padx=10, pady=10)
input_frame.pack(fill="x", pady=5)
tk.Label(input_frame, text="中心頻率 (MHz):").grid(row=0, column=0, sticky="w")
self.entry_freq = tk.Entry(input_frame)
self.entry_freq.insert(0, "433.0")
self.entry_freq.grid(row=0, column=1, padx=5, pady=5)
tk.Label(input_frame, text="總節數 (Elements):").grid(row=1, column=0, sticky="w")
self.entry_elements = tk.Entry(input_frame)
self.entry_elements.insert(0, "5")
self.entry_elements.grid(row=1, column=1, padx=5, pady=5)
self.btn_calc = tk.Button(input_frame, text="計算並更新圖表", command=self.calculate_yagi,
bg="#28a745", fg="white", font=("Arial", 10, "bold"), pady=5)
self.btn_calc.grid(row=2, column=0, columnspan=2, sticky="ew", pady=10)
# 表格顯示
self.tree = ttk.Treeview(control_frame, columns=("element", "length", "total_dist"), show="headings", height=15)
self.tree.heading("element", text="元件")
self.tree.heading("length", text="長度(mm)")
self.tree.heading("total_dist", text="位置(mm)")
self.tree.column("element", width=100, anchor="center")
self.tree.column("length", width=100, anchor="center")
self.tree.column("total_dist", width=100, anchor="center")
self.tree.pack(fill="both", expand=True)
# --- 右側繪圖區 ---
self.plot_frame = tk.LabelFrame(main_frame, text=" 天線佈局預覽 ")
self.plot_frame.pack(side="right", fill="both", expand=True)
self.fig, self.ax = plt.subplots(figsize=(6, 8))
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
self.canvas.get_tk_widget().pack(fill="both", expand=True)
# 初始執行一次
self.calculate_yagi()
def calculate_yagi(self):
try:
freq = float(self.entry_freq.get())
num = int(self.entry_elements.get())
if num < 3:
messagebox.showwarning("提醒", "八木天線至少需要 3 節元件")
return
for i in self.tree.get_children(): self.tree.delete(i)
wavelength = 299792 / freq
elements_data = []
# 1. 反射器
refl_len = wavelength * 0.495
pos = 0.0
elements_data.append(("反射器", refl_len, pos))
self.tree.insert("", "end", values=("反射器", f"{refl_len:.1f}", f"{pos:.1f}"))
# 2. 驅動元件
de_len = wavelength * 0.473
pos += wavelength * 0.125
elements_data.append(("驅動元件", de_len, pos))
self.tree.insert("", "end", values=("驅動元件", f"{de_len:.1f}", f"{pos:.1f}"))
# 3. 引向器
for i in range(1, num - 1):
d_len = wavelength * (0.440 - (i * 0.005))
pos += wavelength * 0.2
elements_data.append((f"引向器 D{i}", d_len, pos))
self.tree.insert("", "end", values=(f"引向器 D{i}", f"{d_len:.1f}", f"{pos:.1f}"))
self.update_plot(elements_data)
except Exception as e:
messagebox.showerror("錯誤", f"輸入數據無效: {e}")
def update_plot(self, data):
self.ax.clear()
# 繪製支撐桿 (Boom)
max_pos = data[-1][2]
self.ax.plot([0, max_pos], [0, 0], color='gray', linestyle='--', linewidth=2, alpha=0.5)
for name, length, pos in data:
half = length / 2
# 判斷顏色
color = 'red' if "驅動" in name else ('blue' if "反射" in name else 'green')
# 繪製天線元件(垂直線)
self.ax.plot([pos, pos], [-half, half], color=color, linewidth=4, solid_capstyle='round')
# 標註文字
self.ax.text(pos, half + (length*0.05), name, rotation=45, fontsize=9, ha='left')
self.ax.text(pos, -half - (length*0.1), f"{length:.0f}mm", fontsize=8, ha='center', color='gray')
self.ax.set_title(f"八木天線設計圖 ({self.entry_freq.get()} MHz)", pad=20)
self.ax.set_xlabel("支撐桿位置 (mm)")
self.ax.set_ylabel("元件長度方向 (mm)")
self.ax.set_aspect('equal')
self.ax.grid(True, alpha=0.3)
self.fig.tight_layout()
self.canvas.draw()
if __name__ == "__main__":
root = tk.Tk()
app = YagiCalculatorApp(root)
root.mainloop()
沒有留言:
張貼留言