zusammenfügen
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user