IntoMindLearn

Lab 2 · Middle school

BCI Flight Simulator

Can you steer a ship just by looking? Fly one, and find out how a computer can tell which light you are looking at.

This demo flickers

Flicker between about 3 and 60 times a second can trigger a seizure in people with photosensitive epilepsy. Don't play it or watch it if you have epilepsy, or if flickering light has ever made you feel unwell.

Whether you play or watch, sit back from the screen, in a well-lit room. If you feel strange while it runs, cover one eye with your hand, turn away from the screen, and tell your teacher.

Get ready

You need

  • An IntoMind One, charged, with its cable unplugged
  • A laptop or monitor with Chrome or Edge, never a projector or a big screen
  • A chair for the pilot, back from the screen, in a well-lit room
  • A clean microfiber, soft cotton or eyeglass cloth

Roles

Pilot
Wears the IntoMind One and flies by looking.
Caller
Names a light for the pilot to steer toward.
Recorder
Counts the right turns and the wrong ones.
Skeptic
Watches the pilot, and writes down every blink, glance away or movement.

Take turns, so everyone who wants to can be the pilot. In a group of three, the caller is the skeptic too.

Rules

  • Unplug the cable before anyone puts it on.
  • Flying is your choice. Every role is a real one.
  • If the flicker makes you feel strange, cover one eye, turn away and say so.
  • Wipe the electrodes between pilots with the soft cloth, since a rough one can scratch their gold coating.

Steps

  1. Put it on and connect

    The IntoMind One goes on the back of the head, in the middle, a little above the bump where the skull meets the neck: the same place as in Alpha Flower.

    Unplug the cable. The pilot sits down, back from the screen. Put it on with the strap around the head. Part the hair under each electrode so it touches skin, and press it down until it feels firm but not tight.

    Open the BCI Flight Simulator demo. Its first screen begins with the flicker warning: read it again. Then press Connect your IntoMind One, and choose yours from the list.

    You should seeConnected at the top right, and the ship with four lights, not yet flickering, at the edges of the screen.

  2. Hold still for ten seconds

    Look at the middle of the screen and sit still. For ten seconds, with the lights steady, the demo measures how much of each light's speed your brain makes on its own.

    You should seeA count from ten to zero in the middle of the screen. Then the lights start to flicker.

  3. Fly by looking

    PredictSteering by looking will be than steering with the keys.

    Look at a light to turn toward it. The ship turns for as long as you look, and flies on the way it points when you look away. Follow the coins to the next ring.

    The caller names a light, again and again. The recorder counts each time the ship turned toward it, and each time it turned another way.

    You should seeThe ship turning a second or two after you look at a light.

  4. Fly with your hands

    Fly one more round with the arrow keys. Which was easier, and which was faster?

    PilotTurned the right wayTurned the wrong wayEasier: eyes or keys?Skeptic's notes
    1
    2
    3
    4

    You should seeThe keys answer at once. The lights take a second or two.

What happened

What you see is handled first at the back of your brain, in the visual cortex. When you look at a light that flickers at a steady speed, the neurons there start pulsing at that same speed.

Each light in the demo flickers at its own speed: 8, 10, 12 or 15 times a second. So the speed that shows up in your EEG says which light you are looking at, and the ship turns toward it. The IntoMind One never watches your eyes.

The ten seconds of stillness at the start told the demo how much of each speed your brain makes anyway, so it could tell a real rise from what was already there.

How it decides The ring marks the light being looked at. Its speed shows up in the EEG far more strongly than the others', and the ship turns toward it. The 10 hertz light stands out least, because the brain's own alpha is there too. Nothing on this page flickers.

Talk about it

  1. The IntoMind One doesn't watch your eyes. How does it know where you are looking?

  2. Why does the demo ask you to look at the middle for ten seconds first?

  3. Steering with your eyes was slower than with the keys. Who could a brain-computer interface still help, and why?

Lab 2 · High school

BCI Flight Simulator

How can a computer tell which of four lights you are looking at, from the back of your brain? And why is one of them harder than the rest?

This demo flickers

Flicker between about 3 and 60 times a second can trigger a seizure in people with photosensitive epilepsy. Don't play it or watch it if you have epilepsy, or if flickering light has ever made you feel unwell.

