imap-processing 0.12.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 +1 -0
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/ccsds_data.py +1 -2
- imap_processing/ccsds/excel_to_xtce.py +1 -2
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -12
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +569 -0
- imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +5 -5
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +20 -1
- 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_mag_global_cdf_attrs.yaml +15 -0
- imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +16 -0
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +178 -5
- imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -19
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +8 -48
- imap_processing/cdf/utils.py +41 -33
- imap_processing/cli.py +463 -234
- imap_processing/codice/codice_l1a.py +260 -47
- imap_processing/codice/codice_l1b.py +51 -152
- imap_processing/codice/constants.py +38 -1
- imap_processing/ena_maps/ena_maps.py +658 -65
- imap_processing/ena_maps/utils/coordinates.py +1 -1
- imap_processing/ena_maps/utils/spatial_utils.py +10 -5
- 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 +31 -12
- imap_processing/hi/l1b/hi_l1b.py +80 -43
- imap_processing/hi/l1c/hi_l1c.py +12 -16
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
- imap_processing/hit/hit_utils.py +93 -35
- imap_processing/hit/l0/decom_hit.py +3 -1
- imap_processing/hit/l1a/hit_l1a.py +30 -25
- imap_processing/hit/l1b/constants.py +6 -2
- imap_processing/hit/l1b/hit_l1b.py +279 -318
- imap_processing/hit/l2/constants.py +37 -0
- imap_processing/hit/l2/hit_l2.py +373 -264
- imap_processing/ialirt/l0/parse_mag.py +138 -10
- imap_processing/ialirt/l0/process_swapi.py +69 -0
- imap_processing/ialirt/l0/process_swe.py +318 -22
- imap_processing/ialirt/packet_definitions/ialirt.xml +216 -212
- 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_swapi.xml +14 -14
- imap_processing/ialirt/utils/grouping.py +1 -1
- imap_processing/idex/idex_constants.py +9 -1
- imap_processing/idex/idex_l0.py +22 -8
- imap_processing/idex/idex_l1a.py +75 -44
- imap_processing/idex/idex_l1b.py +9 -8
- imap_processing/idex/idex_l2a.py +79 -45
- imap_processing/idex/idex_l2b.py +120 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -39
- imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
- imap_processing/lo/l0/lo_science.py +1 -2
- imap_processing/lo/l1a/lo_l1a.py +1 -4
- imap_processing/lo/l1b/lo_l1b.py +527 -6
- imap_processing/lo/l1b/tof_conversions.py +11 -0
- imap_processing/lo/l1c/lo_l1c.py +1 -4
- imap_processing/mag/constants.py +43 -0
- imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
- imap_processing/mag/l1a/mag_l1a.py +2 -9
- imap_processing/mag/l1a/mag_l1a_data.py +10 -10
- imap_processing/mag/l1b/mag_l1b.py +84 -17
- imap_processing/mag/l1c/interpolation_methods.py +180 -3
- imap_processing/mag/l1c/mag_l1c.py +236 -70
- imap_processing/mag/l2/mag_l2.py +140 -0
- imap_processing/mag/l2/mag_l2_data.py +288 -0
- imap_processing/spacecraft/quaternions.py +1 -3
- imap_processing/spice/geometry.py +3 -3
- imap_processing/spice/kernels.py +0 -276
- imap_processing/spice/pointing_frame.py +257 -0
- imap_processing/spice/repoint.py +48 -19
- imap_processing/spice/spin.py +38 -33
- imap_processing/spice/time.py +24 -0
- imap_processing/swapi/l1/swapi_l1.py +16 -12
- imap_processing/swapi/l2/swapi_l2.py +116 -4
- imap_processing/swapi/swapi_utils.py +32 -0
- imap_processing/swe/l1a/swe_l1a.py +2 -9
- imap_processing/swe/l1a/swe_science.py +8 -11
- imap_processing/swe/l1b/swe_l1b.py +898 -23
- imap_processing/swe/l2/swe_l2.py +21 -77
- imap_processing/swe/utils/swe_constants.py +1 -0
- imap_processing/tests/ccsds/test_excel_to_xtce.py +1 -1
- 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-pha_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/test_codice_l1a.py +20 -11
- imap_processing/tests/codice/test_codice_l1b.py +6 -7
- imap_processing/tests/conftest.py +78 -22
- imap_processing/tests/ena_maps/test_ena_maps.py +462 -33
- imap_processing/tests/ena_maps/test_spatial_utils.py +1 -1
- 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/test_hi_l1b.py +10 -12
- imap_processing/tests/hi/test_hi_l1c.py +27 -24
- imap_processing/tests/hi/test_l1a.py +7 -9
- imap_processing/tests/hi/test_science_direct_event.py +2 -2
- imap_processing/tests/hit/helpers/l1_validation.py +44 -43
- imap_processing/tests/hit/test_decom_hit.py +1 -1
- imap_processing/tests/hit/test_hit_l1a.py +9 -9
- imap_processing/tests/hit/test_hit_l1b.py +172 -217
- imap_processing/tests/hit/test_hit_l2.py +380 -118
- imap_processing/tests/hit/test_hit_utils.py +122 -55
- imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
- imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
- imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
- imap_processing/tests/ialirt/unit/test_grouping.py +2 -2
- imap_processing/tests/ialirt/unit/test_parse_mag.py +71 -16
- 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 +115 -7
- imap_processing/tests/idex/conftest.py +72 -7
- 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_idex_l0.py +33 -11
- imap_processing/tests/idex/test_idex_l1a.py +50 -23
- imap_processing/tests/idex/test_idex_l1b.py +104 -25
- imap_processing/tests/idex/test_idex_l2a.py +48 -32
- imap_processing/tests/idex/test_idex_l2b.py +93 -0
- imap_processing/tests/lo/test_lo_l1a.py +3 -3
- imap_processing/tests/lo/test_lo_l1b.py +371 -6
- imap_processing/tests/lo/test_lo_l1c.py +1 -1
- imap_processing/tests/lo/test_lo_science.py +6 -7
- imap_processing/tests/lo/test_star_sensor.py +1 -1
- imap_processing/tests/mag/conftest.py +58 -9
- imap_processing/tests/mag/test_mag_decom.py +4 -3
- imap_processing/tests/mag/test_mag_l1a.py +13 -7
- imap_processing/tests/mag/test_mag_l1b.py +9 -9
- imap_processing/tests/mag/test_mag_l1c.py +151 -47
- imap_processing/tests/mag/test_mag_l2.py +130 -0
- imap_processing/tests/mag/test_mag_validation.py +144 -7
- 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_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/test_quaternions.py +1 -1
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +4 -4
- imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
- imap_processing/tests/spice/test_geometry.py +3 -3
- 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 +20 -10
- 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 +7 -9
- imap_processing/tests/swapi/test_swapi_l2.py +180 -8
- 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 +6 -6
- 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 +82 -102
- imap_processing/tests/test_cli.py +171 -88
- imap_processing/tests/test_utils.py +2 -1
- imap_processing/tests/ultra/data/mock_data.py +49 -21
- imap_processing/tests/ultra/unit/conftest.py +53 -70
- imap_processing/tests/ultra/unit/test_badtimes.py +2 -4
- imap_processing/tests/ultra/unit/test_cullingmask.py +4 -6
- imap_processing/tests/ultra/unit/test_de.py +3 -10
- imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +15 -16
- 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 +3 -4
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +84 -307
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +30 -12
- imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +4 -1
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +163 -29
- imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +32 -43
- 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 +21 -34
- 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 -243
- imap_processing/ultra/l1b/badtimes.py +1 -4
- imap_processing/ultra/l1b/cullingmask.py +2 -6
- imap_processing/ultra/l1b/de.py +62 -47
- imap_processing/ultra/l1b/extendedspin.py +8 -4
- imap_processing/ultra/l1b/lookup_utils.py +72 -9
- imap_processing/ultra/l1b/ultra_l1b.py +3 -8
- imap_processing/ultra/l1b/ultra_l1b_culling.py +4 -4
- imap_processing/ultra/l1b/ultra_l1b_extended.py +236 -78
- imap_processing/ultra/l1c/histogram.py +2 -6
- imap_processing/ultra/l1c/spacecraft_pset.py +2 -4
- imap_processing/ultra/l1c/ultra_l1c.py +1 -5
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +107 -60
- 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 +13 -12
- imap_processing/utils.py +1 -1
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +3 -2
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/RECORD +264 -225
- imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
- imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +0 -6
- imap_processing/mag/l1b/__init__.py +0 -0
- imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
- imap_processing/swe/l1b/swe_l1b_science.py +0 -699
- imap_processing/tests/swe/test_swe_l1b_science.py +0 -103
- imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
- imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
- /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/461971383-404.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971384-405.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971385-406.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971386-407.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971387-408.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971388-409.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971389-410.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971390-411.bin +0 -0
- /imap_processing/tests/ialirt/{test_data → data}/l0/461971391-412.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/tests/ialirt/{test_data → data}/l0/sample_decoded_i-alirt_data.csv +0 -0
- /imap_processing/tests/mag/validation/{imap_calibration_mag_20240229_v01.cdf → calibration/imap_mag_l1b-calibration_20240229_v001.cdf} +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-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Functions to support I-ALiRT MAG packet parsing."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from typing import Union
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
6
7
|
import xarray as xr
|
|
@@ -13,6 +14,13 @@ from imap_processing.ialirt.l0.mag_l0_ialirt_data import (
|
|
|
13
14
|
)
|
|
14
15
|
from imap_processing.ialirt.utils.grouping import find_groups
|
|
15
16
|
from imap_processing.ialirt.utils.time import calculate_time
|
|
17
|
+
from imap_processing.mag.l1a.mag_l1a_data import TimeTuple
|
|
18
|
+
from imap_processing.mag.l1b.mag_l1b import (
|
|
19
|
+
calibrate_vector,
|
|
20
|
+
retrieve_matrix_from_l1b_calibration,
|
|
21
|
+
shift_time,
|
|
22
|
+
)
|
|
23
|
+
from imap_processing.spice.time import met_to_ttj2000ns
|
|
16
24
|
|
|
17
25
|
logger = logging.getLogger(__name__)
|
|
18
26
|
|
|
@@ -132,7 +140,13 @@ def extract_magnetic_vectors(science_values: xr.DataArray) -> dict:
|
|
|
132
140
|
return vectors
|
|
133
141
|
|
|
134
142
|
|
|
135
|
-
def get_time(
|
|
143
|
+
def get_time(
|
|
144
|
+
grouped_data: xr.Dataset,
|
|
145
|
+
group: int,
|
|
146
|
+
pkt_counter: xr.DataArray,
|
|
147
|
+
time_shift_mago: xr.DataArray,
|
|
148
|
+
time_shift_magi: xr.DataArray,
|
|
149
|
+
) -> dict:
|
|
136
150
|
"""
|
|
137
151
|
Get the time for the grouped data.
|
|
138
152
|
|
|
@@ -144,12 +158,22 @@ def get_time(grouped_data: xr.Dataset, group: int, pkt_counter: xr.DataArray) ->
|
|
|
144
158
|
Group number.
|
|
145
159
|
pkt_counter : xr.DataArray
|
|
146
160
|
Packet counter.
|
|
161
|
+
time_shift_mago : xr.DataArray
|
|
162
|
+
Time shift value mago.
|
|
163
|
+
time_shift_magi : xr.DataArray
|
|
164
|
+
Time shift value magi.
|
|
147
165
|
|
|
148
166
|
Returns
|
|
149
167
|
-------
|
|
150
168
|
time_data : dict
|
|
151
169
|
Coarse and fine time for Primary and Secondary Sensors.
|
|
170
|
+
|
|
171
|
+
Notes
|
|
172
|
+
-----
|
|
173
|
+
Packet id 0 is course and fine time for the primary sensor PRI.
|
|
174
|
+
Packet id 2 is the course time for the secondary sensor SEC.
|
|
152
175
|
"""
|
|
176
|
+
# Get the coarse and fine time for the primary and secondary sensors.
|
|
153
177
|
pri_coarsetm = grouped_data["mag_acq_tm_coarse"][
|
|
154
178
|
(grouped_data["group"] == group).values
|
|
155
179
|
][pkt_counter == 0]
|
|
@@ -166,17 +190,102 @@ def get_time(grouped_data: xr.Dataset, group: int, pkt_counter: xr.DataArray) ->
|
|
|
166
190
|
(grouped_data["group"] == group).values
|
|
167
191
|
][pkt_counter == 2]
|
|
168
192
|
|
|
169
|
-
time_data = {
|
|
170
|
-
"pri_coarsetm": int(pri_coarsetm),
|
|
171
|
-
"pri_fintm": int(pri_fintm),
|
|
172
|
-
"sec_coarsetm": int(sec_coarsetm),
|
|
173
|
-
"sec_fintm": int(sec_fintm),
|
|
193
|
+
time_data: dict[str, Union[int, float]] = {
|
|
194
|
+
"pri_coarsetm": int(pri_coarsetm.item()),
|
|
195
|
+
"pri_fintm": int(pri_fintm.item()),
|
|
196
|
+
"sec_coarsetm": int(sec_coarsetm.item()),
|
|
197
|
+
"sec_fintm": int(sec_fintm.item()),
|
|
174
198
|
}
|
|
175
199
|
|
|
200
|
+
primary_time = TimeTuple(int(pri_coarsetm.item()), int(pri_fintm.item()))
|
|
201
|
+
secondary_time = TimeTuple(int(sec_coarsetm.item()), int(sec_fintm.item()))
|
|
202
|
+
time_data_pri_met = primary_time.to_seconds()
|
|
203
|
+
time_data_primary_ttj2000ns = met_to_ttj2000ns(time_data_pri_met)
|
|
204
|
+
time_data["primary_epoch"] = shift_time(
|
|
205
|
+
time_data_primary_ttj2000ns, time_shift_mago
|
|
206
|
+
)
|
|
207
|
+
time_data_sec_met = secondary_time.to_seconds()
|
|
208
|
+
time_data_secondary_ttj2000ns = met_to_ttj2000ns(time_data_sec_met)
|
|
209
|
+
time_data["secondary_epoch"] = shift_time(
|
|
210
|
+
time_data_secondary_ttj2000ns, time_shift_magi
|
|
211
|
+
)
|
|
212
|
+
|
|
176
213
|
return time_data
|
|
177
214
|
|
|
178
215
|
|
|
179
|
-
def
|
|
216
|
+
def calculate_l1b(
|
|
217
|
+
grouped_data: xr.Dataset,
|
|
218
|
+
group: int,
|
|
219
|
+
pkt_counter: xr.DataArray,
|
|
220
|
+
science_data: dict,
|
|
221
|
+
status_data: dict,
|
|
222
|
+
calibration_dataset: xr.Dataset,
|
|
223
|
+
) -> tuple[np.ndarray, np.ndarray, dict]:
|
|
224
|
+
"""
|
|
225
|
+
Calculate equivalent of l1b data product.
|
|
226
|
+
|
|
227
|
+
Parameters
|
|
228
|
+
----------
|
|
229
|
+
grouped_data : xr.Dataset
|
|
230
|
+
Grouped data.
|
|
231
|
+
group : int
|
|
232
|
+
Group number.
|
|
233
|
+
pkt_counter : xr.DataArray
|
|
234
|
+
Packet counter.
|
|
235
|
+
science_data : dict
|
|
236
|
+
Science data.
|
|
237
|
+
status_data : dict
|
|
238
|
+
Status data.
|
|
239
|
+
calibration_dataset : xr.Dataset
|
|
240
|
+
Calibration dataset.
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
updated_vector_mago : numpy.ndarray
|
|
245
|
+
Calibrated mago vector.
|
|
246
|
+
updated_vector_magi : numpy.ndarray
|
|
247
|
+
Calibrated magi vector.
|
|
248
|
+
time_data : dict
|
|
249
|
+
Time data.
|
|
250
|
+
"""
|
|
251
|
+
calibration_matrix_mago, time_shift_mago = retrieve_matrix_from_l1b_calibration(
|
|
252
|
+
calibration_dataset, is_mago=True
|
|
253
|
+
)
|
|
254
|
+
calibration_matrix_magi, time_shift_magi = retrieve_matrix_from_l1b_calibration(
|
|
255
|
+
calibration_dataset, is_mago=False
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Get time values for each group.
|
|
259
|
+
time_data = get_time(
|
|
260
|
+
grouped_data, group, pkt_counter, time_shift_mago, time_shift_magi
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
input_vector_mago = np.array(
|
|
264
|
+
[
|
|
265
|
+
science_data["pri_x"],
|
|
266
|
+
science_data["pri_y"],
|
|
267
|
+
science_data["pri_z"],
|
|
268
|
+
status_data["fob_range"],
|
|
269
|
+
]
|
|
270
|
+
)
|
|
271
|
+
input_vector_magi = np.array(
|
|
272
|
+
[
|
|
273
|
+
science_data["sec_x"],
|
|
274
|
+
science_data["sec_y"],
|
|
275
|
+
science_data["sec_z"],
|
|
276
|
+
status_data["fib_range"],
|
|
277
|
+
]
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
updated_vector_mago = calibrate_vector(input_vector_mago, calibration_matrix_mago)
|
|
281
|
+
updated_vector_magi = calibrate_vector(input_vector_magi, calibration_matrix_magi)
|
|
282
|
+
|
|
283
|
+
return updated_vector_mago, updated_vector_magi, time_data
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def process_packet(
|
|
287
|
+
accumulated_data: xr.Dataset, calibration_dataset: xr.Dataset
|
|
288
|
+
) -> list[dict]:
|
|
180
289
|
"""
|
|
181
290
|
Parse the MAG packets.
|
|
182
291
|
|
|
@@ -184,6 +293,8 @@ def parse_packet(accumulated_data: xr.Dataset) -> list[dict]:
|
|
|
184
293
|
----------
|
|
185
294
|
accumulated_data : xr.Dataset
|
|
186
295
|
Packets dataset accumulated over 1 min.
|
|
296
|
+
calibration_dataset : xr.Dataset
|
|
297
|
+
Calibration dataset.
|
|
187
298
|
|
|
188
299
|
Returns
|
|
189
300
|
-------
|
|
@@ -237,9 +348,26 @@ def parse_packet(accumulated_data: xr.Dataset) -> list[dict]:
|
|
|
237
348
|
(grouped_data["group"] == group).values
|
|
238
349
|
]
|
|
239
350
|
science_data = extract_magnetic_vectors(science_values)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
351
|
+
updated_vector_mago, updated_vector_magi, time_data = calculate_l1b(
|
|
352
|
+
grouped_data,
|
|
353
|
+
group,
|
|
354
|
+
pkt_counter,
|
|
355
|
+
science_data,
|
|
356
|
+
status_data,
|
|
357
|
+
calibration_dataset,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Note: primary = MAGo, secondary = MAGi.
|
|
361
|
+
science_data.update(
|
|
362
|
+
{
|
|
363
|
+
"calibrated_pri_x": updated_vector_mago[0],
|
|
364
|
+
"calibrated_pri_y": updated_vector_mago[1],
|
|
365
|
+
"calibrated_pri_z": updated_vector_mago[2],
|
|
366
|
+
"calibrated_sec_x": updated_vector_magi[0],
|
|
367
|
+
"calibrated_sec_y": updated_vector_magi[1],
|
|
368
|
+
"calibrated_sec_z": updated_vector_magi[2],
|
|
369
|
+
}
|
|
370
|
+
)
|
|
243
371
|
|
|
244
372
|
mag_data.append({**status_data, **science_data, **time_data})
|
|
245
373
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Functions to support I-ALiRT SWAPI processing."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import xarray as xr
|
|
7
|
+
from xarray import DataArray
|
|
8
|
+
|
|
9
|
+
from imap_processing.ialirt.utils.grouping import find_groups
|
|
10
|
+
|
|
11
|
+
# from imap_processing.swapi.l1.swapi_l1 import process_sweep_data
|
|
12
|
+
# from imap_processing.swapi.l2.swapi_l2 import TIME_PER_BIN
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def process_swapi_ialirt(unpacked_data: xr.Dataset) -> dict[str, DataArray]:
|
|
18
|
+
"""
|
|
19
|
+
Extract I-ALiRT variables and calculate coincidence count rate.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
unpacked_data : xr.Dataset
|
|
24
|
+
SWAPI I-ALiRT data that has been parsed from the spacecraft packet.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
swapi_data : dict
|
|
29
|
+
Dictionary containing all data variables for SWAPI I-ALiRT product.
|
|
30
|
+
"""
|
|
31
|
+
logger.info("Processing SWAPI.")
|
|
32
|
+
|
|
33
|
+
sci_dataset = unpacked_data.sortby("epoch", ascending=True)
|
|
34
|
+
|
|
35
|
+
grouped_dataset = find_groups(sci_dataset, (0, 11), "swapi_seq_number", "swapi_acq")
|
|
36
|
+
|
|
37
|
+
for group in np.unique(grouped_dataset["group"]):
|
|
38
|
+
# Sequence values for the group should be 0-11 with no duplicates.
|
|
39
|
+
seq_values = grouped_dataset["swapi_seq_number"][
|
|
40
|
+
(grouped_dataset["group"] == group)
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
# Ensure no duplicates and all values from 0 to 11 are present
|
|
44
|
+
if not np.array_equal(seq_values.astype(int), np.arange(12)):
|
|
45
|
+
logger.info(
|
|
46
|
+
f"SWAPI group {group} does not contain all sequence values from 0 to "
|
|
47
|
+
f"11 without duplicates."
|
|
48
|
+
)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
total_packets = len(grouped_dataset["swapi_seq_number"].data)
|
|
52
|
+
|
|
53
|
+
# It takes 12 sequence data to make one full SWAPI sweep
|
|
54
|
+
total_sequence = 12
|
|
55
|
+
total_full_sweeps = total_packets // total_sequence
|
|
56
|
+
|
|
57
|
+
met_values = grouped_dataset["swapi_shcoarse"].data.reshape(total_full_sweeps, 12)[
|
|
58
|
+
:, 0
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# raw_coin_count = process_sweep_data(grouped_dataset, "coin_cnt")
|
|
62
|
+
# raw_coin_rate = raw_coin_count / TIME_PER_BIN
|
|
63
|
+
|
|
64
|
+
swapi_data = {
|
|
65
|
+
"met": met_values
|
|
66
|
+
# more variables to go here
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return swapi_data
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Functions to support I-ALiRT SWE
|
|
1
|
+
"""Functions to support I-ALiRT SWE processing."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ from numpy.typing import NDArray
|
|
|
9
9
|
|
|
10
10
|
from imap_processing.ialirt.utils.grouping import find_groups
|
|
11
11
|
from imap_processing.swe.l1a.swe_science import decompressed_counts
|
|
12
|
-
from imap_processing.swe.l1b.
|
|
12
|
+
from imap_processing.swe.l1b.swe_l1b import (
|
|
13
13
|
deadtime_correction,
|
|
14
14
|
read_in_flight_cal_data,
|
|
15
15
|
)
|
|
@@ -22,6 +22,16 @@ from imap_processing.swe.utils.swe_utils import combine_acquisition_time
|
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
+
# Energy bin lookup table (indexed by quarter cycle)
|
|
26
|
+
ENERGY_BINS = np.array(
|
|
27
|
+
[
|
|
28
|
+
[1, 5, 7, 3], # 0 to 14 (Q1)
|
|
29
|
+
[2, 6, 4, 0], # 15 to 29 (Q2)
|
|
30
|
+
[3, 7, 5, 1], # 30 to 44 (Q3)
|
|
31
|
+
[0, 4, 6, 2], # 45 to 59 (Q4)
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
|
|
25
35
|
|
|
26
36
|
def decompress_counts(raw_counts: NDArray) -> NDArray:
|
|
27
37
|
"""
|
|
@@ -36,6 +46,10 @@ def decompress_counts(raw_counts: NDArray) -> NDArray:
|
|
|
36
46
|
-------
|
|
37
47
|
counts : np.ndarray
|
|
38
48
|
Array of decompressed counts with the same shape as raw_counts.
|
|
49
|
+
|
|
50
|
+
Notes
|
|
51
|
+
-----
|
|
52
|
+
CEM is channel electron multiplier.
|
|
39
53
|
"""
|
|
40
54
|
decompression_table = np.array([decompressed_counts(i) for i in range(256)])
|
|
41
55
|
|
|
@@ -100,19 +114,9 @@ def prepare_raw_counts(grouped: xr.Dataset, cem_number: int = N_CEMS) -> NDArray
|
|
|
100
114
|
)
|
|
101
115
|
phi_bins = phi_to_bin(phi_values).astype(int) # Get phi bin indices
|
|
102
116
|
|
|
103
|
-
# Energy bin lookup table (indexed by quarter cycle)
|
|
104
|
-
energy_bins = np.array(
|
|
105
|
-
[
|
|
106
|
-
[1, 5, 7, 3], # 0-14 (first quarter cycle)
|
|
107
|
-
[2, 6, 4, 0], # 15-29 (second quarter cycle)
|
|
108
|
-
[3, 7, 5, 1], # 30-44 (third quarter cycle)
|
|
109
|
-
[0, 4, 6, 2], # 45-59 (fourth quarter cycle)
|
|
110
|
-
]
|
|
111
|
-
)
|
|
112
|
-
|
|
113
117
|
# The first 15 seconds is the first quarter cycle, etc.
|
|
114
118
|
quarter_cycles = np.floor(grouped["swe_seq"] / 15).astype(int)
|
|
115
|
-
e_bins =
|
|
119
|
+
e_bins = ENERGY_BINS[quarter_cycles]
|
|
116
120
|
|
|
117
121
|
# Populate raw_counts
|
|
118
122
|
for cem in range(1, cem_number + 1): # 7 CEMs
|
|
@@ -168,21 +172,283 @@ def normalize_counts(counts: NDArray, latest_cal: pd.Series) -> NDArray:
|
|
|
168
172
|
latest_cal = latest_cal.to_numpy()
|
|
169
173
|
|
|
170
174
|
# Norm counts where counts are non-negative
|
|
171
|
-
# TODO: confirm fv is counts with Ruth
|
|
172
175
|
norm_counts = counts * (latest_cal / GEOMETRIC_FACTORS)[:, np.newaxis]
|
|
173
176
|
norm_counts[norm_counts < 0] = 0
|
|
174
177
|
|
|
175
178
|
return norm_counts
|
|
176
179
|
|
|
177
180
|
|
|
178
|
-
def
|
|
181
|
+
def find_bin_offsets(
|
|
182
|
+
peak_bins: NDArray, offsets: tuple[int, int]
|
|
183
|
+
) -> tuple[NDArray, NDArray]:
|
|
184
|
+
"""
|
|
185
|
+
Find the bins with offsets from the peak bins.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
peak_bins : np.ndarray
|
|
190
|
+
Bins that correspond to the maximum counts at each energy.
|
|
191
|
+
offsets : tuple[int, int]
|
|
192
|
+
Offset values for the bins.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
bin_0 : np.ndarray
|
|
197
|
+
First bin used for the average.
|
|
198
|
+
bin_1 : np.ndarray
|
|
199
|
+
Second bin used for the average.
|
|
200
|
+
"""
|
|
201
|
+
# Azimuth has 30 values.
|
|
202
|
+
# Therefore, anything greater than 30 should be wrapped around.
|
|
203
|
+
bin_0, bin_1 = (peak_bins + offsets[0]) % 30, (peak_bins + offsets[1]) % 30
|
|
204
|
+
|
|
205
|
+
return bin_0, bin_1
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def average_counts(
|
|
209
|
+
peak_bins: NDArray, summed_half_cycle: NDArray, offsets: tuple[int, int]
|
|
210
|
+
) -> NDArray:
|
|
211
|
+
"""
|
|
212
|
+
Get the counts value for the offset bins at each energy level and average them.
|
|
213
|
+
|
|
214
|
+
Parameters
|
|
215
|
+
----------
|
|
216
|
+
peak_bins : np.ndarray
|
|
217
|
+
Bins that corresponds to the maximum counts at each energy.
|
|
218
|
+
summed_half_cycle : np.ndarray
|
|
219
|
+
Counts summed over the 7 CEM detectors.
|
|
220
|
+
offsets : tuple
|
|
221
|
+
Offset values for the bins.
|
|
222
|
+
Offsets +6 and +8 correspond to +90 degrees.
|
|
223
|
+
Offsets +14 and +16 correspond to 180 degrees.
|
|
224
|
+
Offsets -6 and -8 correspond to -90 degrees.
|
|
225
|
+
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
avg_counts : np.ndarray
|
|
229
|
+
Average counts of offset bin.
|
|
230
|
+
"""
|
|
231
|
+
# Find the bins with offsets from the peak bins.
|
|
232
|
+
bin_0, bin_1 = find_bin_offsets(peak_bins, offsets)
|
|
233
|
+
|
|
234
|
+
# Get the counts value for the offset bins at each energy level and average them.
|
|
235
|
+
row_idx = np.arange(len(peak_bins))
|
|
236
|
+
avg_counts = (
|
|
237
|
+
summed_half_cycle[row_idx, bin_0] + summed_half_cycle[row_idx, bin_1]
|
|
238
|
+
) / 2
|
|
239
|
+
|
|
240
|
+
return avg_counts
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def find_min_counts(
|
|
244
|
+
summed_half_cycle: NDArray,
|
|
245
|
+
) -> tuple[NDArray, NDArray, tuple[NDArray, NDArray, NDArray]]:
|
|
246
|
+
"""
|
|
247
|
+
Find min counts (cmin), defined as the minimum of counts_180 and counts_90.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
summed_half_cycle : np.ndarray
|
|
252
|
+
Counts summed over the 7 CEM detectors.
|
|
253
|
+
|
|
254
|
+
Returns
|
|
255
|
+
-------
|
|
256
|
+
cpeak : np.ndarray
|
|
257
|
+
Maximum counts for each energy level.
|
|
258
|
+
cmin : np.ndarray
|
|
259
|
+
Minimum of counts_neg_90, counts_90, counts_180.
|
|
260
|
+
counts : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
261
|
+
Counts at +/- 90 and 180 degrees from peak.
|
|
262
|
+
"""
|
|
263
|
+
# Find the maximum counts for each energy level
|
|
264
|
+
cpeak = np.max(summed_half_cycle, axis=1)
|
|
265
|
+
|
|
266
|
+
# Find the bin that corresponds to the maximum counts at each energy
|
|
267
|
+
peak_bin = np.argmax(summed_half_cycle, axis=1)
|
|
268
|
+
|
|
269
|
+
# Find the counts in each offset bins.
|
|
270
|
+
# Offsets +6 and +8 correspond to +90 degrees.
|
|
271
|
+
counts_90 = average_counts(peak_bin, summed_half_cycle, (6, 8))
|
|
272
|
+
|
|
273
|
+
# Find the counts in each offset bins.
|
|
274
|
+
# Offsets +14 and +16 correspond to 180 degrees.
|
|
275
|
+
counts_180 = average_counts(peak_bin, summed_half_cycle, (14, 16))
|
|
276
|
+
|
|
277
|
+
# Find the counts in each offset bins.
|
|
278
|
+
# Offsets -6 and -8 correspond to -90 degrees.
|
|
279
|
+
counts_neg_90 = average_counts(peak_bin, summed_half_cycle, (-6, -8))
|
|
280
|
+
|
|
281
|
+
counts_stacked = np.hstack(
|
|
282
|
+
[
|
|
283
|
+
counts_90[:, np.newaxis],
|
|
284
|
+
counts_180[:, np.newaxis],
|
|
285
|
+
counts_neg_90[:, np.newaxis],
|
|
286
|
+
]
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Find the minimum value for each energy level
|
|
290
|
+
cmin = np.min(counts_stacked, axis=1)
|
|
291
|
+
|
|
292
|
+
return cpeak, cmin, (counts_neg_90, counts_90, counts_180)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def determine_streaming(
|
|
296
|
+
numerator_1: NDArray,
|
|
297
|
+
numerator_2: NDArray,
|
|
298
|
+
denominator: NDArray,
|
|
299
|
+
threshold: float = 1.75,
|
|
300
|
+
) -> NDArray:
|
|
301
|
+
"""
|
|
302
|
+
Determine if any energy level satisfies the bidirectional streaming condition.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
numerator_1 : np.ndarray
|
|
307
|
+
For the first streaming check : cpeak.
|
|
308
|
+
For the second streaming check : ccem_1.
|
|
309
|
+
numerator_2 : np.ndarray
|
|
310
|
+
For the first streaming check : counts_180.
|
|
311
|
+
For the second streaming check : ccem_7.
|
|
312
|
+
denominator : np.ndarray
|
|
313
|
+
For both streaming checks: cmin.
|
|
314
|
+
threshold : float (optional)
|
|
315
|
+
Threshold value for the streaming condition.
|
|
316
|
+
|
|
317
|
+
Returns
|
|
318
|
+
-------
|
|
319
|
+
streaming_flag : np.ndarray
|
|
320
|
+
Array of 1s and 0s indicating if the condition is satisfied.
|
|
321
|
+
"""
|
|
322
|
+
ratio_1 = numerator_1 / denominator
|
|
323
|
+
ratio_2 = numerator_2 / denominator
|
|
324
|
+
|
|
325
|
+
return ((ratio_1 > threshold) & (ratio_2 > threshold)).astype(int)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def compute_bidirectional(
|
|
329
|
+
streaming_first_half: NDArray,
|
|
330
|
+
streaming_second_half: NDArray,
|
|
331
|
+
min_esa_steps: int = 3,
|
|
332
|
+
) -> tuple[int, int]:
|
|
333
|
+
"""
|
|
334
|
+
Compute the Bidirectional Electron parameter (BDE).
|
|
335
|
+
|
|
336
|
+
Parameters
|
|
337
|
+
----------
|
|
338
|
+
streaming_first_half : np.ndarray
|
|
339
|
+
Array of 1s and 0s indicating bidirectional streaming for first half-cycle.
|
|
340
|
+
streaming_second_half : np.ndarray
|
|
341
|
+
Array of 1s and 0s indicating bidirectional streaming for second half-cycle.
|
|
342
|
+
min_esa_steps : int (optional)
|
|
343
|
+
Minimum number of ESA steps for bidirectional streaming.
|
|
344
|
+
If either of the half cycles has bidirectional streaming for
|
|
345
|
+
3/8 energies then bde = 1.
|
|
346
|
+
|
|
347
|
+
Returns
|
|
348
|
+
-------
|
|
349
|
+
bde : tuple
|
|
350
|
+
Indicator for counter-streaming.
|
|
351
|
+
"""
|
|
352
|
+
count_first: int = int(np.sum(streaming_first_half))
|
|
353
|
+
count_second: int = int(np.sum(streaming_second_half))
|
|
354
|
+
|
|
355
|
+
return int(count_first >= min_esa_steps), int(count_second >= min_esa_steps)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def azimuthal_check_counterstreaming(
|
|
359
|
+
summed_first_half: NDArray, summed_second_half: NDArray
|
|
360
|
+
) -> tuple[int, int]:
|
|
361
|
+
"""
|
|
362
|
+
Check if counterstreaming is observed in azimuthal angle direction.
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
summed_first_half : np.ndarray
|
|
367
|
+
Counts summed over the 7 CEM detectors for first half-cycle.
|
|
368
|
+
summed_second_half : np.ndarray
|
|
369
|
+
Counts summed over the 7 CEM detectors for second half-cycle.
|
|
370
|
+
|
|
371
|
+
Returns
|
|
372
|
+
-------
|
|
373
|
+
bde : tuple
|
|
374
|
+
Indicator for counter-streaming.
|
|
375
|
+
"""
|
|
376
|
+
# Find peaks, cmin, counts (-90, 90, 180)
|
|
377
|
+
cpeak_first_half, cmin_first_half, counts_first_half = find_min_counts(
|
|
378
|
+
summed_first_half
|
|
379
|
+
)
|
|
380
|
+
cpeak_second_half, cmin_second_half, counts_second_half = find_min_counts(
|
|
381
|
+
summed_second_half
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# First search for counter-streaming
|
|
385
|
+
streaming_first_half = determine_streaming(
|
|
386
|
+
cpeak_first_half, counts_first_half[2], cmin_first_half
|
|
387
|
+
)
|
|
388
|
+
streaming_second_half = determine_streaming(
|
|
389
|
+
cpeak_second_half, counts_second_half[2], cmin_second_half
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
# If either of the half cycles has bidirectional streaming
|
|
393
|
+
# for 3/8 energies then bde = 1
|
|
394
|
+
bde_first_search = compute_bidirectional(
|
|
395
|
+
streaming_first_half, streaming_second_half
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
return bde_first_search
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def polar_check_counterstreaming(
|
|
402
|
+
summed_first_half: NDArray, summed_second_half: NDArray
|
|
403
|
+
) -> tuple[int, int]:
|
|
179
404
|
"""
|
|
180
|
-
|
|
405
|
+
Check if counterstreaming is observed in the polar angle direction.
|
|
406
|
+
|
|
407
|
+
Parameters
|
|
408
|
+
----------
|
|
409
|
+
summed_first_half : np.ndarray
|
|
410
|
+
Counts summed over the azimuth for first half-cycle.
|
|
411
|
+
summed_second_half : np.ndarray
|
|
412
|
+
Counts summed over the azimuth for second half-cycle.
|
|
413
|
+
|
|
414
|
+
Returns
|
|
415
|
+
-------
|
|
416
|
+
bde : tuple
|
|
417
|
+
Indicator for counter-streaming.
|
|
418
|
+
"""
|
|
419
|
+
# Cmin is the average of the counts in CEMs 3, 4, and 5
|
|
420
|
+
cmin_first_half = summed_first_half[:, 2:5].mean(axis=1)
|
|
421
|
+
cmin_second_half = summed_second_half[:, 2:5].mean(axis=1)
|
|
422
|
+
|
|
423
|
+
# Determine if streaming is observed.
|
|
424
|
+
# Note: Bidirectional electron streaming at a given ESA step
|
|
425
|
+
# is identified if both c_cem1/cmin and c_cem7/cmin are > 1.75.
|
|
426
|
+
streaming_first_half = determine_streaming(
|
|
427
|
+
summed_first_half[:, 0], summed_first_half[:, 6], cmin_first_half
|
|
428
|
+
)
|
|
429
|
+
streaming_second_half = determine_streaming(
|
|
430
|
+
summed_second_half[:, 0], summed_second_half[:, 6], cmin_second_half
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# If either of the half cycles has bidirectional streaming
|
|
434
|
+
# for 3/8 energies then bde = 1
|
|
435
|
+
bde_second_search = compute_bidirectional(
|
|
436
|
+
streaming_first_half, streaming_second_half
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
return bde_second_search
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def process_swe(accumulated_data: xr.Dataset, in_flight_cal_files: list) -> list[dict]:
|
|
443
|
+
"""
|
|
444
|
+
Create L1 data dictionary.
|
|
181
445
|
|
|
182
446
|
Parameters
|
|
183
447
|
----------
|
|
184
448
|
accumulated_data : xr.Dataset
|
|
185
449
|
Packets dataset accumulated over 1 min.
|
|
450
|
+
in_flight_cal_files : list
|
|
451
|
+
List of path to the in-flight calibration files.
|
|
186
452
|
|
|
187
453
|
Returns
|
|
188
454
|
-------
|
|
@@ -209,7 +475,7 @@ def process_swe(accumulated_data: xr.Dataset) -> list[dict]:
|
|
|
209
475
|
|
|
210
476
|
# Ensure no duplicates and all values from 0 to 59 are present
|
|
211
477
|
if not np.array_equal(seq_values, np.arange(60)):
|
|
212
|
-
logger.
|
|
478
|
+
logger.info(
|
|
213
479
|
f"Group {group} does not contain all values from 0 to "
|
|
214
480
|
f"59 without duplicates."
|
|
215
481
|
)
|
|
@@ -237,16 +503,46 @@ def process_swe(accumulated_data: xr.Dataset) -> list[dict]:
|
|
|
237
503
|
corrected_second_half = deadtime_correction(counts_second_half, 80 * 10**3)
|
|
238
504
|
|
|
239
505
|
# Grab the latest calibration factor
|
|
240
|
-
in_flight_cal_df = read_in_flight_cal_data()
|
|
506
|
+
in_flight_cal_df = read_in_flight_cal_data(in_flight_cal_files)
|
|
241
507
|
latest_cal = in_flight_cal_df.sort_values("met_time").iloc[-1][1::]
|
|
242
508
|
|
|
243
509
|
normalized_first_half = normalize_counts(corrected_first_half, latest_cal)
|
|
244
510
|
normalized_second_half = normalize_counts(corrected_second_half, latest_cal)
|
|
245
511
|
|
|
246
512
|
# Sum over the 7 detectors
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
513
|
+
summed_first_half_cem = np.sum(normalized_first_half, axis=1)
|
|
514
|
+
summed_second_half_cem = np.sum(normalized_second_half, axis=1)
|
|
515
|
+
bde_first_search = azimuthal_check_counterstreaming(
|
|
516
|
+
summed_first_half_cem, summed_second_half_cem
|
|
517
|
+
)
|
|
518
|
+
# Sum over azimuth.
|
|
519
|
+
summed_first_half_az = np.sum(normalized_first_half, axis=2)
|
|
520
|
+
summed_second_half_az = np.sum(normalized_second_half, axis=2)
|
|
521
|
+
bde_second_search = polar_check_counterstreaming(
|
|
522
|
+
summed_first_half_az, summed_second_half_az
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
# BDE value
|
|
526
|
+
bde_first_half = max(bde_first_search[0], bde_second_search[0])
|
|
527
|
+
bde_second_half = max(bde_first_search[1], bde_second_search[1])
|
|
528
|
+
|
|
529
|
+
# For normalized counts each ESA step is summed
|
|
530
|
+
# over both azimuthal and polar angles
|
|
531
|
+
# Sum over CEMs (axis=1) and azimuths (axis=2)
|
|
532
|
+
summed_first = normalized_first_half.sum(axis=(1, 2))
|
|
533
|
+
summed_second = normalized_second_half.sum(axis=(1, 2))
|
|
534
|
+
times = np.unique(grouped["time_seconds"].values)
|
|
535
|
+
|
|
536
|
+
swe_data.append(
|
|
537
|
+
{
|
|
538
|
+
# Select times corresponding to energy level.
|
|
539
|
+
"met_first_half_cycle": times[[1, 0] * 4],
|
|
540
|
+
"met_second_half_cycle": times[[3, 2] * 4],
|
|
541
|
+
"normalized_counts_first_half_cycle": summed_first,
|
|
542
|
+
"normalized_counts_second_half_cycle": summed_second,
|
|
543
|
+
"bde_first_half_cycle": np.full(summed_first.shape, bde_first_half),
|
|
544
|
+
"bde_second_half_cycle": np.full(summed_second.shape, bde_second_half),
|
|
545
|
+
}
|
|
546
|
+
)
|
|
251
547
|
|
|
252
548
|
return swe_data
|