diff --git a/ale/base/data_naif.py b/ale/base/data_naif.py index 5fe0baa0a57bd002fb6fc228cb94e28cd0cbfe4a..422c3247bd79f42b8bf913c34485e657867f4ec7 100644 --- a/ale/base/data_naif.py +++ b/ale/base/data_naif.py @@ -331,8 +331,8 @@ class NaifSpice(): vel.append(state[3:]) # By default, spice works in km, and the vector returned by spkezr points the opposite # direction to what ALE needs, so it must be multiplied by (-1) - self._position = [p * -1000 for p in pos] - self._velocity = [v * -1000 for v in vel] + self._position = np.asarray([p * -1000 for p in pos]) + self._velocity = np.asarray([v * -1000 for v in vel]) return self._position, self._velocity, self.ephemeris_time @property diff --git a/ale/base/type_sensor.py b/ale/base/type_sensor.py index 43e421cc1a1d500d4f3722f81518b495c862e1ee..9c26ebeab0fa06e4cb388e07e1dd3c96f08c0ed8 100644 --- a/ale/base/type_sensor.py +++ b/ale/base/type_sensor.py @@ -51,7 +51,7 @@ class LineScanner(): : ndarray ephemeris times split based on image lines """ - return np.linspace(self.ephemeris_start_time, self.ephemeris_stop_time, self.image_lines / 64) + return np.linspace(self.ephemeris_start_time, self.ephemeris_stop_time, max(2, int(self.image_lines / 64))) @property def ephemeris_stop_time(self): diff --git a/ale/drivers/mes_drivers.py b/ale/drivers/mes_drivers.py index 2998dc29ee8ec027e260d295da376aa5dca7b6e6..915be1297ab86605880f907c3090a887e86c63c2 100644 --- a/ale/drivers/mes_drivers.py +++ b/ale/drivers/mes_drivers.py @@ -107,7 +107,7 @@ class MessengerMdisPds3NaifSpiceDriver(Framer, Pds3Label, NaifSpice, Driver): : double focal length in meters """ - coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS '.format(self.fikid), 0, 6) + coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS'.format(self.fikid), 0, 6) # reverse coeffs, MDIS coeffs are listed a_0, a_1, a_2 ... a_n where # numpy wants them a_n, a_n-1, a_n-2 ... a_0 @@ -223,8 +223,24 @@ class MessengerMdisPds3NaifSpiceDriver(Framer, Pds3Label, NaifSpice, Driver): class MessengerMdisIsisLabelNaifSpiceDriver(IsisLabel, NaifSpice, Framer, Driver): """ Driver for reading MDIS ISIS3 Labels. These are Labels that have been ingested - into ISIS from PDS EDR images but have not been spiceinit'd yet. + into ISIS from PDS EDR images. Any SPCIE data attached by the spiceinit application + will be ignored. """ + @property + def platform_name(self): + """ + Returns the name of the platform containing the sensor. This is usually + the spacecraft name. + + Messenger MDIS ISIS labels use upper camel case so this converts it to + all upper case. + + Returns + ------- + : str + Spacecraft name + """ + return super().platform_name.upper() @property def instrument_id(self): @@ -315,7 +331,7 @@ class MessengerMdisIsisLabelNaifSpiceDriver(IsisLabel, NaifSpice, Framer, Driver : double focal length in meters """ - coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS '.format(self.fikid), 0, 5) + coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS'.format(self.fikid), 0, 6) # reverse coeffs, MDIS coeffs are listed a_0, a_1, a_2 ... a_n where # numpy wants them a_n, a_n-1, a_n-2 ... a_0 f_t = np.poly1d(coeffs[::-1]) diff --git a/tests/pytests/test_dawn_drivers.py b/tests/pytests/test_dawn_drivers.py index fef09fcadd55e97cd8f71c5cde2be0fc7b8e785c..7a195cc87fdf2ce5fa577752d9bf61f95be11fd8 100644 --- a/tests/pytests/test_dawn_drivers.py +++ b/tests/pytests/test_dawn_drivers.py @@ -65,7 +65,6 @@ def test_kernels(): def test_dawn_load(test_kernels, usgscsm_compare_dict): label_file = get_image_label('FC21A0038582_15170161546F6F') usgscsm_isd = ale.load(label_file, props={'kernels': test_kernels}, formatter='usgscsm') - print(usgscsm_isd) assert compare_dicts(usgscsm_isd, usgscsm_compare_dict) == [] diff --git a/tests/pytests/test_mdis_drivers.py b/tests/pytests/test_mdis_drivers.py index 3bc3135804cbe0a220c71172e16e0fca01e122ab..c322893d277cb930085c27917b5750e26bcce80a 100644 --- a/tests/pytests/test_mdis_drivers.py +++ b/tests/pytests/test_mdis_drivers.py @@ -4,230 +4,206 @@ import numpy as np import spiceypy as spice from importlib import reload import json +import unittest +from unittest.mock import patch -from conftest import get_image_label, get_image_kernels, convert_kernels - +from conftest import get_image_label, get_image_kernels, convert_kernels, compare_dicts import ale from ale.drivers.mes_drivers import MessengerMdisPds3NaifSpiceDriver from ale.drivers.mes_drivers import MessengerMdisIsisLabelNaifSpiceDriver -@pytest.fixture(scope="module", autouse=True) +@pytest.fixture(scope='module') def test_kernels(): kernels = get_image_kernels('EN1072174528M') updated_kernels, binary_kernels = convert_kernels(kernels) - spice.furnsh(updated_kernels) yield updated_kernels - spice.unload(updated_kernels) for kern in binary_kernels: os.remove(kern) -@pytest.fixture(params=["Pds3NaifDriver", "IsisNaifDriver"]) -def driver(request): - if request.param == "IsisNaifDriver": - label = get_image_label("EN1072174528M", "isis3") - return MessengerMdisIsisLabelNaifSpiceDriver(label) +@pytest.fixture() +def usgscsm_compare_dict(): + return { + 'radii': { + 'semimajor': 2439.4, + 'semiminor': 2439.4, + 'unit': 'km'}, + 'sensor_position': { + 'positions': np.array([[ -629496.48621395, -1582946.00913133, 1783952.64031042]]), + 'velocities': np.array([[1732.18666313, 2502.76825324, 2412.65232223]]), + 'unit': 'm'}, + 'sun_position': { + 'positions': np.array([[-4.68946673e+10, -5.36158427e+08, 2.71167863e+07]]), + 'velocities': np.array([[-4629.73346128, 256.72086237, 10.63960444]]), + 'unit': 'm'}, + 'sensor_orientation': { + 'quaternions': np.array([[ 0.93418372, 0.00144773, -0.00449382, -0.35676112]])}, + 'detector_sample_summing': 2, + 'detector_line_summing': 2, + 'focal_length_model': { + 'focal_length': 549.5535053027719}, + 'detector_center': { + 'line': 512, + 'sample': 512}, + 'starting_detector_line': 1, + 'starting_detector_sample': 9, + 'focal2pixel_lines': [0.0, 0.0, 71.42857143], + 'focal2pixel_samples': [0.0, 71.42857143, 0.0], + 'optical_distortion': { + 'transverse': { + 'x': [0.0, 1.001854269623802, 0.0, 0.0, -0.0005094440474941111, 0.0, 1.004010471468856e-05, 0.0, 1.004010471468856e-05, 0.0], + 'y': [0.0, 0.0, 1.0, 0.0009060010594996751, 0.0, 0.0003574842626620758, 0.0, 1.004010471468856e-05, 0.0, 1.004010471468856e-05]}}, + 'image_lines': 512, + 'image_samples': 512, + 'name_platform': 'MESSENGER', + 'name_sensor': 'MERCURY DUAL IMAGING SYSTEM NARROW ANGLE CAMERA', + 'reference_height': { + 'maxheight': 1000, + 'minheight': -1000, + 'unit': 'm'}, + 'name_model': 'USGS_ASTRO_FRAME_SENSOR_MODEL', + 'center_ephemeris_time': 483122606.85252464} + +@pytest.mark.parametrize("label_type", ["pds3", "isis3"]) +def test_load(test_kernels, usgscsm_compare_dict, label_type): + label_file = get_image_label('EN1072174528M', label_type) + + usgscsm_isd_str = ale.loads(label_file, props={'kernels': test_kernels}, formatter='usgscsm') + usgscsm_isd_obj = json.loads(usgscsm_isd_str) - else: + assert compare_dicts(usgscsm_isd_obj, usgscsm_compare_dict) == [] + +# ========= Test Pds3 Label and NAIF Spice driver ========= +class test_pds3_naif(unittest.TestCase): + + def setUp(self): label = get_image_label("EN1072174528M", "pds3") - return MessengerMdisPds3NaifSpiceDriver(label) + self.driver = MessengerMdisPds3NaifSpiceDriver(label) -def test_short_mission_name(driver): - assert driver.short_mission_name=='mes' + def test_short_mission_name(self): + assert self.driver.short_mission_name=='mes' -def test_no_metakernels(driver, tmpdir, monkeypatch): - monkeypatch.setenv('ALESPICEROOT', str(tmpdir)) - reload(ale) + def test_spacecraft_name(self): + assert self.driver.spacecraft_name == 'MESSENGER' - with pytest.raises(ValueError): - with driver as failure: - pass + def test_fikid(self): + with patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.spacecraft_name == 'MESSENGER' -def test_no_spice_root(driver, monkeypatch): - monkeypatch.delenv('ALESPICEROOT', raising=False) - reload(ale) + def test_instrument_id(self): + assert self.driver.instrument_id == 'MSGR_MDIS_NAC' - with pytest.raises(EnvironmentError): - with driver as failure: - pass + def test_sampling_factor(self): + assert self.driver.sampling_factor == 2 -def test_load(test_kernels): - label_file = get_image_label('EN1072174528M') + def test_focal_length(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([pow(4.07, -x) for x in np.arange(6)])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.focal_length == pytest.approx(6.0) + gdpool.assert_called_with('INS-12345_FL_TEMP_COEFFS', 0, 6) - usgscsm_isd_str = ale.loads(label_file, props={'kernels': test_kernels}, formatter='usgscsm') - usgscsm_isd_obj = json.loads(usgscsm_isd_str) + def test_detector_start_sample(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([10.0])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_start_sample == 10.0 + gdpool.assert_called_with('INS-12345_FPUBIN_START_SAMPLE', 0, 1) + + def test_detector_start_line(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([10.0])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_start_line == 10.0 + gdpool.assert_called_with('INS-12345_FPUBIN_START_LINE', 0, 1) - assert usgscsm_isd_obj['name_platform'] == 'MESSENGER' - assert usgscsm_isd_obj['name_sensor'] == 'MERCURY DUAL IMAGING SYSTEM NARROW ANGLE CAMERA' - assert usgscsm_isd_obj['name_model'] == 'USGS_ASTRO_FRAME_SENSOR_MODEL' - -def test_test_image_lines(driver): - assert driver.image_lines == 512 - -def test_image_samples(driver): - assert driver.image_samples == 512 - -def test_usgscsm_distortion_model(driver): - dist = driver.usgscsm_distortion_model - assert 'transverse' in dist - assert 'x' in dist['transverse'] - assert 'y' in dist['transverse'] - np.testing.assert_almost_equal(dist['transverse']['x'], - [0.0, - 1.0018542696238023333, - 0.0, - 0.0, - -5.0944404749411114E-4, - 0.0, - 1.0040104714688569E-5, - 0.0, - 1.0040104714688569E-5, - 0.0]) - np.testing.assert_almost_equal(dist['transverse']['y'], - [0.0, - 0.0, - 1.0, - 9.06001059499675E-4, - 0.0, - 3.5748426266207586E-4, - 0.0, - 1.0040104714688569E-5, - 0.0, - 1.0040104714688569E-5]) - -def test_detector_start_line(driver): - assert driver.detector_start_line == 1 - -def test_detector_start_sample(driver): - assert driver.detector_start_sample == 9 - -def test_sample_summing(driver): - assert driver.sample_summing == 2 - -def test_line_summing(driver): - assert driver.line_summing == 2 - -def test_platform_name(driver): - assert driver.platform_name.upper() == 'MESSENGER' - -def test_sensor_name(driver): - assert driver.sensor_name == 'MERCURY DUAL IMAGING SYSTEM NARROW ANGLE CAMERA' - -def test_target_body_radii(driver): - np.testing.assert_equal(driver.target_body_radii, [2439.4, 2439.4, 2439.4]) - -def test_focal_length(driver): - assert driver.focal_length == 549.5535053027719 - -def test_detector_center_line(driver): - assert driver.detector_center_line == 512 - -def test_detector_center_sample(driver): - assert driver.detector_center_sample == 512 - -def test_sensor_position(driver): - """ - Returns - ------- - : (positions, velocities, times) - a tuple containing a list of positions, a list of velocities, and a list of times - """ - position, velocity, time = driver.sensor_position - image_et = spice.scs2e(-236, '2/0072174528:989000') + 0.0005 - expected_state, _ = spice.spkez(199, image_et, 'IAU_MERCURY', 'LT+S', -236) - expected_position = -1000 * np.asarray(expected_state[:3]) - expected_velocity = -1000 * np.asarray(expected_state[3:]) - np.testing.assert_allclose(position, - [expected_position], - rtol=1e-8) - np.testing.assert_allclose(velocity, - [expected_velocity], - rtol=1e-8) - np.testing.assert_almost_equal(time, - [image_et]) - -def test_frame_chain(driver): - assert driver.frame_chain.has_node(1) - assert driver.frame_chain.has_node(10011) - assert driver.frame_chain.has_node(-236820) - image_et = spice.scs2e(-236, '2/0072174528:989000') + 0.0005 - target_to_j2000 = driver.frame_chain.compute_rotation(10011, 1) - target_to_j2000_mat = spice.pxform('IAU_MERCURY', 'J2000', image_et) - target_to_j2000_quats = spice.m2q(target_to_j2000_mat) - np.testing.assert_almost_equal(target_to_j2000.quats, - [-np.roll(target_to_j2000_quats, -1)]) - sensor_to_j2000 = driver.frame_chain.compute_rotation(-236820, 1) - sensor_to_j2000_mat = spice.pxform('MSGR_MDIS_NAC', 'J2000', image_et) - sensor_to_j2000_quats = spice.m2q(sensor_to_j2000_mat) - np.testing.assert_almost_equal(sensor_to_j2000.quats, - [-np.roll(sensor_to_j2000_quats, -1)]) - -def test_sun_position(driver): - position, velocity, time = driver.sun_position - image_et = spice.scs2e(-236, '2/0072174528:989000') + 0.0005 - expected_state, _ = spice.spkez(10, image_et, 'IAU_MERCURY', 'LT+S', 199) - expected_position = 1000 * np.asarray(expected_state[:3]) - expected_velocity = 1000 * np.asarray(expected_state[3:]) - np.testing.assert_allclose(position, - [expected_position], - rtol=1e-8) - np.testing.assert_allclose(velocity, - [expected_velocity], - rtol=1e-8) - np.testing.assert_almost_equal(time, - [image_et]) - -def test_target_name(driver): - assert driver.target_name.upper() == 'MERCURY' - -def test_target_frame_id(driver): - assert driver.target_frame_id == 10011 - -def test_sensor_frame_id(driver): - assert driver.sensor_frame_id == -236820 - -def test_isis_naif_keywords(driver): - expected_keywords = { - 'BODY199_RADII' : driver.target_body_radii, - 'BODY_FRAME_CODE' : 10011, - 'INS-236820_PIXEL_PITCH' : 0.014, - 'INS-236820_ITRANSL' : [0.0, 0.0, 71.42857143], - 'INS-236820_ITRANSS' : [0.0, 71.42857143, 0.0], - 'INS-236820_FOCAL_LENGTH' : 549.1178195372703, - 'INS-236820_BORESIGHT_SAMPLE' : 512.5, - 'INS-236820_BORESIGHT_LINE' : 512.5 - } - print(driver.isis_naif_keywords.keys()) - assert set(expected_keywords.keys()).issubset(set(driver.isis_naif_keywords.keys())) - for key, value in expected_keywords.items(): - if isinstance(value, np.ndarray) or isinstance(value, list): - np.testing.assert_almost_equal(value, driver.isis_naif_keywords[key]) - else: - assert value == driver.isis_naif_keywords[key] - -def test_sensor_model_version(driver): - assert driver.sensor_model_version == 2 - -def test_focal2pixel_lines(driver): - np.testing.assert_almost_equal(driver.focal2pixel_lines, - [0.0, 0.0, 71.42857143]) - -def test_focal2pixel_samples(driver): - np.testing.assert_almost_equal(driver.focal2pixel_samples, - [0.0, 71.42857143, 0.0]) - -def test_pixel2focal_x(driver): - np.testing.assert_almost_equal(driver.pixel2focal_x, - [0.0, 0.014, 0.0]) - -def test_pixel2focal_y(driver): - np.testing.assert_almost_equal(driver.pixel2focal_y, - [0.0, 0.0, 0.014]) - -def test_ephemeris_start_time(driver): - assert driver.ephemeris_start_time == 483122606.8520247 - -def test_ephemeris_stop_time(driver): - assert driver.ephemeris_stop_time == 483122606.85302466 - -def test_center_ephemeris_time(driver): - assert driver.center_ephemeris_time == 483122606.85252464 + def test_detector_center_sample(self): + assert self.driver.detector_center_sample == 512 + + def test_detector_center_line(self): + assert self.driver.detector_center_line == 512 + + def test_sensor_model_version(self): + assert self.driver.sensor_model_version == 2 + + def test_usgscsm_distortion_model(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', side_effect=[np.array([1, 2, 3, 4, 5]), np.array([-1, -2, -3, -4, -5])]) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.usgscsm_distortion_model == {"transverse" : { + "x" : [1, 2, 3, 4, 5], + "y" : [-1, -2, -3, -4, -5]}} + + + + def test_pixel_size(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([0.1])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.pixel_size == 0.1 + gdpool.assert_called_with('INS-12345_PIXEL_PITCH', 0, 1) + +# ========= Test ISIS3 Label and NAIF Spice driver ========= +class test_isis3_naif(unittest.TestCase): + + def setUp(self): + label = get_image_label("EN1072174528M", "isis3") + self.driver = MessengerMdisIsisLabelNaifSpiceDriver(label) + + def test_short_mission_name(self): + assert self.driver.short_mission_name=='mes' + + def test_platform_name(self): + assert self.driver.platform_name == 'MESSENGER' + + def test_fikid(self): + with patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.spacecraft_name == 'MESSENGER' + + def test_instrument_id(self): + assert self.driver.instrument_id == 'MSGR_MDIS_NAC' + + def test_sampling_factor(self): + assert self.driver.sampling_factor == 2 + + def test_focal_length(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([pow(4.07, -x) for x in np.arange(6)])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.focal_length == pytest.approx(6.0) + gdpool.assert_called_with('INS-12345_FL_TEMP_COEFFS', 0, 6) + + def test_detector_start_sample(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([10.0])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_start_sample == 10.0 + gdpool.assert_called_with('INS-12345_FPUBIN_START_SAMPLE', 0, 1) + + def test_detector_start_line(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([10.0])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_start_line == 10.0 + gdpool.assert_called_with('INS-12345_FPUBIN_START_LINE', 0, 1) + + def test_detector_center_sample(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([512.5, 512.5, 1])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_center_sample == 512 + gdpool.assert_called_with('INS-12345_CCD_CENTER', 0, 3) + + def test_detector_center_line(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([512.5, 512.5, 1])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.detector_center_line == 512 + gdpool.assert_called_with('INS-12345_CCD_CENTER', 0, 3) + + def test_sensor_model_version(self): + assert self.driver.sensor_model_version == 2 + + def test_usgscsm_distortion_model(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', side_effect=[np.array([1, 2, 3, 4, 5]), np.array([-1, -2, -3, -4, -5])]) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.usgscsm_distortion_model == {"transverse" : { + "x" : [1, 2, 3, 4, 5], + "y" : [-1, -2, -3, -4, -5]}} + + + + def test_pixel_size(self): + with patch('ale.drivers.mes_drivers.spice.gdpool', return_value=np.array([0.1])) as gdpool, \ + patch('ale.base.data_naif.spice.bods2c', return_value=-12345) as bods2c: + assert self.driver.pixel_size == 0.1 + gdpool.assert_called_with('INS-12345_PIXEL_PITCH', 0, 1)