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()