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.

Files changed (272) hide show
  1. imap_processing/__init__.py +1 -0
  2. imap_processing/_version.py +2 -2
  3. imap_processing/ccsds/ccsds_data.py +1 -2
  4. imap_processing/ccsds/excel_to_xtce.py +1 -2
  5. imap_processing/cdf/config/imap_codice_global_cdf_attrs.yaml +18 -12
  6. imap_processing/cdf/config/imap_codice_l1a_variable_attrs.yaml +569 -0
  7. imap_processing/cdf/config/imap_codice_l1b_variable_attrs.yaml +1846 -128
  8. imap_processing/cdf/config/imap_hit_global_cdf_attrs.yaml +5 -5
  9. imap_processing/cdf/config/imap_idex_global_cdf_attrs.yaml +20 -1
  10. imap_processing/cdf/config/imap_idex_l1a_variable_attrs.yaml +6 -4
  11. imap_processing/cdf/config/imap_idex_l1b_variable_attrs.yaml +3 -3
  12. imap_processing/cdf/config/imap_mag_global_cdf_attrs.yaml +15 -0
  13. imap_processing/cdf/config/imap_swapi_variable_attrs.yaml +22 -0
  14. imap_processing/cdf/config/imap_swe_l1b_variable_attrs.yaml +16 -0
  15. imap_processing/cdf/config/imap_ultra_global_cdf_attrs.yaml +178 -5
  16. imap_processing/cdf/config/imap_ultra_l1a_variable_attrs.yaml +5045 -41
  17. imap_processing/cdf/config/imap_ultra_l1b_variable_attrs.yaml +33 -19
  18. imap_processing/cdf/config/imap_ultra_l1c_variable_attrs.yaml +8 -48
  19. imap_processing/cdf/utils.py +41 -33
  20. imap_processing/cli.py +463 -234
  21. imap_processing/codice/codice_l1a.py +260 -47
  22. imap_processing/codice/codice_l1b.py +51 -152
  23. imap_processing/codice/constants.py +38 -1
  24. imap_processing/ena_maps/ena_maps.py +658 -65
  25. imap_processing/ena_maps/utils/coordinates.py +1 -1
  26. imap_processing/ena_maps/utils/spatial_utils.py +10 -5
  27. imap_processing/glows/l1a/glows_l1a.py +28 -99
  28. imap_processing/glows/l1a/glows_l1a_data.py +2 -2
  29. imap_processing/glows/l1b/glows_l1b.py +1 -4
  30. imap_processing/glows/l1b/glows_l1b_data.py +1 -3
  31. imap_processing/glows/l2/glows_l2.py +2 -5
  32. imap_processing/hi/l1a/hi_l1a.py +31 -12
  33. imap_processing/hi/l1b/hi_l1b.py +80 -43
  34. imap_processing/hi/l1c/hi_l1c.py +12 -16
  35. imap_processing/hit/ancillary/imap_hit_l1b-to-l2-sector-dt0-factors_20250219_v002.csv +81 -0
  36. imap_processing/hit/hit_utils.py +93 -35
  37. imap_processing/hit/l0/decom_hit.py +3 -1
  38. imap_processing/hit/l1a/hit_l1a.py +30 -25
  39. imap_processing/hit/l1b/constants.py +6 -2
  40. imap_processing/hit/l1b/hit_l1b.py +279 -318
  41. imap_processing/hit/l2/constants.py +37 -0
  42. imap_processing/hit/l2/hit_l2.py +373 -264
  43. imap_processing/ialirt/l0/parse_mag.py +138 -10
  44. imap_processing/ialirt/l0/process_swapi.py +69 -0
  45. imap_processing/ialirt/l0/process_swe.py +318 -22
  46. imap_processing/ialirt/packet_definitions/ialirt.xml +216 -212
  47. imap_processing/ialirt/packet_definitions/ialirt_codicehi.xml +1 -1
  48. imap_processing/ialirt/packet_definitions/ialirt_codicelo.xml +1 -1
  49. imap_processing/ialirt/packet_definitions/ialirt_swapi.xml +14 -14
  50. imap_processing/ialirt/utils/grouping.py +1 -1
  51. imap_processing/idex/idex_constants.py +9 -1
  52. imap_processing/idex/idex_l0.py +22 -8
  53. imap_processing/idex/idex_l1a.py +75 -44
  54. imap_processing/idex/idex_l1b.py +9 -8
  55. imap_processing/idex/idex_l2a.py +79 -45
  56. imap_processing/idex/idex_l2b.py +120 -0
  57. imap_processing/idex/idex_variable_unpacking_and_eu_conversion.csv +33 -39
  58. imap_processing/idex/packet_definitions/idex_housekeeping_packet_definition.xml +9130 -0
  59. imap_processing/lo/l0/lo_science.py +1 -2
  60. imap_processing/lo/l1a/lo_l1a.py +1 -4
  61. imap_processing/lo/l1b/lo_l1b.py +527 -6
  62. imap_processing/lo/l1b/tof_conversions.py +11 -0
  63. imap_processing/lo/l1c/lo_l1c.py +1 -4
  64. imap_processing/mag/constants.py +43 -0
  65. imap_processing/mag/imap_mag_sdc_configuration_v001.py +8 -0
  66. imap_processing/mag/l1a/mag_l1a.py +2 -9
  67. imap_processing/mag/l1a/mag_l1a_data.py +10 -10
  68. imap_processing/mag/l1b/mag_l1b.py +84 -17
  69. imap_processing/mag/l1c/interpolation_methods.py +180 -3
  70. imap_processing/mag/l1c/mag_l1c.py +236 -70
  71. imap_processing/mag/l2/mag_l2.py +140 -0
  72. imap_processing/mag/l2/mag_l2_data.py +288 -0
  73. imap_processing/spacecraft/quaternions.py +1 -3
  74. imap_processing/spice/geometry.py +3 -3
  75. imap_processing/spice/kernels.py +0 -276
  76. imap_processing/spice/pointing_frame.py +257 -0
  77. imap_processing/spice/repoint.py +48 -19
  78. imap_processing/spice/spin.py +38 -33
  79. imap_processing/spice/time.py +24 -0
  80. imap_processing/swapi/l1/swapi_l1.py +16 -12
  81. imap_processing/swapi/l2/swapi_l2.py +116 -4
  82. imap_processing/swapi/swapi_utils.py +32 -0
  83. imap_processing/swe/l1a/swe_l1a.py +2 -9
  84. imap_processing/swe/l1a/swe_science.py +8 -11
  85. imap_processing/swe/l1b/swe_l1b.py +898 -23
  86. imap_processing/swe/l2/swe_l2.py +21 -77
  87. imap_processing/swe/utils/swe_constants.py +1 -0
  88. imap_processing/tests/ccsds/test_excel_to_xtce.py +1 -1
  89. imap_processing/tests/cdf/test_utils.py +14 -16
  90. imap_processing/tests/codice/conftest.py +44 -33
  91. imap_processing/tests/codice/data/validation/imap_codice_l1a_hi-pha_20241110193700_v0.0.0.cdf +0 -0
  92. imap_processing/tests/codice/data/validation/imap_codice_l1a_lo-pha_20241110193700_v0.0.0.cdf +0 -0
  93. imap_processing/tests/codice/test_codice_l1a.py +20 -11
  94. imap_processing/tests/codice/test_codice_l1b.py +6 -7
  95. imap_processing/tests/conftest.py +78 -22
  96. imap_processing/tests/ena_maps/test_ena_maps.py +462 -33
  97. imap_processing/tests/ena_maps/test_spatial_utils.py +1 -1
  98. imap_processing/tests/glows/conftest.py +10 -14
  99. imap_processing/tests/glows/test_glows_decom.py +4 -4
  100. imap_processing/tests/glows/test_glows_l1a_cdf.py +6 -27
  101. imap_processing/tests/glows/test_glows_l1a_data.py +6 -8
  102. imap_processing/tests/glows/test_glows_l1b.py +11 -11
  103. imap_processing/tests/glows/test_glows_l1b_data.py +5 -5
  104. imap_processing/tests/glows/test_glows_l2.py +2 -8
  105. imap_processing/tests/hi/conftest.py +1 -1
  106. imap_processing/tests/hi/test_hi_l1b.py +10 -12
  107. imap_processing/tests/hi/test_hi_l1c.py +27 -24
  108. imap_processing/tests/hi/test_l1a.py +7 -9
  109. imap_processing/tests/hi/test_science_direct_event.py +2 -2
  110. imap_processing/tests/hit/helpers/l1_validation.py +44 -43
  111. imap_processing/tests/hit/test_decom_hit.py +1 -1
  112. imap_processing/tests/hit/test_hit_l1a.py +9 -9
  113. imap_processing/tests/hit/test_hit_l1b.py +172 -217
  114. imap_processing/tests/hit/test_hit_l2.py +380 -118
  115. imap_processing/tests/hit/test_hit_utils.py +122 -55
  116. imap_processing/tests/hit/validation_data/hit_l1b_standard_sample2_nsrl_v4_3decimals.csv +62 -62
  117. imap_processing/tests/hit/validation_data/sci_sample_raw.csv +1 -1
  118. imap_processing/tests/ialirt/unit/test_decom_ialirt.py +16 -81
  119. imap_processing/tests/ialirt/unit/test_grouping.py +2 -2
  120. imap_processing/tests/ialirt/unit/test_parse_mag.py +71 -16
  121. imap_processing/tests/ialirt/unit/test_process_codicehi.py +3 -3
  122. imap_processing/tests/ialirt/unit/test_process_codicelo.py +3 -10
  123. imap_processing/tests/ialirt/unit/test_process_ephemeris.py +4 -4
  124. imap_processing/tests/ialirt/unit/test_process_hit.py +3 -3
  125. imap_processing/tests/ialirt/unit/test_process_swapi.py +24 -16
  126. imap_processing/tests/ialirt/unit/test_process_swe.py +115 -7
  127. imap_processing/tests/idex/conftest.py +72 -7
  128. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20241206_v001.pkts +0 -0
  129. imap_processing/tests/idex/test_data/imap_idex_l0_raw_20250108_v001.pkts +0 -0
  130. imap_processing/tests/idex/test_idex_l0.py +33 -11
  131. imap_processing/tests/idex/test_idex_l1a.py +50 -23
  132. imap_processing/tests/idex/test_idex_l1b.py +104 -25
  133. imap_processing/tests/idex/test_idex_l2a.py +48 -32
  134. imap_processing/tests/idex/test_idex_l2b.py +93 -0
  135. imap_processing/tests/lo/test_lo_l1a.py +3 -3
  136. imap_processing/tests/lo/test_lo_l1b.py +371 -6
  137. imap_processing/tests/lo/test_lo_l1c.py +1 -1
  138. imap_processing/tests/lo/test_lo_science.py +6 -7
  139. imap_processing/tests/lo/test_star_sensor.py +1 -1
  140. imap_processing/tests/mag/conftest.py +58 -9
  141. imap_processing/tests/mag/test_mag_decom.py +4 -3
  142. imap_processing/tests/mag/test_mag_l1a.py +13 -7
  143. imap_processing/tests/mag/test_mag_l1b.py +9 -9
  144. imap_processing/tests/mag/test_mag_l1c.py +151 -47
  145. imap_processing/tests/mag/test_mag_l2.py +130 -0
  146. imap_processing/tests/mag/test_mag_validation.py +144 -7
  147. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-in.csv +1217 -0
  148. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-magi-normal-out.csv +1857 -0
  149. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-in.csv +1217 -0
  150. imap_processing/tests/mag/validation/L1c/T013/mag-l1b-l1c-t013-mago-normal-out.csv +1857 -0
  151. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-in.csv +1217 -0
  152. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-magi-normal-out.csv +1793 -0
  153. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-in.csv +1217 -0
  154. imap_processing/tests/mag/validation/L1c/T014/mag-l1b-l1c-t014-mago-normal-out.csv +1793 -0
  155. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-burst-in.csv +2561 -0
  156. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-in.csv +961 -0
  157. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-magi-normal-out.csv +1539 -0
  158. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-in.csv +1921 -0
  159. imap_processing/tests/mag/validation/L1c/T015/mag-l1b-l1c-t015-mago-normal-out.csv +2499 -0
  160. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-in.csv +865 -0
  161. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-magi-normal-out.csv +1196 -0
  162. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-in.csv +1729 -0
  163. imap_processing/tests/mag/validation/L1c/T016/mag-l1b-l1c-t016-mago-normal-out.csv +3053 -0
  164. imap_processing/tests/mag/validation/L2/imap_mag_l1b_norm-mago_20251017_v002.cdf +0 -0
  165. imap_processing/tests/mag/validation/calibration/imap_mag_l2-calibration-matrices_20251017_v004.cdf +0 -0
  166. imap_processing/tests/mag/validation/calibration/imap_mag_l2-offsets-norm_20251017_20251017_v001.cdf +0 -0
  167. imap_processing/tests/spacecraft/test_quaternions.py +1 -1
  168. imap_processing/tests/spice/test_data/fake_repoint_data.csv +4 -4
  169. imap_processing/tests/spice/test_data/fake_spin_data.csv +11 -11
  170. imap_processing/tests/spice/test_geometry.py +3 -3
  171. imap_processing/tests/spice/test_kernels.py +1 -200
  172. imap_processing/tests/spice/test_pointing_frame.py +185 -0
  173. imap_processing/tests/spice/test_repoint.py +20 -10
  174. imap_processing/tests/spice/test_spin.py +50 -9
  175. imap_processing/tests/spice/test_time.py +14 -0
  176. imap_processing/tests/swapi/lut/imap_swapi_esa-unit-conversion_20250211_v000.csv +73 -0
  177. imap_processing/tests/swapi/lut/imap_swapi_lut-notes_20250211_v000.csv +1025 -0
  178. imap_processing/tests/swapi/test_swapi_l1.py +7 -9
  179. imap_processing/tests/swapi/test_swapi_l2.py +180 -8
  180. imap_processing/tests/swe/lut/checker-board-indices.csv +24 -0
  181. imap_processing/tests/swe/lut/imap_swe_esa-lut_20250301_v000.csv +385 -0
  182. imap_processing/tests/swe/lut/imap_swe_l1b-in-flight-cal_20240510_20260716_v000.csv +3 -0
  183. imap_processing/tests/swe/test_swe_l1a.py +6 -6
  184. imap_processing/tests/swe/test_swe_l1a_science.py +3 -3
  185. imap_processing/tests/swe/test_swe_l1b.py +162 -24
  186. imap_processing/tests/swe/test_swe_l2.py +82 -102
  187. imap_processing/tests/test_cli.py +171 -88
  188. imap_processing/tests/test_utils.py +2 -1
  189. imap_processing/tests/ultra/data/mock_data.py +49 -21
  190. imap_processing/tests/ultra/unit/conftest.py +53 -70
  191. imap_processing/tests/ultra/unit/test_badtimes.py +2 -4
  192. imap_processing/tests/ultra/unit/test_cullingmask.py +4 -6
  193. imap_processing/tests/ultra/unit/test_de.py +3 -10
  194. imap_processing/tests/ultra/unit/test_decom_apid_880.py +27 -76
  195. imap_processing/tests/ultra/unit/test_decom_apid_881.py +15 -16
  196. imap_processing/tests/ultra/unit/test_decom_apid_883.py +12 -10
  197. imap_processing/tests/ultra/unit/test_decom_apid_896.py +202 -55
  198. imap_processing/tests/ultra/unit/test_lookup_utils.py +23 -1
  199. imap_processing/tests/ultra/unit/test_spacecraft_pset.py +3 -4
  200. imap_processing/tests/ultra/unit/test_ultra_l1a.py +84 -307
  201. imap_processing/tests/ultra/unit/test_ultra_l1b.py +30 -12
  202. imap_processing/tests/ultra/unit/test_ultra_l1b_annotated.py +2 -2
  203. imap_processing/tests/ultra/unit/test_ultra_l1b_culling.py +4 -1
  204. imap_processing/tests/ultra/unit/test_ultra_l1b_extended.py +163 -29
  205. imap_processing/tests/ultra/unit/test_ultra_l1c.py +5 -5
  206. imap_processing/tests/ultra/unit/test_ultra_l1c_pset_bins.py +32 -43
  207. imap_processing/tests/ultra/unit/test_ultra_l2.py +230 -0
  208. imap_processing/ultra/constants.py +1 -1
  209. imap_processing/ultra/l0/decom_tools.py +21 -34
  210. imap_processing/ultra/l0/decom_ultra.py +168 -204
  211. imap_processing/ultra/l0/ultra_utils.py +152 -136
  212. imap_processing/ultra/l1a/ultra_l1a.py +55 -243
  213. imap_processing/ultra/l1b/badtimes.py +1 -4
  214. imap_processing/ultra/l1b/cullingmask.py +2 -6
  215. imap_processing/ultra/l1b/de.py +62 -47
  216. imap_processing/ultra/l1b/extendedspin.py +8 -4
  217. imap_processing/ultra/l1b/lookup_utils.py +72 -9
  218. imap_processing/ultra/l1b/ultra_l1b.py +3 -8
  219. imap_processing/ultra/l1b/ultra_l1b_culling.py +4 -4
  220. imap_processing/ultra/l1b/ultra_l1b_extended.py +236 -78
  221. imap_processing/ultra/l1c/histogram.py +2 -6
  222. imap_processing/ultra/l1c/spacecraft_pset.py +2 -4
  223. imap_processing/ultra/l1c/ultra_l1c.py +1 -5
  224. imap_processing/ultra/l1c/ultra_l1c_pset_bins.py +107 -60
  225. imap_processing/ultra/l2/ultra_l2.py +299 -0
  226. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_LeftSlit.csv +526 -0
  227. imap_processing/ultra/lookup_tables/Angular_Profiles_FM45_RightSlit.csv +526 -0
  228. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_LeftSlit.csv +526 -0
  229. imap_processing/ultra/lookup_tables/Angular_Profiles_FM90_RightSlit.csv +526 -0
  230. imap_processing/ultra/lookup_tables/FM45_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -2
  231. imap_processing/ultra/lookup_tables/FM90_Startup1_ULTRA_IMGPARAMS_20240719.csv +2 -0
  232. imap_processing/ultra/packet_definitions/README.md +38 -0
  233. imap_processing/ultra/packet_definitions/ULTRA_SCI_COMBINED.xml +15302 -482
  234. imap_processing/ultra/utils/ultra_l1_utils.py +13 -12
  235. imap_processing/utils.py +1 -1
  236. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/METADATA +3 -2
  237. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/RECORD +264 -225
  238. imap_processing/hi/l1b/hi_eng_unit_convert_table.csv +0 -154
  239. imap_processing/mag/imap_mag_sdc-configuration_v001.yaml +0 -6
  240. imap_processing/mag/l1b/__init__.py +0 -0
  241. imap_processing/swe/l1b/swe_esa_lookup_table.csv +0 -1441
  242. imap_processing/swe/l1b/swe_l1b_science.py +0 -699
  243. imap_processing/tests/swe/test_swe_l1b_science.py +0 -103
  244. imap_processing/ultra/lookup_tables/dps_sensitivity45.cdf +0 -0
  245. imap_processing/ultra/lookup_tables/ultra_90_dps_exposure_compressed.cdf +0 -0
  246. /imap_processing/idex/packet_definitions/{idex_packet_definition.xml → idex_science_packet_definition.xml} +0 -0
  247. /imap_processing/tests/ialirt/{test_data → data}/l0/20240827095047_SWE_IALIRT_packet.bin +0 -0
  248. /imap_processing/tests/ialirt/{test_data → data}/l0/461971383-404.bin +0 -0
  249. /imap_processing/tests/ialirt/{test_data → data}/l0/461971384-405.bin +0 -0
  250. /imap_processing/tests/ialirt/{test_data → data}/l0/461971385-406.bin +0 -0
  251. /imap_processing/tests/ialirt/{test_data → data}/l0/461971386-407.bin +0 -0
  252. /imap_processing/tests/ialirt/{test_data → data}/l0/461971387-408.bin +0 -0
  253. /imap_processing/tests/ialirt/{test_data → data}/l0/461971388-409.bin +0 -0
  254. /imap_processing/tests/ialirt/{test_data → data}/l0/461971389-410.bin +0 -0
  255. /imap_processing/tests/ialirt/{test_data → data}/l0/461971390-411.bin +0 -0
  256. /imap_processing/tests/ialirt/{test_data → data}/l0/461971391-412.bin +0 -0
  257. /imap_processing/tests/ialirt/{test_data → data}/l0/BinLog CCSDS_FRAG_TLM_20240826_152323Z_IALIRT_data_for_SDC.bin +0 -0
  258. /imap_processing/tests/ialirt/{test_data → data}/l0/IALiRT Raw Packet Telemetry.txt +0 -0
  259. /imap_processing/tests/ialirt/{test_data → data}/l0/apid01152.tlm +0 -0
  260. /imap_processing/tests/ialirt/{test_data → data}/l0/eu_SWP_IAL_20240826_152033.csv +0 -0
  261. /imap_processing/tests/ialirt/{test_data → data}/l0/hi_fsw_view_1_ccsds.bin +0 -0
  262. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.ccsds +0 -0
  263. /imap_processing/tests/ialirt/{test_data → data}/l0/hit_ialirt_sample.csv +0 -0
  264. /imap_processing/tests/ialirt/{test_data → data}/l0/idle_export_eu.SWE_IALIRT_20240827_093852.csv +0 -0
  265. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_hi-ialirt_20240523200000_v0.0.0.cdf +0 -0
  266. /imap_processing/tests/ialirt/{test_data → data}/l0/imap_codice_l1a_lo-ialirt_20241110193700_v0.0.0.cdf +0 -0
  267. /imap_processing/tests/ialirt/{test_data → data}/l0/sample_decoded_i-alirt_data.csv +0 -0
  268. /imap_processing/tests/mag/validation/{imap_calibration_mag_20240229_v01.cdf → calibration/imap_mag_l1b-calibration_20240229_v001.cdf} +0 -0
  269. /imap_processing/{swe/l1b/engineering_unit_convert_table.csv → tests/swe/lut/imap_swe_eu-conversion_20240510_v000.csv} +0 -0
  270. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/LICENSE +0 -0
  271. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/WHEEL +0 -0
  272. {imap_processing-0.12.0.dist-info → imap_processing-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,8 @@
1
+ """File describing configuration changes that are not expected to change often."""
2
+
3
+ # interpolation method used in L1C processing
4
+ L1C_INTERPOLATION_METHOD = "linear_filtered"
5
+
6
+ # Setting for L2 output
7
+ # if this setting is updated, the dependency settings for L2 will also need to change.
8
+ ALWAYS_OUTPUT_MAGO = True
@@ -24,7 +24,7 @@ from imap_processing.spice.time import (
24
24
  logger = logging.getLogger(__name__)
25
25
 
26
26
 
27
- def mag_l1a(packet_filepath: Path, data_version: str) -> list[xr.Dataset]:
27
+ def mag_l1a(packet_filepath: Path) -> list[xr.Dataset]:
28
28
  """
29
29
  Will process MAG L0 data into L1A CDF files at cdf_filepath.
30
30
 
@@ -32,8 +32,6 @@ def mag_l1a(packet_filepath: Path, data_version: str) -> list[xr.Dataset]:
32
32
  ----------
33
33
  packet_filepath : pathlib.Path
34
34
  Packet files for processing.
35
- data_version : str
36
- Data version to write to CDF files.
37
35
 
38
36
  Returns
39
37
  -------
@@ -51,8 +49,6 @@ def mag_l1a(packet_filepath: Path, data_version: str) -> list[xr.Dataset]:
51
49
  attribute_manager = ImapCdfAttributes()
52
50
  attribute_manager.add_instrument_global_attrs("mag")
53
51
  attribute_manager.add_instrument_variable_attrs("mag", "l1a")
54
-
55
- attribute_manager.add_global_attribute("Data_version", data_version)
56
52
  attribute_manager.add_global_attribute("Input_files", str(input_files))
57
53
  attribute_manager.add_global_attribute(
58
54
  "Generation_date",
@@ -179,7 +175,7 @@ def process_packets(
179
175
  # each sensor, we can calculate how much data is in this packet and where the
180
176
  # byte boundaries are.
181
177
  primary_vectors, secondary_vectors = MagL1a.process_vector_data(
182
- mag_l0.VECTORS, # type: ignore
178
+ mag_l0.VECTORS,
183
179
  primary_packet_properties.total_vectors,
184
180
  secondary_packet_data.total_vectors,
185
181
  mag_l0.COMPRESSION,
@@ -297,8 +293,6 @@ def generate_dataset(
297
293
  ),
298
294
  )
299
295
 
300
- # TODO: Epoch here refers to the start of the sample. Confirm that this is
301
- # what mag is expecting, and if it is, CATDESC needs to be updated.
302
296
  epoch_time = xr.DataArray(
303
297
  time_data,
304
298
  name="epoch",
@@ -338,7 +332,6 @@ def generate_dataset(
338
332
  ),
339
333
  )
340
334
  global_attributes = attribute_manager.get_global_attributes(logical_file_id)
341
- # TODO: this method won't work because these values are not in the schema.
342
335
  global_attributes["is_mago"] = str(bool(single_file_l1a.is_mago))
343
336
  global_attributes["is_active"] = str(bool(single_file_l1a.is_active))
344
337
  global_attributes["vectors_per_second"] = (
@@ -368,7 +368,7 @@ class MagL1a:
368
368
 
369
369
  @staticmethod
370
370
  def process_vector_data(
371
- vector_data: np.ndarray,
371
+ vector_data: np.ndarray | bytes,
372
372
  primary_count: int,
373
373
  secondary_count: int,
374
374
  compression: int,
@@ -679,10 +679,9 @@ class MagL1a:
679
679
  primary_boundaries[-1] - primary_boundaries[-4]
680
680
  > MAX_COMPRESSED_VECTOR_BITS
681
681
  )
682
- or (
683
- vector_count == 2
684
- and primary_boundaries[-1] > MAX_COMPRESSED_VECTOR_BITS
685
- )
682
+ ) or (
683
+ vector_count == 2
684
+ and primary_boundaries[-1] > MAX_COMPRESSED_VECTOR_BITS
686
685
  ):
687
686
  # Since we know how long each uncompressed vector is,
688
687
  # we can determine the end of the primary vectors.
@@ -792,7 +791,7 @@ class MagL1a:
792
791
  return primary_vectors, secondary_vectors
793
792
 
794
793
  @staticmethod
795
- def _process_vector_section( # noqa: PLR0913
794
+ def _process_vector_section(
796
795
  vector_bits: np.ndarray,
797
796
  split_bits: list,
798
797
  last_index: int,
@@ -998,7 +997,7 @@ class MagL1a:
998
997
  """
999
998
  if np.any(vector_data > 1):
1000
999
  raise ValueError(
1001
- "unpack_one_vector method is expecting an array of bits as" "input."
1000
+ "unpack_one_vector method is expecting an array of bits as input."
1002
1001
  )
1003
1002
 
1004
1003
  if len(vector_data) != width * AXIS_COUNT + RANGE_BIT_WIDTH * has_range:
@@ -1107,13 +1106,14 @@ class MagL1a:
1107
1106
  """
1108
1107
  output_str = ""
1109
1108
  last_vectors_per_second = None
1110
- for start_time, packet in self.packet_definitions.items():
1109
+ for _, packet in self.packet_definitions.items():
1111
1110
  vecsec = packet.vectors_per_second
1111
+ time: np.int64 = packet.start_time.to_j2000ns().astype(np.int64)
1112
1112
  if vecsec != last_vectors_per_second:
1113
1113
  if output_str == "":
1114
- output_str = f"{start_time}:{vecsec}"
1114
+ output_str = f"{time}:{vecsec}"
1115
1115
  else:
1116
- output_str += f",{start_time}:{vecsec}"
1116
+ output_str += f",{time}:{vecsec}"
1117
1117
  last_vectors_per_second = vecsec
1118
1118
 
1119
1119
  return output_str
@@ -9,12 +9,13 @@ from xarray import Dataset
9
9
 
10
10
  from imap_processing.cdf.imap_cdf_manager import ImapCdfAttributes
11
11
  from imap_processing.cdf.utils import load_cdf
12
+ from imap_processing.mag.constants import vectors_per_second_from_string
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
15
16
 
16
17
  def mag_l1b(
17
- input_dataset: xr.Dataset, version: str, calibration_dataset: xr.Dataset = None
18
+ input_dataset: xr.Dataset, calibration_dataset: xr.Dataset = None
18
19
  ) -> Dataset:
19
20
  """
20
21
  Will process MAG L1B data from L1A data.
@@ -23,8 +24,6 @@ def mag_l1b(
23
24
  ----------
24
25
  input_dataset : xr.Dataset
25
26
  The input dataset to process.
26
- version : str
27
- The version of the output data.
28
27
  calibration_dataset : xr.Dataset
29
28
  The calibration dataset containing calibration matrices and timeshift values for
30
29
  mago and magi.
@@ -56,7 +55,6 @@ def mag_l1b(
56
55
  mag_attributes = ImapCdfAttributes()
57
56
  mag_attributes.add_instrument_global_attrs("mag")
58
57
  mag_attributes.add_instrument_variable_attrs("mag", "l1b")
59
- mag_attributes.add_global_attribute("Data_version", version)
60
58
  source = source.replace("l1a", "l1b")
61
59
 
62
60
  output_dataset = mag_l1b_processing(
@@ -100,15 +98,10 @@ def mag_l1b_processing(
100
98
  output_dataset : xr.Dataset
101
99
  L1b dataset.
102
100
  """
103
- dims = [["direction"], ["compression"]]
104
- new_dims = [["direction"], ["compression"]]
105
-
106
101
  if "mago" in logical_source:
107
- calibration_matrix = calibration_dataset["MFOTOURFO"]
108
- time_shift = calibration_dataset["OTS"]
102
+ is_mago = True
109
103
  elif "magi" in logical_source:
110
- calibration_matrix = calibration_dataset["MFITOURFI"]
111
- time_shift = calibration_dataset["ITS"]
104
+ is_mago = False
112
105
  else:
113
106
  raise ValueError(
114
107
  f"Calibration matrix not found, invalid logical source "
@@ -116,6 +109,12 @@ def mag_l1b_processing(
116
109
  )
117
110
 
118
111
  # TODO: Check validity of time range for calibration
112
+ calibration_matrix, time_shift = retrieve_matrix_from_l1b_calibration(
113
+ calibration_dataset, is_mago
114
+ )
115
+
116
+ dims = [["direction"], ["compression"]]
117
+ new_dims = [["direction"], ["compression"]]
119
118
 
120
119
  l1b_fields = xr.apply_ufunc(
121
120
  update_vector,
@@ -129,6 +128,8 @@ def mag_l1b_processing(
129
128
  )
130
129
 
131
130
  epoch_time = shift_time(input_dataset["epoch"], time_shift)
131
+
132
+ # Update attributes and assemble dataset
132
133
  epoch_time.attrs = mag_attributes.get_variable_attributes("epoch")
133
134
 
134
135
  direction = xr.DataArray(
@@ -171,9 +172,9 @@ def mag_l1b_processing(
171
172
  try:
172
173
  global_attributes["is_mago"] = input_dataset.attrs["is_mago"]
173
174
  global_attributes["is_active"] = input_dataset.attrs["is_active"]
174
- global_attributes["vectors_per_second"] = input_dataset.attrs[
175
- "vectors_per_second"
176
- ]
175
+ global_attributes["vectors_per_second"] = timeshift_vectors_per_second(
176
+ input_dataset.attrs["vectors_per_second"], time_shift
177
+ )
177
178
  global_attributes["missing_sequences"] = input_dataset.attrs[
178
179
  "missing_sequences"
179
180
  ]
@@ -210,6 +211,37 @@ def mag_l1b_processing(
210
211
  return output_dataset
211
212
 
212
213
 
214
+ def retrieve_matrix_from_l1b_calibration(
215
+ calibration_dataset: xr.Dataset, is_mago: bool = True
216
+ ) -> tuple[xr.DataArray, xr.DataArray]:
217
+ """
218
+ Retrieve the calibration matrix and time shift from the calibration dataset.
219
+
220
+ Parameters
221
+ ----------
222
+ calibration_dataset : xarray.Dataset
223
+ The calibration dataset containing the calibration matrices and time shift.
224
+ is_mago : bool
225
+ Whether the calibration is for mago or magi. If True, it retrieves the mago
226
+ calibration matrix and time shift. If False, it retrieves the magi calibration
227
+ matrix and time shift.
228
+
229
+ Returns
230
+ -------
231
+ tuple[xr.DataArray, xr.DataArray]
232
+ The calibration matrix and time shift. These can be passed directly into
233
+ update_vector, calibrate_vector, and shift_time.
234
+ """
235
+ if is_mago:
236
+ calibration_matrix = calibration_dataset["MFOTOURFO"]
237
+ time_shift = calibration_dataset["OTS"]
238
+ else:
239
+ calibration_matrix = calibration_dataset["MFITOURFI"]
240
+ time_shift = calibration_dataset["ITS"]
241
+
242
+ return calibration_matrix, time_shift
243
+
244
+
213
245
  def update_vector(
214
246
  input_vector: np.ndarray,
215
247
  input_compression: np.ndarray,
@@ -302,13 +334,14 @@ def calibrate_vector(
302
334
  updated_vector : numpy.ndarray
303
335
  Calibrated vector.
304
336
  """
305
- updated_vector: np.ndarray = input_vector.copy()
337
+ updated_vector: np.ndarray = input_vector.copy().astype(np.float64)
306
338
  if input_vector[3] % 1 != 0:
307
339
  raise ValueError("Range must be an integer.")
308
340
 
309
341
  range = int(input_vector[3])
310
- x_y_z = input_vector[:3]
342
+ x_y_z = updated_vector[:3]
311
343
  updated_vector[:3] = np.dot(calibration_matrix.values[:, :, range], x_y_z)
344
+
312
345
  return updated_vector
313
346
 
314
347
 
@@ -336,7 +369,7 @@ def shift_time(epoch_times: xr.DataArray, time_shift: xr.DataArray) -> xr.DataAr
336
369
  Parameters
337
370
  ----------
338
371
  epoch_times : xr.DataArray
339
- The input epoch times, in J2000 ns.
372
+ The input epoch times, in TT J2000 ns.
340
373
  time_shift : xr.DataArray
341
374
  The time shift to apply for the given sensor. This should be one value and is
342
375
  in seconds.
@@ -353,3 +386,37 @@ def shift_time(epoch_times: xr.DataArray, time_shift: xr.DataArray) -> xr.DataAr
353
386
  time_shift_ns = time_shift.data * 1e9
354
387
 
355
388
  return epoch_times + time_shift_ns
389
+
390
+
391
+ def timeshift_vectors_per_second(
392
+ vectors_per_second: str, time_shift: xr.DataArray
393
+ ) -> str:
394
+ """
395
+ Shift the vectors per second attribute by the time shift value.
396
+
397
+ This ensures that the vectors per second attribute is aligned with the epoch values
398
+ if the time is shifted.
399
+
400
+ Parameters
401
+ ----------
402
+ vectors_per_second : str
403
+ The vectors per second attribute from the input dataset, in the format
404
+ "timestamp:rate,timestamp:rate".
405
+ time_shift : xr.DataArray
406
+ The time shift to apply for the given sensor. This should be one value and is
407
+ in seconds.
408
+
409
+ Returns
410
+ -------
411
+ str
412
+ The updated vectors per second attribute.
413
+ """
414
+ time_shift_ns = time_shift.data * 1e9
415
+
416
+ vecsec = vectors_per_second_from_string(vectors_per_second)
417
+ new_vecsec = ""
418
+ for time, rate in vecsec.items():
419
+ new_time = time + time_shift_ns
420
+ new_vecsec += f"{new_time.astype(np.int64)}:{rate},"
421
+
422
+ return new_vecsec[:-1]
@@ -1,16 +1,51 @@
1
1
  # mypy: ignore-errors
2
2
  """Module containing interpolation methods for MAG L1C."""
3
3
 
4
+ import logging
4
5
  from enum import Enum
6
+ from typing import Optional
5
7
 
6
8
  import numpy as np
7
9
  from scipy.interpolate import make_interp_spline
10
+ from scipy.signal import lfilter
11
+
12
+ from imap_processing.mag.constants import POSSIBLE_RATES, VecSec
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def remove_invalid_output_timestamps(
18
+ input_timestamps: np.ndarray, output_timestamps: np.ndarray
19
+ ) -> np.ndarray:
20
+ """
21
+ Remove output timestamps where we don't have input timestamps to interpolate.
22
+
23
+ I.E. We should never create science data outside of the timeline of burst data.
24
+
25
+ Parameters
26
+ ----------
27
+ input_timestamps : numpy.ndarray
28
+ List of input timestamps (from burst data).
29
+ output_timestamps : numpy.ndarray
30
+ List of output timestamps (from norm data) to downsample/interpolate to.
31
+
32
+ Returns
33
+ -------
34
+ numpy.ndarray
35
+ All valid output timestamps where there exists input data.
36
+ """
37
+ if input_timestamps[0] > output_timestamps[0]:
38
+ # Chop data where we don't have input timestamps to interpolate
39
+ output_timestamps = output_timestamps[output_timestamps >= input_timestamps[0]]
40
+ return output_timestamps
8
41
 
9
42
 
10
43
  def linear(
11
44
  input_vectors: np.ndarray,
12
45
  input_timestamps: np.ndarray,
13
46
  output_timestamps: np.ndarray,
47
+ input_rate: Optional[VecSec] = None,
48
+ output_rate: Optional[VecSec] = None,
14
49
  ) -> np.ndarray:
15
50
  """
16
51
  Linear interpolation of input vectors to output timestamps.
@@ -25,6 +60,10 @@ def linear(
25
60
  vectors.
26
61
  output_timestamps : numpy.ndarray
27
62
  Output timestamps of shape (m,) to generate interpolated vectors for.
63
+ input_rate : VecSec, optional
64
+ Not required for this interpolation method.
65
+ output_rate : VecSec, optional
66
+ Not required for this interpolation method.
28
67
 
29
68
  Returns
30
69
  -------
@@ -32,6 +71,7 @@ def linear(
32
71
  Interpolated vectors of shape (m, 3) where m is equal to the number of output
33
72
  timestamps. Contains x, y, z components of the vector.
34
73
  """
74
+ # TODO: Remove invalid timestamps using remove_invalid_output_timestamps
35
75
  spline = make_interp_spline(input_timestamps, input_vectors, k=1)
36
76
  return spline(output_timestamps)
37
77
 
@@ -40,6 +80,8 @@ def quadratic(
40
80
  input_vectors: np.ndarray,
41
81
  input_timestamps: np.ndarray,
42
82
  output_timestamps: np.ndarray,
83
+ input_rate: Optional[VecSec] = None,
84
+ output_rate: Optional[VecSec] = None,
43
85
  ) -> np.ndarray:
44
86
  """
45
87
  Quadratic interpolation of input vectors to output timestamps.
@@ -54,6 +96,10 @@ def quadratic(
54
96
  vectors.
55
97
  output_timestamps : numpy.ndarray
56
98
  Output timestamps of shape (m,) to generate interpolated vectors for.
99
+ input_rate : VecSec, optional
100
+ Not required for this interpolation method.
101
+ output_rate : VecSec, optional
102
+ Not required for this interpolation method.
57
103
 
58
104
  Returns
59
105
  -------
@@ -69,6 +115,8 @@ def cubic(
69
115
  input_vectors: np.ndarray,
70
116
  input_timestamps: np.ndarray,
71
117
  output_timestamps: np.ndarray,
118
+ input_rate: Optional[VecSec] = None,
119
+ output_rate: Optional[VecSec] = None,
72
120
  ) -> np.ndarray:
73
121
  """
74
122
  Cubic interpolation of input vectors to output timestamps.
@@ -83,6 +131,10 @@ def cubic(
83
131
  vectors.
84
132
  output_timestamps : numpy.ndarray
85
133
  Output timestamps of shape (m,) to generate interpolated vectors for.
134
+ input_rate : VecSec, optional
135
+ Not required for this interpolation method.
136
+ output_rate : VecSec, optional
137
+ Not required for this interpolation method.
86
138
 
87
139
  Returns
88
140
  -------
@@ -94,10 +146,104 @@ def cubic(
94
146
  return spline(output_timestamps)
95
147
 
96
148
 
149
+ def estimate_rate(timestamps: np.ndarray) -> VecSec:
150
+ """
151
+ Given a set of timestamps, estimate the rate of the timestamps.
152
+
153
+ This rate will be one of the defined rates in the VecSec enum. The calculation
154
+ assumes there are no significant gaps in the timestamps.
155
+
156
+ Parameters
157
+ ----------
158
+ timestamps : numpy.ndarray
159
+ 1D array of timestamps to estimate the rate of.
160
+
161
+ Returns
162
+ -------
163
+ VecSec
164
+ Estimated rate of the timestamps.
165
+ """
166
+ samples_per_second = timestamps.shape[0] / (timestamps[-1] - timestamps[0]) * 1e9
167
+ per_second = VecSec(
168
+ POSSIBLE_RATES[(np.abs(POSSIBLE_RATES - samples_per_second)).argmin()]
169
+ )
170
+
171
+ return per_second
172
+
173
+
174
+ def cic_filter(
175
+ input_vectors: np.ndarray,
176
+ input_timestamps: np.ndarray,
177
+ output_timestamps: np.ndarray,
178
+ input_rate: Optional[VecSec],
179
+ output_rate: Optional[VecSec],
180
+ ):
181
+ """
182
+ Apply CIC filter to data before interpolating.
183
+
184
+ The filtering uses a Cascaded integrator-comb (CIC) filter which is used in FSW to
185
+ filter down the raw data to telemetered data.
186
+
187
+ This assumes that the input_vectors and input_timestamps are downsampled to
188
+ the output_timestamps rate. Neither input_timestamps nor output_timestamps should
189
+ have significant gaps.
190
+
191
+ After the CIC filter is applied, the timestamps at the beginning and end of the
192
+ output are invalid. Therefore, we must pass in extra values and remove them after
193
+ applying the filter. This needs about double the input values to work.
194
+
195
+ Parameters
196
+ ----------
197
+ input_vectors : numpy.ndarray
198
+ Input vectors of shape (n, 3) where n is equal to the number of input
199
+ timestamps. Contains x, y, z components of the vector.
200
+ input_timestamps : numpy.ndarray
201
+ Input timestamps of shape (n,) which correspond to the timestamps of the input
202
+ vectors.
203
+ output_timestamps : numpy.ndarray
204
+ Output timestamps of shape (m,) to generate interpolated vectors for.
205
+ input_rate : VecSec, optional
206
+ Expected rate of input timestamps.
207
+ output_rate : VecSec, optional
208
+ Expected rate of output timestamps.
209
+
210
+ Returns
211
+ -------
212
+ input_filtered, vectors_filtered : tuple[numpy.ndarray]
213
+ Filtered input timestamps and filtered input vectors.
214
+ """
215
+ # output rate should always be higher
216
+ input_rate = estimate_rate(input_timestamps) if input_rate is None else input_rate
217
+ output_rate = (
218
+ estimate_rate(output_timestamps) if output_rate is None else output_rate
219
+ )
220
+
221
+ if input_rate.value <= output_rate.value:
222
+ raise ValueError(
223
+ f"Burst mode input rate {input_rate} should never be less than "
224
+ f"the normal mode output rate {output_rate}. "
225
+ f"Both rates are required"
226
+ )
227
+
228
+ decimation_factor = int(input_rate.value / output_rate.value)
229
+ cic1 = np.ones(decimation_factor)
230
+ cic1 = cic1 / decimation_factor
231
+ cic2 = np.convolve(cic1, cic1)
232
+ delay = (len(cic2) - 1) // 2
233
+ input_filtered = input_timestamps
234
+ if delay != 0:
235
+ input_filtered = input_timestamps[:-delay]
236
+
237
+ vectors_filtered = lfilter(cic2, 1, input_vectors, axis=0)[delay:]
238
+ return input_filtered, vectors_filtered
239
+
240
+
97
241
  def linear_filtered(
98
242
  input_vectors: np.ndarray,
99
243
  input_timestamps: np.ndarray,
100
244
  output_timestamps: np.ndarray,
245
+ input_rate: Optional[VecSec] = None,
246
+ output_rate: Optional[VecSec] = None,
101
247
  ) -> np.ndarray:
102
248
  """
103
249
  Linear filtered interpolation of input vectors to output timestamps.
@@ -112,6 +258,12 @@ def linear_filtered(
112
258
  vectors.
113
259
  output_timestamps : numpy.ndarray
114
260
  Output timestamps of shape (m,) to generate interpolated vectors for.
261
+ input_rate : VecSec, optional
262
+ Expected rate of input timestamps to be passed into the CIC filter. If not
263
+ provided, this will be estimated.
264
+ output_rate : VecSec, optional
265
+ Expected rate of output timestamps to be passed into the CIC filter. If not
266
+ provided, this will be estimated.
115
267
 
116
268
  Returns
117
269
  -------
@@ -119,13 +271,18 @@ def linear_filtered(
119
271
  Interpolated vectors of shape (m, 3) where m is equal to the number of output
120
272
  timestamps. Contains x, y, z components of the vector.
121
273
  """
122
- pass
274
+ input_filtered, vectors_filtered = cic_filter(
275
+ input_vectors, input_timestamps, output_timestamps, input_rate, output_rate
276
+ )
277
+ return linear(vectors_filtered, input_filtered, output_timestamps)
123
278
 
124
279
 
125
280
  def quadratic_filtered(
126
281
  input_vectors: np.ndarray,
127
282
  input_timestamps: np.ndarray,
128
283
  output_timestamps: np.ndarray,
284
+ input_rate: Optional[VecSec] = None,
285
+ output_rate: Optional[VecSec] = None,
129
286
  ) -> np.ndarray:
130
287
  """
131
288
  Quadratic filtered interpolation of input vectors to output timestamps.
@@ -140,6 +297,12 @@ def quadratic_filtered(
140
297
  vectors.
141
298
  output_timestamps : numpy.ndarray
142
299
  Output timestamps of shape (m,) to generate interpolated vectors for.
300
+ input_rate : VecSec, optional
301
+ Expected rate of input timestamps to be passed into the CIC filter. If not
302
+ provided, this will be estimated.
303
+ output_rate : VecSec, optional
304
+ Expected rate of output timestamps to be passed into the CIC filter. If not
305
+ provided, this will be estimated.
143
306
 
144
307
  Returns
145
308
  -------
@@ -147,13 +310,18 @@ def quadratic_filtered(
147
310
  Interpolated vectors of shape (m, 3) where m is equal to the number of output
148
311
  timestamps. Contains x, y, z components of the vector.
149
312
  """
150
- pass
313
+ input_filtered, vectors_filtered = cic_filter(
314
+ input_vectors, input_timestamps, output_timestamps, input_rate, output_rate
315
+ )
316
+ return quadratic(vectors_filtered, input_filtered, output_timestamps)
151
317
 
152
318
 
153
319
  def cubic_filtered(
154
320
  input_vectors: np.ndarray,
155
321
  input_timestamps: np.ndarray,
156
322
  output_timestamps: np.ndarray,
323
+ input_rate: Optional[VecSec] = None,
324
+ output_rate: Optional[VecSec] = None,
157
325
  ) -> np.ndarray:
158
326
  """
159
327
  Cubic filtered interpolation of input vectors to output timestamps.
@@ -168,6 +336,12 @@ def cubic_filtered(
168
336
  vectors.
169
337
  output_timestamps : numpy.ndarray
170
338
  Output timestamps of shape (m,) to generate interpolated vectors for.
339
+ input_rate : VecSec, optional
340
+ Expected rate of input timestamps to be passed into the CIC filter. If not
341
+ provided, this will be estimated.
342
+ output_rate : VecSec, optional
343
+ Expected rate of output timestamps to be passed into the CIC filter. If not
344
+ provided, this will be estimated.
171
345
 
172
346
  Returns
173
347
  -------
@@ -175,7 +349,10 @@ def cubic_filtered(
175
349
  Interpolated vectors of shape (m, 3) where m is equal to the number of output
176
350
  timestamps. Contains x, y, z components of the vector.
177
351
  """
178
- pass
352
+ input_filtered, vectors_filtered = cic_filter(
353
+ input_vectors, input_timestamps, output_timestamps, input_rate, output_rate
354
+ )
355
+ return cubic(vectors_filtered, input_filtered, output_timestamps)
179
356
 
180
357
 
181
358
  class InterpolationFunction(Enum):