Python plotte program

Her er et eksempel på python kode

Det er fremstillet af Gemini . Det er nok en god ide at vide lidt om Python og at have importeret og installeret de biblioteker der er omtalt i starten af programmet.
Man kan installere THONNY til at køre Python programmet. Det er også den der er indbygget i Raspberry Pi hvis man bruger
raspberry pi os

import math
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
from tkinter import messagebox

# --- Hjælpefunktioner ---
def konverter_grader_til_radianer(grader):
    """Konverterer grader til radianer."""
    return math.radians(grader)

def konverter_radianer_til_grader(radianer):
    """Konverterer radianer til grader og normaliserer til 0-360."""
    grader = math.degrees(radianer)
    return (grader + 360) % 360

# --- CPA/TCPA Beregningsfunktion ---
def beregn_cpa_tcpa(relativ_fart_knob, relativ_kurs_grader, relativ_position_x_nm, relativ_position_y_nm):
    """
    Beregner CPA (Closest Point of Approach) og TCPA (Time to Closest Point of Approach).
    """
    relativ_kurs_rad = konverter_grader_til_radianer(relativ_kurs_grader)

    rel_vx = relativ_fart_knob * math.sin(relativ_kurs_rad)
    rel_vy = relativ_fart_knob * math.cos(relativ_kurs_rad)

    if relativ_fart_knob == 0:
        cpa = math.sqrt(relativ_position_x_nm**2 + relativ_position_y_nm**2)
        return round(cpa, 2), float('inf')

    tcpa_timer = -(relativ_position_x_nm * rel_vx + relativ_position_y_nm * rel_vy) / (rel_vx**2 + rel_vy**2)

    cpa_x = relativ_position_x_nm + rel_vx * tcpa_timer
    cpa_y = relativ_position_y_nm + rel_vy * tcpa_timer

    cpa_nm = math.sqrt(cpa_x**2 + cpa_y**2)

    return round(cpa_nm, 2), round(tcpa_timer, 2)


# --- Hovedberegningsfunktion ---
def beregn_skib_data(plots, eget_kurs_grader, eget_fart):
    """
    Beregner et andet skibs relative og sande kurs og fart, samt CPA og TCPA.
    """
    if len(plots) < 2:
        return "Fejl: Der skal bruges mindst 2 plots for at udføre beregningen.", None

    plots_sorteret = sorted(plots, key=lambda p: p['tid'])
    plot1 = plots_sorteret[0]
    plot2 = plots_sorteret[1]
    
    tid1 = plot1['tid']
    leje1_rad = konverter_grader_til_radianer(plot1['leje'])
    afstand1 = plot1['afstand']
    tid2 = plot2['tid']
    leje2_rad = konverter_grader_til_radianer(plot2['leje'])
    afstand2 = plot2['afstand']
    
    x1_rel = afstand1 * math.sin(leje1_rad)
    y1_rel = afstand1 * math.cos(leje1_rad)
    x2_rel = afstand2 * math.sin(leje2_rad)
    y2_rel = afstand2 * math.cos(leje2_rad)

    delta_x_rel = x2_rel - x1_rel
    delta_y_rel = y2_rel - y1_rel
    delta_tid_minutter = tid2 - tid1
    
    if delta_tid_minutter == 0:
        return "Fejl: Tidsintervallet mellem plots er nul. Kan ikke beregne fart.", None
    
    delta_tid_timer = delta_tid_minutter / 60.0

    relativ_fart = math.sqrt(delta_x_rel**2 + delta_y_rel**2) / delta_tid_timer
    relativ_kurs_rad = math.atan2(delta_x_rel, delta_y_rel)
    relativ_kurs_grader = konverter_radianer_til_grader(relativ_kurs_rad)

    eget_kurs_rad = konverter_grader_til_radianer(eget_kurs_grader)
    eget_vx = eget_fart * math.sin(eget_kurs_rad)
    eget_vy = eget_fart * math.cos(eget_kurs_rad)

    relativ_vx = relativ_fart * math.sin(relativ_kurs_rad)
    relativ_vy = relativ_fart * math.cos(relativ_kurs_rad)

    andet_vx = relativ_vx + eget_vx
    andet_vy = relativ_vy + eget_vy

    sog = math.sqrt(andet_vx**2 + andet_vy**2)
    cow_rad = math.atan2(andet_vx, andet_vy)
    cow_grader = konverter_radianer_til_grader(cow_rad)

    cpa, tcpa_timer = beregn_cpa_tcpa(relativ_fart, relativ_kurs_grader, x1_rel, y1_rel)
    tcpa_minutter = tcpa_timer * 60 if tcpa_timer != float('inf') else float('inf')

    resultater = {
        "relativ_kurs": round(relativ_kurs_grader, 2),
        "relativ_fart": round(relativ_fart, 2),
        "sog": round(sog, 2),
        "cow": round(cow_grader, 2),
        "cpa": cpa,
        "tcpa_minutter": tcpa_minutter,
        "plots_kartesisk": [(x1_rel, y1_rel), (x2_rel, y2_rel)],
        "relativ_hastighedsvektor_komponenter": (relativ_vx, relativ_vy),
        "sand_hastighedsvektor_komponenter": (andet_vx, andet_vy),
    }

    output_str = f"--- Beregningsresultater ---\n"
    output_str += f"Relativ kurs: {resultater['relativ_kurs']} grader\n"
    output_str += f"Relativ fart: {resultater['relativ_fart']} knob\n"
    output_str += f"SOG (Speed Over Ground): {resultater['sog']} knob\n"
    output_str += f"COW (Course Over Water): {resultater['cow']} grader\n\n"
    
    output_str += "--- Kollisionsrisiko (CPA/TCPA) ---\n"
    if resultater['tcpa_minutter'] == float('inf'):
        output_str += f"CPA: {resultater['cpa']} NM (Ingen relativ bevægelse)\n"
        output_str += "TCPA: Uendelig\n"
    elif resultater['tcpa_minutter'] < 0:
        output_str += f"CPA: {resultater['cpa']} NM\n"
        output_str += f"TCPA: {abs(resultater['tcpa_minutter']):.1f} minutter siden (passeret)\n"
    else:
        output_str += f"CPA: {resultater['cpa']} NM\n"
        output_str += f"TCPA: {resultater['tcpa_minutter']:.1f} minutter\n"
        
    return output_str, resultater


