mne_rt.RTStream#

class mne_rt.RTStream(subject_id: str, session: str, subjects_dir: str, montage: str | None, data_type: str = 'eeg', mri: bool = False, subject_fs_id: str = 'fsaverage', subjects_fs_dir: str | None = None, bandpass_freq: tuple | None = None, notch_freq: float | list | None = None, artifact_correction: bool | str = False, save_nf_signal: bool = True, config_file: str | None = None, verbose: bool | str | None = None)[source]#

Bases: ModalityMixin

Real-time Real-time M/EEG session controller.

Orchestrates LSL streaming, optional artifact rejection, parallel feature extraction, and real-time visualisation for a complete neurofeedback session. Inherits all feature-extraction methods from ModalityMixin.

Parameters:
subject_idstr

Unique subject identifier (non-empty string). Used as the BIDS subject label (e.g. "sub01" → folder sub-sub01/).

sessionstr

BIDS session label (e.g. "01", "pre", "week1"). Used to name output files and directories following the BIDS convention sub-<ID>_ses-<session>_task-<task>.

subjects_dirstr

Root directory that holds one sub-folder per subject.

montagestr | None

EEG montage — a MNE built-in name (e.g. "easycap-M1"), a path to a .bvct CapTrak file, or None for MEG.

data_type{“eeg”, “meg”}, default “eeg”

Recording modality. Controls channel selection and forward model.

mribool, default False

If True, individual MRI anatomy is used for source localisation instead of the fsaverage template.

subject_fs_idstr, default “fsaverage”

FreeSurfer subject identifier. Use "fsaverage" for template- based source localisation.

subjects_fs_dirstr | None, default None

FreeSurfer subjects directory. Required when subject_fs_id != "fsaverage" or when show_brain_activation is requested.

bandpass_freqtuple(float, float) | None, default None

Online band-pass filter applied to the LSL stream before feature extraction, as (l_freq, h_freq) in Hz. None disables band-pass filtering. Example: (1.0, 40.0) for a standard EEG band-pass.

notch_freqfloat | list[float] | None, default None

One or more frequencies (Hz) to suppress with an IIR notch filter. None disables notch filtering. Example: 50 or [50, 100] for 50 Hz power-line interference and its harmonic.

artifact_correction{False, “lms”, “orica”, “gedai”, “asr”, “maxwell”}, default False

Real-time artifact correction strategy applied sample-by-sample inside the acquisition loop.

save_nf_signalbool, default True

Save extracted feature time-series as JSON.

config_filestr | None, default None

Path to a YAML configuration file. None uses the bundled default (config_methods.yml).

verbosebool | str | None, default None

Verbosity level. Mirrors MNE’s convention: True/"INFO" → informational, False/"WARNING" → warnings only, "DEBUG" → all messages.

Raises:
ValueError

If any constructor argument fails validation.

See also

ant.modalities.ModalityMixin

All supported NF feature methods.

mne_rt.viz.NFPlot

Scrolling real-time NF signal display.

mne_rt.viz.RawPlot

Scrolling raw M/EEG channel viewer (bad-channel / bad-segment marking).

mne_rt.viz.EpochPlot

Scrolling raw viewer with trigger/epoch overlays.

mne_rt.viz.BrainPlot

3D brain activation display.

Notes

Typical workflow:

nf = RTStream("sub01", session="01",
                subjects_dir="/data/subjects",
                montage="easycap-M1")
nf.connect_to_lsl()
nf.record_baseline(baseline_duration=120)
nf.record_main(duration=600, modality=["sensor_power", "erd_ers"])

The main main loop runs M/EEG acquisition in a background daemon thread and drives all visualisation windows (StreamViewer, signal plot, brain plot) from the Qt event loop on the main thread via a 33 ms pump timer, ensuring all three windows are truly parallel and non-blocking.

Added in version 1.0.0.

__init__(subject_id: str, session: str, subjects_dir: str, montage: str | None, data_type: str = 'eeg', mri: bool = False, subject_fs_id: str = 'fsaverage', subjects_fs_dir: str | None = None, bandpass_freq: tuple | None = None, notch_freq: float | list | None = None, artifact_correction: bool | str = False, save_nf_signal: bool = True, config_file: str | None = None, verbose: bool | str | None = None) None[source]#

Methods

__init__(subject_id, session, subjects_dir, ...)

compute_inv_operator([loose, depth, ...])

Compute and save the inverse operator for source localisation.

connect_to_lsl([chunk_size, mock_lsl, ...])

Connect to an LSL M/EEG stream.

create_report([overwrite, include_psd, ...])

Generate an HTML MNE report for the session.

fit_asr([cutoff, window_len, ...])

Fit an ASRDenoiser from the recorded baseline.

fit_gedai([band, shrinkage, use_leadfield, ...])

Fit a GEDAIDenoiser from the recorded baseline.

fit_maxwell([int_order, ext_order, origin, ...])

Prepare real-time Maxwell filtering (SSS / tSSS) for MEG data.

