zusammenfügen

This commit is contained in:
2026-01-07 13:51:17 +01:00
parent 4db7f1c3cc
commit dd447e59e1
16 changed files with 18867 additions and 17080 deletions

View File

@@ -1,168 +1,113 @@
from dataclasses import dataclass
from typing import Sequence, List, Dict
import sympy as sp
import numpy as np
import math
from decimal import Decimal
import matplotlib.pyplot as plt
import plotly.graph_objects as go
from scipy.stats import f as f_dist
@dataclass
class Genauigkeitsmaße:
def __init__(self):
pass
@staticmethod
def s0apost(v, P, r):
vv = (v.T * P * v)[0, 0]
vv = float(vv) # Sympy -> float
if r <= 0:
raise ValueError(f"Redundanz r muss > 0 sein, ist {r}.")
if not math.isfinite(vv):
raise ValueError(f"vv ist nicht endlich (NaN/Inf). vv={vv}")
if vv < 0:
raise ValueError(f"vv ist negativ. vv={vv}")
return math.sqrt(vv / float(r))
def berechne_s0apost(v: np.ndarray, P: np.ndarray, r: int) -> float:
vTPv_matrix = v.T @ P @ v
vTPv = float(vTPv_matrix.item())
s0apost = np.sqrt(vTPv / r)
return float(s0apost)
def helmertscher_punktfehler_3D(self, sigma_x: float, sigma_y: float, sigma_z: float) -> float:
sx = sp.sympify(sigma_x)
sy = sp.sympify(sigma_y)
sz = sp.sympify(sigma_z)
helmert_pf_3D = sp.sqrt(sx**2 + sy**2 + sz**2)
return float(helmert_pf_3D)
@staticmethod
def berechne_helmert_punktfehler_3D(Qxx_matrix: np.ndarray, s0apost: float, punkt_namen: list) -> dict:
helmert_punktfehler_ergebnisse_3D = {}
diag_Q = np.diag(Qxx_matrix)
if len(diag_Q) < len(punkt_namen) * 3:
raise ValueError("Die Matrix Qxx ist zu klein für die Anzahl der Punkte (3D erwartet).")
for i, name in enumerate(punkt_namen):
idx_x, idx_y, idx_z = 3 * i, 3 * i + 1, 3 * i + 2
q_xx, q_yy, q_zz = diag_Q[idx_x], diag_Q[idx_y], diag_Q[idx_z]
helmert_punktfehler_3D = s0apost * np.sqrt(q_xx + q_yy + q_zz)
helmert_punktfehler_ergebnisse_3D[name] = round(float(helmert_punktfehler_3D), 4)
return helmert_punktfehler_ergebnisse_3D
def helmertscher_punktfehler_3D_alle(
self,
sigma_x_list: Sequence[float],
sigma_y_list: Sequence[float],
sigma_z_list: Sequence[float],
) -> List[float]:
if not (
len(sigma_x_list) == len(sigma_y_list) == len(sigma_z_list)
):
raise ValueError("Listen sigma_x, sigma_y, sigma_z müssen gleich lang sein.")
return [
self.helmertscher_punktfehler_3D(sx, sy, sz)
for sx, sy, sz in zip(sigma_x_list, sigma_y_list, sigma_z_list)
]
@staticmethod
def berechne_standardellipsen(Qxx: np.ndarray, s0: float, punkt_namen: list):
standardellipsen = []
for i, name in enumerate(punkt_namen):
ix, iy = 3 * i, 3 * i + 1
qxx, qyy, qxy = Qxx[ix, ix], Qxx[iy, iy], Qxx[ix, iy]
k = np.sqrt((qxx - qyy) ** 2 + 4 * qxy ** 2)
qa, qb = 0.5 * (qxx + qyy + k), 0.5 * (qxx + qyy - k)
a, b = s0 * np.sqrt(qa), s0 * np.sqrt(qb)
theta = 0.5 * np.arctan2(2 * qxy, qxx - qyy)
standardellipsen.append({
"name": name, "a": a, "b": b, "theta": theta, "prob": 0.39 # Standard ca. 39%
})
return standardellipsen
def fehlerellipse_standard_2D(
self,
Q_xx: float,
Q_yy: float,
Q_xy: float,
sigma_0: float,
) -> Dict[str, float]:
@staticmethod
def berechne_konfidenzellipsen(Qxx: np.ndarray, s0: float, r: int, punkt_namen: list,
wahrscheinlichkeit: float = 0.95):
# Quantil der F-Verteilung (df1=2 für die Ebene, df2=r für Redundanz)
f_quantil = f_dist.ppf(wahrscheinlichkeit, 2, r)
k_faktor = np.sqrt(2 * f_quantil)
Q_xx_s = sp.sympify(Q_xx)
Q_yy_s = sp.sympify(Q_yy)
Q_xy_s = sp.sympify(Q_xy)
sigma0_s = sp.sympify(sigma_0)
k = sp.sqrt((Q_xx_s - Q_yy_s)**2 + 4 * Q_xy_s**2)
Q_dmax = sp.Rational(1, 2) * (Q_xx_s + Q_yy_s + k)
Q_dmin = sp.Rational(1, 2) * (Q_xx_s + Q_yy_s - k)
s_max = sigma0_s * sp.sqrt(Q_dmax)
s_min = sigma0_s * sp.sqrt(Q_dmin)
theta_rad = sp.Rational(1, 2) * sp.atan2(2 * Q_xy_s, Q_xx_s - Q_yy_s)
theta_gon = theta_rad * 200 / sp.pi
return {
"Q_dmax": float(Q_dmax),
"Q_dmin": float(Q_dmin),
"s_max": float(s_max),
"s_min": float(s_min),
"theta_rad": float(theta_rad),
"theta_gon": float(theta_gon),
}
standard_ellipsen = Genauigkeitsmaße.berechne_standardellipsen(Qxx, s0, punkt_namen)
konfidenz_ellipsen = []
for ell in standard_ellipsen:
konfidenz_ellipsen.append({
"name": ell['name'],
"a": ell['a'] * k_faktor,
"b": ell['b'] * k_faktor,
"theta": ell['theta'],
"prob": wahrscheinlichkeit,
"k_faktor": k_faktor
})
return konfidenz_ellipsen
@staticmethod
def plot_ellipsen(punkt_coords: dict, ellipsen_parameter: list, scale: float = 1000):
fig = go.Figure()
def fehlerellipse_konfidenz_2D(
self,
s_max: float,
s_min: float,
scale_value: float,
) -> Dict[str, float]:
# Titel dynamisch anpassen
prob = ellipsen_parameter[0].get('prob', 0)
titel = "Standard-Fehlerellipsen" if prob < 0.4 else f"{prob * 100:.0f}% Konfidenzellipsen"
s_max_s = sp.sympify(s_max)
s_min_s = sp.sympify(s_min)
scale_s = sp.sympify(scale_value)
for p in ellipsen_parameter:
name = p['name']
x0, y0 = punkt_coords[name][0], punkt_coords[name][1]
a, b, theta = p['a'], p['b'], p['theta']
faktor = sp.sqrt(scale_s)
t = np.linspace(0, 2 * np.pi, 100)
xs, ys = a * scale * np.cos(t), b * scale * np.sin(t)
A_K = faktor * s_max_s
B_K = faktor * s_min_s
x_plot = x0 + xs * np.cos(theta) - ys * np.sin(theta)
y_plot = y0 + xs * np.sin(theta) + ys * np.cos(theta)
return {
"A_K": float(A_K),
"B_K": float(B_K),
}
# Punkt
fig.add_trace(go.Scatter(
x=[x0], y=[y0], mode='markers+text',
name=f"Punkt {name}", text=[name], textposition="top right",
marker=dict(size=8, color='black')
))
# Ellipse
fig.add_trace(go.Scatter(
x=x_plot, y=y_plot, mode='lines',
name=f"Ellipse {name}",
line=dict(color='blue' if prob > 0.4 else 'red', width=2,
dash='dash' if prob > 0.4 else 'solid'),
fill='toself',
fillcolor='rgba(0, 0, 255, 0.1)' if prob > 0.4 else 'rgba(255, 0, 0, 0.1)',
hoverinfo='text',
text=(f"Punkt: {name}<br>a: {a * 1000:.2f} mm<br>"
f"b: {b * 1000:.2f} mm<br>Theta: {np.degrees(theta):.2f}°")
))
def plot_ellipsen_alle(
self,
x_list: Sequence[float],
y_list: Sequence[float],
Q_xx_list: Sequence[float],
Q_yy_list: Sequence[float],
Q_xy_list: Sequence[float],
sigma_0: float,
scale_value: float | None = None, # None = Standardellipse, sonst Konfidenzellipse
) -> plt.Axes:
n = len(x_list)
if not (
n == len(y_list) == len(Q_xx_list) == len(Q_yy_list) == len(Q_xy_list)
):
raise ValueError("Alle Listen müssen gleich lang sein (ein Eintrag pro Punkt).")
fig, ax = plt.subplots()
for i in range(n):
std = self.fehlerellipse_standard_2D(
Q_xx_list[i], Q_yy_list[i], Q_xy_list[i], sigma_0
)
s_max = std["s_max"]
s_min = std["s_min"]
theta = std["theta_rad"]
# ggf. Konfidenzellipse statt Standardellipse
if scale_value is not None:
konf = self.fehlerellipse_konfidenz_2D(s_max, s_min, scale_value)
A = konf["A_K"]
B = konf["B_K"]
else:
A = s_max
B = s_min
t = np.linspace(0, 2 * np.pi, 200)
ct = np.cos(theta)
st = np.sin(theta)
x_local = A * np.cos(t)
y_local = B * np.sin(t)
x_ell = x_list[i] + ct * x_local - st * y_local
y_ell = y_list[i] + st * x_local + ct * y_local
ax.plot(x_ell, y_ell, linewidth=0.8)
ax.plot(x_list[i], y_list[i], marker=".", color="k", markersize=3)
ax.set_aspect("equal", "box")
ax.grid(True)
ax.set_xlabel("x")
ax.set_ylabel("y")
return ax
fig.update_layout(
title=titel,
xaxis_title="Rechtswert (E) [m]", yaxis_title="Hochwert (N) [m]",
yaxis=dict(scaleanchor="x", scaleratio=1),
template="plotly_white", showlegend=True
)
return fig