Switch-a-roo w/ local updates

Esse commit está contido em:
Conor Russomanno
2016-07-22 10:03:21 -04:00
commit 9a8caf158f
1718 arquivos alterados com 297554 adições e 951959 exclusões
-17
Ver Arquivo
@@ -1,17 +0,0 @@
<?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>
-648
Ver Arquivo
@@ -1,648 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// Channel Controller
// - responsible for addressing channel data (Ganglion 1-4, 32bit
// - 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)
//
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//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];
//Channel Colors -- Defaulted to matching the OpenBCI electrode ribbon cable
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)
};
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
public void updateChannelArrays(int _nchan) {
channelSettingValues = new char [_nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
impedanceCheckValues = new char [_nchan][2];
}
//activateChannel: Ichan is [0 nchan-1] (aka zero referenced)
void activateChannel(int Ichan) {
println("OpenBCI_GUI: activating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL || eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, true); //activate
}
}
if (Ichan < gui.chanButtons.length) {
channelSettingValues[Ichan][0] = '0';
gui.cc.update();
}
}
void deactivateChannel(int Ichan) {
println("OpenBCI_GUI: deactivating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL || eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, false); //de-activate
}
}
if (Ichan < gui.chanButtons.length) {
channelSettingValues[Ichan][0] = '1';
gui.cc.update();
}
}
//Ichan is zero referenced (not one referenced)
boolean isChannelActive(int Ichan) {
boolean return_val = false;
if (channelSettingValues[Ichan][0] == '1') {
return_val = false;
} else {
return_val = true;
}
return return_val;
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
}
}
};
-267
Ver Arquivo
@@ -1,267 +0,0 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This code is used for GUI-wide spacing. It defines the GUI layout as a grid
// with the following design:
//
// The #s shown below fall at the center of their corresponding container[].
// Ex: container[1] is the upper left corner of the large rectangle between [0] & [10]
// Ex 2: container[6] is the entire right half of the same rectangle.
//
// ------------------------------------------------
// | [0] |
// ------------------------------------------------
// | | |
// | [1] [2] [3] |
// | | |
// |---------[4]----------[5]---------[6]---------|
// | | |
// | [7] [8] [9] |
// | | |
// ------------------------------------------------
// | [10] |
// ------------------------------------------------
//
// Created by: Conor Russomanno (May 2016)
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
boolean drawContainers = true;
Container[] container = new Container[11];
// Container container0;
// Container container1;
// Container container2;
// Container container3;
// Container container4;
// Container container5;
// Container container6;
// Container container7;
// Container container8;
// Container container9;
// Container container10;
//Container container11;
//Container container12;
//Viz extends container (example below)
//Viz viz1;
//Viz viz2;
int widthOfLastScreen_C = 0;
int heightOfLastScreen_C = 0;
void setupContainers() {
//size(1024, 768, P2D);
//frameRate(30);
//smooth();
//surface.setResizable(true);
widthOfLastScreen_C = width;
heightOfLastScreen_C = height;
int topNav_h = 32; //tie this to a global variable or one attached to GUI_Manager
int bottomNav_h = 30; //same
int leftNav_w = 0; //not used currently, maybe if we add a left-side tool bar
int rightNav_w = 0; //not used currently
container[0] = new Container(0, 0, width, topNav_h, 0);
container[5] = new Container(0, topNav_h, width, height - (topNav_h + bottomNav_h), 10);
container[1] = new Container(container[5], "TOP_LEFT");
container[2] = new Container(container[5], "TOP");
container[3] = new Container(container[5], "TOP_RIGHT");
container[4] = new Container(container[5], "LEFT");
container[6] = new Container(container[5], "RIGHT");
container[7] = new Container(container[5], "BOTTOM_LEFT");
container[8] = new Container(container[5], "BOTTOM");
container[9] = new Container(container[5], "BOTTOM_RIGHT");
container[10] = new Container(0, height - bottomNav_h, width, 50, 0);
//container11 = new Container(container1, "LEFT");
//container12 = new Container(container1, "RIGHT");
//setup viz objects... example of container extension (more below)
//setupVizs();
}
void drawContainers() {
//background(255);
for(int i = 0; i < container.length; i++){
container[i].draw();
}
//container11.draw();
//container12.draw();
//Draw viz objects.. exampl extension of container class (more below)
//viz1.draw();
//viz2.draw();
//alternative component listener function (line 177 - 187 frame.addComponentListener) for processing 3,
if (widthOfLastScreen_C != width || heightOfLastScreen_C != height) {
println("OpenBCI_GUI: setup: RESIZED");
//screenHasBeenResized = true;
//timeOfLastScreenResize = millis();
setupContainers();
//setupVizs(); //container extension example (more below)
widthOfLastScreen = width;
heightOfLastScreen = height;
}
}
public class Container {
//key Container Variables
public float x0, y0, w0, h0; //true dimensions.. without margins
public float x, y, w, h; //dimensions with margins
public float margin; //margin
//constructor 1 -- comprehensive
public Container(float _x0, float _y0, float _w0, float _h0, float _margin) {
margin = _margin;
x0 = _x0;
y0 = _y0;
w0 = _w0;
h0 = _h0;
x = x0 + margin;
y = y0 + margin;
w = w0 - margin*2;
h = h0 - margin*2;
}
//constructor 2 -- recursive constructor -- for quickly building sub-containers based on a super container (aka master)
public Container(Container master, String _type) {
margin = master.margin;
if(_type == "WHOLE"){
x0 = master.x0;
y0 = master.y0;
w0 = master.w0;
h0 = master.h0;
w = master.w;
h = master.h;
x = master.x;
y = master.y;
} else if (_type == "LEFT") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0;
w = (master.w - margin)/2;
h = master.h;
x = master.x;
y = master.y;
} else if (_type == "RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0;
w = (master.w - margin)/2;
h = master.h;
x = master.x + w + margin;
y = master.y;
} else if (_type == "TOP") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0;
h0 = master.h0/2;
w = master.w;
h = (master.h - margin)/2;
x = master.x;
y = master.y;
} else if (_type == "BOTTOM") {
x0 = master.x0;
y0 = master.y0 + master.h0/2;
w0 = master.w0;
h0 = master.h0/2;
w = master.w;
h = (master.h - margin)/2;
x = master.x;
y = master.y + h + margin;
} else if (_type == "TOP_LEFT") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x;
y = master.y;
} else if (_type == "TOP_RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x + w + margin;
y = master.y;
} else if (_type == "BOTTOM_LEFT") {
x0 = master.x0;
y0 = master.y0 + master.h0/2;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x;
y = master.y + h + margin;
} else if (_type == "BOTTOM_RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0 + master.h0/2;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x + w + margin;
y = master.y + h + margin;
}
}
public void draw() {
if(drawContainers){
pushStyle();
//draw margin area
fill(102, 255, 71, 100);
noStroke();
rect(x0, y0, w0, h0);
//noFill();
//stroke(255, 0, 0);
//rect(x0, y0, w0, h0);
fill(31, 69, 110, 100);
noStroke();
rect(x, y, w, h);
popStyle();
}
}
};
// --- EXAMPLE OF EXTENDING THE CONTAINER --- //
//public class Viz extends Container {
// public float abc;
// public Viz(float _abc, Container master) {
// super(master, "WHOLE");
// abc = _abc;
// }
// void draw() {
// pushStyle();
// noStroke();
// fill(255, 0, 0, 50);
// rect(x, y, w, h);
// popStyle();
// }
//};
//void setupVizs() {
// viz1 = new Viz (10f, container2);
// viz2 = new Viz (10f, container4);
//}
// --- END OF EXAMPLE OF EXTENDING THE CONTAINER --- //
-992
Ver Arquivo
@@ -1,992 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// 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.*;
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
ControlPanel controlPanel;
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;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
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);
}
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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);
if(hasIntroAnimation){
isOpen = false;
} else {
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;
}
};
//==============================================================================//
// 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();
}
};
//===================== 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;
}
//=======================================================================================================================================
//
// MenuList Class
//
//The MenuList class is implemented by the Control Panel. It allows you to set up a list of selectable items within a fixed rectangle size
//Currently used for Serial/COM select, SD settings, and System Mode
//
//=======================================================================================================================================
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);
}
};
-354
Ver Arquivo
@@ -1,354 +0,0 @@
////////////////////////////////////////////////////////////
// Class: OutputFile_rawtxt
// Purpose: handle file creation and writing for the text log file
// Created: Chip Audette May 2, 2014
////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void openNewLogFile(String _fileName) {
//close the file if it's open
if (fileoutput != null) {
println("OpenBCI_GUI: closing log file");
closeLogFile();
}
//open the new file
fileoutput = new OutputFile_rawtxt(openBCI.get_fs_Hz(), _fileName);
output_fname = fileoutput.fname;
println("openBCI: openNewLogFile: opened output file: " + output_fname);
output("openBCI: openNewLogFile: opened output file: " + output_fname);
}
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();
}
}
void closeLogFile() {
if (fileoutput != null) fileoutput.closeFile();
}
void fileSelected(File selection) { //called by the Open File dialog box after a file has been selected
if (selection == null) {
println("fileSelected: no selection so far...");
} else {
//inputFile = selection;
playbackData_fname = selection.getAbsolutePath();
}
}
String getDateString() {
String fname = year() + "-";
if (month() < 10) fname=fname+"0";
fname = fname + month() + "-";
if (day() < 10) fname = fname + "0";
fname = fname + day();
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();
return fname;
}
//these functions are relevant to convertSDFile
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();
}
}
//------------------------------------------------------------------------
// CLASSES
//------------------------------------------------------------------------
//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);
//
///////////////////////////////////////////////////////////////
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);
}
}
}
//////////////////////////////////
//
// This collection of functions/methods - convertSDFile, createPlaybackFileFromSD, & sdFileSelected - 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();
}
}
}
-453
Ver Arquivo
@@ -1,453 +0,0 @@
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
DataProcessing dataProcessing;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//called from systemUpdate when mode=10 and isRunning = true
int getDataIfAvailable(int pointCounter) {
if ( (eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX) ) {
//get data from serial port as it streams in
//next, gather any new data into the "little buffer"
while ( (curDataPacketInd != lastReadDataPacketInd) && (pointCounter < nPointsPerUpdate)) {
lastReadDataPacketInd = (lastReadDataPacketInd+1) % dataPacketBuff.length; //increment to read the next packet
for (int Ichan=0; Ichan < nchan; Ichan++) { //loop over each cahnnel
//scale the data into engineering units ("microvolts") and save to the "little buffer"
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan] * openBCI.get_scale_fac_uVolts_per_count();
}
pointCounter++; //increment counter for "little buffer"
}
} else {
// make or load data to simulate real time
//has enough time passed?
int current_millis = millis();
if (current_millis >= nextPlayback_millis) {
//prepare for next time
int increment_millis = int(round(float(nPointsPerUpdate)*1000.f/openBCI.get_fs_Hz())/playback_speed_fac);
if (nextPlayback_millis < 0) nextPlayback_millis = current_millis;
nextPlayback_millis += increment_millis;
// generate or read the data
lastReadDataPacketInd = 0;
for (int i = 0; i < nPointsPerUpdate; i++) {
// println();
dataPacketBuff[lastReadDataPacketInd].sampleIndex++;
switch (eegDataSource) {
case DATASOURCE_SYNTHETIC: //use synthetic data (for GUI debugging)
synthesizeData(nchan, openBCI.get_fs_Hz(), openBCI.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
case DATASOURCE_PLAYBACKFILE:
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, openBCI.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
default:
//no action
}
//gather the data into the "little buffer"
for (int Ichan=0; Ichan < nchan; Ichan++) {
//scale the data into engineering units..."microvolts"
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan]* openBCI.get_scale_fac_uVolts_per_count();
}
pointCounter++;
} //close the loop over data points
//if (eegDataSource==DATASOURCE_PLAYBACKFILE) println("OpenBCI_GUI: getDataIfAvailable: currentTableRowIndex = " + currentTableRowIndex);
//println("OpenBCI_GUI: getDataIfAvailable: pointCounter = " + pointCounter);
} // close "has enough time passed"
}
return pointCounter;
}
RunningMean avgBitRate = new RunningMean(10); //10 point running average...at 5 points per second, this should be 2 second running average
void processNewData() {
//compute instantaneous byte rate
float inst_byteRate_perSec = (int)(1000.f * ((float)(openBCI_byteCount - prevBytes)) / ((float)(millis() - prevMillis)));
prevMillis=millis(); //store for next time
prevBytes = openBCI_byteCount; //store for next time
//compute smoothed byte rate
avgBitRate.addValue(inst_byteRate_perSec);
byteRate_perSec = (int)avgBitRate.calcMean();
//prepare to update the data buffers
float foo_val;
float prevFFTdata[] = new float[fftBuff[0].specSize()];
double foo;
//update the data buffers
for (int Ichan=0; Ichan < nchan; Ichan++) {
//append the new data to the larger data buffer...because we want the plotting routines
//to show more than just the most recent chunk of data. This will be our "raw" data.
appendAndShift(dataBuffY_uV[Ichan], yLittleBuff_uV[Ichan]);
//make a copy of the data that we'll apply processing to. This will be what is displayed on the full montage
dataBuffY_filtY_uV[Ichan] = dataBuffY_uV[Ichan].clone();
}
//if you want to, re-reference the montage to make it be a mean-head reference
if (false) rereferenceTheMontage(dataBuffY_filtY_uV);
//update the FFT (frequency spectrum)
for (int Ichan=0; Ichan < nchan; Ichan++) {
//copy the previous FFT data...enables us to apply some smoothing to the FFT data
for (int I=0; I < fftBuff[Ichan].specSize(); I++) prevFFTdata[I] = fftBuff[Ichan].getBand(I); //copy the old spectrum values
//prepare the data for the new FFT
float[] fooData_raw = dataBuffY_uV[Ichan]; //use the raw data for the FFT
fooData_raw = Arrays.copyOfRange(fooData_raw, fooData_raw.length-Nfft, fooData_raw.length); //trim to grab just the most recent block of data
float meanData = mean(fooData_raw); //compute the mean
for (int I=0; I < fooData_raw.length; I++) fooData_raw[I] -= meanData; //remove the mean (for a better looking FFT
//compute the FFT
fftBuff[Ichan].forward(fooData_raw); //compute FFT on this channel of data
//convert to uV_per_bin...still need to confirm the accuracy of this code.
//Do we need to account for the power lost in the windowing function? CHIP 2014-10-24
for (int I=0; I < fftBuff[Ichan].specSize(); I++) { //loop over each FFT bin
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) / fftBuff[Ichan].specSize()));
}
//average the FFT with previous FFT data so that it makes it smoother in time
double min_val = 0.01d;
for (int I=0; I < fftBuff[Ichan].specSize(); I++) { //loop over each fft bin
if (prevFFTdata[I] < min_val) prevFFTdata[I] = (float)min_val; //make sure we're not too small for the log calls
foo = fftBuff[Ichan].getBand(I);
if (foo < min_val) foo = min_val; //make sure this value isn't too small
if (true) {
//smooth in dB power space
foo = (1.0d-smoothFac[smoothFac_ind]) * java.lang.Math.log(java.lang.Math.pow(foo, 2));
foo += smoothFac[smoothFac_ind] * java.lang.Math.log(java.lang.Math.pow((double)prevFFTdata[I], 2));
foo = java.lang.Math.sqrt(java.lang.Math.exp(foo)); //average in dB space
} else {
//smooth (average) in linear power space
foo = (1.0d-smoothFac[smoothFac_ind]) * java.lang.Math.pow(foo, 2);
foo+= smoothFac[smoothFac_ind] * java.lang.Math.pow((double)prevFFTdata[I], 2);
// take sqrt to be back into uV_rtHz
foo = java.lang.Math.sqrt(foo);
}
fftBuff[Ichan].setBand(I, (float)foo); //put the smoothed data back into the fftBuff data holder for use by everyone else
} //end loop over FFT bins
} //end the loop over channels.
//apply additional processing for the time-domain montage plot (ie, filtering)
dataProcessing.process(yLittleBuff_uV, dataBuffY_uV, dataBuffY_filtY_uV, fftBuff);
//apply user processing
// ...yLittleBuff_uV[Ichan] is the most recent raw data since the last call to this processing routine
// ...dataBuffY_filtY_uV[Ichan] is the full set of filtered data as shown in the time-domain plot in the GUI
// ...fftBuff[Ichan] is the FFT data structure holding the frequency spectrum as shown in the freq-domain plot in the GUI
dataProcessing_user.process(yLittleBuff_uV, dataBuffY_uV, dataBuffY_filtY_uV, fftBuff);
//look to see if the latest data is railed so that we can notify the user on the GUI
for (int Ichan=0; Ichan < nchan; Ichan++) is_railed[Ichan].update(dataPacketBuff[lastReadDataPacketInd].values[Ichan]);
//compute the electrode impedance. Do it in a very simple way [rms to amplitude, then uVolt to Volt, then Volt/Amp to Ohm]
for (int Ichan=0; Ichan < nchan; Ichan++) data_elec_imp_ohm[Ichan] = (sqrt(2.0)*dataProcessing.data_std_uV[Ichan]*1.0e-6) / openBCI.get_leadOffDrive_amps();
}
//helper function in handling the EEG data
void appendAndShift(float[] data, float[] newData) {
int nshift = newData.length;
int end = data.length-nshift;
for (int i=0; i < end; i++) {
data[i]=data[i+nshift]; //shift data points down by 1
}
for (int i=0; i<nshift; i++) {
data[end+i] = newData[i]; //append new data
}
}
final float sine_freq_Hz = 10.0f;
float[] sine_phase_rad = new float[nchan];
void synthesizeData(int nchan, float fs_Hz, float scale_fac_uVolts_per_count, DataPacket_ADS1299 curDataPacket) {
float val_uV;
for (int Ichan=0; Ichan < nchan; Ichan++) {
if (isChannelActive(Ichan)) {
val_uV = randomGaussian()*sqrt(fs_Hz/2.0f); // ensures that it has amplitude of one unit per sqrt(Hz) of signal bandwidth
//val_uV = random(1)*sqrt(fs_Hz/2.0f); // ensures that it has amplitude of one unit per sqrt(Hz) of signal bandwidth
if (Ichan==0) val_uV*= 10f; //scale one channel higher
if (Ichan==1) {
//add sine wave at 10 Hz at 10 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * sine_freq_Hz / fs_Hz;
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 10.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]);
} else if (Ichan==2) {
//50 Hz interference at 50 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * 50.0f / fs_Hz; //60 Hz
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 50.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]); //20 uVrms
} else if (Ichan==3) {
//60 Hz interference at 50 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * 60.0f / fs_Hz; //50 Hz
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 50.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]); //20 uVrms
}
} else {
val_uV = 0.0f;
}
curDataPacket.values[Ichan] = (int) (0.5f+ val_uV / scale_fac_uVolts_per_count); //convert to counts, the 0.5 is to ensure rounding
}
}
//some data initialization routines
void prepareData(float[] dataBuffX, float[][] dataBuffY_uV, float fs_Hz) {
//initialize the x and y data
int xoffset = dataBuffX.length - 1;
for (int i=0; i < dataBuffX.length; i++) {
dataBuffX[i] = ((float)(i-xoffset)) / fs_Hz; //x data goes from minus time up to zero
for (int Ichan = 0; Ichan < nchan; Ichan++) {
dataBuffY_uV[Ichan][i] = 0f; //make the y data all zeros
}
}
}
void initializeFFTObjects(FFT[] fftBuff, float[][] dataBuffY_uV, int N, float fs_Hz) {
float[] fooData;
for (int Ichan=0; Ichan < nchan; Ichan++) {
//make the FFT objects...Following "SoundSpectrum" example that came with the Minim library
//fftBuff[Ichan] = new FFT(Nfft, fs_Hz); //I can't have this here...it must be in setup
fftBuff[Ichan].window(FFT.HAMMING);
//do the FFT on the initial data
fooData = dataBuffY_uV[Ichan];
fooData = Arrays.copyOfRange(fooData, fooData.length-Nfft, fooData.length);
fftBuff[Ichan].forward(fooData); //compute FFT on this channel of data
}
}
int getPlaybackDataFromTable(Table datatable, int currentTableRowIndex, float scale_fac_uVolts_per_count, DataPacket_ADS1299 curDataPacket) {
float val_uV = 0.0f;
//check to see if we can load a value from the table
if (currentTableRowIndex >= datatable.getRowCount()) {
//end of file
println("OpenBCI_GUI: getPlaybackDataFromTable: hit the end of the playback data file. starting over...");
//if (isRunning) stopRunning();
currentTableRowIndex = 0;
} else {
//get the row
TableRow row = datatable.getRow(currentTableRowIndex);
currentTableRowIndex++; //increment to the next row
//get each value
for (int Ichan=0; Ichan < nchan; Ichan++) {
if (isChannelActive(Ichan) && (Ichan < datatable.getColumnCount())) {
val_uV = row.getFloat(Ichan);
} else {
//use zeros for the missing channels
val_uV = 0.0f;
}
//put into data structure
curDataPacket.values[Ichan] = (int) (0.5f+ val_uV / scale_fac_uVolts_per_count); //convert to counts, the 0.5 is to ensure rounding
}
}
return currentTableRowIndex;
}
//------------------------------------------------------------------------
// CLASSES
//------------------------------------------------------------------------
class DataProcessing {
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[];
DataProcessing(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;
}
}
}
};
@@ -1,532 +0,0 @@
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
DataProcessing_User dataProcessing_user;
boolean drawEMG = false; //if true... toggles on EEG_Processing_User.draw and toggles off the headplot in Gui_Manager
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
class DataProcessing_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 = 3 - 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
DataProcessing_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 bio 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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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);
}
}
}
};
-116
Ver Arquivo
@@ -1,116 +0,0 @@
//////////////////////////////////////
//
// This file contains classes that are helpful for debugging, as well as the HelpWidget,
// which is used to give feedback to the GUI user in the small text window at the bottom of the GUI
//
// Created: Conor Russomanno, June 2016
// Based on code: Chip Audette, Oct 2013 - Dec 2014
//
//
/////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//set true if you want more verbosity in console.. verbosePrint("print_this_thing") is used to output feedback when isVerbose = true
boolean isVerbose = false;
//Help Widget initiation
HelpWidget helpWidget;
//use signPost(String identifier) to print 'identifier' text and time since last signPost() for debugging latency/timing issues
boolean printSignPosts = false;
float millisOfLastSignPost = 0.0;
float millisSinceLastSignPost = 0.0;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void verbosePrint(String _string) {
if (isVerbose) {
println(_string);
}
}
void delay(int delay)
{
int time = millis();
while (millis() - time <= delay);
}
//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() {
//nothing needed here
}
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);
}
// created 2/10/16 by Conor Russomanno to dissect the aspects of the GUI that are slowing it down
// here I will create methods used to identify where there are inefficiencies in the code
// note to self: make sure to check the frameRate() in setup... switched from 16 to 30... working much faster now... still a useful method below.
// -------------------------------------------------------------- START -------------------------------------------------------------------------------
//method for printing out an ["indentifier"][millisSinceLastSignPost] for debugging purposes... allows us to look at what is taking too long.
void signPost(String identifier) {
if (printSignPosts) {
millisSinceLastSignPost = millis() - millisOfLastSignPost;
println("SIGN POST: [" + identifier + "][" + millisSinceLastSignPost + "]");
millisOfLastSignPost = millis();
}
}
// ---------------------------------------------------------------- FINISH -----------------------------------------------------------------------------
-370
Ver Arquivo
@@ -1,370 +0,0 @@
//////////////////////////////////////
//
// This file contains classes that are helfpul in some way.
// Created: Chip Audette, Oct 2013 - Dec 2014
//
/////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//////////////////////////////////////////////////
//
// Formerly, Math.pde
// - std
// - mean
// - medianDestructive
// - findMax
// - mean
// - sum
// - CalcDotProduct
// - log10
// - filterWEA_1stOrderIIR
// - filterIIR
// - removeMean
// - rereferenceTheMontage
// - CLASS RunningMean
//
// Created: Chip Audette, Oct 2013
//
//////////////////////////////////////////////////
//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;
}
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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);
}
};
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;
}
};
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;
}
};
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() {
}
};
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;
};
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);
}
};
-975
Ver Arquivo
@@ -1,975 +0,0 @@
////////////////////////////////////////////////////
//
// 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()
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//GUI plotting constants
GUI_Manager gui;
color bgColor = color(1, 18, 41);
int navBarHeight = 32;
float default_vertScale_uV = 200.0f; //used for vertical scale of time-domain montage plot and frequency-domain FFT plot
float displayTime_sec = 5f; //define how much time is shown on the time-domain montage plot (and how much is used in the FFT plot?)
float dataBuff_len_sec = displayTime_sec + 3f; //needs to be wider than actual display so that filter startup is hidden
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void initializeGUI() {
verbosePrint("OpenBCI_GUI: initializeGUI: Starting...");
String filterDescription = dataProcessing.getFilterDescription(); verbosePrint("OpenBCI_GUI: initializeGUI: 2");
gui = new GUI_Manager(this, win_x, win_y, nchan, displayTime_sec, default_vertScale_uV, filterDescription, smoothFac[smoothFac_ind]); verbosePrint("OpenBCI_GUI: initializeGUI: 3");
//associate the data to the GUI traces
gui.initDataTraces(dataBuffX, dataBuffY_filtY_uV, fftBuff, dataProcessing.data_std_uV, is_railed, dataProcessing.polarity); verbosePrint("OpenBCI_GUI: initializeGUI: 4");
//limit how much data is plotted...hopefully to speed things up a little
gui.setDoNotPlotOutsideXlim(true); verbosePrint("OpenBCI_GUI: initializeGUI: 5");
gui.setDecimateFactor(2); verbosePrint("OpenBCI_GUI: initializeGUI: Done.");
}
void incrementFilterConfiguration() {
dataProcessing.incrementFilterConfiguration();
//update the button strings
gui.filtBPButton.but_txt = "BP Filt\n" + dataProcessing.getShortFilterDescription();
gui.titleMontage.string = "EEG Data (" + dataProcessing.getFilterDescription() + ")";
}
void incrementNotchConfiguration() {
dataProcessing.incrementNotchConfiguration();
//update the button strings
gui.filtNotchButton.but_txt = "Notch\n" + dataProcessing.getShortNotchDescription();
gui.titleMontage.string = "EEG Data (" + dataProcessing.getFilterDescription() + ")";
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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.5f; //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,
left_right_split-gutter_left,
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,
(1f-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" + dataProcessing.getShortNotchDescription(),fontInfo.buttonLabel_size);
x = calcButtonXLocation(Ibut++, win_x, w, xoffset,gutter_between_buttons);
filtBPButton = new Button(x,y,w,h,"BP Filt\n" + dataProcessing.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);
stopButton.setColorNotPressed(color(184, 220, 105));
//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);
}
};
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-793
Ver Arquivo
@@ -1,793 +0,0 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//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 ',':
drawContainers = !drawContainers;
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...");
dataProcessing_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...");
dataProcessing_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;
}
}
//swtich yard if a click is detected
void mousePressed() {
verbosePrint("OpenBCI_GUI: mousePressed: mouse pressed");
//if not in initial setup...
if (systemMode >= 10) {
//limit interactivity of main GUI if control panel is open
if (controlPanel.isOpen == false) {
//was the stopButton pressed?
gui.mousePressed(); // trigger mousePressed function in GUI
//most of the logic below should be migrated into the GUI_Manager specific function above
if (gui.stopButton.isMouseHere()) {
gui.stopButton.setIsActive(true);
stopButtonWasPressed();
}
// //was the gui page button pressed?
// if (gui.guiPageButton.isMouseHere()) {
// gui.guiPageButton.setIsActive(true);
// gui.incrementGUIpage();
// }
//check the buttons
switch (gui.guiPage) {
case GUI_Manager.GUI_PAGE_CHANNEL_ONOFF:
//check the channel buttons
// for (int Ibut = 0; Ibut < gui.chanButtons.length; Ibut++) {
// if (gui.chanButtons[Ibut].isMouseHere()) {
// toggleChannelState(Ibut);
// }
// }
//check the detection button
//if (gui.detectButton.updateIsMouseHere()) toggleDetectionState();
//check spectrogram button
//if (gui.spectrogramButton.updateIsMouseHere()) toggleSpectrogramState();
break;
case GUI_Manager.GUI_PAGE_IMPEDANCE_CHECK:
// ============ DEPRECATED ============== //
// //check the impedance buttons
// for (int Ibut = 0; Ibut < gui.impedanceButtonsP.length; Ibut++) {
// if (gui.impedanceButtonsP[Ibut].isMouseHere()) {
// toggleChannelImpedanceState(gui.impedanceButtonsP[Ibut],Ibut,0);
// }
// if (gui.impedanceButtonsN[Ibut].isMouseHere()) {
// toggleChannelImpedanceState(gui.impedanceButtonsN[Ibut],Ibut,1);
// }
// }
// if (gui.biasButton.isMouseHere()) {
// gui.biasButton.setIsActive(true);
// setBiasState(!openBCI.isBiasAuto);
// }
// break;
case GUI_Manager.GUI_PAGE_HEADPLOT_SETUP:
if (gui.intensityFactorButton.isMouseHere()) {
gui.intensityFactorButton.setIsActive(true);
gui.incrementVertScaleFactor();
}
if (gui.loglinPlotButton.isMouseHere()) {
gui.loglinPlotButton.setIsActive(true);
gui.set_vertScaleAsLog(!gui.vertScaleAsLog); //toggle the state
}
if (gui.filtBPButton.isMouseHere()) {
gui.filtBPButton.setIsActive(true);
incrementFilterConfiguration();
}
if (gui.filtNotchButton.isMouseHere()) {
gui.filtNotchButton.setIsActive(true);
incrementNotchConfiguration();
}
if (gui.smoothingButton.isMouseHere()) {
gui.smoothingButton.setIsActive(true);
incrementSmoothing();
}
if (gui.showPolarityButton.isMouseHere()) {
gui.showPolarityButton.setIsActive(true);
toggleShowPolarity();
}
if (gui.maxDisplayFreqButton.isMouseHere()) {
gui.maxDisplayFreqButton.setIsActive(true);
gui.incrementMaxDisplayFreq();
}
break;
//default:
}
//check the graphs
if (gui.isMouseOnFFT(mouseX, mouseY)) {
GraphDataPoint dataPoint = new GraphDataPoint();
gui.getFFTdataPoint(mouseX, mouseY, dataPoint);
println("OpenBCI_GUI: FFT data point: " + String.format("%4.2f", dataPoint.x) + " " + dataPoint.x_units + ", " + String.format("%4.2f", dataPoint.y) + " " + dataPoint.y_units);
} else if (gui.headPlot1.isPixelInsideHead(mouseX, mouseY)) {
//toggle the head plot contours
gui.headPlot1.drawHeadAsContours = !gui.headPlot1.drawHeadAsContours;
} else if (gui.isMouseOnMontage(mouseX, mouseY)) {
//toggle the display of the montage values
gui.showMontageValues = !gui.showMontageValues;
}
}
}
//=============================//
// CONTROL PANEL INTERACTIVITY //
//=============================//
//was control panel button pushed
if (controlPanelCollapser.isMouseHere()) {
if (controlPanelCollapser.isActive && systemMode == 10) {
controlPanelCollapser.setIsActive(false);
controlPanel.isOpen = false;
} else {
controlPanelCollapser.setIsActive(true);
controlPanel.isOpen = true;
}
} else {
if (controlPanel.isOpen) {
controlPanel.CPmousePressed();
}
}
//interacting with control panel
if (controlPanel.isOpen) {
//close control panel if you click outside...
if (systemMode == 10) {
if (mouseX > 0 && mouseX < controlPanel.w && mouseY > 0 && mouseY < controlPanel.initBox.y+controlPanel.initBox.h) {
println("OpenBCI_GUI: mousePressed: clicked in CP box");
controlPanel.CPmousePressed();
}
//if clicked out of panel
else {
println("OpenBCI_GUI: mousePressed: outside of CP clicked");
controlPanel.isOpen = false;
controlPanelCollapser.setIsActive(false);
output("Press the \"Press to Start\" button to initialize the data stream.");
}
}
}
redrawScreenNow = true; //command a redraw of the GUI whenever the mouse is pressed
if (playground.isMouseHere()) {
playground.mousePressed();
}
if (playground.isMouseInButton()) {
playground.toggleWindow();
}
}
void mouseReleased() {
verbosePrint("OpenBCI_GUI: mouseReleased: mouse released");
//some buttons light up only when being actively pressed. Now that we've
//released the mouse button, turn off those buttons.
//interacting with control panel
if (controlPanel.isOpen) {
//if clicked in panel
controlPanel.CPmouseReleased();
}
if (systemMode >= 10) {
gui.mouseReleased();
redrawScreenNow = true; //command a redraw of the GUI whenever the mouse is released
}
if (screenHasBeenResized) {
println("OpenBCI_GUI: mouseReleased: screen has been resized...");
screenHasBeenResized = false;
}
//Playground Interactivity
if (playground.isMouseHere()) {
playground.mouseReleased();
}
if (playground.isMouseInButton()) {
// playground.toggleWindow();
}
}
void incrementSmoothing() {
smoothFac_ind++;
if (smoothFac_ind >= smoothFac.length) smoothFac_ind = 0;
//tell the GUI
gui.setSmoothFac(smoothFac[smoothFac_ind]);
//update the button
gui.smoothingButton.but_txt = "Smooth\n" + smoothFac[smoothFac_ind];
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Formerly Button.pde
// 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
@@ -1,583 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// GUI for controlling the ADS1299-based OpenBCI
//
// Created: Chip Audette, Oct 2013 - May 2014
// Modified: Conor Russomanno & Joel Murphy, August 2014 - Dec 2014
// Modified (v2.0): Conor Russomanno & Joel Murphy, June 2016
//
// Requires gwoptics graphing library for processing. Built on V0.5.0
// http://www.gwoptics.org/processing/gwoptics_p5lib/
//
// Requires ControlP5 library, but an older one. This will only work
// with the ControlP5 library that is included with this GitHub repository
//
// No warranty. Use at your own risk. Use for whatever you'd like.
//
////////////////////////////////////////////////////////////////////////////////
import ddf.minim.analysis.*; //for FFT
//import ddf.minim.*; // commented because too broad.. contains "Controller" class which is also contained in ControlP5... need to be more specific // To make sound. Following minim example "frequencyModulation"
import ddf.minim.ugens.*; // To make sound. Following minim example "frequencyModulation"
import java.lang.Math; //for exp, log, sqrt...they seem better than Processing's built-in
import processing.core.PApplet;
import java.util.*; //for Array.copyOfRange()
import java.util.Map.Entry;
import processing.serial.*; //for serial communication to Arduino/OpenBCI
import java.awt.event.*; //to allow for event listener on screen resize
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//used to switch between application states
int systemMode = -10; /* Modes: -10 = intro sequence; 0 = system stopped/control panel setings; 10 = gui; 20 = help guide */
boolean hasIntroAnimation = true;
PImage cog;
//choose where to get the EEG data
final int DATASOURCE_NORMAL = 3; //looking for signal from OpenBCI board via Serial/COM port, no Aux data
final int DATASOURCE_PLAYBACKFILE = 1; //playback from a pre-recorded text file
final int DATASOURCE_SYNTHETIC = 2; //Synthetically generated data
final int DATASOURCE_NORMAL_W_AUX = 0; // new default, data from serial with Accel data CHIP 2014-11-03
public int eegDataSource = -1; //default to none of the options
//here are variables that are used if loading input data from a CSV text file...double slash ("\\") is necessary to make a single slash
String playbackData_fname = "N/A"; //only used if loading input data from a file
// String playbackData_fname; //leave blank to cause an "Open File" dialog box to appear at startup. USEFUL!
float playback_speed_fac = 1.0f; //make 1.0 for real-time. larger for faster playback
int currentTableRowIndex = 0;
Table_CSV playbackData_table;
int nextPlayback_millis = -100; //any negative number
//Global Serial/COM communications constants
OpenBCI_ADS1299 openBCI = new OpenBCI_ADS1299(); //dummy creation to get access to constants, create real one later
String openBCI_portName = "N/A"; //starts as N/A but is selected from control panel to match your OpenBCI USB Dongle's serial/COM
int openBCI_baud = 115200; //baud rate from the Arduino
////// ---- Define variables related to OpenBCI board operations
//Define number of channels from openBCI...first EEG channels, then aux channels
int nchan = 8; //Normally, 8 or 16. Choose a smaller number to show fewer on the GUI
int n_aux_ifEnabled = 3; // this is the accelerometer data CHIP 2014-11-03
//define variables related to warnings to the user about whether the EEG data is nearly railed (and, therefore, of dubious quality)
DataStatus is_railed[];
final int threshold_railed = int(pow(2, 23)-1000); //fully railed should be +/- 2^23, so set this threshold close to that value
final int threshold_railed_warn = int(pow(2, 23)*0.75); //set a somewhat smaller value as the warning threshold
//OpenBCI SD Card setting (if eegDataSource == 0)
int sdSetting = 0; //0 = do not write; 1 = 5 min; 2 = 15 min; 3 = 30 min; etc...
String sdSettingString = "Do not write to SD";
//openBCI data packet
final int nDataBackBuff = 3*(int)openBCI.get_fs_Hz();
DataPacket_ADS1299 dataPacketBuff[] = new DataPacket_ADS1299[nDataBackBuff]; //allocate the array, but doesn't call constructor. Still need to call the constructor!
int curDataPacketInd = -1;
int lastReadDataPacketInd = -1;
//related to sync'ing communiction to OpenBCI hardware?
boolean currentlySyncing = false;
long timeOfLastCommand = 0;
////// ---- End variables related to the OpenBCI boards
// define some timing variables for this program's operation
long timeOfLastFrame = 0;
int newPacketCounter = 0;
long timeOfInit;
long timeSinceStopRunning = 1000;
int prev_time_millis = 0;
final int nPointsPerUpdate = 50; //update the GUI after this many data points have been received
//define some data fields for handling data here in processing
float dataBuffX[]; //define the size later
float dataBuffY_uV[][]; //2D array to handle multiple data channels, each row is a new channel so that dataBuffY[3][] is channel 4
float dataBuffY_filtY_uV[][];
float yLittleBuff[] = new float[nPointsPerUpdate];
float yLittleBuff_uV[][] = new float[nchan][nPointsPerUpdate]; //small buffer used to send data to the filters
float data_elec_imp_ohm[];
//variables for writing EEG data out to a file
OutputFile_rawtxt fileoutput;
String output_fname;
String fileName = "N/A";
// Serial output
String serial_output_portName = "/dev/tty.usbmodem1411"; //must edit this based on the name of the serial/COM port
Serial serial_output;
int serial_output_baud = 115200; //baud rate from the Arduino
//fft constants
int Nfft = 256; //set resolution of the FFT. Use N=256 for normal, N=512 for MU waves
FFT fftBuff[] = new FFT[nchan]; //from the minim library
float[] smoothFac = new float[]{0.75, 0.9, 0.95, 0.98, 0.0, 0.5};
int smoothFac_ind = 0; //initial index into the smoothFac array
//Control Panel for (re)configuring system settings
Button controlPanelCollapser;
PlotFontInfo fontInfo;
//program constants
boolean isRunning = false;
boolean redrawScreenNow = true;
int openBCI_byteCount = 0;
int inByte = -1; // Incoming serial data
//for screen resizing
boolean screenHasBeenResized = false;
float timeOfLastScreenResize = 0;
float timeOfGUIreinitialize = 0;
int reinitializeGUIdelay = 125;
//Tao's variabiles
int widthOfLastScreen = 0;
int heightOfLastScreen = 0;
//set window size
int win_x = 1024; //window width
int win_y = 768; //window height
PImage logo;
PFont f1;
PFont f2;
PFont f3;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//========================SETUP============================//
//========================SETUP============================//
//========================SETUP============================//
void setup() {
println("Welcome to the Processing-based OpenBCI GUI!"); //Welcome line.
println("Last update: 6/25/2016"); //Welcome line.
println("For more information about how to work with this code base, please visit: http://docs.openbci.com/tutorials/01-GettingStarted");
println("For specific questions, please post them to the Software section of the OpenBCI Forum: http://openbci.com/index.php/forum/#/categories/software");
//open window
size(1024, 768, P2D);
// size(displayWidth, displayHeight, P2D);
//if (frame != null) frame.setResizable(true); //make window resizable
//attach exit handler
//prepareExitHandler();
frameRate(120); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
smooth(); //turn this off if it's too slow
surface.setResizable(true); //updated from frame.setResizable in Processing 2
widthOfLastScreen = width; //for screen resizing (Thank's Tao)
heightOfLastScreen = height;
setupContainers();
//V1 FONTS
f1 = createFont("fonts/Raleway-SemiBold.otf", 16);
f2 = createFont("fonts/Raleway-Regular.otf", 15);
f3 = createFont("fonts/Raleway-SemiBold.otf", 15);
//V2 FONTS
//f1 = createFont("fonts/Montserrat-SemiBold.otf", 16);
//f2 = createFont("fonts/Montserrat-Light.otf", 15);
//f3 = createFont("fonts/Montserrat-SemiBold.otf", 15);
//listen for window resize ... used to adjust elements in application
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (e.getSource()==frame) {
println("OpenBCI_GUI: setup: RESIZED");
screenHasBeenResized = true;
timeOfLastScreenResize = millis();
// initializeGUI();
}
}
}
);
//set up controlPanelCollapser button
fontInfo = new PlotFontInfo();
helpWidget = new HelpWidget(0, win_y - 30, win_x, 30);
// println("..." + this);
// controlPanelCollapser = new Button(2, 2, 256, int((float)win_y*(0.03f)), "SYSTEM CONTROL PANEL", fontInfo.buttonLabel_size);
controlPanelCollapser = new Button(2, 2, 256, 26, "SYSTEM CONTROL PANEL", fontInfo.buttonLabel_size);
controlPanelCollapser.setIsActive(true);
controlPanelCollapser.makeDropdownButton(true);
//from the user's perspective, the program hangs out on the ControlPanel until the user presses "Start System".
print("Graphics & GUI Library: ");
controlPanel = new ControlPanel(this);
//The effect of "Start System" is that initSystem() gets called, which starts up the conneciton to the OpenBCI
//hardware (via the "updateSyncState()" process) as well as initializing the rest of the GUI elements.
//Once the hardware is synchronized, the main GUI is drawn and the user switches over to the main GUI.
logo = loadImage("logo2.png");
cog = loadImage("cog_1024x1024.png");
playground = new Playground(navBarHeight);
//attempt to open a serial port for "output"
try {
verbosePrint("OpenBCI_GUI.pde: attempting to open serial port for data output = " + serial_output_portName);
serial_output = new Serial(this, serial_output_portName, serial_output_baud); //open the com port
serial_output.clear(); // clear anything in the com port's buffer
}
catch (RuntimeException e) {
verbosePrint("OpenBCI_GUI.pde: *** ERROR ***: Could not open " + serial_output_portName);
}
myPresentation = new Presentation();
}
//====================== END-OF-SETUP ==========================//
//====================== END-OF-SETUP ==========================//
//====================== END-OF-SETUP ==========================//
//======================== DRAW LOOP =============================//
//======================== DRAW LOOP =============================//
//======================== DRAW LOOP =============================//
void draw() {
drawLoop_counter++; //signPost("10");
systemUpdate(); //signPost("20");
systemDraw(); //signPost("30");
}
//====================== END-OF-DRAW ==========================//
//====================== END-OF-DRAW ==========================//
//====================== END-OF-DRAW ==========================//
int pointCounter = 0;
int prevBytes = 0;
int prevMillis = millis();
int byteRate_perSec = 0;
int drawLoop_counter = 0;
//used to init system based on initial settings...Called from the "Start System" button in the GUI's ControlPanel
void initSystem() {
verbosePrint("OpenBCI_GUI: initSystem: -- Init 0 --");
timeOfInit = millis(); //store this for timeout in case init takes too long
//prepare data variables
verbosePrint("OpenBCI_GUI: initSystem: Preparing data variables...");
dataBuffX = new float[(int)(dataBuff_len_sec * openBCI.get_fs_Hz())];
dataBuffY_uV = new float[nchan][dataBuffX.length];
dataBuffY_filtY_uV = new float[nchan][dataBuffX.length];
//data_std_uV = new float[nchan];
data_elec_imp_ohm = new float[nchan];
is_railed = new DataStatus[nchan];
for (int i=0; i<nchan; i++) is_railed[i] = new DataStatus(threshold_railed, threshold_railed_warn);
for (int i=0; i<nDataBackBuff; i++) {
//dataPacketBuff[i] = new DataPacket_ADS1299(nchan+n_aux_ifEnabled);
// dataPacketBuff[i] = new DataPacket_ADS1299(OpenBCI_Nchannels+n_aux_ifEnabled);
dataPacketBuff[i] = new DataPacket_ADS1299(nchan, n_aux_ifEnabled);
}
dataProcessing = new DataProcessing(nchan, openBCI.get_fs_Hz());
dataProcessing_user = new DataProcessing_User(nchan, openBCI.get_fs_Hz());
//initialize the data
prepareData(dataBuffX, dataBuffY_uV, openBCI.get_fs_Hz());
verbosePrint("OpenBCI_GUI: initSystem: -- Init 1 --");
//initialize the FFT objects
for (int Ichan=0; Ichan < nchan; Ichan++) {
verbosePrint("a--"+Ichan);
fftBuff[Ichan] = new FFT(Nfft, openBCI.get_fs_Hz());
}; //make the FFT objects
verbosePrint("OpenBCI_GUI: initSystem: b");
initializeFFTObjects(fftBuff, dataBuffY_uV, Nfft, openBCI.get_fs_Hz());
//prepare some signal processing stuff
//for (int Ichan=0; Ichan < nchan; Ichan++) { detData_freqDomain[Ichan] = new DetectionData_FreqDomain(); }
verbosePrint("OpenBCI_GUI: initSystem: -- Init 2 --");
//prepare the source of the input data
switch (eegDataSource) {
case DATASOURCE_NORMAL:
case DATASOURCE_NORMAL_W_AUX:
// int nDataValuesPerPacket = OpenBCI_Nchannels;
int nEEDataValuesPerPacket = nchan;
boolean useAux = false;
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) useAux = true; //switch this back to true CHIP 2014-11-04
openBCI = new OpenBCI_ADS1299(this, openBCI_portName, openBCI_baud, nEEDataValuesPerPacket, useAux, n_aux_ifEnabled); //this also starts the data transfer after XX seconds
break;
case DATASOURCE_SYNTHETIC:
//do nothing
break;
case DATASOURCE_PLAYBACKFILE:
//open and load the data file
println("OpenBCI_GUI: initSystem: loading playback data from " + playbackData_fname);
try {
playbackData_table = new Table_CSV(playbackData_fname);
}
catch (Exception e) {
println("OpenBCI_GUI: initSystem: could not open file for playback: " + playbackData_fname);
println(" : quitting...");
exit();
}
println("OpenBCI_GUI: initSystem: loading complete. " + playbackData_table.getRowCount() + " rows of data, which is " + round(float(playbackData_table.getRowCount())/openBCI.get_fs_Hz()) + " seconds of EEG data");
//removing first column of data from data file...the first column is a time index and not eeg data
playbackData_table.removeColumn(0);
break;
default:
}
verbosePrint("OpenBCI_GUI: initSystem: -- Init 3 --");
//initilize the GUI
initializeGUI();
//final config
// setBiasState(openBCI.isBiasAuto);
verbosePrint("OpenBCI_GUI: initSystem: -- Init 4 --");
//open data file
if ((eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX)) openNewLogFile(fileName); //open a new log file
nextPlayback_millis = millis(); //used for synthesizeData and readFromFile. This restarts the clock that keeps the playback at the right pace.
if (eegDataSource != DATASOURCE_NORMAL && eegDataSource != DATASOURCE_NORMAL_W_AUX) {
systemMode = 10; //tell system it's ok to leave control panel and start interfacing GUI
}
//sync GUI default settings with OpenBCI's default settings...
// openBCI.syncWithHardware(); //this starts the sequence off ... read in OpenBCI_ADS1299 iterates through the rest based on the ASCII trigger "$$$"
// verbosePrint("OpenBCI_GUI: initSystem: -- Init 5 [COMPLETE] --");
}
//halt the data collection
void haltSystem() {
println("openBCI_GUI: haltSystem: Halting system for reconfiguration of settings...");
stopRunning(); //stop data transfer
//reset variables for data processing
curDataPacketInd = -1;
lastReadDataPacketInd = -1;
pointCounter = 0;
prevBytes = 0;
prevMillis = millis();
byteRate_perSec = 0;
drawLoop_counter = 0;
// eegDataSource = -1;
//set all data source list items inactive
// stopDataTransfer(); // make sure to stop data transfer, if data is streaming and being drawn
if ((eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX)) {
closeLogFile(); //close log file
openBCI.closeSDandSerialPort();
}
systemMode = 0;
}
void systemUpdate() { // for updating data values and variables
//update the sync state with the OpenBCI hardware
openBCI.updateSyncState(sdSetting);
//prepare for updating the GUI
win_x = width;
win_y = height;
//updates while in intro screen
if (systemMode == 0) {
}
if (systemMode == 10) {
if (isRunning) {
//get the data, if it is available
pointCounter = getDataIfAvailable(pointCounter);
//has enough data arrived to process it and update the GUI?
if (pointCounter >= nPointsPerUpdate) {
pointCounter = 0; //reset for next time
//process the data
processNewData();
//try to detect the desired signals, do it in frequency space...for OpenBCI_GUI_Simpler
//detectInFreqDomain(fftBuff,inband_Hz,guard_Hz,detData_freqDomain);
//gui.setDetectionData_freqDomain(detData_freqDomain);
//tell the GUI that it has received new data via dumping new data into arrays that the GUI has pointers to
// println("packet counter = " + newPacketCounter);
// for(int i = 0; i < dataProcessing.data_std_uV.length; i++){
// println("dataProcessing.data_std_uV[" + i + "] = " + dataProcessing.data_std_uV[i]);
// }
if ((millis() - timeOfGUIreinitialize) > reinitializeGUIdelay) { //wait 1 second for GUI to reinitialize
try {
gui.update(dataProcessing.data_std_uV, data_elec_imp_ohm);
}
catch (Exception e) {
println(e.getMessage());
reinitializeGUIdelay = reinitializeGUIdelay * 2;
println("OpenBCI_GUI: systemUpdate: New GUI reinitialize delay = " + reinitializeGUIdelay);
}
} else {
println("OpenBCI_GUI: systemUpdate: reinitializing GUI after resize... not updating GUI");
}
///add raw data to spectrogram...if the correct channel...
//...look for the first channel that is active (meaning button is not active) or, if it
// hasn't yet sent any data, send the last channel even if the channel is off
// if (sendToSpectrogram & (!(gui.chanButtons[Ichan].isActive()) | (Ichan == (nchan-1)))) { //send data to spectrogram
// sendToSpectrogram = false; //prevent us from sending more data after this time through
// for (int Idata=0;Idata < nPointsPerUpdate;Idata++) {
// gui.spectrogram.addDataPoint(yLittleBuff_uV[Ichan][Idata]);
// gui.tellGUIWhichChannelForSpectrogram(Ichan);
// //gui.spectrogram.addDataPoint(100.0f+(float)Idata);
// }
// }
redrawScreenNow=true;
} else {
//not enough data has arrived yet... only update the channel controller
}
}
gui.cc.update(); //update Channel Controller even when not updating certain parts of the GUI... (this is a bit messy...)
//alternative component listener function (line 177 - 187 frame.addComponentListener) for processing 3,
if (widthOfLastScreen != width || heightOfLastScreen != height) {
println("OpenBCI_GUI: setup: RESIZED");
screenHasBeenResized = true;
timeOfLastScreenResize = millis();
widthOfLastScreen = width;
heightOfLastScreen = height;
}
//re-initialize GUI if screen has been resized and it's been more than 1/2 seccond (to prevent reinitialization of GUI from happening too often)
if (screenHasBeenResized == true && (millis() - timeOfLastScreenResize) > reinitializeGUIdelay) {
screenHasBeenResized = false;
println("systemUpdate: reinitializing GUI");
timeOfGUIreinitialize = millis();
initializeGUI();
playground.x = width; //reset the x for the playground...
}
playground.update();
}
controlPanel.update();
}
void systemDraw() { //for drawing to the screen
//redraw the screen...not every time, get paced by when data is being plotted
background(bgColor); //clear the screen
if (systemMode == 10) {
int drawLoopCounter_thresh = 100;
if ((redrawScreenNow) || (drawLoop_counter >= drawLoopCounter_thresh)) {
//if (drawLoop_counter >= drawLoopCounter_thresh) println("OpenBCI_GUI: redrawing based on loop counter...");
drawLoop_counter=0; //reset for next time
redrawScreenNow = false; //reset for next time
//update the title of the figure;
switch (eegDataSource) {
case DATASOURCE_NORMAL:
case DATASOURCE_NORMAL_W_AUX:
surface.setTitle(int(frameRate) + " fps, Byte Count = " + openBCI_byteCount + ", bit rate = " + byteRate_perSec*8 + " bps" + ", " + int(float(fileoutput.getRowsWritten())/openBCI.get_fs_Hz()) + " secs Saved, Writing to " + output_fname);
break;
case DATASOURCE_SYNTHETIC:
surface.setTitle(int(frameRate) + " fps, Using Synthetic EEG Data");
break;
case DATASOURCE_PLAYBACKFILE:
surface.setTitle(int(frameRate) + " fps, Playing " + int(float(currentTableRowIndex)/openBCI.get_fs_Hz()) + " of " + int(float(playbackData_table.getRowCount())/openBCI.get_fs_Hz()) + " secs, Reading from: " + playbackData_fname);
break;
}
}
//wait 1 second for GUI to reinitialize
if ((millis() - timeOfGUIreinitialize) > reinitializeGUIdelay) {
// println("attempting to draw GUI...");
try {
// println("GUI DRAW!!! " + millis());
pushStyle();
fill(255);
noStroke();
rect(0, 0, width, navBarHeight);
popStyle();
gui.draw(); //draw the GUI
// playground.draw();
}
catch (Exception e) {
println(e.getMessage());
reinitializeGUIdelay = reinitializeGUIdelay * 2;
println("OpenBCI_GUI: systemDraw: New GUI reinitialize delay = " + reinitializeGUIdelay);
}
} else {
//reinitializing GUI after resize
println("OpenBCI_GUI: systemDraw: reinitializing GUI after resize... not drawing GUI");
}
playground.draw();
dataProcessing_user.draw();
drawContainers();
} else { //systemMode != 10
//still print title information about fps
surface.setTitle(int(frameRate) + " fps — OpenBCI GUI");
}
//control panel
if (controlPanel.isOpen) {
controlPanel.draw();
}
controlPanelCollapser.draw();
helpWidget.draw();
if ((openBCI.get_state() == openBCI.STATE_COMINIT || openBCI.get_state() == openBCI.STATE_SYNCWITHHARDWARE) && systemMode == 0) {
//make out blink the text "Initalizing GUI..."
if (millis()%1000 < 500) {
output("Iniitializing communication w/ your OpenBCI board...");
} else {
output("");
}
if (millis() - timeOfInit > 12000) {
haltSystem();
initSystemButton.but_txt = "START SYSTEM";
output("Init timeout. Verify your Serial/COM Port. Power DOWN/UP your OpenBCI & USB Dongle. Then retry Initialization.");
}
}
if (drawPresentation) {
myPresentation.draw();
dataProcessing_user.drawTriggerFeedback();
}
// use commented code below to verify frameRate and check latency
// println("Time since start: " + millis() + " || Time since last frame: " + str(millis()-timeOfLastFrame));
// timeOfLastFrame = millis();
if (systemMode == -10) {
//intro animation sequence
if (hasIntroAnimation) {
introAnimation();
} else {
systemMode = 0;
}
}
}
void introAnimation() {
pushStyle();
imageMode(CENTER);
background(255);
int t1 = 4000;
int t2 = 6000;
int t3 = 8000;
float transparency = 0;
if (millis() >= t1) {
transparency = map(millis(), t1, t2, 0, 255);
tint(255, transparency);
//draw OpenBCI Logo Front & Center
image(cog, width/2, height/2, width/6, width/6);
}
//exit intro animation at t2
if (millis() >= t3) {
systemMode = 0;
controlPanel.isOpen = true;
}
popStyle();
}
-134
Ver Arquivo
@@ -1,134 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// Playground Class
// Created: 11/22/14 by Conor Russomanno
// An extra interface pane for additional GUI features
//
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
Playground playground;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
}
};
-119
Ver Arquivo
@@ -1,119 +0,0 @@
///////////////////////////////////////////////////////////////////////////
//
// 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
//
///////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
Presentation myPresentation;
boolean drawPresentation = false;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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();
}
}
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -1,4 +0,0 @@
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.
@@ -1,4 +0,0 @@
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
-425
Ver Arquivo
@@ -1,425 +0,0 @@
//////////////////
//
// 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;
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
};
-175
Ver Arquivo
@@ -1,175 +0,0 @@
///////////////////////////////////////////////
//
// Created: Chip Audette, Oct 2013
// Modified: through May 2014
//
// No warranty. Use at your own risk. Use for whatever you'd like.
//
///////////////////////////////////////////////
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
}
}
Arquivo binário não exibido.
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 402 KiB

