import pytest import os import numpy as np import spiceypy as spice import json from unittest.mock import patch, PropertyMock import unittest from conftest import get_image_label, get_image_kernels, convert_kernels, compare_dicts import ale from ale.drivers.mex_drivers import MexHrscPds3NaifSpiceDriver, MexHrscIsisLabelNaifSpiceDriver from ale.formatters.usgscsm_formatter import to_usgscsm @pytest.fixture() def usgscsm_compare_dict(): return { "h5270_0000_ir2" : { "usgscsm" : { "radii": { "semimajor": 3396.19, "semiminor": 3376.2, "unit": "km" }, "sensor_position": { "positions": [ [ 711902.968354, 3209827.60790571, 1748326.86116295 ], [ 727778.89367768, 3287885.02005966, 1594882.70156054 ], [ 743098.34408384, 3361360.97987664, 1439236.15929773 ], [ 757817.60768561, 3430175.96634995, 1281609.7397002 ], [ 771895.91691839, 3494268.82029183, 1122231.09675971 ], [ 785295.6426853, 3553596.86562762, 961331.02292412 ] ], "velocities": [ [ 396.19391017, 1971.70523609, -3738.08862116 ], [ 383.09225613, 1860.35892297, -3794.8312507 ], [ 368.88320115, 1746.8383078, -3846.19171074 ], [ 353.63635876, 1631.58973504, -3892.0182367 ], [ 337.42645506, 1515.06674438, -3932.20958309 ], [ 320.33289561, 1397.72243165, -3966.71239887 ] ], "unit": "m" }, "sun_position": { "positions": [ [ 2.05222074E+11, 1.19628335E+11, 5.02349719E+10 ] ], "velocities": [ [ 8468758.54, -14528713.8, 8703.55212 ] ], "unit": "m" }, "sensor_orientation": { "quaternions": [ [ -0.09146728, -0.85085751, 0.51357522, 0.06257586 ], [ -0.09123532, -0.83858882, 0.53326329, 0.0638371 ], [ -0.09097193, -0.82586685, 0.55265188, 0.06514567 ], [ -0.09050679, -0.81278131, 0.57165363, 0.06638667 ], [ -0.08988786, -0.79935128, 0.59024631, 0.06757961 ], [ -0.08924306, -0.78551905, 0.60849234, 0.06879366 ] ] }, "detector_sample_summing": 1, "detector_line_summing": 1, "focal_length_model": { "focal_length": 174.82 }, "detector_center": { "line": 0.0, "sample": 2592.0 }, "starting_detector_line": 1, "starting_detector_sample": 0, "focal2pixel_lines": [ -7113.11359717265, 0.062856784318668, 142.857129028729 ], "focal2pixel_samples": [ -0.778052433438109, -142.857129028729, 0.062856784318668 ], "optical_distortion": { "radial": { "coefficients": [ 0.0, 0.0, 0.0 ] } }, "image_lines": 400, "image_samples": 1288, "name_platform": "MARS EXPRESS", "name_sensor": "HIGH RESOLUTION STEREO CAMERA", "reference_height": { "maxheight": 1000, "minheight": -1000, "unit": "m" }, "name_model": "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL", "interpolation_method": "lagrange", "line_scan_rate": [ [ 0.5, -94.88182842731476, 0.012800790786743165 ], [ 15086.5, 101.82391116023064, 0.013227428436279297 ] ], "starting_ephemeris_time": 255744592.07217148, "center_ephemeris_time": 255744693.90931007, "t0_ephemeris": -101.83713859319687, "dt_ephemeris": 40.734855437278746, "t0_quaternion": -101.83713859319687, "dt_quaternion": 40.734855437278746 }, "isis" : { "CameraVersion": 1, "NaifKeywords": { "BODY499_RADII": [ 3396.19, 3396.19, 3376.2 ], "BODY_FRAME_CODE": 10014, "BODY_CODE": 499, "INS-41210_FOV_FRAME": "MEX_HRSC_HEAD", "FRAME_-41210_NAME": "MEX_HRSC_HEAD", "INS-41210_CK_TIME_TOLERANCE": 1.0, "TKFRAME_-41210_AXES": [ 1.0, 2.0, 3.0 ], "TKFRAME_-41210_SPEC": "ANGLES", "FRAME_-41210_CLASS": 4.0, "INS-41210_FOV_ANGULAR_SIZE": [ 0.2, 0.659734 ], "INS-41210_OD_K": [ 0.0, 0.0, 0.0 ], "INS-41210_F/RATIO": 5.6, "INS-41210_PLATFORM_ID": -41000.0, "TKFRAME_-41210_ANGLES": [ -0.334, 0.0101, 0.0 ], "INS-41210_SPK_TIME_BIAS": 0.0, "FRAME_-41210_CENTER": -41.0, "TKFRAME_-41210_UNITS": "DEGREES", "INS-41210_BORESIGHT": [ 0.0, 0.0, 175.0 ], "INS-41210_CK_TIME_BIAS": 0.0, "FRAME_-41210_CLASS_ID": -41210.0, "INS-41210_IFOV": 4e-05, "INS-41210_FOV_BOUNDARY_CORNERS": [ 18.187, 60.0641, 175.0, 18.1281, -60.0399, 175.0, -18.1862, -60.0435, 175.0, -18.142 ], "INS-41210_FOV_SHAPE": "RECTANGLE", "TKFRAME_-41210_RELATIVE": "MEX_HRSC_BASE", "INS-41210_PIXEL_PITCH": 0.007, "INS-41210_FOCAL_LENGTH": 175.0, "BODY499_POLE_DEC": [ 52.8865, -0.0609, 0.0 ], "BODY499_POLE_RA": [ 317.68143, -0.1061, 0.0 ], "BODY499_PM": [ 176.63, 350.89198226, 0.0 ], "INS-41218_ITRANSL": [ -7113.11359717265, 0.062856784318668, 142.857129028729 ], "INS-41218_ITRANSS": [ -0.778052433438109, -142.857129028729, 0.062856784318668 ], "INS-41218_FOV_SHAPE": "RECTANGLE", "INS-41218_PIXEL_SIZE": [ 7.0, 7.0 ], "INS-41218_CK_REFERENCE_ID": 1.0, "INS-41218_FOV_FRAME": "MEX_HRSC_HEAD", "INS-41218_CCD_CENTER": [ 2592.5, 0.5 ], "INS-41218_CK_FRAME_ID": -41001.0, "INS-41218_F/RATIO": 5.6, "INS-41218_PIXEL_SAMPLES": 5184.0, "INS-41218_BORESIGHT_SAMPLE": 2592.5, "INS-41218_FILTER_BANDWIDTH": 90.0, "INS-41218_BORESIGHT_LINE": 0.0, "INS-41218_PIXEL_LINES": 1.0, "INS-41218_FOCAL_LENGTH": 174.82, "INS-41218_FOV_ANGULAR_SIZE": [ 0.2, 4e-05 ], "INS-41218_FILTER_BANDCENTER": 970.0, "INS-41218_TRANSX": [ 0.016461898406507, -0.006999999322408, 3.079982431615e-06 ], "INS-41218_TRANSY": [ 49.7917927568053, 3.079982431615e-06, 0.006999999322408 ], "INS-41218_FOV_BOUNDARY_CORNERS": [ 18.1982, 49.9121, 175.0, 18.1982, 49.9051, 175.0, -18.1693, 49.8901, 175.0, -18.1693 ], "INS-41218_BORESIGHT": [ 0.0151, 49.9039, 175.0 ], "INS-41218_IFOV": 4e-05 }, "InstrumentPointing": { "TimeDependentFrames": [ -41001, 1 ], "CkTableStartTime": 255744599.02748, "CkTableEndTime": 255744635.91477, "CkTableOriginalSize": 3, "EphemerisTimes": [ 255744599.02748, 255744623.61901, 255744635.91477 ], "Quaternions": [ [ -0.34147103206303764, 0.46006200041554185, -0.48264106492774883, -0.6624183666542334 ], [ -0.34862899148129517, 0.4555408857335137, -0.47327265910130095, -0.668545673735942 ], [ -0.3521802679309037, 0.45323805476596757, -0.46855266563769715, -0.6715673637959837 ] ], "AngularVelocity": [ [ 0.00035176331113592204, 0.0010154650024473105, 0.0003877175924478187 ], [ 0.0003524285580283372, 0.001014970147047595, 0.0003878218830533074 ], [ 0.00035026208236974156, 0.001017194110775444, 0.000384764361044439 ] ], "ConstantFrames": [ -41210, -41200, -41000, -41001 ], "ConstantRotation": [ -0.9999999844629888, 1.027590578527487e-06, 0.00017627525841189352, 1.2246232944813223e-16, -0.9999830090976747, 0.00582936668603668, 0.0001762782535384808, 0.0058293665954657434, 0.9999829935609271 ] }, "BodyRotation": { "TimeDependentFrames": [ 10014, 1 ], "CkTableStartTime": 255744599.02748, "CkTableEndTime": 255744635.91477, "CkTableOriginalSize": 3, "EphemerisTimes": [ 255744599.02748, 255744623.61901, 255744635.91477 ], "Quaternions": [ [ -0.6525755651363003, -0.0231514239139282, 0.3174415084289179, -0.6876336467074378 ], [ -0.6531746247480361, -0.022874748805603497, 0.31746156550431237, -0.6870646329712322 ], [ -0.6534739684048748, -0.022736404778153148, 0.31747150360998055, -0.68677993048033 ] ], "AngularVelocity": [ [ 3.1623981615137114e-05, -2.8803031775991542e-05, 5.6520727317788564e-05 ], [ 3.162398161506756e-05, -2.8803031776763114e-05, 5.652072731743428e-05 ], [ 3.1623981615032794e-05, -2.8803031777148914e-05, 5.6520727317257115e-05 ] ] }, "InstrumentPosition": { "SpkTableStartTime": 255744599.02748, "SpkTableEndTime": 255744635.91477, "SpkTableOriginalSize": 3, "EphemerisTimes": [ 255744599.02748, 255744623.61901, 255744635.91477 ], "Positions": [ [ 3508.767882205483, -1180.0905787748716, -404.6580659358628 ], [ 3509.6584138014186, -1143.4324359500313, -502.6029463204848 ], [ 3509.4431532823473, -1124.886654875713, -551.4851113671591 ] ], "Velocities": [ [ 0.07204008324341263, 1.4787375673363454, -3.987265079143158 ], [ 0.0003930097221548436, 1.5024971608640412, -3.9781429684078495 ], [ -0.03540185319234399, 1.5140837760694033, -3.9728346761041364 ] ] }, "SunPosition": { "SpkTableStartTime": 255744697.39357847, "SpkTableEndTime": 255744697.39357847, "SpkTableOriginalSize": 1, "EphemerisTimes": [ 255744697.39357847 ], "Positions": [ [ 97397666.49661352, -201380879.84291452, -94392949.82617083 ] ], "Velocities": [ [ 21.26085726371221, 7.17339564842172, 2.739589556465391 ] ] } } }} @pytest.fixture(scope="module") def test_kernels(): kernels = get_image_kernels('h5270_0000_ir2') updated_kernels, binary_kernels = convert_kernels(kernels) yield updated_kernels print(updated_kernels) for kern in binary_kernels: os.remove(kern) # Eventually all label/formatter combinations should be tested. For now, isis3/usgscsm and # pds3/isis will fail. @pytest.mark.xfail @pytest.mark.parametrize("label,formatter", [('isis3','isis'), ('pds3', 'usgscsm'), pytest.param('isis3','usgscsm', marks=pytest.mark.xfail), pytest.param('pds3','isis', marks=pytest.mark.xfail),]) def test_mex_load(test_kernels, formatter, usgscsm_compare_dict, label): label_file = get_image_label('h5270_0000_ir2', label) with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_ephemeris_times', \ new_callable=PropertyMock) as binary_ephemeris_times, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_exposure_durations', \ new_callable=PropertyMock) as binary_exposure_durations, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_lines', \ new_callable=PropertyMock) as binary_lines, \ patch('ale.drivers.mex_drivers.MexHrscIsisLabelNaifSpiceDriver.ephemeris_time', \ new_callable=PropertyMock) as ephemeris_time, \ patch('ale.drivers.mex_drivers.read_table_data', return_value=12345) as read_table_data, \ patch('ale.drivers.mex_drivers.parse_table', return_value={'EphemerisTime': [255744599.02748165, 255744684.33197814, 255744684.34504557], \ 'ExposureTime': [0.012800790786743165, 0.012907449722290038, 0.013227428436279297], \ 'LineStart': [1, 6665, 6666]}) as parse_table: ephemeris_time.return_value = [255744599.02748, 255744623.61901, 255744635.91477] binary_ephemeris_times.return_value = [255744599.02748165, 255744599.04028246, 255744795.73322123] binary_exposure_durations.return_value = [0.012800790786743165, 0.012800790786743165, 0.013227428436279297] binary_lines.return_value = [0.5, 1.5, 15086.5] usgscsm_isd = ale.load(label_file, props={'kernels': test_kernels}, formatter=formatter, verbose='true') assert compare_dicts(usgscsm_isd, usgscsm_compare_dict['h5270_0000_ir2'][formatter]) == [] # ========= Test mex pds3label and naifspice driver ========= class test_mex_pds3_naif(unittest.TestCase): def setUp(self): label = get_image_label("h5270_0000_ir2", "pds3") self.driver = MexHrscPds3NaifSpiceDriver(label) def test_short_mission_name(self): assert self.driver.short_mission_name=='mex' def test_odtk(self): assert self.driver.odtk == [0.0, 0.0, 0.0] def test_ikid(self): with patch('ale.drivers.mex_drivers.spice.bods2c', return_value=12345) as bods2c: assert self.driver.ikid == 12345 bods2c.assert_called_with('MEX_HRSC_HEAD') def test_fikid(self): with patch('ale.drivers.mex_drivers.spice.bods2c', return_value=12345) as bods2c: assert self.driver.fikid == 12345 bods2c.assert_called_with('MEX_HRSC_IR') def test_instrument_id(self): assert self.driver.instrument_id == 'MEX_HRSC_IR' def test_spacecraft_name(self): assert self.driver.spacecraft_name =='MEX' def test_focal_length(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.fikid', \ new_callable=PropertyMock) as fikid: fikid.return_value = -41218 assert self.driver.focal_length == 174.82 def test_focal2pixel_lines(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.fikid', \ new_callable=PropertyMock) as fikid: fikid.return_value = -41218 np.testing.assert_almost_equal(self.driver.focal2pixel_lines, [-7113.11359717265, 0.062856784318668, 142.857129028729]) def test_focal2pixel_samples(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.fikid', \ new_callable=PropertyMock) as fikid: fikid.return_value = -41218 np.testing.assert_almost_equal(self.driver.focal2pixel_samples, [-0.778052433438109, -142.857129028729, 0.062856784318668]) def test_pixel2focal_x(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.fikid', \ new_callable=PropertyMock) as fikid: fikid.return_value = -41218 np.testing.assert_almost_equal(self.driver.pixel2focal_x, [0.016461898406507, -0.006999999322408, 3.079982431615e-06]) def test_pixel2focal_y(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.fikid', \ new_callable=PropertyMock) as fikid: fikid.return_value = -41218 np.testing.assert_almost_equal(self.driver.pixel2focal_y, [49.7917927568053, 3.079982431615e-06, 0.006999999322408]) def test_detector_start_line(self): assert self.driver.detector_start_line == 1 def test_detector_center_line(self): assert self.driver.detector_center_line == 0.0 def test_detector_center_sample(self): assert self.driver.detector_center_sample == 2592.0 def test_center_ephemeris_time(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_ephemeris_times', \ new_callable=PropertyMock) as binary_ephemeris_times, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_exposure_durations', \ new_callable=PropertyMock) as binary_exposure_durations, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.ephemeris_start_time', new_callable=PropertyMock) as ephemeris_start_time: binary_ephemeris_times.return_value = [255744795.73322123] binary_exposure_durations.return_value = [0.013227428436279297] ephemeris_start_time.return_value = 255744592.07217148 assert self.driver.center_ephemeris_time == 255744693.90931007 def test_ephemeris_stop_time(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_ephemeris_times', \ new_callable=PropertyMock) as binary_ephemeris_times, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_exposure_durations', \ new_callable=PropertyMock) as binary_exposure_durations : binary_ephemeris_times.return_value = [255744795.73322123] binary_exposure_durations.return_value = [0.013227428436279297] assert self.driver.ephemeris_stop_time == 255744795.74644867 def test_line_scan_rate(self): with patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_ephemeris_times', \ new_callable=PropertyMock) as binary_ephemeris_times, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_exposure_durations', \ new_callable=PropertyMock) as binary_exposure_durations, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.binary_lines', \ new_callable=PropertyMock) as binary_lines, \ patch('ale.drivers.mex_drivers.MexHrscPds3NaifSpiceDriver.ephemeris_start_time', new_callable=PropertyMock) as ephemeris_start_time: binary_ephemeris_times.return_value = [0, 1, 2, 3, 5, 7, 9] binary_exposure_durations.return_value = [1, 1, 1, 2, 2, 2, 2] binary_lines.return_value = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5] ephemeris_start_time.return_value = 0 assert self.driver.line_scan_rate == ([0.5, 3.5], [-5.5, -2.5], [1, 2]) def test_sensor_model_version(self): assert self.driver.sensor_model_version == 1 # ========= Test mex pds3label and naifspice driver ========= class test_mex_isis3_naif(unittest.TestCase): def setUp(self): label = get_image_label("h5270_0000_ir2", "isis3") self.driver = MexHrscIsisLabelNaifSpiceDriver(label) def test_instrument_id(self): assert self.driver.instrument_id == 'MEX_HRSC_IR' def test_ikid(self): with patch('ale.drivers.mex_drivers.spice.bods2c', return_value=12345) as bods2c: assert self.driver.ikid == 12345 bods2c.assert_called_with('MEX_HRSC_HEAD') def test_fikid(self): with patch('ale.drivers.mex_drivers.spice.bods2c', return_value=12345) as bods2c: assert self.driver.fikid == 12345 bods2c.assert_called_with('MEX_HRSC_IR') def test_ephemeris_start_time(self): with patch('ale.drivers.mex_drivers.read_table_data', return_value=12345) as read_table_data, \ patch('ale.drivers.mex_drivers.parse_table', return_value={'EphemerisTime': [255744599.02748165, 255744684.33197814, 255744684.34504557], \ 'ExposureTime': [0.012800790786743165, 0.012907449722290038, 0.013227428436279297], \ 'LineStart': [1, 6665, 6666]}) as parse_table: assert self.driver.ephemeris_start_time == 255744599.02748165 def test_line_scan_rate(self): with patch('ale.drivers.mex_drivers.read_table_data', return_value=12345) as read_table_data, \ patch('ale.drivers.mex_drivers.parse_table', return_value={'EphemerisTime': [255744599.02748165, 255744684.33197814, 255744684.34504557], \ 'ExposureTime': [0.012800790786743165, 0.012907449722290038, 0.013227428436279297], \ 'LineStart': [1, 6665, 6666]}) as parse_table: assert self.driver.line_scan_rate == ([1, 6665, 6666], [255744599.02748165, 255744684.33197814, 255744684.34504557], [0.012800790786743165, 0.012907449722290038, 0.013227428436279297]) def test_ephemeris_stop_time(self): with patch('ale.drivers.mex_drivers.read_table_data', return_value=12345) as read_table_data, \ patch('ale.drivers.mex_drivers.parse_table', return_value={'EphemerisTime': [255744599.02748165, 255744684.33197814, 255744684.34504557], \ 'ExposureTime': [0.012800790786743165, 0.012907449722290038, 0.013227428436279297], \ 'LineStart': [1, 6665, 6666]}) as parse_table: assert self.driver.ephemeris_stop_time == 255744684.34504557 + ((15088 - 6666 + 1) * 0.013227428436279297) def test_ephemeris_center_time(self): with patch('ale.drivers.mex_drivers.read_table_data', return_value=12345) as read_table_data, \ patch('ale.drivers.mex_drivers.parse_table', return_value={'EphemerisTime': [255744599.02748165, 255744684.33197814, 255744684.34504557], \ 'ExposureTime': [0.012800790786743165, 0.012907449722290038, 0.013227428436279297], \ 'LineStart': [1, 6665, 6666]}) as parse_table: assert self.driver.center_ephemeris_time == (255744599.02748165 + 255744684.34504557 + ((15088 - 6666 + 1) * 0.013227428436279297)) / 2 def test_sensor_model_version(self): assert self.driver.sensor_model_version == 1