ibl-neuropixel 1.9.3__py3-none-any.whl → 1.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ibl_neuropixel-1.9.3.dist-info → ibl_neuropixel-1.10.0.dist-info}/METADATA +2 -2
- {ibl_neuropixel-1.9.3.dist-info → ibl_neuropixel-1.10.0.dist-info}/RECORD +17 -15
- {ibl_neuropixel-1.9.3.dist-info → ibl_neuropixel-1.10.0.dist-info}/WHEEL +1 -1
- ibldsp/fourier.py +2 -0
- ibldsp/sync.py +147 -0
- ibldsp/utils.py +23 -58
- ibldsp/voltage.py +197 -74
- neuropixel.py +136 -88
- spikeglx.py +39 -12
- tests/unit/test_ephys_np2.py +76 -106
- tests/unit/test_neuropixel.py +37 -0
- tests/unit/test_spikeglx.py +20 -7
- tests/unit/test_sync.py +104 -0
- tests/unit/test_utils.py +0 -49
- tests/unit/test_voltage.py +159 -19
- {ibl_neuropixel-1.9.3.dist-info → ibl_neuropixel-1.10.0.dist-info}/licenses/LICENSE +0 -0
- {ibl_neuropixel-1.9.3.dist-info → ibl_neuropixel-1.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ibl-neuropixel
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: Collection of tools for Neuropixel 1.0 and 2.0 probes data
|
|
5
5
|
Home-page: https://github.com/int-brain-lab/ibl-neuropixel
|
|
6
6
|
Author: The International Brain Laboratory
|
|
@@ -36,7 +36,7 @@ Collection of tools to handle Neuropixel 1.0 and 2.0 data
|
|
|
36
36
|
|
|
37
37
|
## Installation
|
|
38
38
|
Minimum Python version supported is 3.10
|
|
39
|
-
`pip install ibl-neuropixel`
|
|
39
|
+
`uv pip install ibl-neuropixel`
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
## Destriping
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
neuropixel.py,sha256=
|
|
2
|
-
spikeglx.py,sha256=
|
|
3
|
-
ibl_neuropixel-1.
|
|
1
|
+
neuropixel.py,sha256=Fp5J09CBtWtyyl4kuuJf3UE9KxS5YYbGMskfEck4A7M,39850
|
|
2
|
+
spikeglx.py,sha256=iyw-Gxqhi_ioVHqEhZmQgCSFiW1ysEkhwPxQmP70bPg,41939
|
|
3
|
+
ibl_neuropixel-1.10.0.dist-info/licenses/LICENSE,sha256=JJCjBeS78UPiX7TZpE-FnMjNNpCyrFb4s8VDGG2wD10,1087
|
|
4
4
|
ibldsp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
ibldsp/cadzow.py,sha256=pAtxDxBwoNhoxFNc2R5WLwUrmKsq4rQuaglRNgW2Lj8,7251
|
|
6
6
|
ibldsp/cuda_tools.py,sha256=6LpVhYOCuOXEEg8kJ3aOCE4hzA1Yq1dojsbbBQmQCF4,2387
|
|
7
7
|
ibldsp/destripe_gpu.py,sha256=I5jzFocpsYw36kMMd533YThbrQaZix5e1sHqsUjHvO4,2824
|
|
8
8
|
ibldsp/filter_gpu.py,sha256=DPrPBLRXeCh_6BcJWJnPFaxS9Q6kX4nPENZg-c2q5rc,5789
|
|
9
|
-
ibldsp/fourier.py,sha256=
|
|
9
|
+
ibldsp/fourier.py,sha256=mKg9Inv7N1vi-IxAArlwNOCHsuX7Dre02664K5Uy4ws,10648
|
|
10
10
|
ibldsp/icsd.py,sha256=y9NWOXBB4Nfb5A1fQMKlOu0PdVDVOZ39v2pwk2zzB84,44923
|
|
11
11
|
ibldsp/plots.py,sha256=XmYC4yca_seZYNEmC5hE5wBiJAl_fi_KU00DbNcM6jI,4577
|
|
12
12
|
ibldsp/raw_metrics.py,sha256=Ie4b7unuFc-XiFc9-tpTsUkph29G-20NvM7iJ25jAPI,5198
|
|
13
13
|
ibldsp/smooth.py,sha256=m_mByXHG_JyFErnYsZ27gXjcqpfwCEuWa6eOb9eFuyg,8033
|
|
14
14
|
ibldsp/spiketrains.py,sha256=lYP1PD4l6T-4KhFu8ZXlbnUUnEQLOriGxN1szacolPY,6878
|
|
15
|
-
ibldsp/
|
|
16
|
-
ibldsp/
|
|
15
|
+
ibldsp/sync.py,sha256=wBZgHVS3y3ih7zRcHy1KavITwV1VQz1F62Dy9iiehUQ,5210
|
|
16
|
+
ibldsp/utils.py,sha256=1-K7J7zzJtyYntwUylDTQmzzxpAQTQque9TAuIVYmVQ,17062
|
|
17
|
+
ibldsp/voltage.py,sha256=aSTl8v1gApkE7CCWjxdlHAuA_LK0M-tDkTMAffYPgMA,52622
|
|
17
18
|
ibldsp/waveform_extraction.py,sha256=yKrldgHqpwQ_Dq6xdoSCceKkfrL9FUXnpwKJUM3R41M,26570
|
|
18
19
|
ibldsp/waveforms.py,sha256=XKWO0sSEhZR1mBsXCdGpVU3ga96HX3CViXIgpl3bml8,35280
|
|
19
20
|
neurowaveforms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -23,14 +24,15 @@ tests/integration/csd_experiments.py,sha256=bddMl2SCzeEM_QnBrZGypUYMKxFVDc6qdery
|
|
|
23
24
|
tests/integration/test_destripe.py,sha256=ZV7gasuFib2AbVb63WczgQvc13PbIX9t4pQgamBMgRY,6161
|
|
24
25
|
tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
26
|
tests/unit/test_cadzow.py,sha256=Ky16lMe0guSQznlA1w76wSqQJ52P7LeYl7catAIb3Pg,681
|
|
26
|
-
tests/unit/test_ephys_np2.py,sha256=
|
|
27
|
-
tests/unit/test_neuropixel.py,sha256=
|
|
27
|
+
tests/unit/test_ephys_np2.py,sha256=dFPe53Btqxc0n2lScEUQ0gR-S0vkwnaH1YOsdA5zaG8,14198
|
|
28
|
+
tests/unit/test_neuropixel.py,sha256=c27B_jHhOH9vk8Rk-rAA7vGdeaw93SrS3YtmezrwTnU,3903
|
|
28
29
|
tests/unit/test_plots.py,sha256=PhCxrEN1Zd1jTgmiwd16_dEghcI7kwmHT3AQmAPpzkA,850
|
|
29
|
-
tests/unit/test_spikeglx.py,sha256=
|
|
30
|
-
tests/unit/
|
|
31
|
-
tests/unit/
|
|
30
|
+
tests/unit/test_spikeglx.py,sha256=BQ8dgCe-DczCmsa_xI0R7H-b33k49SWcucXiFWcSgEw,35110
|
|
31
|
+
tests/unit/test_sync.py,sha256=ak282Ela5SRRT3gohjOvj8xQQvttSvNuGcYQCUH2ABE,3779
|
|
32
|
+
tests/unit/test_utils.py,sha256=H2PSS5Y31-ZSJC-7n_PK6kOuJ9zdHZ_qIVTT4fM3K_o,20071
|
|
33
|
+
tests/unit/test_voltage.py,sha256=Za9bpHmpHrTyQz4KrUcstPOtQ5_tre9jgpJzQPIcChE,11469
|
|
32
34
|
tests/unit/test_waveforms.py,sha256=VnFvUi1pteROwwbC5Ebp2lqSxF3a8a7eXHpD8OUeuTg,16237
|
|
33
|
-
ibl_neuropixel-1.
|
|
34
|
-
ibl_neuropixel-1.
|
|
35
|
-
ibl_neuropixel-1.
|
|
36
|
-
ibl_neuropixel-1.
|
|
35
|
+
ibl_neuropixel-1.10.0.dist-info/METADATA,sha256=bdbrWgWJM0h3VjqDvF2ipPD88w7dpMPRjcIkjFgaKSQ,3750
|
|
36
|
+
ibl_neuropixel-1.10.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
37
|
+
ibl_neuropixel-1.10.0.dist-info/top_level.txt,sha256=WtVcEUptnwU6BT72cgGmrWYFGM9d9qCEqe3LwR9FIw4,48
|
|
38
|
+
ibl_neuropixel-1.10.0.dist-info/RECORD,,
|
ibldsp/fourier.py
CHANGED
ibldsp/sync.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import scipy.interpolate
|
|
6
|
+
|
|
7
|
+
import one.alf.io as alfio
|
|
8
|
+
|
|
9
|
+
import spikeglx
|
|
10
|
+
import ibldsp.utils
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def extract_spikeglx_sync(raw_ephys_apfile, output_path=None, save=False, parts=""):
|
|
14
|
+
"""
|
|
15
|
+
Extracts sync.times, sync.channels and sync.polarities from binary ephys dataset
|
|
16
|
+
|
|
17
|
+
:param raw_ephys_apfile: bin file containing ephys data or spike
|
|
18
|
+
:param output_path: output directory
|
|
19
|
+
:param save: bool write to disk only if True
|
|
20
|
+
:param parts: string or list of strings that will be appended to the filename before extension
|
|
21
|
+
:return:
|
|
22
|
+
"""
|
|
23
|
+
# handles input argument: support ibllib.io.spikeglx.Reader, str and pathlib.Path
|
|
24
|
+
if isinstance(raw_ephys_apfile, spikeglx.Reader):
|
|
25
|
+
sr = raw_ephys_apfile
|
|
26
|
+
else:
|
|
27
|
+
raw_ephys_apfile = Path(raw_ephys_apfile)
|
|
28
|
+
sr = spikeglx.Reader(raw_ephys_apfile)
|
|
29
|
+
|
|
30
|
+
SYNC_BATCH_SIZE_SECS = 100
|
|
31
|
+
|
|
32
|
+
if not (opened := sr.is_open):
|
|
33
|
+
sr.open()
|
|
34
|
+
# if no output, need a temp folder to swap for big files
|
|
35
|
+
if output_path is None:
|
|
36
|
+
output_path = sr.file_bin.parent
|
|
37
|
+
file_ftcp = Path(output_path).joinpath(
|
|
38
|
+
f"fronts_times_channel_polarity{uuid.uuid4()}.bin"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# loop over chunks of the raw ephys file
|
|
42
|
+
wg = ibldsp.utils.WindowGenerator(
|
|
43
|
+
sr.ns, int(SYNC_BATCH_SIZE_SECS * sr.fs), overlap=1
|
|
44
|
+
)
|
|
45
|
+
fid_ftcp = open(file_ftcp, "wb")
|
|
46
|
+
for sl in wg.slice:
|
|
47
|
+
ss = sr.read_sync(sl)
|
|
48
|
+
ind, fronts = ibldsp.utils.fronts(ss, axis=0)
|
|
49
|
+
# a = sr.read_sync_analog(sl)
|
|
50
|
+
sav = np.c_[(ind[0, :] + sl.start) / sr.fs, ind[1, :], fronts.astype(np.double)]
|
|
51
|
+
sav.tofile(fid_ftcp)
|
|
52
|
+
# close temp file, read from it and delete
|
|
53
|
+
fid_ftcp.close()
|
|
54
|
+
tim_chan_pol = np.fromfile(str(file_ftcp))
|
|
55
|
+
tim_chan_pol = tim_chan_pol.reshape((int(tim_chan_pol.size / 3), 3))
|
|
56
|
+
file_ftcp.unlink()
|
|
57
|
+
sync = {
|
|
58
|
+
"times": tim_chan_pol[:, 0],
|
|
59
|
+
"channels": tim_chan_pol[:, 1],
|
|
60
|
+
"polarities": tim_chan_pol[:, 2],
|
|
61
|
+
}
|
|
62
|
+
# If opened Reader was passed into function, leave open
|
|
63
|
+
if not opened:
|
|
64
|
+
sr.close()
|
|
65
|
+
if save:
|
|
66
|
+
out_files = alfio.save_object_npy(
|
|
67
|
+
output_path, sync, "sync", namespace="spikeglx", parts=parts
|
|
68
|
+
)
|
|
69
|
+
return sync, out_files
|
|
70
|
+
else:
|
|
71
|
+
return sync
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
|
|
75
|
+
"""
|
|
76
|
+
Sync two arrays of time stamps
|
|
77
|
+
:param tsa: vector of timestamps
|
|
78
|
+
:param tsb: vector of timestamps
|
|
79
|
+
:param tbin: time bin length
|
|
80
|
+
:param return_indices (bool), if True returns 2 sets of indices for tsa and tsb with
|
|
81
|
+
:param linear: (bool) if True, restricts the fit to linear
|
|
82
|
+
identified matches
|
|
83
|
+
:return:
|
|
84
|
+
function: interpolation function such as fnc(tsa) = tsb
|
|
85
|
+
float: drift in ppm
|
|
86
|
+
numpy array: of indices ia
|
|
87
|
+
numpy array: of indices ib
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
def _interp_fcn(tsa, tsb, ib, linear=linear):
|
|
91
|
+
# now compute the bpod/fpga drift and precise time shift
|
|
92
|
+
ab = np.polyfit(tsa[ib >= 0], tsb[ib[ib >= 0]] - tsa[ib >= 0], 1)
|
|
93
|
+
drift_ppm = ab[0] * 1e6
|
|
94
|
+
if linear:
|
|
95
|
+
fcn_a2b = lambda x: x * (1 + ab[0]) + ab[1] # noqa
|
|
96
|
+
else:
|
|
97
|
+
fcn_a2b = scipy.interpolate.interp1d(
|
|
98
|
+
tsa[ib >= 0], tsb[ib[ib >= 0]], fill_value="extrapolate"
|
|
99
|
+
)
|
|
100
|
+
return fcn_a2b, drift_ppm
|
|
101
|
+
|
|
102
|
+
# assert sorted inputs
|
|
103
|
+
tmin = np.min([np.min(tsa), np.min(tsb)])
|
|
104
|
+
tmax = np.max([np.max(tsa), np.max(tsb)])
|
|
105
|
+
# brute force correlation to get an estimate of the delta_t between series
|
|
106
|
+
x = np.zeros(int(np.ceil(tmax - tmin) / tbin))
|
|
107
|
+
y = np.zeros_like(x)
|
|
108
|
+
x[np.int32(np.floor((tsa - tmin) / tbin))] = 1
|
|
109
|
+
y[np.int32(np.floor((tsb - tmin) / tbin))] = 1
|
|
110
|
+
delta_t = (
|
|
111
|
+
ibldsp.utils.parabolic_max(scipy.signal.correlate(x, y, mode="full"))[0]
|
|
112
|
+
- x.shape[0]
|
|
113
|
+
+ 1
|
|
114
|
+
) * tbin
|
|
115
|
+
# do a first assignment at a DT threshold
|
|
116
|
+
ib = np.zeros(tsa.shape, dtype=np.int32) - 1
|
|
117
|
+
threshold = tbin
|
|
118
|
+
for m in np.arange(tsa.shape[0]):
|
|
119
|
+
dt = np.abs(tsa[m] - delta_t - tsb)
|
|
120
|
+
inds = np.where(dt < threshold)[0]
|
|
121
|
+
if inds.size == 1:
|
|
122
|
+
ib[m] = inds[0]
|
|
123
|
+
elif inds.size > 1:
|
|
124
|
+
candidates = inds[~np.isin(inds, ib[:m])]
|
|
125
|
+
if candidates.size == 1:
|
|
126
|
+
ib[m] = candidates[0]
|
|
127
|
+
elif candidates.size > 1:
|
|
128
|
+
ib[m] = inds[np.argmin(dt[inds])]
|
|
129
|
+
|
|
130
|
+
fcn_a2b, _ = _interp_fcn(tsa, tsb, ib)
|
|
131
|
+
# do a second assignment - this time a full matrix of candidate matches is computed
|
|
132
|
+
# the most obvious matches are assigned first and then one by one
|
|
133
|
+
iamiss = np.where(ib < 0)[0]
|
|
134
|
+
ibmiss = np.setxor1d(np.arange(tsb.size), ib[ib >= 0])
|
|
135
|
+
dt = np.abs(fcn_a2b(tsa[iamiss]) - tsb[ibmiss][:, np.newaxis])
|
|
136
|
+
dt[dt > tbin] = np.nan
|
|
137
|
+
while ~np.all(np.isnan(dt)):
|
|
138
|
+
_b, _a = np.unravel_index(np.nanargmin(dt), dt.shape)
|
|
139
|
+
ib[iamiss[_a]] = ibmiss[_b]
|
|
140
|
+
dt[:, _a] = np.nan
|
|
141
|
+
dt[_b, :] = np.nan
|
|
142
|
+
fcn_a2b, drift_ppm = _interp_fcn(tsa, tsb, ib, linear=linear)
|
|
143
|
+
|
|
144
|
+
if return_indices:
|
|
145
|
+
return fcn_a2b, drift_ppm, np.where(ib >= 0)[0], ib[ib >= 0]
|
|
146
|
+
else:
|
|
147
|
+
return fcn_a2b, drift_ppm
|
ibldsp/utils.py
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
Window generator, front detections, rms
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import warnings
|
|
6
|
+
|
|
5
7
|
import numpy as np
|
|
6
8
|
import scipy
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
|
|
10
12
|
"""
|
|
13
|
+
.. deprecated:: X.Y.Z
|
|
14
|
+
`sync_timestamps` has been moved to `ibldsp.sync` module.
|
|
15
|
+
Use `from ibldsp.sync import sync_timestamps` instead.
|
|
16
|
+
This function will be removed in version X.Y.Z.
|
|
17
|
+
|
|
11
18
|
Sync two arrays of time stamps
|
|
19
|
+
|
|
12
20
|
:param tsa: vector of timestamps
|
|
13
21
|
:param tsb: vector of timestamps
|
|
14
22
|
:param tbin: time bin length
|
|
@@ -18,66 +26,23 @@ def sync_timestamps(tsa, tsb, tbin=0.1, return_indices=False, linear=False):
|
|
|
18
26
|
:return:
|
|
19
27
|
function: interpolation function such as fnc(tsa) = tsb
|
|
20
28
|
float: drift in ppm
|
|
21
|
-
numpy array: of indices ia
|
|
22
|
-
numpy array: of indices ib
|
|
29
|
+
numpy array: of indices ia (if return_indices=True)
|
|
30
|
+
numpy array: of indices ib (if return_indices=True)
|
|
23
31
|
"""
|
|
32
|
+
warnings.warn(
|
|
33
|
+
"sync_timestamps has been moved to ibldsp.sync module and will be removed "
|
|
34
|
+
"from ibldsp.utils in version X.Y.Z. "
|
|
35
|
+
"Please use 'from ibldsp.sync import sync_timestamps' instead.",
|
|
36
|
+
DeprecationWarning,
|
|
37
|
+
stacklevel=2,
|
|
38
|
+
)
|
|
24
39
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
else:
|
|
32
|
-
fcn_a2b = scipy.interpolate.interp1d(
|
|
33
|
-
tsa[ib >= 0], tsb[ib[ib >= 0]], fill_value="extrapolate"
|
|
34
|
-
)
|
|
35
|
-
return fcn_a2b, drift_ppm
|
|
36
|
-
|
|
37
|
-
# assert sorted inputs
|
|
38
|
-
tmin = np.min([np.min(tsa), np.min(tsb)])
|
|
39
|
-
tmax = np.max([np.max(tsa), np.max(tsb)])
|
|
40
|
-
# brute force correlation to get an estimate of the delta_t between series
|
|
41
|
-
x = np.zeros(int(np.ceil(tmax - tmin) / tbin))
|
|
42
|
-
y = np.zeros_like(x)
|
|
43
|
-
x[np.int32(np.floor((tsa - tmin) / tbin))] = 1
|
|
44
|
-
y[np.int32(np.floor((tsb - tmin) / tbin))] = 1
|
|
45
|
-
delta_t = (
|
|
46
|
-
parabolic_max(scipy.signal.correlate(x, y, mode="full"))[0] - x.shape[0] + 1
|
|
47
|
-
) * tbin
|
|
48
|
-
# do a first assignment at a DT threshold
|
|
49
|
-
ib = np.zeros(tsa.shape, dtype=np.int32) - 1
|
|
50
|
-
threshold = tbin
|
|
51
|
-
for m in np.arange(tsa.shape[0]):
|
|
52
|
-
dt = np.abs(tsa[m] - delta_t - tsb)
|
|
53
|
-
inds = np.where(dt < threshold)[0]
|
|
54
|
-
if inds.size == 1:
|
|
55
|
-
ib[m] = inds[0]
|
|
56
|
-
elif inds.size > 1:
|
|
57
|
-
candidates = inds[~np.isin(inds, ib[:m])]
|
|
58
|
-
if candidates.size == 1:
|
|
59
|
-
ib[m] = candidates[0]
|
|
60
|
-
elif candidates.size > 1:
|
|
61
|
-
ib[m] = inds[np.argmin(dt[inds])]
|
|
62
|
-
|
|
63
|
-
fcn_a2b, _ = _interp_fcn(tsa, tsb, ib)
|
|
64
|
-
# do a second assignment - this time a full matrix of candidate matches is computed
|
|
65
|
-
# the most obvious matches are assigned first and then one by one
|
|
66
|
-
iamiss = np.where(ib < 0)[0]
|
|
67
|
-
ibmiss = np.setxor1d(np.arange(tsb.size), ib[ib >= 0])
|
|
68
|
-
dt = np.abs(fcn_a2b(tsa[iamiss]) - tsb[ibmiss][:, np.newaxis])
|
|
69
|
-
dt[dt > tbin] = np.nan
|
|
70
|
-
while ~np.all(np.isnan(dt)):
|
|
71
|
-
_b, _a = np.unravel_index(np.nanargmin(dt), dt.shape)
|
|
72
|
-
ib[iamiss[_a]] = ibmiss[_b]
|
|
73
|
-
dt[:, _a] = np.nan
|
|
74
|
-
dt[_b, :] = np.nan
|
|
75
|
-
fcn_a2b, drift_ppm = _interp_fcn(tsa, tsb, ib, linear=linear)
|
|
76
|
-
|
|
77
|
-
if return_indices:
|
|
78
|
-
return fcn_a2b, drift_ppm, np.where(ib >= 0)[0], ib[ib >= 0]
|
|
79
|
-
else:
|
|
80
|
-
return fcn_a2b, drift_ppm
|
|
40
|
+
# Import and delegate to the new location
|
|
41
|
+
from ibldsp.sync import sync_timestamps as _sync_timestamps
|
|
42
|
+
|
|
43
|
+
return _sync_timestamps(
|
|
44
|
+
tsa, tsb, tbin=tbin, return_indices=return_indices, linear=linear
|
|
45
|
+
)
|
|
81
46
|
|
|
82
47
|
|
|
83
48
|
def parabolic_max(x):
|