imap-processing 0.9.0__py3-none-any.whl → 0.11.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.

Files changed (243) hide show
  1. imap_processing/_version.py +2 -2
  2. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +749 -442
  3. imap_processing/cdf/config/imap_glows_global_cdf_attrs.yaml +7 -0
  4. imap_processing/cdf/config/imap_glows_l1a_variable_attrs.yaml +8 -2
  5. imap_processing/cdf/config/imap_glows_l1b_variable_attrs.yaml +0 -1
  6. imap_processing/cdf/config/imap_glows_l2_variable_attrs.yaml +358 -0
  7. imap_processing/cdf/config/imap_hi_variable_attrs.yaml +59 -25
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +22 -0
  9. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +32 -8
  10. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +94 -5
  11. imap_processing/cdf/config/imap_lo_l1a_variable_attrs.yaml +65 -37
  12. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +16 -1
  13. imap_processing/cdf/config/imap_swe_global_cdf_attrs.yaml +7 -0
  14. imap_processing/cdf/config/imap_swe_l1a_variable_attrs.yaml +14 -14
  15. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +25 -24
  16. imap_processing/cdf/config/imap_swe_l2_variable_attrs.yaml +238 -0
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +100 -92
  18. imap_processing/cdf/utils.py +2 -2
  19. imap_processing/cli.py +45 -9
  20. imap_processing/codice/codice_l1a.py +104 -58
  21. imap_processing/codice/constants.py +111 -155
  22. imap_processing/codice/data/esa_sweep_values.csv +256 -256
  23. imap_processing/codice/data/lo_stepping_values.csv +128 -128
  24. imap_processing/ena_maps/ena_maps.py +519 -0
  25. imap_processing/ena_maps/utils/map_utils.py +145 -0
  26. imap_processing/ena_maps/utils/spatial_utils.py +226 -0
  27. imap_processing/glows/__init__.py +3 -0
  28. imap_processing/glows/ancillary/imap_glows_pipeline_settings_v001.json +52 -0
  29. imap_processing/glows/l1a/glows_l1a.py +72 -14
  30. imap_processing/glows/l1b/glows_l1b.py +2 -1
  31. imap_processing/glows/l1b/glows_l1b_data.py +25 -1
  32. imap_processing/glows/l2/glows_l2.py +324 -0
  33. imap_processing/glows/l2/glows_l2_data.py +156 -51
  34. imap_processing/hi/l1a/science_direct_event.py +57 -51
  35. imap_processing/hi/l1b/hi_l1b.py +43 -28
  36. imap_processing/hi/l1c/hi_l1c.py +225 -42
  37. imap_processing/hi/utils.py +20 -3
  38. imap_processing/hit/l0/constants.py +2 -2
  39. imap_processing/hit/l0/decom_hit.py +1 -1
  40. imap_processing/hit/l1a/hit_l1a.py +94 -13
  41. imap_processing/hit/l1b/hit_l1b.py +158 -9
  42. imap_processing/ialirt/l0/process_codicehi.py +156 -0
  43. imap_processing/ialirt/l0/process_codicelo.py +5 -2
  44. imap_processing/ialirt/packet_definitions/ialirt.xml +28 -20
  45. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +241 -0
  46. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +170 -0
  47. imap_processing/ialirt/packet_definitions/ialirt_swe.xml +258 -0
  48. imap_processing/ialirt/process_ephemeris.py +72 -40
  49. imap_processing/idex/decode.py +241 -0
  50. imap_processing/idex/idex_l1a.py +143 -81
  51. imap_processing/idex/idex_l1b.py +244 -10
  52. imap_processing/lo/l0/lo_science.py +61 -0
  53. imap_processing/lo/l1a/lo_l1a.py +98 -10
  54. imap_processing/lo/l1b/lo_l1b.py +2 -2
  55. imap_processing/lo/l1c/lo_l1c.py +2 -2
  56. imap_processing/lo/packet_definitions/lo_xtce.xml +1082 -9178
  57. imap_processing/mag/l0/decom_mag.py +2 -2
  58. imap_processing/mag/l1a/mag_l1a.py +7 -7
  59. imap_processing/mag/l1a/mag_l1a_data.py +62 -30
  60. imap_processing/mag/l1b/mag_l1b.py +11 -6
  61. imap_processing/quality_flags.py +18 -3
  62. imap_processing/spice/geometry.py +149 -177
  63. imap_processing/spice/kernels.py +26 -26
  64. imap_processing/spice/spin.py +233 -0
  65. imap_processing/spice/time.py +96 -31
  66. imap_processing/swapi/l1/swapi_l1.py +60 -31
  67. imap_processing/swapi/packet_definitions/swapi_packet_definition.xml +363 -384
  68. imap_processing/swe/l1a/swe_l1a.py +8 -3
  69. imap_processing/swe/l1a/swe_science.py +24 -24
  70. imap_processing/swe/l1b/swe_l1b.py +2 -1
  71. imap_processing/swe/l1b/swe_l1b_science.py +181 -122
  72. imap_processing/swe/l2/swe_l2.py +337 -70
  73. imap_processing/swe/utils/swe_utils.py +28 -0
  74. imap_processing/tests/cdf/test_utils.py +2 -2
  75. imap_processing/tests/codice/conftest.py +20 -17
  76. imap_processing/tests/codice/data/validation/imap_codice_l1a_hskp_20241110193622_v0.0.0.cdf +0 -0
  77. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-aggregated_20241110193700_v0.0.0.cdf +0 -0
  78. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-counters-singles_20241110193700_v0.0.0.cdf +0 -0
  79. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-angular_20241110193700_v0.0.0.cdf +0 -0
  80. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-priority_20241110193700_v0.0.0.cdf +0 -0
  81. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-nsw-species_20241110193700_v0.0.0.cdf +0 -0
  82. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-angular_20241110193700_v0.0.0.cdf +0 -0
  83. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-priority_20241110193700_v0.0.0.cdf +0 -0
  84. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-sw-species_20241110193700_v0.0.0.cdf +0 -0
  85. imap_processing/tests/codice/test_codice_l0.py +55 -121
  86. imap_processing/tests/codice/test_codice_l1a.py +147 -59
  87. imap_processing/tests/conftest.py +81 -22
  88. imap_processing/tests/ena_maps/test_ena_maps.py +309 -0
  89. imap_processing/tests/ena_maps/test_map_utils.py +286 -0
  90. imap_processing/tests/ena_maps/test_spatial_utils.py +161 -0
  91. imap_processing/tests/glows/conftest.py +7 -1
  92. imap_processing/tests/glows/test_glows_l1a_cdf.py +3 -7
  93. imap_processing/tests/glows/test_glows_l1a_data.py +34 -6
  94. imap_processing/tests/glows/test_glows_l1b_data.py +29 -17
  95. imap_processing/tests/glows/test_glows_l2.py +101 -0
  96. imap_processing/tests/hi/conftest.py +3 -3
  97. imap_processing/tests/hi/data/l1/imap_hi_l1b_45sensor-de_20250415_v999.cdf +0 -0
  98. imap_processing/tests/hi/data/l1/imap_his_pset-calibration-prod-config_20240101_v001.csv +31 -0
  99. imap_processing/tests/hi/test_hi_l1b.py +14 -9
  100. imap_processing/tests/hi/test_hi_l1c.py +136 -36
  101. imap_processing/tests/hi/test_l1a.py +0 -2
  102. imap_processing/tests/hi/test_science_direct_event.py +18 -14
  103. imap_processing/tests/hi/test_utils.py +16 -11
  104. imap_processing/tests/hit/helpers/__init__.py +0 -0
  105. imap_processing/tests/hit/helpers/l1_validation.py +405 -0
  106. imap_processing/tests/hit/test_data/sci_sample.ccsds +0 -0
  107. imap_processing/tests/hit/test_decom_hit.py +8 -10
  108. imap_processing/tests/hit/test_hit_l1a.py +117 -180
  109. imap_processing/tests/hit/test_hit_l1b.py +149 -55
  110. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -0
  111. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +62 -0
  112. imap_processing/tests/ialirt/test_data/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  113. imap_processing/tests/ialirt/test_data/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  114. imap_processing/tests/ialirt/test_data/l0/eu_SWP_IAL_20240826_152033.csv +644 -0
  115. imap_processing/tests/ialirt/test_data/l0/hi_fsw_view_1_ccsds.bin +0 -0
  116. imap_processing/tests/ialirt/test_data/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +914 -0
  117. imap_processing/tests/ialirt/test_data/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  118. imap_processing/tests/ialirt/unit/test_process_codicehi.py +106 -0
  119. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +33 -5
  120. imap_processing/tests/ialirt/unit/test_process_swapi.py +85 -0
  121. imap_processing/tests/ialirt/unit/test_process_swe.py +106 -0
  122. imap_processing/tests/idex/conftest.py +29 -1
  123. imap_processing/tests/idex/test_data/compressed_2023_102_14_24_55.pkts +0 -0
  124. imap_processing/tests/idex/test_data/non_compressed_2023_102_14_22_26.pkts +0 -0
  125. imap_processing/tests/idex/test_idex_l0.py +6 -3
  126. imap_processing/tests/idex/test_idex_l1a.py +151 -1
  127. imap_processing/tests/idex/test_idex_l1b.py +124 -2
  128. imap_processing/tests/lo/test_lo_l1a.py +62 -2
  129. imap_processing/tests/lo/test_lo_science.py +85 -0
  130. imap_processing/tests/lo/validation_data/Instrument_FM1_T104_R129_20240803_ILO_SPIN_EU.csv +2 -0
  131. imap_processing/tests/mag/conftest.py +16 -0
  132. imap_processing/tests/mag/test_mag_decom.py +6 -4
  133. imap_processing/tests/mag/test_mag_l1a.py +36 -7
  134. imap_processing/tests/mag/test_mag_l1b.py +55 -4
  135. imap_processing/tests/mag/test_mag_validation.py +148 -0
  136. imap_processing/tests/mag/validation/L1a/T001/all_p_ones.txt +19200 -0
  137. imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-in.bin +0 -0
  138. imap_processing/tests/mag/validation/L1a/T001/mag-l0-l1a-t001-out.csv +17 -0
  139. imap_processing/tests/mag/validation/L1a/T002/all_n_ones.txt +19200 -0
  140. imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-in.bin +0 -0
  141. imap_processing/tests/mag/validation/L1a/T002/mag-l0-l1a-t002-out.csv +17 -0
  142. imap_processing/tests/mag/validation/L1a/T003/field_like.txt +19200 -0
  143. imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-in.bin +0 -0
  144. imap_processing/tests/mag/validation/L1a/T003/mag-l0-l1a-t003-out.csv +17 -0
  145. imap_processing/tests/mag/validation/L1a/T004/field_like.txt +19200 -0
  146. imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-in.bin +0 -0
  147. imap_processing/tests/mag/validation/L1a/T004/mag-l0-l1a-t004-out.csv +17 -0
  148. imap_processing/tests/mag/validation/L1a/T005/field_like_range_change.txt +19200 -0
  149. imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-in.bin +0 -0
  150. imap_processing/tests/mag/validation/L1a/T005/mag-l0-l1a-t005-out.csv +17 -0
  151. imap_processing/tests/mag/validation/L1a/T006/hdr_field.txt +19200 -0
  152. imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-in.bin +0 -0
  153. imap_processing/tests/mag/validation/L1a/T006/mag-l0-l1a-t006-out.csv +17 -0
  154. imap_processing/tests/mag/validation/L1a/T007/hdr_field_and_range_change.txt +19200 -0
  155. imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-in.bin +0 -0
  156. imap_processing/tests/mag/validation/L1a/T007/mag-l0-l1a-t007-out.csv +17 -0
  157. imap_processing/tests/mag/validation/L1a/T008/field_like_range_change.txt +19200 -0
  158. imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-in.bin +0 -0
  159. imap_processing/tests/mag/validation/L1a/T008/mag-l0-l1a-t008-out.csv +17 -0
  160. imap_processing/tests/mag/validation/L1b/T009/data.bin +0 -0
  161. imap_processing/tests/mag/validation/L1b/T009/field_like_all_ranges.txt +19200 -0
  162. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-in.csv +17 -0
  163. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-magi-out.csv +17 -0
  164. imap_processing/tests/mag/validation/L1b/T009/mag-l1a-l1b-t009-mago-out.csv +17 -0
  165. imap_processing/tests/mag/validation/L1b/T010/data.bin +0 -0
  166. imap_processing/tests/mag/validation/L1b/T010/field_like_all_ranges.txt +19200 -0
  167. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-in.csv +17 -0
  168. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-magi-out.csv +17 -0
  169. imap_processing/tests/mag/validation/L1b/T010/mag-l1a-l1b-t010-mago-out.csv +17 -0
  170. imap_processing/tests/mag/validation/L1b/T011/data.bin +0 -0
  171. imap_processing/tests/mag/validation/L1b/T011/field_like_all_ranges.txt +19200 -0
  172. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-in.csv +17 -0
  173. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-magi-out.csv +17 -0
  174. imap_processing/tests/mag/validation/L1b/T011/mag-l1a-l1b-t011-mago-out.csv +17 -0
  175. imap_processing/tests/spice/test_geometry.py +128 -133
  176. imap_processing/tests/spice/test_kernels.py +37 -37
  177. imap_processing/tests/spice/test_spin.py +184 -0
  178. imap_processing/tests/spice/test_time.py +43 -20
  179. imap_processing/tests/swapi/test_swapi_l1.py +11 -10
  180. imap_processing/tests/swapi/test_swapi_l2.py +13 -3
  181. imap_processing/tests/swe/test_swe_l1a.py +1 -1
  182. imap_processing/tests/swe/test_swe_l1b.py +20 -3
  183. imap_processing/tests/swe/test_swe_l1b_science.py +54 -35
  184. imap_processing/tests/swe/test_swe_l2.py +148 -5
  185. imap_processing/tests/test_cli.py +39 -7
  186. imap_processing/tests/test_quality_flags.py +19 -19
  187. imap_processing/tests/test_utils.py +3 -2
  188. imap_processing/tests/ultra/test_data/l0/ultra45_raw_sc_ultrarawimg_withFSWcalcs_FM45_40P_Phi28p5_BeamCal_LinearScan_phi2850_theta-000_20240207T102740.csv +3314 -3314
  189. imap_processing/tests/ultra/test_data/mock_data.py +161 -0
  190. imap_processing/tests/ultra/unit/conftest.py +73 -0
  191. imap_processing/tests/ultra/unit/test_badtimes.py +58 -0
  192. imap_processing/tests/ultra/unit/test_cullingmask.py +87 -0
  193. imap_processing/tests/ultra/unit/test_de.py +61 -60
  194. imap_processing/tests/ultra/unit/test_ultra_l1a.py +3 -3
  195. imap_processing/tests/ultra/unit/test_ultra_l1b.py +51 -77
  196. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +5 -5
  197. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +114 -0
  198. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +86 -26
  199. imap_processing/tests/ultra/unit/test_ultra_l1c.py +1 -1
  200. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +3 -3
  201. imap_processing/ultra/constants.py +11 -1
  202. imap_processing/ultra/l1a/ultra_l1a.py +2 -2
  203. imap_processing/ultra/l1b/badtimes.py +22 -5
  204. imap_processing/ultra/l1b/cullingmask.py +31 -5
  205. imap_processing/ultra/l1b/de.py +32 -37
  206. imap_processing/ultra/l1b/extendedspin.py +44 -20
  207. imap_processing/ultra/l1b/ultra_l1b.py +21 -22
  208. imap_processing/ultra/l1b/ultra_l1b_culling.py +190 -0
  209. imap_processing/ultra/l1b/ultra_l1b_extended.py +81 -30
  210. imap_processing/ultra/l1c/histogram.py +6 -2
  211. imap_processing/ultra/l1c/pset.py +6 -2
  212. imap_processing/ultra/l1c/ultra_l1c.py +2 -3
  213. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +4 -3
  214. imap_processing/ultra/utils/ultra_l1_utils.py +70 -14
  215. imap_processing/utils.py +2 -2
  216. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/METADATA +7 -2
  217. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/RECORD +235 -152
  218. imap_processing/tests/codice/data/eu_unit_lookup_table.csv +0 -101
  219. imap_processing/tests/codice/data/idle_export_eu.COD_NHK_20230822_122700 2.csv +0 -100
  220. imap_processing/tests/codice/data/idle_export_raw.COD_NHK_20230822_122700.csv +0 -100
  221. imap_processing/tests/codice/data/imap_codice_l0_raw_20241110_v001.pkts +0 -0
  222. imap_processing/tests/hi/test_data/l1a/imap_hi_l1a_45sensor-de_20250415_v000.cdf +0 -0
  223. imap_processing/tests/hit/test_data/sci_sample1.ccsds +0 -0
  224. imap_processing/tests/ultra/unit/test_spatial_utils.py +0 -125
  225. imap_processing/ultra/utils/spatial_utils.py +0 -221
  226. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.bin +0 -0
  227. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_APP_NHK.csv +0 -0
  228. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_CNT.bin +0 -0
  229. /imap_processing/tests/hi/{test_data → data}/l0/20231030_H45_SCI_DE.bin +0 -0
  230. /imap_processing/tests/hi/{test_data → data}/l0/H90_NHK_20241104.bin +0 -0
  231. /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_cnt_20241104.bin +0 -0
  232. /imap_processing/tests/hi/{test_data → data}/l0/H90_sci_de_20241104.bin +0 -0
  233. /imap_processing/tests/hi/{test_data → data}/l0/README.txt +0 -0
  234. /imap_processing/tests/idex/{imap_idex_l0_raw_20231214_v001.pkts → test_data/imap_idex_l0_raw_20231214_v001.pkts} +0 -0
  235. /imap_processing/tests/idex/{impact_14_tof_high_data.txt → test_data/impact_14_tof_high_data.txt} +0 -0
  236. /imap_processing/tests/mag/{imap_mag_l1a_norm-magi_20251017_v001.cdf → validation/imap_mag_l1a_norm-magi_20251017_v001.cdf} +0 -0
  237. /imap_processing/tests/mag/{mag_l0_test_data.pkts → validation/mag_l0_test_data.pkts} +0 -0
  238. /imap_processing/tests/mag/{mag_l0_test_output.csv → validation/mag_l0_test_output.csv} +0 -0
  239. /imap_processing/tests/mag/{mag_l1_test_data.pkts → validation/mag_l1_test_data.pkts} +0 -0
  240. /imap_processing/tests/mag/{mag_l1a_test_output.csv → validation/mag_l1a_test_output.csv} +0 -0
  241. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/LICENSE +0 -0
  242. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/WHEEL +0 -0
  243. {imap_processing-0.9.0.dist-info → imap_processing-0.11.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,106 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ from imap_processing import imap_module_directory
5
+ from imap_processing.cdf.utils import load_cdf
6
+ from imap_processing.ialirt.l0.process_codicehi import (
7
+ append_cod_hi_data,
8
+ find_groups,
9
+ process_codicehi,
10
+ )
11
+ from imap_processing.utils import packet_file_to_datasets
12
+
13
+
14
+ @pytest.fixture(scope="session")
15
+ def xtce_codicehi_path():
16
+ """Returns the xtce directory."""
17
+ return (
18
+ imap_module_directory / "ialirt" / "packet_definitions" / "ialirt_codicehi.xml"
19
+ )
20
+
21
+
22
+ @pytest.fixture(scope="session")
23
+ def binary_packet_path():
24
+ """Returns the xtce directory."""
25
+ return (
26
+ imap_module_directory
27
+ / "tests"
28
+ / "ialirt"
29
+ / "test_data"
30
+ / "l0"
31
+ / "hi_fsw_view_1_ccsds.bin"
32
+ )
33
+
34
+
35
+ @pytest.fixture(scope="session")
36
+ def codicehi_validation_data():
37
+ """Returns the test data directory."""
38
+ data_path = (
39
+ imap_module_directory
40
+ / "tests"
41
+ / "ialirt"
42
+ / "test_data"
43
+ / "l0"
44
+ / "imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf"
45
+ )
46
+ data = load_cdf(data_path)
47
+
48
+ return data
49
+
50
+
51
+ @pytest.fixture()
52
+ def codicehi_test_data(binary_packet_path, xtce_codicehi_path):
53
+ """Create xarray data"""
54
+ apid = 1168
55
+ codicehi_test_data = packet_file_to_datasets(
56
+ binary_packet_path, xtce_codicehi_path
57
+ )[apid]
58
+
59
+ return codicehi_test_data
60
+
61
+
62
+ def test_find_groups(codicehi_test_data):
63
+ """Tests find_groups"""
64
+
65
+ grouped_data = find_groups(codicehi_test_data)
66
+ unique_groups = np.unique(grouped_data["group"])
67
+ for group in unique_groups:
68
+ group_data = grouped_data["cod_hi_counter"].values[
69
+ grouped_data["group"] == group
70
+ ]
71
+ np.testing.assert_array_equal(group_data, np.arange(234))
72
+
73
+
74
+ def test_append_cod_hi_data(codicehi_test_data):
75
+ """Tests append_cod_hi_data"""
76
+
77
+ grouped_data = find_groups(codicehi_test_data)
78
+ unique_groups = np.unique(grouped_data["group"])
79
+ for group in unique_groups:
80
+ mask = grouped_data["group"] == group
81
+ filtered_indices = np.where(mask)[0]
82
+ group_data = grouped_data.isel(epoch=filtered_indices)
83
+ expected_cod_hi_counter = np.repeat(group_data["cod_hi_counter"].values, 5)
84
+ appended_data = append_cod_hi_data(group_data)
85
+ assert np.array_equal(
86
+ appended_data["cod_hi_counter"].values, expected_cod_hi_counter.astype(int)
87
+ )
88
+
89
+
90
+ def test_process_codicehi(codicehi_test_data, codicehi_validation_data, caplog):
91
+ """Tests process_codicehi."""
92
+ codicehi_product = process_codicehi(codicehi_test_data)
93
+ assert codicehi_product == [{}]
94
+
95
+ indices = (codicehi_test_data["cod_hi_acq"] != 0).values.nonzero()[0]
96
+ codicehi_test_data["cod_hi_counter"].values[indices[0] : indices[0] + 234] = (
97
+ np.random.permutation(234)
98
+ )
99
+
100
+ with caplog.at_level("WARNING"):
101
+ process_codicehi(codicehi_test_data)
102
+
103
+ assert any(
104
+ "does not contain all values from 0 to 233 without duplicates" in message
105
+ for message in caplog.text.splitlines()
106
+ )
@@ -7,20 +7,46 @@ from imap_processing.ialirt import process_ephemeris
7
7
  from imap_processing.spice.time import str_to_et
8
8
 
9
9
 
10
- def test_calculate_doppler():
10
+ @pytest.mark.external_kernel()
11
+ def test_calculate_doppler(furnish_kernels):
11
12
  """
12
13
  Test the calculate_doppler() function.
13
14
  """
14
- assert process_ephemeris.calculate_doppler(805794429.1837295) == 1
15
+ longitude = -71.41 # longitude in degrees
16
+ latitude = -33.94 # latitude in degrees
17
+ altitude = 0.157 # altitude in kilometers
18
+ # test single observation time
19
+ observation_time = 843307269.1823885 # "2026-09-22T00:00:00.000"
20
+
21
+ kernels = [
22
+ "pck00011.tpc",
23
+ "imap_spk_demo.bsp",
24
+ "de440s.bsp",
25
+ "earth_1962_240827_2124_combined.bpc",
26
+ ]
27
+ with furnish_kernels(kernels):
28
+ doppler_result = process_ephemeris.calculate_doppler(
29
+ longitude, latitude, altitude, observation_time
30
+ )
31
+ assert doppler_result is not None
32
+
33
+ # test array of observation times
34
+ time_endpoints = ("2026 SEP 22 00:00:00", "2026 SEP 22 23:59:59")
35
+ time_interval = int(1e3) # seconds between data points
36
+ observation_time = np.arange(
37
+ str_to_et(time_endpoints[0]), str_to_et(time_endpoints[1]), time_interval
38
+ )
39
+ with furnish_kernels(kernels):
40
+ doppler_result = process_ephemeris.calculate_doppler(
41
+ longitude, latitude, altitude, observation_time
42
+ )
43
+ assert doppler_result is not None
15
44
 
16
45
 
17
46
  @pytest.mark.external_kernel()
18
47
  def test_latitude_longitude_to_ecef(furnish_kernels):
19
48
  """
20
49
  Test the latitude_longitude_to_ecef() function.
21
-
22
- Test data is from https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/cspice/
23
- georec_c.html.
24
50
  """
25
51
  longitude = 118.0 # degrees
26
52
  latitude = 30.0 # degrees
@@ -92,6 +118,8 @@ def test_build_output(furnish_kernels):
92
118
  "naif0012.tls",
93
119
  "pck00011.tpc",
94
120
  "de440s.bsp",
121
+ "imap_spk_demo.bsp",
122
+ "earth_1962_240827_2124_combined.bpc",
95
123
  ]