# --- Grafisk fremstillingsfunktion (polar plot) ---
def tegn_plot(data):
    """
    Tegner en grafisk fremstilling af plottene som en 360-graders cirkel med fast skala.
    """
    plots_kartesisk = data['plots_kartesisk']
    cpa = data['cpa']
    tcpa_minutter = data['tcpa_minutter']
    relativ_hastighedsvektor_komponenter = data['relativ_hastighedsvektor_komponenter']
    sand_hastighedsvektor_komponenter = data['sand_hastighedsvektor_komponenter']
    
    fig = plt.figure(figsize=(8, 8))
    ax = fig.add_subplot(111, polar=True)
    
    ax.set_theta_zero_location('N')
    ax.set_theta_direction(-1)
    
    max_afstand = 20.0
    ax.set_ylim(0, max_afstand)
    
    ax.set_rgrids([5, 10, 15, 20], labels=['5 NM', '10 NM', '15 NM', '20 NM'])
    ax.set_xticks(np.deg2rad(np.arange(0, 360, 30)))
    ax.set_xticklabels([f'{i}°' for i in range(0, 360, 30)])
    
    ax.plot(0, 0, 'o', color='blue', markersize=10, label='Dit Skib (Eget)', zorder=5)

    plots_rad = [math.atan2(p[0], p[1]) for p in plots_kartesisk]
    plots_afst = [math.sqrt(p[0]**2 + p[1]**2) for p in plots_kartesisk]
    
    ax.plot(plots_rad, plots_afst, 'ro-', markersize=8, label='Andet Skibs Plots', zorder=4)
    ax.text(plots_rad[0], plots_afst[0] * 1.1, 'Plot 1', horizontalalignment='center', color='red', fontsize=10)
    if len(plots_kartesisk) > 1:
        ax.text(plots_rad[1], plots_afst[1] * 1.1, 'Plot 2', horizontalalignment='center', color='red', fontsize=10)

    rel_vx, rel_vy = relativ_hastighedsvektor_komponenter
    rel_kurs_rad_line = [plots_rad[0], plots_rad[1]]
    rel_kurs_afst_line = [plots_afst[0], plots_afst[1]]
    ax.plot(rel_kurs_rad_line, rel_kurs_afst_line, 'g--', linewidth=1.5, label='Relativ Kurslinje', zorder=3)
    
    rel_kurs_rad = math.atan2(plots_kartesisk[1][0] - plots_kartesisk[0][0], plots_kartesisk[1][1] - plots_kartesisk[0][1])
    rel_kurs_afst_mid = (plots_afst[0] + plots_afst[1]) / 2.0
    ax.arrow(rel_kurs_rad, rel_kurs_afst_mid, 0, 0.001, head_width=0.08, head_length=0.5, fc='green', ec='green')
    ax.text(rel_kurs_rad, plots_afst[1] + 2, 'Relativ Kurs', color='green', horizontalalignment='center')

    sand_vx, sand_vy = sand_hastighedsvektor_komponenter
    ax.arrow(math.atan2(sand_vx, sand_vy), plots_afst[0], 0, 1, head_width=0.08, head_length=1.0, fc='orange', ec='orange', label='Andet Skibs Sande Kurs (COW)', zorder=3)
    ax.text(math.atan2(sand_vx, sand_vy), plots_afst[0] + 3.5, 'Sand Kurs', color='orange', horizontalalignment='center')

    if cpa is not None and not math.isinf(tcpa_minutter) and tcpa_minutter >= 0:
        tcpa_timer = tcpa_minutter / 60.0
        cpa_x = plots_kartesisk[0][0] + rel_vx * tcpa_timer
        cpa_y = plots_kartesisk[0][1] + rel_vy * tcpa_timer
        
        cpa_rad = math.atan2(cpa_x, cpa_y)
        cpa_afst = math.sqrt(cpa_x**2 + cpa_y**2)
        
        ax.plot(cpa_rad, cpa_afst, 'x', color='purple', markersize=12, mew=2, label='CPA Punkt', zorder=6)
        ax.text(cpa_rad, cpa_afst, f'CPA: {cpa} NM', horizontalalignment='center', color='purple', verticalalignment='bottom')
    
    ax.set_title("360° Relativ Positionsplot", va='bottom')
    ax.legend(loc='lower left', bbox_to_anchor=(0.9, 0.9))
    plt.show()

