Skip to content

Commit

Permalink
Working on #43 and support for UV
Browse files Browse the repository at this point in the history
  • Loading branch information
tomarnepedersen committed Jan 16, 2025
1 parent 92a355f commit 8045f40
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 210 deletions.
6 changes: 3 additions & 3 deletions docs/make.bat
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set SOURCEDIR=source
set BUILDDIR=build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
Expand All @@ -25,6 +23,8 @@ if errorlevel 9009 (
exit /b 1
)

if "%1" == "" goto help

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -a -E
goto end

Expand Down
4 changes: 3 additions & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ ignore-names = [
]

[lint.pylint]
max-args = 7
max-args = 10
max-statements = 75
max-branches = 15

[lint.flake8-pytest-style]
raises-require-match-for = [
Expand Down
22 changes: 11 additions & 11 deletions src/trafficgen/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

@click.group()
@click_log.simple_verbosity_option(logger)
def main(args=None):
def main(args=None): # noqa: ANN001, ANN201, ARG001
"""Entry point for console script as configured in pyproject.toml.
Runs the command line interface and parses arguments and options entered on the console.
Expand Down Expand Up @@ -113,16 +113,16 @@ def main(args=None):
help="Plot individual traffic situation, specify an INTEGER value larger than 0.",
)
def gen_situation(
situations,
own_ship,
targets,
settings,
visualize,
col,
row,
visualize_situation,
output,
):
situations: str,
own_ship: str,
targets: str,
settings: str,
col: int,
row: int,
visualize_situation: int,
output: str,
visualize: bool, # noqa: FBT001
) -> None:
r"""Console script for trafficgen.
Example: \n
trafficgen gen-situation -s ./data/example_situations_input
Expand Down
60 changes: 30 additions & 30 deletions src/trafficgen/encounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def generate_encounter(

# Searching for encounter. Two loops used. Only vector time is locked in the
# first loop. In the second loop, beta and sog are assigned.
while not encounter_found and outer_counter < 5:
maximum_loops = 5
while not encounter_found and outer_counter < maximum_loops:
outer_counter += 1
inner_counter: int = 0

Expand Down Expand Up @@ -120,7 +121,7 @@ def generate_encounter(
own_ship_position_future, lat_lon0, settings.max_meeting_distance
)

while not encounter_found and inner_counter < 5:
while not encounter_found and inner_counter < maximum_loops:
inner_counter += 1
relative_sog = relative_sog_default
if relative_sog is None:
Expand Down Expand Up @@ -492,15 +493,16 @@ def find_start_position_target_ship(
alpha2, beta2, theta13_criteria, theta14_criteria, theta15_criteria, theta15
)

limit: float = 0.01
if (
desired_encounter_type is colreg_state1
and np.abs(convert_angle_0_to_2_pi_to_minus_pi_to_pi(np.abs(beta1 - desired_beta))) < 0.01
and np.abs(convert_angle_0_to_2_pi_to_minus_pi_to_pi(np.abs(beta1 - desired_beta))) < limit
):
start_position_target_ship = GeoPosition(lat=lat31, lon=lon31)
start_position_found = True
elif (
desired_encounter_type is colreg_state2
and np.abs(convert_angle_0_to_2_pi_to_minus_pi_to_pi(np.abs(beta2 - desired_beta))) < 0.01
and np.abs(convert_angle_0_to_2_pi_to_minus_pi_to_pi(np.abs(beta2 - desired_beta))) < limit
):
start_position_target_ship = GeoPosition(lat=lat32, lon=lon32)
start_position_found = True
Expand Down Expand Up @@ -578,16 +580,18 @@ def determine_colreg(
alpha_2_pi: float = alpha if alpha >= 0.0 else alpha + 2 * np.pi
beta_pi: float = beta if (beta >= 0.0) & (beta <= np.pi) else beta - 2 * np.pi

limit: float = 0.001

# Find appropriate rule set
if (beta > theta15[0]) & (beta < theta15[1]) & (abs(alpha) - theta13_criteria <= 0.001):
if (beta > theta15[0]) & (beta < theta15[1]) & (abs(alpha) - theta13_criteria <= limit):
return EncounterType.OVERTAKING_STAND_ON
if (alpha_2_pi > theta15[0]) & (alpha_2_pi < theta15[1]) & (abs(beta_pi) - theta13_criteria <= 0.001):
if (alpha_2_pi > theta15[0]) & (alpha_2_pi < theta15[1]) & (abs(beta_pi) - theta13_criteria <= limit):
return EncounterType.OVERTAKING_GIVE_WAY
if (abs(beta_pi) - theta14_criteria <= 0.001) & (abs(alpha) - theta14_criteria <= 0.001):
if (abs(beta_pi) - theta14_criteria <= limit) & (abs(alpha) - theta14_criteria <= limit):
return EncounterType.HEAD_ON
if (beta > 0) & (beta < theta15[0]) & (alpha > -theta15[0]) & (alpha - theta15_criteria <= 0.001):
if (beta > 0) & (beta < theta15[0]) & (alpha > -theta15[0]) & (alpha - theta15_criteria <= limit):
return EncounterType.CROSSING_GIVE_WAY
if (alpha_2_pi > 0) & (alpha_2_pi < theta15[0]) & (beta_pi > -theta15[0]) & (beta_pi - theta15_criteria <= 0.001):
if (alpha_2_pi > 0) & (alpha_2_pi < theta15[0]) & (beta_pi > -theta15[0]) & (beta_pi - theta15_criteria <= limit):
return EncounterType.CROSSING_STAND_ON
return EncounterType.NO_RISK_COLLISION

Expand Down Expand Up @@ -626,29 +630,24 @@ def calculate_relative_bearing(
# Absolute bearing of target ship relative to own ship
bng_own_ship_target_ship: float = 0.0
if e_own_ship == e_target_ship:
bng_own_ship_target_ship = 0.0 if n_own_ship <= n_target_ship else np.pi
elif e_own_ship < e_target_ship:
if n_own_ship <= n_target_ship:
bng_own_ship_target_ship = 0.0
bng_own_ship_target_ship = 1 / 2 * np.pi - np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
else:
bng_own_ship_target_ship = np.pi
bng_own_ship_target_ship = 1 / 2 * np.pi + np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
elif n_own_ship <= n_target_ship:
bng_own_ship_target_ship = 3 / 2 * np.pi + np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
else:
if e_own_ship < e_target_ship:
if n_own_ship <= n_target_ship:
bng_own_ship_target_ship = 1 / 2 * np.pi - np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
else:
bng_own_ship_target_ship = 1 / 2 * np.pi + np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
else:
if n_own_ship <= n_target_ship:
bng_own_ship_target_ship = 3 / 2 * np.pi + np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
else:
bng_own_ship_target_ship = 3 / 2 * np.pi - np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)
bng_own_ship_target_ship = 3 / 2 * np.pi - np.arctan(
abs(n_target_ship - n_own_ship) / abs(e_target_ship - e_own_ship)
)

# Bearing of own ship from the perspective of the contact
bng_target_ship_own_ship: float = bng_own_ship_target_ship + np.pi
Expand Down Expand Up @@ -763,7 +762,8 @@ def assign_beta_from_list(beta_limit: list[float]) -> float:
-------
* Relative bearing between own ship and target ship seen from own ship [rad]
"""
assert len(beta_limit) == 2
beta_limit_length = 2
assert len(beta_limit) == beta_limit_length
beta: float = beta_limit[0] + random.uniform(0, 1) * (beta_limit[1] - beta_limit[0])
return beta

Expand Down
10 changes: 8 additions & 2 deletions src/trafficgen/plot_traffic_situation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# pyright: reportUnknownMemberType=false
"""Functions to prepare and plot traffic situations."""

import logging
import math

import matplotlib.pyplot as plt
Expand Down Expand Up @@ -132,8 +133,13 @@ def plot_specific_traffic_situation(
"""
num_situations = len(traffic_situations)
if situation_number > num_situations:
print(
f"Situation_number specified higher than number of situations available, plotting last situation: {num_situations}" # noqa: E501
# Configure logging
logging.basicConfig(level=logging.INFO)

# Replace print with logging
logging.info(
f"Situation_number specified higher than number of situations available, "
f"plotting last situation: {num_situations}"
)
situation_number = num_situations

Expand Down
114 changes: 86 additions & 28 deletions src/trafficgen/read_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
from typing import Any, cast

from trafficgen.types import (
Encounter,
EncounterSettings,
Initial,
ShipStatic,
SituationInput,
TrafficSituation,
Waypoint,
)
from trafficgen.utils import deg_2_rad, knot_2_m_pr_s, min_2_s, nm_2_m

Expand Down Expand Up @@ -72,56 +75,111 @@ def convert_situation_data_from_maritime_to_si_units(situation: SituationInput)
Convert situation data which is given in maritime units to SI units.
Params:
* own_ship_file: Path to the own_ship_file file
* situation: Situation data to be converted
Returns
-------
* own_ship information
* situation: Converted situation data
"""
assert situation.own_ship is not None
assert situation.own_ship.initial is not None
assert situation.own_ship.initial.heading is not None
situation.own_ship.initial.position.lon = deg_2_rad(situation.own_ship.initial.position.lon)
situation.own_ship.initial.position.lat = deg_2_rad(situation.own_ship.initial.position.lat)
situation.own_ship.initial.cog = deg_2_rad(situation.own_ship.initial.cog)
situation.own_ship.initial.heading = deg_2_rad(situation.own_ship.initial.heading)
situation.own_ship.initial.sog = knot_2_m_pr_s(situation.own_ship.initial.sog)

situation.own_ship.initial = convert_own_ship_initial_data(situation.own_ship.initial)
if situation.own_ship.waypoints is not None:
for waypoint in situation.own_ship.waypoints:
waypoint.position.lat = deg_2_rad(waypoint.position.lat)
waypoint.position.lon = deg_2_rad(waypoint.position.lon)
if waypoint.turn_radius is not None:
waypoint.turn_radius = nm_2_m(waypoint.turn_radius)
if waypoint.leg is not None:
if waypoint.leg.starboard_xtd is not None:
waypoint.leg.starboard_xtd = nm_2_m(waypoint.leg.starboard_xtd)
if waypoint.leg.portside_xtd is not None:
waypoint.leg.portside_xtd = nm_2_m(waypoint.leg.portside_xtd)
if waypoint.leg.data is not None:
if waypoint.leg.data.sog is not None:
assert waypoint.leg.data.sog.value is not None
assert waypoint.leg.data.sog.interp_start is not None
assert waypoint.leg.data.sog.interp_end is not None
waypoint.leg.data.sog.value = knot_2_m_pr_s(waypoint.leg.data.sog.value)
waypoint.leg.data.sog.interp_start = nm_2_m(waypoint.leg.data.sog.interp_start)
waypoint.leg.data.sog.interp_end = nm_2_m(waypoint.leg.data.sog.interp_end)
situation.own_ship.waypoints = convert_own_ship_waypoints(situation.own_ship.waypoints)

assert situation.encounters is not None
for encounter in situation.encounters:
situation.encounters = convert_encounters(situation.encounters)

return situation


def convert_own_ship_initial_data(initial: Initial) -> Initial:
"""
Convert own ship initial data which is given in maritime units to SI units.
Params:
* initial: Own ship initial data to be converted
Returns
-------
* initial: Converted own ship initial data
"""
initial.position.lon = deg_2_rad(initial.position.lon)
initial.position.lat = deg_2_rad(initial.position.lat)
initial.cog = deg_2_rad(initial.cog)
assert initial.heading is not None
initial.heading = deg_2_rad(initial.heading)
initial.sog = knot_2_m_pr_s(initial.sog)
return initial


def convert_own_ship_waypoints(waypoints: list[Waypoint]) -> list[Waypoint]:
"""
Convert own ship waypoint data which is given in maritime units to SI units.
Parameters
----------
waypoints : list[Waypoint]
Waypoint data to be converted
Returns
-------
list[Waypoint]
Converted waypoint data
"""
for waypoint in waypoints:
waypoint.position.lat = deg_2_rad(waypoint.position.lat)
waypoint.position.lon = deg_2_rad(waypoint.position.lon)
if waypoint.turn_radius is not None:
waypoint.turn_radius = nm_2_m(waypoint.turn_radius)
if waypoint.leg is not None:
if waypoint.leg.starboard_xtd is not None:
waypoint.leg.starboard_xtd = nm_2_m(waypoint.leg.starboard_xtd)
if waypoint.leg.portside_xtd is not None:
waypoint.leg.portside_xtd = nm_2_m(waypoint.leg.portside_xtd)
if waypoint.leg.data is not None and waypoint.leg.data.sog is not None:
assert waypoint.leg.data.sog.value is not None
assert waypoint.leg.data.sog.interp_start is not None
assert waypoint.leg.data.sog.interp_end is not None
waypoint.leg.data.sog.value = knot_2_m_pr_s(waypoint.leg.data.sog.value)
waypoint.leg.data.sog.interp_start = nm_2_m(waypoint.leg.data.sog.interp_start)
waypoint.leg.data.sog.interp_end = nm_2_m(waypoint.leg.data.sog.interp_end)
return waypoints


def convert_encounters(encounters: list[Encounter]) -> list[Encounter]:
"""
Convert encounter data which is given in maritime units to SI units.
Parameters
----------
encounters : list[Encounter]
Encounter data to be converted
Returns
-------
list[Encounter]
Converted encounter data
"""
assert encounters is not None
beta_list_length = 2

for encounter in encounters:
beta: list[float] | float | None = encounter.beta
vector_time: float | None = encounter.vector_time
if beta is not None:
if isinstance(beta, list):
assert len(beta) == 2
assert len(beta) == beta_list_length
for i in range(len(beta)):
beta[i] = deg_2_rad(beta[i])
encounter.beta = beta
else:
encounter.beta = deg_2_rad(beta)
if vector_time is not None:
encounter.vector_time = min_2_s(vector_time)
return situation
return encounters


def read_own_ship_static_file(own_ship_static_file: Path) -> ShipStatic:
Expand Down
Loading

0 comments on commit 8045f40

Please sign in to comment.