96
124
  with furnish_kernels(kernels):
97
125
  output_dict = process_ephemeris.build_output(
@@ -0,0 +1,85 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import pytest
4
+
5
+ from imap_processing import imap_module_directory
6
+ from imap_processing.utils import packet_file_to_datasets
7
+
8
+
9
+ @pytest.fixture(scope="session")
10
+ def xtce_swapi_path():
11
+ """Returns the xtce auxiliary directory."""
12
+ return imap_module_directory / "ialirt" / "packet_definitions" / "ialirt_swapi.xml"
13
+
14
+
15
+ @pytest.fixture(scope="session")
16
+ def binary_packet_path():
17
+ """Returns the xtce directory."""
18
+ return (
19
+ imap_module_directory
20
+ / "tests"
21
+ / "ialirt"
22
+ / "test_data"
23
+ / "l0"
24
+ / "BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin"
25
+ )
26
+
27
+
28
+ @pytest.fixture(scope="session")
29
+ def swapi_test_data():
30
+ """Returns the test data directory."""
31
+ data_path = (
32
+ imap_module_directory
33
+ / "tests"
34
+ / "ialirt"
35
+ / "test_data"
36
+ / "l0"
37
+ / "eu_SWP_IAL_20240826_152033.csv"
38
+ )
39
+ data = pd.read_csv(data_path)
40
+
41
+ return data
42
+
43
+
44
+ @pytest.fixture()
45
+ def xarray_data(binary_packet_path, xtce_swapi_path):
46
+ """Create xarray data"""
47
+ apid = 1187
48
+
49
+ xarray_data = packet_file_to_datasets(
50
+ binary_packet_path, xtce_swapi_path, use_derived_value=True
51
+ )[apid]
52
+ return xarray_data
53
+
54
+
55
+ def test_decom_packets(xarray_data, swapi_test_data):
56
+ """This function checks that all instrument parameters are accounted for."""
57
+
58
+ # TODO: confirm w/ SWAPI team validity_enum flag can be
59
+ # consistent with other instruments.
60
+ fields_to_test = {
61
+ "swapi_flag": "I_ALIRT_STATUS",
62
+ "swapi_reserved": "INST_RES_ST",
63
+ "swapi_seq": "SEQ_NUMBER",
64
+ "swapi_version": "SWEEP_TABLE",
65
+ "swapi_coin_1": "COIN_CNT0",
66
+ "swapi_coin_2": "COIN_CNT1",
67
+ "swapi_coin_3": "COIN_CNT2",
68
+ "swapi_coin_4": "COIN_CNT3",
69
+ "swapi_coin_5": "COIN_CNT4",
70
+ "swapi_coin_6": "COIN_CNT5",
71
+ "swapi_spare": "SPARE",
72
+ }
73
+ _, index, test_index = np.intersect1d(
74
+ xarray_data["swapi_acq"], swapi_test_data["ACQ_TIME"], return_indices=True
75
+ )
76
+
77
+ for xarray_field, test_field in fields_to_test.items():
78
+ actual_values = xarray_data[xarray_field].values[index]
79
+ expected_values = swapi_test_data[test_field].values[test_index]
80
+
81
+ # Assert that all values match
82
+ assert np.all(actual_values == expected_values), (
83
+ f"Mismatch found in {xarray_field}: "
84
+ f"actual {actual_values}, expected {expected_values}"
85
+ )
@@ -0,0 +1,106 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+ import pytest
4
+
5
+ from imap_processing import imap_module_directory
6
+ from imap_processing.utils import packet_file_to_datasets
7
+
8
+
9
+ @pytest.fixture(scope="session")
10
+ def xtce_swe_path():
11
+ """Returns the xtce auxiliary directory."""
12
+ return imap_module_directory / "ialirt" / "packet_definitions" / "ialirt_swe.xml"
13
+
14
+
15
+ @pytest.fixture(scope="session")
16
+ def binary_packet_path():
17
+ """Returns the xtce directory."""
18
+ return (
19
+ imap_module_directory
20
+ / "tests"
21
+ / "ialirt"
22
+ / "test_data"
23
+ / "l0"
24
+ / "20240827095047_SWE_IALIRT_packet.bin"
25
+ )
26
+
27
+
28
+ @pytest.fixture(scope="session")
29
+ def swe_test_data():
30
+ """Returns the test data directory."""
31
+ data_path = (
32
+ imap_module_directory
33
+ / "tests"
34
+ / "ialirt"
35
+ / "test_data"
36
+ / "l0"
37
+ / "idle_export_eu.SWE_IALIRT_20240827_093852.csv"
38
+ )
39
+ data = pd.read_csv(data_path)
40
+
41
+ return data
42
+
43
+
44
+ @pytest.fixture()
45
+ def xarray_data(binary_packet_path, xtce_swe_path):
46
+ """Create xarray data"""
47
+ apid = 1360
48
+
49
+ xarray_data = packet_file_to_datasets(
50
+ binary_packet_path, xtce_swe_path, use_derived_value=True
51
+ )[apid]
52
+ return xarray_data
53
+
54
+
55
+ def test_decom_packets(xarray_data, swe_test_data):
56
+ """This function checks that all instrument parameters are accounted for."""
57
+
58
+ fields_to_test = {
59
+ "swe_shcoarse": "SHCOARSE",
60
+ "swe_acq_sec": "ACQUISITION_TIME",
61
+ "swe_acq_sub": "ACQUISITION_TIME_SUBSECOND",
62
+ "swe_nom_flag": "INSTRUMENT_NOMINAL_FLAG",
63
+ "swe_ops_flag": "SCIENCE_OPERATION_FLAG",
64
+ "swe_seq": "SEQUENCE_NUMBER",
65
+ "swe_cem1_e1": "ELEC_COUNTS_SPIN_I_POL_0_E_0J",
66
+ "swe_cem1_e2": "ELEC_COUNTS_SPIN_I_POL_0_E_1J",
67
+ "swe_cem1_e3": "ELEC_COUNTS_SPIN_I_POL_0_E_2J",
68
+ "swe_cem1_e4": "ELEC_COUNTS_SPIN_I_POL_0_E_3J",
69
+ "swe_cem2_e1": "ELEC_COUNTS_SPIN_I_POL_1_E_0J",
70
+ "swe_cem2_e2": "ELEC_COUNTS_SPIN_I_POL_1_E_1J",
71
+ "swe_cem2_e3": "ELEC_COUNTS_SPIN_I_POL_1_E_2J",
72
+ "swe_cem2_e4": "ELEC_COUNTS_SPIN_I_POL_1_E_3J",
73
+ "swe_cem3_e1": "ELEC_COUNTS_SPIN_I_POL_2_E_0J",
74
+ "swe_cem3_e2": "ELEC_COUNTS_SPIN_I_POL_2_E_1J",
75
+ "swe_cem3_e3": "ELEC_COUNTS_SPIN_I_POL_2_E_2J",
76
+ "swe_cem3_e4": "ELEC_COUNTS_SPIN_I_POL_2_E_3J",
77
+ "swe_cem4_e1": "ELEC_COUNTS_SPIN_I_POL_3_E_0J",
78
+ "swe_cem4_e2": "ELEC_COUNTS_SPIN_I_POL_3_E_1J",
79
+ "swe_cem4_e3": "ELEC_COUNTS_SPIN_I_POL_3_E_2J",
80
+ "swe_cem4_e4": "ELEC_COUNTS_SPIN_I_POL_3_E_3J",
81
+ "swe_cem5_e1": "ELEC_COUNTS_SPIN_I_POL_4_E_0J",
82
+ "swe_cem5_e2": "ELEC_COUNTS_SPIN_I_POL_4_E_1J",
83
+ "swe_cem5_e3": "ELEC_COUNTS_SPIN_I_POL_4_E_2J",
84
+ "swe_cem5_e4": "ELEC_COUNTS_SPIN_I_POL_4_E_3J",
85
+ "swe_cem6_e1": "ELEC_COUNTS_SPIN_I_POL_5_E_0J",
86
+ "swe_cem6_e2": "ELEC_COUNTS_SPIN_I_POL_5_E_1J",
87
+ "swe_cem6_e3": "ELEC_COUNTS_SPIN_I_POL_5_E_2J",
88
+ "swe_cem6_e4": "ELEC_COUNTS_SPIN_I_POL_5_E_3J",
89
+ "swe_cem7_e1": "ELEC_COUNTS_SPIN_I_POL_6_E_0J",
90
+ "swe_cem7_e2": "ELEC_COUNTS_SPIN_I_POL_6_E_1J",
91
+ "swe_cem7_e3": "ELEC_COUNTS_SPIN_I_POL_6_E_2J",
92
+ "swe_cem7_e4": "ELEC_COUNTS_SPIN_I_POL_6_E_3J",
93
+ }
94
+ _, index, test_index = np.intersect1d(
95
+ xarray_data["swe_shcoarse"], swe_test_data["SHCOARSE"], return_indices=True
96
+ )
97
+
98
+ for xarray_field, test_field in fields_to_test.items():
99
+ actual_values = xarray_data[xarray_field].values[index]
100
+ expected_values = swe_test_data[test_field].values[test_index]
101
+
102
+ # Assert that all values match
103
+ assert np.all(actual_values == expected_values), (
104
+ f"Mismatch found in {xarray_field}: "
105
+ f"actual {actual_values}, expected {expected_values}"
106
+ )
@@ -1,11 +1,25 @@
1
1
  from pathlib import Path
2
2
 
3
+ import numpy as np
3
4
  import pytest
4
5
  import xarray as xr
5
6
 
6
7
  from imap_processing import imap_module_directory
7
8
  from imap_processing.idex.idex_l1a import PacketParser
8
9
 
10
+ SPICE_ARRAYS = [
11
+ "ephemeris_position_x",
12
+ "ephemeris_position_y",
13
+ "ephemeris_position_z",
14
+ "ephemeris_velocity_x",
15
+ "ephemeris_velocity_y",
16
+ "ephemeris_velocity_z",
17
+ "right_ascension",
18
+ "declination",
19
+ "solar_longitude",
20
+ "spin_phase",
21
+ ]
22
+
9
23
 
10
24
  @pytest.fixture(scope="module")
11
25
  def decom_test_data() -> xr.Dataset:
@@ -17,6 +31,20 @@ def decom_test_data() -> xr.Dataset:
17
31
  A ``xarray`` dataset containing the test data
18
32
  """
19
33
  test_file = Path(
20
- f"{imap_module_directory}/tests/idex/imap_idex_l0_raw_20231214_v001.pkts"
34
+ f"{imap_module_directory}/tests/idex/test_data/imap_idex_l0_raw_20231214_v001.pkts"
21
35
  )
22
36
  return PacketParser(test_file, "001").data
37
+
38
+
39
+ def get_spice_data_side_effect_func(l1a_ds, idex_attrs):
40
+ # Create a mock dictionary of spice arrays
41
+
42
+ return {
43
+ name: xr.DataArray(
44
+ name=name,
45
+ data=np.ones(len(l1a_ds["epoch"])),
46
+ dims="epoch",
47
+ attrs=idex_attrs.get_variable_attributes(name),
48
+ )
49
+ for name in SPICE_ARRAYS
50
+ }
@@ -14,7 +14,7 @@ def test_idex_decom_length(decom_test_data: xr.Dataset):
14
14
  decom_test_data : xarray.Dataset
15
15
  The dataset to test with
16
16
  """
17
- assert len(decom_test_data) == 106
17
+ assert len(decom_test_data) == 110
18
18
 
19
19
 
20
20
  def test_idex_decom_event_num(decom_test_data: xr.Dataset):
@@ -26,7 +26,8 @@ def test_idex_decom_event_num(decom_test_data: xr.Dataset):
26
26
  The dataset to test with
27
27
  """
28
28
  for var in decom_test_data:
29
- assert len(decom_test_data[var]) == 14
29
+ if "epoch" in decom_test_data[var].dims:
30
+ assert len(decom_test_data[var]) == 14
30
31
 
31
32
 
32
33
  def test_idex_tof_high_data(decom_test_data: xr.Dataset):
@@ -40,6 +41,8 @@ def test_idex_tof_high_data(decom_test_data: xr.Dataset):
40
41
  The dataset to test with
41
42
  """
42
43
 
43
- with open(f"{imap_module_directory}/tests/idex/impact_14_tof_high_data.txt") as f:
44
+ with open(
45
+ f"{imap_module_directory}/tests/idex/test_data/impact_14_tof_high_data.txt"
46
+ ) as f:
44
47
  data = np.array([int(line.rstrip("\n")) for line in f])
45
48
  assert (decom_test_data["TOF_High"][13].data == data).all()
@@ -1,5 +1,8 @@
1
1
  """Tests the L1 processing for decommutated IDEX data"""
2
2
 
3
+ from pathlib import Path
4
+ from unittest import mock
5
+
3
6
  import numpy as np
4
7
  import pytest
5
8
  import xarray as xr
@@ -7,6 +10,8 @@ from cdflib.xarray.xarray_to_cdf import ISTPError
7
10
 
8
11
  from imap_processing import imap_module_directory
9
12
  from imap_processing.cdf.utils import load_cdf, write_cdf
13
+ from imap_processing.idex.decode import _decode_sub_frame, read_bits, rice_decode
14
+ from imap_processing.idex.idex_l1a import PacketParser
10
15
 
11
16
 
12
17
  def test_idex_cdf_file(decom_test_data: xr.Dataset):
@@ -89,9 +94,154 @@ def test_idex_tof_high_data_from_cdf(decom_test_data: xr.Dataset):
89
94
  decom_test_data : xarray.Dataset
90
95
  The dataset to test with
91
96
  """
92
- with open(f"{imap_module_directory}/tests/idex/impact_14_tof_high_data.txt") as f:
97
+ with open(
98
+ f"{imap_module_directory}/tests/idex/test_data/impact_14_tof_high_data.txt"
99
+ ) as f:
93
100
  data = np.array([int(line.rstrip()) for line in f])
94
101
 
95
102
  file_name = write_cdf(decom_test_data)
96
103
  l1_data = load_cdf(file_name)
97
104
  assert (l1_data["TOF_High"][13].data == data).all()
105
+
106
+
107
+ def test_compressed_packet():
108
+ """
109
+ Test compressed data decompression against known non-compressed data.
110
+ """
111
+ test_data_dir = f"{imap_module_directory}/tests/idex/test_data"
112
+
113
+ compressed = Path(f"{test_data_dir}/compressed_2023_102_14_24_55.pkts")
114
+ non_compressed = Path(f"{test_data_dir}/non_compressed_2023_102_14_22_26.pkts")
115
+
116
+ decompressed = PacketParser(compressed, "001").data
117
+ expected = PacketParser(non_compressed, "001").data
118
+
119
+ waveforms = [
120
+ "TOF_High",
121
+ "TOF_Low",
122
+ "TOF_Mid",
123
+ "Ion_Grid",
124
+ "Target_High",
125
+ "Target_Low",
126
+ ]
127
+
128
+ # Compare each decompressed waveform with known non-compressed waveform.
129
+ for var in waveforms:
130
+ assert np.allclose(decompressed[var], expected[var]), (
131
+ f"Variable: {var} is different for the decompressed and non compressed "
132
+ f"datasets."
133
+ )
134
+
135
+
136
+ def test_read_bits():
137
+ """
138
+ Test that read_bits() properly increments the pointer and reads the correct ints.
139
+ """
140
+
141
+ binary_str_6 = "110"
142
+ binary_str_neg_6 = "1010"
143
+ binary_str_1 = "0001"
144
+
145
+ full_string = binary_str_6 + binary_str_neg_6 + binary_str_1
146
+
147
+ pointer = 0
148
+ val, pointer = read_bits(full_string, pointer, 3)
149
+ assert val == 6
150
+ assert pointer == 3
151
+ # Test with a signed binary string
152
+ val, pointer = read_bits(full_string, pointer, 4, signed=True)
153
+ assert val == -6
154
+ assert pointer == 7
155
+
156
+ val, pointer = read_bits(full_string, pointer, 4)
157
+ assert val == 1
158
+ assert pointer == 11
159
+
160
+
161
+ @mock.patch("imap_processing.idex.decode._decode_sub_frame")
162
+ def test_rice_decode(mock_decode_sub_frame):
163
+ """
164
+ Verify that rice_decode() returns the expected list of integers.
165
+ """
166
+
167
+ mock_decode_sub_frame.return_value = [1 for _ in range(64)], 1
168
+ # mock binary string.
169
+ bstr = "0100000"
170
+ ints = rice_decode(bstr, True, 640)
171
+ # If there are 64 samples per block and the sample count is 640,
172
+ # There should be 10 calls to _decode_sub_frame()
173
+ assert mock_decode_sub_frame.call_count == 10
174
+ # There should be 640 samples
175
+ assert ints == [1 for _ in range(640)]
176
+
177
+
178
+ def test_decode_sub_frame_psel_0():
179
+ """Test constant predictor (psel=0) where all samples equal first sample."""
180
+ psel = 0
181
+ bstring = "0" * 9 + "1" # 1 in 10-bit binary
182
+ ints, bp = _decode_sub_frame(bstring, bp=0, psel=psel, k=0, n_bits=10)
183
+ assert ints == [1 for _ in range(64)]
184
+
185
+
186
+ def test_decode_sub_frame_psel_1():
187
+ """Test verbatim predictor (psel=1) where each sample stored directly."""
188
+ # Create a binary string that is equal to 1, 2, 3, 4 to 64. Each int is represented
189
+ # in 10 bits to mock the expected high sample waveform data.
190
+ psel = 1
191
+ bstring = "".join([bin(i)[2:].zfill(10) for i in range(64)])
192
+ ints, bp = _decode_sub_frame(bstring, bp=0, psel=psel, k=0, n_bits=10)
193
+ assert ints == [i for i in range(64)]
194
+
195
+
196
+ def test_decode_sub_frame_psel_2():
197
+ """Test linear predictor 1 (psel=2) with rice encoded residuals."""
198
+ psel = 2
199
+ k = 1
200
+ # This encoding takes the signed residual value (current value - last value)
201
+ # Example values 1, 2, 3, 8
202
+ # residual of 1
203
+ # quotient = 1 >> (k+1) = 0 (unary = "1")
204
+ # remainder = "01"
205
+ residual_1_and_2 = "101"
206
+ # residual of 5
207
+ # quotient = 4 >> (k+1) = 1 maps to 2 (unary = "001")
208
+ # remainder = "01"
209
+ residual_3 = "00101"
210
+ warmup = "0" * 9 + "1" # equal to 1
211
+
212
+ bstring = warmup + residual_1_and_2 + residual_1_and_2 + residual_3
213
+ ints, bp = _decode_sub_frame(bstring, bp=0, psel=psel, k=k, n_bits=10)
214
+ assert ints == [1, 2, 3, 8]
215
+
216
+
217
+ def test_decode_sub_frame_psel_3():
218
+ """Test linear predictor 2 (psel=3) with rice encoded residuals."""
219
+ # This predictor assumes that sample X(n) = 2X(n-1)-X(n-2). This predictor
220
+ # calculates the slope of the signal based on the two previous values
221
+ # (slope = X(n-1)-X(n-2)) and predicts the value of X(n) will follow on a straight
222
+ # line drawn between the two previous points.
223
+ # X(n) = X(n-1) + (X(n-1)-X(n-2)) = 2X(n-1)-X(n-2).
224
+ psel = 3
225
+ k = 1
226
+ # Example values: [1, 2, 4, 1, 5]
227
+ # For X(4)=1: predicted=3 (2*2-1), actual=4, residual=1
228
+ # For X(1)=-5: predicted=6 (2*4-2), actual=1, residual=-5
229
+ # For X(5)=7: predicted=-2 (2*1-4), actual=5, residual=7
230
+ # residual of 1
231
+ # quotient = 1 >> (K+1) = 0 (unary = "1")
232
+ # remainder = "01"
233
+ residual_1 = "101"
234
+ # residual of -5
235
+ # quotient = -5 >> (K+1) = -2 maps to 3 (unary = "0001")
236
+ # remainder = "11"
237
+ residual_2 = "000111"
238
+ # residual of 7
239
+ # quotient = 7 >> (K+1) = 1 maps to 2 (unary = "001")
240
+ # remainder = "11"
241
+ residual_3 = "00111"
242
+ warmup1 = "0" * 9 + "1" # 1
243
+ warmup2 = "0" * 8 + "10" # 2
244
+
245
+ bstring = warmup1 + warmup2 + residual_1 + residual_2 + residual_3
246
+ ints, bp = _decode_sub_frame(bstring, bp=0, psel=psel, k=k, n_bits=10)
247
+ assert ints == [1, 2, 4, 1, 5]