rapidtide 2.9.6__py3-none-any.whl → 3.1.3__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.
Files changed (405) hide show
  1. cloud/gmscalc-HCPYA +1 -1
  2. cloud/mount-and-run +2 -0
  3. cloud/rapidtide-HCPYA +3 -3
  4. rapidtide/Colortables.py +538 -38
  5. rapidtide/OrthoImageItem.py +1094 -51
  6. rapidtide/RapidtideDataset.py +1709 -114
  7. rapidtide/__init__.py +0 -8
  8. rapidtide/_version.py +4 -4
  9. rapidtide/calccoherence.py +242 -97
  10. rapidtide/calcnullsimfunc.py +240 -140
  11. rapidtide/calcsimfunc.py +314 -129
  12. rapidtide/correlate.py +1211 -389
  13. rapidtide/data/examples/src/testLD +56 -0
  14. rapidtide/data/examples/src/test_findmaxlag.py +2 -2
  15. rapidtide/data/examples/src/test_mlregressallt.py +32 -17
  16. rapidtide/data/examples/src/testalign +1 -1
  17. rapidtide/data/examples/src/testatlasaverage +35 -7
  18. rapidtide/data/examples/src/testboth +21 -0
  19. rapidtide/data/examples/src/testcifti +11 -0
  20. rapidtide/data/examples/src/testdelayvar +13 -0
  21. rapidtide/data/examples/src/testdlfilt +25 -0
  22. rapidtide/data/examples/src/testfft +35 -0
  23. rapidtide/data/examples/src/testfileorfloat +37 -0
  24. rapidtide/data/examples/src/testfmri +92 -42
  25. rapidtide/data/examples/src/testfuncs +3 -3
  26. rapidtide/data/examples/src/testglmfilt +8 -6
  27. rapidtide/data/examples/src/testhappy +84 -51
  28. rapidtide/data/examples/src/testinitdelay +19 -0
  29. rapidtide/data/examples/src/testmodels +33 -0
  30. rapidtide/data/examples/src/testnewrefine +26 -0
  31. rapidtide/data/examples/src/testnoiseamp +2 -2
  32. rapidtide/data/examples/src/testppgproc +17 -0
  33. rapidtide/data/examples/src/testrefineonly +22 -0
  34. rapidtide/data/examples/src/testretro +26 -13
  35. rapidtide/data/examples/src/testretrolagtcs +16 -0
  36. rapidtide/data/examples/src/testrolloff +11 -0
  37. rapidtide/data/examples/src/testsimdata +45 -28
  38. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  39. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  40. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  41. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  42. rapidtide/data/models/model_cnn_pytorch_fulldata/loss.png +0 -0
  43. rapidtide/data/models/model_cnn_pytorch_fulldata/loss.txt +1 -0
  44. rapidtide/data/models/model_cnn_pytorch_fulldata/model.pth +0 -0
  45. rapidtide/data/models/model_cnn_pytorch_fulldata/model_meta.json +80 -0
  46. rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.png +0 -0
  47. rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.txt +1 -0
  48. rapidtide/data/models/model_cnnbp_pytorch_fullldata/model.pth +0 -0
  49. rapidtide/data/models/model_cnnbp_pytorch_fullldata/model_meta.json +138 -0
  50. rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.png +0 -0
  51. rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.txt +1 -0
  52. rapidtide/data/models/model_cnnfft_pytorch_fulldata/model.pth +0 -0
  53. rapidtide/data/models/model_cnnfft_pytorch_fulldata/model_meta.json +128 -0
  54. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.png +0 -0
  55. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.txt +1 -0
  56. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model.pth +0 -0
  57. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model_meta.json +49 -0
  58. rapidtide/data/models/model_revised_tf2/model.keras +0 -0
  59. rapidtide/data/models/{model_serdar → model_revised_tf2}/model_meta.json +1 -1
  60. rapidtide/data/models/model_serdar2_tf2/model.keras +0 -0
  61. rapidtide/data/models/{model_serdar2 → model_serdar2_tf2}/model_meta.json +1 -1
  62. rapidtide/data/models/model_serdar_tf2/model.keras +0 -0
  63. rapidtide/data/models/{model_revised → model_serdar_tf2}/model_meta.json +1 -1
  64. rapidtide/data/reference/HCP1200v2_MTT_2mm.nii.gz +0 -0
  65. rapidtide/data/reference/HCP1200v2_binmask_2mm.nii.gz +0 -0
  66. rapidtide/data/reference/HCP1200v2_csf_2mm.nii.gz +0 -0
  67. rapidtide/data/reference/HCP1200v2_gray_2mm.nii.gz +0 -0
  68. rapidtide/data/reference/HCP1200v2_graylaghist.json +7 -0
  69. rapidtide/data/reference/HCP1200v2_graylaghist.tsv.gz +0 -0
  70. rapidtide/data/reference/HCP1200v2_laghist.json +7 -0
  71. rapidtide/data/reference/HCP1200v2_laghist.tsv.gz +0 -0
  72. rapidtide/data/reference/HCP1200v2_mask_2mm.nii.gz +0 -0
  73. rapidtide/data/reference/HCP1200v2_maxcorr_2mm.nii.gz +0 -0
  74. rapidtide/data/reference/HCP1200v2_maxtime_2mm.nii.gz +0 -0
  75. rapidtide/data/reference/HCP1200v2_maxwidth_2mm.nii.gz +0 -0
  76. rapidtide/data/reference/HCP1200v2_negmask_2mm.nii.gz +0 -0
  77. rapidtide/data/reference/HCP1200v2_timepercentile_2mm.nii.gz +0 -0
  78. rapidtide/data/reference/HCP1200v2_white_2mm.nii.gz +0 -0
  79. rapidtide/data/reference/HCP1200v2_whitelaghist.json +7 -0
  80. rapidtide/data/reference/HCP1200v2_whitelaghist.tsv.gz +0 -0
  81. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2.xml +131 -0
  82. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_regions.txt +60 -0
  83. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_space-MNI152NLin6Asym_2mm.nii.gz +0 -0
  84. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm.nii.gz +0 -0
  85. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm_mask.nii.gz +0 -0
  86. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
  87. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL2_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
  88. rapidtide/data/reference/MNI152_T1_1mm_Brain_FAST_seg.nii.gz +0 -0
  89. rapidtide/data/reference/MNI152_T1_1mm_Brain_Mask.nii.gz +0 -0
  90. rapidtide/data/reference/MNI152_T1_2mm_Brain_FAST_seg.nii.gz +0 -0
  91. rapidtide/data/reference/MNI152_T1_2mm_Brain_Mask.nii.gz +0 -0
  92. rapidtide/decorators.py +91 -0
  93. rapidtide/dlfilter.py +2553 -414
  94. rapidtide/dlfiltertorch.py +5201 -0
  95. rapidtide/externaltools.py +328 -13
  96. rapidtide/fMRIData_class.py +108 -92
  97. rapidtide/ffttools.py +168 -0
  98. rapidtide/filter.py +2704 -1462
  99. rapidtide/fit.py +2361 -579
  100. rapidtide/genericmultiproc.py +197 -0
  101. rapidtide/happy_supportfuncs.py +3255 -548
  102. rapidtide/helper_classes.py +587 -1116
  103. rapidtide/io.py +2569 -468
  104. rapidtide/linfitfiltpass.py +784 -0
  105. rapidtide/makelaggedtcs.py +267 -97
  106. rapidtide/maskutil.py +555 -25
  107. rapidtide/miscmath.py +835 -144
  108. rapidtide/multiproc.py +217 -44
  109. rapidtide/patchmatch.py +752 -0
  110. rapidtide/peakeval.py +32 -32
  111. rapidtide/ppgproc.py +2205 -0
  112. rapidtide/qualitycheck.py +353 -40
  113. rapidtide/refinedelay.py +854 -0
  114. rapidtide/refineregressor.py +939 -0
  115. rapidtide/resample.py +725 -204
  116. rapidtide/scripts/__init__.py +1 -0
  117. rapidtide/scripts/{adjustoffset → adjustoffset.py} +7 -2
  118. rapidtide/scripts/{aligntcs → aligntcs.py} +7 -2
  119. rapidtide/scripts/{applydlfilter → applydlfilter.py} +7 -2
  120. rapidtide/scripts/applyppgproc.py +28 -0
  121. rapidtide/scripts/{atlasaverage → atlasaverage.py} +7 -2
  122. rapidtide/scripts/{atlastool → atlastool.py} +7 -2
  123. rapidtide/scripts/{calcicc → calcicc.py} +7 -2
  124. rapidtide/scripts/{calctexticc → calctexticc.py} +7 -2
  125. rapidtide/scripts/{calcttest → calcttest.py} +7 -2
  126. rapidtide/scripts/{ccorrica → ccorrica.py} +7 -2
  127. rapidtide/scripts/delayvar.py +28 -0
  128. rapidtide/scripts/{diffrois → diffrois.py} +7 -2
  129. rapidtide/scripts/{endtidalproc → endtidalproc.py} +7 -2
  130. rapidtide/scripts/{fdica → fdica.py} +7 -2
  131. rapidtide/scripts/{filtnifti → filtnifti.py} +7 -2
  132. rapidtide/scripts/{filttc → filttc.py} +7 -2
  133. rapidtide/scripts/{fingerprint → fingerprint.py} +20 -16
  134. rapidtide/scripts/{fixtr → fixtr.py} +7 -2
  135. rapidtide/scripts/{gmscalc → gmscalc.py} +7 -2
  136. rapidtide/scripts/{happy → happy.py} +7 -2
  137. rapidtide/scripts/{happy2std → happy2std.py} +7 -2
  138. rapidtide/scripts/{happywarp → happywarp.py} +8 -4
  139. rapidtide/scripts/{histnifti → histnifti.py} +7 -2
  140. rapidtide/scripts/{histtc → histtc.py} +7 -2
  141. rapidtide/scripts/{glmfilt → linfitfilt.py} +7 -4
  142. rapidtide/scripts/{localflow → localflow.py} +7 -2
  143. rapidtide/scripts/{mergequality → mergequality.py} +7 -2
  144. rapidtide/scripts/{pairproc → pairproc.py} +7 -2
  145. rapidtide/scripts/{pairwisemergenifti → pairwisemergenifti.py} +7 -2
  146. rapidtide/scripts/{physiofreq → physiofreq.py} +7 -2
  147. rapidtide/scripts/{pixelcomp → pixelcomp.py} +7 -2
  148. rapidtide/scripts/{plethquality → plethquality.py} +7 -2
  149. rapidtide/scripts/{polyfitim → polyfitim.py} +7 -2
  150. rapidtide/scripts/{proj2flow → proj2flow.py} +7 -2
  151. rapidtide/scripts/{rankimage → rankimage.py} +7 -2
  152. rapidtide/scripts/{rapidtide → rapidtide.py} +7 -2
  153. rapidtide/scripts/{rapidtide2std → rapidtide2std.py} +7 -2
  154. rapidtide/scripts/{resamplenifti → resamplenifti.py} +7 -2
  155. rapidtide/scripts/{resampletc → resampletc.py} +7 -2
  156. rapidtide/scripts/retrolagtcs.py +28 -0
  157. rapidtide/scripts/retroregress.py +28 -0
  158. rapidtide/scripts/{roisummarize → roisummarize.py} +7 -2
  159. rapidtide/scripts/{runqualitycheck → runqualitycheck.py} +7 -2
  160. rapidtide/scripts/{showarbcorr → showarbcorr.py} +7 -2
  161. rapidtide/scripts/{showhist → showhist.py} +7 -2
  162. rapidtide/scripts/{showstxcorr → showstxcorr.py} +7 -2
  163. rapidtide/scripts/{showtc → showtc.py} +7 -2
  164. rapidtide/scripts/{showxcorr_legacy → showxcorr_legacy.py} +8 -8
  165. rapidtide/scripts/{showxcorrx → showxcorrx.py} +7 -2
  166. rapidtide/scripts/{showxy → showxy.py} +7 -2
  167. rapidtide/scripts/{simdata → simdata.py} +7 -2
  168. rapidtide/scripts/{spatialdecomp → spatialdecomp.py} +7 -2
  169. rapidtide/scripts/{spatialfit → spatialfit.py} +7 -2
  170. rapidtide/scripts/{spatialmi → spatialmi.py} +7 -2
  171. rapidtide/scripts/{spectrogram → spectrogram.py} +7 -2
  172. rapidtide/scripts/stupidramtricks.py +238 -0
  173. rapidtide/scripts/{synthASL → synthASL.py} +7 -2
  174. rapidtide/scripts/{tcfrom2col → tcfrom2col.py} +7 -2
  175. rapidtide/scripts/{tcfrom3col → tcfrom3col.py} +7 -2
  176. rapidtide/scripts/{temporaldecomp → temporaldecomp.py} +7 -2
  177. rapidtide/scripts/{testhrv → testhrv.py} +1 -1
  178. rapidtide/scripts/{threeD → threeD.py} +7 -2
  179. rapidtide/scripts/{tidepool → tidepool.py} +7 -2
  180. rapidtide/scripts/{variabilityizer → variabilityizer.py} +7 -2
  181. rapidtide/simFuncClasses.py +2113 -0
  182. rapidtide/simfuncfit.py +312 -108
  183. rapidtide/stats.py +579 -247
  184. rapidtide/tests/.coveragerc +27 -6
  185. rapidtide-2.9.6.data/scripts/fdica → rapidtide/tests/cleanposttest +4 -6
  186. rapidtide/tests/happycomp +9 -0
  187. rapidtide/tests/resethappytargets +1 -1
  188. rapidtide/tests/resetrapidtidetargets +1 -1
  189. rapidtide/tests/resettargets +1 -1
  190. rapidtide/tests/runlocaltest +3 -3
  191. rapidtide/tests/showkernels +1 -1
  192. rapidtide/tests/test_aliasedcorrelate.py +4 -4
  193. rapidtide/tests/test_aligntcs.py +1 -1
  194. rapidtide/tests/test_calcicc.py +1 -1
  195. rapidtide/tests/test_cleanregressor.py +184 -0
  196. rapidtide/tests/test_congrid.py +70 -81
  197. rapidtide/tests/test_correlate.py +1 -1
  198. rapidtide/tests/test_corrpass.py +4 -4
  199. rapidtide/tests/test_delayestimation.py +54 -59
  200. rapidtide/tests/test_dlfiltertorch.py +437 -0
  201. rapidtide/tests/test_doresample.py +2 -2
  202. rapidtide/tests/test_externaltools.py +69 -0
  203. rapidtide/tests/test_fastresampler.py +9 -5
  204. rapidtide/tests/test_filter.py +96 -57
  205. rapidtide/tests/test_findmaxlag.py +50 -19
  206. rapidtide/tests/test_fullrunhappy_v1.py +15 -10
  207. rapidtide/tests/test_fullrunhappy_v2.py +19 -13
  208. rapidtide/tests/test_fullrunhappy_v3.py +28 -13
  209. rapidtide/tests/test_fullrunhappy_v4.py +30 -11
  210. rapidtide/tests/test_fullrunhappy_v5.py +62 -0
  211. rapidtide/tests/test_fullrunrapidtide_v1.py +61 -7
  212. rapidtide/tests/test_fullrunrapidtide_v2.py +26 -14
  213. rapidtide/tests/test_fullrunrapidtide_v3.py +28 -8
  214. rapidtide/tests/test_fullrunrapidtide_v4.py +16 -8
  215. rapidtide/tests/test_fullrunrapidtide_v5.py +15 -6
  216. rapidtide/tests/test_fullrunrapidtide_v6.py +142 -0
  217. rapidtide/tests/test_fullrunrapidtide_v7.py +114 -0
  218. rapidtide/tests/test_fullrunrapidtide_v8.py +66 -0
  219. rapidtide/tests/test_getparsers.py +158 -0
  220. rapidtide/tests/test_io.py +59 -18
  221. rapidtide/tests/{test_glmpass.py → test_linfitfiltpass.py} +10 -10
  222. rapidtide/tests/test_mi.py +1 -1
  223. rapidtide/tests/test_miscmath.py +1 -1
  224. rapidtide/tests/test_motionregress.py +5 -5
  225. rapidtide/tests/test_nullcorr.py +6 -9
  226. rapidtide/tests/test_padvec.py +216 -0
  227. rapidtide/tests/test_parserfuncs.py +101 -0
  228. rapidtide/tests/test_phaseanalysis.py +1 -1
  229. rapidtide/tests/test_rapidtideparser.py +59 -53
  230. rapidtide/tests/test_refinedelay.py +296 -0
  231. rapidtide/tests/test_runmisc.py +5 -5
  232. rapidtide/tests/test_sharedmem.py +60 -0
  233. rapidtide/tests/test_simroundtrip.py +132 -0
  234. rapidtide/tests/test_simulate.py +1 -1
  235. rapidtide/tests/test_stcorrelate.py +4 -2
  236. rapidtide/tests/test_timeshift.py +2 -2
  237. rapidtide/tests/test_valtoindex.py +1 -1
  238. rapidtide/tests/test_zRapidtideDataset.py +5 -3
  239. rapidtide/tests/utils.py +10 -9
  240. rapidtide/tidepoolTemplate.py +88 -70
  241. rapidtide/tidepoolTemplate.ui +60 -46
  242. rapidtide/tidepoolTemplate_alt.py +88 -53
  243. rapidtide/tidepoolTemplate_alt.ui +62 -52
  244. rapidtide/tidepoolTemplate_alt_qt6.py +921 -0
  245. rapidtide/tidepoolTemplate_big.py +1125 -0
  246. rapidtide/tidepoolTemplate_big.ui +2386 -0
  247. rapidtide/tidepoolTemplate_big_qt6.py +1129 -0
  248. rapidtide/tidepoolTemplate_qt6.py +793 -0
  249. rapidtide/util.py +1389 -148
  250. rapidtide/voxelData.py +1048 -0
  251. rapidtide/wiener.py +138 -25
  252. rapidtide/wiener2.py +114 -8
  253. rapidtide/workflows/adjustoffset.py +107 -5
  254. rapidtide/workflows/aligntcs.py +86 -3
  255. rapidtide/workflows/applydlfilter.py +231 -89
  256. rapidtide/workflows/applyppgproc.py +540 -0
  257. rapidtide/workflows/atlasaverage.py +309 -48
  258. rapidtide/workflows/atlastool.py +130 -9
  259. rapidtide/workflows/calcSimFuncMap.py +490 -0
  260. rapidtide/workflows/calctexticc.py +202 -10
  261. rapidtide/workflows/ccorrica.py +123 -15
  262. rapidtide/workflows/cleanregressor.py +415 -0
  263. rapidtide/workflows/delayvar.py +1268 -0
  264. rapidtide/workflows/diffrois.py +84 -6
  265. rapidtide/workflows/endtidalproc.py +149 -9
  266. rapidtide/workflows/fdica.py +197 -17
  267. rapidtide/workflows/filtnifti.py +71 -4
  268. rapidtide/workflows/filttc.py +76 -5
  269. rapidtide/workflows/fitSimFuncMap.py +578 -0
  270. rapidtide/workflows/fixtr.py +74 -4
  271. rapidtide/workflows/gmscalc.py +116 -6
  272. rapidtide/workflows/happy.py +1242 -480
  273. rapidtide/workflows/happy2std.py +145 -13
  274. rapidtide/workflows/happy_parser.py +277 -59
  275. rapidtide/workflows/histnifti.py +120 -4
  276. rapidtide/workflows/histtc.py +85 -4
  277. rapidtide/workflows/{glmfilt.py → linfitfilt.py} +128 -14
  278. rapidtide/workflows/localflow.py +329 -29
  279. rapidtide/workflows/mergequality.py +80 -4
  280. rapidtide/workflows/niftidecomp.py +323 -19
  281. rapidtide/workflows/niftistats.py +178 -8
  282. rapidtide/workflows/pairproc.py +99 -5
  283. rapidtide/workflows/pairwisemergenifti.py +86 -3
  284. rapidtide/workflows/parser_funcs.py +1488 -56
  285. rapidtide/workflows/physiofreq.py +139 -12
  286. rapidtide/workflows/pixelcomp.py +211 -9
  287. rapidtide/workflows/plethquality.py +105 -23
  288. rapidtide/workflows/polyfitim.py +159 -19
  289. rapidtide/workflows/proj2flow.py +76 -3
  290. rapidtide/workflows/rankimage.py +115 -8
  291. rapidtide/workflows/rapidtide.py +1785 -1858
  292. rapidtide/workflows/rapidtide2std.py +101 -3
  293. rapidtide/workflows/rapidtide_parser.py +590 -389
  294. rapidtide/workflows/refineDelayMap.py +249 -0
  295. rapidtide/workflows/refineRegressor.py +1215 -0
  296. rapidtide/workflows/regressfrommaps.py +308 -0
  297. rapidtide/workflows/resamplenifti.py +86 -4
  298. rapidtide/workflows/resampletc.py +92 -4
  299. rapidtide/workflows/retrolagtcs.py +442 -0
  300. rapidtide/workflows/retroregress.py +1501 -0
  301. rapidtide/workflows/roisummarize.py +176 -7
  302. rapidtide/workflows/runqualitycheck.py +72 -7
  303. rapidtide/workflows/showarbcorr.py +172 -16
  304. rapidtide/workflows/showhist.py +87 -3
  305. rapidtide/workflows/showstxcorr.py +161 -4
  306. rapidtide/workflows/showtc.py +172 -10
  307. rapidtide/workflows/showxcorrx.py +250 -62
  308. rapidtide/workflows/showxy.py +186 -16
  309. rapidtide/workflows/simdata.py +418 -112
  310. rapidtide/workflows/spatialfit.py +83 -8
  311. rapidtide/workflows/spatialmi.py +252 -29
  312. rapidtide/workflows/spectrogram.py +306 -33
  313. rapidtide/workflows/synthASL.py +157 -6
  314. rapidtide/workflows/tcfrom2col.py +77 -3
  315. rapidtide/workflows/tcfrom3col.py +75 -3
  316. rapidtide/workflows/tidepool.py +3829 -666
  317. rapidtide/workflows/utils.py +45 -19
  318. rapidtide/workflows/utils_doc.py +293 -0
  319. rapidtide/workflows/variabilityizer.py +118 -5
  320. {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info}/METADATA +30 -223
  321. rapidtide-3.1.3.dist-info/RECORD +393 -0
  322. {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info}/WHEEL +1 -1
  323. rapidtide-3.1.3.dist-info/entry_points.txt +65 -0
  324. rapidtide-3.1.3.dist-info/top_level.txt +2 -0
  325. rapidtide/calcandfitcorrpairs.py +0 -262
  326. rapidtide/data/examples/src/testoutputsize +0 -45
  327. rapidtide/data/models/model_revised/model.h5 +0 -0
  328. rapidtide/data/models/model_serdar/model.h5 +0 -0
  329. rapidtide/data/models/model_serdar2/model.h5 +0 -0
  330. rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm.nii.gz +0 -0
  331. rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm_mask.nii.gz +0 -0
  332. rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm.nii.gz +0 -0
  333. rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm_mask.nii.gz +0 -0
  334. rapidtide/data/reference/HCP1200_binmask_2mm_2009c_asym.nii.gz +0 -0
  335. rapidtide/data/reference/HCP1200_lag_2mm_2009c_asym.nii.gz +0 -0
  336. rapidtide/data/reference/HCP1200_mask_2mm_2009c_asym.nii.gz +0 -0
  337. rapidtide/data/reference/HCP1200_negmask_2mm_2009c_asym.nii.gz +0 -0
  338. rapidtide/data/reference/HCP1200_sigma_2mm_2009c_asym.nii.gz +0 -0
  339. rapidtide/data/reference/HCP1200_strength_2mm_2009c_asym.nii.gz +0 -0
  340. rapidtide/glmpass.py +0 -434
  341. rapidtide/refine_factored.py +0 -641
  342. rapidtide/scripts/retroglm +0 -23
  343. rapidtide/workflows/glmfrommaps.py +0 -202
  344. rapidtide/workflows/retroglm.py +0 -643
  345. rapidtide-2.9.6.data/scripts/adjustoffset +0 -23
  346. rapidtide-2.9.6.data/scripts/aligntcs +0 -23
  347. rapidtide-2.9.6.data/scripts/applydlfilter +0 -23
  348. rapidtide-2.9.6.data/scripts/atlasaverage +0 -23
  349. rapidtide-2.9.6.data/scripts/atlastool +0 -23
  350. rapidtide-2.9.6.data/scripts/calcicc +0 -22
  351. rapidtide-2.9.6.data/scripts/calctexticc +0 -23
  352. rapidtide-2.9.6.data/scripts/calcttest +0 -22
  353. rapidtide-2.9.6.data/scripts/ccorrica +0 -23
  354. rapidtide-2.9.6.data/scripts/diffrois +0 -23
  355. rapidtide-2.9.6.data/scripts/endtidalproc +0 -23
  356. rapidtide-2.9.6.data/scripts/filtnifti +0 -23
  357. rapidtide-2.9.6.data/scripts/filttc +0 -23
  358. rapidtide-2.9.6.data/scripts/fingerprint +0 -593
  359. rapidtide-2.9.6.data/scripts/fixtr +0 -23
  360. rapidtide-2.9.6.data/scripts/glmfilt +0 -24
  361. rapidtide-2.9.6.data/scripts/gmscalc +0 -22
  362. rapidtide-2.9.6.data/scripts/happy +0 -25
  363. rapidtide-2.9.6.data/scripts/happy2std +0 -23
  364. rapidtide-2.9.6.data/scripts/happywarp +0 -350
  365. rapidtide-2.9.6.data/scripts/histnifti +0 -23
  366. rapidtide-2.9.6.data/scripts/histtc +0 -23
  367. rapidtide-2.9.6.data/scripts/localflow +0 -23
  368. rapidtide-2.9.6.data/scripts/mergequality +0 -23
  369. rapidtide-2.9.6.data/scripts/pairproc +0 -23
  370. rapidtide-2.9.6.data/scripts/pairwisemergenifti +0 -23
  371. rapidtide-2.9.6.data/scripts/physiofreq +0 -23
  372. rapidtide-2.9.6.data/scripts/pixelcomp +0 -23
  373. rapidtide-2.9.6.data/scripts/plethquality +0 -23
  374. rapidtide-2.9.6.data/scripts/polyfitim +0 -23
  375. rapidtide-2.9.6.data/scripts/proj2flow +0 -23
  376. rapidtide-2.9.6.data/scripts/rankimage +0 -23
  377. rapidtide-2.9.6.data/scripts/rapidtide +0 -23
  378. rapidtide-2.9.6.data/scripts/rapidtide2std +0 -23
  379. rapidtide-2.9.6.data/scripts/resamplenifti +0 -23
  380. rapidtide-2.9.6.data/scripts/resampletc +0 -23
  381. rapidtide-2.9.6.data/scripts/retroglm +0 -23
  382. rapidtide-2.9.6.data/scripts/roisummarize +0 -23
  383. rapidtide-2.9.6.data/scripts/runqualitycheck +0 -23
  384. rapidtide-2.9.6.data/scripts/showarbcorr +0 -23
  385. rapidtide-2.9.6.data/scripts/showhist +0 -23
  386. rapidtide-2.9.6.data/scripts/showstxcorr +0 -23
  387. rapidtide-2.9.6.data/scripts/showtc +0 -23
  388. rapidtide-2.9.6.data/scripts/showxcorr_legacy +0 -536
  389. rapidtide-2.9.6.data/scripts/showxcorrx +0 -23
  390. rapidtide-2.9.6.data/scripts/showxy +0 -23
  391. rapidtide-2.9.6.data/scripts/simdata +0 -23
  392. rapidtide-2.9.6.data/scripts/spatialdecomp +0 -23
  393. rapidtide-2.9.6.data/scripts/spatialfit +0 -23
  394. rapidtide-2.9.6.data/scripts/spatialmi +0 -23
  395. rapidtide-2.9.6.data/scripts/spectrogram +0 -23
  396. rapidtide-2.9.6.data/scripts/synthASL +0 -23
  397. rapidtide-2.9.6.data/scripts/tcfrom2col +0 -23
  398. rapidtide-2.9.6.data/scripts/tcfrom3col +0 -23
  399. rapidtide-2.9.6.data/scripts/temporaldecomp +0 -23
  400. rapidtide-2.9.6.data/scripts/threeD +0 -236
  401. rapidtide-2.9.6.data/scripts/tidepool +0 -23
  402. rapidtide-2.9.6.data/scripts/variabilityizer +0 -23
  403. rapidtide-2.9.6.dist-info/RECORD +0 -359
  404. rapidtide-2.9.6.dist-info/top_level.txt +0 -86
  405. {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info/licenses}/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