Whether you play or watch, sit back from the screen, in a well-lit room. If you feel strange while it runs, cover one eye with your hand, turn away from the screen, and tell your teacher.

Background

Read it the night before.

Frequency tagging

A light flickering at a steady rate drives the visual cortex at that rate. The response is a steady-state visually evoked potential, or SSVEP. It is too small to pick out of the EEG by eye, and clear in its spectrum: a peak at the light's rate, and smaller ones at its harmonics.

Give each light its own rate, and the rate that stands out in the EEG says which light has your attention. That is frequency tagging.

Simulated The spectrum from the back of the head while looking at the 12 hertz light: a peak at 12 hertz, a smaller one at 24, and the brain's own rhythms beneath.

The baseline, and the score

The demo's lights flicker at 8, 10, 12 and 15 hertz. Your brain makes rhythms of its own, and alpha sits right around 10. So before the lights start, the demo listens for ten seconds and measures how much of each rate is already there: its baseline.

Then, four times a second, it takes the last two seconds of signal, and scores each rate by its power compared with the power just beside it, against its baseline. A rate that stands out for two updates in a row turns the ship.

How it decides The ring marks the light being looked at. Its rate stands out far above the others, and the ship turns toward it. The 10 hertz light stands out least, because alpha is there too. Nothing on this page flickers.

Where it goes, and why

The response comes from the primary visual cortex, the first part of the brain to handle what the eyes see, at the back of the head on both sides of the midline. It is strongest on the midline just above the inion, at Oz, weaker one step to either side, at O1 and O2, and weak at the front.

The 10–20 system The IntoMind One over Oz, from above and from the left. The response is strongest there, weaker at O1 and O2, and weak at the forehead, Fpz.

Get ready

You need

  • An IntoMind One, charged, with its cable unplugged
  • A laptop or monitor with Chrome or Edge, never a projector or a big screen
  • A chair for the pilot, back from the screen, in a well-lit room
  • A timer for the caller
  • A clean microfiber, soft cotton or eyeglass cloth

Roles

Pilot
Wears the IntoMind One and flies by looking.
Caller
Names the lights, and times each look.
Recorder
Writes down each delay, and each wrong turn.
Skeptic
Watches the pilot, and writes down every blink, glance away or movement.

Take turns, so everyone who wants to can be the pilot. In a group of three, the caller is the skeptic too.

Rules

  • Unplug the cable before anyone puts it on.
  • Flying is your choice. Every role is a real one.
  • If the flicker makes you feel strange, cover one eye, turn away and say so.
  • Wipe the electrodes between pilots with the soft cloth, since a rough one can scratch their gold coating.

Steps

  1. Put it on and connect

    The IntoMind One goes on the midline just above the inion, over Oz. Unplug the cable. The pilot sits down, back from the screen. Part the hair under each electrode, and press it down until it feels firm but not tight.

    Open the BCI Flight Simulator demo. Its first screen begins with the flicker warning: read it again. Then press Connect your IntoMind One, and choose yours from the list.

    You should seeConnected at the top right, and the ship with four lights, not yet flickering, at the edges of the screen.

  2. Hold still for ten seconds

    Look at the middle of the screen and sit still, while the demo measures your baseline with the lights steady.

    You should seeA count from ten to zero in the middle of the screen. Then the lights start to flicker.

  3. Fly

    Look at a light to turn toward it. The ship turns for as long as you look, and flies on the way it points when you look away. Follow the coins to the next ring, for a minute or two, until steering feels familiar.

    You should seeThe ship turning a second or two after you look at a light.

  4. Time ten looks

    PredictThe light that will be slowest to steer with is the hertz light, because .

    The lights are 8 hertz at the top, 10 on the right, 12 at the bottom, and 15 on the left. The caller names a light at random, ten times, and times how long the ship takes to start turning that way. The recorder writes each delay under its light, and counts the turns the wrong way.

    Pilot8 hertz, seconds10 hertz, seconds12 hertz, seconds15 hertz, secondsWrong turns
    1
    2
    3
    4

    You should seeDelays of a second or more, and some lights answering faster than others.

Work it out

Work it outAcross your group, the median delay for each light: 8 hertz , 10 hertz , 12 hertz , 15 hertz . The slowest was . Did your prediction hold?

