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

File diff suppressed because it is too large Load Diff

View File

@@ -124,7 +124,7 @@ class Datumsfestlegung:
return Q_ext, P
@staticmethod
def indizes_beobachtungsvektor_nach_beobachtungsgruppe(Jacobimatrix_symbolisch_liste_beobachtungsvektor):
def indizes_beobachtungsvektor_nach_beobachtungsgruppe(Jacobimatrix_symbolisch_liste_beobachtungsvektor: list[str]) -> dict[str, tuple[int, int]]:
"""
Ermittelt Indexbereiche des Beobachtungsvektors getrennt nach Beobachtungsgruppen.
@@ -190,112 +190,3 @@ class Datumsfestlegung:
dict_beobachtungsgruppen_indizes["lA"] = min(liste_anschlusspunkte_indizes), max(liste_anschlusspunkte_indizes)
return dict_beobachtungsgruppen_indizes
@staticmethod
def make_index(unbekannten_liste):
names = [str(s).strip() for s in unbekannten_liste]
return names, {n: i for i, n in enumerate(names)}
@staticmethod
def erstelle_G(x0, unbekannten_liste, liste_punktnummern=None, mit_massstab=True):
"""
Baut G (u x d) in den vollen Unbekanntenraum.
Wenn liste_punktnummern=None, werden alle Punkt-IDs aus unbekannten_liste
über X*/Y*/Z* automatisch bestimmt.
"""
x0 = np.asarray(x0, float).reshape(-1)
names, idx = Datumsfestlegung.make_index(unbekannten_liste)
u = len(names)
d = 7 if mit_massstab else 6
G = np.zeros((u, d), dtype=float)
# --- Punktliste automatisch, falls nicht gegeben ---
if liste_punktnummern is None:
pids = set()
for n in names:
if len(n) >= 2 and n[0].upper() in ("X", "Y", "Z"):
pids.add(n[1:]) # alles nach X/Y/Z
liste_punktnummern = sorted(pids)
for pid in liste_punktnummern:
sx, sy, sz = f"X{pid}", f"Y{pid}", f"Z{pid}"
if sx not in idx or sy not in idx or sz not in idx:
continue # Punkt nicht vollständig als XYZ-Unbekannte vorhanden
ix, iy, iz = idx[sx], idx[sy], idx[sz]
xi, yi, zi = x0[ix], x0[iy], x0[iz]
# Translationen
G[ix, 0] = 1.0
G[iy, 1] = 1.0
G[iz, 2] = 1.0
# Rotationen (δr = ω × r)
# Rx: δY=-Z, δZ=+Y
G[iy, 3] = -zi
G[iz, 3] = yi
# Ry: δX=+Z, δZ=-X
G[ix, 4] = zi
G[iz, 4] = -xi
# Rz: δX=-Y, δY=+X
G[ix, 5] = -yi
G[iy, 5] = xi
# Maßstab
if mit_massstab:
G[ix, 6] = xi
G[iy, 6] = yi
G[iz, 6] = zi
return G
def aktive_indices_from_selection(auswahl, unbekannten_liste):
names, idx = Datumsfestlegung.make_index(unbekannten_liste)
aktive = []
for pid, comp in auswahl:
key = f"{comp.upper()}{str(pid)}"
if key not in idx:
raise KeyError(f"{key} nicht im Unbekanntenvektor.")
aktive.append(idx[key])
# unique
out = []
seen = set()
for i in aktive:
if i not in seen:
seen.add(i)
out.append(i)
return out
def auswahlmatrix_E(u, aktive_indices):
E = np.zeros((u, u), dtype=float)
for i in aktive_indices:
E[int(i), int(i)] = 1.0
return E
def berechne_dx(N, n, G):
N = np.asarray(N, float)
n = np.asarray(n, float).reshape(-1, 1)
G = np.asarray(G, float)
u = N.shape[0]
d = G.shape[1]
K = np.block([
[N, G],
[G.T, np.zeros((d, d))]
])
rhs = np.vstack([n, np.zeros((d, 1))])
K_inv = np.linalg.inv(K)
sol = K_inv @ rhs
dx = sol[:u]
k = sol[u:]
Q_xx = K_inv[:u, :u]
return dx, k, Q_xx

View File

@@ -65,11 +65,7 @@ class Export:
@staticmethod
def erzeuge_ergebnis_datenframes(
pfad_datenbank: str,
pfad_tif_quasigeoidundulation: str,
dict_koordinaten_ausgleichungsergebnis: dict
):
def erzeuge_ergebnis_datenframes(pfad_datenbank: str, pfad_tif_quasigeoidundulation: str, dict_koordinaten_ausgleichungsergebnis: dict) -> tuple[pd.DataFrame, pd.DataFrame]:
"""
Erzeugt Ergebnis-DataFrames für ausgeglichene Koordinaten in ECEF (XYZ) und UTM.

View File

