Skip to content

Commit 8eabc56

Browse files
authored
Merge pull request #1174 from OpenBCI/software-marker-widget
Add Software Marker Widget
2 parents c5a35e9 + 7987509 commit 8eabc56

26 files changed

Lines changed: 1040 additions & 68 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Improvements
44
- Update repository to Processing 4.2 #1111
5+
- Add Software Marker Widget #1091
56

67
# v5.2.1
78

Networking-Test-Kit/LSL/lslStreamTest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ def testLSLSamplingRate():
2929
# print( len(chunk) )
3030
totalNumSamples += len(chunk)
3131
# print(chunk)
32+
i = 0
3233
for sample in chunk:
33-
print(sample)
34+
print(sample, timestamp[i])
3435
validSamples += 1
36+
i += 1
3537

3638
print( "Number of Chunks and Samples == {} , {}".format(numChunks, totalNumSamples) )
3739
print( "Valid Samples and Duration == {} / {}".format(validSamples, duration) )
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import socket
2+
import sys
3+
import time
4+
import argparse
5+
import signal
6+
import struct
7+
import os
8+
import json
9+
10+
numSamples = 0
11+
12+
# Print received message to console
13+
def print_message(*args):
14+
try:
15+
#print(args[0]) #added to see raw data
16+
obj = json.loads(args[0].decode())
17+
print(obj.get('data'))
18+
num_samples_packet = len(obj.get('data'))
19+
global numSamples
20+
print("NumSamplesInPacket_Marker == " + str(num_samples_packet))
21+
numSamples += num_samples_packet
22+
if obj:
23+
return True
24+
else:
25+
return False
26+
except BaseException as e:
27+
print(e)
28+
return False
29+
30+
# Clean exit from print mode
31+
def exit_print(signal, frame):
32+
print("Closing listener")
33+
sys.exit(0)
34+
35+
# Record received message in text file
36+
def record_to_file(*args):
37+
textfile.write(str(time.time()) + ",")
38+
obj = json.loads(args[0].decode())
39+
#print(obj.get('data'))
40+
textfile.write(json.dumps(obj))
41+
textfile.write("\n")
42+
43+
# Save recording, clean exit from record mode
44+
def close_file(*args):
45+
print("\nFILE SAVED")
46+
textfile.close()
47+
sys.exit(0)
48+
49+
if __name__ == "__main__":
50+
# Collect command line arguments
51+
parser = argparse.ArgumentParser()
52+
parser.add_argument("--ip",
53+
default="127.0.0.1", help="The ip to listen on")
54+
parser.add_argument("--port",
55+
type=int, default=12345, help="The port to listen on")
56+
parser.add_argument("--address",default="/openbci", help="address to listen to")
57+
parser.add_argument("--option",default="print",help="Debugger option")
58+
parser.add_argument("--len",default=9,help="Debugger option")
59+
args = parser.parse_args()
60+
61+
# Set up necessary parameters from command line
62+
length = int(args.len)
63+
if args.option=="print":
64+
signal.signal(signal.SIGINT, exit_print)
65+
elif args.option=="record":
66+
i = 0
67+
while os.path.exists("udp_test%s.txt" % i):
68+
i += 1
69+
filename = "udp_test%i.txt" % i
70+
textfile = open(filename, "w")
71+
textfile.write("time,address,messages\n")
72+
textfile.write("-------------------------\n")
73+
print("Recording to %s" % filename)
74+
signal.signal(signal.SIGINT, close_file)
75+
76+
# Connect to socket
77+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
78+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
79+
server_address = (args.ip, args.port)
80+
sock.bind(server_address)
81+
82+
# Display socket attributes
83+
print('--------------------')
84+
print("-- UDP LISTENER -- ")
85+
print('--------------------')
86+
print("IP:", args.ip)
87+
print("PORT:", args.port)
88+
print('--------------------')
89+
print("%s option selected" % args.option)
90+
91+
# Receive messages
92+
print("Listening...")
93+
start = time.time()
94+
95+
duration = 10
96+
while time.time() <= start + duration:
97+
data, addr = sock.recvfrom(20000) # buffer size is 20000 bytes
98+
if args.option=="print":
99+
print_message(data)
100+
elif args.option=="record":
101+
record_to_file(data)
102+
numSamples += 1
103+
104+
print( "Samples == {}".format(numSamples) )
105+
print( "Duration == {}".format(duration) )
106+
print( "Avg Sampling Rate == {}".format(numSamples / duration) )
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import socket
2+
import random
3+
import struct
4+
import time
5+
import argparse
6+
7+
if __name__ == "__main__":
8+
9+
test_duration = 10
10+
11+
# Collect command line arguments
12+
parser = argparse.ArgumentParser()
13+
parser.add_argument("--ip", default="127.0.0.1",
14+
help="The IP of the UDP server")
15+
parser.add_argument("--port", type=int, default=12350,
16+
help="The port the UDP server is sending to")
17+
args = parser.parse_args()
18+
19+
# Establish UDP socket
20+
UDP_IP = args.ip
21+
UDP_PORT = args.port
22+
sock = socket.socket(socket.AF_INET, # Internet
23+
socket.SOCK_DGRAM) # UDP
24+
25+
# Display socket attributes
26+
print('---------------------')
27+
print("-- UDP MARKER SEND -- ")
28+
print('---------------------')
29+
print("IP:", args.ip)
30+
print("PORT:", args.port)
31+
print('--------------------=')
32+
33+
# Send test data
34+
start = time.time()
35+
while time.time() <= start + test_duration:
36+
# generate random float
37+
marker = random.uniform(0, 4)
38+
# package as byte array
39+
msg = struct.pack('!d', marker)
40+
# send through socket
41+
sock.sendto(msg, (UDP_IP, UDP_PORT))
42+
time.sleep(.25)