get_blink_template([max_iter, method, ...])

Identify the eye-blink ICA component from baseline EEG.

load_nf_data(path)

Load a saved NF data file produced by save().

open_stream_viewer([bufsize])

Open the mne-lsl StreamViewer for raw M/EEG monitoring.

record_baseline(baseline_duration[, ...])

Record a resting-state baseline segment.

record_main(duration[, modality, picks, ...])

Stream M/EEG, extract neural features, and drive NF visualisation.

replay(fname[, modality, duration, winsize, ...])

Replay a saved recording as a mock LSL session.

run_blocks(blocks[, rest_duration, verbose])

Run multiple NF blocks separated by rest periods.

run_orica(n_channels[, learning_rate, ...])

Initialise an Online Recursive ICA (ORICA) instance.

save([nf_data, acq_delay, artifact_delay, ...])

Save session outputs and disconnect the LSL stream.

Attributes

modality_params

Per-modality parameter overrides applied during the NF session.

connect_to_lsl(chunk_size: int = 10, mock_lsl: bool = False, fname: str | None = None, n_repeat: int | float = inf, bufsize_baseline: int = 4, bufsize_main: int = 3, acquisition_delay: float = 0.001, timeout: float = 15.0, stream_name: str | None = None, stream_source_id: str | None = None, pick_types: str | None = None, verbose: bool | str | None = None) None[source]#

Connect to an LSL M/EEG stream.

Parameters:
chunk_sizeint, default 10

Samples per chunk for mock streaming.

mock_lslbool, default False

Stream a pre-recorded file instead of live hardware. Requires fname or uses the bundled sample file.

fnamestr | None, default None

Path to any MNE-readable recording for mock streaming (.fif, .vhdr, .edf, .bdf, .set, …). None uses the bundled sample recording.

n_repeatint | float, default np.inf

How many times to loop the mock recording.

bufsize_baselineint, default 4

LSL buffer size in seconds for baseline sessions.

bufsize_mainint, default 3

LSL buffer size in seconds for main sessions.

acquisition_delayfloat, default 0.001

Seconds between acquisition polling attempts.

timeoutfloat, default 15.0

Maximum wait time in seconds for the LSL connection.

stream_namestr | None, default None

Connect by stream name (e.g. neuromag2lsl for MEG devices).

stream_source_idstr | None, default None

Connect by stream source ID.

pick_typesstr | None, default None

Channel type to keep (e.g. "eeg", "mag"). None keeps all available channels.

verbosebool | str | None, default None

Override the instance-level verbosity for this call.

Raises:
RuntimeError

If no LSL stream matching the given criteria is found within timeout seconds.

Notes

All public methods of mne_lsl.stream.StreamLSL are also exposed directly on the RTStream instance after connection.

Examples

Connect to a live EEG amplifier:

nf.connect_to_lsl()

Simulate from any MNE-readable file:

nf.connect_to_lsl(mock_lsl=True, fname="path/to/data.fif")
nf.connect_to_lsl(mock_lsl=True, fname="path/to/data.edf")
record_baseline(baseline_duration: float, winsize: float = 3.0, verbose: bool | str | None = None) None[source]#

Record a resting-state baseline segment.

Collects baseline_duration seconds of M/EEG, stores it as raw_baseline, saves it to disk, and computes the inverse operator (stored as inv).

Parameters:
baseline_durationfloat

Total recording duration in seconds.

winsizefloat, default 3.0

Duration in seconds of each data fetch chunk.

verbosebool | str | None, default None

Override the instance-level verbosity for this call.

Notes

Output files written under <subjects_dir>/sub-<ID>/ses-<session>/:

  • eeg/sub-<ID>_ses-<session>_task-baseline_eeg.fif

  • inv/sub-<ID>_ses-<session>_task-baseline_inv.fif

  • inv/sub-<ID>_ses-<session>_task-baseline_fwd.fif

  • inv/sub-<ID>_ses-<session>_task-baseline_cov.fif

Examples

>>> nf.record_baseline(baseline_duration=120)
record_main(duration: float, modality: str | list[str] = 'sensor_power', picks: str | list[str] | None = None, winsize: float = 1.0, estimate_delays: bool = False, modality_params: dict[str, Any] | None = None, show_raw_signal: bool = True, show_nf_signal: bool = True, time_window: float = 10.0, show_topo: bool = False, topo_bands: dict | None = None, show_brain_activation: bool = False, brain_surf: str = 'inflated', brain_mode: str = 'power', brain_freq_range: tuple[float, float] = (8.0, 13.0), zscore_normalize: bool = False, zscore_warmup: int = 10, zscore_alpha: float = 0.0, osc_sender: Any | None = None, lsl_sender: Any | None = None, protocol: Any | None = None, save_raw: bool = False, ref_channel: str = 'Fp1', signal_smoothing: float = 0.25, display_smoothing: float = 0.3, topo_display_smoothing: float = 1.0, brain_display_smoothing: float = 0.3, track_artifact_rate: bool = True, artifact_threshold_uv: float = 100.0, track_snr: bool = False, snr_frange: tuple | None = None, verbose: bool | str | None = None) None[source]#

Stream M/EEG, extract neural features, and drive NF visualisation.

This is the main closed-loop entry point. It:

  1. Prepares each requested modality (calls _<modality>_prep).

  2. Opens the selected visualisation windows (StreamViewer, signal plot, brain plot) on the main thread.

  3. Starts a background daemon thread that continuously fetches data, runs artifact correction, and computes features in parallel via a thread pool.

  4. Drives all windows from the Qt event loop via a 33 ms pump timer.

  5. Saves raw data and feature time-series to disk when finished.

Parameters:
durationfloat

Total recording length in seconds.

modalitystr | list of str, default “sensor_power”

NF feature(s) to extract. Must match keys in config_methods.yml. Multiple modalities are extracted in parallel. Available modalities: "sensor_power", "band_ratio", "erd_ers", "laterality", "laterality_erd_ers", "hjorth", "spectral_centroid", "argmax_freq", "individual_peak_power", "entropy", "instantaneous_phase", "scp", "peak_alpha_freq", "sensor_connectivity", "cfc_sensor", "sensor_graph", "connectivity_ratio", "source_power", "source_connectivity", "source_graph".

picksstr | list of str | None, default None

Channel selection passed to the LSL stream. None uses all available channels. Must be None for source-space modalities.

winsizefloat, default 1.0

Analysis window length in seconds.

estimate_delaysbool, default False

Measure and save per-step timing (acquisition, artifact correction, feature extraction).

modality_paramsdict | None, default None

Per-modality parameter overrides. Keys are modality names; values are dicts of {parameter: new_value} pairs that override the config-file defaults.

show_raw_signalbool, default True

Show the RawPlot scrolling raw M/EEG viewer.

show_nf_signalbool, default True

Show the NFPlot real-time NF monitor.

time_windowfloat, default 10.0

Visible time range in seconds for the signal plot.

show_topobool, default False

Show the TopomapPlot real-time scalp topomap display. Requires the montage to be set on the channel info.

topo_bandsdict | None, default None

Frequency bands to show in the topomap as {label: (f_low, f_high)}. None uses the default δ/θ/α/β/γ bands.

show_brain_activationbool, default False

Show the BrainPlot 3D brain activation display (requires subjects_fs_dir and a fitted inverse operator).

brain_surf{“inflated”, “pial”, “white”, “sphere”}, default “pial”

Cortical surface geometry for the brain display. "pial" shows the true cortical folding; "inflated" unfolds gyri/sulci for easier label inspection.

brain_mode{“power”, “activation”}, default “power”

Source-space display mode. "power" shows mean squared amplitude; "activation" shows mean amplitude.

brain_freq_range(float, float), default (8.0, 13.0)

Frequency band in Hz used to band-pass the data before computing source power for the brain display.

zscore_normalizebool, default False

Apply online z-score normalisation to each NF feature value before storing and displaying it. During the first zscore_warmup windows the raw value is passed through unchanged; once warmup completes, each value is normalised as z = (x μ) / σ where μ and σ are estimated from the warmup windows. If zscore_alpha > 0 the statistics are updated after every window with an exponential moving average so the normaliser slowly tracks drift.

zscore_warmupint, default 10

Number of windows to collect before activating normalisation. The mean and standard deviation of these windows are used as the initial statistics.

zscore_alphafloat, default 0.0

EMA forgetting factor for updating μ and σ each window. 0.0 freezes statistics after warmup (recommended for most NF protocols). Values in [0.01, 0.1] add slow adaptation.

osc_senderOSCSender | None, default None

If provided, each computed NF value is also broadcast over OSC to the configured host/port after every update cycle. See OSCSender.

lsl_senderLSLSender | None, default None

If provided, each computed NF value is pushed into an LSL stream outlet after every update cycle. Faster and more reliable than OSC for same-machine feedback delivery. See LSLSender.

protocolProtocol instance | dict | None, default None

Real-time NF reward protocol evaluated on every analysis window. Pass a single Protocol instance (e.g. ThresholdProtocol) to apply it to the first modality, or a {modality_name: protocol} dict to apply different protocols to different modalities. On each window the protocol’s evaluate(value) method is called and (crossed, magnitude) is recorded. Results are accessible via reward_data after the session.

save_rawbool, default False

Persist the raw pre-correction M/EEG acquired during the main session to raw/<stem>-raw.fif. Off by default because FIF files can be large; enable when the raw continuous signal is needed for offline re-analysis or provenance.

signal_smoothingfloat, default 0.25

Exponential moving average (EMA) factor applied to each NF feature value before it is stored and displayed. Controls the trade-off between signal smoothness and responsiveness:

  • 1.0 — no smoothing; raw per-window estimate passed through.

  • 0.5 — moderate smoothing; each value is 50 % new + 50 % history.

  • 0.1 — heavy smoothing; very slow response to rapid changes.