@@ -25,7 +25,7 @@ class FunktionalesModell:
Die grundlegende Funktionsweise der Matrixdefinition lautet:
1) Einmaligen Aufbauen der Symbolischen Matrix einmalig
2) In jeder Iteration Substituieren der Symbolischen Matrizen in Numerische np.asarrays
2) In jeder Iteration Substituieren der Symbolischen Matrizen in Numerische np.ndarray
"""
def __init__(self, pfad_datenbank: str, a: float, b: float, pfad_tif_quasigeoidundulation: str = None) -> None:
@@ -703,8 +703,8 @@ class FunktionalesModell:
def unbekanntenvektor_numerisch(self, liste_unbekanntenvektor_symbolisch: list,
unbekanntenvektor_symbolisch: sp.Matrix,
dX_Vektor: np.asarray = None,
unbekanntenvektor_numerisch_vorherige_Iteration: np.asarray = None,
dX_Vektor: np.ndarray = None,
unbekanntenvektor_numerisch_vorherige_Iteration: np.ndarray = None,
iterationsnummer: int = 0) -> ndarray[tuple[int, int], Any] | ndarray[tuple[Any, ...], dtype[Any]]:
"""Erstellt den numerischen Unbekanntenvektor jeder Iteration.
@@ -722,9 +722,9 @@ class FunktionalesModell:
:param unbekanntenvektor_symbolisch: Symbolischer Unbekanntenvektor.
:type unbekanntenvektor_symbolisch: sp.Matrix
:param dX_Vektor: Verbesserungsvektor der aktuellen Iteration (optional).
:type dX_Vektor: np.asarray | None
:type dX_Vektor: np.ndarray | None
:param unbekanntenvektor_numerisch_vorherige_Iteration: Numerischer Unbekanntenvektor der vorherigen Iteration (optional).
:type unbekanntenvektor_numerisch_vorherige_Iteration: np.asarray | None
:type unbekanntenvektor_numerisch_vorherige_Iteration: np.ndarray | None
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
:type iterationsnummer: int
:return: Numerischer Unbekanntenvektor als Numpy-Array
@@ -754,7 +754,7 @@ class FunktionalesModell:
"Unbekanntenvektor")
return unbekanntenvektor_numerisch
def unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(self, liste_unbekanntenvektor_symbolisch: list, unbekanntenvektor_numerisch: np.asarray) -> dict:
def unbekanntenvektor_numerisch_to_dict_unbekanntenvektor(self, liste_unbekanntenvektor_symbolisch: list, unbekanntenvektor_numerisch: np.ndarray) -> dict:
"""Wandelt einen numerischen Unbekanntenvektor in ein Koordinatendictionary um.
Aus dem numerischen Unbekanntenvektor werden für alle Punkte die Koordinaten (X, Y, Z) extrahiert
@@ -763,7 +763,7 @@ class FunktionalesModell:
:param liste_unbekanntenvektor_symbolisch: Liste der Unbekannten (Symbole) in der Reihenfolge des numerischen Vektors.
:type liste_unbekanntenvektor_symbolisch: list
:param unbekanntenvektor_numerisch: Numerischer Unbekanntenvektor.
:type unbekanntenvektor_numerisch: np.asarray
:type unbekanntenvektor_numerisch: np.ndarray
:return: Dictionary {punktnummer: sp.Matrix([X, Y, Z])}.
:rtype: dict
"""
@@ -796,8 +796,8 @@ class FunktionalesModell:
return dict_koordinaten
def berechnung_dl(self, beobachtungsvektor_numerisch: np.asarray, beobachtungsvektor_naeherung_numerisch: sp.Matrix,
liste_beobachtungsvektor_symbolisch: list = None, iterationsnummer: int = 0) -> np.asarray:
def berechnung_dl(self, beobachtungsvektor_numerisch: np.ndarray, beobachtungsvektor_naeherung_numerisch: sp.Matrix,
liste_beobachtungsvektor_symbolisch: list = None, iterationsnummer: int = 0) -> np.ndarray:
"""Berechnet den Verbesserungsvektor dl = l f(x0).
Der Vektor wird als Differenz aus numerischem Beobachtungsvektor und numerischem Näherungs-Beobachtungsvektor gebildet.
@@ -806,7 +806,7 @@ class FunktionalesModell:
Der Vektor dl wird als CSV-Datei in den Ordner Zwischenergebnisse exportiert.
:param beobachtungsvektor_numerisch: Numerischer Beobachtungsvektor l.
:type beobachtungsvektor_numerisch: np.asarray
:type beobachtungsvektor_numerisch: np.ndarray
:param beobachtungsvektor_naeherung_numerisch: Numerischer Näherungs-Beobachtungsvektor f(x0).
:type beobachtungsvektor_naeherung_numerisch: sp.Matrix
:param liste_beobachtungsvektor_symbolisch: Optional: Liste der Beobachtungskennungen.
@@ -814,7 +814,7 @@ class FunktionalesModell:
:param iterationsnummer: Iterationsnummer für Dateinamen der Zwischenergebnisse.
:type iterationsnummer: int
:return: Verbesserungsvektor dl.
:rtype: np.asarray
:rtype: np.ndarray
"""
dl = beobachtungsvektor_numerisch - beobachtungsvektor_naeherung_numerisch
# Umwandeln in einen Numpy-Array, um Rechenzeit im Vergleich zu einer sympy.MAtrix zu sparen
@@ -839,7 +839,7 @@ class FunktionalesModell:
return dl
def dict_substitutionen_uebergeordnetes_system(self,
unbekanntenvektor_aus_iteration: np.asarray = None) -> dict[Any, Any]:
unbekanntenvektor_aus_iteration: np.ndarray = None) -> dict[Any, Any]:
"""Erstellt das Substitutions-Dictionary für das geozentrisch-kartesische System.
Es werden (abhängig davon, ob ein Unbekanntenvektor aus einer Iteration übergeben wurde) die aktuellen
@@ -852,7 +852,7 @@ class FunktionalesModell:
- Orientierungsunbekannte O.
:param unbekanntenvektor_aus_iteration: Optionaler numerischer Unbekanntenvektor einer Iteration zur Aktualisierung der Substitutionen.
:type unbekanntenvektor_aus_iteration: np.asarray | None
:type unbekanntenvektor_aus_iteration: np.ndarray | None
:return: Dictionary mit SymPy-Symbolen als Key und numerischen Werten als Value.
:rtype: dict[Any, Any]
"""
@@ -976,7 +976,7 @@ class FunktionalesModell:
return substitutionen
def unbekanntenvektor_numerisch_to_dict_orientierungen(self, liste_unbekanntenvektor_symbolisch: list,
unbekanntenvektor_numerisch: np.asarray) -> dict[Any, Any]:
unbekanntenvektor_numerisch: np.ndarray) -> dict[Any, Any]:
"""Extrahiert Orientierungsparameter aus einem numerischen Unbekanntenvektor in ein Dictionary.
Alle Unbekannten, deren Symbolname mit "O" beginnt, werden als als Dictionary in der Form {orientierungs_id: wert} zurückgegeben.
@@ -984,7 +984,7 @@ class FunktionalesModell:
:param liste_unbekanntenvektor_symbolisch: Liste der Unbekannten (Symbole) in der Reihenfolge des numerischen Vektors.
:type liste_unbekanntenvektor_symbolisch: list
:param unbekanntenvektor_numerisch: Numerischer Unbekanntenvektor.
:type unbekanntenvektor_numerisch: np.asarray
:type unbekanntenvektor_numerisch: np.ndarray
:return: Dictionary der Orientierungen je Beobachtungsgruppe.
:rtype: dict[Any, Any]
"""