@@ -1,18 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,18 +0,0 @@
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
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 4.0 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 5.4 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 328 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 341 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 509 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 459 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 288 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 555 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 424 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 310 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 539 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 374 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 412 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 475 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 463 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 613 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 218 KiB

Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+1 -1
Ver Arquivo
@@ -25,7 +25,7 @@
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
boolean drawContainers = true;
boolean drawContainers = false;
Container[] container = new Container[11];
// Container container0;
+1 -1
Ver Arquivo
@@ -21,7 +21,7 @@ boolean isVerbose = false;
HelpWidget helpWidget;
//use signPost(String identifier) to print 'identifier' text and time since last signPost() for debugging latency/timing issues
boolean printSignPosts = false;
boolean printSignPosts = true;
float millisOfLastSignPost = 0.0;
float millisSinceLastSignPost = 0.0;
+147
Ver Arquivo
@@ -0,0 +1,147 @@
////////////////////////////////////////////////////
//
// This class creates an FFT Plot separate from the old Gui_Manager
//
// Conor Russomanno, July 2016
//
// Requires the plotting library from grafica ... replacing the old gwoptics (which is now no longer supported)
//
///////////////////////////////////////////////////
import grafica.*;
import java.util.Random;
FFT_Widget fft_widget;
class FFT_Widget {
int x, y, w, h;
int[] positioning = {0, 0, 0, 0}; // {x0, y0, w, h} retreived from corresponding container
GPlot fft_plot; //create an fft plot for each active channel
GPointsArray[] fft_points = new GPointsArray[nchan]; //create an array of points for each channel of data (4, 8, or 16)
int nPoints = 256; //resolution of FFT plots
int parentContainer = 7; //which container is it mapped to by default?
int[] lineColor = {
(int)color(129, 129, 129),
(int)color(124, 75, 141),
(int)color(54, 87, 158),
(int)color(49, 113, 89),
(int)color(221, 178, 13),
(int)color(253, 94, 52),
(int)color(224, 56, 45),
(int)color(162, 82, 49)
};
int xLim = 60; //maximum value of x axis ... in this case 20 Hz, 40 Hz, 60 Hz, 120 Hz
int yLim = 100; //maximum value of y axis ... 100 uV
//constructor 1
FFT_Widget(PApplet parent) {
x = (int)container[parentContainer].x;
y = (int)container[parentContainer].y;
w = (int)container[parentContainer].w;
h = (int)container[parentContainer].h;
//setup GPlot for FFT
fft_plot = new GPlot(parent, x, y+navHeight, w, h-navHeight); //based on container dimensions
fft_plot.getXAxis().setAxisLabelText("Frequency (Hz)");
fft_plot.getYAxis().setAxisLabelText("EEG Amplitude (uV per bin)");
//fft_plot.setMar(50,50,50,50); //{ bot=60, left=70, top=40, right=30 } by default
fft_plot.setMar(60, 70, 40, 30); //{ bot=60, left=70, top=40, right=30 } by default
fft_plot.setLogScale("y");
fft_plot.setYLim(0.1, yLim);
int _nTicks = int(yLim/10 - 1); //number of axis subdivisions
fft_plot.getYAxis().setNTicks(_nTicks); //sets the number of axis divisions...
fft_plot.setXLim(0.1, xLim);
fft_plot.getYAxis().setDrawTickLabels(true);
fft_plot.setPointSize(2);
fft_plot.setPointColor(0);
//setup points of fft point arrays
for (int i = 0; i < fft_points.length; i++) {
fft_points[i] = new GPointsArray(xLim);
}
//fill fft point arrays
for (int i = 0; i < fft_points.length; i++) {
for (int j = 0; j < xLim; j++) {
//GPoint temp = new GPoint(i, 15*noise(0.1*i));
//println(i + " " + j);
GPoint temp = new GPoint(j, 15*random(0.1*j));
fft_points[i].set(j, temp);
}
}
//map fft point arrays to fft plots
fft_plot.setPoints(fft_points[0]);
}
void update() {
//update position/size of FFT Plot
x = (int)container[parentContainer].x;
y = (int)container[parentContainer].y;
w = (int)container[parentContainer].w;
h = (int)container[parentContainer].h;
fft_plot.setPos(x, y+navHeight);//update position
fft_plot.setOuterDim(w, h-navHeight);//update dimensions
//update the points of the FFT channel arrays
//update fft point arrays
for (int i = 0; i < fft_points.length; i++) {
for (int j = 0; j < xLim + 1; j++) { //loop through frequency domain data, and store into points array
//GPoint powerAtBin = new GPoint(j, 15*random(0.1*j));
GPoint powerAtBin = new GPoint(j, fftBuff[i].getBand(j));
fft_points[i].set(j, powerAtBin);
//println("=========================================");
//println(j);
//println(fftBuff[i].getBand(j) + " :: " + fft_points[i].getX(j) + " :: " + fft_points[i].getY(j));
//println("=========================================");
}
}
//remap fft point arrays to fft plots
fft_plot.setPoints(fft_points[0]);
}
void draw() {
pushStyle();
//draw FFT Graph w/ all plots
fft_plot.beginDraw();
fft_plot.drawBackground();
fft_plot.drawBox();
fft_plot.drawXAxis();
fft_plot.drawYAxis();
//fft_plot.drawTopAxis();
//fft_plot.drawRightAxis();
//fft_plot.drawTitle();
fft_plot.drawGridLines(2);
//here is where we will update points & loop...
for (int i = 0; i < fft_points.length; i++) {
fft_plot.setLineColor(lineColor[i]);
fft_plot.setPoints(fft_points[i]);
fft_plot.drawLines();
fft_plot.drawPoints(); //draw points
}
fft_plot.endDraw();
fill(200, 200, 200);
rect(x, y, w, navHeight); //top bar
fill(bgColor);
textSize(18);
text("FFT Plot", x+w/2, y+navHeight/2);
//fill(255,0,0,150);
//rect(x,y,w,h);
popStyle();
}
}
+19
Ver Arquivo
@@ -0,0 +1,19 @@
int navHeight = 25;
void setupGUIWidgets() {
fft_widget = new FFT_Widget(this);
headPlot_widget = new HeadPlot_Widget();
}
void updateGUIWidgets() {
headPlot_widget.update();
fft_widget.update();
}
void drawGUIWidgets() {
//if () {
headPlot_widget.draw();
fft_widget.draw();
//}
}
+3 -9
Ver Arquivo
@@ -76,7 +76,9 @@ class GUI_Manager {
GridBackground gbMontage, gbFFT;
Button stopButton;
PlotFontInfo fontInfo;
HeadPlot headPlot1;
Button[] chanButtons;
// Button guiPageButton;
//boolean showImpedanceButtons;
@@ -211,13 +213,6 @@ class GUI_Manager {
//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,
@@ -267,8 +262,6 @@ class GUI_Manager {
//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
@@ -784,6 +777,7 @@ class GUI_Manager {
montageTrace.generate(); //graph doesn't update without this
fftTrace.generate(); //graph doesn't update without this
headPlot1.update();
//headPlot_widget.headPlot.update();
cc.update();
//update the text strings
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+21 -4
Ver Arquivo
@@ -32,7 +32,8 @@ import java.awt.event.*; //to allow for event listener on screen resize
//used to switch between application states
int systemMode = -10; /* Modes: -10 = intro sequence; 0 = system stopped/control panel setings; 10 = gui; 20 = help guide */
boolean hasIntroAnimation = true;
boolean hasIntroAnimation = false;
PImage cog;
//choose where to get the EEG data
@@ -155,15 +156,15 @@ void setup() {
//if (frame != null) frame.setResizable(true); //make window resizable
//attach exit handler
//prepareExitHandler();
frameRate(120); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
frameRate(30); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
smooth(); //turn this off if it's too slow
surface.setResizable(true); //updated from frame.setResizable in Processing 2
widthOfLastScreen = width; //for screen resizing (Thank's Tao)
heightOfLastScreen = height;
setupContainers();
//setupGUIWidgets();
//V1 FONTS
f1 = createFont("fonts/Raleway-SemiBold.otf", 16);
@@ -325,6 +326,7 @@ void initSystem() {
//initilize the GUI
initializeGUI();
setupGUIWidgets(); //####
//final config
// setBiasState(openBCI.isBiasAuto);
@@ -403,7 +405,13 @@ void systemUpdate() { // for updating data values and variables
// }
if ((millis() - timeOfGUIreinitialize) > reinitializeGUIdelay) { //wait 1 second for GUI to reinitialize
try {
//-----------------------------------------------------------
//-----------------------------------------------------------
gui.update(dataProcessing.data_std_uV, data_elec_imp_ohm);
updateGUIWidgets(); //####
//-----------------------------------------------------------
//-----------------------------------------------------------
}
catch (Exception e) {
println(e.getMessage());
@@ -462,6 +470,7 @@ void systemDraw() { //for drawing to the screen
//redraw the screen...not every time, get paced by when data is being plotted
background(bgColor); //clear the screen
//background(255); //clear the screen
if (systemMode == 10) {
int drawLoopCounter_thresh = 100;
@@ -495,7 +504,14 @@ void systemDraw() { //for drawing to the screen
noStroke();
rect(0, 0, width, navBarHeight);
popStyle();
//----------------------------
gui.draw(); //draw the GUI
//updateGUIWidgets(); //####
drawGUIWidgets();
//----------------------------
// playground.draw();
}
catch (Exception e) {
@@ -508,8 +524,9 @@ void systemDraw() { //for drawing to the screen
println("OpenBCI_GUI: systemDraw: reinitializing GUI after resize... not drawing GUI");
}
playground.draw();
dataProcessing_user.draw();
playground.draw();
drawContainers();
} else { //systemMode != 10
//still print title information about fps
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 174 KiB

@@ -1,17 +0,0 @@
<?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>
@@ -1,648 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// Channel Controller
// - responsible for addressing channel data (Ganglion 1-4, 32bit
// - 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)
//
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//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];
//Channel Colors -- Defaulted to matching the OpenBCI electrode ribbon cable
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)
};
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
public void updateChannelArrays(int _nchan) {
channelSettingValues = new char [_nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
impedanceCheckValues = new char [_nchan][2];
}
//activateChannel: Ichan is [0 nchan-1] (aka zero referenced)
void activateChannel(int Ichan) {
println("OpenBCI_GUI: activating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL || eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, true); //activate
}
}
if (Ichan < gui.chanButtons.length) {
channelSettingValues[Ichan][0] = '0';
gui.cc.update();
}
}
void deactivateChannel(int Ichan) {
println("OpenBCI_GUI: deactivating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL || eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, false); //de-activate
}
}
if (Ichan < gui.chanButtons.length) {
channelSettingValues[Ichan][0] = '1';
gui.cc.update();
}
}
//Ichan is zero referenced (not one referenced)
boolean isChannelActive(int Ichan) {
boolean return_val = false;
if (channelSettingValues[Ichan][0] == '1') {
return_val = false;
} else {
return_val = true;
}
return return_val;
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
}
}
};
@@ -1,267 +0,0 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This code is used for GUI-wide spacing. It defines the GUI layout as a grid
// with the following design:
//
// The #s shown below fall at the center of their corresponding container[].
// Ex: container[1] is the upper left corner of the large rectangle between [0] & [10]
// Ex 2: container[6] is the entire right half of the same rectangle.
//
// ------------------------------------------------
// | [0] |
// ------------------------------------------------
// | | |
// | [1] [2] [3] |
// | | |
// |---------[4]----------[5]---------[6]---------|
// | | |
// | [7] [8] [9] |
// | | |
// ------------------------------------------------
// | [10] |
// ------------------------------------------------
//
// Created by: Conor Russomanno (May 2016)
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
boolean drawContainers = true;
Container[] container = new Container[11];
// Container container0;
// Container container1;
// Container container2;
// Container container3;
// Container container4;
// Container container5;
// Container container6;
// Container container7;
// Container container8;
// Container container9;
// Container container10;
//Container container11;
//Container container12;
//Viz extends container (example below)
//Viz viz1;
//Viz viz2;
int widthOfLastScreen_C = 0;
int heightOfLastScreen_C = 0;
void setupContainers() {
//size(1024, 768, P2D);
//frameRate(30);
//smooth();
//surface.setResizable(true);
widthOfLastScreen_C = width;
heightOfLastScreen_C = height;
int topNav_h = 32; //tie this to a global variable or one attached to GUI_Manager
int bottomNav_h = 30; //same
int leftNav_w = 0; //not used currently, maybe if we add a left-side tool bar
int rightNav_w = 0; //not used currently
container[0] = new Container(0, 0, width, topNav_h, 0);
container[5] = new Container(0, topNav_h, width, height - (topNav_h + bottomNav_h), 10);
container[1] = new Container(container[5], "TOP_LEFT");
container[2] = new Container(container[5], "TOP");
container[3] = new Container(container[5], "TOP_RIGHT");
container[4] = new Container(container[5], "LEFT");
container[6] = new Container(container[5], "RIGHT");
container[7] = new Container(container[5], "BOTTOM_LEFT");
container[8] = new Container(container[5], "BOTTOM");
container[9] = new Container(container[5], "BOTTOM_RIGHT");
container[10] = new Container(0, height - bottomNav_h, width, 50, 0);
//container11 = new Container(container1, "LEFT");
//container12 = new Container(container1, "RIGHT");
//setup viz objects... example of container extension (more below)
//setupVizs();
}
void drawContainers() {
//background(255);
for(int i = 0; i < container.length; i++){
container[i].draw();
}
//container11.draw();
//container12.draw();
//Draw viz objects.. exampl extension of container class (more below)
//viz1.draw();
//viz2.draw();
//alternative component listener function (line 177 - 187 frame.addComponentListener) for processing 3,
if (widthOfLastScreen_C != width || heightOfLastScreen_C != height) {
println("OpenBCI_GUI: setup: RESIZED");
//screenHasBeenResized = true;
//timeOfLastScreenResize = millis();
setupContainers();
//setupVizs(); //container extension example (more below)
widthOfLastScreen = width;
heightOfLastScreen = height;
}
}
public class Container {
//key Container Variables
public float x0, y0, w0, h0; //true dimensions.. without margins
public float x, y, w, h; //dimensions with margins
public float margin; //margin
//constructor 1 -- comprehensive
public Container(float _x0, float _y0, float _w0, float _h0, float _margin) {
margin = _margin;
x0 = _x0;
y0 = _y0;
w0 = _w0;
h0 = _h0;
x = x0 + margin;
y = y0 + margin;
w = w0 - margin*2;
h = h0 - margin*2;
}
//constructor 2 -- recursive constructor -- for quickly building sub-containers based on a super container (aka master)
public Container(Container master, String _type) {
margin = master.margin;
if(_type == "WHOLE"){
x0 = master.x0;
y0 = master.y0;
w0 = master.w0;
h0 = master.h0;
w = master.w;
h = master.h;
x = master.x;
y = master.y;
} else if (_type == "LEFT") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0;
w = (master.w - margin)/2;
h = master.h;
x = master.x;
y = master.y;
} else if (_type == "RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0;
w = (master.w - margin)/2;
h = master.h;
x = master.x + w + margin;
y = master.y;
} else if (_type == "TOP") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0;
h0 = master.h0/2;
w = master.w;
h = (master.h - margin)/2;
x = master.x;
y = master.y;
} else if (_type == "BOTTOM") {
x0 = master.x0;
y0 = master.y0 + master.h0/2;
w0 = master.w0;
h0 = master.h0/2;
w = master.w;
h = (master.h - margin)/2;
x = master.x;
y = master.y + h + margin;
} else if (_type == "TOP_LEFT") {
x0 = master.x0;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x;
y = master.y;
} else if (_type == "TOP_RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x + w + margin;
y = master.y;
} else if (_type == "BOTTOM_LEFT") {
x0 = master.x0;
y0 = master.y0 + master.h0/2;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x;
y = master.y + h + margin;
} else if (_type == "BOTTOM_RIGHT") {
x0 = master.x0 + master.w0/2;
y0 = master.y0 + master.h0/2;
w0 = master.w0/2;
h0 = master.h0/2;
w = (master.w - margin)/2;
h = (master.h - margin)/2;
x = master.x + w + margin;
y = master.y + h + margin;
}
}
public void draw() {
if(drawContainers){
pushStyle();
//draw margin area
fill(102, 255, 71, 100);
noStroke();
rect(x0, y0, w0, h0);
//noFill();
//stroke(255, 0, 0);
//rect(x0, y0, w0, h0);
fill(31, 69, 110, 100);
noStroke();
rect(x, y, w, h);
popStyle();
}
}
};
// --- EXAMPLE OF EXTENDING THE CONTAINER --- //
//public class Viz extends Container {
// public float abc;
// public Viz(float _abc, Container master) {
// super(master, "WHOLE");
// abc = _abc;
// }
// void draw() {
// pushStyle();
// noStroke();
// fill(255, 0, 0, 50);
// rect(x, y, w, h);
// popStyle();
// }
//};
//void setupVizs() {
// viz1 = new Viz (10f, container2);
// viz2 = new Viz (10f, container4);
//}
// --- END OF EXAMPLE OF EXTENDING THE CONTAINER --- //
-988
Ver Arquivo
@@ -1,988 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// 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.*;
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
ControlPanel controlPanel;
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;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
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);
}
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
};
//==============================================================================//
// 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();
}
};
//===================== 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;
}
//=======================================================================================================================================
//
// MenuList Class
//
//The MenuList class is implemented by the Control Panel. It allows you to set up a list of selectable items within a fixed rectangle size
//Currently used for Serial/COM select, SD settings, and System Mode
//
//=======================================================================================================================================
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);
}
};
-354
Ver Arquivo
@@ -1,354 +0,0 @@
////////////////////////////////////////////////////////////
// Class: OutputFile_rawtxt
// Purpose: handle file creation and writing for the text log file
// Created: Chip Audette May 2, 2014
////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void openNewLogFile(String _fileName) {
//close the file if it's open
if (fileoutput != null) {
println("OpenBCI_GUI: closing log file");
closeLogFile();
}
//open the new file
fileoutput = new OutputFile_rawtxt(openBCI.get_fs_Hz(), _fileName);
output_fname = fileoutput.fname;
println("openBCI: openNewLogFile: opened output file: " + output_fname);
output("openBCI: openNewLogFile: opened output file: " + output_fname);
}
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();
}
}
void closeLogFile() {
if (fileoutput != null) fileoutput.closeFile();
}
void fileSelected(File selection) { //called by the Open File dialog box after a file has been selected
if (selection == null) {
println("fileSelected: no selection so far...");
} else {
//inputFile = selection;
playbackData_fname = selection.getAbsolutePath();
}
}
String getDateString() {
String fname = year() + "-";
if (month() < 10) fname=fname+"0";
fname = fname + month() + "-";
if (day() < 10) fname = fname + "0";
fname = fname + day();
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();
return fname;
}
//these functions are relevant to convertSDFile
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();
}
}
//------------------------------------------------------------------------
// CLASSES
//------------------------------------------------------------------------
//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);
//
///////////////////////////////////////////////////////////////
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);
}
}
}
//////////////////////////////////
//
// This collection of functions/methods - convertSDFile, createPlaybackFileFromSD, & sdFileSelected - 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();
}
}
}
-453
Ver Arquivo
@@ -1,453 +0,0 @@
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
DataProcessing dataProcessing;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//called from systemUpdate when mode=10 and isRunning = true
int getDataIfAvailable(int pointCounter) {
if ( (eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX) ) {
//get data from serial port as it streams in
//next, gather any new data into the "little buffer"
while ( (curDataPacketInd != lastReadDataPacketInd) && (pointCounter < nPointsPerUpdate)) {
lastReadDataPacketInd = (lastReadDataPacketInd+1) % dataPacketBuff.length; //increment to read the next packet
for (int Ichan=0; Ichan < nchan; Ichan++) { //loop over each cahnnel
//scale the data into engineering units ("microvolts") and save to the "little buffer"
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan] * openBCI.get_scale_fac_uVolts_per_count();
}
pointCounter++; //increment counter for "little buffer"
}
} else {
// make or load data to simulate real time
//has enough time passed?
int current_millis = millis();
if (current_millis >= nextPlayback_millis) {
//prepare for next time
int increment_millis = int(round(float(nPointsPerUpdate)*1000.f/openBCI.get_fs_Hz())/playback_speed_fac);
if (nextPlayback_millis < 0) nextPlayback_millis = current_millis;
nextPlayback_millis += increment_millis;
// generate or read the data
lastReadDataPacketInd = 0;
for (int i = 0; i < nPointsPerUpdate; i++) {
// println();
dataPacketBuff[lastReadDataPacketInd].sampleIndex++;
switch (eegDataSource) {
case DATASOURCE_SYNTHETIC: //use synthetic data (for GUI debugging)
synthesizeData(nchan, openBCI.get_fs_Hz(), openBCI.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
case DATASOURCE_PLAYBACKFILE:
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, openBCI.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
default:
//no action
}
//gather the data into the "little buffer"
for (int Ichan=0; Ichan < nchan; Ichan++) {
//scale the data into engineering units..."microvolts"
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan]* openBCI.get_scale_fac_uVolts_per_count();
}
pointCounter++;
} //close the loop over data points
//if (eegDataSource==DATASOURCE_PLAYBACKFILE) println("OpenBCI_GUI: getDataIfAvailable: currentTableRowIndex = " + currentTableRowIndex);
//println("OpenBCI_GUI: getDataIfAvailable: pointCounter = " + pointCounter);
} // close "has enough time passed"
}
return pointCounter;
}
RunningMean avgBitRate = new RunningMean(10); //10 point running average...at 5 points per second, this should be 2 second running average
void processNewData() {
//compute instantaneous byte rate
float inst_byteRate_perSec = (int)(1000.f * ((float)(openBCI_byteCount - prevBytes)) / ((float)(millis() - prevMillis)));
prevMillis=millis(); //store for next time
prevBytes = openBCI_byteCount; //store for next time
//compute smoothed byte rate
avgBitRate.addValue(inst_byteRate_perSec);
byteRate_perSec = (int)avgBitRate.calcMean();
//prepare to update the data buffers
float foo_val;
float prevFFTdata[] = new float[fftBuff[0].specSize()];
double foo;
//update the data buffers
for (int Ichan=0; Ichan < nchan; Ichan++) {
//append the new data to the larger data buffer...because we want the plotting routines
//to show more than just the most recent chunk of data. This will be our "raw" data.
appendAndShift(dataBuffY_uV[Ichan], yLittleBuff_uV[Ichan]);
//make a copy of the data that we'll apply processing to. This will be what is displayed on the full montage
dataBuffY_filtY_uV[Ichan] = dataBuffY_uV[Ichan].clone();
}
//if you want to, re-reference the montage to make it be a mean-head reference
if (false) rereferenceTheMontage(dataBuffY_filtY_uV);
//update the FFT (frequency spectrum)
for (int Ichan=0; Ichan < nchan; Ichan++) {
//copy the previous FFT data...enables us to apply some smoothing to the FFT data
for (int I=0; I < fftBuff[Ichan].specSize(); I++) prevFFTdata[I] = fftBuff[Ichan].getBand(I); //copy the old spectrum values
//prepare the data for the new FFT
float[] fooData_raw = dataBuffY_uV[Ichan]; //use the raw data for the FFT
fooData_raw = Arrays.copyOfRange(fooData_raw, fooData_raw.length-Nfft, fooData_raw.length); //trim to grab just the most recent block of data
float meanData = mean(fooData_raw); //compute the mean
for (int I=0; I < fooData_raw.length; I++) fooData_raw[I] -= meanData; //remove the mean (for a better looking FFT
//compute the FFT
fftBuff[Ichan].forward(fooData_raw); //compute FFT on this channel of data
//convert to uV_per_bin...still need to confirm the accuracy of this code.
//Do we need to account for the power lost in the windowing function? CHIP 2014-10-24
for (int I=0; I < fftBuff[Ichan].specSize(); I++) { //loop over each FFT bin
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) / fftBuff[Ichan].specSize()));
}
//average the FFT with previous FFT data so that it makes it smoother in time
double min_val = 0.01d;
for (int I=0; I < fftBuff[Ichan].specSize(); I++) { //loop over each fft bin
if (prevFFTdata[I] < min_val) prevFFTdata[I] = (float)min_val; //make sure we're not too small for the log calls
foo = fftBuff[Ichan].getBand(I);
if (foo < min_val) foo = min_val; //make sure this value isn't too small
if (true) {
//smooth in dB power space
foo = (1.0d-smoothFac[smoothFac_ind]) * java.lang.Math.log(java.lang.Math.pow(foo, 2));
foo += smoothFac[smoothFac_ind] * java.lang.Math.log(java.lang.Math.pow((double)prevFFTdata[I], 2));
foo = java.lang.Math.sqrt(java.lang.Math.exp(foo)); //average in dB space
} else {
//smooth (average) in linear power space
foo = (1.0d-smoothFac[smoothFac_ind]) * java.lang.Math.pow(foo, 2);
foo+= smoothFac[smoothFac_ind] * java.lang.Math.pow((double)prevFFTdata[I], 2);
// take sqrt to be back into uV_rtHz
foo = java.lang.Math.sqrt(foo);
}
fftBuff[Ichan].setBand(I, (float)foo); //put the smoothed data back into the fftBuff data holder for use by everyone else
} //end loop over FFT bins
} //end the loop over channels.
//apply additional processing for the time-domain montage plot (ie, filtering)
dataProcessing.process(yLittleBuff_uV, dataBuffY_uV, dataBuffY_filtY_uV, fftBuff);
//apply user processing
// ...yLittleBuff_uV[Ichan] is the most recent raw data since the last call to this processing routine
// ...dataBuffY_filtY_uV[Ichan] is the full set of filtered data as shown in the time-domain plot in the GUI
// ...fftBuff[Ichan] is the FFT data structure holding the frequency spectrum as shown in the freq-domain plot in the GUI
dataProcessing_user.process(yLittleBuff_uV, dataBuffY_uV, dataBuffY_filtY_uV, fftBuff);
//look to see if the latest data is railed so that we can notify the user on the GUI
for (int Ichan=0; Ichan < nchan; Ichan++) is_railed[Ichan].update(dataPacketBuff[lastReadDataPacketInd].values[Ichan]);
//compute the electrode impedance. Do it in a very simple way [rms to amplitude, then uVolt to Volt, then Volt/Amp to Ohm]
for (int Ichan=0; Ichan < nchan; Ichan++) data_elec_imp_ohm[Ichan] = (sqrt(2.0)*dataProcessing.data_std_uV[Ichan]*1.0e-6) / openBCI.get_leadOffDrive_amps();
}
//helper function in handling the EEG data
void appendAndShift(float[] data, float[] newData) {
int nshift = newData.length;
int end = data.length-nshift;
for (int i=0; i < end; i++) {
data[i]=data[i+nshift]; //shift data points down by 1
}
for (int i=0; i<nshift; i++) {
data[end+i] = newData[i]; //append new data
}
}
final float sine_freq_Hz = 10.0f;
float[] sine_phase_rad = new float[nchan];
void synthesizeData(int nchan, float fs_Hz, float scale_fac_uVolts_per_count, DataPacket_ADS1299 curDataPacket) {
float val_uV;
for (int Ichan=0; Ichan < nchan; Ichan++) {
if (isChannelActive(Ichan)) {
val_uV = randomGaussian()*sqrt(fs_Hz/2.0f); // ensures that it has amplitude of one unit per sqrt(Hz) of signal bandwidth
//val_uV = random(1)*sqrt(fs_Hz/2.0f); // ensures that it has amplitude of one unit per sqrt(Hz) of signal bandwidth
if (Ichan==0) val_uV*= 10f; //scale one channel higher
if (Ichan==1) {
//add sine wave at 10 Hz at 10 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * sine_freq_Hz / fs_Hz;
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 10.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]);
} else if (Ichan==2) {
//50 Hz interference at 50 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * 50.0f / fs_Hz; //60 Hz
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 50.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]); //20 uVrms
} else if (Ichan==3) {
//60 Hz interference at 50 uVrms
sine_phase_rad[Ichan] += 2.0f*PI * 60.0f / fs_Hz; //50 Hz
if (sine_phase_rad[Ichan] > 2.0f*PI) sine_phase_rad[Ichan] -= 2.0f*PI;
val_uV += 50.0f * sqrt(2.0)*sin(sine_phase_rad[Ichan]); //20 uVrms
}
} else {
val_uV = 0.0f;
}
curDataPacket.values[Ichan] = (int) (0.5f+ val_uV / scale_fac_uVolts_per_count); //convert to counts, the 0.5 is to ensure rounding
}
}
//some data initialization routines
void prepareData(float[] dataBuffX, float[][] dataBuffY_uV, float fs_Hz) {
//initialize the x and y data
int xoffset = dataBuffX.length - 1;
for (int i=0; i < dataBuffX.length; i++) {
dataBuffX[i] = ((float)(i-xoffset)) / fs_Hz; //x data goes from minus time up to zero
for (int Ichan = 0; Ichan < nchan; Ichan++) {
dataBuffY_uV[Ichan][i] = 0f; //make the y data all zeros
}
}
}
void initializeFFTObjects(FFT[] fftBuff, float[][] dataBuffY_uV, int N, float fs_Hz) {
float[] fooData;
for (int Ichan=0; Ichan < nchan; Ichan++) {
//make the FFT objects...Following "SoundSpectrum" example that came with the Minim library
//fftBuff[Ichan] = new FFT(Nfft, fs_Hz); //I can't have this here...it must be in setup
fftBuff[Ichan].window(FFT.HAMMING);
//do the FFT on the initial data
fooData = dataBuffY_uV[Ichan];
fooData = Arrays.copyOfRange(fooData, fooData.length-Nfft, fooData.length);
fftBuff[Ichan].forward(fooData); //compute FFT on this channel of data
}
}
int getPlaybackDataFromTable(Table datatable, int currentTableRowIndex, float scale_fac_uVolts_per_count, DataPacket_ADS1299 curDataPacket) {
float val_uV = 0.0f;
//check to see if we can load a value from the table
if (currentTableRowIndex >= datatable.getRowCount()) {
//end of file
println("OpenBCI_GUI: getPlaybackDataFromTable: hit the end of the playback data file. starting over...");
//if (isRunning) stopRunning();
currentTableRowIndex = 0;
} else {
//get the row
TableRow row = datatable.getRow(currentTableRowIndex);
currentTableRowIndex++; //increment to the next row
//get each value
for (int Ichan=0; Ichan < nchan; Ichan++) {
if (isChannelActive(Ichan) && (Ichan < datatable.getColumnCount())) {
val_uV = row.getFloat(Ichan);
} else {
//use zeros for the missing channels
val_uV = 0.0f;
}
//put into data structure
curDataPacket.values[Ichan] = (int) (0.5f+ val_uV / scale_fac_uVolts_per_count); //convert to counts, the 0.5 is to ensure rounding
}
}
return currentTableRowIndex;
}
//------------------------------------------------------------------------
// CLASSES
//------------------------------------------------------------------------
class DataProcessing {
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[];
DataProcessing(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;
}
}
}
};
@@ -1,532 +0,0 @@
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
DataProcessing_User dataProcessing_user;
boolean drawEMG = false; //if true... toggles on EEG_Processing_User.draw and toggles off the headplot in Gui_Manager
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
class DataProcessing_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 = 3 - 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
DataProcessing_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 bio 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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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 (dataProcessing_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);
}
}
}
};
-116
Ver Arquivo
@@ -1,116 +0,0 @@
//////////////////////////////////////
//
// This file contains classes that are helpful for debugging, as well as the HelpWidget,
// which is used to give feedback to the GUI user in the small text window at the bottom of the GUI
//
// Created: Conor Russomanno, June 2016
// Based on code: Chip Audette, Oct 2013 - Dec 2014
//
//
/////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//set true if you want more verbosity in console.. verbosePrint("print_this_thing") is used to output feedback when isVerbose = true
boolean isVerbose = false;
//Help Widget initiation
HelpWidget helpWidget;
//use signPost(String identifier) to print 'identifier' text and time since last signPost() for debugging latency/timing issues
boolean printSignPosts = false;
float millisOfLastSignPost = 0.0;
float millisSinceLastSignPost = 0.0;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void verbosePrint(String _string) {
if (isVerbose) {
println(_string);
}
}
void delay(int delay)
{
int time = millis();
while (millis() - time <= delay);
}
//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() {
//nothing needed here
}
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);
}
// created 2/10/16 by Conor Russomanno to dissect the aspects of the GUI that are slowing it down
// here I will create methods used to identify where there are inefficiencies in the code
// note to self: make sure to check the frameRate() in setup... switched from 16 to 30... working much faster now... still a useful method below.
// -------------------------------------------------------------- START -------------------------------------------------------------------------------
//method for printing out an ["indentifier"][millisSinceLastSignPost] for debugging purposes... allows us to look at what is taking too long.
void signPost(String identifier) {
if (printSignPosts) {
millisSinceLastSignPost = millis() - millisOfLastSignPost;
println("SIGN POST: [" + identifier + "][" + millisSinceLastSignPost + "]");
millisOfLastSignPost = millis();
}
}
// ---------------------------------------------------------------- FINISH -----------------------------------------------------------------------------
-370
Ver Arquivo
@@ -1,370 +0,0 @@
//////////////////////////////////////
//
// This file contains classes that are helfpul in some way.
// Created: Chip Audette, Oct 2013 - Dec 2014
//
/////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//////////////////////////////////////////////////
//
// Formerly, Math.pde
// - std
// - mean
// - medianDestructive
// - findMax
// - mean
// - sum
// - CalcDotProduct
// - log10
// - filterWEA_1stOrderIIR
// - filterIIR
// - removeMean
// - rereferenceTheMontage
// - CLASS RunningMean
//
// Created: Chip Audette, Oct 2013
//
//////////////////////////////////////////////////
//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;
}
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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);
}
};
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;
}
};
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;
}
};
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() {
}
};
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;
};
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);
}
};
-179
Ver Arquivo
@@ -1,179 +0,0 @@
FFT_Plot fft;
public void initializeFFT(){
fft = new FFT_Plot(this);
}
class FFT_Plot extends Container{
ScatterTrace_FFT fftTrace;
Graph2D gFFT;
GridBackground gbMontage, gbFFT;
TextBox titleFFT;
//maybe necessary
int bgColorGraphs = 255;
int gridColor = 200;
int borderColor = 50;
int axisColor = 50;
int fontColor = 255;
int bx, by, bw, bh; //button x,y,w,h
Button maxDisplayFreqButton;
Button fftNButton;
Button smoothingButton;
float vertScale_uV = default_vertScale_uV; //default_vertScale_uV at the top of GUI_Manager.pde
float vertScaleMin_uV_whenLog = 0.1f;
private float[] maxDisplayFreq_Hz = {20.0f, 40.0f, 60.0f, 120.0f};
private int maxDisplayFreq_ind = 2;
// float[] axisFFT_relPos = {
// gutter_left + left_right_split, // + 0.1f,
// up_down_split*available_top2bot + height_UI_tray + gutter_topbot,
// (1f-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);
FFT_Plot(PApplet parent){
super(container[9], "WHOLE"); //grabs x, y, w, h, and other variable settings from container[9]
gFFT = new Graph2D(parent, int(w), int(h), false);
gFFT.setAxisColour(axisColor, axisColor, axisColor);
gFFT.setFontColour(fontColor, fontColor, fontColor);
gFFT.position.x = x;
gFFT.position.y = y;
//gFFT.position.y = 0;
//setup the y axis
gFFT.setYAxisMin(vertScaleMin_uV_whenLog);
gFFT.setYAxisMax(vertScale_uV);
gFFT.setYAxisTickSpacing(1);
gFFT.setYAxisMinorTicks(0);
gFFT.setYAxisLabelAccuracy(0);
//gFFT.setYAxisLabel("EEG Amplitude (uV/sqrt(Hz))"); // Some people prefer this...but you'll have to change the normalization in OpenBCI_GUI\processNewData()
gFFT.setYAxisLabel("EEG Amplitude (uV per bin)"); // CHIP 2014-10-24...currently, this matches the normalization in OpenBCI_GUI\processNewData()
gFFT.setYAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
gFFT.setYAxisTickFont(fontInfo.fontName,fontInfo.tickLabel_size, false);
//get the Y-axis and make it log
Axis2D ay=gFFT.getYAxis();
ay.setLogarithmicAxis(true);
//setup the x axis
gFFT.setXAxisMin(0f);
gFFT.setXAxisMax(maxDisplayFreq_Hz[maxDisplayFreq_ind]); //default to 60 Hz
gFFT.setXAxisTickSpacing(10f);
gFFT.setXAxisMinorTicks(2);
gFFT.setXAxisLabelAccuracy(0);
gFFT.setXAxisLabel("Frequency (Hz)");
gFFT.setXAxisLabelFont(fontInfo.fontName,fontInfo.axisLabel_size, false);
gFFT.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);
gFFT.setBackground(gbFFT);
gFFT.setBorderColour(borderColor,borderColor,borderColor);
// add title
titleFFT = new TextBox("FFT Plot",0,0);
int x2 = int(x) + int(w/2);
int y2 = int(y) - 2; //deflect two pixels upward
titleFFT.x = x2;
titleFFT.y = y2;
titleFFT.textColor = color(255,255,255);
titleFFT.setFontSize(16);
titleFFT.alignH = CENTER;
//buttons
bx = 0;
by = 2; //button y position, measured top
bw = 80; //button width
bh = 26; //button height, was 25
float gutter_between_buttons = 0.005f; //space between buttons
bx = calcButtonXLocation(Ibut++, win_x, w, width/2, gutter_between_buttons);
maxDisplayFreqButton = new Button(x,y,w,h,"Max Freq\n" + round(maxDisplayFreq_Hz[maxDisplayFreq_ind]) + " Hz",fontInfo.buttonLabel_size);
}
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 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");
}
};
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-595
Ver Arquivo
@@ -1,595 +0,0 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//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 ',':
drawContainers = !drawContainers;
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...");
dataProcessing_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...");
dataProcessing_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;
}
}
//swtich yard if a click is detected
void mousePressed() {
verbosePrint("OpenBCI_GUI: mousePressed: mouse pressed");
//if not in initial setup...
if (systemMode >= 10) {
//limit interactivity of main GUI if control panel is open
if (controlPanel.isOpen == false) {
//was the stopButton pressed?
gui.mousePressed(); // trigger mousePressed function in GUI
//most of the logic below should be migrated into the GUI_Manager specific function above
if (gui.stopButton.isMouseHere()) {
gui.stopButton.setIsActive(true);
stopButtonWasPressed();
}
// //was the gui page button pressed?
// if (gui.guiPageButton.isMouseHere()) {
// gui.guiPageButton.setIsActive(true);
// gui.incrementGUIpage();
// }
//check the buttons
switch (gui.guiPage) {
case GUI_Manager.GUI_PAGE_CHANNEL_ONOFF:
//check the channel buttons
// for (int Ibut = 0; Ibut < gui.chanButtons.length; Ibut++) {
// if (gui.chanButtons[Ibut].isMouseHere()) {
// toggleChannelState(Ibut);
// }
// }
//check the detection button
//if (gui.detectButton.updateIsMouseHere()) toggleDetectionState();
//check spectrogram button
//if (gui.spectrogramButton.updateIsMouseHere()) toggleSpectrogramState();
break;
case GUI_Manager.GUI_PAGE_IMPEDANCE_CHECK:
// ============ DEPRECATED ============== //
// //check the impedance buttons
// for (int Ibut = 0; Ibut < gui.impedanceButtonsP.length; Ibut++) {
// if (gui.impedanceButtonsP[Ibut].isMouseHere()) {
// toggleChannelImpedanceState(gui.impedanceButtonsP[Ibut],Ibut,0);
// }
// if (gui.impedanceButtonsN[Ibut].isMouseHere()) {
// toggleChannelImpedanceState(gui.impedanceButtonsN[Ibut],Ibut,1);
// }
// }
// if (gui.biasButton.isMouseHere()) {
// gui.biasButton.setIsActive(true);
// setBiasState(!openBCI.isBiasAuto);
// }
// break;
case GUI_Manager.GUI_PAGE_HEADPLOT_SETUP:
if (gui.intensityFactorButton.isMouseHere()) {
gui.intensityFactorButton.setIsActive(true);
gui.incrementVertScaleFactor();
}
if (gui.loglinPlotButton.isMouseHere()) {
gui.loglinPlotButton.setIsActive(true);
gui.set_vertScaleAsLog(!gui.vertScaleAsLog); //toggle the state
}
if (gui.filtBPButton.isMouseHere()) {
gui.filtBPButton.setIsActive(true);
incrementFilterConfiguration();
}
if (gui.filtNotchButton.isMouseHere()) {
gui.filtNotchButton.setIsActive(true);
incrementNotchConfiguration();
}
if (gui.smoothingButton.isMouseHere()) {
gui.smoothingButton.setIsActive(true);
incrementSmoothing();
}
if (gui.showPolarityButton.isMouseHere()) {
gui.showPolarityButton.setIsActive(true);
toggleShowPolarity();
}
if (gui.maxDisplayFreqButton.isMouseHere()) {
gui.maxDisplayFreqButton.setIsActive(true);
gui.incrementMaxDisplayFreq();
}
break;
//default:
}
//check the graphs
if (gui.isMouseOnFFT(mouseX, mouseY)) {
GraphDataPoint dataPoint = new GraphDataPoint();
gui.getFFTdataPoint(mouseX, mouseY, dataPoint);
println("OpenBCI_GUI: FFT data point: " + String.format("%4.2f", dataPoint.x) + " " + dataPoint.x_units + ", " + String.format("%4.2f", dataPoint.y) + " " + dataPoint.y_units);
} else if (gui.headPlot1.isPixelInsideHead(mouseX, mouseY)) {
//toggle the head plot contours
gui.headPlot1.drawHeadAsContours = !gui.headPlot1.drawHeadAsContours;
} else if (gui.isMouseOnMontage(mouseX, mouseY)) {
//toggle the display of the montage values
gui.showMontageValues = !gui.showMontageValues;
}
}
}
//=============================//
// CONTROL PANEL INTERACTIVITY //
//=============================//
//was control panel button pushed
if (controlPanelCollapser.isMouseHere()) {
if (controlPanelCollapser.isActive && systemMode == 10) {
controlPanelCollapser.setIsActive(false);
controlPanel.isOpen = false;
} else {
controlPanelCollapser.setIsActive(true);
controlPanel.isOpen = true;
}
} else {
if (controlPanel.isOpen) {
controlPanel.CPmousePressed();
}
}
//interacting with control panel
if (controlPanel.isOpen) {
//close control panel if you click outside...
if (systemMode == 10) {
if (mouseX > 0 && mouseX < controlPanel.w && mouseY > 0 && mouseY < controlPanel.initBox.y+controlPanel.initBox.h) {
println("OpenBCI_GUI: mousePressed: clicked in CP box");
controlPanel.CPmousePressed();
}
//if clicked out of panel
else {
println("OpenBCI_GUI: mousePressed: outside of CP clicked");
controlPanel.isOpen = false;
controlPanelCollapser.setIsActive(false);
output("Press the \"Press to Start\" button to initialize the data stream.");
}
}
}
redrawScreenNow = true; //command a redraw of the GUI whenever the mouse is pressed
if (playground.isMouseHere()) {
playground.mousePressed();
}
if (playground.isMouseInButton()) {
playground.toggleWindow();
}
}
void mouseReleased() {
verbosePrint("OpenBCI_GUI: mouseReleased: mouse released");
//some buttons light up only when being actively pressed. Now that we've
//released the mouse button, turn off those buttons.
//interacting with control panel
if (controlPanel.isOpen) {
//if clicked in panel
controlPanel.CPmouseReleased();
}
if (systemMode >= 10) {
gui.mouseReleased();
redrawScreenNow = true; //command a redraw of the GUI whenever the mouse is released
}
if (screenHasBeenResized) {
println("OpenBCI_GUI: mouseReleased: screen has been resized...");
screenHasBeenResized = false;
}
//Playground Interactivity
if (playground.isMouseHere()) {
playground.mouseReleased();
}
if (playground.isMouseInButton()) {
// playground.toggleWindow();
}
}
void incrementSmoothing() {
smoothFac_ind++;
if (smoothFac_ind >= smoothFac.length) smoothFac_ind = 0;
//tell the GUI
gui.setSmoothFac(smoothFac[smoothFac_ind]);
//update the button
gui.smoothingButton.but_txt = "Smooth\n" + smoothFac[smoothFac_ind];
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
-550
Ver Arquivo
@@ -1,550 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// GUI for controlling the ADS1299-based OpenBCI
//
// Created: Chip Audette, Oct 2013 - May 2014
// Modified: Conor Russomanno & Joel Murphy, August 2014 - Dec 2014
// Modified (v2.0): Conor Russomanno & Joel Murphy, June 2016
//
// Requires gwoptics graphing library for processing. Built on V0.5.0
// http://www.gwoptics.org/processing/gwoptics_p5lib/
//
// Requires ControlP5 library, but an older one. This will only work
// with the ControlP5 library that is included with this GitHub repository
//
// No warranty. Use at your own risk. Use for whatever you'd like.
//
////////////////////////////////////////////////////////////////////////////////
import ddf.minim.analysis.*; //for FFT
//import ddf.minim.*; // commented because too broad.. contains "Controller" class which is also contained in ControlP5... need to be more specific // To make sound. Following minim example "frequencyModulation"
import ddf.minim.ugens.*; // To make sound. Following minim example "frequencyModulation"
import java.lang.Math; //for exp, log, sqrt...they seem better than Processing's built-in
import processing.core.PApplet;
import java.util.*; //for Array.copyOfRange()
import java.util.Map.Entry;
import processing.serial.*; //for serial communication to Arduino/OpenBCI
import java.awt.event.*; //to allow for event listener on screen resize
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//used to switch between application states
int systemMode = 0; /* Modes: 0 = system stopped/control panel setings / 10 = gui / 20 = help guide */
//choose where to get the EEG data
final int DATASOURCE_NORMAL = 3; //looking for signal from OpenBCI board via Serial/COM port, no Aux data
final int DATASOURCE_PLAYBACKFILE = 1; //playback from a pre-recorded text file
final int DATASOURCE_SYNTHETIC = 2; //Synthetically generated data
final int DATASOURCE_NORMAL_W_AUX = 0; // new default, data from serial with Accel data CHIP 2014-11-03
public int eegDataSource = -1; //default to none of the options
//here are variables that are used if loading input data from a CSV text file...double slash ("\\") is necessary to make a single slash
String playbackData_fname = "N/A"; //only used if loading input data from a file
// String playbackData_fname; //leave blank to cause an "Open File" dialog box to appear at startup. USEFUL!
float playback_speed_fac = 1.0f; //make 1.0 for real-time. larger for faster playback
int currentTableRowIndex = 0;
Table_CSV playbackData_table;
int nextPlayback_millis = -100; //any negative number
//Global Serial/COM communications constants
OpenBCI_ADS1299 openBCI = new OpenBCI_ADS1299(); //dummy creation to get access to constants, create real one later
String openBCI_portName = "N/A"; //starts as N/A but is selected from control panel to match your OpenBCI USB Dongle's serial/COM
int openBCI_baud = 115200; //baud rate from the Arduino
////// ---- Define variables related to OpenBCI board operations
//Define number of channels from openBCI...first EEG channels, then aux channels
int nchan = 8; //Normally, 8 or 16. Choose a smaller number to show fewer on the GUI
int n_aux_ifEnabled = 3; // this is the accelerometer data CHIP 2014-11-03
//define variables related to warnings to the user about whether the EEG data is nearly railed (and, therefore, of dubious quality)
DataStatus is_railed[];
final int threshold_railed = int(pow(2, 23)-1000); //fully railed should be +/- 2^23, so set this threshold close to that value
final int threshold_railed_warn = int(pow(2, 23)*0.75); //set a somewhat smaller value as the warning threshold
//OpenBCI SD Card setting (if eegDataSource == 0)
int sdSetting = 0; //0 = do not write; 1 = 5 min; 2 = 15 min; 3 = 30 min; etc...
String sdSettingString = "Do not write to SD";
//openBCI data packet
final int nDataBackBuff = 3*(int)openBCI.get_fs_Hz();
DataPacket_ADS1299 dataPacketBuff[] = new DataPacket_ADS1299[nDataBackBuff]; //allocate the array, but doesn't call constructor. Still need to call the constructor!
int curDataPacketInd = -1;
int lastReadDataPacketInd = -1;
//related to sync'ing communiction to OpenBCI hardware?
boolean currentlySyncing = false;
long timeOfLastCommand = 0;
////// ---- End variables related to the OpenBCI boards
// define some timing variables for this program's operation
long timeOfLastFrame = 0;
int newPacketCounter = 0;
long timeOfInit;
long timeSinceStopRunning = 1000;
int prev_time_millis = 0;
final int nPointsPerUpdate = 50; //update the GUI after this many data points have been received
//define some data fields for handling data here in processing
float dataBuffX[]; //define the size later
float dataBuffY_uV[][]; //2D array to handle multiple data channels, each row is a new channel so that dataBuffY[3][] is channel 4
float dataBuffY_filtY_uV[][];
float yLittleBuff[] = new float[nPointsPerUpdate];
float yLittleBuff_uV[][] = new float[nchan][nPointsPerUpdate]; //small buffer used to send data to the filters
float data_elec_imp_ohm[];
//variables for writing EEG data out to a file
OutputFile_rawtxt fileoutput;
String output_fname;
String fileName = "N/A";
// Serial output
String serial_output_portName = "/dev/tty.usbmodem1411"; //must edit this based on the name of the serial/COM port
Serial serial_output;
int serial_output_baud = 115200; //baud rate from the Arduino
//fft constants
int Nfft = 256; //set resolution of the FFT. Use N=256 for normal, N=512 for MU waves
FFT fftBuff[] = new FFT[nchan]; //from the minim library
float[] smoothFac = new float[]{0.75, 0.9, 0.95, 0.98, 0.0, 0.5};
int smoothFac_ind = 0; //initial index into the smoothFac array
//Control Panel for (re)configuring system settings
Button controlPanelCollapser;
PlotFontInfo fontInfo;
//program constants
boolean isRunning = false;
boolean redrawScreenNow = true;
int openBCI_byteCount = 0;
int inByte = -1; // Incoming serial data
//for screen resizing
boolean screenHasBeenResized = false;
float timeOfLastScreenResize = 0;
float timeOfGUIreinitialize = 0;
int reinitializeGUIdelay = 125;
//Tao's variabiles
int widthOfLastScreen = 0;
int heightOfLastScreen = 0;
//set window size
int win_x = 1024; //window width
int win_y = 768; //window height
PImage logo;
PFont f1;
PFont f2;
PFont f3;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//========================SETUP============================//
//========================SETUP============================//
//========================SETUP============================//
void setup() {
println("Welcome to the Processing-based OpenBCI GUI!"); //Welcome line.
println("Last update: 6/25/2016"); //Welcome line.
println("For more information about how to work with this code base, please visit: http://docs.openbci.com/tutorials/01-GettingStarted");
println("For specific questions, please post them to the Software section of the OpenBCI Forum: http://openbci.com/index.php/forum/#/categories/software");
//open window
size(1024, 768, P2D);
// size(displayWidth, displayHeight, P2D);
//if (frame != null) frame.setResizable(true); //make window resizable
//attach exit handler
//prepareExitHandler();
frameRate(30); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
smooth(); //turn this off if it's too slow
surface.setResizable(true); //updated from frame.setResizable in Processing 2
widthOfLastScreen = width; //for screen resizing (Thank's Tao)
heightOfLastScreen = height;
setupContainers();
//V1 FONTS
f1 = createFont("fonts/Raleway-SemiBold.otf", 16);
f2 = createFont("fonts/Raleway-Regular.otf", 15);
f3 = createFont("fonts/Raleway-SemiBold.otf", 15);
//V2 FONTS
//f1 = createFont("fonts/Montserrat-SemiBold.otf", 16);
//f2 = createFont("fonts/Montserrat-Light.otf", 15);
//f3 = createFont("fonts/Montserrat-SemiBold.otf", 15);
//listen for window resize ... used to adjust elements in application
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (e.getSource()==frame) {
println("OpenBCI_GUI: setup: RESIZED");
screenHasBeenResized = true;
timeOfLastScreenResize = millis();
// initializeGUI();
}
}
}
);
//set up controlPanelCollapser button
fontInfo = new PlotFontInfo();
helpWidget = new HelpWidget(0, win_y - 30, win_x, 30);
// println("..." + this);
// controlPanelCollapser = new Button(2, 2, 256, int((float)win_y*(0.03f)), "SYSTEM CONTROL PANEL", fontInfo.buttonLabel_size);
controlPanelCollapser = new Button(2, 2, 256, 26, "SYSTEM CONTROL PANEL", fontInfo.buttonLabel_size);
controlPanelCollapser.setIsActive(true);
controlPanelCollapser.makeDropdownButton(true);
//from the user's perspective, the program hangs out on the ControlPanel until the user presses "Start System".
print("Graphics & GUI Library: ");
controlPanel = new ControlPanel(this);
//The effect of "Start System" is that initSystem() gets called, which starts up the conneciton to the OpenBCI
//hardware (via the "updateSyncState()" process) as well as initializing the rest of the GUI elements.
//Once the hardware is synchronized, the main GUI is drawn and the user switches over to the main GUI.
logo = loadImage("logo2.png");
playground = new Playground(navBarHeight);
//attempt to open a serial port for "output"
try {
verbosePrint("OpenBCI_GUI.pde: attempting to open serial port for data output = " + serial_output_portName);
serial_output = new Serial(this, serial_output_portName, serial_output_baud); //open the com port
serial_output.clear(); // clear anything in the com port's buffer
}
catch (RuntimeException e) {
verbosePrint("OpenBCI_GUI.pde: *** ERROR ***: Could not open " + serial_output_portName);
}
myPresentation = new Presentation();
}
//====================== END-OF-SETUP ==========================//
//====================== END-OF-SETUP ==========================//
//====================== END-OF-SETUP ==========================//
//======================== DRAW LOOP =============================//
//======================== DRAW LOOP =============================//
//======================== DRAW LOOP =============================//
void draw() {
drawLoop_counter++; //signPost("10");
systemUpdate(); //signPost("20");
systemDraw(); //signPost("30");
}
//====================== END-OF-DRAW ==========================//
//====================== END-OF-DRAW ==========================//
//====================== END-OF-DRAW ==========================//
int pointCounter = 0;
int prevBytes = 0;
int prevMillis = millis();
int byteRate_perSec = 0;
int drawLoop_counter = 0;
//used to init system based on initial settings...Called from the "Start System" button in the GUI's ControlPanel
void initSystem() {
verbosePrint("OpenBCI_GUI: initSystem: -- Init 0 --");
timeOfInit = millis(); //store this for timeout in case init takes too long
//prepare data variables
verbosePrint("OpenBCI_GUI: initSystem: Preparing data variables...");
dataBuffX = new float[(int)(dataBuff_len_sec * openBCI.get_fs_Hz())];
dataBuffY_uV = new float[nchan][dataBuffX.length];
dataBuffY_filtY_uV = new float[nchan][dataBuffX.length];
//data_std_uV = new float[nchan];
data_elec_imp_ohm = new float[nchan];
is_railed = new DataStatus[nchan];
for (int i=0; i<nchan; i++) is_railed[i] = new DataStatus(threshold_railed, threshold_railed_warn);
for (int i=0; i<nDataBackBuff; i++) {
//dataPacketBuff[i] = new DataPacket_ADS1299(nchan+n_aux_ifEnabled);
// dataPacketBuff[i] = new DataPacket_ADS1299(OpenBCI_Nchannels+n_aux_ifEnabled);
dataPacketBuff[i] = new DataPacket_ADS1299(nchan, n_aux_ifEnabled);
}
dataProcessing = new DataProcessing(nchan, openBCI.get_fs_Hz());
dataProcessing_user = new DataProcessing_User(nchan, openBCI.get_fs_Hz());
//initialize the data
prepareData(dataBuffX, dataBuffY_uV, openBCI.get_fs_Hz());
verbosePrint("OpenBCI_GUI: initSystem: -- Init 1 --");
//initialize the FFT objects
for (int Ichan=0; Ichan < nchan; Ichan++) {
verbosePrint("a--"+Ichan);
fftBuff[Ichan] = new FFT(Nfft, openBCI.get_fs_Hz());
}; //make the FFT objects
verbosePrint("OpenBCI_GUI: initSystem: b");
initializeFFTObjects(fftBuff, dataBuffY_uV, Nfft, openBCI.get_fs_Hz());
//prepare some signal processing stuff
//for (int Ichan=0; Ichan < nchan; Ichan++) { detData_freqDomain[Ichan] = new DetectionData_FreqDomain(); }
verbosePrint("OpenBCI_GUI: initSystem: -- Init 2 --");
//prepare the source of the input data
switch (eegDataSource) {
case DATASOURCE_NORMAL:
case DATASOURCE_NORMAL_W_AUX:
// int nDataValuesPerPacket = OpenBCI_Nchannels;
int nEEDataValuesPerPacket = nchan;
boolean useAux = false;
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) useAux = true; //switch this back to true CHIP 2014-11-04
openBCI = new OpenBCI_ADS1299(this, openBCI_portName, openBCI_baud, nEEDataValuesPerPacket, useAux, n_aux_ifEnabled); //this also starts the data transfer after XX seconds
break;
case DATASOURCE_SYNTHETIC:
//do nothing
break;
case DATASOURCE_PLAYBACKFILE:
//open and load the data file
println("OpenBCI_GUI: initSystem: loading playback data from " + playbackData_fname);
try {
playbackData_table = new Table_CSV(playbackData_fname);
}
catch (Exception e) {
println("OpenBCI_GUI: initSystem: could not open file for playback: " + playbackData_fname);
println(" : quitting...");
exit();
}
println("OpenBCI_GUI: initSystem: loading complete. " + playbackData_table.getRowCount() + " rows of data, which is " + round(float(playbackData_table.getRowCount())/openBCI.get_fs_Hz()) + " seconds of EEG data");
//removing first column of data from data file...the first column is a time index and not eeg data
playbackData_table.removeColumn(0);
break;
default:
}
verbosePrint("OpenBCI_GUI: initSystem: -- Init 3 --");
//initilize the GUI
initializeGUI();
//final config
// setBiasState(openBCI.isBiasAuto);
verbosePrint("OpenBCI_GUI: initSystem: -- Init 4 --");
//open data file
if ((eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX)) openNewLogFile(fileName); //open a new log file
nextPlayback_millis = millis(); //used for synthesizeData and readFromFile. This restarts the clock that keeps the playback at the right pace.
if (eegDataSource != DATASOURCE_NORMAL && eegDataSource != DATASOURCE_NORMAL_W_AUX) {
systemMode = 10; //tell system it's ok to leave control panel and start interfacing GUI
}
//sync GUI default settings with OpenBCI's default settings...
// openBCI.syncWithHardware(); //this starts the sequence off ... read in OpenBCI_ADS1299 iterates through the rest based on the ASCII trigger "$$$"
// verbosePrint("OpenBCI_GUI: initSystem: -- Init 5 [COMPLETE] --");
}
//halt the data collection
void haltSystem() {
println("openBCI_GUI: haltSystem: Halting system for reconfiguration of settings...");
stopRunning(); //stop data transfer
//reset variables for data processing
curDataPacketInd = -1;
lastReadDataPacketInd = -1;
pointCounter = 0;
prevBytes = 0;
prevMillis = millis();
byteRate_perSec = 0;
drawLoop_counter = 0;
// eegDataSource = -1;
//set all data source list items inactive
// stopDataTransfer(); // make sure to stop data transfer, if data is streaming and being drawn
if ((eegDataSource == DATASOURCE_NORMAL) || (eegDataSource == DATASOURCE_NORMAL_W_AUX)) {
closeLogFile(); //close log file
openBCI.closeSDandSerialPort();
}
systemMode = 0;
}
void systemUpdate() { // for updating data values and variables
//update the sync state with the OpenBCI hardware
openBCI.updateSyncState(sdSetting);
//prepare for updating the GUI
win_x = width;
win_y = height;
//updates while in intro screen
if (systemMode == 0) {
}
if (systemMode == 10) {
if (isRunning) {
//get the data, if it is available
pointCounter = getDataIfAvailable(pointCounter);
//has enough data arrived to process it and update the GUI?
if (pointCounter >= nPointsPerUpdate) {
pointCounter = 0; //reset for next time
//process the data
processNewData();
//try to detect the desired signals, do it in frequency space...for OpenBCI_GUI_Simpler
//detectInFreqDomain(fftBuff,inband_Hz,guard_Hz,detData_freqDomain);
//gui.setDetectionData_freqDomain(detData_freqDomain);
//tell the GUI that it has received new data via dumping new data into arrays that the GUI has pointers to
// println("packet counter = " + newPacketCounter);
// for(int i = 0; i < dataProcessing.data_std_uV.length; i++){
// println("dataProcessing.data_std_uV[" + i + "] = " + dataProcessing.data_std_uV[i]);
// }
if ((millis() - timeOfGUIreinitialize) > reinitializeGUIdelay) { //wait 1 second for GUI to reinitialize
try {
gui.update(dataProcessing.data_std_uV, data_elec_imp_ohm);
}
catch (Exception e) {
println(e.getMessage());
reinitializeGUIdelay = reinitializeGUIdelay * 2;
println("OpenBCI_GUI: systemUpdate: New GUI reinitialize delay = " + reinitializeGUIdelay);
}
} else {
println("OpenBCI_GUI: systemUpdate: reinitializing GUI after resize... not updating GUI");
}
///add raw data to spectrogram...if the correct channel...
//...look for the first channel that is active (meaning button is not active) or, if it
// hasn't yet sent any data, send the last channel even if the channel is off
// if (sendToSpectrogram & (!(gui.chanButtons[Ichan].isActive()) | (Ichan == (nchan-1)))) { //send data to spectrogram
// sendToSpectrogram = false; //prevent us from sending more data after this time through
// for (int Idata=0;Idata < nPointsPerUpdate;Idata++) {
// gui.spectrogram.addDataPoint(yLittleBuff_uV[Ichan][Idata]);
// gui.tellGUIWhichChannelForSpectrogram(Ichan);
// //gui.spectrogram.addDataPoint(100.0f+(float)Idata);
// }
// }
redrawScreenNow=true;
} else {
//not enough data has arrived yet... only update the channel controller
}
}
gui.cc.update(); //update Channel Controller even when not updating certain parts of the GUI... (this is a bit messy...)
//alternative component listener function (line 177 - 187 frame.addComponentListener) for processing 3,
if (widthOfLastScreen != width || heightOfLastScreen != height) {
println("OpenBCI_GUI: setup: RESIZED");
screenHasBeenResized = true;
timeOfLastScreenResize = millis();
widthOfLastScreen = width;
heightOfLastScreen = height;
}
//re-initialize GUI if screen has been resized and it's been more than 1/2 seccond (to prevent reinitialization of GUI from happening too often)
if (screenHasBeenResized == true && (millis() - timeOfLastScreenResize) > reinitializeGUIdelay) {
screenHasBeenResized = false;
println("systemUpdate: reinitializing GUI");
timeOfGUIreinitialize = millis();
initializeGUI();
playground.x = width; //reset the x for the playground...
}
playground.update();
}
controlPanel.update();
}
void systemDraw() { //for drawing to the screen
//redraw the screen...not every time, get paced by when data is being plotted
background(bgColor); //clear the screen
if (systemMode == 10) {
int drawLoopCounter_thresh = 100;
if ((redrawScreenNow) || (drawLoop_counter >= drawLoopCounter_thresh)) {
//if (drawLoop_counter >= drawLoopCounter_thresh) println("OpenBCI_GUI: redrawing based on loop counter...");
drawLoop_counter=0; //reset for next time
redrawScreenNow = false; //reset for next time
//update the title of the figure;
switch (eegDataSource) {
case DATASOURCE_NORMAL:
case DATASOURCE_NORMAL_W_AUX:
surface.setTitle(int(frameRate) + " fps, Byte Count = " + openBCI_byteCount + ", bit rate = " + byteRate_perSec*8 + " bps" + ", " + int(float(fileoutput.getRowsWritten())/openBCI.get_fs_Hz()) + " secs Saved, Writing to " + output_fname);
break;
case DATASOURCE_SYNTHETIC:
surface.setTitle(int(frameRate) + " fps, Using Synthetic EEG Data");
break;
case DATASOURCE_PLAYBACKFILE:
surface.setTitle(int(frameRate) + " fps, Playing " + int(float(currentTableRowIndex)/openBCI.get_fs_Hz()) + " of " + int(float(playbackData_table.getRowCount())/openBCI.get_fs_Hz()) + " secs, Reading from: " + playbackData_fname);
break;
}
}
//wait 1 second for GUI to reinitialize
if ((millis() - timeOfGUIreinitialize) > reinitializeGUIdelay) {
// println("attempting to draw GUI...");
try {
// println("GUI DRAW!!! " + millis());
pushStyle();
fill(255);
noStroke();
rect(0, 0, width, navBarHeight);
popStyle();
gui.draw(); //draw the GUI
// playground.draw();
}
catch (Exception e) {
println(e.getMessage());
reinitializeGUIdelay = reinitializeGUIdelay * 2;
println("OpenBCI_GUI: systemDraw: New GUI reinitialize delay = " + reinitializeGUIdelay);
}
} else {
//reinitializing GUI after resize
println("OpenBCI_GUI: systemDraw: reinitializing GUI after resize... not drawing GUI");
}
playground.draw();
dataProcessing_user.draw();
drawContainers();
} else { //systemMode != 10
//still print title information about fps
surface.setTitle(int(frameRate) + " fps — OpenBCI GUI");
}
//control panel
if (controlPanel.isOpen) {
controlPanel.draw();
}
controlPanelCollapser.draw();
helpWidget.draw();
if ((openBCI.get_state() == openBCI.STATE_COMINIT || openBCI.get_state() == openBCI.STATE_SYNCWITHHARDWARE) && systemMode == 0) {
//make out blink the text "Initalizing GUI..."
if (millis()%1000 < 500) {
output("Iniitializing communication w/ your OpenBCI board...");
} else {
output("");
}
if (millis() - timeOfInit > 12000) {
haltSystem();
initSystemButton.but_txt = "START SYSTEM";
output("Init timeout. Verify your Serial/COM Port. Power DOWN/UP your OpenBCI & USB Dongle. Then retry Initialization.");
}
}
if (drawPresentation) {
myPresentation.draw();
dataProcessing_user.drawTriggerFeedback();
}
// use commented code below to verify frameRate and check latency
// println("Time since start: " + millis() + " || Time since last frame: " + str(millis()-timeOfLastFrame));
// timeOfLastFrame = millis();
}
-134
Ver Arquivo
@@ -1,134 +0,0 @@
//////////////////////////////////////////////////////////////////////////
//
// Playground Class
// Created: 11/22/14 by Conor Russomanno
// An extra interface pane for additional GUI features
//
//////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
Playground playground;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
}
};
-119
Ver Arquivo
@@ -1,119 +0,0 @@
///////////////////////////////////////////////////////////////////////////
//
// 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
//
///////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
Presentation myPresentation;
boolean drawPresentation = false;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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();
}
}
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -1,4 +0,0 @@
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.
@@ -1,4 +0,0 @@
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
-425
Ver Arquivo
@@ -1,425 +0,0 @@
//////////////////
//
// 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;
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
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;
}
};
-175
Ver Arquivo
@@ -1,175 +0,0 @@
///////////////////////////////////////////////
//
// Created: Chip Audette, Oct 2013
// Modified: through May 2014
//
// No warranty. Use at your own risk. Use for whatever you'd like.
//
///////////////////////////////////////////////
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
}
}
Arquivo binário não exibido.
@@ -1,18 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,18 +0,0 @@
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
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.
Arquivo binário não exibido.

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais