Skip to content

truncate_corrupted_offsets() dropping valid samples #176

Closed

Description

@jamieforth

I've also experienced labstreaminglayer/pylsl#67, so was happy to see this new feature (#150, #158, #160). However, in my data it seems to drop valid samples more often than I'd like!

A quick fix could be to expose the time_thresh and value_thresh parameters in the top-level load_xdf().

But digging a little deeper, good samples seem to be dropped most often in streams running on the same host as LabRecorder. As these typically have tiny clock offsets (±4e-05) it's quite easy for reasonable jitter (e.g. ±2e-05) to be flagged as an outlier according to the MAD threshold.

As I understand it _detect_corrupted_clock_offset() is effectively looking for a clock reset between the entire clock offset history and the final measurement. As such, significant drift and genuine clock resets could also bias the statistic. The logic is similar to _detect_clock_resets(), but an important difference there is we take the first derivative of clock offsets as the basis for MAD.

Adding some np.diff() calls to "Strategy 2" in _detect_corrupted_clock_offset() seems to restore my dropped samples, but I'm not sure to what extent the current implementation is necessary in addition to what _detect_clock_resets() already does.

So I don't have a PR but I pushed a couple of tests to try and think this through.

Activity

  1. cbrnr commented on Sep 10, 2026

    @cbrnr
    Contributor

    Hmmm, thanks for the input and the tests. We should think about the current mechanism and how we could make it more robust. I had the impression that this was a fix for something very obviously wrong, but if this is affecting (i.e., dropping) perfectly valid samples, we should come up with a better solution ASAP. @mscheltienne WDYT?

  2. jamieforth commented on Sep 10, 2026

    @jamieforth
    ContributorAuthor

    I should add the data I'm looking at was recorded prior to labstreaminglayer/App-LabRecorder#123, so all my footer sample counts are off-by-one - making this partly a backwards compatibility issue.

    As far as I can see #167 makes the existing _detect_clock_resets() handle corrupt offsets correctly, without dropping any samples. However, this doesn't guarantee the final sample is valid, beyond ensuring it's detected as part of a different clock segment if the sample time-stamp is closer to the start of the corrupt clock segment than the end of the valid clock segment.

    If your data requires continuing to handle corrupt offsets as a special case, one possibility to avoid adding two new parameters to load_xdf() could be to use _detect_clock_resets() instead of the dedicated _detect_corrupted_clock_offset() function. Or at least make the calculation of MAD consistent across both functions so they can both use the existing reset_threshold* parameters?

    FWIW I just tried replacing this:

            if len(stream.clock_times) >= 3:
                clock_corrupted = _detect_corrupted_clock_offset(
                    stream.clock_times, stream.clock_values
                )
    

    with:

            clock_segments = _detect_clock_resets(
                stream,
                reset_threshold_stds,
                reset_threshold_seconds,
                reset_threshold_offset_stds,
                reset_threshold_offset_seconds
            )
            if len(clock_segments) > 0:
                final_segment = clock_segments[-1]
                if final_segment[0] == final_segment[1]:
                    # Redundant (=corrupt) final clock segment.
                    clock_corrupted = True
    

    and the existing truncate_corrupted_offsets tests still passed.

  3. mscheltienne commented on Sep 29, 2026

    @mscheltienne
    Contributor

    Hey, @jamieforth - can you give a shot to #178 ?
    On our file (I actually manage to find only one of the files I used in the past..) it seems to work while removing the truncation entirely thanks to a better fix in #167

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions