imap-processing 0.11.0__py3-none-any.whl → 0.13.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/__init__.py +11 -11
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/ccsds_data.py +1 -2
- imap_processing/ccsds/excel_to_xtce.py +66 -18
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +24 -40
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +934 -42
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
- imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +27 -14
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
- imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +25 -9
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
- imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +23 -20
- imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
- imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
- imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
- imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
- imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
- imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +64 -52
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +180 -19
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +80 -17
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +32 -57
- imap_processing/cdf/utils.py +52 -38
- imap_processing/cli.py +477 -233
- imap_processing/codice/codice_l1a.py +466 -131
- imap_processing/codice/codice_l1b.py +51 -152
- imap_processing/codice/constants.py +1360 -569
- imap_processing/codice/decompress.py +2 -6
- imap_processing/ena_maps/ena_maps.py +1103 -146
- imap_processing/ena_maps/utils/coordinates.py +19 -0
- imap_processing/ena_maps/utils/map_utils.py +14 -17
- imap_processing/ena_maps/utils/spatial_utils.py +55 -52
- imap_processing/glows/l1a/glows_l1a.py +28 -99
- imap_processing/glows/l1a/glows_l1a_data.py +2 -2
- imap_processing/glows/l1b/glows_l1b.py +1 -4
- imap_processing/glows/l1b/glows_l1b_data.py +1 -3
- imap_processing/glows/l2/glows_l2.py +2 -5
- imap_processing/hi/l1a/hi_l1a.py +54 -29
- imap_processing/hi/l1a/histogram.py +0 -1
- imap_processing/hi/l1a/science_direct_event.py +6 -8
- imap_processing/hi/l1b/hi_l1b.py +111 -82
- imap_processing/hi/l1c/hi_l1c.py +416 -32
- imap_processing/hi/utils.py +58 -12
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
- imap_processing/hit/hit_utils.py +235 -5
- imap_processing/hit/l0/constants.py +20 -11
- imap_processing/hit/l0/decom_hit.py +21 -5
- imap_processing/hit/l1a/hit_l1a.py +71 -75
- imap_processing/hit/l1b/constants.py +321 -0
- imap_processing/hit/l1b/hit_l1b.py +377 -67
- imap_processing/hit/l2/constants.py +318 -0
- imap_processing/hit/l2/hit_l2.py +723 -0
- imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
- imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
- imap_processing/ialirt/l0/parse_mag.py +374 -0
- imap_processing/ialirt/l0/process_swapi.py +69 -0
- imap_processing/ialirt/l0/process_swe.py +548 -0
- imap_processing/ialirt/packet_definitions/ialirt.xml +216 -208
- imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
- imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
- imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
- imap_processing/ialirt/utils/grouping.py +114 -0
- imap_processing/ialirt/utils/time.py +29 -0
- imap_processing/idex/atomic_masses.csv +22 -0
- imap_processing/idex/decode.py +2 -2
- imap_processing/idex/idex_constants.py +33 -0
- imap_processing/idex/idex_l0.py +22 -8
- imap_processing/idex/idex_l1a.py +81 -51
- imap_processing/idex/idex_l1b.py +13 -39
- imap_processing/idex/idex_l2a.py +823 -0
- imap_processing/idex/idex_l2b.py +120 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +11 -11
- imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
- imap_processing/lo/l0/lo_science.py +7 -2
- imap_processing/lo/l1a/lo_l1a.py +1 -5
- imap_processing/lo/l1b/lo_l1b.py +702 -29
- imap_processing/lo/l1b/tof_conversions.py +11 -0
- imap_processing/lo/l1c/lo_l1c.py +1 -4
- imap_processing/mag/constants.py +51 -0
- imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
- imap_processing/mag/l0/decom_mag.py +10 -3
- imap_processing/mag/l1a/mag_l1a.py +23 -19
- imap_processing/mag/l1a/mag_l1a_data.py +35 -10
- imap_processing/mag/l1b/mag_l1b.py +259 -50
- imap_processing/mag/l1c/interpolation_methods.py +388 -0
- imap_processing/mag/l1c/mag_l1c.py +621 -17
- imap_processing/mag/l2/mag_l2.py +140 -0
- imap_processing/mag/l2/mag_l2_data.py +288 -0
- imap_processing/quality_flags.py +1 -0
- imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
- imap_processing/spacecraft/quaternions.py +121 -0
- imap_processing/spice/geometry.py +19 -22
- imap_processing/spice/kernels.py +0 -276
- imap_processing/spice/pointing_frame.py +257 -0
- imap_processing/spice/repoint.py +149 -0
- imap_processing/spice/spin.py +38 -33
- imap_processing/spice/time.py +24 -0
- imap_processing/swapi/l1/swapi_l1.py +20 -12
- imap_processing/swapi/l2/swapi_l2.py +116 -5
- imap_processing/swapi/swapi_utils.py +32 -0
- imap_processing/swe/l1a/swe_l1a.py +44 -12
- imap_processing/swe/l1a/swe_science.py +13 -13
- imap_processing/swe/l1b/swe_l1b.py +898 -23
- imap_processing/swe/l2/swe_l2.py +75 -136
- imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
- imap_processing/swe/utils/swe_constants.py +64 -0
- imap_processing/swe/utils/swe_utils.py +85 -28
- imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
- imap_processing/tests/ccsds/test_excel_to_xtce.py +24 -21
- imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
- imap_processing/tests/cdf/test_utils.py +14 -16
- imap_processing/tests/codice/conftest.py +44 -33
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_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-ialirt_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-pha_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_l1a.py +126 -53
- imap_processing/tests/codice/test_codice_l1b.py +6 -7
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +239 -27
- imap_processing/tests/ena_maps/conftest.py +51 -0
- imap_processing/tests/ena_maps/test_ena_maps.py +1068 -110
- imap_processing/tests/ena_maps/test_map_utils.py +66 -43
- imap_processing/tests/ena_maps/test_spatial_utils.py +17 -21
- imap_processing/tests/glows/conftest.py +10 -14
- imap_processing/tests/glows/test_glows_decom.py +4 -4
- imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
- imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
- imap_processing/tests/glows/test_glows_l1b.py +11 -11
- imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
- imap_processing/tests/glows/test_glows_l2.py +2 -8
- imap_processing/tests/hi/conftest.py +1 -1
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
- imap_processing/tests/hi/test_hi_l1b.py +22 -27
- imap_processing/tests/hi/test_hi_l1c.py +249 -18
- imap_processing/tests/hi/test_l1a.py +35 -7
- imap_processing/tests/hi/test_science_direct_event.py +3 -3
- imap_processing/tests/hi/test_utils.py +24 -2
- imap_processing/tests/hit/helpers/l1_validation.py +74 -73
- imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
- imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
- imap_processing/tests/hit/test_decom_hit.py +5 -1
- imap_processing/tests/hit/test_hit_l1a.py +32 -36
- imap_processing/tests/hit/test_hit_l1b.py +300 -81
- imap_processing/tests/hit/test_hit_l2.py +716 -0
- imap_processing/tests/hit/test_hit_utils.py +184 -7
- imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
- imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
- imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
- imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
- imap_processing/tests/ialirt/data/l0/461971383-404.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971384-405.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971385-406.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971386-407.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971387-408.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971388-409.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971389-410.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971390-411.bin +0 -0
- imap_processing/tests/ialirt/data/l0/461971391-412.bin +0 -0
- imap_processing/tests/ialirt/data/l0/sample_decoded_i-alirt_data.csv +383 -0
- imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
- imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
- imap_processing/tests/ialirt/unit/test_parse_mag.py +223 -0
- imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
- imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
- imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
- imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
- imap_processing/tests/ialirt/unit/test_process_swe.py +319 -6
- imap_processing/tests/ialirt/unit/test_time.py +16 -0
- imap_processing/tests/idex/conftest.py +127 -6
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
- imap_processing/tests/idex/test_idex_l0.py +33 -11
- imap_processing/tests/idex/test_idex_l1a.py +92 -21
- imap_processing/tests/idex/test_idex_l1b.py +106 -27
- imap_processing/tests/idex/test_idex_l2a.py +399 -0
- imap_processing/tests/idex/test_idex_l2b.py +93 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_lo_l1a.py +3 -3
- imap_processing/tests/lo/test_lo_l1b.py +515 -6
- imap_processing/tests/lo/test_lo_l1c.py +1 -1
- imap_processing/tests/lo/test_lo_science.py +7 -7
- imap_processing/tests/lo/test_star_sensor.py +1 -1
- imap_processing/tests/mag/conftest.py +120 -2
- imap_processing/tests/mag/test_mag_decom.py +5 -4
- imap_processing/tests/mag/test_mag_l1a.py +51 -7
- imap_processing/tests/mag/test_mag_l1b.py +40 -59
- imap_processing/tests/mag/test_mag_l1c.py +354 -19
- imap_processing/tests/mag/test_mag_l2.py +130 -0
- imap_processing/tests/mag/test_mag_validation.py +247 -26
- imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
- imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
- imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
- imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
- imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l1b-calibration_20240229_v001.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
- imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
- imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
- imap_processing/tests/spacecraft/test_quaternions.py +71 -0
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
- imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
- imap_processing/tests/spice/test_geometry.py +9 -12
- imap_processing/tests/spice/test_kernels.py +1 -200
- imap_processing/tests/spice/test_pointing_frame.py +185 -0
- imap_processing/tests/spice/test_repoint.py +121 -0
- imap_processing/tests/spice/test_spin.py +50 -9
- imap_processing/tests/spice/test_time.py +14 -0
- imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
- imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
- imap_processing/tests/swapi/test_swapi_l1.py +13 -11
- imap_processing/tests/swapi/test_swapi_l2.py +180 -8
- imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
- imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
- imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
- imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
- imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
- imap_processing/tests/swe/test_swe_l1a.py +20 -2
- imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
- imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
- imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
- imap_processing/tests/swe/test_swe_l1b.py +162 -24
- imap_processing/tests/swe/test_swe_l2.py +153 -91
- imap_processing/tests/test_cli.py +171 -88
- imap_processing/tests/test_utils.py +140 -17
- imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
- imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
- imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
- imap_processing/tests/ultra/data/mock_data.py +369 -0
- imap_processing/tests/ultra/unit/conftest.py +115 -89
- imap_processing/tests/ultra/unit/test_badtimes.py +4 -4
- imap_processing/tests/ultra/unit/test_cullingmask.py +8 -6
- imap_processing/tests/ultra/unit/test_de.py +14 -13
- imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +54 -11
- imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
- imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
- imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
- imap_processing/tests/ultra/unit/test_spacecraft_pset.py +77 -0
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +98 -305
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +60 -14
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +26 -27
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +239 -70
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +114 -83
- imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
- imap_processing/ultra/constants.py +1 -1
- imap_processing/ultra/l0/decom_tools.py +27 -39
- imap_processing/ultra/l0/decom_ultra.py +168 -204
- imap_processing/ultra/l0/ultra_utils.py +152 -136
- imap_processing/ultra/l1a/ultra_l1a.py +55 -271
- imap_processing/ultra/l1b/badtimes.py +1 -4
- imap_processing/ultra/l1b/cullingmask.py +2 -6
- imap_processing/ultra/l1b/de.py +116 -57
- imap_processing/ultra/l1b/extendedspin.py +20 -18
- imap_processing/ultra/l1b/lookup_utils.py +72 -9
- imap_processing/ultra/l1b/ultra_l1b.py +36 -16
- imap_processing/ultra/l1b/ultra_l1b_culling.py +66 -30
- imap_processing/ultra/l1b/ultra_l1b_extended.py +297 -94
- imap_processing/ultra/l1c/histogram.py +2 -6
- imap_processing/ultra/l1c/spacecraft_pset.py +84 -0
- imap_processing/ultra/l1c/ultra_l1c.py +8 -9
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +206 -108
- imap_processing/ultra/l2/ultra_l2.py +299 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
- imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
- imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
- imap_processing/ultra/packet_definitions/README.md +38 -0
- imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
- imap_processing/ultra/utils/ultra_l1_utils.py +31 -12
- imap_processing/utils.py +69 -29
- {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +10 -6
- imap_processing-0.13.0.dist-info/RECORD +578 -0
- imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
- imap_processing/hi/l1a/housekeeping.py +0 -27
- imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
- imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
- imap_processing/swe/l1b/swe_l1b_science.py +0 -652
- 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/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -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/validation_data/hskp_sample_eu.csv +0 -89
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_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/swe/test_swe_l1b_science.py +0 -84
- imap_processing/tests/ultra/test_data/mock_data.py +0 -161
- imap_processing/ultra/l1c/pset.py +0 -40
- imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
- imap_processing-0.11.0.dist-info/RECORD +0 -488
- /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- /imap_processing/{mag/l1b → tests/spacecraft}/__init__.py +0 -0
- /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
imap_processing/hi/l1c/hi_l1c.py
CHANGED
|
@@ -4,25 +4,45 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
from typing import NamedTuple
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
import pandas as pd
|
|
10
11
|
import xarray as xr
|
|
12
|
+
from numpy import typing as npt
|
|
13
|
+
from numpy._typing import NDArray
|
|
11
14
|
|
|
12
15
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
13
16
|
from imap_processing.cdf.utils import parse_filename_like
|
|
14
|
-
from imap_processing.hi.
|
|
17
|
+
from imap_processing.hi.l1a.science_direct_event import (
|
|
18
|
+
DE_CLOCK_TICK_S,
|
|
19
|
+
HALF_CLOCK_TICK_S,
|
|
20
|
+
)
|
|
21
|
+
from imap_processing.hi.utils import (
|
|
22
|
+
CoincidenceBitmap,
|
|
23
|
+
create_dataset_variables,
|
|
24
|
+
full_dataarray,
|
|
25
|
+
parse_sensor_number,
|
|
26
|
+
)
|
|
15
27
|
from imap_processing.spice.geometry import (
|
|
16
28
|
SpiceFrame,
|
|
17
29
|
frame_transform,
|
|
18
30
|
frame_transform_az_el,
|
|
19
31
|
)
|
|
32
|
+
from imap_processing.spice.spin import (
|
|
33
|
+
get_instrument_spin_phase,
|
|
34
|
+
get_spin_data,
|
|
35
|
+
)
|
|
20
36
|
from imap_processing.spice.time import ttj2000ns_to_et
|
|
21
37
|
|
|
38
|
+
N_SPIN_BINS = 3600
|
|
39
|
+
SPIN_PHASE_BIN_EDGES = np.linspace(0, 1, N_SPIN_BINS + 1)
|
|
40
|
+
SPIN_PHASE_BIN_CENTERS = (SPIN_PHASE_BIN_EDGES[:-1] + SPIN_PHASE_BIN_EDGES[1:]) / 2
|
|
41
|
+
|
|
22
42
|
logger = logging.getLogger(__name__)
|
|
23
43
|
|
|
24
44
|
|
|
25
|
-
def hi_l1c(dependencies: list
|
|
45
|
+
def hi_l1c(dependencies: list) -> list[xr.Dataset]:
|
|
26
46
|
"""
|
|
27
47
|
High level IMAP-Hi l1c processing function.
|
|
28
48
|
|
|
@@ -36,10 +56,6 @@ def hi_l1c(dependencies: list, data_version: str) -> xr.Dataset:
|
|
|
36
56
|
dependencies : list
|
|
37
57
|
Input dependencies needed for l1c processing.
|
|
38
58
|
|
|
39
|
-
data_version : str
|
|
40
|
-
Data version to write to CDF files and the Data_version CDF attribute.
|
|
41
|
-
Should be in the format Vxxx.
|
|
42
|
-
|
|
43
59
|
Returns
|
|
44
60
|
-------
|
|
45
61
|
l1c_dataset : xarray.Dataset
|
|
@@ -56,9 +72,7 @@ def hi_l1c(dependencies: list, data_version: str) -> xr.Dataset:
|
|
|
56
72
|
"Input dependencies not recognized for l1c pset processing."
|
|
57
73
|
)
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
l1c_dataset.attrs["Data_version"] = data_version
|
|
61
|
-
return l1c_dataset
|
|
75
|
+
return [l1c_dataset]
|
|
62
76
|
|
|
63
77
|
|
|
64
78
|
def generate_pset_dataset(
|
|
@@ -88,18 +102,19 @@ def generate_pset_dataset(
|
|
|
88
102
|
config_df = CalibrationProductConfig.from_csv(calibration_prod_config_path)
|
|
89
103
|
|
|
90
104
|
pset_dataset = empty_pset_dataset(
|
|
105
|
+
de_dataset.epoch.data[0],
|
|
91
106
|
de_dataset.esa_energy_step.data,
|
|
92
107
|
config_df.cal_prod_config.number_of_products,
|
|
93
108
|
logical_source_parts["sensor"],
|
|
94
109
|
)
|
|
95
|
-
# For ISTP, epoch should be the center of the time bin.
|
|
96
|
-
pset_dataset.epoch.data[0] = np.mean(de_dataset.epoch.data[[0, -1]]).astype(
|
|
97
|
-
np.int64
|
|
98
|
-
)
|
|
99
110
|
pset_et = ttj2000ns_to_et(pset_dataset.epoch.data[0])
|
|
100
111
|
# Calculate and add despun_z, hae_latitude, and hae_longitude variables to
|
|
101
112
|
# the pset_dataset
|
|
102
113
|
pset_dataset.update(pset_geometry(pset_et, logical_source_parts["sensor"]))
|
|
114
|
+
# Bin the counts into the spin-bins
|
|
115
|
+
pset_dataset.update(pset_counts(pset_dataset.coords, config_df, de_dataset))
|
|
116
|
+
# Calculate and add the exposure time to the pset_dataset
|
|
117
|
+
pset_dataset.update(pset_exposure(pset_dataset.coords, de_dataset))
|
|
103
118
|
|
|
104
119
|
# TODO: The following section will go away as PSET algorithms to populate
|
|
105
120
|
# these variables are written.
|
|
@@ -107,8 +122,6 @@ def generate_pset_dataset(
|
|
|
107
122
|
attr_mgr.add_instrument_global_attrs("hi")
|
|
108
123
|
attr_mgr.add_instrument_variable_attrs(instrument="hi", level=None)
|
|
109
124
|
for var_name in [
|
|
110
|
-
"counts",
|
|
111
|
-
"exposure_times",
|
|
112
125
|
"background_rates",
|
|
113
126
|
"background_rates_uncertainty",
|
|
114
127
|
]:
|
|
@@ -122,13 +135,15 @@ def generate_pset_dataset(
|
|
|
122
135
|
|
|
123
136
|
|
|
124
137
|
def empty_pset_dataset(
|
|
125
|
-
l1b_energy_steps: np.ndarray, n_cal_prods: int, sensor_str: str
|
|
138
|
+
epoch_val: int, l1b_energy_steps: np.ndarray, n_cal_prods: int, sensor_str: str
|
|
126
139
|
) -> xr.Dataset:
|
|
127
140
|
"""
|
|
128
141
|
Allocate an empty xarray.Dataset with appropriate pset coordinates.
|
|
129
142
|
|
|
130
143
|
Parameters
|
|
131
144
|
----------
|
|
145
|
+
epoch_val : int
|
|
146
|
+
The starting epoch in J2000 TT nanoseconds for data in the PSET.
|
|
132
147
|
l1b_energy_steps : np.ndarray
|
|
133
148
|
The array of esa_energy_step data from the L1B DE product.
|
|
134
149
|
n_cal_prods : int
|
|
@@ -148,12 +163,12 @@ def empty_pset_dataset(
|
|
|
148
163
|
# preallocate coordinates xr.DataArrays
|
|
149
164
|
coords = dict()
|
|
150
165
|
# epoch coordinate has only 1 entry for pointing set
|
|
151
|
-
epoch_attrs = attr_mgr.get_variable_attributes("epoch")
|
|
166
|
+
epoch_attrs = attr_mgr.get_variable_attributes("epoch", check_schema=False)
|
|
152
167
|
epoch_attrs.update(
|
|
153
168
|
attr_mgr.get_variable_attributes("hi_pset_epoch", check_schema=False)
|
|
154
169
|
)
|
|
155
170
|
coords["epoch"] = xr.DataArray(
|
|
156
|
-
np.
|
|
171
|
+
np.array([epoch_val], dtype=np.int64), # TODO: get dtype from cdf attrs?
|
|
157
172
|
name="epoch",
|
|
158
173
|
dims=["epoch"],
|
|
159
174
|
attrs=epoch_attrs,
|
|
@@ -188,7 +203,7 @@ def empty_pset_dataset(
|
|
|
188
203
|
).copy()
|
|
189
204
|
dtype = attrs.pop("dtype")
|
|
190
205
|
coords["spin_angle_bin"] = xr.DataArray(
|
|
191
|
-
np.arange(
|
|
206
|
+
np.arange(N_SPIN_BINS, dtype=dtype),
|
|
192
207
|
name="spin_angle_bin",
|
|
193
208
|
dims=["spin_angle_bin"],
|
|
194
209
|
attrs=attrs,
|
|
@@ -271,8 +286,8 @@ def pset_geometry(pset_et: float, sensor_str: str) -> dict[str, xr.DataArray]:
|
|
|
271
286
|
el = 0 if "90" in sensor_str else -45
|
|
272
287
|
dps_az_el = np.array(
|
|
273
288
|
[
|
|
274
|
-
|
|
275
|
-
np.full(
|
|
289
|
+
SPIN_PHASE_BIN_CENTERS * 360,
|
|
290
|
+
np.full(N_SPIN_BINS, el),
|
|
276
291
|
]
|
|
277
292
|
).T
|
|
278
293
|
hae_az_el = frame_transform_az_el(
|
|
@@ -282,7 +297,7 @@ def pset_geometry(pset_et: float, sensor_str: str) -> dict[str, xr.DataArray]:
|
|
|
282
297
|
geometry_vars.update(
|
|
283
298
|
create_dataset_variables(
|
|
284
299
|
["hae_latitude", "hae_longitude"],
|
|
285
|
-
(1,
|
|
300
|
+
(1, N_SPIN_BINS),
|
|
286
301
|
att_manager_lookup_str="hi_pset_{0}",
|
|
287
302
|
)
|
|
288
303
|
)
|
|
@@ -295,6 +310,361 @@ def pset_geometry(pset_et: float, sensor_str: str) -> dict[str, xr.DataArray]:
|
|
|
295
310
|
return geometry_vars
|
|
296
311
|
|
|
297
312
|
|
|
313
|
+
def pset_counts(
|
|
314
|
+
pset_coords: dict[str, xr.DataArray],
|
|
315
|
+
config_df: pd.DataFrame,
|
|
316
|
+
l1b_de_dataset: xr.Dataset,
|
|
317
|
+
) -> dict[str, xr.DataArray]:
|
|
318
|
+
"""
|
|
319
|
+
Bin direct events into PSET spin-bins.
|
|
320
|
+
|
|
321
|
+
Parameters
|
|
322
|
+
----------
|
|
323
|
+
pset_coords : dict[str, xr.DataArray]
|
|
324
|
+
The PSET coordinates from the xr.Dataset.
|
|
325
|
+
config_df : pd.DataFrame
|
|
326
|
+
The calibration product configuration dataframe.
|
|
327
|
+
l1b_de_dataset : xr.Dataset
|
|
328
|
+
The L1B dataset for the pointing being processed.
|
|
329
|
+
|
|
330
|
+
Returns
|
|
331
|
+
-------
|
|
332
|
+
dict[str, xr.DataArray]
|
|
333
|
+
Dictionary containing new exposure_times DataArray to be added to the PSET
|
|
334
|
+
dataset.
|
|
335
|
+
"""
|
|
336
|
+
# Generate exposure time variable filled with zeros
|
|
337
|
+
counts_var = create_dataset_variables(
|
|
338
|
+
["counts"],
|
|
339
|
+
coords=pset_coords,
|
|
340
|
+
att_manager_lookup_str="hi_pset_{0}",
|
|
341
|
+
fill_value=0,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Convert list of DEs to pandas dataframe for ease indexing/filtering
|
|
345
|
+
de_df = l1b_de_dataset.drop_dims("epoch").to_pandas()
|
|
346
|
+
|
|
347
|
+
# Remove DEs not in Goodtimes/angles
|
|
348
|
+
good_mask = good_time_and_phase_mask(
|
|
349
|
+
l1b_de_dataset.event_met.values, l1b_de_dataset.spin_phase.values
|
|
350
|
+
)
|
|
351
|
+
de_df = de_df[good_mask]
|
|
352
|
+
|
|
353
|
+
# The calibration product configuration potentially has different coincidence
|
|
354
|
+
# types for each ESA and different TOF windows for each calibration product,
|
|
355
|
+
# esa energy step combination. Because of this we need to filter DEs that
|
|
356
|
+
# belong to each combo individually.
|
|
357
|
+
# Loop over the esa_energy_step values first
|
|
358
|
+
for esa_energy, esa_df in config_df.groupby(level="esa_energy_step"):
|
|
359
|
+
# Create a mask for all DEs at the current esa_energy_step.
|
|
360
|
+
# esa_energy_step is recorded for each packet rather than for each DE,
|
|
361
|
+
# so we use ccsds_index to get the esa_energy_step for each DE
|
|
362
|
+
esa_mask = (
|
|
363
|
+
l1b_de_dataset["esa_energy_step"].data[de_df["ccsds_index"].to_numpy()]
|
|
364
|
+
== esa_energy
|
|
365
|
+
)
|
|
366
|
+
# Now loop over the calibration products for the current ESA energy
|
|
367
|
+
for config_row in esa_df.itertuples():
|
|
368
|
+
# Remove DEs that are not at the current ESA energy and in the list
|
|
369
|
+
# of coincidence types for the current calibration product
|
|
370
|
+
type_mask = de_df["coincidence_type"].isin(
|
|
371
|
+
config_row.coincidence_type_values
|
|
372
|
+
)
|
|
373
|
+
filtered_de_df = de_df[(esa_mask & type_mask)]
|
|
374
|
+
|
|
375
|
+
# Use the TOF window mask to remove DEs with TOFs outside the allowed range
|
|
376
|
+
tof_fill_vals = {
|
|
377
|
+
f"tof_{detector_pair}": l1b_de_dataset[f"tof_{detector_pair}"].attrs[
|
|
378
|
+
"FILLVAL"
|
|
379
|
+
]
|
|
380
|
+
for detector_pair in CalibrationProductConfig.tof_detector_pairs
|
|
381
|
+
}
|
|
382
|
+
tof_in_window_mask = get_tof_window_mask(
|
|
383
|
+
filtered_de_df, config_row, tof_fill_vals
|
|
384
|
+
)
|
|
385
|
+
filtered_de_df = filtered_de_df[tof_in_window_mask]
|
|
386
|
+
|
|
387
|
+
# Bin remaining DEs into spin-bins
|
|
388
|
+
i_esa = np.flatnonzero(pset_coords["esa_energy_step"].data == esa_energy)[0]
|
|
389
|
+
# spin_phase is in the range [0, 1). Multiplying by N_SPIN_BINS and
|
|
390
|
+
# truncating to an integer gives the correct bin index
|
|
391
|
+
spin_bin_indices = (
|
|
392
|
+
filtered_de_df["spin_phase"].to_numpy() * N_SPIN_BINS
|
|
393
|
+
).astype(int)
|
|
394
|
+
# When iterating over rows of a dataframe, the names of the multi-index
|
|
395
|
+
# are not preserved. Below, `config_row.Index[0]` gets the cal_prod_num
|
|
396
|
+
# value from the namedtuple representing the dataframe row.
|
|
397
|
+
np.add.at(
|
|
398
|
+
counts_var["counts"].data[0, i_esa, config_row.Index[0]],
|
|
399
|
+
spin_bin_indices,
|
|
400
|
+
1,
|
|
401
|
+
)
|
|
402
|
+
return counts_var
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def get_tof_window_mask(
|
|
406
|
+
de_df: pd.DataFrame, prod_config_row: NamedTuple, fill_vals: dict
|
|
407
|
+
) -> NDArray[bool]:
|
|
408
|
+
"""
|
|
409
|
+
Generate a mask indicating which DEs to keep based on TOF windows.
|
|
410
|
+
|
|
411
|
+
Parameters
|
|
412
|
+
----------
|
|
413
|
+
de_df : pd.DataFrame
|
|
414
|
+
The Direct Event dataframe for the DEs to filter based on the TOF
|
|
415
|
+
windows.
|
|
416
|
+
prod_config_row : namedtuple
|
|
417
|
+
A single row of the prod config dataframe represented as a named tuple.
|
|
418
|
+
fill_vals : dict
|
|
419
|
+
A dictionary containing the fill values used in the input DE TOF
|
|
420
|
+
dataframe values. This value should be derived from the L1B DE CDF
|
|
421
|
+
TOF variable attributes.
|
|
422
|
+
|
|
423
|
+
Returns
|
|
424
|
+
-------
|
|
425
|
+
window_mask : np.ndarray
|
|
426
|
+
A mask with one entry per DE in the input `de_df` indicating which DEs
|
|
427
|
+
contain TOF values within the windows specified by `prod_config_row`.
|
|
428
|
+
The mask is intended to directly filter the DE dataframe.
|
|
429
|
+
"""
|
|
430
|
+
detector_pairs = CalibrationProductConfig.tof_detector_pairs
|
|
431
|
+
tof_in_window_mask = np.empty((len(detector_pairs), len(de_df)), dtype=bool)
|
|
432
|
+
for i_pair, detector_pair in enumerate(detector_pairs):
|
|
433
|
+
low_limit = getattr(prod_config_row, f"tof_{detector_pair}_low")
|
|
434
|
+
high_limit = getattr(prod_config_row, f"tof_{detector_pair}_high")
|
|
435
|
+
tof_array = de_df[f"tof_{detector_pair}"].to_numpy()
|
|
436
|
+
# The TOF in window mask contains True wherever the TOF is within
|
|
437
|
+
# the configuration low/high bounds OR the FILLVAL is present. The
|
|
438
|
+
# FILLVAL indicates that the detector pair was not hit. DEs with
|
|
439
|
+
# the incorrect coincidence_type are already filtered out and this
|
|
440
|
+
# implementation simplifies combining the tof_in_window_masks in
|
|
441
|
+
# the next step.
|
|
442
|
+
tof_in_window_mask[i_pair] = np.logical_or(
|
|
443
|
+
np.logical_and(low_limit <= tof_array, tof_array <= high_limit),
|
|
444
|
+
tof_array == fill_vals[f"tof_{detector_pair}"],
|
|
445
|
+
)
|
|
446
|
+
return np.all(tof_in_window_mask, axis=0)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def pset_exposure(
|
|
450
|
+
pset_coords: dict[str, xr.DataArray], l1b_de_dataset: xr.Dataset
|
|
451
|
+
) -> dict[str, xr.DataArray]:
|
|
452
|
+
"""
|
|
453
|
+
Calculate PSET exposure time.
|
|
454
|
+
|
|
455
|
+
Parameters
|
|
456
|
+
----------
|
|
457
|
+
pset_coords : dict[str, xr.DataArray]
|
|
458
|
+
The PSET coordinates from the xr.Dataset.
|
|
459
|
+
l1b_de_dataset : xr.Dataset
|
|
460
|
+
The L1B dataset for the pointing being processed.
|
|
461
|
+
|
|
462
|
+
Returns
|
|
463
|
+
-------
|
|
464
|
+
dict[str, xr.DataArray]
|
|
465
|
+
Dictionary containing new exposure_times DataArray to be added to the PSET
|
|
466
|
+
dataset.
|
|
467
|
+
"""
|
|
468
|
+
# Extract the sensor number (45 or 90) for computing spin phase
|
|
469
|
+
sensor_number = parse_sensor_number(l1b_de_dataset.attrs["Logical_source"])
|
|
470
|
+
|
|
471
|
+
# Generate exposure time variable filled with zeros
|
|
472
|
+
exposure_var = create_dataset_variables(
|
|
473
|
+
["exposure_times"],
|
|
474
|
+
coords=pset_coords,
|
|
475
|
+
att_manager_lookup_str="hi_pset_{0}",
|
|
476
|
+
fill_value=0,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# Get a subset of the l1b_de_dataset that contains only the second
|
|
480
|
+
# of each pair of packets at an ESA step.
|
|
481
|
+
data_subset = find_second_de_packet_data(l1b_de_dataset)
|
|
482
|
+
|
|
483
|
+
# Get the pandas dataframe with spin data
|
|
484
|
+
spin_df = get_spin_data()
|
|
485
|
+
|
|
486
|
+
# Loop over each of the CCSDS data rows that have been identified as the second
|
|
487
|
+
# packet at an ESA step.
|
|
488
|
+
# When implementing this, the memory needed to avoid this for loop was computed
|
|
489
|
+
# and determined to be so large that the for loop is warranted.
|
|
490
|
+
for _, packet_row in data_subset.groupby("epoch"):
|
|
491
|
+
clock_tick_mets, clock_tick_weights = get_de_clock_ticks_for_esa_step(
|
|
492
|
+
packet_row["ccsds_met"].values, spin_df
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Clock tick MET times are accumulation "edges". To get the mean spin-phase
|
|
496
|
+
# for a given clock tick, add 1/2 clock tick and compute spin-phase.
|
|
497
|
+
spin_phases = np.atleast_1d(
|
|
498
|
+
get_instrument_spin_phase(
|
|
499
|
+
clock_tick_mets + HALF_CLOCK_TICK_S,
|
|
500
|
+
SpiceFrame[f"IMAP_HI_{sensor_number}"],
|
|
501
|
+
)
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Remove ticks not in good times/angles
|
|
505
|
+
good_mask = good_time_and_phase_mask(clock_tick_mets, spin_phases)
|
|
506
|
+
spin_phases = spin_phases[good_mask]
|
|
507
|
+
clock_tick_weights = clock_tick_weights[good_mask]
|
|
508
|
+
|
|
509
|
+
# TODO: Account for flyback time. See alg doc section 2.3.5
|
|
510
|
+
|
|
511
|
+
# Bin exposure times into spin-phase bins
|
|
512
|
+
new_exposure_times, _ = np.histogram(
|
|
513
|
+
spin_phases, bins=SPIN_PHASE_BIN_EDGES, weights=clock_tick_weights
|
|
514
|
+
)
|
|
515
|
+
# Accumulate the new exposure times for current esa_step
|
|
516
|
+
i_esa = np.flatnonzero(
|
|
517
|
+
pset_coords["esa_energy_step"].values
|
|
518
|
+
== packet_row["esa_energy_step"].values
|
|
519
|
+
)[0]
|
|
520
|
+
exposure_var["exposure_times"].values[:, i_esa] += new_exposure_times
|
|
521
|
+
|
|
522
|
+
# Convert exposure clock ticks to seconds
|
|
523
|
+
exposure_var["exposure_times"].values *= DE_CLOCK_TICK_S
|
|
524
|
+
|
|
525
|
+
return exposure_var
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def find_second_de_packet_data(l1b_dataset: xr.Dataset) -> xr.Dataset:
|
|
529
|
+
"""
|
|
530
|
+
Find the telemetry entries for the second packet at an ESA step.
|
|
531
|
+
|
|
532
|
+
Parameters
|
|
533
|
+
----------
|
|
534
|
+
l1b_dataset : xr.Dataset
|
|
535
|
+
The L1B Direct Event Dataset for the current pointing.
|
|
536
|
+
|
|
537
|
+
Returns
|
|
538
|
+
-------
|
|
539
|
+
reduced_dataset : xr.Dataset
|
|
540
|
+
A dataset containing only the entries for the second packet at an ESA step.
|
|
541
|
+
"""
|
|
542
|
+
epoch_dataset = l1b_dataset.drop_dims("event_met")
|
|
543
|
+
# We should get two CCSDS packets per 8-spin ESA step.
|
|
544
|
+
# Get the indices of the packet before each ESA change.
|
|
545
|
+
esa_step = epoch_dataset["esa_step"].values
|
|
546
|
+
second_esa_packet_idx = np.append(
|
|
547
|
+
np.flatnonzero(np.diff(esa_step) != 0), len(esa_step) - 1
|
|
548
|
+
)
|
|
549
|
+
# Remove esa steps at 0 - these are calibrations
|
|
550
|
+
second_esa_packet_idx = second_esa_packet_idx[esa_step[second_esa_packet_idx] != 0]
|
|
551
|
+
# Remove indices where we don't have two consecutive packets at the same ESA
|
|
552
|
+
if second_esa_packet_idx[0] == 0:
|
|
553
|
+
logger.warning(
|
|
554
|
+
f"Removing packet 0 with ESA step: {esa_step[0]} from"
|
|
555
|
+
f"calculation of exposure time due to missing matched pair."
|
|
556
|
+
)
|
|
557
|
+
second_esa_packet_idx = second_esa_packet_idx[1:]
|
|
558
|
+
missing_esa_pair_mask = (
|
|
559
|
+
esa_step[second_esa_packet_idx - 1] != esa_step[second_esa_packet_idx]
|
|
560
|
+
)
|
|
561
|
+
if missing_esa_pair_mask.any():
|
|
562
|
+
logger.warning(
|
|
563
|
+
f"Removing {missing_esa_pair_mask.sum()} packets from exposure "
|
|
564
|
+
f"time calculation due to missing ESA step DE packet pairs."
|
|
565
|
+
)
|
|
566
|
+
second_esa_packet_idx = second_esa_packet_idx[~missing_esa_pair_mask]
|
|
567
|
+
# Reduce the dataset to just the second packet entries
|
|
568
|
+
data_subset = epoch_dataset.isel(epoch=second_esa_packet_idx)
|
|
569
|
+
return data_subset
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def get_de_clock_ticks_for_esa_step(
|
|
573
|
+
ccsds_met: float, spin_df: pd.DataFrame
|
|
574
|
+
) -> tuple[np.ndarray, np.ndarray]:
|
|
575
|
+
"""
|
|
576
|
+
Generate an array of clock tick MET times for an 8-spin ESA step.
|
|
577
|
+
|
|
578
|
+
Find the closest spin start time in the input spin dataframe to the packet
|
|
579
|
+
creation time (`ccsds_met`) and generate an array of clock tick MET times
|
|
580
|
+
for the period covered by the previous 8-spin group and an array of weights
|
|
581
|
+
that represent the fraction of each clock tick that occurred in the 8-spin
|
|
582
|
+
group.
|
|
583
|
+
|
|
584
|
+
Parameters
|
|
585
|
+
----------
|
|
586
|
+
ccsds_met : float
|
|
587
|
+
The CCSDS MET of the second packet in a DE packet pair.
|
|
588
|
+
spin_df : pd.DataFrame
|
|
589
|
+
Universal spin table dataframe.
|
|
590
|
+
|
|
591
|
+
Returns
|
|
592
|
+
-------
|
|
593
|
+
clock_tick_mets : np.ndarray
|
|
594
|
+
Array of MET times that a clock tick occurred in an 8-spin group of spins
|
|
595
|
+
during which the ESA step was constant.
|
|
596
|
+
clock_tick_weights : np.ndarray
|
|
597
|
+
Array of weights to use when binning the clock tick MET times into spin-bins.
|
|
598
|
+
"""
|
|
599
|
+
# Find the last spin_table entry with the start less than the CCSDS MET.
|
|
600
|
+
# The CCSDS packet gets created just AFTER the final spin in the 8-spin
|
|
601
|
+
# ESA step group so this match is the end time. The start time is
|
|
602
|
+
# 8-spins earlier.
|
|
603
|
+
spin_start_mets = spin_df.spin_start_met.to_numpy()
|
|
604
|
+
# CCSDS MET has one second resolution, add one to it to make sure it is
|
|
605
|
+
# greater than the spin start time it ended on.
|
|
606
|
+
end_time_ind = np.flatnonzero(ccsds_met + 1 >= spin_start_mets).max()
|
|
607
|
+
|
|
608
|
+
# If the minimum absolute difference is greater than 1/2 the spin-phase
|
|
609
|
+
# we have a problem.
|
|
610
|
+
if (
|
|
611
|
+
ccsds_met - spin_start_mets[end_time_ind]
|
|
612
|
+
> spin_df.iloc[end_time_ind].spin_period_sec / 2
|
|
613
|
+
):
|
|
614
|
+
raise ValueError(
|
|
615
|
+
"The difference between ccsds_met and spin_start_met, "
|
|
616
|
+
f"{ccsds_met - spin_start_mets[end_time_ind]} seconds, "
|
|
617
|
+
f"is too large. Check the spin table loaded for this pointing."
|
|
618
|
+
)
|
|
619
|
+
# If the end time index less than 8, we don't have enough spins in the
|
|
620
|
+
# spin table to get a start time, so raise an error.
|
|
621
|
+
if end_time_ind < 8:
|
|
622
|
+
raise ValueError(
|
|
623
|
+
"Error determining start/end time for exposure time. "
|
|
624
|
+
f"The CCSDS MET time {ccsds_met} "
|
|
625
|
+
"is less than 8 spins from the loaded spin table data."
|
|
626
|
+
)
|
|
627
|
+
clock_tick_mets = np.arange(
|
|
628
|
+
spin_start_mets[end_time_ind - 8],
|
|
629
|
+
spin_start_mets[end_time_ind],
|
|
630
|
+
DE_CLOCK_TICK_S,
|
|
631
|
+
dtype=float,
|
|
632
|
+
)
|
|
633
|
+
# The final clock-tick bin has less exposure time because the next spin
|
|
634
|
+
# will trigger FSW to change ESA steps part way through that time. To
|
|
635
|
+
# account for this in exposure time calculation, assign an array of
|
|
636
|
+
# weights to use when binnig the clock-ticks to spin-bins. Weights are
|
|
637
|
+
# fractional clock ticks. All weights are 1 except for the last one in
|
|
638
|
+
# the array.
|
|
639
|
+
clock_tick_weights = np.ones_like(clock_tick_mets, dtype=float)
|
|
640
|
+
clock_tick_weights[-1] = (
|
|
641
|
+
spin_start_mets[end_time_ind] - clock_tick_mets[-1]
|
|
642
|
+
) / DE_CLOCK_TICK_S
|
|
643
|
+
return clock_tick_mets, clock_tick_weights
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def good_time_and_phase_mask(
|
|
647
|
+
tick_mets: np.ndarray, spin_phases: np.ndarray
|
|
648
|
+
) -> npt.NDArray:
|
|
649
|
+
"""
|
|
650
|
+
Filter out the clock tick times that are not in good times and angles.
|
|
651
|
+
|
|
652
|
+
Parameters
|
|
653
|
+
----------
|
|
654
|
+
tick_mets : np.ndarray
|
|
655
|
+
Clock-tick MET times.
|
|
656
|
+
spin_phases : np.ndarray
|
|
657
|
+
Spin phases for each clock tick.
|
|
658
|
+
|
|
659
|
+
Returns
|
|
660
|
+
-------
|
|
661
|
+
keep_mask : np.ndarray
|
|
662
|
+
Boolean mask indicating which clock ticks are in good times/phases.
|
|
663
|
+
"""
|
|
664
|
+
# TODO: Implement this once we have Goodtimes data product defined.
|
|
665
|
+
return np.full_like(tick_mets, True, dtype=bool)
|
|
666
|
+
|
|
667
|
+
|
|
298
668
|
@pd.api.extensions.register_dataframe_accessor("cal_prod_config")
|
|
299
669
|
class CalibrationProductConfig:
|
|
300
670
|
"""
|
|
@@ -310,21 +680,20 @@ class CalibrationProductConfig:
|
|
|
310
680
|
"cal_prod_num",
|
|
311
681
|
"esa_energy_step",
|
|
312
682
|
)
|
|
683
|
+
tof_detector_pairs = ("ab", "ac1", "bc1", "c1c2")
|
|
313
684
|
required_columns = (
|
|
314
685
|
"coincidence_type_list",
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
"tof_bc1_high",
|
|
321
|
-
"tof_c1c2_low",
|
|
322
|
-
"tof_c1c2_high",
|
|
686
|
+
*[
|
|
687
|
+
f"tof_{det_pair}_{limit}"
|
|
688
|
+
for det_pair in tof_detector_pairs
|
|
689
|
+
for limit in ["low", "high"]
|
|
690
|
+
],
|
|
323
691
|
)
|
|
324
692
|
|
|
325
693
|
def __init__(self, pandas_obj: pd.DataFrame) -> None:
|
|
326
694
|
self._validate(pandas_obj)
|
|
327
695
|
self._obj = pandas_obj
|
|
696
|
+
self._add_coincidence_values_column()
|
|
328
697
|
|
|
329
698
|
def _validate(self, df: pd.DataFrame) -> None:
|
|
330
699
|
"""
|
|
@@ -351,6 +720,18 @@ class CalibrationProductConfig:
|
|
|
351
720
|
# TODO: Verify that the same ESA energy steps exist in all unique calibration
|
|
352
721
|
# product numbers
|
|
353
722
|
|
|
723
|
+
def _add_coincidence_values_column(self) -> None:
|
|
724
|
+
"""Generate and add the coincidence_type_values column to the dataframe."""
|
|
725
|
+
# Add a column that consists of the coincidence type strings converted
|
|
726
|
+
# to integer values
|
|
727
|
+
self._obj["coincidence_type_values"] = self._obj.apply(
|
|
728
|
+
lambda row: tuple(
|
|
729
|
+
CoincidenceBitmap.detector_hit_str_to_int(entry)
|
|
730
|
+
for entry in row["coincidence_type_list"]
|
|
731
|
+
),
|
|
732
|
+
axis=1,
|
|
733
|
+
)
|
|
734
|
+
|
|
354
735
|
@classmethod
|
|
355
736
|
def from_csv(cls, path: Path) -> pd.DataFrame:
|
|
356
737
|
"""
|
|
@@ -366,12 +747,15 @@ class CalibrationProductConfig:
|
|
|
366
747
|
dataframe : pandas.DataFrame
|
|
367
748
|
Validated calibration product configuration data frame.
|
|
368
749
|
"""
|
|
369
|
-
|
|
750
|
+
df = pd.read_csv(
|
|
370
751
|
path,
|
|
371
752
|
index_col=cls.index_columns,
|
|
372
|
-
converters={"coincidence_type_list": lambda s: s.split("|")},
|
|
753
|
+
converters={"coincidence_type_list": lambda s: tuple(s.split("|"))},
|
|
373
754
|
comment="#",
|
|
374
755
|
)
|
|
756
|
+
# Force the _init_ method to run by using the namespace
|
|
757
|
+
_ = df.cal_prod_config.number_of_products
|
|
758
|
+
return df
|
|
375
759
|
|
|
376
760
|
@property
|
|
377
761
|
def number_of_products(self) -> int:
|