Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add insertMarker and getMarkerChannel methods to all relevant boards
Browse files Browse the repository at this point in the history
retiutut committed Aug 3, 2023
1 parent 4303a75 commit 160e4c5
Showing 11 changed files with 281 additions and 228 deletions.
256 changes: 130 additions & 126 deletions OpenBCI_GUI/Board.pde
Original file line number Diff line number Diff line change
@@ -1,126 +1,130 @@
import org.apache.commons.lang3.tuple.Pair;

abstract class Board implements DataSource {

private FixedStack<double[]> accumulatedData = new FixedStack<double[]>();
private double[][] dataThisFrame;
protected PacketLossTracker packetLossTracker;

// accessible by all boards, can be returned as valid empty data
protected double[][] emptyData;

@Override
public boolean initialize() {
boolean res = initializeInternal();

double[] fillData = new double[getTotalChannelCount()];
accumulatedData.setSize(getCurrentBoardBufferSize());
accumulatedData.fill(fillData);

emptyData = new double[getTotalChannelCount()][0];

packetLossTracker = setupPacketLossTracker();

return res;
}

@Override
public void uninitialize() {
uninitializeInternal();
}

@Override
public void startStreaming() {
packetLossTracker.onStreamStart();
}

@Override
public void stopStreaming() {

// empty
}

@Override
public void update() {
updateInternal();

dataThisFrame = getNewDataInternal();

for (int i = 0; i < dataThisFrame[0].length; i++) {
double[] newEntry = new double[getTotalChannelCount()];
for (int j = 0; j < getTotalChannelCount(); j++) {
newEntry[j] = dataThisFrame[j][i];
}

accumulatedData.push(newEntry);
}

if( packetLossTracker != null) {
//TODO: make all API including getNewDataInternal() return List<double[]>
// and we can just pass dataThisFrame here.
packetLossTracker.addSamples(getData(dataThisFrame[0].length));
}
}

@Override
public int getNumEXGChannels() {
return getEXGChannels().length;
}

// returns all the data this board has received in this frame
@Override
public double[][] getFrameData() {
return dataThisFrame;
}

@Override
public List<double[]> getData(int maxSamples) {
int endIndex = accumulatedData.size();
int startIndex = max(0, endIndex - maxSamples);

return accumulatedData.subList(startIndex, endIndex);
}

public String[] getChannelNames() {
String[] names = new String[getTotalChannelCount()];
Arrays.fill(names, "Other");

names[getTimestampChannel()] = "Timestamp";
names[getSampleIndexChannel()] = "Sample Index";

int[] exgChannels = getEXGChannels();
for (int i=0; i<exgChannels.length; i++) {
names[exgChannels[i]] = "EXG Channel " + i;
}

addChannelNamesInternal(names);
return names;
}

public PacketLossTracker getPacketLossTracker() {
return packetLossTracker;
}

public abstract boolean isConnected();

public abstract boolean isStreaming();

public abstract Pair <Boolean, String> sendCommand(String command);

// ***************************************
// protected methods implemented by board

// implemented by each board class and used internally here to accumulate the FixedStack
// and provide with public interfaces getFrameData() and getData(int)
protected abstract double[][] getNewDataInternal();

protected abstract boolean initializeInternal();

protected abstract void uninitializeInternal();

protected abstract void updateInternal();

protected abstract void addChannelNamesInternal(String[] channelNames);

protected abstract PacketLossTracker setupPacketLossTracker();
};
import org.apache.commons.lang3.tuple.Pair;

abstract class Board implements DataSource {

private FixedStack<double[]> accumulatedData = new FixedStack<double[]>();
private double[][] dataThisFrame;
protected PacketLossTracker packetLossTracker;

// accessible by all boards, can be returned as valid empty data
protected double[][] emptyData;

@Override
public boolean initialize() {
boolean res = initializeInternal();

double[] fillData = new double[getTotalChannelCount()];
accumulatedData.setSize(getCurrentBoardBufferSize());
accumulatedData.fill(fillData);

emptyData = new double[getTotalChannelCount()][0];

packetLossTracker = setupPacketLossTracker();

return res;
}

@Override
public void uninitialize() {
uninitializeInternal();
}

@Override
public void startStreaming() {
packetLossTracker.onStreamStart();
}

@Override
public void stopStreaming() {

// empty
}

@Override
public void update() {
updateInternal();

dataThisFrame = getNewDataInternal();

for (int i = 0; i < dataThisFrame[0].length; i++) {
double[] newEntry = new double[getTotalChannelCount()];
for (int j = 0; j < getTotalChannelCount(); j++) {
newEntry[j] = dataThisFrame[j][i];
}

accumulatedData.push(newEntry);
}

if( packetLossTracker != null) {
//TODO: make all API including getNewDataInternal() return List<double[]>
// and we can just pass dataThisFrame here.
packetLossTracker.addSamples(getData(dataThisFrame[0].length));
}
}

@Override
public int getNumEXGChannels() {
return getEXGChannels().length;
}

// returns all the data this board has received in this frame
@Override
public double[][] getFrameData() {
return dataThisFrame;
}

@Override
public List<double[]> getData(int maxSamples) {
int endIndex = accumulatedData.size();
int startIndex = max(0, endIndex - maxSamples);

return accumulatedData.subList(startIndex, endIndex);
}

public String[] getChannelNames() {
String[] names = new String[getTotalChannelCount()];
Arrays.fill(names, "Other");

names[getTimestampChannel()] = "Timestamp";
names[getSampleIndexChannel()] = "Sample Index";

int[] exgChannels = getEXGChannels();
for (int i=0; i<exgChannels.length; i++) {
names[exgChannels[i]] = "EXG Channel " + i;
}

addChannelNamesInternal(names);
return names;
}

public PacketLossTracker getPacketLossTracker() {
return packetLossTracker;
}

public abstract boolean isConnected();

public abstract boolean isStreaming();

public abstract Pair <Boolean, String> sendCommand(String command);

public abstract void insertMarker(int value);

public abstract void insertMarker(double value);

// ***************************************
// protected methods implemented by board

// implemented by each board class and used internally here to accumulate the FixedStack
// and provide with public interfaces getFrameData() and getData(int)
protected abstract double[][] getNewDataInternal();

protected abstract boolean initializeInternal();

protected abstract void uninitializeInternal();

protected abstract void updateInternal();

protected abstract void addChannelNamesInternal(String[] channelNames);

protected abstract PacketLossTracker setupPacketLossTracker();
};
3 changes: 2 additions & 1 deletion OpenBCI_GUI/BoardBrainFlowSynthetic.pde
Original file line number Diff line number Diff line change
@@ -96,9 +96,10 @@ class BoardBrainFlowSynthetic extends BoardBrainFlow implements AccelerometerCap

