Files
FitTrack2/FitnessSync/scratch/trace_turns.py
2026-01-11 06:06:43 -08:00

147 lines
4.8 KiB
Python

import sys
import os
import math
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import logging
sys.path.append('/app/backend')
from src.services.discovery import SegmentDiscoveryService
from src.models.activity import Activity
from src.utils.geo import calculate_bearing, haversine_distance
from src.services.parsers import extract_activity_data
logging.basicConfig(level=logging.INFO)
def trace_activity(activity_id):
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:password@localhost:5433/fitbit_garmin_sync")
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db = SessionLocal()
# 1. Fetch Activity
act = db.query(Activity).filter(Activity.garmin_activity_id == str(activity_id)).first()
if not act:
print(f"Activity {activity_id} NOT FOUND.")
return
print(f"Tracing Activity {act.id} ({act.garmin_activity_id})")
# 2. Extract Data
# Parser returns a dict: {'points': [], 'timestamps': [], ...}
result = extract_activity_data(act.file_content, act.file_type)
points = result['points']
timestamps = result['timestamps']
# Filter points (same logic as service)
clean_points = []
clean_ts = []
for i in range(len(points)):
if points[i][0] is not None and points[i][1] is not None:
clean_points.append(points[i])
clean_ts.append(timestamps[i])
print(f"Clean points: {len(clean_points)}")
# 3. Simulate Analysis Loop
current_segment = [clean_points[0]]
last_bearing = None
segments_found = 0
skipped_dist = 0
max_dist = 0
max_time_diff = 0
max_bearing_diff = 0
for i in range(1, len(clean_points)):
p1 = clean_points[i-1]
p2 = clean_points[i]
# dist
dist = haversine_distance(p1[1], p1[0], p2[1], p2[0])
max_dist = max(max_dist, dist)
if dist < 2.0:
skipped_dist += 1
continue
bearing = calculate_bearing(p1[1], p1[0], p2[1], p2[0])
is_turn = False
diff = 0
if last_bearing is not None:
diff = abs(bearing - last_bearing)
if diff > 180: diff = 360 - diff
max_bearing_diff = max(max_bearing_diff, diff)
if diff > 60:
is_turn = True
print(f"TURN DETECTED at index {i}: Bearing {last_bearing:.1f} -> {bearing:.1f} (Diff: {diff:.1f})")
elif diff > 10: # Log even smaller turns to see noise
print(f" Minor Turn at index {i}: Bearing {last_bearing:.1f} -> {bearing:.1f} (Diff: {diff:.1f})")
last_bearing = bearing
t1 = clean_ts[i-1]
t2 = clean_ts[i]
time_diff = (t2 - t1).total_seconds()
max_time_diff = max(max_time_diff, time_diff)
is_pause = time_diff > 10
if is_pause:
print(f"PAUSE DETECTED at index {i}: {time_diff}s")
if is_pause or is_turn:
if len(current_segment) > 10:
segments_found += 1
print(f" -> Segment Created (Points: {len(current_segment)})")
current_segment = [p2]
else:
current_segment.append(p2)
print(f"\nStats:")
print(f" Total Points: {len(clean_points)}")
print(f" Skipped (dist < 5m): {skipped_dist}")
print(f" Max Point-to-Point Dist: {max_dist:.2f}m")
print(f" Max Time Diff: {max_time_diff}s")
print(f" Max Bearing Diff: {max_bearing_diff:.1f}")
print(f" Segments Found (from splits): {segments_found}")
from src.utils.geo import ramer_douglas_peucker
for eps in [2.0, 5.0, 10.0, 15.0]:
print(f"\n--- Riper RDP Trace (epsilon={eps}m) ---")
simplified = ramer_douglas_peucker(clean_points, eps)
print(f"Simplified points: {len(simplified)} (from {len(clean_points)})")
last_bearing = None
turns_found = 0
for i in range(1, len(simplified)):
p1 = simplified[i-1]
p2 = simplified[i]
bearing = calculate_bearing(p1[1], p1[0], p2[1], p2[0])
if last_bearing is not None:
diff = abs(bearing - last_bearing)
if diff > 180: diff = 360 - diff
if diff > 60:
print(f"TURN: {last_bearing:.1f} -> {bearing:.1f} (Diff: {diff:.1f})")
turns_found += 1
elif diff > 45:
print(f" Minor Turn (>45): {last_bearing:.1f} -> {bearing:.1f} (Diff: {diff:.1f})")
last_bearing = bearing
print(f"Total Turns > 60: {turns_found}")
if __name__ == "__main__":
trace_activity(21465710074)