Skip to content

Commit

Permalink
Search area dimensions (#73)
Browse files Browse the repository at this point in the history
* FOV for vertical frustum

* Variable frustum

* Fixed trig errors

* Parameter for degrees or radians

* Fixed factor bug; decimal default values

* Search area dimensions unit tests

* Fixed linter

* Removed dots

* Deleted init.py

* Deleted more init files

* Search area dimensions (#71)

* FOV for vertical frustum

* Variable frustum

* Fixed trig errors

* Parameter for degrees or radians

* Fixed factor bug; decimal default values

* Added back the init files I wasn't supposed to delete

* Deleted duplicate
  • Loading branch information
luckyducky037 authored Oct 11, 2024
1 parent 1e0fb79 commit e51b6d1
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
60 changes: 60 additions & 0 deletions modules/search_area_dimensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Converts field of view to display dimensions in metres.
"""

from math import tan, pi


# Measurements from https://uwarg-docs.atlassian.net/wiki/spaces/CV/pages/2236613655/200+CV+Camera
FIELD_OF_VISION_X = 0.64889
FIELD_OF_VISION_Y = 0.41438


def search_area_dimensions(
height: int,
frustum_angle_x: float,
frustum_angle_y: float,
frustum_radians: bool = True,
field_of_vision_x: float = FIELD_OF_VISION_X,
field_of_vision_y: float = FIELD_OF_VISION_Y,
field_of_vision_radians: bool = True,
) -> "tuple[float, float]":
"""
Parameters:
- height: height of the drone, in metres
- frustum_angle_x: the left-right camera direction angle w/ respect to vertical
- frustum_angle_y: the up-down camera direction angle w/ respect to vertical
- frustum_radians: Boolean for whether frustum input is in radians or not
- field_of_vision_x: the horizontal field of vision of the camera, in radians (default to measured constant)
- field_of_vision_y: the vertical field of vision of the camera, in radians (default to measured constant)
- field_of_vision_radians: Boolean for whether field of vision input is in radians or not
Return:
- tuple containing the rectangular dimensions of the field of view of the camera, in metres
"""
frustum_factor = 1
if not frustum_radians:
frustum_factor = pi / 180
field_of_vision_factor = 1
if not field_of_vision_radians:
field_of_vision_factor = pi / 180

left_distance = (
tan((field_of_vision_x * field_of_vision_factor) / 2 - frustum_angle_x * frustum_factor)
* height
)
right_distance = (
tan((field_of_vision_x * field_of_vision_factor) / 2 + frustum_angle_x * frustum_factor)
* height
)
horizontal_distance = left_distance + right_distance
up_distance = (
tan((field_of_vision_y * field_of_vision_factor) / 2 - frustum_angle_y * frustum_factor)
* height
)
down_distance = (
tan((field_of_vision_y * field_of_vision_factor) / 2 + frustum_angle_y * frustum_factor)
* height
)
vertical_distance = up_distance + down_distance
return horizontal_distance, vertical_distance
103 changes: 103 additions & 0 deletions tests/unit/test_search_area_dimensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""
Test cases for the search_area_dimensions module.
"""

from math import pi, sqrt

from modules import search_area_dimensions

# Test functions use test fixture signature names and access class privates
# No enable
# pylint: disable=duplicate-code,protected-access,redefined-outer-name

THRESHOLD = 1e-6


def test_no_field_of_view() -> None:
"""
Testing 0 degree field of view.
"""
height = 10
frustum_angle_x = 10
frustum_angle_y = 20
field_of_vision_x = 0
field_of_vision_y = 0

x, y = search_area_dimensions.search_area_dimensions(
height, frustum_angle_x, frustum_angle_y, True, field_of_vision_x, field_of_vision_y, True
)

assert abs(x - 0) < THRESHOLD
assert abs(y - 0) < THRESHOLD


def test_vertical_field_of_view() -> None:
"""
Testing straight-down camera angle.
"""
height = 20
frustum_angle_x = 0
frustum_angle_y = 0
field_of_vision_x = pi / 2
field_of_vision_y = 2 * pi / 3

x, y = search_area_dimensions.search_area_dimensions(
height, frustum_angle_x, frustum_angle_y, True, field_of_vision_x, field_of_vision_y, True
)

assert abs(x - 40) < THRESHOLD
assert abs(y - 40 * sqrt(3)) < THRESHOLD


def test_side_vertical_field_of_view() -> None:
"""
Test for when one side of the field of view is vertical.
"""
height = 30
frustum_angle_x = pi / 8
frustum_angle_y = pi / 6
field_of_vision_x = pi / 4
field_of_vision_y = pi / 3

x, y = search_area_dimensions.search_area_dimensions(
height, frustum_angle_x, frustum_angle_y, True, field_of_vision_x, field_of_vision_y, True
)

assert abs(x - 30) < THRESHOLD
assert abs(y - 30 * sqrt(3)) < THRESHOLD


def test_askew_acute_field_of_view() -> None:
"""
Test for when field of view spreads outwards on both sides.
"""
height = 40
frustum_angle_x = pi / 12
frustum_angle_y = pi / 24
field_of_vision_x = pi / 2
field_of_vision_y = 7 * pi / 12

x, y = search_area_dimensions.search_area_dimensions(
height, frustum_angle_x, frustum_angle_y, True, field_of_vision_x, field_of_vision_y, True
)

assert abs(x - (40 * sqrt(3) + 40 / sqrt(3))) < THRESHOLD
assert abs(y - (40 + 40 * sqrt(3))) < THRESHOLD


def test_askew_obtuse_field_of_view() -> None:
"""
Test for when field of view spreads to one side from both directions.
"""
height = 50
frustum_angle_x = pi / 4
frustum_angle_y = 7 * pi / 24
field_of_vision_x = pi / 6
field_of_vision_y = pi / 12

x, y = search_area_dimensions.search_area_dimensions(
height, frustum_angle_x, frustum_angle_y, True, field_of_vision_x, field_of_vision_y, True
)

assert abs(x - (50 * sqrt(3) - 50 / sqrt(3))) < THRESHOLD
assert abs(y - (50 * sqrt(3) - 50)) < THRESHOLD

0 comments on commit e51b6d1

Please sign in to comment.