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
|
@@ -1,50 +1,925 @@
|
|
|
1
|
-
"""Contains code to perform SWE L1b processing."""
|
|
1
|
+
"""Contains code to perform SWE L1b science processing."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Union
|
|
4
6
|
|
|
7
|
+
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
9
|
+
import pandas as pd
|
|
5
10
|
import xarray as xr
|
|
11
|
+
from imap_data_access.processing_input import ProcessingInputCollection
|
|
6
12
|
|
|
7
|
-
from imap_processing import
|
|
8
|
-
from imap_processing.
|
|
9
|
-
from imap_processing.
|
|
13
|
+
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
14
|
+
from imap_processing.cdf.utils import load_cdf
|
|
15
|
+
from imap_processing.spice.time import met_to_ttj2000ns
|
|
16
|
+
from imap_processing.swe.utils import swe_constants
|
|
17
|
+
from imap_processing.swe.utils.swe_utils import (
|
|
18
|
+
SWEAPID,
|
|
19
|
+
calculate_data_acquisition_time,
|
|
20
|
+
combine_acquisition_time,
|
|
21
|
+
read_lookup_table,
|
|
22
|
+
)
|
|
10
23
|
from imap_processing.utils import convert_raw_to_eu
|
|
11
24
|
|
|
12
25
|
logger = logging.getLogger(__name__)
|
|
13
26
|
|
|
14
27
|
|
|
15
|
-
def
|
|
28
|
+
def get_esa_dataframe(esa_table_number: int) -> pd.DataFrame:
|
|
16
29
|
"""
|
|
17
|
-
|
|
30
|
+
Read lookup table from file.
|
|
18
31
|
|
|
19
32
|
Parameters
|
|
20
33
|
----------
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
data_version : str
|
|
24
|
-
Version of the data product being created.
|
|
34
|
+
esa_table_number : int
|
|
35
|
+
ESA table index number.
|
|
25
36
|
|
|
26
37
|
Returns
|
|
27
38
|
-------
|
|
28
|
-
|
|
29
|
-
|
|
39
|
+
esa_steps : pandas.DataFrame
|
|
40
|
+
ESA table_number and its associated values.
|
|
30
41
|
"""
|
|
31
|
-
|
|
42
|
+
if esa_table_number not in [0, 1]:
|
|
43
|
+
raise ValueError(f"Unknown ESA table number {esa_table_number}")
|
|
32
44
|
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
# Get the lookup table DataFrame
|
|
46
|
+
lookup_table = read_lookup_table()
|
|
47
|
+
|
|
48
|
+
esa_steps = lookup_table.loc[lookup_table["table_index"] == esa_table_number]
|
|
49
|
+
return esa_steps
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def deadtime_correction(
|
|
53
|
+
counts: np.ndarray, acq_duration: Union[int, npt.NDArray]
|
|
54
|
+
) -> npt.NDArray:
|
|
55
|
+
"""
|
|
56
|
+
Calculate deadtime correction.
|
|
57
|
+
|
|
58
|
+
Deadtime correction is a technique used in various fields, including
|
|
59
|
+
nuclear physics, radiation detection, and particle counting, to compensate
|
|
60
|
+
for the effects of the time period during which a detector is not able to
|
|
61
|
+
record new events or measurements after detecting a previous event.
|
|
62
|
+
This "deadtime" is essentially the time during which the detector is
|
|
63
|
+
recovering from the previous detection and is unable to detect new events.
|
|
64
|
+
|
|
65
|
+
In particle detectors, there is a finite time required for the detector to
|
|
66
|
+
reset or recover after detecting a particle. During this deadtime, any
|
|
67
|
+
subsequent particles that may have arrived go undetected. As a result,
|
|
68
|
+
the recorded count rate appears to be lower than the actual count rate.
|
|
69
|
+
|
|
70
|
+
Deadtime correction involves mathematically adjusting the measured count
|
|
71
|
+
rates to compensate for this deadtime effect. This correction is crucial
|
|
72
|
+
when dealing with high-intensity sources or particle fluxes, as the deadtime
|
|
73
|
+
can significantly affect the accuracy of the measurements.
|
|
74
|
+
|
|
75
|
+
Deadtime correction is important to ensure accurate measurements and data
|
|
76
|
+
analysis in fields where event detection rates are high and where every
|
|
77
|
+
detected event is critical for understanding physical processes.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
counts : numpy.ndarray
|
|
82
|
+
Counts data before deadtime corrections.
|
|
83
|
+
acq_duration : int or numpy.ndarray
|
|
84
|
+
This is ACQ_DURATION from science packet. acq_duration is in microseconds.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
corrected_count : numpy.ndarray
|
|
89
|
+
Corrected counts.
|
|
90
|
+
"""
|
|
91
|
+
# deadtime is 360 ns
|
|
92
|
+
deadtime = 360e-9
|
|
93
|
+
if isinstance(acq_duration, int):
|
|
94
|
+
# Convert acq_duration to a numpy array for consistency
|
|
95
|
+
acq_duration = np.array([acq_duration])
|
|
96
|
+
correct = 1.0 - (deadtime * (counts / (acq_duration[..., np.newaxis] * 1e-6)))
|
|
97
|
+
# NOTE: 0.1 is defined in SWE algorithm document. It says
|
|
98
|
+
# 'arbitrary x10 cutoff' in the document.
|
|
99
|
+
correct = np.maximum(0.1, correct)
|
|
100
|
+
corrected_count = counts.astype(np.float64) / correct
|
|
101
|
+
return corrected_count
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def convert_counts_to_rate(data: np.ndarray, acq_duration: np.ndarray) -> npt.NDArray:
|
|
105
|
+
"""
|
|
106
|
+
Convert counts to rate using sampling time.
|
|
107
|
+
|
|
108
|
+
acq_duration is ACQ_DURATION from science packet.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
data : numpy.ndarray
|
|
113
|
+
Counts data.
|
|
114
|
+
acq_duration : numpy.ndarray
|
|
115
|
+
Acquisition duration. acq_duration is in microseconds.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
numpy.ndarray
|
|
120
|
+
Count rates array in seconds.
|
|
121
|
+
"""
|
|
122
|
+
# Convert microseconds to seconds without modifying the original acq_duration
|
|
123
|
+
acq_duration_sec = acq_duration * 1e-6
|
|
124
|
+
|
|
125
|
+
# Ensure acq_duration_sec is broadcastable to data
|
|
126
|
+
if acq_duration_sec.ndim < data.ndim:
|
|
127
|
+
acq_duration_sec = acq_duration_sec[
|
|
128
|
+
..., np.newaxis
|
|
129
|
+
] # Add a new axis for broadcasting
|
|
130
|
+
|
|
131
|
+
# Perform element-wise division
|
|
132
|
+
count_rate = data.astype(np.float64) / acq_duration_sec
|
|
133
|
+
return count_rate
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def read_in_flight_cal_data(in_flight_cal_files: list) -> pd.DataFrame:
|
|
137
|
+
"""
|
|
138
|
+
Read in-flight calibration data.
|
|
139
|
+
|
|
140
|
+
In-flight calibration data file will contain rows where each line
|
|
141
|
+
has 8 numbers, with the first being a time stamp in MET, and the next
|
|
142
|
+
7 being the factors for the 7 detectors.
|
|
143
|
+
|
|
144
|
+
This file will be updated weekly with new calibration data. In other
|
|
145
|
+
words, one line of data will be added each week to the existing file.
|
|
146
|
+
File will be in CSV format. Processing won't be kicked off until there
|
|
147
|
+
is in-flight calibration data that covers science data.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
in_flight_cal_files : list
|
|
152
|
+
List of in-flight calibration files.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
in_flight_cal_df : pandas.DataFrame
|
|
157
|
+
DataFrame with in-flight calibration data.
|
|
158
|
+
"""
|
|
159
|
+
column_names = [
|
|
160
|
+
"met_time",
|
|
161
|
+
"cem1",
|
|
162
|
+
"cem2",
|
|
163
|
+
"cem3",
|
|
164
|
+
"cem4",
|
|
165
|
+
"cem5",
|
|
166
|
+
"cem6",
|
|
167
|
+
"cem7",
|
|
168
|
+
]
|
|
169
|
+
in_flight_cal_df = pd.concat(
|
|
170
|
+
[
|
|
171
|
+
pd.read_csv(file_path, header=0, names=column_names)
|
|
172
|
+
for file_path in in_flight_cal_files
|
|
173
|
+
]
|
|
174
|
+
)
|
|
175
|
+
# Drop duplicates and keep only last occurrence
|
|
176
|
+
in_flight_cal_df = in_flight_cal_df.drop_duplicates(
|
|
177
|
+
subset=["met_time"], keep="last"
|
|
36
178
|
)
|
|
179
|
+
# Sort by 'met_time' column
|
|
180
|
+
in_flight_cal_df = in_flight_cal_df.sort_values(by="met_time")
|
|
181
|
+
return in_flight_cal_df
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def calculate_calibration_factor(
|
|
185
|
+
acquisition_times: np.ndarray, cal_times: np.ndarray, cal_data: np.ndarray
|
|
186
|
+
) -> npt.NDArray:
|
|
187
|
+
"""
|
|
188
|
+
Calculate calibration factor using linear interpolation.
|
|
189
|
+
|
|
190
|
+
Steps to calculate calibration factor:
|
|
191
|
+
1. Convert input time to match time format in the calibration data file.
|
|
192
|
+
Both times should be in S/C MET time.
|
|
193
|
+
2. Find the nearest in time calibration data point.
|
|
194
|
+
3. Linear interpolate between those two nearest time and get factor for
|
|
195
|
+
input time.
|
|
196
|
+
|
|
197
|
+
Parameters
|
|
198
|
+
----------
|
|
199
|
+
acquisition_times : numpy.ndarray
|
|
200
|
+
Data points to interpolate. Shape is (N_ESA_STEPS, N_ANGLE_SECTORS).
|
|
201
|
+
cal_times : numpy.ndarray
|
|
202
|
+
X-coordinates data points. Calibration times. Shape is (n,).
|
|
203
|
+
cal_data : numpy.ndarray
|
|
204
|
+
Y-coordinates data points. Calibration data of corresponding cal_times.
|
|
205
|
+
Shape is (n, N_CEMS).
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
calibration_factor : numpy.ndarray
|
|
210
|
+
Calibration factor for each CEM detector. Shape is
|
|
211
|
+
(N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS) where last 7 dimension
|
|
212
|
+
contains calibration factor for each CEM detector.
|
|
213
|
+
"""
|
|
214
|
+
# Raise error if there is no pre or post time in cal_times. SWE does not
|
|
215
|
+
# want to extrapolate calibration data.
|
|
216
|
+
if (
|
|
217
|
+
acquisition_times.min() < cal_times.min()
|
|
218
|
+
or acquisition_times.max() > cal_times.max()
|
|
219
|
+
):
|
|
220
|
+
raise ValueError(
|
|
221
|
+
f"Acquisition min/max times: {acquisition_times.min()} to "
|
|
222
|
+
f"{acquisition_times.max()}. "
|
|
223
|
+
f"Calibration min/max times: {cal_times.min()} to {cal_times.max()}. "
|
|
224
|
+
"Acquisition times should be within calibration time range."
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# This line of code finds the indices of acquisition_times in cal_times where
|
|
228
|
+
# acquisition_times should be inserted to maintain order. As a result, it finds
|
|
229
|
+
# its nearest pre and post time from cal_times.
|
|
230
|
+
input_time_indices = np.searchsorted(cal_times, acquisition_times)
|
|
231
|
+
|
|
232
|
+
# Assign to a variable for better readability
|
|
233
|
+
x = acquisition_times
|
|
234
|
+
xp = cal_times
|
|
235
|
+
fp = cal_data
|
|
236
|
+
|
|
237
|
+
# Given this situation which will be the case for SWE data
|
|
238
|
+
# where data will fall in between two calibration times and
|
|
239
|
+
# not be exactly equal to any calibration time,
|
|
240
|
+
# >>> a = [1, 2, 3]
|
|
241
|
+
# >>> np.searchsorted(a, [2.5])
|
|
242
|
+
# array([2])
|
|
243
|
+
# we need to use (j - 1) to get pre time indices. (j-1) is
|
|
244
|
+
# pre time indices and j is post time indices.
|
|
245
|
+
j = input_time_indices
|
|
246
|
+
w = (x - xp[j - 1]) / (xp[j] - xp[j - 1])
|
|
247
|
+
return fp[j - 1] + w[..., None] * (fp[j] - fp[j - 1])
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def apply_in_flight_calibration(
|
|
251
|
+
corrected_counts: np.ndarray,
|
|
252
|
+
acquisition_time: np.ndarray,
|
|
253
|
+
in_flight_cal_files: list,
|
|
254
|
+
) -> npt.NDArray:
|
|
255
|
+
"""
|
|
256
|
+
Apply in flight calibration to full cycle data.
|
|
257
|
+
|
|
258
|
+
These factors are used to account for changes in gain with time.
|
|
259
|
+
|
|
260
|
+
They are derived from the weekly electron calibration data.
|
|
261
|
+
|
|
262
|
+
Parameters
|
|
263
|
+
----------
|
|
264
|
+
corrected_counts : numpy.ndarray
|
|
265
|
+
Corrected count of full cycle data. Data shape is
|
|
266
|
+
(N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS).
|
|
267
|
+
acquisition_time : numpy.ndarray
|
|
268
|
+
Acquisition time of full cycle data. Data shape is
|
|
269
|
+
(N_ESA_STEPS, N_ANGLE_SECTORS).
|
|
270
|
+
in_flight_cal_files : list
|
|
271
|
+
List of in-flight calibration files.
|
|
272
|
+
|
|
273
|
+
Returns
|
|
274
|
+
-------
|
|
275
|
+
corrected_counts : numpy.ndarray
|
|
276
|
+
Corrected count of full cycle data after applying in-flight calibration.
|
|
277
|
+
Array shape is (N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS).
|
|
278
|
+
"""
|
|
279
|
+
# Read in in-flight calibration data
|
|
280
|
+
in_flight_cal_df = read_in_flight_cal_data(in_flight_cal_files)
|
|
281
|
+
# calculate calibration factor.
|
|
282
|
+
# return shape of calculate_calibration_factor is
|
|
283
|
+
# (N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS) where
|
|
284
|
+
# last 7 dimension contains calibration factor for each CEM detector.
|
|
285
|
+
cal_factor = calculate_calibration_factor(
|
|
286
|
+
acquisition_time,
|
|
287
|
+
in_flight_cal_df["met_time"].values,
|
|
288
|
+
in_flight_cal_df.iloc[:, 1:].values,
|
|
289
|
+
)
|
|
290
|
+
# Apply to full cycle data
|
|
291
|
+
return corrected_counts.astype(np.float64) * cal_factor
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def find_cycle_starts(cycles: np.ndarray) -> npt.NDArray:
|
|
295
|
+
"""
|
|
296
|
+
Find index of where new cycle started.
|
|
297
|
+
|
|
298
|
+
Brandon Stone helped developed this algorithm.
|
|
299
|
+
|
|
300
|
+
Parameters
|
|
301
|
+
----------
|
|
302
|
+
cycles : numpy.ndarray
|
|
303
|
+
Array that contains quarter cycle information.
|
|
304
|
+
|
|
305
|
+
Returns
|
|
306
|
+
-------
|
|
307
|
+
first_quarter_indices : numpy.ndarray
|
|
308
|
+
Array of indices of start cycle.
|
|
309
|
+
"""
|
|
310
|
+
if cycles.size < swe_constants.N_QUARTER_CYCLES:
|
|
311
|
+
return np.array([], np.int64)
|
|
312
|
+
|
|
313
|
+
# calculate difference between consecutive cycles
|
|
314
|
+
diff = cycles[1:] - cycles[:-1]
|
|
315
|
+
|
|
316
|
+
# This uses sliding window to find index where cycle starts.
|
|
317
|
+
# This is what this below code line is doing:
|
|
318
|
+
# [1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0] # Is cycle zero?
|
|
319
|
+
# [1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1] # Next diff is one?
|
|
320
|
+
# [1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1 0] # Next diff is one?
|
|
321
|
+
# [0 1 1 1 0 1 0 0 1 0 1 1 1 0 1 0 0] # Next diff is one?
|
|
322
|
+
#
|
|
323
|
+
# [0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0] # And all?
|
|
324
|
+
ione = diff == 1
|
|
325
|
+
valid = (cycles == 0)[:-3] & ione[:-2] & ione[1:-1] & ione[2:]
|
|
326
|
+
first_quarter_indices = np.where(valid)[0]
|
|
327
|
+
return first_quarter_indices
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def get_indices_of_full_cycles(quarter_cycle: np.ndarray) -> npt.NDArray:
|
|
331
|
+
"""
|
|
332
|
+
Get indices of full cycles.
|
|
333
|
+
|
|
334
|
+
Parameters
|
|
335
|
+
----------
|
|
336
|
+
quarter_cycle : numpy.ndarray
|
|
337
|
+
Array that contains quarter cycles information.
|
|
338
|
+
|
|
339
|
+
Returns
|
|
340
|
+
-------
|
|
341
|
+
full_cycles_indices : numpy.ndarray
|
|
342
|
+
1D array with indices of full cycle data.
|
|
343
|
+
"""
|
|
344
|
+
indices_of_start = find_cycle_starts(quarter_cycle)
|
|
345
|
+
# indices_of_start[..., None] creates array of shape(n, 1).
|
|
346
|
+
# Eg. [[3], [8]]
|
|
347
|
+
# np.arange(4)[None, ...] creates array of shape(1, 4)
|
|
348
|
+
# Eg. [[0, 1, 2, 3]]
|
|
349
|
+
# then we add both of them together to get an array of shape(n, 4)
|
|
350
|
+
# Eg. [[3, 4, 5, 6], [8, 9, 10, 11]]
|
|
351
|
+
full_cycles_indices = (
|
|
352
|
+
indices_of_start[..., None]
|
|
353
|
+
+ np.arange(swe_constants.N_QUARTER_CYCLES)[None, ...]
|
|
354
|
+
)
|
|
355
|
+
return full_cycles_indices.reshape(-1)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def get_esa_energy_pattern(esa_lut_file: Path, esa_table_num: int = 0) -> npt.NDArray:
|
|
359
|
+
"""
|
|
360
|
+
Get energy in the checkerboard pattern of a full cycle of SWE data.
|
|
361
|
+
|
|
362
|
+
This uses ESA Table index number to look up which pattern to use from
|
|
363
|
+
ESA LUT. This is used in L2 to process data further.
|
|
364
|
+
|
|
365
|
+
Parameters
|
|
366
|
+
----------
|
|
367
|
+
esa_lut_file : pathlib.Path
|
|
368
|
+
ESA LUT file.
|
|
369
|
+
esa_table_num : int
|
|
370
|
+
ESA table number. Default is 0.
|
|
371
|
+
|
|
372
|
+
Returns
|
|
373
|
+
-------
|
|
374
|
+
energy_pattern : numpy.ndarray
|
|
375
|
+
(esa_step, spin_sector) array with energies of cycle data.
|
|
376
|
+
"""
|
|
377
|
+
esa_lut_df = pd.read_csv(esa_lut_file)
|
|
378
|
+
# Get the pattern from the ESA LUT
|
|
379
|
+
esa_table_df = esa_lut_df[esa_lut_df["table_idx"] == esa_table_num]
|
|
380
|
+
|
|
381
|
+
# Now define variable to store pattern for the first two columns
|
|
382
|
+
# because that pattern is repeated in the rest of the columns.
|
|
383
|
+
first_two_columns = np.zeros((swe_constants.N_ESA_STEPS, 2), dtype=np.float64)
|
|
384
|
+
# Get row indices of all four quarter cycles. Then minus 1 to get
|
|
385
|
+
# the row indices in 0-23 instead of 1-24.
|
|
386
|
+
cycle_row_indices = esa_table_df["v_index"].values - 1
|
|
387
|
+
esa_v = esa_table_df["esa_v"].values
|
|
388
|
+
# Reshaping the 'v_index' into 4 x 12 gets 12 repeated row_indices and
|
|
389
|
+
# energy steps of each quarter cycle
|
|
390
|
+
row_indices = cycle_row_indices.reshape(4, 12)
|
|
391
|
+
esa_v = esa_v.reshape(4, 12)
|
|
392
|
+
for i in range(4):
|
|
393
|
+
# Split each quarter's 12 steps into 2 x 6 blocks for even
|
|
394
|
+
# and odd columns
|
|
395
|
+
even_odd_column_info = row_indices[i].reshape(2, 6)
|
|
396
|
+
even_row_indices = even_odd_column_info[0]
|
|
397
|
+
odd_row_indices = even_odd_column_info[1]
|
|
398
|
+
|
|
399
|
+
# Get even and odd column's ESA voltage information
|
|
400
|
+
esa_v_info = esa_v[i].reshape(2, 6)
|
|
401
|
+
first_two_columns[even_row_indices, 0] = esa_v_info[0]
|
|
402
|
+
first_two_columns[odd_row_indices, 1] = esa_v_info[1]
|
|
403
|
+
|
|
404
|
+
# Repeat the first 2 column pattern 15 times across 30 columns
|
|
405
|
+
# (2 columns x 15 = 30)
|
|
406
|
+
energy_pattern = np.tile(first_two_columns, (1, 15))
|
|
407
|
+
|
|
408
|
+
# Convert
|
|
409
|
+
return energy_pattern
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def get_checker_board_pattern(
|
|
413
|
+
esa_lut_file: pd.DataFrame, esa_table_num: int = 0
|
|
414
|
+
) -> npt.NDArray:
|
|
415
|
+
"""
|
|
416
|
+
Generate the checkerboard pattern index map for a full cycle of SWE data.
|
|
417
|
+
|
|
418
|
+
Find indices of where full cycle data goes in the checkerboard pattern.
|
|
419
|
+
This is used to populate full cycle data in the full cycle data array.
|
|
420
|
+
This uses ESA Table index number to look up which pattern to use from
|
|
421
|
+
ESA LUT.
|
|
422
|
+
|
|
423
|
+
Parameters
|
|
424
|
+
----------
|
|
425
|
+
esa_lut_file : pathlib.Path
|
|
426
|
+
ESA LUT file.
|
|
427
|
+
esa_table_num : int
|
|
428
|
+
ESA table number. Default is 0.
|
|
429
|
+
|
|
430
|
+
Returns
|
|
431
|
+
-------
|
|
432
|
+
checkerboard_pattern : numpy.ndarray
|
|
433
|
+
(esa_step * spin_sector) array with indices of where each cycle data goes in.
|
|
434
|
+
"""
|
|
435
|
+
esa_lut_df = pd.read_csv(esa_lut_file)
|
|
436
|
+
# Get the pattern from the ESA LUT
|
|
437
|
+
esa_table_df = esa_lut_df[esa_lut_df["table_idx"] == esa_table_num]
|
|
438
|
+
|
|
439
|
+
# Now define variable to store pattern for the first two columns
|
|
440
|
+
# because that pattern is repeated in the rest of the columns.
|
|
441
|
+
first_two_columns = np.zeros((24, 2), dtype=np.int64)
|
|
442
|
+
# Get row indices of all four quarter cycles. Then minus 1 to get
|
|
443
|
+
# the row indices in 0-23 instead of 1-24.
|
|
444
|
+
cycle_row_indices = esa_table_df["v_index"].values - 1
|
|
445
|
+
esa_step = esa_table_df["esa_step"].values
|
|
446
|
+
# Reshaping the 'v_index' into 4 x 12 gets 12 repeated row_indices and
|
|
447
|
+
# energy steps of each quarter cycle
|
|
448
|
+
row_indices = cycle_row_indices.reshape(4, 12)
|
|
449
|
+
esa_step = esa_step.reshape(4, 12)
|
|
450
|
+
for i in range(4):
|
|
451
|
+
# Split each quarter's 12 steps into 2 x 6 blocks for even
|
|
452
|
+
# and odd columns
|
|
453
|
+
even_odd_column_info = row_indices[i].reshape(2, 6)
|
|
454
|
+
even_row_indices = even_odd_column_info[0]
|
|
455
|
+
odd_row_indices = even_odd_column_info[1]
|
|
456
|
+
|
|
457
|
+
# Starting ESA step value for this quarter cycle. Eg.
|
|
458
|
+
# 0, 180, 360, 540
|
|
459
|
+
start_esa_step = esa_step[i][0]
|
|
460
|
+
# Populate the first two columns of the checkerboard pattern
|
|
461
|
+
# using the start_esa_step value. Eg.
|
|
462
|
+
# Even row indices: 0, 1, 2, 3, 4, 5
|
|
463
|
+
# Odd row indices: 6, 7, 8, 9, 10, 11
|
|
464
|
+
first_two_columns[even_row_indices, 0] = np.arange(6) + start_esa_step
|
|
465
|
+
first_two_columns[odd_row_indices, 1] = np.arange(6) + start_esa_step + 6
|
|
466
|
+
|
|
467
|
+
# Repeat the first 2 column pattern 15 times across 30 columns
|
|
468
|
+
# (2 columns x 15 = 30)
|
|
469
|
+
base_pattern = np.tile(first_two_columns, (1, 15))
|
|
470
|
+
|
|
471
|
+
# Generate increment offsets: [0, 0, 12, 12, ..., 168, 168] -
|
|
472
|
+
# shape: (30,)
|
|
473
|
+
column_offsets = np.repeat(np.arange(15) * 12, 2)
|
|
474
|
+
increment_by = np.tile(column_offsets, (24, 1))
|
|
475
|
+
|
|
476
|
+
# Final checkerboard pattern with index offsets applied
|
|
477
|
+
checkerboard_pattern = base_pattern + increment_by
|
|
478
|
+
return checkerboard_pattern
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def populated_data_in_checkerboard_pattern(
|
|
482
|
+
data_ds: xr.Dataset, checkerboard_pattern: npt.NDArray
|
|
483
|
+
) -> dict:
|
|
484
|
+
"""
|
|
485
|
+
Put input data in the checkerboard pattern.
|
|
486
|
+
|
|
487
|
+
Put these data variables from l1a data into the checkerboard pattern:
|
|
488
|
+
a. science_data
|
|
489
|
+
b. acq_start_coarse
|
|
490
|
+
c. acq_start_fine
|
|
491
|
+
d. acq_duration
|
|
492
|
+
e. settle_duration
|
|
493
|
+
f. esa_steps_number (This is created in the code and not from science packet)
|
|
494
|
+
|
|
495
|
+
These last five variables are used to calculate acquisition time of each
|
|
496
|
+
count data. Acquisition time and duration are carried in l1b for level 2
|
|
497
|
+
and 3 processing.
|
|
498
|
+
|
|
499
|
+
Parameters
|
|
500
|
+
----------
|
|
501
|
+
data_ds : xarray.Dataset
|
|
502
|
+
Input data to be populated in the checkerboard pattern.
|
|
503
|
+
checkerboard_pattern : numpy.ndarray
|
|
504
|
+
Array with indices of where each cycle data goes in the checkerboard
|
|
505
|
+
pattern.
|
|
506
|
+
|
|
507
|
+
Returns
|
|
508
|
+
-------
|
|
509
|
+
var_names : dict
|
|
510
|
+
Dictionary with subset data populated in the checkerboard pattern.
|
|
511
|
+
"""
|
|
512
|
+
# Flatten with top-down and left-right order. This will be used as
|
|
513
|
+
# indices to take and put data in the checkerboard pattern.
|
|
514
|
+
checkerboard_pattern = checkerboard_pattern.flatten(order="F")
|
|
515
|
+
|
|
516
|
+
# Variables that need to be put in the checkerboard pattern
|
|
517
|
+
var_names = {
|
|
518
|
+
"science_data": np.empty(
|
|
519
|
+
(
|
|
520
|
+
0,
|
|
521
|
+
swe_constants.N_ESA_STEPS,
|
|
522
|
+
swe_constants.N_ANGLE_SECTORS,
|
|
523
|
+
swe_constants.N_CEMS,
|
|
524
|
+
)
|
|
525
|
+
),
|
|
526
|
+
"acq_start_coarse": np.empty(
|
|
527
|
+
(0, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
528
|
+
),
|
|
529
|
+
"acq_start_fine": np.empty(
|
|
530
|
+
(0, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
531
|
+
),
|
|
532
|
+
"acq_duration": np.empty(
|
|
533
|
+
(0, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
534
|
+
),
|
|
535
|
+
"settle_duration": np.empty(
|
|
536
|
+
(0, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
537
|
+
),
|
|
538
|
+
"esa_step_number": np.empty(
|
|
539
|
+
(0, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
540
|
+
),
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
for var_name in var_names:
|
|
544
|
+
# Reshape the data of input variable for easier processing.
|
|
545
|
+
if var_name == "science_data":
|
|
546
|
+
# Science data shape before reshaping is
|
|
547
|
+
# (number of packets, 180, 7)
|
|
548
|
+
# Reshape it to
|
|
549
|
+
# (number of full cycle, 720, N_CEMS)
|
|
550
|
+
data = data_ds[var_name].data.reshape(
|
|
551
|
+
-1,
|
|
552
|
+
swe_constants.N_QUARTER_CYCLES * swe_constants.N_QUARTER_CYCLE_STEPS,
|
|
553
|
+
swe_constants.N_CEMS,
|
|
554
|
+
)
|
|
555
|
+
# Apply the checkerboard pattern directly
|
|
556
|
+
populated_data = data[:, checkerboard_pattern, :]
|
|
557
|
+
# Reshape back into (n, 24, 30, 7)
|
|
558
|
+
populated_data = populated_data.reshape(
|
|
559
|
+
-1,
|
|
560
|
+
swe_constants.N_ESA_STEPS,
|
|
561
|
+
swe_constants.N_ANGLE_SECTORS,
|
|
562
|
+
swe_constants.N_CEMS,
|
|
563
|
+
order="F",
|
|
564
|
+
)
|
|
565
|
+
elif var_name == "esa_step_number":
|
|
566
|
+
# This needs to be created to capture information about esa step number
|
|
567
|
+
# from 0 to 179 for each quarter cycle. This is used to calculate data
|
|
568
|
+
# acquisition time.
|
|
569
|
+
epoch_data = data_ds["epoch"].data
|
|
570
|
+
total_cycles = epoch_data.reshape(-1, swe_constants.N_QUARTER_CYCLES).shape[
|
|
571
|
+
0
|
|
572
|
+
]
|
|
573
|
+
# Now repeat this pattern n number of cycles
|
|
574
|
+
data = np.tile(
|
|
575
|
+
np.tile(
|
|
576
|
+
np.arange(swe_constants.N_QUARTER_CYCLE_STEPS),
|
|
577
|
+
swe_constants.N_QUARTER_CYCLES,
|
|
578
|
+
),
|
|
579
|
+
(total_cycles, 1),
|
|
580
|
+
)
|
|
581
|
+
# Apply the checkerboard pattern directly
|
|
582
|
+
populated_data = data[:, checkerboard_pattern]
|
|
583
|
+
# Reshape back into (n, 24, 30)
|
|
584
|
+
populated_data = populated_data.reshape(
|
|
585
|
+
-1, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS, order="F"
|
|
586
|
+
)
|
|
587
|
+
else:
|
|
588
|
+
# Input shape is number of packets. Reshape it to
|
|
589
|
+
# (number of full cycle, 4)
|
|
590
|
+
# This is because we have the same value for each quarter
|
|
591
|
+
# cycle.
|
|
592
|
+
data = data_ds[var_name].data.reshape(-1, swe_constants.N_QUARTER_CYCLES)
|
|
593
|
+
# Repeat the data 180 times to match the checkerboard pattern
|
|
594
|
+
data = np.repeat(data, swe_constants.N_QUARTER_CYCLE_STEPS).reshape(-1, 720)
|
|
595
|
+
# Apply the checkerboard pattern directly
|
|
596
|
+
populated_data = data[:, checkerboard_pattern]
|
|
597
|
+
# Reshape back into (n, 24, 30)
|
|
598
|
+
populated_data = populated_data.reshape(
|
|
599
|
+
-1, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS, order="F"
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
# Save the populated data in the dictionary
|
|
603
|
+
var_names[var_name] = populated_data
|
|
604
|
+
|
|
605
|
+
return var_names
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def filter_full_cycle_data(
|
|
609
|
+
full_cycle_data_indices: np.ndarray, l1a_data: xr.Dataset
|
|
610
|
+
) -> xr.Dataset:
|
|
611
|
+
"""
|
|
612
|
+
Filter metadata and science of packets that makes full cycles.
|
|
613
|
+
|
|
614
|
+
Parameters
|
|
615
|
+
----------
|
|
616
|
+
full_cycle_data_indices : numpy.ndarray
|
|
617
|
+
Array with indices of full cycles.
|
|
618
|
+
l1a_data : xarray.Dataset
|
|
619
|
+
L1A dataset.
|
|
620
|
+
|
|
621
|
+
Returns
|
|
622
|
+
-------
|
|
623
|
+
l1a_data : xarray.Dataset
|
|
624
|
+
L1A dataset with filtered metadata.
|
|
625
|
+
"""
|
|
626
|
+
for key, value in l1a_data.items():
|
|
627
|
+
l1a_data[key] = value.data[full_cycle_data_indices]
|
|
628
|
+
return l1a_data
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def swe_l1b(dependencies: ProcessingInputCollection) -> xr.Dataset:
|
|
632
|
+
"""
|
|
633
|
+
SWE l1b science processing.
|
|
634
|
+
|
|
635
|
+
Parameters
|
|
636
|
+
----------
|
|
637
|
+
dependencies : ProcessingInputCollection
|
|
638
|
+
Object containing lists of dependencies that CLI dependency
|
|
639
|
+
parameter received.
|
|
640
|
+
|
|
641
|
+
Returns
|
|
642
|
+
-------
|
|
643
|
+
dataset : xarray.Dataset
|
|
644
|
+
Processed l1b data.
|
|
645
|
+
"""
|
|
646
|
+
# Read science data
|
|
647
|
+
science_files = dependencies.get_file_paths(descriptor="sci")
|
|
648
|
+
l1a_data = load_cdf(science_files[0])
|
|
649
|
+
|
|
650
|
+
total_packets = len(l1a_data["science_data"].data)
|
|
651
|
+
|
|
652
|
+
l1a_data_copy = l1a_data.copy(deep=True)
|
|
653
|
+
|
|
654
|
+
# First convert some science data to engineering units
|
|
655
|
+
# ---------------------------------------------------------------
|
|
656
|
+
apid = int(l1a_data_copy.attrs["packet_apid"])
|
|
657
|
+
|
|
658
|
+
# convert value from raw to engineering units as needed
|
|
659
|
+
conversion_table_path = dependencies.get_file_paths(descriptor="eu-conversion")[0]
|
|
37
660
|
# Look up packet name from APID
|
|
38
661
|
packet_name = next(packet for packet in SWEAPID if packet.value == apid)
|
|
39
662
|
|
|
40
663
|
# Convert raw data to engineering units as needed
|
|
41
|
-
|
|
42
|
-
|
|
664
|
+
l1a_data_copy = convert_raw_to_eu(
|
|
665
|
+
l1a_data_copy,
|
|
43
666
|
conversion_table_path=conversion_table_path,
|
|
44
667
|
packet_name=packet_name.name,
|
|
45
668
|
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
669
|
+
|
|
670
|
+
# Filter out all in-flight calibration data
|
|
671
|
+
# -----------------------------------------
|
|
672
|
+
# If ESA lookup table number is in-flight calibration
|
|
673
|
+
# mode, then skip all those data per SWE teams specification.
|
|
674
|
+
# SWE team only wants in-flight calibration data to be processed
|
|
675
|
+
# upto l1a. In-flight calibration data looks same as science data
|
|
676
|
+
# but it only measures one energy or specific energy steps during
|
|
677
|
+
# the whole duration. Right now, only index 0 in LUT collects
|
|
678
|
+
# science data.
|
|
679
|
+
science_data = l1a_data_copy["esa_table_num"].data == 0
|
|
680
|
+
# Filter out all in-flight calibration data
|
|
681
|
+
l1a_data_copy = l1a_data_copy.isel({"epoch": science_data})
|
|
682
|
+
|
|
683
|
+
full_cycle_data_indices = get_indices_of_full_cycles(
|
|
684
|
+
l1a_data_copy["quarter_cycle"].data
|
|
685
|
+
)
|
|
686
|
+
logger.debug(
|
|
687
|
+
f"Quarter cycle data before filtering: {l1a_data_copy['quarter_cycle'].data}"
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Delete Raw Science Data from l1b and onwards
|
|
691
|
+
del l1a_data_copy["raw_science_data"]
|
|
692
|
+
|
|
693
|
+
if full_cycle_data_indices.size == 0:
|
|
694
|
+
# Log that no data is found for science data
|
|
695
|
+
logger.info("No full cycle data found. Skipping.")
|
|
696
|
+
return None
|
|
697
|
+
|
|
698
|
+
# In this case, we found incomplete cycle data. We need to filter
|
|
699
|
+
# out all the data that does not make a full cycle.
|
|
700
|
+
if len(full_cycle_data_indices) != total_packets:
|
|
701
|
+
# Filter metadata and science data of packets that makes full cycles
|
|
702
|
+
full_cycle_l1a_data = l1a_data_copy.isel({"epoch": full_cycle_data_indices})
|
|
703
|
+
|
|
704
|
+
# Update total packets
|
|
705
|
+
total_packets = len(full_cycle_data_indices)
|
|
706
|
+
logger.debug(
|
|
707
|
+
"Quarters cycle after filtering: "
|
|
708
|
+
f"{full_cycle_l1a_data['quarter_cycle'].data}"
|
|
709
|
+
)
|
|
710
|
+
if len(full_cycle_data_indices) != len(
|
|
711
|
+
full_cycle_l1a_data["quarter_cycle"].data
|
|
712
|
+
):
|
|
713
|
+
raise ValueError(
|
|
714
|
+
"Error: full cycle data indices and filtered quarter cycle data size "
|
|
715
|
+
"mismatch"
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
# Main science processing steps
|
|
719
|
+
# ---------------------------------------------------------------
|
|
720
|
+
# 1. Populate data in the checkerboard pattern. This can return
|
|
721
|
+
# data in a dictionary.
|
|
722
|
+
# 2. Apply deadtime correction to each count data
|
|
723
|
+
# 3. Apply in-flight calibration to count data
|
|
724
|
+
# 4. Convert counts to rate using acquisition duration
|
|
725
|
+
|
|
726
|
+
# Read ESA lookup table
|
|
727
|
+
esa_lut_files = dependencies.get_file_paths(descriptor="esa-lut")
|
|
728
|
+
if len(esa_lut_files) > 1:
|
|
729
|
+
logger.warning(
|
|
730
|
+
f"More than one ESA lookup table file found: {esa_lut_files}. "
|
|
731
|
+
"Using the first one."
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
# Get checkerboard pattern
|
|
735
|
+
checkerboard_pattern = get_checker_board_pattern(esa_lut_files[0])
|
|
736
|
+
|
|
737
|
+
# Put data in the checkerboard pattern
|
|
738
|
+
populated_data = populated_data_in_checkerboard_pattern(
|
|
739
|
+
full_cycle_l1a_data, checkerboard_pattern
|
|
740
|
+
)
|
|
741
|
+
acq_duration = populated_data["acq_duration"]
|
|
742
|
+
acq_start_time = combine_acquisition_time(
|
|
743
|
+
populated_data["acq_start_coarse"],
|
|
744
|
+
populated_data["acq_start_fine"],
|
|
745
|
+
)
|
|
746
|
+
acq_time = calculate_data_acquisition_time(
|
|
747
|
+
acq_start_time,
|
|
748
|
+
populated_data["esa_step_number"],
|
|
749
|
+
acq_duration,
|
|
750
|
+
populated_data["settle_duration"],
|
|
751
|
+
)
|
|
752
|
+
corrected_count = deadtime_correction(populated_data["science_data"], acq_duration)
|
|
753
|
+
|
|
754
|
+
# Read in-flight calibration data
|
|
755
|
+
in_flight_cal_files = dependencies.get_file_paths(descriptor="l1b-in-flight-cal")
|
|
756
|
+
|
|
757
|
+
inflight_applied_count = apply_in_flight_calibration(
|
|
758
|
+
corrected_count, acq_time, in_flight_cal_files
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
count_rate = convert_counts_to_rate(inflight_applied_count, acq_duration)
|
|
762
|
+
|
|
763
|
+
# Store ESA energies of full cycle for L2 purposes.
|
|
764
|
+
esa_energies = get_esa_energy_pattern(esa_lut_files[0])
|
|
765
|
+
# Repeat energies to be in the same shape as the science data
|
|
766
|
+
esa_energies = np.repeat(esa_energies, total_packets // 4).reshape(
|
|
767
|
+
-1, swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS
|
|
768
|
+
)
|
|
769
|
+
# Convert voltage to electron energy in eV by apply conversion factor
|
|
770
|
+
esa_energies = esa_energies * swe_constants.ENERGY_CONVERSION_FACTOR
|
|
771
|
+
# ------------------------------------------------------------------
|
|
772
|
+
# Save data to dataset.
|
|
773
|
+
# ------------------------------------------------------------------
|
|
774
|
+
# Load CDF attrs
|
|
775
|
+
cdf_attrs = ImapCdfAttributes()
|
|
776
|
+
cdf_attrs.add_instrument_global_attrs("swe")
|
|
777
|
+
cdf_attrs.add_instrument_variable_attrs("swe", "l1b")
|
|
778
|
+
|
|
779
|
+
# One full cycle data combines four quarter cycles data.
|
|
780
|
+
# Epoch will store center of each science meansurement using
|
|
781
|
+
# third acquisition start time coarse and fine value
|
|
782
|
+
# of four quarter cycle data packets. For example, we want to
|
|
783
|
+
# get indices of 3rd quarter cycle data packet in each full cycle
|
|
784
|
+
# and use that to calculate center time of data acquisition time.
|
|
785
|
+
# Quarter cycle indices: 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, ...
|
|
786
|
+
indices_of_center_time = np.arange(2, total_packets, swe_constants.N_QUARTER_CYCLES)
|
|
787
|
+
|
|
788
|
+
center_time = combine_acquisition_time(
|
|
789
|
+
full_cycle_l1a_data["acq_start_coarse"].data[indices_of_center_time],
|
|
790
|
+
full_cycle_l1a_data["acq_start_fine"].data[indices_of_center_time],
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
epoch_time = xr.DataArray(
|
|
794
|
+
met_to_ttj2000ns(center_time),
|
|
795
|
+
name="epoch",
|
|
796
|
+
dims=["epoch"],
|
|
797
|
+
attrs=cdf_attrs.get_variable_attributes("epoch", check_schema=False),
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
esa_step = xr.DataArray(
|
|
801
|
+
np.arange(swe_constants.N_ESA_STEPS),
|
|
802
|
+
name="esa_step",
|
|
803
|
+
dims=["esa_step"],
|
|
804
|
+
attrs=cdf_attrs.get_variable_attributes("esa_step", check_schema=False),
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
# NOTE: LABL_PTR_1 should be CDF_CHAR.
|
|
808
|
+
esa_step_label = xr.DataArray(
|
|
809
|
+
esa_step.values.astype(str),
|
|
810
|
+
name="esa_step_label",
|
|
811
|
+
dims=["esa_step"],
|
|
812
|
+
attrs=cdf_attrs.get_variable_attributes("esa_step_label", check_schema=False),
|
|
813
|
+
)
|
|
814
|
+
|
|
815
|
+
spin_sector = xr.DataArray(
|
|
816
|
+
np.arange(swe_constants.N_ANGLE_SECTORS),
|
|
817
|
+
name="spin_sector",
|
|
818
|
+
dims=["spin_sector"],
|
|
819
|
+
attrs=cdf_attrs.get_variable_attributes("spin_sector", check_schema=False),
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
# NOTE: LABL_PTR_2 should be CDF_CHAR.
|
|
823
|
+
spin_sector_label = xr.DataArray(
|
|
824
|
+
spin_sector.values.astype(str),
|
|
825
|
+
name="spin_sector_label",
|
|
826
|
+
dims=["spin_sector"],
|
|
827
|
+
attrs=cdf_attrs.get_variable_attributes(
|
|
828
|
+
"spin_sector_label", check_schema=False
|
|
829
|
+
),
|
|
830
|
+
)
|
|
831
|
+
|
|
832
|
+
cycle = xr.DataArray(
|
|
833
|
+
np.arange(swe_constants.N_QUARTER_CYCLES),
|
|
834
|
+
name="cycle",
|
|
835
|
+
dims=["cycle"],
|
|
836
|
+
attrs=cdf_attrs.get_variable_attributes("cycle", check_schema=False),
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
cycle_label = xr.DataArray(
|
|
840
|
+
cycle.values.astype(str),
|
|
841
|
+
name="cycle_label",
|
|
842
|
+
dims=["cycle"],
|
|
843
|
+
attrs=cdf_attrs.get_variable_attributes("cycle_label", check_schema=False),
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
cem_id = xr.DataArray(
|
|
847
|
+
np.arange(swe_constants.N_CEMS, dtype=np.int8),
|
|
848
|
+
name="cem_id",
|
|
849
|
+
dims=["cem_id"],
|
|
850
|
+
attrs=cdf_attrs.get_variable_attributes("cem_id", check_schema=False),
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
# NOTE: LABL_PTR_3 should be CDF_CHAR.
|
|
854
|
+
cem_id_label = xr.DataArray(
|
|
855
|
+
cem_id.values.astype(str),
|
|
856
|
+
name="cem_id_label",
|
|
857
|
+
dims=["cem_id"],
|
|
858
|
+
attrs=cdf_attrs.get_variable_attributes("cem_id_label", check_schema=False),
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
# Add science data and it's associated metadata into dataset.
|
|
862
|
+
# SCIENCE_DATA has array of this shape:
|
|
863
|
+
# (n, 24, 30, 7)
|
|
864
|
+
# n = total number of full cycles
|
|
865
|
+
# 24 rows --> 24 esa voltage measurements
|
|
866
|
+
# 30 columns --> 30 spin angle measurements
|
|
867
|
+
# 7 elements --> 7 CEMs counts
|
|
868
|
+
#
|
|
869
|
+
# The metadata array will need to have this shape:
|
|
870
|
+
# (n, 4)
|
|
871
|
+
# n = total number of full cycles
|
|
872
|
+
# 4 rows --> metadata for each full cycle. Each element of 4 maps to
|
|
873
|
+
# metadata of one quarter cycle.
|
|
874
|
+
|
|
875
|
+
# Create the dataset
|
|
876
|
+
dataset = xr.Dataset(
|
|
877
|
+
coords={
|
|
878
|
+
"epoch": epoch_time,
|
|
879
|
+
"esa_step": esa_step,
|
|
880
|
+
"spin_sector": spin_sector,
|
|
881
|
+
"cem_id": cem_id,
|
|
882
|
+
"cycle": cycle,
|
|
883
|
+
"esa_step_label": esa_step_label,
|
|
884
|
+
"spin_sector_label": spin_sector_label,
|
|
885
|
+
"cem_id_label": cem_id_label,
|
|
886
|
+
"cycle_label": cycle_label,
|
|
887
|
+
},
|
|
888
|
+
attrs=cdf_attrs.get_global_attributes("imap_swe_l1b_sci"),
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
dataset["science_data"] = xr.DataArray(
|
|
892
|
+
count_rate,
|
|
893
|
+
dims=["epoch", "esa_step", "spin_sector", "cem_id"],
|
|
894
|
+
attrs=cdf_attrs.get_variable_attributes("science_data"),
|
|
895
|
+
)
|
|
896
|
+
dataset["acquisition_time"] = xr.DataArray(
|
|
897
|
+
acq_time,
|
|
898
|
+
dims=["epoch", "esa_step", "spin_sector"],
|
|
899
|
+
attrs=cdf_attrs.get_variable_attributes("acquisition_time"),
|
|
900
|
+
)
|
|
901
|
+
dataset["acq_duration"] = xr.DataArray(
|
|
902
|
+
acq_duration,
|
|
903
|
+
dims=["epoch", "esa_step", "spin_sector"],
|
|
904
|
+
attrs=cdf_attrs.get_variable_attributes("acq_duration"),
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
dataset["esa_energy"] = xr.DataArray(
|
|
908
|
+
esa_energies,
|
|
909
|
+
dims=["epoch", "esa_step", "spin_sector"],
|
|
910
|
+
attrs=cdf_attrs.get_variable_attributes("esa_energy"),
|
|
911
|
+
)
|
|
912
|
+
|
|
913
|
+
# create xarray dataarray for each data field
|
|
914
|
+
for key, value in full_cycle_l1a_data.items():
|
|
915
|
+
if key in ["science_data", "acq_duration"]:
|
|
916
|
+
continue
|
|
917
|
+
varname = key.lower()
|
|
918
|
+
dataset[varname] = xr.DataArray(
|
|
919
|
+
value.data.reshape(-1, swe_constants.N_QUARTER_CYCLES),
|
|
920
|
+
dims=["epoch", "cycle"],
|
|
921
|
+
attrs=cdf_attrs.get_variable_attributes(varname),
|
|
922
|
+
)
|
|
923
|
+
|
|
924
|
+
logger.info("SWE L1b science processing completed")
|
|
925
|
+
return [dataset]
|