- # Copyright 2016-2024 Blaise Frederick
4
+ # Copyright 2016-2025 Blaise Frederick
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -17,18 +17,13 @@
17
17
  #
18
18
  #
19
19
  import sys
20
- import warnings
20
+ from typing import Any
21
21
 
22
22
  import matplotlib.pyplot as plt
23
23
  import numpy as np
24
24
  import scipy as sp
25
- from numpy.polynomial import Polynomial
26
- from scipy.optimize import curve_fit
27
- from statsmodels.robust import mad
25
+ from numpy.typing import NDArray
28
26
 
29
- import rapidtide.correlate as tide_corr
30
- import rapidtide.filter as tide_filt
31
- import rapidtide.fit as tide_fit
32
27
  import rapidtide.miscmath as tide_math
33
28
  import rapidtide.util as tide_util
34
29
 
@@ -46,9 +41,46 @@ class fMRIDataset:
46
41
  numskip = 0
47
42
  validvoxels = None
48
43
 
49
- def __init__(self, thedata, zerodata=False, copydata=False, numskip=0):
44
+ def __init__(
45
+ self, thedata: NDArray, zerodata: bool = False, copydata: bool = False, numskip: int = 0
46
+ ) -> None:
47
+ """
48
+ Initialize the fMRIDataset with data and configuration parameters.
49
+
50
+ Parameters
51
+ ----------
52
+ thedata : NDArray
53
+ The input data array to be stored in the object.
54
+ zerodata : bool, optional
55
+ If True, initializes the data with zeros instead of copying the input data.
56
+ Default is False.
57
+ copydata : bool, optional
58
+ If True and zerodata is False, creates a copy of the input data.
59
+ If False and zerodata is False, uses the input data directly.
60
+ Default is False.
61
+ numskip : int, optional
62
+ Number of elements to skip during processing. Default is 0.
63
+
64
+ Returns
65
+ -------
66
+ None
67
+ This method does not return any value.
68
+
69
+ Notes
70
+ -----
71
+ The initialization process involves:
72
+ 1. Setting the data based on the zerodata and copydata parameters
73
+ 2. Calling getsizes() to determine data dimensions
74
+ 3. Calling setnumskip() to configure the skip parameter
75
+
76
+ Examples
77
+ --------
78
+ >>> obj = MyClass(data_array)
79
+ >>> obj = MyClass(data_array, zerodata=True)
80
+ >>> obj = MyClass(data_array, copydata=True, numskip=5)
81
+ """
50
82
  if zerodata:
51
- self.thedata = thedata * 0.0
83
+ self.thedata = np.zeros_like(thedata)
52
84
  else:
53
85
  if copydata:
54
86
  self.thedata = thedata + 0.0
@@ -57,7 +89,56 @@ class fMRIDataset:
57
89
  self.getsizes()
58
90
  self.setnumskip(numskip)
59
91
 
60
- def getsizes(self):
92
+ def getsizes(self) -> None:
93
+ """
94
+ Calculate and store various size parameters from data shape.
95
+
96
+ This method extracts dimensional information from the data shape and computes
97
+ derived quantities such as slice size and total number of voxels. It handles
98
+ both 3D and 4D data arrays by checking for the presence of a fourth dimension.
99
+
100
+ Parameters
101
+ ----------
102
+ self : object
103
+ The instance containing the data array in `thedata` attribute.
104
+ The `thedata` attribute should be a numpy array with shape (xsize, ysize, numslices, [realtimepoints])
105
+
106
+ Returns
107
+ -------
108
+ None
109
+ This method does not return any value but modifies the instance attributes.
110
+
111
+ Notes
112
+ -----
113
+ The method assumes `thedata` is a numpy array with at least 2 dimensions.
114
+ If the fourth dimension is not present, `realtimepoints` is set to 1.
115
+
116
+ Attributes Modified
117
+ -------------------
118
+ theshape : tuple
119
+ The shape of the data array
120
+ xsize : int
121
+ Size of the first dimension (x-axis)
122
+ ysize : int
123
+ Size of the second dimension (y-axis)
124
+ numslices : int
125
+ Number of slices (third dimension)
126
+ realtimepoints : int
127
+ Number of real-time points (fourth dimension, default 1)
128
+ slicesize : int
129
+ Product of xsize and ysize (number of pixels per slice)
130
+ numvox : int
131
+ Total number of voxels (slicesize * numslices)
132
+
133
+ Examples
134
+ --------
135
+ >>> # Assuming self.thedata has shape (64, 64, 30, 100)
136
+ >>> getsizes(self)
137
+ >>> print(self.xsize, self.ysize, self.numslices, self.realtimepoints)
138
+ 64 64 30 100
139
+ >>> print(self.slicesize, self.numvox)
140
+ 4096 122880
141
+ """
61
142
  self.theshape = self.thedata.shape
62
143
  self.xsize = self.theshape[0]
63
144
  self.ysize = self.theshape[1]
@@ -69,22 +150,157 @@ class fMRIDataset:
69
150
  self.slicesize = self.xsize * self.ysize
70
151
  self.numvox = self.slicesize * self.numslices
71
152
 
72
- def setnumskip(self, numskip):
153
+ def setnumskip(self, numskip: int) -> None:
154
+ """
155
+ Set the number of timepoints to skip and update the timepoints accordingly.
156
+
157
+ This method updates the internal `numskip` attribute and recalculates the
158
+ `timepoints` by subtracting the number of skipped points from the real timepoints.
159
+
160
+ Parameters
161
+ ----------
162
+ numskip : int
163
+ The number of timepoints to skip. This value is stored in the `numskip`
164
+ attribute and used to compute the effective timepoints.
165
+
166
+ Returns
167
+ -------
168
+ None
169
+ This method modifies the object's attributes in-place and does not return
170
+ any value.
171
+
172
+ Notes
173
+ -----
174
+ The `timepoints` attribute is automatically updated to reflect the difference
175
+ between `realtimepoints` and `numskip`. This is typically used in time-series
176
+ analysis where certain initial timepoints are excluded from calculations.
177
+
178
+ Examples
179
+ --------
180
+ >>> obj.setnumskip(5)
181
+ >>> print(obj.numskip)
182
+ 5
183
+ >>> print(obj.timepoints)
184
+ # Will show the difference between realtimepoints and 5
185
+ """
73
186
  self.numskip = numskip
74
187
  self.timepoints = self.realtimepoints - self.numskip
75
188
 
76
- def setvalid(self, validvoxels):
189
+ def setvalid(self, validvoxels: NDArray) -> None:
190
+ """
191
+ Set the valid voxels for the object.
192
+
193
+ Parameters
194
+ ----------
195
+ validvoxels : NDArray
196
+ Array containing the valid voxel indices or flags indicating
197
+ which voxels are considered valid in the dataset.
198
+
199
+ Returns
200
+ -------
201
+ None
202
+ This method does not return any value.
203
+
204
+ Notes
205
+ -----
206
+ This method assigns the provided array to the internal `validvoxels` attribute
207
+ of the object, which is typically used to filter or validate voxel data
208
+ during processing operations.
209
+
210
+ Examples
211
+ --------
212
+ >>> obj = MyClass()
213
+ >>> valid_voxels = np.array([1, 2, 3, 5, 8])
214
+ >>> obj.setvalid(valid_voxels)
215
+ >>> print(obj.validvoxels)
216
+ [1 2 3 5 8]
217
+ """
77
218
  self.validvoxels = validvoxels
78
219
 
79
- def byslice(self):
220
+ def byslice(self) -> NDArray:
221
+ """
222
+ Return data sliced along the time dimension with specified skip.
223
+
224
+ This method extracts data from the internal `thedata` array, skipping
225
+ the first `numskip` time points and reshaping the result into a
226
+ standardized 3D array format.
227
+
228
+ Returns
229
+ -------
230
+ NDArray
231
+ 3D array with shape (slicesize, numslices, timepoints) containing
232
+ the sliced data with skipped time points removed.
233
+
234
+ Notes
235
+ -----
236
+ The returned array is reshaped from the original data structure to
237
+ facilitate further processing and analysis. The slicing operation
238
+ removes the first `numskip` time points from the original data.
239
+
240
+ Examples
241
+ --------
242
+ >>> result = obj.byslice()
243
+ >>> print(result.shape)
244
+ (100, 50, 200)
245
+ """
80
246
  return self.thedata[:, :, :, self.numskip :].reshape(
81
247
  (self.slicesize, self.numslices, self.timepoints)
82
248
  )
83
249
 