Talk about it

  1. Why is the 10 hertz light often the hardest for the demo to detect?

  2. The score compares the power at a light's rate with the power just beside it. Why not use the power at the rate alone?

  3. The demo waits for two updates in a row before it turns. What does that trade?

  4. Why 8, 10, 12 and 15 hertz, and not 8, 9, 10 and 11?

Sources

Wolpaw, J. R., Birbaumer, N., McFarland, D. J., Pfurtscheller, G., and Vaughan, T. M. (2002). Brain-computer interfaces for communication and control. Clinical Neurophysiology.

Herrmann, C. S. (2001). Human EEG responses to 1–100 Hz flicker: resonance phenomena in visual cortex and their potential correlation to cognitive phenomena. Experimental Brain Research.

Fisher, R. S., Harding, G., Erba, G., Barkley, G. L., and Wilkins, A. (2005). Photic- and pattern-induced seizures: a review for the Epilepsy Foundation of America Working Group. Epilepsia.

Lab 2 · College

BCI Flight Simulator

Can you find each light's flicker rate in your own EEG, rebuild the demo's score from its definition, and measure what a faster decision costs in accuracy?

This demo flickers

Flicker between about 3 and 60 times a second can trigger a seizure in people with photosensitive epilepsy. Don't play it or watch it if you have epilepsy, or if flickering light has ever made you feel unwell.

Whether you play or watch, sit back from the screen, in a well-lit room. If you feel strange while it runs, cover one eye with your hand, turn away from the screen, and tell your instructor.

Background

Read it before the lab.

Frequency tagging

A light flickering at a steady rate drives the primary visual cortex at that rate, with smaller responses at its harmonics: the steady-state visually evoked potential. Give each choice its own rate, and the rate that stands out in the EEG says which one has the eyes. The response is largest over the occipital pole, at Oz, and small beside the EEG's own rhythms, so it is found in the spectrum, not the trace.

Simulated The spectrum while looking at the 12 hertz light: a peak at 12 hertz, a smaller one at 24, on the brain's own rhythms.

The demo's score

Four times a second, on the last two seconds of the four channels, the demo computes for each light's rate f:

score = r(f) + ½ max(0, r(2f) − 1)

where r(f) is the power at exactly f, from a Hann-windowed segment and averaged over the channels, divided by the mean power at 1, 1.5 and 2 hertz either side. A score near 1 means nothing is there.

Each score is divided by its average over a ten-second baseline taken with the lights steady, or by 1 if that is smaller. A light counts when its score reaches 2.5 and is 1.25 times the next, two updates in a row, and lets go after two updates without.

Simulated The four lights as the demo lays them out, and each rate's score. The ring marks the light looked at, and its rate stands out. Nothing here flickers.

Speed against accuracy

A longer window resolves frequency more finely and averages away more noise, but every decision waits for it. Wolpaw's information transfer rate puts both in one number. With N choices, each right with probability P, a decision carries

B = log2 N + P log2 P + (1 − P) log2((1 − P) / (N − 1))

bits, and a decision every T seconds delivers B × 60 / T bits a minute.

Hypothesis

Write it before anyone flies.

HypothesisThe light hardest to steer with will be , because . Accuracy with one-second windows will be than with two, and the most bits a minute will come at about .

Get ready

You need

  • An IntoMind One, charged, with its cable unplugged
  • A laptop or monitor, never a projector or a large display, with Chrome or Edge, Python 3 with the intomind package and MNE, and the demos' source
  • A chair back from the screen, in a well-lit room, and a stopwatch
  • A clean microfiber, soft cotton or eyeglass cloth

Roles

Pilot
Wears the IntoMind One, and looks where the operator says.
Operator
Runs the demo and the recordings, names the lights, and times each look.
Analyst
Writes down each delay and wrong turn, and any look that strayed.

Rotate, so everyone who wants to flies.

Rules

  • Unplug the cable before anyone puts it on.
  • Flying is your choice. Every role is a real one.
  • A recording is its wearer's personal data. Keep it only with their permission.
  • Wipe the electrodes between pilots with the soft cloth, since a rough one can scratch their gold coating.

