From 6cae62141cdffcf799336db91129c73211534e55 Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Tue, 6 Aug 2024 14:37:14 -0600 Subject: [PATCH 1/7] Add preliminary decorator that handles automatically furnishing of spice kernels in correct module Add test incomplete test coverage of ensure_spice decorator in correct location --- imap_processing/spice/kernels.py | 157 ++++++++++++++++++++ imap_processing/tests/spice/test_kernels.py | 47 ++++++ 2 files changed, 204 insertions(+) diff --git a/imap_processing/spice/kernels.py b/imap_processing/spice/kernels.py index 1ff30df21..2d4e21154 100644 --- a/imap_processing/spice/kernels.py +++ b/imap_processing/spice/kernels.py @@ -1 +1,158 @@ """Functions for furnishing and tracking SPICE kernels.""" + +import functools +import logging +import os +from typing import Any, Callable, Optional + +import spiceypy as spice +from spiceypy.utils.exceptions import SpiceyError + +logger = logging.getLogger(__name__) + + +def ensure_spice( + f_py: Optional[Callable] = None, time_kernels_only: bool = False +) -> Callable: + """ + Decorator/wrapper that automatically furnishes SPICE kernels. + + Parameters + ---------- + f_py : Callable + The function requiring SPICE that we are going to wrap if being used + explicitly, otherwise None, in which case ensure_spice is being used, + not as a function wrapper (see l2a_processing.py) but as a true + decorator without an explicit function argument. + time_kernels_only : bool + Specify that we only need to furnish time kernels (if SPICE_METAKERNEL + is set, we still just furnish that metakernel and assume the time + kernels are included. + + Returns + ------- + Callable + Decorated function, with spice error handling. + + Notes + ----- + Before trying to understand this piece of code, read this: + https://stackoverflow.com/questions/5929107/decorators-with-parameters/60832711#60832711 + + **Control flow overview:** + 1. Try simply calling the wrapped function naively. + * SUCCESS? Great! We're done. + * SpiceyError? Go to step 2. + + 2. Furnish metakernel at SPICE_METAKERNEL + * SUCCESS? Great, return the original function again (so it can be + re-run). + * KeyError? Seems like SPICE_METAKERNEL isn't set, no problem. Go to + step 3. + + 3. Did we get the parameter time_kernels_only=True? + --> YES? We only need LSK and SCLK kernels to run this function. Go fetch + those and furnish and return the original function (so it can be re-run). + --> NO? Dang. This is sort of the end of the line. Re-raise the error + generated from the failed spiceypy function call but add a better + message to it. + + Examples + -------- + There are three ways to use this object + + 1. A decorator with no arguments + >>> @ensure_spice + ... def my_spicey_func(a, b): + ... pass + + 2. A decorator with parameters. This is useful + if we only need the latest SCLK and LSK kernels for the function involved. + >>> @ensure_spice(time_kernels_only=True) + ... def my_spicey_time_func(a, b): + ... pass + + 3. An explicit wrapper function, providing a dynamically set value for + parameters, e.g. time_kernels_only + >>> wrapped = ensure_spice(spicey_func, time_kernels_only=True) + ... result = wrapped(*args, **kwargs) + """ + if f_py and not callable(f_py): + raise ValueError( + f"Received a non-callable object {f_py} as the f_py argument to" + f"ensure_spice. f_py must be a callable object." + ) + + def _decorator(func: Callable[..., Callable]) -> Callable: + """ + Decorate or wrap input function depending on how ensure_spice is used. + + Parameters + ---------- + func : Callable + The function to be decorated/wrapped. + + Returns + ------- + Callable + If used as a function wrapper, the decorated function is returned. + """ + + @functools.wraps(func) + def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any: + """ + Wrap the function that ensure_spice is used on. + + Parameters + ---------- + *args : list + The positional arguments passed to the decorated function. + **kwargs + The keyword arguments passed to the decorated function. + + Returns + ------- + Object + Output from wrapped function. + """ + try: + # Step 1. + return func( + *args, **kwargs + ) # Naive first try. Maybe SPICE is already furnished. + except SpiceyError as spicey_err: + try: + # Step 2. + metakernel_path = os.environ["SPICE_METAKERNEL"] + spice.furnsh(metakernel_path) + except KeyError: + # TODO: An additional step that was used on EMUS was to get + # a custom metakernel from the SDC API based on an input + # time range. + if time_kernels_only: + # Step 3. + # TODO: Decide if this is useful for IMAP. Possible + # implementation could include downloading + # the most recent leapsecond kernel from NAIF (see: + # https://lasp.colorado.edu/nucleus/projects/LIBSDC/repos/libera_utils/browse/libera_utils/spice_utils.py + # for LIBERA implementation of downloading and caching + # kernels) and finding the most recent IMAP clock + # kernel in EFS. + raise NotImplementedError from spicey_err + else: + raise SpiceyError( + "When calling a function requiring SPICE, we failed" + "to load a metakernel. SPICE_METAKERNEL is not set," + "and time_kernels_only is not set to True" + ) from spicey_err + return func(*args, **kwargs) + + return wrapper_ensure_spice + + # Note: This return was originally implemented as a ternary operator, but + # this caused mypy to fail due to this bug: + # https://github.com/python/mypy/issues/4134 + if callable(f_py): + return _decorator(f_py) + else: + return _decorator diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index 059d0f231..f318279fb 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -1 +1,48 @@ """Tests coverage for imap_processing/spice/kernels.py""" + +import pytest +import spiceypy as spice + +from imap_processing import kernels + + +@kernels.ensure_spice +def single_wrap_et2utc(et, fmt, prec): + """Directly decorate a spice function with ensure_spice for use in tests""" + return spice.et2utc(et, fmt, prec) + + +@kernels.ensure_spice +def double_wrap_et2utc(et, fmt, prec): + """Decorate a spice function twice with ensure_spice for use in tests. This + simulates some decorated outer functions that call lower level functions + that are already decorated.""" + return single_wrap_et2utc(et, fmt, prec) + + +@kernels.ensure_spice(time_kernels_only=True) +def single_wrap_et2utc_tk_only(et, fmt, prec): + """Directly wrap a spice function with optional time_kernels_only set True""" + return spice.et2utc(et, fmt, prec) + + +@kernels.ensure_spice(time_kernels_only=True) +def double_wrap_et2utc_tk_only(et, fmt, prec): + """Decorate a spice function twice with ensure_spice for use in tests. This + simulates some decorated outer functions that call lower level functions + that are already decorated.""" + return single_wrap_et2utc(et, fmt, prec) + + +@pytest.mark.parametrize( + "func", + [ + single_wrap_et2utc, + single_wrap_et2utc_tk_only, + double_wrap_et2utc, + double_wrap_et2utc_tk_only, + ], +) +def test_ensure_spice_emus_mk_path(func): + """Test functionality of ensure spice with EMUS_METAKERNEL_PATH set""" + assert func(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" From 136d4858bdd50c686496e7051b4d4f1df4926638 Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Tue, 6 Aug 2024 16:23:25 -0600 Subject: [PATCH 2/7] Add spice kernel furnishing fixtures Add leapsecond kernel to test data Add sclk kernel to test data Add metekrnel template to test data --- imap_processing/tests/conftest.py | 117 +++++++++++++ .../tests/spice/test_data/imap_sclk_0000.tsc | 156 ++++++++++++++++++ .../test_data/imap_test_metakernel.template | 2 + .../tests/spice/test_data/naif0012.tls | 150 +++++++++++++++++ imap_processing/tests/spice/test_kernels.py | 6 +- 5 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 imap_processing/tests/spice/test_data/imap_sclk_0000.tsc create mode 100644 imap_processing/tests/spice/test_data/imap_test_metakernel.template create mode 100644 imap_processing/tests/spice/test_data/naif0012.tls diff --git a/imap_processing/tests/conftest.py b/imap_processing/tests/conftest.py index 4a7c9bd76..16b723233 100644 --- a/imap_processing/tests/conftest.py +++ b/imap_processing/tests/conftest.py @@ -1,7 +1,11 @@ """Global pytest configuration for the package.""" +import re + import imap_data_access +import numpy as np import pytest +import spiceypy as spice from imap_processing import imap_module_directory @@ -15,6 +19,119 @@ def _set_global_config(monkeypatch, tmp_path): ) +@pytest.fixture(scope="session") +def monkeypatch_session(): + from _pytest.monkeypatch import MonkeyPatch + + m = MonkeyPatch() + yield m + m.undo() + + @pytest.fixture(scope="session") def imap_tests_path(): return imap_module_directory / "tests" + + +# Furnishing fixtures for testing kernels +# --------------------------------------- +@pytest.fixture(autouse=True) +def _autoclear_spice(): + """Automatically clears out all SPICE remnants after every single test to + prevent the kernel pool from interfering with future tests. Option autouse + ensures this is run after every test.""" + yield + spice.kclear() + + +@pytest.fixture(scope="session") +def spice_test_data_path(imap_tests_path): + return imap_tests_path / "spice/test_data" + + +@pytest.fixture() +def furnish_test_lsk(spice_test_data_path): + """Furnishes (temporarily) the testing LSK""" + test_lsk = spice_test_data_path / "naif0012.tls" + spice.furnsh(test_lsk) + yield test_lsk + spice.kclear() + + +@pytest.fixture() +def furnish_sclk(spice_test_data_path): + """Furnishes (temporarily) the SCLK for JPSS stored in the package data directory""" + test_sclk = spice_test_data_path / "imap_sclk_0000.tsc" + spice.furnsh(test_sclk) + yield test_sclk + spice.kclear() + + +@pytest.fixture(scope="session") +def use_test_metakernel(tmpdir_factory, monkeypatch_session, spice_test_data_path): + """For the whole test session, set the SPICE_METAKERNEL environment variable + Prime the test metakernel by creating it from the template metakernel + (allows using absolute paths on any dev system)""" + + def make_metakernel_from_kernels(metakernel, kernels): + """Helper function that writes a test metakernel from a list of filenames""" + with open(metakernel, "w") as mk: + mk.writelines( + [ + "\n", + "\\begintext\n", + "\n", + "This is a temporary metakernel for imap_processing" + " unit and integration testing.\n", + "\n", + "\\begindata\n", + "\n", + "KERNELS_TO_LOAD = (\n", + ] + ) + # Put single quotes around every kernel name + kernels_with_quotes = [" '" + kern + "'" for kern in kernels] + # Add a comma and EOL to the end of each kernel path except the last. + formated_kernels = [kern + ",\n" for kern in kernels_with_quotes[0:-1]] + # Add ')' to the last kernel + formated_kernels.append(kernels_with_quotes[-1] + "\n)\n\n") + mk.writelines(formated_kernels) + + def get_test_kernels_to_load(): + """ + Helper function for grabbing a list of kernel filenames from the test + metakernel template. This is necessary in order to get absolute paths on + any system. Formats the absolute paths using the test data path fixture + value. + """ + test_metakernel = spice_test_data_path / "imap_test_metakernel.template" + kernels_to_load = [] + max_line_length = 80 + with open(test_metakernel) as mk: + for k in mk: + kernel = k.rstrip("\n").format( + **{"SPICE_TEST_DATA_PATH": str(spice_test_data_path.absolute())} + ) + while len(kernel) > 0: + if len(kernel) <= max_line_length: + kernels_to_load.append(kernel) + break + else: + slash_positions = np.array( + [m.start() for m in re.finditer("/", kernel)] + ) + stop_idx = ( + slash_positions[slash_positions < max_line_length - 1].max() + + 1 + ) + kernels_to_load.append(kernel[0:stop_idx] + "+") + kernel = kernel[stop_idx:] + return kernels_to_load + + metakernel_path = tmpdir_factory.mktemp("spice") + metakernel = metakernel_path.join("imap_2024_v001.tm") + kernels_to_load = get_test_kernels_to_load() + make_metakernel_from_kernels(metakernel, kernels_to_load) + monkeypatch_session.setenv("SPICE_METAKERNEL", str(metakernel)) + yield str(metakernel) + spice.kclear() diff --git a/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc b/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc new file mode 100644 index 000000000..345cedeb0 --- /dev/null +++ b/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc @@ -0,0 +1,156 @@ +\begintext + +FILENAME = "imap_0000.tsc" +CREATION_DATE = "5-January-2021" + + +IMAP Spacecraft Clock Kernel (SCLK) +=========================================================================== + + This file is a SPICE spacecraft clock (SCLK) kernel containing + information required for time conversions involving the on-board + IMAP spacecraft clock. + +Version +-------------------------------------------------------- + + IMAP SCLK Kernel Version: + + IMAP version 0.3 - April 22, 2022 -- Mike Ruffolo + Updated to use NAIF SC ID 43 + + IMAP Version 0.2 - June 2, 2021 -- Caroline Cocca + Updated to use temporary spacecraft id of 225 + + IMAP Version 0.1 - March 6, 2015 -- Eric Melin + Updated text to replace references to RBSP with SPP + + IMAP Version 0.0 - August 7, 2014 -- Eric Melin + The initial SPP spice kernel. + This file was created by using RBSPA initial kernel and + modifying the spacecraft ID. + + +Usage +-------------------------------------------------------- + + This file is used by the SPICE system as follows: programs that + make use of this SCLK kernel must 'load' the kernel, normally + during program initialization. Loading the kernel associates + the data items with their names in a data structure called the + 'kernel pool'. The SPICELIB routine FURNSH loads text kernel + files, such as this one, into the pool as shown below: + + FORTRAN: + + CALL FURNSH ( SCLK_kernel_name ) + + C: + + furnsh_c ( SCLK_kernel_name ); + + Once loaded, the SCLK time conversion routines will be able to + access the necessary data located in this kernel for their + designed purposes. + +References +-------------------------------------------------------- + + 1. "SCLK Required Reading" + +Inquiries +-------------------------------------------------------- + + If you have any questions regarding this file or its usage, + contact: + + Scott Turner + (443)778-1693 + Scott.Turner@jhuapl.edu + +Kernel Data +-------------------------------------------------------- + + The first block of keyword equals value assignments define the + type, parallel time system, and format of the spacecraft clock. + These fields are invariant from SCLK kernel update to SCLK + kernel update. + + The IMAP spacecraft clock is represented by the SPICE + type 1 SCLK kernel. It uses TDT, Terrestrial Dynamical Time, + as its parallel time system. + +\begindata + +SCLK_KERNEL_ID = ( @2009-07-09T12:20:32 ) +SCLK_DATA_TYPE_43 = ( 1 ) +SCLK01_TIME_SYSTEM_43 = ( 2 ) + + +\begintext + + In a particular partition of the IMAP spacecraft clock, + the clock read-out consists of two separate stages: + + 1/18424652:24251 + + The first stage, a 32 bit field, represents the spacecraft + clock seconds count. The second, a 16 bit field, represents + counts of 20 microsecond increments of the spacecraft clock. + + The following keywords and their values establish this structure: + +\begindata + +SCLK01_N_FIELDS_43 = ( 2 ) +SCLK01_MODULI_43 = ( 4294967296 50000 ) +SCLK01_OFFSETS_43 = ( 0 0 ) +SCLK01_OUTPUT_DELIM_43 = ( 2 ) + + +\begintext + + This concludes the invariant portion of the SCLK kernel data. The + remaining sections of the kernel may require updates as the clock + correlation coefficients evolve in time. The first section below + establishes the clock partitions. The data in this section consists + of two parallel arrays, which denote the start and end values in + ticks of each partition of the spacecraft clock. + + SPICE utilizes these two arrays to map from spacecraft clock ticks, + determined with the usual modulo arithmetic, to encoded SCLK--the + internal, monotonically increasing sequence used to tag various + data sources with spacecraft clock. + +\begindata + +SCLK_PARTITION_START_43 = ( 0.00000000000000e+00 ) + +SCLK_PARTITION_END_43 = ( 2.14748364799999e+14 ) + +\begintext + + The remaining section of the SCLK kernel defines the clock correlation + coefficients. Each line contains a 'coefficient triple': + + Encoded SCLK at which Rate is introduced. + Corresponding TDT Epoch at which Rate is introduced. + Rate in TDT (seconds) / most significant clock count (~seconds). + + SPICE uses linear extrapolation to convert between the parallel time + system and encoded SCLK. The triples are stored in the array defined + below. + + The first time triplet below was entered manually and represents the + approximate time (in TDT) at which SCLK = zero. The current plan for + IMAP is that the given epoch will be used for both Observatory I&T + and launch. Note that the conversion from UTC to TDT used 34 leap + seconds. + +\begindata + +SCLK01_COEFFICIENTS_43 = ( + + 0 @01-JAN-2010-00:01:06.184000 1.00000000000 + +) diff --git a/imap_processing/tests/spice/test_data/imap_test_metakernel.template b/imap_processing/tests/spice/test_data/imap_test_metakernel.template new file mode 100644 index 000000000..b4a5d9f82 --- /dev/null +++ b/imap_processing/tests/spice/test_data/imap_test_metakernel.template @@ -0,0 +1,2 @@ +{SPICE_TEST_DATA_PATH}/imap_sclk_0000.tsc +{SPICE_TEST_DATA_PATH}/naif0012.tls \ No newline at end of file diff --git a/imap_processing/tests/spice/test_data/naif0012.tls b/imap_processing/tests/spice/test_data/naif0012.tls new file mode 100644 index 000000000..caa6a4d13 --- /dev/null +++ b/imap_processing/tests/spice/test_data/naif0012.tls @@ -0,0 +1,150 @@ +KPL/LSK + + +LEAPSECONDS KERNEL FILE +=========================================================================== + +Modifications: +-------------- + +2016, Jul. 14 NJB Modified file to account for the leapsecond that + will occur on December 31, 2016. + +2015, Jan. 5 NJB Modified file to account for the leapsecond that + will occur on June 30, 2015. + +2012, Jan. 5 NJB Modified file to account for the leapsecond that + will occur on June 30, 2012. + +2008, Jul. 7 NJB Modified file to account for the leapsecond that + will occur on December 31, 2008. + +2005, Aug. 3 NJB Modified file to account for the leapsecond that + will occur on December 31, 2005. + +1998, Jul 17 WLT Modified file to account for the leapsecond that + will occur on December 31, 1998. + +1997, Feb 22 WLT Modified file to account for the leapsecond that + will occur on June 30, 1997. + +1995, Dec 14 KSZ Corrected date of last leapsecond from 1-1-95 + to 1-1-96. + +1995, Oct 25 WLT Modified file to account for the leapsecond that + will occur on Dec 31, 1995. + +1994, Jun 16 WLT Modified file to account for the leapsecond on + June 30, 1994. + +1993, Feb. 22 CHA Modified file to account for the leapsecond on + June 30, 1993. + +1992, Mar. 6 HAN Modified file to account for the leapsecond on + June 30, 1992. + +1990, Oct. 8 HAN Modified file to account for the leapsecond on + Dec. 31, 1990. + + +Explanation: +------------ + +The contents of this file are used by the routine DELTET to compute the +time difference + +[1] DELTA_ET = ET - UTC + +the increment to be applied to UTC to give ET. + +The difference between UTC and TAI, + +[2] DELTA_AT = TAI - UTC + +is always an integral number of seconds. The value of DELTA_AT was 10 +seconds in January 1972, and increases by one each time a leap second +is declared. Combining [1] and [2] gives + +[3] DELTA_ET = ET - (TAI - DELTA_AT) + + = (ET - TAI) + DELTA_AT + +The difference (ET - TAI) is periodic, and is given by + +[4] ET - TAI = DELTA_T_A + K sin E + +where DELTA_T_A and K are constant, and E is the eccentric anomaly of the +heliocentric orbit of the Earth-Moon barycenter. Equation [4], which ignores +small-period fluctuations, is accurate to about 0.000030 seconds. + +The eccentric anomaly E is given by + +[5] E = M + EB sin M + +where M is the mean anomaly, which in turn is given by + +[6] M = M + M t + 0 1 + +where t is the number of ephemeris seconds past J2000. + +Thus, in order to compute DELTA_ET, the following items are necessary. + + DELTA_TA + K + EB + M0 + M1 + DELTA_AT after each leap second. + +The numbers, and the formulation, are taken from the following sources. + + 1) Moyer, T.D., Transformation from Proper Time on Earth to + Coordinate Time in Solar System Barycentric Space-Time Frame + of Reference, Parts 1 and 2, Celestial Mechanics 23 (1981), + 33-56 and 57-68. + + 2) Moyer, T.D., Effects of Conversion to the J2000 Astronomical + Reference System on Algorithms for Computing Time Differences + and Clock Rates, JPL IOM 314.5--942, 1 October 1985. + +The variable names used above are consistent with those used in the +Astronomical Almanac. + +\begindata + +DELTET/DELTA_T_A = 32.184 +DELTET/K = 1.657D-3 +DELTET/EB = 1.671D-2 +DELTET/M = ( 6.239996D0 1.99096871D-7 ) + +DELTET/DELTA_AT = ( 10, @1972-JAN-1 + 11, @1972-JUL-1 + 12, @1973-JAN-1 + 13, @1974-JAN-1 + 14, @1975-JAN-1 + 15, @1976-JAN-1 + 16, @1977-JAN-1 + 17, @1978-JAN-1 + 18, @1979-JAN-1 + 19, @1980-JAN-1 + 20, @1981-JUL-1 + 21, @1982-JUL-1 + 22, @1983-JUL-1 + 23, @1985-JUL-1 + 24, @1988-JAN-1 + 25, @1990-JAN-1 + 26, @1991-JAN-1 + 27, @1992-JUL-1 + 28, @1993-JUL-1 + 29, @1994-JUL-1 + 30, @1996-JAN-1 + 31, @1997-JUL-1 + 32, @1999-JAN-1 + 33, @2006-JAN-1 + 34, @2009-JAN-1 + 35, @2012-JUL-1 + 36, @2015-JUL-1 + 37, @2017-JAN-1 ) + +\begintext diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index f318279fb..af7067e9c 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -3,7 +3,7 @@ import pytest import spiceypy as spice -from imap_processing import kernels +from imap_processing.spice import kernels @kernels.ensure_spice @@ -43,6 +43,6 @@ def double_wrap_et2utc_tk_only(et, fmt, prec): double_wrap_et2utc_tk_only, ], ) -def test_ensure_spice_emus_mk_path(func): - """Test functionality of ensure spice with EMUS_METAKERNEL_PATH set""" +def test_ensure_spice_emus_mk_path(func, use_test_metakernel): + """Test functionality of ensure spice with SPICE_METAKERNEL set""" assert func(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" From c20188978722357cdc4d2ef54b87e637202b86e9 Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Tue, 6 Aug 2024 16:38:54 -0600 Subject: [PATCH 3/7] Add test coverage for time kernels only use of ensure_spice Add test coverage for ensure_spice failure -> SpiceyError --- imap_processing/tests/conftest.py | 6 +++--- imap_processing/tests/spice/test_kernels.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/imap_processing/tests/conftest.py b/imap_processing/tests/conftest.py index 16b723233..49a12b01c 100644 --- a/imap_processing/tests/conftest.py +++ b/imap_processing/tests/conftest.py @@ -67,8 +67,8 @@ def furnish_sclk(spice_test_data_path): spice.kclear() -@pytest.fixture(scope="session") -def use_test_metakernel(tmpdir_factory, monkeypatch_session, spice_test_data_path): +@pytest.fixture() +def use_test_metakernel(tmpdir_factory, monkeypatch, spice_test_data_path): """For the whole test session, set the SPICE_METAKERNEL environment variable Prime the test metakernel by creating it from the template metakernel (allows using absolute paths on any dev system)""" @@ -132,6 +132,6 @@ def get_test_kernels_to_load(): metakernel = metakernel_path.join("imap_2024_v001.tm") kernels_to_load = get_test_kernels_to_load() make_metakernel_from_kernels(metakernel, kernels_to_load) - monkeypatch_session.setenv("SPICE_METAKERNEL", str(metakernel)) + monkeypatch.setenv("SPICE_METAKERNEL", str(metakernel)) yield str(metakernel) spice.kclear() diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index af7067e9c..dcaa90131 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -2,6 +2,7 @@ import pytest import spiceypy as spice +from spiceypy.utils.exceptions import SpiceyError from imap_processing.spice import kernels @@ -46,3 +47,17 @@ def double_wrap_et2utc_tk_only(et, fmt, prec): def test_ensure_spice_emus_mk_path(func, use_test_metakernel): """Test functionality of ensure spice with SPICE_METAKERNEL set""" assert func(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" + + +def test_ensure_spice_time_kernels(): + """Test functionality of ensure spice with timekernels set""" + wrapped = kernels.ensure_spice(spice.et2utc, time_kernels_only=True) + with pytest.raises(NotImplementedError): + _ = wrapped(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" + + +def test_ensure_spice_key_error(): + """Test functionality of ensure spice with timekernels set""" + wrapped = kernels.ensure_spice(spice.et2utc) + with pytest.raises(SpiceyError): + _ = wrapped(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" From 304956b6d290b528ec634a579c8f5adb6d33f41b Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Wed, 7 Aug 2024 17:31:28 -0600 Subject: [PATCH 4/7] Remove unused fixture Move metakernel template to other spice kernel location --- imap_processing/tests/conftest.py | 11 +---------- .../test_data/spice}/imap_test_metakernel.template | 0 2 files changed, 1 insertion(+), 10 deletions(-) rename {imap_processing/tests/spice/test_data => tools/tests/test_data/spice}/imap_test_metakernel.template (100%) diff --git a/imap_processing/tests/conftest.py b/imap_processing/tests/conftest.py index 49a12b01c..c1decb21f 100644 --- a/imap_processing/tests/conftest.py +++ b/imap_processing/tests/conftest.py @@ -19,15 +19,6 @@ def _set_global_config(monkeypatch, tmp_path): ) -@pytest.fixture(scope="session") -def monkeypatch_session(): - from _pytest.monkeypatch import MonkeyPatch - - m = MonkeyPatch() - yield m - m.undo() - - @pytest.fixture(scope="session") def imap_tests_path(): return imap_module_directory / "tests" @@ -46,7 +37,7 @@ def _autoclear_spice(): @pytest.fixture(scope="session") def spice_test_data_path(imap_tests_path): - return imap_tests_path / "spice/test_data" + return imap_module_directory.parent / "tools/tests/test_data/spice" @pytest.fixture() diff --git a/imap_processing/tests/spice/test_data/imap_test_metakernel.template b/tools/tests/test_data/spice/imap_test_metakernel.template similarity index 100% rename from imap_processing/tests/spice/test_data/imap_test_metakernel.template rename to tools/tests/test_data/spice/imap_test_metakernel.template From 78ae704cfedf652bafe3e9f430d0aa912b611dfe Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Wed, 7 Aug 2024 17:33:06 -0600 Subject: [PATCH 5/7] remove duplicate kernel files --- .../tests/spice/test_data/imap_sclk_0000.tsc | 156 ------------------ .../tests/spice/test_data/naif0012.tls | 150 ----------------- 2 files changed, 306 deletions(-) delete mode 100644 imap_processing/tests/spice/test_data/imap_sclk_0000.tsc delete mode 100644 imap_processing/tests/spice/test_data/naif0012.tls diff --git a/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc b/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc deleted file mode 100644 index 345cedeb0..000000000 --- a/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc +++ /dev/null @@ -1,156 +0,0 @@ -\begintext - -FILENAME = "imap_0000.tsc" -CREATION_DATE = "5-January-2021" - - -IMAP Spacecraft Clock Kernel (SCLK) -=========================================================================== - - This file is a SPICE spacecraft clock (SCLK) kernel containing - information required for time conversions involving the on-board - IMAP spacecraft clock. - -Version --------------------------------------------------------- - - IMAP SCLK Kernel Version: - - IMAP version 0.3 - April 22, 2022 -- Mike Ruffolo - Updated to use NAIF SC ID 43 - - IMAP Version 0.2 - June 2, 2021 -- Caroline Cocca - Updated to use temporary spacecraft id of 225 - - IMAP Version 0.1 - March 6, 2015 -- Eric Melin - Updated text to replace references to RBSP with SPP - - IMAP Version 0.0 - August 7, 2014 -- Eric Melin - The initial SPP spice kernel. - This file was created by using RBSPA initial kernel and - modifying the spacecraft ID. - - -Usage --------------------------------------------------------- - - This file is used by the SPICE system as follows: programs that - make use of this SCLK kernel must 'load' the kernel, normally - during program initialization. Loading the kernel associates - the data items with their names in a data structure called the - 'kernel pool'. The SPICELIB routine FURNSH loads text kernel - files, such as this one, into the pool as shown below: - - FORTRAN: - - CALL FURNSH ( SCLK_kernel_name ) - - C: - - furnsh_c ( SCLK_kernel_name ); - - Once loaded, the SCLK time conversion routines will be able to - access the necessary data located in this kernel for their - designed purposes. - -References --------------------------------------------------------- - - 1. "SCLK Required Reading" - -Inquiries --------------------------------------------------------- - - If you have any questions regarding this file or its usage, - contact: - - Scott Turner - (443)778-1693 - Scott.Turner@jhuapl.edu - -Kernel Data --------------------------------------------------------- - - The first block of keyword equals value assignments define the - type, parallel time system, and format of the spacecraft clock. - These fields are invariant from SCLK kernel update to SCLK - kernel update. - - The IMAP spacecraft clock is represented by the SPICE - type 1 SCLK kernel. It uses TDT, Terrestrial Dynamical Time, - as its parallel time system. - -\begindata - -SCLK_KERNEL_ID = ( @2009-07-09T12:20:32 ) -SCLK_DATA_TYPE_43 = ( 1 ) -SCLK01_TIME_SYSTEM_43 = ( 2 ) - - -\begintext - - In a particular partition of the IMAP spacecraft clock, - the clock read-out consists of two separate stages: - - 1/18424652:24251 - - The first stage, a 32 bit field, represents the spacecraft - clock seconds count. The second, a 16 bit field, represents - counts of 20 microsecond increments of the spacecraft clock. - - The following keywords and their values establish this structure: - -\begindata - -SCLK01_N_FIELDS_43 = ( 2 ) -SCLK01_MODULI_43 = ( 4294967296 50000 ) -SCLK01_OFFSETS_43 = ( 0 0 ) -SCLK01_OUTPUT_DELIM_43 = ( 2 ) - - -\begintext - - This concludes the invariant portion of the SCLK kernel data. The - remaining sections of the kernel may require updates as the clock - correlation coefficients evolve in time. The first section below - establishes the clock partitions. The data in this section consists - of two parallel arrays, which denote the start and end values in - ticks of each partition of the spacecraft clock. - - SPICE utilizes these two arrays to map from spacecraft clock ticks, - determined with the usual modulo arithmetic, to encoded SCLK--the - internal, monotonically increasing sequence used to tag various - data sources with spacecraft clock. - -\begindata - -SCLK_PARTITION_START_43 = ( 0.00000000000000e+00 ) - -SCLK_PARTITION_END_43 = ( 2.14748364799999e+14 ) - -\begintext - - The remaining section of the SCLK kernel defines the clock correlation - coefficients. Each line contains a 'coefficient triple': - - Encoded SCLK at which Rate is introduced. - Corresponding TDT Epoch at which Rate is introduced. - Rate in TDT (seconds) / most significant clock count (~seconds). - - SPICE uses linear extrapolation to convert between the parallel time - system and encoded SCLK. The triples are stored in the array defined - below. - - The first time triplet below was entered manually and represents the - approximate time (in TDT) at which SCLK = zero. The current plan for - IMAP is that the given epoch will be used for both Observatory I&T - and launch. Note that the conversion from UTC to TDT used 34 leap - seconds. - -\begindata - -SCLK01_COEFFICIENTS_43 = ( - - 0 @01-JAN-2010-00:01:06.184000 1.00000000000 - -) diff --git a/imap_processing/tests/spice/test_data/naif0012.tls b/imap_processing/tests/spice/test_data/naif0012.tls deleted file mode 100644 index caa6a4d13..000000000 --- a/imap_processing/tests/spice/test_data/naif0012.tls +++ /dev/null @@ -1,150 +0,0 @@ -KPL/LSK - - -LEAPSECONDS KERNEL FILE -=========================================================================== - -Modifications: --------------- - -2016, Jul. 14 NJB Modified file to account for the leapsecond that - will occur on December 31, 2016. - -2015, Jan. 5 NJB Modified file to account for the leapsecond that - will occur on June 30, 2015. - -2012, Jan. 5 NJB Modified file to account for the leapsecond that - will occur on June 30, 2012. - -2008, Jul. 7 NJB Modified file to account for the leapsecond that - will occur on December 31, 2008. - -2005, Aug. 3 NJB Modified file to account for the leapsecond that - will occur on December 31, 2005. - -1998, Jul 17 WLT Modified file to account for the leapsecond that - will occur on December 31, 1998. - -1997, Feb 22 WLT Modified file to account for the leapsecond that - will occur on June 30, 1997. - -1995, Dec 14 KSZ Corrected date of last leapsecond from 1-1-95 - to 1-1-96. - -1995, Oct 25 WLT Modified file to account for the leapsecond that - will occur on Dec 31, 1995. - -1994, Jun 16 WLT Modified file to account for the leapsecond on - June 30, 1994. - -1993, Feb. 22 CHA Modified file to account for the leapsecond on - June 30, 1993. - -1992, Mar. 6 HAN Modified file to account for the leapsecond on - June 30, 1992. - -1990, Oct. 8 HAN Modified file to account for the leapsecond on - Dec. 31, 1990. - - -Explanation: ------------- - -The contents of this file are used by the routine DELTET to compute the -time difference - -[1] DELTA_ET = ET - UTC - -the increment to be applied to UTC to give ET. - -The difference between UTC and TAI, - -[2] DELTA_AT = TAI - UTC - -is always an integral number of seconds. The value of DELTA_AT was 10 -seconds in January 1972, and increases by one each time a leap second -is declared. Combining [1] and [2] gives - -[3] DELTA_ET = ET - (TAI - DELTA_AT) - - = (ET - TAI) + DELTA_AT - -The difference (ET - TAI) is periodic, and is given by - -[4] ET - TAI = DELTA_T_A + K sin E - -where DELTA_T_A and K are constant, and E is the eccentric anomaly of the -heliocentric orbit of the Earth-Moon barycenter. Equation [4], which ignores -small-period fluctuations, is accurate to about 0.000030 seconds. - -The eccentric anomaly E is given by - -[5] E = M + EB sin M - -where M is the mean anomaly, which in turn is given by - -[6] M = M + M t - 0 1 - -where t is the number of ephemeris seconds past J2000. - -Thus, in order to compute DELTA_ET, the following items are necessary. - - DELTA_TA - K - EB - M0 - M1 - DELTA_AT after each leap second. - -The numbers, and the formulation, are taken from the following sources. - - 1) Moyer, T.D., Transformation from Proper Time on Earth to - Coordinate Time in Solar System Barycentric Space-Time Frame - of Reference, Parts 1 and 2, Celestial Mechanics 23 (1981), - 33-56 and 57-68. - - 2) Moyer, T.D., Effects of Conversion to the J2000 Astronomical - Reference System on Algorithms for Computing Time Differences - and Clock Rates, JPL IOM 314.5--942, 1 October 1985. - -The variable names used above are consistent with those used in the -Astronomical Almanac. - -\begindata - -DELTET/DELTA_T_A = 32.184 -DELTET/K = 1.657D-3 -DELTET/EB = 1.671D-2 -DELTET/M = ( 6.239996D0 1.99096871D-7 ) - -DELTET/DELTA_AT = ( 10, @1972-JAN-1 - 11, @1972-JUL-1 - 12, @1973-JAN-1 - 13, @1974-JAN-1 - 14, @1975-JAN-1 - 15, @1976-JAN-1 - 16, @1977-JAN-1 - 17, @1978-JAN-1 - 18, @1979-JAN-1 - 19, @1980-JAN-1 - 20, @1981-JUL-1 - 21, @1982-JUL-1 - 22, @1983-JUL-1 - 23, @1985-JUL-1 - 24, @1988-JAN-1 - 25, @1990-JAN-1 - 26, @1991-JAN-1 - 27, @1992-JUL-1 - 28, @1993-JUL-1 - 29, @1994-JUL-1 - 30, @1996-JAN-1 - 31, @1997-JUL-1 - 32, @1999-JAN-1 - 33, @2006-JAN-1 - 34, @2009-JAN-1 - 35, @2012-JUL-1 - 36, @2015-JUL-1 - 37, @2017-JAN-1 ) - -\begintext From 8ff11368f894ca6d0838658321c4125198ea88f3 Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Wed, 7 Aug 2024 17:45:58 -0600 Subject: [PATCH 6/7] Add comments about why ensure_spice tests expect an exception to be raised --- imap_processing/tests/spice/test_kernels.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/imap_processing/tests/spice/test_kernels.py b/imap_processing/tests/spice/test_kernels.py index dcaa90131..c8ce926ad 100644 --- a/imap_processing/tests/spice/test_kernels.py +++ b/imap_processing/tests/spice/test_kernels.py @@ -52,12 +52,18 @@ def test_ensure_spice_emus_mk_path(func, use_test_metakernel): def test_ensure_spice_time_kernels(): """Test functionality of ensure spice with timekernels set""" wrapped = kernels.ensure_spice(spice.et2utc, time_kernels_only=True) + # TODO: Update/remove this test when a decision has been made about + # whether IMAP will use the time_kernels_only functionality and the + # ensure_spice decorator has been update. with pytest.raises(NotImplementedError): _ = wrapped(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" def test_ensure_spice_key_error(): - """Test functionality of ensure spice with timekernels set""" + """Test functionality of ensure spice when all branches fail""" wrapped = kernels.ensure_spice(spice.et2utc) + # The ensure_spice decorator should raise a SpiceyError when all attempts to + # furnish a set of kernels with sufficient coverage for the spiceypy + # functions that it decorates. with pytest.raises(SpiceyError): _ = wrapped(577365941.184, "ISOC", 3) == "2018-04-18T23:24:31.998" From 5c14546e2b162ed00bc1d112b0e8a97b80c4cb0e Mon Sep 17 00:00:00 2001 From: Tim Plummer Date: Mon, 12 Aug 2024 14:50:55 -0600 Subject: [PATCH 7/7] Use imap_data_access.config DATA_DIR instead of new tmp_path to write test metakernel Move leapsecond and spacecraft clock kernel into imap_processing package --- imap_processing/tests/conftest.py | 13 ++++++------- .../tests/spice/test_data}/imap_sclk_0000.tsc | 0 .../spice/test_data}/imap_test_metakernel.template | 0 .../tests/spice/test_data}/naif0012.tls | 0 tools/tests/unit/test_spice_examples.py | 7 +++++++ 5 files changed, 13 insertions(+), 7 deletions(-) rename {tools/tests/test_data/spice => imap_processing/tests/spice/test_data}/imap_sclk_0000.tsc (100%) rename {tools/tests/test_data/spice => imap_processing/tests/spice/test_data}/imap_test_metakernel.template (100%) rename {tools/tests/test_data/spice => imap_processing/tests/spice/test_data}/naif0012.tls (100%) diff --git a/imap_processing/tests/conftest.py b/imap_processing/tests/conftest.py index c1decb21f..b9adc63e3 100644 --- a/imap_processing/tests/conftest.py +++ b/imap_processing/tests/conftest.py @@ -37,7 +37,7 @@ def _autoclear_spice(): @pytest.fixture(scope="session") def spice_test_data_path(imap_tests_path): - return imap_module_directory.parent / "tools/tests/test_data/spice" + return imap_tests_path / "spice/test_data" @pytest.fixture() @@ -59,7 +59,7 @@ def furnish_sclk(spice_test_data_path): @pytest.fixture() -def use_test_metakernel(tmpdir_factory, monkeypatch, spice_test_data_path): +def use_test_metakernel(monkeypatch, spice_test_data_path): """For the whole test session, set the SPICE_METAKERNEL environment variable Prime the test metakernel by creating it from the template metakernel (allows using absolute paths on any dev system)""" @@ -119,10 +119,9 @@ def get_test_kernels_to_load(): kernel = kernel[stop_idx:] return kernels_to_load - metakernel_path = tmpdir_factory.mktemp("spice") - metakernel = metakernel_path.join("imap_2024_v001.tm") + metakernel_path = imap_data_access.config["DATA_DIR"] / "imap_2024_v001.tm" kernels_to_load = get_test_kernels_to_load() - make_metakernel_from_kernels(metakernel, kernels_to_load) - monkeypatch.setenv("SPICE_METAKERNEL", str(metakernel)) - yield str(metakernel) + make_metakernel_from_kernels(metakernel_path, kernels_to_load) + monkeypatch.setenv("SPICE_METAKERNEL", str(metakernel_path)) + yield str(metakernel_path) spice.kclear() diff --git a/tools/tests/test_data/spice/imap_sclk_0000.tsc b/imap_processing/tests/spice/test_data/imap_sclk_0000.tsc similarity index 100% rename from tools/tests/test_data/spice/imap_sclk_0000.tsc rename to imap_processing/tests/spice/test_data/imap_sclk_0000.tsc diff --git a/tools/tests/test_data/spice/imap_test_metakernel.template b/imap_processing/tests/spice/test_data/imap_test_metakernel.template similarity index 100% rename from tools/tests/test_data/spice/imap_test_metakernel.template rename to imap_processing/tests/spice/test_data/imap_test_metakernel.template diff --git a/tools/tests/test_data/spice/naif0012.tls b/imap_processing/tests/spice/test_data/naif0012.tls similarity index 100% rename from tools/tests/test_data/spice/naif0012.tls rename to imap_processing/tests/spice/test_data/naif0012.tls diff --git a/tools/tests/unit/test_spice_examples.py b/tools/tests/unit/test_spice_examples.py index d998a3008..d47a607cd 100644 --- a/tools/tests/unit/test_spice_examples.py +++ b/tools/tests/unit/test_spice_examples.py @@ -4,6 +4,7 @@ import pytest import spiceypy as spice +from imap_processing import imap_module_directory from tools.spice.spice_examples import ( _get_particle_velocity, build_annotated_events, @@ -29,6 +30,12 @@ def kernels(kernel_directory): kernels = list_files_with_extensions( kernel_directory, [".tsc", ".tls", ".tf", ".bsp", ".ck"] ) + # Some kernels were moved into imap_processing package + kernels.extend( + list_files_with_extensions( + imap_module_directory / "tests/spice/test_data", [".tsc", ".tls"] + ) + ) return kernels