diff --git a/backend/config/settings.py b/backend/config/settings.py index 5133a7d..0f15ca9 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -91,6 +91,12 @@ USE_TZ = True STATIC_URL = 'static/' CORS_ALLOW_ALL_ORIGINS = True +CORS_EXPOSE_HEADERS = [ + 'X-Blitz-Frame-Seq', + 'X-Blitz-Backend-Received-Unix-Ns', + 'X-Blitz-Frame-Hash', + 'X-Blitz-BSide-Capture-To-Send-Ms', +] DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/backend/monitoring/telemetry.py b/backend/monitoring/telemetry.py index 65f4f8c..e9680d2 100644 --- a/backend/monitoring/telemetry.py +++ b/backend/monitoring/telemetry.py @@ -105,13 +105,30 @@ class GpsDataService: } def _build_payload_from_metadata(self, metadata: FrameTrailerMetadata) -> dict[str, Any]: - timestamp_seconds = metadata.timestamp_ns / 1_000_000_000 updated_at = _utc_from_epoch(metadata.received_at) or "" + if not metadata.has_gps_fix: + return { + "has_fix": False, + "utc_time": "--:--:--", + "latitude": None, + "longitude": None, + "raw_latitude_hex": f"0x{metadata.raw_latitude_hex}", + "raw_longitude_hex": f"0x{metadata.raw_longitude_hex}", + "satellites": None, + "altitude_m": None, + "coordinate_system": "WGS84", + "source_sentence": "VIDEO_TRAILER", + "raw_coordinate_format": VIDEO_TRAILER_COORDINATE_FORMAT, + "source_mode": "video-frame-trailer-no-fix", + "updated_at": updated_at, + } + + timestamp_seconds = metadata.timestamp_ns / 1_000_000_000 return { "has_fix": True, "utc_time": datetime.fromtimestamp(timestamp_seconds, timezone.utc).strftime("%H:%M:%S"), - "latitude": round(metadata.latitude, 6), - "longitude": round(metadata.longitude, 6), + "latitude": round(metadata.latitude, 6) if metadata.latitude is not None else None, + "longitude": round(metadata.longitude, 6) if metadata.longitude is not None else None, "raw_latitude_hex": f"0x{metadata.raw_latitude_hex}", "raw_longitude_hex": f"0x{metadata.raw_longitude_hex}", "satellites": None, diff --git a/backend/monitoring/video.py b/backend/monitoring/video.py index b1b321e..7b137bd 100644 --- a/backend/monitoring/video.py +++ b/backend/monitoring/video.py @@ -103,13 +103,17 @@ class VideoDisplayProbeStore: @dataclass(frozen=True) class FrameTrailerMetadata: timestamp_ns: int - latitude: float - longitude: float + latitude: float | None + longitude: float | None capture_to_send_ms: int raw_latitude_hex: str raw_longitude_hex: str received_at: float + @property + def has_gps_fix(self) -> bool: + return self.latitude is not None and self.longitude is not None + @dataclass(frozen=True) class VideoDisplayProbeStatus: @@ -271,21 +275,23 @@ class OmniSocketVideoReceiver: if timestamp_ms <= 0: return None - if not math.isfinite(latitude) or not math.isfinite(longitude): - return None - if not (-90.0 <= latitude <= 90.0) or not (-180.0 <= longitude <= 180.0): - return None - if abs(latitude) < 1e-9 and abs(longitude) < 1e-9: - return None timestamp_ns = timestamp_ms * VIDEO_TRAILER_TIMESTAMP_MULTIPLIER_NS if abs(time.time_ns() - timestamp_ns) > VIDEO_TRAILER_TIMESTAMP_MAX_SKEW_NS: return None + gps_fix_available = ( + math.isfinite(latitude) + and math.isfinite(longitude) + and (-90.0 <= latitude <= 90.0) + and (-180.0 <= longitude <= 180.0) + and not (abs(latitude) < 1e-9 and abs(longitude) < 1e-9) + ) + return FrameTrailerMetadata( timestamp_ns=timestamp_ns, - latitude=latitude, - longitude=longitude, + latitude=latitude if gps_fix_available else None, + longitude=longitude if gps_fix_available else None, capture_to_send_ms=int(capture_to_send_ms), raw_latitude_hex=trailer[8:16].hex(), raw_longitude_hex=trailer[16:24].hex(),