class PlotterApp:
    def __init__(self, master):
        self.master = master
        master.title("Kollisionsplotter")

        self.beregningsresultater = None

        self.create_widgets()

    def create_widgets(self):
        own_ship_frame = tk.LabelFrame(self.master, text="Eget Skib")
        own_ship_frame.pack(padx=10, pady=5, fill="x")

        tk.Label(own_ship_frame, text="Kurs (grader):").pack(side="left", padx=5)
        self.own_course_entry = tk.Entry(own_ship_frame, width=10)
        self.own_course_entry.pack(side="left", padx=5)

        tk.Label(own_ship_frame, text="Fart (knob):").pack(side="left", padx=5)
        self.own_speed_entry = tk.Entry(own_ship_frame, width=10)
        self.own_speed_entry.pack(side="left", padx=5)

        plots_frame = tk.LabelFrame(self.master, text="Andet Skibs Plots")
        plots_frame.pack(padx=10, pady=5, fill="x")

        plot1_frame = tk.Frame(plots_frame)
        plot1_frame.pack(padx=5, pady=2, fill="x")
        tk.Label(plot1_frame, text="Plot 1: Tid (min):").pack(side="left")
        self.p1_time_entry = tk.Entry(plot1_frame, width=5)
        self.p1_time_entry.pack(side="left")
        tk.Label(plot1_frame, text="Pejling (gr):").pack(side="left", padx=5)
        self.p1_bearing_entry = tk.Entry(plot1_frame, width=5)
        self.p1_bearing_entry.pack(side="left")
        tk.Label(plot1_frame, text="Afst (NM):").pack(side="left", padx=5)
        self.p1_dist_entry = tk.Entry(plot1_frame, width=5)
        self.p1_dist_entry.pack(side="left")
        
        plot2_frame = tk.Frame(plots_frame)
        plot2_frame.pack(padx=5, pady=2, fill="x")
        tk.Label(plot2_frame, text="Plot 2: Tid (min):").pack(side="left")
        self.p2_time_entry = tk.Entry(plot2_frame, width=5)
        self.p2_time_entry.pack(side="left")
        tk.Label(plot2_frame, text="Pejling (gr):").pack(side="left", padx=5)
        self.p2_bearing_entry = tk.Entry(plot2_frame, width=5)
        self.p2_bearing_entry.pack(side="left")
        tk.Label(plot2_frame, text="Afst (NM):").pack(side="left", padx=5)
        self.p2_dist_entry = tk.Entry(plot2_frame, width=5)
        self.p2_dist_entry.pack(side="left")

        button_frame = tk.Frame(self.master)
        button_frame.pack(pady=10)
        tk.Button(button_frame, text="Beregn", command=self.run_calculation).pack(side="left", padx=5)
        tk.Button(button_frame, text="Tegn Plot", command=self.draw_plot).pack(side="left", padx=5)

        output_frame = tk.LabelFrame(self.master, text="Resultater")
        output_frame.pack(padx=10, pady=5, fill="both", expand=True)
        self.output_text = tk.Text(output_frame, height=15, width=50)
        self.output_text.pack(padx=5, pady=5, fill="both", expand=True)

    def run_calculation(self):
        try:
            eget_kurs = float(self.own_course_entry.get())
            eget_fart = float(self.own_speed_entry.get())

            plots = [
                {'tid': float(self.p1_time_entry.get()), 'leje': float(self.p1_bearing_entry.get()), 'afstand': float(self.p1_dist_entry.get())},
                {'tid': float(self.p2_time_entry.get()), 'leje': float(self.p2_bearing_entry.get()), 'afstand': float(self.p2_dist_entry.get())}
            ]

            output_str, self.beregningsresultater = beregn_skib_data(plots, eget_kurs, eget_fart)
            
            self.output_text.delete(1.0, tk.END)
            self.output_text.insert(tk.END, output_str)
            
            if "Fejl" in output_str:
                self.beregningsresultater = None
                
        except ValueError:
            messagebox.showerror("Fejl", "Ugyldigt input. Sørg for at indtaste tal i alle felter.")
            self.beregningsresultater = None
        except Exception as e:
            messagebox.showerror("Fejl", f"Der opstod en uventet fejl: {e}")
            self.beregningsresultater = None
            
    def draw_plot(self):
        if self.beregningsresultater:
            tegn_plot(self.beregningsresultater)
        else:
            messagebox.showwarning("Advarsel", "Udfør beregningen først.")

root = tk.Tk()
app = PlotterApp(root)
root.mainloop()