Skip to content

Commit 600ec18

Browse files
author
AJ Keller
authored
Merge pull request #161 from OpenBCI/development
Development
2 parents eff2079 + 3ea77b8 commit 600ec18

File tree

4 files changed

+60
-34
lines changed

4 files changed

+60
-34
lines changed

OpenBCI_GUI/DataProcessing.pde

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,15 @@ class DataProcessing {
675675

676676
//convert to uV_per_bin...still need to confirm the accuracy of this code.
677677
//Do we need to account for the power lost in the windowing function? CHIP 2014-10-24
678-
for (int I=0; I < fftBuff[Ichan].specSize(); I++) { //loop over each FFT bin
679-
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) / fftBuff[Ichan].specSize()));
678+
679+
// FFT ref: https://www.mathworks.com/help/matlab/ref/fft.html
680+
// first calculate double-sided FFT amplitude spectrum
681+
for (int I=0; I <= Nfft/2; I++) {
682+
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) / Nfft));
683+
}
684+
// then convert into single-sided FFT spectrum: DC & Nyquist (i=0 & i=N/2) remain the same, others multiply by two.
685+
for (int I=1; I < Nfft/2; I++) {
686+
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) * 2));
680687
}
681688

682689
//average the FFT with previous FFT data so that it makes it smoother in time
@@ -699,13 +706,34 @@ class DataProcessing {
699706
foo = java.lang.Math.sqrt(foo);
700707
}
701708
fftBuff[Ichan].setBand(I, (float)foo); //put the smoothed data back into the fftBuff data holder for use by everyone else
709+
// fftBuff[Ichan].setBand(I, 1.0f); // test
702710
} //end loop over FFT bins
711+
712+
// calculate single-sided psd by single-sided FFT amplitude spectrum
713+
// PSD ref: https://www.mathworks.com/help/dsp/ug/estimate-the-power-spectral-density-in-matlab.html
714+
// when i = 1 ~ (N/2-1), psd = (N / fs) * mag(i)^2 / 4
715+
// when i = 0 or i = N/2, psd = (N / fs) * mag(i)^2
716+
703717
for (int i = 0; i < processing_band_low_Hz.length; i++) {
704718
float sum = 0;
705-
for (int j = processing_band_low_Hz[i]; j < processing_band_high_Hz[i]; j++) {
706-
sum += fftBuff[Ichan].getBand(j);
719+
// int binNum = 0;
720+
for (int Ibin = 0; Ibin <= Nfft/2; Ibin ++) { // loop over FFT bins
721+
float FFT_freq_Hz = fftBuff[Ichan].indexToFreq(Ibin); // center frequency of this bin
722+
float psdx = 0;
723+
// if the frequency matches a band
724+
if (FFT_freq_Hz >= processing_band_low_Hz[i] && FFT_freq_Hz < processing_band_high_Hz[i]) {
725+
if (Ibin != 0 && Ibin != Nfft/2) {
726+
psdx = fftBuff[Ichan].getBand(Ibin) * fftBuff[Ichan].getBand(Ibin) * Nfft/get_fs_Hz_safe() / 4;
727+
}
728+
else {
729+
psdx = fftBuff[Ichan].getBand(Ibin) * fftBuff[Ichan].getBand(Ibin) * Nfft/get_fs_Hz_safe();
730+
}
731+
sum += psdx;
732+
// binNum ++;
733+
}
707734
}
708-
avgPowerInBins[Ichan][i] = sum;
735+
avgPowerInBins[Ichan][i] = sum; // total power in a band
736+
// println(i, binNum, sum);
709737
}
710738
} //end the loop over channels.
711739
for (int i = 0; i < processing_band_low_Hz.length; i++) {
@@ -714,7 +742,7 @@ class DataProcessing {
714742
for (int j = 0; j < nchan; j++) {
715743
sum += avgPowerInBins[j][i];
716744
}
717-
headWidePower[i] = sum/nchan;
745+
headWidePower[i] = sum/nchan; // averaging power over all channels
718746
}
719747

720748
//delta in channel 2 ... avgPowerInBins[1][DELTA];

OpenBCI_GUI/W_PowerBands.pde renamed to OpenBCI_GUI/W_BandPower.pde

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11

22
////////////////////////////////////////////////////
33
//
4-
// W_PowerBands.pde
4+
// W_BandPowers.pde
55
//
6-
// This is a power band visualization widget!
6+
// This is a band power visualization widget!
77
// (Couldn't think up more)
8-
// This is for visualizing the average power of each brainwave band: delta, theta, alpha, beta, gamma
9-
// Of all active channels
8+
// This is for visualizing the power of each brainwave band: delta, theta, alpha, beta, gamma
9+
// Averaged over all channels
1010
//
1111
// Created by: Wangshu Sun, May 2017
1212
//
1313
///////////////////////////////////////////////////,
1414

15-
class W_PowerBands extends Widget {
15+
class W_BandPower extends Widget {
1616

1717
GPlot plot3;
1818
String bands[] = {"DELTA", "THETA", "ALPHA", "BETA", "GAMMA"};
1919

20-
W_PowerBands(PApplet _parent){
20+
W_BandPower(PApplet _parent){
2121
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
2222

2323
//This is the protocol for setting up dropdowns.
@@ -35,11 +35,9 @@ class W_PowerBands extends Widget {
3535
plot3.setYLim(0.1, 100);
3636
plot3.setXLim(0, 5);
3737
plot3.getYAxis().setNTicks(9);
38-
// plot3.getTitle().setText("Gaussian distribution (" + str(0) + " points)"); // AJK
39-
plot3.getTitle().setText("Gaussian distribution (" + str(0) + " points)");
4038
plot3.getTitle().setTextAlignment(LEFT);
4139
plot3.getTitle().setRelativePos(0);
42-
plot3.getYAxis().getAxisLabel().setText("Relative probability");
40+
plot3.getYAxis().getAxisLabel().setText("(uV)^2 / Hz per channel");
4341
plot3.getYAxis().getAxisLabel().setTextAlignment(RIGHT);
4442
plot3.getYAxis().getAxisLabel().setRelativePos(1);
4543
// plot3.setPoints(points3);

OpenBCI_GUI/W_networking.pde

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class W_networking extends Widget {
7575
stream1 = null;
7676
stream2 = null;
7777
stream3 = null;
78-
dataTypes = Arrays.asList("None", "TimeSeries", "FFT", "EMG", "PowerBands", "Widget");
78+
dataTypes = Arrays.asList("None", "TimeSeries", "FFT", "EMG", "BandPower", "Widget");
7979
defaultBaud = "9600";
8080
baudRates = Arrays.asList("1200", "9600", "57600", "115200");
8181
protocolMode = "OSC"; //default to OSC
@@ -735,7 +735,7 @@ class W_networking extends Widget {
735735
break;
736736
case 3 : dt1 = "EMG";
737737
break;
738-
case 4 : dt1 = "PowerBands";
738+
case 4 : dt1 = "BandPower";
739739
break;
740740
case 5 : dt1 = "Widget";
741741
break;
@@ -749,7 +749,7 @@ class W_networking extends Widget {
749749
break;
750750
case 3 : dt2 = "EMG";
751751
break;
752-
case 4 : dt2 = "PowerBands";
752+
case 4 : dt2 = "BandPower";
753753
break;
754754
case 5 : dt2 = "Widget";
755755
break;
@@ -763,7 +763,7 @@ class W_networking extends Widget {
763763
break;
764764
case 3 : dt3 = "EMG";
765765
break;
766-
case 4 : dt3 = "PowerBands";
766+
case 4 : dt3 = "BandPower";
767767
break;
768768
case 5 : dt3 = "Widget";
769769
break;
@@ -1060,7 +1060,7 @@ class Stream extends Thread{
10601060
sendFFTData();
10611061
}else if (this.dataType.equals("EMG")){
10621062
sendEMGData();
1063-
}else if (this.dataType.equals("PowerBands")){
1063+
}else if (this.dataType.equals("BandPower")){
10641064
sendPowerBandData();
10651065
}else if (this.dataType.equals("WIDGET")){
10661066
sendWidgetData();
@@ -1090,7 +1090,7 @@ class Stream extends Thread{
10901090
sendFFTData();
10911091
}else if (this.dataType.equals("EMG")){
10921092
sendEMGData();
1093-
}else if (this.dataType.equals("PowerBands")){
1093+
}else if (this.dataType.equals("BandPower")){
10941094
sendPowerBandData();
10951095
}else if (this.dataType.equals("WIDGET")){
10961096
sendWidgetData();
@@ -1109,7 +1109,7 @@ class Stream extends Thread{
11091109
return dataProcessing.newDataToSend;
11101110
}else if (this.dataType.equals("EMG")){
11111111
return dataProcessing.newDataToSend;
1112-
}else if (this.dataType.equals("PowerBands")){
1112+
}else if (this.dataType.equals("BandPower")){
11131113
return dataProcessing.newDataToSend;
11141114
}else if (this.dataType.equals("WIDGET")){
11151115
/* ENTER YOUR WIDGET "NEW DATA" RETURN FUNCTION */
@@ -1124,7 +1124,7 @@ class Stream extends Thread{
11241124
dataProcessing.newDataToSend = false;
11251125
}else if (this.dataType.equals("EMG")){
11261126
dataProcessing.newDataToSend = false;
1127-
}else if (this.dataType.equals("PowerBands")){
1127+
}else if (this.dataType.equals("BandPower")){
11281128
dataProcessing.newDataToSend = false;
11291129
}else if (this.dataType.equals("WIDGET")){
11301130
/* ENTER YOUR WIDGET "NEW DATA" RETURN FUNCTION */
@@ -1300,15 +1300,15 @@ class Stream extends Thread{
13001300

13011301
void sendPowerBandData(){
13021302
// UNFILTERED & FILTERED ... influenced globally by the FFT filters dropdown ... just like the FFT data
1303-
int numPowerBands = 5; //DELTA, THETA, ALPHA, BETA, GAMMA
1303+
int numBandPower = 5; //DELTA, THETA, ALPHA, BETA, GAMMA
13041304

13051305
if(this.filter==0 || this.filter==1){
13061306
// OSC
13071307
if (this.protocol.equals("OSC")){
13081308
for (int i=0;i<numChan;i++){
13091309
msg.clearArguments();
13101310
msg.add(i+1);
1311-
for (int j=0;j<numPowerBands;j++){
1311+
for (int j=0;j<numBandPower;j++){
13121312
msg.add(dataProcessing.avgPowerInBins[i][j]); // [CHAN][BAND]
13131313
}
13141314
try{
@@ -1322,7 +1322,7 @@ class Stream extends Thread{
13221322
for (int i=0;i<numChan;i++){
13231323
buffer.rewind();
13241324
buffer.putFloat(i+1);
1325-
for (int j=0;j<numPowerBands;j++){
1325+
for (int j=0;j<numBandPower;j++){
13261326
buffer.putFloat(dataProcessing.avgPowerInBins[i][j]); //[CHAN][BAND]
13271327
}
13281328
try{
@@ -1334,21 +1334,21 @@ class Stream extends Thread{
13341334
// LSL
13351335
}else if (this.protocol.equals("LSL")){
13361336

1337-
float[] avgPowerLSL = new float[numChan*numPowerBands];
1337+
float[] avgPowerLSL = new float[numChan*numBandPower];
13381338
for (int i=0; i<numChan;i++){
1339-
for(int j=0;j<numPowerBands;j++){
1339+
for(int j=0;j<numBandPower;j++){
13401340
dataToSend[j+numChan*i] = dataProcessing.avgPowerInBins[i][j];
13411341
}
13421342
}
13431343
outlet_data.push_chunk(dataToSend);
13441344
}else if (this.protocol.equals("Serial")){
13451345
for (int i=0;i<numChan;i++){
13461346
serialMessage = "[" + (i+1) + ","; //clear message
1347-
for (int j=0;j<numPowerBands;j++){
1347+
for (int j=0;j<numBandPower;j++){
13481348
float power_band = dataProcessing.avgPowerInBins[i][j];
13491349
String power_band_3dec = String.format("%.3f", power_band);
13501350
serialMessage += power_band_3dec;
1351-
if(j < numPowerBands-1){
1351+
if(j < numBandPower-1){
13521352
serialMessage += ","; //add a comma to serialMessage to separate chan values, as long as it isn't last value...
13531353
}
13541354
}

OpenBCI_GUI/WidgetManager.pde

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ W_ganglionImpedance w_ganglionImpedance;
2121
W_template w_template1;
2222
W_emg w_emg;
2323
W_openBionics w_openbionics;
24-
W_PowerBands w_powerBands;
24+
W_BandPower w_bandPower;
2525

2626
//ADD YOUR WIDGET TO WIDGETS OF WIDGETMANAGER
2727
void setupWidgets(PApplet _this, ArrayList<Widget> w){
@@ -56,9 +56,9 @@ void setupWidgets(PApplet _this, ArrayList<Widget> w){
5656
w_emg.setTitle("EMG");
5757
addWidget(w_emg, w);
5858

59-
w_powerBands = new W_PowerBands(_this);
60-
w_powerBands.setTitle("Power Bands");
61-
addWidget(w_powerBands, w);
59+
w_bandPower = new W_BandPower(_this);
60+
w_bandPower.setTitle("Band Power");
61+
addWidget(w_bandPower, w);
6262

6363
w_template1 = new W_template(_this);
6464
w_template1.setTitle("Widget Template 1");

0 commit comments

Comments
 (0)