Procedure

  1. Put it on at Oz

    On the midline just above the inion, over Oz, with the strap around the head. Unplug the cable. Part the hair under each electrode, and press it down until it feels firm but not tight: hair at the back of the head is the usual reason a pilot can't steer.

  2. Connect, and hold still for the baseline

    Open the BCI Flight Simulator demo, read its warning again, and connect. Look at the center of the screen for the ten-second baseline, with the lights steady. Then fly: look at a light to turn toward it, and follow the coins to the next ring.

    You should seeThe ship turning a second or two after you look at a light.

  3. Time ten looks

    The operator names a light at random, ten times, and times how long the ship takes to start turning that way. The analyst writes down each delay, and each turn the wrong way.

    LookLightSeconds to turnTurned the wrong way?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. Record each light

    cd python
    python3 record_and_export.py --seconds 20 --label look-8
    python3 record_and_export.py --seconds 20 --label look-10
    python3 record_and_export.py --seconds 20 --label look-12
    python3 record_and_export.py --seconds 20 --label look-15
    python3 record_and_export.py --seconds 20 --label center

    Close the demo's tab, so the IntoMind One is free. Open a second copy with ?dev on the end of its address: it flies on a simulated IntoMind One, and shows the same warning and lights. After its baseline, while its lights flicker, record twenty seconds looking at each light, 8 hertz at the top, 10 on the right, 12 at the bottom and 15 on the left, and twenty with the eyes on the center of the screen.

    You should seeFive recordings, each written to a BDF file whose path the program prints.

Analysis

Plot each recording's spectrum, with 8, 10, 12 and 15 hertz and their harmonics marked. Then compute the demo's score yourself, on windows of 1, 2 and 4 seconds a quarter second apart, divide each light's score by its mean with the eyes on the center, and count how often the looked-at light scores highest.

import numpy as np
import mne
from scipy.signal.windows import hann

RATES = {"top": 8, "right": 10, "bottom": 12, "left": 15}
NEAR = (-2, -1.5, -1, 1, 1.5, 2)

def power(x, fs, hz):
    """Power at exactly hz, Hann-windowed, averaged over channels, as the demo does."""
    n = x.shape[1]
    xs = (x - x.mean(axis=1, keepdims=True)) * hann(n, sym=False)
    k = np.exp(-2j * np.pi * hz * np.arange(n) / fs)
    return float(np.mean(np.abs(xs @ k) ** 2)) / n ** 2

def score(x, fs, hz):
    snr = lambda f: power(x, fs, f) / np.mean([power(x, fs, f + d) for d in NEAR])
    return snr(hz) + 0.5 * max(0.0, snr(2 * hz) - 1)

def windows(path, seconds, step=0.25):
    """Every light's score in each window of a recording, a quarter second apart."""
    raw = mne.io.read_raw_bdf(path, preload=True, verbose="error")
    fs = raw.info["sfreq"]
    x = raw.get_data(picks="eeg")
    n = int(seconds * fs)
    return [{d: score(x[:, i:i + n], fs, hz) for d, hz in RATES.items()}
            for i in range(0, x.shape[1] - n + 1, int(step * fs))]

def bits_a_minute(n, p, seconds):
    """Wolpaw's information transfer rate, for n choices each right with probability p."""
    if p <= 1 / n:
        return 0.0
    bits = np.log2(n) + p * np.log2(p)
    if p < 1:
        bits += (1 - p) * np.log2((1 - p) / (n - 1))
    return bits * 60 / seconds

for seconds in (1, 2, 4):
    # each light's score with the eyes on the center: the demo's baseline, divided out
    center = windows("center.bdf", seconds)
    base = {d: max(1.0, np.mean([s[d] for s in center])) for d in RATES}
    right = total = 0
    for d, hz in RATES.items():
        for s in windows(f"look-{hz}.bdf", seconds):
            adjusted = {k: v / base[k] for k, v in s.items()}
            right += max(adjusted, key=adjusted.get) == d
            total += 1
    p = right / total
    print(seconds, "s:", round(p, 2), "right,", round(bits_a_minute(4, p, seconds), 1), "bits a minute")
Window, secondsRight, of all windowsBits a minuteThe light most often missed
1
2
4

