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
|
@@ -83,8 +83,7 @@ def parse_histogram(dataset: xr.Dataset, attr_mgr: ImapCdfAttributes) -> xr.Data
|
|
|
83
83
|
# initialize the starting bit for the sections of data
|
|
84
84
|
section_start = 0
|
|
85
85
|
# for each field type in the histogram data
|
|
86
|
-
for field in HIST_DATA_META:
|
|
87
|
-
data_meta = HIST_DATA_META[field]
|
|
86
|
+
for field, data_meta in HIST_DATA_META.items():
|
|
88
87
|
# for each histogram binary string decompress
|
|
89
88
|
# the data
|
|
90
89
|
decompressed_data = [
|
imap_processing/lo/l1a/lo_l1a.py
CHANGED
|
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
|
|
21
21
|
logger.setLevel(logging.INFO)
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def lo_l1a(dependency: Path
|
|
24
|
+
def lo_l1a(dependency: Path) -> list[xr.Dataset]:
|
|
25
25
|
"""
|
|
26
26
|
Will process IMAP-Lo L0 data into L1A CDF data products.
|
|
27
27
|
|
|
@@ -30,8 +30,6 @@ def lo_l1a(dependency: Path, data_version: str) -> list[xr.Dataset]:
|
|
|
30
30
|
dependency : Path
|
|
31
31
|
Dependency file needed for data product creation.
|
|
32
32
|
Should always be only one for L1A.
|
|
33
|
-
data_version : str
|
|
34
|
-
Version of the data product being created.
|
|
35
33
|
|
|
36
34
|
Returns
|
|
37
35
|
-------
|
|
@@ -51,7 +49,6 @@ def lo_l1a(dependency: Path, data_version: str) -> list[xr.Dataset]:
|
|
|
51
49
|
attr_mgr = ImapCdfAttributes()
|
|
52
50
|
attr_mgr.add_instrument_global_attrs(instrument="lo")
|
|
53
51
|
attr_mgr.add_instrument_variable_attrs(instrument="lo", level="l1a")
|
|
54
|
-
attr_mgr.add_global_attribute("Data_version", data_version)
|
|
55
52
|
|
|
56
53
|
if LoAPID.ILO_SPIN in datasets_by_apid:
|
|
57
54
|
logger.info(
|
imap_processing/lo/l1b/lo_l1b.py
CHANGED
|
@@ -8,10 +8,17 @@ import numpy as np
|
|
|
8
8
|
import xarray as xr
|
|
9
9
|
|
|
10
10
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
11
|
-
from imap_processing.
|
|
11
|
+
from imap_processing.lo.l1b.tof_conversions import (
|
|
12
|
+
TOF0_CONV,
|
|
13
|
+
TOF1_CONV,
|
|
14
|
+
TOF2_CONV,
|
|
15
|
+
TOF3_CONV,
|
|
16
|
+
)
|
|
17
|
+
from imap_processing.spice.geometry import SpiceFrame, instrument_pointing
|
|
18
|
+
from imap_processing.spice.time import met_to_ttj2000ns, ttj2000ns_to_et
|
|
12
19
|
|
|
13
20
|
|
|
14
|
-
def lo_l1b(dependencies: dict
|
|
21
|
+
def lo_l1b(dependencies: dict) -> list[Path]:
|
|
15
22
|
"""
|
|
16
23
|
Will process IMAP-Lo L1A data into L1B CDF data products.
|
|
17
24
|
|
|
@@ -19,8 +26,6 @@ def lo_l1b(dependencies: dict, data_version: str) -> list[Path]:
|
|
|
19
26
|
----------
|
|
20
27
|
dependencies : dict
|
|
21
28
|
Dictionary of datasets needed for L1B data product creation in xarray Datasets.
|
|
22
|
-
data_version : str
|
|
23
|
-
Version of the data product being created.
|
|
24
29
|
|
|
25
30
|
Returns
|
|
26
31
|
-------
|
|
@@ -31,7 +36,6 @@ def lo_l1b(dependencies: dict, data_version: str) -> list[Path]:
|
|
|
31
36
|
attr_mgr_l1b = ImapCdfAttributes()
|
|
32
37
|
attr_mgr_l1b.add_instrument_global_attrs(instrument="lo")
|
|
33
38
|
attr_mgr_l1b.add_instrument_variable_attrs(instrument="lo", level="l1b")
|
|
34
|
-
attr_mgr_l1b.add_global_attribute("Data_version", data_version)
|
|
35
39
|
# create the attribute manager to access L1A fillval attributes
|
|
36
40
|
attr_mgr_l1a = ImapCdfAttributes()
|
|
37
41
|
attr_mgr_l1a.add_instrument_variable_attrs(instrument="lo", level="l1a")
|
|
@@ -48,12 +52,36 @@ def lo_l1b(dependencies: dict, data_version: str) -> list[Path]:
|
|
|
48
52
|
# Get the start and end times for each spin epoch
|
|
49
53
|
acq_start, acq_end = convert_start_end_acq_times(spin_data)
|
|
50
54
|
# Get the average spin durations for each epoch
|
|
51
|
-
avg_spin_durations = get_avg_spin_durations(acq_start, acq_end)
|
|
55
|
+
avg_spin_durations = get_avg_spin_durations(acq_start, acq_end)
|
|
52
56
|
# get spin angle (0 - 360 degrees) for each DE
|
|
53
57
|
spin_angle = get_spin_angle(l1a_de)
|
|
54
58
|
# calculate and set the spin bin based on the spin angle
|
|
55
59
|
# spin bins are 0 - 60 bins
|
|
56
60
|
l1b_de = set_spin_bin(l1b_de, spin_angle)
|
|
61
|
+
# set the spin cycle for each direct event
|
|
62
|
+
l1b_de = set_spin_cycle(l1a_de, l1b_de)
|
|
63
|
+
# get spin start times for each event
|
|
64
|
+
spin_start_time = get_spin_start_times(l1a_de, l1b_de, spin_data, acq_end)
|
|
65
|
+
# get the absolute met for each event
|
|
66
|
+
l1b_de = set_event_met(l1a_de, l1b_de, spin_start_time, avg_spin_durations)
|
|
67
|
+
# set the epoch for each event
|
|
68
|
+
l1b_de = set_each_event_epoch(l1b_de)
|
|
69
|
+
# calculate the TOF1 for golden triples
|
|
70
|
+
# store in the l1a dataset to use in l1b calculations
|
|
71
|
+
l1a_de = calculate_tof1_for_golden_triples(l1a_de)
|
|
72
|
+
# set the coincidence type string for each direct event
|
|
73
|
+
l1b_de = set_coincidence_type(l1a_de, l1b_de, attr_mgr_l1a)
|
|
74
|
+
# convert the TOFs to engineering units
|
|
75
|
+
l1b_de = convert_tofs_to_eu(l1a_de, l1b_de, attr_mgr_l1a, attr_mgr_l1b)
|
|
76
|
+
# set the species for each direct event
|
|
77
|
+
l1b_de = identify_species(l1b_de)
|
|
78
|
+
# set the badtimes
|
|
79
|
+
l1b_de = set_bad_times(l1b_de)
|
|
80
|
+
# set the pointing direction for each direct event
|
|
81
|
+
l1b_de = set_pointing_direction(l1b_de)
|
|
82
|
+
# calculate and set the pointing bin based on the spin phase
|
|
83
|
+
# pointing bin is 3600 x 40 bins
|
|
84
|
+
l1b_de = set_pointing_bin(l1b_de)
|
|
57
85
|
|
|
58
86
|
return [l1b_de]
|
|
59
87
|
|
|
@@ -211,6 +239,499 @@ def set_spin_bin(l1b_de: xr.Dataset, spin_angle: np.ndarray) -> xr.Dataset:
|
|
|
211
239
|
return l1b_de
|
|
212
240
|
|
|
213
241
|
|
|
242
|
+
def set_spin_cycle(l1a_de: xr.Dataset, l1b_de: xr.Dataset) -> xr.Dataset:
|
|
243
|
+
"""
|
|
244
|
+
Set the spin cycle for each direct event.
|
|
245
|
+
|
|
246
|
+
spin_cycle = spin_start + 7 + (esa_step - 1) * 2
|
|
247
|
+
|
|
248
|
+
where spin_start is the spin number for the first spin
|
|
249
|
+
in an Aggregated Science Cycle (ASC) and esa_step is the esa_step for a direct event
|
|
250
|
+
|
|
251
|
+
The 28 spins in a spin epoch spans one ASC.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
l1a_de : xarray.Dataset
|
|
256
|
+
The L1A DE dataset.
|
|
257
|
+
l1b_de : xarray.Dataset
|
|
258
|
+
The L1B DE dataset.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
l1b_de : xarray.Dataset
|
|
263
|
+
The L1B DE dataset with the spin cycle added for each direct event.
|
|
264
|
+
"""
|
|
265
|
+
counts = l1a_de["de_count"].values
|
|
266
|
+
# split the esa_steps into ASC groups
|
|
267
|
+
de_asc_groups = np.split(l1a_de["esa_step"].values, np.cumsum(counts)[:-1])
|
|
268
|
+
spin_cycle = []
|
|
269
|
+
for i, esa_asc_group in enumerate(de_asc_groups):
|
|
270
|
+
# TODO: Spin Number does not reset for each pointing. Need to figure out
|
|
271
|
+
# how to retain this information across days
|
|
272
|
+
# increment the spin_start by 28 after each aggregated science cycle
|
|
273
|
+
spin_start = i * 28
|
|
274
|
+
# calculate the spin cycle for each DE in the ASC group
|
|
275
|
+
# TODO: Add equation number in algorithm document when new version is
|
|
276
|
+
# available. Add to docstring as well
|
|
277
|
+
spin_cycle.extend(spin_start + 7 + (esa_asc_group - 1) * 2)
|
|
278
|
+
|
|
279
|
+
l1b_de["spin_cycle"] = xr.DataArray(
|
|
280
|
+
spin_cycle,
|
|
281
|
+
dims=["epoch"],
|
|
282
|
+
# TODO: Add spin cycle to YAML file
|
|
283
|
+
# attrs=attr_mgr.get_variable_attributes("spin_cycle"),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
return l1b_de
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def get_spin_start_times(
|
|
290
|
+
l1a_de: xr.Dataset, l1b_de: xr.Dataset, spin_data: xr.Dataset, acq_end: xr.DataArray
|
|
291
|
+
) -> xr.DataArray:
|
|
292
|
+
"""
|
|
293
|
+
Get the start time for the spin that each direct event is in.
|
|
294
|
+
|
|
295
|
+
The resulting array of spin start times will be equal to the length of the direct
|
|
296
|
+
events. If two direct events occurred in the same spin, then there will be repeating
|
|
297
|
+
spin start times.
|
|
298
|
+
|
|
299
|
+
Parameters
|
|
300
|
+
----------
|
|
301
|
+
l1a_de : xr.Dataset
|
|
302
|
+
The L1A DE dataset.
|
|
303
|
+
l1b_de : xr.Dataset
|
|
304
|
+
The L1B DE dataset.
|
|
305
|
+
spin_data : xr.Dataset
|
|
306
|
+
The L1A Spin dataset.
|
|
307
|
+
acq_end : xr.DataArray
|
|
308
|
+
The end acquisition times for each spin ASC.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
spin_start_time : xr.DataArray
|
|
313
|
+
The start time for the spin that each direct event is in.
|
|
314
|
+
"""
|
|
315
|
+
met = l1a_de["met"].values
|
|
316
|
+
# Find the closest stop_acq for each shcoarse
|
|
317
|
+
closest_stop_acq_indices = np.abs(met[:, None] - acq_end.values).argmin(axis=1)
|
|
318
|
+
# There are 28 spins per epoch (1 aggregated science cycle)
|
|
319
|
+
# Set the spin_cycle_num to the spin number relative to the
|
|
320
|
+
# start of the ASC
|
|
321
|
+
spin_cycle_num = l1b_de["spin_cycle"] % 28
|
|
322
|
+
# Get the seconds portion of the start time for each spin
|
|
323
|
+
start_sec_spins = spin_data["start_sec_spin"].values[
|
|
324
|
+
closest_stop_acq_indices, spin_cycle_num
|
|
325
|
+
]
|
|
326
|
+
# Get the subseconds portion of the spin start time and convert from
|
|
327
|
+
# microseconds to seconds
|
|
328
|
+
start_subsec_spins = (
|
|
329
|
+
spin_data["start_subsec_spin"].values[closest_stop_acq_indices, spin_cycle_num]
|
|
330
|
+
* 1e-6
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Combine the seconds and subseconds to get the start time for each spin
|
|
334
|
+
spin_start_time = start_sec_spins + start_subsec_spins
|
|
335
|
+
return xr.DataArray(spin_start_time)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def set_event_met(
|
|
339
|
+
l1a_de: xr.Dataset,
|
|
340
|
+
l1b_de: xr.Dataset,
|
|
341
|
+
spin_start_time: xr.DataArray,
|
|
342
|
+
avg_spin_durations: xr.DataArray,
|
|
343
|
+
) -> xr.Dataset:
|
|
344
|
+
"""
|
|
345
|
+
Get the event MET for each direct event.
|
|
346
|
+
|
|
347
|
+
Each direct event is converted from a data number to engineering unit in seconds.
|
|
348
|
+
de_eu_time de_dn_time / 4096 * avg_spin_duration
|
|
349
|
+
where de_time is the direct event time Data Number (DN) and avg_spin_duration
|
|
350
|
+
is the average spin duration for the ASC that the event was measured in.
|
|
351
|
+
|
|
352
|
+
The direct event time is the time of direct event relative to the start of the spin.
|
|
353
|
+
The event MET is the sum of the start time of the spin and the
|
|
354
|
+
direct event EU time.
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
l1a_de : xr.Dataset
|
|
359
|
+
The L1A DE dataset.
|
|
360
|
+
l1b_de : xr.Dataset
|
|
361
|
+
The L1B DE dataset.
|
|
362
|
+
spin_start_time : np.ndarray
|
|
363
|
+
The start time for the spin that each direct event is in.
|
|
364
|
+
avg_spin_durations : xr.DataArray
|
|
365
|
+
The average spin duration for each epoch.
|
|
366
|
+
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
l1b_de : xr.Dataset
|
|
370
|
+
The L1B DE dataset with the event MET.
|
|
371
|
+
"""
|
|
372
|
+
counts = l1a_de["de_count"].values
|
|
373
|
+
de_time_asc_groups = np.split(l1a_de["de_time"].values, np.cumsum(counts)[:-1])
|
|
374
|
+
de_times_eu = []
|
|
375
|
+
for i, de_time_asc in enumerate(de_time_asc_groups):
|
|
376
|
+
# DE Time is 12 bit DN. The max possible value is 4095
|
|
377
|
+
# divide by 4096 to get fraction of a spin duration
|
|
378
|
+
de_times_eu.extend(de_time_asc / 4096 * avg_spin_durations[i].values)
|
|
379
|
+
|
|
380
|
+
l1b_de["event_met"] = xr.DataArray(
|
|
381
|
+
spin_start_time + de_times_eu,
|
|
382
|
+
dims=["epoch"],
|
|
383
|
+
# attrs=attr_mgr.get_variable_attributes("epoch")
|
|
384
|
+
)
|
|
385
|
+
return l1b_de
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def set_each_event_epoch(l1b_de: xr.Dataset) -> xr.Dataset:
|
|
389
|
+
"""
|
|
390
|
+
Set the epoch for each direct event.
|
|
391
|
+
|
|
392
|
+
Parameters
|
|
393
|
+
----------
|
|
394
|
+
l1b_de : xr.Dataset
|
|
395
|
+
The L1B DE dataset.
|
|
396
|
+
|
|
397
|
+
Returns
|
|
398
|
+
-------
|
|
399
|
+
l1b_de : xr.Dataset
|
|
400
|
+
The L1B DE dataset with the epoch set for each event.
|
|
401
|
+
"""
|
|
402
|
+
l1b_de["epoch"] = xr.DataArray(
|
|
403
|
+
met_to_ttj2000ns(l1b_de["event_met"].values),
|
|
404
|
+
dims=["epoch"],
|
|
405
|
+
# attrs=attr_mgr.get_variable_attributes("epoch")
|
|
406
|
+
)
|
|
407
|
+
return l1b_de
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def calculate_tof1_for_golden_triples(l1a_de: xr.Dataset) -> xr.Dataset:
|
|
411
|
+
"""
|
|
412
|
+
Calculate the TOF1 for golden triples.
|
|
413
|
+
|
|
414
|
+
TOF1 is not transmitted for golden triples, but is recovered on the
|
|
415
|
+
ground using the TOF0, TOF2, TOF3, and CKSUM values. The equation is:
|
|
416
|
+
TOF1 = (TOF0 + TOF3 - TOF2 - CKSUM - left_cksm_bound) << 1
|
|
417
|
+
|
|
418
|
+
where left_cksm_bound is the left checksum boundary value. This is a
|
|
419
|
+
constant value that is not transmitted in the telemetry.
|
|
420
|
+
|
|
421
|
+
Parameters
|
|
422
|
+
----------
|
|
423
|
+
l1a_de : xr.Dataset
|
|
424
|
+
The L1A DE dataset.
|
|
425
|
+
|
|
426
|
+
Returns
|
|
427
|
+
-------
|
|
428
|
+
l1a_de : xr.Dataset
|
|
429
|
+
The L1A DE dataset with the TOF1 calculated for golden triples.
|
|
430
|
+
"""
|
|
431
|
+
for idx, coin_type in enumerate(l1a_de["coincidence_type"].values):
|
|
432
|
+
if coin_type == 0 and l1a_de["mode"][idx] == 0:
|
|
433
|
+
# Calculate TOF1
|
|
434
|
+
# TOF1 equation requires values to be right bit shifted. These values were
|
|
435
|
+
# originally right bit shifted when packed in the telemetry packet, but were
|
|
436
|
+
# left bit shifted for the L1A product. Need to right bit shift them again
|
|
437
|
+
# to apply the TOF1 equation
|
|
438
|
+
tof0 = l1a_de["tof0"][idx] >> 1
|
|
439
|
+
tof2 = l1a_de["tof2"][idx] >> 1
|
|
440
|
+
tof3 = l1a_de["tof3"][idx] >> 1
|
|
441
|
+
cksm = l1a_de["cksm"][idx] >> 1
|
|
442
|
+
# TODO: will get left checksum boundary from LUT table when available
|
|
443
|
+
left_cksm_bound = -21
|
|
444
|
+
# Calculate TOF1, then left bit shift it to store it with the rest of the
|
|
445
|
+
# left shifted L1A dataset data.
|
|
446
|
+
l1a_de["tof1"][idx] = (tof0 + tof3 - tof2 - cksm - left_cksm_bound) << 1
|
|
447
|
+
return l1a_de
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def set_coincidence_type(
|
|
451
|
+
l1a_de: xr.Dataset,
|
|
452
|
+
l1b_de: xr.Dataset,
|
|
453
|
+
attr_mgr_l1a: ImapCdfAttributes,
|
|
454
|
+
) -> xr.Dataset:
|
|
455
|
+
"""
|
|
456
|
+
Set the coincidence type for each direct event.
|
|
457
|
+
|
|
458
|
+
The coincidence type is a string that indicates the type of coincidence
|
|
459
|
+
for each direct event. The string is a combination of the following depending
|
|
460
|
+
on whether the TOF or CKSM value is present (1) or absent (0) and the value
|
|
461
|
+
of the mode for each direct event:
|
|
462
|
+
"<TOF0><TOF1><TOF2><TOF3><CKSM><Mode>"
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
l1a_de : xarray.Dataset
|
|
467
|
+
The L1A DE dataset.
|
|
468
|
+
l1b_de : xarray.Dataset
|
|
469
|
+
The L1B DE dataset.
|
|
470
|
+
attr_mgr_l1a : ImapCdfAttributes
|
|
471
|
+
Attribute manager used to get the fill values for the L1A DE dataset.
|
|
472
|
+
|
|
473
|
+
Returns
|
|
474
|
+
-------
|
|
475
|
+
l1b_de : xarray.Dataset
|
|
476
|
+
The L1B DE dataset with the coincidence type added.
|
|
477
|
+
"""
|
|
478
|
+
tof0_fill = attr_mgr_l1a.get_variable_attributes("tof0")["FILLVAL"]
|
|
479
|
+
tof0_mask = (l1a_de["tof0"].values != tof0_fill).astype(int)
|
|
480
|
+
tof1_fill = attr_mgr_l1a.get_variable_attributes("tof1")["FILLVAL"]
|
|
481
|
+
tof1_mask = (l1a_de["tof1"].values != tof1_fill).astype(int)
|
|
482
|
+
tof2_fill = attr_mgr_l1a.get_variable_attributes("tof2")["FILLVAL"]
|
|
483
|
+
tof2_mask = (l1a_de["tof2"].values != tof2_fill).astype(int)
|
|
484
|
+
tof3_fill = attr_mgr_l1a.get_variable_attributes("tof3")["FILLVAL"]
|
|
485
|
+
tof3_mask = (l1a_de["tof3"].values != tof3_fill).astype(int)
|
|
486
|
+
cksm_fill = attr_mgr_l1a.get_variable_attributes("cksm")["FILLVAL"]
|
|
487
|
+
cksm_mask = (l1a_de["cksm"].values != cksm_fill).astype(int)
|
|
488
|
+
|
|
489
|
+
coincidence_type = [
|
|
490
|
+
f"{tof0_mask[i]}{tof1_mask[i]}{tof2_mask[i]}{tof3_mask[i]}{cksm_mask[i]}{l1a_de['mode'].values[i]}"
|
|
491
|
+
for i in range(len(l1a_de["direct_events"]))
|
|
492
|
+
]
|
|
493
|
+
|
|
494
|
+
l1b_de["coincidence_type"] = xr.DataArray(
|
|
495
|
+
coincidence_type,
|
|
496
|
+
dims=["epoch"],
|
|
497
|
+
# TODO: Add coincidence_type to YAML file
|
|
498
|
+
# attrs=attr_mgr.get_variable_attributes("spin_cycle"),
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
return l1b_de
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def convert_tofs_to_eu(
|
|
505
|
+
l1a_de: xr.Dataset,
|
|
506
|
+
l1b_de: xr.Dataset,
|
|
507
|
+
attr_mgr_l1a: ImapCdfAttributes,
|
|
508
|
+
attr_mgr_l1b: ImapCdfAttributes,
|
|
509
|
+
) -> xr.Dataset:
|
|
510
|
+
"""
|
|
511
|
+
Convert the TOFs to engineering units.
|
|
512
|
+
|
|
513
|
+
The TOFs are converted from data numbers (DN) to engineering units (EU) using the
|
|
514
|
+
following equation:
|
|
515
|
+
TOF_EU = C0 + C1 * TOF_DN
|
|
516
|
+
|
|
517
|
+
where C0 and C1 are the conversion coefficients for each TOF.
|
|
518
|
+
|
|
519
|
+
This equation is applied to all four TOFs (TOF0, TOF1, TOF2, TOF3).
|
|
520
|
+
|
|
521
|
+
Parameters
|
|
522
|
+
----------
|
|
523
|
+
l1a_de : xarray.Dataset
|
|
524
|
+
The L1A DE dataset.
|
|
525
|
+
l1b_de : xarray.Dataset
|
|
526
|
+
The L1B DE dataset.
|
|
527
|
+
attr_mgr_l1a : ImapCdfAttributes
|
|
528
|
+
Attribute manager used to get the fill values for the L1A DE dataset.
|
|
529
|
+
attr_mgr_l1b : ImapCdfAttributes
|
|
530
|
+
Attribute manager used to get the fill values for the L1B DE dataset.
|
|
531
|
+
|
|
532
|
+
Returns
|
|
533
|
+
-------
|
|
534
|
+
l1b_de : xarray.Dataset
|
|
535
|
+
The L1B DE dataset with the TOFs converted to engineering units.
|
|
536
|
+
"""
|
|
537
|
+
tof_fields = ["tof0", "tof1", "tof2", "tof3"]
|
|
538
|
+
tof_conversions = [TOF0_CONV, TOF1_CONV, TOF2_CONV, TOF3_CONV]
|
|
539
|
+
|
|
540
|
+
# Loop through the TOF fields and convert them to engineering units
|
|
541
|
+
for tof, conv in zip(tof_fields, tof_conversions):
|
|
542
|
+
# Get the fill value for the L1A and L1B TOF
|
|
543
|
+
fillval_1a = attr_mgr_l1a.get_variable_attributes(tof)["FILLVAL"]
|
|
544
|
+
fillval_1b = attr_mgr_l1b.get_variable_attributes(tof)["FILLVAL"]
|
|
545
|
+
# Create a mask for the TOF
|
|
546
|
+
mask = l1a_de[tof] != fillval_1a
|
|
547
|
+
# Convert the DN TOF to EU and add the EU TOF to the dataset.
|
|
548
|
+
# If the TOF is not present, set it to the fill value for the L1B TOF data.
|
|
549
|
+
tof_eu = np.where(
|
|
550
|
+
mask,
|
|
551
|
+
conv.C0 + conv.C1 * l1a_de[tof],
|
|
552
|
+
fillval_1b,
|
|
553
|
+
)
|
|
554
|
+
l1b_de[tof] = xr.DataArray(
|
|
555
|
+
tof_eu,
|
|
556
|
+
dims=["epoch"],
|
|
557
|
+
attrs=attr_mgr_l1b.get_variable_attributes(tof),
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
return l1b_de
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def identify_species(l1b_de: xr.Dataset) -> xr.Dataset:
|
|
564
|
+
"""
|
|
565
|
+
Identify the species for each direct event.
|
|
566
|
+
|
|
567
|
+
The species are determined using the U_PAC 7-13kV range table with the TOF2 value.
|
|
568
|
+
Each event is set to "H" for Hydrogen, "O" for Oxygen, or "U" for Unknown.
|
|
569
|
+
|
|
570
|
+
See the species identification section in the Lo algorithm document for more
|
|
571
|
+
information on the ranges used to identify the species.
|
|
572
|
+
|
|
573
|
+
Parameters
|
|
574
|
+
----------
|
|
575
|
+
l1b_de : xarray.Dataset
|
|
576
|
+
The L1B DE dataset.
|
|
577
|
+
|
|
578
|
+
Returns
|
|
579
|
+
-------
|
|
580
|
+
l1b_de : xarray.Dataset
|
|
581
|
+
The L1B DE dataset with the species identified.
|
|
582
|
+
"""
|
|
583
|
+
# Define upper and lower ranges for Hydrogen and Oxygen
|
|
584
|
+
# Table defined in 9.3.4.4 of the Lo algorithm document
|
|
585
|
+
# UNH-IMAP-Lo-27850-6002-Data-Product-Algorithms-v9_&_IMAP-LoMappingAlgorithm
|
|
586
|
+
# The ranges are used for U_PAC voltages 7-12kV. Lo does not expect to use
|
|
587
|
+
# voltages outside of that range.
|
|
588
|
+
range_hydrogen = (13, 40)
|
|
589
|
+
range_oxygen = (75, 200)
|
|
590
|
+
|
|
591
|
+
# Initialize the species array with U for Unknown
|
|
592
|
+
species = np.full(len(l1b_de["epoch"]), "U")
|
|
593
|
+
|
|
594
|
+
tof2 = l1b_de["tof2"]
|
|
595
|
+
# Check for range Hydrogen using the TOF2 value
|
|
596
|
+
mask_h = (tof2 >= range_hydrogen[0]) & (tof2 <= range_hydrogen[1])
|
|
597
|
+
species[mask_h] = "H"
|
|
598
|
+
|
|
599
|
+
# Check for range Oxygen using the TOF2 value
|
|
600
|
+
mask_oxygen = (tof2 >= range_oxygen[0]) & (tof2 <= range_oxygen[1])
|
|
601
|
+
species[mask_oxygen] = "O"
|
|
602
|
+
|
|
603
|
+
# Add species to the dataset
|
|
604
|
+
l1b_de["species"] = xr.DataArray(
|
|
605
|
+
species,
|
|
606
|
+
dims=["epoch"],
|
|
607
|
+
# TODO: Add to yaml
|
|
608
|
+
# attrs=attr_mgr.get_variable_attributes("species"),
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
return l1b_de
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def set_bad_times(l1b_de: xr.Dataset) -> xr.Dataset:
|
|
615
|
+
"""
|
|
616
|
+
Set the bad times for each direct event.
|
|
617
|
+
|
|
618
|
+
Parameters
|
|
619
|
+
----------
|
|
620
|
+
l1b_de : xarray.Dataset
|
|
621
|
+
The L1B DE dataset.
|
|
622
|
+
|
|
623
|
+
Returns
|
|
624
|
+
-------
|
|
625
|
+
l1b_de : xarray.Dataset
|
|
626
|
+
The L1B DE dataset with the bad times added.
|
|
627
|
+
"""
|
|
628
|
+
# Initialize all times as not bad for now
|
|
629
|
+
# TODO: Update to set badtimes based on criteria that
|
|
630
|
+
# will be defined in the algorithm document
|
|
631
|
+
# 1 = badtime, 0 = not badtime
|
|
632
|
+
l1b_de["badtimes"] = xr.DataArray(
|
|
633
|
+
np.zeros(len(l1b_de["epoch"]), dtype=int),
|
|
634
|
+
dims=["epoch"],
|
|
635
|
+
# TODO: Add to yaml
|
|
636
|
+
# attrs=attr_mgr.get_variable_attributes("bad_times"),
|
|
637
|
+
)
|
|
638
|
+
|
|
639
|
+
return l1b_de
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def set_pointing_direction(l1b_de: xr.Dataset) -> xr.Dataset:
|
|
643
|
+
"""
|
|
644
|
+
Set the pointing direction for each direct event.
|
|
645
|
+
|
|
646
|
+
The pointing direction is determined using the SPICE instrument pointing
|
|
647
|
+
function. The pointing direction are two 1D vectors in units of degrees
|
|
648
|
+
for longitude and latitude sharing the same epoch dimension.
|
|
649
|
+
|
|
650
|
+
Parameters
|
|
651
|
+
----------
|
|
652
|
+
l1b_de : xarray.Dataset
|
|
653
|
+
The L1B DE dataset.
|
|
654
|
+
|
|
655
|
+
Returns
|
|
656
|
+
-------
|
|
657
|
+
l1b_de : xarray.Dataset
|
|
658
|
+
The L1B DE dataset with the pointing direction added.
|
|
659
|
+
"""
|
|
660
|
+
# Get the pointing bin for each DE
|
|
661
|
+
et = ttj2000ns_to_et(l1b_de["epoch"])
|
|
662
|
+
|
|
663
|
+
direction = instrument_pointing(et, SpiceFrame.IMAP_LO_BASE, SpiceFrame.IMAP_DPS)
|
|
664
|
+
# TODO: Need to ask Lo what to do if a latitude is outside of the
|
|
665
|
+
# +/-2 degree range. Is that possible?
|
|
666
|
+
l1b_de["direction_lon"] = xr.DataArray(
|
|
667
|
+
direction[:, 0],
|
|
668
|
+
dims=["epoch"],
|
|
669
|
+
# TODO: Add direction_lon to YAML file
|
|
670
|
+
# attrs=attr_mgr.get_variable_attributes("direction_lon"),
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
l1b_de["direction_lat"] = xr.DataArray(
|
|
674
|
+
direction[:, 1],
|
|
675
|
+
dims=["epoch"],
|
|
676
|
+
# TODO: Add direction_lat to YAML file
|
|
677
|
+
# attrs=attr_mgr.get_variable_attributes("direction_lat"),
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
return l1b_de
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def set_pointing_bin(l1b_de: xr.Dataset) -> xr.Dataset:
|
|
684
|
+
"""
|
|
685
|
+
Set the pointing bin for each direct event.
|
|
686
|
+
|
|
687
|
+
The pointing bins are defined as 3600 bins for longitude and 40 bins for latitude.
|
|
688
|
+
Each bin is 0.1 degrees. The bins are defined as follows:
|
|
689
|
+
Longitude bins: -180 to 180 degrees
|
|
690
|
+
Latitude bins: -2 to 2 degrees
|
|
691
|
+
|
|
692
|
+
Parameters
|
|
693
|
+
----------
|
|
694
|
+
l1b_de : xarray.Dataset
|
|
695
|
+
The L1B DE dataset.
|
|
696
|
+
|
|
697
|
+
Returns
|
|
698
|
+
-------
|
|
699
|
+
l1b_de : xarray.Dataset
|
|
700
|
+
The L1B DE dataset with the pointing bins added.
|
|
701
|
+
"""
|
|
702
|
+
# First column: latitudes
|
|
703
|
+
lats = l1b_de["direction_lat"]
|
|
704
|
+
# Second column: longitudes
|
|
705
|
+
lons = l1b_de["direction_lon"]
|
|
706
|
+
|
|
707
|
+
# Define bin edges
|
|
708
|
+
# 3600 bins, 0.1° each
|
|
709
|
+
lon_bins = np.linspace(-180, 180, 3601)
|
|
710
|
+
# 40 bins, 0.1° each
|
|
711
|
+
lat_bins = np.linspace(-2, 2, 41)
|
|
712
|
+
|
|
713
|
+
# put the lons and lats into bins
|
|
714
|
+
# shift to 0-based index
|
|
715
|
+
lon_bins = np.digitize(lons, lon_bins) - 1
|
|
716
|
+
lat_bins = np.digitize(lats, lat_bins) - 1
|
|
717
|
+
|
|
718
|
+
l1b_de["pointing_bin_lon"] = xr.DataArray(
|
|
719
|
+
lon_bins,
|
|
720
|
+
dims=["epoch"],
|
|
721
|
+
# TODO: Add pointing_bin_lon to YAML file
|
|
722
|
+
# attrs=attr_mgr.get_variable_attributes("pointing_bin_lon"),
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
l1b_de["pointing_bin_lat"] = xr.DataArray(
|
|
726
|
+
lat_bins,
|
|
727
|
+
dims=["epoch"],
|
|
728
|
+
# TODO: Add point_bin_lat to YAML file
|
|
729
|
+
# attrs=attr_mgr.get_variable_attributes("pointing_bin_lat"),
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
return l1b_de
|
|
733
|
+
|
|
734
|
+
|
|
214
735
|
# TODO: This is going to work differently when I sample data.
|
|
215
736
|
# The data_fields input is temporary.
|
|
216
737
|
def create_datasets(
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Lo TOF EU Conversions."""
|
|
2
|
+
|
|
3
|
+
from collections import namedtuple
|
|
4
|
+
|
|
5
|
+
tof_conv = namedtuple("tof_conv", ["C0", "C1"])
|
|
6
|
+
# TOF conversion coefficients from Lo's TOF Conversion_annotated.docx
|
|
7
|
+
# TODO: Ask Lo to put these in the algorithm document for better reference
|
|
8
|
+
TOF0_CONV = tof_conv(C0=5.52524e-01, C1=1.68374e-01)
|
|
9
|
+
TOF1_CONV = tof_conv(C0=-7.20181e-01, C1=1.65124e-01)
|
|
10
|
+
TOF2_CONV = tof_conv(C0=3.74422e-01, C1=1.66409e-01)
|
|
11
|
+
TOF3_CONV = tof_conv(C0=4.41970e-01, C1=1.72024e-01)
|
imap_processing/lo/l1c/lo_l1c.py
CHANGED
|
@@ -11,7 +11,7 @@ from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
|
11
11
|
from imap_processing.spice.time import met_to_ttj2000ns
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def lo_l1c(dependencies: dict
|
|
14
|
+
def lo_l1c(dependencies: dict) -> list[Path]:
|
|
15
15
|
"""
|
|
16
16
|
Will process IMAP-Lo L1B data into L1C CDF data products.
|
|
17
17
|
|
|
@@ -19,8 +19,6 @@ def lo_l1c(dependencies: dict, data_version: str) -> list[Path]:
|
|
|
19
19
|
----------
|
|
20
20
|
dependencies : dict
|
|
21
21
|
Dictionary of datasets needed for L1C data product creation in xarray Datasets.
|
|
22
|
-
data_version : str
|
|
23
|
-
Version of the data product being created.
|
|
24
22
|
|
|
25
23
|
Returns
|
|
26
24
|
-------
|
|
@@ -31,7 +29,6 @@ def lo_l1c(dependencies: dict, data_version: str) -> list[Path]:
|
|
|
31
29
|
attr_mgr = ImapCdfAttributes()
|
|
32
30
|
attr_mgr.add_instrument_global_attrs(instrument="lo")
|
|
33
31
|
attr_mgr.add_instrument_variable_attrs(instrument="lo", level="l1c")
|
|
34
|
-
attr_mgr.add_global_attribute("Data_version", data_version)
|
|
35
32
|
|
|
36
33
|
# if the dependencies are used to create Annotated Direct Events
|
|
37
34
|
if "imap_lo_l1b_de" in dependencies:
|
imap_processing/mag/constants.py
CHANGED
|
@@ -59,6 +59,23 @@ class PrimarySensor(Enum):
|
|
|
59
59
|
MAGI = 1
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
class VecSec(Enum):
|
|
63
|
+
"""Enum for all valid vector rates (Vectors per second)."""
|
|
64
|
+
|
|
65
|
+
ONE_VEC_PER_S = 1
|
|
66
|
+
TWO_VECS_PER_S = 2
|
|
67
|
+
FOUR_VECS_PER_S = 4
|
|
68
|
+
EIGHT_VECS_PER_S = 8
|
|
69
|
+
SIXTEEN_VECS_PER_S = 16
|
|
70
|
+
THIRTY_TWO_VECS_PER_S = 32
|
|
71
|
+
SIXTY_FOUR_VECS_PER_S = 64
|
|
72
|
+
ONE_TWENTY_EIGHT_VECS_PER_S = 128
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# Possible sensor rates
|
|
76
|
+
POSSIBLE_RATES = [e.value for e in VecSec]
|
|
77
|
+
|
|
78
|
+
|
|
62
79
|
class ModeFlags(Enum):
|
|
63
80
|
"""Enum for MAG mode flags: burst and normal (BURST + NORM)."""
|
|
64
81
|
|
|
@@ -114,3 +131,29 @@ MAX_FINE_TIME = np.iinfo(np.uint16).max # maximum 16 bit unsigned int
|
|
|
114
131
|
AXIS_COUNT = 3
|
|
115
132
|
RANGE_BIT_WIDTH = 2
|
|
116
133
|
MAX_COMPRESSED_VECTOR_BITS = 60
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def vectors_per_second_from_string(vecsec_string: str) -> dict:
|
|
137
|
+
"""
|
|
138
|
+
Extract the vectors per second from a string into a dictionary.
|
|
139
|
+
|
|
140
|
+
Dictionary format: {start_time: vecsec, start_time: vecsec}.
|
|
141
|
+
|
|
142
|
+
Parameters
|
|
143
|
+
----------
|
|
144
|
+
vecsec_string : str
|
|
145
|
+
A string of the form "start:vecsec,start:vecsec" where start is the time in
|
|
146
|
+
nanoseconds and vecsec is the number of vectors per second.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
dict
|
|
151
|
+
A dictionary of the form {start_time: vecsec, start_time: vecsec}.
|
|
152
|
+
"""
|
|
153
|
+
vecsec_dict = {}
|
|
154
|
+
vecsec_segments = vecsec_string.split(",")
|
|
155
|
+
for vecsec_segment in vecsec_segments:
|
|
156
|
+
start_time, vecsec = vecsec_segment.split(":")
|
|
157
|
+
vecsec_dict[int(start_time)] = int(vecsec)
|
|
158
|
+
|
|
159
|
+
return vecsec_dict
|