2025年12月6日 星期六

8-QAM Signal 4 Phases 2 Amplitudes + 8PSK

 8-QAM Signal 4 Phases 2 Amplitudes + 8PSK






import tkinter as tk

from tkinter import messagebox

import math

import cmath


# --- 8-QAM 參數設定 ---

# 振幅大小定義

AMP_SMALL = 0.5

AMP_LARGE = 1.0


# 8-QAM 符號定義 (I, Q 座標對)

QAM_SYMBOLS = {

    # 小振幅 (內環)

    '000': (AMP_SMALL, AMP_SMALL),   # 45度

    '001': (-AMP_SMALL, AMP_SMALL),  # 135度

    '010': (-AMP_SMALL, -AMP_SMALL), # 225度

    '011': (AMP_SMALL, -AMP_SMALL),  # 315度

    

    # 大振幅 (外環)

    '100': (AMP_LARGE, AMP_LARGE),   # 45度

    '101': (-AMP_LARGE, AMP_LARGE),  # 135度

    '110': (-AMP_LARGE, -AMP_LARGE), # 225度

    '111': (AMP_LARGE, -AMP_LARGE)   # 315度

}


# 按照 000 -> 001 -> ... -> 111 排序的符號列表,用於自動模式

SYMBOL_SEQUENCE = sorted(QAM_SYMBOLS.keys())


# --- 模式與自動控制變數 ---

AUTO_MODE_RUNNING = False 

AUTO_DELAY_MS = 3000      

current_symbol_index = 0  

after_id = None           


# --- 星座圖參數 (左側) ---

CONST_W = 400

CONST_H = 400

CONST_CX = CONST_W // 2

CONST_CY = CONST_H // 2

SCALE_FACTOR = 150  

POINT_R = 10

I_OFFSET_Y = 25  

Q_OFFSET_X = 25  


# --- 靜態波形圖參數 (右側 8 個) ---

ALL_WAVE_W = 350

ALL_WAVE_H = 400

SMALL_WAVE_H_PER_SYMBOL = 45 

SMALL_FREQUENCY = 2  

SMALL_SYMBOL_DURATION = 200 

STATIC_TEXT_OFFSET = 100 

SMALL_WAVE_AMPLITUDE = 20 


# --- 動態波形圖參數 (底部) ---

DYNAMIC_WAVE_W = 800

DYNAMIC_WAVE_H = 300

DYNAMIC_WAVE_CY = DYNAMIC_WAVE_H // 2

WAVE_SCALING = 100   

FREQUENCY = 2        

SYMBOL_DURATION = 200 

DYNAMIC_WAVE_START_X = (DYNAMIC_WAVE_W - SYMBOL_DURATION) // 2 

# *** 修正點:定義紅字標註的右側偏移量 ***

RED_TEXT_OFFSET_RIGHT = 80 



# --- 輔助函數 ---

def calculate_polar(i_coord, q_coord):

    """計算給定 I/Q 座標的振幅和相位 (度)。"""

    

    magnitude = math.sqrt(i_coord**2 + q_coord**2)

    phase_rad = cmath.phase(complex(i_coord, q_coord))

    phase_deg = math.degrees(phase_rad)

    

    if phase_deg < 0:

        phase_deg += 360

        

    return magnitude, phase_deg


# --- 函數定義 ---


def draw_8qam_constellation(canvas):

    """繪製 8-QAM 星座圖 (4 Phases, 2 Amplitudes 佈局)。"""

    

    canvas.delete("all")

    

    # 1. 繪製標題

    canvas.create_text(CONST_CX, 20, text="8-QAM 星座圖 (4 Phases, 2 Amplitudes)", font=('Arial', 12, 'bold'))

    

    # 2. 繪製座標軸 (I軸 和 Q軸)

    axis_end = SCALE_FACTOR * AMP_LARGE * 1.5

    canvas.create_line(CONST_CX - axis_end, CONST_CY, CONST_CX + axis_end, CONST_CY, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + axis_end, CONST_CY + I_OFFSET_Y, text='I', fill='black', font=('Arial', 12, 'bold'))

    

    canvas.create_line(CONST_CX, CONST_CY + axis_end, CONST_CX, CONST_CY - axis_end, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + Q_OFFSET_X, CONST_CY - axis_end, text='Q', fill='black', font=('Arial', 12, 'bold'))

    

    # 繪製代表振幅的虛線方格/圓 (這裡繪製方格)

    square_size_small = AMP_SMALL * SCALE_FACTOR

    square_size_large = AMP_LARGE * SCALE_FACTOR

    

    # 小振幅方格

    x1_s, y1_s = CONST_CX - square_size_small, CONST_CY - square_size_small

    x2_s, y2_s = CONST_CX + square_size_small, CONST_CY + square_size_small

    canvas.create_rectangle(x1_s, y1_s, x2_s, y2_s, outline='gray', dash=(5, 3))


    # 大振幅方格

    x1_l, y1_l = CONST_CX - square_size_large, CONST_CY - square_size_large

    x2_l, y2_l = CONST_CX + square_size_large, CONST_CY + square_size_large

    canvas.create_rectangle(x1_l, y1_l, x2_l, y2_l, outline='black', dash=(5, 3))

    

    # 3. 繪製 8 個符號點和標註

    for bits, (i_norm, q_norm) in QAM_SYMBOLS.items():

        

        i_coord = CONST_CX + i_norm * SCALE_FACTOR

        q_coord = CONST_CY - q_norm * SCALE_FACTOR 

        

        canvas.create_oval(i_coord - POINT_R, q_coord - POINT_R, 

                           i_coord + POINT_R, q_coord + POINT_R, 

                           fill='pink' if abs(i_norm)==AMP_SMALL else 'red', outline='black')

        

        offset = 20 

        

        angle_rad = cmath.phase(complex(i_norm, q_norm))

        text_i_coord = i_coord + offset * math.cos(angle_rad)

        text_q_coord = q_coord - offset * math.sin(angle_rad)

        

        canvas.create_text(text_i_coord, text_q_coord, text=bits, font=('Arial', 10, 'bold'), fill='darkblue')


