imap-processing 0.11.0__py3-none-any.whl → 0.12.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 +10 -11
- imap_processing/_version.py +2 -2
- imap_processing/ccsds/excel_to_xtce.py +65 -16
- imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +6 -28
- imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +365 -42
- imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +0 -5
- imap_processing/cdf/config/imap_hi_global_cdf_attrs.yaml +10 -11
- imap_processing/cdf/config/imap_hi_variable_attrs.yaml +17 -19
- imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +26 -13
- imap_processing/cdf/config/imap_hit_l1a_variable_attrs.yaml +106 -116
- imap_processing/cdf/config/imap_hit_l1b_variable_attrs.yaml +120 -145
- imap_processing/cdf/config/imap_hit_l2_variable_attrs.yaml +14 -0
- imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +6 -9
- imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_lo_global_cdf_attrs.yaml +0 -12
- imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +1 -1
- imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +9 -21
- imap_processing/cdf/config/imap_mag_l1a_variable_attrs.yaml +361 -0
- imap_processing/cdf/config/imap_mag_l1b_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_mag_l1c_variable_attrs.yaml +160 -0
- imap_processing/cdf/config/imap_spacecraft_global_cdf_attrs.yaml +18 -0
- imap_processing/cdf/config/imap_spacecraft_variable_attrs.yaml +40 -0
- imap_processing/cdf/config/imap_swapi_global_cdf_attrs.yaml +1 -5
- imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +12 -4
- imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +16 -2
- imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +48 -52
- imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +71 -47
- imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +2 -14
- imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +51 -2
- imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +29 -14
- imap_processing/cdf/utils.py +13 -7
- imap_processing/cli.py +23 -8
- imap_processing/codice/codice_l1a.py +207 -85
- imap_processing/codice/constants.py +1322 -568
- imap_processing/codice/decompress.py +2 -6
- imap_processing/ena_maps/ena_maps.py +480 -116
- imap_processing/ena_maps/utils/coordinates.py +19 -0
- imap_processing/ena_maps/utils/map_utils.py +14 -17
- imap_processing/ena_maps/utils/spatial_utils.py +45 -47
- imap_processing/hi/l1a/hi_l1a.py +24 -18
- imap_processing/hi/l1a/histogram.py +0 -1
- imap_processing/hi/l1a/science_direct_event.py +6 -8
- imap_processing/hi/l1b/hi_l1b.py +31 -39
- imap_processing/hi/l1c/hi_l1c.py +405 -17
- imap_processing/hi/utils.py +58 -12
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt0-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt1-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt2-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-standard-dt3-factors_20250219_v002.csv +205 -0
- imap_processing/hit/ancillary/imap_hit_l1b-to-l2-summed-dt0-factors_20250219_v002.csv +68 -0
- imap_processing/hit/hit_utils.py +173 -1
- imap_processing/hit/l0/constants.py +20 -11
- imap_processing/hit/l0/decom_hit.py +18 -4
- imap_processing/hit/l1a/hit_l1a.py +45 -54
- imap_processing/hit/l1b/constants.py +317 -0
- imap_processing/hit/l1b/hit_l1b.py +367 -18
- imap_processing/hit/l2/constants.py +281 -0
- imap_processing/hit/l2/hit_l2.py +614 -0
- imap_processing/hit/packet_definitions/hit_packet_definitions.xml +1323 -71
- imap_processing/ialirt/l0/mag_l0_ialirt_data.py +155 -0
- imap_processing/ialirt/l0/parse_mag.py +246 -0
- imap_processing/ialirt/l0/process_swe.py +252 -0
- imap_processing/ialirt/packet_definitions/ialirt.xml +7 -3
- imap_processing/ialirt/packet_definitions/ialirt_mag.xml +115 -0
- imap_processing/ialirt/utils/grouping.py +114 -0
- imap_processing/ialirt/utils/time.py +29 -0
- imap_processing/idex/atomic_masses.csv +22 -0
- imap_processing/idex/decode.py +2 -2
- imap_processing/idex/idex_constants.py +25 -0
- imap_processing/idex/idex_l1a.py +6 -7
- imap_processing/idex/idex_l1b.py +4 -31
- imap_processing/idex/idex_l2a.py +789 -0
- imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +39 -33
- imap_processing/lo/l0/lo_science.py +6 -0
- imap_processing/lo/l1a/lo_l1a.py +0 -1
- imap_processing/lo/l1b/lo_l1b.py +177 -25
- imap_processing/mag/constants.py +8 -0
- imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +6 -0
- imap_processing/mag/l0/decom_mag.py +10 -3
- imap_processing/mag/l1a/mag_l1a.py +22 -11
- imap_processing/mag/l1a/mag_l1a_data.py +28 -3
- imap_processing/mag/l1b/mag_l1b.py +190 -48
- imap_processing/mag/l1c/interpolation_methods.py +211 -0
- imap_processing/mag/l1c/mag_l1c.py +447 -9
- imap_processing/quality_flags.py +1 -0
- imap_processing/spacecraft/packet_definitions/scid_x252.xml +538 -0
- imap_processing/spacecraft/quaternions.py +123 -0
- imap_processing/spice/geometry.py +16 -19
- imap_processing/spice/repoint.py +120 -0
- imap_processing/swapi/l1/swapi_l1.py +4 -0
- imap_processing/swapi/l2/swapi_l2.py +0 -1
- imap_processing/swe/l1a/swe_l1a.py +47 -8
- imap_processing/swe/l1a/swe_science.py +5 -2
- imap_processing/swe/l1b/swe_l1b_science.py +103 -56
- imap_processing/swe/l2/swe_l2.py +60 -65
- imap_processing/swe/packet_definitions/swe_packet_definition.xml +1121 -1
- imap_processing/swe/utils/swe_constants.py +63 -0
- imap_processing/swe/utils/swe_utils.py +85 -28
- imap_processing/tests/ccsds/test_data/expected_output.xml +40 -1
- imap_processing/tests/ccsds/test_excel_to_xtce.py +23 -20
- imap_processing/tests/cdf/test_data/imap_instrument2_global_cdf_attrs.yaml +0 -2
- imap_processing/tests/codice/conftest.py +1 -1
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-omni_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-priorities_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-sectored_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
- imap_processing/tests/codice/test_codice_l1a.py +110 -46
- imap_processing/tests/codice/test_decompress.py +4 -4
- imap_processing/tests/conftest.py +166 -10
- imap_processing/tests/ena_maps/conftest.py +51 -0
- imap_processing/tests/ena_maps/test_ena_maps.py +638 -109
- imap_processing/tests/ena_maps/test_map_utils.py +66 -43
- imap_processing/tests/ena_maps/test_spatial_utils.py +16 -20
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208.bin +0 -0
- imap_processing/tests/hi/data/l0/H45_diag_fee_20250208_verify.csv +205 -0
- imap_processing/tests/hi/test_hi_l1b.py +12 -15
- imap_processing/tests/hi/test_hi_l1c.py +234 -6
- imap_processing/tests/hi/test_l1a.py +30 -0
- imap_processing/tests/hi/test_science_direct_event.py +1 -1
- imap_processing/tests/hi/test_utils.py +24 -2
- imap_processing/tests/hit/helpers/l1_validation.py +39 -39
- imap_processing/tests/hit/test_data/hskp_sample.ccsds +0 -0
- imap_processing/tests/hit/test_data/imap_hit_l0_raw_20100105_v001.pkts +0 -0
- imap_processing/tests/hit/test_decom_hit.py +4 -0
- imap_processing/tests/hit/test_hit_l1a.py +24 -28
- imap_processing/tests/hit/test_hit_l1b.py +304 -40
- imap_processing/tests/hit/test_hit_l2.py +454 -0
- imap_processing/tests/hit/test_hit_utils.py +112 -2
- imap_processing/tests/hit/validation_data/hskp_sample_eu_3_6_2025.csv +89 -0
- imap_processing/tests/hit/validation_data/hskp_sample_raw.csv +89 -88
- imap_processing/tests/ialirt/test_data/l0/461971383-404.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971384-405.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971385-406.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971386-407.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971387-408.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971388-409.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971389-410.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971390-411.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/461971391-412.bin +0 -0
- imap_processing/tests/ialirt/test_data/l0/sample_decoded_i-alirt_data.csv +383 -0
- imap_processing/tests/ialirt/unit/test_grouping.py +81 -0
- imap_processing/tests/ialirt/unit/test_parse_mag.py +168 -0
- imap_processing/tests/ialirt/unit/test_process_swe.py +208 -3
- imap_processing/tests/ialirt/unit/test_time.py +16 -0
- imap_processing/tests/idex/conftest.py +62 -6
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231218_v001.pkts +0 -0
- imap_processing/tests/idex/test_data/impact_14_tof_high_data.txt +4508 -4508
- imap_processing/tests/idex/test_idex_l1a.py +48 -4
- imap_processing/tests/idex/test_idex_l1b.py +3 -3
- imap_processing/tests/idex/test_idex_l2a.py +383 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf +0 -0
- imap_processing/tests/lo/test_lo_l1b.py +148 -4
- imap_processing/tests/lo/test_lo_science.py +1 -0
- imap_processing/tests/mag/conftest.py +69 -0
- imap_processing/tests/mag/test_mag_decom.py +1 -1
- imap_processing/tests/mag/test_mag_l1a.py +38 -0
- imap_processing/tests/mag/test_mag_l1b.py +34 -53
- imap_processing/tests/mag/test_mag_l1c.py +251 -20
- imap_processing/tests/mag/test_mag_validation.py +109 -25
- imap_processing/tests/mag/validation/L1b/T009/MAGScience-normal-(2,2)-8s-20250204-16h39.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T010/MAGScience-normal-(2,2)-8s-20250206-12h05.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +16 -16
- imap_processing/tests/mag/validation/L1b/T012/MAGScience-normal-(2,2)-8s-20250204-16h08.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/data.bin +0 -0
- imap_processing/tests/mag/validation/L1b/T012/field_like_all_ranges.txt +19200 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-cal.cdf +0 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-in.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-magi-out.csv +17 -0
- imap_processing/tests/mag/validation/L1b/T012/mag-l1a-l1b-t012-mago-out.csv +17 -0
- imap_processing/tests/mag/validation/imap_calibration_mag_20240229_v01.cdf +0 -0
- imap_processing/tests/spacecraft/__init__.py +0 -0
- imap_processing/tests/spacecraft/data/SSR_2024_190_20_08_12_0483851794_2_DA_apid0594_1packet.pkts +0 -0
- imap_processing/tests/spacecraft/test_quaternions.py +71 -0
- imap_processing/tests/spice/test_data/fake_repoint_data.csv +5 -0
- imap_processing/tests/spice/test_geometry.py +6 -9
- imap_processing/tests/spice/test_repoint.py +111 -0
- imap_processing/tests/swapi/test_swapi_l1.py +7 -3
- imap_processing/tests/swe/l0_data/2024051010_SWE_HK_packet.bin +0 -0
- imap_processing/tests/swe/l0_data/2024051011_SWE_CEM_RAW_packet.bin +0 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_APP_HK_20240510_092742.csv +49 -0
- imap_processing/tests/swe/l0_validation_data/idle_export_eu.SWE_CEM_RAW_20240510_092742.csv +593 -0
- imap_processing/tests/swe/test_swe_l1a.py +18 -0
- imap_processing/tests/swe/test_swe_l1a_cem_raw.py +52 -0
- imap_processing/tests/swe/test_swe_l1a_hk.py +68 -0
- imap_processing/tests/swe/test_swe_l1b_science.py +23 -4
- imap_processing/tests/swe/test_swe_l2.py +112 -30
- imap_processing/tests/test_cli.py +2 -2
- imap_processing/tests/test_utils.py +138 -16
- imap_processing/tests/ultra/data/l0/FM45_UltraFM45_Functional_2024-01-22T0105_20240122T010548.CCSDS +0 -0
- imap_processing/tests/ultra/data/l0/ultra45_raw_sc_ultraimgrates_20220530_00.csv +164 -0
- imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3243 -3243
- imap_processing/tests/ultra/data/mock_data.py +341 -0
- imap_processing/tests/ultra/unit/conftest.py +69 -26
- imap_processing/tests/ultra/unit/test_badtimes.py +2 -0
- imap_processing/tests/ultra/unit/test_cullingmask.py +4 -0
- imap_processing/tests/ultra/unit/test_de.py +12 -4
- imap_processing/tests/ultra/unit/test_decom_apid_881.py +44 -0
- imap_processing/tests/ultra/unit/test_spacecraft_pset.py +78 -0
- imap_processing/tests/ultra/unit/test_ultra_l1a.py +28 -12
- imap_processing/tests/ultra/unit/test_ultra_l1b.py +34 -6
- imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +22 -26
- imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -51
- imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +94 -52
- imap_processing/ultra/l0/decom_tools.py +6 -5
- imap_processing/ultra/l1a/ultra_l1a.py +28 -56
- imap_processing/ultra/l1b/de.py +72 -28
- imap_processing/ultra/l1b/extendedspin.py +12 -14
- imap_processing/ultra/l1b/ultra_l1b.py +34 -9
- imap_processing/ultra/l1b/ultra_l1b_culling.py +65 -29
- imap_processing/ultra/l1b/ultra_l1b_extended.py +64 -19
- imap_processing/ultra/l1c/spacecraft_pset.py +86 -0
- imap_processing/ultra/l1c/ultra_l1c.py +7 -4
- imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +112 -61
- imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
- imap_processing/ultra/utils/ultra_l1_utils.py +20 -2
- imap_processing/utils.py +68 -28
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/METADATA +8 -5
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/RECORD +250 -199
- imap_processing/cdf/config/imap_mag_l1_variable_attrs.yaml +0 -237
- imap_processing/hi/l1a/housekeeping.py +0 -27
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1a_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-omni_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hi-sectored_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_hskp_20100101_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-aggregated_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-counters-singles_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-nsw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-angular_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-priority_20240429_v001.cdf +0 -0
- imap_processing/tests/codice/data/imap_codice_l1b_lo-sw-species_20240429_v001.cdf +0 -0
- imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1251.pkts +0 -0
- imap_processing/tests/hit/PREFLIGHT_raw_record_2023_256_15_59_04_apid1252.pkts +0 -0
- imap_processing/tests/hit/validation_data/hskp_sample_eu.csv +0 -89
- imap_processing/tests/hit/validation_data/sci_sample_raw1.csv +0 -29
- imap_processing/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_de_20100101_v001.cdf +0 -0
- imap_processing/tests/lo/test_cdfs/imap_lo_l1a_spin_20100101_v001.cdf +0 -0
- imap_processing/tests/ultra/test_data/mock_data.py +0 -161
- imap_processing/ultra/l1c/pset.py +0 -40
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_40P_Phi28p5_BeamCal_LinearScan_phi28.50_theta-0.00_20240207T102740.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_7P_Phi0.0_BeamCal_LinearScan_phi0.04_theta-0.01_20230821T121304.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.CCSDS +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_auxdata_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_enaphxtofhangimg_FM45_TV_Cycle6_Hot_Ops_Front212_20240124T063837.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultraimgrates_Ultra45_EM_SwRI_Cal_Run7_ThetaScan_20220530T225054.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l0/ultra45_raw_sc_ultrarawimgevent_FM45_7P_Phi00_BeamCal_LinearScan_phi004_theta-001_20230821T121304.csv +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E1.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E12.cdf +0 -0
- /imap_processing/tests/ultra/{test_data → data}/l1/dps_exposure_helio_45_E24.cdf +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/LICENSE +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/WHEEL +0 -0
- {imap_processing-0.11.0.dist-info → imap_processing-0.12.0.dist-info}/entry_points.txt +0 -0
|
@@ -22,11 +22,10 @@ def test_idex_cdf_file(decom_test_data: xr.Dataset):
|
|
|
22
22
|
decom_test_data : xarray.Dataset
|
|
23
23
|
The dataset to test with
|
|
24
24
|
"""
|
|
25
|
-
|
|
26
25
|
file_name = write_cdf(decom_test_data)
|
|
27
26
|
|
|
28
27
|
assert file_name.exists()
|
|
29
|
-
assert file_name.name == "
|
|
28
|
+
assert file_name.name == "imap_idex_l1a_sci-1week_20231218_v001.cdf"
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
def test_bad_cdf_attributes(decom_test_data: xr.Dataset):
|
|
@@ -41,7 +40,7 @@ def test_bad_cdf_attributes(decom_test_data: xr.Dataset):
|
|
|
41
40
|
del decom_test_data["TOF_High"].attrs["CATDESC"]
|
|
42
41
|
|
|
43
42
|
with pytest.raises(ISTPError):
|
|
44
|
-
write_cdf(decom_test_data)
|
|
43
|
+
write_cdf(decom_test_data, istp=True, terminate_on_warning=True)
|
|
45
44
|
|
|
46
45
|
# Add attributes back so future tests do not fail
|
|
47
46
|
decom_test_data["TOF_High"].attrs["CATDESC"] = tof_catdesc
|
|
@@ -79,7 +78,7 @@ def test_bad_cdf_file_data(decom_test_data: xr.Dataset):
|
|
|
79
78
|
decom_test_data["Bad_data"] = bad_data_xr
|
|
80
79
|
|
|
81
80
|
with pytest.raises(ISTPError):
|
|
82
|
-
write_cdf(decom_test_data)
|
|
81
|
+
write_cdf(decom_test_data, istp=True, terminate_on_warning=True)
|
|
83
82
|
|
|
84
83
|
del decom_test_data["Bad_data"]
|
|
85
84
|
|
|
@@ -104,6 +103,51 @@ def test_idex_tof_high_data_from_cdf(decom_test_data: xr.Dataset):
|
|
|
104
103
|
assert (l1_data["TOF_High"][13].data == data).all()
|
|
105
104
|
|
|
106
105
|
|
|
106
|
+
def test_validate_l1a_idex_data_variables(
|
|
107
|
+
decom_test_data: xr.Dataset, l1a_example_data: xr.Dataset
|
|
108
|
+
):
|
|
109
|
+
"""
|
|
110
|
+
Verify that each of the 6 waveform and telemetry arrays are equal to the
|
|
111
|
+
corresponding array produced by the IDEX team using the same l0 file.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
decom_test_data : xarray.Dataset
|
|
117
|
+
The dataset to test with
|
|
118
|
+
l1a_example_data: xarray.Dataset
|
|
119
|
+
A dataset containing the 6 waveform and telemetry arrays
|
|
120
|
+
"""
|
|
121
|
+
# Lookup table to match the SDS array names to the Idex Team array names
|
|
122
|
+
match_variables = {
|
|
123
|
+
"TOF L": "TOF_Low",
|
|
124
|
+
"TOF H": "TOF_High",
|
|
125
|
+
"TOF M": "TOF_Mid",
|
|
126
|
+
"Target H": "Target_High",
|
|
127
|
+
"Target L": "Target_Low",
|
|
128
|
+
"Ion Grid": "Ion_Grid",
|
|
129
|
+
"Time (high sampling)": "time_high_sample_rate",
|
|
130
|
+
"Time (low sampling)": "time_low_sample_rate",
|
|
131
|
+
}
|
|
132
|
+
# The Engineering data is converting to UTC, and the SDC is converting to J2000,
|
|
133
|
+
# for 'epoch' and 'Timestamp' so this test is using the raw time value 'SCHOARSE' to
|
|
134
|
+
# validate time
|
|
135
|
+
arrays_to_skip = ["Timestamp", "Epoch", "event"]
|
|
136
|
+
|
|
137
|
+
# loop through all keys from the l1a example dict
|
|
138
|
+
for var in l1a_example_data.variables:
|
|
139
|
+
if var not in arrays_to_skip:
|
|
140
|
+
# Find the corresponding array name
|
|
141
|
+
cdf_var = match_variables.get(var, var.lower())
|
|
142
|
+
|
|
143
|
+
np.testing.assert_array_equal(
|
|
144
|
+
decom_test_data[cdf_var],
|
|
145
|
+
l1a_example_data[var],
|
|
146
|
+
f"The array '{cdf_var}' does not equal the expected example "
|
|
147
|
+
f"array '{var}' produced by the IDEX team",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
107
151
|
def test_compressed_packet():
|
|
108
152
|
"""
|
|
109
153
|
Test compressed data decompression against known non-compressed data.
|
|
@@ -65,7 +65,7 @@ def test_l1b_cdf_filenames(l1b_dataset: xr.Dataset):
|
|
|
65
65
|
l1b_dataset : xr.Dataset
|
|
66
66
|
A ``xarray`` dataset containing the test data
|
|
67
67
|
"""
|
|
68
|
-
expected_src = "imap_idex_l1b_sci"
|
|
68
|
+
expected_src = "imap_idex_l1b_sci-1week"
|
|
69
69
|
assert l1b_dataset.attrs["Logical_source"] == expected_src
|
|
70
70
|
|
|
71
71
|
|
|
@@ -81,7 +81,7 @@ def test_idex_cdf_file(l1b_dataset: xr.Dataset):
|
|
|
81
81
|
file_name = write_cdf(l1b_dataset)
|
|
82
82
|
|
|
83
83
|
assert file_name.exists()
|
|
84
|
-
assert file_name.name == "
|
|
84
|
+
assert file_name.name == "imap_idex_l1b_sci-1week_20231218_v001.cdf"
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
def test_idex_waveform_units(l1b_dataset: xr.Dataset):
|
|
@@ -100,7 +100,7 @@ def test_idex_waveform_units(l1b_dataset: xr.Dataset):
|
|
|
100
100
|
# Check instrument setting units
|
|
101
101
|
for _, row in cdf_var_defs.iterrows():
|
|
102
102
|
var_name = row["mnemonic"]
|
|
103
|
-
assert l1b_dataset[var_name].attrs["
|
|
103
|
+
assert l1b_dataset[var_name].attrs["UNITS"] == row["unit"]
|
|
104
104
|
|
|
105
105
|
# Check waveform units
|
|
106
106
|
waveform_var_names = [
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
"""Tests the L2a processing for IDEX data"""
|
|
2
|
+
|
|
3
|
+
from unittest import mock
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pytest
|
|
7
|
+
import xarray as xr
|
|
8
|
+
from scipy.stats import exponnorm
|
|
9
|
+
|
|
10
|
+
from imap_processing.idex import idex_constants
|
|
11
|
+
from imap_processing.idex.idex_l1b import idex_l1b
|
|
12
|
+
from imap_processing.idex.idex_l2a import (
|
|
13
|
+
BaselineNoiseTime,
|
|
14
|
+
analyze_peaks,
|
|
15
|
+
butter_lowpass_filter,
|
|
16
|
+
calculate_kappa,
|
|
17
|
+
calculate_snr,
|
|
18
|
+
estimate_dust_mass,
|
|
19
|
+
fit_impact,
|
|
20
|
+
idex_l2a,
|
|
21
|
+
remove_signal_noise,
|
|
22
|
+
time_to_mass,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def mock_microphonics_noise(time: np.ndarray) -> np.ndarray:
|
|
27
|
+
"""Function to mock signal noise (linear and sine wave) due to microphonics."""
|
|
28
|
+
noise_frequency = idex_constants.TARGET_NOISE_FREQUENCY
|
|
29
|
+
phase_shift = 45
|
|
30
|
+
amp = 10
|
|
31
|
+
# Create a sine wave signal
|
|
32
|
+
sine_signal = amp * np.sin(2 * np.pi * noise_frequency * time + phase_shift)
|
|
33
|
+
# Combine the sine wave signals with a linear signal to create noise
|
|
34
|
+
combined_sig = sine_signal + (time * 5)
|
|
35
|
+
|
|
36
|
+
return combined_sig
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture(scope="module")
|
|
40
|
+
def l2a_dataset(decom_test_data: xr.Dataset) -> xr.Dataset:
|
|
41
|
+
"""Return a ``xarray`` dataset containing test data.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
dataset : xr.Dataset
|
|
46
|
+
A ``xarray`` dataset containing the test data
|
|
47
|
+
"""
|
|
48
|
+
with mock.patch("imap_processing.idex.idex_l1b.get_spice_data", return_value={}):
|
|
49
|
+
dataset = idex_l2a(
|
|
50
|
+
idex_l1b(decom_test_data, data_version="001"), data_version="001"
|
|
51
|
+
)
|
|
52
|
+
return dataset
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_l2a_cdf_filenames(l2a_dataset: xr.Dataset):
|
|
56
|
+
"""Tests that the ``idex_l2a`` function generates datasets
|
|
57
|
+
with the expected logical source.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
l2a_dataset : xr.Dataset
|
|
62
|
+
A ``xarray`` dataset containing the test data
|
|
63
|
+
"""
|
|
64
|
+
expected_src = "imap_idex_l2a_sci-1week"
|
|
65
|
+
assert l2a_dataset.attrs["Logical_source"] == expected_src
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_l2a_cdf_variables(l2a_dataset: xr.Dataset):
|
|
69
|
+
"""Tests that the ``idex_l2a`` function generates datasets
|
|
70
|
+
with the expected variables.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
l2a_dataset : xr.Dataset
|
|
75
|
+
A ``xarray`` dataset containing the test data
|
|
76
|
+
"""
|
|
77
|
+
expected_vars = [
|
|
78
|
+
"mass",
|
|
79
|
+
"target_low_fit_parameters",
|
|
80
|
+
"target_low_fit_imapct_charge",
|
|
81
|
+
"target_low_fit_imapct_mass_estimate",
|
|
82
|
+
"target_low_chi_squared",
|
|
83
|
+
"target_low_reduced_chi_squared",
|
|
84
|
+
"target_low_fit_results",
|
|
85
|
+
"target_high_fit_parameters",
|
|
86
|
+
"target_high_fit_imapct_charge",
|
|
87
|
+
"target_high_fit_imapct_mass_estimate",
|
|
88
|
+
"target_high_chi_squared",
|
|
89
|
+
"target_high_reduced_chi_squared",
|
|
90
|
+
"target_high_fit_results",
|
|
91
|
+
"ion_grid_fit_parameters",
|
|
92
|
+
"ion_grid_fit_imapct_charge",
|
|
93
|
+
"ion_grid_fit_imapct_mass_estimate",
|
|
94
|
+
"ion_grid_chi_squared",
|
|
95
|
+
"ion_grid_reduced_chi_squared",
|
|
96
|
+
"ion_grid_fit_results",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
cdf_vars = l2a_dataset.variables
|
|
100
|
+
for var in expected_vars:
|
|
101
|
+
assert var in cdf_vars
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_time_to_mass_zero_lag():
|
|
105
|
+
"""
|
|
106
|
+
Tests that the time_to_mass function correctly converts time-of-flight
|
|
107
|
+
to a mass scale using known peak positions.
|
|
108
|
+
"""
|
|
109
|
+
carbon_mass = 12
|
|
110
|
+
masses = np.asarray([1, 4, 9])
|
|
111
|
+
|
|
112
|
+
expected_lag = 10
|
|
113
|
+
expected_stretch = 1500
|
|
114
|
+
# Create a 2d time of flight array exactly where we would expect the peaks to be
|
|
115
|
+
# Each mass should appear at time t = 1400 * sqrt(m) ns
|
|
116
|
+
tof = np.zeros((15, int(np.sqrt(masses[-1]) * expected_stretch + 1 + expected_lag)))
|
|
117
|
+
min_stretch = 1400
|
|
118
|
+
# Mass 1 expected tof
|
|
119
|
+
tof[:-1, min_stretch] = 1
|
|
120
|
+
# Mass 4 expected tof
|
|
121
|
+
tof[:-1, min_stretch * 2] = 1
|
|
122
|
+
# Mass 9 expected tof
|
|
123
|
+
tof[:-1, min_stretch * 3] = 1
|
|
124
|
+
# Change the last TOF array to be shifted and 'stretched'
|
|
125
|
+
# Mass 1 expected tof
|
|
126
|
+
tof[-1, expected_stretch + expected_lag] = 1
|
|
127
|
+
# Mass 4 expected tof
|
|
128
|
+
tof[-1, expected_stretch * 2 + expected_lag] = 1
|
|
129
|
+
# Mass 9 expected tof
|
|
130
|
+
tof[-1, expected_stretch * 3 + expected_lag] = 1
|
|
131
|
+
|
|
132
|
+
time = np.tile(np.arange(len(tof[0])), (15, 1))
|
|
133
|
+
stretch, shift, mass_scale = time_to_mass(tof, time, masses)
|
|
134
|
+
|
|
135
|
+
# Test with carbon mass
|
|
136
|
+
carbon_time = (stretch[0] * np.sqrt(carbon_mass)) / 1e-6 # Convert ms to s
|
|
137
|
+
mass = np.interp(carbon_time, time[0], mass_scale[0])
|
|
138
|
+
assert np.allclose(carbon_mass, mass, rtol=1e-2)
|
|
139
|
+
|
|
140
|
+
# Test shift is zero since peaks are aligned
|
|
141
|
+
assert np.all(shift[:-1] == 0)
|
|
142
|
+
# Test stretch factor matches expected 1400 ns in seconds
|
|
143
|
+
assert np.all(stretch[:-1] == 1400 * 1e-9)
|
|
144
|
+
# Test output shape
|
|
145
|
+
assert mass_scale.shape == time.shape
|
|
146
|
+
# Test that the last shift and stretch are the expected values
|
|
147
|
+
assert shift[-1] == -expected_lag * idex_constants.FM_SAMPLING_RATE
|
|
148
|
+
# Test stretch factor matches expected 1400 ns in seconds
|
|
149
|
+
assert stretch[-1] == expected_stretch * 1e-9
|
|
150
|
+
|
|
151
|
+
# Test with carbon mass
|
|
152
|
+
carbon_time = (stretch[-1] * np.sqrt(carbon_mass) + shift[-1]) / 1e-6
|
|
153
|
+
mass = np.interp(carbon_time, time[-1], mass_scale[-1])
|
|
154
|
+
assert np.allclose(carbon_mass, mass, rtol=1e-2)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def test_time_to_mass_zero_correlation_warning(caplog):
|
|
158
|
+
"""
|
|
159
|
+
Tests that the time_to_mass function correctly logs a warning if zero correlations
|
|
160
|
+
are found between the TOF and expected mass times array.
|
|
161
|
+
"""
|
|
162
|
+
masses = np.asarray([1, 4, 9])
|
|
163
|
+
# Create a time of flight array that will result in no correlation between the
|
|
164
|
+
# Expected tof peaks.
|
|
165
|
+
tof = np.zeros((10, 8000))
|
|
166
|
+
time = np.tile(np.arange(len(tof[0])), (10, 1))
|
|
167
|
+
with caplog.at_level("WARNING"):
|
|
168
|
+
time_to_mass(tof, time, masses)
|
|
169
|
+
|
|
170
|
+
assert any(
|
|
171
|
+
"There are no correlations found between the"
|
|
172
|
+
" TOF array and the expected mass times array" in message
|
|
173
|
+
for message in caplog.text.splitlines()
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_calculate_kappa():
|
|
178
|
+
"""Tests the functionality of calculate_kappa()."""
|
|
179
|
+
# Create a 2d list of peak indices
|
|
180
|
+
peaks = [[0, 1], [1, 2], [0, 1, 2]]
|
|
181
|
+
|
|
182
|
+
# Create mass_scales array
|
|
183
|
+
mass_scales = np.array(
|
|
184
|
+
[
|
|
185
|
+
[1.2, 2.2, 3.2], # The kappa value for peaks 0,1 should be .2
|
|
186
|
+
[1.4, 2.4, 3.4], # The kappa value for peaks 1,2 should be .4
|
|
187
|
+
[1.7, 2.7, 3.7], # The kappa value for peaks 2,3,4 should be -0.3
|
|
188
|
+
]
|
|
189
|
+
)
|
|
190
|
+
kappas = calculate_kappa(mass_scales, peaks)
|
|
191
|
+
|
|
192
|
+
assert np.allclose(list(kappas), [0.2, 0.4, -0.3], rtol=1e-12)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_calculate_snr():
|
|
196
|
+
"""Tests the functionality of calculate_snr()."""
|
|
197
|
+
step = 0.5
|
|
198
|
+
max_tof = 10
|
|
199
|
+
time = np.arange(BaselineNoiseTime.START, 5, step)
|
|
200
|
+
|
|
201
|
+
# Create a baseline noise array with an std of 1 and mean of 1
|
|
202
|
+
baseline_noise = np.asarray([0, 0, 1, 2, 2])
|
|
203
|
+
signal_length = len(time) - len(baseline_noise)
|
|
204
|
+
tof_signal = np.full(int(signal_length), max_tof)
|
|
205
|
+
|
|
206
|
+
tof = np.tile(np.append(baseline_noise, tof_signal), (3, 1))
|
|
207
|
+
time = np.tile(time, (3, 1))
|
|
208
|
+
|
|
209
|
+
snr = calculate_snr(tof, time)
|
|
210
|
+
|
|
211
|
+
# Since std=1 and mean=1, SNR should be (max_tof - mean)/std
|
|
212
|
+
assert np.all(snr == (max_tof - 1))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_calculate_snr_warning(caplog):
|
|
216
|
+
"""Tests that calculate_snr() throws warning if no baseline noise is found."""
|
|
217
|
+
time = np.tile(np.arange(10), (3, 1))
|
|
218
|
+
tof = np.ones_like(time)
|
|
219
|
+
|
|
220
|
+
with caplog.at_level("WARNING"):
|
|
221
|
+
calculate_snr(tof, time)
|
|
222
|
+
assert any(
|
|
223
|
+
"Unable to find baseline noise" in message
|
|
224
|
+
for message in caplog.text.splitlines()
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_analyze_peaks_warning(caplog):
|
|
229
|
+
"""Tests that analyze_peaks() throws warning if the emg curve fit fails."""
|
|
230
|
+
# Create a 2d list of peak indices
|
|
231
|
+
peaks = [[2]]
|
|
232
|
+
time = xr.DataArray(np.arange(6))
|
|
233
|
+
# When there is a flat signal for TOF, we expect the fit to fail and a
|
|
234
|
+
# warning to be logged.
|
|
235
|
+
tof = np.ones_like(time)
|
|
236
|
+
mass_scale = np.ones_like(time)
|
|
237
|
+
with caplog.at_level("WARNING"):
|
|
238
|
+
fit_params, area_under_curve = analyze_peaks(tof, time, mass_scale, 0, peaks)
|
|
239
|
+
assert any(
|
|
240
|
+
"Failed to fit EMG curve" in message for message in caplog.text.splitlines()
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# The fit_params and area_under_curve arrays should be zero
|
|
244
|
+
assert np.all(fit_params == 0)
|
|
245
|
+
assert np.all(area_under_curve == 0)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def test_analyze_peaks_perfect_fits():
|
|
249
|
+
"""Tests that analyze_peaks() returns the expected fit params and areas."""
|
|
250
|
+
event = 0
|
|
251
|
+
# Create a 2d list of peak indices
|
|
252
|
+
peak_1 = 7
|
|
253
|
+
peak_2 = 25
|
|
254
|
+
peak_3 = 80
|
|
255
|
+
# Create tof array of ones
|
|
256
|
+
time = xr.DataArray(np.arange(100))
|
|
257
|
+
tof = np.zeros(100)
|
|
258
|
+
mass_scale = np.arange(100) + 0.5
|
|
259
|
+
# Only test peaks[0] this function is not vectorized but we pass in the full 2d peak
|
|
260
|
+
# array.
|
|
261
|
+
peaks = [np.asarray([peak_1, peak_2, peak_3]), np.asarray([])]
|
|
262
|
+
sigma = 2.0
|
|
263
|
+
lam = 1.0
|
|
264
|
+
k = 1 / (lam * sigma)
|
|
265
|
+
# Create a tof array with an emg curve at each peak
|
|
266
|
+
for peak in peaks[event]:
|
|
267
|
+
# Create a perfect emg curve
|
|
268
|
+
mu = peak - 0.4
|
|
269
|
+
gauss = exponnorm.pdf(time.data, k, mu, sigma)
|
|
270
|
+
tof[peak - 5 : peak + 6] = gauss[peak - 5 : peak + 6]
|
|
271
|
+
|
|
272
|
+
fit_params, area_under_curve = analyze_peaks(tof, time, mass_scale, event, peaks)
|
|
273
|
+
|
|
274
|
+
for peak in peaks[event]:
|
|
275
|
+
mu = peak - 0.4
|
|
276
|
+
mass = round(mass_scale[round(mu)])
|
|
277
|
+
# Test that the fitted parameters at the mass index match our input parameters
|
|
278
|
+
assert np.allclose(fit_params[mass], np.asarray([mu, sigma, lam]), rtol=1e-12)
|
|
279
|
+
# Test that there is a value greater than zero at this index
|
|
280
|
+
assert area_under_curve[mass] > 0
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def test_estimate_dust_mass_no_noise_removal():
|
|
284
|
+
"""
|
|
285
|
+
Test that estimate_dust_mass() is fitting the signal properly when there is no
|
|
286
|
+
noise removal.
|
|
287
|
+
"""
|
|
288
|
+
# TODO: The IDEX team is iterating on this function and will provide more
|
|
289
|
+
# information soon.
|
|
290
|
+
start_time = -60
|
|
291
|
+
total_low_sampling_microseconds = 126.03 # see algorithm document.
|
|
292
|
+
num_samples = 512
|
|
293
|
+
|
|
294
|
+
# Create realistic low sampling time
|
|
295
|
+
time = xr.DataArray(
|
|
296
|
+
np.linspace(
|
|
297
|
+
start_time, total_low_sampling_microseconds - start_time, num_samples
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
signal = xr.DataArray(
|
|
301
|
+
fit_impact(
|
|
302
|
+
time.data,
|
|
303
|
+
time_of_impact=0.0,
|
|
304
|
+
constant_offset=1.0,
|
|
305
|
+
amplitude=10.0,
|
|
306
|
+
rise_time=0.371,
|
|
307
|
+
discharge_time=0.371,
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
param, sig_amp, chisqr, redchi, result = estimate_dust_mass(
|
|
311
|
+
time, signal, remove_noise=False
|
|
312
|
+
)
|
|
313
|
+
# Assert that the chi square value indicates a very good fit
|
|
314
|
+
assert chisqr <= 1e-12
|
|
315
|
+
|
|
316
|
+
assert np.allclose(result, signal)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def test_lowpass_filter():
|
|
320
|
+
"""
|
|
321
|
+
Tests that the lowpass filter is filtering out high frequency signals.
|
|
322
|
+
|
|
323
|
+
Look at
|
|
324
|
+
https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.filtfilt.html#scipy.signal.filtfilt
|
|
325
|
+
for the source of the testing example.
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
time = np.linspace(-60, 60, 512)
|
|
329
|
+
# Calculate nyquist frequency to help get cutoff.
|
|
330
|
+
# This is the highest frequency that can be captured
|
|
331
|
+
time_between_samples = time[1] - time[0]
|
|
332
|
+
nqf = (1 / time_between_samples) / 2
|
|
333
|
+
# Choose cutoff of 0.125 times the Nyquist frequency
|
|
334
|
+
cutoff = nqf * 0.125
|
|
335
|
+
# Create two signals with different frequencies and combine them
|
|
336
|
+
low_freq = cutoff / 4 # Lower than cutoff
|
|
337
|
+
high_freq = nqf # The nyquist frequency is much higher than the cutoff
|
|
338
|
+
# Create sine signals
|
|
339
|
+
signal_low = np.sin(2 * np.pi * low_freq * time)
|
|
340
|
+
signal_high = np.sin(2 * np.pi * high_freq * time)
|
|
341
|
+
combined_sig = signal_low + signal_high
|
|
342
|
+
# The filter should filter out the high frequency signal
|
|
343
|
+
filtered_sig = butter_lowpass_filter(time, combined_sig, cutoff)
|
|
344
|
+
# Assert that the filtered signal is relatively close to the original low
|
|
345
|
+
# frequency signal.
|
|
346
|
+
np.allclose(filtered_sig, signal_low)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def test_remove_signal_noise():
|
|
350
|
+
"""
|
|
351
|
+
Tests that remove_signal_noise() function is filtering out sine wave and linear
|
|
352
|
+
noise due to "microphonics"
|
|
353
|
+
"""
|
|
354
|
+
start_time = -60
|
|
355
|
+
total_low_sampling_microseconds = 126.03 # see algorithm document.
|
|
356
|
+
num_samples = 512
|
|
357
|
+
|
|
358
|
+
# Create realistic low sampling time
|
|
359
|
+
time = np.linspace(
|
|
360
|
+
start_time, total_low_sampling_microseconds - start_time, num_samples
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
mask = time <= (start_time + total_low_sampling_microseconds) / 2
|
|
364
|
+
noisy_signal = mock_microphonics_noise(time)
|
|
365
|
+
# Filter signal
|
|
366
|
+
filtered_sig = remove_signal_noise(time, noisy_signal, mask)
|
|
367
|
+
|
|
368
|
+
np.allclose(filtered_sig, np.zeros_like(filtered_sig), atol=1e-2)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def test_remove_signal_noise_no_sine_wave(caplog):
|
|
372
|
+
"""
|
|
373
|
+
Tests that remove_signal_noise() function filters linear noise when there is no
|
|
374
|
+
sine wave.
|
|
375
|
+
"""
|
|
376
|
+
time = np.linspace(-60, 60, 512)
|
|
377
|
+
# linear signal to create noise
|
|
378
|
+
signal = time * 10
|
|
379
|
+
mask = time <= 0.5
|
|
380
|
+
# Filter signal
|
|
381
|
+
filtered_sig = remove_signal_noise(time, signal, mask)
|
|
382
|
+
# Test that the filtered signal is close to zero
|
|
383
|
+
assert np.allclose(filtered_sig, np.zeros_like(filtered_sig), rtol=1e-24)
|
|
Binary file
|
|
Binary file
|
|
@@ -1,18 +1,53 @@
|
|
|
1
1
|
from collections import namedtuple
|
|
2
2
|
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
import xarray as xr
|
|
6
|
+
|
|
3
7
|
from imap_processing import imap_module_directory
|
|
4
8
|
from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
|
|
5
9
|
from imap_processing.cdf.utils import load_cdf
|
|
6
|
-
from imap_processing.lo.l1b.lo_l1b import
|
|
10
|
+
from imap_processing.lo.l1b.lo_l1b import (
|
|
11
|
+
convert_start_end_acq_times,
|
|
12
|
+
create_datasets,
|
|
13
|
+
get_avg_spin_durations,
|
|
14
|
+
get_spin_angle,
|
|
15
|
+
initialize_l1b_de,
|
|
16
|
+
lo_l1b,
|
|
17
|
+
set_spin_bin,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture()
|
|
22
|
+
def dependencies():
|
|
23
|
+
return {
|
|
24
|
+
"imap_lo_l1a_de": load_cdf(
|
|
25
|
+
imap_module_directory
|
|
26
|
+
/ "tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf"
|
|
27
|
+
),
|
|
28
|
+
"imap_lo_l1a_spin": load_cdf(
|
|
29
|
+
imap_module_directory
|
|
30
|
+
/ "tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf"
|
|
31
|
+
),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture()
|
|
36
|
+
def attr_mgr_l1b():
|
|
37
|
+
attr_mgr_l1b = ImapCdfAttributes()
|
|
38
|
+
attr_mgr_l1b.add_instrument_global_attrs(instrument="lo")
|
|
39
|
+
attr_mgr_l1b.add_instrument_variable_attrs(instrument="lo", level="l1b")
|
|
40
|
+
attr_mgr_l1b.add_global_attribute("Data_version", "000")
|
|
41
|
+
return attr_mgr_l1b
|
|
7
42
|
|
|
8
43
|
|
|
9
44
|
def test_lo_l1b():
|
|
10
45
|
# Arrange
|
|
11
46
|
de_file = (
|
|
12
|
-
imap_module_directory / "tests/lo/test_cdfs/
|
|
47
|
+
imap_module_directory / "tests/lo/test_cdfs/imap_lo_l1a_de_20241022_v002.cdf"
|
|
13
48
|
)
|
|
14
49
|
spin_file = (
|
|
15
|
-
imap_module_directory / "tests/lo/test_cdfs/
|
|
50
|
+
imap_module_directory / "tests/lo/test_cdfs/imap_lo_l1a_spin_20241022_v002.cdf"
|
|
16
51
|
)
|
|
17
52
|
data = {}
|
|
18
53
|
for file in [de_file, spin_file]:
|
|
@@ -24,7 +59,7 @@ def test_lo_l1b():
|
|
|
24
59
|
output_file = lo_l1b(data, "001")
|
|
25
60
|
|
|
26
61
|
# Assert
|
|
27
|
-
assert expected_logical_source == output_file.attrs["Logical_source"]
|
|
62
|
+
assert expected_logical_source == output_file[0].attrs["Logical_source"]
|
|
28
63
|
|
|
29
64
|
|
|
30
65
|
def test_create_datasets():
|
|
@@ -72,3 +107,112 @@ def test_create_datasets():
|
|
|
72
107
|
assert dataset.badtime.shape[0] == 3
|
|
73
108
|
assert len(dataset.esa_step.shape) == 1
|
|
74
109
|
assert dataset.esa_step.shape[0] == 3
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_initialize_dataset(dependencies, attr_mgr_l1b):
|
|
113
|
+
# Arrange
|
|
114
|
+
l1a_de = dependencies["imap_lo_l1a_de"]
|
|
115
|
+
logical_source = "imap_lo_l1b_de"
|
|
116
|
+
|
|
117
|
+
# Act
|
|
118
|
+
l1b_de = initialize_l1b_de(l1a_de, attr_mgr_l1b, logical_source)
|
|
119
|
+
|
|
120
|
+
# Assert
|
|
121
|
+
assert l1b_de.attrs["Logical_source"] == logical_source
|
|
122
|
+
assert list(l1b_de.coords.keys()) == []
|
|
123
|
+
assert len(l1b_de.data_vars) == 4
|
|
124
|
+
assert len(l1b_de.coords) == 0
|
|
125
|
+
for l1b_name, l1a_name in {
|
|
126
|
+
"pos": "pos",
|
|
127
|
+
"mode": "mode",
|
|
128
|
+
"absent": "coincidence_type",
|
|
129
|
+
"esa_step": "esa_step",
|
|
130
|
+
}.items():
|
|
131
|
+
assert l1b_name in l1b_de.data_vars
|
|
132
|
+
np.testing.assert_array_equal(l1b_de[l1b_name], l1a_de[l1a_name])
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_convert_start_end_acq_times():
|
|
136
|
+
# Arrange
|
|
137
|
+
spin = xr.Dataset(
|
|
138
|
+
{
|
|
139
|
+
"acq_start_sec": ("epoch", [1, 2, 3]),
|
|
140
|
+
"acq_start_subsec": ("epoch", [4, 5, 6]),
|
|
141
|
+
"acq_end_sec": ("epoch", [7, 8, 9]),
|
|
142
|
+
"acq_end_subsec": ("epoch", [10, 11, 12]),
|
|
143
|
+
},
|
|
144
|
+
coords={"epoch": [0, 1, 2]},
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
acq_start_expected = xr.DataArray(
|
|
148
|
+
[
|
|
149
|
+
spin["acq_start_sec"][0] + spin["acq_start_subsec"][0] * 1e-6,
|
|
150
|
+
spin["acq_start_sec"][1] + spin["acq_start_subsec"][1] * 1e-6,
|
|
151
|
+
spin["acq_start_sec"][2] + spin["acq_start_subsec"][2] * 1e-6,
|
|
152
|
+
],
|
|
153
|
+
dims="epoch",
|
|
154
|
+
)
|
|
155
|
+
acq_end_expected = xr.DataArray(
|
|
156
|
+
[
|
|
157
|
+
spin["acq_end_sec"][0] + spin["acq_end_subsec"][0] * 1e-6,
|
|
158
|
+
spin["acq_end_sec"][1] + spin["acq_end_subsec"][1] * 1e-6,
|
|
159
|
+
spin["acq_end_sec"][2] + spin["acq_end_subsec"][2] * 1e-6,
|
|
160
|
+
],
|
|
161
|
+
dims="epoch",
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Act
|
|
165
|
+
acq_start, acq_end = convert_start_end_acq_times(spin)
|
|
166
|
+
|
|
167
|
+
# Assert
|
|
168
|
+
np.testing.assert_array_equal(acq_start.values, acq_start_expected.values)
|
|
169
|
+
np.testing.assert_array_equal(acq_end.values, acq_end_expected.values)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_get_avg_spin_durations():
|
|
173
|
+
# Arrange
|
|
174
|
+
acq_start = xr.DataArray([0, 423, 846.2], dims="epoch")
|
|
175
|
+
acq_end = xr.DataArray([422.8, 846, 1269.7], dims="epoch")
|
|
176
|
+
expected_avg_spin_durations = np.array([422.8, 423, 423.5]) / 28
|
|
177
|
+
|
|
178
|
+
# Act
|
|
179
|
+
avg_spin_durations = get_avg_spin_durations(acq_start, acq_end)
|
|
180
|
+
|
|
181
|
+
# Assert
|
|
182
|
+
np.testing.assert_array_equal(avg_spin_durations, expected_avg_spin_durations)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_get_spin_angle():
|
|
186
|
+
# Arrange
|
|
187
|
+
de = xr.Dataset(
|
|
188
|
+
{
|
|
189
|
+
"de_count": ("epoch", [2, 3]),
|
|
190
|
+
"de_time": ("direct_event", [0000, 1000, 2000, 3000, 4000]),
|
|
191
|
+
},
|
|
192
|
+
coords={"epoch": [0, 1], "direct_event": [0, 1, 2, 3, 4]},
|
|
193
|
+
)
|
|
194
|
+
spin_angle_expected = np.array([0, 87.89, 175.78, 263.67, 351.56])
|
|
195
|
+
|
|
196
|
+
# Act
|
|
197
|
+
spin_angle = get_spin_angle(de)
|
|
198
|
+
|
|
199
|
+
# Assert
|
|
200
|
+
np.testing.assert_allclose(
|
|
201
|
+
spin_angle,
|
|
202
|
+
spin_angle_expected,
|
|
203
|
+
atol=1e-2,
|
|
204
|
+
err_msg=f"Spin angle: {spin_angle} vs {spin_angle_expected}",
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_spin_bin():
|
|
209
|
+
# Arrange
|
|
210
|
+
l1b_de = xr.Dataset()
|
|
211
|
+
spin_angle = np.array([0, 50, 150, 250, 365])
|
|
212
|
+
expected_spin_bins = np.array([0, 8, 25, 41, 60])
|
|
213
|
+
|
|
214
|
+
# Act
|
|
215
|
+
l1b_de = set_spin_bin(l1b_de, spin_angle)
|
|
216
|
+
|
|
217
|
+
# Assert
|
|
218
|
+
np.testing.assert_array_equal(l1b_de["spin_bin"], expected_spin_bins)
|
|
@@ -261,6 +261,7 @@ def test_combine_segmented_packets(segmented_pkts_fake_data):
|
|
|
261
261
|
),
|
|
262
262
|
)
|
|
263
263
|
np.testing.assert_array_equal(dataset["epoch"].values, np.array([0, 10, 30]))
|
|
264
|
+
np.testing.assert_array_equal(dataset["met"].values, np.array([0, 10, 30]))
|
|
264
265
|
|
|
265
266
|
|
|
266
267
|
def test_validate_parse_events(sample_data, attr_mgr):
|