Report

  1. The question, and the hypothesis as you wrote it before flying.
  2. Methods: the site, the recordings, the score's definition, the baseline, and the windows.
  3. Results: spectra with the rates marked, the median delay for each light from the timed looks, and accuracy and bits a minute for each window.
  4. Interpretation: which light was hardest and why, and where speed and accuracy balance.
  5. Limitations: twenty seconds a light, one pilot, a baseline taken with the lights flickering, and any looks that strayed.
  6. Your code, as an appendix.

Problems

  1. What frequency resolution does a two-second window give? Could it separate lights at 8 and 8.5 hertz?

  2. Why does the score add evidence from the second harmonic?

  3. Why compare the power at a rate with the power just beside it, rather than use the power alone?

  4. Your baseline came from the eyes on the center while the lights flickered at the edges. How might it differ from the demo's, taken with the lights steady?

  5. With four lights, which delivers more bits a minute: 90 percent right every two seconds, or 70 percent right every second?

Sources

Herrmann, C. S. (2001). Human EEG responses to 1–100 Hz flicker: resonance phenomena in visual cortex and their potential correlation to cognitive phenomena. Experimental Brain Research.

Vialatte, F.-B., Maurice, M., Dauwels, J., and Cichocki, A. (2010). Steady-state visually evoked potentials: focus on essential paradigms and future perspectives. Progress in Neurobiology.

Lin, Z., Zhang, C., Wu, W., and Gao, X. (2006). Frequency recognition based on canonical correlation analysis for SSVEP-based BCIs. IEEE Transactions on Biomedical Engineering.

Wolpaw, J. R., Birbaumer, N., McFarland, D. J., Pfurtscheller, G., and Vaughan, T. M. (2002). Brain-computer interfaces for communication and control. Clinical Neurophysiology.

Fisher, R. S., Harding, G., Erba, G., Barkley, G. L., and Wilkins, A. (2005). Photic- and pattern-induced seizures: a review for the Epilepsy Foundation of America Working Group. Epilepsia.

Lab 2 · Graduate

BCI Flight Simulator

How many bits a minute does the demo's decoder deliver from your visual cortex, how much would a better decoder add, and does each light's response follow the half of the visual field it falls in?

This demo flickers

Flicker between about 3 and 60 times a second can trigger a seizure in people with photosensitive epilepsy. Don't play it or watch it if you have epilepsy, or if flickering light has ever made you feel unwell.

Whether you play or watch, sit back from the screen, in a well-lit room. If you feel strange while it runs, cover one eye with your hand, turn away from the screen, and tell your instructor.

The demo, exactly

PartWhat it doesSource
The lights8 hertz at the top, 10 on the right, 12 at the bottom and 15 on the left. Each one's brightness is a sinusoid sampled at every frame's own time, so its rate doesn't depend on the screen's refresh. Each is small enough that no ten-degree field of view is more than a quarter flickering.lib/flicker.js
The windowThe last 2 seconds of the four channels, four times a second.flight-simulator/main.js
PowerAt each rate exactly, by Goertzel's algorithm, with each channel's mean removed and a periodic Hann window, divided by the window's length squared, and averaged over the channels.lib/ssvep.js
The scoreThe power at the rate over the mean power 1, 1.5 and 2 hertz either side, plus half of the same ratio at twice the rate, less 1, when that is positive.lib/ssvep.js
The baselineTen seconds looking at the center with the lights steady. Every later score is divided by its mean over the baseline, or by 1 if the mean is smaller.flight-simulator/main.js, lib/ssvep.js
The decisionThe top light, if it scores at least 2.5 and at least 1.25 times the next. It is held after two updates in a row, and let go after two updates with no light.lib/ssvep.js
SteeringThe ship turns toward the held light for as long as it is held.flight-simulator/main.js

What it leaves out: the pattern across channels and the phase, both averaged away. Harmonics past the second. Any calibration to the pilot beyond one baseline. And attention without gaze: it needs the eyes on the light. The 10 hertz light sits in the alpha band, and the baseline divides out the alpha of one moment at rest, not the alpha of the flight.

Preregister

Fill it in before the lab, and change nothing after the first recording. The report names every deviation.

ItemYour plan, before any data
Primary outcome
Hypotheses: the hardest light, the best window, and O1 against O2 for 10 and 15 hertz
Decoders and their parameters
Exclusions: glances away, gaps, poor contact
How you will judge each hypothesis, given windows that overlap

