Esse commit está contido em:
Conor Russomanno
2016-06-16 10:53:28 -04:00
commit bbe64eecfd
59 arquivos alterados com 475393 adições e 0 exclusões
BIN
Ver Arquivo
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 266 KiB

+17
Ver Arquivo
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package=""
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<application android:label=""
android:icon="@drawable/icon"
android:debuggable="true">
<activity android:name="">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
+196
Ver Arquivo
@@ -0,0 +1,196 @@
////////////////////
//
// This class creates and manages a button for use on the screen to trigger actions.
//
// Created: Chip Audette, Oct 2013.
// Modified: Conor Russomanno, Oct 2014
//
// Based on Processing's "Button" example code
//
////////////////////
class Button {
int but_x, but_y, but_dx, but_dy; // Position of square button
//int rectSize = 90; // Diameter of rect
color currentColor;
color color_hover = color(127, 134, 143);//color(252, 221, 198);
color color_pressed = color(150,170,200); //bgColor;
color color_highlight = color(102);
color color_notPressed = color(255); //color(227,118,37);
color buttonStrokeColor = bgColor;
color textColorActive = color(255);
color textColorNotActive = bgColor;
color rectHighlight;
boolean drawHand = false;
//boolean isMouseHere = false;
boolean buttonHasStroke = true;
boolean isActive = false;
boolean isDropdownButton = false;
boolean wasPressed = false;
public String but_txt;
PFont buttonFont = f2;
public Button(int x, int y, int w, int h, String txt, int fontSize) {
setup(x, y, w, h, txt);
//println(PFont.list()); //see which fonts are available
//font = createFont("SansSerif.plain",fontSize);
//font = createFont("Lucida Sans Regular",fontSize);
// font = createFont("Arial",fontSize);
//font = loadFont("SansSerif.plain.vlw");
}
public void setup(int x, int y, int w, int h, String txt) {
but_x = x;
but_y = y;
but_dx = w;
but_dy = h;
setString(txt);
}
public void setString(String txt) {
but_txt = txt;
//println("Button: setString: string = " + txt);
}
public boolean isActive() {
return isActive;
}
public void setIsActive(boolean val) {
isActive = val;
}
public void makeDropdownButton(boolean val) {
isDropdownButton = val;
}
public boolean isMouseHere() {
if ( overRect(but_x, but_y, but_dx, but_dy) ) {
cursor(HAND);
return true;
} else {
return false;
}
}
color getColor() {
if (isActive) {
currentColor = color_pressed;
} else if (isMouseHere()) {
currentColor = color_hover;
} else {
currentColor = color_notPressed;
}
return currentColor;
}
public void setCurrentColor(color _color){
currentColor = _color;
}
public void setColorPressed(color _color) {
color_pressed = _color;
}
public void setColorNotPressed(color _color) {
color_notPressed = _color;
}
public void setStrokeColor(color _color) {
buttonStrokeColor = _color;
}
public void hasStroke(boolean _trueORfalse) {
buttonHasStroke = _trueORfalse;
}
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}
public void draw(int _x, int _y) {
but_x = _x;
but_y = _y;
draw();
}
public void draw() {
//draw the button
fill(getColor());
if (buttonHasStroke) {
stroke(buttonStrokeColor); //button border
} else {
noStroke();
}
// noStroke();
rect(but_x, but_y, but_dx, but_dy);
//draw the text
if (isActive) {
fill(textColorActive);
} else {
fill(textColorNotActive);
}
stroke(255);
textFont(buttonFont); //load f2 ... from control panel
textSize(12);
textAlign(CENTER, CENTER);
textLeading(round(0.9*(textAscent()+textDescent())));
// int x1 = but_x+but_dx/2;
// int y1 = but_y+but_dy/2;
int x1, y1;
//no auto wrap
x1 = but_x+but_dx/2;
y1 = but_y+but_dy/2;
text(but_txt, x1, y1);
//draw open/close arrow if it's a dropdown button
if (isDropdownButton) {
pushStyle();
fill(255);
noStroke();
// smooth();
// stroke(255);
// strokeWeight(1);
if (isActive) {
float point1x = but_x + (but_dx - ((3f*but_dy)/4f));
float point1y = but_y + but_dy/3f;
float point2x = but_x + (but_dx-(but_dy/4f));
float point2y = but_y + but_dy/3f;
float point3x = but_x + (but_dx - (but_dy/2f));
float point3y = but_y + (2f*but_dy)/3f;
triangle(point1x, point1y, point2x, point2y, point3x, point3y); //downward triangle, indicating open
} else {
float point1x = but_x + (but_dx - ((3f*but_dy)/4f));
float point1y = but_y + (2f*but_dy)/3f;
float point2x = but_x + (but_dx-(but_dy/4f));
float point2y = but_y + (2f*but_dy)/3f;
float point3x = but_x + (but_dx - (but_dy/2f));
float point3y = but_y + but_dy/3f;
triangle(point1x, point1y, point2x, point2y, point3x, point3y); //upward triangle, indicating closed
}
popStyle();
}
if (true) {
if (!isMouseHere() && drawHand) {
cursor(ARROW);
drawHand = false;
verbosePrint("don't draw hand");
}
//if cursor is over button change cursor icon to hand!
if (isMouseHere() && !drawHand) {
cursor(HAND);
drawHand = true;
verbosePrint("draw hand");
}
}
}
};
+583
Ver Arquivo
@@ -0,0 +1,583 @@
//these arrays of channel values need to be global so that they don't reset on screen resize, when GUI reinitializes (there's definitely a more efficient way to do this...)
int numSettingsPerChannel = 6; //each channel has 6 different settings
char[][] channelSettingValues = new char [nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
char[][] impedanceCheckValues = new char [nchan][2];
public void updateChannelArrays(int _nchan) {
channelSettingValues = new char [_nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
impedanceCheckValues = new char [_nchan][2];
}
// color[] channelColors = new color[16];
color[] channelColors = {
color(129, 129, 129),
color(124, 75, 141),
color(54, 87, 158),
color(49, 113, 89),
color(221, 178, 13),
color(253, 94, 52),
color(224, 56, 45),
color(162, 82, 49)
};
class ChannelController {
public float x1, y1, w1, h1, x2, y2, w2, h2; //all 1 values refer to the left panel that is always visible ... al 2 values refer to the right panel that is only visible when showFullController = true
public int montage_w, montage_h;
public int rowHeight;
public int buttonSpacing;
boolean showFullController = false;
boolean[] drawImpedanceValues = new boolean [nchan];
int spacer1 = 3;
int spacer2 = 5; //space between buttons
// [Number of Channels] x 6 array of buttons for channel settings
Button[][] channelSettingButtons = new Button [nchan][numSettingsPerChannel]; // [channel#][Button#]
// char[][] channelSettingValues = new char [nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
//buttons just to the left of
Button[][] impedanceCheckButtons = new Button [nchan][2];
// char [][] impedanceCheckValues = new char [nchan][2];
// Array for storing SRB2 history settings of channels prior to shutting off .. so you can return to previous state when reactivating channel
char[] previousSRB2 = new char [nchan];
// Array for storing SRB2 history settings of channels prior to shutting off .. so you can return to previous state when reactivating channel
char[] previousBIAS = new char [nchan];
//maximum different values for the different settings (Power Down, Gain, Input Type, BIAS, SRB2, SRB1) of
//refer to page 44 of ADS1299 Datasheet: http://www.ti.com/lit/ds/symlink/ads1299.pdf
char[] maxValuesPerSetting = {
'1', // Power Down :: (0)ON, (1)OFF
'6', // Gain :: (0) x1, (1) x2, (2) x4, (3) x6, (4) x8, (5) x12, (6) x24 ... default
'7', // Channel Input :: (0)Normal Electrode Input, (1)Input Shorted, (2)Used in conjunction with BIAS_MEAS, (3)MVDD for supply measurement, (4)Temperature Sensor, (5)Test Signal, (6)BIAS_DRP ... positive electrode is driver, (7)BIAS_DRN ... negative electrode is driver
'1', // BIAS :: (0) Yes, (1) No
'1', // SRB2 :: (0) Open, (1) Closed
'1'
}; // SRB1 :: (0) Yes, (1) No ... this setting affects all channels ... either all on or all off
//variables used for channel write timing in writeChannelSettings()
//long timeOfLastChannelWrite = 0;
int channelToWrite = -1;
//int channelWriteCounter = 0;
//boolean isWritingChannel = false;
//variables use for imp write timing with writeImpedanceSettings()
//long timeOfLastImpWrite = 0;
int impChannelToWrite = -1;
//int impWriteCounter = 0;
//boolean isWritingImp = false;
boolean rewriteChannelWhenDoneWriting = false;
int channelToWriteWhenDoneWriting = 0;
boolean rewriteImpedanceWhenDoneWriting = false;
int impChannelToWriteWhenDoneWriting = 0;
char final_pORn = '0';
char final_onORoff = '0';
ChannelController(float _xPos, float _yPos, float _width, float _height, float _montage_w, float _montage_h) {
//positioning values for left panel (that is always visible)
x1 = _xPos;
y1 = _yPos;
w1 = _width;
h1 = _height;
//positioning values for right panel that is only visible when showFullController = true (behind the graph)
x2 = x1 + w1;
// x2 = gui.showMontageButton.but_x;
y2 = y1;
w2 = _montage_w;
h2 = h1;
createChannelSettingButtons();
// set on/off buttons to default channel colors
for (int i = 0; i < nchan; i++) {
channelSettingButtons[i][0].setColorNotPressed(channelColors[i%8]);
}
}
public void loadDefaultChannelSettings() {
verbosePrint("ChannelController: loading default channel settings to GUI's channel controller...");
for (int i = 0; i < nchan; i++) {
verbosePrint("chan: " + i + " ");
for (int j = 0; j < numSettingsPerChannel; j++) { //channel setting values
channelSettingValues[i][j] = char(openBCI.get_defaultChannelSettings().toCharArray()[j]); //parse defaultChannelSettings string created in the OpenBCI_ADS1299 class
if (j == numSettingsPerChannel - 1) {
println(char(openBCI.get_defaultChannelSettings().toCharArray()[j]));
} else {
print(char(openBCI.get_defaultChannelSettings().toCharArray()[j]) + ",");
}
}
for (int k = 0; k < 2; k++) { //impedance setting values
impedanceCheckValues[i][k] = '0';
}
}
verbosePrint("made it!");
update(); //update 1 time to refresh button values based on new loaded settings
}
public void update() {
//make false to check again below
for (int i = 0; i < nchan; i++) {
drawImpedanceValues[i] = false;
}
for (int i = 0; i < nchan; i++) { //for every channel
//update buttons based on channelSettingValues[i][j]
for (int j = 0; j < numSettingsPerChannel; j++) {
switch(j) { //what setting are we looking at
case 0: //on/off ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][0].setColorNotPressed(channelColors[i%8]);// power down == false, set color to vibrant
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][0].setColorNotPressed(color(75)); // channelSettingButtons[i][0].setString("B"); // power down == true, set color to dark gray, indicating power down
break;
case 1: //GAIN ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][1].setString("x1");
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][1].setString("x2");
if (channelSettingValues[i][j] == '2') channelSettingButtons[i][1].setString("x4");
if (channelSettingValues[i][j] == '3') channelSettingButtons[i][1].setString("x6");
if (channelSettingValues[i][j] == '4') channelSettingButtons[i][1].setString("x8");
if (channelSettingValues[i][j] == '5') channelSettingButtons[i][1].setString("x12");
if (channelSettingValues[i][j] == '6') channelSettingButtons[i][1].setString("x24");
break;
case 2: //input type ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][2].setString("Normal");
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][2].setString("Shorted");
if (channelSettingValues[i][j] == '2') channelSettingButtons[i][2].setString("BIAS_MEAS");
if (channelSettingValues[i][j] == '3') channelSettingButtons[i][2].setString("MVDD");
if (channelSettingValues[i][j] == '4') channelSettingButtons[i][2].setString("Temp.");
if (channelSettingValues[i][j] == '5') channelSettingButtons[i][2].setString("Test");
if (channelSettingValues[i][j] == '6') channelSettingButtons[i][2].setString("BIAS_DRP");
if (channelSettingValues[i][j] == '7') channelSettingButtons[i][2].setString("BIAS_DRN");
break;
case 3: //BIAS ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][3].setString("Don't Include");
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][3].setString("Include");
break;
case 4: // SRB2 ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][4].setString("Off");
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][4].setString("On");
break;
case 5: // SRB1 ??
if (channelSettingValues[i][j] == '0') channelSettingButtons[i][5].setString("No");
if (channelSettingValues[i][j] == '1') channelSettingButtons[i][5].setString("Yes");
break;
}
}
for (int k = 0; k < 2; k++) {
switch(k) {
case 0: // P Imp Buttons
if (impedanceCheckValues[i][k] == '0') {
impedanceCheckButtons[i][0].setColorNotPressed(color(75));
impedanceCheckButtons[i][0].setString("");
}
if (impedanceCheckValues[i][k] == '1') {
impedanceCheckButtons[i][0].setColorNotPressed(isSelected_color);
impedanceCheckButtons[i][0].setString("");
if (showFullController) {
drawImpedanceValues[i] = false;
} else {
drawImpedanceValues[i] = true;
}
}
break;
case 1: // N Imp Buttons
if (impedanceCheckValues[i][k] == '0') {
impedanceCheckButtons[i][1].setColorNotPressed(color(75));
impedanceCheckButtons[i][1].setString("");
}
if (impedanceCheckValues[i][k] == '1') {
impedanceCheckButtons[i][1].setColorNotPressed(isSelected_color);
impedanceCheckButtons[i][1].setString("");
if (showFullController) {
drawImpedanceValues[i] = false;
} else {
drawImpedanceValues[i] = true;
}
}
break;
}
}
}
//then reset to 1
//
if (openBCI.get_isWritingChannel()) {
openBCI.writeChannelSettings(channelToWrite,channelSettingValues);
}
if (rewriteChannelWhenDoneWriting == true && openBCI.get_isWritingChannel() == false) {
initChannelWrite(channelToWriteWhenDoneWriting);
rewriteChannelWhenDoneWriting = false;
}
if (openBCI.get_isWritingImp()) {
openBCI.writeImpedanceSettings(impChannelToWrite,impedanceCheckValues);
}
if (rewriteImpedanceWhenDoneWriting == true && openBCI.get_isWritingImp() == false) {
initImpWrite(impChannelToWriteWhenDoneWriting, final_pORn, final_onORoff);
rewriteImpedanceWhenDoneWriting = false;
}
}
public void draw() {
pushStyle();
noStroke();
//draw phantom rectangle to cover up random crap from Graph2D... we are replacing this stuff with the Montage Controller
fill(bgColor);
rect(x1 - 2, y1-(height*0.01f), w1, h1+(height*0.02f));
//draw light green rect behind pane title
fill(216, 233, 171);
rect(x2-2, y2-25, w2+1, 25);
//BG of montage controller (for debugging mainly)
// fill(255,255,255,123);
// rect(x1, y1 - 1, w1, h1);
//draw background pane of impedance buttons
fill(221);
rect(x1 + w1/3 + 1, y1, 2*(w1/3) - 3, h1 - 2);
//draw slightly darker line guides/separators for impedance buttons
stroke(175);
strokeWeight(2);
for (int i = 0; i < nchan; i++) {
line(x1 + w1/3 + 2, y1 + (((h1-1)/(nchan+1))*(i+1)), x2 - 3, y1 + (((h1-1)/(nchan+1))*(i+1)));
}
line(x1 + 2*(w1/3) - 1, y1 + 1, x1 + 2*(w1/3) - 1, y1 + (h1-1) - 1);
strokeWeight(0);
//channel buttons
for (int i = 0; i < nchan; i++) {
channelSettingButtons[i][0].draw(); //draw on/off channel buttons
//draw impedance buttons
for (int j = 0; j < 2; j++) {
impedanceCheckButtons[i][j].draw();
}
}
//label impedance button columns
fill(bgColor);
text("P", x1 + 1*(w1/2), y1 + 12);
text("N", x1 + 5*(w1/6) - 2, y1 + 12);
if (showFullController) {
//background
noStroke();
fill(0, 0, 0, 100);
rect(x1 + w1, y1, w2, h2);
// [numChan] x 5 ... all channel setting buttons (other than on/off)
for (int i = 0; i < nchan; i++) {
for (int j = 1; j < 6; j++) {
// println("drawing button " + i + "," + j);
// println("Button: " + channelSettingButtons[i][j]);
channelSettingButtons[i][j].draw();
}
}
//draw column headers for channel settings behind EEG graph
fill(bgColor);
text("PGA Gain", x2 + (w2/10)*1, y1 - 12);
text("Input Type", x2 + (w2/10)*3, y1 - 12);
text(" Bias ", x2 + (w2/10)*5, y1 - 12);
text("SRB2", x2 + (w2/10)*7, y1 - 12);
text("SRB1", x2 + (w2/10)*9, y1 - 12);
//if mode is not from OpenBCI, draw a dark overlay to indicate that you cannot edit these settings
if (eegDataSource != DATASOURCE_NORMAL && eegDataSource != DATASOURCE_NORMAL_W_AUX) {
fill(0, 0, 0, 200);
noStroke();
rect(x2-2, y2, w2+1, h2);
fill(255);
textSize(24);
text("DATA SOURCE (LIVE) only", x2 + (w2/2), y2 + (h2/2));
}
}
if ((eegDataSource != DATASOURCE_NORMAL) && (eegDataSource != DATASOURCE_NORMAL_W_AUX)) {
fill(0, 0, 0, 200);
rect(x1 + w1/3 + 1, y1, 2*(w1/3) - 3, h1 - 2);
}
for (int i = 0; i < nchan; i++) {
if (drawImpedanceValues[i] == true) {
gui.impValuesMontage[i].draw(); //impedance values on montage plot
}
}
popStyle();
}
public void mousePressed() {
//if fullChannelController and one of the buttons (other than ON/OFF) is clicked
//if dataSource is coming from OpenBCI, allow user to interact with channel controller
if ( (eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX) ) {
if (showFullController) {
for (int i = 0; i < nchan; i++) { //When [i][j] button is clicked
for (int j = 1; j < numSettingsPerChannel; j++) {
if (channelSettingButtons[i][j].isMouseHere()) {
//increment [i][j] channelSettingValue by, until it reaches max values per setting [j],
channelSettingButtons[i][j].wasPressed = true;
channelSettingButtons[i][j].isActive = true;
}
}
}
}
}
//on/off button and Imp buttons can always be clicked/released
for (int i = 0; i < nchan; i++) {
if (channelSettingButtons[i][0].isMouseHere()) {
channelSettingButtons[i][0].wasPressed = true;
channelSettingButtons[i][0].isActive = true;
}
//only allow editing of impedance if dataSource == from OpenBCI
if ((eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX)) {
if (impedanceCheckButtons[i][0].isMouseHere()) {
impedanceCheckButtons[i][0].wasPressed = true;
impedanceCheckButtons[i][0].isActive = true;
}
if (impedanceCheckButtons[i][1].isMouseHere()) {
impedanceCheckButtons[i][1].wasPressed = true;
impedanceCheckButtons[i][1].isActive = true;
}
}
}
}
public void mouseReleased() {
//if fullChannelController and one of the buttons (other than ON/OFF) is released
if (showFullController) {
for (int i = 0; i < nchan; i++) { //When [i][j] button is clicked
for (int j = 1; j < numSettingsPerChannel; j++) {
if (channelSettingButtons[i][j].isMouseHere() && channelSettingButtons[i][j].wasPressed == true) {
if (channelSettingValues[i][j] < maxValuesPerSetting[j]) {
channelSettingValues[i][j]++; //increment [i][j] channelSettingValue by, until it reaches max values per setting [j],
} else {
channelSettingValues[i][j] = '0';
}
// if you're not currently writing a channel and not waiting to rewrite after you've finished mashing the button
if (!openBCI.get_isWritingChannel() && rewriteChannelWhenDoneWriting == false) {
initChannelWrite(i);//write new ADS1299 channel row values to OpenBCI
} else { //else wait until a the current write has finished and then write again ... this is to not overwrite the wrong values while writing a channel
verbosePrint("CONGRATULATIONS, YOU'RE MASHING BUTTONS!");
rewriteChannelWhenDoneWriting = true;
channelToWriteWhenDoneWriting = i;
}
}
// if(!channelSettingButtons[i][j].isMouseHere()){
channelSettingButtons[i][j].isActive = false;
channelSettingButtons[i][j].wasPressed = false;
// }
}
}
}
//ON/OFF button can always be clicked/released
for (int i = 0; i < nchan; i++) {
//was on/off clicked?
if (channelSettingButtons[i][0].isMouseHere() && channelSettingButtons[i][0].wasPressed == true) {
if (channelSettingValues[i][0] < maxValuesPerSetting[0]) {
channelSettingValues[i][0] = '1'; //increment [i][j] channelSettingValue by, until it reaches max values per setting [j],
// channelSettingButtons[i][0].setColorNotPressed(color(25,25,25));
// powerDownChannel(i);
deactivateChannel(i);
} else {
channelSettingValues[i][0] = '0';
// channelSettingButtons[i][0].setColorNotPressed(color(255));
// powerUpChannel(i);
activateChannel(i);
}
// writeChannelSettings(i);//write new ADS1299 channel row values to OpenBCI
}
//was P imp check button clicked?
if (impedanceCheckButtons[i][0].isMouseHere() && impedanceCheckButtons[i][0].wasPressed == true) {
if (impedanceCheckValues[i][0] < '1') {
// impedanceCheckValues[i][0] = '1'; //increment [i][j] channelSettingValue by, until it reaches max values per setting [j],
// channelSettingButtons[i][0].setColorNotPressed(color(25,25,25));
// writeImpedanceSettings(i);
initImpWrite(i, 'p', '1');
//initImpWrite
verbosePrint("a");
} else {
// impedanceCheckValues[i][0] = '0';
// channelSettingButtons[i][0].setColorNotPressed(color(255));
// writeImpedanceSettings(i);
initImpWrite(i, 'p', '0');
verbosePrint("b");
}
// writeChannelSettings(i);//write new ADS1299 channel row values to OpenBCI
}
//was N imp check button clicked?
if (impedanceCheckButtons[i][1].isMouseHere() && impedanceCheckButtons[i][1].wasPressed == true) {
if (impedanceCheckValues[i][1] < '1') {
initImpWrite(i, 'n', '1');
//initImpWrite
verbosePrint("c");
} else {
initImpWrite(i, 'n', '0');
verbosePrint("d");
}
// writeChannelSettings(i);//write new ADS1299 channel row values to OpenBCI
}
channelSettingButtons[i][0].isActive = false;
channelSettingButtons[i][0].wasPressed = false;
impedanceCheckButtons[i][0].isActive = false;
impedanceCheckButtons[i][0].wasPressed = false;
impedanceCheckButtons[i][1].isActive = false;
impedanceCheckButtons[i][1].wasPressed = false;
}
update(); //update once to refresh button values
}
public void fillValuesBasedOnDefault(byte _defaultValues) {
//interpret incoming HEX value (from OpenBCI) and pass into all default channelSettingValues
//dencode byte from OpenBCI and break it apart into the channelSettingValues[][] array
}
public void powerDownChannel(int _numChannel) {
verbosePrint("Powering down channel " + str(int(_numChannel) + int(1)));
//save SRB2 and BIAS settings in 2D history array (to turn back on when channel is reactivated)
previousBIAS[_numChannel] = channelSettingValues[_numChannel][3];
previousSRB2[_numChannel] = channelSettingValues[_numChannel][4];
channelSettingValues[_numChannel][3] = '0'; //make sure to disconnect from BIAS
channelSettingValues[_numChannel][4] = '0'; //make sure to disconnect from SRB2
// initChannelWrite(_numChannel);//writeChannelSettings
channelSettingValues[_numChannel][0] = '1'; //update powerUp/powerDown value of 2D array
//if(_numChannel < 8){
verbosePrint("Command: " + command_deactivate_channel[_numChannel]);
//openBCI.serial_openBCI.write(command_deactivate_channel[_numChannel]);
openBCI.deactivateChannel(_numChannel); //assumes numChannel counts from zero (not one)...handles regular and daisy channels
//}else{ //if a daisy channel
// verbosePrint("Command: " + command_deactivate_channel_daisy[_numChannel - 8]);
// openBCI.serial_openBCI.write(command_deactivate_channel_daisy[_numChannel - 8]);
//}
}
public void powerUpChannel(int _numChannel) {
verbosePrint("Powering up channel " + str(int(_numChannel) + int(1)));
//replace SRB2 and BIAS settings with values from 2D history array
channelSettingValues[_numChannel][3] = previousBIAS[_numChannel];
channelSettingValues[_numChannel][4] = previousSRB2[_numChannel];
// initChannelWrite(_numChannel);//writeChannelSettings
channelSettingValues[_numChannel][0] = '0'; //update powerUp/powerDown value of 2D array
//if(_numChannel < 8){
verbosePrint("Command: " + command_activate_channel[_numChannel]);
//openBCI.serial_openBCI.write(command_activate_channel[_numChannel]);
openBCI.activateChannel(_numChannel); //assumes numChannel counts from zero (not one)...handles regular and daisy channels//assumes numChannel counts from zero (not one)...handles regular and daisy channels
//} else{ //if a daisy channel
// verbosePrint("Command: " + command_activate_channel_daisy[_numChannel - 8]);
// openBCI.serial_openBCI.write(command_activate_channel_daisy[_numChannel - 8]);
//}
}
public void initChannelWrite(int _numChannel) {
//after clicking any button, write the new settings for that channel to OpenBCI
if (!openBCI.get_isWritingImp()) { //make sure you aren't currently writing imp settings for a channel
verbosePrint("Writing channel settings for channel " + str(_numChannel+1) + " to OpenBCI!");
openBCI.initChannelWrite(_numChannel);
channelToWrite = _numChannel;
}
}
public void initImpWrite(int _numChannel, char pORn, char onORoff) {
//after clicking any button, write the new settings for that channel to OpenBCI
if (!openBCI.get_isWritingChannel()) { //make sure you aren't currently writing imp settings for a channel
// if you're not currently writing a channel and not waiting to rewrite after you've finished mashing the button
if (!openBCI.get_isWritingImp() && rewriteImpedanceWhenDoneWriting == false) {
verbosePrint("Writing impedance check settings (" + pORn + "," + onORoff + ") for channel " + str(_numChannel+1) + " to OpenBCI!");
if (pORn == 'p') {
impedanceCheckValues[_numChannel][0] = onORoff;
}
if (pORn == 'n') {
impedanceCheckValues[_numChannel][1] = onORoff;
}
openBCI.initImpWrite(_numChannel);
impChannelToWrite = _numChannel;
} else { //else wait until a the current write has finished and then write again ... this is to not overwrite the wrong values while writing a channel
verbosePrint("CONGRATULATIONS, YOU'RE MASHING BUTTONS!");
rewriteImpedanceWhenDoneWriting = true;
impChannelToWriteWhenDoneWriting = _numChannel;
if (pORn == 'p') {
final_pORn = 'p';
}
if (pORn == 'n') {
final_pORn = 'n';
}
final_onORoff = onORoff;
}
}
}
public void createChannelSettingButtons() {
//the size and space of these buttons are dependendant on the size of the screen and full ChannelController
verbosePrint("ChannelController: createChannelSettingButtons: creating channel setting buttons...");
int buttonW = 0;
int buttonX = 0;
int buttonH = 0;
int buttonY = 0; //variables to be used for button creation below
String buttonString = "";
Button tempButton;
//create all activate/deactivate buttons (left-most button in widget left of EEG graph). These buttons are always visible
for (int i = 0; i < nchan; i++) {
buttonW = int((w1 - (spacer1 *4)) / 3);
buttonX = int(x1 + (spacer1));
// buttonH = int((h1 / (nchan + 1)) - (spacer1/2));
buttonH = buttonW;
buttonY = int(y1 + ((h1/(nchan+1))*(i+1)) - (buttonH/2));
buttonString = str(i+1);
tempButton = new Button (buttonX, buttonY, buttonW, buttonH, buttonString, 14);
channelSettingButtons[i][0] = tempButton;
}
//create all (P)ositive impedance check butttons ... these are the buttons just to the right of activate/deactivate buttons ... These are also always visible
//create all (N)egative impedance check butttons ... these are the buttons just to the right of activate/deactivate buttons ... These are also always visible
int downSizer = 6;
for (int i = 0; i < nchan; i++) {
for (int j = 1; j < 3; j++) {
buttonW = int(((w1 - (spacer1 *4)) / 3) - downSizer);
buttonX = int((x1 + j*(buttonW+6) + (j+1)*(spacer1)) + (downSizer/2) + 1);
// buttonH = int((h2 / (nchan + 1)) - (spacer2/2));
buttonY = int((y1 + (((h1-1)/(nchan+1))*(i+1)) - (buttonH/2)) + (downSizer/2) + 1);
buttonString = "";
tempButton = new Button (buttonX, buttonY, buttonW, buttonW, buttonString, 14);
impedanceCheckButtons[i][j-1] = tempButton;
}
}
//create all other channel setting buttons... these are only visible when the user toggles to "showFullController = true"
for (int i = 0; i < nchan; i++) {
for (int j = 1; j < 6; j++) {
buttonW = int((w2 - (spacer2*6)) / 5);
buttonX = int((x2 + (spacer2 * (j))) + ((j-1) * buttonW));
// buttonH = int((h2 / (nchan + 1)) - (spacer2/2));
buttonY = int(y2 + (((h2-1)/(nchan+1))*(i+1)) - (buttonH/2));
buttonString = "N/A";
tempButton = new Button (buttonX, buttonY, buttonW, buttonH, buttonString, 14);
channelSettingButtons[i][j] = tempButton;
}
}
}
};
+792
Ver Arquivo
@@ -0,0 +1,792 @@
//////////////////////////////////////////////////////////////////////////
//
// System Control Panel
// - Select serial port from dropdown
// - Select default configuration (EEG, EKG, EMG)
// - Select Electrode Count (8 vs 16)
// - Select data mode (synthetic, playback file, real-time)
// - Record data? (y/n)
// - select output location
// - link to help guide
// - buttons to start/stop/reset application
//
// Written by: Conor Russomanno (Oct. 2014)
//
//////////////////////////////////////////////////////////////////////////
import controlP5.*;
ControlP5 cp5; //program-wide instance of ControlP5
CallbackListener cb = new CallbackListener() { //used by ControlP5 to clear text field on double-click
public void controlEvent(CallbackEvent theEvent) {
println("CallbackListener: controlEvent: clearing");
cp5.get(Textfield.class, "fileName").clear();
}
};
MenuList sourceList;
//Global buttons and elements for the control panel (changed within the classes below)
MenuList serialList;
String[] serialPorts = new String[Serial.list().length];
MenuList sdTimes;
color boxColor = color(200);
color boxStrokeColor = color(138, 146, 153);
color isSelected_color = color(184, 220, 105);
// Button openClosePort;
// boolean portButtonPressed;
Button refreshPort;
boolean refreshButtonPressed = false;
Button initSystemButton;
boolean initButtonPressed = false; //default false
Button autoFileName;
boolean fileButtonPressed = false;
Button chanButton8;
boolean chanButton8Pressed = false;
Button chanButton16;
boolean chanButton16Pressed = false;
Button selectPlaybackFile;
boolean selectPlaybackFilePressed = false;
Button selectSDFile;
boolean selectSDFilePressed = false;
class ControlPanel {
public int x, y, w, h;
public boolean isOpen;
boolean showSourceBox, showSerialBox, showFileBox, showChannelBox, showInitBox;
PlotFontInfo fontInfo;
//various control panel elements that are unique to specific datasources
DataSourceBox dataSourceBox;
SerialBox serialBox;
DataLogBox dataLogBox;
ChannelCountBox channelCountBox;
InitBox initBox;
PlaybackFileBox playbackFileBox;
SDConverterBox sdConverterBox;
SDBox sdBox;
boolean drawStopInstructions;
int globalPadding; //design feature: passed through to all box classes as the global spacing .. in pixels .. for all elements/subelements
int globalBorder;
boolean convertingSD = false;
ControlPanel(OpenBCI_GUI mainClass) {
x = 2;
y = 2 + controlPanelCollapser.but_dy;
w = controlPanelCollapser.but_dx;
h = height - int(helpWidget.h);
isOpen = true;
fontInfo = new PlotFontInfo();
// f1 = createFont("Raleway-SemiBold.otf", 16);
// f2 = createFont("Raleway-Regular.otf", 15);
// f3 = createFont("Raleway-SemiBold.otf", 15);
globalPadding = 10; //controls the padding of all elements on the control panel
globalBorder = 0; //controls the border of all elements in the control panel ... using processing's stroke() instead
cp5 = new ControlP5(mainClass);
//boxes active when eegDataSource = Normal (OpenBCI)
dataSourceBox = new DataSourceBox(x, y, w, h, globalPadding);
serialBox = new SerialBox(x + w, dataSourceBox.y, w, h, globalPadding);
dataLogBox = new DataLogBox(x + w, (serialBox.y + serialBox.h), w, h, globalPadding);
channelCountBox = new ChannelCountBox(x + w, (dataLogBox.y + dataLogBox.h), w, h, globalPadding);
sdBox = new SDBox(x + w, (channelCountBox.y + channelCountBox.h), w, h, globalPadding);
//boxes active when eegDataSource = Playback
playbackFileBox = new PlaybackFileBox(x + w, dataSourceBox.y, w, h, globalPadding);
sdConverterBox = new SDConverterBox(x + w, (playbackFileBox.y + playbackFileBox.h), w, h, globalPadding);
initBox = new InitBox(x, (dataSourceBox.y + dataSourceBox.h), w, h, globalPadding);
}
public void update() {
//toggle view of cp5 / serial list selection table
if (isOpen) { // if control panel is open
if (!cp5.isVisible()) { //and cp5 is not visible
cp5.show(); // shot it
}
} else { //the opposite of above
if (cp5.isVisible()) {
cp5.hide();
}
}
//update all boxes if they need to be
dataSourceBox.update();
serialBox.update();
dataLogBox.update();
channelCountBox.update();
sdBox.update();
initBox.update();
serialList.updateMenu();
//SD File Conversion
while (convertingSD == true) {
convertSDFile();
}
}
public void draw() {
pushStyle();
noStroke();
//dark overlay of rest of interface to indicate it's not clickable
fill(0, 0, 0, 185);
rect(0, 0, width, height);
pushStyle();
fill(255);
noStroke();
rect(0, 0, width, 32);
popStyle();
// //background pane of control panel
// fill(35,35,35);
// rect(0,0,w,h);
popStyle();
initBox.draw();
if (systemMode == 10) {
drawStopInstructions = true;
}
if (systemMode != 10) { // only draw control panel boxes if system running is false
dataSourceBox.draw();
drawStopInstructions = false;
cp5.setVisible(true);//make sure controlP5 elements are visible
if (eegDataSource == 0) { //when data source is from OpenBCI
serialBox.draw();
dataLogBox.draw();
channelCountBox.draw();
sdBox.draw();
cp5.get(Textfield.class, "fileName").setVisible(true); //make sure the data file field is visible
cp5.get(MenuList.class, "serialList").setVisible(true); //make sure the serialList menulist is visible
cp5.get(MenuList.class, "sdTimes").setVisible(true); //make sure the SD time record options menulist is visible
//make sure serial list is visible
//set other CP5 controllers invisible
} else if (eegDataSource == 1) { //when data source is from playback file
playbackFileBox.draw();
sdConverterBox.draw();
//set other CP5 controllers invisible
cp5.get(Textfield.class, "fileName").setVisible(false); //make sure the data file field is visible
cp5.get(MenuList.class, "serialList").setVisible(false);
cp5.get(MenuList.class, "sdTimes").setVisible(false);
} else if (eegDataSource == 2) {
//make sure serial list is visible
//set other CP5 controllers invisible
cp5.get(Textfield.class, "fileName").setVisible(false); //make sure the data file field is visible
cp5.get(MenuList.class, "serialList").setVisible(false);
cp5.get(MenuList.class, "sdTimes").setVisible(false);
} else {
//set other CP5 controllers invisible
cp5.get(Textfield.class, "fileName").setVisible(false); //make sure the data file field is visible
cp5.get(MenuList.class, "serialList").setVisible(false);
cp5.get(MenuList.class, "sdTimes").setVisible(false);
}
} else {
cp5.setVisible(false); // if isRunning is true, hide all controlP5 elements
}
//draw the box that tells you to stop the system in order to edit control settings
if (drawStopInstructions) {
pushStyle();
fill(boxColor);
strokeWeight(1);
stroke(boxStrokeColor);
rect(x, y, w, dataSourceBox.h); //draw background of box
String stopInstructions = "Press the \"STOP SYSTEM\" button to edit system settings.";
textAlign(CENTER, TOP);
textFont(f2);
fill(bgColor);
text(stopInstructions, x + globalPadding*2, y + globalPadding*4, w - globalPadding*4, dataSourceBox.h - globalPadding*4);
popStyle();
}
}
//mouse pressed in control panel
public void CPmousePressed() {
verbosePrint("CPmousePressed");
if (initSystemButton.isMouseHere()) {
initSystemButton.setIsActive(true);
initButtonPressed = true;
}
//only able to click buttons of control panel when system is not running
if (systemMode != 10) {
//active buttons during DATASOURCE_NORMAL
if (eegDataSource == 0) {
if (refreshPort.isMouseHere()) {
refreshPort.setIsActive(true);
refreshButtonPressed = true;
}
if (autoFileName.isMouseHere()) {
autoFileName.setIsActive(true);
fileButtonPressed = true;
}
if (chanButton8.isMouseHere()) {
chanButton8.setIsActive(true);
chanButton8Pressed = true;
chanButton8.color_notPressed = isSelected_color;
chanButton16.color_notPressed = autoFileName.color_notPressed; //default color of button
}
if (chanButton16.isMouseHere()) {
chanButton16.setIsActive(true);
chanButton16Pressed = true;
chanButton8.color_notPressed = autoFileName.color_notPressed; //default color of button
chanButton16.color_notPressed = isSelected_color;
}
}
//active buttons during DATASOURCE_PLAYBACKFILE
if (eegDataSource == 1) {
if (selectPlaybackFile.isMouseHere()) {
selectPlaybackFile.setIsActive(true);
selectPlaybackFilePressed = true;
}
if (selectSDFile.isMouseHere()) {
selectSDFile.setIsActive(true);
selectSDFilePressed = true;
}
}
}
// output("Text File Name: " + cp5.get(Textfield.class,"fileName").getText());
}
//mouse released in control panel
public void CPmouseReleased() {
verbosePrint("CPMouseReleased: CPmouseReleased start...");
if (initSystemButton.isMouseHere() && initButtonPressed) {
//if system is not active ... initate system and flip button state
if (initSystemButton.but_txt == "START SYSTEM") {
if ((eegDataSource == DATASOURCE_NORMAL || eegDataSource == DATASOURCE_NORMAL_W_AUX) && openBCI_portName == "N/A") { //if data source == normal && if no serial port selected OR no SD setting selected
output("No Serial/COM port selected. Please select your Serial/COM port and retry system initiation.");
initButtonPressed = false;
initSystemButton.setIsActive(false);
return;
} else if (eegDataSource == DATASOURCE_PLAYBACKFILE && playbackData_fname == "N/A") { //if data source == playback && playback file == 'N/A'
output("No playback file selected. Please select a playback file and retry system initiation."); // tell user that they need to select a file before the system can be started
initButtonPressed = false;
initSystemButton.setIsActive(false);
return;
} else if (eegDataSource == -1) {//if no data source selected
output("No DATA SOURCE selected. Please select a DATA SOURCE and retry system initiation.");//tell user they must select a data source before initiating system
initButtonPressed = false;
initSystemButton.setIsActive(false);
return;
} else { //otherwise, initiate system!
verbosePrint("ControlPanel: CPmouseReleased: init");
initSystemButton.setString("STOP SYSTEM");
//global steps to START SYSTEM
// prepare the serial port
verbosePrint("ControlPanel — port is open: " + openBCI.isSerialPortOpen());
if (openBCI.isSerialPortOpen() == true) {
openBCI.closeSerialPort();
}
fileName = cp5.get(Textfield.class, "fileName").getText(); // store the current text field value of "File Name" to be passed along to dataFiles
initSystem();
}
}
//if system is already active ... stop system and flip button state back
else {
output("SYSTEM STOPPED");
initSystemButton.setString("START SYSTEM");
haltSystem();
}
//cursor(ARROW); //this this back to ARROW
}
//open or close serial port if serial port button is pressed (left button in serial widget)
if (refreshPort.isMouseHere() && refreshButtonPressed) {
output("Serial/COM List Refreshed");
serialPorts = new String[Serial.list().length];
serialPorts = Serial.list();
serialList.items.clear();
for (int i = 0; i < serialPorts.length; i++) {
String tempPort = serialPorts[(serialPorts.length-1) - i]; //list backwards... because usually our port is at the bottom
serialList.addItem(makeItem(tempPort));
}
serialList.updateMenu();
}
//open or close serial port if serial port button is pressed (left button in serial widget)
if (autoFileName.isMouseHere() && fileButtonPressed) {
output("Autogenerated \"File Name\" based on current date/time");
cp5.get(Textfield.class, "fileName").setText(getDateString());
}
if (chanButton8.isMouseHere() && chanButton8Pressed) {
nchan = 8;
fftBuff = new FFT[nchan]; //from the minim library
yLittleBuff_uV = new float[nchan][nPointsPerUpdate];
output("channel count set to " + str(nchan));
updateChannelArrays(nchan); //make sure to reinitialize the channel arrays with the right number of channels
}
if (chanButton16.isMouseHere() && chanButton16Pressed) {
nchan = 16;
fftBuff = new FFT[nchan]; //reinitialize the FFT buffer
yLittleBuff_uV = new float[nchan][nPointsPerUpdate];
output("channel count set to " + str(nchan));
updateChannelArrays(nchan); //make sure to reinitialize the channel arrays with the right number of channels
}
if (selectPlaybackFile.isMouseHere() && selectPlaybackFilePressed) {
output("select a file for playback");
selectInput("Select a pre-recorded file for playback:", "playbackSelected");
}
if (selectSDFile.isMouseHere() && selectSDFilePressed) {
output("select an SD file to convert to a playback file");
createPlaybackFileFromSD();
selectInput("Select an SD file to convert for playback:", "sdFileSelected");
}
//reset all buttons to false
refreshPort.setIsActive(false);
refreshButtonPressed = false;
initSystemButton.setIsActive(false);
initButtonPressed = false;
autoFileName.setIsActive(false);
fileButtonPressed = false;
chanButton8.setIsActive(false);
chanButton8Pressed = false;
chanButton16.setIsActive(false);
chanButton16Pressed = false;
selectPlaybackFile.setIsActive(false);
selectPlaybackFilePressed = false;
selectSDFile.setIsActive(false);
selectSDFilePressed = false;
}
};
public void controlEvent(ControlEvent theEvent) {
if (theEvent.isFrom("sourceList")) {
Map bob = ((MenuList)theEvent.getController()).getItem(int(theEvent.getValue()));
String str = (String)bob.get("headline");
str = str.substring(0, str.length()-5);
//output("Data Source = " + str);
int newDataSource = int(theEvent.getValue());
eegDataSource = newDataSource; // reset global eegDataSource to the selected value from the list
output("The new data source is " + str);
}
if (theEvent.isFrom("serialList")) {
Map bob = ((MenuList)theEvent.getController()).getItem(int(theEvent.getValue()));
openBCI_portName = (String)bob.get("headline");
output("OpenBCI Port Name = " + openBCI_portName);
}
if (theEvent.isFrom("sdTimes")) {
Map bob = ((MenuList)theEvent.getController()).getItem(int(theEvent.getValue()));
sdSettingString = (String)bob.get("headline");
sdSetting = int(theEvent.getValue());
if (sdSetting != 0) {
output("OpenBCI microSD Setting = " + sdSettingString + " recording time");
} else {
output("OpenBCI microSD Setting = " + sdSettingString);
}
verbosePrint("SD setting = " + sdSetting);
}
}
//==============================================================================//
// BELOW ARE THE CLASSES FOR THE VARIOUS //
// CONTROL PANEL BOXes (control widgets) //
//==============================================================================//
class DataSourceBox {
int x, y, w, h, padding; //size and position
CheckBox sourceCheckBox;
DataSourceBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 115;
padding = _padding;
sourceList = new MenuList(cp5, "sourceList", w - padding*2, 72, f2);
// sourceList.itemHeight = 28;
// sourceList.padding = 9;
sourceList.setPosition(x + padding, y + padding*2 + 13);
sourceList.addItem(makeItem("LIVE (from OpenBCI) >"));
sourceList.addItem(makeItem("PLAYBACK (from file) >"));
sourceList.addItem(makeItem("SYNTHETIC (algorithmic) >"));
sourceList.scrollerLength = 10;
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("DATA SOURCE", x + padding, y + padding);
popStyle();
//draw contents of Data Source Box at top of control panel
//Title
//checkboxes of system states
}
};
class SerialBox {
int x, y, w, h, padding; //size and position
//connect/disconnect button
//Refresh list button
//String port status;
SerialBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 147;
padding = _padding;
// openClosePort = new Button (padding + border, y + padding*3 + 13 + 150, (w-padding*3)/2, 24, "OPEN PORT", fontInfo.buttonLabel_size);
refreshPort = new Button (x + padding, y + padding*3 + 13 + 71, w - padding*2, 24, "REFRESH LIST", fontInfo.buttonLabel_size);
serialList = new MenuList(cp5, "serialList", w - padding*2, 72, f2);
serialList.setPosition(x + padding, y + padding*2 + 13);
serialPorts = Serial.list();
for (int i = 0; i < serialPorts.length; i++) {
String tempPort = serialPorts[(serialPorts.length-1) - i]; //list backwards... because usually our port is at the bottom
serialList.addItem(makeItem(tempPort));
}
}
public void update() {
// serialList.updateMenu();
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("SERIAL/COM PORT", x + padding, y + padding);
popStyle();
// openClosePort.draw();
refreshPort.draw();
}
public void refreshSerialList() {
}
};
class DataLogBox {
int x, y, w, h, padding; //size and position
String fileName;
//text field for inputing text
//create/open/closefile button
String fileStatus;
boolean isFileOpen; //true if file has been activated and is ready to write to
//String port status;
DataLogBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 101;
padding = _padding;
//instantiate button
//figure out default file name (from Chip's code)
isFileOpen = false; //set to true on button push
fileStatus = "NO FILE CREATED";
//button to autogenerate file name based on time/date
autoFileName = new Button (x + padding, y + 66, w-(padding*2), 24, "AUTOGENERATE FILE NAME", fontInfo.buttonLabel_size);
cp5.addTextfield("fileName")
.setPosition(x + 90, y + 32)
.setCaptionLabel("")
.setSize(157, 26)
.setFont(f2)
.setFocus(false)
.setColor(color(26, 26, 26))
.setColorBackground(color(255, 255, 255)) // text field bg color
.setColorValueLabel(color(0, 0, 0)) // text color
.setColorForeground(isSelected_color) // border color when not selected
.setColorActive(isSelected_color) // border color when selected
.setColorCursor(color(26, 26, 26))
.setText(getDateString())
.align(5, 10, 20, 40)
.onDoublePress(cb)
.setAutoClear(true);
//clear text field on double click
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("DATA LOG FILE", x + padding, y + padding);
textFont(f3);
text("File Name", x + padding, y + padding*2 + 18);
popStyle();
autoFileName.draw();
}
};
class ChannelCountBox {
int x, y, w, h, padding; //size and position
boolean isSystemInitialized;
// button for init/halt system
ChannelCountBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 73;
padding = _padding;
chanButton8 = new Button (x + padding, y + padding*2 + 18, (w-padding*3)/2, 24, "8 CHANNELS", fontInfo.buttonLabel_size);
if (nchan == 8) chanButton8.color_notPressed = isSelected_color; //make it appear like this one is already selected
chanButton16 = new Button (x + padding*2 + (w-padding*3)/2, y + padding*2 + 18, (w-padding*3)/2, 24, "16 CHANNELS", fontInfo.buttonLabel_size);
if (nchan == 16) chanButton16.color_notPressed = isSelected_color; //make it appear like this one is already selected
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("CHANNEL COUNT", x + padding, y + padding);
fill(bgColor); //set color to green
textFont(f1);
textAlign(LEFT, TOP);
text("(" + str(nchan) + ")", x + padding + 142, y + padding); // print the channel count in green next to the box title
popStyle();
chanButton8.draw();
chanButton16.draw();
}
};
class PlaybackFileBox {
int x, y, w, h, padding; //size and position
PlaybackFileBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 67;
padding = _padding;
selectPlaybackFile = new Button (x + padding, y + padding*2 + 13, w - padding*2, 24, "SELECT PLAYBACK FILE", fontInfo.buttonLabel_size);
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("PLAYBACK FILE", x + padding, y + padding);
popStyle();
selectPlaybackFile.draw();
// chanButton16.draw();
}
};
class SDBox {
int x, y, w, h, padding; //size and position
SDBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 150;
padding = _padding;
sdTimes = new MenuList(cp5, "sdTimes", w - padding*2, 108, f2);
sdTimes.setPosition(x + padding, y + padding*2 + 13);
serialPorts = Serial.list();
//add items for the various SD times
sdTimes.addItem(makeItem("Do not write to SD..."));
sdTimes.addItem(makeItem("5 minute maximum"));
sdTimes.addItem(makeItem("15 minute maximum"));
sdTimes.addItem(makeItem("30 minute maximum"));
sdTimes.addItem(makeItem("1 hour maximum"));
sdTimes.addItem(makeItem("2 hours maximum"));
sdTimes.addItem(makeItem("4 hour maximum"));
sdTimes.addItem(makeItem("12 hour maximum"));
sdTimes.addItem(makeItem("24 hour maximum"));
sdTimes.activeItem = sdSetting; //added to indicate default choice (sdSetting is in OpenBCI_GUI)
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("WRITE TO SD (Y/N)?", x + padding, y + padding);
popStyle();
//the drawing of the sdTimes is handled earlier in ControlPanel.draw()
}
};
class SDConverterBox {
int x, y, w, h, padding; //size and position
SDConverterBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 67;
padding = _padding;
selectSDFile = new Button (x + padding, y + padding*2 + 13, w - padding*2, 24, "SELECT SD FILE", fontInfo.buttonLabel_size);
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
fill(bgColor);
textFont(f1);
textAlign(LEFT, TOP);
text("CONVERT SD FOR PLAYBACK", x + padding, y + padding);
popStyle();
selectSDFile.draw();
}
};
class InitBox {
int x, y, w, h, padding; //size and position
boolean initButtonPressed; //default false
boolean isSystemInitialized;
// button for init/halt system
InitBox(int _x, int _y, int _w, int _h, int _padding) {
x = _x;
y = _y;
w = _w;
h = 50;
padding = _padding;
//init button
initSystemButton = new Button (padding, y + padding, w-padding*2, h - padding*2, "START SYSTEM", fontInfo.buttonLabel_size);
//initSystemButton.color_notPressed = color(boolor);
//initSystemButton.buttonStrokeColor = color(boxColor);
initButtonPressed = false;
}
public void update() {
}
public void draw() {
pushStyle();
fill(boxColor);
stroke(boxStrokeColor);
strokeWeight(1);
rect(x, y, w, h);
popStyle();
initSystemButton.draw();
}
};
void playbackSelected(File selection) {
if (selection == null) {
println("ControlPanel: playbackSelected: Window was closed or the user hit cancel.");
} else {
println("ControlPanel: playbackSelected: User selected " + selection.getAbsolutePath());
output("You have selected \"" + selection.getAbsolutePath() + "\" for playback.");
playbackData_fname = selection.getAbsolutePath();
}
}
+717
Ver Arquivo
@@ -0,0 +1,717 @@
//import ddf.minim.analysis.*; //for FFT
boolean drawEMG = false; //if true... toggles on EEG_Processing_User.draw and toggles off the headplot in Gui_Manager
class EEG_Processing_User {
private float fs_Hz; //sample rate
private int nchan;
boolean switchesActive = false;
//Left Eye Variables
boolean isTriggered_L = false;
float upperThreshold_L = 25; //default uV upper threshold value ... this will automatically change over time
float lowerThreshold_L = 0; //default uV lower threshold value ... this will automatically change over time
int averagePeriod_L = 125; //number of data packets to average over (250 = 1 sec)
int thresholdPeriod_L = 1250; //number of packets
int ourChan_L = 1 - 1; //channel being monitored ... "3 - 1" means channel 3 (with a 0 index)
float myAverage_L = 0.0; //this will change over time ... used for calculations below
float acceptableLimitUV_L = 200; //uV values above this limit are excluded, as a result of them almost certainly being noise...
int uncounted_L = 0;
//prez related
boolean switchTripped_L = false;
int switchCounter_L = 0;
float timeOfLastTrip_L = 0;
float tripThreshold_L = 0.75;
float untripThreshold_L = 0.6;
//Right Eye Variables
boolean isTriggered_R = false;
float upperThreshold_R= 25; //default uV upper threshold value ... this will automatically change over time
float lowerThreshold_R = 0; //default uV lower threshold value ... this will automatically change over time
int averagePeriod_R = 125; //number of data packets to average over (250 = 1 sec)
int thresholdPeriod_R = 1250; //number of packets
int ourChan_R = 2 - 1; //channel being monitored ... "3 - 1" means channel 3 (with a 0 index)
float myAverage_R = 0.0; //this will change over time ... used for calculations below
float acceptableLimitUV_R = 200; //uV values above this limit are excluded, as a result of them almost certainly being noise...
int uncounted_R = 0;
//prez related
boolean switchTripped_R = false;
int switchCounter_R = 0;
float timeOfLastTrip_R = 0;
float tripThreshold_R = 0.75;
float untripThreshold_R = 0.60;
//add your own variables here
boolean isTriggered = false; //boolean to keep track of when the trigger condition is met
float upperThreshold = 25; //default uV upper threshold value ... this will automatically change over time
float lowerThreshold = 0; //default uV lower threshold value ... this will automatically change over time
int averagePeriod = 125; //number of data packets to average over (250 = 1 sec)
int thresholdPeriod = 1250; //number of packets
int ourChan = 4 - 1; //channel being monitored ... "3 - 1" means channel 3 (with a 0 index)
float myAverage = 0.0; //this will change over time ... used for calculations below
float acceptableLimitUV = 150; //uV values above this limit are excluded, as a result of them almost certainly being noise...
boolean switchTripped = false;
int switchCounter = 0;
float timeOfLastTrip = 0;
float tripThreshold = 0.50;
float untripThreshold = 0.30;
//if writing to a serial port
int output = 0; //value between 0-255 that is the relative position of the current uV average between the rolling lower and upper uV thresholds
float output_normalized = 0; //converted to between 0-1
float output_adjusted = 0; //adjusted depending on range that is expected on the other end, ie 0-255?
//if writing to a serial port
int output_L = 0; //value between 0-255 that is the relative position of the current uV average between the rolling lower and upper uV thresholds
float output_normalized_L = 0; //converted to between 0-1
float output_adjusted_L = 0; //adjusted depending on range that is expected on the other end, ie 0-255?
//if writing to a serial port
int output_R = 0; //value between 0-255 that is the relative position of the current uV average between the rolling lower and upper uV thresholds
float output_normalized_R = 0; //converted to between 0-1
float output_adjusted_R = 0; //adjusted depending on range that is expected on the other end, ie 0-255?
//class constructor
EEG_Processing_User(int NCHAN, float sample_rate_Hz) {
nchan = NCHAN;
fs_Hz = sample_rate_Hz;
}
//add some functions here...if you'd like
//here is the processing routine called by the OpenBCI main program...update this with whatever you'd like to do
public void process(float[][] data_newest_uV, //holds raw EEG data that is new since the last call
float[][] data_long_uV, //holds a longer piece of buffered EEG data, of same length as will be plotted on the screen
float[][] data_forDisplay_uV, //this data has been filtered and is ready for plotting on the screen
FFT[] fftData) { //holds the FFT (frequency spectrum) of the latest data
//for example, you could loop over each EEG channel to do some sort of time-domain processing
//using the sample values that have already been filtered, as will be plotted on the display
float EEG_value_uV;
//chan 3
myAverage = 0.0;
for (int i = data_forDisplay_uV[ourChan].length - averagePeriod; i < data_forDisplay_uV[ourChan].length; i++) {
if (abs(data_forDisplay_uV[ourChan][i]) <= acceptableLimitUV) { //prevent BIG spikes from effecting the average
myAverage += abs(data_forDisplay_uV[ourChan][i]); //add value to average ... we will soon divide by # of packets
}
}
myAverage = myAverage / float(averagePeriod); //finishing the average
//Left Eye -- Chan 1
myAverage_L = 0.0;
for (int i = data_forDisplay_uV[ourChan_L].length - averagePeriod_L; i < data_forDisplay_uV[ourChan_L].length; i++) {
if (abs(data_forDisplay_uV[ourChan_L][i]) <= acceptableLimitUV_L) { //prevent BIG spikes from effecting the average
myAverage_L += abs(data_forDisplay_uV[ourChan_L][i]); //add value to average ... we will soon divide by # of packets
} else {
myAverage_L += acceptableLimitUV_L; //if it's greater than the limit, just add the limit
}
}
myAverage_L = myAverage_L / float(averagePeriod_L); //finishing the average
uncounted_L = 0;
//println("myAverage_L = " + myAverage_L);
//Right Eye -- Chan 2
myAverage_R = 0.0;
for (int i = data_forDisplay_uV[ourChan_R].length - averagePeriod_R; i < data_forDisplay_uV[ourChan_R].length; i++) {
if (abs(data_forDisplay_uV[ourChan_R][i]) <= acceptableLimitUV_R) { //prevent BIG spikes from effecting the average
myAverage_R += abs(data_forDisplay_uV[ourChan_R][i]); //add value to average ... we will soon divide by # of packets
} else {
myAverage_R += acceptableLimitUV_R;
}
}
myAverage_R = myAverage_R / float(averagePeriod_R); //finishing the average
uncounted_R = 0;
//println("uncounted_R" + uncounted_R);
//println("averagePeriod_R = " + averagePeriod_R);
//println("myAverage_R = " + myAverage_R);
//println("------------------");
//--------------------- some conditionals -- CHAN 3 -------------------------
if (myAverage >= upperThreshold && myAverage <= acceptableLimitUV) { //
upperThreshold = myAverage;
}
if (myAverage <= lowerThreshold) {
lowerThreshold = myAverage;
}
if (upperThreshold >= myAverage) {
upperThreshold -= (upperThreshold - 25)/(frameRate * 5); //have upper threshold creep downwards to keep range tight
}
if (lowerThreshold <= myAverage) {
lowerThreshold += (25 - lowerThreshold)/(frameRate * 5); //have lower threshold creep upwards to keep range tight
}
output = (int)map(myAverage, lowerThreshold, upperThreshold, 0, 255);
output_normalized = map(myAverage, lowerThreshold, upperThreshold, 0, 1);
output_adjusted = ((-0.1/(output_normalized*255.0)) + 255.0);
//trip the output to a value between 0-255
if (output < 0) output = 0;
if (output > 255) output = 255;
//attempt to write to serial_output. If this serial port does not exist, do nothing.
try {
//println("inMoov_output: | " + output + " |");
serial_output.write(output);
}
catch(RuntimeException e) {
if (isVerbose) println("serial not present");
}
//------------------ LEFT EYE & RIGHT EYE ------------------------- //
//LEFT
if (myAverage_L >= upperThreshold_L && myAverage_L <= acceptableLimitUV_L) { //
upperThreshold_L = myAverage_L;
}
if (myAverage_L <= lowerThreshold_L) {
lowerThreshold_L = myAverage_L;
}
if (upperThreshold_L >= (myAverage_L + 35)) {
//upperThreshold_L -= (upperThreshold_L)/(frameRate * 5); //have upper threshold creep downwards to keep range tight
upperThreshold_L *= .97;
}
if (lowerThreshold_L <= myAverage_L) {
lowerThreshold_L += (10 - lowerThreshold_L)/(frameRate * 5); //have lower threshold creep upwards to keep range tight
}
//output_L = (int)map(myAverage_L, lowerThreshold_L, upperThreshold_L, 0, 255);
output_normalized_L = map(myAverage_L, lowerThreshold_L, upperThreshold_L, 0, 1);
//output_adjusted_L = ((-0.1/(output_normalized_L*255.0)) + 255.0);
//RIGHT
if (myAverage_R >= upperThreshold_R && myAverage_R <= acceptableLimitUV_R) { //
upperThreshold_R = myAverage_R;
}
if (myAverage_R <= lowerThreshold_R) {
lowerThreshold_R = myAverage_R;
}
if (upperThreshold_R >= myAverage_R + 35) {
//upperThreshold_R -= (upperThreshold_R - 25)/(frameRate * 5); //have upper threshold creep downwards to keep range tight
upperThreshold_R *= .97;
}
if (lowerThreshold_R <= myAverage_R) {
lowerThreshold_R += (10 - lowerThreshold_R)/(frameRate * 5); //have lower threshold creep upwards to keep range tight
}
//output_L = (int)map(myAverage_L, lowerThreshold_L, upperThreshold_L, 0, 255);
output_normalized_R = map(myAverage_R, lowerThreshold_R, upperThreshold_R, 0, 1);
//output_adjusted_L = ((-0.1/(output_normalized_L*255.0)) + 255.0);
//======================= TRIPPING SWITCHES ==========================//
if (switchesActive) {
// =========================== RIGHT ================================ //
if (output_normalized_L >= tripThreshold_L && switchTripped_L == false && (millis() - timeOfLastTrip_L) >= 2000 && switchTripped_R == false) {
println("switchTripped_L = true");
switchTripped_L = true;
timeOfLastTrip_L = millis();
switchCounter_L = 1;
}
if (output_normalized_R >= tripThreshold_R && switchTripped_R == false && (millis() - timeOfLastTrip_R) >= 2000 && switchTripped_L == false) {
println("switchTripped_R = true");
switchTripped_R = true;
timeOfLastTrip_R = millis();
switchCounter_R = 1;
}
if ((millis() - timeOfLastTrip_R) >= 750 && (millis() - timeOfLastTrip_R) <= 1250) {
println("sweet zone R");
if (switchTripped_L) {
switchTripped_L = false;
myPresentation.slideBack();
}
}
//=========================== LEFT ================================
if ((millis() - timeOfLastTrip_L) >= 750 && (millis() - timeOfLastTrip_L) <= 1250) {
println("sweet zone L");
if (switchTripped_R) {
switchTripped_R = false;
myPresentation.slideForward();
}
}
if (millis() - timeOfLastTrip_L >= 250) {
switchTripped_L = false;
switchCounter_L = 0;
}
if (millis() - timeOfLastTrip_R >= 250) {
switchTripped_R = false;
switchCounter_R = 0;
}
//============================= JAW ===================================
if (output_normalized >= tripThreshold && switchTripped == false && millis() - timeOfLastTrip >= 750) {
switchTripped = true;
switchCounter++;
timeOfLastTrip = millis();
}
if (switchTripped == true && output_normalized <= untripThreshold) {
switchTripped = false;
}
}
if (millis() - timeOfLastTrip >= 1250) {
if (switchCounter == 1) {
//do nothing
println("Reset Switch...");
} else if (switchCounter == 2) {
//do nothing
//lock slides
myPresentation.lockSlides = !myPresentation.lockSlides;
println("Lock Slides");
} else if (switchCounter == 3) {
//next slide
drawPresentation = !drawPresentation;
println("Next Slide!");
} else if (switchCounter == 4) {
//previous slide
println("Previous Slide!!!");
myPresentation.slideBack();
} else if (switchCounter == 5) {
//previous slide
drawPresentation = !drawPresentation;
println("Turning on presentation!!!");
} else if (switchCounter == 6) {
//previous slide
// robotHand = !robotHand;
println("Turn Robot Hand ON/OFF!!!");
}
switchCounter = 0; //reset switch counter
}
////--RIGHT
//if (output_normalized_R >= tripThreshold_R && switchTripped_R == false && millis() - timeOfLastTrip_R >= 750) {
// switchTripped_R = true;
// switchCounter_R++;
// timeOfLastTrip_R = millis();
//}
//if (switchTripped_R == true && output_normalized_R <= untripThreshold_R) {
// switchTripped_R = false;
//}
//if (millis() - timeOfLastTrip_R >= 1250) {
// if (switchCounter_R == 1) {
// if (output_normalized_L >= tripThreshold_L && switchTripped_L == false && millis() - timeOfLastTrip_L >= 750) {
// switchTripped_L = true;
// switchCounter_L++;
// timeOfLastTrip_L = millis();
// myPresentation.slideBack();
// }
// println("Reset Switch...");
// } else if (switchCounter_R == 2) {
// //do nothing
// println("Reset Switch...");
// } else if (switchCounter_R == 3) {
// //next slide
// myPresentation.slideForward();
// println("Next Slide!");
// } else if (switchCounter_R == 4) {
// //previous slide
// println("Previous Slide!!!");
// myPresentation.slideBack();
// } else if (switchCounter_R == 5) {
// //previous slide
// drawPresentation = !drawPresentation;
// println("Turning on presentation!!!");
// } else if (switchCounter_R == 6) {
// //previous slide
// // robotHand = !robotHand;
// println("Turn Robot Hand ON/OFF!!!");
// }
// switchCounter_R = 0; //reset switch counter
//}
//==================================================================
//if switchCounter_L was triggered between 800 & 1200 ms ago
//and switchCounter_R is triggered
//go forward 1 slide
//if switchCounter_R was triggered between 800 & 1200 ms ago
//and switchCounter_L is triggered
//go back 1 slide
//OR, you could loop over each EEG channel and do some sort of frequency-domain processing from the FFT data
float FFT_freq_Hz, FFT_value_uV;
for (int Ichan=0; Ichan < nchan; Ichan++) {
//loop over each new sample
for (int Ibin=0; Ibin < fftBuff[Ichan].specSize(); Ibin++) {
FFT_freq_Hz = fftData[Ichan].indexToFreq(Ibin);
FFT_value_uV = fftData[Ichan].getBand(Ibin);
//add your processing here...
//println("EEG_Processing_User: Ichan = " + Ichan + ", Freq = " + FFT_freq_Hz + "Hz, FFT Value = " + FFT_value_uV + "uV/bin");
}
}
}
public void draw() {
if (drawEMG) {
pushStyle();
//circle for outer threshold
noFill();
stroke(0, 255, 0);
strokeWeight(2);
float scaleFactor = 1.0;
float scaleFactorJaw = 1.5;
//LEFT -- draw visualizer
pushMatrix();
translate((-width)/8.0, 0);
ellipse(3*(width/4), height/4, scaleFactor * upperThreshold_L, scaleFactor * upperThreshold_L);
//circle for inner threshold
stroke(0, 255, 255);
ellipse(3*(width/4), height/4, scaleFactor * lowerThreshold_L, scaleFactor * lowerThreshold_L);
//realtime
fill(255, 0, 0, 125);
noStroke();
ellipse(3*(width/4), height/4, scaleFactor * myAverage_L, scaleFactor * myAverage_L);
//draw background bar for mapped uV value indication
fill(0, 255, 255, 125);
rect(13*(width/16), height/8, (width/64), (height/4));
//draw real time bar of actually mapped value
fill(0, 255, 255);
rect(13*(width/16), 3*(height/8), (width/64), map(output_normalized_L, 0, 1, 0, (-1) * (height/4)));
popMatrix();
noFill();
stroke(0, 255, 0);
strokeWeight(2);
//RIGHT -- draw visualizer
pushMatrix();
translate(width/8, 0);
ellipse(3*(width/4), height/4, scaleFactor * upperThreshold_R, scaleFactor * upperThreshold_R);
//circle for inner threshold
stroke(0, 255, 255);
ellipse(3*(width/4), height/4, scaleFactor * lowerThreshold_R, scaleFactor * lowerThreshold_R);
//realtime
fill(255, 0, 0, 125);
noStroke();
ellipse(3*(width/4), height/4, scaleFactor * myAverage_R, scaleFactor * myAverage_R);
//draw background bar for mapped uV value indication
fill(0, 255, 255, 125);
rect(13*(width/16), height/8, (width/64), (height/4));
//draw real time bar of actually mapped value
fill(0, 255, 255);
rect(13*(width/16), 3*(height/8), (width/64), map(output_normalized_R, 0, 1, 0, (-1) * (height/4)));
popMatrix();
//circle for outer threshold
noFill();
stroke(0, 255, 0);
strokeWeight(2);
ellipse(3*(width/4), height/4, scaleFactorJaw * upperThreshold, scaleFactorJaw * upperThreshold);
//circle for inner threshold
stroke(0, 255, 255);
ellipse(3*(width/4), height/4, scaleFactorJaw * lowerThreshold, scaleFactorJaw * lowerThreshold);
//realtime
fill(255, 0, 0, 125);
noStroke();
ellipse(3*(width/4), height/4, scaleFactorJaw * myAverage, scaleFactorJaw * myAverage);
//draw background bar for mapped uV value indication
fill(0, 255, 255, 125);
rect(13*(width/16), height/8, (width/64), (height/4));
//draw real time bar of actually mapped value
fill(0, 255, 255);
rect(13*(width/16), 3*(height/8), (width/64), map(output_normalized, 0, 1, 0, (-1) * (height/4)));
popStyle();
}
drawTriggerFeedback();
} //end of draw
public void drawTriggerFeedback() {
//Is the board streaming data?
//if so ... draw left eye trigger feedback
if (isRunning) {
//LEFT
if (eegProcessing_user.switchCounter_L == 1) {
//draw red circle
fill(255, 0, 0);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_L == 2) {
//draw green circle
fill(0, 255, 0);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_L == 3) {
//draw blue circle
fill(0, 0, 255);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_L == 4) {
//draw blue circle
fill(0, 255, 255);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_L == 5) {
//draw blue circle
fill(255, 255, 0);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_L == 6) {
//draw blue circle
fill(255, 0, 255);
ellipse(width/2-40, height - 40, 20, 20);
noFill();
ellipse(width/2+40, height - 40, 20, 20);
}
//RIGHT
if (eegProcessing_user.switchCounter_R == 1) {
//draw red circle
fill(255, 0, 0);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_R == 2) {
//draw green circle
fill(0, 255, 0);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_R == 3) {
//draw blue circle
fill(0, 0, 255);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_R == 4) {
//draw blue circle
fill(0, 255, 255);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_R == 5) {
//draw blue circle
fill(255, 255, 0);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
} else if (eegProcessing_user.switchCounter_R == 6) {
//draw blue circle
fill(255, 0, 255);
ellipse(width/2+40, height - 40, 20, 20);
noFill();
ellipse(width/2-40, height - 40, 20, 20);
}
if (switchCounter == 1) {
//draw red circle
fill(255, 0, 0);
ellipse(width/2, height - 40, 20, 20);
} else if (switchCounter == 2) {
//draw green circle
fill(0, 255, 0);
ellipse(width/2, height - 40, 20, 20);
} else if (switchCounter == 3) {
//draw blue circle
fill(0, 0, 255);
ellipse(width/2, height - 40, 20, 20);
}
}
}
}
class EEG_Processing {
private float fs_Hz; //sample rate
private int nchan;
final int N_FILT_CONFIGS = 5;
FilterConstants[] filtCoeff_bp = new FilterConstants[N_FILT_CONFIGS];
final int N_NOTCH_CONFIGS = 3;
FilterConstants[] filtCoeff_notch = new FilterConstants[N_NOTCH_CONFIGS];
private int currentFilt_ind = 0;
private int currentNotch_ind = 0; // set to 0 to default to 60Hz, set to 1 to default to 50Hz
float data_std_uV[];
float polarity[];
EEG_Processing(int NCHAN, float sample_rate_Hz) {
nchan = NCHAN;
fs_Hz = sample_rate_Hz;
data_std_uV = new float[nchan];
polarity = new float[nchan];
//check to make sure the sample rate is acceptable and then define the filters
if (abs(fs_Hz-250.0f) < 1.0) {
defineFilters();
} else {
println("EEG_Processing: *** ERROR *** Filters can currently only work at 250 Hz");
defineFilters(); //define the filters anyway just so that the code doesn't bomb
}
}
public float getSampleRateHz() {
return fs_Hz;
};
//define filters...assumes sample rate of 250 Hz !!!!!
private void defineFilters() {
int n_filt;
double[] b, a, b2, a2;
String filt_txt, filt_txt2;
String short_txt, short_txt2;
//loop over all of the pre-defined filter types
n_filt = filtCoeff_notch.length;
for (int Ifilt=0; Ifilt < n_filt; Ifilt++) {
switch (Ifilt) {
case 0:
//60 Hz notch filter, assumed fs = 250 Hz. 2nd Order Butterworth: b, a = signal.butter(2,[59.0 61.0]/(fs_Hz / 2.0), 'bandstop')
b2 = new double[] { 9.650809863447347e-001, -2.424683201757643e-001, 1.945391494128786e+000, -2.424683201757643e-001, 9.650809863447347e-001 };
a2 = new double[] { 1.000000000000000e+000, -2.467782611297853e-001, 1.944171784691352e+000, -2.381583792217435e-001, 9.313816821269039e-001 };
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "Notch 60Hz", "60Hz");
break;
case 1:
//50 Hz notch filter, assumed fs = 250 Hz. 2nd Order Butterworth: b, a = signal.butter(2,[49.0 51.0]/(fs_Hz / 2.0), 'bandstop')
b2 = new double[] { 0.96508099, -1.19328255, 2.29902305, -1.19328255, 0.96508099 };
a2 = new double[] { 1.0, -1.21449348, 2.29780334, -1.17207163, 0.93138168 };
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "Notch 50Hz", "50Hz");
break;
case 2:
//no notch filter
b2 = new double[] { 1.0 };
a2 = new double[] { 1.0 };
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "No Notch", "None");
break;
}
} // end loop over notch filters
n_filt = filtCoeff_bp.length;
for (int Ifilt=0; Ifilt<n_filt; Ifilt++) {
//define bandpass filter
switch (Ifilt) {
case 0:
//butter(2,[1 50]/(250/2)); %bandpass filter
b = new double[] {
2.001387256580675e-001, 0.0f, -4.002774513161350e-001, 0.0f, 2.001387256580675e-001
};
a = new double[] {
1.0f, -2.355934631131582e+000, 1.941257088655214e+000, -7.847063755334187e-001, 1.999076052968340e-001
};
filt_txt = "Bandpass 1-50Hz";
short_txt = "1-50 Hz";
break;
case 1:
//butter(2,[7 13]/(250/2));
b = new double[] {
5.129268366104263e-003, 0.0f, -1.025853673220853e-002, 0.0f, 5.129268366104263e-003
};
a = new double[] {
1.0f, -3.678895469764040e+000, 5.179700413522124e+000, -3.305801890016702e+000, 8.079495914209149e-001
};
filt_txt = "Bandpass 7-13Hz";
short_txt = "7-13 Hz";
break;
case 2:
//[b,a]=butter(2,[15 50]/(250/2)); %matlab command
b = new double[] {
1.173510367246093e-001, 0.0f, -2.347020734492186e-001, 0.0f, 1.173510367246093e-001
};
a = new double[] {
1.0f, -2.137430180172061e+000, 2.038578008108517e+000, -1.070144399200925e+000, 2.946365275879138e-001
};
filt_txt = "Bandpass 15-50Hz";
short_txt = "15-50 Hz";
break;
case 3:
//[b,a]=butter(2,[5 50]/(250/2)); %matlab command
b = new double[] {
1.750876436721012e-001, 0.0f, -3.501752873442023e-001, 0.0f, 1.750876436721012e-001
};
a = new double[] {
1.0f, -2.299055356038497e+000, 1.967497759984450e+000, -8.748055564494800e-001, 2.196539839136946e-001
};
filt_txt = "Bandpass 5-50Hz";
short_txt = "5-50 Hz";
break;
default:
//no filtering
b = new double[] {
1.0
};
a = new double[] {
1.0
};
filt_txt = "No BP Filter";
short_txt = "No Filter";
} //end switch block
//create the bandpass filter
filtCoeff_bp[Ifilt] = new FilterConstants(b, a, filt_txt, short_txt);
} //end loop over band pass filters
} //end defineFilters method
public String getFilterDescription() {
return filtCoeff_bp[currentFilt_ind].name + ", " + filtCoeff_notch[currentNotch_ind].name;
}
public String getShortFilterDescription() {
return filtCoeff_bp[currentFilt_ind].short_name;
}
public String getShortNotchDescription() {
return filtCoeff_notch[currentNotch_ind].short_name;
}
public void incrementFilterConfiguration() {
//increment the index
currentFilt_ind++;
if (currentFilt_ind >= N_FILT_CONFIGS) currentFilt_ind = 0;
}
public void incrementNotchConfiguration() {
//increment the index
currentNotch_ind++;
if (currentNotch_ind >= N_NOTCH_CONFIGS) currentNotch_ind = 0;
}
public void process(float[][] data_newest_uV, //holds raw EEG data that is new since the last call
float[][] data_long_uV, //holds a longer piece of buffered EEG data, of same length as will be plotted on the screen
float[][] data_forDisplay_uV, //put data here that should be plotted on the screen
FFT[] fftData) { //holds the FFT (frequency spectrum) of the latest data
//loop over each EEG channel
for (int Ichan=0; Ichan < nchan; Ichan++) {
//filter the data in the time domain
filterIIR(filtCoeff_notch[currentNotch_ind].b, filtCoeff_notch[currentNotch_ind].a, data_forDisplay_uV[Ichan]); //notch
filterIIR(filtCoeff_bp[currentFilt_ind].b, filtCoeff_bp[currentFilt_ind].a, data_forDisplay_uV[Ichan]); //bandpass
//compute the standard deviation of the filtered signal...this is for the head plot
float[] fooData_filt = dataBuffY_filtY_uV[Ichan]; //use the filtered data
fooData_filt = Arrays.copyOfRange(fooData_filt, fooData_filt.length-((int)fs_Hz), fooData_filt.length); //just grab the most recent second of data
data_std_uV[Ichan]=std(fooData_filt); //compute the standard deviation for the whole array "fooData_filt"
} //close loop over channels
//find strongest channel
int refChanInd = findMax(data_std_uV);
//println("EEG_Processing: strongest chan (one referenced) = " + (refChanInd+1));
float[] refData_uV = dataBuffY_filtY_uV[refChanInd]; //use the filtered data
refData_uV = Arrays.copyOfRange(refData_uV, refData_uV.length-((int)fs_Hz), refData_uV.length); //just grab the most recent second of data
//compute polarity of each channel
for (int Ichan=0; Ichan < nchan; Ichan++) {
float[] fooData_filt = dataBuffY_filtY_uV[Ichan]; //use the filtered data
fooData_filt = Arrays.copyOfRange(fooData_filt, fooData_filt.length-((int)fs_Hz), fooData_filt.length); //just grab the most recent second of data
float dotProd = calcDotProduct(fooData_filt, refData_uV);
if (dotProd >= 0.0f) {
polarity[Ichan]=1.0;
} else {
polarity[Ichan]=-1.0;
}
}
}
}
+928
Ver Arquivo
@@ -0,0 +1,928 @@
////////////////////////////////////////////////////
//
// This class creates and manages all of the graphical user interface (GUI) elements
// for the primary display. This is the display with the head, with the FFT frequency
// traces, and with the montage of time-domain traces. It also holds all of the buttons.
//
// Chip Audette, Oct 2013 - May 2014
//
// Requires the plotting library from gwoptics. Built on gwoptics 0.5.0
// http://www.gwoptics.org/processing/gwoptics_p5lib/
//
///////////////////////////////////////////////////
//import processing.core.PApplet;
import org.gwoptics.graphics.*;
import org.gwoptics.graphics.graph2D.*;
import org.gwoptics.graphics.graph2D.Graph2D;
import org.gwoptics.graphics.graph2D.LabelPos;
import org.gwoptics.graphics.graph2D.traces.Blank2DTrace;
import org.gwoptics.graphics.graph2D.backgrounds.*;
import ddf.minim.analysis.*; //for FFT
import java.util.*; //for Array.copyOfRange()
class Gui_Manager {
ScatterTrace montageTrace;
ScatterTrace_FFT fftTrace;
Graph2D gMontage, gFFT, gSpectrogram;
GridBackground gbMontage, gbFFT;
Button stopButton;
PlotFontInfo fontInfo;
HeadPlot headPlot1;
Button[] chanButtons;
// Button guiPageButton;
//boolean showImpedanceButtons;
Button[] impedanceButtonsP;
Button[] impedanceButtonsN;
Button biasButton;
Button intensityFactorButton;
Button loglinPlotButton;
Button filtBPButton;
Button filtNotchButton;
Button fftNButton;
Button smoothingButton;
Button maxDisplayFreqButton;
Button showPolarityButton;
//these two buttons toggle between EEG graph state (they are mutually exclusive states)
Button showMontageButton; // to show uV time graph as opposed to channel controller
Button showChannelControllerButton; //to drawChannelController on top of gMontage
// boolean isChannelControllerVisible;
TextBox titleMontage, titleFFT,titleSpectrogram;
TextBox[] chanValuesMontage;
TextBox[] impValuesMontage;
boolean showMontageValues;
public int guiPage;
boolean vertScaleAsLog = true;
Spectrogram spectrogram;
boolean showSpectrogram;
int whichChannelForSpectrogram;
//define some color variables
int bgColorGraphs = 255;
int gridColor = 200;
int borderColor = 50;
int axisColor = 50;
int fontColor = 255;
// MontageController mc;
ChannelController cc;
private float fftYOffset[];
private float default_vertScale_uV=200.0; //this defines the Y-scale on the montage plots...this is the vertical space between traces
private float[] vertScaleFactor = {1.0f, 2.0f, 5.0f, 50.0f, 0.25f, 0.5f};
private int vertScaleFactor_ind = 0;
float vertScale_uV=default_vertScale_uV;
float vertScaleMin_uV_whenLog = 0.1f;
float montage_yoffsets[];
private float[] maxDisplayFreq_Hz = {20.0f, 40.0f, 60.0f, 120.0f};
private int maxDisplayFreq_ind = 2;
public final static int GUI_PAGE_CHANNEL_ONOFF = 0;
public final static int GUI_PAGE_IMPEDANCE_CHECK = 1;
public final static int GUI_PAGE_HEADPLOT_SETUP = 2;
public final static int N_GUI_PAGES = 3;
public final static String stopButton_pressToStop_txt = "Stop Data Stream";
public final static String stopButton_pressToStart_txt = "Start Data Stream";
Gui_Manager(PApplet parent,int win_x, int win_y,int nchan,float displayTime_sec, float default_yScale_uV,
String filterDescription, float smooth_fac) {
// Gui_Manager(PApplet parent,int win_x, int win_y,int nchan,float displayTime_sec, float yScale_uV, float fs_Hz,
// String montageFilterText, String detectName) {
showSpectrogram = false;
whichChannelForSpectrogram = 0; //assume
//define some layout parameters
float axes_x, axes_y;
float spacer_bottom = 30/float(win_y); //want this to be a fixed 30 pixels
float spacer_top = float(controlPanelCollapser.but_dy)/float(win_y);
float gutter_topbot = 0.03f;
float gutter_left = 0.08f; //edge around the GUI
float gutter_right = 0.015f; //edge around the GUI
float height_UI_tray = 0.1f + spacer_bottom; //0.1f;//0.10f; //empty space along bottom for UI elements
float left_right_split = 0.45f; //notional dividing line between left and right plots, measured from left
float available_top2bot = 1.0f - 2*gutter_topbot - height_UI_tray;
float up_down_split = 0.5f; //notional dividing line between top and bottom plots, measured from top
float gutter_between_buttons = 0.005f; //space between buttons
float title_gutter = 0.02f;
float headPlot_fromTop = 0.12f;
fontInfo = new PlotFontInfo();
//montage control panel variables
// float x_cc = float(win_x)*(left_right_split+gutter_right - 0.01f);
float x_cc = 5;
// float y_cc = float(win_y)*(gutter_topbot+title_gutter+spacer_top);
float y_cc = float(win_y)*(height_UI_tray);
float w_cc = float(win_x)*(0.09f-gutter_right); //width of montage controls (on left of montage)
float h_cc = float(win_y)*(available_top2bot-title_gutter-spacer_top); //height of montage controls (on left of montage)
//setup the montage plot...the right side
default_vertScale_uV = default_yScale_uV; //here is the vertical scaling of the traces
// float[] axisMontage_relPos = {
// left_right_split+gutter_left,
// gutter_topbot+title_gutter+spacer_top,
// (1.0f-left_right_split)-gutter_left-gutter_right,
// available_top2bot-title_gutter-spacer_top
// }; //from left, from top, width, height
float[] axisMontage_relPos = {
gutter_left,
height_UI_tray,
(1.0f-left_right_split)-gutter_left-gutter_right,
available_top2bot-title_gutter-spacer_top
}; //from left, from top, width, height
axes_x = float(win_x)*axisMontage_relPos[2]; //width of the axis in pixels
axes_y = float(win_y)*axisMontage_relPos[3]; //height of the axis in pixels
gMontage = new Graph2D(parent, int(axes_x), int(axes_y), false); //last argument is whether the axes cross at zero
setupMontagePlot(gMontage, win_x, win_y, axisMontage_relPos,displayTime_sec,fontInfo,filterDescription);
verbosePrint("Gui_Manager: Buttons: " + int(float(win_x)*axisMontage_relPos[0]) + ", " + (int(float(win_y)*axisMontage_relPos[1])-40));
showMontageButton = new Button (int(float(win_x)*axisMontage_relPos[0]) - 1, int(float(win_y)*axisMontage_relPos[1])-45, 125, 21, "EEG DATA", 14);
showMontageButton.makeDropdownButton(true);
showMontageButton.setColorPressed(color(184,220,105));
showMontageButton.setColorNotPressed(color(255));
showMontageButton.hasStroke(false);
showMontageButton.setIsActive(true);
showMontageButton.buttonFont = f1;
showMontageButton.textColorActive = bgColor;
showChannelControllerButton = new Button (int(float(win_x)*axisMontage_relPos[0])+127, int(float(win_y)*axisMontage_relPos[1])-45, 125, 21, "CHAN SET", 14);
showChannelControllerButton.makeDropdownButton(true);
showChannelControllerButton.setColorPressed(color(184,220,105));
showChannelControllerButton.setColorNotPressed(color(255));
showChannelControllerButton.hasStroke(false);
showChannelControllerButton.setIsActive(false);
showChannelControllerButton.textColorActive = bgColor;
//setup montage controller
cc = new ChannelController(x_cc, y_cc, w_cc, h_cc, axes_x, axes_y);
//setup the FFT plot...bottom on left side
//float height_subplot = 0.5f*(available_top2bot-2*gutter_topbot);
// float[] axisFFT_relPos = {
// gutter_left,
// gutter_topbot+ up_down_split*available_top2bot + gutter_topbot+title_gutter + spacer_top,
// left_right_split-gutter_left-gutter_right,
// available_top2bot*(1.0f-up_down_split) - gutter_topbot-title_gutter - spacer_top
// }; //from left, from top, width, height
float[] axisFFT_relPos = {
gutter_left + left_right_split + 0.1f,
up_down_split*available_top2bot + height_UI_tray + gutter_topbot,
left_right_split-gutter_left-gutter_right,
available_top2bot*(1.0f-up_down_split) - gutter_topbot-title_gutter - spacer_top
}; //from left, from top, width, height
axes_x = int(float(win_x)*axisFFT_relPos[2]); //width of the axis in pixels
axes_y = int(float(win_y)*axisFFT_relPos[3]); //height of the axis in pixels
gFFT = new Graph2D(parent, int(axes_x), int(axes_y), false); //last argument is whether the axes cross at zero
setupFFTPlot(gFFT, win_x, win_y, axisFFT_relPos,fontInfo);
//setup the spectrogram plot
// float[] axisSpectrogram_relPos = axisMontage_relPos;
// axes_x = int(float(win_x)*axisSpectrogram_relPos[2]);
// axes_y = int(float(win_y)*axisSpectrogram_relPos[3]);
// gSpectrogram = new Graph2D(parent, axes_x, axes_y, false); //last argument is wheter the axes cross at zero
// setupSpectrogram(gSpectrogram, win_x, win_y, axisMontage_relPos,displayTime_sec,fontInfo);
// int Nspec = 256;
// int Nstep = 32;
// spectrogram = new Spectrogram(Nspec,openBCI.fs_Hz,Nstep,displayTime_sec);
// spectrogram.clim[0] = java.lang.Math.log(gFFT.getYAxis().getMinValue()); //set the minium value for the color scale on the spectrogram
// spectrogram.clim[1] = java.lang.Math.log(gFFT.getYAxis().getMaxValue()/10.0); //set the maximum value for the color scale on the spectrogram
// updateMaxDisplayFreq();
//setup the head plot...top on the left side
float[] axisHead_relPos = axisFFT_relPos.clone();
// axisHead_relPos[1] = gutter_topbot + spacer_top; //set y position to be at top of left side
axisHead_relPos[1] = headPlot_fromTop; //set y position to be at top of right side
axisHead_relPos[3] = available_top2bot*up_down_split - gutter_topbot;
headPlot1 = new HeadPlot(axisHead_relPos[0],axisHead_relPos[1],axisHead_relPos[2],axisHead_relPos[3],win_x,win_y,nchan);
setSmoothFac(smooth_fac);
//setup the buttons
int w,h,x,y;
h = 26; //button height, was 25
y = 2; //button y position, measured top
// //// Is this block used anymore? Chip 2014-11-23
//setup the gui page button
w = 80; //button width
x = (int)((3*gutter_between_buttons + left_right_split) * win_x);
// x = int(float(win_x)*0.3f);
// guiPageButton = new Button(x,y,w,h,"Page\n" + (guiPage+1) + " of " + N_GUI_PAGES,fontInfo.buttonLabel_size);
// //// End Ques by Chip 2014-11-12
//setup the channel on/off buttons...only plot 8 buttons, even if there are more channels
//because as of 4/3/2014, you can only turn on/off the higher channels (the ones above chan 8)
//by also turning off the corresponding lower channel. So, deactiving channel 9 must also
//deactivate channel 1, therefore, we might as well use just the 1 button.
// int xoffset = x + w + (int)(2*gutter_between_buttons*win_x);
// int xoffset = (int)(float(win_x)*gutter_left);
int xoffset = (int)(float(win_x)*0.5f);
w = 80; //button width
int w_orig = w;
//if (nchan > 10) w -= (nchan-8)*2; //make the buttons skinnier
int nChanBut = min(nchan,8);
chanButtons = new Button[nChanBut];
String txt;
for (int Ibut = 0; Ibut < nChanBut; Ibut++) {
x = calcButtonXLocation(Ibut, win_x, w, xoffset,gutter_between_buttons);
txt = "Chan\n" + Integer.toString(Ibut+1);
if (nchan > 8+Ibut) txt = txt + "+" + Integer.toString(Ibut+1+8);
chanButtons[Ibut] = new Button(x,y,w,h,txt,fontInfo.buttonLabel_size);
}
//setup the impedance measurement (lead-off) control buttons
//showImpedanceButtons = false; //by default, do not show the buttons
int vertspace_pix = max(1,int(gutter_between_buttons*win_x/4));
int w1 = w_orig; //use same width as for buttons above
int h1 = h/2-vertspace_pix; //use buttons with half the height
impedanceButtonsP = new Button[nchan];
for (int Ibut = 0; Ibut < nchan; Ibut++) {
x = calcButtonXLocation(Ibut, win_x, w1, xoffset, gutter_between_buttons);
impedanceButtonsP[Ibut] = new Button(x,y,w1,h1,"Imp P" + (Ibut+1),fontInfo.buttonLabel_size);
}
impedanceButtonsN = new Button[nchan];
for (int Ibut = 0; Ibut < nchan; Ibut++) {
x = calcButtonXLocation(Ibut, win_x, w1, xoffset, gutter_between_buttons);
impedanceButtonsN[Ibut] = new Button(x,y+h-h1,w1,h1,"Imp N" + (Ibut+1),fontInfo.buttonLabel_size);
}
h1 = h;
x = calcButtonXLocation(nchan, win_x, w1, xoffset, gutter_between_buttons);
biasButton = new Button(x,y,w1,h1,"Bias\n" + "Auto",fontInfo.buttonLabel_size);
//setup the buttons to control the processing and frequency displays
int Ibut=0;
w = 70;
h = 26;
y = 2;
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
maxDisplayFreqButton = new Button(x,y,w,h,"Max Freq\n" + round(maxDisplayFreq_Hz[maxDisplayFreq_ind]) + " Hz",fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
showPolarityButton = new Button(x,y,w,h,"Polarity\n" + headPlot1.getUsePolarityTrueFalse(),fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
smoothingButton = new Button(x,y,w,h,"Smooth\n" + headPlot1.smooth_fac,fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
loglinPlotButton = new Button(x,y,w,h,"Vert Scale\n" + get_vertScaleAsLogText(),fontInfo.buttonLabel_size);
//x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
//fftNButton = new Button(x,y,w,h,"FFT N\n" + Nfft,fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
intensityFactorButton = new Button(x,y,w,h,"Vert Scale\n" + round(vertScale_uV) + "uV",fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
filtNotchButton = new Button(x,y,w,h,"Notch\n" + eegProcessing.getShortNotchDescription(),fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
filtBPButton = new Button(x,y,w,h,"BP Filt\n" + eegProcessing.getShortFilterDescription(),fontInfo.buttonLabel_size);
set_vertScaleAsLog(true);
//setup start/stop button
// x = win_x - int(gutter_right*float(win_x)) - w;
//x = width/2 - w;
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
int w_wide = 120; //button width, wider
x = x + w - w_wide-((int)(gutter_between_buttons*win_x)); //adjust the x position for the wider button, plus double the gutter
stopButton = new Button(x,y,w_wide,h,stopButton_pressToStart_txt,fontInfo.buttonLabel_size);
//set the initial display page for the GUI
setGUIpage(GUI_PAGE_HEADPLOT_SETUP);
}
private int calcButtonXLocation(int Ibut,int win_x,int w, int xoffset, float gutter_between_buttons) {
// return xoffset + (Ibut * (w + (int)(gutter_between_buttons*win_x)));
return width - ((Ibut+1) * (w + 2)) - 1;
}
public void setDefaultVertScale(float val_uV) {
default_vertScale_uV = val_uV;
updateVertScale();
}
public void setVertScaleFactor_ind(int ind) {
vertScaleFactor_ind = max(0,ind);
if (ind >= vertScaleFactor.length) vertScaleFactor_ind = 0;
updateVertScale();
}
public void incrementVertScaleFactor() {
setVertScaleFactor_ind(vertScaleFactor_ind+1); //wrap-around is handled inside the function
}
public void updateVertScale() {
vertScale_uV = default_vertScale_uV*vertScaleFactor[vertScaleFactor_ind];
//println("Gui_Manager: updateVertScale: vertScale_uV = " + vertScale_uV);
//update how the plots are scaled
if (montageTrace != null) montageTrace.setYScale_uV(vertScale_uV); //the Y-axis on the montage plot is fixed...the data is simply scaled prior to plotting
if (gFFT != null) gFFT.setYAxisMax(vertScale_uV);
headPlot1.setMaxIntensity_uV(vertScale_uV);
intensityFactorButton.setString("Vert Scale\n" + round(vertScale_uV) + "uV");
//update the Yticks on the FFT plot
if (gFFT != null) {
if (vertScaleAsLog) {
gFFT.setYAxisTickSpacing(1);
} else {
gFFT.setYAxisTickSpacing(pow(10.0,floor(log10(vertScale_uV/4))));
}
}
}
public String get_vertScaleAsLogText() {
if (vertScaleAsLog) {
return "Log";
} else {
return "Linear";
}
}
public void set_vertScaleAsLog(boolean state) {
vertScaleAsLog = state;
//change the FFT Plot
if (gFFT != null) {
if (vertScaleAsLog) {
gFFT.setYAxisMin(vertScaleMin_uV_whenLog);
Axis2D ay=gFFT.getYAxis();
ay.setLogarithmicAxis(true);
updateVertScale(); //force a re-do of the Yticks
} else {
Axis2D ay=gFFT.getYAxis();
ay.setLogarithmicAxis(false);
gFFT.setYAxisMin(0.0f);
updateVertScale(); //force a re-do of the Yticks
}
}
//change the head plot
headPlot1.set_plotColorAsLog(vertScaleAsLog);
//change the button
if (loglinPlotButton != null) {
loglinPlotButton.setString("Vert Scale\n" + get_vertScaleAsLogText());
}
}
public void setSmoothFac(float fac) {
headPlot1.smooth_fac = fac;
}
public void setMaxDisplayFreq_ind(int ind) {
maxDisplayFreq_ind = max(0,ind);
if (ind >= maxDisplayFreq_Hz.length) maxDisplayFreq_ind = 0;
updateMaxDisplayFreq();
}
public void incrementMaxDisplayFreq() {
setMaxDisplayFreq_ind(maxDisplayFreq_ind+1); //wrap-around is handled inside the function
}
public void updateMaxDisplayFreq() {
//set the frequency limit of the display
float foo_Hz = maxDisplayFreq_Hz[maxDisplayFreq_ind];
gFFT.setXAxisMax(foo_Hz);
if (fftTrace != null) fftTrace.set_plotXlim(0.0f,foo_Hz);
//gSpectrogram.setYAxisMax(foo_Hz);
//set the ticks
if (foo_Hz < 38.0f) {
foo_Hz = 5.0f;
} else if (foo_Hz < 78.0f) {
foo_Hz = 10.0f;
} else if (foo_Hz < 168.0f) {
foo_Hz = 20.0f;
} else {
foo_Hz = (float)floor(foo_Hz / 50.0) * 50.0f;
}
gFFT.setXAxisTickSpacing(foo_Hz);
//gSpectrogram.setYAxisTickSpacing(foo_Hz);
if (maxDisplayFreqButton != null) maxDisplayFreqButton.setString("Max Freq\n" + round(maxDisplayFreq_Hz[maxDisplayFreq_ind]) + " Hz");
}
public void setDoNotPlotOutsideXlim(boolean state) {
if (state) {
//println("GUI_Manager: setDoNotPlotAboveXlim: " + gFFT.getXAxis().getMaxValue());
fftTrace.set_plotXlim(gFFT.getXAxis().getMinValue(),gFFT.getXAxis().getMaxValue());
montageTrace.set_plotXlim(gMontage.getXAxis().getMinValue(),gMontage.getXAxis().getMaxValue());
} else {
fftTrace.set_plotXlim(Float.NaN,Float.NaN);
}
}
public void setDecimateFactor(int fac) {
montageTrace.setDecimateFactor(fac);
}
public void setupMontagePlot(Graph2D g, int win_x, int win_y, float[] axis_relPos,float displayTime_sec, PlotFontInfo fontInfo,String filterDescription) {
g.setAxisColour(axisColor, axisColor, axisColor);
g.setFontColour(fontColor, fontColor, fontColor);
int x1,y1;
x1 = int(axis_relPos[0]*float(win_x));
g.position.x = x1;
y1 = int(axis_relPos[1]*float(win_y));
g.position.y = y1;
//g.position.y = 0;
g.setYAxisMin(-nchan-1.0f);
g.setYAxisMax(0.0f);
g.setYAxisTickSpacing(1f);
g.setYAxisMinorTicks(0);
g.setYAxisLabelAccuracy(0);
g.setYAxisLabel("EEG Channel");
g.setYAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, true);
g.setYAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
g.setXAxisMin(-displayTime_sec);
g.setXAxisMax(0f);
g.setXAxisTickSpacing(1f);
g.setXAxisMinorTicks(1);
g.setXAxisLabelAccuracy(0);
g.setXAxisLabel("Time (sec)");
g.setXAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
g.setXAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
// switching on Grid, with different colours for X and Y lines
gbMontage = new GridBackground(new GWColour(bgColorGraphs));
gbMontage.setGridColour(gridColor, gridColor, gridColor, gridColor, gridColor, gridColor);
g.setBackground(gbMontage);
g.setBorderColour(borderColor,borderColor,borderColor);
// add title
titleMontage = new TextBox("EEG Data (" + filterDescription + ")",0,0);
int x2 = x1 + int(round(0.5*axis_relPos[2]*float(win_x)));
int y2 = y1 - 2; //deflect two pixels upward
titleMontage.x = x2;
titleMontage.y = y2;
titleMontage.textColor = color(bgColor);
titleMontage.setFontSize(14);
titleMontage.alignH = CENTER;
//add channel data values and impedance values
int x3, y3;
//float w = int(round(axis_relPos[2]*win_x));
TextBox fooBox = new TextBox("",0,0);
chanValuesMontage = new TextBox[nchan];
impValuesMontage = new TextBox[nchan];
Axis2D xAxis = g.getXAxis();
Axis2D yAxis = g.getYAxis();
int h = int(round(axis_relPos[3]*win_y));
for (int i=0; i<nchan; i++) {
y3 = y1 + h - yAxis.valueToPosition((float)(-(i+1))); //set to be on the centerline of the trace
for (int j=0; j<2; j++) { //loop over the different text box types
switch (j) {
case 0:
//voltage value text
x3 = x1 + xAxis.valueToPosition(xAxis.getMaxValue()) - 2; //set to right edge of plot. nudge 2 pixels to the left
fooBox = new TextBox("0.00 uVrms",x3,y3);
break;
case 1:
//impedance value text
x3 = x1 + xAxis.valueToPosition(xAxis.getMinValue()) + 2; //set to left edge of plot. nudge 2 pixels to the right
fooBox = new TextBox("0.00 kOhm",x3,y3);
break;
}
fooBox.textColor = color(0,0,0);
fooBox.drawBackground = true;
fooBox.backgroundColor = color(255,255,255, 125);
noStroke();
switch (j) {
case 0:
//voltage value text
fooBox.alignH = RIGHT;
chanValuesMontage[i] = fooBox;
break;
case 1:
//impedance value text
fooBox.alignH = LEFT;
impValuesMontage[i] = fooBox;
break;
}
}
}
showMontageValues = true; // default to having them NOT displayed
}
public void setupFFTPlot(Graph2D g, int win_x, int win_y, float[] axis_relPos,PlotFontInfo fontInfo) {
g.setAxisColour(axisColor, axisColor, axisColor);
g.setFontColour(fontColor, fontColor, fontColor);
int x1,y1;
x1 = int(axis_relPos[0]*float(win_x));
g.position.x = x1;
y1 = int(axis_relPos[1]*float(win_y));
g.position.y = y1;
//g.position.y = 0;
//setup the y axis
g.setYAxisMin(vertScaleMin_uV_whenLog);
g.setYAxisMax(vertScale_uV);
g.setYAxisTickSpacing(1);
g.setYAxisMinorTicks(0);
g.setYAxisLabelAccuracy(0);
//g.setYAxisLabel("EEG Amplitude (uV/sqrt(Hz))"); // Some people prefer this...but you'll have to change the normalization in OpenBCI_GUI\processNewData()
g.setYAxisLabel("EEG Amplitude (uV per bin)"); // CHIP 2014-10-24...currently, this matches the normalization in OpenBCI_GUI\processNewData()
g.setYAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
g.setYAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
//get the Y-axis and make it log
Axis2D ay=g.getYAxis();
ay.setLogarithmicAxis(true);
//setup the x axis
g.setXAxisMin(0f);
g.setXAxisMax(maxDisplayFreq_Hz[maxDisplayFreq_ind]);
g.setXAxisTickSpacing(10f);
g.setXAxisMinorTicks(2);
g.setXAxisLabelAccuracy(0);
g.setXAxisLabel("Frequency (Hz)");
g.setXAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
g.setXAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
// switching on Grid, with differetn colours for X and Y lines
gbFFT = new GridBackground(new GWColour(bgColorGraphs));
gbFFT.setGridColour(gridColor, gridColor, gridColor, gridColor, gridColor, gridColor);
g.setBackground(gbFFT);
g.setBorderColour(borderColor,borderColor,borderColor);
// add title
titleFFT = new TextBox("FFT Plot",0,0);
int x2 = x1 + int(round(0.5*axis_relPos[2]*float(win_x)));
int y2 = y1 - 2; //deflect two pixels upward
titleFFT.x = x2;
titleFFT.y = y2;
titleFFT.textColor = color(255,255,255);
titleFFT.setFontSize(16);
titleFFT.alignH = CENTER;
}
public void setupSpectrogram(Graph2D g, int win_x, int win_y, float[] axis_relPos,float displayTime_sec, PlotFontInfo fontInfo) {
//start by setting up as if it were the montage plot
//setupMontagePlot(g, win_x, win_y, axis_relPos,displayTime_sec,fontInfo,title);
g.setAxisColour(220, 220, 220);
g.setFontColour(255, 255, 255);
int x1 = int(axis_relPos[0]*float(win_x));
g.position.x = x1;
int y1 = int(axis_relPos[1]*float(win_y));
g.position.y = y1;
//setup the x axis
g.setXAxisMin(-displayTime_sec);
g.setXAxisMax(0f);
g.setXAxisTickSpacing(1f);
g.setXAxisMinorTicks(1);
g.setXAxisLabelAccuracy(0);
g.setXAxisLabel("Time (sec)");
g.setXAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
g.setXAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
//setup the y axis...frequency
g.setYAxisMin(0.0f-0.5f);
g.setYAxisMax(maxDisplayFreq_Hz[maxDisplayFreq_ind]);
g.setYAxisTickSpacing(10.0f);
g.setYAxisMinorTicks(2);
g.setYAxisLabelAccuracy(0);
g.setYAxisLabel("Frequency (Hz)");
g.setYAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
g.setYAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
//make title
titleSpectrogram = new TextBox(makeSpectrogramTitle(),0,0);
int x2 = x1 + int(round(0.5*axis_relPos[2]*float(win_x)));
int y2 = y1 - 2; //deflect two pixels upward
titleSpectrogram.x = x2;
titleSpectrogram.y = y2;
titleSpectrogram.textColor = color(255,255,255);
titleSpectrogram.setFontSize(16);
titleSpectrogram.alignH = CENTER;
}
public void initializeMontageTraces(float[] dataBuffX, float [][] dataBuffY) {
//create the trace object, add it to the plotting object, and set the data and scale factor
//montageTrace = new ScatterTrace(); //I can't have this here because it dies. It must be in setup()
gMontage.addTrace(montageTrace);
montageTrace.setXYData_byRef(dataBuffX, dataBuffY);
montageTrace.setYScaleFac(1f / vertScale_uV);
//montageTrace.setYScaleFac(1.0f); //for OpenBCI_GUI_Simpler
//set the y-offsets for each trace in the fft plot.
//have each trace bumped down by -1.0.
for (int Ichan=0; Ichan < nchan; Ichan++) {
montage_yoffsets[Ichan]=(float)(-(Ichan+1));
}
montageTrace.setYOffset_byRef(montage_yoffsets);
}
public void initializeFFTTraces(ScatterTrace_FFT fftTrace,FFT[] fftBuff,float[] fftYOffset,Graph2D gFFT) {
for (int Ichan = 0; Ichan < fftYOffset.length; Ichan++) {
//set the Y-offste for the individual traces in the plots
fftYOffset[Ichan]= 0f; //set so that there is no additional offset
}
//make the trace for the FFT and add it to the FFT Plot axis
//fftTrace = new ScatterTrace_FFT(fftBuff); //can't put this here...must be in setup()
fftTrace.setYOffset(fftYOffset);
gFFT.addTrace(fftTrace);
}
public void initDataTraces(float[] dataBuffX,float[][] dataBuffY,FFT[] fftBuff,float[] dataBuffY_std, DataStatus[] is_railed, float[] dataBuffY_polarity) {
//initialize the time-domain montage-plot traces
montageTrace = new ScatterTrace();
montage_yoffsets = new float[nchan];
initializeMontageTraces(dataBuffX,dataBuffY);
montageTrace.set_isRailed(is_railed);
//initialize the FFT traces
fftTrace = new ScatterTrace_FFT(fftBuff); //can't put this here...must be in setup()
fftYOffset = new float[nchan];
initializeFFTTraces(fftTrace,fftBuff,fftYOffset,gFFT);
//link the data to the head plot
headPlot1.setIntensityData_byRef(dataBuffY_std,is_railed);
headPlot1.setPolarityData_byRef(dataBuffY_polarity);
}
public void setShowSpectrogram(boolean show) {
showSpectrogram = show;
}
public void tellGUIWhichChannelForSpectrogram(int Ichan) { // Ichan starts at zero
if (Ichan != whichChannelForSpectrogram) {
whichChannelForSpectrogram = Ichan;
titleSpectrogram.string = makeSpectrogramTitle();
}
}
public String makeSpectrogramTitle() {
return ("Spectrogram, Channel " + (whichChannelForSpectrogram+1) + " (As Received)");
}
public void setGUIpage(int page) {
if ((page >= 0) && (page < N_GUI_PAGES)) {
guiPage = page;
} else {
guiPage = 0;
}
//update the text on the button
// guiPageButton.setString("Page\n" + (guiPage+1) + " of " + N_GUI_PAGES);
}
public void incrementGUIpage() {
setGUIpage( (guiPage+1) % N_GUI_PAGES );
}
public boolean isMouseOnGraph2D(Graph2D g, int mouse_x, int mouse_y) {
GraphDataPoint dataPoint = new GraphDataPoint();
getGraph2DdataPoint(g,mouse_x,mouse_y,dataPoint);
if ( (dataPoint.x >= g.getXAxis().getMinValue()) &
(dataPoint.x <= g.getXAxis().getMaxValue()) &
(dataPoint.y >= g.getYAxis().getMinValue()) &
(dataPoint.y <= g.getYAxis().getMaxValue()) ) {
return true;
} else {
return false;
}
}
public boolean isMouseOnMontage(int mouse_x, int mouse_y) {
return isMouseOnGraph2D(gMontage,mouse_x,mouse_y);
}
public boolean isMouseOnFFT(int mouse_x, int mouse_y) {
return isMouseOnGraph2D(gFFT,mouse_x,mouse_y);
}
public void getGraph2DdataPoint(Graph2D g, int mouse_x,int mouse_y, GraphDataPoint dataPoint) {
int rel_x = mouse_x - int(g.position.x);
int rel_y = g.getYAxis().getLength() - (mouse_y - int(g.position.y));
dataPoint.x = g.getXAxis().positionToValue(rel_x);
dataPoint.y = g.getYAxis().positionToValue(rel_y);
}
public void getMontageDataPoint(int mouse_x, int mouse_y, GraphDataPoint dataPoint) {
getGraph2DdataPoint(gMontage,mouse_x,mouse_y,dataPoint);
dataPoint.x_units = "sec";
dataPoint.y_units = "uV";
}
public void getFFTdataPoint(int mouse_x,int mouse_y,GraphDataPoint dataPoint) {
getGraph2DdataPoint(gFFT, mouse_x,mouse_y,dataPoint);
dataPoint.x_units = "Hz";
dataPoint.y_units = "uV/sqrt(Hz)";
}
// public boolean isMouseOnHeadPlot(int mouse_x, int mouse_y) {
// return headPlot1.isPixelInsideHead(mouse_x,mouse_y) {
// }
public void update(float[] data_std_uV,float[] data_elec_imp_ohm) {
//assume new data has already arrived via the pre-existing references to dataBuffX and dataBuffY and FftBuff
montageTrace.generate(); //graph doesn't update without this
fftTrace.generate(); //graph doesn't update without this
headPlot1.update();
cc.update();
//update the text strings
String fmt; float val;
for (int Ichan=0; Ichan < data_std_uV.length; Ichan++) {
//update the voltage values
val = data_std_uV[Ichan];
chanValuesMontage[Ichan].string = String.format(getFmt(val),val) + " uVrms";
if (montageTrace.is_railed != null) {
if (montageTrace.is_railed[Ichan].is_railed == true) {
chanValuesMontage[Ichan].string = "RAILED";
} else if (montageTrace.is_railed[Ichan].is_railed_warn == true) {
chanValuesMontage[Ichan].string = "NEAR RAILED";
}
}
//update the impedance values
val = data_elec_imp_ohm[Ichan]/1000;
impValuesMontage[Ichan].string = String.format(getFmt(val),val) + " kOhm";
if (montageTrace.is_railed != null) {
if (montageTrace.is_railed[Ichan].is_railed == true) {
impValuesMontage[Ichan].string = "RAILED";
}
}
}
}
private String getFmt(float val) {
String fmt;
if (val > 100.0f) {
fmt = "%.0f";
} else if (val > 10.0f) {
fmt = "%.1f";
} else {
fmt = "%.2f";
}
return fmt;
}
public void draw() {
if(!drawEMG){
headPlot1.draw();
}
//draw montage or spectrogram
if (showSpectrogram == false) {
//show time-domain montage, only if full channel controller is not visible, to save some processing
gMontage.draw();
//add annotations
if (showMontageValues) {
for (int Ichan = 0; Ichan < chanValuesMontage.length; Ichan++) {
chanValuesMontage[Ichan].draw();
}
}
} else {
//show the spectrogram
gSpectrogram.draw(); //draw the spectrogram axes
titleSpectrogram.draw(); //draw the spectrogram title
//draw the spectrogram image
PVector pos = gSpectrogram.position;
Axis2D ax = gSpectrogram.getXAxis();
int x = ax.valueToPosition(ax.getMinValue())+(int)pos.x;
int w = ax.valueToPosition(ax.getMaxValue());
ax = gSpectrogram.getYAxis();
int y = (int) pos.y - ax.valueToPosition(ax.getMinValue()); //position needs top-left. The MAX value is at the top-left for this plot.
int h = ax.valueToPosition(ax.getMaxValue());
//println("gui_Manager.draw(): x,y,w,h = " + x + " " + y + " " + w + " " + h);
float max_freq_Hz = gSpectrogram.getYAxis().getMaxValue()-0.5f;
spectrogram.draw(x,y,w,h,max_freq_Hz);
}
//draw the regular FFT spectrum display
gFFT.draw();
titleFFT.draw();//println("completed FFT draw...");
//draw the UI buttons and other elements
stopButton.draw();
//commented out because pages 1-2 are being moved to the left of the EEG montage
// guiPageButton.draw();
switch (guiPage) { //the rest of the elements depend upon what GUI page we're on
//note: GUI_PAGE_CHANNEL_ON_OFF is the default at the end
case GUI_PAGE_IMPEDANCE_CHECK:
//show impedance buttons and text
for (int Ichan = 0; Ichan < chanButtons.length; Ichan++) {
impedanceButtonsP[Ichan].draw(); //P-channel buttons
impedanceButtonsN[Ichan].draw(); //N-channel buttons
}
for (int Ichan = 0; Ichan < impValuesMontage.length; Ichan++) {
impValuesMontage[Ichan].draw(); //impedance values on montage plot
}
biasButton.draw();
break;
case GUI_PAGE_HEADPLOT_SETUP:
intensityFactorButton.draw();
loglinPlotButton.draw();
filtBPButton.draw();
filtNotchButton.draw();
//fftNButton.draw();
smoothingButton.draw();
showPolarityButton.draw();
maxDisplayFreqButton.draw();
break;
default: //assume GUI_PAGE_CHANNEL_ONOFF:
//show channel buttons
for (int Ichan = 0; Ichan < chanButtons.length; Ichan++) { chanButtons[Ichan].draw(); }
//detectButton.draw();
//spectrogramButton.draw();
}
if (showMontageValues) {
for (int Ichan = 0; Ichan < chanValuesMontage.length; Ichan++) {
chanValuesMontage[Ichan].draw();
}
}
// if(controlPanelCollapser.isActive){
// controlPanel.draw();
// }
// controlPanelCollapser.draw();
cc.draw();
if(cc.showFullController == false){
titleMontage.draw();
}
showMontageButton.draw();
showChannelControllerButton.draw();
}
public void mousePressed(){
verbosePrint("Gui_Manager: mousePressed: mouse pressed.");
//if showMontage button pressed
if(showMontageButton.isMouseHere()){
//turn off visibility of channel full controller
cc.showFullController = false;
showMontageButton.setIsActive(true);
showMontageButton.buttonFont = f1;
showChannelControllerButton.setIsActive(false);
showChannelControllerButton.buttonFont = f2;
}
//if showChannelController is pressed
if(showChannelControllerButton.isMouseHere()){
cc.showFullController = true;
showMontageButton.setIsActive(false);
showMontageButton.buttonFont = f2;
showChannelControllerButton.setIsActive(true);
showChannelControllerButton.buttonFont = f1;
}
//if cursor inside channel controller
// if(mouseX >= cc.x1 && mouseX <= (cc.x2 - cc.w2) && mouseY >= cc.y1 && mouseY <= (cc.y1 + cc.h1) ){
verbosePrint("Gui_Manager: mousePressed: Channel Controller mouse pressed...");
cc.mousePressed();
// }
//turn off visibility of graph
// turn on drawing and interactivity of channel controller
//however, the on/off & impedance values must show to the right at all times ... so it should change a boolean in ChannelController
}
public void mouseReleased(){
//verbosePrint("Gui_Manager: mouseReleased()");
// if(mouseX >= cc.x1 && mouseX <= (cc.x2 - cc.w2) && mouseY >= cc.y1 && mouseY <= (cc.y1 + cc.h1) ){
verbosePrint("Gui_Manager: mouseReleased(): Channel Controller mouse released...");
cc.mouseReleased();
stopButton.setIsActive(false);
// guiPageButton.setIsActive(false);
intensityFactorButton.setIsActive(false);
loglinPlotButton.setIsActive(false);
filtBPButton.setIsActive(false);
filtNotchButton.setIsActive(false);
smoothingButton.setIsActive(false);
showPolarityButton.setIsActive(false);
maxDisplayFreqButton.setIsActive(false);
biasButton.setIsActive(false);
}
};
Arquivo executável
+1040
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Arquivo executável
+58
Ver Arquivo
@@ -0,0 +1,58 @@
//this class is used to create the help widget that provides system feedback in response to interactivity
//it is intended to serve as a pseudo-console, allowing us to print useful information to the interface as opposed to an IDE console
class HelpWidget {
public float x, y, w, h;
// ArrayList<String> prevOutputs; //growing list of all previous system interactivity
String currentOutput = "..."; //current text shown in help widget, based on most recent command
int padding = 5;
HelpWidget(float _xPos, float _yPos, float _width, float _height) {
x = _xPos;
y = _yPos;
w = _width;
h = _height;
}
public void update() {
}
public void draw() {
pushStyle();
noStroke();
// draw background of widget
fill(255);
rect(x, height-h, width, h);
//draw bg of text field of widget
strokeWeight(1);
stroke(color(0, 5, 11));
fill(color(0, 5, 11));
rect(x + padding, height-h + padding, width - padding*5 - 128, h - padding *2);
textSize(14);
fill(255);
textAlign(LEFT, TOP);
text(currentOutput, padding*2, height - h + padding + 4);
//draw OpenBCI LOGO
image(logo, width - (128+padding*2), height - 26, 128, 22);
popStyle();
}
public void output(String _output) {
currentOutput = _output;
// prevOutputs.add(_output);
}
};
public void output(String _output) {
helpWidget.output(_output);
}
+186
Ver Arquivo
@@ -0,0 +1,186 @@
//===================== MENU LIST CLASS ===========================//
//==================EXTENSION OF CONTROLP5=========================//
//==============USED FOR SOURCEBOX & SERIALBOX=====================//
//
// Created: Conor Russomanno Oct. 2014
// Based on ControlP5 Processing Library example, written by Andreas Schlegel
//
/////////////////////////////////////////////////////////////////////
//makeItem function used by MenuList class below
Map<String, Object> makeItem(String theHeadline) {
Map m = new HashMap<String, Object>();
m.put("headline", theHeadline);
return m;
}
public class MenuList extends Controller {
float pos, npos;
int itemHeight = 24;
int scrollerLength = 40;
int scrollerWidth = 15;
List< Map<String, Object>> items = new ArrayList< Map<String, Object>>();
PGraphics menu;
boolean updateMenu;
boolean drawHand;
int hoverItem = -1;
int activeItem = -1;
PFont menuFont = f2;
int padding = 7;
MenuList(ControlP5 c, String theName, int theWidth, int theHeight, PFont theFont) {
super( c, theName, 0, 0, theWidth, theHeight );
c.register( this );
menu = createGraphics(getWidth(),getHeight());
menuFont = theFont;
setView(new ControllerView<MenuList>() {
public void display(PGraphics pg, MenuList t) {
if (updateMenu) {
updateMenu();
}
if (inside()) {
if(!drawHand){
cursor(HAND);
drawHand = true;
}
menu.beginDraw();
int len = -(itemHeight * items.size()) + getHeight();
int ty;
if(len != 0){
ty = int(map(pos, len, 0, getHeight() - scrollerLength - 2, 2 ) );
} else {
ty = 0;
}
menu.fill(bgColor, 100);
if(ty > 0){
menu.rect(getWidth()-scrollerWidth-2, ty, scrollerWidth, scrollerLength );
}
menu.endDraw();
}
else {
if(drawHand){
drawHand = false;
cursor(ARROW);
}
}
pg.image(menu, 0, 0);
}
}
);
updateMenu();
}
/* only update the image buffer when necessary - to save some resources */
void updateMenu() {
int len = -(itemHeight * items.size()) + getHeight();
npos = constrain(npos, len, 0);
pos += (npos - pos) * 0.1;
// pos += (npos - pos) * 0.1;
menu.beginDraw();
menu.noStroke();
menu.background(255, 64);
menu.textFont(cp5.getFont().getFont());
menu.pushMatrix();
menu.translate( 0, pos );
menu.pushMatrix();
int i0;
if((itemHeight * items.size()) != 0){
i0 = PApplet.max( 0, int(map(-pos, 0, itemHeight * items.size(), 0, items.size())));
} else{
i0 = 0;
}
int range = ceil((float(getHeight())/float(itemHeight))+1);
int i1 = PApplet.min( items.size(), i0 + range );
menu.translate(0, i0*itemHeight);
for (int i=i0; i<i1; i++) {
Map m = items.get(i);
menu.fill(255, 100);
if (i == hoverItem) {
menu.fill(127, 134, 143);
}
if (i == activeItem) {
menu.stroke(184, 220, 105, 255);
menu.strokeWeight(1);
menu.fill(184, 220, 105, 255);
menu.rect(0, 0, getWidth()-1, itemHeight-1 );
menu.noStroke();
} else {
menu.rect(0, 0, getWidth(), itemHeight-1 );
}
menu.fill(bgColor);
menu.textFont(menuFont);
menu.text(m.get("headline").toString(), 8, itemHeight - padding); // 5/17
menu.translate( 0, itemHeight );
}
menu.popMatrix();
menu.popMatrix();
menu.endDraw();
updateMenu = abs(npos-pos)>0.01 ? true:false;
}
/* when detecting a click, check if the click happend to the far right, if yes, scroll to that position,
* otherwise do whatever this item of the list is supposed to do.
*/
public void onClick() {
if (getPointer().x()>getWidth()-scrollerWidth) {
npos= -map(getPointer().y(), 0, getHeight(), 0, items.size()*itemHeight);
updateMenu = true;
} else {
int len = itemHeight * items.size();
int index = int( map( getPointer().y() - pos, 0, len, 0, items.size() ) ) ;
setValue(index);
activeItem = index;
}
updateMenu = true;
}
public void onMove() {
if (getPointer().x()>getWidth() || getPointer().x()<0 || getPointer().y()<0 || getPointer().y()>getHeight() ) {
hoverItem = -1;
} else {
int len = itemHeight * items.size();
int index = int( map( getPointer().y() - pos, 0, len, 0, items.size() ) ) ;
hoverItem = index;
}
updateMenu = true;
}
public void onDrag() {
if (getPointer().x() > (getWidth()-scrollerWidth)) {
npos= -map(getPointer().y(), 0, getHeight(), 0, items.size()*itemHeight);
updateMenu = true;
} else {
npos += getPointer().dy() * 2;
updateMenu = true;
}
}
public void onScroll(int n) {
npos += ( n * 4 );
updateMenu = true;
}
void addItem(Map<String, Object> m) {
items.add(m);
updateMenu = true;
}
void removeItem(Map<String, Object> m) {
items.remove(m);
updateMenu = true;
}
Map<String, Object> getItem(int theIndex) {
return items.get(theIndex);
}
};
+1000
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+382
Ver Arquivo
@@ -0,0 +1,382 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This file contains all key commands for interactivity with GUI & OpenBCI
// Created by Chip Audette, Joel Murphy, & Conor Russomanno
// - Extracted from OpenBCI_GUI because it was getting too klunky
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//interpret a keypress...the key pressed comes in as "key"
void keyPressed() {
//note that the Processing variable "key" is the keypress as an ASCII character
//note that the Processing variable "keyCode" is the keypress as a JAVA keycode. This differs from ASCII
//println("OpenBCI_GUI: keyPressed: key = " + key + ", int(key) = " + int(key) + ", keyCode = " + keyCode);
if(!controlPanel.isOpen){ //don't parse the key if the control panel is open
if ((int(key) >=32) && (int(key) <= 126)) { //32 through 126 represent all the usual printable ASCII characters
parseKey(key);
} else {
parseKeycode(keyCode);
}
}
if(key==27){
key=0; //disable 'esc' quitting program
}
}
void parseKey(char val) {
int Ichan; boolean activate; int code_P_N_Both;
//assumes that val is a usual printable ASCII character (ASCII 32 through 126)
switch (val) {
case '.':
drawEMG = !drawEMG;
break;
case '1':
deactivateChannel(1-1);
break;
case '2':
deactivateChannel(2-1);
break;
case '3':
deactivateChannel(3-1);
break;
case '4':
deactivateChannel(4-1);
break;
case '5':
deactivateChannel(5-1);
break;
case '6':
deactivateChannel(6-1);
break;
case '7':
deactivateChannel(7-1);
break;
case '8':
deactivateChannel(8-1);
break;
case 'q':
if(nchan == 16){
deactivateChannel(9-1);
}
break;
case 'w':
if(nchan == 16){
deactivateChannel(10-1);
}
break;
case 'e':
if(nchan == 16){
deactivateChannel(11-1);
}
break;
case 'r':
if(nchan == 16){
deactivateChannel(12-1);
}
break;
case 't':
if(nchan == 16){
deactivateChannel(13-1);
}
break;
case 'y':
if(nchan == 16){
deactivateChannel(14-1);
}
break;
case 'u':
if(nchan == 16){
deactivateChannel(15-1);
}
break;
case 'i':
if(nchan == 16){
deactivateChannel(16-1);
}
break;
//activate channels 1-8
case '!':
activateChannel(1-1);
break;
case '@':
activateChannel(2-1);
break;
case '#':
activateChannel(3-1);
break;
case '$':
activateChannel(4-1);
break;
case '%':
activateChannel(5-1);
break;
case '^':
activateChannel(6-1);
break;
case '&':
activateChannel(7-1);
break;
case '*':
activateChannel(8-1);
break;
//activate channels 9-16 (DAISY MODE ONLY)
case 'Q':
if(nchan == 16){
activateChannel(9-1);
}
break;
case 'W':
if(nchan == 16){
activateChannel(10-1);
}
break;
case 'E':
if(nchan == 16){
activateChannel(11-1);
}
break;
case 'R':
if(nchan == 16){
activateChannel(12-1);
}
break;
case 'T':
if(nchan == 16){
activateChannel(13-1);
}
break;
case 'Y':
if(nchan == 16){
activateChannel(14-1);
}
break;
case 'U':
if(nchan == 16){
activateChannel(15-1);
}
break;
case 'I':
if(nchan == 16){
activateChannel(16-1);
}
break;
//other controls
case 's':
println("case s...");
stopRunning();
// stopButtonWasPressed();
break;
case 'b':
println("case b...");
startRunning();
// stopButtonWasPressed();
break;
case 'n':
println("openBCI: " + openBCI);
break;
case '?':
printRegisters();
break;
case 'd':
verbosePrint("Updating GUI's channel settings to default...");
gui.cc.loadDefaultChannelSettings();
//openBCI.serial_openBCI.write('d');
openBCI.configureAllChannelsToDefault();
break;
// //change the state of the impedance measurements...activate the N-channels
// case 'A':
// Ichan = 1; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'S':
// Ichan = 2; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'D':
// Ichan = 3; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'F':
// Ichan = 4; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'G':
// Ichan = 5; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'H':
// Ichan = 6; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'J':
// Ichan = 7; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'K':
// Ichan = 8; activate = true; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// //change the state of the impedance measurements...deactivate the N-channels
// case 'Z':
// Ichan = 1; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'X':
// Ichan = 2; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'C':
// Ichan = 3; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'V':
// Ichan = 4; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'B':
// Ichan = 5; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'N':
// Ichan = 6; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case 'M':
// Ichan = 7; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
// case '<':
// Ichan = 8; activate = false; code_P_N_Both = 1; setChannelImpedanceState(Ichan-1,activate,code_P_N_Both);
// break;
case 'm':
String picfname = "OpenBCI-" + getDateString() + ".jpg";
println("OpenBCI_GUI: 'm' was pressed...taking screenshot:" + picfname);
saveFrame("./SavedData/" + picfname); // take a shot of that!
break;
default:
println("OpenBCI_GUI: '" + key + "' Pressed...sending to OpenBCI...");
// if (openBCI.serial_openBCI != null) openBCI.serial_openBCI.write(key);//send the value as ascii with a newline character
//if (openBCI.serial_openBCI != null) openBCI.serial_openBCI.write(key);//send the value as ascii with a newline character
openBCI.sendChar(key);
break;
}
}
void parseKeycode(int val) {
//assumes that val is Java keyCode
switch (val) {
case 8:
println("OpenBCI_GUI: parseKeycode(" + val + "): received BACKSPACE keypress. Ignoring...");
break;
case 9:
println("OpenBCI_GUI: parseKeycode(" + val + "): received TAB keypress. Ignoring...");
//gui.showImpedanceButtons = !gui.showImpedanceButtons;
// gui.incrementGUIpage(); //deprecated with new channel controller
break;
case 10:
println("Entering Presentation Mode");
drawPresentation = !drawPresentation;
break;
case 16:
println("OpenBCI_GUI: parseKeycode(" + val + "): received SHIFT keypress. Ignoring...");
break;
case 17:
//println("OpenBCI_GUI: parseKeycode(" + val + "): received CTRL keypress. Ignoring...");
break;
case 18:
println("OpenBCI_GUI: parseKeycode(" + val + "): received ALT keypress. Ignoring...");
break;
case 20:
println("OpenBCI_GUI: parseKeycode(" + val + "): received CAPS LOCK keypress. Ignoring...");
break;
case 27:
println("OpenBCI_GUI: parseKeycode(" + val + "): received ESC keypress. Stopping OpenBCI...");
//stopRunning();
break;
case 33:
println("OpenBCI_GUI: parseKeycode(" + val + "): received PAGE UP keypress. Ignoring...");
break;
case 34:
println("OpenBCI_GUI: parseKeycode(" + val + "): received PAGE DOWN keypress. Ignoring...");
break;
case 35:
println("OpenBCI_GUI: parseKeycode(" + val + "): received END keypress. Ignoring...");
break;
case 36:
println("OpenBCI_GUI: parseKeycode(" + val + "): received HOME keypress. Ignoring...");
break;
case 37:
println("Slide Back!");
if (millis() - myPresentation.timeOfLastSlideChange >= 250) {
if(myPresentation.currentSlide >= 0){
myPresentation.slideBack();
myPresentation.timeOfLastSlideChange = millis();
}
}
break;
case 38:
println("OpenBCI_GUI: parseKeycode(" + val + "): received UP ARROW keypress. Ignoring...");
eegProcessing_user.switchesActive = true;
break;
case 39:
println("Forward!");
if (millis() - myPresentation.timeOfLastSlideChange >= 250) {
if(myPresentation.currentSlide < myPresentation.presentationSlides.length - 1){
myPresentation.slideForward();
myPresentation.timeOfLastSlideChange = millis();
}
}
break;
case 40:
println("OpenBCI_GUI: parseKeycode(" + val + "): received DOWN ARROW keypress. Ignoring...");
eegProcessing_user.switchesActive = false;
break;
case 112:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F1 keypress. Ignoring...");
break;
case 113:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F2 keypress. Ignoring...");
break;
case 114:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F3 keypress. Ignoring...");
break;
case 115:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F4 keypress. Ignoring...");
break;
case 116:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F5 keypress. Ignoring...");
break;
case 117:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F6 keypress. Ignoring...");
break;
case 118:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F7 keypress. Ignoring...");
break;
case 119:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F8 keypress. Ignoring...");
break;
case 120:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F9 keypress. Ignoring...");
break;
case 121:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F10 keypress. Ignoring...");
break;
case 122:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F11 keypress. Ignoring...");
break;
case 123:
println("OpenBCI_GUI: parseKeycode(" + val + "): received F12 keypress. Ignoring...");
break;
case 127:
println("OpenBCI_GUI: parseKeycode(" + val + "): received DELETE keypress. Ignoring...");
break;
case 155:
println("OpenBCI_GUI: parseKeycode(" + val + "): received INSERT keypress. Ignoring...");
break;
default:
println("OpenBCI_GUI: parseKeycode(" + val + "): value is not known. Ignoring...");
break;
}
}
Arquivo executável
+122
Ver Arquivo
@@ -0,0 +1,122 @@
//////////////////////////////////////////////////////////////////////////
//
// Playground Class
// Created: 11/22/14 by Conor Russomanno
// An extra interface pane for additional GUI features
//
//////////////////////////////////////////////////////////////////////////
class Playground {
//button for opening and closing
float x, y, w, h;
color boxBG;
color strokeColor;
float topMargin, bottomMargin;
boolean isOpen;
boolean collapsing;
Button collapser;
Playground(int _topMargin) {
topMargin = _topMargin;
bottomMargin = helpWidget.h;
isOpen = false;
collapsing = true;
boxBG = color(255);
strokeColor = color(138, 146, 153);
collapser = new Button(0, 0, 20, 60, "<", 14);
x = width;
y = topMargin;
w = 0;
h = height - (topMargin+bottomMargin);
}
public void update() {
// verbosePrint("uh huh");
if (collapsing) {
collapse();
} else {
expand();
}
if (x > width) {
x = width;
}
}
public void draw() {
// verbosePrint("yeaaa");
pushStyle();
fill(boxBG);
stroke(strokeColor);
rect(width - w, topMargin, w, height - (topMargin + bottomMargin));
textFont(f1);
textAlign(LEFT, TOP);
fill(bgColor);
text("Developer Playground", x + 10, y + 10);
fill(255, 0, 0);
collapser.draw(int(x - collapser.but_dx), int(topMargin + (h-collapser.but_dy)/2));
popStyle();
}
boolean isMouseHere() {
if (mouseX >= x && mouseX <= width && mouseY >= y && mouseY <= height - bottomMargin) {
return true;
} else {
return false;
}
}
boolean isMouseInButton() {
verbosePrint("Playground: isMouseInButton: attempting");
if (mouseX >= collapser.but_x && mouseX <= collapser.but_x+collapser.but_dx && mouseY >= collapser.but_y && mouseY <= collapser.but_y + collapser.but_dy) {
return true;
} else {
return false;
}
}
public void toggleWindow() {
if (isOpen) {//if open
verbosePrint("close");
collapsing = true;//collapsing = true;
isOpen = false;
collapser.but_txt = "<";
} else {//if closed
verbosePrint("open");
collapsing = false;//expanding = true;
isOpen = true;
collapser.but_txt = ">";
}
}
public void mousePressed() {
verbosePrint("Playground >> mousePressed()");
}
public void mouseReleased() {
verbosePrint("Playground >> mouseReleased()");
}
public void expand() {
if (w <= width/3) {
w = w + 50;
x = width - w;
}
}
public void collapse() {
if (w >= 0) {
w = w - 50;
x = width - w;
}
}
};
+106
Ver Arquivo
@@ -0,0 +1,106 @@
///////////////////////////////////////////////////////////////////////////
//
// Created: 2/19/16
// by Conor Russomanno for BodyHacking Con DIY Cyborgia Presentation
// This code is used to organize a neuro-powered presentation... refer to triggers in the EEG_Processing_User class of the EEG_Processing.pde file
//
///////////////////////////////////////////////////////////////////////////
boolean drawPresentation = false;
class Presentation {
//presentation images
PImage presentationSlides[] = new PImage[16];
float timeOfLastSlideChange = 0;
int currentSlide = 1;
int slideCount = 0;
boolean lockSlides = false;
Presentation (){
//loading presentation images
println("attempting to load images for presentation...");
presentationSlides[0] = loadImage("prez-images/DemocratizationOfNeurotech.001.jpg");
presentationSlides[1] = loadImage("prez-images/DemocratizationOfNeurotech.001.jpg");
presentationSlides[2] = loadImage("prez-images/DemocratizationOfNeurotech.002.jpg");
presentationSlides[3] = loadImage("prez-images/DemocratizationOfNeurotech.003.jpg");
presentationSlides[4] = loadImage("prez-images/DemocratizationOfNeurotech.004.jpg");
presentationSlides[5] = loadImage("prez-images/DemocratizationOfNeurotech.005.jpg");
presentationSlides[6] = loadImage("prez-images/DemocratizationOfNeurotech.006.jpg");
presentationSlides[7] = loadImage("prez-images/DemocratizationOfNeurotech.007.jpg");
presentationSlides[8] = loadImage("prez-images/DemocratizationOfNeurotech.008.jpg");
presentationSlides[9] = loadImage("prez-images/DemocratizationOfNeurotech.009.jpg");
presentationSlides[10] = loadImage("prez-images/DemocratizationOfNeurotech.010.jpg");
presentationSlides[11] = loadImage("prez-images/DemocratizationOfNeurotech.011.jpg");
presentationSlides[12] = loadImage("prez-images/DemocratizationOfNeurotech.012.jpg");
presentationSlides[13] = loadImage("prez-images/DemocratizationOfNeurotech.013.jpg");
presentationSlides[14] = loadImage("prez-images/DemocratizationOfNeurotech.014.jpg");
presentationSlides[15] = loadImage("prez-images/DemocratizationOfNeurotech.015.jpg");
//presentationSlides[16] = loadImage("prez-images/DemocratizationOfNeurotech.016.jpg");
//presentationSlides[17] = loadImage("prez-images/DemocratizationOfNeurotech.017.jpg");
//presentationSlides[18] = loadImage("prez-images/DemocratizationOfNeurotech.018.jpg");
//presentationSlides[19] = loadImage("prez-images/DemocratizationOfNeurotech.019.jpg");
//presentationSlides[20] = loadImage("prez-images/DemocratizationOfNeurotech.020.jpg");
//presentationSlides[21] = loadImage("prez-images/DemocratizationOfNeurotech.021.jpg");
//presentationSlides[22] = loadImage("prez-images/DemocratizationOfNeurotech.022.jpg");
//presentationSlides[23] = loadImage("prez-images/DemocratizationOfNeurotech.023.jpg");
//presentationSlides[24] = loadImage("prez-images/DemocratizationOfNeurotech.024.jpg");
//presentationSlides[25] = loadImage("prez-images/DemocratizationOfNeurotech.025.jpg");
//presentationSlides[26] = loadImage("prez-images/DemocratizationOfNeurotech.026.jpg");
//presentationSlides[27] = loadImage("prez-images/DemocratizationOfNeurotech.027.jpg");
//presentationSlides[28] = loadImage("prez-images/DemocratizationOfNeurotech.028.jpg");
//presentationSlides[29] = loadImage("prez-images/DemocratizationOfNeurotech.029.jpg");
//presentationSlides[30] = loadImage("prez-images/DemocratizationOfNeurotech.030.jpg");
//presentationSlides[31] = loadImage("prez-images/DemocratizationOfNeurotech.031.jpg");
//presentationSlides[32] = loadImage("prez-images/DemocratizationOfNeurotech.032.jpg");
//presentationSlides[33] = loadImage("prez-images/DemocratizationOfNeurotech.033.jpg");
//presentationSlides[34] = loadImage("prez-images/DemocratizationOfNeurotech.034.jpg");
//presentationSlides[35] = loadImage("prez-images/DemocratizationOfNeurotech.035.jpg");
//presentationSlides[36] = loadImage("prez-images/DemocratizationOfNeurotech.036.jpg");
//presentationSlides[37] = loadImage("prez-images/DemocratizationOfNeurotech.037.jpg");
//presentationSlides[38] = loadImage("prez-images/DemocratizationOfNeurotech.038.jpg");
//presentationSlides[39] = loadImage("prez-images/DemocratizationOfNeurotech.039.jpg");
//presentationSlides[40] = loadImage("prez-images/DemocratizationOfNeurotech.040.jpg");
//presentationSlides[41] = loadImage("prez-images/DemocratizationOfNeurotech.041.jpg");
//presentationSlides[42] = loadImage("prez-images/DemocratizationOfNeurotech.042.jpg");
//presentationSlides[43] = loadImage("prez-images/DemocratizationOfNeurotech.043.jpg");
//presentationSlides[44] = loadImage("prez-images/DemocratizationOfNeurotech.044.jpg");
//presentationSlides[45] = loadImage("prez-images/DemocratizationOfNeurotech.045.jpg");
//presentationSlides[46] = loadImage("prez-images/DemocratizationOfNeurotech.046.jpg");
//presentationSlides[47] = loadImage("prez-images/DemocratizationOfNeurotech.047.jpg");
//presentationSlides[48] = loadImage("prez-images/DemocratizationOfNeurotech.048.jpg");
slideCount = 28;
println("DONE loading images!");
}
public void slideForward() {
if(currentSlide < slideCount - 1 && drawPresentation && !lockSlides){
println("Slide Forward!");
currentSlide++;
}
}
public void slideBack() {
if(currentSlide > 0 && drawPresentation && !lockSlides){
println("Slide Back!");
currentSlide--;
}
}
public void draw() {
// ----- Drawing Presentation -------
if (drawPresentation == true) {
image(presentationSlides[currentSlide], 0, 0, width, height);
}
if(lockSlides){
//draw red rectangle to indicate that slides are locked
pushStyle();
fill(255,0,0);
rect(width - 50, 25, 25, 25);
popStyle();
}
}
}
Arquivo executável
+107
Ver Arquivo
@@ -0,0 +1,107 @@
//////////////////////////////////
//
// This file contains code used to convert HEX files (stored by OpenBCI on the local SD) into
// text files that can be used for PLAYBACK mode.
// Created: Conor Russomanno - 10/22/14 (based on code written by Joel Murphy summer 2014)
//
//////////////////////////////////
//variables for SD file conversion
BufferedReader dataReader;
String dataLine;
PrintWriter dataWriter;
String convertedLine;
String thisLine;
String h;
float[] intData = new float[20];
String logFileName;
long thisTime;
long thatTime;
public void convertSDFile() {
println("");
try {
dataLine = dataReader.readLine();
}
catch (IOException e) {
e.printStackTrace();
dataLine = null;
}
if (dataLine == null) {
// Stop reading because of an error or file is empty
thisTime = millis() - thatTime;
controlPanel.convertingSD = false;
println("nothing left in file");
println("SD file conversion took "+thisTime+" mS");
dataWriter.flush();
dataWriter.close();
} else {
// println(dataLine);
String[] hexNums = splitTokens(dataLine, ",");
if (hexNums[0].charAt(0) == '%') {
// println(dataLine);
dataWriter.println(dataLine);
println(dataLine);
} else {
for (int i=0; i<hexNums.length; i++) {
h = hexNums[i];
if (i > 0) {
if (h.charAt(0) > '7') { // if the number is negative
h = "FF" + hexNums[i]; // keep it negative
} else { // if the number is positive
h = "00" + hexNums[i]; // keep it positive
}
if (i > 8) { // accelerometer data needs another byte
if (h.charAt(0) == 'F') {
h = "FF" + h;
} else {
h = "00" + h;
}
}
}
// println(h); // use for debugging
if (h.length()%2 == 0) { // make sure this is a real number
intData[i] = unhex(h);
} else {
intData[i] = 0;
}
//if not first column(sample #) or columns 9-11 (accelerometer), convert to uV
if (i>=1 && i<=8) {
intData[i] *= openBCI.get_scale_fac_uVolts_per_count();
}
//print the current channel value
dataWriter.print(intData[i]);
if (i < hexNums.length-1) {
//print "," separator
dataWriter.print(",");
}
}
//println();
dataWriter.println();
}
}
}
void createPlaybackFileFromSD() {
logFileName = "data/EEG_Data/SDconverted-"+getDateString()+".txt";
dataWriter = createWriter(logFileName);
dataWriter.println("%OBCI Data Log - " + getDateString());
}
void sdFileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
println("User selected " + selection.getAbsolutePath());
dataReader = createReader(selection.getAbsolutePath()); // ("positions.txt");
controlPanel.convertingSD = true;
println("Timing SD file conversion...");
thatTime = millis();
}
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+4
Ver Arquivo
@@ -0,0 +1,4 @@
Should be about 23 minutes long....
5 minutes of baseline (reading google news on my cell phone, my default activity)
15 minutes of breathing meditation (there were two interval bells at 5 minute intervals tha tmay be visible on eeg)
then, another ~ 3 min of baseline google news reading.
+4
Ver Arquivo
@@ -0,0 +1,4 @@
Should be about 9 minutes long....
2 minutes of baseline (reading google news on my cell phone, my default activity)
5 minutes of relaxation, letting thoughts wander, pondering last minute christmas presents
then, 2 min of reading chip's blog post on eye blinks.
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Arquivo executável
+414
Ver Arquivo
@@ -0,0 +1,414 @@
//////////////////
//
// The ScatterTrace class is used to draw and manage the traces on each
// X-Y line plot created using gwoptics graphing library
//
// Created: Chip Audette, May 2014
//
// Based on examples in gwoptics graphic library v0.5.0
// http://www.gwoptics.org/processing/gwoptics_p5lib/
//
// Note that this class does NOT store any of the data used for the
// plot. Instead, you point it to the data that lives in your
// own program. In Java-speak, I believe that this is called
// "aliasing"...in this class, I have made an "alias" to your data.
// Some people consider this dangerous. Because Processing is slow,
// this was one technique for making it faster. By making an alias
// to your data, you don't need to pass me the data for every update
// and I don't need to make a copy of it. Instead, once you update
// your data array, the alias in this class is already pointing to
// the right place. Cool, huh?
//
////////////////
//import processing.core.PApplet;
import org.gwoptics.graphics.*;
import org.gwoptics.graphics.graph2D.*;
import org.gwoptics.graphics.graph2D.Graph2D;
import org.gwoptics.graphics.graph2D.LabelPos;
import org.gwoptics.graphics.graph2D.traces.Blank2DTrace;
import org.gwoptics.graphics.graph2D.backgrounds.*;
import java.awt.Color;
class ScatterTrace extends Blank2DTrace {
private float[] dataX;
private float[][] dataY;
private float plotYScale = 1f; //multiplied to data prior to plotting
private float plotYOffset[]; //added to data prior to plotting, after applying plotYScale
private int decimate_factor = 1; // set to 1 to plot all points, 2 to plot every other point, 3 for every third point
private DataStatus[] is_railed;
PFont font = createFont("Arial", 16);
float[] plotXlim;
public ScatterTrace() {
//font = createFont("Arial",10);
plotXlim = new float[] {
Float.NaN, Float.NaN
};
}
/* set the plot's X and Y data by overwriting the existing data */
public void setXYData_byRef(float[] x, float[][] y) {
//dataX = x.clone(); //makes a copy
dataX = x; //just copies the reference!
setYData_byRef(y);
}
public void setYData_byRef(float[][] y) {
//dataY = y.clone(); //makes a copy
dataY = y;//just copies the reference!
}
public void setYOffset_byRef(float[] yoff) {
plotYOffset = yoff; //just copies the reference!
}
public void setYScale_uV(float yscale_uV) {
setYScaleFac(1.0f/yscale_uV);
}
public void setYScaleFac(float yscale) {
plotYScale = yscale;
}
public void set_plotXlim(float val_low, float val_high) {
if (val_high < val_low) {
float foo = val_low;
val_low = val_high;
val_high = foo;
}
plotXlim[0]=val_low;
plotXlim[1]=val_high;
}
public void set_isRailed(DataStatus[] is_rail) {
is_railed = is_rail;
}
//here is the fucntion that gets called with every call to the GUI's own draw() fucntion
public void TraceDraw(Blank2DTrace.PlotRenderer pr) {
float x_val;
if (dataX.length > 0) {
pr.canvas.pushStyle(); //save whatever was the previous style
//pr.canvas.stroke(255, 0, 0); //set the new line's color
//pr.canvas.strokeWeight(1); //set the new line's linewidth
//draw all the individual segments
for (int Ichan = 0; Ichan < dataY.length; Ichan++) {
//if colorMode == 1 ...
switch (Ichan % 8) {
case 0:
pr.canvas.stroke(129, 129, 129); //set the new line's color;
break;
case 1:
pr.canvas.stroke(124, 75, 141); //set the new line's color;
break;
case 2:
pr.canvas.stroke(54, 87, 158); //set the new line's color;
break;
case 3:
pr.canvas.stroke(49, 113, 89); //set the new line's color;
break;
case 4:
pr.canvas.stroke(221, 178, 13); //set the new line's color;
break;
case 5:
pr.canvas.stroke(253, 94, 52); //set the new line's color;
break;
case 6:
pr.canvas.stroke(224, 56, 45); //set the new line's color;
break;
case 7:
pr.canvas.stroke(162, 82, 49); //set the new line's color;
break;
}
//if colorMode == 2 ... for future dev work ... want to be able to edit colors of EEG montage traces
// color _RGB = Color.HSBtoRGB(float((255/OpenBCI_Nchannels)*Ichan), 100.0f, 100.0f);
// println("_RGB: " + _RGB);
// pr.canvas.stroke(_RGB);
// pr.canvas.stroke((int((255/OpenBCI_Nchannels)*Ichan)), 125-(int(((255/OpenBCI_Nchannels)*Ichan)/2)), 255-(int((255/OpenBCI_Nchannels)*Ichan)));
// pr.canvas.stroke((int((255/nchan)*Ichan)), 125-(int(((255/nchan)*Ichan)/2)), 255-(int((255/nchan)*Ichan)));
float new_x = pr.valToX(dataX[0]); //first point, convert from data coordinates to pixel coordinates
float new_y = pr.valToY(dataY[Ichan][0]*plotYScale+plotYOffset[Ichan]); //first point, convert from data coordinates to pixel coordinate
float prev_x, prev_y;
for (int i=1; i < dataY[Ichan].length; i+= decimate_factor) {
prev_x = new_x;
prev_y = new_y;
x_val = dataX[i];
if ( (Float.isNaN(plotXlim[0])) || ((x_val >= plotXlim[0]) && (x_val <= plotXlim[1])) ) {
new_x = pr.valToX(x_val);
new_y = pr.valToY(dataY[Ichan][i]*plotYScale+plotYOffset[Ichan]);
pr.canvas.line(prev_x, prev_y, new_x, new_y);
//if (i==1) println("ScatterTrace: first point: new_x, new_y = " + new_x + ", " + new_y);
} else {
//do nothing
}
}
//add annotation for is_railed...doesn't work right
// if (is_railed != null) {
// if (Ichan < is_railed.length) {
// if (is_railed[Ichan]) {
// new_x = pr.valToX(-2.0); //near time zero
// new_y = pr.valToY(0.0+plotYOffset[Ichan]);
// println("ScatterTrace: text: new_x, new_y = " + new_x + ", " + new_y);
// fill(50,50,50);
// textFont(font);
// textAlign(RIGHT, BOTTOM);
// pr.canvas.text("RAILED",new_x,new_y,100);
// }
// }
// }
}
pr.canvas.popStyle(); //restore whatever was the previous style
}
}
public void setDecimateFactor(int val) {
decimate_factor = max(1, val);
//println("ScatterTrace: setDecimateFactor to " + decimate_factor);
}
}
// /////////////////////////////////////////////////////////////////////////////////////////////
class ScatterTrace_FFT extends Blank2DTrace {
private FFT[] fftData;
private float plotYOffset[];
private float[] plotXlim = new float[] {
Float.NaN, Float.NaN
};
private float[] goodBand_Hz = {
-1.0f, -1.0f
};
private float[] badBand_Hz = {
-1.0f, -1.0f
};
private boolean showFFTFilteringData = false;
private DetectionData_FreqDomain[] detectionData;
private Oscil wave;
public ScatterTrace_FFT() {
}
public ScatterTrace_FFT(FFT foo_fft[]) {
setFFT_byRef(foo_fft);
// if (foo_fft.length != plotYOffset.length) {
// plotYOffset = new float[foo_fft.length];
// }
}
public void setFFT_byRef(FFT foo_fft[]) {
fftData = foo_fft;//just copies the reference!
}
public void setYOffset(float yoff[]) {
plotYOffset = yoff;
}
public void set_plotXlim(float val_low, float val_high) {
if (val_high < val_low) {
float foo = val_low;
val_low = val_high;
val_high = foo;
}
plotXlim[0]=val_low;
plotXlim[1]=val_high;
}
public void setGoodBand(float band_Hz[]) {
for (int i=0; i<2; i++) {
goodBand_Hz[i]=band_Hz[i];
};
}
public void setBadBand(float band_Hz[]) {
for (int i=0; i<2; i++) {
badBand_Hz[i]=band_Hz[i];
};
}
public void showFFTFilteringData(boolean show) {
showFFTFilteringData = show;
}
public void setDetectionData_freqDomain(DetectionData_FreqDomain[] data) {
detectionData = data.clone();
}
public void setAudioOscillator(Oscil wave_given) {
wave = wave_given;
}
public void TraceDraw(Blank2DTrace.PlotRenderer pr) {
float x_val, spec_value;
//save whatever was the previous style
pr.canvas.pushStyle();
// //add FFT processing bands
// float[] fooBand_Hz;
// for (int i=0; i<2; i++) {
// if (i==0) {
// fooBand_Hz = goodBand_Hz;
// pr.canvas.stroke(100,255,100);
// } else {
// fooBand_Hz = badBand_Hz;
// pr.canvas.stroke(255,100,100);
// }
// pr.canvas.strokeWeight(13);
// float x1 = pr.valToX(fooBand_Hz[0]);
// float x2 = pr.valToX(fooBand_Hz[1]);
// if (!showFFTFilteringData) {
// x1 = -1.0f; x2=-1.0f; //draw offscreen when not active
// }
// float y1 = pr.valToY(0.13f);
// float y2 = pr.valToY(0.13f);
// pr.canvas.line(x1,y1,x2,y2);
// }
if (fftData != null) {
pr.canvas.pushStyle(); //save whatever was the previous style
//draw all the individual segments
for (int Ichan=0; Ichan < fftData.length; Ichan++) {
//if colorMode == 1 ...
switch (Ichan % 8) {
case 0:
pr.canvas.stroke(129, 129, 129); //set the new line's color;
break;
case 1:
pr.canvas.stroke(124, 75, 141); //set the new line's color;
break;
case 2:
pr.canvas.stroke(54, 87, 158); //set the new line's color;
break;
case 3:
pr.canvas.stroke(49, 113, 89); //set the new line's color;
break;
case 4:
pr.canvas.stroke(221, 178, 13); //set the new line's color;
break;
case 5:
pr.canvas.stroke(253, 94, 52); //set the new line's color;
break;
case 6:
pr.canvas.stroke(224, 56, 45); //set the new line's color;
break;
case 7:
pr.canvas.stroke(162, 82, 49); //set the new line's color;
break;
}
// //if colorMode == 2...
// // pr.canvas.stroke((int((255/OpenBCI_Nchannels)*Ichan)), 125-(int(((255/OpenBCI_Nchannels)*Ichan)/2)), 255-(int((255/OpenBCI_Nchannels)*Ichan)));
// pr.canvas.stroke((int((255/nchan)*Ichan)), 125-(int(((255/nchan)*Ichan)/2)), 255-(int((255/nchan)*Ichan)));
float new_x = pr.valToX(fftData[Ichan].indexToFreq(0)); //first point, convert from data coordinates to pixel coordinates
float new_y = pr.valToY(fftData[Ichan].getBand(0)+plotYOffset[Ichan]); //first point, convert from data coordinates to pixel coordinate
float prev_x, prev_y;
for (int i=1; i < fftData[Ichan].specSize (); i++) {
prev_x = new_x;
prev_y = new_y;
x_val = fftData[Ichan].indexToFreq(i);
//only plot those points that are within the frequency limits of the plot
if ( (Float.isNaN(plotXlim[0])) || ((x_val >= plotXlim[0]) && (x_val <= plotXlim[1])) ) {
new_x = pr.valToX(x_val);
//spec_value = fftData[Ichan].getBand(i)/fftData[Ichan].specSize(); //uV_per_bin...this normalization is now done elsewhere
spec_value = fftData[Ichan].getBand(i);
new_y = pr.valToY(spec_value+plotYOffset[Ichan]);
pr.canvas.line(prev_x, prev_y, new_x, new_y);
} else {
//do nothing
} // end if Float.isNan
} //end of loop over spec size
// //add detection-related graphics
// if (showFFTFilteringData) {
// //add ellipse showing peak
// float new_x2 = pr.valToX(detectionData[Ichan].inband_freq_Hz);
// float new_y2 = pr.valToY(detectionData[Ichan].inband_uV);
// int diam = 8;
// pr.canvas.strokeWeight(1); //set the new line's linewidth
// if (detectionData[Ichan].isDetected) { //if there is a detection, make more prominent
// diam = 8;
// pr.canvas.strokeWeight(4); //set the new line's linewidth
// }
// ellipseMode(CENTER);
// pr.canvas.ellipse(new_x2,new_y2,diam,diam);
//
// //add horizontal lines indicating the detction threshold and guard level (use a dashed line)
// for (int Iband=0;Iband<2;Iband++) {
// float x1, x2,y;
// if (Iband==1) {
// x1 = pr.valToX(badBand_Hz[0]);
// x2 = pr.valToX(badBand_Hz[1]);
// y = pr.valToY(detectionData[Ichan].guard_uV);
// } else {
// x1 = pr.valToX(goodBand_Hz[0]);
// x2 = pr.valToX(goodBand_Hz[1]);
// y = pr.valToY(detectionData[Ichan].thresh_uV);
// }
//
// pr.canvas.strokeWeight(1.5);
// float dx = 8; //how big is the dash+space
// float nudge = 2;
// float foo_x=min(x1+dx,x2); //start here
// while (foo_x < x2) {
// pr.canvas.line(foo_x-dx+nudge,y,foo_x-(5*dx)/8+nudge,y);
// foo_x += dx;
// }
// }
// }
} // end loop over channels
// //update the audio
// if (showFFTFilteringData & (wave != null)) {
// //find if any channels have detected, and which is the strongest SNR
// float maxExcessSNR = -100.0f;
// for (int Ichan=0; Ichan < detectionData.length; Ichan++) {
// if (detectionData[Ichan].isDetected) {
// //how much above the threshold are we
// maxExcessSNR = max(maxExcessSNR,(detectionData[Ichan].inband_uV)/(detectionData[Ichan].thresh_uV));
// }
// }
// float audioFreq_Hz = calcDesiredAudioFrequency(maxExcessSNR);
// if (audioFreq_Hz > 0) {
// wave.amplitude.setLastValue(0.8); //turn on
// wave.frequency.setLastValue(audioFreq_Hz); //set the desired frequency
// println("ScatterTrace: excessSNR = " + maxExcessSNR + ", freq = " + audioFreq_Hz + " Hz");
// } else {
// //turn off
// wave.amplitude.setLastValue(0.0);
// }
// } else {
// //ensure that the audio is off
// wave.amplitude.setLastValue(0);
// }
pr.canvas.popStyle(); //restore whatever was the previous style
}
}
float calcDesiredAudioFrequency(float excessSNR) {
//set some constants
final float excessSNRRange[] = {
1.0f, 3.0f
}; //not dB, just linear units
final float freqRange_Hz[] = {
200.0f, 600.0f
};
//compute the desired snr
float outputFreq_Hz = -1.0f;
if (excessSNR >= excessSNRRange[0]) {
excessSNR = constrain(excessSNR, excessSNRRange[0], excessSNRRange[1]);
outputFreq_Hz = map(excessSNR, excessSNRRange[0], excessSNRRange[1], freqRange_Hz[0], freqRange_Hz[1]);
}
return outputFreq_Hz;
}
};
+178
Ver Arquivo
@@ -0,0 +1,178 @@
///////////////////////////////////////////////
//
// Created: Chip Audette, Oct 2013
// Modified: through May 2014
//
// No warranty. Use at your own risk. Use for whatever you'd like.
//
///////////////////////////////////////////////
//import ddf.minim.analysis.*; //for FFT
class Spectrogram {
public int Nfft;
//public float dataSlices[][]; //holds the data in [Nslices][Nfft] manner
//public float dT_perSlice_sec; //time interval between slices
public float fs_Hz; //sample rate
public PImage img;
public double clim[] = {0.0d, 1.0d};
private FFT localFftData;
private float[] localDataBuff;
private int localDataBuffCounter;
public int fft_stepsize;
public int Nslices;
Spectrogram(int N, float fs, int fft_step, float tmax_sec) {
println("Spectrogram: N, fs, fft_step, tmax_sec = " + N + " " + fs + " " + fft_step + " " + tmax_sec);
Nfft=N;
fs_Hz = fs;
//dT_perSlice_sec = ((float)Nfft) / fs;
fft_stepsize = constrain(fft_step,1,Nfft);
// clim[0] = java.lang.Math.log(0.01f);
// clim[1] = java.lang.Math.log(200.0f);
//create zero data for the local time-domain buffer
localDataBuff = new float[Nfft];
for (int I=0; I < Nfft; I++) {
localDataBuff[I] = 0.0f; //initialize
}
localDataBuffCounter = Nfft-fft_stepsize;
//initialize FFT
localFftData = new FFT(Nfft, fs_Hz);
localFftData.window(FFT.HAMMING);
//create the image
int tmax_samps = (int)(tmax_sec * fs_Hz + 0.5f); //the 0.5 is to round, not truncate
Nslices = (int)(((float)(tmax_samps-Nfft))/((float)fft_stepsize+0.5)) + 1;
img = createImage(Nslices,localFftData.specSize(),RGB);
println("Spectrogram: image is " + Nslices + " x " + localFftData.specSize());
img.loadPixels(); //this is apparently necessary to allocate the space for the pixels
int count=0;
for (int J=0; J < localFftData.specSize(); J++) {
for (int I=0; I<Nslices;I++) {
img.pixels[count]=getColor(java.lang.Math.log(0.0001f));
count++;
}
}
img.updatePixels();
}
public void addDataPoint(float dataPoint) {
//add point
localDataBuff[localDataBuffCounter] = dataPoint;
//println("Spectrogram.addDataPoint(): counter = " + localDataBuffCounter + ", data = " + localDataBuff[localDataBuffCounter]);
//increment counter for next time
localDataBuffCounter++;
//are we full?
if (localDataBuffCounter >= Nfft) {
//println("Spectrogra.addDataPoint(): processing the FFT block");
//compute the new FFT and update the overall image
addDataBlock(localDataBuff);
//shift the data buffer to make space for the next points
//println("addDataPoint: Nfft, fft_stepsize + " + Nfft + " " + fft_stepsize);
for (int I=0; I < (Nfft-fft_stepsize); I++) {
localDataBuff[I]=localDataBuff[(int)(I+fft_stepsize)];
//println("addDataPoint: Shifting " + I + " from " + (I+fft_stepsize) + ", val = " + (localDataBuff[I]));
}
//point the counter to the new location to start accumulating data
localDataBuffCounter = Nfft-fft_stepsize;
}
}
public void addDataBlock(float[] dataHere) {
float foo;
//do the FFT on the data block
float[] localCopy = new float[dataHere.length];
localCopy = Arrays.copyOfRange(dataHere,0, dataHere.length);
float meanVal = mean(localCopy);
for (int I=0; I<localCopy.length;I++) localCopy[I] -= meanVal; //remove mean before doing FFT
localFftData.forward(localCopy);
//convert fft data to uV_per_sqrtHz
//final float mean_winpow_sqr = 0.3966; //account for power lost when windowing...mean(hamming(N).^2) = 0.3966
final float mean_winpow = 1.0f/sqrt(2.0f); //account for power lost when windowing...mean(hamming(N).^2) = 0.3966
final float scale_rawPSDtoPSDPerHz = ((float)localFftData.specSize())*fs_Hz*mean_winpow; //normalize the amplitude by the number of bins to get the correct scaling to uV/sqrt(Hz)???
for (int I=0; I < localFftData.specSize(); I++) { //loop over each FFT bin
foo = sqrt(pow(localFftData.getBand(I),2)/scale_rawPSDtoPSDPerHz);
//if ((I > 5) & (I < 15)) println("Spectrogram: uV/rtHz = " + I + " " + foo);
localFftData.setBand(I,foo);
}
//update image...shift all previous pixels to the left
int pixel_ind=0;
int nPixelsWide = Nslices;
for (int J=0; J < localFftData.specSize(); J++) {
for (int I=0; I < (nPixelsWide-1); I++) {
pixel_ind = J*nPixelsWide + I;
img.pixels[pixel_ind] = img.pixels[pixel_ind+1];
}
}
//update image...set the color based on the latest data
for (int J=0; J < localFftData.specSize(); J++) {
pixel_ind = (localFftData.specSize()-J-1)*nPixelsWide + (nPixelsWide-1); //build from bottom-left
foo = localFftData.getBand(J); foo=max(foo,0.001f);
img.pixels[pixel_ind] = getColor(java.lang.Math.log(foo));
}
//we're finished with the pixels, so update the image
//println("addNewData: updating the pixels");
img.updatePixels();
}
//model after matlab's "jet" color scheme
private color getColor(double given_val) {
float r,b,g;
float val = (float)((given_val - clim[0])/(clim[1]-clim[0]));
val = min(1.0f,max(0.0f,val)); //span [0.0 1.0]
//compute color
float[] bounds = {1.0f/8.0f, 3.0f/8.0f, 5.0f/8.0f, 7.0f/8.0f};
if (val < bounds[0]) {
r = 0.0f;
g = 0.0f;
b = map(val,0.0f,bounds[0],0.5f,1.0f);
} else if (val < bounds[1]) {
r = 0.0f;
g = map(val,bounds[0],bounds[1],0.0f,1.0f);
b = 1.0f;
} else if (val < bounds[2]) {
r = map(val,bounds[1],bounds[2],0.0f,1.0f);
g = 1.0f;
b = map(val,bounds[1],bounds[2],1.0f,0.0f);
} else if (val < bounds[3]) {
r = 1.0f;
g = map(val,bounds[2],bounds[3],1.0f,0.0f);
b = 0.0f;
} else {
r = map(val,bounds[3],1.0f,1.0f,0.5f);
g = 0.0f;
b = 0.0f;
}
return color((int)(r*255.f),(int)(g*255.f),(int)(b*255.f));
}
public void draw(int x, int y, int w, int h,float max_freq_Hz) {
//float max_freq_Hz = freq_lim_Hz[1];
int max_ind = 0;
while ((localFftData.indexToFreq(max_ind) <= max_freq_Hz) & (max_ind < localFftData.specSize()-1)) max_ind++;
//println("Spectrogram.draw(): max_ind = " + max_ind);
//PImage foo = (PImage)(img.get(0,localFftData.specSize()-1-max_ind,Nslices,localFftData.specSize()-1)).clone();
//println("spectrogram.draw() max freq = " + localFftData.indexToFreq(max_ind));
int img_x = 0;
int img_y = localFftData.specSize()-1-max_ind;
int img_w = Nslices - img_x + 1;
int img_h = localFftData.specSize()-1 - img_y + 1;
image(img.get(img_x,img_y,img_w,img_h),x,y,w,h); //plot a subset
}
}
BIN
Ver Arquivo
Arquivo binário não exibido.
+18
Ver Arquivo
@@ -0,0 +1,18 @@
X,Y
-0.30,-0.275
-0.225,-0.05
-0.225,0.175
-0.275,0.335
0.0,-0.4
0.0,-0.15
0.0,0.025
0.0,0.25
0.3,-0.275
0.225,-0.025
0.225,0.175
0.275,0.335
+0.1,0
+0.1,0
+0.1,0
+0.1,0
0.0,0.025
+18
Ver Arquivo
@@ -0,0 +1,18 @@
X,Y
-0.125,-0.416
0.125,-0.416
-0.2,0.0
0.2,0.0
-0.3425,0.27
0.3425,0.27
-0.125,0.416
0.125,0.416
-0.3425,-0.27
0.3425,-0.27
-0.18,-0.15
0.18,-0.15
-0.416,0.0
0.416,0.0
-0.18,0.15
0.18,0.15
0.0,0.0
+18
Ver Arquivo
@@ -0,0 +1,18 @@
X,Y
-0.125,-0.416
0.125,-0.416
-0.2,0.0
0.2,0.0
-0.3425,0.27
0.3425,0.27
-0.125,0.416
0.125,0.416
0.0,0.0
0.0,0.0
0.0,0.0
0.0,0.0
0.0,0.0
0.0,0.0
0.0,0.0
0.0,0.0
0.0,-0.275
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
BIN
Ver Arquivo
Arquivo binário não exibido.
Arquivo executável
BIN
Ver Arquivo
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 4.0 KiB

Arquivo executável
BIN
Ver Arquivo
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 5.4 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 328 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 341 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 509 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 459 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 288 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 555 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 424 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 310 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 539 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 374 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 412 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 475 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 463 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 613 KiB

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 218 KiB

+180
Ver Arquivo
@@ -0,0 +1,180 @@
////////////////////////////////////////////////////////////
// Class: OutputFile_rawtxt
// Purpose: handle file creation and writing for the text log file
// Created: Chip Audette May 2, 2014
//
//write data to a text file
public class OutputFile_rawtxt {
PrintWriter output;
String fname;
private int rowsWritten;
OutputFile_rawtxt(float fs_Hz) {
//build up the file name
fname = "SavedData"+System.getProperty("file.separator")+"OpenBCI-RAW-";
//add year month day to the file name
fname = fname + year() + "-";
if (month() < 10) fname=fname+"0";
fname = fname + month() + "-";
if (day() < 10) fname = fname + "0";
fname = fname + day();
//add hour minute sec to the file name
fname = fname + "_";
if (hour() < 10) fname = fname + "0";
fname = fname + hour() + "-";
if (minute() < 10) fname = fname + "0";
fname = fname + minute() + "-";
if (second() < 10) fname = fname + "0";
fname = fname + second();
//add the extension
fname = fname + ".txt";
//open the file
output = createWriter(fname);
//add the header
writeHeader(fs_Hz);
//init the counter
rowsWritten = 0;
}
//variation on constructor to have custom name
OutputFile_rawtxt(float fs_Hz, String _fileName) {
fname = "SavedData"+System.getProperty("file.separator")+"OpenBCI-RAW-";
fname += _fileName;
fname += ".txt";
output = createWriter(fname); //open the file
writeHeader(fs_Hz); //add the header
rowsWritten = 0; //init the counter
}
public void writeHeader(float fs_Hz) {
output.println("%OpenBCI Raw EEG Data");
output.println("%");
output.println("%Sample Rate = " + fs_Hz + " Hz");
output.println("%First Column = SampleIndex");
output.println("%Other Columns = EEG data in microvolts followed by Accel Data (in G) interleaved with Aux Data");
output.flush();
}
// public void writeRawData_dataPacket(DataPacket_ADS1299 data, float scale_to_uV, float scale_for_aux) {
// writeRawData_dataPacket(data, scale_to_uV, data.values.length);
// }
public void writeRawData_dataPacket(DataPacket_ADS1299 data, float scale_to_uV, float scale_for_aux) {
if (output != null) {
output.print(Integer.toString(data.sampleIndex));
writeValues(data.values,scale_to_uV);
writeValues(data.auxValues,scale_for_aux);
output.println(); rowsWritten++;
//output.flush();
}
}
private void writeValues(int[] values, float scale_fac) {
int nVal = values.length;
for (int Ival = 0; Ival < nVal; Ival++) {
output.print(", ");
output.print(String.format("%.2f", scale_fac * float(values[Ival])));
}
}
public void closeFile() {
output.flush();
output.close();
}
public int getRowsWritten() {
return rowsWritten;
}
}
///////////////////////////////////////////////////////////////
// Class: Table_CSV
// Purpose: Extend the Table class to handle data files with comment lines
// Created: Chip Audette May 2, 2014
//
// Usage: Only invoke this object when you want to read in a data
// file in CSV format. Read it in at the time of creation via
//
// String fname = "myfile.csv";
// TableCSV myTable = new TableCSV(fname);
//
//import java.io.*;
//import processing.core.PApplet;
class Table_CSV extends Table {
Table_CSV(String fname) throws IOException {
init();
readCSV(PApplet.createReader(createInput(fname)));
}
//this function is nearly completely copied from parseBasic from Table.java
void readCSV(BufferedReader reader) throws IOException {
boolean header=false; //added by Chip, May 2, 2014;
boolean tsv = false; //added by Chip, May 2, 2014;
String line = null;
int row = 0;
if (rowCount == 0) {
setRowCount(10);
}
//int prev = 0; //-1;
try {
while ( (line = reader.readLine ()) != null) {
//added by Chip, May 2, 2014 to ignore lines that are comments
if (line.charAt(0) == '%') {
//println("Table_CSV: readCSV: ignoring commented line...");
continue;
}
if (row == getRowCount()) {
setRowCount(row << 1);
}
if (row == 0 && header) {
setColumnTitles(tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
header = false;
}
else {
setRow(row, tsv ? PApplet.split(line, '\t') : splitLineCSV(line));
row++;
}
// this is problematic unless we're going to calculate rowCount first
if (row % 10000 == 0) {
/*
if (row < rowCount) {
int pct = (100 * row) / rowCount;
if (pct != prev) { // also prevents "0%" from showing up
System.out.println(pct + "%");
prev = pct;
}
}
*/
try {
// Sleep this thread so that the GC can catch up
Thread.sleep(10);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
catch (Exception e) {
throw new RuntimeException("Error reading table on line " + row, e);
}
// shorten or lengthen based on what's left
if (row != getRowCount()) {
setRowCount(row);
}
}
}
+175
Ver Arquivo
@@ -0,0 +1,175 @@
//////////////////////////////////////
//
// This file contains classes that are helfpul in some way.
//
// Created: Chip Audette, Oct 2013 - Dec 2014
//
/////////////////////////////////////
class DataPacket_ADS1299 {
int sampleIndex;
int[] values;
int[] auxValues;
//constructor, give it "nValues", which should match the number of values in the
//data payload in each data packet from the Arduino. This is likely to be at least
//the number of EEG channels in the OpenBCI system (ie, 8 channels if a single OpenBCI
//board) plus whatever auxiliary data the Arduino is sending.
DataPacket_ADS1299(int nValues, int nAuxValues) {
values = new int[nValues];
auxValues = new int[nAuxValues];
}
int printToConsole() {
print("printToConsole: DataPacket = ");
print(sampleIndex);
for (int i=0; i < values.length; i++) {
print(", " + values[i]);
}
for (int i=0; i < auxValues.length; i++) {
print(", " + auxValues[i]);
}
println();
return 0;
}
int copyTo(DataPacket_ADS1299 target) { return copyTo(target, 0, 0); }
int copyTo(DataPacket_ADS1299 target, int target_startInd_values, int target_startInd_aux) {
target.sampleIndex = sampleIndex;
return copyValuesAndAuxTo(target, target_startInd_values, target_startInd_aux);
}
int copyValuesAndAuxTo(DataPacket_ADS1299 target, int target_startInd_values, int target_startInd_aux) {
int nvalues = values.length;
for (int i=0; i < nvalues; i++) {
target.values[target_startInd_values + i] = values[i];
}
nvalues = auxValues.length;
for (int i=0; i < nvalues; i++) {
target.auxValues[target_startInd_aux + i] = auxValues[i];
}
return 0;
}
};
class DataStatus {
public boolean is_railed;
private int threshold_railed;
public boolean is_railed_warn;
private int threshold_railed_warn;
DataStatus(int thresh_railed, int thresh_railed_warn) {
is_railed = false;
threshold_railed = thresh_railed;
is_railed_warn = false;
threshold_railed_warn = thresh_railed_warn;
}
public void update(int data_value) {
is_railed = false;
if (abs(data_value) >= threshold_railed) is_railed = true;
is_railed_warn = false;
if (abs(data_value) >= threshold_railed_warn) is_railed_warn = true;
}
};
public class FilterConstants {
public double[] a;
public double[] b;
public String name;
public String short_name;
FilterConstants(double[] b_given, double[] a_given, String name_given, String short_name_given) {
b = new double[b_given.length];a = new double[b_given.length];
for (int i=0; i<b.length;i++) { b[i] = b_given[i];}
for (int i=0; i<a.length;i++) { a[i] = a_given[i];}
name = name_given;
short_name = short_name_given;
}
}
public class DetectionData_FreqDomain {
public float inband_uV = 0.0f;
public float inband_freq_Hz = 0.0f;
public float guard_uV = 0.0f;
public float thresh_uV = 0.0f;
public boolean isDetected = false;
DetectionData_FreqDomain() {
}
};
public class GraphDataPoint {
public double x;
public double y;
public String x_units;
public String y_units;
};
class PlotFontInfo {
String fontName = "fonts/Raleway-Regular.otf";
int axisLabel_size = 16;
int tickLabel_size = 14;
int buttonLabel_size = 12;
};
public class TextBox {
public int x, y;
public color textColor;
public color backgroundColor;
private PFont font;
private int fontSize;
public String string;
public boolean drawBackground;
public int backgroundEdge_pixels;
public int alignH,alignV;
// textBox(String s,int x1,int y1) {
// textBox(s,x1,y1,0);
// }
TextBox(String s, int x1, int y1) {
string = s; x = x1; y = y1;
backgroundColor = color(255,255,255);
textColor = color(0,0,0);
fontSize = 12;
font = createFont("Arial",fontSize);
backgroundEdge_pixels = 1;
drawBackground = false;
alignH = LEFT;
alignV = BOTTOM;
}
public void setFontSize(int size) {
fontSize = size;
font = createFont("fonts/Raleway-SemiBold.otf",fontSize);
}
public void draw() {
//define text
noStroke();
textFont(font);
//draw the box behind the text
if (drawBackground == true) {
int w = int(round(textWidth(string)));
int xbox = x - backgroundEdge_pixels;
switch (alignH) {
case LEFT:
xbox = x - backgroundEdge_pixels;
break;
case RIGHT:
xbox = x - w - backgroundEdge_pixels;
break;
case CENTER:
xbox = x - int(round(w/2.0)) - backgroundEdge_pixels;
break;
}
w = w + 2*backgroundEdge_pixels;
int h = int(textAscent())+2*backgroundEdge_pixels;
int ybox = y - int(round(textAscent())) - backgroundEdge_pixels -2;
fill(backgroundColor);
rect(xbox,ybox,w,h);
}
//draw the text itself
fill(textColor);
textAlign(alignH,alignV);
text(string,x,y);
strokeWeight(1);
}
};
Arquivo executável
+166
Ver Arquivo
@@ -0,0 +1,166 @@
//compute the standard deviation
float std(float[] data) {
//calc mean
float ave = mean(data);
//calc sum of squares relative to mean
float val = 0;
for (int i=0; i < data.length; i++) {
val += pow(data[i]-ave,2);
}
// divide by n to make it the average
val /= data.length;
//take square-root and return the standard
return (float)Math.sqrt(val);
}
float mean(float[] data) {
return mean(data,data.length);
}
int medianDestructive(int[] data) {
sort(data);
int midPoint = data.length / 2;
return data[midPoint];
}
//////////////////////////////////////////////////
//
// Some functions to implement some math and some filtering. These functions
// probably already exist in Java somewhere, but it was easier for me to just
// recreate them myself as I needed them.
//
// Created: Chip Audette, Oct 2013
//
//////////////////////////////////////////////////
int findMax(float[] data) {
float maxVal = data[0];
int maxInd = 0;
for (int I=1; I<data.length; I++) {
if (data[I] > maxVal) {
maxVal = data[I];
maxInd = I;
}
}
return maxInd;
}
float mean(float[] data, int Nback) {
return sum(data,Nback)/Nback;
}
float sum(float[] data) {
return sum(data, data.length);
}
float sum(float[] data, int Nback) {
float sum = 0;
if (Nback > 0) {
for (int i=(data.length)-Nback; i < data.length; i++) {
sum += data[i];
}
}
return sum;
}
float calcDotProduct(float[] data1, float[] data2) {
int len = min(data1.length, data2.length);
float val=0.0;
for (int I=0;I<len;I++) {
val+=data1[I]*data2[I];
}
return val;
}
float log10(float val) {
return (float)Math.log10(val);
}
float filterWEA_1stOrderIIR(float[] filty, float learn_fac, float filt_state) {
float prev = filt_state;
for (int i=0; i < filty.length; i++) {
filty[i] = prev*(1-learn_fac) + filty[i]*learn_fac;
prev = filty[i]; //save for next time
}
return prev;
}
void filterIIR(double[] filt_b, double[] filt_a, float[] data) {
int Nback = filt_b.length;
double[] prev_y = new double[Nback];
double[] prev_x = new double[Nback];
//step through data points
for (int i = 0; i < data.length; i++) {
//shift the previous outputs
for (int j = Nback-1; j > 0; j--) {
prev_y[j] = prev_y[j-1];
prev_x[j] = prev_x[j-1];
}
//add in the new point
prev_x[0] = data[i];
//compute the new data point
double out = 0;
for (int j = 0; j < Nback; j++) {
out += filt_b[j]*prev_x[j];
if (j > 0) {
out -= filt_a[j]*prev_y[j];
}
}
//save output value
prev_y[0] = out;
data[i] = (float)out;
}
}
void removeMean(float[] filty, int Nback) {
float meanVal = mean(filty,Nback);
for (int i=0; i < filty.length; i++) {
filty[i] -= meanVal;
}
}
void rereferenceTheMontage(float[][] data) {
int n_chan = data.length;
int n_points = data[0].length;
float sum, mean;
//loop over all data points
for (int Ipoint=0;Ipoint<n_points;Ipoint++) {
//compute mean signal right now
sum=0.0;
for (int Ichan=0;Ichan<n_chan;Ichan++) sum += data[Ichan][Ipoint];
mean = sum / n_chan;
//remove the mean signal from all channels
for (int Ichan=0;Ichan<n_chan;Ichan++) data[Ichan][Ipoint] -= mean;
}
}
class RunningMean {
private float[] values;
private int cur_ind = 0;
RunningMean(int N) {
values = new float[N];
cur_ind = 0;
}
public void addValue(float val) {
values[cur_ind] = val;
cur_ind = (cur_ind + 1) % values.length;
}
public float calcMean() {
return mean(values);
}
};
+7764
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff