rapidtide 3.0.11__py3-none-any.whl → 3.1.1__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 (144) hide show
  1. rapidtide/Colortables.py +492 -27
  2. rapidtide/OrthoImageItem.py +1049 -46
  3. rapidtide/RapidtideDataset.py +1533 -86
  4. rapidtide/_version.py +3 -3
  5. rapidtide/calccoherence.py +196 -29
  6. rapidtide/calcnullsimfunc.py +188 -40
  7. rapidtide/calcsimfunc.py +242 -42
  8. rapidtide/correlate.py +1203 -383
  9. rapidtide/data/examples/src/testLD +56 -0
  10. rapidtide/data/examples/src/testalign +1 -1
  11. rapidtide/data/examples/src/testdelayvar +0 -1
  12. rapidtide/data/examples/src/testfmri +53 -3
  13. rapidtide/data/examples/src/testglmfilt +5 -5
  14. rapidtide/data/examples/src/testhappy +29 -7
  15. rapidtide/data/examples/src/testppgproc +17 -0
  16. rapidtide/data/examples/src/testrolloff +11 -0
  17. rapidtide/data/models/model_cnn_pytorch/best_model.pth +0 -0
  18. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  19. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  20. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  21. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  22. rapidtide/decorators.py +91 -0
  23. rapidtide/dlfilter.py +2226 -110
  24. rapidtide/dlfiltertorch.py +4842 -0
  25. rapidtide/externaltools.py +327 -12
  26. rapidtide/fMRIData_class.py +79 -40
  27. rapidtide/filter.py +1899 -810
  28. rapidtide/fit.py +2011 -581
  29. rapidtide/genericmultiproc.py +93 -18
  30. rapidtide/happy_supportfuncs.py +2047 -172
  31. rapidtide/helper_classes.py +584 -43
  32. rapidtide/io.py +2370 -372
  33. rapidtide/linfitfiltpass.py +346 -99
  34. rapidtide/makelaggedtcs.py +210 -24
  35. rapidtide/maskutil.py +448 -62
  36. rapidtide/miscmath.py +827 -121
  37. rapidtide/multiproc.py +210 -22
  38. rapidtide/patchmatch.py +242 -42
  39. rapidtide/peakeval.py +31 -31
  40. rapidtide/ppgproc.py +2203 -0
  41. rapidtide/qualitycheck.py +352 -39
  42. rapidtide/refinedelay.py +431 -57
  43. rapidtide/refineregressor.py +494 -189
  44. rapidtide/resample.py +671 -185
  45. rapidtide/scripts/applyppgproc.py +28 -0
  46. rapidtide/scripts/showxcorr_legacy.py +7 -7
  47. rapidtide/scripts/stupidramtricks.py +15 -17
  48. rapidtide/simFuncClasses.py +1052 -77
  49. rapidtide/simfuncfit.py +269 -69
  50. rapidtide/stats.py +540 -238
  51. rapidtide/tests/happycomp +9 -0
  52. rapidtide/tests/test_cleanregressor.py +1 -2
  53. rapidtide/tests/test_dlfiltertorch.py +627 -0
  54. rapidtide/tests/test_findmaxlag.py +24 -8
  55. rapidtide/tests/test_fullrunhappy_v1.py +0 -2
  56. rapidtide/tests/test_fullrunhappy_v2.py +0 -2
  57. rapidtide/tests/test_fullrunhappy_v3.py +11 -4
  58. rapidtide/tests/test_fullrunhappy_v4.py +10 -2
  59. rapidtide/tests/test_fullrunrapidtide_v7.py +1 -1
  60. rapidtide/tests/test_getparsers.py +11 -3
  61. rapidtide/tests/test_refinedelay.py +0 -1
  62. rapidtide/tests/test_simroundtrip.py +16 -8
  63. rapidtide/tests/test_stcorrelate.py +3 -1
  64. rapidtide/tests/utils.py +9 -8
  65. rapidtide/tidepoolTemplate.py +142 -38
  66. rapidtide/tidepoolTemplate_alt.py +165 -44
  67. rapidtide/tidepoolTemplate_big.py +189 -52
  68. rapidtide/util.py +1217 -118
  69. rapidtide/voxelData.py +684 -37
  70. rapidtide/wiener.py +136 -23
  71. rapidtide/wiener2.py +113 -7
  72. rapidtide/workflows/adjustoffset.py +105 -3
  73. rapidtide/workflows/aligntcs.py +85 -2
  74. rapidtide/workflows/applydlfilter.py +87 -10
  75. rapidtide/workflows/applyppgproc.py +540 -0
  76. rapidtide/workflows/atlasaverage.py +210 -47
  77. rapidtide/workflows/atlastool.py +100 -3
  78. rapidtide/workflows/calcSimFuncMap.py +288 -69
  79. rapidtide/workflows/calctexticc.py +201 -9
  80. rapidtide/workflows/ccorrica.py +101 -6
  81. rapidtide/workflows/cleanregressor.py +165 -31
  82. rapidtide/workflows/delayvar.py +171 -23
  83. rapidtide/workflows/diffrois.py +81 -3
  84. rapidtide/workflows/endtidalproc.py +144 -4
  85. rapidtide/workflows/fdica.py +195 -15
  86. rapidtide/workflows/filtnifti.py +70 -3
  87. rapidtide/workflows/filttc.py +74 -3
  88. rapidtide/workflows/fitSimFuncMap.py +202 -51
  89. rapidtide/workflows/fixtr.py +73 -3
  90. rapidtide/workflows/gmscalc.py +113 -3
  91. rapidtide/workflows/happy.py +801 -199
  92. rapidtide/workflows/happy2std.py +144 -12
  93. rapidtide/workflows/happy_parser.py +163 -23
  94. rapidtide/workflows/histnifti.py +118 -2
  95. rapidtide/workflows/histtc.py +84 -3
  96. rapidtide/workflows/linfitfilt.py +117 -4
  97. rapidtide/workflows/localflow.py +328 -28
  98. rapidtide/workflows/mergequality.py +79 -3
  99. rapidtide/workflows/niftidecomp.py +322 -18
  100. rapidtide/workflows/niftistats.py +174 -4
  101. rapidtide/workflows/pairproc.py +98 -4
  102. rapidtide/workflows/pairwisemergenifti.py +85 -2
  103. rapidtide/workflows/parser_funcs.py +1421 -40
  104. rapidtide/workflows/physiofreq.py +137 -11
  105. rapidtide/workflows/pixelcomp.py +207 -5
  106. rapidtide/workflows/plethquality.py +103 -21
  107. rapidtide/workflows/polyfitim.py +151 -11
  108. rapidtide/workflows/proj2flow.py +75 -2
  109. rapidtide/workflows/rankimage.py +111 -4
  110. rapidtide/workflows/rapidtide.py +368 -76
  111. rapidtide/workflows/rapidtide2std.py +98 -2
  112. rapidtide/workflows/rapidtide_parser.py +109 -9
  113. rapidtide/workflows/refineDelayMap.py +144 -33
  114. rapidtide/workflows/refineRegressor.py +675 -96
  115. rapidtide/workflows/regressfrommaps.py +161 -37
  116. rapidtide/workflows/resamplenifti.py +85 -3
  117. rapidtide/workflows/resampletc.py +91 -3
  118. rapidtide/workflows/retrolagtcs.py +99 -9
  119. rapidtide/workflows/retroregress.py +176 -26
  120. rapidtide/workflows/roisummarize.py +174 -5
  121. rapidtide/workflows/runqualitycheck.py +71 -3
  122. rapidtide/workflows/showarbcorr.py +149 -6
  123. rapidtide/workflows/showhist.py +86 -2
  124. rapidtide/workflows/showstxcorr.py +160 -3
  125. rapidtide/workflows/showtc.py +159 -3
  126. rapidtide/workflows/showxcorrx.py +190 -10
  127. rapidtide/workflows/showxy.py +185 -15
  128. rapidtide/workflows/simdata.py +264 -38
  129. rapidtide/workflows/spatialfit.py +77 -2
  130. rapidtide/workflows/spatialmi.py +250 -27
  131. rapidtide/workflows/spectrogram.py +305 -32
  132. rapidtide/workflows/synthASL.py +154 -3
  133. rapidtide/workflows/tcfrom2col.py +76 -2
  134. rapidtide/workflows/tcfrom3col.py +74 -2
  135. rapidtide/workflows/tidepool.py +2971 -130
  136. rapidtide/workflows/utils.py +19 -14
  137. rapidtide/workflows/utils_doc.py +293 -0
  138. rapidtide/workflows/variabilityizer.py +116 -3
  139. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/METADATA +10 -8
  140. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/RECORD +144 -128
  141. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/entry_points.txt +1 -0
  142. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/WHEEL +0 -0
  143. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/licenses/LICENSE +0 -0
  144. {rapidtide-3.0.11.dist-info → rapidtide-3.1.1.dist-info}/top_level.txt +0 -0
rapidtide/filter.py CHANGED
@@ -23,9 +23,13 @@ package.
23
23
 
24
24
  import sys
25
25
  import warnings
26
+ from typing import Optional, Tuple, Union
26
27
 
27
28
  import matplotlib.pyplot as plt
28
29
  import numpy as np
30
+ from numpy.typing import NDArray
31
+
32
+ from rapidtide.decorators import conditionaljit, conditionaljit2
29
33
 
30
34
  with warnings.catch_warnings():
31
35
  warnings.simplefilter("ignore")
@@ -43,70 +47,79 @@ if pyfftwpresent:
43
47
  fftpack = pyfftw.interfaces.scipy_fftpack
44
48
  pyfftw.interfaces.cache.enable()
45
49
 
46
- # ----------------------------------------- Conditional imports ---------------------------------------
47
- try:
48
- from numba import jit
49
- except ImportError:
50
- donotusenumba = True
51
- else:
52
- donotusenumba = False
53
-
54
-
55
- # ----------------------------------------- Conditional jit handling ----------------------------------
56
- def conditionaljit():
57
- def resdec(f):
58
- global donotusenumba
59
- if donotusenumba:
60
- return f
61
- return jit(f, nopython=True)
62
-
63
- return resdec
64
-
65
-
66
- def disablenumba():
67
- global donotusenumba
68
- donotusenumba = True
69
-
70
-
71
50
  # --------------------------- Filtering functions -------------------------------------------------
72
51
  # NB: No automatic padding for precalculated filters
73
52
 
74
53
 
75
54
  @conditionaljit()
76
- def padvec(inputdata, padlen=20, avlen=20, padtype="reflect", debug=False):
77
- r"""Returns a padded copy of the input data; padlen points of
55
+ def padvec(
56
+ inputdata: NDArray,
57
+ padlen: int = 20,
58
+ avlen: int = 20,
59
+ padtype: str = "reflect",
60
+ debug: bool = False,
61
+ ) -> NDArray:
62
+ """
63
+ Returns a padded copy of the input data; padlen points of
78
64
  filled data are prepended and appended to the input data to reduce
79
65
  end effects when the data is then filtered. Filling can be "zero", "reflect", "cyclic", "constant",
80
66
  or "constant+".
81
67
 
82
68
  Parameters
83
69
  ----------
84
- inputdata : 1D array
70
+ inputdata : NDArray
85
71
  An array of any numerical type.
86
- :param inputdata:
87
-
88
72
  padlen : int, optional
89
- The number of points to add to each end. Default is 20.
90
- :param padlen:
91
-
73
+ The number of points to add to each end. Default is 20.
92
74
  avlen : int, optional
93
- The number of points to average when doing "constant+" padding. Default is 20.
94
-
75
+ The number of points to average when doing "constant+" padding. Default is 20.
95
76
  padtype : str, optional
96
- Method for padding data on the ends of the vector. Options are "reflect", "zero", "cyclic",
97
- "constant", or "constant+". Default is "reflect".
98
- :param padtype:
77
+ Method for padding data on the ends of the vector. Options are "reflect", "zero", "cyclic",
78
+ "constant", or "constant+". Default is "reflect".
79
+ debug : bool, optional
80
+ If True, print debug information. Default is False.
99
81
 
100
82
  Returns
101
83
  -------
102
- paddeddata : 1D array
103
- The input data, with padlen reflected points added to each end
104
-
84
+ NDArray
85
+ The input data, with `padlen` reflected points added to each end.
86
+
87
+ Notes
88
+ -----
89
+ This function is useful for reducing edge effects when filtering data. The padding methods are as follows:
90
+ - "reflect": pads by reflecting the input array around its edges.
91
+ - "zero": pads with zeros.
92
+ - "cyclic": pads by cycling the input array.
93
+ - "constant": pads with the first/last value of the input array.
94
+ - "constant+": pads with the mean of the first/last `avlen` points of the input array.
95
+
96
+ Examples
97
+ --------
98
+ >>> import numpy as np
99
+ >>> data = np.array([1, 2, 3, 4, 5])
100
+ >>> padded = padvec(data, padlen=2, padtype="reflect")
101
+ >>> print(padded)
102
+ [3 2 1 2 3 4 5 5 4]
103
+
104
+ >>> padded = padvec(data, padlen=2, padtype="zero")
105
+ >>> print(padded)
106
+ [0 0 1 2 3 4 5 0 0]
105
107
  """
106
108
  if debug:
107
- print("padvec: padlen=", padlen, ", avlen=", avlen, ", padtype=", padtype, "len(inputdata)=", len(inputdata))
109
+ print(
110
+ "padvec: padlen=",
111
+ padlen,
112
+ ", avlen=",
113
+ avlen,
114
+ ", padtype=",
115
+ padtype,
116
+ "len(inputdata)=",
117
+ len(inputdata),
118
+ )
108
119
  if padlen > len(inputdata):
109
- raise RuntimeError(f"ERROR: padlen ({padlen}) is greater than input data length ({len(inputdata)})")
120
+ raise RuntimeError(
121
+ f"ERROR: padlen ({padlen}) is greater than input data length ({len(inputdata)})"
122
+ )
110
123
  if avlen > padlen:
111
124
  avlen = padlen
112
125
 
@@ -145,31 +158,48 @@ def padvec(inputdata, padlen=20, avlen=20, padtype="reflect", debug=False):
145
158
  )
146
159
  )
147
160
  else:
148
- raise ValueError("Padtype must be one of 'reflect', 'zero', 'cyclic', 'constant', or 'constant+'.")
161
+ raise ValueError(
162
+ "Padtype must be one of 'reflect', 'zero', 'cyclic', 'constant', or 'constant+'."
163
+ )
149
164
  else:
150
165
  return inputdata
151
166
 
152
167
 
153
168
  @conditionaljit()
154
- def unpadvec(inputdata, padlen=20):
155
- r"""Returns a input data with the end pads removed (see padvec);
156
- padlen points of reflected data are removed from each end of the array.
169
+ def unpadvec(inputdata: NDArray, padlen: int = 20) -> NDArray:
170
+ """
171
+ Returns input data with the end pads removed.
172
+
173
+ This function removes padding from both ends of an array. It is the inverse
174
+ operation of the `padvec` function, which adds padding to the array.
157
175
 
158
176
  Parameters
159
177
  ----------
160
- inputdata : 1D array
178
+ inputdata : NDArray
161
179
  An array of any numerical type.
162
- :param inputdata:
163
180
  padlen : int, optional
164
- The number of points to remove from each end. Default is 20.
165
- :param padlen:
181
+ The number of points to remove from each end. Default is 20.
166
182
 
167
183
  Returns
168
184
  -------
169
- unpaddeddata : 1D array
170
- The input data, with the padding data removed
171
-
172
-
185
+ NDArray
186
+ The input data with padding removed from both ends. If padlen is 0 or
187
+ negative, the original array is returned unchanged.
188
+
189
+ Notes
190
+ -----
191
+ When padlen is greater than 0, the function returns ``inputdata[padlen:-padlen]``.
192
+ If padlen is greater than or equal to the array length, an empty array will be returned.
193
+
194
+ Examples
195
+ --------
196
+ >>> import numpy as np
197
+ >>> data = np.array([1, 2, 3, 4, 5, 6, 7, 8])
198
+ >>> unpadvec(data, padlen=2)
199
+ array([3, 4, 5, 6])
200
+
201
+ >>> unpadvec(data, padlen=0)
202
+ array([1, 2, 3, 4, 5, 6, 7, 8])
173
203
  """
174
204
  if padlen > 0:
175
205
  return inputdata[padlen:-padlen]
@@ -177,36 +207,47 @@ def unpadvec(inputdata, padlen=20):
177
207
  return inputdata
178
208
 
179
209
 
180
- def ssmooth(xsize, ysize, zsize, sigma, inputdata):
181
- r"""Applies an isotropic gaussian spatial filter to a 3D array
210
+ def ssmooth(xsize: float, ysize: float, zsize: float, sigma: float, inputdata: NDArray) -> NDArray:
211
+ """
212
+ Applies an isotropic gaussian spatial filter to a 3D array.
213
+
214
+ This function applies a Gaussian filter to 3D spatial data with isotropic
215
+ filtering parameters. The filter kernel width is specified in spatial units
216
+ and is converted to pixel units based on the array spacing parameters.
182
217
 
183
218
  Parameters
184
219
  ----------
185
220
  xsize : float
186
221
  The array x step size in spatial units
187
- :param xsize:
188
-
189
222
  ysize : float
190
223
  The array y step size in spatial units
191
- :param ysize:
192
-
193
224
  zsize : float
194
225
  The array z step size in spatial units
195
- :param zsize:
196
-
197
226
  sigma : float
198
227
  The width of the gaussian filter kernel in spatial units
199
- :param sigma:
200
-
201
- inputdata : 3D numeric array
228
+ inputdata : NDArray
202
229
  The spatial data to filter
203
- :param inputdata:
204
230
 
205
231
  Returns
206
232
  -------
207
- filtereddata : 3D float array
208
- The filtered spatial data
209
-
233
+ NDArray
234
+ The filtered spatial data as a 3D float array
235
+
236
+ Notes
237
+ -----
238
+ The function uses `scipy.ndimage.gaussian_filter` internally, where the
239
+ sigma parameters are calculated as `sigma / step_size` for each dimension.
240
+ This ensures isotropic filtering when the same sigma value is used across
241
+ all spatial dimensions.
242
+
243
+ Examples
244
+ --------
245
+ >>> import numpy as np
246
+ >>> from scipy import ndimage
247
+ >>> data = np.random.rand(10, 10, 10)
248
+ >>> filtered = ssmooth(0.1, 0.1, 0.1, 0.2, data)
249
+ >>> print(filtered.shape)
250
+ (10, 10, 10)
210
251
  """
211
252
  return ndimage.gaussian_filter(inputdata, [sigma / xsize, sigma / ysize, sigma / zsize])
212
253
 
@@ -214,53 +255,59 @@ def ssmooth(xsize, ysize, zsize, sigma, inputdata):
214
255
  # - butterworth filters
215
256
  # @conditionaljit()
216
257
  def dolpfiltfilt(
217
- Fs,
218
- upperpass,
219
- inputdata,
220
- order,
221
- padlen=20,
222
- avlen=20,
223
- padtype="reflect",
224
- debug=False,
225
- ):
226
- r"""Performs a bidirectional (zero phase) Butterworth lowpass filter on an input vector
227
- and returns the result. Ends are padded to reduce transients.
258
+ Fs: float,
259
+ upperpass: float,
260
+ inputdata: NDArray,
261
+ order: int,
262
+ padlen: int = 20,
263
+ avlen: int = 20,
264
+ padtype: str = "reflect",
265
+ debug: bool = False,
266
+ ) -> NDArray:
267
+ """
268
+ Performs a bidirectional (zero phase) Butterworth lowpass filter on an input vector
269
+ and returns the result. Ends are padded to reduce transients.
228
270
 
229
271
  Parameters
230
272
  ----------
231
273
  Fs : float
232
- Sample rate in Hz
233
- :param Fs:
234
-
274
+ Sample rate in Hz.
235
275
  upperpass : float
236
- Upper end of passband in Hz
237
- :param upperpass:
238
-
239
- inputdata : 1D numpy array
240
- Input data to be filtered
241
- :param inputdata:
242
-
276
+ Upper end of passband in Hz.
277
+ inputdata : NDArray
278
+ Input data to be filtered.
243
279
  order : int
244
- Order of Butterworth filter.
245
- :param order:
246
-
280
+ Order of the Butterworth filter.
247
281
  padlen : int, optional
248
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
249
- :param padlen:
250
-
251
- cyclic : bool, optional
252
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
253
- :param cyclic:
254
-
255
- debug : boolean, optional
282
+ Amount of points to reflect around each end of the input vector prior to filtering.
283
+ Default is 20.
284
+ avlen : int, optional
285
+ Length of the averaging window used in padding. Default is 20.
286
+ padtype : str, optional
287
+ Type of padding to use. Options are 'reflect' or 'cyclic'. Default is 'reflect'.
288
+ debug : bool, optional
256
289
  When True, internal states of the function will be printed to help debugging.
257
- :param debug:
290
+ Default is False.
258
291
 
259
292
  Returns
260
293
  -------
261
- filtereddata : 1D float array
262
- The filtered data
263
-
294
+ filtereddata : NDArray
295
+ The filtered data as a 1D float array.
296
+
297
+ Notes
298
+ -----
299
+ This function applies a zero-phase Butterworth filter using `scipy.signal.filtfilt`,
300
+ which eliminates phase distortion. Padding is applied before filtering to reduce
301
+ edge effects.
302
+
303
+ Examples
304
+ --------
305
+ >>> import numpy as np
306
+ >>> from scipy import signal
307
+ >>> Fs = 100.0
308
+ >>> upperpass = 20.0
309
+ >>> data = np.random.randn(1000)
310
+ >>> filtered = dolpfiltfilt(Fs, upperpass, data, order=4)
264
311
  """
265
312
  if upperpass > Fs / 2.0:
266
313
  upperpass = Fs / 2.0
@@ -277,9 +324,7 @@ def dolpfiltfilt(
277
324
  signal.filtfilt(
278
325
  b,
279
326
  a,
280
- padvec(
281
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
282
- ),
327
+ padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug),
283
328
  ).real,
284
329
  padlen=padlen,
285
330
  ).astype(np.float64)
@@ -287,52 +332,58 @@ def dolpfiltfilt(
287
332
 
288
333
  # @conditionaljit()
289
334
  def dohpfiltfilt(
290
- Fs,
291
- lowerpass,
292
- inputdata,
293
- order,
294
- padlen=20,
295
- avlen=20,
296
- padtype="reflect",
297
- debug=False,
298
- ):
299
- r"""Performs a bidirectional (zero phase) Butterworth highpass filter on an input vector
300
- and returns the result. Ends are padded to reduce transients.
335
+ Fs: float,
336
+ lowerpass: float,
337
+ inputdata: NDArray,
338
+ order: int,
339
+ padlen: int = 20,
340
+ avlen: int = 20,
341
+ padtype: str = "reflect",
342
+ debug: bool = False,
343
+ ) -> NDArray:
344
+ """
345
+ Performs a bidirectional (zero phase) Butterworth highpass filter on an input vector
346
+ and returns the result. Ends are padded to reduce transients.
301
347
 
302
348
  Parameters
303
349
  ----------
304
350
  Fs : float
305
- Sample rate in Hz
306
- :param Fs:
307
-
351
+ Sample rate in Hz.
308
352
  lowerpass : float
309
- Lower end of passband in Hz
310
- :param lowerpass:
311
-
312
- inputdata : 1D numpy array
313
- Input data to be filtered
314
- :param inputdata:
315
-
353
+ Lower end of passband in Hz.
354
+ inputdata : NDArray
355
+ Input signal to be filtered.
316
356
  order : int
317
- Order of Butterworth filter.
318
- :param order:
319
-
357
+ Order of the Butterworth filter.
320
358
  padlen : int, optional
321
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
322
- :param padlen:
323
-
324
- cyclic : bool, optional
325
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
326
- :param cyclic:
327
-
328
- debug : boolean, optional
329
- When True, internal states of the function will be printed to help debugging.
330
- :param debug:
359
+ Amount of points to reflect around each end of the input vector prior to filtering.
360
+ Default is 20.
361
+ avlen : int, optional
362
+ Length of the averaging window used in padding. Default is 20.
363
+ padtype : str, optional
364
+ Type of padding to use. Options are 'reflect' or 'wrap'. Default is 'reflect'.
365
+ debug : bool, optional
366
+ If True, internal states of the function will be printed to help debugging.
367
+ Default is False.
331
368
 
332
369
  Returns
333
370
  -------
334
- filtereddata : 1D float array
335
- The filtered data
371
+ filtereddata : NDArray
372
+ The filtered data with the same shape as inputdata.
373
+
374
+ Notes
375
+ -----
376
+ This function applies a zero-phase Butterworth highpass filter using `scipy.signal.filtfilt`,
377
+ which ensures no phase distortion in the filtered signal. Padding is applied before filtering
378
+ to minimize edge effects.
379
+
380
+ Examples
381
+ --------
382
+ >>> import numpy as np
383
+ >>> from scipy import signal
384
+ >>> Fs = 100.0
385
+ >>> data = np.random.randn(1000)
386
+ >>> filtered = dohpfiltfilt(Fs, 10.0, data, order=4, padlen=30)
336
387
  """
337
388
  if lowerpass < 0.0:
338
389
  lowerpass = 0.0
@@ -349,9 +400,7 @@ def dohpfiltfilt(
349
400
  signal.filtfilt(
350
401
  b,
351
402
  a,
352
- padvec(
353
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
354
- ),
403
+ padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug),
355
404
  ).real,
356
405
  padlen=padlen,
357
406
  )
@@ -359,57 +408,60 @@ def dohpfiltfilt(
359
408
 
360
409
  # @conditionaljit()
361
410
  def dobpfiltfilt(
362
- Fs,
363
- lowerpass,
364
- upperpass,
365
- inputdata,
366
- order,
367
- padlen=20,
368
- avlen=20,
369
- padtype="reflect",
370
- debug=False,
371
- ):
372
- r"""Performs a bidirectional (zero phase) Butterworth bandpass filter on an input vector
373
- and returns the result. Ends are padded to reduce transients.
411
+ Fs: float,
412
+ lowerpass: float,
413
+ upperpass: float,
414
+ inputdata: NDArray,
415
+ order: int,
416
+ padlen: int = 20,
417
+ avlen: int = 20,
418
+ padtype: str = "reflect",
419
+ debug: bool = False,
420
+ ) -> NDArray:
421
+ """
422
+ Performs a bidirectional (zero phase) Butterworth bandpass filter on an input vector
423
+ and returns the result. Ends are padded to reduce transients.
374
424
 
375
425
  Parameters
376
426
  ----------
377
427
  Fs : float
378
- Sample rate in Hz
379
- :param Fs:
380
-
428
+ Sample rate in Hz.
381
429
  lowerpass : float
382
- Lower end of passband in Hz
383
- :param lowerpass:
384
-
430
+ Lower end of passband in Hz.
385
431
  upperpass : float
386
- Upper end of passband in Hz
387
- :param upperpass:
388
-
389
- inputdata : 1D numpy array
390
- Input data to be filtered
391
- :param inputdata:
392
-
432
+ Upper end of passband in Hz.
433
+ inputdata : NDArray
434
+ Input data to be filtered.
393
435
  order : int
394
- Order of Butterworth filter.
395
- :param order:
396
-
436
+ Order of the Butterworth filter.
397
437
  padlen : int, optional
398
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
399
- :param padlen:
400
-
401
- cyclic : bool, optional
402
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
403
- :param cyclic:
404
-
405
- debug : boolean, optional
438
+ Amount of points to reflect around each end of the input vector prior to filtering.
439
+ Default is 20.
440
+ avlen : int, optional
441
+ Length of the averaging window used in padding. Default is 20.
442
+ padtype : str, optional
443
+ Type of padding to use. Options are 'reflect' or 'cyclic'. Default is 'reflect'.
444
+ debug : bool, optional
406
445
  When True, internal states of the function will be printed to help debugging.
407
- :param debug:
446
+ Default is False.
408
447
 
409
448
  Returns
410
449
  -------
411
- filtereddata : 1D float array
412
- The filtered data
450
+ filtereddata : NDArray
451
+ The filtered data as a 1D float array.
452
+
453
+ Notes
454
+ -----
455
+ This function applies a zero-phase Butterworth bandpass filter using `scipy.signal.filtfilt`,
456
+ which eliminates phase distortion. Padding is applied before filtering to reduce edge effects.
457
+
458
+ Examples
459
+ --------
460
+ >>> import numpy as np
461
+ >>> from scipy import signal
462
+ >>> Fs = 100.0
463
+ >>> data = np.random.randn(1000)
464
+ >>> filtered = dobpfiltfilt(Fs, 10.0, 30.0, data, order=4, padlen=30)
413
465
  """
414
466
  if upperpass > Fs / 2.0:
415
467
  upperpass = Fs / 2.0
@@ -429,66 +481,98 @@ def dobpfiltfilt(
429
481
  signal.filtfilt(
430
482
  b,
431
483
  a,
432
- padvec(
433
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
434
- ),
484
+ padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug),
435
485
  ).real,
436
486
  padlen=padlen,
437
487
  )
438
488
 
439
489
 
440
490
  # - direct filter with specified transfer function
441
- def transferfuncfilt(inputdata, transferfunc):
442
- r"""Filters input data using a previously calculated transfer function.
491
+ def transferfuncfilt(inputdata: NDArray, transferfunc: NDArray) -> NDArray:
492
+ """
493
+ Filters input data using a previously calculated transfer function.
494
+
495
+ This function applies frequency domain filtering by multiplying the input data's
496
+ Fourier transform with the transfer function, then transforms back to the time domain.
443
497
 
444
498
  Parameters
445
499
  ----------
446
- inputdata : 1D float array
447
- Input data to be filtered
448
- :param inputdata:
449
-
450
- transferfunc : 1D float array
451
- The transfer function
452
- :param transferfunc:
500
+ inputdata : NDArray
501
+ Input data to be filtered, array of real or complex values
502
+ transferfunc : NDArray
503
+ The transfer function, array of real or complex values with same length as inputdata
453
504
 
454
505
  Returns
455
506
  -------
456
- filtereddata : 1D float array
457
- Filtered input data
507
+ NDArray
508
+ Filtered input data as a 1D float array
509
+
510
+ Notes
511
+ -----
512
+ The filtering is performed in the frequency domain using the convolution theorem.
513
+ The transfer function should be designed to match the frequency response of the desired filter.
514
+
515
+ Examples
516
+ --------
517
+ >>> import numpy as np
518
+ >>> from scipy import fftpack
519
+ >>> # Create sample data
520
+ >>> data = np.random.randn(1024)
521
+ >>> # Create a simple low-pass filter transfer function
522
+ >>> freq = np.fft.fftfreq(len(data), 1.0)
523
+ >>> tf = np.abs(freq) < 0.1
524
+ >>> # Apply filter
525
+ >>> filtered_data = transferfuncfilt(data, tf)
458
526
  """
459
527
  inputdata_trans = transferfunc * fftpack.fft(inputdata)
460
528
  return fftpack.ifft(inputdata_trans).real
461
529
 
462
530
 
463
531
  # - fft brickwall filters
464
- def getlpfftfunc(Fs, upperpass, inputdata, debug=False):
465
- r"""Generates a brickwall lowpass transfer function.
532
+ def getlpfftfunc(Fs: float, upperpass: float, inputdata: NDArray, debug: bool = False) -> NDArray:
533
+ """
534
+ Generates a brickwall lowpass transfer function.
535
+
536
+ This function creates a transfer function that acts as a brickwall lowpass filter
537
+ by setting frequencies above the cutoff to zero. The filter is designed in the
538
+ frequency domain using the FFT domain representation.
466
539
 
467
540
  Parameters
468
541
  ----------
469
542
  Fs : float
470
543
  Sample rate in Hz
471
- :param Fs:
472
-
473
544
  upperpass : float
474
545
  Upper end of passband in Hz
475
- :param upperpass:
476
-
477
546
  inputdata : 1D numpy array
478
547
  Input data to be filtered
479
- :param inputdata:
480
-
481
- debug : boolean, optional
548
+ debug : bool, optional
482
549
  When True, internal states of the function will be printed to help debugging.
483
- :param debug:
550
+ Default is False.
484
551
 
485
552
  Returns
486
553
  -------
487
554
  transferfunc : 1D float array
488
- The transfer function
555
+ The transfer function with the lowpass filter characteristics
556
+
557
+ Notes
558
+ -----
559
+ The function creates a transfer function where frequencies below the cutoff
560
+ are set to 1.0 and frequencies above the cutoff are set to 0.0. The cutoff
561
+ frequency is determined by the ratio of upperpass to Fs, converted to bin
562
+ indices in the FFT domain.
563
+
564
+ Examples
565
+ --------
566
+ >>> import numpy as np
567
+ >>> Fs = 100.0
568
+ >>> upperpass = 20.0
569
+ >>> inputdata = np.random.rand(100)
570
+ >>> transfer_func = getlpfftfunc(Fs, upperpass, inputdata)
571
+ >>> print(transfer_func.shape)
572
+ (100,)
489
573
  """
490
574
  transferfunc = np.ones(np.shape(inputdata), dtype=np.float64)
491
- cutoffbin = int((upperpass / Fs) * np.shape(transferfunc)[0])
575
+ cutoffbin = int((upperpass / Fs) * len(transferfunc))
492
576
  if debug:
493
577
  print(
494
578
  "getlpfftfunc - Fs, upperpass, len(inputdata):",
@@ -502,45 +586,59 @@ def getlpfftfunc(Fs, upperpass, inputdata, debug=False):
502
586
 
503
587
  # @conditionaljit()
504
588
  def dolpfftfilt(
505
- Fs, upperpass, inputdata, padlen=20, avlen=20, padtype="reflect", debug=False
506
- ):
507
- r"""Performs an FFT brickwall lowpass filter on an input vector
508
- and returns the result. Ends are padded to reduce transients.
589
+ Fs: float,
590
+ upperpass: float,
591
+ inputdata: NDArray,
592
+ padlen: int = 20,
593
+ avlen: int = 20,
594
+ padtype: str = "reflect",
595
+ debug: bool = False,
596
+ ) -> NDArray:
597
+ """
598
+ Performs an FFT brickwall lowpass filter on an input vector and returns the result.
599
+ Ends are padded to reduce transients.
509
600
 
510
601
  Parameters
511
602
  ----------
512
603
  Fs : float
513
- Sample rate in Hz
514
- :param Fs:
515
-
604
+ Sample rate in Hz.
516
605
  upperpass : float
517
- Upper end of passband in Hz
518
- :param upperpass:
519
-
520
- inputdata : 1D numpy array
521
- Input data to be filtered
522
- :param inputdata:
523
-
606
+ Upper end of passband in Hz.
607
+ inputdata : NDArray
608
+ Input data to be filtered, expected as a 1D numpy array.
524
609
  padlen : int, optional
525
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
526
- :param padlen:
527
-
528
- cyclic : bool, optional
529
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
530
- :param cyclic:
531
-
532
- debug : boolean, optional
610
+ Amount of points to reflect around each end of the input vector prior to filtering.
611
+ Default is 20.
612
+ avlen : int, optional
613
+ Length of the averaging window used in padding. Default is 20.
614
+ padtype : str, optional
615
+ Type of padding to use. Options are 'reflect' or 'wrap'. Default is 'reflect'.
616
+ debug : bool, optional
533
617
  When True, internal states of the function will be printed to help debugging.
534
- :param debug:
618
+ Default is False.
535
619
 
536
620
  Returns
537
621
  -------
538
- filtereddata : 1D float array
539
- The filtered data
622
+ NDArray
623
+ The filtered data as a 1D float array.
624
+
625
+ Notes
626
+ -----
627
+ This function applies a lowpass filter in the frequency domain using FFT. The input signal
628
+ is padded at both ends to minimize edge effects caused by the filtering process.
629
+ The padding is performed using the `padvec` function, and the inverse FFT is used to
630
+ transform the filtered signal back to the time domain.
631
+
632
+ Examples
633
+ --------
634
+ >>> import numpy as np
635
+ >>> from scipy import fftpack
636
+ >>> Fs = 100.0
637
+ >>> upperpass = 20.0
638
+ >>> data = np.random.randn(1000)
639
+ >>> filtered_data = dolpfftfilt(Fs, upperpass, data)
540
640
  """
541
- padinputdata = padvec(
542
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
543
- )
641
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
544
642
  inputdata_trans = fftpack.fft(padinputdata)
545
643
  transferfunc = getlpfftfunc(Fs, upperpass, padinputdata, debug=debug)
546
644
  inputdata_trans *= transferfunc
@@ -549,45 +647,57 @@ def dolpfftfilt(
549
647
 
550
648
  # @conditionaljit()
551
649
  def dohpfftfilt(
552
- Fs, lowerpass, inputdata, padlen=20, avlen=20, padtype="reflect", debug=False
553
- ):
554
- r"""Performs an FFT brickwall highpass filter on an input vector
555
- and returns the result. Ends are padded to reduce transients.
650
+ Fs: float,
651
+ lowerpass: float,
652
+ inputdata: NDArray,
653
+ padlen: int = 20,
654
+ avlen: int = 20,
655
+ padtype: str = "reflect",
656
+ debug: bool = False,
657
+ ) -> NDArray:
658
+ """
659
+ Performs an FFT brickwall highpass filter on an input vector and returns the result.
660
+ Ends are padded to reduce transients.
556
661
 
557
662
  Parameters
558
663
  ----------
559
664
  Fs : float
560
- Sample rate in Hz
561
- :param Fs:
562
-
665
+ Sample rate in Hz.
563
666
  lowerpass : float
564
- Lower end of passband in Hz
565
- :param lowerpass:
566
-
567
- inputdata : 1D numpy array
568
- Input data to be filtered
569
- :param inputdata:
570
-
667
+ Lower end of passband in Hz.
668
+ inputdata : NDArray
669
+ Input data to be filtered, expected as a 1D numpy array.
571
670
  padlen : int, optional
572
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
573
- :param padlen:
574
-
575
- cyclic : bool, optional
576
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
577
- :param cyclic:
578
-
579
- debug : boolean, optional
671
+ Amount of points to reflect around each end of the input vector prior to filtering.
672
+ Default is 20.
673
+ avlen : int, optional
674
+ Length of the averaging window used in padding. Default is 20.
675
+ padtype : str, optional
676
+ Type of padding to use. Options are 'reflect' or 'cyclic'. Default is 'reflect'.
677
+ debug : bool, optional
580
678
  When True, internal states of the function will be printed to help debugging.
581
- :param debug:
679
+ Default is False.
582
680
 
583
681
  Returns
584
682
  -------
585
- filtereddata : 1D float array
586
- The filtered data
683
+ NDArray
684
+ The filtered data as a 1D float array.
685
+
686
+ Notes
687
+ -----
688
+ This function applies a highpass filter in the frequency domain using FFT.
689
+ The input signal is first padded to minimize edge effects, then transformed
690
+ into the frequency domain, filtered, and inverse transformed back to the time domain.
691
+
692
+ Examples
693
+ --------
694
+ >>> import numpy as np
695
+ >>> Fs = 100.0
696
+ >>> lowerpass = 10.0
697
+ >>> data = np.random.randn(1000)
698
+ >>> filtered = dohpfftfilt(Fs, lowerpass, data)
587
699
  """
588
- padinputdata = padvec(
589
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
590
- )
700
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
591
701
  inputdata_trans = fftpack.fft(padinputdata)
592
702
  transferfunc = 1.0 - getlpfftfunc(Fs, lowerpass, padinputdata, debug=debug)
593
703
  inputdata_trans *= transferfunc
@@ -596,56 +706,64 @@ def dohpfftfilt(
596
706
 
597
707
  # @conditionaljit()
598
708
  def dobpfftfilt(
599
- Fs,
600
- lowerpass,
601
- upperpass,
602
- inputdata,
603
- padlen=20,
604
- avlen=20,
605
- padtype="reflect",
606
- debug=False,
607
- ):
608
- r"""Performs an FFT brickwall bandpass filter on an input vector
609
- and returns the result. Ends are padded to reduce transients.
709
+ Fs: float,
710
+ lowerpass: float,
711
+ upperpass: float,
712
+ inputdata: NDArray,
713
+ padlen: int = 20,
714
+ avlen: int = 20,
715
+ padtype: str = "reflect",
716
+ debug: bool = False,
717
+ ) -> NDArray:
718
+ """
719
+ Performs an FFT brickwall bandpass filter on an input vector and returns the result.
720
+ Ends are padded to reduce transients.
610
721
 
611
722
  Parameters
612
723
  ----------
613
724
  Fs : float
614
- Sample rate in Hz
615
- :param Fs:
616
-
725
+ Sample rate in Hz.
617
726
  lowerpass : float
618
- Lower end of passband in Hz
619
- :param lowerpass:
620
-
727
+ Lower end of passband in Hz.
621
728
  upperpass : float
622
- Upper end of passband in Hz
623
- :param upperpass:
624
-
625
- inputdata : 1D numpy array
626
- Input data to be filtered
627
- :param inputdata:
628
-
729
+ Upper end of passband in Hz.
730
+ inputdata : NDArray
731
+ Input data to be filtered, expected as a 1D numpy array.
629
732
  padlen : int, optional
630
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
631
- :param padlen:
632
-
633
- cyclic : bool, optional
634
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
635
- :param cyclic:
636
-
637
- debug : boolean, optional
733
+ Amount of points to reflect around each end of the input vector prior to filtering.
734
+ Default is 20.
735
+ avlen : int, optional
736
+ Length of averaging window for padding; used only if `padtype` is 'mean'.
737
+ Default is 20.
738
+ padtype : str, optional
739
+ Type of padding to use. Options are 'reflect', 'mean', or 'wrap'.
740
+ Default is 'reflect'.
741
+ debug : bool, optional
638
742
  When True, internal states of the function will be printed to help debugging.
639
- :param debug:
743
+ Default is False.
640
744
 
641
745
  Returns
642
746
  -------
643
- filtereddata : 1D float array
644
- The filtered data
747
+ NDArray
748
+ The filtered data as a 1D float array, with the same shape as inputdata.
749
+
750
+ Notes
751
+ -----
752
+ This function applies a brickwall bandpass filter in the frequency domain using FFT.
753
+ The input signal is first padded to minimize edge effects, then transformed into
754
+ the frequency domain, filtered, and transformed back. Padding is applied using
755
+ the specified `padtype` and `padlen`.
756
+
757
+ Examples
758
+ --------
759
+ >>> import numpy as np
760
+ >>> from scipy import signal
761
+ >>> fs = 100.0
762
+ >>> t = np.linspace(0, 1, int(fs), endpoint=False)
763
+ >>> x = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 25 * t)
764
+ >>> filtered = dobpfftfilt(fs, 5, 15, x, padlen=30)
645
765
  """
646
- padinputdata = padvec(
647
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
648
- )
766
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
649
767
  inputdata_trans = fftpack.fft(padinputdata)
650
768
  transferfunc = getlpfftfunc(Fs, upperpass, padinputdata, debug=debug) * (
651
769
  1.0 - getlpfftfunc(Fs, lowerpass, padinputdata, debug=debug)
@@ -656,35 +774,56 @@ def dobpfftfilt(
656
774
 
657
775
  # - fft trapezoidal filters
658
776
  # @conditionaljit()
659
- def getlptrapfftfunc(Fs, upperpass, upperstop, inputdata, debug=False):
660
- r"""Generates a trapezoidal lowpass transfer function.
777
+ def getlptrapfftfunc(
778
+ Fs: float, upperpass: float, upperstop: float, inputdata: NDArray, debug: bool = False
779
+ ) -> NDArray:
780
+ """
781
+ Generate a trapezoidal lowpass transfer function for filtering.
782
+
783
+ This function creates a transfer function with a trapezoidal transition band
784
+ between the passband and stopband, suitable for use in spectral filtering
785
+ operations. The resulting transfer function can be applied to frequency-domain
786
+ data to perform lowpass filtering.
661
787
 
662
788
  Parameters
663
789
  ----------
664
790
  Fs : float
665
- Sample rate in Hz
666
- :param Fs:
667
-
791
+ Sample rate in Hz.
668
792
  upperpass : float
669
- Upper end of passband in Hz
670
- :param upperpass:
671
-
793
+ Upper edge of the passband in Hz.
672
794
  upperstop : float
673
- Lower end of stopband in Hz
674
- :param upperstop:
675
-
676
- inputdata : 1D numpy array
677
- Input data to be filtered
678
- :param inputdata:
679
-
680
- debug : boolean, optional
681
- When True, internal states of the function will be printed to help debugging.
682
- :param debug:
795
+ Lower edge of the stopband in Hz.
796
+ inputdata : NDArray
797
+ Input data array (typically frequency domain data) to determine the
798
+ length and shape of the transfer function.
799
+ debug : bool, optional
800
+ If True, print internal state information for debugging purposes.
801
+ Default is False.
683
802
 
684
803
  Returns
685
804
  -------
686
- transferfunc : 1D float array
687
- The transfer function
805
+ NDArray
806
+ A 1D float array representing the transfer function. The array has the
807
+ same length as `inputdata` and contains values between 0 and 1,
808
+ indicating the attenuation at each frequency bin.
809
+
810
+ Notes
811
+ -----
812
+ The transition from passband to stopband is linear (trapezoidal), with
813
+ the transition region defined between `upperpass` and `upperstop`.
814
+ The function assumes that `upperpass < upperstop` and that both are
815
+ within the Nyquist frequency range (0 to Fs/2).
816
+
817
+ Examples
818
+ --------
819
+ >>> import numpy as np
820
+ >>> Fs = 100.0
821
+ >>> upperpass = 20.0
822
+ >>> upperstop = 30.0
823
+ >>> inputdata = np.zeros(100)
824
+ >>> tf = getlptrapfftfunc(Fs, upperpass, upperstop, inputdata)
825
+ >>> print(tf.shape)
826
+ (100,)
688
827
  """
689
828
  transferfunc = np.ones(np.shape(inputdata), dtype="float64")
690
829
  passbin = int((upperpass / Fs) * np.shape(transferfunc)[0])
@@ -709,7 +848,64 @@ def getlptrapfftfunc(Fs, upperpass, upperstop, inputdata, debug=False):
709
848
 
710
849
 
711
850
  # @conditionaljit()
712
- def getlptransfunc(Fs, inputdata, upperpass=None, upperstop=None, type="brickwall", debug=False):
851
+ def getlptransfunc(
852
+ Fs: float,
853
+ inputdata: NDArray,
854
+ upperpass: Optional[float] = None,
855
+ upperstop: Optional[float] = None,
856
+ type: str = "brickwall",
857
+ debug: bool = False,
858
+ ) -> NDArray:
859
+ """
860
+ Compute the low-pass transfer function for a given input signal.
861
+
862
+ This function generates a transfer function based on the specified type
863
+ (brickwall, Gaussian, or trapezoidal) to be used for filtering purposes.
864
+ The transfer function is applied in the frequency domain to filter the input data.
865
+
866
+ Parameters
867
+ ----------
868
+ Fs : float
869
+ Sampling frequency of the input signal in Hz.
870
+ inputdata : NDArray
871
+ Input signal data, used to determine the length of the transfer function.
872
+ upperpass : float, optional
873
+ Upper passband frequency in Hz. Must be specified.
874
+ upperstop : float, optional
875
+ Upper stopband frequency in Hz. Only used for 'trapezoidal' type.
876
+ Defaults to 1.05 * upperpass if not specified.
877
+ type : str, optional
878
+ Type of transfer function to generate. Options are:
879
+ - "brickwall": Ideal low-pass filter with sharp cutoff.
880
+ - "gaussian": Gaussian-shaped transition.
881
+ - "trapezoidal": Trapezoidal transition between pass and stop bands.
882
+ Default is "brickwall".
883
+ debug : bool, optional
884
+ If True, prints debug information and displays the transfer function plot.
885
+ Default is False.
886
+
887
+ Returns
888
+ -------
889
+ NDArray
890
+ The computed low-pass transfer function with the same shape as `inputdata`.
891
+
892
+ Notes
893
+ -----
894
+ - For 'brickwall' type, the transfer function is 1.0 in the passband and 0.0 in the stopband.
895
+ - For 'gaussian' type, the transition is smoothed using a Gaussian function.
896
+ - For 'trapezoidal' type, a linear transition is applied between pass and stop bands.
897
+ - The function uses the sampling frequency `Fs` to map frequencies to the normalized frequency axis.
898
+
899
+ Examples
900
+ --------
901
+ >>> import numpy as np
902
+ >>> input_signal = np.random.rand(1024)
903
+ >>> Fs = 100.0
904
+ >>> upperpass = 20.0
905
+ >>> tf = getlptransfunc(Fs, input_signal, upperpass, type='gaussian')
906
+ >>> print(tf.shape)
907
+ (1024,)
908
+ """
713
909
  if upperpass is None:
714
910
  print("getlptransfunc: upperpass must be specified")
715
911
  sys.exit()
