Realign two simultaneous recordings. Due to clock drift, recordings at a given same sample rate made by two separate devices simultaneously can become out of sync over time. This function uses event times captured by both acquisition devices to resample ``other`` to match ``raw``.
(raw, other, t_raw, t_other, *, verbose=None)
| 12 | |
| 13 | @verbose |
| 14 | def realign_raw(raw, other, t_raw, t_other, *, verbose=None): |
| 15 | """Realign two simultaneous recordings. |
| 16 | |
| 17 | Due to clock drift, recordings at a given same sample rate made by two |
| 18 | separate devices simultaneously can become out of sync over time. This |
| 19 | function uses event times captured by both acquisition devices to resample |
| 20 | ``other`` to match ``raw``. |
| 21 | |
| 22 | Parameters |
| 23 | ---------- |
| 24 | raw : instance of Raw |
| 25 | The first raw instance. |
| 26 | other : instance of Raw |
| 27 | The second raw instance. It will be resampled to match ``raw``. |
| 28 | t_raw : array-like, shape (n_events,) |
| 29 | The times of shared events in ``raw`` relative to ``raw.times[0]`` (0). |
| 30 | Typically these could be events on some TTL channel such as:: |
| 31 | |
| 32 | find_events(raw)[:, 0] / raw.info["sfreq"] - raw.first_time |
| 33 | t_other : array-like, shape (n_events,) |
| 34 | The times of shared events in ``other`` relative to ``other.times[0]``. |
| 35 | %(verbose)s |
| 36 | |
| 37 | Notes |
| 38 | ----- |
| 39 | This function operates inplace. It will: |
| 40 | |
| 41 | 1. Estimate the zero-order (start offset) and first-order (clock drift) |
| 42 | correction. |
| 43 | 2. Crop the start of ``raw`` or ``other``, depending on which started |
| 44 | recording first. |
| 45 | 3. Resample ``other`` to match ``raw`` based on the clock drift. |
| 46 | 4. Realign the onsets and durations in ``other.annotations``. |
| 47 | 5. Crop the end of ``raw`` or ``other``, depending on which stopped |
| 48 | recording first (and the clock drift rate). |
| 49 | |
| 50 | This function is primarily designed to work on recordings made at the same |
| 51 | sample rate, but it can also operate on recordings made at different |
| 52 | sample rates to resample and deal with clock drift simultaneously. |
| 53 | |
| 54 | .. versionadded:: 0.22 |
| 55 | """ |
| 56 | _validate_type(raw, BaseRaw, "raw") |
| 57 | _validate_type(other, BaseRaw, "other") |
| 58 | t_raw = np.array(t_raw, float) |
| 59 | t_other = np.array(t_other, float) |
| 60 | if t_raw.ndim != 1 or t_raw.shape != t_other.shape: |
| 61 | raise ValueError( |
| 62 | "t_raw and t_other must be 1D with the same shape, " |
| 63 | f"got shapes {t_raw.shape} and {t_other.shape}" |
| 64 | ) |
| 65 | if len(t_raw) < 20: |
| 66 | warn("Fewer than 20 times passed, results may be unreliable") |
| 67 | |
| 68 | # 1. Compute correction factors |
| 69 | poly = Polynomial.fit(x=t_other, y=t_raw, deg=1) |
| 70 | converted = poly.convert(domain=(-1, 1)) |
| 71 | [zero_ord, first_ord] = converted.coef |