The EMA is applied after z-score normalisation (if enabled) and before protocol evaluation, so protocols see the smoothed value.

display_smoothingfloat, default 0.3

Additional EMA factor applied only inside the live signal plot. Does not affect stored nf_data or protocol evaluation. Lower values give a smoother, slower-reacting display curve; 1.0 disables this extra layer and shows the already signal_smoothing-filtered values directly.

topo_display_smoothingfloat, default 1.0

EMA factor for the TopomapPlot band-power maps. 1.0 (default) disables smoothing so transient artifacts remain visible for operator monitoring. Lower values progressively smooth the spatial maps across consecutive windows.

brain_display_smoothingfloat, default 0.3

EMA factor for the BrainPlot per-vertex activation arrays. Blends consecutive frames so the cortical map transitions smoothly. 1.0 disables smoothing.

ref_channelstr, default “Fp1”

Reference channel used for LMS artifact correction (artifact_correction="lms" only). Ignored for all other correction methods.

track_artifact_ratebool, default True

If True, count windows whose peak-to-peak amplitude exceeds artifact_threshold_uv and store the fraction as artifact_rate at the end of the session.

artifact_threshold_uvfloat, default 100.0

Peak-to-peak amplitude threshold in µV used to classify a window as artifactual when track_artifact_rate=True.

track_snrbool, default False

If True, compute a per-window signal-to-noise ratio (band power in snr_frange divided by broadband noise power, in dB) and store the resulting time-series as snr_data.

snr_frangetuple(float, float) | None, default None

Frequency band (f_low, f_high) in Hz used as the “signal” band when track_snr=True. None defaults to the alpha band (8.0, 13.0).

verbosebool | str | None, default None

Override the instance-level verbosity for this call.

Raises:
NotImplementedError

If a requested modality is not implemented.

ValueError

If a source-space modality is requested together with non-None picks.

RuntimeError

If show_brain_activation=True but subjects_fs_dir is not set, or if artifact_correction="gedai" but fit_gedai() has not been called.

Notes

Output files written under <subjects_dir>/sub-<ID>/ses-<session>/:

  • beh/sub-<ID>_ses-<session>_task-neurofeedback_beh.json — NF feature time-series plus session metadata

  • delays/sub-<ID>_ses-<session>_task-neurofeedback_delays.json — per-step timing (only when estimate_delays=True)

  • eeg/sub-<ID>_ses-<session>_task-neurofeedback_eeg.fif — pre-correction M/EEG (only when save_raw=True)

Examples

Single-modality alpha-power NF with brain activation:

nf.record_main(
    duration=300,
    modality="sensor_power",
    show_brain_activation=True,
)

Multi-modality session with custom parameters:

nf.record_main(
    duration=600,
    modality=["sensor_power", "erd_ers", "laterality"],
    modality_params={"sensor_power": {"frange": [10, 12]}},
    show_nf_signal=True,
)

Added in version 1.0.0.

replay(fname: str, modality: str | list[str] = 'sensor_power', duration: float | None = None, winsize: float = 1.0, verbose: bool | str | None = None, **record_main_kwargs) None[source]#

Replay a saved recording as a mock LSL session.

Loads a pre-recorded M/EEG file (any MNE-readable format), streams it via PlayerLSL at its native sampling rate, and passes the live stream through record_main() — exercising the full real-time pipeline (artifact correction, feature extraction, protocol evaluation) without live hardware.

Useful for:

  • Offline parameter tuning (test different protocols on the same data).

  • Verifying pipeline latency and modality behaviour.

  • Reproducing a session with modified parameters.

Parameters:
fnamestr

Path to any MNE-readable recording (.fif, .vhdr, .edf, .bdf, .set, …).

modalitystr | list of str, default “sensor_power”

NF modality(ies) to extract.

durationfloat | None, default None

Duration of replay in seconds. None infers the full recording length from the file.

winsizefloat, default 1.0

Analysis window length in seconds.

verbosebool | str | None, default None

Override instance verbosity for this call.

**record_main_kwargs

Additional keyword arguments forwarded to record_main() (e.g. protocol, modality_params, track_snr).

Examples

Replay a saved EEG file and run a ZScore protocol offline:

from mne_rt.protocols import ZScoreProtocol
nf.replay(
    "sub-01/ses-01/eeg/sub-01_ses-01_task-neurofeedback_eeg.fif",
    modality="sensor_power",
    protocol=ZScoreProtocol(),
    show_nf_signal=False,
    show_raw_signal=False,
)
print(f"Artifact rate: {nf.artifact_rate:.1%}")

Added in version 1.0.0.

run_blocks(blocks: list[dict], rest_duration: float = 30.0, verbose: bool | str | None = None) list[dict][source]#

Run multiple NF blocks separated by rest periods.

Each block calls record_main() with the parameters given in the corresponding dict. Blocks are separated by rest_duration seconds of silence (no acquisition, no feedback).

