Files
Masterprojekt_V3/Netzqualität_Zuverlässigkeit.py
2026-01-19 14:48:24 +01:00

217 lines
6.8 KiB
Python

from dataclasses import dataclass
import numpy as np
from scipy import stats
from scipy.stats import norm
import pandas as pd
@dataclass
class Zuverlaessigkeit:
def gesamtredundanz(n, u):
r = n - u
return r
def berechne_R(Q_vv, P):
R = Q_vv @ P
return R #Redundanzmatrix
def berechne_ri(R):
ri = np.diag(R)
EVi = 100.0 * ri
return ri, EVi #Redundanzanteile
def klassifiziere_ri(ri): #Klassifizierung der Redundanzanteile
if ri < 0.01:
return "nicht kontrollierbar"
elif ri < 0.10:
return "schlecht kontrollierbar"
elif ri < 0.30:
return "ausreichend kontrollierbar"
elif ri < 0.70:
return "gut kontrollierbar"
else:
return "nahezu vollständig redundant"
def globaltest(r_gesamt, sigma0_apost, sigma0_apriori, alpha):
T_G = (sigma0_apost ** 2) / (sigma0_apriori ** 2)
F_krit = stats.f.ppf(1 - alpha, r_gesamt, 10 ** 9)
H0 = T_G < F_krit
if H0:
interpretation = (
"Nullhypothese H₀ angenommen.\n"
)
else:
interpretation = (
"Nullhypothese H₀ verworfen!\n"
"Dies kann folgende Gründe haben:\n"
"→ Es befinden sich grobe Fehler im Datenmaterial. Bitte Lokaltest durchführen und ggf. grobe Fehler im Datenmaterial entfernen.\n"
"→ Das stochastische Modell ist zu optimistisch. Bitte Gewichte überprüfen und ggf. anpassen."
)
return {
"r_gesamt": r_gesamt,
"sigma0_apost": sigma0_apost,
"sigma0_apriori": sigma0_apriori,
"alpha": alpha,
"T_G": T_G,
"F_krit": F_krit,
"H0_angenommen": H0,
"Interpretation": interpretation,
}
def lokaltest_innere_Zuverlaessigkeit(v, Q_vv, ri, labels, s0_apost, alpha, beta):
v = np.asarray(v, float).reshape(-1)
Q_vv = np.asarray(Q_vv, float)
ri = np.asarray(ri, float).reshape(-1)
labels = list(labels)
# Grobfehlerabschätzung:
ri_ = np.where(ri == 0, np.nan, ri)
GF = -v / ri_
# Standardabweichungen der Residuen
qv = np.diag(Q_vv).astype(float)
s_vi = float(s0_apost) * np.sqrt(qv)
# Normierte Verbesserung NV
NV = np.abs(v) / s_vi
# Quantile k und kA (zweiseitig),
k = float(norm.ppf(1 - alpha / 2))
kA = float(norm.ppf(1 - beta)) # (Testmacht 1-β)
# Nichtzentralitätsparameter δ0
nzp = k + kA
# Grenzwert für die Aufdeckbarkeit eines GF (GRZW)
GRZW_i = (s_vi / ri_) * nzp
auffaellig = NV > nzp
Lokaltest_innere_Zuv = pd.DataFrame({
"Beobachtung": labels,
"v_i": v,
"r_i": ri,
"s_vi": s_vi,
"k": k,
"NV_i": NV,
"auffaellig": auffaellig,
"GF_i": GF,
"GRZW_i": GRZW_i,
"alpha": alpha,
"beta": beta,
"kA": kA,
"δ0": nzp,
})
return Lokaltest_innere_Zuv
def aeussere_zuverlaessigkeit_EF_EP(Lokaltest, labels, Qxx, A, P, s0_apost, unbekannten_liste, x):
df = Lokaltest.copy()
labels = list(labels)
Qxx = np.asarray(Qxx, float)
A = np.asarray(A, float)
P = np.asarray(P, float)
x = np.asarray(x, float).reshape(-1)
ri = df["r_i"].astype(float).to_numpy()
GF = df["GF_i"].astype(float).to_numpy()
s_vi = df["s_vi"].astype(float).to_numpy()
GRZW = df["GRZW_i"].astype(float).to_numpy()
nzp = df["δ0"].astype(float).to_numpy()
n = A.shape[0] # Anzahl Beobachtungen
u = A.shape[1] # Anzahl Unbekannte
# Einflussfaktor EF berechnen
EF = np.zeros(n, dtype=float)
for i in range(n):
# 1) ∇l_i aufstellen
nabla_l = np.zeros((n, 1))
nabla_l[i, 0] = GRZW[i]
# 2) ∇x_i = Qxx * A^T * P * ∇l_i
nabla_x = Qxx @ (A.T @ (P @ nabla_l))
# 3) EF_i^2 = (∇x_i^T * Qxx^{-1} * ∇x_i) / s0^2
Qxx_inv_nabla_x = np.linalg.solve(Qxx, nabla_x) # = Qxx^{-1} ∇x_i
#EF2 = float((nabla_x.T @ Qxx_inv_nabla_x) / (float(s0_apost) ** 2)).item()
EF2 = ((nabla_x.T @ Qxx_inv_nabla_x) / (float(s0_apost) ** 2)).item()
EF[i] = np.sqrt(EF2)
# Koordinaten-Dict aus x
coords = {}
j = 0
while j < len(unbekannten_liste):
name = str(unbekannten_liste[j])
if name.startswith("X"):
pn = name[1:]
coords[pn] = (x[j], x[j + 1], x[j + 2])
j += 3
else:
j += 1
# EP + Standpunkte
EP_m = np.full(len(labels), np.nan, dtype=float)
standpunkte = [""] * len(labels)
for i, lbl in enumerate(labels):
parts = lbl.split("_")
sp = None
zp = None
# Tachymeter: ID_SD_GRP_SP_ZP / ID_R_GRP_SP_ZP / ID_ZW_GRP_SP_ZP
if ("_SD_" in lbl) or ("_R_" in lbl) or ("_ZW_" in lbl):
if len(parts) >= 5:
sp = parts[3].strip()
zp = parts[4].strip()
# GNSS: *_gnssbx_SP_ZP etc.
if ("gnss" in lbl) and (len(parts) >= 4):
sp = parts[-2].strip()
zp = parts[-1].strip()
standpunkte[i] = sp if sp is not None else ""
one_minus_r = (1.0 - ri[i])
# SD + GNSS: direkt in m
if ("_SD_" in lbl) or ("gnss" in lbl):
EP_m[i] = one_minus_r * GF[i]
# R / ZW: Winkel -> Streckenäquivalent über s
elif ("_R_" in lbl) or ("_ZW_" in lbl):
if sp and zp and (sp in coords) and (zp in coords):
X1, Y1, Z1 = coords[sp]
X2, Y2, Z2 = coords[zp]
s = float(np.sqrt((X2 - X1) ** 2 + (Y2 - Y1) ** 2 + (Z2 - Z1) ** 2))
EP_m[i] = one_minus_r * ((GF[i]) * s)
# SP am Standpunkt (2D)
diagQ = np.diag(Qxx)
SP_cache_mm = {}
for sp in set([s for s in standpunkte if s]):
idx_x = [k for k, sym in enumerate(unbekannten_liste) if str(sym) == f"X{sp}"][0]
qx = diagQ[idx_x]
qy = diagQ[idx_x + 1]
SP_cache_mm[sp] = float(s0_apost) * np.sqrt(qx + qy) * 1000.0
SP_mm = np.array([SP_cache_mm.get(sp, np.nan) for sp in standpunkte], dtype=float)
out = pd.DataFrame({
"Beobachtung": labels,
"Stand-Pkt": standpunkte,
"EF": EF,
"EP [mm]": EP_m * 1000.0,
"SP [mm]": SP_mm,
"EF*SP [mm]": EF * SP_mm,
})
return out