Get ready

You need

  • An IntoMind One, charged, with its cable unplugged
  • A laptop or monitor, never a projector or a large display, with Chrome or Edge, Python 3 with the intomind package, MNE and SciPy, and the demos' source
  • A chair back from the screen, in a well-lit room
  • A clean microfiber, soft cotton or eyeglass cloth

Roles

Pilot
Wears the IntoMind One, and looks where the operator says.
Operator
Runs the demo and the recordings, and says where to look.
Analyst
Watches the pilot's eyes, and writes down every label and every glance away, with its time.

Rotate, so everyone who wants to flies.

Rules

  • Unplug the cable before anyone puts it on.
  • Flying is your choice. Every role is a real one.
  • A recording is its wearer's personal data. Keep it only with their permission.
  • Wipe the electrodes between pilots with the soft cloth, since a rough one can scratch their gold coating.

Procedure

  1. Put it on at Oz

    On the midline just above the inion, over Oz, where the primary visual cortex of both hemispheres is closest. Unplug the cable. Part the hair under each electrode, and press it down until it feels firm but not tight.

  2. Connect, hold still, and fly

    Open the BCI Flight Simulator demo, read its warning again, and connect. Look at the center of the screen for the ten-second baseline, with the lights steady. Then fly for a minute or two: look at a light to turn toward it.

    You should seeThe ship turning a second or two after you look at a light.

  3. Record at Oz

    cd python
    python3 record_and_export.py --seconds 30 --label w1-steady
    python3 record_and_export.py --seconds 30 --label w1-look-8
    python3 record_and_export.py --seconds 30 --label w1-look-10
    python3 record_and_export.py --seconds 30 --label w1-look-12
    python3 record_and_export.py --seconds 30 --label w1-look-15

    Close the demo's tab, so the IntoMind One is free. With nothing flickering on the screen, record thirty seconds with the eyes on its center: the steady baseline. Then open a second copy of the demo with ?dev on the end of its address. It flies on a simulated IntoMind One and shows the same warning and lights. After its baseline, while its lights flicker, record thirty seconds looking at each light.

  4. Record at O1 and O2

    Move the IntoMind One to O1, left of the midline at the height of Oz, and record a minute with the eyes on the center of the ?dev copy while its lights flicker: --seconds 60 --label w1-center-o1. Then move it to O2, on the right, and record w1-center-o2 the same way. With the eyes on the center, the left light falls in the left half of the visual field, which projects to the right hemisphere, and the right light in the right half, projecting to the left.

    You should seeEvery recording written to a BDF file whose path the program prints.

  5. Each pilot in turn

    Wipe the electrodes, and repeat steps 1 to 4 for each pilot, with labels w2 and w3. The analyst records every glance away from where the operator said to look.

    PilotThe folder or paths of the seven filesGlances away, with times
    w1
    w2
    w3

Analyses

For one pilot: the demo's detector rebuilt and run on each look, update by update. Then forced choices on windows of 0.5 to 4 seconds, by the demo's score and by canonical correlation with sinusoids at each rate and its harmonics (Lin and others, 2006), with Wolpaw's bits a minute and half a second added to each choice for moving the eyes. Then each rate's signal-to-noise ratio at O1 and at O2, with the eyes on the center. Change the w1 in the labels for each pilot.

import numpy as np
import mne
from scipy.signal import butter, sosfiltfilt
from scipy.signal.windows import hann

RATES = {"top": 8, "right": 10, "bottom": 12, "left": 15}
NEAR = (-2, -1.5, -1, 1, 1.5, 2)

def load(path):
    raw = mne.io.read_raw_bdf(path, preload=True, verbose="error")
    return raw.info["sfreq"], 1e6 * raw.get_data(picks="eeg")

def power(x, fs, hz):
    """Power at exactly hz, Hann-windowed, averaged over the channels, as the demo does."""
    n = x.shape[1]
    xs = (x - x.mean(axis=1, keepdims=True)) * hann(n, sym=False)
    k = np.exp(-2j * np.pi * hz * np.arange(n) / fs)
    return float(np.mean(np.abs(xs @ k) ** 2)) / n ** 2