View File

@@ -401,7 +401,7 @@ class Transformationen:
def ecef_to_utm(
self,
dict_koordinaten: dict,
pfad_gcg_tif: str | Path | None = None):
pfad_gcg_tif: str | Path | None = None) -> dict:
"""Rechnet ECEF-Koordinaten (ETRS89 geozentrisch-kartesisch) nach nach UTM (+ DHHN2016 Normalhöhe).
Es wird ein PROJ-Transformer von:

View File

@@ -13,7 +13,7 @@ class Genauigkeitsmaße:
Die Klasse stellt Methoden zur Verfügung für:
- Berechnung der a-posteriori Standardabweichung der Gewichtseinheit s
- Berechnung der a-posteriori Standardabweichung der Gewichtseinheit σ̂
- Berechnung des Helmertschen Punktfehlers (2D/3D),
- Berechnung der Standardellipse (Helmertschen Fehlerellipse),
- Berechnung der Konfidenzellipse auf Basis eines Konfidenzniveaus (alpha) mit Skalierung über die F-Verteilung,
@@ -25,19 +25,19 @@ class Genauigkeitsmaße:
@staticmethod
def berechne_sigma0apost(v: np.ndarray, P: np.ndarray, r: int, print_ausgabe = True) -> float:
"""
Berechnet die a-posteriori Standardabweichung der Gewichtseinheit s₀.
Berechnet die a-posteriori Standardabweichung der Gewichtseinheit σ̂₀.
Die a-posteriori Standardabweichung dient als Qualitätsmaß für die
Ausgleichung nach der Methode der kleinsten Quadrate. Dabei beschreibt
r die Redundanz (Freiheitsgrade).
:param v: Residuenvektor der Beobachtungen.
:type v: numpy.ndarray
:type v: np.ndarray
:param P: Gewichtsmatrix der Beobachtungen.
:type P: numpy.ndarray
:type P: np.ndarray
:param r: Redundanz bzw. Anzahl der Freiheitsgrade der Ausgleichung.
:type r: int
:return: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:return: a-posteriori Standardabweichung der Gewichtseinheit σ̂₀.
:rtype: float
"""
@@ -50,12 +50,12 @@ class Genauigkeitsmaße:
@staticmethod
def helmert_punktfehler(Qxx, s0_apost, unbekannten_liste):
def helmert_punktfehler(Qxx: np.ndarray, sigma0_apost: float, unbekannten_liste: list) -> pd.DataFrame:
"""
Berechnet den Helmertschen Punktfehler (2D/3D) anhand der Standardabweichungen der Koordinaten der Punkte.
Aus der Kofaktor-Matrix der Unbekannten Qxx werden die Kofaktoren punktweise ausgelesen. Durch Multiplikation
mit der a-posteriori Standardabweichung der Gewichtseinheit s₀ werden die Standardabweichungen je Koordinate
mit der a-posteriori Standardabweichung der Gewichtseinheit σ̂₀ werden die Standardabweichungen je Koordinate
(σx, σy, σz) sowie der Helmert'sche Punktfehler σP berechnet:
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste.
@@ -63,14 +63,13 @@ class Genauigkeitsmaße:
Ergebnisse als Tabelle ausgegeben und in eine Excel-Datei exportiert.
:param Qxx: Kofaktor-Matrix der Unbekannten.
:type Qxx: numpy.ndarray
:param s0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0_apost: float
:type Qxx: np.ndarray
:param sigma0_apost: a-posteriori Standardabweichung der Gewichtseinheit σ̂₀.
:type sigma0_apost: float
:param unbekannten_liste: Liste der Unbekannten.
:type unbekannten_liste: list
:return: Tabelle mit Standardabweichungen und Helmertschem Punktfehler je Punkt.
:rtype: pandas.DataFrame
:raises ValueError: Wenn eine ungültige Dimension (nicht 2 oder 3) eingegeben wird.
"""
dim = int(input("Helmertscher Punktfehler (2 = 2D, 3 = 3D): "))
diagQ = np.diag(Qxx)
@@ -98,17 +97,17 @@ class Genauigkeitsmaße:
except StopIteration:
qz = 0.0
sx = s0_apost * np.sqrt(qx)
sy = s0_apost * np.sqrt(qy)
sz = s0_apost * np.sqrt(qz) if dim == 3 else 0
sx = sigma0_apost * np.sqrt(qx)
sy = sigma0_apost * np.sqrt(qy)
sz = sigma0_apost * np.sqrt(qz) if dim == 3 else 0
sP = s0_apost * np.sqrt(qx + qy + qz)
sP = sigma0_apost * np.sqrt(qx + qy + qz)
daten.append([pid, float(sx), float(sy), float(sz), float(sP)])
except:
continue
helmert_punktfehler = pd.DataFrame(daten, columns=["Punkt", "σx", "σy", "σz", f"σP_{dim}D"])
mittel_sP = helmert_punktfehler[f"σP_{dim}D"].mean()
helmert_punktfehler = pd.DataFrame(daten, columns=["Punkt", "σ̂x", "σ̂y", "σ̂z", f"σ̂P_{dim}D"])
mittel_sP = helmert_punktfehler[f"σ̂P_{dim}D"].mean()
print(f"Mittlerer Helmert-Punktfehler über alle Punkte: {mittel_sP:.4f} [m]")
display(HTML(helmert_punktfehler.to_html(index=False)))
helmert_punktfehler.to_excel(r"Netzqualitaet\Standardabweichungen_Helmertscher_Punktfehler.xlsx",index=False)
@@ -117,9 +116,9 @@ class Genauigkeitsmaße:
@staticmethod
def standardellipsoid(Qxx, s0_apost, unbekannten_liste):
def standardellipsoid(Qxx: np.ndarray, sigma0_apost: float, unbekannten_liste: list) -> pd.DataFrame:
"""
Berechnet Achsenlängen des Fehlerellipsoids (a, b, c) sowie den 2D-Richtungswinkel θ aus Qxx und s₀ a posteriori.
Berechnet Achsenlängen des Fehlerellipsoids (a, b, c) sowie den 2D-Richtungswinkel θ aus Qxx und σ̂₀ a posteriori.
Für jeden Punkt wird aus der Kofaktor-Matrix der Unbekannten Qxx die 3×3-Submatrix
der Koordinaten (X, Y, Z) gebildet. Daraus werden zunächst die Standardabweichungen
@@ -137,9 +136,9 @@ class Genauigkeitsmaße:
Die Ergebnisse werden tabellarisch ausgegeben und in eine Excel-Datei exportiert.
:param Qxx: Kofaktor-Matrix der Unbekannten.
:type Qxx: numpy.ndarray
:param s0_apost: A-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0_apost: float
:type Qxx: np.ndarray
:param sigma0_apost: A-posteriori Standardabweichung der Gewichtseinheit σ̂₀.
:type sigma0_apost: float
:param unbekannten_liste: Liste der Unbekannten.
:type unbekannten_liste: list
:return: Tabelle mit Standardabweichungen (σx, σy, σz), Ellipsoid-Halbachsen (a, b, c) und Richtungswinkel θ je Punkt.
@@ -165,18 +164,18 @@ class Genauigkeitsmaße:
Q_sub = Qxx[np.ix_(indices, indices)]
# Standardabweichungen
sx = s0_apost * np.sqrt(Q_sub[0, 0])
sy = s0_apost * np.sqrt(Q_sub[1, 1])
sz = s0_apost * np.sqrt(Q_sub[2, 2])
sx = sigma0_apost * np.sqrt(Q_sub[0, 0])
sy = sigma0_apost * np.sqrt(Q_sub[1, 1])
sz = sigma0_apost * np.sqrt(Q_sub[2, 2])
# Eigenwertzerlegung
eigenwerte, eigenvektoren = np.linalg.eigh(Q_sub)
eigenwerte = np.sort(eigenwerte)[::-1]
# Halbachsen
s_a = s0_apost * np.sqrt(eigenwerte[0]) # Große Halbachse a
s_b = s0_apost * np.sqrt(eigenwerte[1]) # Mittlere Halbachse b
s_c = s0_apost * np.sqrt(eigenwerte[2]) # Kleine Halbachse c
s_a = sigma0_apost * np.sqrt(eigenwerte[0]) # Große Halbachse a
s_b = sigma0_apost * np.sqrt(eigenwerte[1]) # Mittlere Halbachse b
s_c = sigma0_apost * np.sqrt(eigenwerte[2]) # Kleine Halbachse c
# Richtungswinkel theta in gon:
qyx = Q_sub[1, 0]
@@ -192,10 +191,10 @@ class Genauigkeitsmaße:
])
except:
continue
standardellipsoid = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σz [m]", "Halbachse a [m]", "Halbachse b [m]", "Halbachse c [m]", "θ [gon]"])
standardellipsoid["σx [m]"] = standardellipsoid["σx [m]"].astype(float).round(4)
standardellipsoid["σy [m]"] = standardellipsoid["σy [m]"].astype(float).round(4)
standardellipsoid["σz [m]"] = standardellipsoid["σz [m]"].astype(float).round(4)
standardellipsoid = pd.DataFrame(daten, columns=["Punkt", "σ̂x [m]", "σ̂y [m]", "σ̂z [m]", "Halbachse a [m]", "Halbachse b [m]", "Halbachse c [m]", "θ [gon]"])
standardellipsoid["σ̂x [m]"] = standardellipsoid["σ̂x [m]"].astype(float).round(4)
standardellipsoid["σ̂y [m]"] = standardellipsoid["σ̂y [m]"].astype(float).round(4)
standardellipsoid["σ̂z [m]"] = standardellipsoid["σ̂z [m]"].astype(float).round(4)
standardellipsoid["Halbachse a [m]"] = standardellipsoid["Halbachse a [m]"].astype(float).round(4)
standardellipsoid["Halbachse b [m]"] = standardellipsoid["Halbachse b [m]"].astype(float).round(4)
standardellipsoid["Halbachse c [m]"] = standardellipsoid["Halbachse c [m]"].astype(float).round(4)
@@ -207,23 +206,34 @@ class Genauigkeitsmaße:
@staticmethod
def konfidenzellipsoid(Qxx, s0_apost, unbekannten_liste, R, ausgabe_erfolgt):
def konfidenzellipsoid(Qxx: np.ndarray, sigma0_apost: float, unbekannten_liste: list, R: int, ausgabe_erfolgt: bool) -> tuple[pd.DataFrame, float]:
"""
Berechnet die Konfidenzellipse für Punkte aus Qxx und einem Konfidenzniveau.
Berechnet die Konfidenzellipsoid für Punkte aus Qxx und einem Konfidenzniveau.
Auf Basis der Kovarianz-Matrix der Unbekannten Qxx und der a-posteriori
Standardabweichung der Gewichtseinheit s₀ werden für jeden Punkt die Parameter
der Konfidenzellipse berechnet. Das Konfidenzniveau wird mittels einer Eingabe
über alpha gewählt (Standard: 0.05 für 95%).
Standardabweichung der Gewichtseinheit σ̂₀ werden für jeden Punkt die Parameter
des Konfidenzellipsoids berechnet. Das Konfidenzniveau wird mittels einer Eingabe
über alpha festgelegt (Standard: 0.05 für 95%).
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste (z.B. X1, Y1).
Für jeden Punkt wird die 3×3-Submatrix der Koordinaten (X, Y, Z) aus Qxx gebildet.
Über eine Eigenwertzerlegung dieser Submatrix werden die drei Hauptachsen des
Fehlerellipsoids bestimmt. Die Halbachsen des Konfidenzellipsoids ergeben sich aus:
- Aₖ: große Halbachse des Konfidenzellipsoids,
- Bₖ: mittlere Halbachse des Konfidenzellipsoids,
- Cₖ: kleine Halbachse des Konfidenzellipsoids,
unter Verwendung eines Faktors aus der F-Verteilung in Abhängigkeit vom
Konfidenzniveau und den Freiheitsgraden.
Die Punktzuordnung erfolgt über die Symbolnamen der Unbekanntenliste.
Optional wird die Tabelle ausgegeben und als Excel-Datei exportiert, abhängig von
ausgabe_erfolgt.
:param Qxx: Kofaktor-Matrix der geschätzten Unbekannten.
:type Qxx: numpy.ndarray
:param s0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0_apost: float
:type Qxx: np.ndarray
:param sigma0_apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type sigma0_apost: float
:param unbekannten_liste: Liste der Unbekannten.
:type unbekannten_liste: list
:param R: Redundanz (Freiheitsgrade) für die F-Verteilung.
@@ -232,7 +242,6 @@ class Genauigkeitsmaße:
:type ausgabe_erfolgt: bool
:return: Tabelle der Konfidenzellipse je Punkt, verwendetes alpha.
:rtype: tuple[pandas.DataFrame, float]
:raises ValueError: Wenn alpha nicht in (0, 1) liegt oder nicht in float umgewandelt werden kann.
"""
alpha_input = input("Irrtumswahrscheinlichkeit α wählen (z.B. 0.05, 0.01) [Standard=0.05]: ")
if alpha_input.strip() == "":
@@ -259,18 +268,18 @@ class Genauigkeitsmaße:
Q_sub = Qxx[np.ix_(indices, indices)]
# Standardabweichungen
sx = s0_apost * np.sqrt(Q_sub[0, 0])
sy = s0_apost * np.sqrt(Q_sub[1, 1])
sz = s0_apost * np.sqrt(Q_sub[2, 2])
sx = sigma0_apost * np.sqrt(Q_sub[0, 0])
sy = sigma0_apost * np.sqrt(Q_sub[1, 1])
sz = sigma0_apost * np.sqrt(Q_sub[2, 2])
# Eigenwertzerlegung
eigenwerte, eigenvektoren = np.linalg.eigh(Q_sub)
eigenwerte = np.sort(eigenwerte)[::-1]
# Halbachsen des Konfidenzellipoid
A_K = k * s0_apost * np.sqrt(eigenwerte[0])
B_K = k * s0_apost * np.sqrt(eigenwerte[1])
C_K = k * s0_apost * np.sqrt(eigenwerte[2])
A_K = k * sigma0_apost * np.sqrt(eigenwerte[0])
B_K = k * sigma0_apost * np.sqrt(eigenwerte[1])
C_K = k * sigma0_apost * np.sqrt(eigenwerte[2])
# Richtungswinkel theta in gon:
qyx = Q_sub[1, 0]
@@ -288,7 +297,7 @@ class Genauigkeitsmaße:
except:
continue
konfidenzellipoid = pd.DataFrame(daten, columns=["Punkt", "σx [m]", "σy [m]", "σz [m]", "Halbachse a_k [m]", "Halbachse b_k [m]", "Halbachse c_k [m]", "θ [gon]"])
konfidenzellipoid = pd.DataFrame(daten, columns=["Punkt", "σ̂x [m]", "σ̂y [m]", "σ̂z [m]", "Halbachse a_k [m]", "Halbachse b_k [m]", "Halbachse c_k [m]", "θ [gon]"])
if ausgabe_erfolgt == False:
display(HTML(konfidenzellipoid.to_html(index=False)))
konfidenzellipoid.to_excel(r"Netzqualitaet\Konfidenzellipoid.xlsx", index=False)
@@ -296,7 +305,7 @@ class Genauigkeitsmaße:
@staticmethod
def konfidenzellipsen_enu(a, b, ausgabe_parameterschaetzung, liste_unbekannte, ausgleichungsergebnis, s0apost, r_gesamt):
def konfidenzellipsen_enu(a: float, b: float, ausgabe_parameterschaetzung: dict, liste_unbekannte: list, ausgleichungsergebnis: dict, sigma0apost: float, r_gesamt: int) -> tuple[pd.DataFrame, np.ndarray]:
"""
Berechnet Konfidenzellipsen im lokalen ENU-System aus einer ins ENU-System transformierten Qxx-Matrix.
@@ -317,13 +326,12 @@ class Genauigkeitsmaße:
:type liste_unbekannte: list
:param ausgleichungsergebnis: Dictionary der geschätzten Punktkoordinaten (XYZ) zur ENU-Referenzbildung.
:type ausgleichungsergebnis: dict
:param s0apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type s0apost: float
:param sigma0apost: a-posteriori Standardabweichung der Gewichtseinheit s₀.
:type sigma0apost: float
:param r_gesamt: Redundanz (Freiheitsgrade) für die Konfidenzberechnung.
:type r_gesamt: int
:return: Tabelle der Konfidenzellipse im ENU-System, Rotationsmatrix R0 der ENU-Transformation.
:rtype: tuple[pandas.DataFrame, numpy.ndarray]
:raises KeyError: Wenn ``ausgabe_parameterschaetzung`` keinen Eintrag ``"Q_xx"`` enthält.
"""
berechnungen = Berechnungen.Berechnungen(a, b)
@@ -342,7 +350,7 @@ class Genauigkeitsmaße:
# 2) Konfidenzellipoid im ENU-System
Konfidenzellipse_ENU, alpha = Genauigkeitsmaße.konfidenzellipsoid(
Qxx_enu,
s0apost,
sigma0apost,
liste_unbekannte,
r_gesamt,
ausgabe_erfolgt = True
@@ -350,16 +358,16 @@ class Genauigkeitsmaße:
# 3) Spaltennamen anpassen
Konfidenzellipse_ENU = Konfidenzellipse_ENU.rename(columns={
"σx [m]": "σE [m]",
"σy [m]": "σN [m]",
"σz [m]": "σU [m]",
"σ̂x [m]": "σ̂E [m]",
"σ̂y [m]": "σ̂N [m]",
"σ̂z [m]": "σ̂U [m]",
"θ [gon]": "θ_EN [gon]"
})
# 4) Runden und Anzeigen
Konfidenzellipse_ENU["σE [m]"] = Konfidenzellipse_ENU["σE [m]"].round(6)
Konfidenzellipse_ENU["σN [m]"] = Konfidenzellipse_ENU["σN [m]"].round(6)
Konfidenzellipse_ENU["σU [m]"] = Konfidenzellipse_ENU["σU [m]"].round(6)
Konfidenzellipse_ENU["σ̂E [m]"] = Konfidenzellipse_ENU["σ̂E [m]"].round(6)
Konfidenzellipse_ENU["σ̂N [m]"] = Konfidenzellipse_ENU["σ̂N [m]"].round(6)
Konfidenzellipse_ENU["σ̂U [m]"] = Konfidenzellipse_ENU["σ̂U [m]"].round(6)
Konfidenzellipse_ENU["Halbachse a_k [m]"] = Konfidenzellipse_ENU["Halbachse a_k [m]"].round(4)
Konfidenzellipse_ENU["Halbachse b_k [m]"] = Konfidenzellipse_ENU["Halbachse b_k [m]"].round(4)
Konfidenzellipse_ENU["Halbachse c_k [m]"] = Konfidenzellipse_ENU["Halbachse c_k [m]"].round(4)
@@ -390,15 +398,7 @@ class Plot:
"""
@staticmethod
def netzplot_ellipsen(
Koord_ENU,
unbekannten_labels,
beobachtungs_labels,
df_konf_ellipsen_enu,
skalierung,
n_ellipse_pts=60,
titel="Netzplot im ENU-System mit Konfidenzellipsen"
):
def netzplot_ellipsen(Koord_ENU: dict[str, tuple[float, float, float]], unbekannten_labels: list, beobachtungs_labels: list, df_konf_ellipsen_enu: pd.DataFrame, skalierung: float, n_ellipse_pts: int = 60, titel: str = "Netzplot im ENU-System mit Konfidenzellipsen") -> go.Figure:
"""
Erstellt einen Netzplot im ENU-System inklusive Konfidenzellipsen, Netzpunkten und Beobachtungslinien.
@@ -430,7 +430,6 @@ class Plot:
:type titel: str
:return: Plotly-Figure-Objekt mit Netz, Beobachtungen und Ellipsen.
:rtype: plotly.graph_objects.Figure
:raises ValueError: Wenn weder "θ_EN [gon]" noch "θ [gon]" im DataFrame vorhanden ist.
"""
namen = [str(s).strip() for s in unbekannten_labels]
@@ -546,7 +545,7 @@ class Plot:
return plot
def plot_speichere_aktuelle_ansicht(plot, dateiname=r"Netzqualitaet\netzplot_ellipsen_zoom_ansicht.png"):
def plot_speichere_aktuelle_ansicht(plot: go.Figure, dateiname: str = r"Netzqualitaet\netzplot_ellipsen_zoom_ansicht.png") -> None:
"""
Speichert die aktuell dargestellte Plot-Ansicht als Bilddatei.
@@ -564,7 +563,6 @@ class Plot:
:type dateiname: str
:return: None
:rtype: None
:raises OSError: Wenn die Bilddatei nicht geschrieben werden kann (z.B. fehlender Pfad oder fehlendes Kaleido).
"""
aktuelles_layout = plot.layout

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".

View File

@@ -53,7 +53,8 @@ class Iterationen:
self.fm = Funktionales_Modell.FunktionalesModell(self.pfad_datenbank, self.a, self.b, self.pfad_tif_quasigeoidundulation)
def ausgleichung_global(self, A, dl, Q_ext, P):
def ausgleichung_global(self, A: np.ndarray, dl: np.ndarray, Q_ext: np.ndarray, P: np.ndarray) -> tuple[dict[str, np.ndarray], np.ndarray]:
"""
Führt eine Ausgleichung nach kleinsten Quadraten durch.
@@ -62,24 +63,23 @@ class Iterationen:
Es werden folgende Berechnungsschitte durchgeführt:
1) Normalgleichungsmatrix N = Aᵀ · P · A und Absolutglied n = Aᵀ · P · dl
2) Lösung dx = N⁻¹ · n
3) Residuen v = A · dx dl
4) Kofaktormatrix der Unbekannten Q_xx
5) Kofaktormatrix der ausgeglichenen Beobachtungen Q_ll_dach
6) Kofaktormatrix der Verbesserungen Q_vv
- Normalgleichungsmatrix N und Absolutgliedvektor n
- Zuschlagsvektor dx
- Residuenvektor v
- Kofaktormatrix der Unbekannten Q_xx
- Kofaktormatrix der ausgeglichenen Beobachtungen Q_ll_dach
- Kofaktormatrix der Verbesserungen Q_vv
:param A: Jacobi-Matrix (A-Matrix).
:type A: array_like
:type A: np.ndarray
:param dl: Verbesserungsvektor bzw. Beobachtungsabweichungen.
:type dl: array_like
:type dl: np.ndarray
:param Q_ext: a-priori Kofaktormatrix der Beobachtungen.
:type Q_ext: array_like
:type Q_ext: np.ndarray
:param P: Gewichtsmatrix der Beobachtungen.
:type P: array_like
:type P: np.ndarray
:return: Dictionary mit Ausgleichungsergebnissen, Zuschlagsvektor dx.
:rtype: tuple[dict[str, Any], numpy.ndarray]
:raises numpy.linalg.LinAlgError: Wenn das Normalgleichungssystem singulär ist und nicht gelöst werden kann.
:rtype: tuple[dict[str, numpy.ndarray], np.ndarray]
"""
A = np.asarray(A, float)
dl = np.asarray(dl, float).reshape(-1, 1)

View File

@@ -1,6 +1,6 @@
import numpy as np
def atpv_probe(A, P, v, tol=1e-7):
def atpv_probe(A: np.ndarray, P: np.ndarray, v: np.ndarray, tol: float = 1e-7) -> None:
"""
Führt die ATPv-Probe zur Kontrolle der Lösung des Normalgleichungssystems durch.
@@ -8,11 +8,11 @@ def atpv_probe(A, P, v, tol=1e-7):
Die Prüfung erfolgt unter Verwendung einer vorgegebenen Toleranz.
:param A: Jacobi-Matrix (A-Matrix).
:type A: np.asarray
:type A: np.ndarray
:param P: Gewichtsmatrix der Beobachtungen.
:type P: np.asarray
:type P: np.ndarray
:param v: Residuenvektor der Beobachtungen.
:type v: np.asarray
:type v: np.ndarray
:param tol: Absolute Toleranz für den Vergleich mit Null.
:type tol: float
:return: None
@@ -30,7 +30,7 @@ def atpv_probe(A, P, v, tol=1e-7):
print("❌ ATPv-Probe nicht erfolgreich. Fehler bei der Lösung des Normalgleichungssystems")
def hauptprobe(A, x, l, v, tol=1e-7):
def hauptprobe(A: np.ndarray, x: np.ndarray, l: np.ndarray, v: np.ndarray, tol: float = 1e-7) -> None:
"""
Führt die Hauptprobe zur Überprüfung der berechneten Residuen durch.
@@ -40,13 +40,13 @@ def hauptprobe(A, x, l, v, tol=1e-7):
innerhalb der Toleranz überein, gilt die Ausgleichung als konsistent.
:param A: Jacobi-Matrix (A-Matrix).
:type A: np.asarray
:type A: np.ndarray
:param x: Lösungsvektor der Unbekannten.
:type x: np.asarray
:type x: np.ndarray
:param l: Beobachtungsvektor.
:type l: np.asarray
:type l: np.ndarray
:param v: Residuenvektor aus der Ausgleichung.
:type v: np.asarray
:type v: np.ndarray
:param tol: Absolute Toleranz für den Vergleich der Residuen.
:type tol: float
:return: None

View File

@@ -205,7 +205,7 @@ class StochastischesModell:
Export.matrix_to_csv(r"Zwischenergebnisse\Qll_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
return Qll
def Qll_numerisch(self, Qll_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.asarray:
def Qll_numerisch(self, Qll_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.ndarray:
"""Erstellt eine numerische Kofaktormatrix der Beobachtungen aus einer symbolischen Qll-Matrix.
Es werden die zur Substitution benötigten Werte aus der Datenbank abgefragt und den in Qll vorkommenden Symbolen zugeordnet,
@@ -228,7 +228,7 @@ class StochastischesModell:
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
:type liste_beobachtungen_symbolisch: list
:return: Numerische Kofaktormatrix Qll als Numpy-Array.
:rtype: np.asarray
:rtype: np.ndarray
:raises ValueError: Falls Symbole in Qll_Matrix_Symbolisch enthalten sind, für die keine Substitutionen vorhanden sind.
"""
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]
@@ -381,7 +381,7 @@ class StochastischesModell:
return Qll_numerisch
def QAA_symbolisch(self, liste_beobachtungen_symbolisch: list) -> np.asarray:
def QAA_symbolisch(self, liste_beobachtungen_symbolisch: list) -> np.ndarray:
"""Erstellt die symbolische Kofaktormatrix der Anschlusspunkte (weiche Lagerung).
Es werden ausschließlich Beobachtungen berücksichtigt, deren Kennung mit "lA_" beginnt. Für jede Anschlussbedingung
@@ -421,7 +421,7 @@ class StochastischesModell:
Export.matrix_to_csv(r"Zwischenergebnisse\QAA_Symbolisch.csv", liste_beobachtungen_symbolisch, liste_beobachtungen_symbolisch, Qll, "Qll")
return Qll
def QAA_numerisch(self, QAA_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.asarray:
def QAA_numerisch(self, QAA_Matrix_Symbolisch: sp.Matrix, liste_beobachtungen_symbolisch: list) -> np.ndarray:
"""Erstellt eine numerische Matrix aus einer symbolischen QAA-Matrix.
Es werden die numerischen Standardabweichungen der Anschlussbedingungen sowie die Varianzkomponente der Gruppe
@@ -435,7 +435,7 @@ class StochastischesModell:
:param liste_beobachtungen_symbolisch: Liste der symbolischen Beobachtungskennungen.
:type liste_beobachtungen_symbolisch: list
:return: Numerische VKofaktormatrix QAA als Numpy-Array.
:rtype: np.asarray
:rtype: np.ndarray
"""
# Symbolische Listen
liste_beobachtungen_symbolisch = [str(b).strip() for b in liste_beobachtungen_symbolisch]

View File

@@ -42,12 +42,12 @@ class VKS:
self.db_zugriff = Datenbank.Datenbankzugriff(self.pfad_datenbank)
def varianzkomponten_berechnen(self, Jacobimatrix_symbolisch_liste_beobachtungsvektor: list, res: dict, R: np.asarray) -> pd.DataFrame:
def varianzkomponten_berechnen(self, Jacobimatrix_symbolisch_liste_beobachtungsvektor: list, res: dict, R: np.ndarray) -> pd.DataFrame:
"""Berechnet a-posteriori Varianzen und a-posteriori Standardabweichungen je Beobachtungsgruppe.
Teilt die Residuen v, die Gewichtsmatrix P sowie die Redundanzmatrix R gemäß der in
Jacobimatrix_symbolisch_liste_beobachtungsvektor enthaltenen Gruppenindizes auf. Für jede
Beobachtungsgruppe wird die Redundanz ri = Spur(R_i) gebildet und daraus s0_aposteriori
Beobachtungsgruppe wird die Redundanz ri = Spur(R_i) gebildet und daraus sigma0_aposteriori
berechnet.
Unterstützte Gruppenkennungen: "SD", "R", "ZW", "gnss", "niv", "lA".
@@ -57,7 +57,7 @@ class VKS:
:param res: Ergebnis-Dictionary der Ausgleichung, erwartet mindestens res["v"] (Residuen) und res["P"] (Gewichte).
:type res: dict
:param R: Redundanzmatrix (z. B. aus R = Q_vv @ P oder äquivalent), passend zu v/P dimensioniert.
:type R: np.asarray
:type R: np.ndarray
:return: DataFrame mit den Spalten 'Beobachtungsgruppe', 'Standardabweichung', 'Varianz'
:rtype: pd.DataFrame
"""
@@ -80,10 +80,10 @@ class VKS:
aufgeteilt_P_SD = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_SD = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_SD = sum(np.diag(aufgeteilt_R_SD))
s0_aposteriori_SD = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_SD, aufgeteilt_P_SD, ri_SD, False)
sigma0_aposteriori_SD = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_SD, aufgeteilt_P_SD, ri_SD, False)
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_SD,
"Varianz a posteriori": s0_aposteriori_SD ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_SD,
"Varianz a posteriori": sigma0_aposteriori_SD ** 2})
# R = Tachymeter Richtungsbeobachtungen
if beobachtungsgruppe == "R":
@@ -92,10 +92,10 @@ class VKS:
aufgeteilt_P_R = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_R = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_R = sum(np.diag(aufgeteilt_R_R))
s0_aposteriori_R = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_R, aufgeteilt_P_R, ri_R, False)
sigma0_aposteriori_R = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_R, aufgeteilt_P_R, ri_R, False)
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_R,
"Varianz a posteriori": s0_aposteriori_R ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_R,
"Varianz a posteriori": sigma0_aposteriori_R ** 2})
# ZW = Tachymeter Zenitwinkelbeobachtung
if beobachtungsgruppe == "ZW":
@@ -104,10 +104,10 @@ class VKS:
aufgeteilt_P_ZW = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_ZW = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_ZW = sum(np.diag(aufgeteilt_R_ZW))
s0_aposteriori_ZW = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_ZW, aufgeteilt_P_ZW, ri_ZW, False)
sigma0_aposteriori_ZW = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_ZW, aufgeteilt_P_ZW, ri_ZW, False)
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_ZW,
"Varianz a posteriori": s0_aposteriori_ZW ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_ZW,
"Varianz a posteriori": sigma0_aposteriori_ZW ** 2})
# GNSS = GNSS-Basisilinien
if beobachtungsgruppe == "gnss":
@@ -116,10 +116,10 @@ class VKS:
aufgeteilt_P_gnss = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_gnss = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_gnss = sum(np.diag(aufgeteilt_R_gnss))
s0_aposteriori_gnss = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_gnss, aufgeteilt_P_gnss, ri_gnss, False)
sigma0_aposteriori_gnss = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_gnss, aufgeteilt_P_gnss, ri_gnss, False)
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_gnss,
"Varianz a posteriori": s0_aposteriori_gnss ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_gnss,
"Varianz a posteriori": sigma0_aposteriori_gnss ** 2})
# niv = geometrisches Nivellement
if beobachtungsgruppe == "niv":
@@ -128,10 +128,10 @@ class VKS:
aufgeteilt_P_niv = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_niv = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_niv = sum(np.diag(aufgeteilt_R_niv))
s0_aposteriori_niv = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_niv, aufgeteilt_P_niv, ri_niv, False)
sigma0_aposteriori_niv = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_niv, aufgeteilt_P_niv, ri_niv, False)
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_niv,
"Varianz a posteriori": s0_aposteriori_niv ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_niv,
"Varianz a posteriori": sigma0_aposteriori_niv ** 2})
# lA = Anschlusspunkte für die weiche Lagerung
if beobachtungsgruppe == "lA":
@@ -140,11 +140,11 @@ class VKS:
aufgeteilt_P_lA = res["P"][z_start: z_ende + 1, s_start: s_ende + 1]
aufgeteilt_R_lA = R[z_start: z_ende + 1, s_start: s_ende + 1]
ri_lA = sum(np.diag(aufgeteilt_R_lA))
s0_aposteriori_lA = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_lA, aufgeteilt_P_lA, ri_lA, False)
sigma0_aposteriori_lA = Genauigkeitsmaße.berechne_sigma0apost(aufgeteilt_v_lA, aufgeteilt_P_lA, ri_lA, False)
# Speichern in Instanzvariable
liste_ergebnisse.append(
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": s0_aposteriori_lA,
"Varianz a posteriori": s0_aposteriori_lA ** 2})
{"Beobachtungsgruppe": beobachtungsgruppe_lang, "Standardabweichung a posteriori": sigma0_aposteriori_lA,
"Varianz a posteriori": sigma0_aposteriori_lA ** 2})
self.liste_ergebnisse = liste_ergebnisse
df_varianzkomponenten = pd.DataFrame(self.liste_ergebnisse)