|
1 |
| -# Authors: Clemens Brunner |
| 1 | +# Authors: Clemens Brunner, Alberto Barradas |
2 | 2 | # License: BSD (3-clause)
|
3 | 3 |
|
4 | 4 | module XDF
|
@@ -185,4 +185,59 @@ function sync_clock(time::Array{Float64,1}, offsets::Array{Float64,2})
|
185 | 185 | return time .+ (coefs[1] .+ coefs[2] .* time)
|
186 | 186 | end
|
187 | 187 |
|
| 188 | +""" |
| 189 | + dejitter(stream::Dict, max_time::Float64=1, max_samples::Int=500) |
| 190 | + Calculate timestamps assuming constant intervals within each continuous segment in a stream. Chooses the minimum of the time difference and the number of samples as indicator for a new segment. |
| 191 | + args: |
| 192 | + stream: Dict |
| 193 | + Stream dictionary. |
| 194 | + max_time: Float64 |
| 195 | + Maximum time difference between two consecutive samples (default: 1 second). |
| 196 | + max_samples: Int |
| 197 | + Maximum number of samples in a segment (default: 500 samples). |
| 198 | + return: |
| 199 | + Dict: Stream dictionary with updated timestamps. |
| 200 | +
|
| 201 | +Example: |
| 202 | +```julia |
| 203 | +stream = read_xdf(Downloads.download("https://github.com/xdf-modules/example-files/blob/master/data_with_clock_resets.xdf?raw=true"))[2] |
| 204 | +stream = dejitter(stream, 1.0, 500) # process segments with a maximum time difference of 1 second or 500 samples |
| 205 | +stream["segments"] # list of segments |
| 206 | +stream["nominal_srate"] # recalculated nominal sampling rate |
| 207 | +``` |
| 208 | +""" |
| 209 | +function dejitter(stream::Dict; max_time::Float64=1.0, max_samples::Int=500) |
| 210 | + srate = stream["srate"] |
| 211 | + if srate == 0 |
| 212 | + @warn "Attempting to dejitter marker streams or streams with zero sampling rate. Skipping." |
| 213 | + return stream |
| 214 | + end |
| 215 | + nsamples = size(stream["data"], 1) |
| 216 | + if nsamples == 0 |
| 217 | + @warn "Attempting to dejitter empty stream. Skipping." |
| 218 | + return stream |
| 219 | + end |
| 220 | + stream["nominal_srate"] = 0 # Recalculated if possible |
| 221 | + stream["segments"] = [] |
| 222 | + time = stream["time"] |
| 223 | + breaks = [1; findall(diff(time) .> min.(max_time, max_samples .* (1 / srate)));] |
| 224 | + seg_starts = breaks |
| 225 | + seg_ends = [breaks[2:end] .- 1; nsamples] |
| 226 | + for (start, stop) in zip(seg_starts, seg_ends) |
| 227 | + push!(stream["segments"], (start, stop)) |
| 228 | + idx = [start:stop;] |
| 229 | + X = hcat(ones(length(idx)), time[idx]) |
| 230 | + y = time[idx] |
| 231 | + coefs = X \ y |
| 232 | + stream["time"][idx] = coefs[1] .+ coefs[2] .* time[idx] |
| 233 | + end |
| 234 | + # Recalculate nominal sampling rate |
| 235 | + counts = (seg_ends .- seg_starts) .+ 1 |
| 236 | + durations = diff([time[seg_starts]; time[seg_ends[end]]]) |
| 237 | + stream["nominal_srate"] = sum(counts) / sum(durations) |
| 238 | + if stream["srate"] != 0 && abs(stream["srate"] - stream["nominal_srate"]) > 1e-1 |
| 239 | + @warn "After dejittering: Nominal sampling rate differs from specified rate: $(stream["nominal_srate"]) vs. $(stream["srate"]) Hz" |
| 240 | + end |
| 241 | + return stream |
| 242 | +end |
188 | 243 | end
|
0 commit comments