84
- def byvol(self):
250
+ def byvol(self) -> NDArray:
251
+ """
252
+ Reshape data to volume-time format.
253
+
254
+ This method extracts a subset of data along the fourth dimension and reshapes
255
+ it into a 2D array where rows represent voxels and columns represent timepoints.
256
+
257
+ Returns
258
+ -------
259
+ NDArray
260
+ 2D array of shape (numvox, timepoints) containing the reshaped data.
261
+ Each row corresponds to a voxel and each column to a timepoint.
262
+
263
+ Notes
264
+ -----
265
+ The method slices the data array starting from index `numskip` along the
266
+ fourth dimension and reshapes the remaining data into a 2D structure.
267
+
268
+ Examples
269
+ --------
270
+ >>> result = obj.byvol()
271
+ >>> print(result.shape)
272
+ (numvox, timepoints)
273
+ """
85
274
  return self.thedata[:, :, :, self.numskip :].reshape((self.numvox, self.timepoints))
86
275
 
87
- def byvox(self):
276
+ def byvox(self) -> NDArray:
277
+ """
278
+ Return voxel data with skip dimension sliced.
279
+
280
+ This method extracts a subset of the fourth dimension from the internal
281
+ data array, starting from the index specified by `numskip` to the end.
282
+
283
+ Returns
284
+ -------
285
+ NDArray
286
+ A numpy array containing the voxel data with the fourth dimension
287
+ sliced from `numskip` index to the end. The shape will be
288
+ (self.thedata.shape[0], self.thedata.shape[1], self.thedata.shape[2],
289
+ self.thedata.shape[3] - self.numskip)
290
+
291
+ Notes
292
+ -----
293
+ The function assumes that `self.thedata` is a 4-dimensional numpy array
294
+ and `self.numskip` is a non-negative integer less than the size of the
295
+ fourth dimension.
296
+
297
+ Examples
298
+ --------
299
+ >>> # Assuming self.thedata has shape (10, 10, 10, 20) and self.numskip = 5
300
+ >>> result = self.byvox()
301
+ >>> result.shape
302
+ (10, 10, 10, 15)
303
+ """
88
304
  return self.thedata[:, :, :, self.numskip :]
89
305
 
90
306
 
@@ -102,17 +318,58 @@ class ProbeRegressor:
102
318
 
103
319
  def __init__(
104
320
  self,
105
- inputvec,
106
- inputfreq,
107
- targetperiod,
108
- targetpoints,
109
- targetstartpoint,
110
- targetoversample=1,
111
- inputstart=0.0,
112
- inputoffset=0.0,
113
- targetstart=0.0,
114
- targetoffset=0.0,
115
- ):
321
+ inputvec: NDArray,
322
+ inputfreq: float,
323
+ targetperiod: float,
324
+ targetpoints: int,
325
+ targetstartpoint: int,
326
+ targetoversample: int = 1,
327
+ inputstart: float = 0.0,
328
+ inputoffset: float = 0.0,
329
+ targetstart: float = 0.0,
330
+ targetoffset: float = 0.0,
331
+ ) -> None:
332
+ """
333
+ Initialize the object with input and target parameters.
334
+
335
+ Parameters
336
+ ----------
337
+ inputvec : NDArray
338
+ Input vector data array
339
+ inputfreq : float
340
+ Input frequency in Hz
341
+ targetperiod : float
342
+ Target period in seconds
343
+ targetpoints : int
344
+ Number of target points
345
+ targetstartpoint : int
346
+ Starting point index for target
347
+ targetoversample : int, optional
348
+ Oversampling factor for target (default is 1)
349
+ inputstart : float, optional
350
+ Starting time for input (default is 0.0)
351
+ inputoffset : float, optional
352
+ Input offset value (default is 0.0)
353
+ targetstart : float, optional
354
+ Starting time for target (default is 0.0)
355
+ targetoffset : float, optional
356
+ Target offset value (default is 0.0)
357
+
358
+ Returns
359
+ -------
360
+ None
361
+ This method initializes the object attributes and does not return any value.
362
+
363
+ Notes
364
+ -----
365
+ This constructor sets up the input vector with specified frequency and start time,
366
+ and initializes target parameters for subsequent processing.
367
+
368
+ Examples
369
+ --------
370
+ >>> obj = MyClass(inputvec=np.array([1, 2, 3]), inputfreq=100.0,
371
+ ... targetperiod=0.1, targetpoints=100, targetstartpoint=0)
372
+ """
116
373
  self.inputoffset = inputoffset
117
374
  self.setinputvec(inputvec, inputfreq, inputstart=inputstart)
118
375
  self.targetperiod = targetperiod
@@ -121,17 +378,111 @@ class ProbeRegressor:
121
378
  self.targetpoints = targetpoints
122
379
  self.targetstartpoint = targetstartpoint
123
380
 
124
- def setinputvec(self, inputvec, inputfreq, inputstart=0.0):
381
+ def setinputvec(self, inputvec: NDArray, inputfreq: float, inputstart: float = 0.0) -> None:
382
+ """
383
+ Set the input vector and associated parameters for the object.
384
+
385
+ Parameters
386
+ ----------
387
+ inputvec : NDArray
388
+ The input vector to be set. This is typically a numpy array containing
389
+ the input signal or data values.
390
+ inputfreq : float
391
+ The input frequency value. This represents the sampling frequency or
392
+ frequency parameter associated with the input vector.
393
+ inputstart : float, optional
394
+ The starting time or phase value for the input. Default is 0.0.
395
+
396
+ Returns
397
+ -------
398
+ None
399
+ This method does not return any value.
400
+
401
+ Notes
402
+ -----
403
+ This method assigns the provided input vector and its associated parameters
404
+ to instance variables. The input vector is stored as ``self.inputvec``,
405
+ the frequency as ``self.inputfreq``, and the start value as ``self.inputstart``.
406
+
407
+ Examples
408
+ --------
409
+ >>> import numpy as np
410
+ >>> obj = MyClass()
411
+ >>> input_data = np.array([1, 2, 3, 4, 5])
412
+ >>> obj.setinputvec(input_data, inputfreq=10.0, inputstart=0.5)
413
+ >>> print(obj.inputvec)
414
+ [1 2 3 4 5]
415
+ >>> print(obj.inputfreq)
416
+ 10.0
417
+ >>> print(obj.inputstart)
418
+ 0.5
419
+ """
125
420
  self.inputvec = inputvec
126
421
  self.inputfreq = inputfreq
127
422
  self.inputstart = inputstart
128
423
 
129
- def makeinputtimeaxis(self):
424
+ def makeinputtimeaxis(self) -> None:
425
+ """
426
+ Create input time axis based on input vector properties.
427
+
428
+ This method generates a time axis for input data by linearly spacing
429
+ from 0 to the length of the input vector, normalized by the input frequency,
430
+ and adjusted by the input start time and offset.
431
+
432
+ Parameters
433
+ ----------
434
+ None
435
+
436
+ Returns
437
+ -------
438
+ None
439
+ This method modifies the instance in-place by setting the `inputtimeaxis` attribute.
440
+
441
+ Notes
442
+ -----
443
+ The time axis is calculated as:
444
+ ``inputtimeaxis = np.linspace(0.0, len(inputvec)) / inputfreq - (inputstarttime + inputoffset)``
445
+
446
+ Examples
447
+ --------
448
+ >>> obj.makeinputtimeaxis()
449
+ >>> print(obj.inputtimeaxis)
450
+ [ 0. 0.001 0.002 ... 0.998 0.999 ]
451
+ """
130
452
  self.inputtimeaxis = np.linspace(0.0, len(self.inputvec)) / self.inputfreq - (
131
453
  self.inputstarttime + self.inputoffset
132
454
  )
133
455
 
134
- def maketargettimeaxis(self):
456
+ def maketargettimeaxis(self) -> None:
457
+ """
458
+ Create a target time axis for signal processing.
459
+
460
+ This method generates a linearly spaced time axis based on the target period,
461
+ start point, and number of points specified in the object's attributes.
462
+
463
+ Parameters
464
+ ----------
465
+ None
466
+
467
+ Returns
468
+ -------
469
+ None
470
+ This method modifies the object's `targettimeaxis` attribute in-place.
471
+
472
+ Notes
473
+ -----
474
+ The time axis is generated using `numpy.linspace` with the following parameters:
475
+ - Start point: `targetperiod * targetstartpoint`
476
+ - End point: `targetperiod * targetstartpoint + targetperiod * targetpoints`
477
+ - Number of points: `targetpoints`
478
+ - Endpoint: True
479
+
480
+ Examples
481
+ --------
482
+ >>> obj.maketargettimeaxis()
483
+ >>> print(obj.targettimeaxis)
484
+ [0. 0.1 0.2 0.3 0.4]
485
+ """
135
486
  self.targettimeaxis = np.linspace(
136
487
  self.targetperiod * self.targetstartpoint,
137
488
  self.targetperiod * self.targetstartpoint + self.targetperiod * self.targetpoints,
@@ -140,322 +491,6 @@ class ProbeRegressor:
140
491
  )
141
492
 
142
493
 
