def setUp(self):
     nav = Navigation(beating_angle=45, utm_zone=30)
     self.hp = HeadingPlan(nav, waypoint=LatLon(50.7, -0.98),
                         tack_decision_samples=100,
                         tack_line_offset=0.01)
     self.hp.nav.update_position(DummyNSF(50.7, -1.02))
 def setUp(self):
     nav = Navigation(beating_angle=45, utm_zone=30)
     self.hp = HeadingPlan(nav, waypoint=LatLon(50.7, -0.98),
                         tack_line_offset=0.01)
     self.hp.nav.update_position(DummyNSF(50.7, -1.02))
class HeadingPlanTests(unittest.TestCase):
    def setUp(self):
        nav = Navigation(beating_angle=45, utm_zone=30)
        self.hp = HeadingPlan(nav, waypoint=LatLon(50.7, -0.98),
                            tack_decision_samples=100,
                            tack_line_offset=0.01)
        self.hp.nav.update_position(DummyNSF(50.7, -1.02))
        # Should head east

    def test_complete_tack_to_port(self):
        self.hp.sailing_state = 'tack_to_port_tack'
        self.hp.nav.wind_direction = 50

        self.hp.nav.heading = 310
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')
        self.assertEqual(goal, 45)

        self.hp.nav.wind_direction = 350
        self.hp.nav.heading = 10
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')
        self.assertEqual(goal, 45)

        self.hp.nav.wind_direction = 313
        self.hp.nav.heading = 47
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertGreater(goal, 45)

    def test_continue_tack_to_stbd(self):
        self.hp.sailing_state = 'tack_to_stbd_tack'
        self.hp.nav.heading = 200
        self.hp.nav.wind_direction = 340  # Wind from the south
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')
        self.assertEqual(goal, 135)

        self.hp.nav.wind_direction = 10
        self.hp.nav.heading = 170
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')
        self.assertEqual(goal, 135)

        self.hp.nav.wind_direction = 48
        self.hp.nav.heading = 132
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertLess(goal, 135)

    def test_plain_sailing(self):
        self.hp.nav.wind_direction = 260
        self.hp.nav.heading = 110
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        # Heading ~= 90, but not exactly, because LatLon calculations are
        # not on a plane.
        self.assertGreater(goal, 89)
        self.assertLess(goal, 91)

    def test_tack_to_port(self):
        self.hp.nav.wind_direction = 90
        self.hp.nav.heading = 280  # We're reaching the wrong way!
        self.hp.tack_voting.reset(0)
        for _ in range(75):  # On tack threshold
            self.hp.tack_voting.vote(1)
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')

    def test_tack_to_stbd(self):
        self.hp.nav.wind_direction = 270
        self.hp.nav.heading = 280  # We're reaching the wrong way!
        self.hp.tack_voting.reset(1)
        for _ in range(75):  # On tack threshold
            self.hp.tack_voting.vote(0)
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')

    def test_to_windward_port_tack(self):
        self.hp.nav.wind_direction = 270
        self.hp.nav.heading = 180
        self.hp.tack_voting.votes_sum = 45  # Below tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertEqual(goal, 135)

        # Time to switch tack
        self.hp.tack_voting.votes_sum = 15  # Beyond tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')

    def test_to_windward_stbd_tack(self):
        self.hp.nav.wind_direction = 90
        self.hp.nav.heading = 0
        self.hp.tack_voting.votes_sum = 45  # Below tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertEqual(goal, 45)

        # Time to switch tack
        self.hp.tack_voting.votes_sum = 85  # Above tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')

    def test_end_condition(self):
        assert not self.hp.check_end_condition()
        self.hp.nav.update_position(DummyNSF(50.7, -0.9800001))
        assert self.hp.check_end_condition()

    def test_distance_heading_to_waypoint(self):
        d, h = self.hp.distance_heading_to_waypoint()
        self.assertGreater(d, 2500)
        self.assertLess(d, 3000)
        self.assertGreater(h, 85)
        self.assertLess(h, 95)
class HeadingPlanTests(unittest.TestCase):
    def setUp(self):
        nav = Navigation(beating_angle=45, utm_zone=30)
        self.hp = HeadingPlan(nav, waypoint=LatLon(50.7, -0.98),
                            tack_line_offset=0.01)
        self.hp.nav.update_position(DummyNSF(50.7, -1.02))
        # Should head east

    def test_complete_tack_to_port(self):
        self.hp.sailing_state = 'tack_to_port_tack'
        self.hp.nav.wind_direction = 50

        self.hp.nav.heading = 310
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')
        self.assertEqual(goal, 45)

        self.hp.nav.wind_direction = 350
        self.hp.nav.heading = 10
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')
        self.assertEqual(goal, 45)

        self.hp.nav.wind_direction = 313
        self.hp.nav.heading = 47
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertGreater(goal, 45)

    def test_continue_tack_to_stbd(self):
        self.hp.sailing_state = 'tack_to_stbd_tack'
        self.hp.nav.heading = 200
        self.hp.nav.wind_direction = 340  # Wind from the south
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')
        self.assertEqual(goal, 135)

        self.hp.nav.wind_direction = 10
        self.hp.nav.heading = 170
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')
        self.assertEqual(goal, 135)

        self.hp.nav.wind_direction = 48
        self.hp.nav.heading = 132
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertLess(goal, 135)

    def test_plain_sailing(self):
        self.hp.nav.wind_direction = 260
        self.hp.nav.heading = 110
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        # Heading ~= 90, but not exactly, because LatLon calculations are
        # not on a plane.
        self.assertGreater(goal, 89)
        self.assertLess(goal, 91)

    def test_tack_to_port(self):
        self.hp.nav.wind_direction = 90
        self.hp.nav.heading = 280  # We're reaching the wrong way!
        self.hp.tack_voting.reset(0)
        for _ in range(75):  # On tack threshold
            self.hp.tack_voting.vote(1)
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')

    def test_tack_to_stbd(self):
        self.hp.nav.wind_direction = 270
        self.hp.nav.heading = 280  # We're reaching the wrong way!
        self.hp.tack_voting.reset(1)
        for _ in range(75):  # On tack threshold
            self.hp.tack_voting.vote(0)
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')

    def test_to_windward_port_tack(self):
        self.hp.nav.wind_direction = 270
        self.hp.nav.heading = 180
        self.hp.tack_voting.votes_sum = 45  # Below tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertEqual(goal, 135)

        # Time to switch tack
        self.hp.tack_voting.votes_sum = 15  # Beyond tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_stbd_tack')

    def test_to_windward_stbd_tack(self):
        self.hp.nav.wind_direction = 90
        self.hp.nav.heading = 0
        self.hp.tack_voting.votes_sum = 45  # Below tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'normal')
        self.assertEqual(goal, 45)

        # Time to switch tack
        self.hp.tack_voting.votes_sum = 85  # Above tack threshold
        state, goal = self.hp.calculate_state_and_goal()
        self.assertEqual(state, 'tack_to_port_tack')

    def test_end_condition(self):
        assert not self.hp.check_end_condition()
        self.hp.nav.update_position(DummyNSF(50.7, -0.9800001))
        assert self.hp.check_end_condition()

    def test_distance_heading_to_waypoint(self):
        d, h = self.hp.distance_heading_to_waypoint()
        self.assertGreater(d, 2500)
        self.assertLess(d, 3000)
        self.assertGreater(h, 85)
        self.assertLess(h, 95)