def snr(x, fs, hz):
    return power(x, fs, hz) / np.mean([power(x, fs, hz + d) for d in NEAR])

def score(x, fs, hz):
    return snr(x, fs, hz) + 0.5 * max(0.0, snr(x, fs, 2 * hz) - 1)

def cca(x, fs, hz, harmonics=3):
    """The largest canonical correlation of the channels with sinusoids at hz's harmonics."""
    t = np.arange(x.shape[1]) / fs
    y = np.array([f(2 * np.pi * k * hz * t) for k in range(1, harmonics + 1)
                  for f in (np.sin, np.cos)])
    qx = np.linalg.qr((x - x.mean(axis=1, keepdims=True)).T)[0]
    qy = np.linalg.qr((y - y.mean(axis=1, keepdims=True)).T)[0]
    return np.linalg.svd(qx.T @ qy, compute_uv=False)[0]

def windows(x, fs, seconds, step=0.25):
    n = int(seconds * fs)
    return [x[:, i:i + n] for i in range(0, x.shape[1] - n + 1, int(step * fs))]

def held(adjusted, threshold=2.5, margin=1.25, hold=2, release=2):
    """The demo's detector, update by update: the light it holds after each, or None."""
    current = pending = None
    count = empty = 0
    out = []
    for s in adjusted:
        best, second = sorted(s.values())[::-1][:2]
        top = max(s, key=s.get) if best >= threshold and best >= margin * second else None
        if top is None:
            pending, count, empty = None, 0, empty + 1
            current = None if empty >= release else current
        else:
            empty = 0
            if top == current:
                pending, count = None, 0
            elif top == pending:
                count += 1
                if count >= hold:
                    current, pending, count = top, None, 0
            else:
                pending, count = top, 1
        out.append(current)
    return out

def bits_a_minute(p, seconds, n=4):
    """Wolpaw's information transfer rate: n choices, each right with probability p."""
    if p <= 1 / n:
        return 0.0
    bits = np.log2(n) + p * np.log2(p)
    if p < 1:
        bits += (1 - p) * np.log2((1 - p) / (n - 1))
    return bits * 60 / seconds

PILOT = "w1"                        # run it in the folder the program wrote the files to
fs, steady = load(f"{PILOT}-steady.bdf")         # the center of a still screen: the baseline
looks = {d: load(f"{PILOT}-look-{hz}.bdf")[1] for d, hz in RATES.items()}

# 1. The demo, exactly: 2-s windows four times a second, divided by the baseline, then held.
base = {d: max(1.0, np.mean([score(w, fs, hz) for w in windows(steady, fs, 2)]))
        for d, hz in RATES.items()}
for d, x in looks.items():
    h = held([{k: score(w, fs, hz) / base[k] for k, hz in RATES.items()}
              for w in windows(x, fs, 2)])
    right, nothing = np.mean([v == d for v in h]), np.mean([v is None for v in h])
    print(f"looking {d:6s} held: right {right:.2f}, wrong {1 - right - nothing:.2f}, "
          f"nothing {nothing:.2f}")

# 2. Forced choices on windows of each length: the demo's score against canonical correlation,
#    which works on the signal band-passed from 6 to 50 Hz, as Lin and others (2006) did.
GAZE = 0.5                          # seconds to move the eyes, added to each choice
bp = butter(4, [6, 50], "bandpass", fs=fs, output="sos")
passed = {d: sosfiltfilt(bp, x, axis=1) for d, x in looks.items()}
for seconds in (0.5, 1, 2, 4):
    base = {d: max(1.0, np.mean([score(w, fs, hz) for w in windows(steady, fs, seconds)]))
            for d, hz in RATES.items()}
    right, n = {"score": 0, "cca": 0}, 0
    for d, x in looks.items():
        for w, v in zip(windows(x, fs, seconds), windows(passed[d], fs, seconds)):
            s = {k: score(w, fs, hz) / base[k] for k, hz in RATES.items()}
            c = {k: cca(v, fs, hz) for k, hz in RATES.items()}
            right["score"] += max(s, key=s.get) == d
            right["cca"] += max(c, key=c.get) == d
            n += 1
    print(f"{seconds} s: " + "   ".join(
        f"{k} {v / n:.2f} right, {bits_a_minute(v / n, seconds + GAZE):.0f} bits a minute"
        for k, v in right.items()))

