Skip to content

Conversation

@Arnav1709
Copy link

Reference issue (if any)

Fixes #13429.

What does this implement/fix?

This PR addresses a critical bug where MNE-Python would silently load EDF+D (discontinuous) and BDF+D files with acquisition gaps between data records, treating them as continuous data. This could lead to:

  • Incorrect time alignment: The RawEDF.times array would show continuous timestamps, ignoring gaps
  • Missing annotations: Annotations occurring after gaps could be dropped or incorrectly positioned
  • Silent data corruption: Users would be unaware that their data contains discontinuities

Changes made:

  1. Detection of EDF+D/BDF+D files: Modified _read_edf_header() to read and check the 44-byte reserved header field for EDF+D or BDF+D markers.

  2. Gap detection: Added two helper functions:

    • _get_tal_record_times(): Extracts onset times of each data record from TAL (Time-stamped Annotation List) annotations
    • _check_edf_discontinuity(): Compares expected vs. actual record timestamps to detect gaps
  3. Error handling: Modified both RawEDF and RawBDF classes to check for discontinuities after reading TAL data. When gaps are detected, a NotImplementedError is raised with:

    • Clear explanation of the issue
    • Location of gaps (onset time and duration)
    • Suggestions for alternatives (luna/lunapi or conversion to EDF+C/BDF+C)
  4. Backward compatibility: EDF+D/BDF+D files that are marked as discontinuous but have no actual gaps (e.g., from some Nihon Kohden systems) continue to load normally.

  5. Documentation: Updated docstrings for read_raw_edf() and read_raw_bdf() to document this limitation.

  6. Tests: Added comprehensive tests covering:

    • Detection of gaps in EDF+D files
    • Successful loading of continuous EDF+C files
    • Successful loading of EDF+D files without actual gaps
    • Edge cases in gap detection logic

Additional information

  • This is a breaking change in behavior: files that previously loaded (incorrectly) will now raise an error. However, this is intentional as the previous behavior was incorrect and could lead to data analysis errors.

  • The error message provides actionable guidance, suggesting specialized tools (luna/lunapi) for handling discontinuous files or converting to continuous format if gaps are not significant.

  • The implementation follows the EDF+ specification for detecting discontinuous files and uses TAL annotations (which are required in EDF+ files) to detect actual gaps.

  • Related discussion in the GitHub issue suggests that full support for EDF+D would require filling gaps with NaN data (similar to how Eyelink handles gaps), which is a more complex feature that could be implemented in a future PR.

Detect and raise NotImplementedError when loading EDF+D or BDF+D files
that contain actual gaps between data records, instead of silently
treating them as continuous data. This prevents incorrect time alignment.

Fixes mne-tools#13429
@welcome
Copy link

welcome bot commented Jan 9, 2026

Hello! 👋 Thanks for opening your first pull request here! ❤️ We will try to get back to you soon. 🚴

Copy link
Contributor

@scott-huberty scott-huberty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Arnav1709 thanks for getting this started!

  • This PR adds about 400 LOC, and so more rigorous testing of the added code would be helpful to reviewers. From a quick check, it looks like only 5% of the LOC added in this PR are covered by tests . In MNE-Python we try to keep code coverage at 95% minimum.

  • This PR doesn't actually add a feature to read EDF+D files, rather it detects if such files contain acquisition gaps, and raises an error? It's a step in the right direction, but I would be interested in hearing from other devs wrt whether it is worth merging this check, or whether we should wait for a PR that actually implements reading EDF+D files.

  • Out of curiosity, why did you attribute commit 694fcaf to @larsoner ?

- Validate n_records matches len(record_times), raise ValueError on mismatch
- Validate record_times are numeric (no strings or invalid types)
- Check for non-finite values (NaN, inf) and raise ValueError
- Warn and sort if record_times are not monotonically increasing
- Add comprehensive tests for all validation scenarios
@Arnav1709 Arnav1709 force-pushed the fix/edf-d-discontinuous-detection branch from cf0ef6b to dbec29c Compare January 14, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

read_raw_edf silently loads EDF+D files without accounting for time gaps/acquisition skips between records

3 participants