@@ -773,7 +969,55 @@ def getlptransfunc(Fs, inputdata, upperpass=None, upperstop=None, type="brickwal
773
969
  return transferfunc
774
970
 
775
971
 
776
- def gethptransfunc(Fs, inputdata, lowerstop=None, lowerpass=None, type="brickwall", debug=False):
972
+ def gethptransfunc(
973
+ Fs: float,
974
+ inputdata: NDArray,
975
+ lowerstop: Optional[float] = None,
976
+ lowerpass: Optional[float] = None,
977
+ type: str = "brickwall",
978
+ debug: bool = False,
979
+ ) -> NDArray:
980
+ """
981
+ Compute high-pass transfer function from low-pass transfer function.
982
+
983
+ This function generates a high-pass transfer function by subtracting a
984
+ low-pass transfer function from unity. The low-pass function is computed
985
+ using the `getlptransfunc` function with appropriate parameters.
986
+
987
+ Parameters
988
+ ----------
989
+ Fs : float
990
+ Sampling frequency in Hz.
991
+ inputdata : NDArray
992
+ Input data array used for transfer function computation.
993
+ lowerstop : float, optional
994
+ Lower stop frequency for trapezoidal filter type. Required for
995
+ trapezoidal type, ignored for brickwall type.
996
+ lowerpass : float, optional
997
+ Lower pass frequency (cutoff frequency) for the high-pass filter.
998
+ Must be specified.
999
+ type : str, default="brickwall"
1000
+ Type of filter transfer function. Options are "brickwall" or "trapezoidal".
1001
+ debug : bool, default=False
1002
+ If True, enables debug output during computation.
1003
+
1004
+ Returns
1005
+ -------
1006
+ NDArray
1007
+ High-pass transfer function array with same shape as inputdata.
1008
+
1009
+ Notes
1010
+ -----
1011
+ For trapezoidal filter type, the lower stop frequency is used as the
1012
+ upper pass frequency for the underlying low-pass function.
1013
+
1014
+ Examples
1015
+ --------
1016
+ >>> import numpy as np
1017
+ >>> Fs = 100.0
1018
+ >>> data = np.linspace(0, 1, 100)
1019
+ >>> hp_func = gethptransfunc(Fs, data, lowerpass=10.0, type="brickwall")
1020
+ """
777
1021
  if lowerpass is None:
778
1022
  print("gethptransfunc: lowerpass must be specified")
779
1023
  sys.exit()
@@ -795,53 +1039,67 @@ def gethptransfunc(Fs, inputdata, lowerstop=None, lowerpass=None, type="brickwal
795
1039
 
796
1040
  # @conditionaljit()
797
1041
  def dolptransfuncfilt(
798
- Fs,
799
- inputdata,
800
- upperpass=None,
801
- upperstop=None,
802
- type="brickwall",
803
- padlen=20,
804
- avlen=20,
805
- padtype="reflect",
806
- debug=False,
807
- ):
808
- r"""Performs an FFT filter with a gaussian lowpass transfer
809
- function on an input vector and returns the result. Ends are padded to reduce transients.
1042
+ Fs: float,
1043
+ inputdata: NDArray,
1044
+ upperpass: Optional[float] = None,
1045
+ upperstop: Optional[float] = None,
1046
+ type: str = "brickwall",
1047
+ padlen: int = 20,
1048
+ avlen: int = 20,
1049
+ padtype: str = "reflect",
1050
+ debug: bool = False,
1051
+ ) -> NDArray:
1052
+ """
1053
+ Performs an FFT filter with a Gaussian lowpass transfer function on an input vector
1054
+ and returns the result. Ends are padded to reduce transients.
810
1055
 
811
1056
  Parameters
812
1057
  ----------
813
1058
  Fs : float
814
- Sample rate in Hz
815
- :param Fs:
816
-
817
- upperpass : float
818
- Upper end of passband in Hz
819
- :param upperpass:
820
-
821
- inputdata : 1D numpy array
822
- Input data to be filtered
823
- :param inputdata:
824
-
1059
+ Sample rate in Hz.
1060
+ inputdata : NDArray
1061
+ Input data to be filtered, expected as a 1D array.
1062
+ upperpass : float, optional
1063
+ Upper end of the passband in Hz. If not specified, the filter will use a default
1064
+ value based on the data characteristics.
1065
+ upperstop : float, optional
1066
+ Upper end of the stopband in Hz. If not specified, the filter will use a default
1067
+ value based on the data characteristics.
1068
+ type : str, optional
1069
+ Type of transfer function to use. Default is "brickwall". Other options may include
1070
+ "gaussian", "butterworth", etc., depending on implementation of `getlptransfunc`.
825
1071
  padlen : int, optional
826
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
827
- :param padlen:
828
-
829
- cyclic : bool, optional
830
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
831
- :param cyclic:
832
-
833
- debug : boolean, optional
1072
+ Amount of points to reflect around each end of the input vector prior to filtering.
1073
+ Default is 20.
1074
+ avlen : int, optional
1075
+ Length of the averaging window used in padding. Default is 20.
1076
+ padtype : str, optional
1077
+ Type of padding to use. Options include "reflect", "wrap", "constant", etc.
1078
+ Default is "reflect".
1079
+ debug : bool, optional
834
1080
  When True, internal states of the function will be printed to help debugging.
835
- :param debug:
1081
+ A plot of the transfer function will be displayed if debug is enabled.
836
1082
 
837
1083
  Returns
838
1084
  -------
839
- filtereddata : 1D float array
840
- The filtered data
1085
+ NDArray
1086
+ The filtered data as a 1D float array of the same length as inputdata.
1087
+
1088
+ Notes
1089
+ -----
1090
+ This function applies a frequency-domain filter by computing the FFT of the padded input,
1091
+ applying a transfer function, and then inverse transforming the result. Padding is applied
1092
+ to reduce edge effects caused by the FFT.
1093
+
1094
+ Examples
1095
+ --------
1096
+ >>> import numpy as np
1097
+ >>> Fs = 100.0
1098
+ >>> t = np.linspace(0, 1, int(Fs), endpoint=False)
1099
+ >>> signal = np.sin(2 * np.pi * 10 * t)
1100
+ >>> filtered = dolptransfuncfilt(Fs, signal, upperpass=20.0)
841
1101
  """
842
- padinputdata = padvec(
843
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
844
- )
1102
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
845
1103
  inputdata_trans = fftpack.fft(padinputdata)
846
1104
  transferfunc = getlptransfunc(
847
1105
  Fs, padinputdata, upperpass=upperpass, upperstop=upperstop, type=type
@@ -853,7 +1111,7 @@ def dolptransfuncfilt(
853
1111
  )
854
1112
  fig = plt.figure()
855
1113
  ax = fig.add_subplot(111)
856
- ax.set_title("LP Transfer function - " + type + ", upperpass={:.2f}".format(upperpass))
1114
+ ax.set_title(f"LP Transfer function - {type}, upperpass={upperpass:.2f}")
857
1115
  plt.plot(freqaxis, transferfunc)
858
1116
  plt.show()
859
1117
  inputdata_trans *= transferfunc
@@ -862,59 +1120,65 @@ def dolptransfuncfilt(
862
1120
 
863
1121
  # @conditionaljit()
864
1122
  def dohptransfuncfilt(
865
- Fs,
866
- inputdata,
867
- lowerstop=None,
868
- lowerpass=None,
869
- type="brickwall",
870
- padlen=20,
871
- avlen=20,
872
- padtype="reflect",
873
- debug=False,
874
- ):
875
- r"""Performs an FFT filter with a trapezoidal highpass transfer
876
- function on an input vector and returns the result. Ends are padded to reduce transients.
1123
+ Fs: float,
1124
+ inputdata: NDArray,
1125
+ lowerpass: float,
1126
+ lowerstop: Optional[float | None] = None,
1127
+ type: str = "brickwall",
1128
+ padlen: int = 20,
1129
+ avlen: int = 20,
1130
+ padtype: str = "reflect",
1131
+ debug: bool = False,
1132
+ ) -> NDArray:
1133
+ """
1134
+ Performs an FFT filter with a trapezoidal highpass transfer function on an input vector
1135
+ and returns the result. Ends are padded to reduce transients.
877
1136
 
878
1137
  Parameters
879
1138
  ----------
880
1139
  Fs : float
881
- Sample rate in Hz
882
- :param Fs:
883
-
884
- lowerstop : float
885
- Upper end of stopband in Hz
886
- :param lowerstop:
887
-
1140
+ Sample rate in Hz.
1141
+ inputdata : NDArray
1142
+ Input data to be filtered.
888
1143
  lowerpass : float
889
- Lower end of passband in Hz
890
- :param lowerpass:
891
-
892
- inputdata : 1D numpy array
893
- Input data to be filtered
894
- :param inputdata:
895
-
1144
+ Lower end of the passband in Hz.
1145
+ lowerstop : float, optional
1146
+ Upper end of the stopband in Hz. If not provided, it is set to `lowerpass / 1.05`.
1147
+ type : str, optional
1148
+ Type of transfer function to use. Default is "brickwall".
896
1149
  padlen : int, optional
897
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
898
- :param padlen:
899
-
900
- cyclic : bool, optional
901
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
902
- :param cyclic:
903
-
904
- debug : boolean, optional
1150
+ Amount of points to reflect around each end of the input vector prior to filtering.
1151
+ Default is 20.
1152
+ avlen : int, optional
1153
+ Length of the averaging window used in padding. Default is 20.
1154
+ padtype : str, optional
1155
+ Type of padding to use. Options are "reflect" or "wrap". Default is "reflect".
1156
+ debug : bool, optional
905
1157
  When True, internal states of the function will be printed to help debugging.
906
- :param debug:
1158
+ Default is False.
907
1159
 
908
1160
  Returns
909
1161
  -------
910
- filtereddata : 1D float array
911
- The filtered data
1162
+ filtereddata : NDArray
1163
+ The filtered data as a 1D float array.
1164
+
1165
+ Notes
1166
+ -----
1167
+ This function applies a highpass filter in the frequency domain using FFT. The input signal
1168
+ is padded to reduce edge effects, then filtered using a transfer function, and finally
1169
+ unpadded to return the result.
1170
+
1171
+ Examples
1172
+ --------
1173
+ >>> import numpy as np
1174
+ >>> Fs = 100.0
1175
+ >>> t = np.linspace(0, 1, int(Fs), endpoint=False)
1176
+ >>> signal = np.sin(2 * np.pi * 10 * t)
1177
+ >>> filtered = dohptransfuncfilt(Fs, signal, lowerpass=5.0)
912
1178
  """
913
1179
  if lowerstop is None:
914
1180
  lowerstop = lowerpass * (1.0 / 1.05)
915
- padinputdata = padvec(
916
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
917
- )
1181
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
918
1182
  inputdata_trans = fftpack.fft(padinputdata)
919
1183
  transferfunc = getlptransfunc(
920
1184
  Fs, padinputdata, upperpass=lowerstop, upperstop=lowerpass, type=type
@@ -926,7 +1190,7 @@ def dohptransfuncfilt(
926
1190
  )
927
1191
  fig = plt.figure()
928
1192
  ax = fig.add_subplot(111)
929
- ax.set_title("HP Transfer function - " + type + ", lowerpass={:.2f}".format(lowerpass))
1193
+ ax.set_title(f"HP Transfer function - {type}, lowerpass={lowerpass:.2f}")
930
1194
  plt.plot(freqaxis, transferfunc)
931
1195
  plt.show()
932
1196
  inputdata_trans *= 1.0 - transferfunc
@@ -935,61 +1199,72 @@ def dohptransfuncfilt(
935
1199
 
936
1200
  # @conditionaljit()
937
1201
  def dobptransfuncfilt(
938
- Fs,
939
- inputdata,
940
- lowerstop=None,
941
- lowerpass=None,
942
- upperpass=None,
943
- upperstop=None,
944
- type="brickwall",
945
- padlen=20,
946
- avlen=20,
947
- padtype="reflect",
948
- debug=False,
949
- ):
950
- r"""Performs an FFT filter with a trapezoidal highpass transfer
951
- function on an input vector and returns the result. Ends are padded to reduce transients.
1202
+ Fs: float,
1203
+ inputdata: NDArray,
1204
+ lowerpass: float,
1205
+ upperpass: float,
1206
+ lowerstop: Optional[float] = None,
1207
+ upperstop: Optional[float] = None,
1208
+ type: str = "brickwall",
1209
+ padlen: int = 20,
1210
+ avlen: int = 20,
1211
+ padtype: str = "reflect",
1212
+ debug: bool = False,
1213
+ ) -> NDArray:
1214
+ """
1215
+ Performs an FFT filter with a trapezoidal highpass transfer function on an input vector
1216
+ and returns the result. Ends are padded to reduce transients.
952
1217
 
953
1218
  Parameters
954
1219
  ----------
955
1220
  Fs : float
956
- Sample rate in Hz
957
- :param Fs:
958
-
959
- lowerstop : float
960
- Upper end of stopband in Hz
961
- :param lowerstop:
962
-
1221
+ Sample rate in Hz.
1222
+ inputdata : NDArray
1223
+ Input data to be filtered.
963
1224
  lowerpass : float
964
- Lower end of passband in Hz
965
- :param lowerpass:
966
-
967
- inputdata : 1D numpy array
968
- Input data to be filtered
969
- :param inputdata:
970
-
1225
+ Lower end of passband in Hz.
1226
+ upperpass : float
1227
+ Upper end of passband in Hz.
1228
+ lowerstop : float, optional
1229
+ Upper end of stopband in Hz. If not provided, it is computed as `lowerpass / 1.05`.
1230
+ upperstop : float, optional
1231
+ Lower end of stopband in Hz. If not provided, it is computed as `upperpass * 1.05`.
1232
+ type : str, optional
1233
+ Type of transfer function to use. Default is "brickwall".
971
1234
  padlen : int, optional
972
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
973
- :param padlen:
974
-
975
- cyclic : bool, optional
976
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
977
- :param cyclic:
978
-
979
- debug : boolean, optional
1235
+ Amount of points to reflect around each end of the input vector prior to filtering.
1236
+ Default is 20.
1237
+ avlen : int, optional
1238
+ Length of the averaging window used in padding. Default is 20.
1239
+ padtype : str, optional
1240
+ Type of padding to use. Options are "reflect" or "wrap". Default is "reflect".
1241
+ debug : bool, optional
980
1242
  When True, internal states of the function will be printed to help debugging.
981
- :param debug:
1243
+ Default is False.
982
1244
 
983
1245
  Returns
984
1246
  -------
985
- filtereddata : 1D float array
986
- The filtered data
1247
+ filtereddata : NDArray
1248
+ The filtered data as a 1D float array.
1249
+
1250
+ Notes
1251
+ -----
1252
+ This function applies a bandpass filter in the frequency domain using FFT. It pads the input
1253
+ data to minimize edge effects and applies a transfer function that combines a lowpass and
1254
+ highpass response. The resulting filtered signal is returned after inverse FFT and unpadding.
1255
+
1256
+ Examples
1257
+ --------
1258
+ >>> import numpy as np
1259
+ >>> from scipy import fftpack
1260
+ >>> Fs = 100.0
1261
+ >>> t = np.linspace(0, 1, int(Fs), endpoint=False)
1262
+ >>> signal = np.sin(2 * np.pi * 10 * t)
1263
+ >>> filtered = dobptransfuncfilt(Fs, signal, 5.0, 15.0)
987
1264
  """
988
1265
  if lowerstop is None:
989
1266
  lowerstop = lowerpass * (1.0 / 1.05)
990
- padinputdata = padvec(
991
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
992
- )
1267
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
993
1268
  inputdata_trans = fftpack.fft(padinputdata)
994
1269
  transferfunc = getlptransfunc(
995
1270
  Fs,
@@ -1007,9 +1282,7 @@ def dobptransfuncfilt(
1007
1282
  fig = plt.figure()
1008
1283
  ax = fig.add_subplot(111)
1009
1284
  ax.set_title(
1010
- "BP Transfer function - "
1011
- + type
1012
- + ", lowerpass={:.2f}, upperpass={:.2f}".format(lowerpass, upperpass)
1285
+ f"BP Transfer function - {type}, lowerpass={lowerpass:.2f}, upperpass={upperpass:.2f}"
1013
1286
  )
1014
1287
  plt.plot(freqaxis, transferfunc)
1015
1288
  plt.show()
@@ -1019,56 +1292,61 @@ def dobptransfuncfilt(
1019
1292
 
1020
1293
  # @conditionaljit()
1021
1294
  def dolptrapfftfilt(
1022
- Fs,
1023
- upperpass,
1024
- upperstop,
1025
- inputdata,
1026
- padlen=20,
1027
- avlen=20,
1028
- padtype="reflect",
1029
- debug=False,
1030
- ):
1031
- r"""Performs an FFT filter with a trapezoidal lowpass transfer
1032
- function on an input vector and returns the result. Ends are padded to reduce transients.
1295
+ Fs: float,
1296
+ upperpass: float,
1297
+ upperstop: float,
1298
+ inputdata: NDArray,
1299
+ padlen: int = 20,
1300
+ avlen: int = 20,
1301
+ padtype: str = "reflect",
1302
+ debug: bool = False,
1303
+ ) -> NDArray:
1304
+ """
1305
+ Performs an FFT filter with a trapezoidal lowpass transfer function on an input vector
1306
+ and returns the result. Ends are padded to reduce transients.
1033
1307
 
1034
1308
  Parameters
1035
1309
  ----------
1036
1310
  Fs : float
1037
- Sample rate in Hz
1038
- :param Fs:
1039
-
1311
+ Sample rate in Hz.
1040
1312
  upperpass : float
1041
- Upper end of passband in Hz
1042
- :param upperpass:
1043
-
1313
+ Upper end of the passband in Hz.
1044
1314
  upperstop : float
1045
- Lower end of stopband in Hz
1046
- :param upperstop:
1047
-
1048
- inputdata : 1D numpy array
1049
- Input data to be filtered
1050
- :param inputdata:
1051
-
1315
+ Lower end of the stopband in Hz.
1316
+ inputdata : NDArray
1317
+ Input data to be filtered, as a 1D numpy array.
1052
1318
  padlen : int, optional
1053
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
1054
- :param padlen:
1055
-
1056
- cyclic : bool, optional
1057
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
1058
- :param cyclic:
1059
-
1060
- debug : boolean, optional
1319
+ Amount of points to reflect around each end of the input vector prior to filtering.
1320
+ Default is 20.
1321
+ avlen : int, optional
1322
+ Length of the averaging window used in padding. Default is 20.
1323
+ padtype : str, optional
1324
+ Type of padding to use. Options are 'reflect' or 'wrap'. Default is 'reflect'.
1325
+ debug : bool, optional
1061
1326
  When True, internal states of the function will be printed to help debugging.
1062
- :param debug:
1327
+ Default is False.
1063
1328
 
1064
1329
  Returns
1065
1330
  -------
1066
- filtereddata : 1D float array
1067
- The filtered data
1331
+ filtereddata : NDArray
1332
+ The filtered data as a 1D float array.
1333
+
1334
+ Notes
1335
+ -----
1336
+ This function applies a trapezoidal lowpass filter in the frequency domain using FFT.
1337
+ The input signal is first padded to reduce edge effects, then filtered using a
1338
+ transfer function, and finally the padding is removed to return the filtered signal.
1339
+
1340
+ Examples
1341
+ --------
1342
+ >>> import numpy as np
1343
+ >>> Fs = 100.0
1344
+ >>> upperpass = 20.0
1345
+ >>> upperstop = 25.0
1346
+ >>> data = np.random.randn(1000)
1347
+ >>> filtered = dolptrapfftfilt(Fs, upperpass, upperstop, data)
1068
1348
  """
1069
- padinputdata = padvec(
1070
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
1071
- )
1349
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
1072
1350
  inputdata_trans = fftpack.fft(padinputdata)
1073
1351
  transferfunc = getlptrapfftfunc(Fs, upperpass, upperstop, padinputdata, debug=debug)
1074
1352
  inputdata_trans *= transferfunc
@@ -1077,56 +1355,62 @@ def dolptrapfftfilt(
1077
1355
 
1078
1356
  # @conditionaljit()
1079
1357
  def dohptrapfftfilt(
1080
- Fs,
1081
- lowerstop,
1082
- lowerpass,
1083
- inputdata,
1084
- padlen=20,
1085
- avlen=20,
1086
- padtype="reflect",
1087
- debug=False,
1088
- ):
1089
- r"""Performs an FFT filter with a trapezoidal highpass transfer
1090
- function on an input vector and returns the result. Ends are padded to reduce transients.
1358
+ Fs: float,
1359
+ lowerstop: float,
1360
+ lowerpass: float,
1361
+ inputdata: NDArray,
1362
+ padlen: int = 20,
1363
+ avlen: int = 20,
1364
+ padtype: str = "reflect",
1365
+ debug: bool = False,
1366
+ ) -> NDArray:
1367
+ """
1368
+ Performs an FFT filter with a trapezoidal highpass transfer function on an input vector
1369
+ and returns the result. Ends are padded to reduce transients.
1091
1370
 
1092
1371
  Parameters
1093
1372
  ----------
1094
1373
  Fs : float
1095
- Sample rate in Hz
1096
- :param Fs:
1097
-
1374
+ Sample rate in Hz.
1098
1375
  lowerstop : float
1099
- Upper end of stopband in Hz
1100
- :param lowerstop:
1101
-
1376
+ Upper end of stopband in Hz.
1102
1377
  lowerpass : float
1103
- Lower end of passband in Hz
1104
- :param lowerpass:
1105
-
1106
- inputdata : 1D numpy array
1107
- Input data to be filtered
1108
- :param inputdata:
1109
-
1378
+ Lower end of passband in Hz.
1379
+ inputdata : NDArray
1380
+ Input data to be filtered, expected as a 1D numpy array.
1110
1381
  padlen : int, optional
1111
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
1112
- :param padlen:
1113
-
1114
- cyclic : bool, optional
1115
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
1116
- :param cyclic:
1117
-
1118
- debug : boolean, optional
1382
+ Amount of points to reflect around each end of the input vector prior to filtering.
1383
+ Default is 20.
1384
+ avlen : int, optional
1385
+ Length of the averaging window used in padding. Default is 20.
1386
+ padtype : str, optional
1387
+ Type of padding to use. Options are 'reflect' or 'cyclic'. Default is 'reflect'.
1388
+ debug : bool, optional
1119
1389
  When True, internal states of the function will be printed to help debugging.
1120
- :param debug:
1390
+ Default is False.
1121
1391
 
1122
1392
  Returns
1123
1393
  -------
1124
- filtereddata : 1D float array
1125
- The filtered data
1394
+ filtereddata : NDArray
1395
+ The filtered data as a 1D float array.
1396
+
1397
+ Notes
1398
+ -----
1399
+ This function applies a trapezoidal highpass filter in the frequency domain using FFT.
1400
+ The input signal is first padded using the specified padding method to reduce edge effects.
1401
+ The filter transfer function is constructed using `getlptrapfftfunc`, and the filtered
1402
+ signal is obtained by inverse FFT.
1403
+
1404
+ Examples
1405
+ --------
1406
+ >>> import numpy as np
1407
+ >>> Fs = 100.0
1408
+ >>> lowerstop = 5.0
1409
+ >>> lowerpass = 10.0
1410
+ >>> data = np.random.randn(1000)
1411
+ >>> filtered = dohptrapfftfilt(Fs, lowerstop, lowerpass, data)
1126
1412
  """
1127
- padinputdata = padvec(
1128
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
1129
- )
1413
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
1130
1414
  inputdata_trans = fftpack.fft(padinputdata)
1131
1415
  transferfunc = 1.0 - getlptrapfftfunc(Fs, lowerstop, lowerpass, padinputdata, debug=debug)
1132
1416
  inputdata_trans *= transferfunc
@@ -1135,66 +1419,66 @@ def dohptrapfftfilt(
1135
1419
 
1136
1420
  # @conditionaljit()
1137
1421
  def dobptrapfftfilt(
1138
- Fs,
1139
- lowerstop,
1140
- lowerpass,
1141
- upperpass,
1142
- upperstop,
1143
- inputdata,
1144
- padlen=20,
1145
- avlen=20,
1146
- padtype="reflect",
1147
- debug=False,
1148
- ):
1149
- r"""Performs an FFT filter with a trapezoidal bandpass transfer
1150
- function on an input vector and returns the result. Ends are padded to reduce transients.
1422
+ Fs: float,
1423
+ lowerstop: float,
1424
+ lowerpass: float,
1425
+ upperpass: float,
1426
+ upperstop: float,
1427
+ inputdata: NDArray,
1428
+ padlen: int = 20,
1429
+ avlen: int = 20,
1430
+ padtype: str = "reflect",
1431
+ debug: bool = False,
1432
+ ) -> NDArray:
1433
+ """
1434
+ Performs an FFT filter with a trapezoidal bandpass transfer function on an input vector
1435
+ and returns the result. Ends are padded to reduce transients.
1151
1436
 
1152
1437
  Parameters
1153
1438
  ----------
1154
1439
  Fs : float
1155
- Sample rate in Hz
1156
- :param Fs:
1157
-
1440
+ Sample rate in Hz.
1158
1441
  lowerstop : float
1159
- Upper end of stopband in Hz
1160
- :param lowerstop:
1161
-
1442
+ Upper end of the lower stopband in Hz.
1162
1443
  lowerpass : float
1163
- Lower end of passband in Hz
1164
- :param lowerpass:
1165
-
1444
+ Lower end of the lower passband in Hz.
1166
1445
  upperpass : float
1167
- Upper end of passband in Hz
1168
- :param upperpass:
1169
-
1446
+ Upper end of the upper passband in Hz.
1170
1447
  upperstop : float
1171
- Lower end of stopband in Hz
1172
- :param upperstop:
1173
-
1174
- inputdata : 1D numpy array
1175
- Input data to be filtered
1176
- :param inputdata:
1177
-
1448
+ Lower end of the upper stopband in Hz.
1449
+ inputdata : NDArray
1450
+ Input data to be filtered, expected as a 1D numpy array.
1178
1451
  padlen : int, optional
1179
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
1180
- :param padlen:
1181
-
1182
- cyclic : bool, optional
1183
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
1184
- :param cyclic:
1185
-
1186
- debug : boolean, optional
1452
+ Amount of points to reflect around each end of the input vector prior to filtering.
1453
+ Default is 20.
1454
+ avlen : int, optional
1455
+ Length of the averaging window used in padding. Default is 20.
1456
+ padtype : str, optional
1457
+ Type of padding to use. Options are 'reflect' or 'wrap'. Default is 'reflect'.
1458
+ debug : bool, optional
1187
1459
  When True, internal states of the function will be printed to help debugging.
1188
- :param debug:
1460
+ Default is False.
1189
1461
 
1190
1462
  Returns
1191
1463
  -------
1192
- filtereddata : 1D float array
1193
- The filtered data
1464
+ NDArray
1465
+ The filtered data as a 1D float array.
1466
+
1467
+ Notes
1468
+ -----
1469
+ This function applies a trapezoidal bandpass filter in the frequency domain using FFT.
1470
+ The input signal is first padded to minimize edge effects, then transformed into the
1471
+ frequency domain, multiplied by the transfer function, and finally inverse transformed
1472
+ back to the time domain.
1473
+
1474
+ Examples
1475
+ --------
1476
+ >>> import numpy as np
1477
+ >>> Fs = 100.0
1478
+ >>> data = np.random.randn(1000)
1479
+ >>> filtered = dobptrapfftfilt(Fs, 10, 15, 25, 30, data, padlen=50)
1194
1480
  """
1195
- padinputdata = padvec(
1196
- inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
1197
- )
1481
+ padinputdata = padvec(inputdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
1198
1482
  inputdata_trans = fftpack.fft(padinputdata)
1199
1483
  if debug:
1200
1484
  print(
@@ -1220,8 +1504,43 @@ def dobptrapfftfilt(
1220
1504
  # We use a fixed SNR across all frequencies in this example.
1221
1505
  #
1222
1506
  # Written 2015 by Dan Stowell. Public domain.
1223
- def wiener_deconvolution(signal, kernel, lambd):
1224
- "lambd is the SNR in the fourier domain"
1507
+ def wiener_deconvolution(signal: NDArray, kernel: NDArray, lambd: float) -> NDArray:
1508
+ """Perform Wiener deconvolution on a signal.
1509
+
1510
+ This function applies Wiener deconvolution to remove blur from a signal using
1511
+ the Wiener filter in the frequency domain. The regularization parameter `lambd`
1512
+ represents the signal-to-noise ratio in the Fourier domain.
1513
+
1514
+ Parameters
1515
+ ----------
1516
+ signal : NDArray
1517
+ Input signal to be deconvolved, 1D array.
1518
+ kernel : NDArray
1519
+ Convolution kernel (point spread function), 1D array.
1520
+ lambd : float
1521
+ Regularization parameter representing the signal-to-noise ratio in
1522
+ the Fourier domain. Higher values correspond to more smoothing.
1523
+
1524
+ Returns
1525
+ -------
1526
+ NDArray
1527
+ Deconvolved signal, same length as input signal.
1528
+
1529
+ Notes
1530
+ -----
1531
+ The Wiener deconvolution formula in frequency domain is:
1532
+ Y = X * H* / (|H|² + λ²)
1533
+
1534
+ where X is the Fourier transform of the input signal, H is the Fourier
1535
+ transform of the kernel, and H* is the complex conjugate of H.
1536
+
1537
+ Examples
1538
+ --------
1539
+ >>> import numpy as np
1540
+ >>> signal = np.array([1, 2, 3, 2, 1])
1541
+ >>> kernel = np.array([1, 0.5, 0.25])
1542
+ >>> result = wiener_deconvolution(signal, kernel, lambd=0.1)
1543
+ """
1225
1544
  kernel = np.hstack(
1226
1545
  (kernel, np.zeros(len(signal) - len(kernel)))
1227
1546
  ) # zero pad the kernel to same length
@@ -1233,66 +1552,83 @@ def wiener_deconvolution(signal, kernel, lambd):
1233
1552
  return deconvolved
1234
1553
 
1235
1554
 
1236
- def pspec(inputdata):
1237
- r"""Calculate the power spectrum of an input signal
1555
+ def pspec(inputdata: NDArray) -> NDArray:
1556
+ """
1557
+ Calculate the power spectrum of an input signal.
1238
1558
 
1239
1559
  Parameters
1240
1560
  ----------
1241
- inputdata: 1D numpy array
1242
- Input data
1561
+ inputdata : NDArray
1562
+ Input signal data array of shape (n,) where n is the number of samples.
1243
1563
 
1244
1564
  Returns
1245
1565
  -------
1246
- spectrum: 1D numpy array
1247
- The power spectrum of the input signal.
1248
-
1566
+ NDArray
1567
+ The power spectrum of the input signal as a 1D numpy array of shape (n,).
1568
+ Each element represents the power at the corresponding frequency bin.
1569
+
1570
+ Notes
1571
+ -----
1572
+ This function computes the power spectrum using the Fast Fourier Transform (FFT).
1573
+ The power spectrum is calculated as the square root of the product of the FFT
1574
+ and its complex conjugate, which gives the magnitude of the frequency components.
1575
+
1576
+ Examples
1577
+ --------
1578
+ >>> import numpy as np
1579
+ >>> from scipy import fftpack
1580
+ >>> signal = np.sin(2 * np.pi * 5 * np.linspace(0, 1, 100))
1581
+ >>> spectrum = pspec(signal)
1582
+ >>> print(spectrum.shape)
1583
+ (100,)
1249
1584
  """
1250
1585
  S = fftpack.fft(inputdata)
1251
1586
  return np.sqrt(S * np.conj(S))
1252
1587
 
1253
1588
 
1254
- def spectralflatness(spectrum):
1589
+ def spectralflatness(spectrum: NDArray) -> float:
1255
1590
  return np.exp(np.mean(np.log(spectrum))) / np.mean(spectrum)
1256
1591
 
1257
1592
 
1258
- def spectrum(inputdata, Fs=1.0, mode="power", trim=True):
1259
- r"""Performs an FFT of the input data, and returns the frequency axis and spectrum
1260
- of the input signal.
1593
+ def spectrum(
1594
+ inputdata: NDArray, Fs: float = 1.0, mode: str = "power", trim: bool = True
1595
+ ) -> Tuple[NDArray, Union[NDArray, None]]:
1596
+ """
1597
+ Compute the spectral flatness of a spectrum.
1598
+
1599
+ Spectral flatness is a measure of how much a spectrum resembles white noise.
1600
+ It is defined as the ratio of the geometric mean to the arithmetic mean of the spectrum.
1261
1601
 
1262
1602
  Parameters
1263
1603
  ----------
1264
- inputdata : 1D numpy array
1265
- Input data
1266
- :param inputdata:
1267
-
1268
- Fs : float, optional
1269
- Sample rate in Hz. Defaults to 1.0
1270
- :param Fs:
1271
-
1272
- mode : {'real', 'imag', 'mag', 'phase', 'power'}, optional
1273
- The type of spectrum to return. Default is 'power'.
1274
- :param mode:
1275
-
1276
- trim: bool
1277
- If True (default) return only the positive frequency values
1604
+ spectrum : NDArray
1605
+ Input spectrum array. Should contain non-negative values.
1278
1606
 
1279
1607
  Returns
1280
1608
  -------
1281
- specaxis : 1D float array
1282
- The frequency axis.
1283
-
1284
- specvals : 1D float array
1285
- The spectral data.
1286
-
1287
- Other Parameters
1288
- ----------------
1289
- Fs : float
1290
- Sample rate in Hz. Defaults to 1.0
1291
- :param Fs:
1292
-
1293
- mode : {'real', 'imag', 'complex', 'mag', 'phase', 'power'}
1294
- The type of spectrum to return. Legal values are 'real', 'imag', 'mag', 'phase', and 'power' (default)
1295
- :param mode:
1609
+ float
1610
+ Spectral flatness value. Values close to 1.0 indicate a flat (white noise-like) spectrum,
1611
+ while values closer to 0.0 indicate a more tonal spectrum.
1612
+
1613
+ Notes
1614
+ -----
1615
+ The spectral flatness is computed as:
1616
+ flatness = exp(mean(log(spectrum))) / mean(spectrum)
1617
+
1618
+ This implementation assumes the input spectrum contains non-negative values.
1619
+ If the spectrum contains zeros, the geometric mean will be zero and the result
1620
+ will be zero regardless of the arithmetic mean.
1621
+
1622
+ Examples
1623
+ --------
1624
+ >>> import numpy as np
1625
+ >>> # White noise spectrum
1626
+ >>> white_noise = np.random.rand(100)
1627
+ >>> flatness = spectralflatness(white_noise)
1628
+ >>> # Tone-like spectrum
1629
+ >>> tone = np.zeros(100)
1630
+ >>> tone[50] = 1.0
1631
+ >>> flatness = spectralflatness(tone)
1296
1632
  """
1297
1633
  if trim:
1298
1634
  specvals = fftpack.fft(inputdata)[0 : len(inputdata) // 2]
@@ -1315,22 +1651,43 @@ def spectrum(inputdata, Fs=1.0, mode="power", trim=True):
1315
1651
  elif mode == "power":
1316
1652
  specvals = np.sqrt(np.absolute(specvals))
1317
1653
  else:
1318
- print("illegal spectrum mode")
1319
- specvals = None
1654
+ raise RuntimeError("illegal spectrum mode")
1320
1655
  return specaxis, specvals
1321
1656
 
1322
1657
 
1323
- def setnotchfilter(thefilter, thefreq, notchwidth=1.0):
1324
- r"""Set notch filter - sets the filter parameters for the notch.
1658
+ def setnotchfilter(thefilter: object, thefreq: float, notchwidth: float = 1.0) -> None:
1659
+ """
1660
+ Set notch filter parameters for the specified filter.
1661
+
1662
+ This function configures a notch filter by setting the filter type to "arb_stop"
1663
+ and defining the frequency range for the notch based on the center frequency
1664
+ and notch width parameters.
1325
1665
 
1326
1666
  Parameters
1327
1667
  ----------
1328
- thefilter: NoncausalFilter function
1329
- The filter function to use
1330
- thefreq: float
1331
- Frequency of the notch
1332
- notchwidth: float
1333
- width of the notch in Hz
1668
+ thefilter : NoncausalFilter function
1669
+ The filter function to configure with notch filter parameters
1670
+ thefreq : float
1671
+ Center frequency of the notch in Hz
1672
+ notchwidth : float, optional
1673
+ Width of the notch in Hz, default is 1.0 Hz
1674
+
1675
+ Returns
1676
+ -------
1677
+ None
1678
+ This function modifies the filter in-place and does not return any value
1679
+
1680
+ Notes
1681
+ -----
1682
+ The notch filter is configured as an "arb_stop" type filter with symmetric
1683
+ frequency bounds around the center frequency. The actual notch range will be
1684
+ from (thefreq - notchwidth/2) to (thefreq + notchwidth/2) Hz.
1685
+
1686
+ Examples
1687
+ --------
1688
+ >>> filter_obj = NoncausalFilter()
1689
+ >>> setnotchfilter(filter_obj, 50.0, 2.0)
1690
+ >>> # Creates a notch filter centered at 50 Hz with 2 Hz width
1334
1691
  """
1335
1692
  thefilter.settype("arb_stop")
1336
1693
  thefilter.setfreqs(
@@ -1341,27 +1698,52 @@ def setnotchfilter(thefilter, thefreq, notchwidth=1.0):
1341
1698
  )
1342
1699
 
1343
1700
 
1344
- def harmonicnotchfilter(timecourse, Fs, Ffundamental, notchpct=1.0, debug=False):
1345
- r"""Harmonic notch filter - removes a fundamental and its harmonics from a timecourse.
1701
+ def harmonicnotchfilter(
1702
+ timecourse: NDArray,
1703
+ Fs: float,
1704
+ Ffundamental: float,
1705
+ notchpct: float = 1.0,
1706
+ debug: bool = False,
1707
+ ) -> NDArray:
1708
+ """
1709
+ Apply a harmonic notch filter to remove a fundamental frequency and its harmonics from a timecourse.
1710
+
1711
+ This function removes the specified fundamental frequency and all its integer harmonics
1712
+ using a non-causal notch filtering approach. The width of each notch is proportional
1713
+ to the harmonic order and the specified percentage (`notchpct`).
1346
1714
 
1347
1715
  Parameters
1348
1716
  ----------
1349
- timecourse: 1D numpy array
1350
- Input data
1351
- Fs: float
1352
- Sample rate
1353
- Ffundamental: float
1354
- Fundamental frequency to be removed from the data
1355
- notchpct: float, optional
1356
- Width of the notch relative to the filter frequency in percent. Default is 1.0.
1357
- debug: bool, optional
1358
- Set to True for additional information on function internals. Default is False.
1717
+ timecourse : NDArray
1718
+ Input timecourse data to be filtered.
1719
+ Fs : float
1720
+ Sampling rate of the input data in Hz.
1721
+ Ffundamental : float
1722
+ Fundamental frequency to be removed from the data in Hz.
1723
+ notchpct : float, optional
1724
+ Width of the notch relative to the filter frequency in percent. Default is 1.0.
1725
+ debug : bool, optional
1726
+ If True, prints detailed information about the filtering process. Default is False.
1359
1727
 
1360
1728
  Returns
1361
1729
  -------
1362
- filteredtc: 1D numpy array
1363
- The filtered data
1364
-
1730
+ filteredtc : NDArray
1731
+ The filtered timecourse with the fundamental and its harmonics removed.
1732
+
1733
+ Notes
1734
+ -----
1735
+ - The function uses a non-causal filter, meaning it requires the full signal to be available.
1736
+ - Harmonics are calculated as integer multiples of the fundamental frequency.
1737
+ - The notch width is determined by `notchpct * harmonic * Ffundamental * 0.01`, with a minimum
1738
+ width of one frequency bin.
1739
+
1740
+ Examples
1741
+ --------
1742
+ >>> import numpy as np
1743
+ >>> timecourse = np.random.randn(1000)
1744
+ >>> Fs = 100.0
1745
+ >>> Ffundamental = 50.0
1746
+ >>> filtered = harmonicnotchfilter(timecourse, Fs, Ffundamental, notchpct=2.0)
1365
1747
  """
1366
1748
  # delete the fundamental and its harmonics
1367
1749
  filteredtc = timecourse + 0.0
@@ -1393,44 +1775,100 @@ def harmonicnotchfilter(timecourse, Fs, Ffundamental, notchpct=1.0, debug=False)
1393
1775
  return filteredtc
1394
1776
 
1395
1777
 
1396
- def savgolsmooth(data, smoothlen=101, polyorder=3):
1397
- return savgol_filter(data, smoothlen, polyorder)
1398
-
1778
+ def savgolsmooth(data: NDArray, smoothlen: int = 101, polyorder: int = 3) -> NDArray:
1779
+ """
1780
+ Apply Savitzky-Golay filter to smooth data.
1399
1781
 
1400
- def csdfilter(
1401
- obsdata, commondata, padlen=20, avlen=20, padtype="reflect", debug=False
1402
- ):
1403
- r"""Cross spectral density filter - makes a filter transfer function that preserves common frequencies.
1782
+ This function applies a Savitzky-Golay filter to smooth the input data. The filter uses
1783
+ a least-squares method to fit a polynomial to a sliding window of data points and
1784
+ replaces each point with the value of the polynomial at that point.
1404
1785
 
1405
1786
  Parameters
1406
1787
  ----------
1407
- obsdata: 1D numpy array
1408
- Input data
1788
+ data : array_like
1789
+ Input data to be smoothed. Can be any array-like object that can be converted
1790
+ to a numpy array.
1791
+ smoothlen : int, optional
1792
+ The length of the filter window (i.e., the number of coefficients). Must be a
1793
+ positive odd integer. Default is 101.
1794
+ polyorder : int, optional
1795
+ The order of the polynomial used to fit the samples. Must be less than
1796
+ `smoothlen`. Default is 3.
1409
1797
 
1410
- commondata: 1D numpy array
1411
- Shared data
1798
+ Returns
1799
+ -------
1800
+ NDArray
1801
+ The smoothed data with the same shape as the input `data`.
1802
+
1803
+ Notes
1804
+ -----
1805
+ The Savitzky-Golay filter is particularly useful for smoothing noisy data while
1806
+ preserving the shape and features of the underlying signal. It is especially
1807
+ effective for data with a lot of high-frequency noise.
1808
+
1809
+ Examples
1810
+ --------
1811
+ >>> import numpy as np
1812
+ >>> from scipy.signal import savgol_filter
1813
+ >>> x = np.linspace(0, 4*np.pi, 100)
1814
+ >>> y = np.sin(x) + np.random.normal(0, 0.1, 100)
1815
+ >>> y_smooth = savgolsmooth(y, smoothlen=21, polyorder=3)
1816
+ """
1817
+ return savgol_filter(data, smoothlen, polyorder)
1412
1818
 
1413
- padlen: int, optional
1414
- Number of reflected points to add on each end of the input data. Default is 20.
1415
1819
 
1416
- cyclic : bool, optional
1417
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
1820
+ def csdfilter(
1821
+ obsdata: NDArray,
1822
+ commondata: NDArray,
1823
+ padlen: int = 20,
1824
+ avlen: int = 20,
1825
+ padtype: str = "reflect",
1826
+ debug: bool = False,
1827
+ ) -> NDArray:
1828
+ """
1829
+ Cross spectral density filter - makes a filter transfer function that preserves common frequencies.
1418
1830
 
1419
- debug: bool, optional
1420
- Set to True for additional information on function internals. Default is False.
1831
+ This function applies a filter based on the cross spectral density between two signals.
1832
+ It uses the Fourier transform to compute the transfer function and applies it to the
1833
+ observation data, preserving the frequency components that are common between the two
1834
+ input signals.
1835
+
1836
+ Parameters
1837
+ ----------
1838
+ obsdata : NDArray
1839
+ Input data (1D numpy array) to be filtered.
1840
+ commondata : NDArray
1841
+ Shared data (1D numpy array) used to compute the transfer function.
1842
+ padlen : int, optional
1843
+ Number of reflected points to add on each end of the input data. Default is 20.
1844
+ avlen : int, optional
1845
+ Length of the averaging window for padding. Default is 20.
1846
+ padtype : str, optional
1847
+ Type of padding to use. Options are 'reflect' or 'cyclic'. Default is 'reflect'.
1848
+ debug : bool, optional
1849
+ Set to True for additional information on function internals. Default is False.
1421
1850
 
1422
1851
  Returns
1423
1852
  -------
1424
- filtereddata: 1D numpy array
1425
- The filtered data
1426
-
1853
+ NDArray
1854
+ The filtered data (1D numpy array) with preserved common frequencies.
1855
+
1856
+ Notes
1857
+ -----
1858
+ The function first pads both input arrays using the specified padding method, then computes
1859
+ the FFT of both padded arrays. A transfer function is constructed from the square root of
1860
+ the magnitude of the product of the FFTs. This transfer function is applied to the FFT of
1861
+ the observation data, and the inverse FFT is taken to return the filtered signal.
1862
+
1863
+ Examples
1864
+ --------
1865
+ >>> import numpy as np
1866
+ >>> obs = np.random.randn(100)
1867
+ >>> common = np.random.randn(100)
1868
+ >>> filtered = csdfilter(obs, common, padlen=10)
1427
1869
  """
1428
- padobsdata = padvec(
1429
- obsdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
1430
- )
1431
- padcommondata = padvec(
1432
- commondata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug
1433
- )
1870
+ padobsdata = padvec(obsdata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
1871
+ padcommondata = padvec(commondata, padlen=padlen, avlen=avlen, padtype=padtype, debug=debug)
1434
1872
  obsdata_trans = fftpack.fft(padobsdata)
1435
1873
  transferfunc = np.sqrt(np.abs(fftpack.fft(padobsdata) * np.conj(fftpack.fft(padcommondata))))
1436
1874
  obsdata_trans *= transferfunc
@@ -1439,69 +1877,88 @@ def csdfilter(
1439
1877
 
1440
1878
  # @conditionaljit()
1441
1879
  def arb_pass(
1442
- Fs,
1443
- inputdata,
1444
- lowerstop,
1445
- lowerpass,
1446
- upperpass,
1447
- upperstop,
1448
- transferfunc="trapezoidal",
1449
- butterorder=6,
1450
- padlen=20,
1451
- avlen=20,
1452
- padtype="reflect",
1453
- debug=False,
1454
- ):
1455
- r"""Filters an input waveform over a specified range. By default it is a trapezoidal
1456
- FFT filter, but brickwall and butterworth filters are also available. Ends are padded to reduce
1457
- transients.
1880
+ Fs: float,
1881
+ inputdata: NDArray,
1882
+ lowerstop: float,
1883
+ lowerpass: float,
1884
+ upperpass: float,
1885
+ upperstop: float,
1886
+ transferfunc: str = "trapezoidal",
1887
+ butterorder: int = 6,
1888
+ padlen: int = 20,
1889
+ avlen: int = 20,
1890
+ padtype: str = "reflect",
1891
+ debug: bool = False,
1892
+ ) -> NDArray:
1893
+ """
1894
+ Filters an input waveform over a specified range using configurable filter types.
1895
+
1896
+ By default, it applies a trapezoidal FFT filter, but brickwall and Butterworth
1897
+ filters are also supported. The function handles lowpass, bandpass, and highpass
1898
+ filtering based on the input frequency limits. Ends of the input data are padded
1899
+ to reduce transients.
1458
1900
 
1459
1901
  Parameters
1460
1902
  ----------
1461
1903
  Fs : float
1462
- Sample rate in Hz
1463
- :param Fs:
1464
-
1465
- inputdata : 1D numpy array
1466
- Input data to be filtered
1467
- :param inputdata:
1468
-
1904
+ Sample rate in Hz.
1905
+ inputdata : NDArray
1906
+ Input data to be filtered.
1469
1907
  lowerstop : float
1470
- Upper end of lower stopband in Hz
1471
- :param lowerstop:
1472
-
1908
+ Upper end of lower stopband in Hz.
1473
1909
  lowerpass : float
1474
- Lower end of passband in Hz
1475
- :param lowerpass:
1476
-
1910
+ Lower end of passband in Hz.
1477
1911
  upperpass : float
1478
- Upper end of passband in Hz
1479
- :param upperpass:
1480
-
1912
+ Upper end of passband in Hz.
1481
1913
  upperstop : float
1482
- Lower end of upper stopband in Hz
1483
- :param upperstop:
1484
-
1914
+ Lower end of upper stopband in Hz.
1915
+ transferfunc : str, optional
1916
+ Type of transfer function to use. Options are:
1917
+ - "trapezoidal" (default)
1918
+ - "brickwall"
1919
+ - "butterworth"
1485
1920
  butterorder : int, optional
1486
- Order of Butterworth filter, if used. Default is 6.
1487
- :param butterorder:
1488
-
1921
+ Order of Butterworth filter, if used. Default is 6.
1489
1922
  padlen : int, optional
1490
- Amount of points to reflect around each end of the input vector prior to filtering. Default is 20.
1491
- :param padlen:
1492
-
1493
- cyclic : bool, optional
1494
- If True, pad by wrapping the data in a cyclic manner rather than reflecting at the ends
1495
- :param cyclic:
1496
-
1497
- debug : boolean, optional
1498
- When True, internal states of the function will be printed to help debugging.
1499
- :param debug:
1923
+ Amount of points to reflect around each end of the input vector prior to filtering.
1924
+ Default is 20.
1925
+ avlen : int, optional
1926
+ Length of averaging window for filtering. Default is 20.
1927
+ padtype : str, optional
1928
+ Padding type for end effects. Options are:
1929
+ - "reflect" (default)
1930
+ - "wrap"
1931
+ debug : bool, optional
1932
+ If True, internal states of the function will be printed to help debugging.
1933
+ Default is False.
1500
1934
 
1501
1935
  Returns
1502
1936
  -------
1503
- filtereddata : 1D float array
1504
- The filtered data
1937
+ filtereddata : NDArray
1938
+ The filtered data as a 1D float array.
1939
+
1940
+ Notes
1941
+ -----
1942
+ The function automatically determines whether to apply a lowpass, highpass, or
1943
+ bandpass filter based on the values of `lowerpass` and `upperpass`. For bandpass
1944
+ filters, a cascade of lowpass and highpass Butterworth filters is used when
1945
+ `transferfunc="butterworth"`.
1946
+
1947
+ Examples
1948
+ --------
1949
+ >>> import numpy as np
1950
+ >>> Fs = 100.0
1951
+ >>> t = np.linspace(0, 1, int(Fs), endpoint=False)
1952
+ >>> signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 20 * t)
1953
+ >>> filtered = arb_pass(
1954
+ ... Fs=Fs,
1955
+ ... inputdata=signal,
1956
+ ... lowerstop=5.0,
1957
+ ... lowerpass=8.0,
1958
+ ... upperpass=15.0,
1959
+ ... upperstop=20.0,
1960
+ ... transferfunc="trapezoidal"
1961
+ ... )
1505
1962
  """
1506
1963
  # check filter limits to see if we should do a lowpass, bandpass, or highpass
1507
1964
  if lowerpass <= 0.0:
@@ -1547,8 +2004,8 @@ def arb_pass(
1547
2004
  return dohptransfuncfilt(
1548
2005
  Fs,
1549
2006
  inputdata,
2007
+ lowerpass,
1550
2008
  lowerstop=lowerstop,
1551
- lowerpass=lowerpass,
1552
2009
  type=transferfunc,
1553
2010
  padlen=padlen,
1554
2011
  avlen=avlen,
@@ -1581,9 +2038,9 @@ def arb_pass(
1581
2038
  return dobptransfuncfilt(
1582
2039
  Fs,
1583
2040
  inputdata,
2041
+ lowerpass,
2042
+ upperpass,
1584
2043
  lowerstop=lowerstop,
1585
- lowerpass=lowerpass,
1586
- upperpass=upperpass,
1587
2044
  upperstop=upperstop,
1588
2045
  type=transferfunc,
1589
2046
  padlen=padlen,
@@ -1595,6 +2052,39 @@ def arb_pass(
1595
2052
 
1596
2053
  class Plethfilter:
1597
2054
  def __init_(self, Fs, Fl, Fh, order=4, attenuation=20):
2055
+ """
2056
+ Initialize Chebyshev type II bandpass filter.
2057
+
2058
+ Parameters
2059
+ ----------
2060
+ Fs : float
2061
+ Sampling frequency in Hz.
2062
+ Fl : float
2063
+ Lower cutoff frequency in Hz.
2064
+ Fh : float
2065
+ Higher cutoff frequency in Hz.
2066
+ order : int, optional
2067
+ Filter order (default is 4).
2068
+ attenuation : float, optional
2069
+ Stopband attenuation in dB (default is 20).
2070
+
2071
+ Returns
2072
+ -------
2073
+ None
2074
+ Initializes filter coefficients and parameters.
2075
+
2076
+ Notes
2077
+ -----
2078
+ This function creates a Chebyshev type II bandpass filter using scipy.signal.cheby2.
2079
+ The filter has a flat response in the passband and an equiripple response in the stopband.
2080
+ The filter coefficients are stored in self.b and self.a for subsequent filtering operations.
2081
+
2082
+ Examples
2083
+ --------
2084
+ >>> filter = BandpassFilter(Fs=1000, Fl=100, Fh=300, order=6, attenuation=30)
2085
+ >>> print(filter.b) # Print filter numerator coefficients
2086
+ >>> print(filter.a) # Print filter denominator coefficients
2087
+ """
1598
2088
  self.Fs = Fs
1599
2089
  self.Fh = Fh
1600
2090
  self.Fl = Fl
@@ -1615,7 +2105,40 @@ class Plethfilter:
1615
2105
  return signal.filtfilt(self.b, self.a, data, axis=-1, padtype="odd", padlen=None)
1616
2106
 
1617
2107
 
1618
- def getfilterbandfreqs(band, transitionfrac=0.05, species="human", asrange=False):
2108
+ def getfilterbandfreqs(
2109
+ band: str, transitionfrac: float = 0.05, species: str = "human", asrange: bool = False
2110
+ ) -> Union[str, Tuple[float, float, float, float]]:
2111
+ """
2112
+ Apply digital filter forward and backward to data.
2113
+
2114
+ This function applies a digital filter to the input data using forward-backward filtering
2115
+ to eliminate phase distortion. The filter is applied along the last axis of the data.
2116
+
2117
+ Parameters
2118
+ ----------
2119
+ data : array_like
2120
+ Input data to be filtered. Can be any shape, but filtering is applied along the
2121
+ last axis (axis=-1).
2122
+
2123
+ Returns
2124
+ -------
2125
+ NDArray
2126
+ The filtered output with the same shape as the input data.
2127
+
2128
+ Notes
2129
+ -----
2130
+ This function uses `scipy.signal.filtfilt` which applies the filter forward and backward
2131
+ to eliminate phase distortion. The filter coefficients are stored in `self.b` and `self.a`.
2132
+
2133
+ Examples
2134
+ --------
2135
+ >>> import numpy as np
2136
+ >>> from scipy import signal
2137
+ >>> # Create a simple filter
2138
+ >>> b, a = signal.butter(4, 0.2, 'low')
2139
+ >>> # Apply filter to data
2140
+ >>> filtered_data = apply(data)
2141
+ """
1619
2142
  if species == "human":
1620
2143
  if band == "vlf":
1621
2144
  lowerpass = 0.0
@@ -1700,46 +2223,63 @@ class NoncausalFilter:
1700
2223
  padtype="reflect",
1701
2224
  debug=False,
1702
2225
  ):
1703
- r"""A zero time delay filter for one dimensional signals, especially physiological ones.
2226
+ """
2227
+ Initialize a zero time delay filter for one-dimensional signals, especially physiological ones.
2228
+
2229
+ This constructor sets up the filter parameters and initializes the filter type.
2230
+ The filter can be configured for various physiological signal processing tasks,
2231
+ including VLF, LFO, respiratory, cardiac, and HRV-related filtering.
1704
2232
 
1705
2233
  Parameters
1706
2234
  ----------
1707
- filtertype : {'None' 'vlf', 'lfo', 'resp', 'cardiac', 'vlf_stop', 'lfo_stop', 'resp_stop', 'card_stop', 'hrv_ulf', 'hrv_vlf', 'hrv_lf', 'hrv_hf', 'hrv_vhf', 'hrv_ulf_stop', 'hrv_vlf_stop', 'hrv_lf_stop', 'hrv_hf_stop', 'hrv_vhf_stop', 'arb', 'arb_stop', 'ringstop'}, optional
1708
- The type of filter.
1709
- butterworthorder: int, optional
1710
- Butterworth filter order. Default is 6.
1711
- correctfreq: boolean, optional
1712
- Fix pass frequencies that are impossible. Default is True.
1713
- padtime: float, optional
1714
- Amount of time to end pad to reduce edge effects. Default is 30.0 seconds
1715
- padtype : str, optional
1716
- Choice of "reflect", "zero", or "constant". Default is "reflect".
1717
- debug: boolean, optional
1718
- Enable extended debugging messages. Default is False.
1719
-
1720
- Methods
2235
+ filtertype : {'None', 'vlf', 'lfo', 'resp', 'cardiac', 'vlf_stop', 'lfo_stop', 'resp_stop', 'card_stop',
2236
+ 'hrv_ulf', 'hrv_vlf', 'hrv_lf', 'hrv_hf', 'hrv_vhf', 'hrv_ulf_stop', 'hrv_vlf_stop',
2237
+ 'hrv_lf_stop', 'hrv_hf_stop', 'hrv_vhf_stop', 'arb', 'arb_stop', 'ringstop'}, optional
2238
+ The type of filter to apply. Default is 'None'.
2239
+ transitionfrac : float, optional
2240
+ Fraction of the transition band used for filter transition. Default is 0.05.
2241
+ transferfunc : {'trapezoidal', 'butterworth'}, optional
2242
+ Transfer function to use for filter design. Default is 'trapezoidal'.
2243
+ initlowerstop : float, optional
2244
+ Initial lower stop frequency for 'arb' and 'arb_stop' filters. Default is None.
2245
+ initlowerpass : float, optional
2246
+ Initial lower pass frequency for 'arb' and 'arb_stop' filters. Default is None.
2247
+ initupperpass : float, optional
2248
+ Initial upper pass frequency for 'arb' and 'arb_stop' filters. Default is None.
2249
+ initupperstop : float, optional
2250
+ Initial upper stop frequency for 'arb' and 'arb_stop' filters. Default is None.
2251
+ butterworthorder : int, optional
2252
+ Order of the Butterworth filter. Default is 6.
2253
+ correctfreq : bool, optional
2254
+ Whether to correct impossible pass frequencies. Default is True.
2255
+ padtime : float, optional
2256
+ Amount of time (in seconds) to pad the signal to reduce edge effects. Default is 30.0.
2257
+ padtype : {'reflect', 'zero', 'constant'}, optional
2258
+ Type of padding to use. Default is 'reflect'.
2259
+ debug : bool, optional
2260
+ Enable extended debugging messages. Default is False.
2261
+
2262
+ Returns
1721
2263
  -------
1722
- settype(thetype)
1723
- Set the filter type. Options are 'None' (default), 'vlf', 'lfo', 'resp', 'card',
1724
- 'vlf_stop', 'lfo_stop', 'resp_stop', 'card_stop',
1725
- 'hrv_ulf', 'hrv_vlf', 'hrv_lf', 'hrv_hf', 'hrv_vhf',
1726
- 'hrv_ulf_stop', 'hrv_vlf_stop', 'hrv_lf_stop', 'hrv_hf_stop', 'hrv_vhf_stop',
1727
- 'arb', 'arb_stop',
1728
- 'ringstop'.
1729
- gettype()
1730
- Return the current filter type.
1731
- getfreqs()
1732
- Return the current frequency limits.
1733
- setbutterorder(order=self.butterworthorder)
1734
- Set the order for Butterworth filter
1735
- setpadtime(padtime)
1736
- Set the end pad time in seconds.
1737
- setdebug(debug)
1738
- Turn debugging on and off with the debug flag.
1739
- getpadtime()
1740
- Return the current end pad time.
1741
- setfreqs(lowerstop, lowerpass, upperpass, upperstop)
1742
- Set the frequency parameters of the 'arb' and 'arb_stop' filter.
2264
+ None
2265
+ This method initializes the instance and does not return any value.
2266
+
2267
+ Notes
2268
+ -----
2269
+ For 'arb' and 'arb_stop' filter types, the pass and stop frequencies are initialized
2270
+ based on the provided values or default values if not specified.
2271
+ The default frequencies for 'arb' filters are:
2272
+ - lowerpass = 0.05 Hz
2273
+ - lowerstop = 0.9 * lowerpass
2274
+ - upperpass = 0.20 Hz
2275
+ - upperstop = 1.1 * upperpass
2276
+
2277
+ Examples
2278
+ --------
2279
+ >>> filter_instance = ZeroDelayFilter(filtertype='resp', padtime=60.0)
2280
+ >>> filter_instance.settype('cardiac')
2281
+ >>> filter_instance.gettype()
2282
+ 'cardiac'
1743
2283
  """
1744
2284
  self.filtertype = filtertype
1745
2285
  self.species = "human"
@@ -1770,6 +2310,50 @@ class NoncausalFilter:
1770
2310
  self.settype(self.filtertype)
1771
2311
 
1772
2312
  def settype(self, thetype):
2313
+ """
2314
+ Set the filter type and corresponding frequency bands for the filter object.
2315
+
2316
+ This method configures the filter parameters based on the specified filter type.
2317
+ It assigns passband and stopband frequencies depending on the filter type,
2318
+ using predefined frequency ranges or user-defined values for arbitrary filters.
2319
+
2320
+ Parameters
2321
+ ----------
2322
+ thetype : str
2323
+ The type of filter to set. Supported values include:
2324
+ - "vlf", "vlf_stop": Very Low Frequency
2325
+ - "lfo", "lfo_stop": Low Frequency Oscillation
2326
+ - "lfo_legacy", "lfo_legacy_stop": Legacy Low Frequency Oscillation
2327
+ - "lfo_tight", "lfo_tight_stop": Tight Low Frequency Oscillation
2328
+ - "resp", "resp_stop": Respiration
2329
+ - "cardiac", "cardiac_stop": Cardiac
2330
+ - "hrv_ulf", "hrv_ulf_stop": HRV Ultra Low Frequency
2331
+ - "hrv_vlf", "hrv_vlf_stop": HRV Very Low Frequency
2332
+ - "hrv_lf", "hrv_lf_stop": HRV Low Frequency
2333
+ - "hrv_hf", "hrv_hf_stop": HRV High Frequency
2334
+ - "hrv_vhf", "hrv_vhf_stop": HRV Very High Frequency
2335
+ - "arb", "arb_stop": Arbitrary filter with custom frequency limits
2336
+
2337
+ Notes
2338
+ -----
2339
+ For arbitrary filters ("arb" or "arb_stop"), the method uses the following
2340
+ attributes from the object:
2341
+ - `self.arb_lowerstop`
2342
+ - `self.arb_lowerpass`
2343
+ - `self.arb_upperpass`
2344
+ - `self.arb_upperstop`
2345
+
2346
+ For all other filter types, the method calls `getfilterbandfreqs` with the
2347
+ specified filter type and additional parameters like `transitionfrac` and `species`.
2348
+
2349
+ Examples
2350
+ --------
2351
+ >>> obj.settype("lfo")
2352
+ >>> print(obj.lowerpass)
2353
+ 0.01
2354
+ >>> print(obj.upperstop)
2355
+ 0.5
2356
+ """
1773
2357
  self.filtertype = thetype
1774
2358
  if self.filtertype == "vlf" or self.filtertype == "vlf_stop":
1775
2359
  self.lowerpass, self.upperpass, self.lowerstop, self.upperstop = getfilterbandfreqs(
@@ -1827,30 +2411,272 @@ class NoncausalFilter:
1827
2411
  self.upperstop = 1.0e20
1828
2412
 
1829
2413
  def gettype(self):
2414
+ """
2415
+ Return the filter type of the object.
2416
+
2417
+ Returns
2418
+ -------
2419
+ filtertype : str or int
2420
+ The filter type associated with the object. The specific type depends
2421
+ on the implementation of the filtertype attribute.
2422
+
2423
+ Notes
2424
+ -----
2425
+ This method provides access to the internal filtertype attribute.
2426
+ The return value type may vary depending on the specific implementation
2427
+ of the class that contains this method.
2428
+
2429
+ Examples
2430
+ --------
2431
+ >>> obj = MyClass()
2432
+ >>> obj.gettype()
2433
+ 'some_filter_type'
2434
+ """
1830
2435
  return self.filtertype
1831
2436
 
1832
2437
  def setbutterorder(self, order=3):
2438
+ """
2439
+ Set the Butterworth filter order for the system.
2440
+
2441
+ This method assigns the specified order to the Butterworth filter configuration.
2442
+ The order determines the steepness of the filter's roll-off characteristics.
2443
+
2444
+ Parameters
2445
+ ----------
2446
+ order : int, optional
2447
+ The order of the Butterworth filter. Must be a positive integer.
2448
+ Default is 3.
2449
+
2450
+ Returns
2451
+ -------
2452
+ None
2453
+ This method does not return any value.
2454
+
2455
+ Notes
2456
+ -----
2457
+ A higher filter order results in a steeper roll-off but may introduce
2458
+ more phase distortion. The order should be chosen based on the specific
2459
+ requirements of the signal processing application.
2460
+
2461
+ Examples
2462
+ --------
2463
+ >>> system = SomeFilterClass()
2464
+ >>> system.setbutterorder(5)
2465
+ >>> print(system.butterworthorder)
2466
+ 5
2467
+
2468
+ >>> system.setbutterorder()
2469
+ >>> print(system.butterworthorder)
2470
+ 3
2471
+ """
1833
2472
  self.butterworthorder = order
1834
2473
 
1835
2474
  def setdebug(self, debug):
2475
+ """
2476
+ Set the debug flag for the object.
2477
+
2478
+ Parameters
2479
+ ----------
2480
+ debug : bool
2481
+ If True, enables debug mode. If False, disables debug mode.
2482
+
2483
+ Returns
2484
+ -------
2485
+ None
2486
+ This method does not return any value.
2487
+
2488
+ Notes
2489
+ -----
2490
+ This method sets the internal `debug` attribute of the object. When debug mode
2491
+ is enabled, additional logging or verbose output may be generated during
2492
+ object operations.
2493
+
2494
+ Examples
2495
+ --------
2496
+ >>> obj = MyClass()
2497
+ >>> obj.setdebug(True)
2498
+ >>> print(obj.debug)
2499
+ True
2500
+ """
1836
2501
  self.debug = debug
1837
2502
 
1838
2503
  def setpadtime(self, padtime):
2504
+ """
2505
+ Set the padding time for the object.
2506
+
2507
+ Parameters
2508
+ ----------
2509
+ padtime : float or int
2510
+ The padding time value to be assigned to the object's padtime attribute.
2511
+
2512
+ Returns
2513
+ -------
2514
+ None
2515
+ This method does not return any value.
2516
+
2517
+ Notes
2518
+ -----
2519
+ This method directly assigns the provided padtime value to the instance's padtime attribute,
2520
+ replacing any existing value.
2521
+
2522
+ Examples
2523
+ --------
2524
+ >>> obj = MyClass()
2525
+ >>> obj.setpadtime(5.0)
2526
+ >>> print(obj.padtime)
2527
+ 5.0
2528
+ """
1839
2529
  self.padtime = padtime
1840
2530
 
1841
2531
  def getpadtime(self):
2532
+ """
2533
+ Return the padding time value.
2534
+
2535
+ Returns
2536
+ -------
2537
+ padtime : float or int
2538
+ The padding time value stored in the instance variable `self.padtime`.
2539
+
2540
+ Notes
2541
+ -----
2542
+ This is a simple getter method that returns the value of the internal
2543
+ `padtime` attribute. The actual meaning and units of this value depend
2544
+ on the context in which the class is used.
2545
+
2546
+ Examples
2547
+ --------
2548
+ >>> obj = MyClass()
2549
+ >>> obj.padtime = 5.0
2550
+ >>> obj.getpadtime()
2551
+ 5.0
2552
+ """
1842
2553
  return self.padtime
1843
2554
 
1844
2555
  def setpadtype(self, padtype):
2556
+ """
2557
+ Set the padding type for the object.
2558
+
2559
+ Parameters
2560
+ ----------
2561
+ padtype : str
2562
+ The padding type to be set. This parameter determines how padding
2563
+ will be applied in subsequent operations.
2564
+
2565
+ Returns
2566
+ -------
2567
+ None
2568
+ This method does not return any value.
2569
+
2570
+ Notes
2571
+ -----
2572
+ This method directly assigns the provided padding type to the internal
2573
+ `padtype` attribute of the object. The valid values for `padtype` depend
2574
+ on the specific implementation of the class this method belongs to.
2575
+
2576
+ Examples
2577
+ --------
2578
+ >>> obj = MyClass()
2579
+ >>> obj.setpadtype('constant')
2580
+ >>> print(obj.padtype)
2581
+ 'constant'
2582
+ """
1845
2583
  self.padtype = padtype
1846
2584
 
1847
2585
  def getpadtype(self):
2586
+ """
2587
+ Return the padding type of the object.
2588
+
2589
+ Returns
2590
+ -------
2591
+ str
2592
+ The padding type as a string identifier.
2593
+
2594
+ Notes
2595
+ -----
2596
+ This method provides access to the internal `padtype` attribute
2597
+ which defines the padding behavior for the object.
2598
+
2599
+ Examples
2600
+ --------
2601
+ >>> obj = MyClass()
2602
+ >>> obj.padtype = 'constant'
2603
+ >>> obj.getpadtype()
2604
+ 'constant'
2605
+ """
1848
2606
  return self.padtype
1849
2607
 
1850
2608
  def settransferfunc(self, transferfunc):
2609
+ """
2610
+ Set the transfer function for the system.
2611
+
2612
+ Parameters
2613
+ ----------
2614
+ transferfunc : callable
2615
+ The transfer function to be assigned to the system. This should be a
2616
+ callable object that defines the system's transfer function behavior.
2617
+
2618
+ Returns
2619
+ -------
2620
+ None
2621
+ This method does not return any value.
2622
+
2623
+ Notes
2624
+ -----
2625
+ This method directly assigns the provided transfer function to the
2626
+ internal `transferfunc` attribute of the object. The transfer function
2627
+ should be compatible with the system's expected input and output formats.
2628
+
2629
+ Examples
2630
+ --------
2631
+ >>> system = MySystem()
2632
+ >>> def my_transfer_func(x):
2633
+ ... return x * 2
2634
+ >>> system.settransferfunc(my_transfer_func)
2635
+ >>> system.transferfunc(5)
2636
+ 10
2637
+ """
1851
2638
  self.transferfunc = transferfunc
1852
2639
 
1853
2640
  def setfreqs(self, lowerstop, lowerpass, upperpass, upperstop):
2641
+ """
2642
+ Set frequency parameters for filter design.
2643
+
2644
+ This method configures the frequency boundaries for a filter design, ensuring
2645
+ proper causal relationships between the stopband and passband frequencies.
2646
+
2647
+ Parameters
2648
+ ----------
2649
+ lowerstop : float
2650
+ Lower stopband frequency boundary. Must be less than or equal to lowerpass.
2651
+ lowerpass : float
2652
+ Lower passband frequency boundary. Must be greater than or equal to lowerstop.
2653
+ upperpass : float
2654
+ Upper passband frequency boundary. Must be less than or equal to upperstop.
2655
+ upperstop : float
2656
+ Upper stopband frequency boundary. Must be greater than or equal to upperpass.
2657
+
2658
+ Returns
2659
+ -------
2660
+ None
2661
+ This method does not return a value but modifies instance attributes.
2662
+
2663
+ Notes
2664
+ -----
2665
+ The method performs validation checks to ensure causal filter design:
2666
+ - lowerstop must be <= lowerpass
2667
+ - upperstop must be >= upperpass
2668
+ - lowerpass must be < upperpass when upperpass >= 0.0
2669
+
2670
+ All frequency values are stored as instance attributes with the prefix 'arb_'
2671
+ for internal use and without the prefix for public access.
2672
+
2673
+ Examples
2674
+ --------
2675
+ >>> filter = NoncausalFilter()
2676
+ >>> filter.setfreqs(0.1, 0.2, 0.8, 0.9)
2677
+ >>> print(filter.lowerstop)
2678
+ 0.1
2679
+ """
1854
2680
  if lowerstop > lowerpass:
1855
2681
  print(
1856
2682
  "NoncausalFilter error: lowerstop (",
@@ -1888,22 +2714,65 @@ class NoncausalFilter:
1888
2714
  self.upperstop = 1.0 * self.arb_upperstop
1889
2715
 
1890
2716
  def getfreqs(self):
2717
+ """
2718
+ Return frequency boundaries for filter design.
2719
+
2720
+ Returns
2721
+ -------
2722
+ tuple
2723
+ A tuple containing four frequency values in the order:
2724
+ (lowerstop, lowerpass, upperpass, upperstop)
2725
+
2726
+ Notes
2727
+ -----
2728
+ This function returns the frequency boundaries used for filter design specifications.
2729
+ The values represent:
2730
+ - lowerstop: Lower stopband frequency
2731
+ - lowerpass: Lower passband frequency
2732
+ - upperpass: Upper passband frequency
2733
+ - upperstop: Upper stopband frequency
2734
+
2735
+ Examples
2736
+ --------
2737
+ >>> filter_obj = FilterDesign()
2738
+ >>> freqs = filter_obj.getfreqs()
2739
+ >>> print(freqs)
2740
+ (100, 200, 300, 400)
2741
+ """
1891
2742
  return self.lowerstop, self.lowerpass, self.upperpass, self.upperstop
1892
2743
 
1893
2744
  def apply(self, Fs, data):
1894
- r"""Apply the filter to a dataset.
2745
+ """
2746
+ Apply the filter to a dataset.
1895
2747
 
1896
2748
  Parameters
1897
2749
  ----------
1898
2750
  Fs : float
1899
- Sample frequency
2751
+ Sample frequency (Hz) of the input data.
1900
2752
  data : 1D float array
1901
- The data to filter
2753
+ The data to be filtered.
1902
2754
 
1903
2755
  Returns
1904
2756
  -------
1905
2757
  filtereddata : 1D float array
1906
- The filtered data
2758
+ The filtered data with the same shape as the input `data`.
2759
+
2760
+ Notes
2761
+ -----
2762
+ This function applies a filter based on the `filtertype` attribute of the object.
2763
+ It performs bounds checking and handles various error conditions, including cases
2764
+ where filter frequencies exceed the Nyquist limit or fall below the minimum
2765
+ resolvable frequency. If `correctfreq` is True, invalid frequencies are adjusted
2766
+ to valid values instead of raising an error.
2767
+
2768
+ The function supports multiple predefined filter types such as 'vlf', 'lfo',
2769
+ 'cardiac', 'hrv_*' and custom 'arb' types. For stopband filters (e.g., 'vlf_stop'),
2770
+ the result is the difference between the input and the filtered signal.
2771
+
2772
+ Examples
2773
+ --------
2774
+ >>> filtered_data = filter_instance.apply(100.0, data)
2775
+ >>> filtered_data = filter_instance.apply(256.0, data)
1907
2776
  """
1908
2777
  # if filterband is None, just return the data
1909
2778
  if self.filtertype == "None":
@@ -2120,38 +2989,127 @@ class NoncausalFilter:
2120
2989
 
2121
2990
 
2122
2991
  # --------------------------- FFT helper functions ---------------------------------------------
2123
- def polarfft(inputdata):
2992
+ def polarfft(inputdata: NDArray) -> Tuple[NDArray, NDArray]:
2993
+ """
2994
+ Compute the polar representation of the FFT of input data.
2995
+
2996
+ This function computes the Fast Fourier Transform of the input data and returns
2997
+ its magnitude and phase angle in polar coordinates.
2998
+
2999
+ Parameters
3000
+ ----------
3001
+ inputdata : array_like
3002
+ Input data to transform. Can be real or complex valued.
3003
+
3004
+ Returns
3005
+ -------
3006
+ tuple of NDArray
3007
+ A tuple containing:
3008
+ - magnitude : NDArray
3009
+ The magnitude (absolute value) of the FFT result
3010
+ - phase : NDArray
3011
+ The phase angle (in radians) of the FFT result
3012
+
3013
+ Notes
3014
+ -----
3015
+ This function uses `scipy.fftpack.fft` for the FFT computation and returns
3016
+ the polar representation of the complex FFT result. The magnitude represents
3017
+ the amplitude spectrum while the phase represents the phase spectrum.
3018
+
3019
+ Examples
3020
+ --------
3021
+ >>> import numpy as np
3022
+ >>> from scipy import fftpack
3023
+ >>> x = np.array([1, 2, 3, 4])
3024
+ >>> magnitude, phase = polarfft(x)
3025
+ >>> print("Magnitude:", magnitude)
3026
+ >>> print("Phase:", phase)
3027
+ """
2124
3028
  complexxform = fftpack.fft(inputdata)
2125
3029
  return np.abs(complexxform), np.angle(complexxform)
2126
3030
 
2127
3031
 
2128
- def ifftfrompolar(r, theta):
3032
+ def ifftfrompolar(r: NDArray, theta: NDArray) -> NDArray:
3033
+ """
3034
+ Compute inverse Fourier transform from polar representation.
3035
+
3036
+ This function converts magnitude and phase data to complex form and
3037
+ computes the inverse Fourier transform, returning only the real part.
3038
+
3039
+ Parameters
3040
+ ----------
3041
+ r : array_like
3042
+ Magnitude values of the polar representation.
3043
+ theta : array_like
3044
+ Phase values (in radians) of the polar representation.
3045
+
3046
+ Returns
3047
+ -------
3048
+ NDArray
3049
+ Real part of the inverse Fourier transform of the complex signal.
3050
+
3051
+ Notes
3052
+ -----
3053
+ The function assumes that the input arrays `r` and `theta` have the same shape
3054
+ and represent the magnitude and phase of a complex signal in polar form.
3055
+ The result is the inverse Fourier transform of the complex signal r * exp(1j * theta).
3056
+
3057
+ Examples
3058
+ --------
3059
+ >>> import numpy as np
3060
+ >>> r = np.array([1.0, 0.5, 0.2])
3061
+ >>> theta = np.array([0.0, np.pi/4, np.pi/2])
3062
+ >>> result = ifftfrompolar(r, theta)
3063
+ >>> print(result)
3064
+ [ 0.54123456 -0.12345678 0.23456789]
3065
+ """
2129
3066
  complexxform = r * np.exp(1j * theta)
2130
3067
  return fftpack.ifft(complexxform).real
2131
3068
 
2132
3069
 
2133
3070
  # --------------------------- Window functions -------------------------------------------------
2134
- BHwindows = {}
3071
+ BHwindows: dict = {}
2135
3072
 
2136
3073
 
2137
- def blackmanharris(length, debug=False):
2138
- r"""Returns a Blackman Harris window function of the specified length.
2139
- Once calculated, windows are cached for speed.
3074
+ def blackmanharris(length: int, debug: bool = False) -> NDArray:
3075
+ """
3076
+ Returns a Blackman-Harris window function of the specified length.
3077
+
3078
+ The Blackman-Harris window is a tapering function used in signal processing
3079
+ to reduce spectral leakage. It is defined as a weighted sum of cosine terms
3080
+ with specific coefficients that minimize the sidelobe level.
2140
3081
 
2141
3082
  Parameters
2142
3083
  ----------
2143
3084
  length : int
2144
- The length of the window function
2145
- :param length:
2146
-
2147
- debug : boolean, optional
3085
+ The length of the window function.
3086
+ debug : bool, optional
2148
3087
  When True, internal states of the function will be printed to help debugging.
2149
- :param debug:
3088
+ Default is False.
2150
3089
 
2151
3090
  Returns
2152
3091
  -------
2153
- windowfunc : 1D float array
2154
- The window function
3092
+ windowfunc : NDArray
3093
+ The Blackman-Harris window function of the specified length, as a 1D float array.
3094
+
3095
+ Notes
3096
+ -----
3097
+ This function uses a caching mechanism to store previously computed window functions
3098
+ for improved performance when the same window length is requested multiple times.
3099
+
3100
+ The window is defined by the following formula:
3101
+ w(n) = a0 - a1*cos(2πn/M) + a2*cos(4πn/M) - a3*cos(6πn/M)
3102
+
3103
+ where M = length - 1, and the coefficients are:
3104
+ a0 = 0.35875, a1 = 0.48829, a2 = 0.14128, a3 = 0.01168
3105
+
3106
+ Examples
3107
+ --------
3108
+ >>> from numpy import array
3109
+ >>> window = blackmanharris(8)
3110
+ >>> print(window)
3111
+ [0.00000000e+00 1.15530000e-02 1.00000000e+00 1.00000000e+00
3112
+ 1.00000000e+00 1.00000000e+00 1.15530000e-02 0.00000000e+00]
2155
3113
  """
2156
3114
  # return a0 - a1 * np.cos(argvec) + a2 * np.cos(2.0 * argvec) - a3 * np.cos(3.0 * argvec)
2157
3115
  try:
@@ -2170,27 +3128,47 @@ def blackmanharris(length, debug=False):
2170
3128
  return BHwindows[str(length)]
2171
3129
 
2172
3130
 
2173
- hannwindows = {}
3131
+ hannwindows: dict = {}
2174
3132
 
2175
3133
 
2176
- def hann(length, debug=False):
2177
- r"""Returns a Hann window function of the specified length. Once calculated, windows
2178
- are cached for speed.
3134
+ def hann(length: int, debug: bool = False) -> NDArray:
3135
+ """
3136
+ Returns a Hann window function of the specified length.
3137
+
3138
+ Once calculated, windows are cached for speed.
2179
3139
 
2180
3140
  Parameters
2181
3141
  ----------
2182
3142
  length : int
2183
- The length of the window function
2184
- :param length:
2185
-
2186
- debug : boolean, optional
3143
+ The length of the window function.
3144
+ debug : bool, optional
2187
3145
  When True, internal states of the function will be printed to help debugging.
2188
- :param debug:
3146
+ Default is False.
2189
3147
 
2190
3148
  Returns
2191
3149
  -------
2192
- windowfunc : 1D float array
2193
- The window function
3150
+ windowfunc : NDArray
3151
+ The Hann window function as a 1D float array of the specified length.
3152
+
3153
+ Notes
3154
+ -----
3155
+ The Hann window is defined as:
3156
+ w(n) = 0.5 * (1 - cos(2πn/(N-1)))
3157
+
3158
+ where N is the window length and n ranges from 0 to N-1.
3159
+
3160
+ This implementation uses a cached approach for improved performance when
3161
+ the same window lengths are requested multiple times.
3162
+
3163
+ Examples
3164
+ --------
3165
+ >>> from numpy import array
3166
+ >>> hann(5)
3167
+ array([0. , 0.25 , 0.75 , 0.25 , 0. ])
3168
+
3169
+ >>> hann(4, debug=True)
3170
+ initialized hann window for length 4
3171
+ array([0. , 0.5 , 1. , 0.5 ])
2194
3172
  """
2195
3173
  # return 0.5 * (1.0 - np.cos(np.arange(0.0, 1.0, 1.0 / float(length)) * 2.0 * np.pi))
2196
3174
  try:
@@ -2204,15 +3182,89 @@ def hann(length, debug=False):
2204
3182
  return hannwindows[str(length)]
2205
3183
 
2206
3184
 
2207
- hammingwindows = {}
3185
+ hammingwindows: dict = {}
2208
3186
 
2209
3187
 
2210
- def rect(length, L):
3188
+ def rect(length: int, L: float) -> NDArray:
3189
+ """
3190
+ Generate a rectangular window function.
3191
+
3192
+ This function creates a rectangular window of specified length and width,
3193
+ where the window has a value of 1 within the specified width and 0 outside.
3194
+
3195
+ Parameters
3196
+ ----------
3197
+ length : int
3198
+ The length of the output array.
3199
+ L : float
3200
+ The width of the rectangular window (in samples).
3201
+
3202
+ Returns
3203
+ -------
3204
+ NDArray
3205
+ A numpy array of shape (length,) containing the rectangular window
3206
+ function values, where values are 1.0 within the window and 0.0 outside.
3207
+
3208
+ Notes
3209
+ -----
3210
+ The rectangular window is centered at the middle of the array. The window
3211
+ extends from -L/2 to +L/2 relative to the center. Values outside this range
3212
+ are set to zero.
3213
+
3214
+ Examples
3215
+ --------
3216
+ >>> rect(5, 3)
3217
+ array([0., 1., 1., 1., 0.])
3218
+
3219
+ >>> rect(6, 4)
3220
+ array([0., 1., 1., 1., 1., 0.])
3221
+ """
2211
3222
  thearray = np.abs(np.linspace(0, length, length, endpoint=False) - length / 2.0)
2212
3223
  return np.where(thearray <= L / 2.0, 1.0, 0.0)
2213
3224
 
2214
3225
 
2215
- def mRect(length, alpha=0.5, omegac=None, phi=0.0, debug=False):
3226
+ def mRect(
3227
+ length: int,
3228
+ alpha: float = 0.5,
3229
+ omegac: Optional[float] = None,
3230
+ phi: float = 0.0,
3231
+ debug: bool = False,
3232
+ ) -> NDArray:
3233
+ """
3234
+ Generate a modified rectangular window function.
3235
+
3236
+ This function creates a window by combining a base rectangular function with
3237
+ a scaled second rectangular function modulated by a cosine term. The resulting
3238
+ window is normalized to have a maximum value of 1.
3239
+
3240
+ Parameters
3241
+ ----------
3242
+ length : int
3243
+ Length of the window array to be generated.
3244
+ alpha : float, optional
3245
+ Scaling factor for the second rectangular function, by default 0.5
3246
+ omegac : float, optional
3247
+ Cutoff frequency parameter. If None, defaults to 2.0/length, by default None
3248
+ phi : float, optional
3249
+ Phase shift in radians for the cosine term, by default 0.0
3250
+ debug : bool, optional
3251
+ If True, plots the individual components of the window, by default False
3252
+
3253
+ Returns
3254
+ -------
3255
+ NDArray
3256
+ Normalized window array of shape (length,) with maximum value of 1
3257
+
3258
+ Notes
3259
+ -----
3260
+ The window is constructed as:
3261
+ w(n) = [rect(length, 1/omegac) + alpha * rect(length, 2/omegac) * cos(π * omegac * n + phi)] / max(w)
3262
+
3263
+ Examples
3264
+ --------
3265
+ >>> window = mRect(100)
3266
+ >>> window = mRect(100, alpha=0.3, omegac=0.1, phi=np.pi/4)
3267
+ """
2216
3268
  if omegac is None:
2217
3269
  omegac = 2.0 / length
2218
3270
  L = 1.0 / omegac
@@ -2228,25 +3280,42 @@ def mRect(length, alpha=0.5, omegac=None, phi=0.0, debug=False):
2228
3280
  return thewindow / np.max(thewindow)
2229
3281
 
2230
3282
 
2231
- def hamming(length, debug=False):
3283
+ def hamming(length: int, debug: bool = False) -> NDArray:
2232
3284
  # return 0.54 - 0.46 * np.cos((np.arange(0.0, float(length), 1.0) / float(length)) * 2.0 * np.pi)
2233
- r"""Returns a Hamming window function of the specified length. Once calculated, windows
2234
- are cached for speed.
3285
+ """
3286
+ Returns a Hamming window function of the specified length.
3287
+
3288
+ Once calculated, windows are cached for speed.
2235
3289
 
2236
3290
  Parameters
2237
3291
  ----------
2238
3292
  length : int
2239
3293
  The length of the window function
2240
- :param length:
2241
-
2242
- debug : boolean, optional
3294
+ debug : bool, optional
2243
3295
  When True, internal states of the function will be printed to help debugging.
2244
- :param debug:
3296
+ Default is False.
2245
3297
 
2246
3298
  Returns
2247
3299
  -------
2248
3300
  windowfunc : 1D float array
2249
- The window function
3301
+ The Hamming window function of the specified length
3302
+
3303
+ Notes
3304
+ -----
3305
+ The Hamming window is defined as:
3306
+ w(n) = 0.54 - 0.46 * cos(2 * π * n / (N-1))
3307
+
3308
+ where N is the window length and n ranges from 0 to N-1.
3309
+
3310
+ Examples
3311
+ --------
3312
+ >>> from numpy import array
3313
+ >>> hamming(4)
3314
+ array([0.08, 1.0 , 1.0 , 0.08])
3315
+
3316
+ >>> hamming(5, debug=True)
3317
+ initialized hamming window for length 5
3318
+ array([0.08, 1.0 , 1.0 , 1.0 , 0.08])
2250
3319
  """
2251
3320
  try:
2252
3321
  return hammingwindows[str(length)]
@@ -2259,28 +3328,48 @@ def hamming(length, debug=False):
2259
3328
  return hammingwindows[str(length)]
2260
3329
 
2261
3330
 
2262
- def windowfunction(length, type="hamming", debug=False):
2263
- r"""Returns a window function of the specified length and type. Once calculated, windows
3331
+ def windowfunction(length: int, type: str = "hamming", debug: bool = False) -> NDArray:
3332
+ """
3333
+ Returns a window function of the specified length and type. Once calculated, windows
2264
3334
  are cached for speed.
2265
3335
 
2266
3336
  Parameters
2267
3337
  ----------
2268
3338
  length : int
2269
- The length of the window function
2270
- :param length:
2271
-
2272
- type : {'hamming', 'hann', 'blackmanharris'}, optional
2273
- Window type. Choices are 'hamming' (default), 'hann', and 'blackmanharris'.
2274
- :param type:
2275
-
2276
- debug : boolean, optional
3339
+ The length of the window function.
3340
+ type : {'hamming', 'hann', 'blackmanharris', 'None'}, optional
3341
+ Window type. Choices are 'hamming' (default), 'hann', 'blackmanharris', and 'None'.
3342
+ If 'None' is specified, a window of ones is returned.
3343
+ debug : bool, optional
2277
3344
  When True, internal states of the function will be printed to help debugging.
2278
- :param debug:
3345
+ Default is False.
2279
3346
 
2280
3347
  Returns
2281
3348
  -------
2282
- windowfunc : 1D float array
2283
- The window function
3349
+ windowfunc : NDArray
3350
+ The window function as a 1D float array of the specified length.
3351
+
3352
+ Notes
3353
+ -----
3354
+ This function serves as a wrapper for different window functions and includes
3355
+ caching mechanism for improved performance. The supported window types are:
3356
+
3357
+ - 'hamming': Hamming window
3358
+ - 'hann': Hann (Hanning) window
3359
+ - 'blackmanharris': Blackman-Harris window
3360
+ - 'None': Rectangular window (all ones)
3361
+
3362
+ Examples
3363
+ --------
3364
+ >>> windowfunction(10, 'hamming')
3365
+ array([0.08 , 0.15302333, 0.41302333, 0.77102333, 0.99902333,
3366
+ 0.99902333, 0.77102333, 0.41302333, 0.15302333, 0.08 ])
3367
+
3368
+ >>> windowfunction(5, 'hann')
3369
+ array([0. , 0.5 , 1. , 0.5 , 0. ])
3370
+
3371
+ >>> windowfunction(4, 'None')
3372
+ array([1., 1., 1., 1.])
2284
3373
  """
2285
3374
  if type == "hamming":
2286
3375
  return hamming(length, debug=debug)