@Override
protected void addChannelNamesInternal(String[] channelNames) {
for (int i=0; i<getAccelerometerChannels().length; i++) {
for (int i = 0; i < getAccelerometerChannels().length; i++) {
channelNames[getAccelerometerChannels()[i]] = "Accel Channel " + i;
}
channelNames[getMarkerChannel()] = "Marker Channel";
}

@Override
16 changes: 14 additions & 2 deletions OpenBCI_GUI/BoardBrainflow.pde
Original file line number Diff line number Diff line change
@@ -20,6 +20,8 @@ abstract class BoardBrainFlow extends Board {
protected double time_last_datapoint = -1.0;
protected boolean data_popup_displayed = false;

private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

/* Abstract Functions.
* Implement these in your board.
*/
@@ -302,6 +304,7 @@ abstract class BoardBrainFlow extends Board {
return otherChannelsCache;
}

@Override
public int getMarkerChannel() {
if (markerChannelCache < 0) {
try {
@@ -314,17 +317,26 @@ abstract class BoardBrainFlow extends Board {
return markerChannelCache;
}

@Override
public void insertMarker(double value) {
if (isConnected()) {
if (isConnected() && streaming) {
try {
boardShim.insert_marker(value);
//long currentTime = (long)(System.currentTimeMillis() * 1000.0);
String currentTimeString = dateFormat.format(new Date());
StringBuilder markerNotification = new StringBuilder("Inserted marker ");
markerNotification.append(value);
markerNotification.append(" at approximately: ");
markerNotification.append(currentTimeString);
println(markerNotification.toString());
} catch (BrainFlowError e) {
e.printStackTrace();
}
}
}

@Override
public void insertMarker(int value) {
double dvalue = (double) value;
insertMarker((double) value);
}
};
1 change: 1 addition & 0 deletions OpenBCI_GUI/BoardCyton.pde
Original file line number Diff line number Diff line change
@@ -608,6 +608,7 @@ implements ImpedanceSettingsBoard, AccelerometerCapableBoard, AnalogCapableBoard
for (int i=0; i<getAnalogChannels().length; i++) {
channelNames[getAnalogChannels()[i]] = "Analog Channel " + i;
}
channelNames[getMarkerChannel()] = "Marker Channel";
}

@Override
1 change: 1 addition & 0 deletions OpenBCI_GUI/BoardGanglion.pde
Original file line number Diff line number Diff line change
@@ -257,6 +257,7 @@ abstract class BoardGanglion extends BoardBrainFlow implements AccelerometerCapa
for (int i=0; i<getAccelerometerChannels().length; i++) {
channelNames[getAccelerometerChannels()[i]] = "Accel Channel " + i;
}
channelNames[getMarkerChannel()] = "Marker Channel";
}

@Override
187 changes: 101 additions & 86 deletions OpenBCI_GUI/BoardNull.pde
Original file line number Diff line number Diff line change
@@ -1,86 +1,101 @@
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.ImmutablePair;

/* This class does nothing, it serves as a signal that the board we are using
* is null, but does not crash if we use it.
*/
class BoardNull extends Board {

@Override
public boolean initializeInternal() {
return true;
}

@Override
public void uninitializeInternal() {
// empty
}

@Override
public void updateInternal() {
// empty
}

@Override
public boolean isConnected() {
return false;
}

@Override
public boolean isStreaming() {
return false;
}

@Override
public int getSampleRate() {
return 0;
}

@Override
public int[] getEXGChannels() {
return new int[0];
}

@Override
public int getTimestampChannel() {
return 0;
}

@Override
public int getSampleIndexChannel() {
return 0;
}

@Override
public void setEXGChannelActive(int channelIndex, boolean active) {
// empty
}

@Override
public boolean isEXGChannelActive(int channelIndex) {
return false;
}

@Override
public Pair<Boolean, String> sendCommand(String command) {
return new ImmutablePair<Boolean, String>(Boolean.valueOf(true), "");
}

protected double[][] getNewDataInternal() {
return new double[1][0];
}

@Override
public int getTotalChannelCount() {
return 0;
}

protected void addChannelNamesInternal(String[] channelNames) {
// nothing
}

@Override
protected PacketLossTracker setupPacketLossTracker() {
return null;
}
};
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.ImmutablePair;

/* This class does nothing, it serves as a signal that the board we are using
* is null, but does not crash if we use it.
*/
class BoardNull extends Board {

@Override
public boolean initializeInternal() {
return true;
}

@Override
public void uninitializeInternal() {
// empty
}

@Override
public void updateInternal() {
// empty
}

@Override
public boolean isConnected() {
return false;
}

@Override
public boolean isStreaming() {
return false;
}

@Override
public int getSampleRate() {
return 0;
}

@Override
public int[] getEXGChannels() {
return new int[0];
}

@Override
public int getTimestampChannel() {
return 0;
}

@Override
public int getSampleIndexChannel() {
return 0;
}

@Override
public int getMarkerChannel() {
return 0;
}

@Override
public void insertMarker(int marker) {
// empty
}

@Override
public void insertMarker(double value) {
// empty
}

@Override
public void setEXGChannelActive(int channelIndex, boolean active) {
// empty
}

@Override
public boolean isEXGChannelActive(int channelIndex) {
return false;
}

@Override
public Pair<Boolean, String> sendCommand(String command) {
return new ImmutablePair<Boolean, String>(Boolean.valueOf(true), "");
}

protected double[][] getNewDataInternal() {
return new double[1][0];
}

@Override
public int getTotalChannelCount() {
return 0;
}

protected void addChannelNamesInternal(String[] channelNames) {
// nothing
}

@Override
protected PacketLossTracker setupPacketLossTracker() {
return null;
}
};
2 changes: 2 additions & 0 deletions OpenBCI_GUI/DataSource.pde
Original file line number Diff line number Diff line change
@@ -32,4 +32,6 @@ interface DataSource {
public int getTotalChannelCount();

public boolean isStreaming();

public int getMarkerChannel();
};
5 changes: 5 additions & 0 deletions OpenBCI_GUI/DataSourcePlayback.pde
Original file line number Diff line number Diff line change
@@ -219,6 +219,11 @@ abstract class DataSourcePlayback implements DataSource, FileBoard {
return float(getCurrentSample()) / float(getSampleRate());
}

@Override
public int getMarkerChannel() {
return underlyingBoard.getMarkerChannel();
}

public void goToIndex(int index) {
currentSampleExg = index;
}
6 changes: 6 additions & 0 deletions OpenBCI_GUI/DataSourceSDCard.pde
Original file line number Diff line number Diff line change
@@ -205,6 +205,12 @@ class DataSourceSDCard implements DataSource, FileBoard, AccelerometerCapableBoa
return totalChannels;
}

@Override
public int getMarkerChannel() {
//There is no software marker channel for SD card playback
return -1;
}

@Override
public double[][] getFrameData() {
double[][] array = new double[getTotalChannelCount()][numNewSamplesThisFrame];
10 changes: 7 additions & 3 deletions OpenBCI_GUI/DataWriterODF.pde
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ public class DataWriterODF {

String[] colNames = getChannelNames();

for (int i=0; i<colNames.length; i++) {
for (int i = 0; i < colNames.length; i++) {
output.print(colNames[i]);
output.print(", ");
}
@@ -37,8 +37,8 @@ public class DataWriterODF {

public void append(double[][] data) {
//get current date time with Date()
for (int iSample=0; iSample<data[0].length; iSample++) {
for (int iChan=0; iChan<data.length; iChan++) {
for (int iSample = 0; iSample < data[0].length; iSample++) {
for (int iChan = 0; iChan < data.length; iChan++) {
output.print(data[iChan][iSample]);
output.print(", ");
}
@@ -81,5 +81,9 @@ public class DataWriterODF {
protected int getTimestampChannel() {
return ((Board)currentBoard).getTimestampChannel();
}

protected int getMarkerChannel() {
return ((Board)currentBoard).getMarkerChannel();
}

};
22 changes: 12 additions & 10 deletions OpenBCI_GUI/W_Marker.pde
Original file line number Diff line number Diff line change
@@ -55,7 +55,9 @@ class W_Marker extends Widget {

private void createMarkerButtons() {
for (int i = 0; i < NUMBER_OF_MARKER_BUTTONS; i++) {
markerButtons[i] = createMarkerButton(i, x + 10 + (i * MARKER_BUTTON_WIDTH), y + 10);
//Create marker buttons
//Marker number is i + 1 because marker numbers start at 1, not 0. Otherwise, will throw BrainFlow error.
markerButtons[i] = createMarkerButton(i + 1, x + 10 + (i * MARKER_BUTTON_WIDTH), y + 10);
}
}

@@ -64,39 +66,39 @@ class W_Marker extends Widget {
newButton.setBorderColor(OBJECT_BORDER_GREY);
newButton.onRelease(new CallbackListener() {
public void controlEvent(CallbackEvent theEvent) {
insertMarker(markerNumber);
insertMarkerFromKeyboardOrButton(markerNumber);
}
});
newButton.setDescription("Click to insert marker " + markerNumber + " into the data stream.");
return newButton;
}

//Called in Interactivity.pde when a key is pressed
//Returns true if a marker key was pressed, false otherwise
//Can be used to check for marker key presses even when this widget is not active
public boolean checkForMarkerKeyPress(char keyPress) {
switch (keyPress) {
case 'z':
insertMarker(0);
insertMarkerFromKeyboardOrButton(1);
return true;
case 'x':
insertMarker(1);
insertMarkerFromKeyboardOrButton(2);
return true;
case 'c':
insertMarker(2);
insertMarkerFromKeyboardOrButton(3);
return true;
case 'v':
insertMarker(3);
insertMarkerFromKeyboardOrButton(4);
return true;
default:
return false;
}
}

private void insertMarker(int markerNumber) {
int markerChannel = ((BoardBrainFlow)currentBoard).getMarkerChannel();
private void insertMarkerFromKeyboardOrButton(int markerNumber) {
if (markerChannel != -1) {
((BoardBrainFlow)currentBoard).insertMarker(markerNumber);
((Board)currentBoard).insertMarker(markerNumber);
}
println("Marker " + markerNumber + " inserted.");
}

};

0 comments on commit 160e4c5

Please sign in to comment.