Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hotspot search path generation #77

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
55 changes: 55 additions & 0 deletions modules/generate_hotspot_search_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Generates search path for hotspots.
"""

import math

from . import plot_circular_path
from .common.modules import position_global_relative_altitude


MINIMUM_POINTS = 3


def generate_search_path(
center: position_global_relative_altitude.PositionGlobalRelativeAltitude,
search_radius: float,
search_area_dimensions: "tuple[float, float]",
) -> "tuple[bool, list[position_global_relative_altitude.PositionGlobalRelativeAltitude] | None]":
"""
Generates list of spline waypoints representing concentric rings for drone search path.

center: waypoint for center of circle.
search_radius: drone search radius.
search_area_dimensions: search area width, height

Returns: Success, list of waypoints.
"""
camera_horizontal_size, camera_vertical_size = search_area_dimensions

if camera_horizontal_size <= 0 or camera_vertical_size <= 0:
print(f"ERROR: Camera dimensions must be greater than 0: {search_area_dimensions}")
return False, None

if search_radius < 0:
print(f"ERROR: Search radius must be greater than or equal to 0: {search_radius}")
return False, None

current_radius = camera_horizontal_size / 2

all_waypoints = []

while current_radius <= search_radius:
circumference = 2 * math.pi * current_radius
num_points = max(MINIMUM_POINTS, int(circumference / camera_vertical_size))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would go with ceil for this to play it safer. Maybe also add a multiplier, like 1.2 to allow for more wiggle room.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also to error on the side of caution, also check if with camera_horizontal_size would yield a higher value.


result, waypoints = plot_circular_path.generate_circular_path(
center, current_radius, num_points
)
if not result or waypoints is None:
return False, None
all_waypoints.extend(waypoints)

current_radius += camera_horizontal_size

return True, all_waypoints
95 changes: 95 additions & 0 deletions tests/unit/test_generate_hotspot_search_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""
Hotspot search path generation unit tests.
"""

import math

from modules import generate_hotspot_search_path
from modules.common.modules import position_global_relative_altitude


class TestGenerateSearchPath:
"""
Test suite for generate_search_path.
"""

def test_generate_search_path(self) -> None:
"""
Test successful generation of search path.
"""
result, center = position_global_relative_altitude.PositionGlobalRelativeAltitude.create(
0.0, 0.0, 100.0
)
search_radius = 100.0
search_area_dimensions = (20.0, 10.0)

result, waypoints = generate_hotspot_search_path.generate_search_path(
center, search_radius, search_area_dimensions
)

assert result
assert waypoints

camera_width, camera_height = search_area_dimensions
current_radius = camera_width / 2
waypoint_index = 0

while current_radius <= search_radius:
circumference = 2 * math.pi * current_radius
num_points = max(3, int(circumference / camera_height))

assert len(waypoints[waypoint_index : waypoint_index + num_points]) == num_points

waypoint_index += num_points
current_radius += camera_width

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna be tedious... but could you hard code the test numbers? So like actual values that are expected of the path. Doesn't have to be extremely complicated, but enough to gauge performance. You can take a look at /Users/janez/Development/WARG/pathing/tests/unit/test_plot_circular_path.py for an example

def test_generate_search_path_zero_radius(self) -> None:
"""
Test generate_search_path with a zero search radius.
"""
result, center = position_global_relative_altitude.PositionGlobalRelativeAltitude.create(
0.0, 0.0, 100.0
)
search_radius = 0.0
search_area_dimensions = (3.0, 3.0)

result, waypoints = generate_hotspot_search_path.generate_search_path(
center, search_radius, search_area_dimensions
)

assert result
assert waypoints == []

def test_generate_search_path_negative_radius(self) -> None:
"""
Test generate_search_path with a negative search radius.
"""
result, center = position_global_relative_altitude.PositionGlobalRelativeAltitude.create(
0.0, 0.0, 100.0
)
search_radius = -10.0
search_area_dimensions = (3.0, 3.0)

result, waypoints = generate_hotspot_search_path.generate_search_path(
center, search_radius, search_area_dimensions
)

assert result is False
assert waypoints is None

def test_generate_search_path_invalid_dimensions(self) -> None:
"""
Test generate_search_path with invalid search area dimensions.
"""
result, center = position_global_relative_altitude.PositionGlobalRelativeAltitude.create(
0.0, 0.0, 100.0
)
search_radius = 10.0
search_area_dimensions = (-3, 3.0)

result, waypoints = generate_hotspot_search_path.generate_search_path(
center, search_radius, search_area_dimensions
)

assert result is False
assert waypoints is None