def draw_all_waveforms(canvas):

    """繪製右側 8 個靜態 8-QAM 波形圖。"""

    

    canvas.delete("all")

    canvas.create_text(ALL_WAVE_W // 2, 10, text="8-QAM 符號波形對照", font=('Arial', 12, 'bold'), fill='red')


    y_offset = 30

    sorted_symbols = SYMBOL_SEQUENCE 

    

    for bits in sorted_symbols:

        i_norm, q_norm = QAM_SYMBOLS[bits]

        magnitude, phase_deg = calculate_polar(i_norm, q_norm)

        phase_rad = math.radians(phase_deg)

        

        # 標註位元組和相位 (靠右對齊)

        label_text = f"{bits} ({phase_deg:.0f}°)"

        canvas.create_text(STATIC_TEXT_OFFSET, y_offset + SMALL_WAVE_H_PER_SYMBOL // 2, 

                           text=label_text, anchor=tk.E, 

                           font=('Arial', 9), fill='darkgreen')

        

        start_x = STATIC_TEXT_OFFSET + 10

        wave_cy = y_offset + SMALL_WAVE_H_PER_SYMBOL // 2

        

        # 繪製時間軸

        canvas.create_line(start_x, wave_cy, start_x + SMALL_SYMBOL_DURATION, wave_cy, fill='gray', width=1)


        waveform_points = []

        for t in range(SMALL_SYMBOL_DURATION):

            t_norm = t / SMALL_SYMBOL_DURATION

            

            amplitude = magnitude * SMALL_WAVE_AMPLITUDE * math.cos(2 * math.pi * SMALL_FREQUENCY * t_norm + phase_rad)

            

            x = start_x + t

            y = wave_cy - amplitude

            waveform_points.extend([x, y])


        canvas.create_line(waveform_points, fill='blue', smooth=True, width=1)

        

        y_offset += SMALL_WAVE_H_PER_SYMBOL


def draw_single_waveform(canvas, bits_input):

    """根據輸入的位元組繪製單個符號的 8-QAM 波形。"""

    

    canvas.delete("all")

    

    i_norm, q_norm = QAM_SYMBOLS[bits_input]

    magnitude, phase_deg = calculate_polar(i_norm, q_norm)

    phase_rad = math.radians(phase_deg)

    

    # 1. 繪製座標軸 (置中對齊)

    x_axis_end = DYNAMIC_WAVE_START_X + SYMBOL_DURATION + 20

    canvas.create_line(DYNAMIC_WAVE_START_X - 20, DYNAMIC_WAVE_CY, x_axis_end, DYNAMIC_WAVE_CY, fill='black', width=2)

    canvas.create_text(DYNAMIC_WAVE_START_X - 30, DYNAMIC_WAVE_CY, text='0', fill='black', anchor=tk.E) 

    canvas.create_text(20, 20, text='Amplitude (A)', anchor=tk.W) 

    canvas.create_text(DYNAMIC_WAVE_W - 10, DYNAMIC_WAVE_CY + 10, text='Time (t)', anchor=tk.E) 

    

    # 2. 繪製符號週期邊界

    canvas.create_line(DYNAMIC_WAVE_START_X, 20, DYNAMIC_WAVE_START_X, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    canvas.create_line(DYNAMIC_WAVE_START_X + SYMBOL_DURATION, 20, DYNAMIC_WAVE_START_X + SYMBOL_DURATION, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    

    # 3. 標註符號 (將紅字更靠右)

    # 這裡將 x 座標設置為接近畫布右側邊緣 RED_TEXT_OFFSET_RIGHT 的位置

    text_x_coord = DYNAMIC_WAVE_W - RED_TEXT_OFFSET_RIGHT 

    

    label_text = f"Symbol: {bits_input} | Mag: {magnitude:.2f} | Phase: {phase_deg:.0f}° | Freq: {FREQUENCY} cycles/symbol"

    canvas.create_text(text_x_coord, 40, 

                       text=label_text, 

                       font=('Arial', 12, 'bold'), fill='red', anchor=tk.E) # anchor=tk.E 確保文字右側對齊於此座標


    # 4. 繪製波形 (置中對齊)

    current_x = DYNAMIC_WAVE_START_X

    waveform_points = []

    

    for t in range(SYMBOL_DURATION):

        t_norm = t / SYMBOL_DURATION

        

        amplitude = magnitude * WAVE_SCALING * math.cos(2 * math.pi * FREQUENCY * t_norm + phase_rad)

        

        x = current_x + t

        y = DYNAMIC_WAVE_CY - amplitude

        waveform_points.extend([x, y])


    canvas.create_line(waveform_points, fill='blue', smooth=True, width=2)



# --- 自動模式控制函數 ---

def start_auto_mode():

    global AUTO_MODE_RUNNING, current_symbol_index, after_id

    

    if AUTO_MODE_RUNNING:

        return

        

    current_symbol_index = 0

    AUTO_MODE_RUNNING = True

    

    input_entry.config(state=tk.DISABLED)

    manual_button.config(state=tk.DISABLED)

    auto_start_button.config(text="運行中...", state=tk.DISABLED, bg='red')

    auto_stop_button.config(state=tk.NORMAL)

    

    run_auto_cycle()


def run_auto_cycle():

    global current_symbol_index, after_id

    

    if not AUTO_MODE_RUNNING:

        return

        

    bits = SYMBOL_SEQUENCE[current_symbol_index]

    draw_single_waveform(dynamic_wave_canvas, bits)

    

    input_entry.config(state=tk.NORMAL)

    input_entry.delete(0, tk.END)

    input_entry.insert(0, bits)

    input_entry.config(state=tk.DISABLED) 

    

    current_symbol_index = (current_symbol_index + 1) % len(SYMBOL_SEQUENCE)

    

    after_id = root.after(AUTO_DELAY_MS, run_auto_cycle)


def stop_auto_mode():

    global AUTO_MODE_RUNNING, after_id

    

    if not AUTO_MODE_RUNNING:

        return

        

    AUTO_MODE_RUNNING = False

    

    if after_id:

        root.after_cancel(after_id)

        after_id = None

        

    input_entry.config(state=tk.NORMAL)

    manual_button.config(state=tk.NORMAL)

    auto_start_button.config(text="啟動自動模式", state=tk.NORMAL, bg='lightgreen')

    auto_stop_button.config(state=tk.DISABLED)


def update_waveform_manual():

    if AUTO_MODE_RUNNING:

        messagebox.showinfo("模式提示", "請先點擊 '停止自動模式' 才能手動輸入。")

        return

        

    input_bits = input_entry.get().strip()

    

    if input_bits not in QAM_SYMBOLS:

        messagebox.showerror("輸入錯誤", "請輸入一個有效的 3-bit 位元組 (000~111)。")

        return

        

    draw_single_waveform(dynamic_wave_canvas, input_bits)


# --- 主程式 ---

root = tk.Tk()

root.title("8-QAM 調變分析工具 (4 Phases, 2 Amplitudes)")


# 1. 頂部主框架 (包含星座圖和靜態波形)

top_frame = tk.Frame(root)

top_frame.pack(pady=10)


# 1A. 星座圖框架 (左側)

const_frame = tk.Frame(top_frame)

const_frame.pack(side=tk.LEFT, padx=10)

const_canvas = tk.Canvas(const_frame, width=CONST_W, height=CONST_H, bg='white')

const_canvas.pack()

draw_8qam_constellation(const_canvas) 

tk.Label(const_frame, text="8-QAM 星座圖", font=('Arial', 14, 'bold')).pack()


# 1B. 靜態波形框架 (右側)

all_wave_frame = tk.Frame(top_frame)

all_wave_frame.pack(side=tk.LEFT, padx=10)

all_wave_canvas = tk.Canvas(all_wave_frame, width=ALL_WAVE_W, height=ALL_WAVE_H, bg='lightyellow')

all_wave_canvas.pack()

draw_all_waveforms(all_wave_canvas) 



# 2. 中間控制與輸入部分 (增加模式選擇)

control_frame = tk.Frame(root)

control_frame.pack(pady=10)


# 2A. 手動控制組

manual_control_frame = tk.Frame(control_frame)

manual_control_frame.pack(side=tk.LEFT, padx=20)

tk.Label(manual_control_frame, text="【手動模式】輸入位元組 (000~111):", font=('Arial', 12)).pack(side=tk.LEFT, padx=5)

input_entry = tk.Entry(manual_control_frame, width=5, font=('Arial', 12))

input_entry.pack(side=tk.LEFT, padx=5)

manual_button = tk.Button(manual_control_frame, text="手動顯示波形", command=update_waveform_manual, font=('Arial', 12, 'bold'), bg='lightblue')

manual_button.pack(side=tk.LEFT, padx=10)


# 2B. 自動控制組

auto_control_frame = tk.Frame(control_frame)

auto_control_frame.pack(side=tk.LEFT, padx=20)

auto_start_button = tk.Button(auto_control_frame, text="啟動自動模式", command=start_auto_mode, font=('Arial', 12, 'bold'), bg='lightgreen')

auto_start_button.pack(side=tk.LEFT, padx=10)

auto_stop_button = tk.Button(auto_control_frame, text="停止自動模式", command=stop_auto_mode, font=('Arial', 12, 'bold'), bg='red', state=tk.DISABLED)

auto_stop_button.pack(side=tk.LEFT, padx=10)



# 3. 底部動態波形圖部分

dynamic_wave_frame = tk.Frame(root)

dynamic_wave_frame.pack(pady=10)

dynamic_wave_canvas = tk.Canvas(dynamic_wave_frame, width=DYNAMIC_WAVE_W, height=DYNAMIC_WAVE_H, bg='white')

dynamic_wave_canvas.pack()

# 初始顯示 '000' 的波形

draw_single_waveform(dynamic_wave_canvas, '000') 

tk.Label(dynamic_wave_frame, text="單一符號動態 QAM 波形圖 (2個週期)", font=('Arial', 14, 'bold')).pack()


# 執行 Tkinter 事件迴圈

root.mainloop()



import tkinter as tk

from tkinter import messagebox

import math

import itertools


# --- 全域參數設定 ---

# 8-PSK 符號定義 (角度,單位:度)

PSK_PHASES = {

    '111': 0,

    '110': 45,

    '010': 90,

    '011': 135,

    '001': 180,

    '000': 225,

    '100': 270,

    '101': 315

}

# 按照 000 -> 001 -> ... -> 111 排序的符號列表,用於自動模式

SYMBOL_SEQUENCE = sorted(PSK_PHASES.keys())


# --- 模式與自動控制變數 ---

AUTO_MODE_RUNNING = False # 追蹤自動模式是否正在運行

AUTO_DELAY_MS = 3000      # 自動模式延遲時間 (3000ms = 3秒)

current_symbol_index = 0  # 追蹤當前自動模式的符號索引

after_id = None           # 用於儲存 tk.after 的 ID,以便取消


# --- 星座圖參數 (左側) ---

CONST_W = 400

CONST_H = 400

CONST_CX = CONST_W // 2

CONST_CY = CONST_H // 2

RADIUS = 150

POINT_R = 10

I_OFFSET_Y = 25  

Q_OFFSET_X = 25  


# --- 靜態波形圖參數 (右側 8 個) ---

ALL_WAVE_W = 350

ALL_WAVE_H = 400

SMALL_WAVE_H_PER_SYMBOL = 45 

SMALL_WAVE_AMPLITUDE = 15

SMALL_FREQUENCY = 3

SMALL_SYMBOL_DURATION = 200 

STATIC_TEXT_OFFSET = 100 


# --- 動態波形圖參數 (底部) ---

DYNAMIC_WAVE_W = 800

DYNAMIC_WAVE_H = 300

DYNAMIC_WAVE_CY = DYNAMIC_WAVE_H // 2

WAVE_AMPLITUDE = 90  

FREQUENCY = 2        

SYMBOL_DURATION = 200 

DYNAMIC_WAVE_START_X = (DYNAMIC_WAVE_W - SYMBOL_DURATION) // 2 


# --- 函數定義 ---


def draw_8psk_constellation(canvas):

    """繪製 8-PSK 星座圖。"""

    

    # 1. 繪製座標軸 (I軸 和 Q軸)

    canvas.create_line(CONST_CX - RADIUS - 20, CONST_CY, CONST_CX + RADIUS + 20, CONST_CY, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + RADIUS + 30, CONST_CY + I_OFFSET_Y, text='I', fill='black', font=('Arial', 12, 'bold'))

    

    canvas.create_line(CONST_CX, CONST_CY + RADIUS + 20, CONST_CX, CONST_CY - RADIUS - 20, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + Q_OFFSET_X, CONST_CY - RADIUS - 30, text='Q', fill='black', font=('Arial', 12, 'bold'))

    

    # 2. 繪製圓形

    x1, y1 = CONST_CX - RADIUS, CONST_CY - RADIUS

    x2, y2 = CONST_CX + RADIUS, CONST_CY + RADIUS

    canvas.create_oval(x1, y1, x2, y2, outline='black')

    

    # 3. 繪製 8 個符號點和標註

    for bits, angle_deg in PSK_PHASES.items():

        angle_rad = math.radians(angle_deg)

        i_coord = CONST_CX + RADIUS * math.cos(angle_rad)

        q_coord = CONST_CY - RADIUS * math.sin(angle_rad) 

        

        canvas.create_oval(i_coord - POINT_R, q_coord - POINT_R, 

                           i_coord + POINT_R, q_coord + POINT_R, 

                           fill='lightblue', outline='black')

        

        offset_dist = RADIUS + 25 

        text_i_coord = CONST_CX + offset_dist * math.cos(angle_rad)

        text_q_coord = CONST_CY - offset_dist * math.sin(angle_rad)

        canvas.create_text(text_i_coord, text_q_coord, text=bits, font=('Arial', 10, 'bold'), fill='darkblue')


def draw_all_waveforms(canvas):

    """繪製右側 8 個靜態波形圖。"""

    

    canvas.delete("all")

    canvas.create_text(ALL_WAVE_W // 2, 10, text="8-PSK 符號相位對照 (全部 8 個)", font=('Arial', 12, 'bold'), fill='red')


    y_offset = 30

    sorted_symbols = SYMBOL_SEQUENCE # 使用排序後的列表

    

    for bits in sorted_symbols:

        phase_deg = PSK_PHASES[bits]

        phase_rad = math.radians(phase_deg)

        

        # 標註位元組和相位 (靠右對齊)

        canvas.create_text(STATIC_TEXT_OFFSET, y_offset + SMALL_WAVE_H_PER_SYMBOL // 2, 

                           text=f"{bits} ({phase_deg}°)", anchor=tk.E, 

                           font=('Arial', 9), fill='darkgreen')

        

        start_x = STATIC_TEXT_OFFSET + 10

        wave_cy = y_offset + SMALL_WAVE_H_PER_SYMBOL // 2

        

        # 繪製時間軸

        canvas.create_line(start_x, wave_cy, start_x + SMALL_SYMBOL_DURATION, wave_cy, fill='gray', width=1)


        waveform_points = []

        for t in range(SMALL_SYMBOL_DURATION):

            t_norm = t / SMALL_SYMBOL_DURATION

            amplitude = SMALL_WAVE_AMPLITUDE * math.cos(2 * math.pi * SMALL_FREQUENCY * t_norm + phase_rad)

            x = start_x + t

            y = wave_cy - amplitude

            waveform_points.extend([x, y])


        canvas.create_line(waveform_points, fill='blue', smooth=True, width=1)

        

        y_offset += SMALL_WAVE_H_PER_SYMBOL


def draw_single_waveform(canvas, bits_input):

    """根據輸入的位元組繪製單個符號的 8-PSK 波形。"""

    

    canvas.delete("all")

    

    # 1. 繪製座標軸 (置中對齊)

    canvas.create_line(DYNAMIC_WAVE_START_X - 20, DYNAMIC_WAVE_CY, DYNAMIC_WAVE_START_X + SYMBOL_DURATION + 20, DYNAMIC_WAVE_CY, fill='black', width=2)

    canvas.create_text(DYNAMIC_WAVE_START_X - 30, DYNAMIC_WAVE_CY, text='0', fill='black', anchor=tk.E) 

    canvas.create_text(20, 20, text='Amplitude (A)', anchor=tk.W) 

    canvas.create_text(DYNAMIC_WAVE_W - 10, DYNAMIC_WAVE_CY + 10, text='Time (t)', anchor=tk.E) 

    

    # 2. 繪製符號週期邊界

    canvas.create_line(DYNAMIC_WAVE_START_X, 20, DYNAMIC_WAVE_START_X, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    canvas.create_line(DYNAMIC_WAVE_START_X + SYMBOL_DURATION, 20, DYNAMIC_WAVE_START_X + SYMBOL_DURATION, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    

    # 3. 標註符號 (靠右對齊)

    phase_deg = PSK_PHASES[bits_input]

    text_x_coord = DYNAMIC_WAVE_START_X + SYMBOL_DURATION + 30 

    

    canvas.create_text(text_x_coord, 40, 

                       text=f"Symbol: {bits_input} | Phase: {phase_deg}° | Freq: {FREQUENCY} cycles/symbol", 

                       font=('Arial', 12, 'bold'), fill='red', anchor=tk.E) 


    phase_rad = math.radians(phase_deg)

    

    # 4. 繪製波形 (置中對齊)

    current_x = DYNAMIC_WAVE_START_X

    waveform_points = []

    

    for t in range(SYMBOL_DURATION):

        t_norm = t / SYMBOL_DURATION

        amplitude = WAVE_AMPLITUDE * math.cos(2 * math.pi * FREQUENCY * t_norm + phase_rad)

        x = current_x + t

        y = DYNAMIC_WAVE_CY - amplitude

        waveform_points.extend([x, y])


    canvas.create_line(waveform_points, fill='blue', smooth=True, width=2)


# --- 自動模式控制函數 ---


def start_auto_mode():

    """啟動自動循環模式。"""

    global AUTO_MODE_RUNNING, current_symbol_index, after_id

    

    if AUTO_MODE_RUNNING:

        return

        

    # 確保從 000 開始

    current_symbol_index = 0

    AUTO_MODE_RUNNING = True

    

    # 禁用手動控制

    input_entry.config(state=tk.DISABLED)

    manual_button.config(state=tk.DISABLED)

    auto_start_button.config(text="運行中...", state=tk.DISABLED, bg='red')

    auto_stop_button.config(state=tk.NORMAL)

    

    # 立即開始循環

    run_auto_cycle()


def run_auto_cycle():

    """自動切換符號並繪製波形。"""

    global current_symbol_index, after_id

    

    if not AUTO_MODE_RUNNING:

        return

        

    bits = SYMBOL_SEQUENCE[current_symbol_index]

    draw_single_waveform(dynamic_wave_canvas, bits)

    

    # 更新控制面板上的顯示

    input_entry.config(state=tk.NORMAL)

    input_entry.delete(0, tk.END)

    input_entry.insert(0, bits)

    input_entry.config(state=tk.DISABLED) # 重新禁用

    

    # 移動到下一個索引,循環

    current_symbol_index = (current_symbol_index + 1) % len(SYMBOL_SEQUENCE)

    

    # 設置下一次循環

    after_id = root.after(AUTO_DELAY_MS, run_auto_cycle)


def stop_auto_mode():

    """停止自動循環模式。"""

    global AUTO_MODE_RUNNING, after_id

    

    if not AUTO_MODE_RUNNING:

        return

        

    AUTO_MODE_RUNNING = False

    

    # 取消排隊中的 after 任務

    if after_id:

        root.after_cancel(after_id)

        after_id = None

        

    # 啟用手動控制

    input_entry.config(state=tk.NORMAL)

    manual_button.config(state=tk.NORMAL)

    auto_start_button.config(text="啟動自動模式", state=tk.NORMAL, bg='lightgreen')

    auto_stop_button.config(state=tk.DISABLED)


def update_waveform_manual():

    """手動模式下,更新波形。"""

    if AUTO_MODE_RUNNING:

        messagebox.showinfo("模式提示", "請先點擊 '停止自動模式' 才能手動輸入。")

        return

        

    input_bits = input_entry.get().strip()

    

    if input_bits not in PSK_PHASES:

        messagebox.showerror("輸入錯誤", "請輸入一個有效的 3-bit 位元組 (000~111)。")

        return

        

    draw_single_waveform(dynamic_wave_canvas, input_bits)


# --- 主程式 ---

root = tk.Tk()

root.title("8-PSK 調變分析工具 (自動/手動模式)")


# 1. 頂部主框架 (包含星座圖和靜態波形)

top_frame = tk.Frame(root)

top_frame.pack(pady=10)


# 1A. 星座圖框架 (左側)

const_frame = tk.Frame(top_frame)

const_frame.pack(side=tk.LEFT, padx=10)

const_canvas = tk.Canvas(const_frame, width=CONST_W, height=CONST_H, bg='white')

const_canvas.pack()

draw_8psk_constellation(const_canvas)

tk.Label(const_frame, text="8-PSK 星座圖", font=('Arial', 14, 'bold')).pack()


# 1B. 靜態波形框架 (右側)

all_wave_frame = tk.Frame(top_frame)

all_wave_frame.pack(side=tk.LEFT, padx=10)

all_wave_canvas = tk.Canvas(all_wave_frame, width=ALL_WAVE_W, height=ALL_WAVE_H, bg='lightyellow')

all_wave_canvas.pack()

draw_all_waveforms(all_wave_canvas)



# 2. 中間控制與輸入部分 (增加模式選擇)

control_frame = tk.Frame(root)

control_frame.pack(pady=10)


# 2A. 手動控制組

manual_control_frame = tk.Frame(control_frame)

manual_control_frame.pack(side=tk.LEFT, padx=20)

tk.Label(manual_control_frame, text="【手動模式】輸入位元組 (000~111):", font=('Arial', 12)).pack(side=tk.LEFT, padx=5)

input_entry = tk.Entry(manual_control_frame, width=5, font=('Arial', 12))

input_entry.pack(side=tk.LEFT, padx=5)

manual_button = tk.Button(manual_control_frame, text="手動顯示波形", command=update_waveform_manual, font=('Arial', 12, 'bold'), bg='lightblue')

manual_button.pack(side=tk.LEFT, padx=10)


# 2B. 自動控制組

auto_control_frame = tk.Frame(control_frame)

auto_control_frame.pack(side=tk.LEFT, padx=20)

auto_start_button = tk.Button(auto_control_frame, text="啟動自動模式", command=start_auto_mode, font=('Arial', 12, 'bold'), bg='lightgreen')

auto_start_button.pack(side=tk.LEFT, padx=10)

auto_stop_button = tk.Button(auto_control_frame, text="停止自動模式", command=stop_auto_mode, font=('Arial', 12, 'bold'), bg='red', state=tk.DISABLED)

auto_stop_button.pack(side=tk.LEFT, padx=10)



# 3. 底部動態波形圖部分

dynamic_wave_frame = tk.Frame(root)

dynamic_wave_frame.pack(pady=10)

dynamic_wave_canvas = tk.Canvas(dynamic_wave_frame, width=DYNAMIC_WAVE_W, height=DYNAMIC_WAVE_H, bg='white')

dynamic_wave_canvas.pack()

# 初始顯示 '111' 的波形

draw_single_waveform(dynamic_wave_canvas, '111') 

tk.Label(dynamic_wave_frame, text="單一符號動態波形圖 (2個週期)", font=('Arial', 14, 'bold')).pack()


# 執行 Tkinter 事件迴圈

root.mainloop()





import tkinter as tk

from tkinter import messagebox

import math


# --- 全域參數設定 ---

# 8-PSK 符號定義 (角度,單位:度)

# 注意: 確保所有 8 個符號都在這裡

PSK_PHASES = {

    '111': 0,    # 修正:靜態圖中缺少的 111 (0度)

    '110': 45,

    '010': 90,

    '011': 135,

    '001': 180,

    '000': 225,

    '100': 270,

    '101': 315

}


# --- 星座圖參數 (左側) ---

CONST_W = 400

CONST_H = 400

CONST_CX = CONST_W // 2

CONST_CY = CONST_H // 2

RADIUS = 150

POINT_R = 10

I_OFFSET_Y = 25  

Q_OFFSET_X = 25  


# --- 靜態波形圖參數 (右側 8 個) ---

ALL_WAVE_W = 350

ALL_WAVE_H = 400

SMALL_WAVE_H_PER_SYMBOL = 45 # 每個小波形的高度稍微縮小,確保 8 個都能放下

SMALL_WAVE_AMPLITUDE = 15

SMALL_FREQUENCY = 3

SMALL_SYMBOL_DURATION = 200 # 修正:將長度從 250 縮短到 200

STATIC_TEXT_OFFSET = 100 # 新增:靜態波形文字 X 軸偏移量


# --- 動態波形圖參數 (底部) ---

DYNAMIC_WAVE_W = 800

DYNAMIC_WAVE_H = 300

DYNAMIC_WAVE_CY = DYNAMIC_WAVE_H // 2

WAVE_AMPLITUDE = 90  

FREQUENCY = 2        

SYMBOL_DURATION = 200 

DYNAMIC_WAVE_START_X = (DYNAMIC_WAVE_W - SYMBOL_DURATION) // 2 # 修正:波形繪製的起始X座標 (置中)


# --- 函數定義 ---


def draw_8psk_constellation(canvas):

    """繪製 8-PSK 星座圖。 (無變化)"""

    

    # 1. 繪製座標軸 (I軸 和 Q軸)

    canvas.create_line(CONST_CX - RADIUS - 20, CONST_CY, CONST_CX + RADIUS + 20, CONST_CY, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + RADIUS + 30, CONST_CY + I_OFFSET_Y, text='I', fill='black', font=('Arial', 12, 'bold'))

    

    canvas.create_line(CONST_CX, CONST_CY + RADIUS + 20, CONST_CX, CONST_CY - RADIUS - 20, arrow=tk.LAST, fill='black')

    canvas.create_text(CONST_CX + Q_OFFSET_X, CONST_CY - RADIUS - 30, text='Q', fill='black', font=('Arial', 12, 'bold'))

    

    # 2. 繪製圓形

    x1, y1 = CONST_CX - RADIUS, CONST_CY - RADIUS

    x2, y2 = CONST_CX + RADIUS, CONST_CY + RADIUS

    canvas.create_oval(x1, y1, x2, y2, outline='black')

    

    # 3. 繪製 8 個符號點和標註

    for bits, angle_deg in PSK_PHASES.items():

        angle_rad = math.radians(angle_deg)

        i_coord = CONST_CX + RADIUS * math.cos(angle_rad)

        q_coord = CONST_CY - RADIUS * math.sin(angle_rad) 

        

        canvas.create_oval(i_coord - POINT_R, q_coord - POINT_R, 

                           i_coord + POINT_R, q_coord + POINT_R, 

                           fill='lightblue', outline='black')

        

        offset_dist = RADIUS + 25 

        text_i_coord = CONST_CX + offset_dist * math.cos(angle_rad)

        text_q_coord = CONST_CY - offset_dist * math.sin(angle_rad)

        canvas.create_text(text_i_coord, text_q_coord, text=bits, font=('Arial', 10, 'bold'), fill='darkblue')


def draw_all_waveforms(canvas):

    """繪製右側 8 個靜態波形圖 (修正:補齊 111 符號並修正標註位置)。"""

    

    # 清空畫布以防萬一

    canvas.delete("all")

    

    # 標題

    canvas.create_text(ALL_WAVE_W // 2, 10, text="8-PSK 符號相位對照 (全部 8 個)", font=('Arial', 12, 'bold'), fill='red')


    y_offset = 30

    

    # 根據位元組排序,確保 111 在列

    sorted_symbols = sorted(PSK_PHASES.items(), key=lambda item: item[0])

    

    for bits, phase_deg in sorted_symbols:

        phase_rad = math.radians(phase_deg)

        

        # 修正 2: 標註位元組和相位,並使用 STATIC_TEXT_OFFSET 靠右

        canvas.create_text(STATIC_TEXT_OFFSET, y_offset + SMALL_WAVE_H_PER_SYMBOL // 2, 

                           text=f"{bits} ({phase_deg}°)", anchor=tk.E, # anchor=tk.E 確保文字右側對齊

                           font=('Arial', 9), fill='darkgreen')

        

        # 波形繪製起始 X 座標 (在標註右側)

        start_x = STATIC_TEXT_OFFSET + 10

        wave_cy = y_offset + SMALL_WAVE_H_PER_SYMBOL // 2

        

        # 繪製時間軸

        canvas.create_line(start_x, wave_cy, start_x + SMALL_SYMBOL_DURATION, wave_cy, fill='gray', width=1)


        waveform_points = []

        for t in range(SMALL_SYMBOL_DURATION):

            t_norm = t / SMALL_SYMBOL_DURATION

            

            # S(t) = A * cos(2*pi*f*t + phi)

            amplitude = SMALL_WAVE_AMPLITUDE * math.cos(2 * math.pi * SMALL_FREQUENCY * t_norm + phase_rad)

            

            x = start_x + t

            y = wave_cy - amplitude

            

            waveform_points.extend([x, y])


        canvas.create_line(waveform_points, fill='blue', smooth=True, width=1)

        

        # 移動到下一個波形的位置

        y_offset += SMALL_WAVE_H_PER_SYMBOL


def draw_single_waveform(canvas, bits_input):

    """根據輸入的位元組繪製單個符號的 8-PSK 波形 (修正:波形置中並調整標註位置)。"""

    

    canvas.delete("all")

    

    # 1. 繪製座標軸 (使用置中的座標)

    canvas.create_line(DYNAMIC_WAVE_START_X - 20, DYNAMIC_WAVE_CY, DYNAMIC_WAVE_START_X + SYMBOL_DURATION + 20, DYNAMIC_WAVE_CY, fill='black', width=2)

    canvas.create_text(DYNAMIC_WAVE_START_X - 30, DYNAMIC_WAVE_CY, text='0', fill='black', anchor=tk.E) # 原點標註

    canvas.create_text(20, 20, text='Amplitude (A)', anchor=tk.W) # 振幅標註

    canvas.create_text(DYNAMIC_WAVE_W - 10, DYNAMIC_WAVE_CY + 10, text='Time (t)', anchor=tk.E) # 時間標註

    

    # 2. 繪製符號週期邊界

    canvas.create_line(DYNAMIC_WAVE_START_X, 20, DYNAMIC_WAVE_START_X, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    canvas.create_line(DYNAMIC_WAVE_START_X + SYMBOL_DURATION, 20, DYNAMIC_WAVE_START_X + SYMBOL_DURATION, DYNAMIC_WAVE_H - 20, dash=(4, 2), fill='gray')

    

    # 3. 標註符號 (修正 3: 將紅色的文字進一步向右移動)

    phase_deg = PSK_PHASES[bits_input]

    

    # 計算靠右邊界附近的 x 座標,例如在 90% 的位置,並使用 anchor=tk.E

    text_x_coord = DYNAMIC_WAVE_START_X + SYMBOL_DURATION + 30 

    

    canvas.create_text(text_x_coord, 40, 

                       text=f"Symbol: {bits_input} | Phase: {phase_deg}° | Freq: {FREQUENCY} cycles/symbol", 

                       font=('Arial', 12, 'bold'), fill='red', anchor=tk.E) 


    phase_rad = math.radians(phase_deg)

    

    # 4. 繪製波形 (修正 4: 使用置中的起始座標)

    current_x = DYNAMIC_WAVE_START_X

    waveform_points = []

    

    for t in range(SYMBOL_DURATION):

        t_norm = t / SYMBOL_DURATION

        

        amplitude = WAVE_AMPLITUDE * math.cos(2 * math.pi * FREQUENCY * t_norm + phase_rad)

        

        x = current_x + t

        y = DYNAMIC_WAVE_CY - amplitude

        waveform_points.extend([x, y])


    canvas.create_line(waveform_points, fill='blue', smooth=True, width=2)



def update_waveform():

    """從輸入框讀取位元組,驗證後更新波形圖。"""

    

    input_bits = input_entry.get().strip()

    

    if input_bits not in PSK_PHASES:

        messagebox.showerror("輸入錯誤", "請輸入一個有效的 3-bit 位元組 (000~111)。")

        return

        

    draw_single_waveform(dynamic_wave_canvas, input_bits)


# --- 主程式 ---

root = tk.Tk()

root.title("8-PSK 調變分析工具 (美化修正版)")


# 1. 頂部主框架 (包含星座圖和靜態波形)

top_frame = tk.Frame(root)

top_frame.pack(pady=10)


# 1A. 星座圖框架 (左側)

const_frame = tk.Frame(top_frame)

const_frame.pack(side=tk.LEFT, padx=10)


const_canvas = tk.Canvas(const_frame, width=CONST_W, height=CONST_H, bg='white')

const_canvas.pack()

draw_8psk_constellation(const_canvas)

tk.Label(const_frame, text="8-PSK 星座圖", font=('Arial', 14, 'bold')).pack()


# 1B. 靜態波形框架 (右側)

all_wave_frame = tk.Frame(top_frame)

all_wave_frame.pack(side=tk.LEFT, padx=10)


all_wave_canvas = tk.Canvas(all_wave_frame, width=ALL_WAVE_W, height=ALL_WAVE_H, bg='lightyellow')

all_wave_canvas.pack()

draw_all_waveforms(all_wave_canvas)



# 2. 中間控制與輸入部分

control_frame = tk.Frame(root)

control_frame.pack(pady=10)


tk.Label(control_frame, text="輸入 3-bit 位元組 (000~111):", font=('Arial', 12)).pack(side=tk.LEFT, padx=5)


input_entry = tk.Entry(control_frame, width=5, font=('Arial', 12))

input_entry.pack(side=tk.LEFT, padx=5)


update_button = tk.Button(control_frame, text="顯示動態波形", command=update_waveform, font=('Arial', 12, 'bold'), bg='lightgreen')

update_button.pack(side=tk.LEFT, padx=10)


# 3. 底部動態波形圖部分

dynamic_wave_frame = tk.Frame(root)

dynamic_wave_frame.pack(pady=10)


dynamic_wave_canvas = tk.Canvas(dynamic_wave_frame, width=DYNAMIC_WAVE_W, height=DYNAMIC_WAVE_H, bg='white')

dynamic_wave_canvas.pack()

# 初始顯示 '111' 的波形

draw_single_waveform(dynamic_wave_canvas, '111') 


tk.Label(dynamic_wave_frame, text="單一符號動態波形圖 (2個週期)", font=('Arial', 14, 'bold')).pack()


# 執行 Tkinter 事件迴圈

root.mainloop()


沒有留言:

張貼留言

8-QAM Signal 4 Phases 2 Amplitudes + 8PSK

 8-QAM Signal 4 Phases 2 Amplitudes + 8PSK import tkinter as tk from tkinter import messagebox import math import cmath # --- 8-QAM 參數設定 ---...