Parameters:
blockslist of dict

Each dict is passed as keyword arguments to record_main(). The key "rest" (optional) overrides rest_duration for the pause after that block. All other keys must be valid record_main() parameters.

Minimal example:

blocks = [
    {"duration": 120, "modality": "sensor_power"},
    {"duration": 120, "modality": "sensor_power", "rest": 60},
    {"duration": 120, "modality": "sensor_power"},
]
rest_durationfloat, default 30.0

Default inter-block rest period in seconds.

verbosebool | str | None, default None

Override instance verbosity for this call.

Returns:
all_nf_datalist of dict

One dict per block, each matching nf_data from that block’s record_main() call. Also stored as block_nf_data.

Notes

save() is called internally at the end of each block by record_main(). The returned list lets you inspect per-block feature time-series without re-loading JSON files.

Examples

Three 2-minute NF runs separated by 30-second rests:

nf.connect_to_lsl(mock_lsl=True, fname="recording.fif")
nf.record_baseline(baseline_duration=60)
results = nf.run_blocks(
    blocks=[
        {"duration": 120, "modality": "sensor_power",
         "show_nf_signal": False, "show_raw_signal": False},
        {"duration": 120, "modality": "sensor_power",
         "show_nf_signal": False, "show_raw_signal": False},
        {"duration": 120, "modality": "sensor_power",
         "show_nf_signal": False, "show_raw_signal": False},
    ],
    rest_duration=30.0,
)
for i, block_data in enumerate(results):
    vals = block_data["sensor_power"]
    print(f"Block {i+1}: {len(vals)} windows, mean={sum(vals)/len(vals):.4f}")

Added in version 1.0.0.

property modality_params: dict#

Per-modality parameter overrides applied during the NF session.

A flat or nested dict that maps modality keys (e.g. "sensor_power") to keyword arguments forwarded to the corresponding feature extractor. None is accepted on assignment and normalised to {}.

Examples

>>> nf.modality_params = {"sensor_power": {"frange": [10, 12]},
...                       "erd_ers": {"frange": [8, 13]}}

Identify the eye-blink ICA component from baseline EEG.

Sets self.blink_template (ndarray) — the spatial template vector for the blink component, used by artifact correction during the main session.

Parameters:
max_iterint, default 800

Maximum number of ICA fitting iterations.

methodstr, default “infomax”

ICA algorithm passed to mne.preprocessing.ICA. Common choices: "infomax", "fastica", "picard".

n_componentsint | float | None, default 0.95

Number of PCA components before ICA. A float in (0, 1) retains enough components to explain that fraction of variance. None uses all channels. Falls back to 5 if the initial fit fails.

fit_paramsdict | None, default None

Extra keyword arguments forwarded to the ICA solver. None defaults to {"extended": True} for infomax (recommended).

random_stateint | None, default 0

Seed for reproducible ICA solutions.

iclabel_thresholdfloat, default 0.5

Minimum ICLabel probability for a component to be classified as "eye blink". Lower values are more permissive; higher values require stronger confidence.

run_orica(n_channels: int, learning_rate: float = 0.1, block_size: int = 256, online_whitening: bool = True, calibrate_pca: bool = False, forgetfac: float = 1.0, nonlinearity: str = 'tanh', random_state: int | None = None) None[source]#

Initialise an Online Recursive ICA (ORICA) instance.

ORICA is a streaming, adaptive ICA algorithm that updates its unmixing matrix incrementally as each new EEG block arrives — without ever storing the full recording. It is the preferred real-time alternative to offline ICA when the signal statistics change over time (non-stationarity).

Parameters:
n_channelsint

Number of EEG/MEG channels. Must match the channel count of the data passed to each subsequent partial_fit() or fit_transform() call.

learning_ratefloat, default 0.1

Step size for the online natural-gradient update of the unmixing matrix W. Larger values adapt faster but may oscillate; values in [0.01, 0.2] are typically stable.

block_sizeint, default 256

Number of samples per update block. Smaller blocks give finer temporal resolution at the cost of noisier gradient estimates. Must be ≥ n_channels.

online_whiteningbool, default True

If True, a recursive PCA whitening step is applied to each block before the ICA update, keeping the algorithm numerically stable as signal variance drifts.

calibrate_pcabool, default False

If True, run a batch PCA on the first block to initialise the whitening matrix before switching to online updates. Recommended when starting cold with no prior covariance estimate.

forgetfacfloat, default 1.0

Exponential forgetting factor for the online covariance estimate (1.0 = no forgetting; 0.99 → slowly decaying influence of older samples). Values < 1 help track gradual changes in the mixing matrix.

nonlinearitystr, default “tanh”

Score function used in the natural-gradient ICA update. "tanh" works well for super-Gaussian sources (spikes, blinks); "logcosh" is a smooth approximation with similar properties.

random_stateint | None, default None

Seed for reproducible initialisation of the unmixing matrix W. None uses a random seed.

See also

ant.tools.ORICA

