195 lines
6.2 KiB
Python
195 lines
6.2 KiB
Python
import numpy as np
|
||
import plotly.graph_objects as go
|
||
from scipy.stats import f as f_dist
|
||
import pandas as pd
|
||
|
||
|
||
class Genauigkeitsmaße:
|
||
|
||
|
||
@staticmethod
|
||
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 helmert_punktfehler(Qxx, s0_apost, unbekannten_liste, dim=3):
|
||
diagQ = np.diag(Qxx)
|
||
daten = []
|
||
|
||
n_punkte = len(unbekannten_liste) // 3
|
||
|
||
for i in range(n_punkte):
|
||
sym_x = str(unbekannten_liste[3 * i]) # z.B. "X10009"
|
||
punkt = sym_x[1:] # -> "10009"
|
||
|
||
qx = diagQ[3 * i]
|
||
qy = diagQ[3 * i + 1]
|
||
qz = diagQ[3 * i + 2]
|
||
|
||
sx = s0_apost * np.sqrt(qx)
|
||
sy = s0_apost * np.sqrt(qy)
|
||
sz = s0_apost * np.sqrt(qz)
|
||
|
||
if dim == 2:
|
||
sP = s0_apost * np.sqrt(qx + qy)
|
||
else:
|
||
sP = s0_apost * np.sqrt(qx + qy + qz)
|
||
|
||
daten.append([
|
||
punkt,
|
||
float(sx),
|
||
float(sy),
|
||
float(sz),
|
||
float(sP)
|
||
])
|
||
helmert_punktfehler = pd.DataFrame(daten, columns=["Punkt", "σx", "σy", "σz", f"σP_{dim}D"])
|
||
return helmert_punktfehler
|
||
|
||
|
||
|
||
def standardellipse(Qxx, s0_apost, unbekannten_liste, dim_labels=3):
|
||
Qxx = np.asarray(Qxx, float)
|
||
data = []
|
||
|
||
n_punkte = len(unbekannten_liste) // dim_labels
|
||
|
||
for i in range(n_punkte):
|
||
sym_x = str(unbekannten_liste[dim_labels * i]) # z.B. "X10009"
|
||
punkt = sym_x[1:] # -> "10009"
|
||
|
||
ix = dim_labels * i
|
||
iy = dim_labels * i + 1
|
||
|
||
# 2x2-Kofaktorblock
|
||
Qxx_ = Qxx[ix, ix]
|
||
Qyy_ = Qxx[iy, iy]
|
||
Qyx_ = Qxx[iy, ix]
|
||
|
||
# Standardabweichungen der Koordinatenkomponenten
|
||
sx = s0_apost * np.sqrt(Qxx_)
|
||
sy = s0_apost * np.sqrt(Qyy_)
|
||
sxy = (s0_apost ** 2) * Qyx_
|
||
|
||
# k und Eigenwerte (Q_dmax, Q_dmin)
|
||
k = np.sqrt((Qxx_ - Qyy_) ** 2 + 4 * (Qyx_ ** 2))
|
||
Q_dmax = 0.5 * (Qxx_ + Qyy_ + k)
|
||
Q_dmin = 0.5 * (Qxx_ + Qyy_ - k)
|
||
|
||
# Halbachsen (Standardabweichungen entlang Hauptachsen)
|
||
s_max = s0_apost * np.sqrt(Q_dmax)
|
||
s_min = s0_apost * np.sqrt(Q_dmin)
|
||
|
||
# Richtungswinkel theta (Hauptachse) in rad:
|
||
theta_rad = 0.5 * np.arctan2(2 * Qyx_, (Qxx_ - Qyy_))
|
||
|
||
# in gon
|
||
theta_gon = theta_rad * (200 / np.pi)
|
||
if theta_gon < 0:
|
||
theta_gon += 200.0
|
||
|
||
data.append([
|
||
punkt,
|
||
float(sx), float(sy), float(sxy),
|
||
float(s_max), float(s_min),
|
||
float(theta_gon)
|
||
])
|
||
|
||
standardellipse = pd.DataFrame(data, columns=["Punkt", "σx", "σy", "σxy", "s_max", "s_min", "θ [gon]"])
|
||
return standardellipse
|
||
|
||
|
||
|
||
def konfidenzellipse(Qxx, s0_apost, unbekannten_liste, R, alpha=0.05):
|
||
Qxx = np.asarray(Qxx, float)
|
||
|
||
data = []
|
||
n_punkte = len(unbekannten_liste) // 3 # X,Y,Z je Punkt angenommen
|
||
|
||
k = float(np.sqrt(2.0 * f_dist.ppf(1.0 - alpha, 2, R)))
|
||
|
||
for i in range(n_punkte):
|
||
punkt = str(unbekannten_liste[3 * i])[1:] # "X10009" -> "10009"
|
||
|
||
ix = 3 * i
|
||
iy = 3 * i + 1
|
||
|
||
Qxx_ = Qxx[ix, ix]
|
||
Qyy_ = Qxx[iy, iy]
|
||
Qxy_ = Qxx[iy, ix] # = Qyx
|
||
|
||
# k für Eigenwerte
|
||
kk = np.sqrt((Qxx_ - Qyy_) ** 2 + 4 * (Qxy_ ** 2))
|
||
Q_dmax = 0.5 * (Qxx_ + Qyy_ + kk)
|
||
Q_dmin = 0.5 * (Qxx_ + Qyy_ - kk)
|
||
|
||
# Standard-Halbachsen (1-sigma)
|
||
s_max = s0_apost * np.sqrt(Q_dmax)
|
||
s_min = s0_apost * np.sqrt(Q_dmin)
|
||
|
||
# Orientierung (Hauptachse) in gon
|
||
theta_rad = 0.5 * np.arctan2(2 * Qxy_, (Qxx_ - Qyy_))
|
||
theta_gon = theta_rad * (200 / np.pi)
|
||
if theta_gon < 0:
|
||
theta_gon += 200.0
|
||
|
||
# Konfidenz-Halbachsen
|
||
a_K = k * s_max
|
||
b_K = k * s_min
|
||
|
||
data.append([punkt, float(a_K), float(b_K), float(theta_gon)])
|
||
|
||
konfidenzellipsen = pd.DataFrame(data, columns=["Punkt", "a_K", "b_K", "θ [gon]"])
|
||
return konfidenzellipsen
|
||
|
||
|
||
|
||
@staticmethod
|
||
def plot_ellipsen(punkt_coords: dict, ellipsen_parameter: list, scale: float = 1000):
|
||
fig = go.Figure()
|
||
|
||
# Titel dynamisch anpassen
|
||
prob = ellipsen_parameter[0].get('prob', 0)
|
||
titel = "Standard-Fehlerellipsen" if prob < 0.4 else f"{prob * 100:.0f}% Konfidenzellipsen"
|
||
|
||
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']
|
||
|
||
t = np.linspace(0, 2 * np.pi, 100)
|
||
xs, ys = a * scale * np.cos(t), b * scale * np.sin(t)
|
||
|
||
x_plot = x0 + xs * np.cos(theta) - ys * np.sin(theta)
|
||
y_plot = y0 + xs * np.sin(theta) + ys * np.cos(theta)
|
||
|
||
# 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}°")
|
||
))
|
||
|
||
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 |