# 3. Eyes on the center, lights flickering: each rate's response at O1 and at O2.
for site in ("o1", "o2"):
    fs, x = load(f"{PILOT}-center-{site}.bdf")
    print(site, "  ".join(f"{d} {np.mean([snr(w, fs, hz) for w in windows(x, fs, 4, 1)]):.2f}"
                          for d, hz in RATES.items()))

Then report, for your group's pilots and the class's:

  1. How often the rebuilt detector holds the right light, a wrong one, or none, for each light.
  2. Accuracy and bits a minute against the window, for both decoders, and the window where bits a minute peak.
  3. The 10 and 15 hertz responses at O1 against O2, with the 8 and 12 hertz lights, which sit on the vertical midline, as controls.
  4. Which light is hardest, and whether the pilot's alpha accounts for it.

Open problems

  1. Without the eyes. The flight steers by gaze, which a person who can't move their eyes doesn't have. With covert attention alone, 6 of 11 people reached 75 percent correct in at least one session (Kelly and others, 2005). Making attention without gaze reliable is open.
  2. Comfort against signal. Flicker from about 3 to 60 hertz is visible, tiring and carries a risk of seizures (Fisher and others, 2005). Faster or fainter flicker is gentler and gives a weaker response, and how far that can be pushed is unsettled.
  3. Calibration. Decoders calibrated to each person do far better than the demo's: with forty targets, 0.5-second flickers and a calibrated decoder, Chen and others (2015) reached 5.32 bits a second, and task-related component analysis averaged 325 bits a minute when cued and 199 when spelling freely (Nakanishi and others, 2018). Keeping that across days and people without recalibrating is open.
  4. Reporting speed. Information transfer rates are often computed in ways that break their assumptions (Yuan and others, 2013), and cued rates overstate free use, as the gap between 325 and 199 shows.

Report

  1. The preregistration as written, and every deviation from it, each with its reason.
  2. Methods to the COBIDAS MEEG recommendations: the device, the sites, the filters in force, the screen and its distance, the recordings, both decoders and every parameter.
  3. Results for each pilot first, then the class's, with how the overlap of windows was handled.
  4. The half-field result, and what it does and doesn't say about where the responses come from.
  5. The code, which another group can run on your files to the same numbers.

Sources

Regan, D. (1966). Some characteristics of average steady-state and transient responses evoked by modulated light. Electroencephalography and Clinical Neurophysiology.

Barrett, G., Blumhardt, L., Halliday, A. M., Halliday, E., and Kriss, A. (1976). A paradox in the lateralisation of the visual evoked response. Nature.

Wolpaw, J. R., Birbaumer, N., McFarland, D. J., Pfurtscheller, G., and Vaughan, T. M. (2002). Brain-computer interfaces for communication and control. Clinical Neurophysiology.

Fisher, R. S., Harding, G., Erba, G., Barkley, G. L., and Wilkins, A. (2005). Photic- and pattern-induced seizures: a review for the Epilepsy Foundation of America Working Group. Epilepsia.

Kelly, S. P., Lalor, E. C., Finucane, C., McDarby, G., and Reilly, R. B. (2005). Visual spatial attention control in an independent brain-computer interface. IEEE Transactions on Biomedical Engineering.

Lin, Z., Zhang, C., Wu, W., and Gao, X. (2006). Frequency recognition based on canonical correlation analysis for SSVEP-based BCIs. IEEE Transactions on Biomedical Engineering.

Yuan, P., and others (2013). A study of the existing problems of estimating the information transfer rate in online brain-computer interfaces. Journal of Neural Engineering.

Chen, X., Wang, Y., Nakanishi, M., Gao, X., Jung, T.-P., and Gao, S. (2015). High-speed spelling with a noninvasive brain-computer interface. Proceedings of the National Academy of Sciences.

Norcia, A. M., Appelbaum, L. G., Ales, J. M., Cottereau, B. R., and Rossion, B. (2015). The steady-state visual evoked potential in vision research: a review. Journal of Vision.

Nakanishi, M., and others (2018). Enhancing detection of SSVEPs for a high-speed brain speller using task-related component analysis. IEEE Transactions on Biomedical Engineering.