Summary of Changes:

  • Replace underlying FFT library by kissfft. It allows non power of 2 FFTs - #533
  • Fixed bug in get_psd_welch in C++ binding
  • Add oxygen level calculations from PPG data - #534
  • Add railed calculations like in OpenBCI GUI - #535
  • Add peak detection calculations using z score algorithm - #539

Oxygen level calculations

We’ve added get_oxygen_level method to calculate SpO2 from PPG data.

It has three optional parameters to calibrate percentage per device. We provided some defaults for them but do not ensure that it will work for all possible cases good enough, you are expected to tune them. More info about tuning can be found here.

Also, Muse S board has slightly different PPG sensor, implemented algorithm will work with it but you will need to calibrate it for sure.

Example for Muse 2:

import time

from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds, BrainFlowPresets
from brainflow.data_filter import DataFilter

def main():

    params = BrainFlowInputParams()

    board = BoardShim(BoardIds.MUSE_2_BOARD, params)
    data = board.get_board_data(preset=BrainFlowPresets.ANCILLARY_PRESET)


    ppg_channels = BoardShim.get_ppg_channels(BoardIds.MUSE_2_BOARD,  BrainFlowPresets.ANCILLARY_PRESET)
    sampling_rate = BoardShim.get_sampling_rate(BoardIds.MUSE_2_BOARD, BrainFlowPresets.ANCILLARY_PRESET)
    ppg_ir = data[ppg_channels[1]]
    ppg_red = data[ppg_channels[0]] 
    oxygen_level = DataFilter.get_oxygen_level(ppg_ir, ppg_red, sampling_rate)

if __name__ == "__main__":

Railed calculations

We’ve added get_railed_percentage method. It does exactly the same as OpenBCI GUI to print messages in timeseries widget.

Peak Detection

In this release we implemented z score peak detection algorithm.

It can be used for all types of biosignals as is or in combination with wavelet analysis. We also added a method to restore data from a single wavelet detailed coefficient.

There are three options you can tune:

  • lag: the lag parameter determines how much your data will be smoothed and how adaptive the algorithm is to changes in the long-term average of the data. The more stationary your data is, the more lags you should include.
  • influence: this parameter determines the influence of signals on the algorithm’s detection threshold. If put at 0, signals have no influence on the threshold, such that future signals are detected based on a threshold that is calculated with a mean and standard deviation that is not influenced by past signals.
  • threshold: the threshold parameter is the number of standard deviations from the moving mean above which the algorithm will classify a new datapoint as being a signal. For example, if a new datapoint is 4.0 standard deviations above the moving mean and the threshold parameter is set as 3.5, the algorithm will identify the datapoint as a signal.