This commit is contained in:
2026-02-09 21:28:10 +01:00
parent ee895f95a9
commit 0ac49902af
11 changed files with 29903 additions and 29329 deletions

View File

@@ -12,9 +12,20 @@ from itables.widget import ITable
@dataclass
class Zuverlaessigkeit:
"""Berechnung von Zuverlässigkeitsmaße zur Bewertung der erreichten Netzqualität.
Die Klasse stellt Methoden zur Verfügung für:
- Berechnung der Gesamtredundanz
- Aufbau der Redundanzmatrix
- Ableitung und Klassifikation der Redundanzanteile
- Durchführung des Globaltests zur Modellprüfung
- Lokaltest und Maße der inneren Zuverlässigkeit
- Berechnung von Kenngrößen der äußeren Zuverlässigkeit
"""
@staticmethod
def gesamtredundanz(n, u):
def gesamtredundanz(n: int, u: int) -> int:
"""
Berechnet die Gesamtredundanz des Netzes.
@@ -35,7 +46,7 @@ class Zuverlaessigkeit:
@staticmethod
def berechne_R(Q_vv, P):
def berechne_R(Q_vv: np.ndarray, P: np.ndarray) -> np.ndarray:
"""
Berechnet die Redundanzmatrix R aus Qvv und der Gewichtsmatrix P.
@@ -54,7 +65,7 @@ class Zuverlaessigkeit:
@staticmethod
def berechne_ri(R):
def berechne_ri(R: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""
Berechnet die Redundanzanteile einzelner Beobachtungen.
@@ -74,7 +85,7 @@ class Zuverlaessigkeit:
@staticmethod
def klassifiziere_ri(ri):
def klassifiziere_ri(ri: float) -> str:
"""
Klassifiziert einen Redundanzanteil rᵢ nach seiner Kontrollierbarkeit.
@@ -99,7 +110,7 @@ class Zuverlaessigkeit:
@staticmethod
def redundanzanteile_ri(Qvv, P, liste_beob):
def redundanzanteile_ri(Qvv: np.ndarray, P: np.ndarray, liste_beob: list[str]) -> tuple[np.ndarray, np.ndarray, np.ndarray, pd.DataFrame]:
"""
Berechnet und dokumentiert Redundanzanteile rᵢ und EVᵢ für alle Beobachtungen.
@@ -129,7 +140,7 @@ class Zuverlaessigkeit:
@staticmethod
def globaltest(r_gesamt, sigma0_apost, sigma0_apriori=1):
def globaltest(r_gesamt: int, sigma0_apost: float, sigma0_apriori: float = 1.0) -> dict[str, int | float | bool | str]:
"""
Führt den Globaltest zur Prüfung des Ausgleichungsmodells durch.
@@ -148,10 +159,8 @@ class Zuverlaessigkeit:
:param sigma0_apriori: a-priori Standardabweichung der Gewichtseinheit σ₀ (Standard=1).
:type sigma0_apriori: float
:return: Dictionary mit Testparametern, Testergebnis (H₀ angenommen/verworfen) und Interpretation.
:rtype: dict[str, Any]
:raises ValueError: Wenn alpha nicht in (0, 1) liegt oder nicht in float umgewandelt werden kann.
:rtype: dict[str, int | float | bool | str]
"""
alpha_input = input("Irrtumswahrscheinlichkeit α wählen (z.B. 0.05, 0.01) [Standard=0.001]: ").strip()
alpha = 0.001 if alpha_input == "" else float(alpha_input)
T_G = (sigma0_apost ** 2) / (sigma0_apriori ** 2)
@@ -193,40 +202,38 @@ class Zuverlaessigkeit:
}
def lokaltest_innere_Zuverlaessigkeit(v, Q_vv, ri, labels, s0_apost, alpha, beta):
def lokaltest_innere_Zuverlaessigkeit(v: np.ndarray, Q_vv: np.ndarray, ri: np.ndarray, labels: list, sigma0_apost: float, alpha: float, beta: float) -> pd.DataFrame:
"""
Führt den Lokaltest zur Grobfehlerdetektion je Beobachtung durch.
Auf Basis der Residuen v, der Kofaktor-Matrix der Residuen Qvv und der Redundanzanteile rᵢ
werden für jede Beobachtung statistische Kennwerte zur Detektion grober Fehler berechnet. Dazu zählen:
- Grobfehlerabschätzung: GFᵢ = vᵢ / rᵢ
- Standardabweichung der Residuen: s_vᵢ = s₀ · √q_vᵢ (mit q_vᵢ = diag(Qvv))
- Normierte Verbesserung: NVᵢ = |vᵢ| / s_vᵢ
- Nichtzentralitätsparameter: δ₀ = k + k_A
mit k aus dem zweiseitigen Normalquantil (α) und k_A aus der Testmacht (1β)
- Grenzwert der Aufdeckbarkeit (Minimal detektierbarer Grobfehler): GRZWᵢ = (s_vᵢ / rᵢ) · δ₀
- Grobfehlerabschätzung GFᵢ
- Standardabweichung der Residuen s_vᵢ
- Normierte Verbesserung NVᵢ
- Nichtzentralitätsparameter δ₀ mit k aus dem zweiseitigen Normalquantil (α) und k_A aus der Testmacht (1β)
- Grenzwert der Aufdeckbarkeit (Minimal detektierbarer Grobfehler) GRZWᵢ
Beobachtungen werden als auffällig markiert, wenn NVᵢ > δ₀. Für rᵢ = 0 wird die Grobfehlerabschätzung
und der Grenzwert als NaN gesetzt.
:param v: Residuenvektor der Beobachtungen.
:type v: np.asarray
:type v: np.ndarray
:param Q_vv: Kofaktor-Matrix der Residuen.
:type Q_vv: np.asarray
:type Q_vv: np.ndarray
:param ri: Redundanzanteile der Beobachtungen.
:type ri: np.asarray
:type ri: np.ndarray
:param labels: Liste der Beobachtungen zur Zuordnung in der Ergebnistabelle.
:type labels: list
:param s0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0_apost: float
:param sigma0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type sigma0_apost: float
:param alpha: Irrtumswahrscheinlichkeit α (Signifikanzniveau, zweiseitiger Test).
:type alpha: float
:param beta: Wahrscheinlichkeit β für einen Fehler 2. Art (Testmacht = 1β).
:type beta: float
:return: DataFrame mit NVᵢ, Auffälligkeit, Grobfehlerabschätzung GFᵢ und Grenzwert GRZWᵢ je Beobachtung.
:rtype: pandas.DataFrame
:raises ValueError: Wenn alpha oder beta nicht im Intervall (0, 1) liegen.
"""
v = np.asarray(v, float).reshape(-1)
Q_vv = np.asarray(Q_vv, float)
@@ -239,7 +246,7 @@ class Zuverlaessigkeit:
# Standardabweichungen der Residuen
qv = np.diag(Q_vv).astype(float)
s_vi = float(s0_apost) * np.sqrt(qv)
s_vi = float(sigma0_apost) * np.sqrt(qv)
# Normierte Verbesserung NV
NV = np.abs(v) / s_vi
@@ -274,7 +281,7 @@ class Zuverlaessigkeit:
return Lokaltest_innere_Zuv
def aufruf_lokaltest(liste_beob, alpha, ausgabe_parameterschaetzung, ri, s0_aposteriori):
def aufruf_lokaltest(liste_beob: list[str], alpha: float, ausgabe_parameterschaetzung: dict, ri: np.ndarray, sigma0_aposteriori: float) -> tuple[dict[str, bool], float]:
"""Startet den Lokaltest und erzeugt die interaktive Tabelle.
:param liste_beob: Liste der Beobachtungslabels.
@@ -284,9 +291,9 @@ class Zuverlaessigkeit:
:param ausgabe_parameterschaetzung: Dictionary mit den Ergebnissen der letzten Iteration der Parameterschätzung.
:type ausgabe_parameterschaetzung: dict
:param ri: Redundanz.
:type ri: Any
:param s0_aposteriori: a-posteriori Standardabweichung.
:type s0_aposteriori: float
:type ri: np.ndarray
:param sigma0_aposteriori: a-posteriori Standardabweichung.
:type sigma0_aposteriori: float
:return: ausschalten_dict
:rtype: dict
"""
@@ -305,7 +312,7 @@ class Zuverlaessigkeit:
Q_vv=ausgabe_parameterschaetzung["Q_vv"],
ri=ri,
labels=labels,
s0_apost=s0_aposteriori,
sigma0_apost=sigma0_aposteriori,
alpha=alpha,
beta=beta
)
@@ -345,36 +352,23 @@ class Zuverlaessigkeit:
def aeussere_zuverlaessigkeit(
Lokaltest, bezeichnung, Qxx, A, P, s0_apost, unbekannten_liste, x,
ausschliessen=("lA_",),
):
def aeussere_zuverlaessigkeit(Lokaltest: pd.DataFrame, bezeichnung: list, Qxx: np.ndarray, A: np.ndarray, P: np.ndarray, sigma0_apost: float, unbekannten_liste: list, x: np.ndarray, ausschliessen: tuple[str, ...] = ("lA_",)) -> pd.DataFrame:
"""
Berechnet Parameter der äußeren Zuverlässigkeit (EP/EF) je Beobachtung.
Auf Basis der Ergebnisse des Lokaltests werden für jede Beobachtung Maße der äußeren
Zuverlässigkeit bestimmt. Dazu zählen:
Auf Basis der Ergebnisse des Lokaltests werden für jede Beobachtung Maße der äußeren Zuverlässigkeit bestimmt. Dazu zählen:
- Einfluss auf die (relative) Punktlage EP:
- aus geschätzter Modellstörung: EP_GF,i = |(1 - r_i) · GF_i|
- aus Grenzwert der nicht mehr aufdeckbaren Modellstörung: EP_GRZW,i = |(1 - r_i) · GRZW_i|
Für Winkelbeobachtungen (R/ZW) wird EP in eine äquivalente Querabweichung (in m) umgerechnet: q = EP · s
wobei EP als Winkelstörung im Bogenmaß (rad) und s als räumliche Strecke zwischen Stand- und
Zielpunkt verwendet wird.
- Einflussfaktor / Netzverzerrung EF (Worst-Case-Einfluss einer nicht detektierten Störung):
Es wird eine Einzelstörung Δl_i = GRZW_i angesetzt (alle anderen Δl_j = 0) und in den
Unbekanntenraum übertragen: Δx = Q_xx · A^T · P · Δl
Der Einflussfaktor wird lokal (nur für die von der Beobachtung berührten Punktkoordinaten,
i.d.R. Stand- und Zielpunkt) über die gewichtete Norm berechnet: EF_i^2 = (Δx_loc^T · Q_loc^{-1} · Δx_loc) / s0^2
mit s0 = a posteriori Standardabweichung der Gewichtseinheit.
- Punktstreuungsmaß SP_3D und maximale Verfälschung EF·SP:
Für die berührten Punkte wird je Punkt der 3×3-Block aus Q_xx betrachtet: als Maß wird die maximale Spur
verwendet: SP_3D,loc = s0 · sqrt( max( tr(Q_P) ) )
und daraus: (EF·SP)_i = EF_i · SP_3D,loc
- Einfluss auf die Punktlage EP
- aus geschätzter Modellstörung EP_GF,i
- aus Grenzwert der nicht mehr aufdeckbaren Modellstörung EP_GRZW,i
- Für Richtungs- und Winkelbeobachtungen erfolgt eine Umrechnung
- Einflussfaktor EF
- Es wird eine Einzelstörung Δl_i = GRZW_i angesetzt und in den Unbekanntenraum übertragen
- Der Einflussfaktor wird lokal berechnet
- Punktstreuungsmaß SP_3D und maximale Verfälschung EF·SP
Pseudobeobachtungen (z.B. Lagerungs-/Anschlussgleichungen) können über Präfixe in
"ausschliessen" aus der Auswertung entfernt werden. Es wird geprüft, ob die Anzahl
der Bezeichnungen und die Zeilenanzahl des Lokaltests zur Beobachtungsanzahl von A passen.
"ausschliessen" aus der Auswertung entfernt werden.
:param Lokaltest: DataFrame des Lokaltests`.
:type Lokaltest: pandas.DataFrame
@@ -386,8 +380,8 @@ class Zuverlaessigkeit:
:type A: numpy.ndarray
:param P: Gewichtsmatrix der Beobachtungen.
:type P: numpy.ndarray
:param s0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0_apost: float
:param sigma0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type sigma0_apost: float
:param unbekannten_liste: Liste der Unbekannten.
:type unbekannten_liste: list
:param x: Unbekanntenvektor.
@@ -395,7 +389,7 @@ class Zuverlaessigkeit:
:param ausschliessen: Präfixe von Beobachtungsbezeichnungen, die aus der Auswertung entfernt werden sollen
(Standard: ("lA_",) für Lagerungs-/Pseudobeobachtungen).
:type ausschliessen: tuple
:return: DataFrame mit Stand/Zielpunkt, Redundanzanteil rᵢ, EP (aus GF und GRZW), EF sowie SP_3D und EF·SP_3D.
:return: DataFrame mit Stand/Zielpunkt, Redundanzanteil rᵢ, EP (aus GF und GRZW), EF, SP_3D und EF·SP_3D.
:rtype: pandas.DataFrame
:raises ValueError: Wenn die Anzahl der Bezeichnungen oder die Zeilenanzahl des Lokaltests nicht zu A passt.
"""
@@ -434,7 +428,7 @@ class Zuverlaessigkeit:
ri = lokaltest_daten["r_i"].astype(float).to_numpy()
GF = lokaltest_daten["GF_i"].astype(float).to_numpy()
GRZW = lokaltest_daten["GRZW_i"].astype(float).to_numpy()
s0 = float(s0_apost)
s0 = float(sigma0_apost)
# Punktkoordinaten
koordinaten = {}
@@ -576,7 +570,7 @@ class LokaltestInnereZuverlaessigkeitGUI:
einer Komponente (bx/by/bz) automatisch das gesamte Trio gewählt bzw. abgewählt wird.
"""
def __init__(self, df):
def __init__(self, df: pd.DataFrame) -> None:
"""Initialisiert die GUI-Objekte.
:param df: DataFrame des Lokaltests (inkl. Spalte "Beobachtung").
@@ -606,7 +600,7 @@ class LokaltestInnereZuverlaessigkeitGUI:
self.btn_auswahl_zuruecksetzen = widgets.Button(description="Rückgängig", icon="refresh")
@staticmethod
def gnss_komponenten_extrahieren(beobachtung: str):
def gnss_komponenten_extrahieren(beobachtung: str) -> tuple[str | None, str | None]:
"""Extrahiert GNSS-Komponente und einen eindeutigen Key für bx/by/bz-Trio.
:param beobachtung: Text aus Spalte "Beobachtung".