Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ session_logs/*/
# Test artifacts
test_camera.jpg
.pytest_cache/
.pytest_temp/
.coverage
htmlcov/

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,5 @@ max-line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = "--basetemp=.pytest_temp"
tmp_path_retention_policy = "none"
9 changes: 0 additions & 9 deletions src/openflight/kld7/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@
existing builds but will not receive further development.
"""

import warnings

from .tracker import KLD7Tracker
from .types import KLD7Angle, KLD7Frame

warnings.warn(
"The K-LD7 angle radar is deprecated; OpenFlight has moved to a more "
"capable radar chip. K-LD7 support is kept for existing builds only.",
DeprecationWarning,
stacklevel=2,
)

__all__ = ["KLD7Angle", "KLD7Frame", "KLD7Tracker"]
10 changes: 8 additions & 2 deletions src/openflight/kld7/radc.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@
VERTICAL_LEGACY_NAIVE_CONFIDENCE_MAX = 0.35
VERTICAL_EARLY_CONTEXT_CONFIDENCE_PENALTY = 0.08

# Default horizontal K-LD7 RADC angle acceptance limit (degrees)
DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG = 15.0


def parse_radc_payload(payload: bytes) -> dict[str, np.ndarray]:
"""Parse a 3072-byte RADC payload into six uint16 channel arrays.
Expand Down Expand Up @@ -1249,7 +1252,10 @@ def empty(reason: str, has_radc: bool, warnings: tuple[str, ...]) -> RADCFrameDi
warnings.append("far_from_ops_bin")
if orientation == "vertical" and (angle_centroid < 0.0 or angle_centroid > 45.0):
warnings.append("outside_vertical_bounds")
if orientation == "horizontal" and abs(angle_centroid) > 15.0:
if (
orientation == "horizontal"
and abs(angle_centroid) > DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG
):
warnings.append("outside_horizontal_bounds")

return RADCFrameDiagnostics(
Expand Down Expand Up @@ -1426,7 +1432,7 @@ def extract_launch_angle(
spectrum_source: str = "f1a",
ops_anchored_peak_min_snr: float = OPS_ANCHORED_PEAK_MIN_SNR,
require_ops_anchored_peak: bool = False,
horizontal_angle_limit_deg: float = 15.0,
horizontal_angle_limit_deg: float = DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
vertical_estimator: str = "naive",
shot_timestamp: float | None = None,
impact_timestamp: float | None = None,
Expand Down
17 changes: 14 additions & 3 deletions src/openflight/kld7/tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
import logging
import threading
import time
import warnings
from collections import deque
from importlib.util import find_spec
from pathlib import Path
from typing import Optional

from ..launch_monitor import ClubType
from ..serial_latency import log_usb_serial_latency_timer
from .radc import RADC_PAYLOAD_BYTES, VERTICAL_FLIGHT_WINDOW_NET_DISTANCE_FT
from .radc import (
DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
RADC_PAYLOAD_BYTES,
VERTICAL_FLIGHT_WINDOW_NET_DISTANCE_FT,
)
from .types import KLD7Angle, KLD7Frame

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -139,7 +144,7 @@ class KLD7Tracker:
radc_vertical_impact_energy_threshold = 3.0
radc_horizontal_impact_energy_threshold = 1.85
radc_horizontal_retry_impact_energy_threshold = 0.5
radc_horizontal_angle_limit_deg = 15.0
radc_horizontal_angle_limit_deg = DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG
vertical_estimator = "naive"
mount_tilt_deg = 18.0
ball_distance_ft = 5.5
Expand All @@ -163,12 +168,18 @@ def __init__(
radc_vertical_impact_energy_threshold: float = 3.0,
radc_horizontal_impact_energy_threshold: float = 1.85,
radc_horizontal_retry_impact_energy_threshold: float = 0.5,
radc_horizontal_angle_limit_deg: float = 15.0,
radc_horizontal_angle_limit_deg: float = DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
vertical_estimator: str = "naive",
mount_tilt_deg: float = 18.0,
ball_distance_ft: float = 5.5,
vertical_flight_window_net_distance_ft: float = VERTICAL_FLIGHT_WINDOW_NET_DISTANCE_FT,
):
warnings.warn(
"The K-LD7 angle radar is deprecated; OpenFlight has moved to a more "
"capable radar chip. K-LD7 support is kept for existing builds only.",
DeprecationWarning,
stacklevel=2,
)
self.port = port
self.range_m = range_m
self.speed_kmh = speed_kmh
Expand Down
6 changes: 5 additions & 1 deletion src/openflight/rolling_buffer/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1820,7 +1820,11 @@ def process_capture(
ball_reading = None
logger.warning("[PROCESSOR] No outbound readings in overlapping timeline")

ball_timestamp_ms = ball_reading.timestamp_ms if ball_reading else 68.0
ball_timestamp_ms = (
ball_reading.timestamp_ms
if ball_reading
else (len(capture.i_samples) / self.SAMPLE_RATE) * 500.0
)

# Find club speed
club_speed_mph, club_timestamp_ms = self.find_club_speed(
Expand Down
13 changes: 7 additions & 6 deletions src/openflight/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from flask_socketio import SocketIO

from .ballistics import resolve_launch, simulate
from .kld7.radc import DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG
from .launch_monitor import SPIN_CONFIDENCE_HIGH, ClubType, Shot
from .ops243 import (
UART_BAUD_COMMANDS,
Expand Down Expand Up @@ -157,7 +158,7 @@
"radc_vertical_impact_energy_threshold": 3.0,
"radc_horizontal_impact_energy_threshold": 1.85,
"radc_horizontal_retry_impact_energy_threshold": 0.5,
"radc_horizontal_angle_limit_deg": 15.0,
"radc_horizontal_angle_limit_deg": DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
}
active_kld7_radc_tuning: dict = dict(_DEFAULT_KLD7_RADC_TUNING)

Expand Down Expand Up @@ -1219,7 +1220,7 @@ def init_kld7(
radc_vertical_impact_energy_threshold=3.0,
radc_horizontal_impact_energy_threshold=1.85,
radc_horizontal_retry_impact_energy_threshold=0.5,
radc_horizontal_angle_limit_deg=15.0,
radc_horizontal_angle_limit_deg=DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
vertical_estimator="naive",
mount_tilt_deg=18.0,
ball_distance_ft=5.5,
Expand Down Expand Up @@ -2441,11 +2442,11 @@ def on_shot_detected(shot: Shot):
float(
active_kld7_radc_tuning.get(
"radc_horizontal_angle_limit_deg",
15.0,
DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
)
)
if experimental_kld7_radc_tuning
else 15.0
else DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG
)
accepted_h, horizontal_selection_details = _select_horizontal_radar_launch(
kld7_angle_h, horizontal_limit
Expand Down Expand Up @@ -3048,7 +3049,7 @@ def _fire_cloud_push(session_logger):
if log_dir is not None:
fire_push_async(config, log_dir=log_dir)
except Exception: # pylint: disable=broad-exception-caught
pass
logger.debug("[SERVER] Cloud push trigger failed", exc_info=True)


def _run_cloud_push_for_ui():
Expand Down Expand Up @@ -3914,7 +3915,7 @@ def main():
parser.add_argument(
"--experimental-kld7-horizontal-angle-limit",
type=float,
default=15.0,
default=DEFAULT_RADC_HORIZONTAL_ANGLE_LIMIT_DEG,
help="Experimental horizontal K-LD7 RADC angle acceptance limit in degrees (default: 15.0)",
)
args = parser.parse_args()
Expand Down
7 changes: 7 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Protocol-agnostic: records bytes received and can send scripted JSON replies or
drop the connection. Used by both GSPro and OpenGolfSim connector tests.
"""

import json
import socket
import sys
Expand Down Expand Up @@ -40,6 +41,8 @@ def _run(self):
self._client_sock, _ = self._sock.accept()
except socket.timeout:
continue
except OSError:
break
self._client_sock.settimeout(0.2)
# Send any scripted replies that were queued before connect
for reply in list(self.scripted_replies):
Expand Down Expand Up @@ -98,6 +101,10 @@ def disconnect_client(self) -> None:
def stop(self) -> None:
self._stop.set()
self.disconnect_client()
try:
self._sock.close()
except OSError:
pass
self._thread.join(timeout=2.0)


Expand Down
4 changes: 4 additions & 0 deletions tests/test_cloud_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests for the openflight-cloud config module."""

import json
import os
import stat

import pytest
Expand Down Expand Up @@ -39,6 +40,9 @@ def test_defaults_endpoint_and_enabled_when_missing(self, tmp_path):


class TestSaveConfig:
@pytest.mark.skipif(
os.name == "nt", reason="POSIX permission bits are not representable on Windows"
)
def test_writes_file_with_0600_permissions(self, tmp_path):
path = tmp_path / "nested" / "cloud.json"
config = cfg.CloudConfig(device_token="tok", device_id="id")
Expand Down
Loading