imap-processing 0.9.0__py3-none-any.whl → 0.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of imap-processing might be problematic. Click here for more details.
- imap_processing/_version.py +2 -2
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +749 -442
- imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +7 -0
- imap_processing/cdf/config/imap_glows_l1a_variable_attrs.yaml +8 -2
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +0 -1
- imap_processing/cdf/config/imap_glows_l2_variable_attrs.yaml +358 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +59 -25
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +22 -0
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +32 -8
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +94 -5
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +65 -37
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +16 -1
- imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +7 -0
- imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +14 -14
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +25 -24
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +238 -0
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +100 -92
- imap_processing/cdf/utils.py +2 -2
- imap_processing/cli.py +45 -9
- imap_processing/codice/codice_l1a.py +104 -58
- imap_processing/codice/constants.py +111 -155
- imap_processing/codice/data/esa_sweep_values.csv +256 -256
- imap_processing/codice/data/lo_stepping_values.csv +128 -128
- imap_processing/ena_maps/ena_maps.py +519 -0
- imap_processing/ena_maps/utils/map_utils.py +145 -0
- imap_processing/ena_maps/utils/spatial_utils.py +226 -0
- imap_processing/glows/__init__.py +3 -0
- imap_processing/glows/ancillary/imap_glows_pipeline_settings_v001.json +52 -0
- imap_processing/glows/l1a/glows_l1a.py +72 -14
- imap_processing/glows/l1b/glows_l1b.py +2 -1
- imap_processing/glows/l1b/glows_l1b_data.py +25 -1
- imap_processing/glows/l2/glows_l2.py +324 -0
- imap_processing/glows/l2/glows_l2_data.py +156 -51
- imap_processing/hi/l1a/science_direct_event.py +57 -51
- imap_processing/hi/l1b/hi_l1b.py +43 -28
- imap_processing/hi/l1c/hi_l1c.py +225 -42
- imap_processing/hi/utils.py +20 -3
- imap_processing/hit/l0/constants.py +2 -2
- imap_processing/hit/l0/decom_hit.py +1 -1
- imap_processing/hit/l1a/hit_l1a.py +94 -13
- imap_processing/hit/l1b/hit_l1b.py +158 -9
- imap_processing/ialirt/l0/process_codicehi.py +156 -0
- imap_processing/ialirt/l0/process_codicelo.py +5 -2
- imap_processing/ialirt/packet_definitions/ialirt.xml +28 -20
- imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +241 -0
- imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +170 -0
- imap_processing/ialirt/packet_definitions/ialirt_swe.xml +258 -0
- imap_processing/ialirt/process_ephemeris.py +72 -40
- imap_processing/idex/decode.py +241 -0
- imap_processing/idex/idex_l1a.py +143 -81
- imap_processing/idex/idex_l1b.py +244 -10
- imap_processing/lo/l0/lo_science.py +61 -0
- imap_processing/lo/l1a/lo_l1a.py +98 -10
- imap_processing/lo/l1b/lo_l1b.py +2 -2
- imap_processing/lo/l1c/lo_l1c.py +2 -2
- imap_processing/lo/packet_definitions/lo_xtce.xml +1082 -9178
- imap_processing/mag/l0/decom_mag.py +2 -2
- imap_processing/mag/l1a/mag_l1a.py +7 -7
- imap_processing/mag/l1a/mag_l1a_data.py +62 -30
- imap_processing/mag/l1b/mag_l1b.py +11 -6
- imap_processing/quality_flags.py +18 -3
- imap_processing/spice/geometry.py +149 -177
- imap_processing/spice/kernels.py +26 -26
- imap_processing/spice/spin.py +233 -0
- imap_processing/spice/time.py +96 -31
- imap_processing/swapi/l1/swapi_l1.py +60 -31
- imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +363 -384
- imap_processing/swe/l1a/swe_l1a.py +8 -3
- imap_processing/swe/l1a/swe_science.py +24 -24
- imap_processing/swe/l1b/swe_l1b.py +2 -1
- imap_processing/swe/l1b/swe_l1b_science.py +181 -122
- imap_processing/swe/l2/swe_l2.py +337 -70
- imap_processing/swe/utils/swe_utils.py +28 -0
- imap_processing/tests/cdf/test_utils.py +2 -2
- imap_processing/tests/codice/conftest.py +20 -17
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hskp_20241110193622_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/test_codice_l0.py +55 -121
- imap_processing/tests/codice/test_codice_l1a.py +147 -59
- imap_processing/tests/conftest.py +81 -22
- imap_processing/tests/ena_maps/test_ena_maps.py +309 -0
- imap_processing/tests/ena_maps/test_map_utils.py +286 -0
- imap_processing/tests/ena_maps/test_spatial_utils.py +161 -0
- imap_processing/tests/glows/conftest.py +7 -1
- imap_processing/tests/glows/test_glows_l1a_cdf.py +3 -7
- imap_processing/tests/glows/test_glows_l1a_data.py +34 -6
- imap_processing/tests/glows/test_glows_l1b_data.py +29 -17
- imap_processing/tests/glows/test_glows_l2.py +101 -0
- imap_processing/tests/hi/conftest.py +3 -3
- imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
- imap_processing/tests/hi/data/l1/imap_his_pset-calibration-prod-config_20240101_v001.csv +31 -0
- imap_processing/tests/hi/test_hi_l1b.py +14 -9
- imap_processing/tests/hi/test_hi_l1c.py +136 -36
- imap_processing/tests/hi/test_l1a.py +0 -2
- imap_processing/tests/hi/test_science_direct_event.py +18 -14
- imap_processing/tests/hi/test_utils.py +16 -11
- imap_processing/tests/hit/helpers/__init__.py +0 -0
- imap_processing/tests/hit/helpers/l1_validation.py +405 -0
- imap_processing/tests/hit/test_data/sci_sample.ccsds +0 -0
- imap_processing/tests/hit/test_decom_hit.py +8 -10
- imap_processing/tests/hit/test_hit_l1a.py +117 -180
- imap_processing/tests/hit/test_hit_l1b.py +149 -55
- imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -0
- imap_processing/tests/hit/validation_data/sci_sample_raw.csv +62 -0
- imap_processing/tests/ialirt/test_data/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/eu_SWP_IAL_20240826_152033.csv +644 -0
- imap_processing/tests/ialirt/test_data/l0/hi_fsw_view_1_ccsds.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +914 -0
- imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
- imap_processing/tests/ialirt/unit/test_process_codicehi.py +106 -0
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +33 -5
- imap_processing/tests/ialirt/unit/test_process_swapi.py +85 -0
- imap_processing/tests/ialirt/unit/test_process_swe.py +106 -0
- imap_processing/tests/idex/conftest.py +29 -1
- imap_processing/tests/idex/test_data/compressed_2023_102_14_24_55.pkts +0 -0
- imap_processing/tests/idex/test_data/non_compressed_2023_102_14_22_26.pkts +0 -0
- imap_processing/tests/idex/test_idex_l0.py +6 -3
- imap_processing/tests/idex/test_idex_l1a.py +151 -1
- imap_processing/tests/idex/test_idex_l1b.py +124 -2
- imap_processing/tests/lo/test_lo_l1a.py +62 -2
- imap_processing/tests/lo/test_lo_science.py +85 -0
- imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SPIN_EU.csv +2 -0
- imap_processing/tests/mag/conftest.py +16 -0
- imap_processing/tests/mag/test_mag_decom.py +6 -4
- imap_processing/tests/mag/test_mag_l1a.py +36 -7
- imap_processing/tests/mag/test_mag_l1b.py +55 -4
- imap_processing/tests/mag/test_mag_validation.py +148 -0
- imap_processing/tests/mag/validation/L1a/T001/all_p_ones.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T002/all_n_ones.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T003/field_like.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T004/field_like.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T005/field_like_range_change.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T006/hdr_field.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T007/hdr_field_and_range_change.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-out.csv +17 -0
- imap_processing/tests/mag/validation/L1a/T008/field_like_range_change.txt +19200 -0
- imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-in.bin +0 -0
- imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T009/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T010/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T010/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-mago-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T011/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +17 -0
- imap_processing/tests/spice/test_geometry.py +128 -133
- imap_processing/tests/spice/test_kernels.py +37 -37
- imap_processing/tests/spice/test_spin.py +184 -0
- imap_processing/tests/spice/test_time.py +43 -20
- imap_processing/tests/swapi/test_swapi_l1.py +11 -10
- imap_processing/tests/swapi/test_swapi_l2.py +13 -3
- imap_processing/tests/swe/test_swe_l1a.py +1 -1
- imap_processing/tests/swe/test_swe_l1b.py +20 -3
- imap_processing/tests/swe/test_swe_l1b_science.py +54 -35
- imap_processing/tests/swe/test_swe_l2.py +148 -5
- imap_processing/tests/test_cli.py +39 -7
- imap_processing/tests/test_quality_flags.py +19 -19
- imap_processing/tests/test_utils.py +3 -2
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
- imap_processing/tests/ultra/test_data/mock_data.py +161 -0
- imap_processing/tests/ultra/unit/conftest.py +73 -0
- imap_processing/tests/ultra/unit/test_badtimes.py +58 -0
- imap_processing/tests/ultra/unit/test_cullingmask.py +87 -0
- imap_processing/tests/ultra/unit/test_de.py +61 -60
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +3 -3
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +51 -77
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +5 -5
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +114 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -26
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +1 -1
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +3 -3
- imap_processing/ultra/constants.py +11 -1
- imap_processing/ultra/l1a/ultra_l1a.py +2 -2
- imap_processing/ultra/l1b/badtimes.py +22 -5
- imap_processing/ultra/l1b/cullingmask.py +31 -5
- imap_processing/ultra/l1b/de.py +32 -37
- imap_processing/ultra/l1b/extendedspin.py +44 -20
- imap_processing/ultra/l1b/ultra_l1b.py +21 -22
- imap_processing/ultra/l1b/ultra_l1b_culling.py +190 -0
- imap_processing/ultra/l1b/ultra_l1b_extended.py +81 -30
- imap_processing/ultra/l1c/histogram.py +6 -2
- imap_processing/ultra/l1c/pset.py +6 -2
- imap_processing/ultra/l1c/ultra_l1c.py +2 -3
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +4 -3
- imap_processing/ultra/utils/ultra_l1_utils.py +70 -14
- imap_processing/utils.py +2 -2
- {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/METADATA +7 -2
- {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/RECORD +235 -152
- imap_processing/tests/codice/data/eu_unit_lookup_table.csv +0 -101
- imap_processing/tests/codice/data/idle_export_eu.COD_NHK_20230822_122700 2.csv +0 -100
- imap_processing/tests/codice/data/idle_export_raw.COD_NHK_20230822_122700.csv +0 -100
- imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
- imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
- imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
- imap_processing/tests/ultra/unit/test_spatial_utils.py +0 -125
- imap_processing/ultra/utils/spatial_utils.py +0 -221
- /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.csv +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_CNT.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_DE.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/H90_NHK_20241104.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_cnt_20241104.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_de_20241104.bin +0 -0
- /imap_processing/tests/hi/{test_data → data}/l0/README.txt +0 -0
- /imap_processing/tests/idex/{imap_idex_l0_raw_20231214_v001.pkts → test_data/imap_idex_l0_raw_20231214_v001.pkts} +0 -0
- /imap_processing/tests/idex/{impact_14_tof_high_data.txt → test_data/impact_14_tof_high_data.txt} +0 -0
- /imap_processing/tests/mag/{imap_mag_l1a_norm-magi_20251017_v001.cdf → validation/imap_mag_l1a_norm-magi_20251017_v001.cdf} +0 -0
- /imap_processing/tests/mag/{mag_l0_test_data.pkts → validation/mag_l0_test_data.pkts} +0 -0
- /imap_processing/tests/mag/{mag_l0_test_output.csv → validation/mag_l0_test_output.csv} +0 -0
- /imap_processing/tests/mag/{mag_l1_test_data.pkts → validation/mag_l1_test_data.pkts} +0 -0
- /imap_processing/tests/mag/{mag_l1a_test_output.csv → validation/mag_l1a_test_output.csv} +0 -0
- {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Functions for computing geometry, many of which use
|
|
2
|
+
Functions for computing geometry, many of which use SPICEYPY.
|
|
3
3
|
|
|
4
4
|
Paradigms for developing this module:
|
|
5
5
|
|
|
@@ -9,16 +9,13 @@ Paradigms for developing this module:
|
|
|
9
9
|
* Always return numpy arrays for vectorized calls.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
import os
|
|
13
12
|
import typing
|
|
14
13
|
from enum import IntEnum
|
|
15
|
-
from pathlib import Path
|
|
16
14
|
from typing import Union
|
|
17
15
|
|
|
18
16
|
import numpy as np
|
|
19
17
|
import numpy.typing as npt
|
|
20
|
-
import
|
|
21
|
-
import spiceypy as spice
|
|
18
|
+
import spiceypy
|
|
22
19
|
from numpy.typing import NDArray
|
|
23
20
|
|
|
24
21
|
from imap_processing.spice.kernels import ensure_spice
|
|
@@ -33,17 +30,18 @@ class SpiceBody(IntEnum):
|
|
|
33
30
|
# IMAP Pointing Frame (Despun) as defined in imap_science_0001.tf
|
|
34
31
|
IMAP_DPS = -43901
|
|
35
32
|
# Standard NAIF bodies
|
|
36
|
-
SOLAR_SYSTEM_BARYCENTER =
|
|
37
|
-
SUN =
|
|
38
|
-
EARTH =
|
|
33
|
+
SOLAR_SYSTEM_BARYCENTER = spiceypy.bodn2c("SOLAR_SYSTEM_BARYCENTER")
|
|
34
|
+
SUN = spiceypy.bodn2c("SUN")
|
|
35
|
+
EARTH = spiceypy.bodn2c("EARTH")
|
|
39
36
|
|
|
40
37
|
|
|
41
38
|
class SpiceFrame(IntEnum):
|
|
42
39
|
"""Enum containing SPICE IDs for reference frames, defined in imap_wkcp.tf."""
|
|
43
40
|
|
|
44
41
|
# Standard SPICE Frames
|
|
45
|
-
J2000 =
|
|
46
|
-
ECLIPJ2000 =
|
|
42
|
+
J2000 = spiceypy.irfnum("J2000")
|
|
43
|
+
ECLIPJ2000 = spiceypy.irfnum("ECLIPJ2000")
|
|
44
|
+
ITRF93 = 13000
|
|
47
45
|
# IMAP Pointing Frame (Despun) as defined in imap_science_0001.tf
|
|
48
46
|
IMAP_DPS = -43901
|
|
49
47
|
# IMAP specific as defined in imap_wkcp.tf
|
|
@@ -85,6 +83,7 @@ BORESIGHT_LOOKUP = {
|
|
|
85
83
|
def imap_state(
|
|
86
84
|
et: Union[np.ndarray, float],
|
|
87
85
|
ref_frame: SpiceFrame = SpiceFrame.ECLIPJ2000,
|
|
86
|
+
abcorr: str = "NONE",
|
|
88
87
|
observer: SpiceBody = SpiceBody.SUN,
|
|
89
88
|
) -> np.ndarray:
|
|
90
89
|
"""
|
|
@@ -96,10 +95,12 @@ def imap_state(
|
|
|
96
95
|
----------
|
|
97
96
|
et : np.ndarray or float
|
|
98
97
|
Epoch time(s) [J2000 seconds] to get the IMAP state for.
|
|
99
|
-
ref_frame : SpiceFrame
|
|
98
|
+
ref_frame : SpiceFrame (Optional)
|
|
100
99
|
Reference frame which the IMAP state is expressed in. Default is
|
|
101
100
|
SpiceFrame.ECLIPJ2000.
|
|
102
|
-
|
|
101
|
+
abcorr : str (Optional)
|
|
102
|
+
Aberration correction flag. Default is "NONE".
|
|
103
|
+
observer : SpiceBody (Optional)
|
|
103
104
|
Observing body. Default is SpiceBody.SUN.
|
|
104
105
|
|
|
105
106
|
Returns
|
|
@@ -108,163 +109,12 @@ def imap_state(
|
|
|
108
109
|
The Cartesian state vector representing the position and velocity of the
|
|
109
110
|
IMAP spacecraft.
|
|
110
111
|
"""
|
|
111
|
-
state, _ =
|
|
112
|
-
SpiceBody.IMAP.name, et, ref_frame.name,
|
|
112
|
+
state, _ = spiceypy.spkezr(
|
|
113
|
+
SpiceBody.IMAP.name, et, ref_frame.name, abcorr, observer.name
|
|
113
114
|
)
|
|
114
115
|
return np.asarray(state)
|
|
115
116
|
|
|
116
117
|
|
|
117
|
-
def get_spin_data() -> pd.DataFrame:
|
|
118
|
-
"""
|
|
119
|
-
Read spin file using environment variable and return spin data.
|
|
120
|
-
|
|
121
|
-
SPIN_DATA_FILEPATH environment variable would be a fixed value.
|
|
122
|
-
It could be s3 filepath that can be used to download the data
|
|
123
|
-
through API or it could be path EFS or Batch volume mount path.
|
|
124
|
-
|
|
125
|
-
Spin data should contain the following fields:
|
|
126
|
-
* spin_number
|
|
127
|
-
* spin_start_sec
|
|
128
|
-
* spin_start_subsec
|
|
129
|
-
* spin_period_sec
|
|
130
|
-
* spin_period_valid
|
|
131
|
-
* spin_phase_valid
|
|
132
|
-
* spin_period_source
|
|
133
|
-
* thruster_firing
|
|
134
|
-
|
|
135
|
-
Returns
|
|
136
|
-
-------
|
|
137
|
-
spin_data : pandas.DataFrame
|
|
138
|
-
Spin data.
|
|
139
|
-
"""
|
|
140
|
-
spin_data_filepath = os.getenv("SPIN_DATA_FILEPATH")
|
|
141
|
-
if spin_data_filepath is not None:
|
|
142
|
-
path_to_spin_file = Path(spin_data_filepath)
|
|
143
|
-
else:
|
|
144
|
-
# Handle the case where the environment variable is not set
|
|
145
|
-
raise ValueError("SPIN_DATA_FILEPATH environment variable is not set.")
|
|
146
|
-
|
|
147
|
-
spin_df = pd.read_csv(path_to_spin_file, comment="#")
|
|
148
|
-
# Combine spin_start_sec and spin_start_subsec to get the spin start
|
|
149
|
-
# time in seconds. The spin start subseconds are in milliseconds.
|
|
150
|
-
spin_df["spin_start_time"] = (
|
|
151
|
-
spin_df["spin_start_sec"] + spin_df["spin_start_subsec"] / 1e3
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
return spin_df
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def get_spacecraft_spin_phase(
|
|
158
|
-
query_met_times: Union[float, npt.NDArray],
|
|
159
|
-
) -> Union[float, npt.NDArray]:
|
|
160
|
-
"""
|
|
161
|
-
Get the spacecraft spin phase for the input query times.
|
|
162
|
-
|
|
163
|
-
Formula to calculate spin phase:
|
|
164
|
-
spin_phase = (query_met_times - spin_start_time) / spin_period_sec
|
|
165
|
-
|
|
166
|
-
Parameters
|
|
167
|
-
----------
|
|
168
|
-
query_met_times : float or np.ndarray
|
|
169
|
-
Query times in Mission Elapsed Time (MET).
|
|
170
|
-
|
|
171
|
-
Returns
|
|
172
|
-
-------
|
|
173
|
-
spin_phase : float or np.ndarray
|
|
174
|
-
Spin phase for the input query times.
|
|
175
|
-
"""
|
|
176
|
-
spin_df = get_spin_data()
|
|
177
|
-
|
|
178
|
-
# Ensure query_met_times is an array
|
|
179
|
-
query_met_times = np.asarray(query_met_times)
|
|
180
|
-
is_scalar = query_met_times.ndim == 0
|
|
181
|
-
if is_scalar:
|
|
182
|
-
# Force scalar to array because np.asarray() will not
|
|
183
|
-
# convert scalar to array
|
|
184
|
-
query_met_times = np.atleast_1d(query_met_times)
|
|
185
|
-
# Empty array check
|
|
186
|
-
if query_met_times.size == 0:
|
|
187
|
-
return query_met_times
|
|
188
|
-
|
|
189
|
-
# Create an empty array to store spin phase results
|
|
190
|
-
spin_phases = np.zeros_like(query_met_times)
|
|
191
|
-
|
|
192
|
-
# Find all spin time that are less or equal to query_met_times.
|
|
193
|
-
# To do that, use side right, a[i-1] <= v < a[i], in the searchsorted.
|
|
194
|
-
# Eg.
|
|
195
|
-
# >>> df['a']
|
|
196
|
-
# array([0, 15, 30, 45, 60])
|
|
197
|
-
# >>> np.searchsorted(df['a'], [0, 13, 15, 32, 70], side='right')
|
|
198
|
-
# array([1, 1, 2, 3, 5])
|
|
199
|
-
last_spin_indices = (
|
|
200
|
-
np.searchsorted(spin_df["spin_start_time"], query_met_times, side="right") - 1
|
|
201
|
-
)
|
|
202
|
-
# Make sure input times are within the bounds of spin data
|
|
203
|
-
spin_df_start_time = spin_df["spin_start_time"].values[0]
|
|
204
|
-
spin_df_end_time = (
|
|
205
|
-
spin_df["spin_start_time"].values[-1] + spin_df["spin_period_sec"].values[-1]
|
|
206
|
-
)
|
|
207
|
-
input_start_time = query_met_times.min()
|
|
208
|
-
input_end_time = query_met_times.max()
|
|
209
|
-
if input_start_time < spin_df_start_time or input_end_time >= spin_df_end_time:
|
|
210
|
-
raise ValueError(
|
|
211
|
-
f"Query times, {query_met_times} are outside of the spin data range, "
|
|
212
|
-
f"{spin_df_start_time, spin_df_end_time}."
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
# Calculate spin phase
|
|
216
|
-
spin_phases = (
|
|
217
|
-
query_met_times - spin_df["spin_start_time"].values[last_spin_indices]
|
|
218
|
-
) / spin_df["spin_period_sec"].values[last_spin_indices]
|
|
219
|
-
|
|
220
|
-
# Check for invalid spin phase using below checks:
|
|
221
|
-
# 1. Check that the spin phase is in valid range, [0, 1).
|
|
222
|
-
# 2. Check invalid spin phase using spin_phase_valid,
|
|
223
|
-
# spin_period_valid columns.
|
|
224
|
-
invalid_spin_phase_range = (spin_phases < 0) | (spin_phases >= 1)
|
|
225
|
-
|
|
226
|
-
invalid_spins = (spin_df["spin_phase_valid"].values[last_spin_indices] == 0) | (
|
|
227
|
-
spin_df["spin_period_valid"].values[last_spin_indices] == 0
|
|
228
|
-
)
|
|
229
|
-
bad_spin_phases = invalid_spin_phase_range | invalid_spins
|
|
230
|
-
spin_phases[bad_spin_phases] = np.nan
|
|
231
|
-
|
|
232
|
-
if is_scalar:
|
|
233
|
-
return spin_phases[0]
|
|
234
|
-
return spin_phases
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def get_instrument_spin_phase(
|
|
238
|
-
query_met_times: Union[float, npt.NDArray],
|
|
239
|
-
instrument: SpiceFrame,
|
|
240
|
-
) -> Union[float, npt.NDArray]:
|
|
241
|
-
"""
|
|
242
|
-
Get the instrument spin phase for the input query times.
|
|
243
|
-
|
|
244
|
-
Formula to calculate spin phase:
|
|
245
|
-
instrument_spin_phase = (spacecraft_spin_phase + instrument_spin_offset) % 1
|
|
246
|
-
|
|
247
|
-
Parameters
|
|
248
|
-
----------
|
|
249
|
-
query_met_times : float or np.ndarray
|
|
250
|
-
Query times in Mission Elapsed Time (MET).
|
|
251
|
-
instrument : SpiceFrame
|
|
252
|
-
Instrument frame to calculate spin phase for.
|
|
253
|
-
|
|
254
|
-
Returns
|
|
255
|
-
-------
|
|
256
|
-
spin_phase : float or np.ndarray
|
|
257
|
-
Instrument spin phase for the input query times. Spin phase is a
|
|
258
|
-
floating point number in the range [0, 1) corresponding to the
|
|
259
|
-
spin angle / 360.
|
|
260
|
-
"""
|
|
261
|
-
spacecraft_spin_phase = get_spacecraft_spin_phase(query_met_times)
|
|
262
|
-
instrument_spin_phase_offset = get_spacecraft_to_instrument_spin_phase_offset(
|
|
263
|
-
instrument
|
|
264
|
-
)
|
|
265
|
-
return (spacecraft_spin_phase + instrument_spin_phase_offset) % 1
|
|
266
|
-
|
|
267
|
-
|
|
268
118
|
def get_spacecraft_to_instrument_spin_phase_offset(instrument: SpiceFrame) -> float:
|
|
269
119
|
"""
|
|
270
120
|
Get the spin phase offset from the spacecraft to the instrument.
|
|
@@ -314,9 +164,9 @@ def frame_transform(
|
|
|
314
164
|
This function is a vectorized equivalent to performing the following SPICE
|
|
315
165
|
calls for each input time and position vector to perform the transform.
|
|
316
166
|
The matrix multiplication step is done using `numpy.matmul` rather than
|
|
317
|
-
`
|
|
318
|
-
>>> rotation_matrix =
|
|
319
|
-
... result =
|
|
167
|
+
`spiceypy.mxv`.
|
|
168
|
+
>>> rotation_matrix = spiceypy.pxform(from_frame, to_frame, et)
|
|
169
|
+
... result = spiceypy.mxv(rotation_matrix, position)
|
|
320
170
|
|
|
321
171
|
Parameters
|
|
322
172
|
----------
|
|
@@ -339,6 +189,10 @@ def frame_transform(
|
|
|
339
189
|
result : np.ndarray
|
|
340
190
|
3d Cartesian position vector(s) in reference frame `to_frame`.
|
|
341
191
|
"""
|
|
192
|
+
# If from_frame and to_frame are the same, no rotation needed
|
|
193
|
+
if from_frame == to_frame:
|
|
194
|
+
return position
|
|
195
|
+
|
|
342
196
|
if position.ndim == 1:
|
|
343
197
|
if not len(position) == 3:
|
|
344
198
|
raise ValueError(
|
|
@@ -372,6 +226,56 @@ def frame_transform(
|
|
|
372
226
|
return result
|
|
373
227
|
|
|
374
228
|
|
|
229
|
+
def frame_transform_az_el(
|
|
230
|
+
et: Union[float, npt.NDArray],
|
|
231
|
+
az_el: npt.NDArray,
|
|
232
|
+
from_frame: SpiceFrame,
|
|
233
|
+
to_frame: SpiceFrame,
|
|
234
|
+
degrees: bool = True,
|
|
235
|
+
) -> npt.NDArray:
|
|
236
|
+
"""
|
|
237
|
+
Transform azimuth and elevation coordinates between reference frames.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
et : float or np.ndarray
|
|
242
|
+
Ephemeris time(s) corresponding to position(s).
|
|
243
|
+
az_el : np.ndarray
|
|
244
|
+
<azimuth, elevation> vector or array of vectors in reference frame `from_frame`.
|
|
245
|
+
There are several possible shapes for the input az_el and et:
|
|
246
|
+
1. A single az_el vector may be provided for multiple `et` query times
|
|
247
|
+
2. A single `et` may be provided for multiple az_el vectors,
|
|
248
|
+
3. The same number of `et` and az_el vectors may be provided.
|
|
249
|
+
It is not allowed to have n az_el vectors and m `et`, where n != m.
|
|
250
|
+
from_frame : SpiceFrame
|
|
251
|
+
Reference frame of input coordinates.
|
|
252
|
+
to_frame : SpiceFrame
|
|
253
|
+
Reference frame of output coordinates.
|
|
254
|
+
degrees : bool
|
|
255
|
+
If True, azimuth and elevation input and output will be in degrees.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
to_frame_az_el : np.ndarray
|
|
260
|
+
Azimuth/elevation coordinates in reference frame `to_frame`. This
|
|
261
|
+
output coordinate vector will have shape (2,) if a single `az_el` position
|
|
262
|
+
vector and single `et` time are input. Otherwise, it will have shape (n, 2)
|
|
263
|
+
where n is the number of input position vector or ephemeris times. The last
|
|
264
|
+
axis of the output vector contains azimuth in the 0th position and elevation
|
|
265
|
+
in the 1st position.
|
|
266
|
+
"""
|
|
267
|
+
# Convert input az/el to Cartesian vectors
|
|
268
|
+
spherical_coords_in = np.array(
|
|
269
|
+
[np.ones_like(az_el[..., 0]), az_el[..., 0], az_el[..., 1]]
|
|
270
|
+
).T
|
|
271
|
+
from_frame_cartesian = spherical_to_cartesian(spherical_coords_in, degrees=degrees)
|
|
272
|
+
# Transform to to_frame
|
|
273
|
+
to_frame_cartesian = frame_transform(et, from_frame_cartesian, from_frame, to_frame)
|
|
274
|
+
# Convert to spherical and extract azimuth/elevation
|
|
275
|
+
to_frame_az_el = cartesian_to_spherical(to_frame_cartesian, degrees=degrees)
|
|
276
|
+
return to_frame_az_el[..., 1:3]
|
|
277
|
+
|
|
278
|
+
|
|
375
279
|
@typing.no_type_check
|
|
376
280
|
@ensure_spice
|
|
377
281
|
def get_rotation_matrix(
|
|
@@ -404,7 +308,7 @@ def get_rotation_matrix(
|
|
|
404
308
|
where `n` matches the number of elements in et.
|
|
405
309
|
"""
|
|
406
310
|
vec_pxform = np.vectorize(
|
|
407
|
-
|
|
311
|
+
spiceypy.pxform,
|
|
408
312
|
excluded=["fromstr", "tostr"],
|
|
409
313
|
signature="(),(),()->(3,3)",
|
|
410
314
|
otypes=[np.float64],
|
|
@@ -446,8 +350,8 @@ def instrument_pointing(
|
|
|
446
350
|
if cartesian:
|
|
447
351
|
return pointing
|
|
448
352
|
if isinstance(et, typing.Collection):
|
|
449
|
-
return np.rad2deg([
|
|
450
|
-
return np.rad2deg(
|
|
353
|
+
return np.rad2deg([spiceypy.reclat(vec)[1:] for vec in pointing])
|
|
354
|
+
return np.rad2deg(spiceypy.reclat(pointing)[1:])
|
|
451
355
|
|
|
452
356
|
|
|
453
357
|
def basis_vectors(
|
|
@@ -483,8 +387,8 @@ def basis_vectors(
|
|
|
483
387
|
Examples
|
|
484
388
|
--------
|
|
485
389
|
>>> from imap_processing.spice.geometry import basis_vectors
|
|
486
|
-
... from imap_processing.spice.time import
|
|
487
|
-
... et =
|
|
390
|
+
... from imap_processing.spice.time import ttj2000ns_to_et
|
|
391
|
+
... et = ttj2000ns_to_et(dataset.epoch.values)
|
|
488
392
|
... basis_vectors = basis_vectors(
|
|
489
393
|
... et, SpiceFrame.IMAP_SPACECRAFT, SpiceFrame.ECLIPJ2000
|
|
490
394
|
... )
|
|
@@ -524,7 +428,7 @@ def cartesian_to_spherical(
|
|
|
524
428
|
output range=[0, 360],
|
|
525
429
|
otherwise in radians if degrees parameter is False:
|
|
526
430
|
output range=[0, 2*pi].
|
|
527
|
-
- elevation : angle from the
|
|
431
|
+
- elevation : angle from the xy-plane
|
|
528
432
|
In degrees if degrees parameter is True (by default):
|
|
529
433
|
output range=[0, 180],
|
|
530
434
|
otherwise in radians if degrees parameter is False:
|
|
@@ -535,7 +439,7 @@ def cartesian_to_spherical(
|
|
|
535
439
|
|
|
536
440
|
vhat = v / magnitude_v
|
|
537
441
|
|
|
538
|
-
# Elevation angle (angle from the
|
|
442
|
+
# Elevation angle (angle from the xy-plane, range: [-pi/2, pi/2])
|
|
539
443
|
el = np.arcsin(vhat[..., 2])
|
|
540
444
|
|
|
541
445
|
# Azimuth angle (angle in the xy-plane, range: [0, 2*pi])
|
|
@@ -553,7 +457,7 @@ def cartesian_to_spherical(
|
|
|
553
457
|
return spherical_coords
|
|
554
458
|
|
|
555
459
|
|
|
556
|
-
def spherical_to_cartesian(spherical_coords: NDArray) -> NDArray:
|
|
460
|
+
def spherical_to_cartesian(spherical_coords: NDArray, degrees: bool = False) -> NDArray:
|
|
557
461
|
"""
|
|
558
462
|
Convert spherical coordinates to Cartesian coordinates.
|
|
559
463
|
|
|
@@ -565,7 +469,10 @@ def spherical_to_cartesian(spherical_coords: NDArray) -> NDArray:
|
|
|
565
469
|
|
|
566
470
|
- r : Distance of the point from the origin.
|
|
567
471
|
- azimuth : angle in the xy-plane in radians [0, 2*pi].
|
|
568
|
-
- elevation : angle from the
|
|
472
|
+
- elevation : angle from the xy-plane in radians [-pi/2, pi/2].
|
|
473
|
+
degrees : bool
|
|
474
|
+
Set to True if input azimuth and elevation angles are in degrees.
|
|
475
|
+
Defaults to False.
|
|
569
476
|
|
|
570
477
|
Returns
|
|
571
478
|
-------
|
|
@@ -576,6 +483,10 @@ def spherical_to_cartesian(spherical_coords: NDArray) -> NDArray:
|
|
|
576
483
|
azimuth = spherical_coords[..., 1]
|
|
577
484
|
elevation = spherical_coords[..., 2]
|
|
578
485
|
|
|
486
|
+
if degrees:
|
|
487
|
+
azimuth = np.radians(azimuth)
|
|
488
|
+
elevation = np.radians(elevation)
|
|
489
|
+
|
|
579
490
|
x = r * np.cos(elevation) * np.cos(azimuth)
|
|
580
491
|
y = r * np.cos(elevation) * np.sin(azimuth)
|
|
581
492
|
z = r * np.sin(elevation)
|
|
@@ -583,3 +494,64 @@ def spherical_to_cartesian(spherical_coords: NDArray) -> NDArray:
|
|
|
583
494
|
cartesian_coords = np.stack((x, y, z), axis=-1)
|
|
584
495
|
|
|
585
496
|
return cartesian_coords
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def cartesian_to_latitudinal(coords: NDArray, degrees: bool = False) -> NDArray:
|
|
500
|
+
"""
|
|
501
|
+
Convert cartesian coordinates to latitudinal coordinates in radians.
|
|
502
|
+
|
|
503
|
+
This is a vectorized wrapper around `spiceypy.reclat`
|
|
504
|
+
"Convert from rectangular coordinates to latitudinal coordinates."
|
|
505
|
+
https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/reclat_c.html
|
|
506
|
+
|
|
507
|
+
Parameters
|
|
508
|
+
----------
|
|
509
|
+
coords : np.ndarray
|
|
510
|
+
Either shape (n, 3) or (3) where the last dimension represents a vector
|
|
511
|
+
with x, y, z-components.
|
|
512
|
+
degrees : bool
|
|
513
|
+
If True, the longitude and latitude coords are returned in degrees.
|
|
514
|
+
Defaults to False.
|
|
515
|
+
|
|
516
|
+
Returns
|
|
517
|
+
-------
|
|
518
|
+
np.ndarray
|
|
519
|
+
A NumPy array with shape (n, 3) or (3), where the last dimension contains
|
|
520
|
+
the latitudinal coordinates (radius, longitude, latitude).
|
|
521
|
+
"""
|
|
522
|
+
# If coords is 1d, add another dimension
|
|
523
|
+
while coords.ndim < 2:
|
|
524
|
+
coords = np.expand_dims(coords, axis=0)
|
|
525
|
+
latitudinal_coords = np.array([spiceypy.reclat(vec) for vec in coords])
|
|
526
|
+
|
|
527
|
+
if degrees:
|
|
528
|
+
latitudinal_coords[..., 1:] = np.degrees(latitudinal_coords[..., 1:])
|
|
529
|
+
# Return array of latitudinal and remove the first dimension if it is 1.
|
|
530
|
+
return np.squeeze(latitudinal_coords)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
def solar_longitude(
|
|
534
|
+
et: Union[np.ndarray, float],
|
|
535
|
+
degrees: bool = False,
|
|
536
|
+
) -> Union[float, npt.NDArray]:
|
|
537
|
+
"""
|
|
538
|
+
Compute the solar longitude of the Imap Spacecraft.
|
|
539
|
+
|
|
540
|
+
Parameters
|
|
541
|
+
----------
|
|
542
|
+
et : float or np.ndarray
|
|
543
|
+
Ephemeris time(s) to at which to compute solar longitude.
|
|
544
|
+
degrees : bool
|
|
545
|
+
If True, the longitude is returned in degrees.
|
|
546
|
+
Defaults to False.
|
|
547
|
+
|
|
548
|
+
Returns
|
|
549
|
+
-------
|
|
550
|
+
float or np.ndarray
|
|
551
|
+
The solar longitude at the specified times.
|
|
552
|
+
"""
|
|
553
|
+
# Get position of IMAP in ecliptic frame
|
|
554
|
+
imap_pos = imap_state(et, observer=SpiceBody.SUN)[..., 0:3]
|
|
555
|
+
lat_coords = cartesian_to_latitudinal(imap_pos, degrees=degrees)[..., 1]
|
|
556
|
+
|
|
557
|
+
return float(lat_coords) if lat_coords.size == 1 else lat_coords
|
imap_processing/spice/kernels.py
CHANGED
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any, Callable, Optional, Union, overload
|
|
10
10
|
|
|
11
11
|
import numpy as np
|
|
12
|
-
import spiceypy
|
|
12
|
+
import spiceypy
|
|
13
13
|
from numpy.typing import NDArray
|
|
14
14
|
from spiceypy.utils.exceptions import SpiceyError
|
|
15
15
|
|
|
@@ -145,7 +145,7 @@ def ensure_spice(
|
|
|
145
145
|
# Step 2.
|
|
146
146
|
if os.getenv("SPICE_METAKERNEL"):
|
|
147
147
|
metakernel_path = os.getenv("SPICE_METAKERNEL")
|
|
148
|
-
|
|
148
|
+
spiceypy.furnsh(metakernel_path)
|
|
149
149
|
else:
|
|
150
150
|
furnish_time_kernel()
|
|
151
151
|
except KeyError:
|
|
@@ -200,13 +200,13 @@ def open_spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]:
|
|
|
200
200
|
# and how that will affect appending to the pointing
|
|
201
201
|
# frame kernel.
|
|
202
202
|
if pointing_frame_path.exists():
|
|
203
|
-
handle =
|
|
203
|
+
handle = spiceypy.dafopw(str(pointing_frame_path))
|
|
204
204
|
else:
|
|
205
|
-
handle =
|
|
205
|
+
handle = spiceypy.ckopn(str(pointing_frame_path), "CK", 0)
|
|
206
206
|
try:
|
|
207
207
|
yield handle
|
|
208
208
|
finally:
|
|
209
|
-
|
|
209
|
+
spiceypy.ckcls(handle)
|
|
210
210
|
|
|
211
211
|
|
|
212
212
|
@ensure_spice
|
|
@@ -245,12 +245,12 @@ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None:
|
|
|
245
245
|
"""
|
|
246
246
|
# Get IDs.
|
|
247
247
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool
|
|
248
|
-
id_imap_dps =
|
|
249
|
-
id_imap_sclk =
|
|
248
|
+
id_imap_dps = spiceypy.gipool("FRAME_IMAP_DPS", 0, 1)
|
|
249
|
+
id_imap_sclk = spiceypy.gipool("CK_-43000_SCLK", 0, 1)
|
|
250
250
|
|
|
251
251
|
# Verify that only ck_path kernel is loaded.
|
|
252
|
-
count =
|
|
253
|
-
loaded_ck_kernel, _, _, _ =
|
|
252
|
+
count = spiceypy.ktotal("ck")
|
|
253
|
+
loaded_ck_kernel, _, _, _ = spiceypy.kdata(count - 1, "ck")
|
|
254
254
|
|
|
255
255
|
if count != 1 or str(ck_path) != loaded_ck_kernel:
|
|
256
256
|
raise ValueError(f"Error: Expected CK kernel {ck_path}")
|
|
@@ -258,28 +258,28 @@ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None:
|
|
|
258
258
|
# If the pointing frame kernel already exists, find the last time.
|
|
259
259
|
if pointing_frame_path.exists():
|
|
260
260
|
# Get the last time in the pointing frame kernel.
|
|
261
|
-
pointing_cover =
|
|
261
|
+
pointing_cover = spiceypy.ckcov(
|
|
262
262
|
str(pointing_frame_path), int(id_imap_dps), True, "SEGMENT", 0, "TDB"
|
|
263
263
|
)
|
|
264
|
-
num_segments =
|
|
265
|
-
_, et_end_pointing_frame =
|
|
264
|
+
num_segments = spiceypy.wncard(pointing_cover)
|
|
265
|
+
_, et_end_pointing_frame = spiceypy.wnfetd(pointing_cover, num_segments - 1)
|
|
266
266
|
else:
|
|
267
267
|
et_end_pointing_frame = None
|
|
268
268
|
|
|
269
269
|
# TODO: Query for .csv file to get the pointing start and end times.
|
|
270
270
|
# TODO: Remove next four lines once query is added.
|
|
271
|
-
id_imap_spacecraft =
|
|
272
|
-
ck_cover =
|
|
271
|
+
id_imap_spacecraft = spiceypy.gipool("FRAME_IMAP_SPACECRAFT", 0, 1)
|
|
272
|
+
ck_cover = spiceypy.ckcov(
|
|
273
273
|
str(ck_path), int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB"
|
|
274
274
|
)
|
|
275
|
-
num_intervals =
|
|
275
|
+
num_intervals = spiceypy.wncard(ck_cover)
|
|
276
276
|
|
|
277
277
|
with open_spice_ck_file(pointing_frame_path) as handle:
|
|
278
278
|
# TODO: this will change to the number of pointings.
|
|
279
279
|
for i in range(num_intervals):
|
|
280
280
|
# Get the coverage window
|
|
281
281
|
# TODO: this will change to pointing start and end time.
|
|
282
|
-
et_start, et_end =
|
|
282
|
+
et_start, et_end = spiceypy.wnfetd(ck_cover, i)
|
|
283
283
|
et_times = _get_et_times(et_start, et_end)
|
|
284
284
|
|
|
285
285
|
# TODO: remove after query is added.
|
|
@@ -294,16 +294,16 @@ def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None:
|
|
|
294
294
|
|
|
295
295
|
# Convert the rotation matrix to a quaternion.
|
|
296
296
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
|
|
297
|
-
q_avg =
|
|
297
|
+
q_avg = spiceypy.m2q(rotation_matrix)
|
|
298
298
|
|
|
299
299
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c
|
|
300
300
|
# Convert start and end times to SCLK.
|
|
301
|
-
sclk_begtim =
|
|
302
|
-
sclk_endtim =
|
|
301
|
+
sclk_begtim = spiceypy.sce2c(int(id_imap_sclk), et_times[0])
|
|
302
|
+
sclk_endtim = spiceypy.sce2c(int(id_imap_sclk), et_times[-1])
|
|
303
303
|
|
|
304
304
|
# Create the pointing frame kernel.
|
|
305
305
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02
|
|
306
|
-
|
|
306
|
+
spiceypy.ckw02(
|
|
307
307
|
# Handle of an open CK file.
|
|
308
308
|
handle,
|
|
309
309
|
# Start time of the segment.
|
|
@@ -355,7 +355,7 @@ def _get_et_times(et_start: float, et_end: float) -> NDArray[np.float64]:
|
|
|
355
355
|
|
|
356
356
|
# 1 spin/15 seconds; 10 quaternions / spin.
|
|
357
357
|
num_samples = (et_end - et_start) / 15 * 10
|
|
358
|
-
# There were rounding errors when using
|
|
358
|
+
# There were rounding errors when using spiceypy.pxform so np.ceil and np.floor
|
|
359
359
|
# were used to ensure the start and end times were included in the array.
|
|
360
360
|
et_times = np.linspace(
|
|
361
361
|
np.ceil(et_start * 1e6) / 1e6, np.floor(et_end * 1e6) / 1e6, int(num_samples)
|
|
@@ -390,10 +390,10 @@ def _average_quaternions(et_times: np.ndarray) -> NDArray:
|
|
|
390
390
|
|
|
391
391
|
# Rotation matrix from IMAP spacecraft frame to ECLIPJ2000.
|
|
392
392
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.pxform
|
|
393
|
-
body_rots =
|
|
393
|
+
body_rots = spiceypy.pxform("IMAP_SPACECRAFT", "ECLIPJ2000", tdb)
|
|
394
394
|
# Convert rotation matrix to quaternion.
|
|
395
395
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
|
|
396
|
-
body_quat =
|
|
396
|
+
body_quat = spiceypy.m2q(body_rots)
|
|
397
397
|
|
|
398
398
|
# Standardize the quaternion so that they may be compared.
|
|
399
399
|
body_quat = body_quat * np.sign(body_quat[0])
|
|
@@ -440,7 +440,7 @@ def _create_rotation_matrix(et_times: np.ndarray) -> NDArray:
|
|
|
440
440
|
# Converts the averaged quaternion (q_avg) into a rotation matrix
|
|
441
441
|
# and get inertial z axis.
|
|
442
442
|
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.q2m
|
|
443
|
-
z_avg =
|
|
443
|
+
z_avg = spiceypy.q2m(list(q_avg))[:, 2]
|
|
444
444
|
# y_avg is perpendicular to both z_avg and the standard Z-axis.
|
|
445
445
|
y_avg = np.cross(z_avg, [0, 0, 1])
|
|
446
446
|
# x_avg is perpendicular to y_avg and z_avg.
|
|
@@ -459,5 +459,5 @@ def furnish_time_kernel() -> None:
|
|
|
459
459
|
# TODO: we need to load these kernels from EFS volumen that is
|
|
460
460
|
# mounted to batch volume and extend this to generate metakernell
|
|
461
461
|
# which is TBD.
|
|
462
|
-
|
|
463
|
-
|
|
462
|
+
spiceypy.furnsh(str(spice_test_data_path / "imap_sclk_0000.tsc"))
|
|
463
|
+
spiceypy.furnsh(str(spice_test_data_path / "naif0012.tls"))
|