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,699 +0,0 @@
|
|
|
1
|
-
"""Contains code to perform SWE L1b science processing."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import numpy.typing as npt
|
|
7
|
-
import pandas as pd
|
|
8
|
-
import xarray as xr
|
|
9
|
-
|
|
10
|
-
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
11
|
-
from imap_processing.spice.time import met_to_ttj2000ns
|
|
12
|
-
from imap_processing.swe.utils import swe_constants
|
|
13
|
-
from imap_processing.swe.utils.swe_utils import (
|
|
14
|
-
calculate_data_acquisition_time,
|
|
15
|
-
combine_acquisition_time,
|
|
16
|
-
read_lookup_table,
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def get_esa_dataframe(esa_table_number: int) -> pd.DataFrame:
|
|
23
|
-
"""
|
|
24
|
-
Read lookup table from file.
|
|
25
|
-
|
|
26
|
-
Parameters
|
|
27
|
-
----------
|
|
28
|
-
esa_table_number : int
|
|
29
|
-
ESA table index number.
|
|
30
|
-
|
|
31
|
-
Returns
|
|
32
|
-
-------
|
|
33
|
-
esa_steps : pandas.DataFrame
|
|
34
|
-
ESA table_number and its associated values.
|
|
35
|
-
"""
|
|
36
|
-
if esa_table_number not in [0, 1]:
|
|
37
|
-
raise ValueError(f"Unknown ESA table number {esa_table_number}")
|
|
38
|
-
|
|
39
|
-
# Get the lookup table DataFrame
|
|
40
|
-
lookup_table = read_lookup_table()
|
|
41
|
-
|
|
42
|
-
esa_steps = lookup_table.loc[lookup_table["table_index"] == esa_table_number]
|
|
43
|
-
return esa_steps
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def deadtime_correction(counts: np.ndarray, acq_duration: int) -> npt.NDArray:
|
|
47
|
-
"""
|
|
48
|
-
Calculate deadtime correction.
|
|
49
|
-
|
|
50
|
-
Deadtime correction is a technique used in various fields, including
|
|
51
|
-
nuclear physics, radiation detection, and particle counting, to compensate
|
|
52
|
-
for the effects of the time period during which a detector is not able to
|
|
53
|
-
record new events or measurements after detecting a previous event.
|
|
54
|
-
This "deadtime" is essentially the time during which the detector is
|
|
55
|
-
recovering from the previous detection and is unable to detect new events.
|
|
56
|
-
|
|
57
|
-
In particle detectors, there is a finite time required for the detector to
|
|
58
|
-
reset or recover after detecting a particle. During this deadtime, any
|
|
59
|
-
subsequent particles that may have arrived go undetected. As a result,
|
|
60
|
-
the recorded count rate appears to be lower than the actual count rate.
|
|
61
|
-
|
|
62
|
-
Deadtime correction involves mathematically adjusting the measured count
|
|
63
|
-
rates to compensate for this deadtime effect. This correction is crucial
|
|
64
|
-
when dealing with high-intensity sources or particle fluxes, as the deadtime
|
|
65
|
-
can significantly affect the accuracy of the measurements.
|
|
66
|
-
|
|
67
|
-
Deadtime correction is important to ensure accurate measurements and data
|
|
68
|
-
analysis in fields where event detection rates are high and where every
|
|
69
|
-
detected event is critical for understanding physical processes.
|
|
70
|
-
|
|
71
|
-
Parameters
|
|
72
|
-
----------
|
|
73
|
-
counts : numpy.ndarray
|
|
74
|
-
Counts data before deadtime corrections.
|
|
75
|
-
acq_duration : int
|
|
76
|
-
This is ACQ_DURATION from science packet. acq_duration is in microseconds.
|
|
77
|
-
|
|
78
|
-
Returns
|
|
79
|
-
-------
|
|
80
|
-
corrected_count : numpy.ndarray
|
|
81
|
-
Corrected counts.
|
|
82
|
-
"""
|
|
83
|
-
# deadtime is 360 ns
|
|
84
|
-
deadtime = 360e-9
|
|
85
|
-
correct = 1.0 - (deadtime * (counts / (acq_duration * 1e-6)))
|
|
86
|
-
correct = np.maximum(0.1, correct)
|
|
87
|
-
corrected_count = np.divide(counts, correct)
|
|
88
|
-
return corrected_count.astype(np.float64)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def convert_counts_to_rate(data: np.ndarray, acq_duration: np.ndarray) -> npt.NDArray:
|
|
92
|
-
"""
|
|
93
|
-
Convert counts to rate using sampling time.
|
|
94
|
-
|
|
95
|
-
acq_duration is ACQ_DURATION from science packet.
|
|
96
|
-
|
|
97
|
-
Parameters
|
|
98
|
-
----------
|
|
99
|
-
data : numpy.ndarray
|
|
100
|
-
Counts data.
|
|
101
|
-
acq_duration : numpy.ndarray
|
|
102
|
-
Acquisition duration. acq_duration is in microseconds.
|
|
103
|
-
|
|
104
|
-
Returns
|
|
105
|
-
-------
|
|
106
|
-
numpy.ndarray
|
|
107
|
-
Count rates array in seconds.
|
|
108
|
-
"""
|
|
109
|
-
# convert microseconds to seconds
|
|
110
|
-
acq_duration_sec = acq_duration * 1e-6
|
|
111
|
-
count_rate = data / acq_duration_sec
|
|
112
|
-
return count_rate.astype(np.float64)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def read_in_flight_cal_data() -> pd.DataFrame:
|
|
116
|
-
"""
|
|
117
|
-
Read in-flight calibration data.
|
|
118
|
-
|
|
119
|
-
In-flight calibration data file will contain rows where each line
|
|
120
|
-
has 8 numbers, with the first being a time stamp in MET, and the next
|
|
121
|
-
7 being the factors for the 7 detectors.
|
|
122
|
-
|
|
123
|
-
This file will be updated weekly with new calibration data. In other
|
|
124
|
-
words, one line of data will be added each week to the existing file.
|
|
125
|
-
File will be in CSV format. Processing won't be kicked off until there
|
|
126
|
-
is in-flight calibration data that covers science data.
|
|
127
|
-
|
|
128
|
-
TODO: decide filename convention given this information. This function
|
|
129
|
-
is a placeholder for reading in the calibration data until we decide on
|
|
130
|
-
how to read calibration data through dependencies list.
|
|
131
|
-
|
|
132
|
-
Returns
|
|
133
|
-
-------
|
|
134
|
-
in_flight_cal_df : pandas.DataFrame
|
|
135
|
-
DataFrame with in-flight calibration data.
|
|
136
|
-
"""
|
|
137
|
-
# TODO: Read in in-flight calibration file.
|
|
138
|
-
|
|
139
|
-
# Define the column headers
|
|
140
|
-
columns = ["met_time", "cem1", "cem2", "cem3", "cem4", "cem5", "cem6", "cem7"]
|
|
141
|
-
|
|
142
|
-
# Create an empty DataFrame with the specified columns
|
|
143
|
-
empty_df = pd.DataFrame(columns=columns)
|
|
144
|
-
return empty_df
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def calculate_calibration_factor(
|
|
148
|
-
acquisition_times: np.ndarray, cal_times: np.ndarray, cal_data: np.ndarray
|
|
149
|
-
) -> npt.NDArray:
|
|
150
|
-
"""
|
|
151
|
-
Calculate calibration factor using linear interpolation.
|
|
152
|
-
|
|
153
|
-
Steps to calculate calibration factor:
|
|
154
|
-
1. Convert input time to match time format in the calibration data file.
|
|
155
|
-
Both times should be in S/C MET time.
|
|
156
|
-
2. Find the nearest in time calibration data point.
|
|
157
|
-
3. Linear interpolate between those two nearest time and get factor for
|
|
158
|
-
input time.
|
|
159
|
-
|
|
160
|
-
Parameters
|
|
161
|
-
----------
|
|
162
|
-
acquisition_times : numpy.ndarray
|
|
163
|
-
Data points to interpolate. Shape is (N_ESA_STEPS, N_ANGLE_SECTORS).
|
|
164
|
-
cal_times : numpy.ndarray
|
|
165
|
-
X-coordinates data points. Calibration times. Shape is (n,).
|
|
166
|
-
cal_data : numpy.ndarray
|
|
167
|
-
Y-coordinates data points. Calibration data of corresponding cal_times.
|
|
168
|
-
Shape is (n, N_CEMS).
|
|
169
|
-
|
|
170
|
-
Returns
|
|
171
|
-
-------
|
|
172
|
-
calibration_factor : numpy.ndarray
|
|
173
|
-
Calibration factor for each CEM detector. Shape is
|
|
174
|
-
(N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS) where last 7 dimension
|
|
175
|
-
contains calibration factor for each CEM detector.
|
|
176
|
-
"""
|
|
177
|
-
# Raise error if there is no pre or post time in cal_times. SWE does not
|
|
178
|
-
# want to extrapolate calibration data.
|
|
179
|
-
if (
|
|
180
|
-
acquisition_times.min() < cal_times.min()
|
|
181
|
-
or acquisition_times.max() > cal_times.max()
|
|
182
|
-
):
|
|
183
|
-
error_msg = (
|
|
184
|
-
f"Acquisition min/max times: {acquisition_times.min()} to "
|
|
185
|
-
f"{acquisition_times.max()}. "
|
|
186
|
-
f"Calibration min/max times: {cal_times.min()} to {cal_times.max()}. "
|
|
187
|
-
"Acquisition times should be within calibration time range."
|
|
188
|
-
)
|
|
189
|
-
raise ValueError(error_msg)
|
|
190
|
-
|
|
191
|
-
# This line of code finds the indices of acquisition_times in cal_times where
|
|
192
|
-
# acquisition_times should be inserted to maintain order. As a result, it finds
|
|
193
|
-
# its nearest pre and post time from cal_times.
|
|
194
|
-
input_time_indices = np.searchsorted(cal_times, acquisition_times)
|
|
195
|
-
|
|
196
|
-
# Assign to a variable for better readability
|
|
197
|
-
x = acquisition_times
|
|
198
|
-
xp = cal_times
|
|
199
|
-
fp = cal_data
|
|
200
|
-
|
|
201
|
-
# Given this situation which will be the case for SWE data
|
|
202
|
-
# where data will fall in between two calibration times and
|
|
203
|
-
# not be exactly equal to any calibration time,
|
|
204
|
-
# >>> a = [1, 2, 3]
|
|
205
|
-
# >>> np.searchsorted(a, [2.5])
|
|
206
|
-
# array([2])
|
|
207
|
-
# we need to use (j - 1) to get pre time indices. (j-1) is
|
|
208
|
-
# pre time indices and j is post time indices.
|
|
209
|
-
j = input_time_indices
|
|
210
|
-
w = (x - xp[j - 1]) / (xp[j] - xp[j - 1])
|
|
211
|
-
return fp[j - 1] + w[..., None] * (fp[j] - fp[j - 1])
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def apply_in_flight_calibration(
|
|
215
|
-
corrected_counts: np.ndarray, acquisition_time: np.ndarray
|
|
216
|
-
) -> npt.NDArray:
|
|
217
|
-
"""
|
|
218
|
-
Apply in flight calibration to full cycle data.
|
|
219
|
-
|
|
220
|
-
These factors are used to account for changes in gain with time.
|
|
221
|
-
|
|
222
|
-
They are derived from the weekly electron calibration data.
|
|
223
|
-
|
|
224
|
-
Parameters
|
|
225
|
-
----------
|
|
226
|
-
corrected_counts : numpy.ndarray
|
|
227
|
-
Corrected count of full cycle data. Data shape is
|
|
228
|
-
(N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS).
|
|
229
|
-
acquisition_time : numpy.ndarray
|
|
230
|
-
Acquisition time of full cycle data. Data shape is
|
|
231
|
-
(N_ESA_STEPS, N_ANGLE_SECTORS).
|
|
232
|
-
|
|
233
|
-
Returns
|
|
234
|
-
-------
|
|
235
|
-
corrected_counts : numpy.ndarray
|
|
236
|
-
Corrected count of full cycle data after applying in-flight calibration.
|
|
237
|
-
Array shape is (N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS).
|
|
238
|
-
"""
|
|
239
|
-
# Read in in-flight calibration data
|
|
240
|
-
in_flight_cal_df = read_in_flight_cal_data()
|
|
241
|
-
# calculate calibration factor.
|
|
242
|
-
# return shape of calculate_calibration_factor is
|
|
243
|
-
# (N_ESA_STEPS, N_ANGLE_SECTORS, N_CEMS) where
|
|
244
|
-
# last 7 dimension contains calibration factor for each CEM detector.
|
|
245
|
-
cal_factor = calculate_calibration_factor(
|
|
246
|
-
acquisition_time,
|
|
247
|
-
in_flight_cal_df["met_time"].values,
|
|
248
|
-
in_flight_cal_df.iloc[:, 1:].values,
|
|
249
|
-
)
|
|
250
|
-
# Apply to full cycle data
|
|
251
|
-
return corrected_counts.astype(np.float64) * cal_factor
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def populate_full_cycle_data(
|
|
255
|
-
l1a_data: xr.Dataset, packet_index: int, esa_table_num: int
|
|
256
|
-
) -> npt.NDArray:
|
|
257
|
-
"""
|
|
258
|
-
Populate full cycle data array using esa lookup table and l1a_data.
|
|
259
|
-
|
|
260
|
-
Parameters
|
|
261
|
-
----------
|
|
262
|
-
l1a_data : xarray.Dataset
|
|
263
|
-
L1a data with full cycle data only.
|
|
264
|
-
packet_index : int
|
|
265
|
-
Index of current packet in the whole packet list.
|
|
266
|
-
esa_table_num : int
|
|
267
|
-
ESA lookup table number.
|
|
268
|
-
|
|
269
|
-
Returns
|
|
270
|
-
-------
|
|
271
|
-
full_cycle_ds : xarray.Dataset
|
|
272
|
-
Full cycle data and its acquisition times.
|
|
273
|
-
"""
|
|
274
|
-
esa_lookup_table = get_esa_dataframe(esa_table_num)
|
|
275
|
-
|
|
276
|
-
# If esa lookup table number is 0, then populate using esa lookup table data
|
|
277
|
-
# with information that esa step ramps up in even column and ramps down
|
|
278
|
-
# in odd column every six steps.
|
|
279
|
-
if esa_table_num == 0:
|
|
280
|
-
# create new full cycle data array
|
|
281
|
-
full_cycle_data = np.zeros(
|
|
282
|
-
(
|
|
283
|
-
swe_constants.N_ESA_STEPS,
|
|
284
|
-
swe_constants.N_ANGLE_SECTORS,
|
|
285
|
-
swe_constants.N_CEMS,
|
|
286
|
-
)
|
|
287
|
-
)
|
|
288
|
-
# SWE needs to store acquisition time of each count data point
|
|
289
|
-
# to use in level 2 processing to calculate
|
|
290
|
-
# spin phase. This is done below by using information from
|
|
291
|
-
# science packet.
|
|
292
|
-
acquisition_times = np.zeros(
|
|
293
|
-
(swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
# Store acquisition duration for later calculation in this function
|
|
297
|
-
acq_duration_arr = np.zeros(
|
|
298
|
-
(swe_constants.N_ESA_STEPS, swe_constants.N_ANGLE_SECTORS)
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
# Initialize esa_step_number and column_index.
|
|
302
|
-
# esa_step_number goes from 0 to 719 range where
|
|
303
|
-
# 720 came from 24 x 30. full_cycle_data array has
|
|
304
|
-
# (N_ESA_STEPS, N_ANGLE_SECTORS) dimension.
|
|
305
|
-
esa_step_number = 0
|
|
306
|
-
# column_index goes from 0 to 29 range where
|
|
307
|
-
# 30 came from 30 column in full_cycle_data array
|
|
308
|
-
column_index = -1
|
|
309
|
-
|
|
310
|
-
# Go through four quarter cycle data packets
|
|
311
|
-
for index in range(swe_constants.N_QUARTER_CYCLES):
|
|
312
|
-
decompressed_counts = l1a_data["science_data"].data[packet_index + index]
|
|
313
|
-
# Do deadtime correction
|
|
314
|
-
acq_duration = l1a_data["acq_duration"].data[packet_index + index]
|
|
315
|
-
settle_duration = l1a_data["settle_duration"].data[packet_index + index]
|
|
316
|
-
corrected_counts = deadtime_correction(decompressed_counts, acq_duration)
|
|
317
|
-
|
|
318
|
-
# Each quarter cycle data should have same acquisition start time coarse
|
|
319
|
-
# and fine value. We will use that as base time to calculate each
|
|
320
|
-
# acquisition time for each count data.
|
|
321
|
-
base_quarter_cycle_acq_time = combine_acquisition_time(
|
|
322
|
-
l1a_data["acq_start_coarse"].data[packet_index + index],
|
|
323
|
-
l1a_data["acq_start_fine"].data[packet_index + index],
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
# Go through each quarter cycle's 180 ESA measurements
|
|
327
|
-
# and put counts rate in full cycle data array
|
|
328
|
-
for step in range(180):
|
|
329
|
-
# Get esa voltage value from esa lookup table and
|
|
330
|
-
# use that to get row index in full data array
|
|
331
|
-
esa_voltage_value = esa_lookup_table.loc[esa_step_number]["esa_v"]
|
|
332
|
-
esa_voltage_row_index = swe_constants.ESA_VOLTAGE_ROW_INDEX_DICT[
|
|
333
|
-
esa_voltage_value
|
|
334
|
-
]
|
|
335
|
-
|
|
336
|
-
# every six steps, increment column index
|
|
337
|
-
if esa_step_number % 6 == 0:
|
|
338
|
-
column_index += 1
|
|
339
|
-
# Put counts rate in full cycle data array
|
|
340
|
-
full_cycle_data[esa_voltage_row_index][column_index] = corrected_counts[
|
|
341
|
-
step
|
|
342
|
-
]
|
|
343
|
-
# Acquisition time (in seconds) of each count data point
|
|
344
|
-
acquisition_times[esa_voltage_row_index][column_index] = (
|
|
345
|
-
calculate_data_acquisition_time(
|
|
346
|
-
base_quarter_cycle_acq_time,
|
|
347
|
-
esa_step_number,
|
|
348
|
-
acq_duration,
|
|
349
|
-
settle_duration,
|
|
350
|
-
)
|
|
351
|
-
)
|
|
352
|
-
# Store acquisition duration for later calculation
|
|
353
|
-
acq_duration_arr[esa_voltage_row_index][column_index] = acq_duration
|
|
354
|
-
esa_step_number += 1
|
|
355
|
-
|
|
356
|
-
# reset column index for next quarter cycle
|
|
357
|
-
column_index = -1
|
|
358
|
-
# TODO: Apply in flight calibration to full cycle data
|
|
359
|
-
|
|
360
|
-
# NOTE: We may get more lookup table with different setup when we get real
|
|
361
|
-
# data. But for now, we are advice to continue with current setup and can
|
|
362
|
-
# add/change it when we get real data.
|
|
363
|
-
|
|
364
|
-
# Apply calibration based on in-flight calibration.
|
|
365
|
-
calibrated_counts = apply_in_flight_calibration(full_cycle_data, acquisition_times)
|
|
366
|
-
|
|
367
|
-
# Convert counts to rate
|
|
368
|
-
counts_rate = convert_counts_to_rate(
|
|
369
|
-
calibrated_counts, acq_duration_arr[:, :, np.newaxis]
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
# Store full cycle data in xr.Dataset for later use.
|
|
373
|
-
full_cycle_ds = xr.Dataset(
|
|
374
|
-
{
|
|
375
|
-
"full_cycle_data": (["esa_step", "spin_sector", "cem_id"], counts_rate),
|
|
376
|
-
"acquisition_time": (["esa_step", "spin_sector"], acquisition_times),
|
|
377
|
-
"acq_duration": (["esa_step", "spin_sector"], acq_duration_arr),
|
|
378
|
-
}
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
return full_cycle_ds
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
def find_cycle_starts(cycles: np.ndarray) -> npt.NDArray:
|
|
385
|
-
"""
|
|
386
|
-
Find index of where new cycle started.
|
|
387
|
-
|
|
388
|
-
Brandon Stone helped developed this algorithm.
|
|
389
|
-
|
|
390
|
-
Parameters
|
|
391
|
-
----------
|
|
392
|
-
cycles : numpy.ndarray
|
|
393
|
-
Array that contains quarter cycle information.
|
|
394
|
-
|
|
395
|
-
Returns
|
|
396
|
-
-------
|
|
397
|
-
first_quarter_indices : numpy.ndarray
|
|
398
|
-
Array of indices of start cycle.
|
|
399
|
-
"""
|
|
400
|
-
if cycles.size < swe_constants.N_QUARTER_CYCLES:
|
|
401
|
-
return np.array([], np.int64)
|
|
402
|
-
|
|
403
|
-
# calculate difference between consecutive cycles
|
|
404
|
-
diff = cycles[1:] - cycles[:-1]
|
|
405
|
-
|
|
406
|
-
# This uses sliding window to find index where cycle starts.
|
|
407
|
-
# This is what this below code line is doing:
|
|
408
|
-
# [1 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0] # Is cycle zero?
|
|
409
|
-
# [1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1] # Next diff is one?
|
|
410
|
-
# [1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1 0] # Next diff is one?
|
|
411
|
-
# [0 1 1 1 0 1 0 0 1 0 1 1 1 0 1 0 0] # Next diff is one?
|
|
412
|
-
#
|
|
413
|
-
# [0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0] # And all?
|
|
414
|
-
ione = diff == 1
|
|
415
|
-
valid = (cycles == 0)[:-3] & ione[:-2] & ione[1:-1] & ione[2:]
|
|
416
|
-
first_quarter_indices = np.where(valid)[0]
|
|
417
|
-
return first_quarter_indices
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
def get_indices_of_full_cycles(quarter_cycle: np.ndarray) -> npt.NDArray:
|
|
421
|
-
"""
|
|
422
|
-
Get indices of full cycles.
|
|
423
|
-
|
|
424
|
-
Parameters
|
|
425
|
-
----------
|
|
426
|
-
quarter_cycle : numpy.ndarray
|
|
427
|
-
Array that contains quarter cycles information.
|
|
428
|
-
|
|
429
|
-
Returns
|
|
430
|
-
-------
|
|
431
|
-
full_cycles_indices : numpy.ndarray
|
|
432
|
-
1D array with indices of full cycle data.
|
|
433
|
-
"""
|
|
434
|
-
indices_of_start = find_cycle_starts(quarter_cycle)
|
|
435
|
-
# indices_of_start[..., None] creates array of shape(n, 1).
|
|
436
|
-
# Eg. [[3], [8]]
|
|
437
|
-
# np.arange(4)[None, ...] creates array of shape(1, 4)
|
|
438
|
-
# Eg. [[0, 1, 2, 3]]
|
|
439
|
-
# then we add both of them together to get an array of shape(n, 4)
|
|
440
|
-
# Eg. [[3, 4, 5, 6], [8, 9, 10, 11]]
|
|
441
|
-
full_cycles_indices = (
|
|
442
|
-
indices_of_start[..., None]
|
|
443
|
-
+ np.arange(swe_constants.N_QUARTER_CYCLES)[None, ...]
|
|
444
|
-
)
|
|
445
|
-
return full_cycles_indices.reshape(-1)
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
def filter_full_cycle_data(
|
|
449
|
-
full_cycle_data_indices: np.ndarray, l1a_data: xr.Dataset
|
|
450
|
-
) -> xr.Dataset:
|
|
451
|
-
"""
|
|
452
|
-
Filter metadata and science of packets that makes full cycles.
|
|
453
|
-
|
|
454
|
-
Parameters
|
|
455
|
-
----------
|
|
456
|
-
full_cycle_data_indices : numpy.ndarray
|
|
457
|
-
Array with indices of full cycles.
|
|
458
|
-
l1a_data : xarray.Dataset
|
|
459
|
-
L1A dataset.
|
|
460
|
-
|
|
461
|
-
Returns
|
|
462
|
-
-------
|
|
463
|
-
l1a_data : xarray.Dataset
|
|
464
|
-
L1A dataset with filtered metadata.
|
|
465
|
-
"""
|
|
466
|
-
for key, value in l1a_data.items():
|
|
467
|
-
l1a_data[key] = value.data[full_cycle_data_indices]
|
|
468
|
-
return l1a_data
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
def swe_l1b_science(l1a_data: xr.Dataset, data_version: str) -> xr.Dataset:
|
|
472
|
-
"""
|
|
473
|
-
SWE l1b science processing.
|
|
474
|
-
|
|
475
|
-
Parameters
|
|
476
|
-
----------
|
|
477
|
-
l1a_data : xarray.Dataset
|
|
478
|
-
Input data.
|
|
479
|
-
data_version : str
|
|
480
|
-
Version of the data product being created.
|
|
481
|
-
|
|
482
|
-
Returns
|
|
483
|
-
-------
|
|
484
|
-
dataset : xarray.Dataset
|
|
485
|
-
Processed l1b data.
|
|
486
|
-
"""
|
|
487
|
-
total_packets = len(l1a_data["science_data"].data)
|
|
488
|
-
|
|
489
|
-
# Array to store list of table populated with data
|
|
490
|
-
# of full cycles
|
|
491
|
-
full_cycle_science_data = []
|
|
492
|
-
# These two are carried in l1b for level 2 and 3 processing
|
|
493
|
-
full_cycle_acq_times = []
|
|
494
|
-
full_cycle_acq_duration = []
|
|
495
|
-
packet_index = 0
|
|
496
|
-
l1a_data_copy = l1a_data.copy(deep=True)
|
|
497
|
-
|
|
498
|
-
full_cycle_data_indices = get_indices_of_full_cycles(l1a_data["quarter_cycle"].data)
|
|
499
|
-
logger.debug(
|
|
500
|
-
f"Quarter cycle data before filtering: {l1a_data_copy['quarter_cycle'].data}"
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
# Delete Raw Science Data from l1b and onwards
|
|
504
|
-
del l1a_data_copy["raw_science_data"]
|
|
505
|
-
|
|
506
|
-
if full_cycle_data_indices.size == 0:
|
|
507
|
-
# Log that no data is found for science data
|
|
508
|
-
return None
|
|
509
|
-
|
|
510
|
-
if len(full_cycle_data_indices) != total_packets:
|
|
511
|
-
# Filter metadata and science data of packets that makes full cycles
|
|
512
|
-
full_cycle_l1a_data = l1a_data_copy.isel({"epoch": full_cycle_data_indices})
|
|
513
|
-
|
|
514
|
-
# Update total packets
|
|
515
|
-
total_packets = len(full_cycle_data_indices)
|
|
516
|
-
logger.debug(
|
|
517
|
-
"Quarters cycle after filtering: "
|
|
518
|
-
f"{full_cycle_l1a_data['quarter_cycle'].data}"
|
|
519
|
-
)
|
|
520
|
-
if len(full_cycle_data_indices) != len(
|
|
521
|
-
full_cycle_l1a_data["quarter_cycle"].data
|
|
522
|
-
):
|
|
523
|
-
raise ValueError(
|
|
524
|
-
"Error: full cycle data indices and filtered quarter cycle data size "
|
|
525
|
-
"mismatch"
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
# Go through each cycle and populate full cycle data
|
|
529
|
-
for packet_index in range(0, total_packets, swe_constants.N_QUARTER_CYCLES):
|
|
530
|
-
# get ESA lookup table information
|
|
531
|
-
esa_table_num = l1a_data["esa_table_num"].data[packet_index]
|
|
532
|
-
|
|
533
|
-
# If ESA lookup table number is in-flight calibration
|
|
534
|
-
# data, then skip current cycle per SWE teams specification.
|
|
535
|
-
# SWE team only wants in-flight calibration data to be processed
|
|
536
|
-
# upto l1a. In-flight calibration data looks same as science data
|
|
537
|
-
# but it only measures one energy steps during the whole duration.
|
|
538
|
-
if esa_table_num == 1:
|
|
539
|
-
continue
|
|
540
|
-
|
|
541
|
-
full_cycle_ds = populate_full_cycle_data(
|
|
542
|
-
full_cycle_l1a_data, packet_index, esa_table_num
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
# save full data array to file
|
|
546
|
-
full_cycle_science_data.append(full_cycle_ds["full_cycle_data"].data)
|
|
547
|
-
full_cycle_acq_times.append(full_cycle_ds["acquisition_time"].data)
|
|
548
|
-
full_cycle_acq_duration.append(full_cycle_ds["acq_duration"].data)
|
|
549
|
-
|
|
550
|
-
# ------------------------------------------------------------------
|
|
551
|
-
# Save data to dataset.
|
|
552
|
-
# ------------------------------------------------------------------
|
|
553
|
-
# Load CDF attrs
|
|
554
|
-
cdf_attrs = ImapCdfAttributes()
|
|
555
|
-
cdf_attrs.add_instrument_global_attrs("swe")
|
|
556
|
-
cdf_attrs.add_instrument_variable_attrs("swe", "l1b")
|
|
557
|
-
cdf_attrs.add_global_attribute("Data_version", data_version)
|
|
558
|
-
|
|
559
|
-
# One full cycle data combines four quarter cycles data.
|
|
560
|
-
# Epoch will store center of each science meansurement using
|
|
561
|
-
# third acquisition start time coarse and fine value
|
|
562
|
-
# of four quarter cycle data packets. For example, we want to
|
|
563
|
-
# get indices of 3rd quarter cycle data packet in each full cycle
|
|
564
|
-
# and use that to calculate center time of data acquisition time.
|
|
565
|
-
# Quarter cycle indices: 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, ...
|
|
566
|
-
indices_of_center_time = np.arange(2, total_packets, swe_constants.N_QUARTER_CYCLES)
|
|
567
|
-
|
|
568
|
-
center_time = combine_acquisition_time(
|
|
569
|
-
full_cycle_l1a_data["acq_start_coarse"].data[indices_of_center_time],
|
|
570
|
-
full_cycle_l1a_data["acq_start_fine"].data[indices_of_center_time],
|
|
571
|
-
)
|
|
572
|
-
|
|
573
|
-
epoch_time = xr.DataArray(
|
|
574
|
-
met_to_ttj2000ns(center_time),
|
|
575
|
-
name="epoch",
|
|
576
|
-
dims=["epoch"],
|
|
577
|
-
attrs=cdf_attrs.get_variable_attributes("epoch", check_schema=False),
|
|
578
|
-
)
|
|
579
|
-
|
|
580
|
-
esa_step = xr.DataArray(
|
|
581
|
-
np.arange(swe_constants.N_ESA_STEPS),
|
|
582
|
-
name="esa_step",
|
|
583
|
-
dims=["esa_step"],
|
|
584
|
-
attrs=cdf_attrs.get_variable_attributes("esa_step", check_schema=False),
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
# NOTE: LABL_PTR_1 should be CDF_CHAR.
|
|
588
|
-
esa_step_label = xr.DataArray(
|
|
589
|
-
esa_step.values.astype(str),
|
|
590
|
-
name="esa_step_label",
|
|
591
|
-
dims=["esa_step"],
|
|
592
|
-
attrs=cdf_attrs.get_variable_attributes("esa_step_label", check_schema=False),
|
|
593
|
-
)
|
|
594
|
-
|
|
595
|
-
spin_sector = xr.DataArray(
|
|
596
|
-
np.arange(swe_constants.N_ANGLE_SECTORS),
|
|
597
|
-
name="spin_sector",
|
|
598
|
-
dims=["spin_sector"],
|
|
599
|
-
attrs=cdf_attrs.get_variable_attributes("spin_sector", check_schema=False),
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
# NOTE: LABL_PTR_2 should be CDF_CHAR.
|
|
603
|
-
spin_sector_label = xr.DataArray(
|
|
604
|
-
spin_sector.values.astype(str),
|
|
605
|
-
name="spin_sector_label",
|
|
606
|
-
dims=["spin_sector"],
|
|
607
|
-
attrs=cdf_attrs.get_variable_attributes(
|
|
608
|
-
"spin_sector_label", check_schema=False
|
|
609
|
-
),
|
|
610
|
-
)
|
|
611
|
-
|
|
612
|
-
cycle = xr.DataArray(
|
|
613
|
-
np.arange(swe_constants.N_QUARTER_CYCLES),
|
|
614
|
-
name="cycle",
|
|
615
|
-
dims=["cycle"],
|
|
616
|
-
attrs=cdf_attrs.get_variable_attributes("cycle", check_schema=False),
|
|
617
|
-
)
|
|
618
|
-
|
|
619
|
-
cycle_label = xr.DataArray(
|
|
620
|
-
cycle.values.astype(str),
|
|
621
|
-
name="cycle_label",
|
|
622
|
-
dims=["cycle"],
|
|
623
|
-
attrs=cdf_attrs.get_variable_attributes("cycle_label", check_schema=False),
|
|
624
|
-
)
|
|
625
|
-
|
|
626
|
-
cem_id = xr.DataArray(
|
|
627
|
-
np.arange(swe_constants.N_CEMS, dtype=np.int8),
|
|
628
|
-
name="cem_id",
|
|
629
|
-
dims=["cem_id"],
|
|
630
|
-
attrs=cdf_attrs.get_variable_attributes("cem_id", check_schema=False),
|
|
631
|
-
)
|
|
632
|
-
|
|
633
|
-
# NOTE: LABL_PTR_3 should be CDF_CHAR.
|
|
634
|
-
cem_id_label = xr.DataArray(
|
|
635
|
-
cem_id.values.astype(str),
|
|
636
|
-
name="cem_id_label",
|
|
637
|
-
dims=["cem_id"],
|
|
638
|
-
attrs=cdf_attrs.get_variable_attributes("cem_id_label", check_schema=False),
|
|
639
|
-
)
|
|
640
|
-
|
|
641
|
-
# Add science data and it's associated metadata into dataset.
|
|
642
|
-
# SCIENCE_DATA has array of this shape:
|
|
643
|
-
# (n, 24, 30, 7)
|
|
644
|
-
# n = total number of full cycles
|
|
645
|
-
# 24 rows --> 24 esa voltage measurements
|
|
646
|
-
# 30 columns --> 30 spin angle measurements
|
|
647
|
-
# 7 elements --> 7 CEMs counts
|
|
648
|
-
#
|
|
649
|
-
# The metadata array will need to have this shape:
|
|
650
|
-
# (n, 4)
|
|
651
|
-
# n = total number of full cycles
|
|
652
|
-
# 4 rows --> metadata for each full cycle. Each element of 4 maps to
|
|
653
|
-
# metadata of one quarter cycle.
|
|
654
|
-
|
|
655
|
-
# Create the dataset
|
|
656
|
-
dataset = xr.Dataset(
|
|
657
|
-
coords={
|
|
658
|
-
"epoch": epoch_time,
|
|
659
|
-
"esa_step": esa_step,
|
|
660
|
-
"spin_sector": spin_sector,
|
|
661
|
-
"cem_id": cem_id,
|
|
662
|
-
"cycle": cycle,
|
|
663
|
-
"esa_step_label": esa_step_label,
|
|
664
|
-
"spin_sector_label": spin_sector_label,
|
|
665
|
-
"cem_id_label": cem_id_label,
|
|
666
|
-
"cycle_label": cycle_label,
|
|
667
|
-
},
|
|
668
|
-
attrs=cdf_attrs.get_global_attributes("imap_swe_l1b_sci"),
|
|
669
|
-
)
|
|
670
|
-
|
|
671
|
-
dataset["science_data"] = xr.DataArray(
|
|
672
|
-
full_cycle_science_data,
|
|
673
|
-
dims=["epoch", "esa_step", "spin_sector", "cem_id"],
|
|
674
|
-
attrs=cdf_attrs.get_variable_attributes("science_data"),
|
|
675
|
-
)
|
|
676
|
-
dataset["acquisition_time"] = xr.DataArray(
|
|
677
|
-
full_cycle_acq_times,
|
|
678
|
-
dims=["epoch", "esa_step", "spin_sector"],
|
|
679
|
-
attrs=cdf_attrs.get_variable_attributes("acquisition_time"),
|
|
680
|
-
)
|
|
681
|
-
dataset["acq_duration"] = xr.DataArray(
|
|
682
|
-
full_cycle_acq_duration,
|
|
683
|
-
dims=["epoch", "esa_step", "spin_sector"],
|
|
684
|
-
attrs=cdf_attrs.get_variable_attributes("acq_duration"),
|
|
685
|
-
)
|
|
686
|
-
|
|
687
|
-
# create xarray dataset for each metadata field
|
|
688
|
-
for key, value in full_cycle_l1a_data.items():
|
|
689
|
-
if key in ["science_data", "acq_duration"]:
|
|
690
|
-
continue
|
|
691
|
-
metadata_field = key.lower()
|
|
692
|
-
dataset[metadata_field] = xr.DataArray(
|
|
693
|
-
value.data.reshape(-1, swe_constants.N_QUARTER_CYCLES),
|
|
694
|
-
dims=["epoch", "cycle"],
|
|
695
|
-
attrs=cdf_attrs.get_variable_attributes(metadata_field),
|
|
696
|
-
)
|
|
697
|
-
|
|
698
|
-
logger.info("SWE L1b science processing completed")
|
|
699
|
-
return dataset
|