143
- class SimilarityFunctionator:
144
- reftc = None
145
- prepreftc = None
146
- testtc = None
147
- preptesttc = None
148
- timeaxis = None
149
- similarityfunclen = 0
150
- datavalid = False
151
- timeaxisvalid = False
152
- similarityfuncorigin = 0
153
-
154
- def __init__(
155
- self,
156
- Fs=0.0,
157
- similarityfuncorigin=0,
158
- lagmininpts=0,
159
- lagmaxinpts=0,
160
- ncprefilter=None,
161
- negativegradient=False,
162
- reftc=None,
163
- reftcstart=0.0,
164
- detrendorder=1,
165
- debug=False,
166
- ):
167
- self.Fs = Fs
168
- self.similarityfuncorigin = similarityfuncorigin
169
- self.lagmininpts = lagmininpts
170
- self.lagmaxinpts = lagmaxinpts
171
- self.ncprefilter = ncprefilter
172
- self.negativegradient = negativegradient
173
- self.reftc = reftc
174
- self.detrendorder = detrendorder
175
- self.debug = debug
176
- if self.reftc is not None:
177
- self.setreftc(self.reftc)
178
- self.reftcstart = reftcstart
179
-
180
- def preptc(self, thetc, isreftc=False):
181
- # prepare timecourse by filtering, normalizing, detrending, and applying a window function
182
- if isreftc or (not self.negativegradient):
183
- thenormtc = tide_math.corrnormalize(
184
- self.ncprefilter.apply(self.Fs, thetc),
185
- detrendorder=self.detrendorder,
186
- windowfunc=self.windowfunc,
187
- )
188
- else:
189
- thenormtc = tide_math.corrnormalize(
190
- -np.gradient(self.ncprefilter.apply(self.Fs, thetc)),
191
- detrendorder=self.detrendorder,
192
- windowfunc=self.windowfunc,
193
- )
194
- return thenormtc
195
-
196
- def trim(self, vector):
197
- return vector[
198
- self.similarityfuncorigin
199
- - self.lagmininpts : self.similarityfuncorigin
200
- + self.lagmaxinpts
201
- ]
202
-
203
- def getfunction(self, trim=True):
204
- if self.datavalid:
205
- if trim:
206
- return (
207
- self.trim(self.thesimfunc),
208
- self.trim(self.timeaxis),
209
- self.theglobalmax,
210
- )
211
- else:
212
- return self.thesimfunc, self.timeaxis, self.theglobalmax
213
- else:
214
- if self.timeaxisvalid:
215
- if trim:
216
- return None, self.trim(self.timeaxis), None
217
- else:
218
- return None, self.timeaxis, None
219
- else:
220
- print("must calculate similarity function before fetching data")
221
- return None, None, None
222
-
223
-
224
- class MutualInformationator(SimilarityFunctionator):
225
- def __init__(
226
- self,
227
- windowfunc="hamming",
228
- norm=True,
229
- madnorm=False,
230
- smoothingtime=-1.0,
231
- bins=20,
232
- sigma=0.25,
233
- *args,
234
- **kwargs,
235
- ):
236
- self.windowfunc = windowfunc
237
- self.norm = norm
238
- self.madnorm = madnorm
239
- self.bins = bins
240
- self.sigma = sigma
241
- self.smoothingtime = smoothingtime
242
- self.smoothingfilter = tide_filt.NoncausalFilter(filtertype="arb")
243
- self.mi_norm = 1.0
244
- if self.smoothingtime > 0.0:
245
- self.smoothingfilter.setfreqs(
246
- 0.0, 0.0, 1.0 / self.smoothingtime, 1.0 / self.smoothingtime
247
- )
248
- super(MutualInformationator, self).__init__(*args, **kwargs)
249
-
250
- def setlimits(self, lagmininpts, lagmaxinpts):
251
- self.lagmininpts = lagmininpts
252
- self.lagmaxinpts = lagmaxinpts
253
- origpadtime = self.smoothingfilter.getpadtime()
254
- timespan = self.timeaxis[-1] - self.timeaxis[0]
255
- newpadtime = np.min([origpadtime, timespan])
256
- if newpadtime < origpadtime:
257
- print("lowering smoothing filter pad time to", newpadtime)
258
- self.smoothingfilter.setpadtime(newpadtime)
259
-
260
- def setbins(self, bins):
261
- self.bins = bins
262
-
263
- def setreftc(self, reftc, offset=0.0):
264
- self.reftc = reftc + 0.0
265
- self.prepreftc = self.preptc(self.reftc, isreftc=True)
266
-
267
- self.timeaxis, self.automi, self.similarityfuncorigin = tide_corr.cross_mutual_info(
268
- self.prepreftc,
269
- self.prepreftc,
270
- Fs=self.Fs,
271
- fast=True,
272
- negsteps=self.lagmininpts,
273
- possteps=self.lagmaxinpts,
274
- returnaxis=True,
275
- )
276
-
277
- self.timeaxis -= offset
278
- self.similarityfunclen = len(self.timeaxis)
279
- self.timeaxisvalid = True
280
- self.datavalid = False
281
- self.mi_norm = np.nan_to_num(1.0 / np.max(self.automi))
282
- if self.debug:
283
- print(f"MutualInformationator setreftc: {len(self.timeaxis)=}")
284
- print(f"MutualInformationator setreftc: {self.timeaxis}")
285
- print(f"MutualInformationator setreftc: {self.mi_norm=}")
286
-
287
- def getnormfac(self):
288
- return self.mi_norm
289
-
290
- def run(self, thetc, locs=None, trim=True, gettimeaxis=True):
291
- if len(thetc) != len(self.reftc):
292
- print(
293
- "timecourses are of different sizes:",
294
- len(thetc),
295
- "!=",
296
- len(self.reftc),
297
- "- exiting",
298
- )
299
- sys.exit()
300
-
301
- self.testtc = thetc
302
- self.preptesttc = self.preptc(self.testtc)
303
-
304
- if locs is not None:
305
- gettimeaxis = True
306
-
307
- # now calculate the similarity function
308
- if trim:
309
- retvals = tide_corr.cross_mutual_info(
310
- self.preptesttc,
311
- self.prepreftc,
312
- norm=self.norm,
313
- negsteps=self.lagmininpts,
314
- possteps=self.lagmaxinpts,
315
- locs=locs,
316
- madnorm=self.madnorm,
317
- returnaxis=gettimeaxis,
318
- fast=True,
319
- Fs=self.Fs,
320
- sigma=self.sigma,
321
- bins=self.bins,
322
- )
323
- else:
324
- retvals = tide_corr.cross_mutual_info(
325
- self.preptesttc,
326
- self.prepreftc,
327
- norm=self.norm,
328
- negsteps=-1,
329
- possteps=-1,
330
- locs=locs,
331
- madnorm=self.madnorm,
332
- returnaxis=gettimeaxis,
333
- fast=True,
334
- Fs=self.Fs,
335
- sigma=self.sigma,
336
- bins=self.bins,
337
- )
338
- if gettimeaxis:
339
- self.timeaxis, self.thesimfunc, self.similarityfuncorigin = (
340
- retvals[0],
341
- retvals[1],
342
- retvals[2],
343
- )
344
- self.timeaxisvalid = True
345
- else:
346
- self.thesimfunc = retvals[0]
347
-
348
- # normalize
349
- self.thesimfunc *= self.mi_norm
350
-
351
- if locs is not None:
352
- return self.thesimfunc
353
-
354
- if self.smoothingtime > 0.0:
355
- self.thesimfunc = self.smoothingfilter.apply(self.Fs, self.thesimfunc)
356
-
357
- self.similarityfunclen = len(self.thesimfunc)
358
- if trim:
359
- self.similarityfuncorigin = self.lagmininpts + 1
360
- else:
361
- self.similarityfuncorigin = self.similarityfunclen // 2 + 1
362
-
363
- # find the global maximum value
364
- self.theglobalmax = np.argmax(self.thesimfunc)
365
- self.datavalid = True
366
-
367
- # make a dummy filtered baseline
368
- self.filteredbaseline = self.thesimfunc * 0.0
369
-
370
- if trim:
371
- return (
372
- self.trim(self.thesimfunc),
373
- self.trim(self.timeaxis),
374
- self.theglobalmax,
375
- )
376
- else:
377
- return self.thesimfunc, self.timeaxis, self.theglobalmax
378
-
379
-
380
- class Correlator(SimilarityFunctionator):
381
- def __init__(
382
- self,
383
- windowfunc="hamming",
384
- corrweighting="None",
385
- corrpadding=0,
386
- baselinefilter=None,
387
- *args,
388
- **kwargs,
389
- ):
390
- self.windowfunc = windowfunc
391
- self.corrweighting = corrweighting
392
- self.corrpadding = corrpadding
393
- self.baselinefilter = baselinefilter
394
- super(Correlator, self).__init__(*args, **kwargs)
395
-
396
- def setlimits(self, lagmininpts, lagmaxinpts):
397
- self.lagmininpts = lagmininpts
398
- self.lagmaxinpts = lagmaxinpts
399
-
400
- def setreftc(self, reftc, offset=0.0):
401
- self.reftc = reftc + 0.0
402
- self.prepreftc = self.preptc(self.reftc, isreftc=True)
403
- self.similarityfunclen = len(self.reftc) * 2 - 1
404
- self.similarityfuncorigin = self.similarityfunclen // 2 + 1
405
-
406
- # make the reference time axis
407
- self.timeaxis = (
408
- np.arange(0.0, self.similarityfunclen) * (1.0 / self.Fs)
409
- - ((self.similarityfunclen - 1) * (1.0 / self.Fs)) / 2.0
410
- ) - offset
411
- self.timeaxisvalid = True
412
- self.datavalid = False
413
-
414
- def run(self, thetc, trim=True):
415
- if len(thetc) != len(self.reftc):
416
- print(
417
- "timecourses are of different sizes:",
418
- len(thetc),
419
- "!=",
420
- len(self.reftc),
421
- "- exiting",
422
- )
423
- sys.exit()
424
-
425
- self.testtc = thetc
426
- self.preptesttc = self.preptc(self.testtc)
427
-
428
- # now actually do the correlation
429
- self.thesimfunc = tide_corr.fastcorrelate(
430
- self.preptesttc,
431
- self.prepreftc,
432
- usefft=True,
433
- weighting=self.corrweighting,
434
- zeropadding=self.corrpadding,
435
- debug=self.debug,
436
- )
437
- self.similarityfunclen = len(self.thesimfunc)
438
- self.similarityfuncorigin = self.similarityfunclen // 2 + 1
439
-
440
- if self.baselinefilter is not None:
441
- self.filteredbaseline = self.baselinefilter.apply(self.Fs, self.thesimfunc)
442
- else:
443
- self.filteredbaseline = self.thesimfunc * 0.0
444
-
445
- # find the global maximum value
446
- self.theglobalmax = np.argmax(self.thesimfunc)
447
- self.datavalid = True
448
-
449
- if trim:
450
- return (
451
- self.trim(self.thesimfunc),
452
- self.trim(self.timeaxis),
453
- self.theglobalmax,
454
- )
455
- else:
456
- return self.thesimfunc, self.timeaxis, self.theglobalmax
457
-
458
-
459
494
  class Coherer:
460
495
  reftc = None
461
496
  prepreftc = None
@@ -471,15 +506,58 @@ class Coherer:
471
506
 
472
507
  def __init__(
473
508
  self,
474
- Fs=0.0,
475
- freqmin=None,
476
- freqmax=None,
477
- ncprefilter=None,
478
- reftc=None,
479
- detrendorder=1,
480
- windowfunc="hamming",
481
- debug=False,
482
- ):
509
+ Fs: float = 0.0,
510
+ freqmin: float | None = None,
511
+ freqmax: float | None = None,
512
+ ncprefilter: Any | None = None,
513
+ reftc: NDArray | None = None,
514
+ detrendorder: int = 1,
515
+ windowfunc: str = "hamming",
516
+ debug: bool = False,
517
+ ) -> None:
518
+ """
519
+ Initialize the Coherer object with configuration parameters.
520
+
521
+ Parameters
522
+ ----------
523
+ Fs : float, default=0.0
524
+ Sampling frequency in Hz.
525
+ freqmin : float, optional
526
+ Minimum frequency for filtering. If None, no minimum frequency filtering is applied.
527
+ freqmax : float, optional
528
+ Maximum frequency for filtering. If None, no maximum frequency filtering is applied.
529
+ ncprefilter : Any, optional
530
+ Pre-filtering configuration for non-coherent filtering.
531
+ reftc : NDArray, optional
532
+ Reference time course for coherence calculations.
533
+ detrendorder : int, default=1
534
+ Order of detrending to apply to the data. 0 for no detrending, 1 for linear detrending.
535
+ windowfunc : str, default="hamming"
536
+ Window function to apply during spectral analysis. Options include 'hamming', 'hanning', 'blackman', etc.
537
+ debug : bool, default=False
538
+ If True, print initialization debug information.
539
+
540
+ Returns
541
+ -------
542
+ None
543
+ This method initializes the object's attributes and performs setup operations.
544
+
545
+ Notes
546
+ -----
547
+ The initialization process sets up all internal parameters and performs optional
548
+ debug printing if requested. The freqmin and freqmax parameters are only stored
549
+ if they are not None. The reftc parameter, if provided, triggers the setreftc method.
550
+
551
+ Examples
552
+ --------
553
+ >>> coherer = Coherer(Fs=100.0, freqmin=1.0, freqmax=50.0, debug=True)
554
+ Coherer init:
555
+ Fs: 100.0
556
+ windowfunc: hamming
557
+ detrendorder: 1
558
+ freqmin: 1.0
559
+ freqmax: 50.0
560
+ """
483
561
  self.Fs = Fs
484
562
  self.ncprefilter = ncprefilter
485
563
  self.reftc = reftc
@@ -500,7 +578,37 @@ class Coherer:
500
578
  print("\tfreqmin:", self.freqmin)
501
579
  print("\tfreqmax:", self.freqmax)
502
580
 
503
- def preptc(self, thetc):
581
+ def preptc(self, thetc: NDArray) -> NDArray:
582
+ """
583
+ Prepare timecourse by filtering, normalizing, detrending, and applying a window function.
584
+
585
+ This function applies a series of preprocessing steps to a timecourse signal including
586
+ noise filtering, normalization, and detrending to prepare it for further analysis.
587
+
588
+ Parameters
589
+ ----------
590
+ thetc : ndarray
591
+ Input timecourse data to be prepared, typically a 1D array of signal values.
592
+
593
+ Returns
594
+ -------
595
+ ndarray
596
+ Preprocessed timecourse data after filtering, normalization, and detrending.
597
+
598
+ Notes
599
+ -----
600
+ The preprocessing pipeline includes:
601
+ 1. Noise filtering using the class's ncprefilter
602
+ 2. Correlation-based normalization
603
+ 3. Detrending with specified order
604
+
605
+ Examples
606
+ --------
607
+ >>> # Assuming 'obj' is an instance of the class containing this method
608
+ >>> processed_tc = obj.preptc(timecourse_data)
609
+ >>> print(processed_tc.shape)
610
+ (n_timepoints,)
611
+ """
504
612
  # prepare timecourse by filtering, normalizing, detrending, and applying a window function
505
613
  return tide_math.corrnormalize(
506
614
  self.ncprefilter.apply(self.Fs, thetc),
@@ -508,7 +616,37 @@ class Coherer:
508
616
  windowfunc="None",
509
617
  )
510
618
 