The underlying ORICA implementation.

RTStream.get_blink_template

Compute a blink spatial template to guide component identification.

Notes

run_orica() is called automatically inside record_baseline() when artifact_correction="orica" is set on the RTStream instance. Call it manually only if you need to tune the ORICA hyperparameters.

fit_gedai(band: tuple[float, float] = (8.0, 13.0), shrinkage: float = 0.01, use_leadfield: bool = True, verbose: bool | str | None = None) None[source]#

Fit a GEDAIDenoiser from the recorded baseline.

Must be called after record_baseline() and before record_main() when artifact_correction="gedai".

Parameters:
bandtuple of float, default (8.0, 13.0)

Target frequency band (low_Hz, high_Hz) used when fitting in band-filter mode (use_leadfield=False). Ignored when use_leadfield=True.

shrinkagefloat, default 0.01

Tikhonov regularisation strength applied to the reference covariance before solving the generalised eigenvalue problem. Larger values improve numerical stability at the cost of slightly less discriminative spatial filters.

use_leadfieldbool, default True

If True and a forward solution is available (i.e., compute_inv_operator() has been called), fit in leadfield mode — the true GEDAI algorithm (Ros et al., 2025). The forward gain matrix \(\mathbf{L}\) is used as the reference covariance \(\mathbf{R} = \mathbf{L}\mathbf{L}^\top\), so components that best explain the theoretical brain-source model are kept and non-leadfield-aligned components (artifacts) are removed.

If False, or if no forward solution is available, falls back to band-filter mode: the GEP is solved between the band-filtered and broadband EEG covariances (Cohen, 2022).

verbosebool | str | None, default None

Override the instance-level verbosity for this call.

Raises:
RuntimeError

If record_baseline() has not been called yet.

See also

ant.tools.GEDAIDenoiser

The underlying GED denoiser class.

RTStream.compute_inv_operator

Computes the forward solution required for leadfield mode.

References

Ros, T., Férat, V., Huang, Y., et al. (2025). Return of the GEDAI: Unsupervised EEG Denoising based on Leadfield Filtering. bioRxiv. https://doi.org/10.1101/2025.10.04.680449

Examples

Leadfield mode (recommended — requires forward solution):

>>> nf.record_baseline(baseline_duration=120)
>>> nf.compute_inv_operator()          # builds the forward model
>>> nf.fit_gedai(use_leadfield=True)
>>> nf.record_main(duration=600, artifact_correction="gedai")

Band-filter mode (no MRI required):

>>> nf.record_baseline(baseline_duration=120)
>>> nf.fit_gedai(band=(8, 13), use_leadfield=False)
>>> nf.record_main(duration=600, artifact_correction="gedai")
fit_asr(cutoff: float = 5.0, window_len: float = 1.0, max_dropout_fraction: float = 0.1, verbose: bool | str | None = None) None[source]#

Fit an ASRDenoiser from the recorded baseline.

Must be called after record_baseline() and before record_main() when artifact_correction="asr".

Parameters:
cutofffloat, default 5.0

Rejection threshold in standard deviations above the clean-data RMS per component. Lower values (e.g. 3) are more aggressive; higher values (e.g. 10) are more conservative. The Mullen et al. (2015) default is 5.0.

window_lenfloat, default 1.0

Calibration window length in seconds. Shorter windows give more estimates but with higher variance. Recommend 0.5–1.0 s.

max_dropout_fractionfloat, default 0.1

Fraction of calibration windows with the highest total power to discard before estimating clean statistics. 0.1 keeps the 90 % cleanest windows.

verbosebool | str | None, default None

Override the instance-level verbosity for this call.

Raises:
RuntimeError

If record_baseline() has not been called yet.

See also

ant.tools.ASRDenoiser

The underlying ASR implementation.

RTStream.record_baseline

Records and stores the baseline segment.

References

Mullen, T. R., et al. (2015). Real-Time Neuroimaging and Cognitive Monitoring Using Wearable Dry EEG. IEEE Trans. Biomed. Eng., 62(11), 2553–2567.

Examples

>>> nf.record_baseline(baseline_duration=120)
>>> nf.fit_asr(cutoff=5.0)
>>> nf.record_main(duration=600, artifact_correction="asr")
fit_maxwell(int_order: int = 8, ext_order: int = 3, origin: str | tuple = 'auto', st_duration: float | None = None, st_correlation: float = 0.98, st_update_interval: int = 1, calibration: str | None = None, cross_talk: str | None = None, coord_frame: str = 'head', regularize: str | None = 'in', mag_scale: float = 100.0, empty_room_raw: Any | None = None, verbose: bool | str | None = None) None[source]#

Prepare real-time Maxwell filtering (SSS / tSSS) for MEG data.

Computes the Signal Space Separation (SSS) projection operator once from sensor geometry. No baseline recording is required — the SSS basis is entirely geometric. Call this method at any point before record_main() when artifact_correction="maxwell".

Parameters:
int_orderint, default 8

