rapidtide 3.0.7.1__py3-none-any.whl → 3.0.8__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.
- rapidtide/_version.py +3 -3
- rapidtide/calcnullsimfunc.py +1 -3
- rapidtide/data/examples/src/test_findmaxlag.py +1 -1
- rapidtide/data/examples/src/testfmri +22 -16
- rapidtide/data/examples/src/testnewrefine +0 -23
- rapidtide/fMRIData_class.py +29 -52
- rapidtide/fit.py +4 -4
- rapidtide/happy_supportfuncs.py +1 -1
- rapidtide/helper_classes.py +0 -1099
- rapidtide/linfitfiltpass.py +59 -0
- rapidtide/makelaggedtcs.py +10 -0
- rapidtide/refinedelay.py +10 -19
- rapidtide/simFuncClasses.py +1132 -0
- rapidtide/simfuncfit.py +30 -30
- rapidtide/stats.py +5 -2
- rapidtide/tests/.coveragerc +6 -0
- rapidtide/tests/cleanposttest +1 -1
- rapidtide/tests/runlocaltest +2 -2
- rapidtide/tests/test_cleanregressor.py +3 -3
- rapidtide/tests/test_congrid.py +1 -1
- rapidtide/tests/test_corrpass.py +3 -3
- rapidtide/tests/test_delayestimation.py +8 -7
- rapidtide/tests/test_findmaxlag.py +2 -2
- rapidtide/tests/test_fullrunrapidtide_v3.py +2 -1
- rapidtide/tests/test_getparsers.py +14 -6
- rapidtide/tests/test_io.py +2 -6
- rapidtide/tests/test_nullcorr.py +3 -3
- rapidtide/tests/test_refinedelay.py +20 -5
- rapidtide/tidepoolTemplate_alt.py +1 -1
- rapidtide/util.py +7 -0
- rapidtide/voxelData.py +3 -6
- rapidtide/workflows/cleanregressor.py +2 -2
- rapidtide/workflows/delayvar.py +44 -58
- rapidtide/workflows/{delayestimation.py → estimateDelayMap.py} +84 -31
- rapidtide/workflows/rapidtide.py +349 -859
- rapidtide/workflows/rapidtide_parser.py +5 -38
- rapidtide/workflows/refineDelayMap.py +138 -0
- rapidtide/{RegressorRefiner.py → workflows/refineRegressor.py} +200 -28
- rapidtide/workflows/regressfrommaps.py +35 -27
- rapidtide/workflows/retrolagtcs.py +5 -6
- rapidtide/workflows/retroregress.py +73 -191
- rapidtide/workflows/showarbcorr.py +2 -2
- rapidtide/workflows/showxcorrx.py +5 -5
- rapidtide/workflows/tidepool.py +5 -5
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/METADATA +2 -2
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/RECORD +50 -48
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/WHEEL +0 -0
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/entry_points.txt +0 -0
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/licenses/LICENSE +0 -0
- {rapidtide-3.0.7.1.dist-info → rapidtide-3.0.8.dist-info}/top_level.txt +0 -0
rapidtide/helper_classes.py
CHANGED
|
@@ -140,342 +140,6 @@ class ProbeRegressor:
|
|
|
140
140
|
)
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
class SimilarityFunctionator:
|
|
144
|
-
reftc = None
|
|
145
|
-
prepreftc = None
|
|
146
|
-
testtc = None
|
|
147
|
-
preptesttc = None
|
|
148
|
-
timeaxis = None
|
|
149
|
-
similarityfunclen = 0
|
|
150
|
-
datavalid = False
|
|
151
|
-
timeaxisvalid = False
|
|
152
|
-
similarityfuncorigin = 0
|
|
153
|
-
|
|
154
|
-
def __init__(
|
|
155
|
-
self,
|
|
156
|
-
Fs=0.0,
|
|
157
|
-
similarityfuncorigin=0,
|
|
158
|
-
lagmininpts=0,
|
|
159
|
-
lagmaxinpts=0,
|
|
160
|
-
ncprefilter=None,
|
|
161
|
-
negativegradient=False,
|
|
162
|
-
reftc=None,
|
|
163
|
-
reftcstart=0.0,
|
|
164
|
-
detrendorder=1,
|
|
165
|
-
filterinputdata=True,
|
|
166
|
-
debug=False,
|
|
167
|
-
):
|
|
168
|
-
self.setFs(Fs)
|
|
169
|
-
self.similarityfuncorigin = similarityfuncorigin
|
|
170
|
-
self.lagmininpts = lagmininpts
|
|
171
|
-
self.lagmaxinpts = lagmaxinpts
|
|
172
|
-
self.ncprefilter = ncprefilter
|
|
173
|
-
self.negativegradient = negativegradient
|
|
174
|
-
self.reftc = reftc
|
|
175
|
-
self.detrendorder = detrendorder
|
|
176
|
-
self.filterinputdata = filterinputdata
|
|
177
|
-
self.debug = debug
|
|
178
|
-
if self.reftc is not None:
|
|
179
|
-
self.setreftc(self.reftc)
|
|
180
|
-
self.reftcstart = reftcstart
|
|
181
|
-
|
|
182
|
-
def setFs(self, Fs):
|
|
183
|
-
self.Fs = Fs
|
|
184
|
-
|
|
185
|
-
def preptc(self, thetc, isreftc=False):
|
|
186
|
-
# prepare timecourse by filtering, normalizing, detrending, and applying a window function
|
|
187
|
-
if isreftc:
|
|
188
|
-
thenormtc = tide_math.corrnormalize(
|
|
189
|
-
self.ncprefilter.apply(self.Fs, thetc),
|
|
190
|
-
detrendorder=self.detrendorder,
|
|
191
|
-
windowfunc=self.windowfunc,
|
|
192
|
-
)
|
|
193
|
-
else:
|
|
194
|
-
if self.negativegradient:
|
|
195
|
-
thenormtc = tide_math.corrnormalize(
|
|
196
|
-
-np.gradient(self.ncprefilter.apply(self.Fs, thetc)),
|
|
197
|
-
detrendorder=self.detrendorder,
|
|
198
|
-
windowfunc=self.windowfunc,
|
|
199
|
-
)
|
|
200
|
-
else:
|
|
201
|
-
if self.filterinputdata:
|
|
202
|
-
thenormtc = tide_math.corrnormalize(
|
|
203
|
-
self.ncprefilter.apply(self.Fs, thetc),
|
|
204
|
-
detrendorder=self.detrendorder,
|
|
205
|
-
windowfunc=self.windowfunc,
|
|
206
|
-
)
|
|
207
|
-
else:
|
|
208
|
-
thenormtc = tide_math.corrnormalize(
|
|
209
|
-
thetc,
|
|
210
|
-
detrendorder=self.detrendorder,
|
|
211
|
-
windowfunc=self.windowfunc,
|
|
212
|
-
)
|
|
213
|
-
|
|
214
|
-
return thenormtc
|
|
215
|
-
|
|
216
|
-
def trim(self, vector):
|
|
217
|
-
return vector[
|
|
218
|
-
self.similarityfuncorigin
|
|
219
|
-
- self.lagmininpts : self.similarityfuncorigin
|
|
220
|
-
+ self.lagmaxinpts
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
def getfunction(self, trim=True):
|
|
224
|
-
if self.datavalid:
|
|
225
|
-
if trim:
|
|
226
|
-
return (
|
|
227
|
-
self.trim(self.thesimfunc),
|
|
228
|
-
self.trim(self.timeaxis),
|
|
229
|
-
self.theglobalmax,
|
|
230
|
-
)
|
|
231
|
-
else:
|
|
232
|
-
return self.thesimfunc, self.timeaxis, self.theglobalmax
|
|
233
|
-
else:
|
|
234
|
-
if self.timeaxisvalid:
|
|
235
|
-
if trim:
|
|
236
|
-
return None, self.trim(self.timeaxis), None
|
|
237
|
-
else:
|
|
238
|
-
return None, self.timeaxis, None
|
|
239
|
-
else:
|
|
240
|
-
print("must calculate similarity function before fetching data")
|
|
241
|
-
return None, None, None
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
class MutualInformationator(SimilarityFunctionator):
|
|
245
|
-
def __init__(
|
|
246
|
-
self,
|
|
247
|
-
windowfunc="hamming",
|
|
248
|
-
norm=True,
|
|
249
|
-
madnorm=False,
|
|
250
|
-
smoothingtime=-1.0,
|
|
251
|
-
bins=20,
|
|
252
|
-
sigma=0.25,
|
|
253
|
-
*args,
|
|
254
|
-
**kwargs,
|
|
255
|
-
):
|
|
256
|
-
self.windowfunc = windowfunc
|
|
257
|
-
self.norm = norm
|
|
258
|
-
self.madnorm = madnorm
|
|
259
|
-
self.bins = bins
|
|
260
|
-
self.sigma = sigma
|
|
261
|
-
self.smoothingtime = smoothingtime
|
|
262
|
-
self.smoothingfilter = tide_filt.NoncausalFilter(filtertype="arb")
|
|
263
|
-
self.mi_norm = 1.0
|
|
264
|
-
if self.smoothingtime > 0.0:
|
|
265
|
-
self.smoothingfilter.setfreqs(
|
|
266
|
-
0.0, 0.0, 1.0 / self.smoothingtime, 1.0 / self.smoothingtime
|
|
267
|
-
)
|
|
268
|
-
super(MutualInformationator, self).__init__(*args, **kwargs)
|
|
269
|
-
|
|
270
|
-
def setlimits(self, lagmininpts, lagmaxinpts):
|
|
271
|
-
self.lagmininpts = lagmininpts
|
|
272
|
-
self.lagmaxinpts = lagmaxinpts
|
|
273
|
-
origpadtime = self.smoothingfilter.getpadtime()
|
|
274
|
-
timespan = self.timeaxis[-1] - self.timeaxis[0]
|
|
275
|
-
newpadtime = np.min([origpadtime, timespan])
|
|
276
|
-
if newpadtime < origpadtime:
|
|
277
|
-
print("lowering smoothing filter pad time to", newpadtime)
|
|
278
|
-
self.smoothingfilter.setpadtime(newpadtime)
|
|
279
|
-
|
|
280
|
-
def setbins(self, bins):
|
|
281
|
-
self.bins = bins
|
|
282
|
-
|
|
283
|
-
def setreftc(self, reftc, offset=0.0):
|
|
284
|
-
self.reftc = reftc + 0.0
|
|
285
|
-
self.prepreftc = self.preptc(self.reftc, isreftc=True)
|
|
286
|
-
|
|
287
|
-
self.timeaxis, self.automi, self.similarityfuncorigin = tide_corr.cross_mutual_info(
|
|
288
|
-
self.prepreftc,
|
|
289
|
-
self.prepreftc,
|
|
290
|
-
Fs=self.Fs,
|
|
291
|
-
fast=True,
|
|
292
|
-
negsteps=self.lagmininpts,
|
|
293
|
-
possteps=self.lagmaxinpts,
|
|
294
|
-
returnaxis=True,
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
self.timeaxis -= offset
|
|
298
|
-
self.similarityfunclen = len(self.timeaxis)
|
|
299
|
-
self.timeaxisvalid = True
|
|
300
|
-
self.datavalid = False
|
|
301
|
-
self.mi_norm = np.nan_to_num(1.0 / np.max(self.automi))
|
|
302
|
-
if self.debug:
|
|
303
|
-
print(f"MutualInformationator setreftc: {len(self.timeaxis)=}")
|
|
304
|
-
print(f"MutualInformationator setreftc: {self.timeaxis}")
|
|
305
|
-
print(f"MutualInformationator setreftc: {self.mi_norm=}")
|
|
306
|
-
|
|
307
|
-
def getnormfac(self):
|
|
308
|
-
return self.mi_norm
|
|
309
|
-
|
|
310
|
-
def run(self, thetc, locs=None, trim=True, gettimeaxis=True):
|
|
311
|
-
if len(thetc) != len(self.reftc):
|
|
312
|
-
print(
|
|
313
|
-
"MutualInformationator: timecourses are of different sizes:",
|
|
314
|
-
len(thetc),
|
|
315
|
-
"!=",
|
|
316
|
-
len(self.reftc),
|
|
317
|
-
"- exiting",
|
|
318
|
-
)
|
|
319
|
-
sys.exit()
|
|
320
|
-
|
|
321
|
-
self.testtc = thetc
|
|
322
|
-
self.preptesttc = self.preptc(self.testtc)
|
|
323
|
-
|
|
324
|
-
if locs is not None:
|
|
325
|
-
gettimeaxis = True
|
|
326
|
-
|
|
327
|
-
# now calculate the similarity function
|
|
328
|
-
if trim:
|
|
329
|
-
retvals = tide_corr.cross_mutual_info(
|
|
330
|
-
self.preptesttc,
|
|
331
|
-
self.prepreftc,
|
|
332
|
-
norm=self.norm,
|
|
333
|
-
negsteps=self.lagmininpts,
|
|
334
|
-
possteps=self.lagmaxinpts,
|
|
335
|
-
locs=locs,
|
|
336
|
-
madnorm=self.madnorm,
|
|
337
|
-
returnaxis=gettimeaxis,
|
|
338
|
-
fast=True,
|
|
339
|
-
Fs=self.Fs,
|
|
340
|
-
sigma=self.sigma,
|
|
341
|
-
bins=self.bins,
|
|
342
|
-
)
|
|
343
|
-
else:
|
|
344
|
-
retvals = tide_corr.cross_mutual_info(
|
|
345
|
-
self.preptesttc,
|
|
346
|
-
self.prepreftc,
|
|
347
|
-
norm=self.norm,
|
|
348
|
-
negsteps=-1,
|
|
349
|
-
possteps=-1,
|
|
350
|
-
locs=locs,
|
|
351
|
-
madnorm=self.madnorm,
|
|
352
|
-
returnaxis=gettimeaxis,
|
|
353
|
-
fast=True,
|
|
354
|
-
Fs=self.Fs,
|
|
355
|
-
sigma=self.sigma,
|
|
356
|
-
bins=self.bins,
|
|
357
|
-
)
|
|
358
|
-
if gettimeaxis:
|
|
359
|
-
self.timeaxis, self.thesimfunc, self.similarityfuncorigin = (
|
|
360
|
-
retvals[0],
|
|
361
|
-
retvals[1],
|
|
362
|
-
retvals[2],
|
|
363
|
-
)
|
|
364
|
-
self.timeaxisvalid = True
|
|
365
|
-
else:
|
|
366
|
-
self.thesimfunc = retvals[0]
|
|
367
|
-
|
|
368
|
-
# normalize
|
|
369
|
-
self.thesimfunc *= self.mi_norm
|
|
370
|
-
|
|
371
|
-
if locs is not None:
|
|
372
|
-
return self.thesimfunc
|
|
373
|
-
|
|
374
|
-
if self.smoothingtime > 0.0:
|
|
375
|
-
self.thesimfunc = self.smoothingfilter.apply(self.Fs, self.thesimfunc)
|
|
376
|
-
|
|
377
|
-
self.similarityfunclen = len(self.thesimfunc)
|
|
378
|
-
if trim:
|
|
379
|
-
self.similarityfuncorigin = self.lagmininpts + 1
|
|
380
|
-
else:
|
|
381
|
-
self.similarityfuncorigin = self.similarityfunclen // 2 + 1
|
|
382
|
-
|
|
383
|
-
# find the global maximum value
|
|
384
|
-
self.theglobalmax = np.argmax(self.thesimfunc)
|
|
385
|
-
self.datavalid = True
|
|
386
|
-
|
|
387
|
-
# make a dummy filtered baseline
|
|
388
|
-
self.filteredbaseline = self.thesimfunc * 0.0
|
|
389
|
-
|
|
390
|
-
if trim:
|
|
391
|
-
return (
|
|
392
|
-
self.trim(self.thesimfunc),
|
|
393
|
-
self.trim(self.timeaxis),
|
|
394
|
-
self.theglobalmax,
|
|
395
|
-
)
|
|
396
|
-
else:
|
|
397
|
-
return self.thesimfunc, self.timeaxis, self.theglobalmax
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
class Correlator(SimilarityFunctionator):
|
|
401
|
-
def __init__(
|
|
402
|
-
self,
|
|
403
|
-
windowfunc="hamming",
|
|
404
|
-
corrweighting="None",
|
|
405
|
-
corrpadding=0,
|
|
406
|
-
baselinefilter=None,
|
|
407
|
-
*args,
|
|
408
|
-
**kwargs,
|
|
409
|
-
):
|
|
410
|
-
self.windowfunc = windowfunc
|
|
411
|
-
self.corrweighting = corrweighting
|
|
412
|
-
self.corrpadding = corrpadding
|
|
413
|
-
self.baselinefilter = baselinefilter
|
|
414
|
-
super(Correlator, self).__init__(*args, **kwargs)
|
|
415
|
-
|
|
416
|
-
def setlimits(self, lagmininpts, lagmaxinpts):
|
|
417
|
-
self.lagmininpts = lagmininpts
|
|
418
|
-
self.lagmaxinpts = lagmaxinpts
|
|
419
|
-
|
|
420
|
-
def setreftc(self, reftc, offset=0.0):
|
|
421
|
-
self.reftc = reftc + 0.0
|
|
422
|
-
self.prepreftc = self.preptc(self.reftc, isreftc=True)
|
|
423
|
-
self.similarityfunclen = len(self.reftc) * 2 - 1
|
|
424
|
-
self.similarityfuncorigin = self.similarityfunclen // 2 + 1
|
|
425
|
-
|
|
426
|
-
# make the reference time axis
|
|
427
|
-
self.timeaxis = (
|
|
428
|
-
np.arange(0.0, self.similarityfunclen) * (1.0 / self.Fs)
|
|
429
|
-
- ((self.similarityfunclen - 1) * (1.0 / self.Fs)) / 2.0
|
|
430
|
-
) - offset
|
|
431
|
-
self.timeaxisvalid = True
|
|
432
|
-
self.datavalid = False
|
|
433
|
-
|
|
434
|
-
def run(self, thetc, trim=True):
|
|
435
|
-
if len(thetc) != len(self.reftc):
|
|
436
|
-
print(
|
|
437
|
-
"Correlator: timecourses are of different sizes:",
|
|
438
|
-
len(thetc),
|
|
439
|
-
"!=",
|
|
440
|
-
len(self.reftc),
|
|
441
|
-
"- exiting",
|
|
442
|
-
)
|
|
443
|
-
sys.exit()
|
|
444
|
-
|
|
445
|
-
self.testtc = thetc
|
|
446
|
-
self.preptesttc = self.preptc(self.testtc)
|
|
447
|
-
|
|
448
|
-
# now actually do the correlation
|
|
449
|
-
self.thesimfunc = tide_corr.fastcorrelate(
|
|
450
|
-
self.preptesttc,
|
|
451
|
-
self.prepreftc,
|
|
452
|
-
usefft=True,
|
|
453
|
-
weighting=self.corrweighting,
|
|
454
|
-
zeropadding=self.corrpadding,
|
|
455
|
-
debug=self.debug,
|
|
456
|
-
)
|
|
457
|
-
self.similarityfunclen = len(self.thesimfunc)
|
|
458
|
-
self.similarityfuncorigin = self.similarityfunclen // 2 + 1
|
|
459
|
-
|
|
460
|
-
if self.baselinefilter is not None:
|
|
461
|
-
self.filteredbaseline = self.baselinefilter.apply(self.Fs, self.thesimfunc)
|
|
462
|
-
else:
|
|
463
|
-
self.filteredbaseline = self.thesimfunc * 0.0
|
|
464
|
-
|
|
465
|
-
# find the global maximum value
|
|
466
|
-
self.theglobalmax = np.argmax(self.thesimfunc)
|
|
467
|
-
self.datavalid = True
|
|
468
|
-
|
|
469
|
-
if trim:
|
|
470
|
-
return (
|
|
471
|
-
self.trim(self.thesimfunc),
|
|
472
|
-
self.trim(self.timeaxis),
|
|
473
|
-
self.theglobalmax,
|
|
474
|
-
)
|
|
475
|
-
else:
|
|
476
|
-
return self.thesimfunc, self.timeaxis, self.theglobalmax
|
|
477
|
-
|
|
478
|
-
|
|
479
143
|
class Coherer:
|
|
480
144
|
reftc = None
|
|
481
145
|
prepreftc = None
|
|
@@ -664,766 +328,3 @@ class Coherer:
|
|
|
664
328
|
else:
|
|
665
329
|
self.themax = np.argmax(self.thecoherence)
|
|
666
330
|
return self.thecoherence, self.freqaxis, self.themax
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
class SimilarityFunctionFitter:
|
|
670
|
-
corrtimeaxis = None
|
|
671
|
-
FML_NOERROR = np.uint32(0x0000)
|
|
672
|
-
|
|
673
|
-
FML_INITAMPLOW = np.uint32(0x0001)
|
|
674
|
-
FML_INITAMPHIGH = np.uint32(0x0002)
|
|
675
|
-
FML_INITWIDTHLOW = np.uint32(0x0004)
|
|
676
|
-
FML_INITWIDTHHIGH = np.uint32(0x0008)
|
|
677
|
-
FML_INITLAGLOW = np.uint32(0x0010)
|
|
678
|
-
FML_INITLAGHIGH = np.uint32(0x0020)
|
|
679
|
-
FML_INITFAIL = (
|
|
680
|
-
FML_INITAMPLOW
|
|
681
|
-
| FML_INITAMPHIGH
|
|
682
|
-
| FML_INITWIDTHLOW
|
|
683
|
-
| FML_INITWIDTHHIGH
|
|
684
|
-
| FML_INITLAGLOW
|
|
685
|
-
| FML_INITLAGHIGH
|
|
686
|
-
)
|
|
687
|
-
|
|
688
|
-
FML_FITAMPLOW = np.uint32(0x0100)
|
|
689
|
-
FML_FITAMPHIGH = np.uint32(0x0200)
|
|
690
|
-
FML_FITWIDTHLOW = np.uint32(0x0400)
|
|
691
|
-
FML_FITWIDTHHIGH = np.uint32(0x0800)
|
|
692
|
-
FML_FITLAGLOW = np.uint32(0x1000)
|
|
693
|
-
FML_FITLAGHIGH = np.uint32(0x2000)
|
|
694
|
-
FML_FITALGOFAIL = np.uint32(0x0400)
|
|
695
|
-
FML_FITFAIL = (
|
|
696
|
-
FML_FITAMPLOW
|
|
697
|
-
| FML_FITAMPHIGH
|
|
698
|
-
| FML_FITWIDTHLOW
|
|
699
|
-
| FML_FITWIDTHHIGH
|
|
700
|
-
| FML_FITLAGLOW
|
|
701
|
-
| FML_FITLAGHIGH
|
|
702
|
-
| FML_FITALGOFAIL
|
|
703
|
-
)
|
|
704
|
-
|
|
705
|
-
def __init__(
|
|
706
|
-
self,
|
|
707
|
-
corrtimeaxis=None,
|
|
708
|
-
lagmin=-30.0,
|
|
709
|
-
lagmax=30.0,
|
|
710
|
-
absmaxsigma=1000.0,
|
|
711
|
-
absminsigma=0.25,
|
|
712
|
-
hardlimit=True,
|
|
713
|
-
bipolar=False,
|
|
714
|
-
lthreshval=0.0,
|
|
715
|
-
uthreshval=1.0,
|
|
716
|
-
debug=False,
|
|
717
|
-
zerooutbadfit=True,
|
|
718
|
-
maxguess=0.0,
|
|
719
|
-
useguess=False,
|
|
720
|
-
searchfrac=0.5,
|
|
721
|
-
lagmod=1000.0,
|
|
722
|
-
enforcethresh=True,
|
|
723
|
-
allowhighfitamps=False,
|
|
724
|
-
displayplots=False,
|
|
725
|
-
functype="correlation",
|
|
726
|
-
peakfittype="gauss",
|
|
727
|
-
):
|
|
728
|
-
r"""
|
|
729
|
-
|
|
730
|
-
Parameters
|
|
731
|
-
----------
|
|
732
|
-
corrtimeaxis: 1D float array
|
|
733
|
-
The time axis of the correlation function
|
|
734
|
-
lagmin: float
|
|
735
|
-
The minimum allowed lag time in seconds
|
|
736
|
-
lagmax: float
|
|
737
|
-
The maximum allowed lag time in seconds
|
|
738
|
-
absmaxsigma: float
|
|
739
|
-
The maximum allowed peak halfwidth in seconds
|
|
740
|
-
hardlimit
|
|
741
|
-
bipolar: boolean
|
|
742
|
-
If true find the correlation peak with the maximum absolute value, regardless of sign
|
|
743
|
-
threshval
|
|
744
|
-
uthreshval
|
|
745
|
-
debug
|
|
746
|
-
zerooutbadfit
|
|
747
|
-
maxguess
|
|
748
|
-
useguess
|
|
749
|
-
searchfrac
|
|
750
|
-
lagmod
|
|
751
|
-
enforcethresh
|
|
752
|
-
displayplots
|
|
753
|
-
|
|
754
|
-
Returns
|
|
755
|
-
-------
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
Methods
|
|
759
|
-
-------
|
|
760
|
-
fit(corrfunc):
|
|
761
|
-
Fit the correlation function given in corrfunc and return the location of the peak in seconds, the maximum
|
|
762
|
-
correlation value, the peak width
|
|
763
|
-
setrange(lagmin, lagmax):
|
|
764
|
-
Specify the search range for lag peaks, in seconds
|
|
765
|
-
"""
|
|
766
|
-
self.setcorrtimeaxis(corrtimeaxis)
|
|
767
|
-
self.lagmin = lagmin
|
|
768
|
-
self.lagmax = lagmax
|
|
769
|
-
self.absmaxsigma = absmaxsigma
|
|
770
|
-
self.absminsigma = absminsigma
|
|
771
|
-
self.hardlimit = hardlimit
|
|
772
|
-
self.bipolar = bipolar
|
|
773
|
-
self.lthreshval = lthreshval
|
|
774
|
-
self.uthreshval = uthreshval
|
|
775
|
-
self.debug = debug
|
|
776
|
-
if functype == "correlation" or functype == "mutualinfo":
|
|
777
|
-
self.functype = functype
|
|
778
|
-
else:
|
|
779
|
-
print("illegal functype")
|
|
780
|
-
sys.exit()
|
|
781
|
-
self.peakfittype = peakfittype
|
|
782
|
-
self.zerooutbadfit = zerooutbadfit
|
|
783
|
-
self.maxguess = maxguess
|
|
784
|
-
self.useguess = useguess
|
|
785
|
-
self.searchfrac = searchfrac
|
|
786
|
-
self.lagmod = lagmod
|
|
787
|
-
self.enforcethresh = enforcethresh
|
|
788
|
-
self.allowhighfitamps = allowhighfitamps
|
|
789
|
-
self.displayplots = displayplots
|
|
790
|
-
|
|
791
|
-
def _maxindex_noedge(self, corrfunc):
|
|
792
|
-
"""
|
|
793
|
-
|
|
794
|
-
Parameters
|
|
795
|
-
----------
|
|
796
|
-
corrfunc
|
|
797
|
-
|
|
798
|
-
Returns
|
|
799
|
-
-------
|
|
800
|
-
|
|
801
|
-
"""
|
|
802
|
-
lowerlim = 0
|
|
803
|
-
upperlim = len(self.corrtimeaxis) - 1
|
|
804
|
-
done = False
|
|
805
|
-
while not done:
|
|
806
|
-
flipfac = 1.0
|
|
807
|
-
done = True
|
|
808
|
-
maxindex = (np.argmax(corrfunc[lowerlim:upperlim]) + lowerlim).astype("int32")
|
|
809
|
-
if self.bipolar:
|
|
810
|
-
minindex = (np.argmax(-corrfunc[lowerlim:upperlim]) + lowerlim).astype("int32")
|
|
811
|
-
if np.fabs(corrfunc[minindex]) > np.fabs(corrfunc[maxindex]):
|
|
812
|
-
maxindex = minindex
|
|
813
|
-
flipfac = -1.0
|
|
814
|
-
if upperlim == lowerlim:
|
|
815
|
-
done = True
|
|
816
|
-
if maxindex == 0:
|
|
817
|
-
lowerlim += 1
|
|
818
|
-
done = False
|
|
819
|
-
if maxindex == upperlim:
|
|
820
|
-
upperlim -= 1
|
|
821
|
-
done = False
|
|
822
|
-
return maxindex, flipfac
|
|
823
|
-
|
|
824
|
-
def setfunctype(self, functype):
|
|
825
|
-
self.functype = functype
|
|
826
|
-
|
|
827
|
-
def setpeakfittype(self, peakfittype):
|
|
828
|
-
self.peakfittype = peakfittype
|
|
829
|
-
|
|
830
|
-
def setrange(self, lagmin, lagmax):
|
|
831
|
-
self.lagmin = lagmin
|
|
832
|
-
self.lagmax = lagmax
|
|
833
|
-
|
|
834
|
-
def setcorrtimeaxis(self, corrtimeaxis):
|
|
835
|
-
if corrtimeaxis is not None:
|
|
836
|
-
self.corrtimeaxis = corrtimeaxis + 0.0
|
|
837
|
-
else:
|
|
838
|
-
self.corrtimeaxis = corrtimeaxis
|
|
839
|
-
|
|
840
|
-
def setguess(self, useguess, maxguess=0.0):
|
|
841
|
-
self.useguess = useguess
|
|
842
|
-
self.maxguess = maxguess
|
|
843
|
-
|
|
844
|
-
def setlthresh(self, lthreshval):
|
|
845
|
-
self.lthreshval = lthreshval
|
|
846
|
-
|
|
847
|
-
def setuthresh(self, uthreshval):
|
|
848
|
-
self.uthreshval = uthreshval
|
|
849
|
-
|
|
850
|
-
def diagnosefail(self, failreason):
|
|
851
|
-
# define error values
|
|
852
|
-
reasons = []
|
|
853
|
-
if failreason.astype(np.uint32) & self.FML_INITAMPLOW:
|
|
854
|
-
reasons.append("Initial amplitude too low")
|
|
855
|
-
if failreason.astype(np.uint32) & self.FML_INITAMPHIGH:
|
|
856
|
-
reasons.append("Initial amplitude too high")
|
|
857
|
-
if failreason.astype(np.uint32) & self.FML_INITWIDTHLOW:
|
|
858
|
-
reasons.append("Initial width too low")
|
|
859
|
-
if failreason.astype(np.uint32) & self.FML_INITWIDTHHIGH:
|
|
860
|
-
reasons.append("Initial width too high")
|
|
861
|
-
if failreason.astype(np.uint32) & self.FML_INITLAGLOW:
|
|
862
|
-
reasons.append("Initial Lag too low")
|
|
863
|
-
if failreason.astype(np.uint32) & self.FML_INITLAGHIGH:
|
|
864
|
-
reasons.append("Initial Lag too high")
|
|
865
|
-
|
|
866
|
-
if failreason.astype(np.uint32) & self.FML_FITAMPLOW:
|
|
867
|
-
reasons.append("Fit amplitude too low")
|
|
868
|
-
if failreason.astype(np.uint32) & self.FML_FITAMPHIGH:
|
|
869
|
-
reasons.append("Fit amplitude too high")
|
|
870
|
-
if failreason.astype(np.uint32) & self.FML_FITWIDTHLOW:
|
|
871
|
-
reasons.append("Fit width too low")
|
|
872
|
-
if failreason.astype(np.uint32) & self.FML_FITWIDTHHIGH:
|
|
873
|
-
reasons.append("Fit width too high")
|
|
874
|
-
if failreason.astype(np.uint32) & self.FML_FITLAGLOW:
|
|
875
|
-
reasons.append("Fit Lag too low")
|
|
876
|
-
if failreason.astype(np.uint32) & self.FML_FITLAGHIGH:
|
|
877
|
-
reasons.append("Fit Lag too high")
|
|
878
|
-
if failreason.astype(np.uint32) & self.FML_FITALGOFAIL:
|
|
879
|
-
reasons.append("Nonlinear fit failed")
|
|
880
|
-
|
|
881
|
-
if len(reasons) > 0:
|
|
882
|
-
return ", ".join(reasons)
|
|
883
|
-
else:
|
|
884
|
-
return "No error"
|
|
885
|
-
|
|
886
|
-
def fit(self, incorrfunc):
|
|
887
|
-
# check to make sure xcorr_x and xcorr_y match
|
|
888
|
-
if self.corrtimeaxis is None:
|
|
889
|
-
print("Correlation time axis is not defined - exiting")
|
|
890
|
-
sys.exit()
|
|
891
|
-
if len(self.corrtimeaxis) != len(incorrfunc):
|
|
892
|
-
print(
|
|
893
|
-
"Correlation time axis and values do not match in length (",
|
|
894
|
-
len(self.corrtimeaxis),
|
|
895
|
-
"!=",
|
|
896
|
-
len(incorrfunc),
|
|
897
|
-
"- exiting",
|
|
898
|
-
)
|
|
899
|
-
sys.exit()
|
|
900
|
-
# set initial parameters
|
|
901
|
-
# absmaxsigma is in seconds
|
|
902
|
-
# maxsigma is in Hz
|
|
903
|
-
# maxlag is in seconds
|
|
904
|
-
warnings.filterwarnings("ignore", "Number*")
|
|
905
|
-
failreason = self.FML_NOERROR
|
|
906
|
-
maskval = np.uint16(1) # start out assuming the fit will succeed
|
|
907
|
-
binwidth = self.corrtimeaxis[1] - self.corrtimeaxis[0]
|
|
908
|
-
|
|
909
|
-
# set the search range
|
|
910
|
-
lowerlim = 0
|
|
911
|
-
upperlim = len(self.corrtimeaxis) - 1
|
|
912
|
-
if self.debug:
|
|
913
|
-
print(
|
|
914
|
-
"initial search indices are",
|
|
915
|
-
lowerlim,
|
|
916
|
-
"to",
|
|
917
|
-
upperlim,
|
|
918
|
-
"(",
|
|
919
|
-
self.corrtimeaxis[lowerlim],
|
|
920
|
-
self.corrtimeaxis[upperlim],
|
|
921
|
-
")",
|
|
922
|
-
)
|
|
923
|
-
|
|
924
|
-
# make an initial guess at the fit parameters for the gaussian
|
|
925
|
-
# start with finding the maximum value and its location
|
|
926
|
-
flipfac = 1.0
|
|
927
|
-
corrfunc = incorrfunc + 0.0
|
|
928
|
-
if self.useguess:
|
|
929
|
-
maxindex = tide_util.valtoindex(self.corrtimeaxis, self.maxguess)
|
|
930
|
-
if (corrfunc[maxindex] < 0.0) and self.bipolar:
|
|
931
|
-
flipfac = -1.0
|
|
932
|
-
else:
|
|
933
|
-
maxindex, flipfac = self._maxindex_noedge(corrfunc)
|
|
934
|
-
corrfunc *= flipfac
|
|
935
|
-
maxlag_init = (1.0 * self.corrtimeaxis[maxindex]).astype("float64")
|
|
936
|
-
maxval_init = corrfunc[maxindex].astype("float64")
|
|
937
|
-
if self.debug:
|
|
938
|
-
print(
|
|
939
|
-
"maxindex, maxlag_init, maxval_init:",
|
|
940
|
-
maxindex,
|
|
941
|
-
maxlag_init,
|
|
942
|
-
maxval_init,
|
|
943
|
-
)
|
|
944
|
-
|
|
945
|
-
# set the baseline and baselinedev levels
|
|
946
|
-
if (self.functype == "correlation") or (self.functype == "hybrid"):
|
|
947
|
-
baseline = 0.0
|
|
948
|
-
baselinedev = 0.0
|
|
949
|
-
else:
|
|
950
|
-
# for mutual information, there is a nonzero baseline, so we want the difference from that.
|
|
951
|
-
baseline = np.median(corrfunc)
|
|
952
|
-
baselinedev = mad(corrfunc)
|
|
953
|
-
if self.debug:
|
|
954
|
-
print("baseline, baselinedev:", baseline, baselinedev)
|
|
955
|
-
|
|
956
|
-
# then calculate the width of the peak
|
|
957
|
-
if self.peakfittype == "fastquad" or self.peakfittype == "COM":
|
|
958
|
-
peakstart = np.max([1, maxindex - 2])
|
|
959
|
-
peakend = np.min([len(self.corrtimeaxis) - 2, maxindex + 2])
|
|
960
|
-
else:
|
|
961
|
-
# come here for peakfittype of None, quad, gauss, fastgauss
|
|
962
|
-
thegrad = np.gradient(corrfunc).astype(
|
|
963
|
-
"float64"
|
|
964
|
-
) # the gradient of the correlation function
|
|
965
|
-
if (self.functype == "correlation") or (self.functype == "hybrid"):
|
|
966
|
-
if self.peakfittype == "quad":
|
|
967
|
-
peakpoints = np.where(
|
|
968
|
-
corrfunc > maxval_init - 0.05, 1, 0
|
|
969
|
-
) # mask for places where correlation exceeds searchfrac*maxval_init
|
|
970
|
-
else:
|
|
971
|
-
peakpoints = np.where(
|
|
972
|
-
corrfunc > (baseline + self.searchfrac * (maxval_init - baseline)), 1, 0
|
|
973
|
-
) # mask for places where correlation exceeds searchfrac*maxval_init
|
|
974
|
-
else:
|
|
975
|
-
# for mutual information, there is a flattish, nonzero baseline, so we want the difference from that.
|
|
976
|
-
peakpoints = np.where(
|
|
977
|
-
corrfunc > (baseline + self.searchfrac * (maxval_init - baseline)),
|
|
978
|
-
1,
|
|
979
|
-
0,
|
|
980
|
-
)
|
|
981
|
-
|
|
982
|
-
peakpoints[0] = 0
|
|
983
|
-
peakpoints[-1] = 0
|
|
984
|
-
peakstart = np.max([1, maxindex - 1])
|
|
985
|
-
peakend = np.min([len(self.corrtimeaxis) - 2, maxindex + 1])
|
|
986
|
-
if self.debug:
|
|
987
|
-
print("initial peakstart, peakend:", peakstart, peakend)
|
|
988
|
-
if self.functype == "mutualinfo":
|
|
989
|
-
while peakpoints[peakend + 1] == 1:
|
|
990
|
-
peakend += 1
|
|
991
|
-
while peakpoints[peakstart - 1] == 1:
|
|
992
|
-
peakstart -= 1
|
|
993
|
-
else:
|
|
994
|
-
while thegrad[peakend + 1] <= 0.0 and peakpoints[peakend + 1] == 1:
|
|
995
|
-
peakend += 1
|
|
996
|
-
while thegrad[peakstart - 1] >= 0.0 and peakpoints[peakstart - 1] == 1:
|
|
997
|
-
peakstart -= 1
|
|
998
|
-
if self.debug:
|
|
999
|
-
print("final peakstart, peakend:", peakstart, peakend)
|
|
1000
|
-
|
|
1001
|
-
# deal with flat peak top
|
|
1002
|
-
while (
|
|
1003
|
-
peakend < (len(self.corrtimeaxis) - 3)
|
|
1004
|
-
and corrfunc[peakend] == corrfunc[peakend - 1]
|
|
1005
|
-
):
|
|
1006
|
-
peakend += 1
|
|
1007
|
-
while peakstart > 2 and corrfunc[peakstart] == corrfunc[peakstart + 1]:
|
|
1008
|
-
peakstart -= 1
|
|
1009
|
-
if self.debug:
|
|
1010
|
-
print("peakstart, peakend after flattop correction:", peakstart, peakend)
|
|
1011
|
-
print("\n")
|
|
1012
|
-
for i in range(peakstart, peakend + 1):
|
|
1013
|
-
print(self.corrtimeaxis[i], corrfunc[i])
|
|
1014
|
-
print("\n")
|
|
1015
|
-
fig = plt.figure()
|
|
1016
|
-
ax = fig.add_subplot(111)
|
|
1017
|
-
ax.set_title("Peak sent to fitting routine")
|
|
1018
|
-
plt.plot(
|
|
1019
|
-
self.corrtimeaxis[peakstart : peakend + 1],
|
|
1020
|
-
corrfunc[peakstart : peakend + 1],
|
|
1021
|
-
"r",
|
|
1022
|
-
)
|
|
1023
|
-
plt.show()
|
|
1024
|
-
|
|
1025
|
-
# This is calculated from first principles, but it's always big by a factor or ~1.4.
|
|
1026
|
-
# Which makes me think I dropped a factor if sqrt(2). So fix that with a final division
|
|
1027
|
-
maxsigma_init = np.float64(
|
|
1028
|
-
((peakend - peakstart + 1) * binwidth / (2.0 * np.sqrt(-np.log(self.searchfrac))))
|
|
1029
|
-
/ np.sqrt(2.0)
|
|
1030
|
-
)
|
|
1031
|
-
if self.debug:
|
|
1032
|
-
print("maxsigma_init:", maxsigma_init)
|
|
1033
|
-
|
|
1034
|
-
# now check the values for errors
|
|
1035
|
-
if self.hardlimit:
|
|
1036
|
-
rangeextension = 0.0
|
|
1037
|
-
else:
|
|
1038
|
-
rangeextension = (self.lagmax - self.lagmin) * 0.75
|
|
1039
|
-
if not (
|
|
1040
|
-
(self.lagmin - rangeextension - binwidth)
|
|
1041
|
-
<= maxlag_init
|
|
1042
|
-
<= (self.lagmax + rangeextension + binwidth)
|
|
1043
|
-
):
|
|
1044
|
-
if maxlag_init <= (self.lagmin - rangeextension - binwidth):
|
|
1045
|
-
failreason |= self.FML_INITLAGLOW
|
|
1046
|
-
maxlag_init = self.lagmin - rangeextension - binwidth
|
|
1047
|
-
else:
|
|
1048
|
-
failreason |= self.FML_INITLAGHIGH
|
|
1049
|
-
maxlag_init = self.lagmax + rangeextension + binwidth
|
|
1050
|
-
if self.debug:
|
|
1051
|
-
print("bad initial")
|
|
1052
|
-
if maxsigma_init > self.absmaxsigma:
|
|
1053
|
-
failreason |= self.FML_INITWIDTHHIGH
|
|
1054
|
-
maxsigma_init = self.absmaxsigma
|
|
1055
|
-
if self.debug:
|
|
1056
|
-
print("bad initial width - too high")
|
|
1057
|
-
if peakend - peakstart < 2:
|
|
1058
|
-
failreason |= self.FML_INITWIDTHLOW
|
|
1059
|
-
maxsigma_init = np.float64(
|
|
1060
|
-
((2 + 1) * binwidth / (2.0 * np.sqrt(-np.log(self.searchfrac)))) / np.sqrt(2.0)
|
|
1061
|
-
)
|
|
1062
|
-
if self.debug:
|
|
1063
|
-
print("bad initial width - too low")
|
|
1064
|
-
if (self.functype == "correlation") or (self.functype == "hybrid"):
|
|
1065
|
-
if not (self.lthreshval <= maxval_init <= self.uthreshval) and self.enforcethresh:
|
|
1066
|
-
failreason |= self.FML_INITAMPLOW
|
|
1067
|
-
if self.debug:
|
|
1068
|
-
print(
|
|
1069
|
-
"bad initial amp:",
|
|
1070
|
-
maxval_init,
|
|
1071
|
-
"is less than",
|
|
1072
|
-
self.lthreshval,
|
|
1073
|
-
)
|
|
1074
|
-
if maxval_init < 0.0:
|
|
1075
|
-
failreason |= self.FML_INITAMPLOW
|
|
1076
|
-
maxval_init = 0.0
|
|
1077
|
-
if self.debug:
|
|
1078
|
-
print("bad initial amp:", maxval_init, "is less than 0.0")
|
|
1079
|
-
if (maxval_init > 1.0) and self.enforcethresh:
|
|
1080
|
-
failreason |= self.FML_INITAMPHIGH
|
|
1081
|
-
maxval_init = 1.0
|
|
1082
|
-
if self.debug:
|
|
1083
|
-
print("bad initial amp:", maxval_init, "is greater than 1.0")
|
|
1084
|
-
else:
|
|
1085
|
-
# somewhat different rules for mutual information peaks
|
|
1086
|
-
if ((maxval_init - baseline) < self.lthreshval * baselinedev) or (
|
|
1087
|
-
maxval_init < baseline
|
|
1088
|
-
):
|
|
1089
|
-
failreason |= self.FML_INITAMPLOW
|
|
1090
|
-
maxval_init = 0.0
|
|
1091
|
-
if self.debug:
|
|
1092
|
-
print("bad initial amp:", maxval_init, "is less than 0.0")
|
|
1093
|
-
if (failreason != self.FML_NOERROR) and self.zerooutbadfit:
|
|
1094
|
-
maxval = np.float64(0.0)
|
|
1095
|
-
maxlag = np.float64(0.0)
|
|
1096
|
-
maxsigma = np.float64(0.0)
|
|
1097
|
-
else:
|
|
1098
|
-
maxval = np.float64(maxval_init)
|
|
1099
|
-
maxlag = np.float64(maxlag_init)
|
|
1100
|
-
maxsigma = np.float64(maxsigma_init)
|
|
1101
|
-
|
|
1102
|
-
# refine if necessary
|
|
1103
|
-
if self.peakfittype != "None":
|
|
1104
|
-
if self.peakfittype == "COM":
|
|
1105
|
-
X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
|
|
1106
|
-
data = corrfunc[peakstart : peakend + 1]
|
|
1107
|
-
maxval = maxval_init
|
|
1108
|
-
maxlag = np.sum(X * data) / np.sum(data)
|
|
1109
|
-
maxsigma = 10.0
|
|
1110
|
-
elif self.peakfittype == "gauss":
|
|
1111
|
-
X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
|
|
1112
|
-
data = corrfunc[peakstart : peakend + 1]
|
|
1113
|
-
# do a least squares fit over the top of the peak
|
|
1114
|
-
# p0 = np.array([maxval_init, np.fmod(maxlag_init, lagmod), maxsigma_init], dtype='float64')
|
|
1115
|
-
p0 = np.array([maxval_init, maxlag_init, maxsigma_init], dtype="float64")
|
|
1116
|
-
if self.debug:
|
|
1117
|
-
print("fit input array:", p0)
|
|
1118
|
-
try:
|
|
1119
|
-
plsq, dummy = sp.optimize.leastsq(
|
|
1120
|
-
tide_fit.gaussresiduals, p0, args=(data, X), maxfev=5000
|
|
1121
|
-
)
|
|
1122
|
-
maxval = plsq[0] + baseline
|
|
1123
|
-
maxlag = np.fmod((1.0 * plsq[1]), self.lagmod)
|
|
1124
|
-
maxsigma = plsq[2]
|
|
1125
|
-
except:
|
|
1126
|
-
failreason |= self.FML_FITALGOFAIL
|
|
1127
|
-
maxval = np.float64(0.0)
|
|
1128
|
-
maxlag = np.float64(0.0)
|
|
1129
|
-
maxsigma = np.float64(0.0)
|
|
1130
|
-
if self.debug:
|
|
1131
|
-
print("fit output array:", [maxval, maxlag, maxsigma])
|
|
1132
|
-
elif self.peakfittype == "gausscf":
|
|
1133
|
-
X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
|
|
1134
|
-
data = corrfunc[peakstart : peakend + 1]
|
|
1135
|
-
# do a least squares fit over the top of the peak
|
|
1136
|
-
try:
|
|
1137
|
-
plsq, pcov = curve_fit(
|
|
1138
|
-
tide_fit.gaussfunc,
|
|
1139
|
-
X,
|
|
1140
|
-
data,
|
|
1141
|
-
p0=[maxval_init, maxlag_init, maxsigma_init],
|
|
1142
|
-
)
|
|
1143
|
-
maxval = plsq[0] + baseline
|
|
1144
|
-
maxlag = np.fmod((1.0 * plsq[1]), self.lagmod)
|
|
1145
|
-
maxsigma = plsq[2]
|
|
1146
|
-
except:
|
|
1147
|
-
failreason |= self.FML_FITALGOFAIL
|
|
1148
|
-
maxval = np.float64(0.0)
|
|
1149
|
-
maxlag = np.float64(0.0)
|
|
1150
|
-
maxsigma = np.float64(0.0)
|
|
1151
|
-
if self.debug:
|
|
1152
|
-
print("fit output array:", [maxval, maxlag, maxsigma])
|
|
1153
|
-
elif self.peakfittype == "fastgauss":
|
|
1154
|
-
X = self.corrtimeaxis[peakstart : peakend + 1] - baseline
|
|
1155
|
-
data = corrfunc[peakstart : peakend + 1]
|
|
1156
|
-
# do a non-iterative fit over the top of the peak
|
|
1157
|
-
# 6/12/2015 This is just broken. Gives quantized maxima
|
|
1158
|
-
maxlag = np.float64(1.0 * np.sum(X * data) / np.sum(data))
|
|
1159
|
-
maxsigma = np.float64(
|
|
1160
|
-
np.sqrt(np.abs(np.sum((X - maxlag) ** 2 * data) / np.sum(data)))
|
|
1161
|
-
)
|
|
1162
|
-
maxval = np.float64(data.max()) + baseline
|
|
1163
|
-
elif self.peakfittype == "fastquad":
|
|
1164
|
-
maxlag, maxval, maxsigma, ismax, badfit = tide_fit.refinepeak_quad(
|
|
1165
|
-
self.corrtimeaxis, corrfunc, maxindex
|
|
1166
|
-
)
|
|
1167
|
-
elif self.peakfittype == "quad":
|
|
1168
|
-
X = self.corrtimeaxis[peakstart : peakend + 1]
|
|
1169
|
-
data = corrfunc[peakstart : peakend + 1]
|
|
1170
|
-
try:
|
|
1171
|
-
thecoffs = Polynomial.fit(X, data, 2).convert().coef[::-1]
|
|
1172
|
-
a = thecoffs[0]
|
|
1173
|
-
b = thecoffs[1]
|
|
1174
|
-
c = thecoffs[2]
|
|
1175
|
-
maxlag = -b / (2.0 * a)
|
|
1176
|
-
maxval = a * maxlag * maxlag + b * maxlag + c
|
|
1177
|
-
maxsigma = 1.0 / np.fabs(a)
|
|
1178
|
-
if self.debug:
|
|
1179
|
-
print("poly coffs:", a, b, c)
|
|
1180
|
-
print("maxlag, maxval, maxsigma:", maxlag, maxval, maxsigma)
|
|
1181
|
-
except np.lib.polynomial.RankWarning:
|
|
1182
|
-
failreason |= self.FML_FITALGOFAIL
|
|
1183
|
-
maxlag = 0.0
|
|
1184
|
-
maxval = 0.0
|
|
1185
|
-
maxsigma = 0.0
|
|
1186
|
-
if self.debug:
|
|
1187
|
-
print("\n")
|
|
1188
|
-
for i in range(len(X)):
|
|
1189
|
-
print(X[i], data[i])
|
|
1190
|
-
print("\n")
|
|
1191
|
-
fig = plt.figure()
|
|
1192
|
-
ax = fig.add_subplot(111)
|
|
1193
|
-
ax.set_title("Peak and fit")
|
|
1194
|
-
plt.plot(X, data, "r")
|
|
1195
|
-
plt.plot(X, c + b * X + a * X * X, "b")
|
|
1196
|
-
plt.show()
|
|
1197
|
-
|
|
1198
|
-
else:
|
|
1199
|
-
print("illegal peak refinement type")
|
|
1200
|
-
|
|
1201
|
-
# check for errors in fit
|
|
1202
|
-
fitfail = False
|
|
1203
|
-
if self.bipolar:
|
|
1204
|
-
lowestcorrcoeff = -1.0
|
|
1205
|
-
else:
|
|
1206
|
-
lowestcorrcoeff = 0.0
|
|
1207
|
-
if (self.functype == "correlation") or (self.functype == "hybrid"):
|
|
1208
|
-
if maxval < lowestcorrcoeff:
|
|
1209
|
-
failreason |= self.FML_FITAMPLOW
|
|
1210
|
-
maxval = lowestcorrcoeff
|
|
1211
|
-
if self.debug:
|
|
1212
|
-
print("bad fit amp: maxval is lower than lower limit")
|
|
1213
|
-
fitfail = True
|
|
1214
|
-
if np.abs(maxval) > 1.0:
|
|
1215
|
-
if not self.allowhighfitamps:
|
|
1216
|
-
failreason |= self.FML_FITAMPHIGH
|
|
1217
|
-
if self.debug:
|
|
1218
|
-
print(
|
|
1219
|
-
"bad fit amp: magnitude of",
|
|
1220
|
-
maxval,
|
|
1221
|
-
"is greater than 1.0",
|
|
1222
|
-
)
|
|
1223
|
-
fitfail = True
|
|
1224
|
-
maxval = 1.0 * np.sign(maxval)
|
|
1225
|
-
else:
|
|
1226
|
-
# different rules for mutual information peaks
|
|
1227
|
-
if ((maxval - baseline) < self.lthreshval * baselinedev) or (maxval < baseline):
|
|
1228
|
-
failreason |= self.FML_FITAMPLOW
|
|
1229
|
-
maxval_init = 0.0
|
|
1230
|
-
if self.debug:
|
|
1231
|
-
if (maxval - baseline) < self.lthreshval * baselinedev:
|
|
1232
|
-
print(
|
|
1233
|
-
"FITAMPLOW: maxval - baseline:",
|
|
1234
|
-
maxval - baseline,
|
|
1235
|
-
" < lthreshval * baselinedev:",
|
|
1236
|
-
self.lthreshval * baselinedev,
|
|
1237
|
-
)
|
|
1238
|
-
if maxval < baseline:
|
|
1239
|
-
print("FITAMPLOW: maxval < baseline:", maxval, baseline)
|
|
1240
|
-
if self.debug:
|
|
1241
|
-
print("bad fit amp: maxval is lower than lower limit")
|
|
1242
|
-
if (self.lagmin > maxlag) or (maxlag > self.lagmax):
|
|
1243
|
-
if self.debug:
|
|
1244
|
-
print("bad lag after refinement")
|
|
1245
|
-
if self.lagmin > maxlag:
|
|
1246
|
-
failreason |= self.FML_FITLAGLOW
|
|
1247
|
-
maxlag = self.lagmin
|
|
1248
|
-
else:
|
|
1249
|
-
failreason |= self.FML_FITLAGHIGH
|
|
1250
|
-
maxlag = self.lagmax
|
|
1251
|
-
fitfail = True
|
|
1252
|
-
if maxsigma > self.absmaxsigma:
|
|
1253
|
-
failreason |= self.FML_FITWIDTHHIGH
|
|
1254
|
-
if self.debug:
|
|
1255
|
-
print("bad width after refinement:", maxsigma, ">", self.absmaxsigma)
|
|
1256
|
-
maxsigma = self.absmaxsigma
|
|
1257
|
-
fitfail = True
|
|
1258
|
-
if maxsigma < self.absminsigma:
|
|
1259
|
-
failreason |= self.FML_FITWIDTHLOW
|
|
1260
|
-
if self.debug:
|
|
1261
|
-
print("bad width after refinement:", maxsigma, "<", self.absminsigma)
|
|
1262
|
-
maxsigma = self.absminsigma
|
|
1263
|
-
fitfail = True
|
|
1264
|
-
if fitfail:
|
|
1265
|
-
if self.debug:
|
|
1266
|
-
print("fit fail")
|
|
1267
|
-
if self.zerooutbadfit:
|
|
1268
|
-
maxval = np.float64(0.0)
|
|
1269
|
-
maxlag = np.float64(0.0)
|
|
1270
|
-
maxsigma = np.float64(0.0)
|
|
1271
|
-
maskval = np.uint16(0)
|
|
1272
|
-
# print(maxlag_init, maxlag, maxval_init, maxval, maxsigma_init, maxsigma, maskval, failreason, fitfail)
|
|
1273
|
-
else:
|
|
1274
|
-
maxval = np.float64(maxval_init)
|
|
1275
|
-
maxlag = np.float64(np.fmod(maxlag_init, self.lagmod))
|
|
1276
|
-
maxsigma = np.float64(maxsigma_init)
|
|
1277
|
-
if failreason != self.FML_NOERROR:
|
|
1278
|
-
maskval = np.uint16(0)
|
|
1279
|
-
else:
|
|
1280
|
-
maskval = np.uint16(1)
|
|
1281
|
-
|
|
1282
|
-
if self.debug or self.displayplots:
|
|
1283
|
-
print(
|
|
1284
|
-
"init to final: maxval",
|
|
1285
|
-
maxval_init,
|
|
1286
|
-
maxval,
|
|
1287
|
-
", maxlag:",
|
|
1288
|
-
maxlag_init,
|
|
1289
|
-
maxlag,
|
|
1290
|
-
", width:",
|
|
1291
|
-
maxsigma_init,
|
|
1292
|
-
maxsigma,
|
|
1293
|
-
)
|
|
1294
|
-
if self.displayplots and (self.peakfittype != "None") and (maskval != 0.0):
|
|
1295
|
-
fig = plt.figure()
|
|
1296
|
-
ax = fig.add_subplot(111)
|
|
1297
|
-
ax.set_title("Data and fit")
|
|
1298
|
-
hiresx = np.arange(X[0], X[-1], (X[1] - X[0]) / 10.0)
|
|
1299
|
-
plt.plot(
|
|
1300
|
-
X,
|
|
1301
|
-
data,
|
|
1302
|
-
"ro",
|
|
1303
|
-
hiresx,
|
|
1304
|
-
tide_fit.gauss_eval(hiresx, np.array([maxval, maxlag, maxsigma])),
|
|
1305
|
-
"b-",
|
|
1306
|
-
)
|
|
1307
|
-
plt.show()
|
|
1308
|
-
return (
|
|
1309
|
-
maxindex,
|
|
1310
|
-
maxlag,
|
|
1311
|
-
flipfac * maxval,
|
|
1312
|
-
maxsigma,
|
|
1313
|
-
maskval,
|
|
1314
|
-
failreason,
|
|
1315
|
-
peakstart,
|
|
1316
|
-
peakend,
|
|
1317
|
-
)
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
class FrequencyTracker:
|
|
1321
|
-
freqs = None
|
|
1322
|
-
times = None
|
|
1323
|
-
|
|
1324
|
-
def __init__(self, lowerlim=0.1, upperlim=0.6, nperseg=32, Q=10.0, debug=False):
|
|
1325
|
-
self.lowerlim = lowerlim
|
|
1326
|
-
self.upperlim = upperlim
|
|
1327
|
-
self.nperseg = nperseg
|
|
1328
|
-
self.Q = Q
|
|
1329
|
-
self.debug = debug
|
|
1330
|
-
self.nfft = self.nperseg
|
|
1331
|
-
|
|
1332
|
-
def track(self, x, fs):
|
|
1333
|
-
self.freqs, self.times, thespectrogram = sp.signal.spectrogram(
|
|
1334
|
-
np.concatenate(
|
|
1335
|
-
[np.zeros(int(self.nperseg // 2)), x, np.zeros(int(self.nperseg // 2))],
|
|
1336
|
-
axis=0,
|
|
1337
|
-
),
|
|
1338
|
-
fs=fs,
|
|
1339
|
-
detrend="constant",
|
|
1340
|
-
scaling="spectrum",
|
|
1341
|
-
nfft=None,
|
|
1342
|
-
window=np.hamming(self.nfft),
|
|
1343
|
-
noverlap=(self.nperseg - 1),
|
|
1344
|
-
)
|
|
1345
|
-
lowerliminpts = tide_util.valtoindex(self.freqs, self.lowerlim)
|
|
1346
|
-
upperliminpts = tide_util.valtoindex(self.freqs, self.upperlim)
|
|
1347
|
-
|
|
1348
|
-
if self.debug:
|
|
1349
|
-
print(self.times.shape, self.freqs.shape, thespectrogram.shape)
|
|
1350
|
-
print(self.times)
|
|
1351
|
-
|
|
1352
|
-
# initialize the peak fitter
|
|
1353
|
-
thefitter = SimilarityFunctionFitter(
|
|
1354
|
-
corrtimeaxis=self.freqs,
|
|
1355
|
-
lagmin=self.lowerlim,
|
|
1356
|
-
lagmax=self.upperlim,
|
|
1357
|
-
absmaxsigma=10.0,
|
|
1358
|
-
absminsigma=0.1,
|
|
1359
|
-
debug=self.debug,
|
|
1360
|
-
peakfittype="fastquad",
|
|
1361
|
-
zerooutbadfit=False,
|
|
1362
|
-
useguess=False,
|
|
1363
|
-
)
|
|
1364
|
-
|
|
1365
|
-
peakfreqs = np.zeros((thespectrogram.shape[1] - 1), dtype=float)
|
|
1366
|
-
for i in range(0, thespectrogram.shape[1] - 1):
|
|
1367
|
-
(
|
|
1368
|
-
maxindex,
|
|
1369
|
-
peakfreqs[i],
|
|
1370
|
-
maxval,
|
|
1371
|
-
maxsigma,
|
|
1372
|
-
maskval,
|
|
1373
|
-
failreason,
|
|
1374
|
-
peakstart,
|
|
1375
|
-
peakend,
|
|
1376
|
-
) = thefitter.fit(thespectrogram[:, i])
|
|
1377
|
-
if not (lowerliminpts <= maxindex <= upperliminpts):
|
|
1378
|
-
peakfreqs[i] = -1.0
|
|
1379
|
-
|
|
1380
|
-
return self.times[:-1], peakfreqs
|
|
1381
|
-
|
|
1382
|
-
def clean(self, x, fs, times, peakfreqs, numharmonics=2):
|
|
1383
|
-
nyquistfreq = 0.5 * fs
|
|
1384
|
-
y = x * 0.0
|
|
1385
|
-
halfwidth = int(self.nperseg // 2)
|
|
1386
|
-
padx = np.concatenate([np.zeros(halfwidth), x, np.zeros(halfwidth)], axis=0)
|
|
1387
|
-
pady = np.concatenate([np.zeros(halfwidth), y, np.zeros(halfwidth)], axis=0)
|
|
1388
|
-
padweight = padx * 0.0
|
|
1389
|
-
if self.debug:
|
|
1390
|
-
print(fs, len(times), len(peakfreqs))
|
|
1391
|
-
for i in range(0, len(times)):
|
|
1392
|
-
centerindex = int(times[i] * fs)
|
|
1393
|
-
xstart = centerindex - halfwidth
|
|
1394
|
-
xend = centerindex + halfwidth
|
|
1395
|
-
if peakfreqs[i] > 0.0:
|
|
1396
|
-
filtsignal = padx[xstart:xend]
|
|
1397
|
-
numharmonics = np.min([numharmonics, int((nyquistfreq // peakfreqs[i]) - 1)])
|
|
1398
|
-
if self.debug:
|
|
1399
|
-
print("numharmonics:", numharmonics, nyquistfreq // peakfreqs[i])
|
|
1400
|
-
for j in range(numharmonics + 1):
|
|
1401
|
-
workingfreq = (j + 1) * peakfreqs[i]
|
|
1402
|
-
if self.debug:
|
|
1403
|
-
print("workingfreq:", workingfreq)
|
|
1404
|
-
ws = [workingfreq * 0.95, workingfreq * 1.05]
|
|
1405
|
-
wp = [workingfreq * 0.9, workingfreq * 1.1]
|
|
1406
|
-
gpass = 1.0
|
|
1407
|
-
gstop = 40.0
|
|
1408
|
-
b, a = sp.signal.iirdesign(wp, ws, gpass, gstop, ftype="cheby2", fs=fs)
|
|
1409
|
-
if self.debug:
|
|
1410
|
-
print(
|
|
1411
|
-
i,
|
|
1412
|
-
j,
|
|
1413
|
-
times[i],
|
|
1414
|
-
centerindex,
|
|
1415
|
-
halfwidth,
|
|
1416
|
-
xstart,
|
|
1417
|
-
xend,
|
|
1418
|
-
xend - xstart,
|
|
1419
|
-
wp,
|
|
1420
|
-
ws,
|
|
1421
|
-
len(a),
|
|
1422
|
-
len(b),
|
|
1423
|
-
)
|
|
1424
|
-
filtsignal = sp.signal.filtfilt(b, a, sp.signal.filtfilt(b, a, filtsignal))
|
|
1425
|
-
pady[xstart:xend] += filtsignal
|
|
1426
|
-
else:
|
|
1427
|
-
pady[xstart:xend] += padx[xstart:xend]
|
|
1428
|
-
padweight[xstart:xend] += 1.0
|
|
1429
|
-
return (pady / padweight)[halfwidth:-halfwidth]
|