imap-processing 0.6.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.
- imap_processing/__init__.py +34 -0
- imap_processing/_version.py +3 -0
- imap_processing/ccsds/__init__.py +0 -0
- imap_processing/ccsds/ccsds_data.py +55 -0
- imap_processing/ccsds/excel_to_xtce.py +477 -0
- imap_processing/cdf/__init__.py +0 -0
- imap_processing/cdf/cdf_attribute_manager.py +322 -0
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +212 -0
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +1358 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +391 -0
- imap_processing/cdf/config/imap_constant_attrs.yaml +33 -0
- imap_processing/cdf/config/imap_default_global_cdf_attrs.yaml +17 -0
- imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +41 -0
- imap_processing/cdf/config/imap_glows_l1a_variable_attrs.yaml +499 -0
- imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +334 -0
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +51 -0
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +435 -0
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +27 -0
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +493 -0
- imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +564 -0
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +24 -0
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +426 -0
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +90 -0
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +487 -0
- imap_processing/cdf/config/imap_lo_l1b_variable_attrs.yaml +121 -0
- imap_processing/cdf/config/imap_lo_l1c_variable_attrs.yaml +179 -0
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +97 -0
- imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +201 -0
- imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +33 -0
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +137 -0
- imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +24 -0
- imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +234 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +273 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +100 -0
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +52 -0
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +297 -0
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +121 -0
- imap_processing/cdf/config/shared/default_global_cdf_attrs_schema.yaml +246 -0
- imap_processing/cdf/config/shared/default_variable_cdf_attrs_schema.yaml +466 -0
- imap_processing/cdf/imap_cdf_manager.py +64 -0
- imap_processing/cdf/utils.py +147 -0
- imap_processing/cli.py +863 -0
- imap_processing/codice/__init__.py +1 -0
- imap_processing/codice/codice_l0.py +54 -0
- imap_processing/codice/codice_l1a.py +558 -0
- imap_processing/codice/codice_l1b.py +194 -0
- imap_processing/codice/constants.py +986 -0
- imap_processing/codice/data/esa_sweep_values.csv +257 -0
- imap_processing/codice/data/lo_stepping_values.csv +129 -0
- imap_processing/codice/decompress.py +142 -0
- imap_processing/codice/packet_definitions/P_COD_NHK.xml +618 -0
- imap_processing/codice/packet_definitions/codice_packet_definition.xml +5073 -0
- imap_processing/codice/utils.py +95 -0
- imap_processing/decom.py +40 -0
- imap_processing/glows/__init__.py +1 -0
- imap_processing/glows/ancillary/l1b_conversion_table_v001.json +42 -0
- imap_processing/glows/l0/__init__.py +0 -0
- imap_processing/glows/l0/decom_glows.py +91 -0
- imap_processing/glows/l0/glows_l0_data.py +194 -0
- imap_processing/glows/l1a/glows_l1a.py +424 -0
- imap_processing/glows/l1a/glows_l1a_data.py +555 -0
- imap_processing/glows/l1b/glows_l1b.py +270 -0
- imap_processing/glows/l1b/glows_l1b_data.py +583 -0
- imap_processing/glows/packet_definitions/GLX_COMBINED.xml +254 -0
- imap_processing/glows/packet_definitions/P_GLX_TMSCDE.xml +97 -0
- imap_processing/glows/packet_definitions/P_GLX_TMSCHIST.xml +215 -0
- imap_processing/glows/utils/__init__.py +0 -0
- imap_processing/glows/utils/constants.py +105 -0
- imap_processing/hi/__init__.py +1 -0
- imap_processing/hi/l0/__init__.py +0 -0
- imap_processing/hi/l0/decom_hi.py +24 -0
- imap_processing/hi/l1a/__init__.py +0 -0
- imap_processing/hi/l1a/hi_l1a.py +73 -0
- imap_processing/hi/l1a/histogram.py +142 -0
- imap_processing/hi/l1a/housekeeping.py +27 -0
- imap_processing/hi/l1a/science_direct_event.py +341 -0
- imap_processing/hi/l1b/__init__.py +0 -0
- imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +154 -0
- imap_processing/hi/l1b/hi_l1b.py +127 -0
- imap_processing/hi/l1c/__init__.py +0 -0
- imap_processing/hi/l1c/hi_l1c.py +228 -0
- imap_processing/hi/packet_definitions/__init__.py +0 -0
- imap_processing/hi/packet_definitions/hi_packet_definition.xml +482 -0
- imap_processing/hi/utils.py +27 -0
- imap_processing/hit/__init__.py +1 -0
- imap_processing/hit/l0/__init__.py +0 -0
- imap_processing/hit/l0/data_classes/housekeeping.py +240 -0
- imap_processing/hit/l0/data_classes/science_packet.py +259 -0
- imap_processing/hit/l0/decom_hit.py +467 -0
- imap_processing/hit/l0/utils/hit_base.py +57 -0
- imap_processing/hit/l1a/__init__.py +0 -0
- imap_processing/hit/l1a/hit_l1a.py +254 -0
- imap_processing/hit/l1b/hit_l1b.py +179 -0
- imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1276 -0
- imap_processing/ialirt/__init__.py +0 -0
- imap_processing/ialirt/l0/__init__.py +0 -0
- imap_processing/ialirt/l0/process_hit.py +220 -0
- imap_processing/ialirt/packet_definitions/__init__.py +0 -0
- imap_processing/ialirt/packet_definitions/ialirt.xml +778 -0
- imap_processing/ialirt/packet_definitions/ialirt_hit.xml +186 -0
- imap_processing/idex/__init__.py +2 -0
- imap_processing/idex/idex_constants.py +27 -0
- imap_processing/idex/idex_l0.py +31 -0
- imap_processing/idex/idex_l1a.py +631 -0
- imap_processing/idex/packet_definitions/idex_packet_definition.xml +3162 -0
- imap_processing/lo/__init__.py +1 -0
- imap_processing/lo/l0/__init__.py +0 -0
- imap_processing/lo/l0/data_classes/science_direct_events.py +215 -0
- imap_processing/lo/l0/data_classes/star_sensor.py +98 -0
- imap_processing/lo/l0/decompression_tables/12_to_16_bit.csv +4097 -0
- imap_processing/lo/l0/decompression_tables/8_to_12_bit.csv +257 -0
- imap_processing/lo/l0/decompression_tables/8_to_16_bit.csv +257 -0
- imap_processing/lo/l0/decompression_tables/decompression_tables.py +75 -0
- imap_processing/lo/l0/lo_apid.py +15 -0
- imap_processing/lo/l0/lo_science.py +150 -0
- imap_processing/lo/l0/utils/binary_string.py +59 -0
- imap_processing/lo/l0/utils/bit_decompression.py +62 -0
- imap_processing/lo/l0/utils/lo_base.py +57 -0
- imap_processing/lo/l1a/__init__.py +0 -0
- imap_processing/lo/l1a/lo_l1a.py +157 -0
- imap_processing/lo/l1b/lo_l1b.py +160 -0
- imap_processing/lo/l1c/lo_l1c.py +180 -0
- imap_processing/lo/packet_definitions/lo_xtce.xml +3541 -0
- imap_processing/mag/__init__.py +2 -0
- imap_processing/mag/constants.py +108 -0
- imap_processing/mag/l0/decom_mag.py +170 -0
- imap_processing/mag/l0/mag_l0_data.py +118 -0
- imap_processing/mag/l1a/mag_l1a.py +317 -0
- imap_processing/mag/l1a/mag_l1a_data.py +1007 -0
- imap_processing/mag/l1b/__init__.py +0 -0
- imap_processing/mag/l1b/imap_calibration_mag_20240229_v01.cdf +0 -0
- imap_processing/mag/l1b/mag_l1b.py +125 -0
- imap_processing/mag/l1c/mag_l1c.py +57 -0
- imap_processing/mag/packet_definitions/MAG_SCI_COMBINED.xml +235 -0
- imap_processing/quality_flags.py +91 -0
- imap_processing/spice/__init__.py +1 -0
- imap_processing/spice/geometry.py +322 -0
- imap_processing/spice/kernels.py +459 -0
- imap_processing/spice/time.py +72 -0
- imap_processing/swapi/__init__.py +1 -0
- imap_processing/swapi/l1/__init__.py +0 -0
- imap_processing/swapi/l1/swapi_l1.py +685 -0
- imap_processing/swapi/l2/__init__.py +0 -0
- imap_processing/swapi/l2/swapi_l2.py +107 -0
- imap_processing/swapi/packet_definitions/__init__.py +0 -0
- imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +708 -0
- imap_processing/swapi/swapi_utils.py +25 -0
- imap_processing/swe/__init__.py +1 -0
- imap_processing/swe/l1a/__init__.py +0 -0
- imap_processing/swe/l1a/swe_l1a.py +48 -0
- imap_processing/swe/l1a/swe_science.py +223 -0
- imap_processing/swe/l1b/engineering_unit_convert_table.csv +65 -0
- imap_processing/swe/l1b/swe_esa_lookup_table.csv +1441 -0
- imap_processing/swe/l1b/swe_l1b.py +49 -0
- imap_processing/swe/l1b/swe_l1b_science.py +557 -0
- imap_processing/swe/packet_definitions/__init__.py +0 -0
- imap_processing/swe/packet_definitions/swe_packet_definition.xml +303 -0
- imap_processing/swe/utils/__init__.py +0 -0
- imap_processing/swe/utils/swe_utils.py +9 -0
- imap_processing/tests/__init__.py +0 -0
- imap_processing/tests/ccsds/test_data/expected_output.xml +171 -0
- imap_processing/tests/ccsds/test_excel_to_xtce.py +285 -0
- imap_processing/tests/cdf/__init__.py +0 -0
- imap_processing/tests/cdf/imap_default_global_cdf_attrs.yaml +8 -0
- imap_processing/tests/cdf/shared/default_global_cdf_attrs_schema.yaml +246 -0
- imap_processing/tests/cdf/shared/default_variable_cdf_attrs_schema.yaml +466 -0
- imap_processing/tests/cdf/test_cdf_attribute_manager.py +353 -0
- imap_processing/tests/cdf/test_data/imap_default_global_test_cdf_attrs.yaml +7 -0
- imap_processing/tests/cdf/test_data/imap_instrument1_global_cdf_attrs.yaml +14 -0
- imap_processing/tests/cdf/test_data/imap_instrument1_level1_variable_attrs.yaml +23 -0
- imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +23 -0
- imap_processing/tests/cdf/test_data/imap_instrument2_level2_variable_attrs.yaml +30 -0
- imap_processing/tests/cdf/test_data/imap_test_global.yaml +26 -0
- imap_processing/tests/cdf/test_data/imap_test_variable.yaml +41 -0
- imap_processing/tests/cdf/test_imap_cdf_manager.py +62 -0
- imap_processing/tests/cdf/test_utils.py +109 -0
- imap_processing/tests/codice/__init__.py +0 -0
- imap_processing/tests/codice/conftest.py +56 -0
- imap_processing/tests/codice/data/eu_unit_lookup_table.csv +101 -0
- imap_processing/tests/codice/data/idle_export_eu.COD_NHK_20230822_122700 2.csv +100 -0
- imap_processing/tests/codice/data/idle_export_raw.COD_NHK_20230822_122700.csv +100 -0
- imap_processing/tests/codice/data/imap_codice_l0_hi-counters-aggregated_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_hi-counters-singles_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_hi-omni_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_hi-pha_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_hi-sectored_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_hskp_20100101_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-counters-aggregated_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-counters-singles_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-angular_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-priority_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-nsw-species_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-pha_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-sw-angular_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-sw-priority_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l0_lo-sw-species_20240429_v001.pkts +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/test_codice_l0.py +144 -0
- imap_processing/tests/codice/test_codice_l1a.py +187 -0
- imap_processing/tests/codice/test_codice_l1b.py +60 -0
- imap_processing/tests/codice/test_decompress.py +50 -0
- imap_processing/tests/conftest.py +372 -0
- imap_processing/tests/glows/direct_events_validation_data_l1a.csv +5704 -0
- imap_processing/tests/glows/glows_test_packet_20110921_v01.pkts +0 -0
- imap_processing/tests/glows/test_glows_decom.py +133 -0
- imap_processing/tests/glows/test_glows_l1a_cdf.py +85 -0
- imap_processing/tests/glows/test_glows_l1a_data.py +510 -0
- imap_processing/tests/glows/test_glows_l1b.py +348 -0
- imap_processing/tests/glows/test_glows_l1b_data.py +70 -0
- imap_processing/tests/hi/__init__.py +0 -0
- imap_processing/tests/hi/conftest.py +133 -0
- imap_processing/tests/hi/test_data/l0/20231030_H45_APP_NHK.bin +0 -0
- imap_processing/tests/hi/test_data/l0/20231030_H45_APP_NHK.csv +201 -0
- imap_processing/tests/hi/test_data/l0/20231030_H45_SCI_CNT.bin +0 -0
- imap_processing/tests/hi/test_data/l0/20231030_H45_SCI_DE.bin +0 -0
- imap_processing/tests/hi/test_data/l0/README.txt +54 -0
- imap_processing/tests/hi/test_decom.py +55 -0
- imap_processing/tests/hi/test_hi_l1b.py +31 -0
- imap_processing/tests/hi/test_hi_l1c.py +69 -0
- imap_processing/tests/hi/test_l1a.py +96 -0
- imap_processing/tests/hi/test_l1a_sci_de.py +72 -0
- imap_processing/tests/hi/test_utils.py +15 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
- imap_processing/tests/hit/__init__.py +0 -0
- imap_processing/tests/hit/test_data/imap_hit_l0_hk_20100105_v001.pkts +0 -0
- imap_processing/tests/hit/test_data/sci_sample.ccsds +0 -0
- imap_processing/tests/hit/test_hit_decom.py +230 -0
- imap_processing/tests/hit/test_hit_l1a.py +224 -0
- imap_processing/tests/hit/test_hit_l1b.py +52 -0
- imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +88 -0
- imap_processing/tests/ialirt/__init__.py +0 -0
- imap_processing/tests/ialirt/test_data/l0/IALiRT Raw Packet Telemetry.txt +33 -0
- imap_processing/tests/ialirt/test_data/l0/hit_ialirt_sample.ccsds +0 -0
- imap_processing/tests/ialirt/test_data/l0/hit_ialirt_sample.csv +1001 -0
- imap_processing/tests/ialirt/unit/__init__.py +0 -0
- imap_processing/tests/ialirt/unit/test_decom_ialirt.py +94 -0
- imap_processing/tests/ialirt/unit/test_process_hit.py +226 -0
- imap_processing/tests/idex/__init__.py +0 -0
- imap_processing/tests/idex/conftest.py +22 -0
- imap_processing/tests/idex/imap_idex_l0_raw_20230725_v001.pkts +0 -0
- imap_processing/tests/idex/impact_14_tof_high_data.txt +8189 -0
- imap_processing/tests/idex/test_idex_l0.py +45 -0
- imap_processing/tests/idex/test_idex_l1a.py +91 -0
- imap_processing/tests/lo/__init__.py +0 -0
- imap_processing/tests/lo/test_binary_string.py +21 -0
- imap_processing/tests/lo/test_bit_decompression.py +39 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l0_raw_20240627_v001.pkts +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1b_de_20100101_v001.cdf +0 -0
- imap_processing/tests/lo/test_lo_l1a.py +66 -0
- imap_processing/tests/lo/test_lo_l1b.py +74 -0
- imap_processing/tests/lo/test_lo_l1c.py +66 -0
- imap_processing/tests/lo/test_science_counts.py +41 -0
- imap_processing/tests/lo/test_science_direct_events.py +209 -0
- imap_processing/tests/lo/test_star_sensor.py +35 -0
- imap_processing/tests/mag/imap_mag_l1a_burst-magi_20231025_v001.cdf +0 -0
- imap_processing/tests/mag/mag_l0_test_data.pkts +0 -0
- imap_processing/tests/mag/mag_l0_test_output.csv +37 -0
- imap_processing/tests/mag/mag_l1_test_data.pkts +0 -0
- imap_processing/tests/mag/mag_l1a_test_output.csv +97 -0
- imap_processing/tests/mag/test_mag_decom.py +117 -0
- imap_processing/tests/mag/test_mag_l1a.py +856 -0
- imap_processing/tests/mag/test_mag_l1b.py +77 -0
- imap_processing/tests/mag/test_mag_l1c.py +40 -0
- imap_processing/tests/spice/__init__.py +0 -0
- imap_processing/tests/spice/test_data/imap_ena_sim_metakernel.template +4 -0
- imap_processing/tests/spice/test_data/imap_science_0001.tf +171 -0
- imap_processing/tests/spice/test_data/imap_sclk_0000.tsc +156 -0
- imap_processing/tests/spice/test_data/imap_sim_ck_2hr_2secsampling_with_nutation.bc +0 -0
- imap_processing/tests/spice/test_data/imap_simple_metakernel.template +3 -0
- imap_processing/tests/spice/test_data/imap_spk_demo.bsp +0 -0
- imap_processing/tests/spice/test_data/imap_wkcp.tf +1806 -0
- imap_processing/tests/spice/test_data/naif0012.tls +150 -0
- imap_processing/tests/spice/test_data/sim_1yr_imap_attitude.bc +0 -0
- imap_processing/tests/spice/test_data/sim_1yr_imap_pointing_frame.bc +0 -0
- imap_processing/tests/spice/test_geometry.py +214 -0
- imap_processing/tests/spice/test_kernels.py +272 -0
- imap_processing/tests/spice/test_time.py +35 -0
- imap_processing/tests/swapi/__init__.py +0 -0
- imap_processing/tests/swapi/conftest.py +16 -0
- imap_processing/tests/swapi/l0_data/__init__.py +0 -0
- imap_processing/tests/swapi/l0_data/imap_swapi_l0_raw_20231012_v001.pkts +0 -0
- imap_processing/tests/swapi/l0_validation_data/__init__.py +0 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_AUT_20231012_125245.csv +124 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_HK_20231012_125245.csv +98 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_MG_20231012_125245.csv +9 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_eu.SWP_SCI_20231012_125245.csv +72 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_AUT_20231012_125245.csv +124 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_HK_20231012_125245.csv +98 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_MG_20231012_125245.csv +9 -0
- imap_processing/tests/swapi/l0_validation_data/idle_export_raw.SWP_SCI_20231012_125245.csv +72 -0
- imap_processing/tests/swapi/test_swapi_decom.py +135 -0
- imap_processing/tests/swapi/test_swapi_l1.py +354 -0
- imap_processing/tests/swapi/test_swapi_l2.py +21 -0
- imap_processing/tests/swe/__init__.py +0 -0
- imap_processing/tests/swe/conftest.py +35 -0
- imap_processing/tests/swe/decompressed/20230927173238_4th_quarter_decompressed.csv +181 -0
- imap_processing/tests/swe/decompressed/20230927173253_1st_quarter_decompressed.csv +181 -0
- imap_processing/tests/swe/decompressed/20230927173308_2nd_quarter_decompressed.csv +181 -0
- imap_processing/tests/swe/decompressed/20230927173323_3rd_quarter_decompressed.csv +181 -0
- imap_processing/tests/swe/l0_data/2024051010_SWE_SCIENCE_packet.bin +0 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_SCIENCE_20240510_092742.csv +544 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_raw.SWE_SCIENCE_20240510_092742.csv +363 -0
- imap_processing/tests/swe/test_swe_l1a.py +12 -0
- imap_processing/tests/swe/test_swe_l1a_science.py +129 -0
- imap_processing/tests/swe/test_swe_l1b.py +61 -0
- imap_processing/tests/swe/test_swe_l1b_science.py +65 -0
- imap_processing/tests/test_cli.py +229 -0
- imap_processing/tests/test_decom.py +66 -0
- imap_processing/tests/test_quality_flags.py +71 -0
- imap_processing/tests/test_utils.py +107 -0
- imap_processing/tests/ultra/__init__.py +0 -0
- imap_processing/tests/ultra/test_data/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
- imap_processing/tests/ultra/test_data/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
- imap_processing/tests/ultra/test_data/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
- imap_processing/tests/ultra/test_data/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +24 -0
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +105 -0
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +24 -0
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -0
- imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +702 -0
- imap_processing/tests/ultra/unit/__init__.py +0 -0
- imap_processing/tests/ultra/unit/conftest.py +210 -0
- imap_processing/tests/ultra/unit/test_decom_apid_880.py +98 -0
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +50 -0
- imap_processing/tests/ultra/unit/test_decom_apid_883.py +44 -0
- imap_processing/tests/ultra/unit/test_decom_apid_896.py +104 -0
- imap_processing/tests/ultra/unit/test_lookup_utils.py +68 -0
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +338 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +122 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +57 -0
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +342 -0
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +104 -0
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +35 -0
- imap_processing/ultra/__init__.py +1 -0
- imap_processing/ultra/constants.py +60 -0
- imap_processing/ultra/l0/__init__.py +0 -0
- imap_processing/ultra/l0/decom_tools.py +281 -0
- imap_processing/ultra/l0/decom_ultra.py +278 -0
- imap_processing/ultra/l0/ultra_utils.py +326 -0
- imap_processing/ultra/l1a/__init__.py +0 -0
- imap_processing/ultra/l1a/ultra_l1a.py +319 -0
- imap_processing/ultra/l1b/badtimes.py +26 -0
- imap_processing/ultra/l1b/cullingmask.py +26 -0
- imap_processing/ultra/l1b/de.py +59 -0
- imap_processing/ultra/l1b/extendedspin.py +45 -0
- imap_processing/ultra/l1b/lookup_utils.py +165 -0
- imap_processing/ultra/l1b/ultra_l1b.py +65 -0
- imap_processing/ultra/l1b/ultra_l1b_annotated.py +54 -0
- imap_processing/ultra/l1b/ultra_l1b_extended.py +764 -0
- imap_processing/ultra/l1c/histogram.py +36 -0
- imap_processing/ultra/l1c/pset.py +36 -0
- imap_processing/ultra/l1c/ultra_l1c.py +52 -0
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +54 -0
- imap_processing/ultra/lookup_tables/EgyNorm.mem.csv +32769 -0
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
- imap_processing/ultra/lookup_tables/ultra45_back-pos-luts.csv +4097 -0
- imap_processing/ultra/lookup_tables/ultra45_tdc_norm.csv +2050 -0
- imap_processing/ultra/lookup_tables/ultra90_back-pos-luts.csv +4097 -0
- imap_processing/ultra/lookup_tables/ultra90_tdc_norm.csv +2050 -0
- imap_processing/ultra/lookup_tables/yadjust.csv +257 -0
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +547 -0
- imap_processing/ultra/packet_definitions/__init__.py +0 -0
- imap_processing/ultra/utils/__init__.py +0 -0
- imap_processing/ultra/utils/ultra_l1_utils.py +50 -0
- imap_processing/utils.py +413 -0
- imap_processing-0.6.0.dist-info/LICENSE +21 -0
- imap_processing-0.6.0.dist-info/METADATA +107 -0
- imap_processing-0.6.0.dist-info/RECORD +398 -0
- imap_processing-0.6.0.dist-info/WHEEL +4 -0
- imap_processing-0.6.0.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
"""Functions for furnishing and tracking SPICE kernels."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Generator
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable, Optional, Union, overload
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import spiceypy as spice
|
|
13
|
+
from numpy.typing import NDArray
|
|
14
|
+
from spiceypy.utils.exceptions import SpiceyError
|
|
15
|
+
|
|
16
|
+
from imap_processing import imap_module_directory
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Declarations to help with typing. Taken from mypy documentation on
|
|
22
|
+
# decorator-factories:
|
|
23
|
+
# https://mypy.readthedocs.io/en/stable/generics.html#decorator-factories
|
|
24
|
+
# Bare decorator usage
|
|
25
|
+
@overload
|
|
26
|
+
def ensure_spice(
|
|
27
|
+
__func: Callable[..., Any],
|
|
28
|
+
) -> Callable[..., Any]: ... # numpydoc ignore=GL08
|
|
29
|
+
# Decorator with arguments
|
|
30
|
+
@overload
|
|
31
|
+
def ensure_spice(
|
|
32
|
+
*, time_kernels_only: bool = False
|
|
33
|
+
) -> Callable[[Callable[..., Any]], Callable[..., Any]]: ... # numpydoc ignore=GL08
|
|
34
|
+
# Implementation
|
|
35
|
+
def ensure_spice(
|
|
36
|
+
__func: Optional[Callable[..., Any]] = None, *, time_kernels_only: bool = False
|
|
37
|
+
) -> Union[Callable[..., Any], Callable[[Callable[..., Any]], Callable[..., Any]]]:
|
|
38
|
+
"""
|
|
39
|
+
Decorator/wrapper that automatically furnishes SPICE kernels.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
__func : Callable
|
|
44
|
+
The function requiring SPICE that we are going to wrap if being used
|
|
45
|
+
explicitly, otherwise None, in which case ensure_spice is being used,
|
|
46
|
+
not as a function wrapper (see l2a_processing.py) but as a true
|
|
47
|
+
decorator without an explicit function argument.
|
|
48
|
+
time_kernels_only : bool
|
|
49
|
+
Specify that we only need to furnish time kernels (if SPICE_METAKERNEL
|
|
50
|
+
is set, we still just furnish that metakernel and assume the time
|
|
51
|
+
kernels are included.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
Callable
|
|
56
|
+
Decorated function, with spice error handling.
|
|
57
|
+
|
|
58
|
+
Notes
|
|
59
|
+
-----
|
|
60
|
+
Before trying to understand this piece of code, read this:
|
|
61
|
+
https://stackoverflow.com/questions/5929107/decorators-with-parameters/60832711#60832711
|
|
62
|
+
|
|
63
|
+
**Control flow overview:**
|
|
64
|
+
1. Try simply calling the wrapped function naively.
|
|
65
|
+
* SUCCESS? Great! We're done.
|
|
66
|
+
* SpiceyError? Go to step 2.
|
|
67
|
+
|
|
68
|
+
2. Furnish metakernel at SPICE_METAKERNEL
|
|
69
|
+
* SUCCESS? Great, return the original function again (so it can be
|
|
70
|
+
re-run).
|
|
71
|
+
* KeyError? Seems like SPICE_METAKERNEL isn't set, no problem. Go to
|
|
72
|
+
step 3.
|
|
73
|
+
|
|
74
|
+
3. Did we get the parameter time_kernels_only=True?
|
|
75
|
+
--> YES? We only need LSK and SCLK kernels to run this function. Go fetch
|
|
76
|
+
those and furnish and return the original function (so it can be re-run).
|
|
77
|
+
--> NO? Dang. This is sort of the end of the line. Re-raise the error
|
|
78
|
+
generated from the failed spiceypy function call but add a better
|
|
79
|
+
message to it.
|
|
80
|
+
|
|
81
|
+
Examples
|
|
82
|
+
--------
|
|
83
|
+
There are three ways to use this object
|
|
84
|
+
|
|
85
|
+
1. A decorator with no arguments
|
|
86
|
+
>>> @ensure_spice
|
|
87
|
+
... def my_spicey_func(a, b):
|
|
88
|
+
... pass
|
|
89
|
+
|
|
90
|
+
2. A decorator with parameters. This is useful
|
|
91
|
+
if we only need the latest SCLK and LSK kernels for the function involved.
|
|
92
|
+
>>> @ensure_spice(time_kernels_only=True)
|
|
93
|
+
... def my_spicey_time_func(a, b):
|
|
94
|
+
... pass
|
|
95
|
+
|
|
96
|
+
3. An explicit wrapper function, providing a dynamically set value for
|
|
97
|
+
parameters, e.g. time_kernels_only
|
|
98
|
+
>>> wrapped = ensure_spice(spicey_func, time_kernels_only=True)
|
|
99
|
+
... result = wrapped(*args, **kwargs)
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def _decorator(func: Callable[..., Callable]) -> Callable:
|
|
103
|
+
"""
|
|
104
|
+
Decorate or wrap input function depending on how ensure_spice is used.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
func : Callable
|
|
109
|
+
The function to be decorated/wrapped.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
Callable
|
|
114
|
+
If used as a function wrapper, the decorated function is returned.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
@functools.wraps(func)
|
|
118
|
+
def wrapper_ensure_spice(*args: Any, **kwargs: Any) -> Any:
|
|
119
|
+
"""
|
|
120
|
+
Wrap the function that ensure_spice is used on.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
*args : list
|
|
125
|
+
The positional arguments passed to the decorated function.
|
|
126
|
+
**kwargs
|
|
127
|
+
The keyword arguments passed to the decorated function.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
Object
|
|
132
|
+
Output from wrapped function.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
# Step 1.
|
|
136
|
+
return func(
|
|
137
|
+
*args, **kwargs
|
|
138
|
+
) # Naive first try. Maybe SPICE is already furnished.
|
|
139
|
+
except SpiceyError as spicey_err:
|
|
140
|
+
try:
|
|
141
|
+
# Step 2.
|
|
142
|
+
if os.getenv("SPICE_METAKERNEL"):
|
|
143
|
+
metakernel_path = os.getenv("SPICE_METAKERNEL")
|
|
144
|
+
spice.furnsh(metakernel_path)
|
|
145
|
+
else:
|
|
146
|
+
furnish_time_kernel()
|
|
147
|
+
except KeyError:
|
|
148
|
+
# TODO: An additional step that was used on EMUS was to get
|
|
149
|
+
# a custom metakernel from the SDC API based on an input
|
|
150
|
+
# time range.
|
|
151
|
+
if time_kernels_only:
|
|
152
|
+
# Step 3.
|
|
153
|
+
# TODO: Decide if this is useful for IMAP. Possible
|
|
154
|
+
# implementation could include downloading
|
|
155
|
+
# the most recent leapsecond kernel from NAIF (see:
|
|
156
|
+
# https://lasp.colorado.edu/nucleus/projects/LIBSDC/repos/libera_utils/browse/libera_utils/spice_utils.py
|
|
157
|
+
# for LIBERA implementation of downloading and caching
|
|
158
|
+
# kernels) and finding the most recent IMAP clock
|
|
159
|
+
# kernel in EFS.
|
|
160
|
+
raise NotImplementedError from spicey_err
|
|
161
|
+
else:
|
|
162
|
+
raise SpiceyError(
|
|
163
|
+
"When calling a function requiring SPICE, we failed "
|
|
164
|
+
"to load a metakernel. SPICE_METAKERNEL is not set,"
|
|
165
|
+
"and time_kernels_only is not set to True"
|
|
166
|
+
) from spicey_err
|
|
167
|
+
return func(*args, **kwargs)
|
|
168
|
+
|
|
169
|
+
return wrapper_ensure_spice
|
|
170
|
+
|
|
171
|
+
# Note: This return was originally implemented as a ternary operator, but
|
|
172
|
+
# this caused mypy to fail due to this bug:
|
|
173
|
+
# https://github.com/python/mypy/issues/4134
|
|
174
|
+
if callable(__func):
|
|
175
|
+
return _decorator(__func)
|
|
176
|
+
else:
|
|
177
|
+
return _decorator
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@contextmanager
|
|
181
|
+
def open_spice_ck_file(pointing_frame_path: Path) -> Generator[int, None, None]:
|
|
182
|
+
"""
|
|
183
|
+
Context manager for handling SPICE CK files.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
pointing_frame_path : str
|
|
188
|
+
Path to the CK file.
|
|
189
|
+
|
|
190
|
+
Yields
|
|
191
|
+
------
|
|
192
|
+
handle : int
|
|
193
|
+
Handle to the opened CK file.
|
|
194
|
+
"""
|
|
195
|
+
# TODO: We will need to figure out if ck kernel changes
|
|
196
|
+
# and how that will affect appending to the pointing
|
|
197
|
+
# frame kernel.
|
|
198
|
+
if pointing_frame_path.exists():
|
|
199
|
+
handle = spice.dafopw(str(pointing_frame_path))
|
|
200
|
+
else:
|
|
201
|
+
handle = spice.ckopn(str(pointing_frame_path), "CK", 0)
|
|
202
|
+
try:
|
|
203
|
+
yield handle
|
|
204
|
+
finally:
|
|
205
|
+
spice.ckcls(handle)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@ensure_spice
|
|
209
|
+
def create_pointing_frame(pointing_frame_path: Path, ck_path: Path) -> None:
|
|
210
|
+
"""
|
|
211
|
+
Create the pointing frame.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
pointing_frame_path : Path
|
|
216
|
+
Location of pointing frame kernel.
|
|
217
|
+
ck_path : Path
|
|
218
|
+
Location of the CK kernel.
|
|
219
|
+
|
|
220
|
+
Notes
|
|
221
|
+
-----
|
|
222
|
+
Kernels required to be furnished:
|
|
223
|
+
"imap_science_0001.tf",
|
|
224
|
+
"imap_sclk_0000.tsc",
|
|
225
|
+
"imap_sim_ck_2hr_2secsampling_with_nutation.bc" or
|
|
226
|
+
"sim_1yr_imap_attitude.bc",
|
|
227
|
+
"imap_wkcp.tf",
|
|
228
|
+
"naif0012.tls"
|
|
229
|
+
|
|
230
|
+
Assumptions:
|
|
231
|
+
- The MOC has removed timeframe in which nutation/procession are present.
|
|
232
|
+
TODO: We may come back and have a check for this.
|
|
233
|
+
- We will continue to append to the pointing frame kernel.
|
|
234
|
+
TODO: Figure out how we want to handle the file size becoming too large.
|
|
235
|
+
- For now we can only furnish a single ck kernel.
|
|
236
|
+
TODO: This will not be the case once we add the ability to query the .csv.
|
|
237
|
+
|
|
238
|
+
References
|
|
239
|
+
----------
|
|
240
|
+
https://numpydoc.readthedocs.io/en/latest/format.html#references
|
|
241
|
+
"""
|
|
242
|
+
# Get IDs.
|
|
243
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.gipool
|
|
244
|
+
id_imap_dps = spice.gipool("FRAME_IMAP_DPS", 0, 1)
|
|
245
|
+
id_imap_sclk = spice.gipool("CK_-43000_SCLK", 0, 1)
|
|
246
|
+
|
|
247
|
+
# Verify that only ck_path kernel is loaded.
|
|
248
|
+
count = spice.ktotal("ck")
|
|
249
|
+
loaded_ck_kernel, _, _, _ = spice.kdata(count - 1, "ck")
|
|
250
|
+
|
|
251
|
+
if count != 1 or str(ck_path) != loaded_ck_kernel:
|
|
252
|
+
raise ValueError(f"Error: Expected CK kernel {ck_path}")
|
|
253
|
+
|
|
254
|
+
# If the pointing frame kernel already exists, find the last time.
|
|
255
|
+
if pointing_frame_path.exists():
|
|
256
|
+
# Get the last time in the pointing frame kernel.
|
|
257
|
+
pointing_cover = spice.ckcov(
|
|
258
|
+
str(pointing_frame_path), int(id_imap_dps), True, "SEGMENT", 0, "TDB"
|
|
259
|
+
)
|
|
260
|
+
num_segments = spice.wncard(pointing_cover)
|
|
261
|
+
_, et_end_pointing_frame = spice.wnfetd(pointing_cover, num_segments - 1)
|
|
262
|
+
else:
|
|
263
|
+
et_end_pointing_frame = None
|
|
264
|
+
|
|
265
|
+
# TODO: Query for .csv file to get the pointing start and end times.
|
|
266
|
+
# TODO: Remove next four lines once query is added.
|
|
267
|
+
id_imap_spacecraft = spice.gipool("FRAME_IMAP_SPACECRAFT", 0, 1)
|
|
268
|
+
ck_cover = spice.ckcov(
|
|
269
|
+
str(ck_path), int(id_imap_spacecraft), True, "INTERVAL", 0, "TDB"
|
|
270
|
+
)
|
|
271
|
+
num_intervals = spice.wncard(ck_cover)
|
|
272
|
+
|
|
273
|
+
with open_spice_ck_file(pointing_frame_path) as handle:
|
|
274
|
+
# TODO: this will change to the number of pointings.
|
|
275
|
+
for i in range(num_intervals):
|
|
276
|
+
# Get the coverage window
|
|
277
|
+
# TODO: this will change to pointing start and end time.
|
|
278
|
+
et_start, et_end = spice.wnfetd(ck_cover, i)
|
|
279
|
+
et_times = _get_et_times(et_start, et_end)
|
|
280
|
+
|
|
281
|
+
# TODO: remove after query is added.
|
|
282
|
+
if (
|
|
283
|
+
et_end_pointing_frame is not None
|
|
284
|
+
and et_times[0] < et_end_pointing_frame
|
|
285
|
+
):
|
|
286
|
+
break
|
|
287
|
+
|
|
288
|
+
# Create a rotation matrix
|
|
289
|
+
rotation_matrix = _create_rotation_matrix(et_times)
|
|
290
|
+
|
|
291
|
+
# Convert the rotation matrix to a quaternion.
|
|
292
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
|
|
293
|
+
q_avg = spice.m2q(rotation_matrix)
|
|
294
|
+
|
|
295
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.sce2c
|
|
296
|
+
# Convert start and end times to SCLK.
|
|
297
|
+
sclk_begtim = spice.sce2c(int(id_imap_sclk), et_times[0])
|
|
298
|
+
sclk_endtim = spice.sce2c(int(id_imap_sclk), et_times[-1])
|
|
299
|
+
|
|
300
|
+
# Create the pointing frame kernel.
|
|
301
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.ckw02
|
|
302
|
+
spice.ckw02(
|
|
303
|
+
# Handle of an open CK file.
|
|
304
|
+
handle,
|
|
305
|
+
# Start time of the segment.
|
|
306
|
+
sclk_begtim,
|
|
307
|
+
# End time of the segment.
|
|
308
|
+
sclk_endtim,
|
|
309
|
+
# Pointing frame ID.
|
|
310
|
+
int(id_imap_dps),
|
|
311
|
+
# Reference frame.
|
|
312
|
+
"ECLIPJ2000", # Reference frame
|
|
313
|
+
# Identifier.
|
|
314
|
+
"IMAP_DPS",
|
|
315
|
+
# Number of pointing intervals.
|
|
316
|
+
1,
|
|
317
|
+
# Start times of individual pointing records within segment.
|
|
318
|
+
# Since there is only a single record this is equal to sclk_begtim.
|
|
319
|
+
np.array([sclk_begtim]),
|
|
320
|
+
# End times of individual pointing records within segment.
|
|
321
|
+
# Since there is only a single record this is equal to sclk_endtim.
|
|
322
|
+
np.array([sclk_endtim]), # Single stop time
|
|
323
|
+
# Average quaternion.
|
|
324
|
+
q_avg,
|
|
325
|
+
# 0.0 Angular rotation terms.
|
|
326
|
+
np.array([0.0, 0.0, 0.0]),
|
|
327
|
+
# Rates (seconds per tick) at which the quaternion and
|
|
328
|
+
# angular velocity change.
|
|
329
|
+
np.array([1.0]),
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _get_et_times(et_start: float, et_end: float) -> NDArray[np.float64]:
|
|
334
|
+
"""
|
|
335
|
+
Get times for pointing start and stop.
|
|
336
|
+
|
|
337
|
+
Parameters
|
|
338
|
+
----------
|
|
339
|
+
et_start : float
|
|
340
|
+
Pointing start time.
|
|
341
|
+
et_end : float
|
|
342
|
+
Pointing end time.
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
et_times : numpy.ndarray
|
|
347
|
+
Array of times between et_start and et_end.
|
|
348
|
+
"""
|
|
349
|
+
# TODO: Queried pointing start and stop times here.
|
|
350
|
+
# TODO removing the @ensure_spice decorator when using the repointing table.
|
|
351
|
+
|
|
352
|
+
# 1 spin/15 seconds; 10 quaternions / spin.
|
|
353
|
+
num_samples = (et_end - et_start) / 15 * 10
|
|
354
|
+
# There were rounding errors when using spice.pxform so np.ceil and np.floor
|
|
355
|
+
# were used to ensure the start and end times were included in the array.
|
|
356
|
+
et_times = np.linspace(
|
|
357
|
+
np.ceil(et_start * 1e6) / 1e6, np.floor(et_end * 1e6) / 1e6, int(num_samples)
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
return et_times
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@ensure_spice
|
|
364
|
+
def _average_quaternions(et_times: np.ndarray) -> NDArray:
|
|
365
|
+
"""
|
|
366
|
+
Average the quaternions.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
et_times : numpy.ndarray
|
|
371
|
+
Array of times between et_start and et_end.
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
q_avg : np.ndarray
|
|
376
|
+
Average quaternion.
|
|
377
|
+
"""
|
|
378
|
+
aggregate = np.zeros((4, 4))
|
|
379
|
+
for tdb in et_times:
|
|
380
|
+
# we use a quick and dirty method here for grabbing the quaternions
|
|
381
|
+
# from the attitude kernel. Depending on how well the kernel input
|
|
382
|
+
# data is built and sampled, there may or may not be aliasing with this
|
|
383
|
+
# approach. If it turns out that we need to pull the quaternions
|
|
384
|
+
# directly from the CK there are several routines that exist to do this
|
|
385
|
+
# but it's not straight forward. We'll revisit this if needed.
|
|
386
|
+
|
|
387
|
+
# Rotation matrix from IMAP spacecraft frame to ECLIPJ2000.
|
|
388
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.pxform
|
|
389
|
+
body_rots = spice.pxform("IMAP_SPACECRAFT", "ECLIPJ2000", tdb)
|
|
390
|
+
# Convert rotation matrix to quaternion.
|
|
391
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.m2q
|
|
392
|
+
body_quat = spice.m2q(body_rots)
|
|
393
|
+
|
|
394
|
+
# Standardize the quaternion so that they may be compared.
|
|
395
|
+
body_quat = body_quat * np.sign(body_quat[0])
|
|
396
|
+
# Aggregate quaternions into a single matrix.
|
|
397
|
+
aggregate += np.outer(body_quat, body_quat)
|
|
398
|
+
|
|
399
|
+
# Reference: "On Averaging Rotations".
|
|
400
|
+
# Link: https://link.springer.com/content/pdf/10.1023/A:1011129215388.pdf
|
|
401
|
+
aggregate /= len(et_times)
|
|
402
|
+
|
|
403
|
+
# Compute eigen values and vectors of the matrix A
|
|
404
|
+
# Eigenvalues tell you how much "influence" each
|
|
405
|
+
# direction (eigenvector) has.
|
|
406
|
+
# The largest eigenvalue corresponds to the direction
|
|
407
|
+
# that has the most influence.
|
|
408
|
+
# The eigenvector corresponding to the largest
|
|
409
|
+
# eigenvalue points in the direction that has the most
|
|
410
|
+
# combined rotation influence.
|
|
411
|
+
eigvals, eigvecs = np.linalg.eig(aggregate)
|
|
412
|
+
# q0: The scalar part of the quaternion.
|
|
413
|
+
# q1, q2, q3: The vector part of the quaternion.
|
|
414
|
+
q_avg = eigvecs[:, np.argmax(eigvals)]
|
|
415
|
+
|
|
416
|
+
return q_avg
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _create_rotation_matrix(et_times: np.ndarray) -> NDArray:
|
|
420
|
+
"""
|
|
421
|
+
Create a rotation matrix.
|
|
422
|
+
|
|
423
|
+
Parameters
|
|
424
|
+
----------
|
|
425
|
+
et_times : numpy.ndarray
|
|
426
|
+
Array of times between et_start and et_end.
|
|
427
|
+
|
|
428
|
+
Returns
|
|
429
|
+
-------
|
|
430
|
+
rotation_matrix : np.ndarray
|
|
431
|
+
Rotation matrix.
|
|
432
|
+
"""
|
|
433
|
+
# Averaged quaternions.
|
|
434
|
+
q_avg = _average_quaternions(et_times)
|
|
435
|
+
|
|
436
|
+
# Converts the averaged quaternion (q_avg) into a rotation matrix
|
|
437
|
+
# and get inertial z axis.
|
|
438
|
+
# https://spiceypy.readthedocs.io/en/main/documentation.html#spiceypy.spiceypy.q2m
|
|
439
|
+
z_avg = spice.q2m(list(q_avg))[:, 2]
|
|
440
|
+
# y_avg is perpendicular to both z_avg and the standard Z-axis.
|
|
441
|
+
y_avg = np.cross(z_avg, [0, 0, 1])
|
|
442
|
+
# x_avg is perpendicular to y_avg and z_avg.
|
|
443
|
+
x_avg = np.cross(y_avg, z_avg)
|
|
444
|
+
|
|
445
|
+
# Construct the rotation matrix from x_avg, y_avg, z_avg
|
|
446
|
+
rotation_matrix = np.asarray([x_avg, y_avg, z_avg])
|
|
447
|
+
|
|
448
|
+
return rotation_matrix
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def furnish_time_kernel() -> None:
|
|
452
|
+
"""Furnish the time kernels."""
|
|
453
|
+
spice_test_data_path = imap_module_directory / "tests/spice/test_data"
|
|
454
|
+
|
|
455
|
+
# TODO: we need to load these kernels from EFS volumen that is
|
|
456
|
+
# mounted to batch volume and extend this to generate metakernell
|
|
457
|
+
# which is TBD.
|
|
458
|
+
spice.furnsh(str(spice_test_data_path / "imap_sclk_0000.tsc"))
|
|
459
|
+
spice.furnsh(str(spice_test_data_path / "naif0012.tls"))
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Time conversion functions that rely on SPICE."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
from collections.abc import Collection
|
|
5
|
+
from typing import Union
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
9
|
+
import spiceypy as spice
|
|
10
|
+
|
|
11
|
+
from imap_processing.spice import IMAP_SC_ID
|
|
12
|
+
from imap_processing.spice.kernels import ensure_spice
|
|
13
|
+
|
|
14
|
+
TICK_DURATION = 2e-5 # 20 microseconds as defined in imap_sclk_0000.tsc
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def met_to_j2000ns(
|
|
18
|
+
met: npt.ArrayLike,
|
|
19
|
+
) -> npt.NDArray[np.int64]:
|
|
20
|
+
"""
|
|
21
|
+
Convert mission elapsed time (MET) to nanoseconds since J2000.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
met : array_like
|
|
26
|
+
Number of seconds since epoch according to the spacecraft clock.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
array_like or scalar, int64
|
|
31
|
+
The mission elapsed time converted to nanoseconds since the J2000 epoch.
|
|
32
|
+
|
|
33
|
+
Notes
|
|
34
|
+
-----
|
|
35
|
+
There are two options when using SPICE to convert from SCLK time (MET) to
|
|
36
|
+
J2000. The conversion can be done on SCLK strings as input or using double
|
|
37
|
+
precision continuous spacecraft clock "ticks". The latter is more accurate
|
|
38
|
+
as it will correctly convert fractional clock ticks to nanoseconds. Since
|
|
39
|
+
some IMAP instruments contain clocks with higher precision than 1 SCLK
|
|
40
|
+
"tick" which is defined to be 20 microseconds, according to the sclk kernel,
|
|
41
|
+
it is preferable to use the higher accuracy method.
|
|
42
|
+
"""
|
|
43
|
+
sclk_ticks = np.asarray(met, dtype=float) / TICK_DURATION
|
|
44
|
+
return np.asarray(_sct2e_wrapper(sclk_ticks) * 1e9, dtype=np.int64)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@typing.no_type_check
|
|
48
|
+
@ensure_spice
|
|
49
|
+
def _sct2e_wrapper(
|
|
50
|
+
sclk_ticks: Union[float, Collection[float]],
|
|
51
|
+
) -> Union[float, np.ndarray]:
|
|
52
|
+
"""
|
|
53
|
+
Convert encoded spacecraft clock "ticks" to ephemeris time.
|
|
54
|
+
|
|
55
|
+
Decorated wrapper for spiceypy.sct2e that vectorizes the function in addition
|
|
56
|
+
to wrapping with the @ensure_spice automatic kernel furnishing functionality.
|
|
57
|
+
https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/sct2e_c.html
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
sclk_ticks : Union[float, Collection[float]]
|
|
62
|
+
Input sclk ticks value(s) to be converted to ephemeris time.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
ephemeris_time: np.ndarray
|
|
67
|
+
Ephemeris time, seconds past J2000.
|
|
68
|
+
"""
|
|
69
|
+
if isinstance(sclk_ticks, Collection):
|
|
70
|
+
return np.array([spice.sct2e(IMAP_SC_ID, s) for s in sclk_ticks])
|
|
71
|
+
else:
|
|
72
|
+
return spice.sct2e(IMAP_SC_ID, sclk_ticks)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "001"
|
|
File without changes
|