Internal spherical-harmonic expansion order. 8 → 80 internal moments (MNE default, adequate for all standard MEG systems).

ext_orderint, default 3

External expansion order (3 → 16 external moments).

originarray_like of shape (3,) | “auto”, default “auto”

SSS expansion origin in metres (head frame). "auto" places it at the geometric centre of the sensor array.

st_durationfloat | None, default None

Temporal SSS (tSSS) buffer duration in seconds.

  • None — spatial SSS only. The pre-computed projector is applied per chunk via a single matrix multiply.

  • float — tSSS mode. A rolling buffer of st_duration seconds feeds MNE’s full tSSS every st_update_interval chunks for temporal interference suppression. The spatial SSS projector is always applied first (zero-latency stage 1).

Typical values: 10 s for persistent shielding leakage, 1–4 s for moving subjects.

st_correlationfloat, default 0.98

Minimum inside-outside correlation for tSSS suppression.

st_update_intervalint, default 1

Apply tSSS every N chunks. Increase to reduce CPU load when winsize is small.

calibrationstr | None, default None

Path to the fine-calibration .dat file. Strongly recommended for Elekta/MEGIN systems — it corrects sensor position and orientation errors.

cross_talkstr | None, default None

Path to the cross-talk .fif file. Compensates for flux leakage between adjacent sensors.

coord_frame{“head”, “meg”}, default “head”

Coordinate frame for the spherical-harmonic expansion.

regularize{“in”, None}, default “in”

Internal-moment regularisation passed to MNE. "in" (Tikhonov) is recommended for most datasets.

mag_scalefloat, default 100.0

Magnetometer/gradiometer balance factor in the SSS decomposition.

empty_room_rawmne.io.Raw | None, default None

Empty-room recording (shielded room, no subject). When provided, the SSS operator is extracted via system identification so that noise-informed regularisation from the empty room is incorporated into the cached matrix. This improves suppression of spatially correlated sensor noise and is equivalent to passing noise_cov to maxwell_filter().

When None, geometric regularisation only is used via compute_maxwell_basis() (faster, adequate when shielding is good).

verbosebool | str | None, default None

Override instance-level verbosity.

Raises:
RuntimeError

If no LSL stream has been connected yet (connect_to_lsl() must be called first so that self.rec_info is populated).

ValueError

If this instance was initialised with data_type="eeg" (Maxwell filtering is MEG-only).

Notes

See RTMaxwellFilter for the underlying filter class and fit_asr() for the EEG alternative (ASR).

Unlike fit_asr() and fit_gedai(), no baseline recording is needed. The SSS operator depends only on sensor positions, not on brain signal statistics. Simply call:

nf.connect_to_lsl()
nf.fit_maxwell()          # operator ready in seconds
nf.record_main(duration=600, artifact_correction="maxwell")

If a fine-calibration file is available, passing it via calibration typically reduces the RMS noise floor by 10–20 % compared to the uncalibrated SSS.

References

Taulu, S., Kajola, M., & Simola, J. (2004). Suppression of interference and artifacts by the Signal Space Separation Method. Brain Topogr., 16(4), 269–275.

Taulu, S., & Simola, J. (2006). Spatiotemporal signal space separation method for rejecting nearby interference in MEG measurements. Phys. Med. Biol., 51(7), 1759–1768.

Examples

SSS-only (fastest, no latency):

>>> nf.connect_to_lsl()
>>> nf.fit_maxwell()
>>> nf.record_main(duration=600, artifact_correction="maxwell")

tSSS with fine calibration and empty room:

>>> import mne
>>> er_raw = mne.io.read_raw_fif("empty_room.fif", preload=True)
>>> nf.connect_to_lsl()
>>> nf.fit_maxwell(
...     st_duration=10.0,
...     calibration="sss_cal.dat",
...     cross_talk="ct_sparse.fif",
...     empty_room_raw=er_raw,
... )
>>> nf.record_main(duration=600, artifact_correction="maxwell")
compute_inv_operator(loose: float = 0.2, depth: float = 0.8, noise_cov_method: str = 'ad_hoc', reg: float = 0.1) None[source]#

Compute and save the inverse operator for source localisation.

Wraps MNE’s forward-solution and inverse-operator pipeline. Results are saved to <subjects_dir>/<subject_id>/inv/.

Parameters:
loosefloat, default 0.2

Orientation constraint for cortical source dipoles. 0 = fixed (normal to surface), 1 = fully free, 0.2 = loose (recommended — allows slight tangential component while favouring surface-normal currents).

depthfloat | None, default 0.8

Depth-weighting exponent to compensate for the MNE bias towards superficial sources. None disables depth weighting; 0.8 is the MNE default. Higher values suppress surface bias more aggressively.

noise_cov_methodstr, default “ad_hoc”

