From 92116f14860980326f4fe4361f2f00163a6afcd2 Mon Sep 17 00:00:00 2001
From: Amy Stamile <astamile@usgs.gov>
Date: Thu, 25 Apr 2024 19:14:33 -0700
Subject: [PATCH] fixed tests.

---
 knoten/sensor_utils.py    |   4 +-
 knoten/shape.py           |   6 ++-
 knoten/utils.py           |   1 +
 tests/test_csm.py         |  10 +---
 tests/test_illuminator.py |  24 +++++++++
 tests/test_sensorutils.py | 108 +++++++++++++++++++++++---------------
 tests/test_shape.py       |  28 ++++++++++
 7 files changed, 127 insertions(+), 54 deletions(-)
 create mode 100644 tests/test_illuminator.py
 create mode 100644 tests/test_shape.py

diff --git a/knoten/sensor_utils.py b/knoten/sensor_utils.py
index cfadaee..be985aa 100644
--- a/knoten/sensor_utils.py
+++ b/knoten/sensor_utils.py
@@ -34,7 +34,7 @@ def phase_angle(image_pt, sensor, shape, illuminator):
 
     sensor_state = csm.get_state(sensor, image_pt)
     ground_pt = shape.intersect_surface(sensor_state["sensorPos"], sensor_state["lookVec"])