511
- def setlimits(self, freqmin, freqmax):
619
+ def setlimits(self, freqmin: float, freqmax: float) -> None:
620
+ """
621
+ Set frequency limits for the object and calculate corresponding indices.
622
+
623
+ This method sets the minimum and maximum frequency values and calculates the
624
+ corresponding indices in the frequency axis if the frequency axis is valid.
625
+
626
+ Parameters
627
+ ----------
628
+ freqmin : float
629
+ The minimum frequency value to set.
630
+ freqmax : float
631
+ The maximum frequency value to set.
632
+
633
+ Returns
634
+ -------
635
+ None
636
+ This method modifies the object's attributes in-place and does not return a value.
637
+
638
+ Notes
639
+ -----
640
+ If `self.freqaxisvalid` is True, the method calculates `freqmininpts` and `freqmaxinpts`
641
+ which represent the indices corresponding to the frequency limits in `self.freqaxis`.
642
+ The calculation ensures that indices stay within valid bounds (0 to len(freqaxis)-1).
643
+
644
+ Examples
645
+ --------
646
+ >>> obj.setlimits(0.1, 1.0)
647
+ >>> print(obj.freqmin, obj.freqmax)
648
+ 0.1 1.0
649
+ """
512
650
  self.freqmin = freqmin
513
651
  self.freqmax = freqmax
514
652
  if self.freqaxisvalid:
@@ -524,7 +662,32 @@ class Coherer:
524
662
  print("\tfreqmin,freqmax:", self.freqmin, self.freqmax)
525
663
  print("\tfreqmininpts,freqmaxinpts:", self.freqmininpts, self.freqmaxinpts)
526
664
 
527
- def getaxisinfo(self):
665
+ def getaxisinfo(self) -> tuple[float, float, float, int]:
666
+ """
667
+ Get frequency axis information for the object.
668
+
669
+ Returns
670
+ -------
671
+ tuple[float, float, float, int]
672
+ A tuple containing:
673
+ - Minimum frequency value (float)
674
+ - Maximum frequency value (float)
675
+ - Frequency step size (float)
676
+ - Number of frequency points (int)
677
+
678
+ Notes
679
+ -----
680
+ This method extracts key frequency axis parameters from the object's frequency array.
681
+ The frequency axis is assumed to be linearly spaced, and the returned values represent
682
+ the range and spacing of the frequency data.
683
+
684
+ Examples
685
+ --------
686
+ >>> info = obj.getaxisinfo()
687
+ >>> print(f"Frequency range: {info[0]} to {info[1]} Hz")
688
+ >>> print(f"Frequency step: {info[2]} Hz")
689
+ >>> print(f"Number of points: {info[3]}")
690
+ """
528
691
  return (
529
692
  self.freqaxis[self.freqmininpts],
530
693
  self.freqaxis[self.freqmaxinpts],
@@ -532,7 +695,43 @@ class Coherer:
532
695
  self.freqmaxinpts - self.freqmininpts,
533
696
  )
534
697
 
535
- def setreftc(self, reftc):
698
+ def setreftc(self, reftc: NDArray) -> None:
699
+ """
700
+ Set reference time series and compute coherence statistics.
701
+
702
+ This method assigns the reference time series, processes it through the preprocessing
703
+ pipeline, and computes coherence statistics between the reference signal and itself.
704
+ The method also sets up frequency axis parameters and validates the data.
705
+
706
+ Parameters
707
+ ----------
708
+ reftc : NDArray
709
+ Reference time series data to be processed. The array will be copied and converted
710
+ to float type to ensure proper numerical operations.
711
+
712
+ Returns
713
+ -------
714
+ None
715
+ This method modifies the instance attributes in-place and does not return any value.
716
+
717
+ Notes
718
+ -----
719
+ The method performs the following operations:
720
+ 1. Assigns the input array to `self.reftc` with explicit float conversion
721
+ 2. Preprocesses the reference time series using `self.preptc()` method
722
+ 3. Computes coherence between the preprocessed reference signal and itself
723
+ 4. Sets up frequency axis and coherence data attributes
724
+ 5. Validates frequency limits and converts them to array indices
725
+ 6. Updates data validity flags
726
+
727
+ Examples
728
+ --------
729
+ >>> # Assuming 'obj' is an instance of the class containing this method
730
+ >>> reference_data = np.array([1.0, 2.0, 3.0, 4.0])
731
+ >>> obj.setreftc(reference_data)
732
+ >>> print(obj.freqaxis)
733
+ >>> print(obj.thecoherence)
734
+ """
536
735
  self.reftc = reftc + 0.0
537
736
  self.prepreftc = self.preptc(self.reftc)
538
737
 
@@ -554,13 +753,39 @@ class Coherer:
554
753
  self.freqaxis, self.freqmax, discretization="ceiling", debug=self.debug
555
754
  )
556
755
 
557
- def trim(self, vector):
756
+ def trim(self, vector: NDArray) -> NDArray:
558
757
  return vector[self.freqmininpts : self.freqmaxinpts]
559
758
 
560
- def run(self, thetc, trim=True, alt=False):
759
+ def run(
760
+ self, thetc: NDArray, trim: bool = True, alt: bool = False
761
+ ) -> tuple[NDArray, NDArray, int] | tuple[NDArray, NDArray, int, NDArray, NDArray, NDArray]:
762
+ """
763
+ Trim vector to specified frequency range indices.
764
+
765
+ Parameters
766
+ ----------
767
+ vector : NDArray
768
+ Input vector to be trimmed.
769
+
770
+ Returns
771
+ -------
772
+ NDArray
773
+ Trimmed vector containing elements from `self.freqmininpts` to `self.freqmaxinpts`.
774
+
775
+ Notes
776
+ -----
777
+ This function uses array slicing to extract a portion of the input vector based on
778
+ the frequency range boundaries defined by `self.freqmininpts` and `self.freqmaxinpts`.
779
+
780
+ Examples
781
+ --------
782
+ >>> trimmed_data = obj.trim(data_vector)
783
+ >>> print(trimmed_data.shape)
784
+ (freqmaxinpts - freqmininpts,)
785
+ """
561
786
  if len(thetc) != len(self.reftc):