OpenBCI_GUI/Board.pde

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ abstract class Board implements DataSource {
107107

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

110+
public abstract void insertMarker(int value);
111+
112+
public abstract void insertMarker(double value);
113+
110114
// ***************************************
111115
// protected methods implemented by board
112116

OpenBCI_GUI/BoardBrainFlowSynthetic.pde

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,10 @@ class BoardBrainFlowSynthetic extends BoardBrainFlow implements AccelerometerCap
9696

9797
@Override
9898
protected void addChannelNamesInternal(String[] channelNames) {
99-
for (int i=0; i<getAccelerometerChannels().length; i++) {
99+
for (int i = 0; i < getAccelerometerChannels().length; i++) {
100100
channelNames[getAccelerometerChannels()[i]] = "Accel Channel " + i;
101101
}
102+
channelNames[getMarkerChannel()] = "Marker Channel";
102103
}
103104

104105
@Override

OpenBCI_GUI/BoardBrainflow.pde

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ abstract class BoardBrainFlow extends Board {
1212
protected int sampleIndexChannelCache = -1;
1313
protected int timeStampChannelCache = -1;
1414
protected int totalChannelsCache = -1;
15+
protected int markerChannelCache = -1;
1516
protected int[] exgChannelsCache = null;
1617
protected int[] otherChannelsCache = null;
1718

1819
protected boolean streaming = false;
1920
protected double time_last_datapoint = -1.0;
2021
protected boolean data_popup_displayed = false;
2122

23+
private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
24+
2225
/* Abstract Functions.
2326
* Implement these in your board.
2427
*/
@@ -300,4 +303,39 @@ abstract class BoardBrainFlow extends Board {
300303

301304
return otherChannelsCache;
302305
}
306+
307+
@Override
308+
public int getMarkerChannel() {
309+
if (markerChannelCache < 0) {
310+
try {
311+
markerChannelCache = BoardShim.get_marker_channel(getBoardIdInt());
312+
} catch (BrainFlowError e) {
313+
e.printStackTrace();
314+
}
315+
}
316+
317+
return markerChannelCache;
318+
}
319+
320+
@Override
321+
public void insertMarker(double value) {
322+
if (isConnected() && streaming) {
323+
try {
324+
boardShim.insert_marker(value);
325+
String currentTimeString = dateFormat.format(new Date());
326+
StringBuilder markerNotification = new StringBuilder("Inserted marker ");
327+
markerNotification.append(value);
328+
markerNotification.append(" at approximately: ");
329+
markerNotification.append(currentTimeString);
330+
println(markerNotification.toString());
331+
} catch (BrainFlowError e) {
332+
e.printStackTrace();
333+
}
334+
}
335+
}
336+
337+
@Override
338+
public void insertMarker(int value) {
339+
insertMarker((double) value);
340+
}
303341
};

OpenBCI_GUI/BoardCyton.pde

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ implements ImpedanceSettingsBoard, AccelerometerCapableBoard, AnalogCapableBoard
608608
for (int i=0; i<getAnalogChannels().length; i++) {
609609
channelNames[getAnalogChannels()[i]] = "Analog Channel " + i;
610610
}
611+
channelNames[getMarkerChannel()] = "Marker Channel";
611612
}
612613

613614
@Override

OpenBCI_GUI/BoardGanglion.pde

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ abstract class BoardGanglion extends BoardBrainFlow implements AccelerometerCapa
257257
for (int i=0; i<getAccelerometerChannels().length; i++) {
258258
channelNames[getAccelerometerChannels()[i]] = "Accel Channel " + i;
259259
}
260+
channelNames[getMarkerChannel()] = "Marker Channel";
260261
}
261262

262263
@Override

OpenBCI_GUI/BoardNull.pde

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ class BoardNull extends Board {
5151
return 0;
5252
}
5353

54+
@Override
55+
public int getMarkerChannel() {
56+
return 0;
57+
}
58+
59+
@Override
60+
public void insertMarker(int marker) {
61+
// empty
62+
}
63+
64+
@Override
65+
public void insertMarker(double value) {
66+
// empty
67+
}
68+
5469
@Override
5570
public void setEXGChannelActive(int channelIndex, boolean active) {
5671
// empty

0 commit comments

Comments
 (0)