trimble_geodesy/debug_adjustment.py

308 lines
13 KiB
Python

#!/usr/bin/env python3
"""
Debug-Skript für Netzausgleichung
Prüft Punktevergleich und findet Fehler
"""
import sys
import math
sys.path.insert(0, '/home/ubuntu/trimble_geodesy')
from modules.jxl_parser import JXLParser
from modules.network_adjustment import NetworkAdjustment
def main():
# Lade JXL
jxl_path = "/home/ubuntu/trimble_geodesy/test_data/Baumschulenstr_93.jxl"
parser = JXLParser()
parser.parse(jxl_path)
print("=" * 80)
print("DEBUG: NETZAUSGLEICHUNG")
print("=" * 80)
# Zeige Winkeleinheit
print(f"\n### JXL Einstellungen:")
print(f" Winkeleinheit: {parser.angle_units}")
# NetworkAdjustment initialisieren
adj = NetworkAdjustment(parser)
# Beobachtungen extrahieren
adj.extract_observations()
adj.initialize_points()
# Debug: Zeige Beobachtungen
print(f"\n### Beobachtungen ({len(adj.observations)} total):")
dir_obs = [o for o in adj.observations if o.obs_type == 'direction']
dist_obs = [o for o in adj.observations if o.obs_type == 'distance']
zen_obs = [o for o in adj.observations if o.obs_type == 'zenith']
print(f" Richtungen: {len(dir_obs)}")
print(f" Strecken: {len(dist_obs)}")
print(f" Zenitwinkel: {len(zen_obs)}")
print("\n### Beispiel-Beobachtungen (erste 5 Richtungen):")
for obs in dir_obs[:5]:
print(f" {obs.from_station} -> {obs.to_point}: {obs.value:.6f} gon (std: {obs.std_dev:.6f})")
print("\n### Beispiel-Beobachtungen (erste 5 Strecken):")
for obs in dist_obs[:5]:
print(f" {obs.from_station} -> {obs.to_point}: {obs.value:.4f} m (std: {obs.std_dev:.6f})")
# Koordinaten vor Ausgleichung speichern
coords_before = {}
for name, pt in adj.points.items():
coords_before[name] = (pt.x, pt.y, pt.z)
print(f"\n### Punkte ({len(adj.points)} total):")
for name, pt in sorted(adj.points.items()):
print(f" {name}: X={pt.x:.4f}, Y={pt.y:.4f}, Z={pt.z:.4f}")
# Festpunkte setzen
ref_points = parser.get_reference_points()
print(f"\n### Referenzpunkte (5xxx): {ref_points}")
for name in ref_points:
adj.set_fixed_point(name)
# Falls keine Referenzpunkte, erste Station als Festpunkt
if not adj.fixed_points:
print("WARNUNG: Keine Referenzpunkte gefunden!")
stations = list(parser.stations.values())
if stations:
first_station = min(stations, key=lambda s: s.timestamp)
adj.set_fixed_point(first_station.name)
print(f" -> Setze {first_station.name} als Festpunkt")
print(f"\n### Festpunkte: {adj.fixed_points}")
# Zeige berechnete Orientierungen
print(f"\n### Berechnete Orientierungen pro Station:")
for station_name, orientation in sorted(adj.orientations.items()):
print(f" {station_name}: {orientation:.4f} gon")
# Konsistenzprüfung
print("\n" + "=" * 80)
print("KONSISTENZPRÜFUNG")
print("=" * 80)
consistency = adj.check_consistency()
print(f"\n### Ergebnis: {'✅ KONSISTENT' if consistency['consistent'] else '❌ INKONSISTENT'}")
if not consistency['consistent']:
print("\n### Probleme gefunden:")
for issue in consistency['issues']:
print(f" ⚠️ {issue}")
print("\n### WICHTIG:")
print(" Die Koordinaten in der JXL-Datei wurden bereits von Trimble Access")
print(" berechnet und sind fertig. Die Beobachtungen (Kreislesungen) sind")
print(" rohe Messwerte, die nicht mit den berechneten Koordinaten konsistent")
print(" verglichen werden können, da die Orientierungen stark variieren.")
print("")
print(" Eine Netzausgleichung mit diesen Daten ist nicht sinnvoll, da sie")
print(" die bereits korrekten Koordinaten verschlechtern würde.")
print("")
print(" Empfehlung: Verwenden Sie die ausgeglichenen Koordinaten aus der JXL")
print(" direkt, ohne weitere Ausgleichung.")
# Ausgleichung durchführen (nur Residuen, keine Koordinatenänderung)
print("\n### Starte Residuenanalyse (keine Koordinatenänderung)...")
try:
result = adj.adjust(mode="residuals_only")
print(f"\n### Ergebnis:")
print(f" Konvergiert: {result.converged}")
print(f" Iterationen: {result.iterations}")
print(f" Sigma-0 posteriori: {result.sigma_0_posteriori:.6f}")
print(f" Redundanz: {result.redundancy}")
# PUNKTEVERGLEICH
print("\n" + "=" * 100)
print("PUNKTEVERGLEICH: VORHER vs. NACHHER")
print("=" * 100)
print(f"{'Punkt':<10} {'X_vorher':>12} {'Y_vorher':>12} {'Z_vorher':>10} | {'X_nachher':>12} {'Y_nachher':>12} {'Z_nachher':>10} | {'ΔX':>10} {'ΔY':>10} {'ΔZ':>10} {'Δ3D':>10}")
print("-" * 120)
deviations = []
for name, pt in sorted(adj.points.items()):
x_before, y_before, z_before = coords_before[name]
x_after, y_after, z_after = pt.x, pt.y, pt.z
dx = x_after - x_before
dy = y_after - y_before
dz = z_after - z_before
d3d = math.sqrt(dx**2 + dy**2 + dz**2)
deviations.append((name, dx, dy, dz, d3d, pt.is_fixed))
fixed_marker = "*" if pt.is_fixed else " "
print(f"{name:<10}{fixed_marker} {x_before:>12.4f} {y_before:>12.4f} {z_before:>10.4f} | "
f"{x_after:>12.4f} {y_after:>12.4f} {z_after:>10.4f} | "
f"{dx:>10.4f} {dy:>10.4f} {dz:>10.4f} {d3d:>10.4f}")
# PLAUSIBILITÄTSPRÜFUNG
print("\n" + "=" * 80)
print("PLAUSIBILITÄTSPRÜFUNG")
print("=" * 80)
# Sortiert nach größter Abweichung
deviations_sorted = sorted(deviations, key=lambda x: x[4], reverse=True)
# Warnungen
critical_errors = []
warnings = []
for name, dx, dy, dz, d3d, is_fixed in deviations_sorted:
if not is_fixed:
if d3d > 1.0:
critical_errors.append((name, d3d))
elif d3d > 0.1:
warnings.append((name, d3d))
if critical_errors:
print("\n🚨 KRITISCHE FEHLER (Abweichung > 1m):")
for name, d3d in critical_errors:
print(f"{name}: {d3d:.4f} m")
if warnings:
print("\n⚠️ WARNUNGEN (Abweichung > 10cm):")
for name, d3d in warnings:
print(f"{name}: {d3d:.4f} m")
# Statistik
non_fixed_deviations = [d for d in deviations if not d[5]]
if non_fixed_deviations:
d3d_values = [d[4] for d in non_fixed_deviations]
dx_values = [abs(d[1]) for d in non_fixed_deviations]
dy_values = [abs(d[2]) for d in non_fixed_deviations]
print("\n### Statistik (nur Neupunkte):")
print(f" Anzahl Punkte: {len(non_fixed_deviations)}")
print(f" Min Δ3D: {min(d3d_values):.6f} m")
print(f" Max Δ3D: {max(d3d_values):.6f} m")
print(f" Mittel Δ3D: {sum(d3d_values)/len(d3d_values):.6f} m")
print(f" Max |ΔX|: {max(dx_values):.6f} m")
print(f" Max |ΔY|: {max(dy_values):.6f} m")
if not critical_errors and not warnings:
print("\n✅ Alle Koordinaten unverändert (wie erwartet bei residuals_only)")
# RESIDUEN-ANALYSE
print("\n" + "=" * 80)
print("RESIDUENANALYSE")
print("=" * 80)
dir_obs = [o for o in adj.observations if o.obs_type == 'direction']
dist_obs = [o for o in adj.observations if o.obs_type == 'distance']
dir_residuals = [o.residual * 1000 for o in dir_obs] # in mgon
dist_residuals = [o.residual * 1000 for o in dist_obs] # in mm
if dir_residuals:
print(f"\n### Richtungsresiduen (in mgon):")
print(f" Anzahl: {len(dir_residuals)}")
print(f" Min: {min(dir_residuals):.2f} mgon")
print(f" Max: {max(dir_residuals):.2f} mgon")
print(f" RMSE: {math.sqrt(sum(r**2 for r in dir_residuals)/len(dir_residuals)):.2f} mgon")
if dist_residuals:
print(f"\n### Streckenresiduen (in mm):")
print(f" Anzahl: {len(dist_residuals)}")
print(f" Min: {min(dist_residuals):.2f} mm")
print(f" Max: {max(dist_residuals):.2f} mm")
print(f" RMSE: {math.sqrt(sum(r**2 for r in dist_residuals)/len(dist_residuals)):.2f} mm")
# Zeige größte Residuen
print(f"\n### Größte Richtungsresiduen:")
sorted_dir = sorted(dir_obs, key=lambda x: abs(x.residual), reverse=True)[:5]
for obs in sorted_dir:
print(f" {obs.from_station} -> {obs.to_point}: {obs.residual*1000:.2f} mgon")
print(f"\n### Größte Streckenresiduen:")
sorted_dist = sorted(dist_obs, key=lambda x: abs(x.residual), reverse=True)[:5]
for obs in sorted_dist:
print(f" {obs.from_station} -> {obs.to_point}: {obs.residual*1000:.2f} mm")
# Qualitätsbewertung
rmse_dir = math.sqrt(sum(r**2 for r in dir_residuals)/len(dir_residuals)) if dir_residuals else 0
rmse_dist = math.sqrt(sum(r**2 for r in dist_residuals)/len(dist_residuals)) if dist_residuals else 0
print("\n### Qualitätsbewertung:")
if rmse_dir < 10:
print(f" ✅ Richtungen: SEHR GUT (RMSE < 10 mgon)")
elif rmse_dir < 50:
print(f" ✅ Richtungen: GUT (RMSE < 50 mgon)")
elif rmse_dir < 100:
print(f" ⚠️ Richtungen: AKZEPTABEL (RMSE < 100 mgon)")
else:
print(f" ❌ Richtungen: SCHLECHT (RMSE = {rmse_dir:.2f} mgon)")
if rmse_dist < 5:
print(f" ✅ Strecken: SEHR GUT (RMSE < 5 mm)")
elif rmse_dist < 20:
print(f" ✅ Strecken: GUT (RMSE < 20 mm)")
elif rmse_dist < 50:
print(f" ⚠️ Strecken: AKZEPTABEL (RMSE < 50 mm)")
else:
print(f" ❌ Strecken: SCHLECHT (RMSE = {rmse_dist:.2f} mm)")
# Prüfe ob Problem besteht
max_d3d = max(d[4] for d in non_fixed_deviations) if non_fixed_deviations else 0
if max_d3d > 1.0:
print("\n" + "=" * 80)
print("🔍 FEHLERANALYSE")
print("=" * 80)
# Prüfe berechnete vs. beobachtete Richtungen
print("\n### Prüfung Richtungsbeobachtungen:")
for obs in dir_obs[:3]:
from_pt = adj.points.get(obs.from_station)
to_pt = adj.points.get(obs.to_point)
if from_pt and to_pt:
dx = to_pt.x - from_pt.x
dy = to_pt.y - from_pt.y
# Berechne Azimut in Gon (aus X,Y)
azimuth_calc = math.atan2(dx, dy) * 200.0 / math.pi
if azimuth_calc < 0:
azimuth_calc += 400.0
diff = obs.value - azimuth_calc
while diff > 200:
diff -= 400
while diff < -200:
diff += 400
print(f" {obs.from_station} -> {obs.to_point}:")
print(f" Beobachtet: {obs.value:.6f} gon")
print(f" Berechnet: {azimuth_calc:.6f} gon")
print(f" Differenz: {diff:.6f} gon ({diff*10000:.2f} mgon)")
# Prüfe berechnete vs. beobachtete Strecken
print("\n### Prüfung Streckenbeobachtungen:")
for obs in dist_obs[:3]:
from_pt = adj.points.get(obs.from_station)
to_pt = adj.points.get(obs.to_point)
if from_pt and to_pt:
dx = to_pt.x - from_pt.x
dy = to_pt.y - from_pt.y
dz = to_pt.z - from_pt.z
dist_calc_3d = math.sqrt(dx**2 + dy**2 + dz**2)
dist_calc_2d = math.sqrt(dx**2 + dy**2)
diff_3d = obs.value - dist_calc_3d
diff_2d = obs.value - dist_calc_2d
print(f" {obs.from_station} -> {obs.to_point}:")
print(f" Beobachtet: {obs.value:.6f} m")
print(f" Berechnet 3D: {dist_calc_3d:.6f} m (Diff: {diff_3d*1000:.2f} mm)")
print(f" Berechnet 2D: {dist_calc_2d:.6f} m (Diff: {diff_2d*1000:.2f} mm)")
except Exception as e:
print(f"FEHLER: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()