How to estimate the noise covariance used by the inverse operator.

  • "ad_hoc" (default)mne.make_ad_hoc_cov() creates a diagonal covariance from standard sensor-noise floors (1 µV for EEG, 20 fT for MEG grads, 200 fT/cm for MEG mags). Recommended when no dedicated noise recording is available: the resting-state baseline contains brain signal, not pure noise, so fitting an empirical covariance on it conflates signal and noise.

  • "empirical" — sample covariance from the baseline raw.

  • "shrunk" — Ledoit-Wolf shrinkage; more stable when n_channels ≈ n_samples.

  • "diagonal_fixed" — diagonal regularisation.

regfloat, default 0.1

Per-channel-type regularisation added to the noise covariance diagonal via mne.cov.regularize(). Applied only when noise_cov_method != "ad_hoc". Set to 0 to skip. Helps numerical stability when the baseline recording is short relative to the number of channels.

Raises:
RuntimeError

If record_baseline() has not been called yet.

See also

mne.make_inverse_operator

Underlying MNE function.

mne.compute_raw_covariance

Noise covariance estimation.

open_stream_viewer(bufsize: float = 0.2) None[source]#

Open the mne-lsl StreamViewer for raw M/EEG monitoring.

Parameters:
bufsizefloat, default 0.2

Display window size (s).

save(nf_data: bool = True, acq_delay: bool = True, artifact_delay: bool = True, method_delay: bool = True, raw_data: bool = False, bids_tsv: bool = False, format: str = 'json', delay_include_trace: bool = False) dict[str, Path][source]#

Save session outputs and disconnect the LSL stream.

All output files share the stem sub-<ID>_ses-<session> set at the start of record_main(), so multiple runs in a day never overwrite each other.

Parameters:
nf_databool, default True

Save feature time-series as beh/<stem>_task-neurofeedback_beh.json. The JSON contains a "meta" block (subject, modalities, sfreq, duration, artifact correction, artifact rate, SNR, start/end timestamps) and a "data" block with per-modality value lists. When track_snr=True was passed to record_main(), the per-window SNR series is included in "data" under the key "snr_db". Reward magnitudes delivered by the protocol are saved under "reward_<modality>" keys (one per modality).

acq_delaybool, default True

Include acquisition-loop timing in the delays file (only written when estimate_delays=True was set in record_main()).

artifact_delaybool, default True

Include artifact-correction timing in the delays file.

method_delaybool, default True

Include per-modality feature-extraction timing in the delays file.

raw_databool, default False

Save the pre-correction M/EEG acquired during the main session as eeg/<stem>_task-neurofeedback_eeg.fif.

bids_tsvbool, default False

Additionally write a BIDS-compliant tab-separated values file beh/<stem>_task-neurofeedback_beh.tsv alongside the JSON. Columns are: one per modality, reward_<modality> per modality, and snr_db when available. Each row is one analysis window. This file passes a BIDS validator and can be loaded directly by EEGLAB, Fieldtrip, or any TSV reader.

formatstr, default “json”

Serialisation format for NF data and delays. Currently only "json" is supported.

delay_include_tracebool, default False

Embed the full per-window delay trace (ms) in the delays JSON alongside the summary statistics. Can be large for long sessions.

Returns:
saveddict[str, Path]

Maps output type ("nf_data", "nf_tsv", "delays", "raw") to the saved file path. Only keys for files actually written are included.

Notes

Disconnects the LSL stream as a side effect — the stream is not needed after the session ends.

classmethod load_nf_data(path: str | Path) dict[source]#

Load a saved NF data file produced by save().

Parameters:
pathstr | Path

Path to a *_nf.json file.

Returns:
payloaddict

Dict with two keys:

  • "meta" — session metadata: subject ID, session type, modalities, sfreq, winsize, duration, n_windows, artifact correction, start/end timestamps.

  • "data"{modality: [values, …]} per-modality lists.

Examples

>>> d = RTStream.load_nf_data("subjects/sub-sub01/ses-01/beh/sub-sub01_ses-01_task-neurofeedback_beh.json")
>>> import numpy as np
>>> alpha = np.array(d["data"]["sensor_power"])
>>> print(f"Mean alpha power: {alpha.mean():.3e}")
>>> print(f"Session sfreq: {d['meta']['sfreq_hz']} Hz")
create_report(overwrite: bool = True, include_psd: bool = True, include_nf_signal: bool = True, open_browser: bool = False) Path[source]#

Generate an HTML MNE report for the session.

Produces a self-contained HTML file containing baseline recording info, sensor layouts, optional PSD, feature time-series, and brain-label diagrams for source-space modalities.

Parameters:
overwritebool, default True

Overwrite an existing report file with the same name.

include_psdbool, default True

If True, add a baseline power-spectral-density plot (1–40 Hz) to the report.

include_nf_signalbool, default True

If True and nf_data is populated (i.e., record_main() has been run), add a time-series plot of the NF feature values for each modality.

open_browserbool, default False

If True, open the saved report in the default web browser immediately after saving.

Returns:
report_pathpathlib.Path

Full path of the saved HTML report file.

Raises:
RuntimeError

If record_baseline() has not been called yet.