562
787
  print(
563
- "timecourses are of different sizes:",
788
+ "Coherer: timecourses are of different sizes:",
564
789
  len(thetc),
565
790
  "!=",
566
791
  len(self.reftc),
@@ -644,757 +869,3 @@ class Coherer:
644
869
  else:
645
870
  self.themax = np.argmax(self.thecoherence)
646
871
  return self.thecoherence, self.freqaxis, self.themax
647
-
648
-
649
- class SimilarityFunctionFitter:
650
- corrtimeaxis = None
651
- FML_NOERROR = np.uint32(0x0000)
652
-
653
- FML_INITAMPLOW = np.uint32(0x0001)
654
- FML_INITAMPHIGH = np.uint32(0x0002)
655
- FML_INITWIDTHLOW = np.uint32(0x0004)
656
- FML_INITWIDTHHIGH = np.uint32(0x0008)
657
- FML_INITLAGLOW = np.uint32(0x0010)
658
- FML_INITLAGHIGH = np.uint32(0x0020)
659
- FML_INITFAIL = (
660
- FML_INITAMPLOW
661
- | FML_INITAMPHIGH
662
- | FML_INITWIDTHLOW
663
- | FML_INITWIDTHHIGH
664
- | FML_INITLAGLOW
665
- | FML_INITLAGHIGH
666
- )
667
-
668
- FML_FITAMPLOW = np.uint32(0x0100)
669
- FML_FITAMPHIGH = np.uint32(0x0200)
670
- FML_FITWIDTHLOW = np.uint32(0x0400)
671
- FML_FITWIDTHHIGH = np.uint32(0x0800)
672
- FML_FITLAGLOW = np.uint32(0x1000)
673
- FML_FITLAGHIGH = np.uint32(0x2000)
674
- FML_FITFAIL = (
675
- FML_FITAMPLOW
676
- | FML_FITAMPHIGH
677
- | FML_FITWIDTHLOW
678
- | FML_FITWIDTHHIGH
679
- | FML_FITLAGLOW
680
- | FML_FITLAGHIGH
681
- )
682
-
683
- def __init__(
684
- self,
685
- corrtimeaxis=None,
686
- lagmin=-30.0,
687
- lagmax=30.0,
688
- absmaxsigma=1000.0,
689
- absminsigma=0.25,
690
- hardlimit=True,
691
- bipolar=False,
692
- lthreshval=0.0,
693
- uthreshval=1.0,
694
- debug=False,
695
- zerooutbadfit=True,
696
- maxguess=0.0,
697
- useguess=False,
698
- searchfrac=0.5,
699
- lagmod=1000.0,
700
- enforcethresh=True,
701
- allowhighfitamps=False,
702
- displayplots=False,
703
- functype="correlation",
704
- peakfittype="gauss",
705
- ):
706
- r"""
707
-
708
- Parameters
709
- ----------
710
- corrtimeaxis: 1D float array
711
- The time axis of the correlation function
712
- lagmin: float
713
- The minimum allowed lag time in seconds
714
- lagmax: float
715
- The maximum allowed lag time in seconds
716
- absmaxsigma: float
717
- The maximum allowed peak halfwidth in seconds
718
- hardlimit
719
- bipolar: boolean
720
- If true find the correlation peak with the maximum absolute value, regardless of sign
721
- threshval
722
- uthreshval
723
- debug
724
- zerooutbadfit
725
- maxguess
726
- useguess
727
- searchfrac
728
- lagmod
729
- enforcethresh
730
- displayplots
731
-
732
- Returns
733
- -------
734
-
735
-
736
- Methods
737
- -------
738
- fit(corrfunc):
739
- Fit the correlation function given in corrfunc and return the location of the peak in seconds, the maximum
740
- correlation value, the peak width
741
- setrange(lagmin, lagmax):
742
- Specify the search range for lag peaks, in seconds
743
- """
744
- self.setcorrtimeaxis(corrtimeaxis)
745
- self.lagmin = lagmin
746
- self.lagmax = lagmax
747
- self.absmaxsigma = absmaxsigma
748
- self.absminsigma = absminsigma
749
- self.hardlimit = hardlimit
750
- self.bipolar = bipolar
751
- self.lthreshval = lthreshval
752
- self.uthreshval = uthreshval
753
- self.debug = debug
754
- if functype == "correlation" or functype == "mutualinfo":
755
- self.functype = functype
756
- else:
757
- print("illegal functype")
758
- sys.exit()
759
- self.peakfittype = peakfittype
760
- self.zerooutbadfit = zerooutbadfit
761
- self.maxguess = maxguess
762
- self.useguess = useguess
763
- self.searchfrac = searchfrac
764
- self.lagmod = lagmod
765
- self.enforcethresh = enforcethresh
766
- self.allowhighfitamps = allowhighfitamps
767
- self.displayplots = displayplots
768
-
769
- def _maxindex_noedge(self, corrfunc):
770
- """
771
-
772
- Parameters
773
- ----------
774
- corrfunc
775
-
776
- Returns
777
- -------
778
-
779
- """
780
- lowerlim = 0
781
- upperlim = len(self.corrtimeaxis) - 1
782
- done = False
783
- while not done:
784
- flipfac = 1.0
785
- done = True
786
- maxindex = (np.argmax(corrfunc[lowerlim:upperlim]) + lowerlim).astype("int32")
787
- if self.bipolar:
788
- minindex = (np.argmax(-corrfunc[lowerlim:upperlim]) + lowerlim).astype("int32")
789
- if np.fabs(corrfunc[minindex]) > np.fabs(corrfunc[maxindex]):
790
- maxindex = minindex
791
- flipfac = -1.0
792
- if upperlim == lowerlim:
793
- done = True
794
- if maxindex == 0:
795
- lowerlim += 1
796
- done = False
797
- if maxindex == upperlim:
798
- upperlim -= 1
799
- done = False
800
- return maxindex, flipfac
801
-
802
- def setfunctype(self, functype):
803
- self.functype = functype
804
-
805
- def setpeakfittype(self, peakfittype):
806
- self.peakfittype = peakfittype
807
-
808
- def setrange(self, lagmin, lagmax):
809
- self.lagmin = lagmin
810
- self.lagmax = lagmax
811
-
812
- def setcorrtimeaxis(self, corrtimeaxis):
813
- if corrtimeaxis is not None:
814
- self.corrtimeaxis = corrtimeaxis + 0.0
815
- else:
816
- self.corrtimeaxis = corrtimeaxis
817
-
818
- def setguess(self, useguess, maxguess=0.0):
819
- self.useguess = useguess
820
- self.maxguess = maxguess
821
-
822
- def setlthresh(self, lthreshval):
823
- self.lthreshval = lthreshval
824
-
825
- def setuthresh(self, uthreshval):
826
- self.uthreshval = uthreshval
827
-
828
- def diagnosefail(self, failreason):
829
- # define error values
830
- reasons = []
831
- if failreason.astype(np.uint32) & self.FML_INITAMPLOW:
832
- reasons.append("Initial amplitude too low")
833
- if failreason.astype(np.uint32) & self.FML_INITAMPHIGH:
834
- reasons.append("Initial amplitude too high")
835
- if failreason.astype(np.uint32) & self.FML_INITWIDTHLOW:
836
- reasons.append("Initial width too low")
837
- if failreason.astype(np.uint32) & self.FML_INITWIDTHHIGH:
838
- reasons.append("Initial width too high")
839
- if failreason.astype(np.uint32) & self.FML_INITLAGLOW:
840
- reasons.append("Initial Lag too low")
841
- if failreason.astype(np.uint32) & self.FML_INITLAGHIGH:
842
- reasons.append("Initial Lag too high")
843
-
844
- if failreason.astype(np.uint32) & self.FML_FITAMPLOW:
845
- reasons.append("Fit amplitude too low")
846
- if failreason.astype(np.uint32) & self.FML_FITAMPHIGH:
847
- reasons.append("Fit amplitude too high")
848
- if failreason.astype(np.uint32) & self.FML_FITWIDTHLOW:
849
- reasons.append("Fit width too low")
850
- if failreason.astype(np.uint32) & self.FML_FITWIDTHHIGH:
851
- reasons.append("Fit width too high")
852
- if failreason.astype(np.uint32) & self.FML_FITLAGLOW:
853
- reasons.append("Fit Lag too low")
854
- if failreason.astype(np.uint32) & self.FML_FITLAGHIGH:
855
- reasons.append("Fit Lag too high")
856
-
857
- if len(reasons) > 0:
858
- return ", ".join(reasons)
859
- else:
860
- return "No error"
861
-
862
- def fit(self, incorrfunc):
863
- # check to make sure xcorr_x and xcorr_y match
864
- if self.corrtimeaxis is None:
865
- print("Correlation time axis is not defined - exiting")
866
- sys.exit()
867
- if len(self.corrtimeaxis) != len(incorrfunc):
868
- print(
869
- "Correlation time axis and values do not match in length (",
870
- len(self.corrtimeaxis),
871
- "!=",
872
- len(incorrfunc),
873
- "- exiting",
874
- )
875
- sys.exit()
876
- # set initial parameters
877
- # absmaxsigma is in seconds
878
- # maxsigma is in Hz
879
- # maxlag is in seconds
880
- warnings.filterwarnings("ignore", "Number*")
881
- failreason = self.FML_NOERROR
882
- maskval = np.uint16(1) # start out assuming the fit will succeed
883
- binwidth = self.corrtimeaxis[1] - self.corrtimeaxis[0]
884
-
885
- # set the search range
886
- lowerlim = 0
887
- upperlim = len(self.corrtimeaxis) - 1
888
- if self.debug:
889
- print(
890
- "initial search indices are",
891
- lowerlim,
892
- "to",
893
- upperlim,
894
- "(",
895
- self.corrtimeaxis[lowerlim],
896
- self.corrtimeaxis[upperlim],
897
- ")",
898
- )
899
-
900
- # make an initial guess at the fit parameters for the gaussian
901
- # start with finding the maximum value and its location
902
- flipfac = 1.0
903
- corrfunc = incorrfunc + 0.0
904
- if self.useguess:
905
- maxindex = tide_util.valtoindex(self.corrtimeaxis, self.maxguess)
906
- if (corrfunc[maxindex] < 0.0) and self.bipolar:
907
- flipfac = -1.0
908
- else:
909
- maxindex, flipfac = self._maxindex_noedge(corrfunc)
910
- corrfunc *= flipfac
911
- maxlag_init = (1.0 * self.corrtimeaxis[maxindex]).astype("float64")
912
- maxval_init = corrfunc[maxindex].astype("float64")
913
- if self.debug:
914
- print(
915
- "maxindex, maxlag_init, maxval_init:",
916
- maxindex,
917
- maxlag_init,
918
- maxval_init,
919
- )
920
-
921
- # set the baseline and baselinedev levels
922
- if (self.functype == "correlation") or (self.functype == "hybrid"):
923
- baseline = 0.0
924
- baselinedev = 0.0
925
- else:
926
- # for mutual information, there is a nonzero baseline, so we want the difference from that.
927
- baseline = np.median(corrfunc)
928
- baselinedev = mad(corrfunc)
929
- if self.debug:
930
- print("baseline, baselinedev:", baseline, baselinedev)
931
-
932
- # then calculate the width of the peak
933
- if self.peakfittype == "fastquad" or self.peakfittype == "COM":
934
- peakstart = np.max([1, maxindex - 2])
935
- peakend = np.min([len(self.corrtimeaxis) - 2, maxindex + 2])
936
- else:
937
- # come here for peakfittype of None, quad, gauss, fastgauss
938
- thegrad = np.gradient(corrfunc).astype(
939
- "float64"
940
- ) # the gradient of the correlation function
941
- if (self.functype == "correlation") or (self.functype == "hybrid"):
942
- if self.peakfittype == "quad":
943
- peakpoints = np.where(
944
- corrfunc > maxval_init - 0.05, 1, 0
945
- ) # mask for places where correlation exceeds searchfrac*maxval_init
946
- else:
947
- peakpoints = np.where(
948
- corrfunc > (baseline + self.searchfrac * (maxval_init - baseline)), 1, 0
949
- ) # mask for places where correlation exceeds searchfrac*maxval_init
950
- else:
951
- # for mutual information, there is a flattish, nonzero baseline, so we want the difference from that.
952
- peakpoints = np.where(
953
- corrfunc > (baseline + self.searchfrac * (maxval_init - baseline)),
954
- 1,
955
- 0,
956
- )
957
-
958
- peakpoints[0] = 0
959
- peakpoints[-1] = 0
960
- peakstart = np.max([1, maxindex - 1])
961
- peakend = np.min([len(self.corrtimeaxis) - 2, maxindex + 1])
962
- if self.debug:
963
- print("initial peakstart, peakend:", peakstart, peakend)
964
- if self.functype == "mutualinfo":
965
- while peakpoints[peakend + 1] == 1:
966
- peakend += 1
967
- while peakpoints[peakstart - 1] == 1:
968
- peakstart -= 1
969
- else:
970
- while thegrad[peakend + 1] <= 0.0 and peakpoints[peakend + 1] == 1:
971
- peakend += 1
972
- while thegrad[peakstart - 1] >= 0.0 and peakpoints[peakstart - 1] == 1:
973
- peakstart -= 1
974
- if self.debug:
975
- print("final peakstart, peakend:", peakstart, peakend)
976
-
977
- # deal with flat peak top
978
- while (
979
- peakend < (len(self.corrtimeaxis) - 3)
980
- and corrfunc[peakend] == corrfunc[peakend - 1]
981
- ):
982
- peakend += 1
983
- while peakstart > 2 and corrfunc[peakstart] == corrfunc[peakstart + 1]:
984
- peakstart -= 1
985
- if self.debug:
986
- print("peakstart, peakend after flattop correction:", peakstart, peakend)
987
- print("\n")
988
- for i in range(peakstart, peakend + 1):
989
- print(self.corrtimeaxis[i], corrfunc[i])
990
- print("\n")
991
- fig = plt.figure()
992
- ax = fig.add_subplot(111)
993
- ax.set_title("Peak sent to fitting routine")
994
- plt.plot(
995
- self.corrtimeaxis[peakstart : peakend + 1],
996
- corrfunc[peakstart : peakend + 1],
997
- "r",
998
- )
999
- plt.show()
1000
-
1001
- # This is calculated from first principles, but it's always big by a factor or ~1.4.
1002
- # Which makes me think I dropped a factor if sqrt(2). So fix that with a final division
1003
- maxsigma_init = np.float64(
1004
- ((peakend - peakstart + 1) * binwidth / (2.0 * np.sqrt(-np.log(self.searchfrac))))
1005
- / np.sqrt(2.0)
1006
- )
1007
- if self.debug:
1008
- print("maxsigma_init:", maxsigma_init)
1009
-
1010
- # now check the values for errors
1011
- if self.hardlimit:
1012
- rangeextension = 0.0
1013
- else:
1014
- rangeextension = (self.lagmax - self.lagmin) * 0.75
1015
- if not (
1016
- (self.lagmin - rangeextension - binwidth)
1017
- <= maxlag_init
1018
- <= (self.lagmax + rangeextension + binwidth)
1019
- ):
1020
- if maxlag_init <= (self.lagmin - rangeextension - binwidth):
1021
- failreason |= self.FML_INITLAGLOW
1022
- maxlag_init = self.lagmin - rangeextension - binwidth
1023
- else:
1024
- failreason |= self.FML_INITLAGHIGH
1025
- maxlag_init = self.lagmax + rangeextension + binwidth
1026
- if self.debug:
1027
- print("bad initial")
1028
- if maxsigma_init > self.absmaxsigma:
1029
- failreason |= self.FML_INITWIDTHHIGH
1030
- maxsigma_init = self.absmaxsigma
1031
- if self.debug:
1032
- print("bad initial width - too high")
1033
- if peakend - peakstart < 2:
1034
- failreason |= self.FML_INITWIDTHLOW
1035
- maxsigma_init = np.float64(
1036
- ((2 + 1) * binwidth / (2.0 * np.sqrt(-np.log(self.searchfrac)))) / np.sqrt(2.0)
1037
- )
1038
- if self.debug:
1039
- print("bad initial width - too low")
1040
- if (self.functype == "correlation") or (self.functype == "hybrid"):
1041
- if not (self.lthreshval <= maxval_init <= self.uthreshval) and self.enforcethresh:
1042
- failreason |= self.FML_INITAMPLOW
1043
- if self.debug:
1044
- print(
1045
- "bad initial amp:",
1046
- maxval_init,
1047
- "is less than",
1048
- self.lthreshval,
1049
- )
1050
- if maxval_init < 0.0:
1051
- failreason |= self.FML_INITAMPLOW
1052
- maxval_init = 0.0
1053
- if self.debug:
1054
- print("bad initial amp:", maxval_init, "is less than 0.0")
1055
- if maxval_init > 1.0:
1056
- failreason |= self.FML_INITAMPHIGH
1057
- maxval_init = 1.0
1058
- if self.debug:
1059
- print("bad initial amp:", maxval_init, "is greater than 1.0")
1060
- else:
1061
- # somewhat different rules for mutual information peaks
1062
- if ((maxval_init - baseline) < self.lthreshval * baselinedev) or (
1063
- maxval_init < baseline
1064
- ):
1065
- failreason |= self.FML_INITAMPLOW
1066
- maxval_init = 0.0
1067
- if self.debug:
1068
- print("bad initial amp:", maxval_init, "is less than 0.0")
1069
- if (failreason != self.FML_NOERROR) and self.zerooutbadfit:
1070
- maxval = np.float64(0.0)
1071
- maxlag = np.float64(0.0)
1072
- maxsigma = np.float64(0.0)
1073
- else:
1074
- maxval = np.float64(maxval_init)
1075
- maxlag = np.float64(maxlag_init)
1076
- maxsigma = np.float64(maxsigma_init)
1077
-
1078
- # refine if necessary
1079
- if self.peakfittype != "None":
1080
- if self.peakfittype == "COM":
1081
- X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
1082
- data = corrfunc[peakstart : peakend + 1]
1083
- maxval = maxval_init
1084
- maxlag = np.sum(X * data) / np.sum(data)
1085
- maxsigma = 10.0
1086
- elif self.peakfittype == "gauss":
1087
- X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
1088
- data = corrfunc[peakstart : peakend + 1]
1089
- # do a least squares fit over the top of the peak
1090
- # p0 = np.array([maxval_init, np.fmod(maxlag_init, lagmod), maxsigma_init], dtype='float64')
1091
- p0 = np.array([maxval_init, maxlag_init, maxsigma_init], dtype="float64")
1092
- if self.debug:
1093
- print("fit input array:", p0)
1094
- try:
1095
- plsq, dummy = sp.optimize.leastsq(
1096
- tide_fit.gaussresiduals, p0, args=(data, X), maxfev=5000
1097
- )
1098
- maxval = plsq[0] + baseline
1099
- maxlag = np.fmod((1.0 * plsq[1]), self.lagmod)
1100
- maxsigma = plsq[2]
1101
- except:
1102
- maxval = np.float64(0.0)
1103
- maxlag = np.float64(0.0)
1104
- maxsigma = np.float64(0.0)
1105
- if self.debug:
1106
- print("fit output array:", [maxval, maxlag, maxsigma])
1107
- elif self.peakfittype == "gausscf":
1108
- X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
1109
- data = corrfunc[peakstart : peakend + 1]
1110
- # do a least squares fit over the top of the peak
1111
- try:
1112
- plsq, pcov = curve_fit(
1113
- tide_fit.gaussfunc,
1114
- X,
1115
- data,
1116
- p0=[maxval_init, maxlag_init, maxsigma_init],
1117
- )
1118
- maxval = plsq[0] + baseline
1119
- maxlag = np.fmod((1.0 * plsq[1]), self.lagmod)
1120
- maxsigma = plsq[2]
1121
- except:
1122
- maxval = np.float64(0.0)
1123
- maxlag = np.float64(0.0)
1124
- maxsigma = np.float64(0.0)
1125
- if self.debug:
1126
- print("fit output array:", [maxval, maxlag, maxsigma])
1127
- elif self.peakfittype == "fastgauss":
1128
- X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
1129
- data = corrfunc[peakstart : peakend + 1]
1130
- # do a non-iterative fit over the top of the peak
1131
- # 6/12/2015 This is just broken. Gives quantized maxima
1132
- maxlag = np.float64(1.0 * np.sum(X * data) / np.sum(data))
1133
- maxsigma = np.float64(
1134
- np.sqrt(np.abs(np.sum((X - maxlag) ** 2 * data) / np.sum(data)))
1135
- )
1136
- maxval = np.float64(data.max()) + baseline
1137
- elif self.peakfittype == "fastquad":
1138
- maxlag, maxval, maxsigma, ismax, badfit = tide_fit.refinepeak_quad(
1139
- self.corrtimeaxis, corrfunc, maxindex
1140
- )
1141
- elif self.peakfittype == "quad":
1142
- X = self.corrtimeaxis[peakstart : peakend + 1]
1143
- data = corrfunc[peakstart : peakend + 1]
1144
- try:
1145
- thecoffs = Polynomial.fit(X, data, 2).convert().coef[::-1]
1146
- a = thecoffs[0]
1147
- b = thecoffs[1]
1148
- c = thecoffs[2]
1149
- maxlag = -b / (2.0 * a)
1150
- maxval = a * maxlag * maxlag + b * maxlag + c
1151
- maxsigma = 1.0 / np.fabs(a)
1152
- if self.debug:
1153
- print("poly coffs:", a, b, c)
1154
- print("maxlag, maxval, maxsigma:", maxlag, maxval, maxsigma)
1155
- except np.lib.polynomial.RankWarning:
1156
- maxlag = 0.0
1157
- maxval = 0.0
1158
- maxsigma = 0.0
1159
- if self.debug:
1160
- print("\n")
1161
- for i in range(len(X)):
1162
- print(X[i], data[i])
1163
- print("\n")
1164
- fig = plt.figure()
1165
- ax = fig.add_subplot(111)
1166
- ax.set_title("Peak and fit")
1167
- plt.plot(X, data, "r")
1168
- plt.plot(X, c + b * X + a * X * X, "b")
1169
- plt.show()
1170
-
1171
- else:
1172
- print("illegal peak refinement type")
1173
-
1174
- # check for errors in fit
1175
- fitfail = False
1176
- if self.bipolar:
1177
- lowestcorrcoeff = -1.0
1178
- else:
1179
- lowestcorrcoeff = 0.0
1180
- if (self.functype == "correlation") or (self.functype == "hybrid"):
1181
- if maxval < lowestcorrcoeff:
1182
- failreason |= self.FML_FITAMPLOW
1183
- maxval = lowestcorrcoeff
1184
- if self.debug:
1185
- print("bad fit amp: maxval is lower than lower limit")
1186
- fitfail = True
1187
- if np.abs(maxval) > 1.0:
1188
- if not self.allowhighfitamps:
1189
- failreason |= self.FML_FITAMPHIGH
1190
- if self.debug:
1191
- print(
1192
- "bad fit amp: magnitude of",
1193
- maxval,
1194
- "is greater than 1.0",
1195
- )
1196
- fitfail = True
1197
- maxval = 1.0 * np.sign(maxval)
1198
- else:
1199
- # different rules for mutual information peaks
1200
- if ((maxval - baseline) < self.lthreshval * baselinedev) or (maxval < baseline):
1201
- failreason |= self.FML_FITAMPLOW
1202
- if self.debug:
1203
- if (maxval - baseline) < self.lthreshval * baselinedev:
1204
- print(
1205
- "FITAMPLOW: maxval - baseline:",
1206
- maxval - baseline,
1207
- " < lthreshval * baselinedev:",
1208
- self.lthreshval * baselinedev,
1209
- )
1210
- if maxval < baseline:
1211
- print("FITAMPLOW: maxval < baseline:", maxval, baseline)
1212
- maxval_init = 0.0
1213
- if self.debug:
1214
- print("bad fit amp: maxval is lower than lower limit")
1215
- if (self.lagmin > maxlag) or (maxlag > self.lagmax):
1216
- if self.debug:
1217
- print("bad lag after refinement")
1218
- if self.lagmin > maxlag:
1219
- failreason |= self.FML_FITLAGLOW
1220
- maxlag = self.lagmin
1221
- else:
1222
- failreason |= self.FML_FITLAGHIGH
1223
- maxlag = self.lagmax
1224
- fitfail = True
1225
- if maxsigma > self.absmaxsigma:
1226
- failreason |= self.FML_FITWIDTHHIGH
1227
- if self.debug:
1228
- print("bad width after refinement:", maxsigma, ">", self.absmaxsigma)
1229
- maxsigma = self.absmaxsigma
1230
- fitfail = True
1231
- if maxsigma < self.absminsigma:
1232
- failreason |= self.FML_FITWIDTHLOW
1233
- if self.debug:
1234
- print("bad width after refinement:", maxsigma, "<", self.absminsigma)
1235
- maxsigma = self.absminsigma
1236
- fitfail = True
1237
- if fitfail:
1238
- if self.debug:
1239
- print("fit fail")
1240
- if self.zerooutbadfit:
1241
- maxval = np.float64(0.0)
1242
- maxlag = np.float64(0.0)
1243
- maxsigma = np.float64(0.0)
1244
- maskval = np.uint16(0)
1245
- # print(maxlag_init, maxlag, maxval_init, maxval, maxsigma_init, maxsigma, maskval, failreason, fitfail)
1246
- else:
1247
- maxval = np.float64(maxval_init)
1248
- maxlag = np.float64(np.fmod(maxlag_init, self.lagmod))
1249
- maxsigma = np.float64(maxsigma_init)
1250
- if failreason != self.FML_NOERROR:
1251
- maskval = np.uint16(0)
1252
-
1253
- if self.debug or self.displayplots:
1254
- print(
1255
- "init to final: maxval",
1256
- maxval_init,
1257
- maxval,
1258
- ", maxlag:",
1259
- maxlag_init,
1260
- maxlag,
1261
- ", width:",
1262
- maxsigma_init,
1263
- maxsigma,
1264
- )
1265
- if self.displayplots and (self.peakfittype != "None") and (maskval != 0.0):
1266
- fig = plt.figure()
1267
- ax = fig.add_subplot(111)
1268
- ax.set_title("Data and fit")
1269
- hiresx = np.arange(X[0], X[-1], (X[1] - X[0]) / 10.0)
1270
- plt.plot(
1271
- X,
1272
- data,
1273
- "ro",
1274
- hiresx,
1275
- tide_fit.gauss_eval(hiresx, np.array([maxval, maxlag, maxsigma])),
1276
- "b-",
1277
- )
1278
- plt.show()
1279
- return (
1280
- maxindex,
1281
- maxlag,
1282
- flipfac * maxval,
1283
- maxsigma,
1284
- maskval,
1285
- failreason,
1286
- peakstart,
1287
- peakend,
1288
- )
1289
-
1290
-
1291
- class FrequencyTracker:
1292
- freqs = None
1293
- times = None
1294
-
1295
- def __init__(self, lowerlim=0.1, upperlim=0.6, nperseg=32, Q=10.0, debug=False):
1296
- self.lowerlim = lowerlim
1297
- self.upperlim = upperlim
1298
- self.nperseg = nperseg
1299
- self.Q = Q
1300
- self.debug = debug
1301
- self.nfft = self.nperseg
1302
-
1303
- def track(self, x, fs):
1304
- self.freqs, self.times, thespectrogram = sp.signal.spectrogram(
1305
- np.concatenate(
1306
- [np.zeros(int(self.nperseg // 2)), x, np.zeros(int(self.nperseg // 2))],
1307
- axis=0,
1308
- ),
1309
- fs=fs,
1310
- detrend="constant",
1311
- scaling="spectrum",
1312
- nfft=None,
1313
- window=np.hamming(self.nfft),
1314
- noverlap=(self.nperseg - 1),
1315
- )
1316
- lowerliminpts = tide_util.valtoindex(self.freqs, self.lowerlim)
1317
- upperliminpts = tide_util.valtoindex(self.freqs, self.upperlim)
1318
-
1319
- if self.debug:
1320
- print(self.times.shape, self.freqs.shape, thespectrogram.shape)
1321
- print(self.times)
1322
-
1323
- # intitialize the peak fitter
1324
- thefitter = SimilarityFunctionFitter(
1325
- corrtimeaxis=self.freqs,
1326
- lagmin=self.lowerlim,
1327
- lagmax=self.upperlim,
1328
- absmaxsigma=10.0,
1329
- absminsigma=0.1,
1330
- debug=self.debug,
1331
- peakfittype="fastquad",
1332
- zerooutbadfit=False,
1333
- useguess=False,
1334
- )
1335
-
1336
- peakfreqs = np.zeros((thespectrogram.shape[1] - 1), dtype=float)
1337
- for i in range(0, thespectrogram.shape[1] - 1):
1338
- (
1339
- maxindex,
1340
- peakfreqs[i],
1341
- maxval,
1342
- maxsigma,
1343
- maskval,
1344
- failreason,
1345
- peakstart,
1346
- peakend,
1347
- ) = thefitter.fit(thespectrogram[:, i])
1348
- if not (lowerliminpts <= maxindex <= upperliminpts):
1349
- peakfreqs[i] = -1.0
1350
-
1351
- return self.times[:-1], peakfreqs
1352
-
1353
- def clean(self, x, fs, times, peakfreqs, numharmonics=2):
1354
- nyquistfreq = 0.5 * fs
1355
- y = x * 0.0
1356
- halfwidth = int(self.nperseg // 2)
1357
- padx = np.concatenate([np.zeros(halfwidth), x, np.zeros(halfwidth)], axis=0)
1358
- pady = np.concatenate([np.zeros(halfwidth), y, np.zeros(halfwidth)], axis=0)
1359
- padweight = padx * 0.0
1360
- if self.debug:
1361
- print(fs, len(times), len(peakfreqs))
1362
- for i in range(0, len(times)):
1363
- centerindex = int(times[i] * fs)
1364
- xstart = centerindex - halfwidth
1365
- xend = centerindex + halfwidth
1366
- if peakfreqs[i] > 0.0:
1367
- filtsignal = padx[xstart:xend]
1368
- numharmonics = np.min([numharmonics, int((nyquistfreq // peakfreqs[i]) - 1)])
1369
- if self.debug:
1370
- print("numharmonics:", numharmonics, nyquistfreq // peakfreqs[i])
1371
- for j in range(numharmonics + 1):
1372
- workingfreq = (j + 1) * peakfreqs[i]
1373
- if self.debug:
1374
- print("workingfreq:", workingfreq)
1375
- ws = [workingfreq * 0.95, workingfreq * 1.05]
1376
- wp = [workingfreq * 0.9, workingfreq * 1.1]
1377
- gpass = 1.0
1378
- gstop = 40.0
1379
- b, a = sp.signal.iirdesign(wp, ws, gpass, gstop, ftype="cheby2", fs=fs)
1380
- if self.debug:
1381
- print(
1382
- i,
1383
- j,
1384
- times[i],
1385
- centerindex,
1386
- halfwidth,
1387
- xstart,
1388
- xend,
1389
- xend - xstart,
1390
- wp,
1391
- ws,
1392
- len(a),
1393
- len(b),
1394
- )
1395
- filtsignal = sp.signal.filtfilt(b, a, sp.signal.filtfilt(b, a, filtsignal))
1396
- pady[xstart:xend] += filtsignal
1397
- else:
1398
- pady[xstart:xend] += padx[xstart:xend]
1399
- padweight[xstart:xend] += 1.0
1400
- return (pady / padweight)[halfwidth:-halfwidth]