-  
+
     illum_pos = illuminator.get_position_from_csm_sensor(sensor, ground_pt)
 
     vec_a = utils.Point(sensor_state["sensorPos"].x - ground_pt.x, 
@@ -44,7 +44,7 @@ def phase_angle(image_pt, sensor, shape, illuminator):
     vec_b = utils.Point(illum_pos.x - ground_pt.x, 
                         illum_pos.y - ground_pt.y, 
                         illum_pos.z - ground_pt.z)
-  
+
     return np.rad2deg(utils.sep_angle(vec_a, vec_b))
 
 def emission_angle(image_pt, sensor, shape):
diff --git a/knoten/shape.py b/knoten/shape.py
index 509e15d..441ab06 100644
--- a/knoten/shape.py
+++ b/knoten/shape.py
@@ -8,7 +8,7 @@ class Ellipsoid:
     A biaxial ellipsoid shape model.
     """
 
-    def __init__(self, semi_major, semi_minor = None):
+    def __init__(self, semi_major, semi_minor=None, median=None):
         """
         Create an ellipsoid shape model from a set of radii
 
@@ -23,9 +23,13 @@ class Ellipsoid:
         self.b = semi_major
         self.c = semi_major
 
+        if median is not None:
+            self.b = median
+
         if semi_minor is not None:
             self.c = semi_minor
 
+
     @classmethod
     def from_csm_sensor(cls, sensor):
         semi_major, semi_minor = csm.get_radii(sensor)
diff --git a/knoten/utils.py b/knoten/utils.py
index cde4fe5..1165b85 100644
--- a/knoten/utils.py
+++ b/knoten/utils.py
@@ -29,6 +29,7 @@ def sep_angle(a_vec, b_vec):
     : np.ndarray
     """
     dot_prod = a_vec.x * b_vec.x + a_vec.y * b_vec.y + a_vec.z * b_vec.z
+    print(dot_prod)
     dot_prod /= magnitude(a_vec) * magnitude(b_vec)
 
     if(dot_prod >= 1.0): return 0.0
diff --git a/tests/test_csm.py b/tests/test_csm.py
index ede5308..b31e9b9 100644
--- a/tests/test_csm.py
+++ b/tests/test_csm.py
@@ -85,12 +85,4 @@ def test_get_state(mock_sensor, pt):
         "imagePoint": pt
     }
 
-    assert state == expected
-
-
-@mock.patch.object(csm, 'get_radii', return_value=(10, 10))
-def test_get_surface_normal(mock_sensor):
-    ground_pt = utils.Point(1, 0, 0)
-    normal = csm.get_surface_normal(mock_sensor, ground_pt)
-
-    assert normal == (1.0, 0.0, 0.0)
\ No newline at end of file
+    assert state == expected
\ No newline at end of file
diff --git a/tests/test_illuminator.py b/tests/test_illuminator.py
new file mode 100644
index 0000000..e7d8688
--- /dev/null
+++ b/tests/test_illuminator.py
@@ -0,0 +1,24 @@
+import unittest
+from unittest import mock
+
+import csmapi
+
+from knoten import utils
+from knoten.illuminator import Illuminator
+
+class TestIlluminator(unittest.TestCase):
+    
+    def test_get_position_from_csm_sensor(self):
+        mock_sensor = mock.MagicMock(spec=csmapi.RasterGM)
+        mock_sensor.getIlluminationDirection.return_value = utils.Point(1.0, 10.0, 0.0)
+
+        test_illum = Illuminator()
+
+        ground_pt = utils.Point(10.0, 10.0, 0.0)
+        
+        position = test_illum.get_position_from_csm_sensor(mock_sensor, ground_pt)
+    
+        self.assertEqual(position, (9.0, 0.0, 0.0))
+
+    def tearDown(self):
+        pass
\ No newline at end of file
diff --git a/tests/test_sensorutils.py b/tests/test_sensorutils.py
index c7be63a..b6731c2 100644
--- a/tests/test_sensorutils.py
+++ b/tests/test_sensorutils.py
@@ -3,7 +3,7 @@ import pytest
 
 import numpy as np
 import csmapi
-from knoten import csm, sensor_utils, utils, bundle
+from knoten import csm, sensor_utils, utils, bundle, shape, illuminator
 
 @pytest.fixture
 def mock_sensor():
@@ -14,32 +14,46 @@ def mock_sensor():
 def pt():
     return csmapi.ImageCoord(0.0, 0.0)
 
-@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0)})
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0,0,0))
-def test_phase_angle(mock_sensor, pt):
-    mock_sensor.getIlluminationDirection.return_value = utils.Point(100.0, 100.0, 0.0)
-    phase_angle = sensor_utils.phase_angle(pt, mock_sensor)
+@pytest.fixture
+def test_shape():
+    return shape.Ellipsoid(0, 0)
 
-    np.testing.assert_array_equal(phase_angle, 135.0)
+@pytest.fixture
+def test_illuminator():
+    return illuminator.Illuminator(0, 0)
 
 
-@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0)})
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0,0,0))
-def test_emission_angle(mock_sensor, pt):
-    with mock.patch.object(csm, 'get_surface_normal', return_value=utils.Point(-1,0,0)) as mock_normal:
-        emission_angle = sensor_utils.emission_angle(pt, mock_sensor)
+def test_phase_angle(mock_sensor, pt, test_shape, test_illuminator):
+    with (
+        mock.patch.object(illuminator.Illuminator, 'get_position_from_csm_sensor', return_value=utils.Point(100.0, 100.0, 0.0)),
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)}),
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0))
+    ):
+        phase_angle = sensor_utils.phase_angle(pt, mock_sensor, test_shape, test_illuminator)
 
-        mock_normal.assert_called()
+        np.testing.assert_allclose(phase_angle, 45.0)
 
-    np.testing.assert_array_equal(emission_angle, 180.0)
 
+def test_emission_angle(mock_sensor, pt, test_shape):
+    with (
+        mock.patch.object(shape.Ellipsoid, 'get_surface_normal', return_value=utils.Point(-1,0,0)), 
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0)),
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)}),
+        mock.patch.object(illuminator.Illuminator, 'get_position_from_csm_sensor', return_value=utils.Point(100.0, 100.0, 0.0))
+    ):
+        emission_angle = sensor_utils.emission_angle(pt, mock_sensor, test_shape)
 
-@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(-100.0, 0.0, 0.0)})
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0,0,0))
-def test_slant_distance(mock_sensor, pt):
-    slant_distance = sensor_utils.slant_distance(pt, mock_sensor)
+        np.testing.assert_array_equal(emission_angle, 180.0)
 
-    np.testing.assert_array_equal(slant_distance, 100.0)
+
+def test_slant_distance(mock_sensor, pt, test_shape):
+    with (
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)}),
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0))
+    ):
+        slant_distance = sensor_utils.slant_distance(pt, mock_sensor, test_shape)
+
+        np.testing.assert_array_equal(slant_distance, 100.0)
 
 
 @mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(-100.0, 0.0, 0.0)})
@@ -56,18 +70,18 @@ def test_sub_spacecraft_point(mock_sensor, pt):
     np.testing.assert_array_equal(sub_spacecraft_point, [90.0, 0.0])
 
 
-@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0)})
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(10.0, 0.0, 0.0))
-def test_local_radius_intersection(mock_sensor, pt):
-    local_radius = sensor_utils.local_radius(pt, mock_sensor)
+@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)})
+@mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(10.0, 0.0, 0.0))
+def test_local_radius_intersection(mock_sensor, pt, test_shape):
+    local_radius = sensor_utils.local_radius(pt, mock_sensor, test_shape)
 
     np.testing.assert_array_equal(local_radius, 10.0)
 
 
-@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(-1000.0, 0.0, 0.0)})
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(10.0, 0.0, 0.0))
-def test_local_radius_ground(mock_sensor, pt):
-    local_radius = sensor_utils.local_radius(pt, mock_sensor)
+@mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(1000.0, 0.0, 0.0), 'lookVec': utils.Point(-1000.0, 0.0, 0.0)})
+@mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(10.0, 0.0, 0.0))
+def test_local_radius_ground(mock_sensor, pt, test_shape):
+    local_radius = sensor_utils.local_radius(pt, mock_sensor, test_shape)
 
     np.testing.assert_array_equal(local_radius, 10.0)
 
@@ -79,25 +93,35 @@ def test_right_ascension_declination(mock_sensor, pt):
     np.testing.assert_array_equal(right_ascension_declination, [180.0, 0.0])
 
 
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0.0, 0.0, 0.0))
-@mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8]))
-def test_line_resolution(mock_sensor, pt):
-    line_resolution = sensor_utils.line_resolution(pt, mock_sensor)
+def test_line_resolution(mock_sensor, pt, test_shape):
+    with (
+        mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8])),
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0)),
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)})
+    ):
+        line_resolution = sensor_utils.line_resolution(pt, mock_sensor, test_shape)
+
+        np.testing.assert_array_equal(line_resolution, 6.0)
 
-    np.testing.assert_array_equal(line_resolution, 6.0)
 
 
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0.0, 0.0, 0.0))
-@mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8]))
-def test_sample_resolution(mock_sensor, pt):
-    sample_resolution = sensor_utils.sample_resolution(pt, mock_sensor)
+def test_sample_resolution(mock_sensor, pt, test_shape):
+    with (
+        mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8])),
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0)),
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)})
+    ):
+        sample_resolution = sensor_utils.sample_resolution(pt, mock_sensor, test_shape)
 
-    np.testing.assert_array_equal(sample_resolution, 9.0)
+        np.testing.assert_array_equal(sample_resolution, 9.0)
 
 
-@mock.patch.object(csm, 'generate_ground_point', return_value=utils.Point(0.0, 0.0, 0.0))
-@mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8]))
-def test_pixel_resolution(mock_sensor, pt):
-    pixel_resolution = sensor_utils.pixel_resolution(pt, mock_sensor)
+def test_pixel_resolution(mock_sensor, pt, test_shape):
+    with (
+        mock.patch.object(bundle, 'compute_image_partials', return_value=np.array([2, 1, 4, 4, 4, 8])),
+        mock.patch.object(shape.Ellipsoid, 'intersect_surface', return_value=utils.Point(0,0,0)),
+        mock.patch.object(csm, 'get_state', return_value={'sensorPos': utils.Point(100.0, 0.0, 0.0), 'lookVec': utils.Point(-1.0, 0.0, 0.0)})
+    ):
+        pixel_resolution = sensor_utils.pixel_resolution(pt, mock_sensor, test_shape)
 
-    np.testing.assert_array_equal(pixel_resolution, 7.5)
\ No newline at end of file
+        np.testing.assert_array_equal(pixel_resolution, 7.5)
\ No newline at end of file
diff --git a/tests/test_shape.py b/tests/test_shape.py
new file mode 100644
index 0000000..07cb559
--- /dev/null
+++ b/tests/test_shape.py
@@ -0,0 +1,28 @@
+import unittest
+from unittest import mock
+
+from knoten import shape, utils
+
+class TestEllipsoid(unittest.TestCase):
+
+    def test_get_surface_normal(self):
+        test_shape = shape.Ellipsoid(10, 10)
+
+        ground_pt = utils.Point(1, 0, 0)
+        normal = test_shape.get_surface_normal(ground_pt)
+        
+        self.assertEqual(normal, (1.0, 0.0, 0.0))
+
+    def test_intersect_surface(self):
+        test_shape = shape.Ellipsoid(10, 10)
+        sensor_pos = utils.Point(100, 0, 0)
+        look_vec = utils.Point(-1, 0, 0)
+
+        intersection = test_shape.intersect_surface(sensor_pos, look_vec)
+
+        self.assertEqual(intersection.x, 10.0)
+        self.assertEqual(intersection.y, 0.0)
+        self.assertEqual(intersection.z, 0.0)
+
+    def tearDown(self):
+        pass
\ No newline at end of file
-- 
GitLab