Skip to content

Fix SourceChGain/SourceChOffset handling in BCI2k reader#13869

Open
HansujaB wants to merge 9 commits intomne-tools:mainfrom
HansujaB:SourceChGain-and-SourceChOffset
Open

Fix SourceChGain/SourceChOffset handling in BCI2k reader#13869
HansujaB wants to merge 9 commits intomne-tools:mainfrom
HansujaB:SourceChGain-and-SourceChOffset

Conversation

@HansujaB
Copy link
Copy Markdown
Contributor

@HansujaB HansujaB commented Apr 26, 2026

Reference issue

Fixes #13851

What does this implement/fix?

  • Parse BCI2000 *list parameters correctly by treating the first token as list length.
  • Parse numeric values with embedded units via a fn _parse_value_with_unit
  • I have removed fn _parse_sampling_rate and used the above fn for that as well
  • Apply SourceChOffset and SourceChGain calibration per channel so EEG data is returned in volts.

Tests

  • Updated BCI2k reader tests to check real-file header parsing for:
    • SourceChOffset == ["0", "0"]
    • SourceChGain == ["0.1muV", "0.1muV"]
  • Wrote a unit test for unit parsing checks for voltage and frequency suffixes.

Questions

  • Do we want the unit parser test (test_parse_value_with_unit) ?
  • Do we also want a test to check scaling is right?
  • Should I edit examples/io/read_bci2k.py as well?

Copy link
Copy Markdown
Contributor

@nordme nordme left a comment

Choose a reason for hiding this comment

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

Hi @HansujaB ! Thanks for the PR!

I think that the unit parser test function is a good idea, and in general we want to have complete test coverage of our code, so I think checking scaling is a good idea too. Actually, I would expand the coverage of the existing test you wrote to check error raising in the parsing function. Generally, an important feature of test functions is providing meaningful information about the type of failure that occurs when a test fails (so that other maintainers can figure what went wrong). pytest.raises() calls are useful for that purpose. I added an example below.

assert _parse_value_with_unit("0.1muV", unit_scale=volt_scale) == (0.1, 1e-6)
assert _parse_value_with_unit("2mV", unit_scale=volt_scale) == (2.0, 1e-3)
assert _parse_value_with_unit("-3.5µV", unit_scale=volt_scale) == (-3.5, 1e-6)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
with pytest.raises(ValueError, match="Could not parse numeric value"):
_parse_value_with_unit("....", unit_scale=volt_scale)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @nordme , Thanks a lot for reviewing the PR , will push the changes after expanding the test fns in some time.
Also do we need changes in examples/io/bci2k.py notebook as well?

Comment thread mne/io/bci2k/bci2k.py Outdated
def _parse_value_with_unit(token, unit_scale=None):
"""Split a numeric token with optional unit into value and scale."""
text = str(token).strip().replace("µ", "u")
m = re.search(r"([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*([a-zA-Z]*)", text)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you could get away with something much simpler, like r"[a-zA-Z]+$" to match the unit, then cut the string before/after result.start() to get the numeric and unit parts.

Copy link
Copy Markdown
Contributor Author

@HansujaB HansujaB May 3, 2026

Choose a reason for hiding this comment

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

Really Sorry for late reply !
I have made this function simpler, also I have assumed if no unit is present for SamplingRate we will take Hz and for SourceChGain we will take µV, as per the BCI2000 technical Ref which states SourceChGain is the factor to convert raw AD units into µV.
image

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.

SourceChGain and SourceChOffset not applied - BCI2k reader

3 participants