309 Commits

Autor SHA1 Mensagem Data
wellton 4a1b0a8028 Update Containers.pde 2018-05-16 14:25:32 -03:00
AJ Keller 16129254c3 Merge pull request #333 from OpenBCI/development
Development
2018-05-16 08:56:07 -04:00
AJ Keller dd1eab12cc Merge pull request #332 from aj-ptw/development
Development
2018-05-16 08:55:34 -04:00
AJ Keller 72877da087 FIX: #316 processing not working with 3.3.7 2018-05-16 08:54:25 -04:00
AJ Keller 47ce0d9910 FIX: clean up bled112 for windows/linux 2018-05-16 08:02:26 -04:00
AJ Keller 3a6283b440 DOCS: Update changelog and version 2018-05-12 10:35:42 -04:00
AJ Keller b43720113a Merge pull request #330 from chrisjz/development
FIX: Freezing in Windows
2018-05-12 10:27:22 -04:00
Chris Julian Zaharia a44f76a101 FIX: Freezing in Windows 2018-05-11 23:45:08 +10:00
AJ Keller 4c4171bc3a FIX: bugs 2018-05-04 09:05:16 -04:00
AJ Keller f2095a0614 FIX: SD Card issues 2018-05-04 08:44:21 -04:00
AJ Keller 8dd516ae73 Merge pull request #327 from aj-ptw/development
FIX: versions
2018-04-24 19:54:45 -04:00
AJ Keller 790a1e49ff FIX: versions 2018-04-24 19:53:25 -04:00
AJ Keller 84544eac61 Merge pull request #326 from OpenBCI/development
Development
2018-04-24 19:51:34 -04:00
AJ Keller 3653a15a8a FIX: spacing issues 2018-04-21 13:29:35 -04:00
AJ Keller e251c865c0 Merge pull request #325 from aj-ptw/development
DOCS: update for new release
2018-04-20 08:18:31 -04:00
AJ Keller 1336c31441 DOCS: update for new release 2018-04-20 08:17:22 -04:00
AJ Keller 9b0953ad2d Merge pull request #324 from aj-ptw/development
Development
2018-04-20 07:36:00 -04:00
AJ Keller 856e584eb3 DOCS: update 2018-04-20 07:34:44 -04:00
AJ Keller e15ac89ce3 FIX: static seems to be almost working, examine is too 2018-04-19 20:03:02 -04:00
AJ Keller 72adb8071a WIP: Adding in static ip to wifi 2018-04-19 09:47:23 -04:00
AJ Keller e478ae2489 BROKEN: missising wifi bled112 2018-04-18 21:27:14 -04:00
AJ Keller 016c702662 ADD: Static IP support 2018-04-18 16:02:06 -04:00
AJ Keller b3b8c6dd0c DOCS: bump hub notes to 1.4.1 and make this beta 2 2018-04-13 07:43:36 -04:00
AJ Keller 05cbf3acbf Merge branch 'firmware' of https://github.com/aj-ptw/OpenBCI_GUI into development 2018-03-08 11:16:13 -05:00
AJ Keller 4538e42a6b Merge pull request #314 from aj-ptw/bled112
Bled112
2018-03-04 10:47:59 -05:00
AJ Keller f05fffbd52 DOCS: add some new release notes 2018-03-04 10:38:44 -05:00
AJ Keller 05f3156659 WIP: bled112 2018-03-03 10:02:59 -05:00
AJ Keller 41ea23ee54 ADD: firmware version printing 2018-03-01 12:47:32 -05:00
AJ Keller ecd2b7f613 WIP 2018-03-01 09:39:30 -05:00
AJ Keller 5ac8adb629 FIX: now compiling woo 2018-02-28 08:35:35 -05:00
AJ Keller 028fe0964b WIP: Add bled112 2018-02-22 21:31:41 -05:00
AJ Keller 7d90ad3906 clean 2018-02-22 20:38:39 -05:00
AJ Keller 86bac7de19 Update ISSUE_TEMPLATE.md 2018-01-09 20:36:17 -05:00
AJ Keller e7b5c71cfb Merge pull request #301 from jooray/patch-3
Correct comments for dropdowns
2017-12-25 10:03:35 -05:00
Juraj Bednar 979ce4dc36 Correct comments for dropdowns 2017-12-25 14:42:44 +01:00
AJ Keller 3557a20e6e FIX: Add to issue template for clarity 2017-12-13 10:43:03 -05:00
AJ Keller 96778b75af Merge pull request #295 from OpenBCI/development
Development
2017-12-11 15:17:10 -05:00
AJ Keller bc8ad100dc Merge pull request #294 from aj-ptw/development
Development
2017-12-11 15:15:09 -05:00
AJ Keller ede04a9beb FIX: placement of UDP on ganglion 2017-12-11 15:14:03 -05:00
AJ Keller 3e51be21df FIX: Don't display accel on ganglion by default 2017-12-11 10:11:45 -05:00
AJ Keller 650087c9ec Merge branch 'master' of https://github.com/OpenBCI/OpenBCI_GUI into development 2017-12-11 09:25:12 -05:00
AJ Keller 3964bb8258 Create ISSUE_TEMPLATE.md 2017-12-05 20:02:32 -05:00
AJ Keller ad039f382d DOC: Add four bugs fixed 2017-12-05 11:08:27 -05:00
AJ Keller 017a040107 Merge pull request #292 from aj-ptw/development
FIX: For ganglion ble
2017-12-05 09:11:11 -05:00
AJ Keller 0c6ea968d9 FIX: For ganglion ble 2017-12-05 08:29:39 -05:00
AJ Keller d23eb24222 Merge pull request #291 from aj-ptw/development
Development
2017-12-04 12:58:41 -05:00
AJ Keller edcb42b1e0 DOCS: add to changelog 2017-12-04 12:47:38 -05:00
AJ Keller 91e1a3be34 ADD: changing the help widget color for three seconds to catch the users eye better on errors and such. 2017-12-04 12:44:43 -05:00
AJ Keller 08ee608d91 FIX: control panel issues and added command processing to work with new hub 2017-12-02 10:40:40 -05:00
AJ Keller 59e7788a0e FIX: internet protocol 2017-12-01 11:09:42 -05:00
AJ Keller eec308c23c ADD: Internet protocol box and controls 2017-11-29 15:08:28 -05:00
AJ Keller aa8c8a2c0e Merge pull request #290 from aj-ptw/development
Development
2017-11-29 13:57:33 -05:00
AJ Keller 30bf9cd9a1 ADD: update new beta num 2017-11-29 13:57:13 -05:00
AJ Keller d1b717d171 Merge pull request #286 from jooray/patch-1
Correct comment, "FFT Plot" is not a widget title
2017-11-29 13:54:11 -05:00
AJ Keller 8dd02edc67 Merge pull request #287 from jooray/patch-2
alpha_count and beta_count don't need to be nulled
2017-11-29 13:53:46 -05:00
AJ Keller 3625dc9d04 Merge pull request #289 from OpenBCI/focus-color
Fix focus widget color scheme, added dropdown
2017-11-29 13:52:42 -05:00
AJ Keller 8fff2a7633 FIX: Accel widget can turn board into default mode 2017-11-29 11:16:35 -05:00
AJ Keller dcca513d35 FIX: Layout of dots 2017-11-29 09:21:43 -05:00
AJ Keller a8a1cfe91e FIX: Spacing on digital read widgit 2017-11-28 15:29:43 -05:00
AJ Keller bc47e6e2dc WIP:Digital and analog read widgets 2017-11-28 13:51:09 -05:00
AJ Keller 559ab19549 FIX: Analog read sensor 2017-11-17 13:41:46 -08:00
sunwangshu 3252b15f55 Fix focus widget color scheme, added dropdown
Green cyan orange
2017-11-16 11:04:25 -08:00
AJ Keller a095ed157b FIX: Analog read 2017-11-15 07:01:38 -08:00
Juraj Bednar cb8a093a0f alpha_count and beta_count don't need to be nulled
alpha_count and beta_count are local variables, it does not matter if we set them to zero before returning from function, this is unnecessary
2017-11-13 23:34:42 +01:00
Juraj Bednar 522ffe36ce Correct comment, "FFT Plot" is not a widget title 2017-11-13 23:32:21 +01:00
AJ Keller d8060e7343 ADD: Analog read bar line graph class and redid analog read widget 2017-11-12 11:20:22 -08:00
AJ Keller 5f9d781c2b WIP: Analog read widget improvements 2017-11-07 10:55:42 -05:00
AJ Keller 51402ddc88 WIP: Analog read widget improvements 2017-11-07 10:55:27 -05:00
AJ Keller 765111b22f Merge pull request #284 from gerrievanzyl/development
Fixing bugs related to makers
2017-11-07 10:16:46 -05:00
Gerrie van Zyl 8e88f7aecc Limit the marker values to printable characters
The previous marker code allowed non printable characters to be sent as
the marker values.  This supported markers in range [1:255].  It was
tested using BLE connection.

However, it seems there are inconsistencies in the implementation of
hub.sendCommand(String) in the ElectronHub code.  When using BLE the
string is correctly sent, however, when using wifi the String is UTF-8
encoded.  This introduces additional characters into the stream that at
minimum produces unpredictable results - and often restarts the Cyton
board.

For the time being, the best option is to limit markers to the 96
printable ascii characters.  This version of the GUI does that by
mapping 1-96 to ‘ ‘ - ‘~’ .
2017-11-06 15:40:57 -05:00
AJ Keller 554d909b4a Merge pull request #283 from aj-ptw/development
WIP: #282
2017-11-06 09:09:12 -05:00
Gerrie van Zyl 0004277117 Send markers as 2 char hex 2017-11-03 21:55:47 -04:00
gerrievanzyl 832671dee1 Merge pull request #1 from OpenBCI/development
Development
2017-11-03 21:50:48 -04:00
AJ Keller 7b14419438 WIP: #282 2017-11-03 13:13:28 -04:00
AJ Keller 8ae2fb04d9 Merge pull request #281 from aj-ptw/development
Development
2017-11-03 11:45:11 -04:00
AJ Keller 94f4ff817e Merge pull request #11 from gerrievanzyl/feature/UDPmarkers
Mostly cleaning up W_MarkerMode
2017-11-03 11:43:33 -04:00
AJ Keller d7612f0b8f Merge branch 'development' into feature/UDPmarkers 2017-11-03 11:43:20 -04:00
AJ Keller e6cf7829f1 Merge branch 'development' into feature/UDPmarkers 2017-11-03 11:39:50 -04:00
Gerrie van Zyl 6fcec99b8b removed autosaves and streamlined DATASOURCE_SYNTHETIC 2017-11-03 09:24:53 -04:00
Gerrie van Zyl 81ac1521bf currentXvalue = 0 2017-11-02 21:26:45 -04:00
AJ Keller 1a0dd293fc DOCS: Add to changelog 2017-11-02 13:49:04 -04:00
AJ Keller ee3efbd973 FIX: remove print statement 2017-11-02 13:45:50 -04:00
AJ Keller 96063a95d2 FIX: Daisy accel data use hub v1.3.5 #272 2017-11-02 13:37:26 -04:00
Gerrie van Zyl 119984281f Added back all the widgets
Now that the underlying issue has been fixed (SD card commands) the
widgets are working
2017-11-02 13:05:29 -04:00
Gerrie van Zyl 3f0eea8867 Cleaned up comments
AJ to review
2017-11-02 13:00:39 -04:00
AJ Keller 10dd400626 FIX: Finish merge of new code 2017-11-02 12:49:38 -04:00
AJ Keller 2c1b839fa8 Merge pull request #9 from gerrievanzyl/feature/UDPmarkers
Feature/ud pmarkers
2017-11-02 12:30:03 -04:00
Gerrie van Zyl 6ad99d3c09 Solved the SD issue and added some console logging 2017-11-02 11:56:22 -04:00
Gerrie van Zyl 6828d01923 Cleanup of W_Markermode and removing testing code
Removed hardwire to BOARD_MODE_MARKER in BoardCyton.pde
2017-11-01 22:48:53 -04:00
Gerrie van Zyl e09e25ff49 disabled analog and digital widgets 2017-11-01 20:37:21 -04:00
Gerrie van Zyl 307ead8c72 Fixes and refinements to MarkerMode widget 2017-11-01 19:56:46 -04:00
Gerrie van Zyl cf7cba0e72 Merge remote-tracking branch 'ajptw/development' into feature/UDPmarkers
adding AJ's fixes
2017-11-01 15:01:35 -04:00
Gerrie van Zyl 82a1443981 Adding Marker Mode window 2017-11-01 15:01:19 -04:00
AJ Keller 25e0653123 FIX: #278 thanks to gerrievanzyl 2017-11-01 14:56:08 -04:00
AJ Keller 5b7629de84 WIP: Digital and analog read to log 2017-11-01 14:52:22 -04:00
Gerrie van Zyl 8cdea473c1 Merge remote-tracking branch 'ajptw/development' into feature/UDPmarkers
AJ's changes
2017-11-01 13:45:03 -04:00
Gerrie van Zyl 6d40dbe1e7 fixed the Board mode error in the Hub 2017-11-01 13:16:16 -04:00
AJ Keller 75a6d4f68d FIX: Remove more ptins and fixes #278 sd card 2017-11-01 12:52:01 -04:00
AJ Keller 8f238f9651 FIX: #277 add sdcardstart 2017-11-01 12:48:40 -04:00
AJ Keller ab942ae21f FIX: #272 I can see the digital data, using the new digital widget outputting the digital data 2017-11-01 12:38:37 -04:00
AJ Keller 113ff47473 Merge branch 'development' of https://github.com/OpenBCI/OpenBCI_GUI into development 2017-11-01 11:45:13 -04:00
Gerrie van Zyl b6b536dea7 Removed the delay - not useful 2017-10-31 10:58:31 -04:00
Gerrie van Zyl c878731fe1 Changes to BoardCyton.pde to support the experimental Marker mode 2017-10-31 10:49:51 -04:00
Gerrie van Zyl e8251669c3 Changes to OpenBCI_GUI.pde to enable UDP messages 2017-10-31 10:22:49 -04:00
Gerrie van Zyl 9c545e06b4 Merge remote-tracking branch 'OpenBCI/master' into development 2017-10-31 09:38:57 -04:00
AJ Keller 4333132e44 Merge pull request #275 from OpenBCI/fft-display-fix
Fixing FFT display
2017-10-28 01:58:59 -04:00
sunwangshu 732a77b78d Fixing FFT display
xMax was set to be 100Hz. Now it is set to the last (biggest) element
of xLimOptions.
2017-10-27 21:02:44 -07:00
AJ Keller 2677da9810 FIX: #272 2017-10-27 23:49:04 -04:00
AJ Keller 80a51bde35 ADD: Parsing data for board mode if stop byte is raw aux fixes #272 2017-10-27 22:22:31 -04:00
AJ Keller 5b51197e1a Merge pull request #274 from OpenBCI/development
Development
2017-10-24 12:05:22 -04:00
AJ Keller 1ceb4ba659 Merge pull request #273 from aj-ptw/development
Development
2017-10-24 12:04:22 -04:00
AJ Keller 26c57c4775 FIX: Info in change log 2017-10-24 12:03:25 -04:00
AJ Keller 99d1457b5e Merge branch 'development' of https://github.com/aj-ptw/OpenBCI_GUI into development 2017-10-24 11:22:07 -04:00
AJ Keller 3896379e29 FIX: Saved data files are now saved as .csv 2017-10-24 11:21:52 -04:00
AJ Keller e7a5fd85f8 FIX: 500Hz sample rate 2017-10-24 11:16:48 -04:00
AJ Keller 00891188a6 WIP: Add 500Hz options for wifi 2017-10-23 08:37:39 -04:00
AJ Keller b436531e65 Merge branch 'development' of https://github.com/aj-ptw/OpenBCI_GUI into development 2017-10-23 08:13:35 -04:00
AJ Keller c1bd680c98 ADD: 500Hz filter coefficients 2017-10-23 08:13:24 -04:00
AJ Keller 6400a6be7f FIX: Networking widget 2017-10-20 12:44:24 -04:00
AJ Keller d81baae274 ADD: networking test code along with new format for UDP sending 2017-10-20 10:58:32 -04:00
AJ Keller be25f63b15 FIX: problem 2017-10-19 09:09:02 -04:00
AJ Keller 304a4ab681 FIX: links/typos 2017-10-19 08:59:21 -04:00
AJ Keller ca156b7282 Merge pull request #269 from aj-ptw/development
Development
2017-10-19 08:57:43 -04:00
AJ Keller d180bebd74 ADD: issues to change log 2017-10-19 08:54:11 -04:00
AJ Keller 17fd478e20 ADD: stuffs changelog 2017-10-19 08:52:17 -04:00
AJ Keller a09a88656e ADD: New files to changelog 2017-10-19 08:50:20 -04:00
AJ Keller ef467cc543 ADD: Contributing, code of conduct and roadmap, banner image, and redid readme 2017-10-19 08:48:49 -04:00
AJ Keller 492a68b84b Merge pull request #268 from aj-ptw/fixes
Fixes
2017-10-18 12:53:16 -04:00
AJ Keller 89f5871e3c ADD: To change long 2017-10-18 12:52:00 -04:00
AJ Keller 5c7d042605 FIX: #261 widget tutorial link 2017-10-18 12:23:24 -04:00
AJ Keller abe7fd6a5f FIX: #267 sd file is converting to csv now and storing in SavedData with output message to user 2017-10-18 12:10:56 -04:00
AJ Keller 0ceca9e882 ADD: Instructions for hub version and bumped version to 3.0.2 2017-10-16 16:48:31 -04:00
AJ Keller f503d429b5 FIX: erroneuous data stream stopped error from showing 2017-10-16 10:14:15 -04:00
AJ Keller 7b288eca63 Merge pull request #256 from OpenBCI/development
Development
2017-09-08 13:16:01 -04:00
AJ Keller c2726b1f24 Merge pull request #255 from aj-ptw/development
FIX: #254 networking array overflow if not 8 chan
2017-09-08 13:15:27 -04:00
AJ Keller 979f887b57 FIX: #254 networking array overflow if not 8 chan 2017-09-08 13:14:52 -04:00
AJ Keller a77533fd95 Merge pull request #250 from OpenBCI/development
Development
2017-09-06 13:10:56 -04:00
AJ Keller 55b8ba73fa Merge pull request #251 from aj-ptw/development
FIX: patch for pulse sensor
2017-09-06 13:09:08 -04:00
AJ Keller 9bb260e953 FIX: patch for pulse sensor 2017-09-06 13:08:26 -04:00
AJ Keller 3778156ffd Merge pull request #248 from aj-ptw/development
Development
2017-09-06 10:30:53 -04:00
AJ Keller 6ec5f51e08 CHORE: Update changelog 2017-09-06 10:30:30 -04:00
AJ Keller bc89658985 FIX: remove dropped packet detection 2017-09-06 10:26:12 -04:00
AJ Keller 41a62d379b FIX: Accel input 2017-09-05 23:47:24 -04:00
AJ Keller 3fc7c8371b Update OpenBCI_GUI.pde 2017-09-05 12:47:54 -04:00
Conor Russomanno dc8d8b6546 Merge pull request #241 from conorrussomanno/development
fps fix
2017-09-05 09:24:46 -07:00
Conor Russomanno 7720932e99 fps fix 2017-09-05 09:23:23 -07:00
AJ Keller d202038e37 Merge pull request #239 from aj-ptw/development
Development
2017-09-05 11:41:51 -04:00
AJ Keller 71cd18e4d7 Merge branch 'development' of https://github.com/aj-ptw/OpenBCI_GUI into development 2017-09-05 11:40:48 -04:00
AJ Keller bb4ccf58e7 ADD: .gitignore will ignore build files 2017-09-05 11:40:29 -04:00
AJ Keller 56330dcec3 Merge pull request #238 from aj-ptw/development
Development
2017-09-05 11:38:58 -04:00
AJ Keller d01b6c0335 Merge branch 'development' into development 2017-09-05 11:38:40 -04:00
AJ Keller 331455f367 FIX: #208 and fix bug where gui started in 45 fps mode 2017-09-05 11:36:26 -04:00
AJ Keller 029ad9e0da Merge pull request #236 from aj-ptw/development
Development
2017-09-05 10:59:17 -04:00
AJ Keller af79980050 Merge branch 'development' of https://github.com/OpenBCI/OpenBCI_GUI into development 2017-09-05 08:24:38 -04:00
Conor Russomanno fe5ddb61ff Merge pull request #235 from conorrussomanno/development
cosmetic additions & other stuff
2017-09-04 23:15:45 -07:00
Conor Russomanno f21e433ace cosmetic additions & other stuff 2017-09-04 23:14:52 -07:00
AJ Keller 5ea33b6e47 FIX: Show issue when hub is not connected 2017-09-04 20:40:20 -04:00
AJ Keller 22461d331c FIX: Not starting hub on windows automatically 2017-09-04 18:35:27 -04:00
AJ Keller 38729797d9 FIX: Windows patches 2017-09-04 16:09:50 -04:00
Conor Russomanno 37e7d33275 Update README.md 2017-09-04 11:29:47 -07:00
Conor Russomanno dff20cf12b Update README.md 2017-09-04 11:29:23 -07:00
AJ Keller 6ed0753b1d Merge pull request #234 from aj-ptw/development
FIX: #233 #228
2017-09-04 14:00:37 -04:00
AJ Keller 182af94b03 FIX: #233 #228 2017-09-04 13:57:59 -04:00
AJ Keller 9a3e77ce35 Merge pull request #232 from aj-ptw/development
FIX: Multiple bugs with sample rates and such
2017-09-04 11:58:58 -04:00
AJ Keller 791b82675a FIX: Multiple bugs with sample rates and such 2017-09-04 11:57:01 -04:00
AJ Keller 2c0d2d8076 Merge pull request #227 from aj-ptw/development
FIX: Issue where connecting to wifi shield with wrong board did not d…
2017-09-03 22:58:32 -04:00
AJ Keller 55a6265cf0 FIX: Issue where connecting to wifi shield with wrong board did not display verbose output 2017-09-03 22:57:35 -04:00
AJ Keller 9f194060e3 Merge pull request #225 from aj-ptw/development
FIX: #219 #218 #217
2017-09-03 22:22:45 -04:00
AJ Keller 9ac95ae367 FIX: #219 #218 #217 2017-09-03 22:21:50 -04:00
AJ Keller 21d6322402 Merge pull request #224 from aj-ptw/development
FIX: portIsOpen not set on examine
2017-09-03 21:54:16 -04:00
AJ Keller b2c2accded FIX: portIsOpen not set on examine 2017-09-03 21:53:44 -04:00
AJ Keller 2a2398b34e Merge pull request #223 from aj-ptw/development
FIX: missing semi again
2017-09-03 21:04:34 -04:00
AJ Keller ef99b600d6 FIX: missing semi again 2017-09-03 21:04:00 -04:00
AJ Keller 496e735495 Merge pull request #222 from aj-ptw/development
FIX: missing semi
2017-09-03 21:01:14 -04:00
AJ Keller beb6882d0a FIX: missing semi 2017-09-03 21:00:36 -04:00
AJ Keller 58bfc2db64 Merge pull request #221 from aj-ptw/development
FIX: multiple fixes
2017-09-03 20:56:37 -04:00
AJ Keller 496cd2d377 FIX: multiple fixes 2017-09-03 20:56:10 -04:00
AJ Keller 9a2b29e33a Merge pull request #220 from aj-ptw/development
Development
2017-09-03 19:48:50 -04:00
AJ Keller df78dffa25 FIX: #216 - Bug where sycning channel settings could cause crash 2017-09-03 19:47:57 -04:00
Conor Russomanno 98160d075d Merge pull request #215 from conorrussomanno/development
local changes from Conor
2017-09-03 12:47:27 -07:00
Conor Russomanno f64fa740d0 Merge branch 'development' into development 2017-09-03 12:47:20 -07:00
Conor Russomanno ab5cdc39bb local changes from Conor 2017-09-03 12:44:02 -07:00
AJ Keller 57b2553881 FIX: Start hub even if windows 2017-09-03 00:34:11 -04:00
AJ Keller 31eff8d58a Merge pull request #214 from aj-ptw/development
Development
2017-09-02 00:47:41 -04:00
AJ Keller 5123f20c99 FIX: Multiple fixes and added lots of stability working with hub 1.1.0 2017-09-02 00:34:17 -04:00
Joel Murphy e48363d64d Merge pull request #213 from OpenBCI/add-license-1
Add License
2017-09-01 09:49:05 -04:00
Joel Murphy 23a3d76ac7 Add License 2017-09-01 09:48:42 -04:00
AJ Keller 538875a95d FIX: at least the app is compiling now but crashing in some areas 2017-08-31 22:04:02 -04:00
AJ Keller 1247eb16d5 ADD: Adding a sample rate and latency options to wifi 2017-08-30 14:27:01 -04:00
AJ Keller 4f45eabdca ADD: Wifi config bug with out functionality yet 2017-08-29 21:32:39 -04:00
AJ Keller 267dc92f55 FIX: closes #202 #205 #207 2017-08-28 16:24:00 -04:00
AJ Keller 6cc544b1d2 FIX: #202 radio config crash 2017-08-26 15:02:43 -04:00
AJ Keller 1b4cb2af68 Merge pull request #206 from aj-ptw/development
FIX: 16 channel bug
2017-08-25 12:36:13 -04:00
AJ Keller 96b000e2f5 FIX: 16 channel bug 2017-08-25 12:35:03 -04:00
AJ Keller efe5dafee4 Merge pull request #199 from aj-ptw/development
Development
2017-08-24 09:31:05 -04:00
AJ Keller 819805b634 CHORE: Bump versions for new beta 2 release 2017-08-24 09:30:00 -04:00
AJ Keller e1ccf7876f FIX: Remove stop search function call for wifi 2017-08-23 21:28:48 -04:00
AJ Keller 4a5e8d3c42 FIX: Many fixes across the board, app running well now 2017-08-23 15:03:18 -04:00
AJ Keller 478d024edc FIX: Headplot loading caused long delay, moved hard calcs to a different thread 2017-08-23 11:13:53 -04:00
AJ Keller 39c7c9682b FIX: Almost everything working 2017-08-22 16:13:32 -05:00
AJ Keller 8b598d4770 FIX: multiple bugs from first candidate 2017-08-22 11:53:14 -05:00
AJ Keller 646d2076a8 FIX: Version number 2017-08-22 11:04:57 -05:00
AJ Keller 2aa68cc3b7 FIX: #188 #194 #193 2017-08-22 11:03:31 -05:00
AJ Keller 5890ed4c06 FIX: Hardware settings controller 2017-08-22 07:35:18 -05:00
AJ Keller 39c84d129b ADD: support for sd 2017-08-17 11:22:55 -04:00
AJ Keller 0a8e78a0a2 FIX: Hardware settings controller almost working fully 2017-08-16 23:00:58 -04:00
AJ Keller 2de3a83440 FIX: Hardware setting controller loading defaults but still not sending values on click 2017-08-16 22:42:51 -04:00
AJ Keller 778f246411 FIX: Almost got hsc working 2017-08-16 19:07:14 -04:00
AJ Keller 6d14f9ebda Fix es 2017-08-16 16:17:31 -04:00
AJ Keller d2f91236b4 ADD: Hardware settings controller support functions to BoardCyton.pde file 2017-08-16 15:43:00 -04:00
AJ Keller ff6ba26587 Merge pull request #197 from aj-ptw/wifi
Wifi
2017-08-16 18:30:38 +00:00
AJ Keller 17e3b368ab Merge pull request #7 from aj-ptw/wifi-update
FFT lim
2017-08-15 03:10:36 +00:00
sunwangshu 9346c25d82 FFT lim
500 & 800 Hz
2017-08-14 17:59:03 -07:00
AJ Keller 62da5ed336 Merge pull request #6 from aj-ptw/wifi-update
Initializing nDataBackBuff later
2017-08-11 19:22:36 -04:00
sunwangshu 3ce0b92324 Fixing #2
Fixed potential problem: nDataBackBuff would not change accordingly to
sampling rate if declared final at line 121
2017-08-11 15:50:21 -07:00
AJ Keller dd944eeb3c Merge pull request #185 from aj-ptw/wifi
Wifi
2017-08-11 11:25:51 -04:00
AJ Keller 2bc0878a08 Merge pull request #184 from OpenBCI/focus-update
Slider
2017-08-11 11:24:06 -04:00
sunwangshu bd3aa02e57 Slider
slider
2017-08-09 02:21:23 -07:00
AJ Keller 7333b12c33 Merge pull request #5 from aj-ptw/wifi-update
Wifi update
2017-08-07 21:47:29 -07:00
sunwangshu 18c6b8f136 defineFilters() for 125 200 250 1000 1600Hz
fin
together with OBCI_Filters.m for auto calc
2017-08-07 19:48:08 -07:00
sunwangshu 84459f63b2 Added getNfftSafe
1. Get Nfft depending on different sampling rate.
125Hz, 200Hz, 250Hz -> 256points.
1000Hz -> 1024points.
1600Hz -> 2048 points.

2. Created fs_Hz back in W_FFT
Planning to call it only once when init instead of calling every time,
just like how Nfft works.
2017-08-07 15:09:35 -07:00
AJ Keller 5a63fa7e68 Merge pull request #4 from aj-ptw/wifi-update
Make nPointsPerUpdate dynamic and based on sample rate
2017-08-04 19:41:32 -07:00
sunwangshu aac74d46dd Make nPointsPerUpdate dynamic and based on sample rate
Calculate $nPointsPerUpdate based on sampling rate and buffer update
rate.  $nPointsPerUpdate is not final any more.

$update_millis: update the buffer every 40 milliseconds

Calculate $nPointsPerUpdate and related variables in initSystem()
2017-08-04 14:44:27 -07:00
AJ Keller db359f3546 FEAT: Adjust GUI to support multiple sample rates based on interface for the board 2017-07-20 13:15:38 -04:00
AJ Keller d77950ae8f FIX: Cyton 2017-07-20 11:47:20 -04:00
AJ Keller a49641865b Merge branch 'master' of https://github.com/OpenBCI/OpenBCI_GUI into wifi 2017-07-20 01:42:47 -04:00
AJ Keller 9130978167 Merge pull request #3 from aj-ptw/cyton-electron
Cyton electron
2017-07-20 01:34:51 -04:00
AJ Keller 34fb04c144 FIX: Cyton daisy fixes plus more disconnect support for serial 2017-07-20 01:34:08 -04:00
AJ Keller 8568411b47 Working on releasing new version of GUI 2017-07-19 10:01:13 -04:00
AJ Keller 9f104a6d42 Update OpenBCI_GUI.pde 2017-07-19 09:54:39 -04:00
AJ Keller 1a17087d5a Update CHANGELOG.md 2017-07-19 09:52:08 -04:00
AJ Keller 46d2f51e52 Merge pull request #181 from OpenBCI/development
Development
2017-07-19 09:49:58 -04:00
AJ Keller b4c53f6caa Working on fixing daisy and disconnect 2017-07-18 12:48:07 -04:00
AJ Keller 51975449af saving 2017-07-18 02:45:22 -04:00
AJ Keller 3f75affd3d Working 2017-07-18 01:20:34 -04:00
AJ Keller d32f71978f Move cyton over to electron hub 2017-07-18 00:26:47 -04:00
AJ Keller 350bbb0bef Connecting and syncing via serial port, not streaming just yet 2017-07-16 18:18:22 -04:00
AJ Keller 3d017f2a7f saving 2017-07-16 04:25:13 -04:00
AJ Keller 8df2422528 Finally seeing streaming data on gui 2017-07-16 02:57:32 -04:00
AJ Keller 379806e850 Connected to wifi shield for the first time 2017-07-16 01:02:10 -04:00
AJ Keller ec4ef76d99 Finially compiling 2017-07-15 20:11:33 -04:00
AJ Keller 67b4d05dd3 About ready to start testing new wifi code 2017-07-15 15:31:12 -04:00
AJ Keller 97c4df2cd5 Refactor backend of the GUI to work hub to cyton 2017-07-15 12:54:40 -04:00
Conor Russomanno d033a562ac Merge pull request #177 from conorrussomanno/development
Development
2017-07-14 14:52:47 -04:00
Conor Russomanno cd2bef7495 updates to networking, widgets, and more 2017-07-14 12:34:57 -04:00
AJ Keller ea73d69b6f Working on the train 2017-07-13 16:27:49 -04:00
Conor Russomanno 675f2c6fac cleanin up old files 2017-07-12 12:32:07 -04:00
Conor Russomanno 3f4c8770fe Merge branch 'development' of https://github.com/OpenBCI/OpenBCI_GUI_v2.0 into development 2017-07-12 12:28:26 -04:00
AJ Keller 0a1d66f4f4 Add new hub file 2017-07-06 10:58:56 -04:00
AJ Keller 6dec81e5ac Merge pull request #170 from sunwangshu/focus-viz-master
Focus viz master
2017-07-06 00:55:56 -04:00
sunwangshu 8f3abb2e67 Added UDP, LSL and serial for focus data, not yet tested 2017-06-29 01:44:56 -04:00
sunwangshu 731f5d55bd Merge remote-tracking branch 'OpenBCI/development' into focus-viz-master
# Conflicts:
#	OpenBCI_GUI/W_networking.pde
#	OpenBCI_GUI/WidgetManager.pde
2017-06-29 01:10:47 -04:00
AJ Keller 656561e906 Merge pull request #169 from aj-ptw/fix-bdf
Update file type from .edf to .bdf
2017-06-27 16:24:48 -04:00
AJ Keller b709b67219 Bump ver num 2017-06-27 16:24:15 -04:00
AJ Keller 3355d89c10 Update file type from .edf to .bdf 2017-06-27 16:23:22 -04:00
Joel Murphy a44abd5cfb Merge pull request #168 from biomurph/development
Development
2017-06-26 16:43:19 -04:00
biomurph 3849f6313c Improved and Verified SDcard file conversion for 8 and 16 Channel data 2017-06-26 16:42:46 -04:00
Joel Murphy 0b30e219be Merge branch 'development' into development 2017-06-12 16:00:57 -04:00
biomurph 0566b2e657 Cleared Synthetic Data bug 2017-06-12 15:36:52 -04:00
Joel Murphy 0f8932cf2f Merge pull request #1 from aj-ptw/joel-pulse-sensor
Finish pulse sensor wdiget
2017-06-12 15:09:34 -04:00
AJ Keller d6cfc23ec8 Finish pulse sensor wdiget 2017-06-12 15:08:28 -04:00
AJ Keller 4059179a78 Merge pull request #162 from aj-ptw/master
Add passthrough keys to ganglion
2017-06-09 13:54:45 -04:00
AJ Keller 08f447ce18 Update readme and change log 2017-06-09 13:53:06 -04:00
AJ Keller 9b5af48c9a Close #142 - pass through key strokes to ganglion 2017-06-09 13:51:58 -04:00
AJ Keller 600ec189aa Merge pull request #161 from OpenBCI/development
Development
2017-06-07 12:47:57 -04:00
AJ Keller 3ea77b89de Merge pull request #160 from OpenBCI/power-band
Band Power Widget
2017-06-07 10:36:50 -04:00
biomurph 616da6c757 Generating Synthetic Data for Pulse Sensor
showing a sine wave as synthetic data for Pulse Sensor.
2017-06-06 12:27:41 -04:00
sunwangshu 3d490214f3 Ylim to 100, add y axis label 2017-06-05 15:38:02 -04:00
sunwangshu cb1bb1b148 tested, name change power bands -> band power
Added testing code, tested
changed PowerBands -> BandPower in w_BandPower.pde
changed PowerBands to BandPower in w_networking

recommending name change: avgPowerInBins -> powerInBins, since it’s not
averaged power but rather the summation of power
2017-06-05 15:28:36 -04:00
sunwangshu 95d43542ba minor error fix 2017-06-05 14:42:36 -04:00
sunwangshu 913b356034 PSD first draft finished
first draft finished, need some testing and cleanups

calculate single-sided psd by mag single-sided FFT magnitude
PSD ref:
https://www.mathworks.com/help/dsp/ug/estimate-the-power-spectral-densit
y-in-matlab.html

when i = 0 or i = N/2, psd = (N / fs) * mag(i)^2
when i = 1 ~ (N/2-1), psd = (N / fs) * mag(i)^2 / 4
2017-06-05 14:34:25 -04:00
sunwangshu adfbd45a60 PSD notes
Derived equation for calculating PSD from single-sided FFT magnitude
(smoothed also)

Need to fix the loop parameter, convert it from frequency-hz to FFT bin
index
2017-06-05 02:54:31 -04:00
sunwangshu ca6d978a03 Merge branch 'development' into power-band 2017-06-05 01:30:51 -04:00
sunwangshu cf6db129ee fix FFT again
Change from getBand/specSize() -> getBand * 2 / timeSize()
2017-06-05 01:30:47 -04:00
AJ Keller eff2079b18 Merge pull request #159 from OpenBCI/development
Development
2017-06-02 17:35:48 -04:00
AJ Keller c3fe433dcf Add electrodes to changelog 2017-06-02 17:34:17 -04:00
AJ Keller bd199f512a Removed line in w_PowerBands that caused crash 2017-06-02 17:29:04 -04:00
AJ Keller dbfbf693be Merge pull request #158 from liqwid/master
Fixed electrode being inside head condition for head plot widget
2017-06-02 17:26:12 -04:00
liqwid 8d89574498 Fixed electrode being inside head condition for head plot widget 2017-06-03 00:23:46 +03:00
AJ Keller 59e7f6704d Merge pull request #152 from liqwid/master
Added electrodes drag and drop to the head widget
2017-06-02 15:31:15 -04:00
AJ Keller 19b0773bfb Update changelog and bump version to 2.2.0 2017-05-31 10:14:09 -04:00
AJ Keller fe8cf65b08 Merge pull request #153 from OpenBCI/power-band
Power band
2017-05-31 10:08:50 -04:00
AJ Keller 93ad739add Merge pull request #157 from aj-ptw/fix-151
Fix 151 by adding channel cound selection on playback files
2017-05-31 10:05:42 -04:00
AJ Keller e2f0e6f27b Merge branch 'master' of https://github.com/OpenBCI/OpenBCI_GUI_v2.0 into development 2017-05-31 09:50:38 -04:00
AJ Keller 66637e4d52 Fix 151 by adding channel cound selection on playback files 2017-05-31 09:46:07 -04:00
sunwangshu 0f35c75b10 Power band 2017-05-25 01:26:04 -04:00
liqwid ade69319a4 Added electrodes drag and drop to the head widget and mouseDrag callback to widget class 2017-05-22 09:56:50 +03:00
biomurph 6a2c03bf45 Pulse Sensor Widget Window Elements 2017-05-16 15:35:18 -04:00
biomurph 581c186c53 added SavedData to gitignore 2017-05-16 12:12:55 -04:00
biomurph 8c9ce74533 Merge remote-tracking branch 'OpenBCI/master' into development 2017-05-16 12:10:51 -04:00
AJ Keller 7e8542b11a Update README.md 2017-05-12 21:34:34 -04:00
AJ Keller 7a368e6d83 Update README.md 2017-05-12 21:34:26 -04:00
AJ Keller 9e184723ab Update README.md 2017-05-12 21:33:49 -04:00
AJ Keller 04c106f0ee Update README.md 2017-05-12 21:33:32 -04:00
AJ Keller 9821f9424f Update README.md 2017-05-12 21:28:30 -04:00
sunwangshu bff5821bfe added osc support in networking widget 2017-04-28 19:57:57 -04:00
sunwangshu 8105336dd5 Merge remote-tracking branch 'OpenBCI/development' into focus-viz-master 2017-04-25 16:21:06 -04:00
Conor Russomanno 61caee3400 remove println 2017-03-21 17:28:33 -04:00
gabrielibagon a5ea844469 LSL support 2017-03-20 23:26:20 -07:00
biomurph d66f90ad97 Merge remote-tracking branch 'OpenBCI/master' into development 2017-03-13 16:01:44 -04:00
sunwangshu 042ab47246 More documentation and supporting files
Added more description.
Jordan’s playback file.
Example Arduino file and explanations.
2017-03-08 16:54:58 -05:00
sunwangshu 8e09cf5070 Jordan's banana focus data
Staring at a banana to generate focus.
2017-03-07 21:09:22 -05:00
sunwangshu e77d4ad127 Added Arduino sending function
You’ll be able to send focused state to arduino
Be sure to set serial port name and baud rate in OpenBCI.pde by
searching “serial_out”.
Added arduino example in W_Focus folder, the example is using baud rate
9600.
2017-03-07 01:44:24 -05:00
sunwangshu 5d9ea0ea98 Updating to the latest OpenBCI GUI 2.0
Now you can ask the robot a favor.
It will stroke the ⬆️ when you are focused, if you turn it on in the
dropdown menu.
Enjoy :)
2017-03-07 00:00:58 -05:00
sunwangshu 49e2bc7c20 Merge remote-tracking branch 'OpenBCI/master' into focus-viz-master
# Conflicts:
#	OpenBCI_GUI/GUI_Widgets.pde
2017-03-06 22:40:39 -05:00
sunwangshu 6f62a1d048 Merge remote-tracking branch 'OpenBCI/master' into focus-viz-master 2016-08-04 16:59:09 -04:00
sunwangshu 205e23b13c Merge remote-tracking branch 'OpenBCI/master' into focus-viz-master
# Conflicts:
#	OpenBCI_GUI/GUI_Widgets.pde
2016-08-04 15:56:53 -04:00
sunwangshu 81e18f7ae9 clarified comments, link text with threshold, simulate Space press| 2016-07-27 16:41:13 -04:00
sunwangshu 9beb219720 Focus Visualizer 2016-07-26 15:45:52 -04:00
1639 arquivos alterados com 40497 adições e 11264 exclusões
+6
Ver Arquivo
@@ -6,3 +6,9 @@ OpenBCI_GUI/temp.txt
OpenBCI_GUI/t-temp.txt
OpenBCI_GUI/build/source/*.java
*.app
OpenBCI_GUI/SavedData
OpenBCI_GUI/SavedData/EEG_Data/SDconverted-*
OpenBCI_GUI/data/EEG_Data/*
OpenBCI_GUI/application.*.zip
OpenBCI_GUI/application.*
*.autosave
+206
Ver Arquivo
@@ -1,3 +1,209 @@
# v3.3.1
Use OpenBCIHub v1.4.2 please.
### Bug Fixes
* Fixed bug where SD files were called csv
* Fixed bug where SD files could not be played back in gui
* Fixed bug where SD files could freeze the GUI
* Fixed bug where GUI crashed on windows and linux (thanks @chrisjz) #331
* Added a bunch of checking to avoid exception errors when not running as mac to prevent BLED112
* Fixed a bug where the GUI did not work with processing 3.3.7 #316
## Beta 1
Initial release with bug fixes!
# v3.3.0
Use OpenBCIHub v1.4.2 please.
### New Features
* Add support for BLED112
* Add support for static IP for wifi
## Beta 5
* Fixes a bunch of spacing and layout issues found when switching between the different interfaces
## Beta 4
* There was a problem with the release
## Beta 3
* Add support for static IP for wifi
* Bump Hub to v1.4.2
## Beta 2
* Fix bug with Hub, bump hub to v1.4.1
## Beta 1
* Initial release
# v3.2.0
Use OpenBCIHub v1.3.9 please.
### New Features
* Add the Digital Read widget
* Add the Analog Read widget
* Add the Marker Mode widget
* Add info, warn, success, and error types to output function to alert user in help widget.
### Bug Fixes
* Did not write aux values for Cyton with Daisy #272
* Did not write to the SD card #277
* Add button to accelerometer widget to turn accel mode on if the user was just using digital, analog, or marker mode.
* Fixes #192 with drop down of different color themes.
* Fixes #285 by moving the wifi options to the right of the drop down pane.
* Fixes #270 where macOS 10.13 could not connect to Ganglion
* Fixes #247 where the timer series graph looked strange with 7 seconds.
## rc2
### Bug Fixes
* Added stability for ganglion bluetooth #270
## rc1
* Update the hub to 1.3.7 to catch more wifi errors
* Finished the udp/tcp
* Add colors to help widget.
## Beta 3
Finished Analog and Digital read widgets.
## Beta 2
Initial release
# v3.1.0
Use hub v1.3.4 please.
### New Features
* Added new files for Contributing, code of conduct and roadmap
* Refactored readme with banner image, and all in all made it sweet.
* Added 500Hz sample rate option for WiFi Shield Cyton
### Breaking Changes
* SD Converted file goes into `data/SavedData` instead of `data/EED_Data`. #267
* Sending data over UDP produced unreadable raw format. Switched to JSON output.
* All UDP output sends a serialized json packet ending with `\r\n`
* Data files are now saved with `.csv` instead of `.txt`
### Bug Fixes
* "Data stream stopped" would be shown to users even if no data stream was stopped #263
* Accel did not work for wifi Daisy #265
* Users would have to close the GUI before restarting after cyton or ganglion session #262
* Design your own widget link #261
## Beta 2
Implement overhaul of GUI docs.
### Bug Fixes
* #261 #267
## Beta 1
Initial release.
# v3.0.1
### Bug Fixes
* FIX: #254 LSL, UDP, OSC ArrayIndexOutOfBoundsException Stream with 4 or 16 channels
# v3.0.0
v3.0.0 set out to move **all** of the data collection to the electron hub. This means moving serial port parsing as well.
### New Features
* Able to use wifi shield with GUI. Streams in at 1000Hz for Cyton and 1600Hz for Ganglion.
### Breaking Changes
* Dependent on electron hub for all data streaming activity.
## Release Candidate 5
Uses OpenBCIHub v1.3.0
### Bug Fixes
* Closes: #191
## Release Candidate 4
Uses OpenBCIHub v1.2.0
### Bug Fixes
* Closes: #208 - ganglion not using correct scale factor when on wifi high resolution mode
* Fixes bug where gui started in 45 fps frame rate
## Release Candidate 2/3
### Bug Fixes
* Critical windows hub patches
## Release Candidate 1
Initial RC
## Beta 6
* Closes #202 #205 #207
## Beta 4
* Closes #203
## Beta 2-3
Required a lot of work on the hub. But none the less, this seems to be working decently.
### Bug Fixes
* Closes #196 #195 #194 #193 #190 #188 #187 #186 #189
## Beta 1
The first beta to be released. There are some [minor issues](https://github.com/OpenBCI/OpenBCI_GUI/issues), but if any are encountered, please [open an issue](https://github.com/OpenBCI/OpenBCI_GUI/issues/new) on the [github page](https://github.com/OpenBCI/OpenBCI_GUI/issues).
# 2.2.1
### Bug Fixes
* Addresses #121 - `.edf` incompatible changed ending to `.bdf`
* Closes #148 - LSL does not stream correctly
# 2.2.0
### Bug Fixes
* Fix #151 - Incorrect number of channels on playback caused index out of bounds errors.
* Addresses #149 - Allows for proper scaling of channels with four thanks to #151 #157
### New Features
* Band power widget #153 (thanks @sunwangshu)
* Closes #138 - Able to drag and drop the electrodes on the head map (thanks @liqwid)
* Closes #142 - GUI needs to pass key strokes to Ganglion
# 2.1.2
### Bug Fixes
Arquivo executável
+69
Ver Arquivo
@@ -0,0 +1,69 @@
# OpenBCI GUI Code of Conduct
## Purpose
It is our hope that any one is able to contribute to OpenBCI GUI regardless of their background. Thus, we hope to provide a safe, welcoming, and warmly geeky environment for everybody, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [contact@openbci.com](mailto:contact@openbci.com). All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
Arquivo executável
+37
Ver Arquivo
@@ -0,0 +1,37 @@
# Contributing
:tada::clinking_glasses: First off, thanks for taking the time to contribute! :tada::clinking_glasses:
Contributions are always welcome, no matter how small.
The following is a small set of guidelines for how to contribute to the project
## Where to start
### Code of Conduct
This project adheres to the Contributor Covenant [Code of Conduct](CODE_OF_CONDUCT.md).
By participating you are expected to adhere to these expectations. Please report unacceptable behaviour to [info@pushtheworld.us](mailto:info@pushtheworld.us)
### Contributing on Github
If you're new to Git and want to learn how to fork this repo, make your own additions, and include those additions in the master version of this project, check out this [great tutorial](http://blog.davidecoppola.com/2016/11/howto-contribute-to-open-source-project-on-github/).
### Community
This project is maintained by the [OpenBCI](www.openbci.com) community. Join the [OpenBCI Forum](http://openbci.com/index.php/forum/), where discussions about OpenBCI takes place.
## How can I contribute?
This is currently a small, humble project so our contribution process is rather casual. If there's a feature you'd be interested in building, go ahead! Let us know on the [OpenBCI Forum](http://openbci.com/index.php/forum/) or [open an issue](../../issues) so others can follow along and we'll support you as much as we can. When you're finished submit a pull request to the master branch referencing the specific issue you addressed.
If you find a bug, or have a suggestion on how to improve the project, just fill out a [Github issue](../../issues)
### Steps to Contribute
1. Fork it!
2. Branch off of `development`: `git checkout development`
2. Create your feature branch: `git checkout -b my-new-feature`
3. Make changes
4. Commit your changes: `git commit -m 'Add some feature'`
5. Push to the branch: `git push origin my-new-feature`
6. Submit a pull request. Make sure it is based off of the `development` branch when submitting! :D
+27
Ver Arquivo
@@ -0,0 +1,27 @@
## Problem
Explain the problem
## Expected
In a perfect world, what do you expect to happen.
## Operating System and Version
macOS/Windows/Linux
## GUI Version
The version is displayed on startup.
## Running standalone app
Are you running the downloaded app or are you running from Processing 3
## Type of OpenBCI Board
Cyton/Cyton+Daisy/Ganglion
## Are you using a WiFi Shield?
Yes/No
+21
Ver Arquivo
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 OpenBCI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+115
Ver Arquivo
@@ -0,0 +1,115 @@
import argparse
import time
import atexit
import os
import signal
import sys
if sys.version_info.major == 3:
from pythonosc import dispatcher
from pythonosc import osc_server
elif sys.version_info.major == 2:
import OSC
# Print received message to console
def print_message(*args):
try:
current = time.time()
if sys.version_info.major == 2:
print("(%f) RECEIVED MESSAGE: %s %s" % (current, args[0], ",".join(str(x) for x in args[2:])))
elif sys.version_info.major == 3:
print("(%f) RECEIVED MESSAGE: %s %s" % (current, args[0], ",".join(str(x) for x in args[1:])))
except ValueError: pass
# Clean exit from print mode
def exit_print(signal, frame):
print("Closing listener")
sys.exit(0)
# Record received message in text file
def record_to_file(*args):
textfile.write(str(time.time()) + ",")
textfile.write(",".join(str(x) for x in args))
textfile.write("\n")
# Save recording, clean exit from record mode
def close_file(*args):
print("\nFILE SAVED")
textfile.close()
sys.exit(0)
if __name__ == "__main__":
# Collect command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--ip",
default="localhost", help="The ip to listen on")
parser.add_argument("--port",
type=int, default=12345, help="The port to listen on")
parser.add_argument("--address",default="/openbci", help="address to listen to")
parser.add_argument("--option",default="print",help="Debugger option")
args = parser.parse_args()
if sys.version_info.major == 3:
# Set up necessary parameters from command line
dispatcher = dispatcher.Dispatcher()
if args.option=="print":
dispatcher.map("/openbci", print_message)
signal.signal(signal.SIGINT, exit_print)
elif args.option=="record":
i = 0
while os.path.exists("osc_test%s.txt" % i):
i += 1
filename = "osc_test%i.txt" % i
textfile = open(filename, "w")
textfile.write("time,address,messages\n")
textfile.write("-------------------------\n")
print("Recording to %s" % filename)
dispatcher.map("/openbci", record_to_file)
signal.signal(signal.SIGINT, close_file)
# Display server attributes
print('--------------------')
print("-- OSC LISTENER -- ")
print('--------------------')
print("IP:", args.ip)
print("PORT:", args.port)
print("ADDRESS:", args.address)
print('--------------------')
print("%s option selected" % args.option)
# connect server
server = osc_server.ThreadingOSCUDPServer(
(args.ip, args.port), dispatcher)
server.serve_forever()
elif sys.version_info.major == 2:
s = OSC.OSCServer((args.ip, args.port)) # listen on localhost, port 57120
if args.option=="print":
s.addMsgHandler(args.address, print_message)
elif args.option=="record":
i = 0
while os.path.exists("osc_test%s.txt" % i):
i += 1
filename = "osc_test%i.txt" % i
textfile = open(filename, "w")
textfile.write("time,address,messages\n")
textfile.write("-------------------------\n")
print("Recording to %s" % filename)
signal.signal(signal.SIGINT, close_file)
# Display server attributes
print('--------------------')
print("-- OSC LISTENER -- ")
print('--------------------')
print("IP:", args.ip)
print("PORT:", args.port)
print("ADDRESS:", args.address)
print('--------------------')
print("%s option selected" % args.option)
print("Listening...")
s.serve_forever()
+59
Ver Arquivo
@@ -0,0 +1,59 @@
import argparse
import random
import time
import sys
if sys.version_info.major == 3:
from pythonosc import osc_message_builder
from pythonosc import udp_client
elif sys.version_info.major == 2:
import OSC
if __name__ == "__main__":
# Collect command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--ip", default="127.0.0.1",
help="The ip of the OSC server")
parser.add_argument("--port", type=int, default=12345,
help="The port the OSC server is listening on")
parser.add_argument("--address", default="/openbci",
help="The address the OSC server is sending to")
args = parser.parse_args()
# Display socket attributes
print('--------------------')
print("-- OSC SIMULATION -- ")
print('--------------------')
print("IP:", args.ip)
print("PORT:", args.port)
print("ADDRESS:", args.address)
print('--------------------')
# Establish UDP client (for OSC)
if sys.version_info.major == 3:
client = udp_client.SimpleUDPClient("127.0.0.1", 12345 )
# Send test data
while (1):
msg = [random.random() for x in range(8)]
print("SENT MESSAGE: ", msg )
client.send_message(args.address, msg)
time.sleep(.25)
elif sys.version_info.major == 2:
client = OSC.OSCClient()
client.connect((args.ip,args.port))
while (1):
msg = [random.random() for x in range(8)]
oscmsg = OSC.OSCMessage()
oscmsg.setAddress(args.address)
oscmsg.append(msg)
print("SENT MESSAGE: ", msg )
try:
client.send(oscmsg)
except:
pass
time.sleep(.25)
+86
Ver Arquivo
@@ -0,0 +1,86 @@
import socket
import sys
import time
import argparse
import signal
import struct
import os
import json
# Print received message to console
def print_message(*args):
try:
obj = json.loads(args[0])
print obj.get('data')
except BaseException as e:
print e
# print("(%s) RECEIVED MESSAGE: " % time.time() +
# ''.join(str(struct.unpack('>%df' % int(length), args[0]))))
# Clean exit from print mode
def exit_print(signal, frame):
print("Closing listener")
sys.exit(0)
# Record received message in text file
def record_to_file(*args):
textfile.write(str(time.time()) + ",")
textfile.write(''.join(str(struct.unpack('>%df' % length,args[0]))))
textfile.write("\n")
# Save recording, clean exit from record mode
def close_file(*args):
print("\nFILE SAVED")
textfile.close()
sys.exit(0)
if __name__ == "__main__":
# Collect command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--ip",
default="127.0.0.1", help="The ip to listen on")
parser.add_argument("--port",
type=int, default=12345, help="The port to listen on")
parser.add_argument("--address",default="/openbci", help="address to listen to")
parser.add_argument("--option",default="print",help="Debugger option")
parser.add_argument("--len",default=8,help="Debugger option")
args = parser.parse_args()
# Set up necessary parameters from command line
length = args.len
if args.option=="print":
signal.signal(signal.SIGINT, exit_print)
elif args.option=="record":
i = 0
while os.path.exists("udp_test%s.txt" % i):
i += 1
filename = "udp_test%i.txt" % i
textfile = open(filename, "w")
textfile.write("time,address,messages\n")
textfile.write("-------------------------\n")
print("Recording to %s" % filename)
signal.signal(signal.SIGINT, close_file)
# Connect to socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = (args.ip, args.port)
sock.bind(server_address)
# Display socket attributes
print('--------------------')
print("-- UDP LISTENER -- ")
print('--------------------')
print("IP:", args.ip)
print("PORT:", args.port)
print('--------------------')
print("%s option selected" % args.option)
# Receive messages
print("Listening...")
while True:
data, addr = sock.recvfrom(20000) # buffer size is 20000 bytes
if args.option=="print":
print_message(data)
elif args.option=="record":
record_to_file(data)
+42
Ver Arquivo
@@ -0,0 +1,42 @@
import socket
import random
import struct
import time
import argparse
if __name__ == "__main__":
# Collect command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--ip", default="127.0.0.1",
help="The ip of the OSC server")
parser.add_argument("--port", type=int, default=12345,
help="The port the OSC server is listening on")
parser.add_argument("--address", default="/openbci",
help="The address the OSC server is sending to")
args = parser.parse_args()
# Establish UDP socket
UDP_IP = args.ip
UDP_PORT = args.port
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
# Display socket attributes
print('--------------------')
print("-- UDP SIMULATION -- ")
print('--------------------')
print("IP:", args.ip)
print("PORT:", args.port)
print('--------------------')
# Send test data
while (1):
# generate random list
ar = [random.random() for x in range(8)]
print("SENT MESSAGE: ",ar)
# package as byte array
msg = struct.pack('>8f', *ar)
# send through socket
sock.sendto(msg, (UDP_IP, UDP_PORT))
time.sleep(.25)
+158
Ver Arquivo
@@ -0,0 +1,158 @@
# Networking Test Kit
This repository contains a collection of scripts for testing networking functionality for OSC, UDP, and LSL. This is intended for use with OpenBCI networking functionality, but could work for other tasks as well.
## Contents
+ [Installation](#Installation)
+ [Usage](#Use)
+ [OSC](#OSC)
+ [Send](#OSC-Sending)
+ [Receive](#OSC-Receiving)
+ [UDP](#UDP)
+ [Send](#UDP-Sending)
2. [Receive](#UDP-Receiving)
<a name="Usage"/>
## Installation
The scripts should work for Python 2 and Python 3. Install a different OSC package depending on your version of python.
Python 2:
```
pip install pyosc
```
Python 3:
```
pip install python-osc
```
<a name="Usage"/>
## Usage
This kit is provided to help you localize and diagnose any issues while using the OpenBCI networking functionality. It is helpful to run scripts that allow you to send and receive data in order to determine if your issue is with the program sending data, the program receiving data, or the network in-between.
If you are using the OpenBCI GUI, the first step might be to check if the GUI is correctly sending data. To do this, check the settings you entered into the Networking Widget, and run the appropriate **receive** networking script with those arguments. If you can correctly receive data, the issue is not with the GUI, but possibly with the 3rd party program or network.
Similarly, the **send** scripts can be used to determine if you can receive data in this 3rd party program. If you are sending data from this script and cannot receive it in a 3rd party program, check to see if your program is correctly configured to accept incoming connections. If you are sending data with the script and can receive it with the 3rd party program, but not with data sent from the OpenBCI GUI, check to see if your setup in the GUI is correct.
The "send" and "receive" scripts can also be used together to test your network, or as a sanity check for the settings your are using.
___
<a name="OSC"/>
## OSC
<a name="OSC-Receiving"/>
### Receiving
Run the script **osc_receive.py** to test listening to OSC messages. Use this to ensure that your program (such as the OpenBCI GUI) is sending messages correctly to the right location.
**Optional Arguments**:
```
--ip - specify an IP address [Default = 127.0.0.1]
--port - specify the port number [Default = 12345]
--address - specify an OSC message address [Default = \openbci]
--option - specify a debugger option [Default = print]
```
**Debugger Options**
There are two debugger options: **print** or **record**. **Print** outputs the messages receives to the console, while **record** saves a text file to this directory with the messages the debugger has received.
**Examples:**
Listen to and print messages from IP 127.0.0.1, port 12345, address "\openbci".
```
python osc_receive.py
```
Listen to and record messages from IP 127.0.0.1, port 12345, address "\openbci"
```
python osc_receive.py --option=record
```
Listen to and print messages from IP 137.110.96.253, port 8888, address "\accel"
```
python osc_receive.py --ip=137.110.96.253 --port=8888 --address=\accel
```
<a name="OSC-Sending"/>
### Sending
Run the script **osc_send.py** to test sending to OSC messages. Use this to ensure that your program is configured to receive messages correctly.
**Optional Arguments**:
```
--ip - specify an IP address [Default = 127.0.0.1]
--port - specify the port number [Default = 12345]
--address - specify an OSC message address [Default = \openbci]
```
**Examples:**
Send messages to IP 127.0.0.1, port 12345, address "\openbci".
```
python osc_send.py
```
Send messages to IP 137.110.96.253, port 8888, address "\accel"
```
python osc_send.py --ip=137.110.96.253 --port=8888 --address=\accel
```
___
<a name="UDP"/>
## UDP
<a name="UDP-Receiving"/>
### Receiving
Run the script **udp_receive.py** to test listening to UDP messages. Use this to ensure that your program (such as the OpenBCI GUI) is sending messages correctly to the right location.
**Optional Arguments**:
```
--ip - specify an IP address [Default = 127.0.0.1]
--port - specify the port number [Default = 12345]
--len - specify the length of message [Default = 8]
--option - specify a debugger option [Default = print]
```
**Len**
If you are receiving data other than 8 channel Time Series data (i.e. FFT or triggers), you *must* specify a length. The length is usually the number of channels you are sending (4 for the Ganglion, 16 for the Cyton with Daisy, 1 for a marker stream, etc). If you are sending FFT data, this must be 126.
**Debugger Options**
There are two debugger options: **print** or **record**. **Print** outputs the messages receives to the console, while **record** saves a text file to this directory with the messages the debugger has received.
**Examples:**
Listen to and print messages from IP 127.0.0.1, port 12345 (defaults).
```
python udp_receive.py
```
Listen to and record messages from IP 127.0.0.1, port 12345.
```
python udp_receive.py --option=record
```
Listen to and print messages from IP 137.110.96.253, port 8888.
```
python udp_receive.py --ip=137.110.96.253 --port=8888
```
<a name="UDP-Sending"/>
### Sending
Run the script **udp_send.py** to test sending to UDP messages. Use this to ensure that your program is configured to receive messages correctly.
**Optional Arguments**:
```
--ip - specify an IP address [Default = 127.0.0.1]
--port - specify the port number [Default = 12345]
```
**Examples:**
Send messages to IP 127.0.0.1, port 12345 (defaults).
```
python udp_send.py
```
Send messages to IP 137.110.96.253, port 8888.
```
python udp_send.py --ip=137.110.96.253 --port=8888
```
+701
Ver Arquivo
@@ -0,0 +1,701 @@
///////////////////////////////////////////////////////////////////////////////
//
// This class configures and manages the connection to the OpenBCI shield for
// the Arduino. The connection is implemented via a Serial connection.
// The OpenBCI is configured using single letter text commands sent from the
// PC to the Arduino. The EEG data streams back from the Arduino to the PC
// continuously (once started). This class defaults to using binary transfer
// for normal operation.
//
// Created: Chip Audette, Oct 2013
// Modified: through April 2014
// Modified again: Conor Russomanno Sept-Oct 2014
// Modified for Daisy (16-chan) OpenBCI V3: Conor Russomanno Nov 2014
// Modified Daisy Behaviors: Chip Audette Dec 2014
//
// Note: this class now expects the data format produced by OpenBCI V3.
//
/////////////////////////////////////////////////////////////////////////////
import java.io.OutputStream; //for logging raw bytes to an output file
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
final char command_stop = 's';
// final String command_startText = "x";
final char command_startBinary = 'b';
final char command_startBinary_wAux = 'n'; // already doing this with 'b' now
final char command_startBinary_4chan = 'v'; // not necessary now
final char command_activateFilters = 'f'; // swithed from 'F' to 'f' ... but not necessary because taken out of hardware code
final char command_deactivateFilters = 'g'; // not necessary anymore
final String command_setMode = "/"; // this is used to set the board into different modes
final char[] command_deactivate_channel = {'1', '2', '3', '4', '5', '6', '7', '8', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i'};
final char[] command_activate_channel = {'!', '@', '#', '$', '%', '^', '&', '*', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I'};
int channelDeactivateCounter = 0; //used for re-deactivating channels after switching settings...
final int BOARD_MODE_DEFAULT = 0;
final int BOARD_MODE_DEBUG = 1;
final int BOARD_MODE_ANALOG = 2;
final int BOARD_MODE_DIGITAL = 3;
final int BOARD_MODE_MARKER = 4;
//everything below is now deprecated...
// final String[] command_activate_leadoffP_channel = {'!', '@', '#', '$', '%', '^', '&', '*'}; //shift + 1-8
// final String[] command_deactivate_leadoffP_channel = {'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I'}; //letters (plus shift) right below 1-8
// final String[] command_activate_leadoffN_channel = {'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K'}; //letters (plus shift) below the letters below 1-8
// final String[] command_deactivate_leadoffN_channel = {'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<'}; //letters (plus shift) below the letters below the letters below 1-8
// final String command_biasAuto = "`";
// final String command_biasFixed = "~";
// ArrayList defaultChannelSettings;
//here is the routine that listens to the serial port.
//if any data is waiting, get it, parse it, and stuff it into our vector of
//pre-allocated dataPacketBuff
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
class Cyton {
private int nEEGValuesPerPacket = 8; //defined by the data format sent by cyton boards
private int nAuxValuesPerPacket = 3; //defined by the data format sent by cyton boards
private DataPacket_ADS1299 rawReceivedDataPacket;
private DataPacket_ADS1299 missedDataPacket;
private DataPacket_ADS1299 dataPacket;
// public int [] validAuxValues = {0, 0, 0};
// public boolean[] freshAuxValuesAvailable = {false, false, false};
// public boolean freshAuxValues = false;
//DataPacket_ADS1299 prevDataPacket;
private int nAuxValues;
private boolean isNewDataPacketAvailable = false;
private OutputStream output; //for debugging WEA 2014-01-26
private int prevSampleIndex = 0;
private int serialErrorCounter = 0;
private final int fsHzSerialCyton = 250; //sample rate used by OpenBCI board...set by its Arduino code
private final int fsHzSerialCytonDaisy = 125; //sample rate used by OpenBCI board...set by its Arduino code
private final int fsHzWifi = 1000; //sample rate used by OpenBCI board...set by its Arduino code
private final int NfftSerialCyton = 256;
private final int NfftSerialCytonDaisy = 256;
private final int NfftWifi = 1024;
private final float ADS1299_Vref = 4.5f; //reference voltage for ADC in ADS1299. set by its hardware
private float ADS1299_gain = 24.0; //assumed gain setting for ADS1299. set by its Arduino code
private float openBCI_series_resistor_ohms = 2200; // Ohms. There is a series resistor on the 32 bit board.
private float scale_fac_uVolts_per_count = ADS1299_Vref / ((float)(pow(2, 23)-1)) / ADS1299_gain * 1000000.f; //ADS1299 datasheet Table 7, confirmed through experiment
//float LIS3DH_full_scale_G = 4; // +/- 4G, assumed full scale setting for the accelerometer
private final float scale_fac_accel_G_per_count = 0.002 / ((float)pow(2, 4)); //assume set to +/4G, so 2 mG per digit (datasheet). Account for 4 bits unused
//private final float scale_fac_accel_G_per_count = 1.0; //to test stimulations //final float scale_fac_accel_G_per_count = 1.0;
private final float leadOffDrive_amps = 6.0e-9; //6 nA, set by its Arduino code
boolean isBiasAuto = true; //not being used?
private int curBoardMode = BOARD_MODE_DEFAULT;
//data related to Conor's setup for V3 boards
final char[] EOT = {'$', '$', '$'};
char[] prev3chars = {'#', '#', '#'};
public String potentialFailureMessage = "";
public String defaultChannelSettings = "";
public String daisyOrNot = "";
public int hardwareSyncStep = 0; //start this at 0...
private long timeOfLastCommand = 0; //used when sync'ing to hardware
private int curInterface = INTERFACE_SERIAL;
private int sampleRate = fsHzWifi;
PApplet mainApplet;
//some get methods
public float getSampleRate() {
if (isSerial()) {
if (nchan == NCHAN_CYTON_DAISY) {
return fsHzSerialCytonDaisy;
} else {
return fsHzSerialCyton;
}
} else {
return hub.getSampleRate();
}
}
// TODO: ADJUST getNfft for new sample variable sample rates
public int getNfft() {
if (isWifi()) {
if (sampleRate == fsHzSerialCyton) {
return NfftSerialCyton;
} else {
return NfftWifi;
}
} else {
if (nchan == NCHAN_CYTON_DAISY) {
return NfftSerialCytonDaisy;
} else {
return NfftSerialCyton;
}
}
}
public int getBoardMode() {
return curBoardMode;
}
public int getInterface() {
return curInterface;
}
public float get_Vref() {
return ADS1299_Vref;
}
public void set_ADS1299_gain(float _gain) {
ADS1299_gain = _gain;
scale_fac_uVolts_per_count = ADS1299_Vref / ((float)(pow(2, 23)-1)) / ADS1299_gain * 1000000.0; //ADS1299 datasheet Table 7, confirmed through experiment
}
public float get_ADS1299_gain() {
return ADS1299_gain;
}
public float get_series_resistor() {
return openBCI_series_resistor_ohms;
}
public float get_scale_fac_uVolts_per_count() {
return scale_fac_uVolts_per_count;
}
public float get_scale_fac_accel_G_per_count() {
return scale_fac_accel_G_per_count;
}
public float get_leadOffDrive_amps() {
return leadOffDrive_amps;
}
public String get_defaultChannelSettings() {
return defaultChannelSettings;
}
public void setBoardMode(int boardMode) {
hub.sendCommand("/" + boardMode);
curBoardMode = boardMode;
print("Cyton: setBoardMode to :" + curBoardMode);
}
public void setSampleRate(int _sampleRate) {
sampleRate = _sampleRate;
output("Setting sample rate for Cyton to " + sampleRate + "Hz");
println("Setting sample rate for Cyton to " + sampleRate + "Hz");
hub.setSampleRate(sampleRate);
}
public boolean setInterface(int _interface) {
curInterface = _interface;
// println("current interface: " + curInterface);
println("setInterface: curInterface: " + getInterface());
if (isWifi()) {
setSampleRate((int)fsHzWifi);
hub.setProtocol(PROTOCOL_WIFI);
} else if (isSerial()) {
setSampleRate((int)fsHzSerialCyton);
hub.setProtocol(PROTOCOL_SERIAL);
}
return true;
}
//constructors
Cyton() {
}; //only use this if you simply want access to some of the constants
Cyton(PApplet applet, String comPort, int baud, int nEEGValuesPerOpenBCI, boolean useAux, int nAuxValuesPerOpenBCI, int _interface) {
curInterface = _interface;
initDataPackets(nEEGValuesPerOpenBCI, nAuxValuesPerOpenBCI);
if (isSerial()) {
hub.connectSerial(comPort);
} else if (isWifi()) {
hub.connectWifi(comPort);
}
}
public void initDataPackets(int _nEEGValuesPerPacket, int _nAuxValuesPerPacket) {
nEEGValuesPerPacket = _nEEGValuesPerPacket;
nAuxValuesPerPacket = _nAuxValuesPerPacket;
//allocate space for data packet
rawReceivedDataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
missedDataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
dataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this could be 8 or 16 channels
//set all values to 0 so not null
for (int i = 0; i < nEEGValuesPerPacket; i++) {
rawReceivedDataPacket.values[i] = 0;
//prevDataPacket.values[i] = 0;
}
for (int i=0; i < nEEGValuesPerPacket; i++) {
dataPacket.values[i] = 0;
missedDataPacket.values[i] = 0;
}
for (int i = 0; i < nAuxValuesPerPacket; i++) {
rawReceivedDataPacket.auxValues[i] = 0;
dataPacket.auxValues[i] = 0;
missedDataPacket.auxValues[i] = 0;
//prevDataPacket.auxValues[i] = 0;
}
}
public int closeSDandPort() {
closeSDFile();
return closePort();
}
public int closePort() {
if (isSerial()) {
return hub.disconnectSerial();
} else {
return hub.disconnectWifi();
}
}
public int closeSDFile() {
println("Closing any open SD file. Writing 'j' to OpenBCI.");
if (isPortOpen()) write('j'); // tell the SD file to close if one is open...
delay(100); //make sure 'j' gets sent to the board
return 0;
}
public void syncWithHardware(int sdSetting) {
switch (hardwareSyncStep) {
case 1: //send # of channels (8 or 16) ... (regular or daisy setup)
println("Cyton: syncWithHardware: [1] Sending channel count (" + nchan + ") to OpenBCI...");
if (nchan == 8) {
write('c');
}
if (nchan == 16) {
write('C', false);
}
break;
case 2: //reset hardware to default registers
println("Cyton: syncWithHardware: [2] Reseting OpenBCI registers to default... writing \'d\'...");
write('d'); // TODO: Why does this not get a $$$ readyToSend = false?
break;
case 3: //ask for series of channel setting ASCII values to sync with channel setting interface in GUI
println("Cyton: syncWithHardware: [3] Retrieving OpenBCI's channel settings to sync with GUI... writing \'D\'... waiting for $$$...");
write('D', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 4: //check existing registers
println("Cyton: syncWithHardware: [4] Retrieving OpenBCI's full register map for verification... writing \'?\'... waiting for $$$...");
write('?', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 5:
// write("j"); // send OpenBCI's 'j' commaned to make sure any already open SD file is closed before opening another one...
switch (sdSetting) {
case 1: //"5 min max"
write('A', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 2: //"5 min max"
write('S', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 3: //"5 min max"
write('F', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 4: //"5 min max"
write('G', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 5: //"5 min max"
write('H', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 6: //"5 min max"
write('J', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 7: //"5 min max"
write('K', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
case 8: //"5 min max"
write('L', false); //wait for $$$ to iterate... applies to commands expecting a response
break;
default:
break; // Do Nothing
}
println("Cyton: syncWithHardware: [5] Writing selected SD setting (" + sdSettingString + ") to OpenBCI...");
//final hacky way of abandoning initiation if someone selected daisy but doesn't have one connected.
if(abandonInit){
haltSystem();
output("No daisy board present. Make sure you selected the correct number of channels.");
controlPanel.open();
abandonInit = false;
}
break;
case 6:
output("Cyton: syncWithHardware: The GUI is done intializing. Click outside of the control panel to interact with the GUI.");
hub.changeState(STATE_STOPPED);
systemMode = 10;
controlPanel.close();
topNav.controlPanelCollapser.setIsActive(false);
//renitialize GUI if nchan has been updated... needs to be built
break;
}
}
public void writeCommand(String val) {
if (hub.isHubRunning()) {
hub.write(String.valueOf(val));
}
}
public boolean write(char val) {
if (hub.isHubRunning()) {
hub.sendCommand(val);
return true;
}
return false;
}
public boolean write(char val, boolean _readyToSend) {
// if (isSerial()) {
// iSerial.setReadyToSend(_readyToSend);
// }
return write(val);
}
public boolean write(String out, boolean _readyToSend) {
// if (isSerial()) {
// iSerial.setReadyToSend(_readyToSend);
// }
return write(out);
}
public boolean write(String out) {
if (hub.isHubRunning()) {
hub.write(out);
return true;
}
return false;
}
private boolean isSerial () {
// println("My interface is " + curInterface);
return curInterface == INTERFACE_SERIAL;
}
private boolean isWifi () {
return curInterface == INTERFACE_HUB_WIFI;
}
public void startDataTransfer() {
if (isPortOpen()) {
// Now give the command to start binary data transmission
if (isSerial()) {
hub.changeState(STATE_NORMAL); // make sure it's now interpretting as binary
println("Cyton: startDataTransfer(): writing \'" + command_startBinary + "\' to the serial port...");
// if (isSerial()) iSerial.clear(); // clear anything in the com port's buffer
write(command_startBinary);
} else if (isWifi()) {
println("Cyton: startDataTransfer(): writing \'" + command_startBinary + "\' to the wifi shield...");
write(command_startBinary);
}
} else {
println("port not open");
}
}
public void stopDataTransfer() {
if (isPortOpen()) {
hub.changeState(STATE_STOPPED); // make sure it's now interpretting as binary
println("Cyton: startDataTransfer(): writing \'" + command_stop + "\' to the serial port...");
write(command_stop);// + "\n");
}
}
public void printRegisters() {
if (isPortOpen()) {
println("Cyton: printRegisters(): Writing ? to OpenBCI...");
write('?');
}
}
/* **** Borrowed from Chris Viegl from his OpenBCI parser for BrainBay
Modified by Joel Murphy and Conor Russomanno to read OpenBCI data
Packet Parser for OpenBCI (1-N channel binary format):
3-byte data values are stored in 'little endian' formant in AVRs
so this protocol parser expects the lower bytes first.
Start Indicator: 0xA0
EXPECTING STANDARD PACKET LENGTH DON'T NEED: Packet_length : 1 byte (length = 4 bytes framenumber + 4 bytes per active channel + (optional) 4 bytes for 1 Aux value)
Framenumber : 1 byte (Sequential counter of packets)
Channel 1 data : 3 bytes
...
Channel 8 data : 3 bytes
Aux Values : UP TO 6 bytes
End Indcator : 0xC0
TOTAL OF 33 bytes ALL DAY
********************************************************************* */
private int nDataValuesInPacket = 0;
private int localByteCounter=0;
private int localChannelCounter=0;
private int PACKET_readstate = 0;
// byte[] localByteBuffer = {0,0,0,0};
private byte[] localAdsByteBuffer = {0, 0, 0};
private byte[] localAccelByteBuffer = {0, 0};
private boolean isPortOpen() {
if (isWifi() || isSerial()) {
return hub.isPortOpen();
} else {
return false;
}
}
//activate or deactivate an EEG channel...channel counting is zero through nchan-1
public void changeChannelState(int Ichan, boolean activate) {
if (isPortOpen()) {
// if ((Ichan >= 0) && (Ichan < command_activate_channel.length)) {
if ((Ichan >= 0)) {
if (activate) {
// write(command_activate_channel[Ichan]);
// gui.cc.powerUpChannel(Ichan);
w_timeSeries.hsc.powerUpChannel(Ichan);
} else {
// write(command_deactivate_channel[Ichan]);
// gui.cc.powerDownChannel(Ichan);
w_timeSeries.hsc.powerDownChannel(Ichan);
}
}
}
}
//deactivate an EEG channel...channel counting is zero through nchan-1
public void deactivateChannel(int Ichan) {
if (isPortOpen()) {
if ((Ichan >= 0) && (Ichan < command_deactivate_channel.length)) {
write(command_deactivate_channel[Ichan]);
}
}
}
//activate an EEG channel...channel counting is zero through nchan-1
public void activateChannel(int Ichan) {
if (isPortOpen()) {
if ((Ichan >= 0) && (Ichan < command_activate_channel.length)) {
write(command_activate_channel[Ichan]);
}
}
}
//return the state
public boolean isStateNormal() {
if (hub.get_state() == STATE_NORMAL) {
return true;
} else {
return false;
}
}
private int copyRawDataToFullData() {
//Prior to the 16-chan OpenBCI, we did NOT have rawReceivedDataPacket along with dataPacket...we just had dataPacket.
//With the 16-chan OpenBCI, where the first 8 channels are sent and then the second 8 channels are sent, we introduced
//this extra structure so that we could alternate between them.
//
//This function here decides how to join the latest data (rawReceivedDataPacket) into the full dataPacket
if (dataPacket.values.length < 2*rawReceivedDataPacket.values.length) {
//this is an 8 channel board, so simply copy the data
return rawReceivedDataPacket.copyTo(dataPacket);
} else {
//this is 16-channels, so copy the raw data into the correct channels of the new data
int offsetInd_values = 0; //this is correct assuming we just recevied a "board" packet (ie, channels 1-8)
int offsetInd_aux = 0; //this is correct assuming we just recevied a "board" packet (ie, channels 1-8)
if (rawReceivedDataPacket.sampleIndex % 2 == 0) { // even data packets are from the daisy board
offsetInd_values = rawReceivedDataPacket.values.length; //start copying to the 8th slot
//offsetInd_aux = rawReceivedDataPacket.auxValues.length; //start copying to the 3rd slot
offsetInd_aux = 0;
}
return rawReceivedDataPacket.copyTo(dataPacket, offsetInd_values, offsetInd_aux);
}
}
public int copyDataPacketTo(DataPacket_ADS1299 target) {
return dataPacket.copyTo(target);
}
private long timeOfLastChannelWrite = 0;
private int channelWriteCounter = 0;
private boolean isWritingChannel = false;
public void configureAllChannelsToDefault() {
write('d');
};
public void initChannelWrite(int _numChannel) { //numChannel counts from zero
timeOfLastChannelWrite = millis();
isWritingChannel = true;
}
public void syncChannelSettings() {
write("r,start" + TCP_STOP);
}
/**
* Used to convert a gain from the hub back into local codes.
*/
public char getCommandForGain(int gain) {
switch (gain) {
case 1:
return '0';
case 2:
return '1';
case 4:
return '2';
case 6:
return '3';
case 8:
return '4';
case 12:
return '5';
case 24:
default:
return '6';
}
}
/**
* Used to convert raw code to hub code
* @param inputType {String} - The input from a hub sync channel with register settings
*/
public char getCommandForInputType(String inputType) {
if (inputType.equals("normal")) return '0';
if (inputType.equals("shorted")) return '1';
if (inputType.equals("biasMethod")) return '2';
if (inputType.equals("mvdd")) return '3';
if (inputType.equals("temp")) return '4';
if (inputType.equals("testsig")) return '5';
if (inputType.equals("biasDrp")) return '6';
if (inputType.equals("biasDrn")) return '7';
return '0';
}
/**
* Used to convert a local channel code into a hub gain which is human
* readable and in scientific values.
*/
public int getGainForCommand(char cmd) {
switch (cmd) {
case '0':
return 1;
case '1':
return 2;
case '2':
return 4;
case '3':
return 6;
case '4':
return 8;
case '5':
return 12;
case '6':
default:
return 24;
}
}
/**
* Used right before a channel setting command is sent to the hub to convert
* local values into the expected form for the hub.
*/
public String getInputTypeForCommand(char cmd) {
final String inputTypeShorted = "shorted";
final String inputTypeBiasMethod = "biasMethod";
final String inputTypeMvdd = "mvdd";
final String inputTypeTemp = "temp";
final String inputTypeTestsig = "testsig";
final String inputTypeBiasDrp = "biasDrp";
final String inputTypeBiasDrn = "biasDrn";
final String inputTypeNormal = "normal";
switch (cmd) {
case '1':
return inputTypeShorted;
case '2':
return inputTypeBiasMethod;
case '3':
return inputTypeMvdd;
case '4':
return inputTypeTemp;
case '5':
return inputTypeTestsig;
case '6':
return inputTypeBiasDrp;
case '7':
return inputTypeBiasDrn;
case '0':
default:
return inputTypeNormal;
}
}
/**
* Used to convert a local index number to a hub human readable sd setting
* command.
*/
public String getSDSettingForSetting(int setting) {
switch (setting) {
case 1:
return "5min";
case 2:
return "15min";
case 3:
return "30min";
case 4:
return "1hour";
case 5:
return "2hour";
case 6:
return "4hour";
case 7:
return "12hour";
case 8:
return "24hour";
default:
return "";
}
}
// FULL DISCLAIMER: this method is messy....... very messy... we had to brute force a firmware miscue
public void writeChannelSettings(int _numChannel, char[][] channelSettingValues) { //numChannel counts from zero
String output = "r,set,";
output += Integer.toString(_numChannel) + ","; // 0 indexed channel number
output += channelSettingValues[_numChannel][0] + ","; // power down
output += getGainForCommand(channelSettingValues[_numChannel][1]) + ","; // gain
output += getInputTypeForCommand(channelSettingValues[_numChannel][2]) + ",";
output += channelSettingValues[_numChannel][3] + ",";
output += channelSettingValues[_numChannel][4] + ",";
output += channelSettingValues[_numChannel][5] + TCP_STOP;
write(output);
// verbosePrint("done writing channel.");
isWritingChannel = false;
}
private long timeOfLastImpWrite = 0;
private int impWriteCounter = 0;
private boolean isWritingImp = false;
public boolean get_isWritingImp() {
return isWritingImp;
}
// public void initImpWrite(int _numChannel) { //numChannel counts from zero
// timeOfLastImpWrite = millis();
// isWritingImp = true;
// }
public void writeImpedanceSettings(int _numChannel, char[][] impedanceCheckValues) { //numChannel counts from zero
String output = "i,set,";
if (_numChannel < 8) {
output += (char)('0'+(_numChannel+1)) + ",";
} else { //(_numChannel >= 8) {
//command_activate_channel holds non-daisy and daisy values
output += command_activate_channel[_numChannel] + ",";
}
output += impedanceCheckValues[_numChannel][0] + ",";
output += impedanceCheckValues[_numChannel][1] + TCP_STOP;
write(output);
isWritingImp = false;
}
};
+321
Ver Arquivo
@@ -0,0 +1,321 @@
///////////////////////////////////////////////////////////////////////////////
//
// This class configures and manages the connection to the OpenBCI Ganglion.
// The connection is implemented via a TCP connection to a TCP port.
// The Gagnlion is configured using single letter text commands sent from the
// PC to the TCP server. The EEG data streams back from the Ganglion, to the
// TCP server and back to the PC continuously (once started).
//
// Created: AJ Keller, August 2016
//
/////////////////////////////////////////////////////////////////////////////
// import java.io.OutputStream; //for logging raw bytes to an output file
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
class Ganglion {
final static String TCP_CMD_ACCEL = "a";
final static String TCP_CMD_CONNECT = "c";
final static String TCP_CMD_COMMAND = "k";
final static String TCP_CMD_DISCONNECT = "d";
final static String TCP_CMD_DATA= "t";
final static String TCP_CMD_ERROR = "e"; //<>//
final static String TCP_CMD_IMPEDANCE = "i";
final static String TCP_CMD_LOG = "l";
final static String TCP_CMD_SCAN = "s";
final static String TCP_CMD_STATUS = "q";
final static String TCP_STOP = ",;\n";
final static String TCP_ACTION_START = "start";
final static String TCP_ACTION_STATUS = "status";
final static String TCP_ACTION_STOP = "stop";
final static String GANGLION_BOOTLOADER_MODE = ">";
final static int NUM_ACCEL_DIMS = 3;
final static int RESP_ERROR_UNKNOWN = 499;
final static int RESP_ERROR_BAD_PACKET = 500;
final static int RESP_ERROR_BAD_NOBLE_START = 501;
final static int RESP_ERROR_ALREADY_CONNECTED = 408;
final static int RESP_ERROR_COMMAND_NOT_RECOGNIZED = 406;
final static int RESP_ERROR_DEVICE_NOT_FOUND = 405;
final static int RESP_ERROR_NO_OPEN_BLE_DEVICE = 400;
final static int RESP_ERROR_UNABLE_TO_CONNECT = 402;
final static int RESP_ERROR_UNABLE_TO_DISCONNECT = 401;
final static int RESP_ERROR_SCAN_ALREADY_SCANNING = 409;
final static int RESP_ERROR_SCAN_NONE_FOUND = 407;
final static int RESP_ERROR_SCAN_NO_SCAN_TO_STOP = 410;
final static int RESP_ERROR_SCAN_COULD_NOT_START = 412;
final static int RESP_ERROR_SCAN_COULD_NOT_STOP = 411;
final static int RESP_GANGLION_FOUND = 201;
final static int RESP_SUCCESS = 200;
final static int RESP_SUCCESS_DATA_ACCEL = 202;
final static int RESP_SUCCESS_DATA_IMPEDANCE = 203;
final static int RESP_SUCCESS_DATA_SAMPLE = 204;
final static int RESP_STATUS_CONNECTED = 300;
final static int RESP_STATUS_DISCONNECTED = 301;
final static int RESP_STATUS_SCANNING = 302;
final static int RESP_STATUS_NOT_SCANNING = 303;
private int nEEGValuesPerPacket = NCHAN_GANGLION; // Defined by the data format sent by cyton boards
private int nAuxValuesPerPacket = NUM_ACCEL_DIMS; // Defined by the arduino code
private final float fsHzBLE = 200.0f; //sample rate used by OpenBCI Ganglion board... set by its Arduino code
private final float fsHzWifi = 1600.0f; //sample rate used by OpenBCI Ganglion board on wifi, set by hub
private final int NfftBLE = 256;
private final int NfftWifi = 2048;
private final float MCP3912_Vref = 1.2f; // reference voltage for ADC in MCP3912 set in hardware
private float MCP3912_gain = 1.0; //assumed gain setting for MCP3912. NEEDS TO BE ADJUSTABLE JM
private float scale_fac_uVolts_per_count = (MCP3912_Vref * 1000000.f) / (8388607.0 * MCP3912_gain * 1.5 * 51.0); //MCP3912 datasheet page 34. Gain of InAmp = 80
// private float scale_fac_accel_G_per_count = 0.032;
private float scale_fac_accel_G_per_count_ble = 0.016;
private float scale_fac_accel_G_per_count_wifi = 0.001;
// private final float scale_fac_accel_G_per_count = 0.002 / ((float)pow(2,4)); //assume set to +/4G, so 2 mG per digit (datasheet). Account for 4 bits unused
// private final float leadOffDrive_amps = 6.0e-9; //6 nA, set by its Arduino code
private int curInterface = INTERFACE_NONE;
private DataPacket_ADS1299 dataPacket;
private boolean connected = false;
public int numberOfDevices = 0;
public int maxNumberOfDevices = 10;
private boolean checkingImpedance = false;
private boolean accelModeActive = false;
public boolean impedanceUpdated = false;
public int[] impedanceArray = new int[NCHAN_GANGLION + 1];
private int sampleRate = (int)fsHzWifi;
// Getters
public float getSampleRate() {
if (isBLE()) {
return fsHzBLE;
} else {
return hub.getSampleRate();
}
}
public int getNfft() {
if (isWifi()) {
if (hub.getSampleRate() == (int)fsHzBLE) {
return NfftBLE;
} else {
return NfftWifi;
}
} else {
return NfftBLE;
}
}
public float get_scale_fac_uVolts_per_count() { return scale_fac_uVolts_per_count; }
public float get_scale_fac_accel_G_per_count() {
if (isWifi()) {
return scale_fac_accel_G_per_count_wifi;
} else {
return scale_fac_accel_G_per_count_ble;
}
}
public boolean isCheckingImpedance() { return checkingImpedance; }
public boolean isAccelModeActive() { return accelModeActive; }
public void overrideCheckingImpedance(boolean val) { checkingImpedance = val; }
public int getInterface() {
return curInterface;
}
public boolean isBLE () {
return curInterface == INTERFACE_HUB_BLE || curInterface == INTERFACE_HUB_BLED112;
}
public boolean isWifi () {
return curInterface == INTERFACE_HUB_WIFI;
}
public boolean isPortOpen() {
return hub.isPortOpen();
}
private PApplet mainApplet;
//constructors
Ganglion() {}; //only use this if you simply want access to some of the constants
Ganglion(PApplet applet) {
mainApplet = applet;
initDataPackets(nEEGValuesPerPacket, nAuxValuesPerPacket);
}
public void initDataPackets(int _nEEGValuesPerPacket, int _nAuxValuesPerPacket) {
nEEGValuesPerPacket = _nEEGValuesPerPacket;
nAuxValuesPerPacket = _nAuxValuesPerPacket;
// For storing data into
dataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
for(int i = 0; i < nEEGValuesPerPacket; i++) {
dataPacket.values[i] = 0;
}
for(int i = 0; i < nAuxValuesPerPacket; i++){
dataPacket.auxValues[i] = 0;
}
}
private void handleError(int code, String msg) {
output("Code " + code + "Error: " + msg);
println("Code " + code + "Error: " + msg);
}
public void processImpedance(String msg) {
String[] list = split(msg, ',');
if (Integer.parseInt(list[1]) == RESP_SUCCESS_DATA_IMPEDANCE) {
int channel = Integer.parseInt(list[2]);
if (channel < 5) { //<>//
int value = Integer.parseInt(list[3]);
impedanceArray[channel] = value;
if (channel == 0) {
impedanceUpdated = true;
println("Impedance for channel reference is " + value + " ohms.");
} else {
println("Impedance for channel " + channel + " is " + value + " ohms.");
}
}
}
}
public void setSampleRate(int _sampleRate) {
sampleRate = _sampleRate;
hub.setSampleRate(sampleRate);
output("Setting sample rate for Ganglion to " + sampleRate + "Hz");
}
public void setInterface(int _interface) {
curInterface = _interface;
if (isBLE()) {
setSampleRate((int)fsHzBLE);
if (_interface == INTERFACE_HUB_BLE) {
hub.setProtocol(PROTOCOL_BLE);
} else {
hub.setProtocol(PROTOCOL_BLED112);
}
// hub.searchDeviceStart();
} else if (isWifi()) {
setSampleRate((int)fsHzWifi);
hub.setProtocol(PROTOCOL_WIFI);
hub.searchDeviceStart();
}
}
public int copyDataPacketTo(DataPacket_ADS1299 target) {
return dataPacket.copyTo(target);
}
// SCANNING/SEARHING FOR DEVICES
public int closePort() {
if (isBLE()) {
hub.disconnectBLE();
} else if (isWifi()) {
hub.disconnectWifi();
}
return 0;
}
/**
* @description Sends a start streaming command to the Ganglion Node module.
*/
void startDataTransfer(){
hub.changeState(STATE_NORMAL); // make sure it's now interpretting as binary
println("Ganglion: startDataTransfer(): sending \'" + command_startBinary);
if (checkingImpedance) {
impedanceStop();
delay(100);
hub.sendCommand('b');
} else {
hub.sendCommand('b');
}
}
/**
* @description Sends a stop streaming command to the Ganglion Node module.
*/
public void stopDataTransfer() {
hub.changeState(STATE_STOPPED); // make sure it's now interpretting as binary
println("Ganglion: stopDataTransfer(): sending \'" + command_stop);
hub.sendCommand('s');
}
private void printGanglion(String msg) {
print("Ganglion: "); println(msg);
}
// Channel setting
//activate or deactivate an EEG channel...channel counting is zero through nchan-1
public void changeChannelState(int Ichan, boolean activate) {
if (isPortOpen()) {
if ((Ichan >= 0)) {
if (activate) {
println("Ganglion: changeChannelState(): activate: sending " + command_activate_channel[Ichan]);
hub.sendCommand(command_activate_channel[Ichan]);
w_timeSeries.hsc.powerUpChannel(Ichan);
} else {
println("Ganglion: changeChannelState(): deactivate: sending " + command_deactivate_channel[Ichan]);
hub.sendCommand(command_deactivate_channel[Ichan]);
w_timeSeries.hsc.powerDownChannel(Ichan);
}
}
}
}
/**
* Used to start accel data mode. Accel arrays will arrive asynchronously!
*/
public void accelStart() {
println("Ganglion: accell: START");
hub.write(TCP_CMD_ACCEL + "," + TCP_ACTION_START + TCP_STOP);
accelModeActive = true;
}
/**
* Used to stop accel data mode. Some accel arrays may arrive after stop command
* was sent by this function.
*/
public void accelStop() {
println("Ganglion: accel: STOP");
hub.write(TCP_CMD_ACCEL + "," + TCP_ACTION_STOP + TCP_STOP);
accelModeActive = false;
}
/**
* Used to start impedance testing. Impedances will arrive asynchronously!
*/
public void impedanceStart() {
println("Ganglion: impedance: START");
hub.write(TCP_CMD_IMPEDANCE + "," + TCP_ACTION_START + TCP_STOP);
checkingImpedance = true;
}
/**
* Used to stop impedance testing. Some impedances may arrive after stop command
* was sent by this function.
*/
public void impedanceStop() {
println("Ganglion: impedance: STOP");
hub.write(TCP_CMD_IMPEDANCE + "," + TCP_ACTION_STOP + TCP_STOP);
checkingImpedance = false;
}
/**
* Puts the ganglion in bootloader mode.
*/
public void enterBootloaderMode() {
println("Ganglion: Entering Bootloader Mode");
hub.sendCommand(GANGLION_BOOTLOADER_MODE.charAt(0));
delay(500);
closePort();
haltSystem();
initSystemButton.setString("START SYSTEM");
controlPanel.open();
output("Ganglion now in bootloader mode! Enjoy!");
}
};
+7 -7
Ver Arquivo
@@ -7,19 +7,19 @@
// 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] |
// ------------------------------------------------
// +----------------------------------------------+
// | | [11] |
// | [1] [2]---[15]--[3]---[16]--|
// | [1] [2]---[15]--[3]---[16]--+
// | | [12] |
// |---------[4]----------[5]---------[6]---------|
// +---------[4]----------[5]---------[6]---------+
// | | [13] |
// | [7] [8]---[17]--[9]---[18]--|
// | [7] [8]---[17]--[9]---[18]--+
// | | [14] |
// ------------------------------------------------
// +----------------------------------------------+
// | [10] |
// ------------------------------------------------
// +----------------------------------------------+
//
// Created by: Conor Russomanno (May 2016)
//
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+314 -80
Ver Arquivo
@@ -49,14 +49,11 @@ void openNewLogFileBDF(String _fileName) {
closeLogFile();
}
//open the new file
if (eegDataSource == DATASOURCE_GANGLION) {
fileoutput_bdf = new OutputFile_BDF(ganglion.get_fs_Hz(), nchan, _fileName);
} else {
fileoutput_bdf = new OutputFile_BDF(openBCI.get_fs_Hz(), nchan, _fileName);
}
fileoutput_bdf = new OutputFile_BDF(getSampleRateSafe(), nchan, _fileName);
output_fname = fileoutput_bdf.fname;
println("openBCI: openNewLogFile: opened BDF output file: " + output_fname);
output("openBCI: openNewLogFile: opened BDF output file: " + output_fname);
println("cyton: openNewLogFile: opened BDF output file: " + output_fname);
output("cyton: openNewLogFile: opened BDF output file: " + output_fname);
}
/**
@@ -70,14 +67,11 @@ void openNewLogFileODF(String _fileName) {
closeLogFile();
}
//open the new file
if (eegDataSource == DATASOURCE_GANGLION) {
fileoutput_odf = new OutputFile_rawtxt(ganglion.get_fs_Hz(), _fileName);
} else {
fileoutput_odf = new OutputFile_rawtxt(openBCI.get_fs_Hz(), _fileName);
}
fileoutput_odf = new OutputFile_rawtxt(getSampleRateSafe(), _fileName);
output_fname = fileoutput_odf.fname;
println("openBCI: openNewLogFile: opened ODF output file: " + output_fname);
output("openBCI: openNewLogFile: opened ODF output file: " + output_fname);
println("cyton: openNewLogFile: opened ODF output file: " + output_fname);
output("cyton: openNewLogFile: opened ODF output file: " + output_fname);
}
/**
@@ -158,9 +152,15 @@ String getDateString() {
//these functions are relevant to convertSDFile
void createPlaybackFileFromSD() {
logFileName = "data/EEG_Data/SDconverted-"+getDateString()+".txt";
logFileName = "SavedData/SDconverted-"+getDateString()+".csv";
dataWriter = createWriter(logFileName);
dataWriter.println("%OBCI Data Log - " + getDateString());
dataWriter.println("%OBCI SD Convert - " + getDateString());
dataWriter.println("%");
dataWriter.println("%Sample Rate = 250.0 Hz");
dataWriter.println("%First Column = SampleIndex");
dataWriter.println("%Last Column = Timestamp");
dataWriter.println("%Other Columns = EEG data in microvolts followed by Accel Data (in G) interleaved with Aux Data");
}
void sdFileSelected(File selection) {
@@ -239,17 +239,22 @@ public class OutputFile_rawtxt {
output.flush();
}
public void writeRawData_dataPacket(DataPacket_ADS1299 data, float scale_to_uV, float scale_for_aux) {
public void writeRawData_dataPacket(DataPacket_ADS1299 data, float scale_to_uV, float scale_for_aux, int stopByte) {
//get current date time with Date()
Date date = new Date();
if (output != null) {
output.print(Integer.toString(data.sampleIndex));
writeValues(data.values,scale_to_uV);
writeAccValues(data.auxValues,scale_for_aux);
if (eegDataSource == DATASOURCE_GANGLION) {
writeAccValues(data.auxValues, scale_for_aux);
} else {
if (stopByte == 0xC1) {
writeAuxValues(data);
} else {
writeAccValues(data.auxValues, scale_for_aux);
}
}
output.print( ", " + dateFormat.format(date));
output.println(); rowsWritten++;
//output.flush();
@@ -272,6 +277,51 @@ public class OutputFile_rawtxt {
}
}
private void writeAuxValues(DataPacket_ADS1299 data) {
if (eegDataSource == DATASOURCE_CYTON) {
// println("board mode: " + cyton.getBoardMode());
if (cyton.getBoardMode() == BOARD_MODE_DIGITAL) {
if (cyton.isWifi()) {
output.print(", " + ((data.auxValues[0] & 0xFF00) >> 8));
output.print(", " + (data.auxValues[0] & 0xFF));
output.print(", " + data.auxValues[1]);
} else {
output.print(", " + ((data.auxValues[0] & 0xFF00) >> 8));
output.print(", " + (data.auxValues[0] & 0xFF));
output.print(", " + ((data.auxValues[1] & 0xFF00) >> 8));
output.print(", " + (data.auxValues[1] & 0xFF));
output.print(", " + data.auxValues[2]);
}
} else if (cyton.getBoardMode() == BOARD_MODE_ANALOG) {
if (cyton.isWifi()) {
output.print(", " + data.auxValues[0]);
output.print(", " + data.auxValues[1]);
} else {
output.print(", " + data.auxValues[0]);
output.print(", " + data.auxValues[1]);
output.print(", " + data.auxValues[2]);
}
} else if (cyton.getBoardMode() == BOARD_MODE_MARKER) {
output.print(", " + data.auxValues[0]);
if ( data.auxValues[0] > 0) {
hub.validLastMarker = data.auxValues[0];
}
} else {
for (int Ival = 0; Ival < 3; Ival++) {
output.print(", " + data.auxValues[Ival]);
}
}
} else {
for (int i = 0; i < 3; i++) {
output.print(", " + (data.auxValues[i] & 0xFF));
output.print(", " + ((data.auxValues[i] & 0xFF00) >> 8));
}
}
}
public void closeFile() {
output.flush();
output.close();
@@ -510,14 +560,14 @@ public class OutputFile_BDF {
}
writeChannelDataValues(data.rawValues);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
writeAuxDataValues(data.rawAuxValues);
}
samplesInDataRecord++;
// writeValues(data.auxValues,scale_for_aux);
if (samplesInDataRecord >= fs_Hz) {
arrayCopy(chanValBuf,chanValBuf_buffer);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
arrayCopy(auxValBuf,auxValBuf_buffer);
}
@@ -535,7 +585,7 @@ public class OutputFile_BDF {
}
}
}
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
for (int i = 0; i < nbAux; i++) {
for (int j = 0; j < fs_Hz; j++) {
for (int k = 0; k < 3; k++) {
@@ -940,9 +990,9 @@ public class OutputFile_BDF {
* @returns {String} - A fully qualified name of an output file with `str`.
*/
private String getFileName(String s) {
String output = "SavedData"+System.getProperty("file.separator")+"OpenBCI-EDF-";
String output = "SavedData"+System.getProperty("file.separator")+"OpenBCI-BDF-";
output += s;
output += ".edf";
output += ".bdf";
return output;
}
@@ -952,7 +1002,7 @@ public class OutputFile_BDF {
* @returns {int} - The number of signals in the header.
*/
private int getNbSignals() {
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
return nbChan + nbAux + nbAnnotations;
} else {
return nbChan + nbAnnotations;
@@ -1229,43 +1279,43 @@ public class OutputFile_BDF {
writeString(padStringRight(str(getNbSignals()),BDF_HEADER_SIZE_NUMBER_SIGNALS), o);
writeStringArrayWithPaddingTimes(labelsEEG, BDF_HEADER_NS_SIZE_LABEL, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(labelsAux, BDF_HEADER_NS_SIZE_LABEL, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(labelsAux, BDF_HEADER_NS_SIZE_LABEL, o);
writeStringArrayWithPaddingTimes(labelsAnnotations, BDF_HEADER_NS_SIZE_LABEL, o);
writeStringArrayWithPaddingTimes(transducerEEG, BDF_HEADER_NS_SIZE_TRANSDUCER_TYPE, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(transducerAux, BDF_HEADER_NS_SIZE_TRANSDUCER_TYPE, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(transducerAux, BDF_HEADER_NS_SIZE_TRANSDUCER_TYPE, o);
writeStringArrayWithPaddingTimes(transducerAnnotations, BDF_HEADER_NS_SIZE_TRANSDUCER_TYPE, o);
writeStringArrayWithPaddingTimes(physicalDimensionEEG, BDF_HEADER_NS_SIZE_PHYSICAL_DIMENSION, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(physicalDimensionAux, BDF_HEADER_NS_SIZE_PHYSICAL_DIMENSION, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(physicalDimensionAux, BDF_HEADER_NS_SIZE_PHYSICAL_DIMENSION, o);
writeStringArrayWithPaddingTimes(physicalDimensionAnnotations, BDF_HEADER_NS_SIZE_PHYSICAL_DIMENSION, o);
writeStringArrayWithPaddingTimes(physicalMinimumEEG, BDF_HEADER_NS_SIZE_PHYSICAL_MINIMUM, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(physicalMinimumAux, BDF_HEADER_NS_SIZE_PHYSICAL_MINIMUM, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(physicalMinimumAux, BDF_HEADER_NS_SIZE_PHYSICAL_MINIMUM, o);
writeStringArrayWithPaddingTimes(physicalMinimumAnnotations, BDF_HEADER_NS_SIZE_PHYSICAL_MINIMUM, o);
writeStringArrayWithPaddingTimes(physicalMaximumEEG, BDF_HEADER_NS_SIZE_PHYSICAL_MAXIMUM, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(physicalMaximumAux, BDF_HEADER_NS_SIZE_PHYSICAL_MAXIMUM, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(physicalMaximumAux, BDF_HEADER_NS_SIZE_PHYSICAL_MAXIMUM, o);
writeStringArrayWithPaddingTimes(physicalMaximumAnnotations, BDF_HEADER_NS_SIZE_PHYSICAL_MAXIMUM, o);
writeStringArrayWithPaddingTimes(digitalMinimumEEG, BDF_HEADER_NS_SIZE_DIGITAL_MINIMUM, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(digitalMinimumAux, BDF_HEADER_NS_SIZE_DIGITAL_MINIMUM, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(digitalMinimumAux, BDF_HEADER_NS_SIZE_DIGITAL_MINIMUM, o);
writeStringArrayWithPaddingTimes(digitalMinimumAnnotations, BDF_HEADER_NS_SIZE_DIGITAL_MINIMUM, o);
writeStringArrayWithPaddingTimes(digitalMaximumEEG, BDF_HEADER_NS_SIZE_DIGITAL_MAXIMUM, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(digitalMaximumAux, BDF_HEADER_NS_SIZE_DIGITAL_MAXIMUM, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(digitalMaximumAux, BDF_HEADER_NS_SIZE_DIGITAL_MAXIMUM, o);
writeStringArrayWithPaddingTimes(digitalMaximumAnnotations, BDF_HEADER_NS_SIZE_DIGITAL_MAXIMUM, o);
writeStringArrayWithPaddingTimes(prefilteringEEG, BDF_HEADER_NS_SIZE_PREFILTERING, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(prefilteringAux, BDF_HEADER_NS_SIZE_PREFILTERING, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(prefilteringAux, BDF_HEADER_NS_SIZE_PREFILTERING, o);
writeStringArrayWithPaddingTimes(prefilteringAnnotations, BDF_HEADER_NS_SIZE_PREFILTERING, o);
writeStringArrayWithPaddingTimes(nbSamplesPerDataRecordEEG, BDF_HEADER_NS_SIZE_NR, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(nbSamplesPerDataRecordAux, BDF_HEADER_NS_SIZE_NR, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(nbSamplesPerDataRecordAux, BDF_HEADER_NS_SIZE_NR, o);
writeStringArrayWithPaddingTimes(nbSamplesPerDataRecordAnnotations, BDF_HEADER_NS_SIZE_NR, o);
writeStringArrayWithPaddingTimes(reservedEEG, BDF_HEADER_NS_SIZE_RESERVED, o);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) writeStringArrayWithPaddingTimes(reservedAux, BDF_HEADER_NS_SIZE_RESERVED, o);
if (eegDataSource == DATASOURCE_CYTON) writeStringArrayWithPaddingTimes(reservedAux, BDF_HEADER_NS_SIZE_RESERVED, o);
writeStringArrayWithPaddingTimes(reservedAnnotations, BDF_HEADER_NS_SIZE_RESERVED, o);
// println("writeHeader: done...");
@@ -1322,6 +1372,8 @@ public class OutputFile_BDF {
///////////////////////////////////////////////////////////////
class Table_CSV extends Table {
private int sampleRate;
public int getSampleRate() { return sampleRate; }
Table_CSV(String fname) throws IOException {
init();
readCSV(PApplet.createReader(createInput(fname)));
@@ -1342,7 +1394,24 @@ class Table_CSV extends Table {
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...");
if (line.length() > 18) {
if (line.charAt(1) == 'S') {
// println(line.substring(15, 18));
sampleRate = Integer.parseInt(line.substring(15, 18));
if (sampleRate == 100 || sampleRate == 160) {
sampleRate = Integer.parseInt(line.substring(15, 19));
}
println("Sample rate set to " + sampleRate);
// String[] m = match(line, "\\d+");
// if (m != null) {
// println("Found '" + m[1] + "' inside the line");
// }
}
}
println(line);
// if (line.charAt(1) == 'S') {
// println("sampel rarteakjdsf;ldj");
// }
continue;
}
@@ -1394,6 +1463,7 @@ class Table_CSV extends Table {
// 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)
// Updated: Joel Murphy - 6/26/17
//
//////////////////////////////////
@@ -1404,13 +1474,16 @@ PrintWriter dataWriter;
String convertedLine;
String thisLine;
String h;
float[] floatData = new float[20];
float[] intData = new float[20];
String logFileName;
String[] hexNums;
long thisTime;
long thatTime;
boolean printNextLine = false;
public void convertSDFile() {
println("");
// println("");
try {
dataLine = dataReader.readLine();
}
@@ -1425,54 +1498,215 @@ public void convertSDFile() {
controlPanel.convertingSD = false;
println("nothing left in file");
println("SD file conversion took "+thisTime+" mS");
outputSuccess("SD file converted to " + logFileName);
dataWriter.flush();
dataWriter.close();
} else {
// println(dataLine);
String[] hexNums = splitTokens(dataLine, ",");
}
else
{
hexNums = splitTokens(dataLine, ",");
if (hexNums[0].charAt(0) == '%') {
// println(dataLine);
dataWriter.println(dataLine);
// dataWriter.println(dataLine);
println(dataLine);
printNextLine = true;
} 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(",");
}
if (hexNums.length < 13){
convert8channelLine();
} else {
convert16channelLine();
}
if(printNextLine){
printNextLine = false;
}
//println();
dataWriter.println();
}
}
}
}
void convert16channelLine() {
if(printNextLine){
for(int i=0; i<hexNums.length; i++){
h = hexNums[i];
if (h.length()%2 == 0) { // make sure this is a real number
intData[i] = unhex(h);
} else {
intData[i] = 0;
}
dataWriter.print(intData[i]);
print(intData[i]);
if(hexNums.length > 1){
dataWriter.print(", ");
print(", ");
}
}
dataWriter.println();
println();
return;
}
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 > 16) { // 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
floatData[i] = unhex(h);
} else {
floatData[i] = 0;
}
if (i>=1 && i<=16) {
floatData[i] *= cyton.get_scale_fac_uVolts_per_count();
}else if(i != 0){
floatData[i] *= cyton.get_scale_fac_accel_G_per_count();
}
if(i == 0){
dataWriter.print(int(floatData[i])); // print the sample counter
}else{
dataWriter.print(floatData[i]); // print the current channel value
}
if (i < hexNums.length-1) { // print the current channel value
dataWriter.print(","); // print "," separator
}
}
dataWriter.println();
}
void convert8channelLine() {
if(printNextLine){
for(int i=0; i<hexNums.length; i++){
h = hexNums[i];
if (h.length()%2 == 0) { // make sure this is a real number
intData[i] = unhex(h);
} else {
intData[i] = 0;
}
print(intData[i]);
dataWriter.print(intData[i]);
if(hexNums.length > 1){
dataWriter.print(", ");
print(", ");
}
}
dataWriter.println();
println();
return;
}
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 + " " + h.length()); // use for debugging
if (h.length() > 8) {
break;
}
if (h.length()%2 == 0) { // make sure this is a real number
floatData[i] = unhex(h);
} else {
floatData[i] = 0;
}
if (i>=1 && i<=8) {
floatData[i] *= cyton.get_scale_fac_uVolts_per_count();
}else if(i != 0){
floatData[i] *= cyton.get_scale_fac_accel_G_per_count();
}
if(i == 0){
dataWriter.print(int(floatData[i])); // print the sample counter
}else{
dataWriter.print(floatData[i]); // print the current channel value
}
if (i < hexNums.length-1) {
dataWriter.print(","); // print "," separator
}
}
dataWriter.println();
}
// BEWARE: Old Stuff Below
//
// // println(dataLine);
// String[] hexNums = splitTokens(dataLine, ",");
//
// if (hexNums[0].charAt(0) == '%') {
// // println(dataLine);
// dataWriter.println(dataLine);
// println(dataLine);
// printNextLine = true;
// } 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();
// }
// }
// }
+259 -191
Ver Arquivo
@@ -11,6 +11,12 @@ HashMap<String,float[][]> processed_file;
HashMap<Integer,String> index_of_times;
HashMap<String,Integer> index_of_times_rev;
// indexs
final int DELTA = 0; // 1-4 Hz
final int THETA = 1; // 4-8 Hz
final int ALPHA = 2; // 8-13 Hz
final int BETA = 3; // 13-30 Hz
final int GAMMA = 4; // 30-55 Hz
//------------------------------------------------------------------------
@@ -26,11 +32,11 @@ void process_input_file() throws Exception {
try {
while (!hasRepeated) {
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, openBCI.get_scale_fac_uVolts_per_count(), openBCI.get_scale_fac_accel_G_per_count(), dataPacketBuff[lastReadDataPacketInd]);
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, cyton.get_scale_fac_uVolts_per_count(), cyton.get_scale_fac_accel_G_per_count(), dataPacketBuff[lastReadDataPacketInd]);
for (int Ichan=0; Ichan < nchan; Ichan++) {
//scale the data into engineering units..."microvolts"
localLittleBuff[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan]* openBCI.get_scale_fac_uVolts_per_count();
localLittleBuff[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan]* cyton.get_scale_fac_uVolts_per_count();
}
processed_file.put(curTimestamp, localLittleBuff);
index_of_times.put(indices,curTimestamp);
@@ -50,14 +56,14 @@ void process_input_file() throws Exception {
/*************************/
int getDataIfAvailable(int pointCounter) {
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
//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();
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan] * cyton.get_scale_fac_uVolts_per_count();
}
for (int auxChan=0; auxChan < 3; auxChan++) auxBuff[auxChan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].auxValues[auxChan];
pointCounter++; //increment counter for "little buffer"
@@ -81,7 +87,7 @@ int getDataIfAvailable(int pointCounter) {
int current_millis = millis();
if (current_millis >= nextPlayback_millis) {
//prepare for next time
int increment_millis = int(round(float(nPointsPerUpdate)*1000.f/get_fs_Hz_safe())/playback_speed_fac);
int increment_millis = int(round(float(nPointsPerUpdate)*1000.f/getSampleRateSafe())/playback_speed_fac);
if (nextPlayback_millis < 0) nextPlayback_millis = current_millis;
nextPlayback_millis += increment_millis;
@@ -92,10 +98,10 @@ int getDataIfAvailable(int pointCounter) {
dataPacketBuff[lastReadDataPacketInd].sampleIndex++;
switch (eegDataSource) {
case DATASOURCE_SYNTHETIC: //use synthetic data (for GUI debugging)
synthesizeData(nchan, get_fs_Hz_safe(), openBCI.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
synthesizeData(nchan, getSampleRateSafe(), cyton.get_scale_fac_uVolts_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
case DATASOURCE_PLAYBACKFILE:
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, openBCI.get_scale_fac_uVolts_per_count(), openBCI.get_scale_fac_accel_G_per_count(), dataPacketBuff[lastReadDataPacketInd]);
currentTableRowIndex=getPlaybackDataFromTable(playbackData_table, currentTableRowIndex, cyton.get_scale_fac_uVolts_per_count(), cyton.get_scale_fac_accel_G_per_count(), dataPacketBuff[lastReadDataPacketInd]);
break;
default:
//no action
@@ -103,7 +109,7 @@ int getDataIfAvailable(int pointCounter) {
//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();
yLittleBuff_uV[Ichan][pointCounter] = dataPacketBuff[lastReadDataPacketInd].values[Ichan]* cyton.get_scale_fac_uVolts_per_count();
}
pointCounter++;
@@ -166,9 +172,9 @@ void processNewData() {
//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++) {
// Calculate the impedance
float impedance = (sqrt(2.0)*dataProcessing.data_std_uV[Ichan]*1.0e-6) / openBCI.get_leadOffDrive_amps();
float impedance = (sqrt(2.0)*dataProcessing.data_std_uV[Ichan]*1.0e-6) / cyton.get_leadOffDrive_amps();
// Subtract the 2.2kOhm resistor
impedance -= openBCI.get_series_resistor();
impedance -= cyton.get_series_resistor();
// Verify the impedance is not less than 0
if (impedance < 0) {
// Incase impedance some how dipped below 2.2kOhm
@@ -248,7 +254,7 @@ void prepareData(float[] dataBuffX, float[][] dataBuffY_uV, float fs_Hz) {
}
void initializeFFTObjects(FFT[] fftBuff, float[][] dataBuffY_uV, int N, float fs_Hz) {
void initializeFFTObjects(FFT[] fftBuff, float[][] dataBuffY_uV, int Nfft, float fs_Hz) {
float[] fooData;
for (int Ichan=0; Ichan < nchan; Ichan++) {
@@ -335,12 +341,14 @@ int getPlaybackDataFromTable(Table datatable, int currentTableRowIndex, float sc
if(!isRunning){
try{
if(!isOldData) row.getString(nchan+4);
else row.getString(nchan+3);
row.getString(nchan+3);
nchan = 16;
// nchan = 16; AJK 5/31/17 see issue #151
}
catch (ArrayIndexOutOfBoundsException e){
println(e);
println("8 Channel");
}
catch (ArrayIndexOutOfBoundsException e){ println("8 Channel");}
}
}
@@ -374,13 +382,6 @@ class DataProcessing {
float headWidePower[];
int numBins;
// indexs
final int DELTA = 0; // 1-4 Hz
final int THETA = 1; // 4-8 Hz
final int ALPHA = 2; // 8-13 Hz
final int BETA = 3; // 13-30 Hz
final int GAMMA = 4; // 30-55 Hz
DataProcessing(int NCHAN, float sample_rate_Hz) {
nchan = NCHAN;
fs_Hz = sample_rate_Hz;
@@ -390,46 +391,87 @@ class DataProcessing {
avgPowerInBins = new float[nchan][processing_band_low_Hz.length];
headWidePower = new float[processing_band_low_Hz.length];
//check to make sure the sample rate is acceptable and then define the filters
if (abs(fs_Hz-250.0f) < 1.0) {
defineFilters(0);
} else if (abs(fs_Hz-200.0f) < 1.0) {
defineFilters(1);
} else {
println("EEG_Processing: *** ERROR *** Filters can currently only work at 250 Hz or 200 Hz");
defineFilters(0); //define the filters anyway just so that the code doesn't bomb
}
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 _mode) {
int mode = _mode; // 0 means classic OpenBCI board, 1 means ganglion
//define filters depending on the sampling rate
private void defineFilters() {
int n_filt;
double[] b, a, b2, a2;
String filt_txt, filt_txt2;
String short_txt, short_txt2;
switch(mode) {
// classic OpenBCI board, sampling rate 250 Hz
case 0:
//loop over all of the pre-defined filter types
n_filt = filtCoeff_notch.length;
for (int Ifilt=0; Ifilt < n_filt; Ifilt++) {
switch (Ifilt) {
//------------ loop over all of the pre-defined filter types -----------
//------------ notch filters ------------
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 };
//60 Hz notch filter, 2nd Order Butterworth: [b, a] = butter(2,[59.0 61.0]/(fs_Hz / 2.0), 'stop') %matlab command
switch(int(fs_Hz)) {
case 125:
b2 = new double[] { 0.931378858122982, 3.70081291785747, 5.53903191270520, 3.70081291785747, 0.931378858122982 };
a2 = new double[] { 1, 3.83246204081167, 5.53431749515949, 3.56916379490328, 0.867472133791669 };
break;
case 200:
b2 = new double[] { 0.956543225556877, 1.18293615779028, 2.27881429174348, 1.18293615779028, 0.956543225556877 };
a2 = new double[] { 1, 1.20922304075909, 2.27692490805580, 1.15664927482146, 0.914975834801436 };
break;
case 250:
b2 = new double[] { 0.965080986344733, -0.242468320175764, 1.94539149412878, -0.242468320175764, 0.965080986344733 };
a2 = new double[] { 1, -0.246778261129785, 1.94417178469135, -0.238158379221743, 0.931381682126902 };
break;
case 500:
b2 = new double[] { 0.982385438526095, -2.86473884662109, 4.05324051877773, -2.86473884662109, 0.982385438526095};
a2 = new double[] { 1, -2.89019558531207, 4.05293022193077, -2.83928210793009, 0.965081173899134 };
break;
case 1000:
b2 = new double[] { 0.991153595101611, -3.68627799048791, 5.40978944177152, -3.68627799048791, 0.991153595101611 };
a2 = new double[] { 1, -3.70265590760266, 5.40971118136100, -3.66990007337352, 0.982385450614122 };
break;
case 1600:
b2 = new double[] { 0.994461788958027, -3.86796874670208, 5.75004904085114, -3.86796874670208, 0.994461788958027 };
a2 = new double[] { 1, -3.87870938463296, 5.75001836883538, -3.85722810877252, 0.988954249933128 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b2 = new double[] { 1.0 };
a2 = new double[] { 1.0 };
}
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 };
//50 Hz notch filter, 2nd Order Butterworth: [b, a] = butter(2,[49.0 51.0]/(fs_Hz / 2.0), 'stop')
switch(int(fs_Hz)) {
case 125:
b2 = new double[] { 0.931378858122983, 3.01781693143160, 4.30731047590091, 3.01781693143160, 0.931378858122983 };
a2 = new double[] { 1, 3.12516981877757, 4.30259605835520, 2.91046404408562, 0.867472133791670 };
break;
case 200:
b2 = new double[] { 0.956543225556877, -2.34285519884863e-16, 1.91308645111375, -2.34285519884863e-16, 0.956543225556877 };
a2 = new double[] { 1, -1.41553435639707e-15, 1.91119706742607, -1.36696209906972e-15, 0.914975834801435 };
break;
case 250:
b2 = new double[] { 0.965080986344734, -1.19328255433335, 2.29902305135123, -1.19328255433335, 0.965080986344734 };
a2 = new double[] { 1, -1.21449347931898, 2.29780334191380, -1.17207162934771, 0.931381682126901 };
break;
case 500:
b2 = new double[] { 0.982385438526090, -3.17931708468811, 4.53709552901242, -3.17931708468811, 0.982385438526090 };
a2 = new double[] { 1, -3.20756923909868, 4.53678523216547, -3.15106493027754, 0.965081173899133 };
break;
case 1000:
b2 = new double[] { 0.991153595101607, -3.77064677042206, 5.56847615976560, -3.77064677042206, 0.991153595101607 };
a2 = new double[] { 1, -3.78739953308251, 5.56839789935513, -3.75389400776205, 0.982385450614127 };
break;
case 1600:
b2 = new double[] { 0.994461788958316, -3.90144402068168, 5.81543195046478, -3.90144402068168, 0.994461788958316 };
a2 = new double[] { 1, -3.91227761329151, 5.81540127844733, -3.89061042807090, 0.988954249933127 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b2 = new double[] { 1.0 };
a2 = new double[] { 1.0 };
}
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "Notch 50Hz", "50Hz");
break;
case 2:
@@ -439,168 +481,166 @@ class DataProcessing {
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "No Notch", "None");
break;
}
} // end loop over notch filters
}// end loop over notch filters
//------------ bandpass 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
};
//1-50 Hz band pass filter, 2nd Order Butterworth: [b, a] = butter(2,[1.0 50.0]/(fs_Hz / 2.0))
switch(int(fs_Hz)) {
case 125:
b = new double[] { 0.615877232553135, 0, -1.23175446510627, 0, 0.615877232553135 };
a = new double[] { 1, -0.789307541613509, -0.853263915766877, 0.263710995896442, 0.385190413112446 };
break;
case 200:
b = new double[] { 0.283751216219319, 0, -0.567502432438638, 0, 0.283751216219319 };
a = new double[] { 1, -1.97380379923172, 1.17181238127012, -0.368664525962831, 0.171812381270120 };
break;
case 250:
b = new double[] { 0.200138725658073, 0, -0.400277451316145, 0, 0.200138725658073 };
a = new double[] { 1, -2.35593463113158, 1.94125708865521, -0.784706375533419, 0.199907605296834 };
break;
case 500:
b = new double[] { 0.0652016551604422, 0, -0.130403310320884, 0, 0.0652016551604422 };
a = new double[] { 1, -3.14636562553919, 3.71754597063790, -1.99118301927812, 0.420045500522989 };
break;
case 1000:
b = new double[] { 0.0193615659240911, 0, -0.0387231318481823, 0, 0.0193615659240911 };
a = new double[] { 1, -3.56607203834158, 4.77991824545949, -2.86091191298975, 0.647068888346475 };
break;
case 1600:
b = new double[] { 0.00812885687466408, 0, -0.0162577137493282, 0, 0.00812885687466408 };
a = new double[] { 1, -3.72780746887970, 5.21756471024747, -3.25152171857009, 0.761764999239264 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b = new double[] { 1.0 };
a = new double[] { 1.0 };
}
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
};
//7-13 Hz band pass filter, 2nd Order Butterworth: [b, a] = butter(2,[7.0 13.0]/(fs_Hz / 2.0))
switch(int(fs_Hz)) {
case 125:
b = new double[] { 0.0186503962278349, 0, -0.0373007924556699, 0, 0.0186503962278349 };
a = new double[] { 1, -3.17162467236842, 4.11670870329067, -2.55619949640702, 0.652837763407545 };
break;
case 200:
b = new double[] { 0.00782020803349772, 0, -0.0156404160669954, 0, 0.00782020803349772 };
a = new double[] { 1, -3.56776916484310, 4.92946172209398, -3.12070317627516, 0.766006600943265 };
break;
case 250:
b = new double[] { 0.00512926836610803, 0, -0.0102585367322161, 0, 0.00512926836610803 };
a = new double[] { 1, -3.67889546976404, 5.17970041352212, -3.30580189001670, 0.807949591420914 };
break;
case 500:
b = new double[] { 0.00134871194834618, 0, -0.00269742389669237, 0, 0.00134871194834618 };
a = new double[] { 1, -3.86550956895320, 5.63152598761351, -3.66467991638185, 0.898858994155253 };
break;
case 1000:
b = new double[] { 0.000346041337684191, 0, -0.000692082675368382, 0, 0.000346041337684191 };
a = new double[] { 1, -3.93960949694447, 5.82749974685320, -3.83595939375067, 0.948081706106736 };
break;
case 1600:
b = new double[] { 0.000136510722194708, 0, -0.000273021444389417, 0, 0.000136510722194708 };
a = new double[] { 1, -3.96389829181139, 5.89507193593518, -3.89839913574117, 0.967227428151860 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b = new double[] { 1.0 };
a = new double[] { 1.0 };
}
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
};
//15-50 Hz band pass filter, 2nd Order Butterworth: [b, a] = butter(2,[15.0 50.0]/(fs_Hz / 2.0))
switch(int(fs_Hz)) {
case 125:
b = new double[] { 0.350346377855414, 0, -0.700692755710828, 0, 0.350346377855414 };
a = new double[] { 1, 0.175228265043619, -0.211846955102387, 0.0137230352398757, 0.180232073898346 };
break;
case 200:
b = new double[] { 0.167483800127017, 0, -0.334967600254034, 0, 0.167483800127017 };
a = new double[] { 1, -1.56695061045088, 1.22696619781982, -0.619519163981229, 0.226966197819818 };
break;
case 250:
b = new double[] { 0.117351036724609, 0, -0.234702073449219, 0, 0.117351036724609 };
a = new double[] { 1, -2.13743018017206, 2.03857800810852, -1.07014439920093, 0.294636527587914 };
break;
case 500:
b = new double[] { 0.0365748358439273, 0, -0.0731496716878546, 0, 0.0365748358439273 };
a = new double[] { 1, -3.18880661866679, 3.98037203788323, -2.31835989524663, 0.537194624801103 };
break;
case 1000:
b = new double[] { 0.0104324133710872, 0, -0.0208648267421744, 0, 0.0104324133710872 };
a = new double[] { 1, -3.63626742713985, 5.01393973667604, -3.10964559897057, 0.732726030371817 };
break;
case 1600:
b = new double[] { 0.00429884732196394, 0, -0.00859769464392787, 0, 0.00429884732196394 };
a = new double[] { 1, -3.78412985599134, 5.39377521548486, -3.43287342581222, 0.823349595537562 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b = new double[] { 1.0 };
a = new double[] { 1.0 };
}
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
};
//5-50 Hz band pass filter, 2nd Order Butterworth: [b, a] = butter(2,[5.0 50.0]/(fs_Hz / 2.0))
switch(int(fs_Hz)) {
case 125:
b = new double[] { 0.529967227069348, 0, -1.05993445413870, 0, 0.529967227069348 };
a = new double[] { 1, -0.517003774490767, -0.734318454224823, 0.103843398397761, 0.294636527587914 };
break;
case 200:
b = new double[] { 0.248341078962541, 0, -0.496682157925081, 0, 0.248341078962541 };
a = new double[] { 1, -1.86549482213123, 1.17757811892770, -0.460665534278457, 0.177578118927698 };
break;
case 250:
b = new double[] { 0.175087643672101, 0, -0.350175287344202, 0, 0.175087643672101 };
a = new double[] { 1, -2.29905535603850, 1.96749775998445, -0.874805556449481, 0.219653983913695 };
break;
case 500:
b = new double[] { 0.0564484622607352, 0, -0.112896924521470, 0, 0.0564484622607352 };
a = new double[] { 1, -3.15946330211917, 3.79268442285094, -2.08257331718360, 0.450445430056042 };
break;
case 1000:
b = new double[] { 0.0165819316692804, 0, -0.0331638633385608, 0, 0.0165819316692804 };
a = new double[] { 1, -3.58623980811691, 4.84628980428803, -2.93042721682014, 0.670457905953175 };
break;
case 1600:
b = new double[] { 0.00692579317243661, 0, -0.0138515863448732, 0, 0.00692579317243661 };
a = new double[] { 1, -3.74392328264678, 5.26758817627966, -3.30252568902969, 0.778873972655117 };
break;
default:
println("EEG_Processing: *** ERROR *** Filters can only work at 125Hz, 200Hz, 250 Hz, 1000Hz or 1600Hz");
b = new double[] { 1.0 };
a = new double[] { 1.0 };
}
filt_txt = "Bandpass 5-50Hz";
short_txt = "5-50 Hz";
break;
default:
//no filtering
b = new double[] {
1.0
};
a = new double[] {
1.0
};
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
break;
// Ganglion board, sampling rate 200 Hz
case 1:
//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 = 200 Hz. 2nd Order Butterworth: b, a = signal.butter(2,[59.0 61.0]/(fs_Hz / 2.0), 'bandstop')
b2 = new double[] { 0.956543225556876, 1.18293615779028, 2.27881429174347, 1.18293615779028, 0.956543225556876 };
a2 = new double[] { 1, 1.20922304075909, 2.27692490805579, 1.15664927482146, 0.914975834801432 };
filtCoeff_notch[Ifilt] = new FilterConstants(b2, a2, "Notch 60Hz", "60Hz");
break;
case 1:
//50 Hz notch filter, assumed fs = 200 Hz. 2nd Order Butterworth: b, a = signal.butter(2,[49.0 51.0]/(fs_Hz / 2.0), 'bandstop')
b2 = new double[] { 0.956543225556877, -2.34285519884863e-16, 1.91308645111375, -2.34285519884863e-16, 0.956543225556877};
a2 = new double[] { 1, -1.02695629777827e-15, 1.91119706742607, -1.01654795692241e-15, 0.914975834801435};
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]/(200/2)); %bandpass filter
b = new double[] {
0.283751216219318, 0, -0.567502432438636, 0, 0.283751216219318
};
a = new double[] {
1, -1.97380379923172, 1.17181238127012, -0.368664525962831, 0.171812381270120
};
filt_txt = "Bandpass 1-50Hz";
short_txt = "1-50 Hz";
break;
case 1:
//butter(2,[7 13]/(200/2));
b = new double[] {
0.00782020803349883, 0, -0.0156404160669977, 0, 0.00782020803349883
};
a = new double[] {
1, -3.56776916484310, 4.92946172209398, -3.12070317627516, 0.766006600943266
};
filt_txt = "Bandpass 7-13Hz";
short_txt = "7-13 Hz";
break;
case 2:
//[b,a]=butter(2,[15 50]/(200/2)); %matlab command
b = new double[] {
0.167483800127017, 0, -0.334967600254034, 0, 0.167483800127017
};
a = new double[] {
1, -1.56695061045088, 1.22696619781982, -0.619519163981230, 0.226966197819818
};
filt_txt = "Bandpass 15-50Hz";
short_txt = "15-50 Hz";
break;
case 3:
//[b,a]=butter(2,[5 50]/(200/2)); %matlab command
b = new double[] {
0.248341078962540, 0, -0.496682157925080, 0, 0.248341078962540
};
a = new double[] {
1, -1.86549482213123, 1.17757811892770, -0.460665534278457, 0.177578118927698
};
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
break;
}
} //end defineFilters method
} //end loop over band pass filters
}
//end defineFilters method
public String getFilterDescription() {
return filtCoeff_bp[currentFilt_ind].name + ", " + filtCoeff_notch[currentNotch_ind].name;
@@ -628,7 +668,7 @@ class DataProcessing {
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
int Nfft = getNfftSafe();
//loop over each EEG channel
for (int Ichan=0; Ichan < nchan; Ichan++) {
@@ -674,8 +714,15 @@ class DataProcessing {
//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()));
// FFT ref: https://www.mathworks.com/help/matlab/ref/fft.html
// first calculate double-sided FFT amplitude spectrum
for (int I=0; I <= Nfft/2; I++) {
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) / Nfft));
}
// then convert into single-sided FFT spectrum: DC & Nyquist (i=0 & i=N/2) remain the same, others multiply by two.
for (int I=1; I < Nfft/2; I++) {
fftBuff[Ichan].setBand(I, (float)(fftBuff[Ichan].getBand(I) * 2));
}
//average the FFT with previous FFT data so that it makes it smoother in time
@@ -698,13 +745,34 @@ class DataProcessing {
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
// fftBuff[Ichan].setBand(I, 1.0f); // test
} //end loop over FFT bins
// calculate single-sided psd by single-sided FFT amplitude spectrum
// PSD ref: https://www.mathworks.com/help/dsp/ug/estimate-the-power-spectral-density-in-matlab.html
// when i = 1 ~ (N/2-1), psd = (N / fs) * mag(i)^2 / 4
// when i = 0 or i = N/2, psd = (N / fs) * mag(i)^2
for (int i = 0; i < processing_band_low_Hz.length; i++) {
float sum = 0;
for (int j = processing_band_low_Hz[i]; j < processing_band_high_Hz[i]; j++) {
sum += fftBuff[Ichan].getBand(j);
// int binNum = 0;
for (int Ibin = 0; Ibin <= Nfft/2; Ibin ++) { // loop over FFT bins
float FFT_freq_Hz = fftBuff[Ichan].indexToFreq(Ibin); // center frequency of this bin
float psdx = 0;
// if the frequency matches a band
if (FFT_freq_Hz >= processing_band_low_Hz[i] && FFT_freq_Hz < processing_band_high_Hz[i]) {
if (Ibin != 0 && Ibin != Nfft/2) {
psdx = fftBuff[Ichan].getBand(Ibin) * fftBuff[Ichan].getBand(Ibin) * Nfft/getSampleRateSafe() / 4;
}
else {
psdx = fftBuff[Ichan].getBand(Ibin) * fftBuff[Ichan].getBand(Ibin) * Nfft/getSampleRateSafe();
}
sum += psdx;
// binNum ++;
}
}
avgPowerInBins[Ichan][i] = sum;
avgPowerInBins[Ichan][i] = sum; // total power in a band
// println(i, binNum, sum);
}
} //end the loop over channels.
for (int i = 0; i < processing_band_low_Hz.length; i++) {
@@ -713,7 +781,7 @@ class DataProcessing {
for (int j = 0; j < nchan; j++) {
sum += avgPowerInBins[j][i];
}
headWidePower[i] = sum/nchan;
headWidePower[i] = sum/nchan; // averaging power over all channels
}
//delta in channel 2 ... avgPowerInBins[1][DELTA];
+85 -9
Ver Arquivo
@@ -25,6 +25,12 @@ boolean printSignPosts = true;
float millisOfLastSignPost = 0.0;
float millisSinceLastSignPost = 0.0;
final static int OUTPUT_LEVEL_DEFAULT = 0;
final static int OUTPUT_LEVEL_INFO = 1;
final static int OUTPUT_LEVEL_SUCCESS = 2;
final static int OUTPUT_LEVEL_WARN = 3;
final static int OUTPUT_LEVEL_ERROR = 4;
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
@@ -49,9 +55,13 @@ 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
String currentOutput = "Learn how to use this application and more at docs.openbci.com/OpenBCI%20Software/01-OpenBCI_GUI"; //current text shown in help widget, based on most recent command
int padding = 5;
int outputStart = 0;
int outputDurationMs = 3000;
boolean animatingMessage = false;
int curOutputLevel = OUTPUT_LEVEL_DEFAULT;
HelpWidget(float _xPos, float _yPos, float _width, float _height) {
x = _xPos;
@@ -61,7 +71,12 @@ class HelpWidget {
}
public void update() {
//nothing needed here
if (animatingMessage) {
if (millis() > outputStart + outputDurationMs) {
animatingMessage = false;
curOutputLevel = OUTPUT_LEVEL_DEFAULT;
}
}
}
public void draw() {
@@ -95,32 +110,93 @@ class HelpWidget {
//draw bg of text field of widget
strokeWeight(1);
stroke(color(0, 5, 11));
fill(200);
fill(255);
stroke(getBackgroundColor());
// fill(200);
// fill(255);
fill(getBackgroundColor());
// fill(57,128,204);
rect(x + padding, height-h + padding, width - padding*2, h - padding *2);
textFont(p4);
textSize(14);
fill(bgColor);
// fill(bgColor);
fill(getTextColor());
// fill(57,128,204);
// fill(openbciBlue);
textAlign(LEFT, TOP);
text(currentOutput, padding*2, height - h + padding);
}
popStyle();
}
public void output(String _output) {
private color getTextColor() {
switch (curOutputLevel) {
case OUTPUT_LEVEL_INFO:
return #00529B;
case OUTPUT_LEVEL_SUCCESS:
return #4F8A10;
case OUTPUT_LEVEL_WARN:
return #9F6000;
case OUTPUT_LEVEL_ERROR:
return #D8000C;
case OUTPUT_LEVEL_DEFAULT:
default:
return color(0, 5, 11);
}
}
private color getBackgroundColor() {
switch (curOutputLevel) {
case OUTPUT_LEVEL_INFO:
return #BDE5F8;
case OUTPUT_LEVEL_SUCCESS:
return #DFF2BF;
case OUTPUT_LEVEL_WARN:
return #FEEFB3;
case OUTPUT_LEVEL_ERROR:
return #FFD2D2;
case OUTPUT_LEVEL_DEFAULT:
default:
return color(255);
}
}
public void output(String _output, int level) {
if (OUTPUT_LEVEL_DEFAULT == level) {
animatingMessage = false;
} else {
animatingMessage = true;
outputStart = millis();
}
curOutputLevel = level;
currentOutput = _output;
// prevOutputs.add(_output);
}
};
public void output(String _output) {
helpWidget.output(_output);
output(_output, OUTPUT_LEVEL_DEFAULT);
}
public void output(String _output, int level) {
helpWidget.output(_output, level);
}
public void outputError(String _output) {
output(_output, OUTPUT_LEVEL_ERROR);
}
public void outputInfo(String _output) {
output(_output, OUTPUT_LEVEL_INFO);
}
public void outputSuccess(String _output) {
output(_output, OUTPUT_LEVEL_SUCCESS);
}
public void outputWarn(String _output) {
output(_output, OUTPUT_LEVEL_WARN);
}
// created 2/10/16 by Conor Russomanno to dissect the aspects of the GUI that are slowing it down
-721
Ver Arquivo
@@ -1,721 +0,0 @@
///////////////////////////////////////////////////////////////////////////////
//
// This class configures and manages the connection to the OpenBCI Ganglion.
// The connection is implemented via a TCP connection to a TCP port.
// The Gagnlion is configured using single letter text commands sent from the
// PC to the TCP server. The EEG data streams back from the Ganglion, to the
// TCP server and back to the PC continuously (once started).
//
// Created: AJ Keller, August 2016
//
/////////////////////////////////////////////////////////////////////////////
// import java.io.OutputStream; //for logging raw bytes to an output file
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
boolean werePacketsDroppedGang = false;
int numPacketsDroppedGang = 0;
void clientEvent(Client someClient) {
// print("Server Says: ");
int p = ganglion.tcpBufferPositon;
ganglion.tcpBuffer[p] = ganglion.tcpClient.readChar();
ganglion.tcpBufferPositon++;
if(p > 2) {
String posMatch = new String(ganglion.tcpBuffer, p - 2, 3);
if (posMatch.equals(ganglion.TCP_STOP)) {
if (!ganglion.nodeProcessHandshakeComplete) {
ganglion.nodeProcessHandshakeComplete = true;
ganglion.setHubIsRunning(true);
println("GanglionSync: clientEvent: handshake complete");
}
// Get a string from the tcp buffer
String msg = new String(ganglion.tcpBuffer, 0, p);
// Send the new string message to be processed
ganglion.parseMessage(msg);
// Check to see if the ganglion ble list needs to be updated
if (ganglion.deviceListUpdated) {
ganglion.deviceListUpdated = false;
controlPanel.bleBox.refreshBLEList();
}
// Reset the buffer position
ganglion.tcpBufferPositon = 0;
}
} //<>//
}
class OpenBCI_Ganglion {
final static String TCP_CMD_ACCEL = "a";
final static String TCP_CMD_CONNECT = "c";
final static String TCP_CMD_COMMAND = "k";
final static String TCP_CMD_DISCONNECT = "d";
final static String TCP_CMD_DATA= "t";
final static String TCP_CMD_ERROR = "e"; //<>//
final static String TCP_CMD_IMPEDANCE = "i";
final static String TCP_CMD_LOG = "l";
final static String TCP_CMD_SCAN = "s";
final static String TCP_CMD_STATUS = "q";
final static String TCP_STOP = ",;\n";
final static String TCP_ACTION_START = "start";
final static String TCP_ACTION_STATUS = "status";
final static String TCP_ACTION_STOP = "stop";
final static String GANGLION_BOOTLOADER_MODE = ">";
final static byte BYTE_START = (byte)0xA0;
final static byte BYTE_END = (byte)0xC0;
// States For Syncing with the hardware
final static int STATE_NOCOM = 0;
final static int STATE_COMINIT = 1;
final static int STATE_SYNCWITHHARDWARE = 2;
final static int STATE_NORMAL = 3;
final static int STATE_STOPPED = 4;
final static int COM_INIT_MSEC = 3000; //you may need to vary this for your computer or your Arduino
final static int NUM_ACCEL_DIMS = 3;
final static int RESP_ERROR_UNKNOWN = 499;
final static int RESP_ERROR_BAD_PACKET = 500;
final static int RESP_ERROR_BAD_NOBLE_START = 501;
final static int RESP_ERROR_ALREADY_CONNECTED = 408;
final static int RESP_ERROR_COMMAND_NOT_RECOGNIZED = 406;
final static int RESP_ERROR_DEVICE_NOT_FOUND = 405;
final static int RESP_ERROR_NO_OPEN_BLE_DEVICE = 400;
final static int RESP_ERROR_UNABLE_TO_CONNECT = 402;
final static int RESP_ERROR_UNABLE_TO_DISCONNECT = 401;
final static int RESP_ERROR_SCAN_ALREADY_SCANNING = 409;
final static int RESP_ERROR_SCAN_NONE_FOUND = 407;
final static int RESP_ERROR_SCAN_NO_SCAN_TO_STOP = 410;
final static int RESP_ERROR_SCAN_COULD_NOT_START = 412;
final static int RESP_ERROR_SCAN_COULD_NOT_STOP = 411;
final static int RESP_GANGLION_FOUND = 201;
final static int RESP_SUCCESS = 200;
final static int RESP_SUCCESS_DATA_ACCEL = 202;
final static int RESP_SUCCESS_DATA_IMPEDANCE = 203;
final static int RESP_SUCCESS_DATA_SAMPLE = 204;
final static int RESP_STATUS_CONNECTED = 300;
final static int RESP_STATUS_DISCONNECTED = 301;
final static int RESP_STATUS_SCANNING = 302;
final static int RESP_STATUS_NOT_SCANNING = 303;
private int state = STATE_NOCOM;
int prevState_millis = 0; // Used for calculating connect time out
private int nEEGValuesPerPacket = NCHAN_GANGLION; // Defined by the data format sent by openBCI boards
private int nAuxValuesPerPacket = NUM_ACCEL_DIMS; // Defined by the arduino code
private int tcpGanglionPort = 10996;
private String tcpGanglionIP = "127.0.0.1";
private String tcpGanglionFull = tcpGanglionIP + ":" + tcpGanglionPort;
private boolean tcpClientActive = false;
private int tcpTimeout = 1000;
private final float fs_Hz = 200.0f; //sample rate used by OpenBCI Ganglion board... set by its Arduino code
private final float MCP3912_Vref = 1.2f; // reference voltage for ADC in MCP3912 set in hardware
private float MCP3912_gain = 1.0; //assumed gain setting for MCP3912. NEEDS TO BE ADJUSTABLE JM
private float scale_fac_uVolts_per_count = (MCP3912_Vref * 1000000.f) / (8388607.0 * MCP3912_gain * 1.5 * 51.0); //MCP3912 datasheet page 34. Gain of InAmp = 80
// private float scale_fac_accel_G_per_count = 0.032;
private float scale_fac_accel_G_per_count = 0.016;
// private final float scale_fac_accel_G_per_count = 0.002 / ((float)pow(2,4)); //assume set to +/4G, so 2 mG per digit (datasheet). Account for 4 bits unused
// private final float leadOffDrive_amps = 6.0e-9; //6 nA, set by its Arduino code
private int bleErrorCounter = 0;
private int prevSampleIndex = 0;
private DataPacket_ADS1299 dataPacket;
public Client tcpClient;
private boolean portIsOpen = false;
private boolean connected = false;
public int numberOfDevices = 0;
public int maxNumberOfDevices = 10;
public String[] deviceList = new String[0];
public boolean deviceListUpdated = false;
private boolean hubRunning = false;
public char[] tcpBuffer = new char[1024];
public int tcpBufferPositon = 0;
private boolean waitingForResponse = false;
private boolean nodeProcessHandshakeComplete = false;
public boolean shouldStartNodeApp = false;
private boolean checkingImpedance = false;
private boolean accelModeActive = false;
private boolean newAccelData = false;
private int[] accelArray = new int[NUM_ACCEL_DIMS];
public boolean impedanceUpdated = false;
public int[] impedanceArray = new int[NCHAN_GANGLION + 1];
// Getters
public float get_fs_Hz() { return fs_Hz; }
public boolean isPortOpen() { return portIsOpen; }
public float get_scale_fac_uVolts_per_count() { return scale_fac_uVolts_per_count; }
public float get_scale_fac_accel_G_per_count() { return scale_fac_accel_G_per_count; }
public boolean isHubRunning() { return hubRunning; }
public boolean isCheckingImpedance() { return checkingImpedance; }
public boolean isAccelModeActive() { return accelModeActive; }
private PApplet mainApplet;
//constructors
OpenBCI_Ganglion() {}; //only use this if you simply want access to some of the constants
OpenBCI_Ganglion(PApplet applet) {
mainApplet = applet;
// Able to start tcpClient connection?
startTCPClient(mainApplet);
// if (getStatus()) {
// println("Able to start tcpClient connection -- YES");
// hubRunning = true;
// } else {
// println("Able to start tcpClient connection -- NO");
// }
// if (getStatus()) {
// println("Able to send status message, now waiting for response.");
// } else {
// // We should try to start the node process because we were not able to
// // establish a connection with the node process.
// println("Failure: Not able to send status message. Trying to start tcpConnection.");
// startTCPClient(applet);
// if (getStatus()) {
// println("Connection established with node server.");
// } else {
// println("Connection failed to establish with node server. Recommend trying to launch application from data dir.");
// shouldStartNodeApp = true;
// }
// }
// For storing data into
dataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
for(int i = 0; i < nEEGValuesPerPacket; i++) {
dataPacket.values[i] = 0;
}
for(int i = 0; i < nAuxValuesPerPacket; i++){
dataPacket.auxValues[i] = 0;
}
}
/**
* @descirpiton Used to `try` and start the tcpClient
* @param applet {PApplet} - The main applet.
* @return {boolean} - True if able to start.
*/
public boolean startTCPClient(PApplet applet) {
try {
tcpClient = new Client(applet, tcpGanglionIP, tcpGanglionPort);
return true;
} catch (Exception e) {
println("startTCPClient: ConnectException: " + e);
return false;
}
}
/**
* Sends a status message to the node process.
*/
public boolean getStatus() {
try {
safeTCPWrite(TCP_CMD_STATUS + TCP_STOP);
waitingForResponse = true;
return true;
} catch (NullPointerException E) {
// The tcp client is not initalized, try now
return false;
}
}
public void setHubIsRunning(boolean isRunning) {
hubRunning = isRunning;
}
// Return true if the display needs to be updated for the BLE list
public void parseMessage(String msg) {
// println(msg);
String[] list = split(msg, ',');
switch (list[0].charAt(0)) {
case 'c': // Connect
processConnect(msg);
break;
case 'a': // Accel
processAccel(msg);
break;
case 'd': // Disconnect
processDisconnect(msg);
break;
case 'i': // Impedance
processImpedance(msg);
break;
case 't': // Data
processData(msg);
break;
case 'e': // Error
println("OpenBCI_Ganglion: parseMessage: error: " + list[2]);
break;
case 's': // Scan
processScan(msg);
break;
case 'l':
println("OpenBCI_Ganglion: Log: " + list[1]);
break;
case 'q':
processStatus(msg);
break;
default:
println("OpenBCI_Ganglion: parseMessage: default: " + msg);
break;
}
}
private void processAccel(String msg) {
String[] list = split(msg, ',');
if (Integer.parseInt(list[1]) == RESP_SUCCESS_DATA_ACCEL) {
for (int i = 0; i < NUM_ACCEL_DIMS; i++) {
accelArray[i] = Integer.parseInt(list[i + 2]);
}
newAccelData = true;
}
}
private void processConnect(String msg) {
String[] list = split(msg, ',');
if (isSuccessCode(Integer.parseInt(list[1]))) {
println("OpenBCI_Ganglion: parseMessage: connect: success!");
output("OpenBCI_Ganglion: The GUI is done intializing. Click outside of the control panel to interact with the GUI.");
systemMode = 10;
connected = true;
controlPanel.close();
} else {
println("OpenBCI_Ganglion: parseMessage: connect: failure!");
haltSystem();
initSystemButton.setString("START SYSTEM");
controlPanel.open();
abandonInit = true;
output("Unable to connect to Ganglion! Please ensure board is powered on and in range!");
connected = false;
}
}
private void processData(String msg) {
String[] list = split(msg, ',');
int code = Integer.parseInt(list[1]);
if (eegDataSource == DATASOURCE_GANGLION && systemMode == 10 && isRunning) { //<>//
if (Integer.parseInt(list[1]) == RESP_SUCCESS_DATA_SAMPLE) { //<>//
// Sample number stuff
dataPacket.sampleIndex = int(Integer.parseInt(list[2]));
if ((dataPacket.sampleIndex - prevSampleIndex) != 1) {
if(dataPacket.sampleIndex != 0){ // if we rolled over, don't count as error
bleErrorCounter++;
werePacketsDroppedGang = true; //set this true to activate packet duplication in serialEvent
if(dataPacket.sampleIndex < prevSampleIndex){ //handle the situation in which the index jumps from 250s past 255, and back to 0
numPacketsDroppedGang = (dataPacket.sampleIndex+200) - prevSampleIndex; //calculate how many times the last received packet should be duplicated...
} else {
numPacketsDroppedGang = dataPacket.sampleIndex - prevSampleIndex; //calculate how many times the last received packet should be duplicated...
}
println("OpenBCI_Ganglion: apparent sampleIndex jump from Serial data: " + prevSampleIndex + " to " + dataPacket.sampleIndex + ". Keeping packet. (" + bleErrorCounter + ")");
println("numPacketsDropped = " + numPacketsDropped);
}
}
prevSampleIndex = dataPacket.sampleIndex;
// Channel data storage
for (int i = 0; i < NCHAN_GANGLION; i++) {
dataPacket.values[i] = Integer.parseInt(list[3 + i]);
}
if (newAccelData) {
newAccelData = false;
for (int i = 0; i < NUM_ACCEL_DIMS; i++) {
dataPacket.auxValues[i] = accelArray[i];
dataPacket.rawAuxValues[i][0] = byte(accelArray[i]);
}
}
getRawValues(dataPacket);
// println(binary(dataPacket.values[0], 24) + '\n' + binary(dataPacket.rawValues[0][0], 8) + binary(dataPacket.rawValues[0][1], 8) + binary(dataPacket.rawValues[0][2], 8) + '\n'); //<>//
curDataPacketInd = (curDataPacketInd+1) % dataPacketBuff.length; // This is also used to let the rest of the code that it may be time to do something
ganglion.copyDataPacketTo(dataPacketBuff[curDataPacketInd]); // Resets isNewDataPacketAvailable to false
// KILL SPIKES!!!
if(werePacketsDroppedGang){
// println("Packets Dropped ... doing some stuff...");
for(int i = numPacketsDroppedGang; i > 0; i--){
int tempDataPacketInd = curDataPacketInd - i; //
if(tempDataPacketInd >= 0 && tempDataPacketInd < dataPacketBuff.length){
// println("i = " + i);
ganglion.copyDataPacketTo(dataPacketBuff[tempDataPacketInd]);
} else {
ganglion.copyDataPacketTo(dataPacketBuff[tempDataPacketInd+200]);
}
//put the last stored packet in # of packets dropped after that packet
}
//reset werePacketsDropped & numPacketsDropped
werePacketsDroppedGang = false;
numPacketsDroppedGang = 0;
}
switch (outputDataSource) {
case OUTPUT_SOURCE_ODF:
fileoutput_odf.writeRawData_dataPacket(dataPacketBuff[curDataPacketInd], ganglion.get_scale_fac_uVolts_per_count(), get_scale_fac_accel_G_per_count());
break;
case OUTPUT_SOURCE_BDF:
// curBDFDataPacketInd = curDataPacketInd;
// thread("writeRawData_dataPacket_bdf");
fileoutput_bdf.writeRawData_dataPacket(dataPacketBuff[curDataPacketInd]);
break;
case OUTPUT_SOURCE_NONE:
default:
// Do nothing...
break;
}
newPacketCounter++;
} else {
bleErrorCounter++;
println("OpenBCI_Ganglion: parseMessage: data: bad");
}
}
}
private void handleError(int code, String msg) {
output("Code " + code + "Error: " + msg);
println("Code " + code + "Error: " + msg);
}
private void processDisconnect(String msg) {
if (!waitingForResponse) {
haltSystem();
initSystemButton.setString("START SYSTEM");
controlPanel.open();
output("Dang! Lost connection to Ganglion. Please move closer or get a new battery!");
} else {
waitingForResponse = false;
}
}
private void processImpedance(String msg) {
String[] list = split(msg, ',');
if (Integer.parseInt(list[1]) == RESP_SUCCESS_DATA_IMPEDANCE) {
int channel = Integer.parseInt(list[2]);
if (channel < 5) { //<>//
int value = Integer.parseInt(list[3]);
impedanceArray[channel] = value;
if (channel == 0) {
impedanceUpdated = true;
println("Impedance for channel reference is " + value + " ohms.");
} else {
println("? for channel " + channel + " is " + value + " ohms.");
}
}
}
}
private void processStatus(String msg) {
String[] list = split(msg, ',');
int code = Integer.parseInt(list[1]);
if (waitingForResponse) {
waitingForResponse = false;
println("Node process up!");
}
if (code == RESP_ERROR_BAD_NOBLE_START) {
println("OpenBCI_Ganglion: processStatus: Problem in the Hub");
output("Problem starting Ganglion Hub. Please make sure compatible USB is configured, then restart this GUI.");
} else {
println("OpenBCI_Ganglion: processStatus: Started Successfully");
}
}
private void processScan(String msg) {
String[] list = split(msg, ',');
int code = Integer.parseInt(list[1]);
switch(code) {
case RESP_GANGLION_FOUND:
// Sent every time a new ganglion device is found
if (searchDeviceAdd(list[2])) {
deviceListUpdated = true;
}
break;
case RESP_ERROR_SCAN_ALREADY_SCANNING:
// Sent when a start send command is sent and the module is already
// scanning.
handleError(code, list[2]);
break;
case RESP_SUCCESS:
// Sent when either a scan was stopped or started Successfully
String action = list[2];
switch (action) {
case TCP_ACTION_START:
break;
case TCP_ACTION_STOP:
break;
}
break;
case RESP_ERROR_SCAN_COULD_NOT_START:
// Sent when err on search start
handleError(code, list[2]);
break;
case RESP_ERROR_SCAN_COULD_NOT_STOP:
// Send when err on search stop
handleError(code, list[2]);
break;
case RESP_STATUS_SCANNING:
// Sent when after status action sent to node and module is searching
break;
case RESP_STATUS_NOT_SCANNING:
// Sent when after status action sent to node and module is NOT searching
break;
case RESP_ERROR_SCAN_NO_SCAN_TO_STOP:
// Sent when a 'stop' action is sent to node and there is no scan to stop.
handleError(code, list[2]);
break;
case RESP_ERROR_UNKNOWN:
default:
handleError(code, list[2]);
break;
}
}
void writeRawData_dataPacket_bdf() {
fileoutput_bdf.writeRawData_dataPacket(dataPacketBuff[curBDFDataPacketInd]);
}
public int copyDataPacketTo(DataPacket_ADS1299 target) {
return dataPacket.copyTo(target);
}
private void getRawValues(DataPacket_ADS1299 packet) {
for (int i=0; i < nchan; i++) {
int val = packet.values[i];
//println(binary(val, 24));
byte rawValue[] = new byte[3];
// Breakdown values into
rawValue[2] = byte(val & 0xFF);
//println("rawValue[2] " + binary(rawValue[2], 8));
rawValue[1] = byte((val & (0xFF << 8)) >> 8);
//println("rawValue[1] " + binary(rawValue[1], 8));
rawValue[0] = byte((val & (0xFF << 16)) >> 16);
//println("rawValue[0] " + binary(rawValue[0], 8));
// Store to the target raw values
packet.rawValues[i] = rawValue;
//println();
}
}
// TODO: Figure out how to ping the server at localhost listening on port 10996
// /**
// * Used to ping the local hub tcp server and check it's status.
// */
// public boolean pingHub() {
// boolean pingStat;
//
// try {
// println("GanglionSync: pingHub: trying... ");
// pingStat = InetAddress.getByName("127.0.0.1:10996").isReachable(tcpTimeout);
// print("GanglionSync: pingHub: ");
// println(pingStat);
// return pingStat;
// }
// catch(Exception E){
// E.printStackTrace();
// return false;
// }
// }
public boolean isSuccessCode(int c) {
return c == RESP_SUCCESS;
}
// SCANNING/SEARHING FOR DEVICES
public void searchDeviceStart() {
deviceList = null;
numberOfDevices = 0;
safeTCPWrite(TCP_CMD_SCAN + ',' + TCP_ACTION_START + TCP_STOP);
}
public void searchDeviceStop() {
safeTCPWrite(TCP_CMD_SCAN + ',' + TCP_ACTION_STOP + TCP_STOP);
}
public boolean searchDeviceAdd(String ganglionLocalName) {
if (numberOfDevices == 0) {
numberOfDevices++;
deviceList = new String[numberOfDevices];
deviceList[0] = ganglionLocalName;
return true;
} else {
boolean willAddToDeviceList = true;
for (int i = 0; i < numberOfDevices; i++) {
if (ganglionLocalName.equals(deviceList[i])) {
willAddToDeviceList = false;
break;
}
}
if (willAddToDeviceList) {
numberOfDevices++;
String[] tempList = new String[numberOfDevices];
arrayCopy(deviceList, tempList);
tempList[numberOfDevices - 1] = ganglionLocalName;
deviceList = tempList;
return true;
}
}
return false;
}
// CONNECTION
public void connectBLE(String id) {
safeTCPWrite(TCP_CMD_CONNECT + "," + id + TCP_STOP);
}
public void disconnectBLE() {
waitingForResponse = true;
safeTCPWrite(TCP_CMD_DISCONNECT + TCP_STOP);
}
public void updateSyncState() {
//has it been 3000 milliseconds since we initiated the serial port? We want to make sure we wait for the OpenBCI board to finish its setup()
if ((millis() - prevState_millis > COM_INIT_MSEC) && (prevState_millis != 0) && (state == openBCI.STATE_COMINIT) ) {
// We are synced and ready to go!
state = STATE_SYNCWITHHARDWARE;
println("OpenBCI_Ganglion: Sending reset command");
// serial_openBCI.write('v');
}
}
/**
* @description Sends a start streaming command to the Ganglion Node module.
*/
void startDataTransfer(){
changeState(STATE_NORMAL); // make sure it's now interpretting as binary
println("OpenBCI_Ganglion: startDataTransfer(): sending \'" + command_startBinary);
safeTCPWrite(TCP_CMD_COMMAND + "," + command_startBinary + TCP_STOP);
}
/**
* @description Sends a stop streaming command to the Ganglion Node module.
*/
public void stopDataTransfer() {
changeState(STATE_STOPPED); // make sure it's now interpretting as binary
println("OpenBCI_Ganglion: stopDataTransfer(): sending \'" + command_stop);
safeTCPWrite(TCP_CMD_COMMAND + "," + command_stop + TCP_STOP);
}
/**
* @description Write to TCP server
* @params out {String} - The string message to write to the server.
* @returns {boolean} - True if able to write, false otherwise.
*/
public boolean safeTCPWrite(String out) {
try {
tcpClient.write(out);
return true;
} catch (Exception e) {
println("Error: Attempted to TCP write with no server connection initialized");
return false;
}
// return false;
// if (nodeProcessHandshakeComplete) { //<>//
// try {
// tcpClient.write(out);
// return true;
// } catch (NullPointerException e) {
// println("Error: Attempted to TCP write with no server connection initialized");
// return false;
// }
// } else {
// println("Waiting on node handshake!");
// return false;
// }
}
private void printGanglion(String msg) {
print("OpenBCI_Ganglion: "); println(msg);
}
public int changeState(int newState) {
state = newState;
prevState_millis = millis();
return 0;
}
// Channel setting
//activate or deactivate an EEG channel...channel counting is zero through nchan-1
public void changeChannelState(int Ichan, boolean activate) {
if (connected) {
if ((Ichan >= 0)) {
if (activate) {
println("OpenBCI_Ganglion: changeChannelState(): activate: sending " + command_activate_channel[Ichan]);
safeTCPWrite(TCP_CMD_COMMAND + "," + command_activate_channel[Ichan] + TCP_STOP);
w_timeSeries.hsc.powerUpChannel(Ichan);
} else {
println("OpenBCI_Ganglion: changeChannelState(): deactivate: sending " + command_deactivate_channel[Ichan]);
safeTCPWrite(TCP_CMD_COMMAND + "," + command_deactivate_channel[Ichan] + TCP_STOP);
w_timeSeries.hsc.powerDownChannel(Ichan);
}
}
}
}
/**
* Used to start accel data mode. Accel arrays will arrive asynchronously!
*/
public void accelStart() {
println("OpenBCI_Ganglion: accell: START");
safeTCPWrite(TCP_CMD_ACCEL + "," + TCP_ACTION_START + TCP_STOP);
accelModeActive = true;
}
/**
* Used to stop accel data mode. Some accel arrays may arrive after stop command
* was sent by this function.
*/
public void accelStop() {
println("OpenBCI_Ganglion: accel: STOP");
safeTCPWrite(TCP_CMD_ACCEL + "," + TCP_ACTION_STOP + TCP_STOP);
accelModeActive = false;
}
/**
* Used to start impedance testing. Impedances will arrive asynchronously!
*/
public void impedanceStart() {
println("OpenBCI_Ganglion: impedance: START");
safeTCPWrite(TCP_CMD_IMPEDANCE + "," + TCP_ACTION_START + TCP_STOP);
checkingImpedance = true;
}
/**
* Used to stop impedance testing. Some impedances may arrive after stop command
* was sent by this function.
*/
public void impedanceStop() {
println("OpenBCI_Ganglion: impedance: STOP");
safeTCPWrite(TCP_CMD_IMPEDANCE + "," + TCP_ACTION_STOP + TCP_STOP);
checkingImpedance = false;
}
/**
* Puts the ganglion in bootloader mode.
*/
public void enterBootloaderMode() {
println("OpenBCI_Ganglion: Entering Bootloader Mode");
safeTCPWrite(TCP_CMD_COMMAND + "," + GANGLION_BOOTLOADER_MODE + TCP_STOP);
delay(500);
disconnectBLE();
haltSystem();
initSystemButton.setString("START SYSTEM");
controlPanel.open();
output("Ganglion now in bootloader mode! Enjoy!");
}
};
+109 -91
Ver Arquivo
@@ -22,10 +22,10 @@ public void updateChannelArrays(int _nchan) {
//activateChannel: Ichan is [0 nchan-1] (aka zero referenced)
void activateChannel(int Ichan) {
println("OpenBCI_GUI: activating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
if (eegDataSource == DATASOURCE_CYTON) {
if (cyton.isPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, true); //activate
cyton.changeChannelState(Ichan, true); //activate
}
} else if (eegDataSource == DATASOURCE_GANGLION) {
// println("activating channel on ganglion");
@@ -38,18 +38,16 @@ void activateChannel(int Ichan) {
}
void deactivateChannel(int Ichan) {
println("OpenBCI_GUI: deactivating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
if (eegDataSource == DATASOURCE_CYTON) {
if (cyton.isPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, false); //de-activate
cyton.changeChannelState(Ichan, false); //de-activate
}
} else if (eegDataSource == DATASOURCE_GANGLION) {
// println("deactivating channel on ganglion");
ganglion.changeChannelState(Ichan, false);
}
if (Ichan < nchan) {
channelSettingValues[Ichan][0] = '1';
// gui.cc.update();
}
}
@@ -70,9 +68,9 @@ class HardwareSettingsController{
int x, y, w, h;
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];
// 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];
int spaceBetweenButtons = 5; //space between buttons
@@ -207,21 +205,21 @@ class HardwareSettingsController{
}
//then reset to 1
//
if (openBCI.get_isWritingChannel()) {
openBCI.writeChannelSettings(channelToWrite,channelSettingValues);
}
// AJ KELLER
// if (cyton.get_isWritingChannel()) {
// cyton.writeChannelSettings(channelToWrite,channelSettingValues);
// }
if (rewriteChannelWhenDoneWriting == true && openBCI.get_isWritingChannel() == false) {
initChannelWrite(channelToWriteWhenDoneWriting);
rewriteChannelWhenDoneWriting = false;
}
// if (rewriteChannelWhenDoneWriting == true) {
// initChannelWrite(channelToWriteWhenDoneWriting);
// rewriteChannelWhenDoneWriting = false;
// }
if (openBCI.get_isWritingImp()) {
openBCI.writeImpedanceSettings(impChannelToWrite,impedanceCheckValues);
}
// if (cyton.get_isWritingImp()) {
// cyton.writeImpedanceSettings(impChannelToWrite,impedanceCheckValues);
// }
if (rewriteImpedanceWhenDoneWriting == true && openBCI.get_isWritingImp() == false) {
if (rewriteImpedanceWhenDoneWriting == true && cyton.get_isWritingImp() == false) {
initImpWrite(impChannelToWriteWhenDoneWriting, final_pORn, final_onORoff);
rewriteImpedanceWhenDoneWriting = false;
}
@@ -246,15 +244,17 @@ class HardwareSettingsController{
}
//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);
fill(bgColor);
textFont(p6, 10);
textAlign(CENTER, TOP);
text("PGA Gain", x + (w/10)*1, y-1);
text("Input Type", x + (w/10)*3, y-1);
text(" Bias ", x + (w/10)*5, y-1);
text("SRB2", x + (w/10)*7, y-1);
text("SRB1", x + (w/10)*9, y-1);
//if mode is not from OpenBCI, draw a dark overlay to indicate that you cannot edit these settings
if (eegDataSource != DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource != DATASOURCE_CYTON) {
fill(0, 0, 0, 200);
noStroke();
rect(x-2, y, w+1, h);
@@ -275,37 +275,44 @@ class HardwareSettingsController{
}
public void loadDefaultChannelSettings() {
verbosePrint("ChannelController: loading default channel settings to GUI's channel controller...");
// println("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]) + ",");
}
}
// verbosePrint("chan: " + i + " ");
channelSettingValues[i][0] = '0';
channelSettingValues[i][1] = '6';
channelSettingValues[i][2] = '0';
channelSettingValues[i][3] = '1';
channelSettingValues[i][4] = '1';
channelSettingValues[i][5] = '0';
// for (int j = 0; j < numSettingsPerChannel; j++) { //channel setting values
// channelSettingValues[i][j] = char(cyton.get_defaultChannelSettings().toCharArray()[j]); //parse defaultChannelSettings string created in the Cyton class
// if (j == numSettingsPerChannel - 1) {
// println(char(cyton.get_defaultChannelSettings().toCharArray()[j]));
// } else {
// print(char(cyton.get_defaultChannelSettings().toCharArray()[j]) + ",");
// }
// }
for (int k = 0; k < 2; k++) { //impedance setting values
impedanceCheckValues[i][k] = '0';
}
}
verbosePrint("made it!");
// verbosePrint("made it!");
update(); //update 1 time to refresh button values based on new loaded settings
}
void updateChannelArrays(int _nchan) {
channelSettingValues = new char [_nchan][numSettingsPerChannel]; // [channel#][Button#-value] ... this will incfluence text of button
impedanceCheckValues = new char [_nchan][2];
}
// 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_W_AUX) {
if (openBCI.isSerialPortOpen()) {
if (eegDataSource == DATASOURCE_CYTON) {
if (cyton.isPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, true); //activate
cyton.changeChannelState(Ichan, true); //activate
}
} else if (eegDataSource == DATASOURCE_GANGLION) {
// println("activating channel on ganglion");
@@ -319,10 +326,10 @@ class HardwareSettingsController{
void deactivateChannel(int Ichan) {
println("OpenBCI_GUI: deactivating channel " + (Ichan+1));
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (openBCI.isSerialPortOpen()) {
if (eegDataSource == DATASOURCE_CYTON) {
if (cyton.isPortOpen()) {
verbosePrint("**");
openBCI.changeChannelState(Ichan, false); //de-activate
cyton.changeChannelState(Ichan, false); //de-activate
}
} else if (eegDataSource == DATASOURCE_GANGLION) {
// println("deactivating channel on ganglion");
@@ -355,7 +362,7 @@ class HardwareSettingsController{
channelSettingValues[_numChannel][0] = '1'; //update powerUp/powerDown value of 2D array
verbosePrint("Command: " + command_deactivate_channel[_numChannel]);
openBCI.deactivateChannel(_numChannel); //assumes numChannel counts from zero (not one)...handles regular and daisy channels
cyton.deactivateChannel(_numChannel); //assumes numChannel counts from zero (not one)...handles regular and daisy channels
}
public void powerUpChannel(int _numChannel) {
@@ -366,46 +373,55 @@ class HardwareSettingsController{
channelSettingValues[_numChannel][0] = '0'; //update powerUp/powerDown value of 2D array
verbosePrint("Command: " + 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
cyton.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
}
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
if (!cyton.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);
cyton.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;
}
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;
}
cyton.writeImpedanceSettings(_numChannel, impedanceCheckValues);
// impChannelToWrite = _numChannel;
//after clicking any button, write the new settings for that channel to OpenBCI
// if (!cyton.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 (!cyton.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;
// }
// cyton.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(int _channelBarHeight) {
@@ -470,14 +486,16 @@ class HardwareSettingsController{
} else {
channelSettingValues[i][j] = '0';
}
cyton.writeChannelSettings(i, channelSettingValues);
// 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 (!cyton.get_isWritingChannel() && rewriteChannelWhenDoneWriting == false) { AJ KEller
// if (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()){
@@ -548,7 +566,7 @@ class HardwareSettingsController{
// //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_W_AUX) {
// if (eegDataSource == DATASOURCE_CYTON) {
// if (showFullController) {
// for (int i = 0; i < nchan; i++) { //When [i][j] button is clicked
// for (int j = 1; j < numSettingsPerChannel; j++) {
@@ -569,7 +587,7 @@ class HardwareSettingsController{
// }
//
// //only allow editing of impedance if dataSource == from OpenBCI
// if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
// if (eegDataSource == DATASOURCE_CYTON) {
// if (impedanceCheckButtons[i][0].isMouseHere()) {
// impedanceCheckButtons[i][0].wasPressed = true;
// impedanceCheckButtons[i][0].isActive = true;
@@ -594,7 +612,7 @@ class HardwareSettingsController{
// 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) {
// if (!cyton.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!");
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+76 -12
Ver Arquivo
@@ -21,7 +21,7 @@ void keyPressed() {
//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(!controlPanel.isOpen && !isNetworkingTextActive()){ //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 {
@@ -163,6 +163,10 @@ void parseKey(char val) {
deactivateChannel(16-1);
}
break;
case ':':
println("test..."); //@@@@@
boolean test = isNetworkingTextActive();
break;
//activate channels 1-8
case '!':
@@ -245,19 +249,19 @@ void parseKey(char val) {
// stopButtonWasPressed();
break;
case 'n':
println("openBCI: " + openBCI);
println("cyton: " + cyton);
break;
case '?':
printRegisters();
cyton.printRegisters();
break;
case 'd':
verbosePrint("Updating GUI's channel settings to default...");
// gui.cc.loadDefaultChannelSettings();
w_timeSeries.hsc.loadDefaultChannelSettings();
//openBCI.serial_openBCI.write('d');
openBCI.configureAllChannelsToDefault();
//cyton.serial_openBCI.write('d');
cyton.configureAllChannelsToDefault();
break;
// //change the state of the impedance measurements...activate the N-channels
@@ -320,12 +324,14 @@ void parseKey(char val) {
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;
if (eegDataSource == DATASOURCE_CYTON) {
println("Interactivity: '" + key + "' Pressed...sending to Cyton...");
cyton.write(key);
} else if (eegDataSource == DATASOURCE_GANGLION) {
println("Interactivity: '" + key + "' Pressed...sending to Ganglion...");
hub.sendCommand(key);
}
break;
}
}
@@ -444,11 +450,22 @@ void parseKeycode(int val) {
}
}
void mouseDragged() {
if (systemMode >= SYSTEMMODE_POSTINIT) {
//calling mouse dragged inly outside of Control Panel
if (controlPanel.isOpen == false) {
wm.mouseDragged();
}
}
}
//swtich yard if a click is detected
void mousePressed() {
verbosePrint("OpenBCI_GUI: mousePressed: mouse pressed");
// verbosePrint("OpenBCI_GUI: mousePressed: mouse pressed");
// println("systemMode" + systemMode);
// controlPanel.CPmousePressed();
//if not before "Start System" ... i.e. after initial setup
if (systemMode >= SYSTEMMODE_POSTINIT) {
@@ -976,3 +993,50 @@ void openURLInBrowser(String _url){
System.out.println(e.getMessage());
}
}
void toggleFrameRate(){
if(frameRateCounter<3){
frameRateCounter++;
} else {
frameRateCounter = 1; // until we resolve the latency issue with 24hz, only allow 30hz minimum (aka frameRateCounter = 1)
}
if(frameRateCounter==0){
frameRate(24); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
topNav.fpsButton.setString("24 fps");
}
if(frameRateCounter==1){
frameRate(30); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
topNav.fpsButton.setString("30 fps");
}
if(frameRateCounter==2){
frameRate(45); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
topNav.fpsButton.setString("45 fps");
}
if(frameRateCounter==3){
frameRate(60); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
topNav.fpsButton.setString("60 fps");
}
}
boolean isNetworkingTextActive(){
boolean isAFieldActive = false;
if (w_networking != null) {
int numTextFields = w_networking.cp5_networking.getAll(Textfield.class).size();
for(int i = 0; i < numTextFields; i++){
if(w_networking.cp5_networking.getAll(Textfield.class).get(i).isFocus()){
isAFieldActive = true;
}
}
}
// println("Test - " + w_networking.cp5_networking.getAll(Textfield.class)); //loop through networking textfields and find out if any of the are active
//isFocus(); returns true if active for textField...
println(isAFieldActive);
return isAFieldActive; //if not, return false
}
boolean highDPI = false;
void toggleHighDPI(){
highDPI = !highDPI;
println("High DPI? " + highDPI);
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+734
Ver Arquivo
@@ -0,0 +1,734 @@
///////////////////////////////////////////////////////////////////////////////
//
// This class configures and manages the connection to the Serial port for
// the Arduino.
//
// Created: Chip Audette, Oct 2013
// Modified: through April 2014
// Modified again: Conor Russomanno Sept-Oct 2014
// Modified for Daisy (16-chan) OpenBCI V3: Conor Russomanno Nov 2014
// Modified Daisy Behaviors: Chip Audette Dec 2014
// Modified For Wifi Addition: AJ Keller July 2017
//
// Note: this class now expects the data format produced by OpenBCI V3.
//
/////////////////////////////////////////////////////////////////////////////
import java.io.OutputStream; //for logging raw bytes to an output file
//------------------------------------------------------------------------
// Global Variables & Instances
//------------------------------------------------------------------------
//these variables are used for "Kill Spikes" ... duplicating the last received data packet if packets were droppeds
boolean werePacketsDroppedSerial = false;
int numPacketsDroppedSerial = 0;
//everything below is now deprecated...
// final String[] command_activate_leadoffP_channel = {"!", "@", "#", "$", "%", "^", "&", "*"}; //shift + 1-8
// final String[] command_deactivate_leadoffP_channel = {"Q", "W", "E", "R", "T", "Y", "U", "I"}; //letters (plus shift) right below 1-8
// final String[] command_activate_leadoffN_channel = {"A", "S", "D", "F", "G", "H", "J", "K"}; //letters (plus shift) below the letters below 1-8
// final String[] command_deactivate_leadoffN_channel = {"Z", "X", "C", "V", "B", "N", "M", "<"}; //letters (plus shift) below the letters below the letters below 1-8
// final String command_biasAuto = "`";
// final String command_biasFixed = "~";
// ArrayList defaultChannelSettings;
//here is the routine that listens to the serial port.
//if any data is waiting, get it, parse it, and stuff it into our vector of
//pre-allocated dataPacketBuff
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
void serialEvent(Serial port){
//check to see which serial port it is
if (iSerial.isOpenBCISerial(port)) {
// boolean echoBytes = !cyton.isStateNormal();
boolean echoBytes;
if (iSerial.isStateNormal() != true) { // || printingRegisters == true){
echoBytes = true;
} else {
echoBytes = false;
}
iSerial.read(echoBytes);
openBCI_byteCount++;
if (iSerial.get_isNewDataPacketAvailable()) {
println("woo got a new packet");
//copy packet into buffer of data packets
curDataPacketInd = (curDataPacketInd+1) % dataPacketBuff.length; //this is also used to let the rest of the code that it may be time to do something
cyton.copyDataPacketTo(dataPacketBuff[curDataPacketInd]);
iSerial.set_isNewDataPacketAvailable(false); //resets isNewDataPacketAvailable to false
// KILL SPIKES!!!
if(werePacketsDroppedSerial){
for(int i = numPacketsDroppedSerial; i > 0; i--){
int tempDataPacketInd = curDataPacketInd - i; //
if(tempDataPacketInd >= 0 && tempDataPacketInd < dataPacketBuff.length){
cyton.copyDataPacketTo(dataPacketBuff[tempDataPacketInd]);
} else {
cyton.copyDataPacketTo(dataPacketBuff[tempDataPacketInd+255]);
}
//put the last stored packet in # of packets dropped after that packet
}
//reset werePacketsDroppedSerial & numPacketsDroppedSerial
werePacketsDroppedSerial = false;
numPacketsDroppedSerial = 0;
}
switch (outputDataSource) {
case OUTPUT_SOURCE_ODF:
fileoutput_odf.writeRawData_dataPacket(dataPacketBuff[curDataPacketInd], cyton.get_scale_fac_uVolts_per_count(), cyton.get_scale_fac_accel_G_per_count(), byte(0xC0));
break;
case OUTPUT_SOURCE_BDF:
curBDFDataPacketInd = curDataPacketInd;
thread("writeRawData_dataPacket_bdf");
// fileoutput_bdf.writeRawData_dataPacket(dataPacketBuff[curDataPacketInd]);
break;
case OUTPUT_SOURCE_NONE:
default:
// Do nothing...
break;
}
newPacketCounter++;
}
} else {
//Used for serial communications, primarily everything in no_start_connection
if (no_start_connection) {
if (board_message == null || dollaBillz>2) {
board_message = new StringBuilder();
dollaBillz = 0;
}
inByte = byte(port.read());
print(inByte);
if (char(inByte) == 'S' || char(inByte) == 'F') isOpenBCI = true;
// print(char(inByte));
if (inByte != -1) {
if (isGettingPoll) {
if (inByte != '$') {
if (!spaceFound) board_message.append(char(inByte));
else hexToInt = Integer.parseInt(String.format("%02X", inByte), 16);
if (char(inByte) == ' ') spaceFound = true;
} else dollaBillz++;
} else {
if (inByte != '$') board_message.append(char(inByte));
else dollaBillz++;
}
}
} else {
//println("Recieved serial data not from OpenBCI"); //this is a bit of a lie
inByte = byte(port.read());
if (isOpenBCI) {
if (board_message == null || dollaBillz >2) {
board_message = new StringBuilder();
dollaBillz=0;
}
if(inByte != '$'){
board_message.append(char(inByte));
} else { dollaBillz++; }
} else if(char(inByte) == 'S' || char(inByte) == 'F'){
isOpenBCI = true;
if(board_message == null){
board_message = new StringBuilder();
board_message.append(char(inByte));
}
}
}
}
}
//------------------------------------------------------------------------
// Classes
//------------------------------------------------------------------------
class InterfaceSerial {
//here is the serial port for this OpenBCI board
private Serial serial_openBCI = null;
private boolean portIsOpen = false;
//final static int DATAMODE_TXT = 0;
final static int DATAMODE_BIN = 2;
final static int DATAMODE_BIN_WAUX = 1; //switched to this value so that receiving Accel data is now the default
//final static int DATAMODE_BIN_4CHAN = 4;
final static int STATE_NOCOM = 0;
final static int STATE_COMINIT = 1;
final static int STATE_SYNCWITHHARDWARE = 2;
final static int STATE_NORMAL = 3;
final static int STATE_STOPPED = 4;
final static int COM_INIT_MSEC = 3000; //you may need to vary this for your computer or your Arduino
//int[] measured_packet_length = {0,0,0,0,0};
//int measured_packet_length_ind = 0;
//int known_packet_length_bytes = 0;
final static byte BYTE_START = (byte)0xA0;
final static byte BYTE_END = (byte)0xC0;
int prefered_datamode = DATAMODE_BIN_WAUX;
private int state = STATE_NOCOM;
int dataMode = -1;
int prevState_millis = 0;
private int nEEGValuesPerPacket = 8; //defined by the data format sent by cyton boards
private int nAuxValuesPerPacket = 3; //defined by the data format sent by cyton boards
private DataPacket_ADS1299 rawReceivedDataPacket;
private DataPacket_ADS1299 missedDataPacket;
private DataPacket_ADS1299 dataPacket;
public int [] validAuxValues = {0, 0, 0};
public boolean[] freshAuxValuesAvailable = {false, false, false};
public boolean freshAuxValues = false;
//DataPacket_ADS1299 prevDataPacket;
private int nAuxValues;
private boolean isNewDataPacketAvailable = false;
private OutputStream output; //for debugging WEA 2014-01-26
private int prevSampleIndex = 0;
private int serialErrorCounter = 0;
private final float fs_Hz = 250.0f; //sample rate used by OpenBCI board...set by its Arduino code
private final float ADS1299_Vref = 4.5f; //reference voltage for ADC in ADS1299. set by its hardware
private float ADS1299_gain = 24.0; //assumed gain setting for ADS1299. set by its Arduino code
private float openBCI_series_resistor_ohms = 2200; // Ohms. There is a series resistor on the 32 bit board.
private float scale_fac_uVolts_per_count = ADS1299_Vref / ((float)(pow(2, 23)-1)) / ADS1299_gain * 1000000.f; //ADS1299 datasheet Table 7, confirmed through experiment
//float LIS3DH_full_scale_G = 4; // +/- 4G, assumed full scale setting for the accelerometer
private final float scale_fac_accel_G_per_count = 0.002 / ((float)pow(2, 4)); //assume set to +/4G, so 2 mG per digit (datasheet). Account for 4 bits unused
//final float scale_fac_accel_G_per_count = 1.0;
private final float leadOffDrive_amps = 6.0e-9; //6 nA, set by its Arduino code
private final String failureMessage = "Failure: Communications timeout - Device failed to poll Host";
boolean isBiasAuto = true; //not being used?
//data related to Conor's setup for V3 boards
final char[] EOT = {'$', '$', '$'};
char[] prev3chars = {'#', '#', '#'};
private boolean readyToSend = false; //system waits for $$$ after requesting information from OpenBCI board
private long timeOfLastCommand = 0; //used when sync'ing to hardware
//wait for $$$ to iterate... applies to commands expecting a response
public boolean isReadyToSend() {
return readyToSend;
}
public void setReadyToSend(boolean _readyToSend) {
readyToSend = _readyToSend;
}
public int get_state() {
return state;
};
public boolean get_isNewDataPacketAvailable() {
return isNewDataPacketAvailable;
}
public void set_isNewDataPacketAvailable(boolean _isNewDataPacketAvailable) {
isNewDataPacketAvailable = _isNewDataPacketAvailable;
}
//constructors
InterfaceSerial() {
}; //only use this if you simply want access to some of the constants
InterfaceSerial(PApplet applet, String comPort, int baud, int nEEGValuesPerOpenBCI, boolean useAux, int nAuxValuesPerOpenBCI) {
//choose data mode
println("InterfaceSerial: prefered_datamode = " + prefered_datamode + ", nValuesPerPacket = " + nEEGValuesPerPacket);
if (prefered_datamode == DATAMODE_BIN_WAUX) {
if (!useAux) {
//must be requesting the aux data, so change the referred data mode
prefered_datamode = DATAMODE_BIN;
nAuxValues = 0;
//println("InterfaceSerial: nAuxValuesPerPacket = " + nAuxValuesPerPacket + " so setting prefered_datamode to " + prefered_datamode);
}
}
dataMode = prefered_datamode;
initDataPackets(nEEGValuesPerOpenBCI, nAuxValuesPerOpenBCI);
}
public void initDataPackets(int numEEG, int numAux) {
nEEGValuesPerPacket = numEEG;
nAuxValuesPerPacket = numAux;
//allocate space for data packet
rawReceivedDataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
missedDataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this should always be 8 channels
dataPacket = new DataPacket_ADS1299(nEEGValuesPerPacket, nAuxValuesPerPacket); //this could be 8 or 16 channels
for (int i = 0; i < nEEGValuesPerPacket; i++) {
rawReceivedDataPacket.values[i] = 0;
//prevDataPacket.values[i] = 0;
}
for (int i=0; i < nEEGValuesPerPacket; i++) {
// println("i = " + i);
dataPacket.values[i] = 0;
missedDataPacket.values[i] = 0;
}
for (int i = 0; i < nAuxValuesPerPacket; i++) {
rawReceivedDataPacket.auxValues[i] = 0;
dataPacket.auxValues[i] = 0;
missedDataPacket.auxValues[i] = 0;
//prevDataPacket.auxValues[i] = 0;
}
}
// //manage the serial port
public int openSerialPort(PApplet applet, String comPort, int baud) {
output("Attempting to open Serial/COM port: " + openBCI_portName);
try {
println("InterfaceSerial: openSerialPort: attempting to open serial port: " + openBCI_portName);
serial_openBCI = new Serial(applet, comPort, baud); //open the com port
serial_openBCI.clear(); // clear anything in the com port's buffer
portIsOpen = true;
println("InterfaceSerial: openSerialPort: port is open (t)? ... " + portIsOpen);
changeState(STATE_COMINIT);
return 0;
}
catch (RuntimeException e) {
if (e.getMessage().contains("<init>")) {
serial_openBCI = null;
System.out.println("InterfaceSerial: openSerialPort: port in use, trying again later...");
portIsOpen = false;
} else {
println("RunttimeException: " + e);
output("Error connecting to selected Serial/COM port. Make sure your board is powered up and your dongle is plugged in.");
abandonInit = true; //global variable in OpenBCI_GUI.pde
}
return 0;
}
}
public int changeState(int newState) {
state = newState;
prevState_millis = millis();
return 0;
}
int finalizeCOMINIT() {
// //wait specified time for COM/serial port to initialize
// if (state == STATE_COMINIT) {
// // println("InterfaceSerial: finalizeCOMINIT: Initializing Serial: millis() = " + millis());
// if ((millis() - prevState_millis) > COM_INIT_MSEC) {
// //serial_openBCI.write(command_activates + "\n"); println("Processing: Serial: activating filters");
// println("InterfaceSerial: finalizeCOMINIT: State = NORMAL");
changeState(STATE_NORMAL);
// // startRunning();
// }
// }
return 0;
}
public int closeSDandSerialPort() {
int returnVal=0;
cyton.closeSDFile();
readyToSend = false;
returnVal = closeSerialPort();
prevState_millis = 0; //reset Serial state clock to use as a conditional for timing at the beginnign of systemUpdate()
cyton.hardwareSyncStep = 0; //reset Hardware Sync step to be ready to go again...
return returnVal;
}
public int closeSerialPort() {
// if (serial_openBCI != null) {
portIsOpen = false;
if (serial_openBCI != null) {
serial_openBCI.stop();
}
serial_openBCI = null;
state = STATE_NOCOM;
println("InterfaceSerial: closeSerialPort: closed");
return 0;
}
public void updateSyncState(int sdSetting) {
//has it been 3000 milliseconds since we initiated the serial port? We want to make sure we wait for the OpenBCI board to finish its setup()
// println("0");
if ( (millis() - prevState_millis > COM_INIT_MSEC) && (prevState_millis != 0) && (state == STATE_COMINIT) ) {
state = STATE_SYNCWITHHARDWARE;
timeOfLastCommand = millis();
serial_openBCI.clear();
cyton.potentialFailureMessage = "";
cyton.defaultChannelSettings = ""; //clear channel setting string to be reset upon a new Init System
cyton.daisyOrNot = ""; //clear daisyOrNot string to be reset upon a new Init System
println("InterfaceSerial: systemUpdate: [0] Sending 'v' to OpenBCI to reset hardware in case of 32bit board...");
serial_openBCI.write('v');
}
//if we are in SYNC WITH HARDWARE state ... trigger a command
if ( (state == STATE_SYNCWITHHARDWARE) && (currentlySyncing == false) ) {
if (millis() - timeOfLastCommand > 200 && readyToSend == true) {
println("sdSetting: " + sdSetting);
timeOfLastCommand = millis();
cyton.hardwareSyncStep++;
cyton.syncWithHardware(sdSetting);
}
}
}
public void sendChar(char val) {
if (isSerialPortOpen()) {
println("sending out: " + val);
serial_openBCI.write(val);//send the value as ascii (with a newline character?)
} else {
println("nope no out: " + val);
}
}
public void write(String msg) {
if (isSerialPortOpen()) {
serial_openBCI.write(msg);
}
}
public boolean isSerialPortOpen() {
if (portIsOpen & (serial_openBCI != null)) {
return true;
} else {
return false;
}
}
public boolean isOpenBCISerial(Serial port) {
if (serial_openBCI == port) {
return true;
} else {
return false;
}
}
public void clear() {
if (serial_openBCI != null) {
serial_openBCI.clear();
}
}
//read from the serial port
public int read() {
return read(false);
}
public int read(boolean echoChar) {
//println("InterfaceSerial: read(): State: " + state);
//get the byte
byte inByte;
if (isSerialPortOpen()) {
inByte = byte(serial_openBCI.read());
} else {
println("InterfaceSerial port not open aborting.");
return 0;
}
print(inByte);
//write the most recent char to the console
// If the GUI is in streaming mode then echoChar will be false
if (echoChar) { //if not in interpret binary (NORMAL) mode
// print("hardwareSyncStep: "); println(hardwareSyncStep);
// print(".");
char inASCII = char(inByte);
if (isRunning == false && (millis() - timeSinceStopRunning) > 500) {
print(char(inByte));
}
//keep track of previous three chars coming from OpenBCI
prev3chars[0] = prev3chars[1];
prev3chars[1] = prev3chars[2];
prev3chars[2] = inASCII;
if (cyton.hardwareSyncStep == 0 && inASCII != '$') {
cyton.potentialFailureMessage+=inASCII;
}
if (cyton.hardwareSyncStep == 1 && inASCII != '$') {
cyton.daisyOrNot+=inASCII;
//if hardware returns 8 because daisy is not attached, switch the GUI mode back to 8 channels
// if(nchan == 16 && char(daisyOrNot.substring(daisyOrNot.length() - 1)) == '8'){
if (nchan == 16 && cyton.daisyOrNot.charAt(cyton.daisyOrNot.length() - 1) == '8') {
// verbosePrint(" received from OpenBCI... Switching to nchan = 8 bc daisy is not present...");
verbosePrint(" received from OpenBCI... Abandoning hardware initiation.");
abandonInit = true;
// haltSystem();
// updateToNChan(8);
//
// //initialize the FFT objects
// for (int Ichan=0; Ichan < nchan; Ichan++) {
// verbosePrint("Init FFT Buff – "+Ichan);
// fftBuff[Ichan] = new FFT(Nfft, getSampleRateSafe());
// } //make the FFT objects
//
// initializeFFTObjects(fftBuff, dataBuffY_uV, Nfft, getSampleRateSafe());
// setupWidgetManager();
}
}
if (cyton.hardwareSyncStep == 3 && inASCII != '$') { //if we're retrieving channel settings from OpenBCI
cyton.defaultChannelSettings+=inASCII;
}
//if the last three chars are $$$, it means we are moving on to the next stage of initialization
if (prev3chars[0] == EOT[0] && prev3chars[1] == EOT[1] && prev3chars[2] == EOT[2]) {
verbosePrint(" > EOT detected...");
// Added for V2 system down rejection line
if (cyton.hardwareSyncStep == 0) {
// Failure: Communications timeout - Device failed to poll Host$$$
if (cyton.potentialFailureMessage.equals(failureMessage)) {
closeLogFile();
return 0;
}
}
// hardwareSyncStep++;
prev3chars[2] = '#';
if (cyton.hardwareSyncStep == 3) {
println("InterfaceSerial: read(): x");
println(cyton.defaultChannelSettings);
println("InterfaceSerial: read(): y");
// gui.cc.loadDefaultChannelSettings();
w_timeSeries.hsc.loadDefaultChannelSettings();
println("InterfaceSerial: read(): z");
}
readyToSend = true;
// println(hardwareSyncStep);
// syncWithHardware(); //haha, I'm getting very verbose with my naming... it's late...
}
}
//write raw unprocessed bytes to a binary data dump file
if (output != null) {
try {
output.write(inByte); //for debugging WEA 2014-01-26
}
catch (IOException e) {
System.err.println("InterfaceSerial: read(): Caught IOException: " + e.getMessage());
//do nothing
}
}
interpretBinaryStream(inByte); //new 2014-02-02 WEA
return int(inByte);
}
/* **** Borrowed from Chris Viegl from his OpenBCI parser for BrainBay
Modified by Joel Murphy and Conor Russomanno to read OpenBCI data
Packet Parser for OpenBCI (1-N channel binary format):
3-byte data values are stored in 'little endian' formant in AVRs
so this protocol parser expects the lower bytes first.
Start Indicator: 0xA0
EXPECTING STANDARD PACKET LENGTH DON'T NEED: Packet_length : 1 byte (length = 4 bytes framenumber + 4 bytes per active channel + (optional) 4 bytes for 1 Aux value)
Framenumber : 1 byte (Sequential counter of packets)
Channel 1 data : 3 bytes
...
Channel 8 data : 3 bytes
Aux Values : UP TO 6 bytes
End Indcator : 0xC0
TOTAL OF 33 bytes ALL DAY
********************************************************************* */
private int nDataValuesInPacket = 0;
private int localByteCounter=0;
private int localChannelCounter=0;
private int PACKET_readstate = 0;
// byte[] localByteBuffer = {0,0,0,0};
private byte[] localAdsByteBuffer = {0, 0, 0};
private byte[] localAccelByteBuffer = {0, 0};
void interpretBinaryStream(byte actbyte) {
boolean flag_copyRawDataToFullData = false;
//println("InterfaceSerial: interpretBinaryStream: PACKET_readstate " + PACKET_readstate);
switch (PACKET_readstate) {
case 0:
//look for header byte
if (actbyte == byte(0xA0)) { // look for start indicator
// println("InterfaceSerial: interpretBinaryStream: found 0xA0");
PACKET_readstate++;
}
break;
case 1:
//check the packet counter
// println("case 1");
byte inByte = actbyte;
rawReceivedDataPacket.sampleIndex = int(inByte); //changed by JAM
if ((rawReceivedDataPacket.sampleIndex-prevSampleIndex) != 1) {
if (rawReceivedDataPacket.sampleIndex != 0) { // if we rolled over, don't count as error
serialErrorCounter++;
werePacketsDroppedSerial = true; //set this true to activate packet duplication in serialEvent
if(rawReceivedDataPacket.sampleIndex < prevSampleIndex){ //handle the situation in which the index jumps from 250s past 255, and back to 0
numPacketsDroppedSerial = (rawReceivedDataPacket.sampleIndex+255) - prevSampleIndex; //calculate how many times the last received packet should be duplicated...
} else {
numPacketsDroppedSerial = rawReceivedDataPacket.sampleIndex - prevSampleIndex; //calculate how many times the last received packet should be duplicated...
}
println("InterfaceSerial: apparent sampleIndex jump from Serial data: " + prevSampleIndex + " to " + rawReceivedDataPacket.sampleIndex + ". Keeping packet. (" + serialErrorCounter + ")");
if (outputDataSource == OUTPUT_SOURCE_BDF) {
int fakePacketsToWrite = (rawReceivedDataPacket.sampleIndex - prevSampleIndex) - 1;
for (int i = 0; i < fakePacketsToWrite; i++) {
fileoutput_bdf.writeRawData_dataPacket(missedDataPacket);
}
println("InterfaceSerial: because BDF, wrote " + fakePacketsToWrite + " empty data packet(s)");
}
}
}
prevSampleIndex = rawReceivedDataPacket.sampleIndex;
localByteCounter=0;//prepare for next usage of localByteCounter
localChannelCounter=0; //prepare for next usage of localChannelCounter
PACKET_readstate++;
break;
case 2:
// get ADS channel values
// println("case 2");
localAdsByteBuffer[localByteCounter] = actbyte;
localByteCounter++;
if (localByteCounter==3) {
rawReceivedDataPacket.values[localChannelCounter] = interpret24bitAsInt32(localAdsByteBuffer);
arrayCopy(localAdsByteBuffer, rawReceivedDataPacket.rawValues[localChannelCounter]);
localChannelCounter++;
if (localChannelCounter==8) { //nDataValuesInPacket) {
// all ADS channels arrived !
// println("InterfaceSerial: interpretBinaryStream: localChannelCounter = " + localChannelCounter);
PACKET_readstate++;
if (prefered_datamode != DATAMODE_BIN_WAUX) PACKET_readstate++; //if not using AUX, skip over the next readstate
localByteCounter = 0;
localChannelCounter = 0;
//isNewDataPacketAvailable = true; //tell the rest of the code that the data packet is complete
} else {
//prepare for next data channel
localByteCounter=0; //prepare for next usage of localByteCounter
}
}
break;
case 3:
// get LIS3DH channel values 2 bytes times 3 axes
// println("case 3");
localAccelByteBuffer[localByteCounter] = actbyte;
localByteCounter++;
if (localByteCounter==2) {
rawReceivedDataPacket.auxValues[localChannelCounter] = interpret16bitAsInt32(localAccelByteBuffer);
arrayCopy(localAccelByteBuffer, rawReceivedDataPacket.rawAuxValues[localChannelCounter]);
if (rawReceivedDataPacket.auxValues[localChannelCounter] != 0) {
validAuxValues[localChannelCounter] = rawReceivedDataPacket.auxValues[localChannelCounter];
freshAuxValuesAvailable[localChannelCounter] = true;
freshAuxValues = true;
} else freshAuxValues = false;
localChannelCounter++;
if (localChannelCounter==nAuxValues) { //number of accelerometer axis) {
// all Accelerometer channels arrived !
// println("InterfaceSerial: interpretBinaryStream: Accel Data: " + rawReceivedDataPacket.auxValues[0] + ", " + rawReceivedDataPacket.auxValues[1] + ", " + rawReceivedDataPacket.auxValues[2]);
PACKET_readstate++;
localByteCounter = 0;
//isNewDataPacketAvailable = true; //tell the rest of the code that the data packet is complete
} else {
//prepare for next data channel
localByteCounter=0; //prepare for next usage of localByteCounter
}
}
break;
case 4:
//look for end byte
// println("case 4");
if (actbyte == byte(0xC0) || actbyte == byte(0xC1)) { // if correct end delimiter found:
// println("... 0xCx found");
// println("InterfaceSerial: interpretBinaryStream: found end byte. Setting isNewDataPacketAvailable to TRUE");
isNewDataPacketAvailable = true; //original place for this. but why not put it in the previous case block
flag_copyRawDataToFullData = true; //time to copy the raw data packet into the full data packet (mainly relevant for 16-chan OpenBCI)
} else {
serialErrorCounter++;
println("InterfaceSerial: interpretBinaryStream: Actbyte = " + actbyte);
println("InterfaceSerial: interpretBinaryStream: expecteding end-of-packet byte is missing. Discarding packet. (" + serialErrorCounter + ")");
}
PACKET_readstate=0; // either way, look for next packet
break;
default:
println("InterfaceSerial: interpretBinaryStream: Unknown byte: " + actbyte + " . Continuing...");
PACKET_readstate=0; // look for next packet
}
if (flag_copyRawDataToFullData) {
copyRawDataToFullData();
}
} // end of interpretBinaryStream
//return the state
public boolean isStateNormal() {
if (state == STATE_NORMAL) {
return true;
} else {
return false;
}
}
private int interpret24bitAsInt32(byte[] byteArray) {
//little endian
int newInt = (
((0xFF & byteArray[0]) << 16) |
((0xFF & byteArray[1]) << 8) |
(0xFF & byteArray[2])
);
if ((newInt & 0x00800000) > 0) {
newInt |= 0xFF000000;
} else {
newInt &= 0x00FFFFFF;
}
return newInt;
}
private int interpret16bitAsInt32(byte[] byteArray) {
int newInt = (
((0xFF & byteArray[0]) << 8) |
(0xFF & byteArray[1])
);
if ((newInt & 0x00008000) > 0) {
newInt |= 0xFFFF0000;
} else {
newInt &= 0x0000FFFF;
}
return newInt;
}
private int copyRawDataToFullData() {
//Prior to the 16-chan OpenBCI, we did NOT have rawReceivedDataPacket along with dataPacket...we just had dataPacket.
//With the 16-chan OpenBCI, where the first 8 channels are sent and then the second 8 channels are sent, we introduced
//this extra structure so that we could alternate between them.
//
//This function here decides how to join the latest data (rawReceivedDataPacket) into the full dataPacket
if (dataPacket.values.length < 2*rawReceivedDataPacket.values.length) {
//this is an 8 channel board, so simply copy the data
return rawReceivedDataPacket.copyTo(dataPacket);
} else {
//this is 16-channels, so copy the raw data into the correct channels of the new data
int offsetInd_values = 0; //this is correct assuming we just recevied a "board" packet (ie, channels 1-8)
int offsetInd_aux = 0; //this is correct assuming we just recevied a "board" packet (ie, channels 1-8)
if (rawReceivedDataPacket.sampleIndex % 2 == 0) { // even data packets are from the daisy board
offsetInd_values = rawReceivedDataPacket.values.length; //start copying to the 8th slot
//offsetInd_aux = rawReceivedDataPacket.auxValues.length; //start copying to the 3rd slot
offsetInd_aux = 0;
}
return rawReceivedDataPacket.copyTo(dataPacket, offsetInd_values, offsetInd_aux);
}
}
public int copyDataPacketTo(DataPacket_ADS1299 target) {
isNewDataPacketAvailable = false;
return dataPacket.copyTo(target);
}
};
+394 -140
Ver Arquivo
@@ -6,6 +6,7 @@
// Created: Chip Audette, Oct 2013 - May 2014
// Modified: Conor Russomanno & Joel Murphy, August 2014 - Dec 2014
// Modified (v2.0): Conor Russomanno & Joel Murphy (AJ Keller helped too), June 2016
// Modified (v3.0) AJ Keller (Conor Russomanno & Joel Murphy & Wangshu), September 2017
//
// Requires gwoptics graphing library for processing. Built on V0.5.0
// http://www.gwoptics.org/processing/gwoptics_p5lib/
@@ -38,6 +39,7 @@ import netP5.*; // for OSC
import oscP5.*; // for OSC
import hypermedia.net.*; //for UDP
import java.nio.ByteBuffer; //for UDP
import edu.ucsd.sccn.LSL; //for LSL
import gifAnimation.*;
@@ -62,18 +64,29 @@ final int NCHAN_CYTON = 8;
final int NCHAN_CYTON_DAISY = 16;
final int NCHAN_GANGLION = 4;
boolean hasIntroAnimation = false;
boolean hasIntroAnimation = true;
PImage cog;
Gif loadingGIF;
Gif loadingGIF_blue;
boolean initSystemThreadLock = false;
// ---- Define variables related to OpenBCI_GUI UDPMarker functionality
UDP udpRX;
//choose where to get the EEG data
final int DATASOURCE_NORMAL_W_AUX = 0; // new default, data from serial with Accel data CHIP 2014-11-03
final int DATASOURCE_CYTON = 0; // new default, data from serial with Accel data CHIP 2014-11-03
final int DATASOURCE_GANGLION = 1; //looking for signal from OpenBCI board via Serial/COM port, no Aux data
final int DATASOURCE_PLAYBACKFILE = 2; //playback from a pre-recorded text file
final int DATASOURCE_SYNTHETIC = 3; //Synthetically generated data
public int eegDataSource = -1; //default to none of the options
final int INTERFACE_NONE = -1; // Used to indicate no choice made yet on interface
final int INTERFACE_SERIAL = 0; // Used only by cyton
final int INTERFACE_HUB_BLE = 1; // used only by ganglion
final int INTERFACE_HUB_WIFI = 2; // used by both cyton and ganglion
final int INTERFACE_HUB_BLED112 = 3; // used only by ganglion with bled dongle
//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!
@@ -82,16 +95,28 @@ 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
// Initialize boards for constants
Cyton cyton = new Cyton(); //dummy creation to get access to constants, create real one later
Ganglion ganglion = new Ganglion(); //dummy creation to get access to constants, create real one later
// Intialize interface protocols
InterfaceSerial iSerial = new InterfaceSerial();
Hub hub = new Hub(); //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
OpenBCI_Ganglion ganglion; //dummy creation to get access to constants, create real one later
String ganglion_portName = "N/A";
String wifi_portName = "N/A";
String wifi_ipAddress = "192.168.4.1";
final static String PROTOCOL_BLE = "ble";
final static String PROTOCOL_BLED112 = "bled112";
final static String PROTOCOL_SERIAL = "serial";
final static String PROTOCOL_WIFI = "wifi";
////// ---- Define variables related to OpenBCI board operations
//Define number of channels from openBCI...first EEG channels, then aux channels
//Define number of channels from cyton...first EEG channels, then aux channels
int nchan = NCHAN_CYTON; //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)
@@ -101,9 +126,9 @@ final int threshold_railed_warn = int(pow(2, 23)*0.9); //set a somewhat smaller
//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)get_fs_Hz_safe();
DataPacket_ADS1299 dataPacketBuff[] = new DataPacket_ADS1299[nDataBackBuff]; //allocate the array, but doesn't call constructor. Still need to call the constructor!
//cyton data packet
int nDataBackBuff;
DataPacket_ADS1299 dataPacketBuff[]; //allocate later in InitSystem
int curDataPacketInd = -1;
int curBDFDataPacketInd = -1;
int lastReadDataPacketInd = -1;
@@ -119,19 +144,24 @@ long timeOfInit;
long timeSinceStopRunning = 1000;
int prev_time_millis = 0;
// Calculate nPointsPerUpdate based on sampling rate and buffer update rate
// @update_millis: update the buffer every 40 milliseconds
// @nPointsPerUpdate: update the GUI after this many data points have been received.
// The sampling rate should be ideally a multiple of 25, so as to make actual buffer update rate exactly 40ms
final int update_millis = 40;
int nPointsPerUpdate; // no longer final, calculate every time in initSystem
// final int nPointsPerUpdate = 50; //update the GUI after this many data points have been received
// final int nPointsPerUpdate = 24; //update the GUI after this many data points have been received
final int nPointsPerUpdate = 10; //update the GUI after this many data points have been received
// final int nPointsPerUpdate = 10; //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 yLittleBuff[];
float yLittleBuff_uV[][]; //small buffer used to send data to the filters
float accelerometerBuff[][]; // accelerometer buff 500 points
float auxBuff[][] = new float[3][nPointsPerUpdate];
float auxBuff[][];
float data_elec_imp_ohm[];
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?)
@@ -149,9 +179,9 @@ public int outputDataSource = OUTPUT_SOURCE_ODF;
// public int outputDataSource = OUTPUT_SOURCE_BDF;
// Serial output
String serial_output_portName = "/dev/tty.usbmodem1411"; //must edit this based on the name of the serial/COM port
String serial_output_portName = "/dev/tty.usbmodem1421"; //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
int serial_output_baud = 9600; //baud rate from the Arduino
//Control Panel for (re)configuring system settings
PlotFontInfo fontInfo;
@@ -211,7 +241,7 @@ PFont p6; //small Open Sans
ButtonHelpText buttonHelpText;
//EMG_Widget emg_widget;
PulseSensor_Widget pulseWidget;
// PulseSensor_Widget pulseWidget;
boolean no_start_connection = false;
boolean has_processed = false;
@@ -223,7 +253,7 @@ boolean synthesizeData = false;
int timeOfSetup = 0;
boolean isHubInitialized = false;
boolean isGanglionObjectInitialized = false;
boolean isHubObjectInitialized = false;
color bgColor = color(1, 18, 41);
color openbciBlue = color(31, 69, 110);
int COLOR_SCHEME_DEFAULT = 1;
@@ -233,38 +263,43 @@ int colorScheme = COLOR_SCHEME_ALTERNATIVE_A;
Process nodeHubby;
int hubPid = 0;
String nodeHubName = "GanglionHub";
String nodeHubName = "OpenBCIHub";
Robot rob3115;
PApplet ourApplet;
//-----------------------------------------1-------------------------------
//------------------------------------------------------------------------
// Global Functions
//------------------------------------------------------------------------
//========================SETUP============================//
int frameRateCounter = 1; //0 = 24, 1 = 30, 2 = 45, 3 = 60
void setup() {
// Step 1: Prepare the exit handler that will attempt to close a running node
// server on shut down of this app, the main process.
// prepareExitHandler();
if (dev == false) {
// On windows wait to start the hub until Ganglion is clicked on in the control panel.
// See issue #111
hubStop(); //kill any existing hubs before starting a new one..
if (!isWindows()) {
hubInit();
}
}
if (!isWindows()) hubStop(); //kill any existing hubs before starting a new one..
hubInit(); // putting down here gives windows time to close any open apps
println("Welcome to the Processing-based OpenBCI GUI!"); //Welcome line.
println("Last update: 12/20/2016"); //Welcome line.
println("Last update: 9/5/2016"); //Welcome line.
println("For more information about how to work with this code base, please visit: http://docs.openbci.com/OpenBCI%20Software/");
//open window
size(1024, 768, P2D);
ourApplet = this;
frameRate(60); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
if(frameRateCounter==0){
frameRate(24); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
}
if(frameRateCounter==1){
frameRate(30); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
}
if(frameRateCounter==2){
frameRate(45); //refresh rate ... this will slow automatically, if your processor can't handle the specified rate
}
if(frameRateCounter==3){
frameRate(60); //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
@@ -330,34 +365,65 @@ void setup() {
playground = new Playground(navBarHeight);
//attempt to open a serial port for "output"
// try {
// verbosePrint("OpenBCI_GUI.pde: attempting to open serial/COM 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: could not open " + serial_output_portName);
// }
// println("OpenBCI_GUI: setup: hub is running " + ganglion.isHubRunning());
buttonHelpText = new ButtonHelpText();
myPresentation = new Presentation();
// try{
// rob3115 = new Robot();
// } catch (AWTException e){
// println("couldn't create robot...");
// }
// UDPMarker functionality
// Setup the UDP receiver
int portRX = 51000; // this is the UDP port the application will be listening on
String ip = "127.0.0.1"; // Currently only localhost is supported as UDP Marker source
// ganglion = new OpenBCI_Ganglion(this);
// wm = new WidgetManager(this);
//create new object for receiving
udpRX=new UDP(this,portRX,ip);
udpRX.setReceiveHandler("udpReceiveHandler");
udpRX.log(true);
udpRX.listen(true);
// Print some useful diagnostics
println("OpenBCI_GUI::Setup: Is RX mulitcast: "+udpRX.isMulticast());
println("OpenBCI_GUI::Setup: Has RX joined multicast: "+udpRX.isJoined());
timeOfSetup = millis(); //keep track of time when setup is finished... used to make sure enough time has passed before creating some other objects (such as the Ganglion instance)
}
//====================== END-OF-SETUP ==========================//
//====================UDP Packet Handler==========================//
// This function handles the received UDP packet
// See the documentation for the Java UDP class here:
// https://ubaa.net/shared/processing/udp/udp_class_udp.htm
String udpReceiveString = null;
void udpReceiveHandler(byte[] data, String ip, int portRX){
String udpString = new String(data);
println(udpString+" from: "+ip+" and port: "+portRX);
if (udpString.length() >=5 && udpString.indexOf("MARK") >= 0){
/* Old version with 10 markers
char c = value.charAt(4);
if ( c>= '0' && c <= '9'){
println("Found a valid UDP STIM of value: "+int(c)+" chr: "+c);
hub.sendCommand("`"+char(c-(int)'0'));
*/
int intValue = Integer.parseInt(udpString.substring(4));
if (intValue > 0 && intValue < 96){ // Since we only send single char ascii value markers (from space to char(126)
String sendString = "`"+char(intValue+31);
println("Marker value: "+udpString+" with numeric value of char("+intValue+") as : "+sendString);
hub.sendCommand(sendString);
} else {
println("udpReceiveHandler::Warning:invalid UDP STIM of value: "+intValue+" Received String: "+udpString);
}
} else {
println("udpReceiveHandler::Warning:invalid UDP marker packet: "+udpString);
}
}
//======================== DRAW LOOP =============================//
void draw() {
@@ -395,8 +461,10 @@ private void prepareExitHandler () {
*/
void hubInit() {
isHubInitialized = true;
hubStart();
prepareExitHandler();
if (!isWindows()) {
hubStart();
prepareExitHandler();
}
}
/**
@@ -408,13 +476,13 @@ void hubStart() {
// https://forum.processing.org/two/discussion/13053/use-launch-for-applications-kept-in-data-folder
if (isWindows()) {
println("OpenBCI_GUI: hubStart: OS Detected: Windows");
nodeHubby = launch(dataPath("GanglionHub.exe"));
nodeHubby = launch(dataPath("OpenBCIHub.exe"));
} else if (isLinux()) {
println("OpenBCI_GUI: hubStart: OS Detected: Linux");
nodeHubby = exec(dataPath("GanglionHub"));
nodeHubby = exec(dataPath("OpenBCIHub"));
} else {
println("OpenBCI_GUI: hubStart: OS Detected: Mac");
nodeHubby = launch(dataPath("GanglionHub.app"));
nodeHubby = launch(dataPath("OpenBCIHub.app"));
}
// hubRunning = true;
}
@@ -451,6 +519,14 @@ private boolean isWindows() {
return System.getProperty("os.name").toLowerCase().indexOf("windows") > -1;
}
/**
* @description Helper function to determine if the system is macOS or not.
* @return {boolean} true if os is windows, false otherwise.
*/
private boolean isMac() {
return !isWindows() && !isLinux();
}
/**
* @description Parses the running process list for processes whose name have ganglion hub, if found, kills them one by one.
* function dubbed "death dealer"
@@ -486,7 +562,7 @@ void killRunningProcessMac() {
*/
boolean killRunningprocessWin() {
try {
Runtime.getRuntime().exec("taskkill /F /IM GanglionHub.exe");
Runtime.getRuntime().exec("taskkill /F /IM OpenBCIHub.exe");
return true;
}
catch (Exception err) {
@@ -502,7 +578,7 @@ boolean killRunningprocessWin() {
int getProcessIdFromLineMac(String line) {
line = trim(line);
String[] components = line.split(" ");
return Integer.parseInt(components[0]); //<>//
return Integer.parseInt(components[0]);
}
void endProcess(int pid) {
@@ -528,7 +604,6 @@ void setupWidgetManager() {
}
void initSystem() {
println();
println();
println("=================================================");
@@ -542,9 +617,34 @@ void initSystem() {
//prepare data variables
verbosePrint("OpenBCI_GUI: initSystem: Preparing data variables...");
dataBuffX = new float[(int)(dataBuff_len_sec * get_fs_Hz_safe())];
if (eegDataSource == 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);
playbackData_table.removeColumn(0);
} catch (Exception e) {
println("OpenBCI_GUI: initSystem: could not open file for playback: " + playbackData_fname);
println(" : quitting...");
hub.killAndShowMsg("Could not open file for playback: " + playbackData_fname);
}
println("OpenBCI_GUI: initSystem: loading complete. " + playbackData_table.getRowCount() + " rows of data, which is " + round(float(playbackData_table.getRowCount())/getSampleRateSafe()) + " seconds of EEG data");
//removing first column of data from data file...the first column is a time index and not eeg data
}
verbosePrint("OpenBCI_GUI: initSystem: Initializing core data objects");
// Nfft = getNfftSafe();
nDataBackBuff = 3*(int)getSampleRateSafe();
dataPacketBuff = new DataPacket_ADS1299[nDataBackBuff]; // call the constructor here
nPointsPerUpdate = int(round(float(update_millis) * getSampleRateSafe()/ 1000.f));
dataBuffX = new float[(int)(dataBuff_len_sec * getSampleRateSafe())];
dataBuffY_uV = new float[nchan][dataBuffX.length];
dataBuffY_filtY_uV = new float[nchan][dataBuffX.length];
yLittleBuff = new float[nPointsPerUpdate];
yLittleBuff_uV = new float[nchan][nPointsPerUpdate]; //small buffer used to send data to the filters
auxBuff = new float[3][nPointsPerUpdate];
accelerometerBuff = new float[3][500]; // 500 points
for (int i=0; i<n_aux_ifEnabled; i++) {
for (int j=0; j<accelerometerBuff[0].length; j++) {
@@ -558,68 +658,76 @@ void initSystem() {
for (int i=0; i<nDataBackBuff; i++) {
dataPacketBuff[i] = new DataPacket_ADS1299(nchan, n_aux_ifEnabled);
}
dataProcessing = new DataProcessing(nchan, get_fs_Hz_safe());
dataProcessing_user = new DataProcessing_User(nchan, get_fs_Hz_safe());
dataProcessing = new DataProcessing(nchan, getSampleRateSafe());
dataProcessing_user = new DataProcessing_User(nchan, getSampleRateSafe());
//initialize the data
prepareData(dataBuffX, dataBuffY_uV, get_fs_Hz_safe());
prepareData(dataBuffX, dataBuffY_uV, getSampleRateSafe());
verbosePrint("OpenBCI_GUI: initSystem: -- Init 1 -- " + millis());
verbosePrint("OpenBCI_GUI: initSystem: Initializing FFT data objects");
//initialize the FFT objects
for (int Ichan=0; Ichan < nchan; Ichan++) {
verbosePrint("Init FFT Buff – "+Ichan);
fftBuff[Ichan] = new FFT(Nfft, get_fs_Hz_safe());
// verbosePrint("Init FFT Buff – " + Ichan);
fftBuff[Ichan] = new FFT(getNfftSafe(), getSampleRateSafe());
} //make the FFT objects
initializeFFTObjects(fftBuff, dataBuffY_uV, Nfft, get_fs_Hz_safe());
initializeFFTObjects(fftBuff, dataBuffY_uV, getNfftSafe(), getSampleRateSafe());
//prepare some signal processing stuff
//for (int Ichan=0; Ichan < nchan; Ichan++) { detData_freqDomain[Ichan] = new DetectionData_FreqDomain(); }
verbosePrint("OpenBCI_GUI: initSystem: -- Init 2 -- " + millis());
verbosePrint("OpenBCI_GUI: initSystem: Closing ControlPanel...");
controlPanel.close();
topNav.controlPanelCollapser.setIsActive(false);
verbosePrint("OpenBCI_GUI: initSystem: Initializing comms with hub....");
hub.changeState(STATE_COMINIT);
// hub.searchDeviceStop();
//prepare the source of the input data
switch (eegDataSource) {
case DATASOURCE_NORMAL_W_AUX:
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);
case DATASOURCE_CYTON:
int nEEDataValuesPerPacket = nchan;
boolean useAux = true;
if (cyton.getInterface() == INTERFACE_SERIAL) {
cyton = new Cyton(this, openBCI_portName, openBCI_baud, nEEDataValuesPerPacket, useAux, n_aux_ifEnabled, cyton.getInterface()); //this also starts the data transfer after XX seconds
} else {
if (hub.getWiFiStyle() == WIFI_DYNAMIC) {
cyton = new Cyton(this, wifi_portName, openBCI_baud, nEEDataValuesPerPacket, useAux, n_aux_ifEnabled, cyton.getInterface()); //this also starts the data transfer after XX seconds
} else {
cyton = new Cyton(this, wifi_ipAddress, openBCI_baud, nEEDataValuesPerPacket, useAux, n_aux_ifEnabled, cyton.getInterface()); //this also starts the data transfer after XX seconds
}
}
break;
case DATASOURCE_SYNTHETIC:
//do nothing
break;
case DATASOURCE_PLAYBACKFILE:
break;
case DATASOURCE_GANGLION:
if (ganglion.getInterface() == INTERFACE_HUB_BLE || ganglion.getInterface() == INTERFACE_HUB_BLED112) {
hub.connectBLE(ganglion_portName);
} else {
if (hub.getWiFiStyle() == WIFI_DYNAMIC) {
hub.connectWifi(wifi_portName);
} else {
hub.connectWifi(wifi_ipAddress);
}
}
break;
default:
break;
}
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())/get_fs_Hz_safe()) + " 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;
case DATASOURCE_GANGLION:
ganglion.connectBLE(ganglion_portName);
break;
default:
break;
}
verbosePrint("OpenBCI_GUI: initSystem: -- Init 3 -- " + millis());
if (abandonInit) {
haltSystem();
println("Failed to connect to data source...");
output("Failed to connect to data source...");
println("Failed to connect to data source... 1");
outputError("Failed to connect to data source fail point 1");
} else {
println(" 3a -- " + millis());
//initilize the GUI
@@ -627,6 +735,10 @@ void initSystem() {
topNav.initSecondaryNav();
println(" 3b -- " + millis());
//open data file
if (eegDataSource == DATASOURCE_CYTON) openNewLogFile(fileName); //open a new log file
if (eegDataSource == DATASOURCE_GANGLION) openNewLogFile(fileName); // println("open ganglion output file");
// wm = new WidgetManager(this);
setupWidgetManager();
@@ -634,27 +746,23 @@ void initSystem() {
println(" 3c -- " + millis());
// setupGUIWidgets(); //####
//open data file
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) openNewLogFile(fileName); //open a new log file
if (eegDataSource == DATASOURCE_GANGLION) openNewLogFile(fileName); // println("open ganglion output file");
nextPlayback_millis = millis(); //used for synthesizeData and readFromFile. This restarts the clock that keeps the playback at the right pace.
w_timeSeries.hsc.loadDefaultChannelSettings();
if (eegDataSource != DATASOURCE_GANGLION && eegDataSource != DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource != DATASOURCE_GANGLION && eegDataSource != DATASOURCE_CYTON) {
systemMode = SYSTEMMODE_POSTINIT; //tell system it's ok to leave control panel and start interfacing GUI
}
if (!abandonInit) {
println("WOOHOO!!!");
controlPanel.close();
} else {
haltSystem();
println("Failed to connect to data source...");
output("Failed to connect to data source...");
println("Failed to connect to data source... 2");
// output("Failed to connect to data source...");
}
} else {
haltSystem();
println("Failed to connect to data source...");
output("Failed to connect to data source...");
println("Failed to connect to data source... 3");
// output("Failed to connect to data source...");
}
}
@@ -669,22 +777,122 @@ void initSystem() {
* @description Useful function to get the correct sample rate based on data source
* @returns `float` - The frequency / sample rate of the data source
*/
float get_fs_Hz_safe() {
float getSampleRateSafe() {
if (eegDataSource == DATASOURCE_GANGLION) {
return ganglion.get_fs_Hz();
return ganglion.getSampleRate();
} else if (eegDataSource == DATASOURCE_CYTON){
return cyton.getSampleRate();
} else if (eegDataSource == DATASOURCE_PLAYBACKFILE) {
return playbackData_table.getSampleRate();
} else {
return openBCI.get_fs_Hz();
return 250;
}
}
/**
* @description Get the correct points of FFT based on sampling rate
* @returns `int` - Points of FFT. 125Hz, 200Hz, 250Hz -> 256points. 1000Hz -> 1024points. 1600Hz -> 2048 points.
*/
int getNfftSafe() {
int sampleRate = (int)getSampleRateSafe();
switch (sampleRate) {
case 1000:
return 1024;
case 1600:
return 2048;
case 125:
case 200:
case 250:
default:
return 256;
}
}
void startRunning() {
verbosePrint("startRunning...");
output("Data stream started.");
if (eegDataSource == DATASOURCE_GANGLION) {
if (ganglion != null) {
ganglion.startDataTransfer();
}
} else {
if (cyton != null) {
println("DEBUG: start data transfer");
cyton.startDataTransfer();
}
}
isRunning = true;
}
void stopRunning() {
// openBCI.changeState(0); //make sure it's no longer interpretting as binary
verbosePrint("OpenBCI_GUI: stopRunning: stop running...");
if (isRunning) {
output("Data stream stopped.");
}
if (eegDataSource == DATASOURCE_GANGLION) {
if (ganglion != null) {
ganglion.stopDataTransfer();
}
} else {
if (cyton != null) {
cyton.stopDataTransfer();
}
}
timeSinceStopRunning = millis(); //used as a timer to prevent misc. bytes from flooding serial...
isRunning = false;
// openBCI.changeState(0); //make sure it's no longer interpretting as binary
// systemMode = 0;
// closeLogFile();
}
//execute this function whenver the stop button is pressed
void stopButtonWasPressed() {
//toggle the data transfer state of the ADS1299...stop it or start it...
if (isRunning) {
verbosePrint("openBCI_GUI: stopButton was pressed...stopping data transfer...");
wm.setUpdating(false);
stopRunning();
topNav.stopButton.setString(topNav.stopButton_pressToStart_txt);
topNav.stopButton.setColorNotPressed(color(184, 220, 105));
if (eegDataSource == DATASOURCE_GANGLION && ganglion.isCheckingImpedance()) {
ganglion.impedanceStop();
w_ganglionImpedance.startStopCheck.but_txt = "Start Impedance Check";
}
} else { //not running
verbosePrint("openBCI_GUI: startButton was pressed...starting data transfer...");
wm.setUpdating(true);
startRunning();
topNav.stopButton.setString(topNav.stopButton_pressToStop_txt);
topNav.stopButton.setColorNotPressed(color(224, 56, 45));
nextPlayback_millis = millis(); //used for synthesizeData and readFromFile. This restarts the clock that keeps the playback at the right pace.
if (eegDataSource == DATASOURCE_GANGLION && ganglion.isCheckingImpedance()) {
ganglion.impedanceStop();
w_ganglionImpedance.startStopCheck.but_txt = "Start Impedance Check";
}
}
}
//halt the data collection
void haltSystem() {
println("openBCI_GUI: haltSystem: Halting system for reconfiguration of settings...");
if (initSystemButton.but_txt == "STOP SYSTEM") {
initSystemButton.but_txt = "START SYSTEM";
}
stopRunning(); //stop data transfer
if(cyton.isPortOpen()) {
if (w_pulsesensor.analogReadOn) {
hub.sendCommand("/0");
println("Stopping Analog Read to read accelerometer");
w_pulsesensor.analogModeButton.setString("Turn Analog Read On");
w_pulsesensor.analogReadOn = false;
}
}
//reset variables for data processing
curDataPacketInd = -1;
lastReadDataPacketInd = -1;
@@ -699,42 +907,64 @@ void haltSystem() {
//reset connect loadStrings
openBCI_portName = "N/A"; // Fixes inability to reconnect after halding JAM 1/2017
ganglion_portName = "";
ganglion_portName = "N/A";
wifi_portName = "N/A";
controlPanel.resetListItems();
// w_networking.clearCP5(); //closes all networking controllers
// stopDataTransfer(); // make sure to stop data transfer, if data is streaming and being drawn
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
if (eegDataSource == DATASOURCE_CYTON) {
closeLogFile(); //close log file
openBCI.closeSDandSerialPort();
cyton.closeSDandPort();
}
if (eegDataSource == DATASOURCE_GANGLION) {
if(ganglion.isCheckingImpedance()){
ganglion.impedanceStop();
w_ganglionImpedance.startStopCheck.but_txt = "Start Impedance Check";
}
closeLogFile(); //close log file
ganglion.disconnectBLE();
ganglion.closePort();
}
systemMode = SYSTEMMODE_PREINIT;
hub.changeState(STATE_NOCOM);
abandonInit = false;
bleList.items.clear();
wifiList.items.clear();
// TODO: Comment this back in
// if (ganglion.isBLE() || ganglion.isWifi() || cyton.isWifi()) {
// hub.searchDeviceStart();
// }
}
void delayedInit() {
// Initialize a plot
GPlot plot = new GPlot(this);
}
void systemUpdate() { // for updating data values and variables
if (isHubInitialized && isGanglionObjectInitialized == false && millis() - timeOfSetup >= 1500) {
ganglion = new OpenBCI_Ganglion(this);
println("Instantiating Ganglion object...");
isGanglionObjectInitialized = true;
if (isHubInitialized && isHubObjectInitialized == false && millis() - timeOfSetup >= 1500) {
hub = new Hub(this);
println("Instantiating hub object...");
isHubObjectInitialized = true;
thread("delayedInit");
}
//update the sync state with the OpenBCI hardware
if (openBCI.state == openBCI.STATE_NOCOM || openBCI.state == openBCI.STATE_COMINIT || openBCI.state == openBCI.STATE_SYNCWITHHARDWARE) {
openBCI.updateSyncState(sdSetting);
}
// //update the sync state with the OpenBCI hardware
// if (iSerial.get_state() == iSerial.STATE_NOCOM || iSerial.get_state() == iSerial.STATE_COMINIT || iSerial.get_state() == iSerial.STATE_SYNCWITHHARDWARE) {
// iSerial.updateSyncState(sdSetting);
// }
// if (hub.get_state() == STATE_NOCOM || hub.get_state() == STATE_COMINIT || hub.get_state() == STATE_SYNCWITHHARDWARE) {
// hub.updateSyncState(sdSetting);
// }
//prepare for updating the GUI
win_x = width;
win_y = height;
helpWidget.update();
if (systemMode == SYSTEMMODE_PREINIT) {
//updates while in system control panel before START SYSTEM
controlPanel.update();
@@ -819,19 +1049,36 @@ void systemUpdate() { // for updating data values and variables
playground.x = width; //reset the x for the playground...
}
wm.update();
playground.update();
if (!initSystemThreadLock) {
if (wm.isWMInitialized) {
wm.update();
playground.update();
}
}
}
}
void systemDraw() { //for drawing to the screen
// Conor's attempt at adjusting the GUI to be 2x in size for High DPI screens ... attempt failed
// int currentWidth;
// int currentHeight;
// if(!highDPI){
// currentWidth = width;
// currentHeight = height;
// }
// if(highDPI){
// pushMatrix();
// scale(2);
// }
//redraw the screen...not every time, get paced by when data is being plotted
background(bgColor); //clear the screen
noStroke();
//background(255); //clear the screen
if (systemMode >= SYSTEMMODE_POSTINIT) {
if (systemMode >= SYSTEMMODE_POSTINIT && !initSystemThreadLock) {
int drawLoopCounter_thresh = 100;
if ((redrawScreenNow) || (drawLoop_counter >= drawLoopCounter_thresh)) {
//if (drawLoop_counter >= drawLoopCounter_thresh) println("OpenBCI_GUI: redrawing based on loop counter...");
@@ -840,17 +1087,17 @@ void systemDraw() { //for drawing to the screen
//update the title of the figure;
switch (eegDataSource) {
case DATASOURCE_NORMAL_W_AUX:
case DATASOURCE_CYTON:
switch (outputDataSource) {
case OUTPUT_SOURCE_ODF:
surface.setTitle(int(frameRate) + " fps, Byte Count = " + openBCI_byteCount + ", bit rate = " + byteRate_perSec*8 + " bps" + ", " + int(float(fileoutput_odf.getRowsWritten())/get_fs_Hz_safe()) + " secs Saved, Writing to " + output_fname);
surface.setTitle(int(frameRate) + " fps, " + int(float(fileoutput_odf.getRowsWritten())/getSampleRateSafe()) + " secs Saved, Writing to " + output_fname);
break;
case OUTPUT_SOURCE_BDF:
surface.setTitle(int(frameRate) + " fps, Byte Count = " + openBCI_byteCount + ", bit rate = " + byteRate_perSec*8 + " bps" + ", " + int(fileoutput_bdf.getRecordsWritten()) + " secs Saved, Writing to " + output_fname);
surface.setTitle(int(frameRate) + " fps, " + int(fileoutput_bdf.getRecordsWritten()) + " secs Saved, Writing to " + output_fname);
break;
case OUTPUT_SOURCE_NONE:
default:
surface.setTitle(int(frameRate) + " fps, Byte Count = " + openBCI_byteCount + ", bit rate = " + byteRate_perSec*8 + " bps");
surface.setTitle(int(frameRate) + " fps");
break;
}
break;
@@ -858,7 +1105,7 @@ void systemDraw() { //for drawing to the screen
surface.setTitle(int(frameRate) + " fps, Using Synthetic EEG Data");
break;
case DATASOURCE_PLAYBACKFILE:
surface.setTitle(int(frameRate) + " fps, Playing " + int(float(currentTableRowIndex)/get_fs_Hz_safe()) + " of " + int(float(playbackData_table.getRowCount())/get_fs_Hz_safe()) + " secs, Reading from: " + playbackData_fname);
surface.setTitle(int(frameRate) + " fps, Playing " + int(float(currentTableRowIndex)/getSampleRateSafe()) + " of " + int(float(playbackData_table.getRowCount())/getSampleRateSafe()) + " secs, Reading from: " + playbackData_fname);
break;
case DATASOURCE_GANGLION:
surface.setTitle(int(frameRate) + " fps, Ganglion!");
@@ -923,8 +1170,7 @@ void systemDraw() { //for drawing to the screen
}
}
if ((openBCI.get_state() == openBCI.STATE_COMINIT || openBCI.get_state() == openBCI.STATE_SYNCWITHHARDWARE) && systemMode == SYSTEMMODE_PREINIT) {
if ((hub.get_state() == STATE_COMINIT || hub.get_state() == STATE_SYNCWITHHARDWARE) && systemMode == SYSTEMMODE_PREINIT) {
//make out blink the text "Initalizing GUI..."
pushStyle();
imageMode(CENTER);
@@ -957,6 +1203,14 @@ void systemDraw() { //for drawing to the screen
buttonHelpText.draw();
mouseOutOfBounds(); // to fix
// Conor's attempt at adjusting the GUI to be 2x in size for High DPI screens ... attempt failed
// if(highDPI){
// popMatrix();
// size(currentWidth*2, currentHeight*2);
// }
}
void introAnimation() {
@@ -977,7 +1231,7 @@ void introAnimation() {
textLeading(24);
fill(31, 69, 110, transparency);
textAlign(CENTER, CENTER);
text("OpenBCI GUI v2.1.2\nJanuary 2017", width/2, height/2 + width/9);
text("OpenBCI GUI v3.3.1\nMay 2018", width/2, height/2 + width/9);
}
//exit intro animation at t2
-187
Ver Arquivo
@@ -1,187 +0,0 @@
/////////////////////////////////////////////////////////////////////////////////
//
// PulseSensor_Widget is used to visiualze heartbeat data using a pulse sensor
//
// Created: Colin Fausnaught, September 2016
// Source Code by Joel Murphy
//
// Use '/' to toggle between accelerometer and pulse sensor.
////////////////////////////////////////////////////////////////////////////////
class PulseSensor_Widget{
//button for opening and closing
int x, y, w, h;
int parentContainer = 3;
color boxBG;
color strokeColor;
// Pulse Sensor Stuff
int count = 0;
int heart = 0;
int PulseBuffSize = 500;
int PulseWindowWidth;
int PulseWindowHeight;
int PulseWindowX;
int PulseWindowY;
color eggshell;
int[] PulseWaveY; // HOLDS HEARTBEAT WAVEFORM DATA
boolean rising;
//boolean OBCI_inited= false;
//OpenBCI_ADS1299 OBCI;
PulseSensor_Widget(PApplet parent) {
x = (int)container[parentContainer].x;
y = (int)container[parentContainer].y;
w = (int)container[parentContainer].w;
h = (int)container[parentContainer].h;
boxBG = bgColor;
strokeColor = color(138, 146, 153);
// Pulse Sensor Stuff
eggshell = color(255, 253, 248);
PulseWindowWidth = 500;
PulseWindowHeight = 183;
PulseWindowX = int(x)+5;
PulseWindowY = int(y)-10+int(h)/2;
PulseWaveY = new int[PulseBuffSize];
rising = true;
for (int i=0; i<PulseWaveY.length; i++){
PulseWaveY[i] = PulseWindowY + PulseWindowHeight/2; // initialize the pulse window data line to V/2
}
}
public void update() {
if (isRunning) {
if(synthesizeData){
count++;
}
if(frameCount%60 == 0){ heart = 15; } // fake the beat for now
if(openBCI.freshAuxValues){ heart = 4; }
heart--; // heart is used to time how long the heart graphic swells when your heart beats
heart = max(heart,0); // don't let the heart variable go into negative numbers
float upperClip = 800.0; // used to keep the pulse waveform within the pulse wave window
float lowerClip = 200.0;
if(synthesizeData){
if(rising){ // MAKE A SAW WAVE FOR TESTING
PulseWaveY[PulseWaveY.length-1]--; // place the new raw datapoint at the end of the array
if(PulseWaveY[PulseWaveY.length-1] == PulseWindowY){ rising = false; }
}else{
PulseWaveY[PulseWaveY.length-1]++; // place the new raw datapoint at the end of the array
if(PulseWaveY[PulseWaveY.length-1] == PulseWindowY+PulseWindowHeight){ rising = true; }
}
}else{
float sensorValue = float(openBCI.rawReceivedDataPacket.auxValues[0]);
PulseWaveY[PulseWaveY.length-1] =
int(map(sensorValue,lowerClip,upperClip,float(PulseWindowY+PulseWindowHeight),float(PulseWindowY)));
PulseWaveY[PulseWaveY.length-1] = constrain(PulseWaveY[PulseWaveY.length-1],PulseWindowY,PulseWindowY+PulseWindowHeight);
}
for (int i = 0; i < PulseWaveY.length-1; i++) { // move the pulse waveform by
PulseWaveY[i] = PulseWaveY[i+1];
}
}
}
public void draw() {
if(drawPulse){
// verbosePrint("yeaaa");
fill(boxBG);
stroke(strokeColor);
rect(x, y, w, h);
textFont(f4,24);
textAlign(LEFT, TOP);
fill(eggshell);
text("Pulse Sensor Amped", x + 10, y + 10);
textFont(f4,32);
if(synthesizeData){
text("BPM " + count, x+10, y+50);
text("IBI 760", x+10, y+100);
//text("Width "+ w, x+10, y+50);
//text("Height "+ h, x+10, y+70);
}else{
text("BPM " + openBCI.validAuxValues[1], x+10, y+40);
text("IBI " + openBCI.validAuxValues[2], x+10, y+100);
}
// heart shape
fill(250,0,0);
stroke(250,0,0);
strokeWeight(1);
if (heart > 0){ // if a beat happened recently,
strokeWeight(8); // make the heart pulse
}
translate(-35,0);
smooth(); // draw the heart with two bezier curves
bezier(x+w-60,y+40, x+w+20,y-30, x+w+40,y+130, x+w-60,y+140);
bezier(x+w-60,y+40, x+w-150,y-30, x+w-160,y+130, x+w-60,y+140);
translate(35,0);
strokeWeight(1); // reset the strokeWeight for next time
fill(eggshell); // pulse window background
stroke(eggshell);
rect(PulseWindowX,PulseWindowY,PulseWindowWidth,PulseWindowHeight);
stroke(255,0,0); // red is a good color for the pulse waveform
noFill();
beginShape(); // using beginShape() renders fast
for (int x = 0; x < PulseWaveY.length; x++) {
int xi = int(map(x, 0, PulseWaveY.length-1, 0, PulseWindowWidth-1));
vertex(PulseWindowX+xi, PulseWaveY[x]); //draw a line connecting the data points
}
endShape();
}
}
void screenResized(PApplet _parent, int _winX, int _winY) {
//when screen is resized...
//update position/size of Pulse Widget
x = (int)container[parentContainer].x;
y = (int)container[parentContainer].y;
w = (int)container[parentContainer].w;
h = (int)container[parentContainer].h;
PulseWindowX = int(x)+5;
PulseWindowY = int(y)-10+int(h)/2;
PulseWindowWidth = int(w)-10;
PulseWindowHeight = 183;
}
//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 mousePressed() {
verbosePrint("PulseSensor >> mousePressed()");
}
public void mouseReleased() {
verbosePrint("PulseSensor >> mouseReleased()");
}
}
+9 -3
Ver Arquivo
@@ -8,7 +8,7 @@
//
// Handles interactions between the radio system and OpenBCI systems.
// It is important to note that this is using Serial communication directly
// rather than the OpenBCI_ADS1299 class. I just found this easier to work
// rather than the Cyton class. I just found this easier to work
// with.
//
// Modified by Joel Murphy, January 2017
@@ -33,7 +33,7 @@ void autoconnect(){
try{
serialPort = serialPorts[i];
board = new Serial(this,serialPort,115200);
print("try "); print(i); print(" "); print(serialPort); println(" at 115200 baud");
print("blasss try "); print(i); print(" "); print(serialPort); println(" at 115200 baud");
output("Attempting to connect at 115200 baud to " + serialPort); // not working
delay(5000);
@@ -212,7 +212,7 @@ boolean connect_to_portName(RadioConfigBox rcConfig){
output("Attempting to open Serial/COM port: " + openBCI_portName);
try {
println("Radios_Config: connect_to_portName: attempting to open serial port: " + openBCI_portName);
serial_output = new Serial(this,openBCI_portName,openBCI_baud); //open the com port
serial_output = new Serial(this, openBCI_portName, openBCI_baud); //open the com port
serial_output.clear(); // clear anything in the com port's buffer
// portIsOpen = true;
println("Radios_Config: connect_to_portName: port is open!");
@@ -255,6 +255,8 @@ boolean connect_to_portName(RadioConfigBox rcConfig){
//==========================================
void system_status(RadioConfigBox rcConfig){
println("Radios_Config: system_status");
if(board == null){
if(!connect_to_portName(rcConfig)){
return;
@@ -275,6 +277,7 @@ void system_status(RadioConfigBox rcConfig){
//Scans through channels until a success message has been found
void scan_channels(RadioConfigBox rcConfig){
println("Radios_Config: scan_channels");
if(board == null){
if(!connect_to_portName(rcConfig)){
return;
@@ -302,6 +305,7 @@ void scan_channels(RadioConfigBox rcConfig){
//==========================================
void get_channel(RadioConfigBox rcConfig){
println("Radios_Config: get_channel");
if(board == null){
if(!connect_to_portName(rcConfig)){
return;
@@ -336,6 +340,7 @@ void get_channel(RadioConfigBox rcConfig){
//==========================================
void set_channel(RadioConfigBox rcConfig, int channel_number){
println("Radios_Config: set_channel");
if(board == null){
if(!connect_to_portName(rcConfig)){
return;
@@ -373,6 +378,7 @@ void set_channel(RadioConfigBox rcConfig, int channel_number){
//==========================================
void set_channel_over(RadioConfigBox rcConfig, int channel_number){
println("Radios_Config: set_ovr_channel");
if(board == null){
if(!connect_to_portName(rcConfig)){
return;
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 241 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 269 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 276 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 275 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 269 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 167 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 168 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 214 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 187 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 167 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 163 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 152 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 150 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 151 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 153 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 152 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 175 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 175 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 173 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 172 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 173 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 221 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 216 KiB

Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 245 KiB

+62 -6
Ver Arquivo
@@ -17,6 +17,9 @@ class TopNav {
Button controlPanelCollapser;
Button fpsButton;
Button highRezButton;
Button stopButton;
public final static String stopButton_pressToStop_txt = "Stop Data Stream";
public final static String stopButton_pressToStart_txt = "Start Data Stream";
@@ -44,16 +47,36 @@ class TopNav {
controlPanelCollapser.setIsActive(true);
controlPanelCollapser.isDropdownButton = true;
fpsButton = new Button(3+3+256, 3, 73, 26, "XX" + " fps", fontInfo.buttonLabel_size);
if(frameRateCounter==0){
fpsButton.setString("24 fps");
}
if(frameRateCounter==1){
fpsButton.setString("30 fps");
}
if(frameRateCounter==2){
fpsButton.setString("45 fps");
}
if(frameRateCounter==3){
fpsButton.setString("60 fps");
}
fpsButton.setFont(h3, 16);
fpsButton.setHelpText("If you're having latency issues, try adjusting the frame rate and see if it helps!");
highRezButton = new Button(3+3+256+73+3, 3, 26, 26, "XX", fontInfo.buttonLabel_size);
controlPanelCollapser.setFont(h3, 16);
//top right buttons from right to left
int butNum = 1;
tutorialsButton = new Button(width - 3*(butNum) - 80, 3, 80, 26, "Help", fontInfo.buttonLabel_size);
tutorialsButton.setFont(h3, 16);
tutorialsButton.setHelpText("Here you will find links to helpful online tutorials and getting started guides. Also, check out how to create custom widgets for the GUI!");
tutorialsButton.setHelpText("Click to find links to helpful online tutorials and getting started guides. Also, check out how to create custom widgets for the GUI!");
butNum = 2;
issuesButton = new Button(width - 3*(butNum) - 80 - tutorialsButton.but_dx, 3, 80, 26, "Issues", fontInfo.buttonLabel_size);
issuesButton.setHelpText("If you have suggestions or want to share a bug you've found, please create an issue on the GUI's Github repo!");
issuesButton.setURL("https://github.com/OpenBCI/OpenBCI_GUI_v2.0/issues");
issuesButton.setURL("https://github.com/OpenBCI/OpenBCI_GUI/issues");
issuesButton.setFont(h3, 16);
butNum = 3;
@@ -79,8 +102,11 @@ class TopNav {
filtNotchButton = new Button(7 + stopButton.but_dx, 35, 70, 26, "Notch\n" + dataProcessing.getShortNotchDescription(), fontInfo.buttonLabel_size);
filtNotchButton.setFont(p5, 12);
filtNotchButton.setHelpText("Here you can adjust the Notch Filter that is applied to all \"Filtered\" data.");
filtBPButton = new Button(11 + stopButton.but_dx + 70, 35, 70, 26, "BP Filt\n" + dataProcessing.getShortFilterDescription(), fontInfo.buttonLabel_size);
filtBPButton.setFont(p5, 12);
filtBPButton.setHelpText("Here you can adjust the Band Pass Filter that is applied to all \"Filtered\" data.");
//right to left in top right (secondary nav)
layoutButton = new Button(width - 3 - 60, 35, 60, 26, "Layout", fontInfo.buttonLabel_size);
@@ -93,11 +119,15 @@ class TopNav {
void updateNavButtonsBasedOnColorScheme(){
if(colorScheme == COLOR_SCHEME_DEFAULT){
controlPanelCollapser.setColorNotPressed(color(255));
fpsButton.setColorNotPressed(color(255));
highRezButton.setColorNotPressed(color(255));
issuesButton.setColorNotPressed(color(255));
shopButton.setColorNotPressed(color(255));
tutorialsButton.setColorNotPressed(color(255));
controlPanelCollapser.textColorNotActive = color(bgColor);
fpsButton.textColorNotActive = color(bgColor);
highRezButton.textColorNotActive = color(bgColor);
issuesButton.textColorNotActive = color(bgColor);
shopButton.textColorNotActive = color(bgColor);
tutorialsButton.textColorNotActive = color(bgColor);
@@ -115,11 +145,15 @@ class TopNav {
// tutorialsButton.setColorNotPressed(bgColor);
controlPanelCollapser.setColorNotPressed(openbciBlue);
fpsButton.setColorNotPressed(openbciBlue);
highRezButton.setColorNotPressed(openbciBlue);
issuesButton.setColorNotPressed(openbciBlue);
shopButton.setColorNotPressed(openbciBlue);
tutorialsButton.setColorNotPressed(openbciBlue);
controlPanelCollapser.textColorNotActive = color(255);
fpsButton.textColorNotActive = color(255);
highRezButton.textColorNotActive = color(255);
issuesButton.textColorNotActive = color(255);
shopButton.textColorNotActive = color(255);
tutorialsButton.textColorNotActive = color(255);
@@ -207,6 +241,8 @@ class TopNav {
}
controlPanelCollapser.draw();
fpsButton.draw();
// highRezButton.draw();
tutorialsButton.draw();
issuesButton.draw();
shopButton.draw();
@@ -273,6 +309,15 @@ class TopNav {
controlPanelCollapser.setIsActive(true);
}
if(fpsButton.isMouseHere()){
fpsButton.setIsActive(true);
}
// Conor's attempt at adjusting the GUI to be 2x in size for High DPI screens ... attempt failed
// if(highRezButton.isMouseHere()){
// highRezButton.setIsActive(true);
// }
if (tutorialsButton.isMouseHere()) {
tutorialsButton.setIsActive(true);
//toggle help/tutorial dropdown menu
@@ -292,6 +337,15 @@ class TopNav {
void mouseReleased(){
if (fpsButton.isMouseHere() && fpsButton.isActive()) {
toggleFrameRate();
}
// Conor's attempt at adjusting the GUI to be 2x in size for High DPI screens ... attempt failed
// if (highRezButton.isMouseHere() && highRezButton.isActive()) {
// toggleHighDPI();
// }
if (tutorialsButton.isMouseHere() && tutorialsButton.isActive()) {
tutorialSelector.toggleVisibility();
tutorialsButton.setIsActive(true);
@@ -325,6 +379,8 @@ class TopNav {
layoutButton.setIsActive(false);
}
fpsButton.setIsActive(false);
highRezButton.setIsActive(false);
tutorialsButton.setIsActive(false);
issuesButton.setIsActive(false);
shopButton.setIsActive(false);
@@ -693,14 +749,14 @@ class TutorialSelector{
int buttonNumber = 0;
Button tempTutorialButton = new Button(x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h, "Getting Started");
tempTutorialButton.setFont(p5, 12);
tempTutorialButton.setURL("http://docs.openbci.com/");
tempTutorialButton.setURL("http://docs.openbci.com/Tutorials/01-Cyton_Getting%20Started_Guide");
tutorialOptions.add(tempTutorialButton);
buttonNumber = 1;
h = margin*(buttonNumber+2) + b_h*(buttonNumber+1);
tempTutorialButton = new Button(x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h, "Testing Impedance");
tempTutorialButton.setFont(p5, 12);
tempTutorialButton.setURL("http://docs.openbci.com/hardware/01-OpenBCI_Hardware");
tempTutorialButton.setURL("http://docs.openbci.com/Tutorials/01-Cyton_Getting%20Started_Guide#cyton-getting-started-guide-v-connect-yourself-to-openbci-4-launch-the-gui-and-adjust-your-channel-settings");
tutorialOptions.add(tempTutorialButton);
buttonNumber = 2;
@@ -712,9 +768,9 @@ class TutorialSelector{
buttonNumber = 3;
h = margin*(buttonNumber+2) + b_h*(buttonNumber+1);
tempTutorialButton = new Button(x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h, "Building Widgets");
tempTutorialButton = new Button(x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h, "Building Custom Widgets");
tempTutorialButton.setFont(p5, 12);
tempTutorialButton.setURL("http://docs.openbci.com/software/01-OpenBCI_SDK");
tempTutorialButton.setURL("http://docs.openbci.com/Tutorials/15-Custom_Widgets");
tutorialOptions.add(tempTutorialButton);
}
+544
Ver Arquivo
@@ -0,0 +1,544 @@
////////////////////////////////////////////////////
//
// W_AnalogRead is used to visiualze analog voltage values
//
// Created: AJ Keller
//
//
///////////////////////////////////////////////////,
class W_AnalogRead extends Widget {
//to see all core variables/methods of the Widget class, refer to Widget.pde
//put your custom variables here...
int numAnalogReadBars;
float xF, yF, wF, hF;
float ts_padding;
float ts_x, ts_y, ts_h, ts_w; //values for actual time series chart (rectangle encompassing all analogReadBars)
float plotBottomWell;
float playbackWidgetHeight;
int analogReadBarHeight;
AnalogReadBar[] analogReadBars;
int[] xLimOptions = {1, 3, 5, 7}; // number of seconds (x axis of graph)
int[] yLimOptions = {0, 50, 100, 200, 400, 1000, 10000}; // 0 = Autoscale ... everything else is uV
boolean allowSpillover = false;
TextBox[] chanValuesMontage;
boolean showMontageValues;
private boolean visible = true;
private boolean updating = true;
int startingVertScaleIndex = 5;
int startingHoriztonalScaleIndex = 2;
private boolean hasScrollbar = false;
Button analogModeButton;
W_AnalogRead(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
//This is the protocol for setting up dropdowns.
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
addDropdown("VertScale_AR", "Vert Scale", Arrays.asList("Auto", "50", "100", "200", "400", "1000", "10000"), startingVertScaleIndex);
addDropdown("Duration_AR", "Window", Arrays.asList("1 sec", "3 sec", "5 sec", "7 sec"), startingHoriztonalScaleIndex);
// addDropdown("Spillover", "Spillover", Arrays.asList("False", "True"), 0);
//set number of anaolg reads
if (cyton.isWifi()) {
numAnalogReadBars = 2;
} else {
numAnalogReadBars = 3;
}
xF = float(x); //float(int( ... is a shortcut for rounding the float down... so that it doesn't creep into the 1px margin
yF = float(y);
wF = float(w);
hF = float(h);
plotBottomWell = 45.0; //this appears to be an arbitrary vertical space adds GPlot leaves at bottom, I derived it through trial and error
ts_padding = 10.0;
ts_x = xF + ts_padding;
ts_y = yF + (ts_padding);
ts_w = wF - ts_padding*2;
ts_h = hF - playbackWidgetHeight - plotBottomWell - (ts_padding*2);
analogReadBarHeight = int(ts_h/numAnalogReadBars);
analogReadBars = new AnalogReadBar[numAnalogReadBars];
//create our channel bars and populate our analogReadBars array!
for(int i = 0; i < numAnalogReadBars; i++){
println("init analog read bar " + i);
int analogReadBarY = int(ts_y) + i*(analogReadBarHeight); //iterate through bar locations
AnalogReadBar tempBar = new AnalogReadBar(_parent, i+5, int(ts_x), analogReadBarY, int(ts_w), analogReadBarHeight); //int _channelNumber, int _x, int _y, int _w, int _h
analogReadBars[i] = tempBar;
analogReadBars[i].adjustVertScale(yLimOptions[startingVertScaleIndex]);
analogReadBars[i].adjustTimeAxis(xLimOptions[startingHoriztonalScaleIndex]);
}
analogModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn Analog Read On", 12);
analogModeButton.setCornerRoundess((int)(navHeight-6));
analogModeButton.setFont(p6,10);
// analogModeButton.setStrokeColor((int)(color(150)));
// analogModeButton.setColorNotPressed(openbciBlue);
analogModeButton.setColorNotPressed(color(57,128,204));
analogModeButton.textColorNotActive = color(255);
// analogModeButton.setStrokeColor((int)(color(138, 182, 229, 100)));
analogModeButton.hasStroke(false);
// analogModeButton.setColorNotPressed((int)(color(138, 182, 229)));
if (cyton.isWifi()) {
analogModeButton.setHelpText("Click this button to activate/deactivate the analog read of your Cyton board from A5(D11) and A6(D12)");
} else {
analogModeButton.setHelpText("Click this button to activate/deactivate the analog read of your Cyton board from A5(D11), A6(D12) and A7(D13)");
}
}
public boolean isVisible() {
return visible;
}
public boolean isUpdating() {
return updating;
}
public void setVisible(boolean _visible) {
visible = _visible;
}
public void setUpdating(boolean _updating) {
updating = _updating;
}
void update(){
if(visible && updating){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
//put your code here...
//update channel bars ... this means feeding new EEG data into plots
for(int i = 0; i < numAnalogReadBars; i++){
analogReadBars[i].update();
}
}
}
void draw(){
if(visible){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
//put your code here... //remember to refer to x,y,w,h which are the positioning variables of the Widget class
pushStyle();
//draw channel bars
analogModeButton.draw();
if (cyton.getBoardMode() != BOARD_MODE_ANALOG) {
analogModeButton.setString("Turn Analog Read On");
} else {
analogModeButton.setString("Turn Analog Read Off");
for(int i = 0; i < numAnalogReadBars; i++){
analogReadBars[i].draw();
}
}
popStyle();
}
}
void screenResized(){
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
//put your code here...
xF = float(x); //float(int( ... is a shortcut for rounding the float down... so that it doesn't creep into the 1px margin
yF = float(y);
wF = float(w);
hF = float(h);
ts_x = xF + ts_padding;
ts_y = yF + (ts_padding);
ts_w = wF - ts_padding*2;
ts_h = hF - playbackWidgetHeight - plotBottomWell - (ts_padding*2);
analogReadBarHeight = int(ts_h/numAnalogReadBars);
for(int i = 0; i < numAnalogReadBars; i++){
int analogReadBarY = int(ts_y) + i*(analogReadBarHeight); //iterate through bar locations
analogReadBars[i].screenResized(int(ts_x), analogReadBarY, int(ts_w), analogReadBarHeight); //bar x, bar y, bar w, bar h
}
analogModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
if (analogModeButton.isMouseHere()) {
analogModeButton.setIsActive(true);
}
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
if(analogModeButton.isActive && analogModeButton.isMouseHere()){
// println("analogModeButton...");
if(cyton.isPortOpen()) {
if (cyton.getBoardMode() != BOARD_MODE_ANALOG) {
cyton.setBoardMode(BOARD_MODE_ANALOG);
if (cyton.isWifi()) {
output("Starting to read analog inputs on pin marked A5 (D11) and A6 (D12)");
} else {
output("Starting to read analog inputs on pin marked A5 (D11), A6 (D12) and A7 (D13)");
}
} else {
cyton.setBoardMode(BOARD_MODE_DEFAULT);
output("Starting to read accelerometer");
}
}
}
analogModeButton.setIsActive(false);
}
};
//These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected
void VertScale_AR(int n) {
if (n==0) { //autoscale
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(0);
}
} else if(n==1) { //50uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(50);
}
} else if(n==2) { //100uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(100);
}
} else if(n==3) { //200uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(200);
}
} else if(n==4) { //400uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(400);
}
} else if(n==5) { //1000uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(1000);
}
} else if(n==6) { //10000uV
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustVertScale(10000);
}
}
closeAllDropdowns();
}
//triggered when there is an event in the LogLin Dropdown
void Duration_AR(int n) {
// println("adjust duration to: ");
if(n==0){ //set time series x axis to 1 secconds
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustTimeAxis(1);
}
} else if(n==1){ //set time series x axis to 3 secconds
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustTimeAxis(3);
}
} else if(n==2){ //set to 5 seconds
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustTimeAxis(5);
}
} else if(n==3){ //set to 7 seconds (max due to arry size ... 2000 total packets saved)
for(int i = 0; i < w_analogRead.numAnalogReadBars; i++){
w_analogRead.analogReadBars[i].adjustTimeAxis(7);
}
}
closeAllDropdowns();
}
//========================================================================================================================
// Analog Voltage BAR CLASS -- Implemented by Analog Read Widget Class
//========================================================================================================================
//this class contains the plot and buttons for a single channel of the Time Series widget
//one of these will be created for each channel (4, 8, or 16)
class AnalogReadBar{
int analogInputPin;
int auxValuesPosition;
String analogInputString;
int x, y, w, h;
boolean isOn; //true means data is streaming and channel is active on hardware ... this will send message to OpenBCI Hardware
GPlot plot; //the actual grafica-based GPlot that will be rendering the Time Series trace
GPointsArray analogReadPoints;
int nPoints;
int numSeconds;
float timeBetweenPoints;
color channelColor; //color of plot trace
boolean isAutoscale; //when isAutoscale equals true, the y-axis of each channelBar will automatically update to scale to the largest visible amplitude
int autoScaleYLim = 0;
TextBox analogValue;
TextBox analogPin;
TextBox digitalPin;
boolean drawAnalogValue;
int lastProcessedDataPacketInd = 0;
int[] analogReadData;
AnalogReadBar(PApplet _parent, int _analogInputPin, int _x, int _y, int _w, int _h){ // channel number, x/y location, height, width
analogInputPin = _analogInputPin;
int digitalPinNum = 0;
if (analogInputPin == 7) {
auxValuesPosition = 2;
digitalPinNum = 13;
} else if (analogInputPin == 6) {
auxValuesPosition = 1;
digitalPinNum = 12;
} else {
analogInputPin = 5;
auxValuesPosition = 0;
digitalPinNum = 11;
}
analogInputString = str(analogInputPin);
isOn = true;
x = _x;
y = _y;
w = _w;
h = _h;
numSeconds = 5;
plot = new GPlot(_parent);
plot.setPos(x + 36 + 4, y);
plot.setDim(w - 36 - 4, h);
plot.setMar(0f, 0f, 0f, 0f);
plot.setLineColor((int)channelColors[(auxValuesPosition)%8]);
plot.setXLim(-3.2,-2.9);
plot.setYLim(-200,200);
plot.setPointSize(2);
plot.setPointColor(0);
if (cyton.isWifi()) {
if(auxValuesPosition == 1){
plot.getXAxis().setAxisLabelText("Time (s)");
}
} else {
if(auxValuesPosition == 2){
plot.getXAxis().setAxisLabelText("Time (s)");
}
}
nPoints = nPointsBasedOnDataSource();
analogReadData = new int[nPoints];
analogReadPoints = new GPointsArray(nPoints);
timeBetweenPoints = (float)numSeconds / (float)nPoints;
for (int i = 0; i < nPoints; i++) {
float time = -(float)numSeconds + (float)i*timeBetweenPoints;
float analog_value = 0.0; //0.0 for all points to start
GPoint tempPoint = new GPoint(time, analog_value);
analogReadPoints.set(i, tempPoint);
}
plot.setPoints(analogReadPoints); //set the plot with 0.0 for all analogReadPoints to start
analogValue = new TextBox("t", x + 36 + 4 + (w - 36 - 4) - 2, y + h);
analogValue.textColor = color(bgColor);
analogValue.alignH = RIGHT;
// analogValue.alignV = TOP;
analogValue.drawBackground = true;
analogValue.backgroundColor = color(255,255,255,125);
analogPin = new TextBox("A" + analogInputString, x+3, y + h);
analogPin.textColor = color(bgColor);
analogPin.alignH = CENTER;
digitalPin = new TextBox("(D" + digitalPinNum + ")", x+3, y + h + 12);
digitalPin.textColor = color(bgColor);
digitalPin.alignH = CENTER;
drawAnalogValue = true;
}
void update(){
//update the voltage value text string
String fmt; float val;
//update the voltage values
val = hub.validAccelValues[auxValuesPosition];
analogValue.string = String.format(getFmt(val),val);
// update data in plot
updatePlotPoints();
if(isAutoscale){
autoScale();
}
}
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;
}
void updatePlotPoints(){
// update data in plot
int numSamplesToProcess = curDataPacketInd - lastProcessedDataPacketInd;
if (numSamplesToProcess < 0) {
numSamplesToProcess += dataPacketBuff.length;
}
// Shift internal ring buffer numSamplesToProcess
if (numSamplesToProcess > 0) {
for(int i = 0; i < analogReadData.length - numSamplesToProcess; i++){
analogReadData[i] = analogReadData[i + numSamplesToProcess];
}
}
// for each new sample
int samplesProcessed = 0;
while (samplesProcessed < numSamplesToProcess) {
lastProcessedDataPacketInd++;
// Watch for wrap around
if (lastProcessedDataPacketInd > dataPacketBuff.length - 1) {
lastProcessedDataPacketInd = 0;
}
int voltage = dataPacketBuff[lastProcessedDataPacketInd].auxValues[auxValuesPosition];
analogReadData[analogReadData.length - numSamplesToProcess + samplesProcessed] = voltage; //<>//
samplesProcessed++;
}
if (numSamplesToProcess > 0) {
for (int i = 0; i < nPoints; i++) {
float timey = -(float)numSeconds + (float)i*timeBetweenPoints;
float voltage = analogReadData[i];
GPoint tempPoint = new GPoint(timey, voltage);
analogReadPoints.set(i, tempPoint);
}
plot.setPoints(analogReadPoints); //reset the plot with updated analogReadPoints
}
}
void draw(){
pushStyle();
//draw plot
stroke(31,69,110, 50);
fill(color(125,30,12,30));
rect(x + 36 + 4, y, w - 36 - 4, h);
plot.beginDraw();
plot.drawBox(); // we won't draw this eventually ...
plot.drawGridLines(0);
plot.drawLines();
// plot.drawPoints();
// plot.drawYAxis();
if (cyton.isWifi()) {
if(auxValuesPosition == 1){ //only draw the x axis label on the bottom channel bar
plot.drawXAxis();
plot.getXAxis().draw();
}
} else {
if(auxValuesPosition == 2){ //only draw the x axis label on the bottom channel bar
plot.drawXAxis();
plot.getXAxis().draw();
}
}
plot.endDraw();
if(drawAnalogValue){
analogValue.draw();
analogPin.draw();
digitalPin.draw();
}
popStyle();
}
int nPointsBasedOnDataSource(){
return numSeconds * (int)getSampleRateSafe();
}
void adjustTimeAxis(int _newTimeSize){
numSeconds = _newTimeSize;
plot.setXLim(-_newTimeSize,0);
nPoints = nPointsBasedOnDataSource();
analogReadPoints = new GPointsArray(nPoints);
if(_newTimeSize > 1){
plot.getXAxis().setNTicks(_newTimeSize); //sets the number of axis divisions...
}else{
plot.getXAxis().setNTicks(10);
}
if (w_analogRead != null) {
if(w_analogRead.isUpdating()){
updatePlotPoints();
}
}
// println("New X axis = " + _newTimeSize);
}
void adjustVertScale(int _vertScaleValue){
if(_vertScaleValue == 0){
isAutoscale = true;
} else {
isAutoscale = false;
plot.setYLim(-_vertScaleValue, _vertScaleValue);
}
}
void autoScale(){
autoScaleYLim = 0;
for(int i = 0; i < nPoints; i++){
if(int(abs(analogReadPoints.getY(i))) > autoScaleYLim){
autoScaleYLim = int(abs(analogReadPoints.getY(i)));
}
}
plot.setYLim(-autoScaleYLim, autoScaleYLim);
}
void screenResized(int _x, int _y, int _w, int _h){
x = _x;
y = _y;
w = _w;
h = _h;
//reposition & resize the plot
plot.setPos(x + 36 + 4, y);
plot.setDim(w - 36 - 4, h);
analogValue.x = x + 36 + 4 + (w - 36 - 4) - 2;
analogValue.y = y + h;
analogPin.x = x + 14;
analogPin.y = y + int(h/2.0);
digitalPin.x = analogPin.x;
digitalPin.y = analogPin.y + 12;
}
};
+137
Ver Arquivo
@@ -0,0 +1,137 @@
////////////////////////////////////////////////////
//
// W_BandPowers.pde
//
// This is a band power visualization widget!
// (Couldn't think up more)
// This is for visualizing the power of each brainwave band: delta, theta, alpha, beta, gamma
// Averaged over all channels
//
// Created by: Wangshu Sun, May 2017
//
///////////////////////////////////////////////////,
class W_BandPower extends Widget {
GPlot plot3;
String bands[] = {"DELTA", "THETA", "ALPHA", "BETA", "GAMMA"};
W_BandPower(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
//This is the protocol for setting up dropdowns.
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
// addDropdown("Dropdown1", "Drop 1", Arrays.asList("A", "B"), 0);
// addDropdown("Dropdown2", "Drop 2", Arrays.asList("C", "D", "E"), 1);
// addDropdown("Dropdown3", "Drop 3", Arrays.asList("F", "G", "H", "I"), 3);
// Setup for the third plot
plot3 = new GPlot(_parent, x, y-navHeight, w, h+navHeight);
plot3.setPos(x, y);
plot3.setDim(w, h);
plot3.setLogScale("y");
plot3.setYLim(0.1, 100);
plot3.setXLim(0, 5);
plot3.getYAxis().setNTicks(9);
plot3.getTitle().setTextAlignment(LEFT);
plot3.getTitle().setRelativePos(0);
plot3.getYAxis().getAxisLabel().setText("(uV)^2 / Hz per channel");
plot3.getYAxis().getAxisLabel().setTextAlignment(RIGHT);
plot3.getYAxis().getAxisLabel().setRelativePos(1);
// plot3.setPoints(points3);
plot3.startHistograms(GPlot.VERTICAL);
plot3.getHistogram().setDrawLabels(true);
//plot3.getHistogram().setRotateLabels(true);
plot3.getHistogram().setBgColors(new color[] {
color(0, 0, 255, 50), color(0, 0, 255, 100),
color(0, 0, 255, 150), color(0, 0, 255, 200)
}
);
}
void update(){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
GPointsArray points3 = new GPointsArray(dataProcessing.headWidePower.length);
points3.add(DELTA + 0.5, dataProcessing.headWidePower[DELTA], "DELTA");
points3.add(THETA + 0.5, dataProcessing.headWidePower[THETA], "THETA");
points3.add(ALPHA + 0.5, dataProcessing.headWidePower[ALPHA], "ALPHA");
points3.add(BETA + 0.5, dataProcessing.headWidePower[BETA], "BETA");
points3.add(GAMMA + 0.5, dataProcessing.headWidePower[GAMMA], "GAMMA");
plot3.setPoints(points3);
plot3.getTitle().setText("Band Power");
}
void draw(){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
//put your code here... //remember to refer to x,y,w,h which are the positioning variables of the Widget class
// Draw the third plot
plot3.beginDraw();
plot3.drawBackground();
plot3.drawBox();
plot3.drawYAxis();
plot3.drawTitle();
plot3.drawHistograms();
plot3.endDraw();
}
void screenResized(){
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
//put your code here...
plot3.setPos(x, y-navHeight);//update position
plot3.setOuterDim(w, h+navHeight);//update dimensions
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
//put your code here...
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
}
//add custom functions here
void customFunction(){
//this is a fake function... replace it with something relevant to this widget
}
};
// //These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected
// void Dropdown1(int n){
// println("Item " + (n+1) + " selected from Dropdown 1");
// if(n==0){
// //do this
// } else if(n==1){
// //do this instead
// }
//
// closeAllDropdowns(); // do this at the end of all widget-activated functions to ensure proper widget interactivity ... we want to make sure a click makes the menu close
// }
//
// void Dropdown2(int n){
// println("Item " + (n+1) + " selected from Dropdown 2");
// closeAllDropdowns();
// }
//
// void Dropdown3(int n){
// println("Item " + (n+1) + " selected from Dropdown 3");
// closeAllDropdowns();
// }
+334
Ver Arquivo
@@ -0,0 +1,334 @@
////////////////////////////////////////////////////
//
// W_DigitalRead is used to visiualze digital input values
//
// Created: AJ Keller
//
//
///////////////////////////////////////////////////,
class W_DigitalRead extends Widget {
//to see all core variables/methods of the Widget class, refer to Widget.pde
//put your custom variables here...
int numDigitalReadDots;
float xF, yF, wF, hF;
int dot_padding;
float dot_x, dot_y, dot_h, dot_w; //values for actual time series chart (rectangle encompassing all digitalReadDots)
float plotBottomWell;
float playbackWidgetHeight;
int digitalReadDotHeight;
DigitalReadDot[] digitalReadDots;
TextBox[] chanValuesMontage;
boolean showMontageValues;
private boolean visible = true;
private boolean updating = true;
Button digitalModeButton;
W_DigitalRead(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
//This is the protocol for setting up dropdowns.
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
//set number of digital reads
if (cyton.isWifi()) {
numDigitalReadDots = 3;
} else {
numDigitalReadDots = 5;
}
xF = float(x); //float(int( ... is a shortcut for rounding the float down... so that it doesn't creep into the 1px margin
yF = float(y);
wF = float(w);
hF = float(h);
dot_padding = 10;
dot_x = xF + dot_padding;
dot_y = yF + (dot_padding);
dot_w = wF - dot_padding*2;
dot_h = hF - playbackWidgetHeight - plotBottomWell - (dot_padding*2);
digitalReadDotHeight = int(dot_h/numDigitalReadDots);
digitalReadDots = new DigitalReadDot[numDigitalReadDots];
//create our channel bars and populate our digitalReadDots array!
for(int i = 0; i < numDigitalReadDots; i++){
int digitalReadDotY = int(dot_y) + i*(digitalReadDotHeight); //iterate through bar locations
int digitalReadDotX = int(dot_x) + i*(digitalReadDotHeight); //iterate through bar locations
int digitalPin = 0;
if (i == 0) {
digitalPin = 11;
} else if (i == 1) {
digitalPin = 12;
} else if (i == 2) {
if (cyton.isWifi()) {
digitalPin = 17;
} else {
digitalPin = 13;
}
} else if (i == 3) {
digitalPin = 17;
} else {
digitalPin = 18;
}
DigitalReadDot tempDot = new DigitalReadDot(_parent, digitalPin, digitalReadDotX, digitalReadDotY, int(dot_w), digitalReadDotHeight, dot_padding);
digitalReadDots[i] = tempDot;
}
digitalModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn Analog Read On", 12);
digitalModeButton.setCornerRoundess((int)(navHeight-6));
digitalModeButton.setFont(p6,10);
digitalModeButton.setColorNotPressed(color(57,128,204));
digitalModeButton.textColorNotActive = color(255);
digitalModeButton.hasStroke(false);
if (cyton.isWifi()) {
digitalModeButton.setHelpText("Click this button to activate/deactivate digital reading on the Cyton D11, D12, and D17");
} else {
digitalModeButton.setHelpText("Click this button to activate/deactivate digital reading on the Cyton D11, D12, D13, D17 and D18");
}
}
public boolean isVisible() {
return visible;
}
public boolean isUpdating() {
return updating;
}
public void setVisible(boolean _visible) {
visible = _visible;
}
public void setUpdating(boolean _updating) {
updating = _updating;
}
void update(){
if(visible && updating){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
//put your code here...
//update channel bars ... this means feeding new EEG data into plots
for(int i = 0; i < numDigitalReadDots; i++){
digitalReadDots[i].update();
}
}
}
void draw(){
if(visible){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
//put your code here... //remember to refer to x,y,w,h which are the positioning variables of the Widget class
pushStyle();
//draw channel bars
digitalModeButton.draw();
if (cyton.getBoardMode() != BOARD_MODE_DIGITAL) {
digitalModeButton.setString("Turn Digital Read On");
} else {
digitalModeButton.setString("Turn Digital Read Off");
for(int i = 0; i < numDigitalReadDots; i++){
digitalReadDots[i].draw();
}
}
popStyle();
}
}
void screenResized(){
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
//put your code here...
xF = float(x); //float(int( ... is a shortcut for rounding the float down... so that it doesn't creep into the 1px margin
yF = float(y);
wF = float(w);
hF = float(h);
// println("w_digitalRead: screenResized: x: " + x + " y: " + y + " w: "+ w + " h: " + h + " navBarHeight: " + navBarHeight);
if (wF > hF) {
digitalReadDotHeight = int(hF/(numDigitalReadDots+1));
} else {
digitalReadDotHeight = int(wF/(numDigitalReadDots+1));
}
if (numDigitalReadDots == 3) {
digitalReadDots[0].screenResized(x+int(wF*(1.0/3.0)), y+int(hF*(1.0/3.0)), digitalReadDotHeight, digitalReadDotHeight); //bar x, bar y, bar w, bar h
digitalReadDots[1].screenResized(x+int(wF/2), y+int(hF/2), digitalReadDotHeight, digitalReadDotHeight); //bar x, bar y, bar w, bar h
digitalReadDots[2].screenResized(x+int(wF*(2.0/3.0)), y+int(hF*(2.0/3.0)), digitalReadDotHeight, digitalReadDotHeight); //bar x, bar y, bar w, bar h
} else {
int y_pad = y + dot_padding;
digitalReadDots[0].screenResized(x+int(wF*(1.0/8.0)), y_pad+int(hF*(1.0/8.0)), digitalReadDotHeight, digitalReadDotHeight);
digitalReadDots[2].screenResized(x+int(wF/2), y_pad+int(hF/2), digitalReadDotHeight, digitalReadDotHeight);
digitalReadDots[4].screenResized(x+int(wF*(7.0/8.0)), y_pad+int(hF*(7.0/8.0)), digitalReadDotHeight, digitalReadDotHeight);
digitalReadDots[1].screenResized(digitalReadDots[0].DotX+int(wF*(3.0/16.0)), digitalReadDots[0].DotY+int(hF*(3.0/16.0)), digitalReadDotHeight, digitalReadDotHeight);
digitalReadDots[3].screenResized(digitalReadDots[2].DotX+int(wF*(3.0/16.0)), digitalReadDots[2].DotY+int(hF*(3.0/16.0)), digitalReadDotHeight, digitalReadDotHeight);
}
digitalModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
if (digitalModeButton.isMouseHere()) {
digitalModeButton.setIsActive(true);
}
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
if(digitalModeButton.isActive && digitalModeButton.isMouseHere()){
// println("digitalModeButton...");
if(cyton.isPortOpen()) {
if (cyton.getBoardMode() != BOARD_MODE_DIGITAL) {
cyton.setBoardMode(BOARD_MODE_DIGITAL);
if (cyton.isWifi()) {
output("Starting to read digital inputs on pin marked D11, D12 and D17");
} else {
output("Starting to read digital inputs on pin marked D11, D12, D13, D17 and D18");
}
} else {
cyton.setBoardMode(BOARD_MODE_DEFAULT);
output("Starting to read accelerometer");
}
}
}
digitalModeButton.setIsActive(false);
}
};
//========================================================================================================================
// Analog Voltage BAR CLASS -- Implemented by Analog Read Widget Class
//========================================================================================================================
//this class contains the plot and buttons for a single channel of the Time Series widget
//one of these will be created for each channel (4, 8, or 16)
class DigitalReadDot{
int digitalInputPin;
int digitalInputVal;
String digitalInputString;
int padding;
boolean isOn; //true means data is streaming and channel is active on hardware ... this will send message to OpenBCI Hardware
TextBox digitalValue;
TextBox digitalPin;
boolean drawDigitalValue;
color dotStroke = #d2d2d2;
color dot0Fill = #f5f5f5;
color dot1Fill = #f5f5f5;
color val0Fill = #000000;
color val1Fill = #ffffff;
int DotX;
int DotY;
int DotWidth;
int DotHeight;
float DotCorner;
DigitalReadDot(PApplet _parent, int _digitalInputPin, int _x, int _y, int _w, int _h, int _padding){ // channel number, x/y location, height, width
digitalInputPin = _digitalInputPin;
digitalInputString = str(digitalInputPin);
digitalInputVal = 0;
isOn = true;
if (digitalInputPin == 11) {
dot1Fill = channelColors[0];
} else if (digitalInputPin == 12) {
dot1Fill = channelColors[1];
} else if (digitalInputPin == 13) {
dot1Fill = channelColors[2];
} else if (digitalInputPin == 17) {
dot1Fill = channelColors[3];
} else { // 18
dot1Fill = channelColors[4];
}
DotX = _x;
DotY = _y;
DotWidth = _w;
DotHeight = _h;
padding = _padding;
digitalValue = new TextBox("", DotX, DotY);
digitalValue.textColor = color(val0Fill);
digitalValue.alignH = CENTER;
digitalValue.alignV = CENTER;
digitalPin = new TextBox("D" + digitalInputString, DotX, DotY - DotWidth);
digitalPin.textColor = color(bgColor);
digitalPin.alignH = CENTER;
// digitalPin.alignV = CENTER;
drawDigitalValue = true;
}
void update(){
//update the voltage values
if (digitalInputPin == 11) {
digitalInputVal = (hub.validAccelValues[0] & 0xFF00) >> 8;
} else if (digitalInputPin == 12) {
digitalInputVal = hub.validAccelValues[0] & 0xFF;
} else if (digitalInputPin == 13) {
digitalInputVal = (hub.validAccelValues[1] & 0xFF00) >> 8;
} else if (digitalInputPin == 17) {
digitalInputVal = hub.validAccelValues[1] & 0xFF;
} else { // 18
digitalInputVal = hub.validAccelValues[2];
}
digitalValue.string = String.format("%d", digitalInputVal);
}
void draw(){
pushStyle();
//draw plot
if (digitalInputVal == 1) {
fill(dot1Fill);
digitalValue.textColor = val1Fill;
} else {
fill(dot0Fill);
digitalValue.textColor = val0Fill;
}
stroke(dotStroke);
ellipse(DotX, DotY, DotWidth, DotHeight);
if(drawDigitalValue){
digitalValue.draw();
digitalPin.draw();
}
popStyle();
}
void screenResized(int _x, int _y, int _w, int _h){
DotX = _x;
DotY = _y;
DotWidth = _w;
DotHeight = _h;
DotCorner = (sqrt(2)*DotWidth/2)/2;
// println("DigitalReadDot: " + digitalInputPin + " screenResized: DotX: " + DotX + " DotY: " + DotY + " DotWidth: "+ DotWidth + " DotHeight: " + DotHeight);
digitalPin.x = DotX;
digitalPin.y = DotY - int(DotWidth/2.0);
digitalValue.x = DotX;
digitalValue.y = DotY;
}
};
+16 -13
Ver Arquivo
@@ -10,8 +10,9 @@
//
///////////////////////////////////////////////////
//fft constants
int Nfft = 256; //set resolution of the FFT. Use N=256 for normal, N=512 for MU waves
//fft global variables
// int Nfft; //125Hz, 200Hz, 250Hz -> 256points. 1000Hz -> 1024points. 1600Hz -> 2048 points. //prev: Use N=256 for normal, N=512 for MU waves
// float fs_Hz; // AJ Keller removed because shall get sample rate at runtime
FFT[] fftBuff = new FFT[nchan]; //from the minim library
boolean isFFTFiltered = true; //yes by default ... this is used in dataProcessing.pde to determine which uV array feeds the FFT calculation
@@ -41,13 +42,13 @@ class W_fft extends Widget {
(int)color(162, 82, 49)
};
int[] xLimOptions = {20, 40, 60, 120};
int[] xLimOptions = {20, 40, 60, 100, 120, 250, 500, 800};
int[] yLimOptions = {10, 50, 100, 1000};
int xLim = xLimOptions[2]; //maximum value of x axis ... in this case 20 Hz, 40 Hz, 60 Hz, 120 Hz
int xMax = xLimOptions[3];
int FFT_indexLim = int(1.0*xMax*(Nfft/get_fs_Hz_safe())); // maxim value of FFT index
int yLim = 100; //maximum value of y axis ... 100 uV
int xMax = xLimOptions[xLimOptions.length-1]; //maximum possible frequency in FFT
int FFT_indexLim = int(1.0*xMax*(getNfftSafe()/getSampleRateSafe())); // maxim value of FFT index
int yLim = yLimOptions[2]; //maximum value of y axis ... 100 uV
W_fft(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
@@ -55,14 +56,14 @@ class W_fft extends Widget {
//This is the protocol for setting up dropdowns.
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
addDropdown("MaxFreq", "Max Freq", Arrays.asList("20 Hz", "40 Hz", "60 Hz", "120 Hz"), 2);
addDropdown("MaxFreq", "Max Freq", Arrays.asList("20 Hz", "40 Hz", "60 Hz", "100 Hz", "120 Hz", "250 Hz", "500 Hz", "800 Hz"), 2);
addDropdown("VertScale", "Max uV", Arrays.asList("10 uV", "50 uV", "100 uV", "1000 uV"), 2);
addDropdown("LogLin", "Log/Lin", Arrays.asList("Log", "Linear"), 0);
addDropdown("Smoothing", "Smooth", Arrays.asList("0.0", "0.5", "0.75", "0.9", "0.95", "0.98"), smoothFac_ind); //smoothFac_ind is a global variable at the top of W_headPlot.pde
addDropdown("UnfiltFilt", "Filters?", Arrays.asList("Filtered", "Unfilt."), 0);
fft_points = new GPointsArray[nchan];
println(fft_points.length);
// println("fft_points.length: " + fft_points.length);
initializeFFTPlot(_parent);
}
@@ -106,6 +107,8 @@ class W_fft extends Widget {
void update(){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
float sr = getSampleRateSafe();
int nfft = getNfftSafe();
//put your code here...
//update the points of the FFT channel arrays
@@ -119,14 +122,14 @@ class W_fft extends Widget {
GPoint powerAtBin;
// println("i = " + i);
// float a = get_fs_Hz_safe();
// float a = getSampleRateSafe();
// float aa = fftBuff[i].getBand(j);
// float b = fftBuff[i].getBand(j);
// float c = Nfft;
powerAtBin = new GPoint((1.0*get_fs_Hz_safe()/Nfft)*j, fftBuff[i].getBand(j));
powerAtBin = new GPoint((1.0*sr/nfft)*j, fftBuff[i].getBand(j));
fft_points[i].set(j, powerAtBin);
// GPoint powerAtBin = new GPoint((1.0*get_fs_Hz_safe()/Nfft)*j, fftBuff[i].getBand(j));
// GPoint powerAtBin = new GPoint((1.0*getSampleRateSafe()/Nfft)*j, fftBuff[i].getBand(j));
//println("=========================================");
//println(j);
@@ -225,13 +228,13 @@ void LogLin(int n) {
closeAllDropdowns();
}
//triggered when there is an event in the LogLin Dropdown
//triggered when there is an event in the Smoothing Dropdown
void Smoothing(int n) {
smoothFac_ind = n;
closeAllDropdowns();
}
//triggered when there is an event in the LogLin Dropdown
//triggered when there is an event in the UnfiltFilt Dropdown
void UnfiltFilt(int n) {
if (n==0) {
//have FFT use filtered data -- default
+593
Ver Arquivo
@@ -0,0 +1,593 @@
////////////////////////////////////////////////////
//
// W_focus.pde (ie "Focus Widget")
//
// This widget helps you visualize the alpha and beta value and the calculated focused state
// You can ask a robot to press Up Arrow key stroke whenever you are focused.
// You can also send the focused state to Arduino
//
// Created by: Wangshu Sun, August 2016
//
///////////////////////////////////////////////////,
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
// color enums
public enum FocusColors {
GREEN, CYAN, ORANGE
}
class W_Focus extends Widget {
//to see all core variables/methods of the Widget class, refer to Widget.pde
Robot robot; // a key-stroking robot waiting for focused state
boolean enableKey = false; // enable key stroke by the robot
int keyNum = 0; // 0 - up arrow, 1 - Spacebar
boolean enableSerial = false; // send the Focused state to Arduino
// output values
float alpha_avg = 0, beta_avg = 0;
boolean isFocused;
// alpha, beta threshold default values
float alpha_thresh = 0.7, beta_thresh = 0.7, alpha_upper = 2, beta_upper = 2;
// drawing parameters
boolean showAbout = false;
PFont myfont = createFont("fonts/Raleway-SemiBold.otf", 12);
PFont f = createFont("Arial Bold", 24); //for widget title
FocusColors focusColors = FocusColors.GREEN;
color cBack, cDark, cMark, cFocus, cWave, cPanel;
// float x, y, w, h; //widget topleft xy, width and height
float xc, yc, wc, hc; // crystal ball center xy, width and height
float wg, hg; //graph width, graph height
float wl; // line width
float xg1, yg1; //graph1 center xy
float xg2, yg2; //graph1 center xy
float rp; // padding radius
float rb; // button radius
float xb, yb; // button center xy
// two sliders for alpha and one slider for beta
FocusSlider sliderAlphaMid, sliderBetaMid;
FocusSlider_Static sliderAlphaTop;
W_Focus(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
// initialize graphics parameters
onColorChange();
update_graphic_parameters();
// sliders
sliderAlphaMid = new FocusSlider(x + xg1 + wg * 0.8, y + yg1 + hg/2, y + yg1 - hg/2, alpha_thresh / alpha_upper);
sliderAlphaTop = new FocusSlider_Static(x + xg1 + wg * 0.8, y + yg1 + hg/2, y + yg1 - hg/2);
sliderBetaMid = new FocusSlider(x + xg2 + wg * 0.8, y + yg2 + hg/2, y + yg2 - hg/2, beta_thresh / beta_upper);
//Dropdowns.
addDropdown("ChooseFocusColor", "Theme", Arrays.asList("Green", "Orange", "Cyan"), 0);
addDropdown("StrokeKeyWhenFocused", "KeyPress", Arrays.asList("OFF", "UP", "SPACE"), 0);
addDropdown("SerialSendFocused", "Serial", Arrays.asList("OFF", "ON"), 0);
// prepare simulate keystroking
try {
robot = new Robot();
} catch (AWTException e) {
e.printStackTrace();
exit();
}
}
void onColorChange() {
switch(focusColors) {
case GREEN:
cBack = #ffffff; //white
cDark = #3068a6; //medium/dark blue
cMark = #4d91d9; //lighter blue
cFocus = #b8dc69; //theme green
cWave = #ffdd3a; //yellow
cPanel = #f5f5f5; //little grey
break;
case ORANGE:
cBack = #ffffff; //white
cDark = #377bc4; //medium/dark blue
cMark = #5e9ee2; //lighter blue
cFocus = #fcce51; //orange
cWave = #ffdd3a; //yellow
cPanel = #f5f5f5; //little grey
break;
case CYAN:
cBack = #ffffff; //white
cDark = #377bc4; //medium/dark blue
cMark = #5e9ee2; //lighter blue
cFocus = #91f4fc; //cyan
cWave = #ffdd3a; //yellow
cPanel = #f5f5f5; //little grey
break;
}
}
void update(){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
updateFocusState(); // focus calculation
invokeKeyStroke(); // robot keystroke
sendFocusSerial(); // send focus data to serial port
// update sliders
sliderAlphaMid.update();
sliderAlphaTop.update();
sliderBetaMid.update();
// update threshold values
alpha_thresh = alpha_upper * sliderAlphaMid.getVal();
beta_thresh = beta_upper * sliderBetaMid.getVal();
alpha_upper = sliderAlphaTop.getVal() * 2;
beta_upper = alpha_upper;
sliderAlphaMid.setVal(alpha_thresh / alpha_upper);
sliderBetaMid.setVal(beta_thresh / beta_upper);
}
void updateFocusState() {
// focus detection algorithm based on Jordan's clean mind: focus == high alpha average && low beta average
float FFT_freq_Hz, FFT_value_uV;
int alpha_count = 0, beta_count = 0;
for (int Ichan=0; Ichan < 2; Ichan++) { // only consider first two channels
for (int Ibin=0; Ibin < fftBuff[Ichan].specSize(); Ibin++) {
FFT_freq_Hz = fftBuff[Ichan].indexToFreq(Ibin);
FFT_value_uV = fftBuff[Ichan].getBand(Ibin);
if (FFT_freq_Hz >= 7.5 && FFT_freq_Hz <= 12.5) { //FFT bins in alpha range
alpha_avg += FFT_value_uV;
alpha_count ++;
}
else if (FFT_freq_Hz > 12.5 && FFT_freq_Hz <= 30) { //FFT bins in beta range
beta_avg += FFT_value_uV;
beta_count ++;
}
}
}
alpha_avg = alpha_avg / alpha_count; // average uV per bin
//alpha_avg = alpha_avg / (cyton.getSampleRate()/Nfft); // average uV per delta freq
beta_avg = beta_avg / beta_count; // average uV per bin
//beta_avg = beta_avg / (cyton.getSampleRate()/Nfft); // average uV per delta freq
//current time = int(float(currentTableRowIndex)/cyton.getSampleRate());
// version 1
if (alpha_avg > alpha_thresh && alpha_avg < alpha_upper && beta_avg < beta_thresh) {
isFocused = true;
} else {
isFocused = false;
}
//alpha_avg = beta_avg = 0;
}
void invokeKeyStroke() {
// robot keystroke
if (enableKey) {
if (keyNum == 0) {
if (isFocused) {
robot.keyPress(KeyEvent.VK_UP); //if you want to change to other key, google "java keyEvent" to see the full list
}
else {
robot.keyRelease(KeyEvent.VK_UP);
}
}
else if (keyNum == 1) {
if (isFocused) {
robot.keyPress(KeyEvent.VK_SPACE); //if you want to change to other key, google "java keyEvent" to see the full list
}
else {
robot.keyRelease(KeyEvent.VK_SPACE);
}
}
}
}
void sendFocusSerial() {
// ----------- if turned on, send the focused state to Arduino via serial port -----------
if (enableSerial) {
try {
serial_output.write(int(isFocused) + 48);
serial_output.write('\n');
}
catch(RuntimeException e) {
if (isVerbose) println("serial not present, search 'serial_output' in OpenBCI.pde and check serial settings.");
}
}
}
void draw(){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
//put your code here... //remember to refer to x,y,w,h which are the positioning variables of the Widget class
pushStyle();
//----------------- presettings before drawing Focus Viz --------------
translate(x, y);
textAlign(CENTER, CENTER);
textFont(myfont);
//----------------- draw background rectangle and panel -----------------
fill(cBack);
noStroke();
rect(0, 0, w, h);
fill(cPanel);
noStroke();
rect(rp, rp, w-rp*2, h-rp*2);
//----------------- draw focus crystalball -----------------
noStroke();
if (isFocused) {
fill(cFocus);
stroke(cFocus);
} else {
fill(cDark);
}
ellipse(xc, yc, wc, hc);
noStroke();
// draw focus label
if (isFocused) {
fill(cFocus);
text("focused!", xc, yc + hc/2 + 16);
} else {
fill(cMark);
text("not focused", xc, yc + hc/2 + 16);
}
//----------------- draw alpha meter -----------------
noStroke();
fill(cDark);
rect(xg1 - wg/2, yg1 - hg/2, wg, hg);
float hat = map(alpha_thresh, 0, alpha_upper, 0, hg); // alpha threshold height
stroke(cMark);
line(xg1 - wl/2, yg1 + hg/2, xg1 + wl/2, yg1 + hg/2);
line(xg1 - wl/2, yg1 - hg/2, xg1 + wl/2, yg1 - hg/2);
line(xg1 - wl/2, yg1 + hg/2 - hat, xg1 + wl/2, yg1 + hg/2 - hat);
// draw alpha zone and text
noStroke();
if (alpha_avg > alpha_thresh && alpha_avg < alpha_upper) {
fill(cFocus);
} else {
fill(cMark);
}
rect(xg1 - wg/2, yg1 - hg/2, wg, hg - hat);
text("alpha", xg1, yg1 + hg/2 + 16);
// draw connection between two sliders
stroke(cMark);
line(xg1 + wg * 0.8, yg1 - hg/2 + 10, xg1 + wg * 0.8, yg1 + hg/2 - hat - 10);
noStroke();
fill(cMark);
text(String.format("%.01f", alpha_upper), xg1 - wl/2 - 14, yg1 - hg/2);
text(String.format("%.01f", alpha_thresh), xg1 - wl/2 - 14, yg1 + hg/2 - hat);
text("0.0", xg1 - wl/2 - 14, yg1 + hg/2);
stroke(cWave);
strokeWeight(4);
float ha = map(alpha_avg, 0, alpha_upper, 0, hg); //alpha height
ha = constrain(ha, 0, hg);
line(xg1 - wl/2, yg1 + hg/2 - ha, xg1 + wl/2, yg1 + hg/2 - ha);
strokeWeight(1);
//----------------- draw beta meter -----------------
noStroke();
fill(cDark);
rect(xg2 - wg/2, yg2 - hg/2, wg, hg);
float hbt = map(beta_thresh, 0, beta_upper, 0, hg); // beta threshold height
stroke(cMark);
line(xg2 - wl/2, yg2 + hg/2, xg2 + wl/2, yg2 + hg/2);
line(xg2 - wl/2, yg2 - hg/2, xg2 + wl/2, yg2 - hg/2);
line(xg2 - wl/2, yg2 + hg/2 - hbt, xg2 + wl/2, yg2 + hg/2 - hbt);
// draw beta zone and text
noStroke();
if (beta_avg < beta_thresh) {
fill(cFocus);
} else {
fill(cMark);
}
rect(xg2 - wg/2, yg2 + hg/2 - hbt, wg, hbt);
text("beta", xg2, yg2 + hg/2 + 16);
// draw connection between slider and bottom
stroke(cMark);
float yt = yg2 + hg/2 - hbt + 10; // y threshold
yt = constrain(yt, yg2 - hg/2 + 10, yg2 + hg/2);
line(xg2 + wg * 0.8, yg2 + hg/2, xg2 + wg * 0.8, yt);
noStroke();
fill(cMark);
text(String.format("%.01f", beta_upper), xg2 - wl/2 - 14, yg2 - hg/2);
text(String.format("%.01f", beta_thresh), xg2 - wl/2 - 14, yg2 + hg/2 - hbt);
text("0.0", xg2 - wl/2 - 14, yg2 + hg/2);
stroke(cWave);
strokeWeight(4);
float hb = map(beta_avg, 0, beta_upper, 0, hg); //beta height
hb = constrain(hb, 0, hg);
line(xg2 - wl/2, yg2 + hg/2 - hb, xg2 + wl/2, yg2 + hg/2 - hb);
strokeWeight(1);
translate(-x, -y);
//------------------ draw sliders --------------------
sliderAlphaMid.draw();
sliderAlphaTop.draw();
sliderBetaMid.draw();
//----------------- draw about button -----------------
translate(x, y);
if (showAbout) {
stroke(cDark);
fill(cBack);
rect(rp, rp, w-rp*2, h-rp*2);
textAlign(LEFT, TOP);
fill(cDark);
text("This widget recognizes a focused mental state by looking at alpha and beta wave levels on channel 1 & 2. For better result, try setting the smooth at 0.98 in FFT plot.\n\nThe algorithm thinks you are focused when the alpha level is between 0.7~2uV and the beta level is between 0~0.7 uV, otherwise it thinks you are not focused. It is designed based on Jordan Frands brainwave and tested on other subjects, and you can playback Jordan's file in W_Focus folder.\n\nYou can turn on KeyPress and use your focus play a game, so whenever you are focused, the specified UP arrow or SPACE key will be pressed down, otherwise it will be released. You can also try out the Arduino output feature, example and instructions are included in W_Focus folder. For more information, contact wangshu.sun@hotmail.com.", rp*1.5, rp*1.5, w-rp*3, h-rp*3);
}
// draw the button that toggles information
noStroke();
fill(cDark);
ellipse(xb, yb, rb, rb);
fill(cBack);
textAlign(CENTER, CENTER);
if (showAbout) {
text("x", xb, yb);
} else {
text("?", xb, yb);
}
//----------------- revert origin point of draw to default -----------------
translate(-x, -y);
textAlign(LEFT, BASELINE);
popStyle();
}
void screenResized(){
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
//put your code here...
update_graphic_parameters();
//update sliders...
sliderAlphaMid.screenResized(x + xg1 + wg * 0.8, y + yg1 + hg/2, y + yg1 - hg/2);
sliderAlphaTop.screenResized(x + xg1 + wg * 0.8, y + yg1 + hg/2, y + yg1 - hg/2);
sliderBetaMid.screenResized(x + xg2 + wg * 0.8, y + yg2 + hg/2, y + yg2 - hg/2);
}
void update_graphic_parameters () {
xc = w/4;
yc = h/2;
wc = w/4;
hc = w/4;
wg = 0.07*w;
hg = 0.64*h;
wl = 0.11*w;
xg1 = 0.6*w;
yg1 = 0.5*h;
xg2 = 0.83*w;
yg2 = 0.5*h;
rp = max(w*0.05, h*0.05);
rb = 20;
xb = w-rp;
yb = rp;
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
// about button
if (dist(mouseX,mouseY,xb+x,yb+y) <= rb) {
showAbout = !showAbout;
}
// sliders
sliderAlphaMid.mousePressed();
sliderAlphaTop.mousePressed();
sliderBetaMid.mousePressed();
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
// sliders
sliderAlphaMid.mouseReleased();
sliderAlphaTop.mouseReleased();
sliderBetaMid.mouseReleased();
}
};
/* ---------------------- Supporting Slider Classes ---------------------------*/
// abstract basic slider
public abstract class BasicSlider {
float x, y, w, h; // center x, y. w, h means width and height of triangle
float yBot, yTop; // y range. Notice val of top y is less than bottom y
boolean isPressed = false;
color cNormal = #CCCCCC;
color cPressed = #FF0000;
BasicSlider(float _x, float _yBot, float _yTop) {
x = _x;
yBot = _yBot;
yTop = _yTop;
w = 10;
h = 10;
}
// abstract functions
abstract void update();
abstract void screenResized(float _x, float _yBot, float _yTop);
abstract float getVal();
abstract void setVal(float _val);
// shared functions
void draw() {
if (isPressed) fill(cPressed);
else fill(cNormal);
noStroke();
triangle(x-w/2, y, x+w/2, y-h/2, x+w/2, y+h/2);
}
void mousePressed() {
if (abs(mouseX - (x)) <= w/2 && abs(mouseY - y) <= h/2) {
isPressed = true;
}
}
void mouseReleased() {
if (isPressed) {
isPressed = false;
}
}
}
// middle slider that changes value and move
public class FocusSlider extends BasicSlider {
private float val = 0; // val = 0 ~ 1 -> yBot to yTop
final float valMin = 0;
final float valMax = 0.90;
FocusSlider(float _x, float _yBot, float _yTop, float _val) {
super(_x, _yBot, _yTop);
val = constrain(_val, valMin, valMax);
y = map(val, 0, 1, yBot, yTop);
}
public void update() {
if (isPressed) {
float newVal = map(mouseY, yBot, yTop, 0, 1);
val = constrain(newVal, valMin, valMax);
y = map(val, 0, 1, yBot, yTop);
println(val);
}
}
public void screenResized(float _x, float _yBot, float _yTop) {
x = _x;
yBot = _yBot;
yTop = _yTop;
y = map(val, 0, 1, yBot, yTop);
}
public float getVal() {
return val;
}
public void setVal(float _val) {
val = constrain(_val, valMin, valMax);
y = map(val, 0, 1, yBot, yTop);
}
}
// top slider that changes value but doesn't move
public class FocusSlider_Static extends BasicSlider {
private float val = 0; // val = 0 ~ 1 -> yBot to yTop
final float valMin = 0.5;
final float valMax = 5.0;
FocusSlider_Static(float _x, float _yBot, float _yTop) {
super(_x, _yBot, _yTop);
val = 1;
y = yTop;
}
public void update() {
if (isPressed) {
float diff = map(mouseY, yBot, yTop, -0.07, 0);
val = constrain(val + diff, valMin, valMax);
println(val);
}
}
public void screenResized(float _x, float _yBot, float _yTop) {
x = _x;
yBot = _yBot;
yTop = _yTop;
y = yTop;
}
public float getVal() {
return val;
}
public void setVal(float _val) {
val = constrain(_val, valMin, valMax);
}
}
/* ---------------- Global Functions For Menu Entries --------------------*/
// //These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected
void StrokeKeyWhenFocused(int n){
// println("Item " + (n+1) + " selected from Dropdown 1");
if(n==0){
//do this
w_focus.enableKey = false;
println("The robot ignores focused state and will not press any key.");
} else if(n==1){
//do this instead
w_focus.enableKey = true;
w_focus.keyNum = 0;
println("The robot will keep pressing Arrow Up key when you are focused, and release the key when you lose focus.");
} else if(n==2){
//do this instead
w_focus.enableKey = true;
w_focus.keyNum = 1;
println("The robot will keep pressing Spacebar when you are focused, and release the key when you lose focus.");
}
closeAllDropdowns(); // do this at the end of all widget-activated functions to ensure proper widget interactivity ... we want to make sure a click makes the menu close
}
void SerialSendFocused(int n){
if(n==0){
//do this
w_focus.enableSerial = false;
println("Serial write off.");
} else if(n==1){
//do this instead
w_focus.enableSerial = true;
println("Serial write on, writing character 1 (int 49) when focused, and character 0 (int 48) when losing focus.");
println("Current output port name: " + serial_output_portName + ". Current baud rate: " + serial_output_baud + ".");
println("You can change serial settings in OpenBCI_GUI.pde by searching serial_output.");
}
closeAllDropdowns();
}
void ChooseFocusColor(int n){
if(n==0){
w_focus.focusColors = FocusColors.GREEN;
w_focus.onColorChange();
} else if(n==1){
w_focus.focusColors = FocusColors.ORANGE;
w_focus.onColorChange();
} else if(n==2){
w_focus.focusColors = FocusColors.CYAN;
w_focus.onColorChange();
}
closeAllDropdowns();
}
@@ -0,0 +1,69 @@
/*
Serial Event example
When new serial data arrives, this sketch adds it to a String.
When a newline is received, the loop prints the string and
clears it.
A good test for this is to try it with a GPS receiver
that sends out NMEA 0183 sentences.
Created 9 May 2011
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/SerialEvent
*/
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
void setup() {
// initialize serial:
Serial.begin(9600);
pinMode(2, OUTPUT); //use pin 2 to turn on a light
// reserve 200 bytes for the inputString:
//inputString.reserve(20);
}
void loop() {
// print the string when a newline arrives:
if (stringComplete) {
if (inputString[0] == '1') {
digitalWrite(2, HIGH);
}
else {
digitalWrite(2, LOW);
}
//clear the string:
inputString = "";
stringComplete = false;
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
*/
void serialEvent() {
if (Serial.available() > 0) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
@@ -0,0 +1,11 @@
notes_Arduino
Modified from Tom Igoe's code.
It should be able to turn on an LED by focus on pin 2.
To replicate this example, you will need an Arduino Uno (101 is ok as well) and set up an LED output there, then upload this code to your Arduino.
Also you need to set the serial_output_portName in OpenBCI.pde as your Arduino's serial port name, and also set serial_output_baud to be 9600 (you can use other baud rate but need to ensure serial_output_baud is the same as the number in Serial.begin() in the Arduino file).
Once you're prepared, you can run the openBCI GUI and turn on Serial in Focus Widget.
Enjoy then :)
Wangshu
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -0,0 +1,3 @@
0-30s prep
30-85s focus on a banana
85s- losing focus
+287
Ver Arquivo
@@ -0,0 +1,287 @@
////////////////////////////////////////////////////
//
// W_MarkerMode is used to put the board into marker mode
// by Gerrie van Zyl
// Basd on W_Analogread by AJ Keller
//
//
///////////////////////////////////////////////////,
class W_MarkerMode extends Widget {
//to see all core variables/methods of the Widget class, refer to Widget.pde
//put your custom variables here...
// color boxBG;
color graphStroke = #d2d2d2;
color graphBG = #f5f5f5;
color textColor = #000000;
color strokeColor;
// Accelerometer Stuff
int MarkerBuffSize = 500; //points registered in accelerometer buff
int padding = 30;
// bottom xyz graph
int MarkerWindowWidth;
int MarkerWindowHeight;
int MarkerWindowX;
int MarkerWindowY;
color eggshell;
color Xcolor;
float yMaxMin;
float currentXvalue;
int[] X;
int lastMarker=0;
int localValidLastMarker;
float dummyX;
// for the synthetic markers
float synthTime;
int synthCount;
boolean OBCI_inited= true;
Button markerModeButton;
W_MarkerMode(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
// boxBG = bgColor;
strokeColor = color(138, 146, 153);
// Marker Sensor Stuff
eggshell = color(255, 253, 248);
Xcolor = color(224, 56, 45);
setGraphDimensions();
// The range of markers
yMaxMin = 256;
// XYZ buffer for bottom graph
X = new int[MarkerBuffSize];
// for synthesizing values
synthTime = 0.0;
markerModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn MarkerMode On", 12);
markerModeButton.setCornerRoundess((int)(navHeight-6));
markerModeButton.setFont(p6,10);
markerModeButton.setColorNotPressed(color(57,128,204));
markerModeButton.textColorNotActive = color(255);
markerModeButton.hasStroke(false);
markerModeButton.setHelpText("Click this button to activate/deactivate the MarkerMode of your Cyton board!");
}
public void initPlayground(Cyton _OBCI) {
OBCI_inited = true;
}
void update(){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
localValidLastMarker = hub.validLastMarker; // make a local copy so it can be manipulated in SYNTHETIC mode
hub.validLastMarker = 0;
if (eegDataSource == DATASOURCE_SYNTHETIC) {
localValidLastMarker = synthesizeMarkerData();
}
if (eegDataSource == DATASOURCE_CYTON || eegDataSource == DATASOURCE_SYNTHETIC) {
if (isRunning && cyton.getBoardMode() == BOARD_MODE_MARKER) {
if (localValidLastMarker > 0){
lastMarker = localValidLastMarker; // this holds the last marker for the display
}
X[X.length-1] =
int(map(logScaleMarker(localValidLastMarker), 0, yMaxMin, float(MarkerWindowY+MarkerWindowHeight), float(MarkerWindowY)));
X[X.length-1] = constrain(X[X.length-1], MarkerWindowY, MarkerWindowY+MarkerWindowHeight);
shiftWave();
}
} else { // playback data
currentXvalue = accelerometerBuff[0][accelerometerBuff[0].length-1];
}
}
void draw(){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
pushStyle();
//put your code here...
//remember to refer to x,y,w,h which are the positioning variables of the Widget class
if (true) {
fill(50);
textFont(p4, 14);
textAlign(CENTER,CENTER);
fill(graphBG);
stroke(graphStroke);
rect(MarkerWindowX, MarkerWindowY, MarkerWindowWidth, MarkerWindowHeight);
line(MarkerWindowX, MarkerWindowY + MarkerWindowHeight/2, MarkerWindowX+MarkerWindowWidth, MarkerWindowY + MarkerWindowHeight/2); //midline
fill(50);
textFont(p5, 12);
textAlign(CENTER,CENTER);
text((int)yMaxMin, MarkerWindowX+MarkerWindowWidth + 12, MarkerWindowY);
text((int)16, MarkerWindowX+MarkerWindowWidth + 12, MarkerWindowY + MarkerWindowHeight/2);
text("0", MarkerWindowX+MarkerWindowWidth + 12, MarkerWindowY + MarkerWindowHeight);
fill(graphBG); // pulse window background
stroke(graphStroke);
stroke(180);
fill(50);
textFont(p3, 16);
if (eegDataSource == DATASOURCE_CYTON || eegDataSource == DATASOURCE_SYNTHETIC) { // LIVE
markerModeButton.draw();
drawMarkerValues();
drawMarkerWave();
}
else { // PLAYBACK
drawMarkerValues();
drawMarkerWave2();
}
}
popStyle();
}
void setGraphDimensions(){
MarkerWindowWidth = w - padding*2;
MarkerWindowHeight = int((float(h) - float(padding*3)));
MarkerWindowX = x + padding;
MarkerWindowY = y + h - MarkerWindowHeight - padding;
}
void screenResized(){
int prevX = x;
int prevY = y;
int prevW = w;
int prevH = h;
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
int dy = y - prevY;
println("dy = " + dy);
//put your code here...
println("Acc Widget -- Screen Resized.");
setGraphDimensions();
//empty arrays to start redrawing from scratch
for (int i=0; i<X.length; i++) { // initialize the accelerometer data
X[i] = MarkerWindowY + MarkerWindowHeight; // X at 1/4
}
markerModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
if (markerModeButton.isMouseHere()) {
markerModeButton.setIsActive(true);
}
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
if(markerModeButton.isActive && markerModeButton.isMouseHere()){
// println("markerModeButton...");
if((cyton.isPortOpen() && eegDataSource == DATASOURCE_CYTON) || eegDataSource == DATASOURCE_SYNTHETIC) {
if (cyton.getBoardMode() != BOARD_MODE_MARKER) {
cyton.setBoardMode(BOARD_MODE_MARKER);
output("Starting to read markers");
markerModeButton.setString("Turn Marker Off");
} else {
cyton.setBoardMode(BOARD_MODE_DEFAULT);
output("Starting to read accelerometer");
markerModeButton.setString("Turn Marker On");
}
}
}
markerModeButton.setIsActive(false);
}
//add custom classes functions here
void drawMarkerValues() {
textAlign(LEFT,CENTER);
textFont(h1,20);
fill(Xcolor);
text("Last Marker = " + lastMarker, x+padding , y + (h/12)*1.5);
}
void shiftWave() {
for (int i = 0; i < X.length-1; i++) { // move the pulse waveform by
X[i] = X[i+1];
}
}
void drawMarkerWave() {
noFill();
strokeWeight(2);
beginShape(); // using beginShape() renders fast
stroke(Xcolor);
for (int i = 0; i < X.length; i++) {
// int xi = int(map(i, 0, X.length-1, 0, MarkerWindowWidth-1));
// vertex(MarkerWindowX+xi, X[i]); //draw a line connecting the data points
int xi = int(map(i, 0, X.length-1, 0, MarkerWindowWidth-1));
// int yi = int(map(X[i], yMaxMin, -yMaxMin, 0.0, MarkerWindowHeight-1));
// int yi = 2;
vertex(MarkerWindowX+xi, X[i]); //draw a line connecting the data points
}
endShape();
}
void drawMarkerWave2() {
noFill();
strokeWeight(1);
beginShape(); // using beginShape() renders fast
stroke(Xcolor);
for (int i = 0; i < accelerometerBuff[0].length; i++) {
int x = int(map(accelerometerBuff[0][i], -yMaxMin, yMaxMin, float(MarkerWindowY+MarkerWindowHeight), float(MarkerWindowY))); // ss
x = constrain(x, MarkerWindowY, MarkerWindowY+MarkerWindowHeight);
vertex(MarkerWindowX+i, x); //draw a line connecting the data points
}
endShape();
}
int synthesizeMarkerData() {
synthTime += 0.02;
int valueMarker;
if (synthCount++ > 10){
valueMarker = int((sin(synthTime) +1.0)*127.);
synthCount = 0;
} else {
valueMarker = 0;
}
return valueMarker;
}
int logScaleMarker( float value ) {
// this returns log value between 0 and yMaxMin for a value between 0. and 255.
return int(log(int(value)+1.0)*yMaxMin/log(yMaxMin+1));
}
};
+404
Ver Arquivo
@@ -0,0 +1,404 @@
////////////////////////////////////////////////////
//
// W_PulseSensor.pde
//
// Created: Joel Murphy, Spring 2017
//
///////////////////////////////////////////////////,
class W_PulseSensor extends Widget {
//to see all core variables/methods of the Widget class, refer to Widget.pde
//put your custom variables here...
color graphStroke = #d2d2d2;
color graphBG = #f5f5f5;
color textColor = #000000;
// Pulse Sensor Visualizer Stuff
int count = 0;
int heart = 0;
int PulseBuffSize = dataPacketBuff.length; // Originally 400
int BPMbuffSize = 100;
int PulseWindowWidth;
int PulseWindowHeight;
int PulseWindowX;
int PulseWindowY;
int BPMwindowWidth;
int BPMwindowHeight;
int BPMwindowX;
int BPMwindowY;
int BPMposX;
int BPMposY;
int IBIposX;
int IBIposY;
int padding = 15;
color eggshell;
color pulseWave;
int[] PulseWaveY; // HOLDS HEARTBEAT WAVEFORM DATA
int[] BPMwaveY; // HOLDS BPM WAVEFORM DATA
boolean rising;
// Synthetic Wave Generator Stuff
float theta; // Start angle at 0
float amplitude; // Height of wave
int syntheticMultiplier;
long thisTime;
long thatTime;
int refreshRate;
// Pulse Sensor Beat Finder Stuff
// ASSUMES 250Hz SAMPLE RATE
int[] rate; // array to hold last ten IBI values
int sampleCounter; // used to determine pulse timing
int lastBeatTime; // used to find IBI
int P =512; // used to find peak in pulse wave, seeded
int T = 512; // used to find trough in pulse wave, seeded
int thresh = 530; // used to find instant moment of heart beat, seeded
int amp = 0; // used to hold amplitude of pulse waveform, seeded
boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
int BPM; // int that holds raw Analog in 0. updated every 2mS
int Signal; // holds the incoming raw data
int IBI = 600; // int that holds the time interval between beats! Must be seeded!
boolean Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat".
boolean QS = false; // becomes true when Arduoino finds a beat.
int lastProcessedDataPacketInd = 0;
boolean analogReadOn = false;
// testing stuff
Button analogModeButton;
W_PulseSensor(PApplet _parent){
super(_parent); //calls the parent CONSTRUCTOR method of Widget (DON'T REMOVE)
// Pulse Sensor Stuff
eggshell = color(255, 253, 248);
pulseWave = color(224, 56, 45);
PulseWaveY = new int[PulseBuffSize];
BPMwaveY = new int[BPMbuffSize];
rate = new int[10];
setPulseWidgetVariables();
initializePulseFinderVariables();
analogModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn Analog Read On", 12);
analogModeButton.setCornerRoundess((int)(navHeight-6));
analogModeButton.setFont(p6,10);
analogModeButton.setColorNotPressed(color(57,128,204));
analogModeButton.textColorNotActive = color(255);
analogModeButton.hasStroke(false);
analogModeButton.setHelpText("Click this button to activate analog reading on the Cyton");
}
void update(){
super.update(); //calls the parent update() method of Widget (DON'T REMOVE)
if (curDataPacketInd < 0) return;
if (eegDataSource == DATASOURCE_CYTON) { // LIVE FROM CYTON
} else if (eegDataSource == DATASOURCE_GANGLION) { // LIVE FROM GANGLION
} else if (eegDataSource == DATASOURCE_SYNTHETIC) { // SYNTHETIC
}
else { // PLAYBACK
}
int numSamplesToProcess = curDataPacketInd - lastProcessedDataPacketInd;
if (numSamplesToProcess < 0) {
numSamplesToProcess += dataPacketBuff.length; //<>//
}
// Shift internal ring buffer numSamplesToProcess
if (numSamplesToProcess > 0) {
for(int i=0; i < PulseWaveY.length - numSamplesToProcess; i++){
PulseWaveY[i] = PulseWaveY[i+numSamplesToProcess]; //<>//
}
}
// for each new sample
int samplesProcessed = 0;
while (samplesProcessed < numSamplesToProcess) {
lastProcessedDataPacketInd++;
// Watch for wrap around
if (lastProcessedDataPacketInd > dataPacketBuff.length - 1) {
lastProcessedDataPacketInd = 0;
}
int signal = dataPacketBuff[lastProcessedDataPacketInd].auxValues[0];
processSignal(signal);
PulseWaveY[PulseWaveY.length - numSamplesToProcess + samplesProcessed] = signal; //<>//
samplesProcessed++;
}
if(QS){
QS = false;
for(int i=0; i<BPMwaveY.length-1; i++){
BPMwaveY[i] = BPMwaveY[i+1];
}
BPMwaveY[BPMwaveY.length-1] = BPM;
}
}
void draw(){
super.draw(); //calls the parent draw() method of Widget (DON'T REMOVE)
//remember to refer to x,y,w,h which are the positioning variables of the Widget class
pushStyle();
fill(graphBG);
stroke(graphStroke);
rect(PulseWindowX,PulseWindowY,PulseWindowWidth,PulseWindowHeight);
rect(BPMwindowX,BPMwindowY,BPMwindowWidth,BPMwindowHeight);
fill(50);
textFont(p4, 16);
textAlign(LEFT,CENTER);
text("BPM "+BPM, BPMposX, BPMposY);
text("IBI "+IBI+"mS", IBIposX, IBIposY);
if (analogReadOn) {
drawWaves();
}
analogModeButton.draw();
popStyle();
}
void screenResized(){
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
println("Pulse Sensor Widget -- Screen Resized.");
setPulseWidgetVariables();
analogModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
void mousePressed(){
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
if (analogModeButton.isMouseHere()) {
analogModeButton.setIsActive(true);
}
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
if(analogModeButton.isActive && analogModeButton.isMouseHere()){
// println("analogModeButton...");
if(cyton.isPortOpen()) {
if (analogReadOn) {
cyton.setBoardMode(BOARD_MODE_DEFAULT);
output("Starting to read accelerometer");
analogModeButton.setString("Turn Analog Read On");
} else {
cyton.setBoardMode(BOARD_MODE_ANALOG);
output("Starting to read analog inputs on pin marked D11");
analogModeButton.setString("Turn Analog Read Off");
}
analogReadOn = !analogReadOn;
}
}
analogModeButton.setIsActive(false);
}
//add custom functions here
void setPulseWidgetVariables(){
PulseWindowWidth = ((w/4)*3) - padding;
PulseWindowHeight = h - padding *2;
PulseWindowX = x + padding;
PulseWindowY = y + h - PulseWindowHeight - padding;
BPMwindowWidth = w/4 - (padding + padding/2);
BPMwindowHeight = PulseWindowHeight; // - padding;
BPMwindowX = PulseWindowX + PulseWindowWidth + padding/2;
BPMwindowY = PulseWindowY; // + padding;
BPMposX = BPMwindowX + padding/2;
BPMposY = y - padding; // BPMwindowHeight + int(float(padding)*2.5);
IBIposX = PulseWindowX + PulseWindowWidth/2; // + padding/2
IBIposY = y - padding;
// float py;
// float by;
// for(int i=0; i<PulseWaveY.length; i++){
// py = map(float(PulseWaveY[i]),
// 0.0,1023.0,
// float(PulseWindowY + PulseWindowHeight),float(PulseWindowY)
// );
// PulseWaveY[i] = int(py);
// }
// for(int i=0; i<BPMwaveY.length; i++){
// BPMwaveY[i] = BPMwindowY + BPMwindowHeight-1;
// }
}
void initializePulseFinderVariables(){
sampleCounter = 0;
lastBeatTime = 0;
P = 512;
T = 512;
thresh = 530;
amp = 0;
firstBeat = true;
secondBeat = false;
BPM = 0;
Signal = 512;
IBI = 600;
Pulse = false;
QS = false;
theta = 0.0;
amplitude = 300;
syntheticMultiplier = 1;
thatTime = millis();
// float py = map(float(Signal),
// 0.0,1023.0,
// float(PulseWindowY + PulseWindowHeight),float(PulseWindowY)
// );
for(int i=0; i<PulseWaveY.length; i++){
PulseWaveY[i] = Signal;
// PulseWaveY[i] = PulseWindowY + PulseWindowHeight/2;
}
for(int i=0; i<BPMwaveY.length; i++){
BPMwaveY[i] = BPM;
}
}
void drawWaves(){
int xi, yi;
noFill();
strokeWeight(1);
stroke(pulseWave);
beginShape(); // using beginShape() renders fast
for(int i=0; i<PulseWaveY.length; i++){
xi = int(map(i,0, PulseWaveY.length-1,0, PulseWindowWidth-1));
xi += PulseWindowX;
yi = int(map(PulseWaveY[i],0.0,1023.0,
float(PulseWindowY + PulseWindowHeight),float(PulseWindowY)));
vertex(xi, yi);
}
endShape();
strokeWeight(2);
stroke(pulseWave);
beginShape(); // using beginShape() renders fast
for(int i=0; i<BPMwaveY.length; i++){
xi = int(map(i,0, BPMwaveY.length-1,0, BPMwindowWidth-1));
xi += BPMwindowX;
yi = int(map(BPMwaveY[i], 0.0,200.0,
float(BPMwindowY + BPMwindowHeight), float(BPMwindowY)));
vertex(xi, yi);
}
endShape();
}
// THIS IS THE BEAT FINDING FUNCTION
// BASED ON CODE FROM World Famous Electronics, MAKERS OF PULSE SENSOR
// https://github.com/WorldFamousElectronics/PulseSensor_Amped_Arduino
void processSignal(int sample){ // triggered when Timer2 counts to 124
// cli(); // disable interrupts while we do this
// Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += (4 * syntheticMultiplier); // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(sample < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (sample < T){ // T is the trough
T = sample; // keep track of lowest point in pulse wave
}
}
if(sample > thresh && sample > P){ // thresh condition helps avoid noise
P = sample; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (sample > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
// sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
int runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
BPM = constrain(BPM,0,200);
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS FUNCTION
}
}
if (sample < thresh && Pulse == true){ // when the values are going down, the beat is over
// digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 530; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
// sei(); // enable interrupts when youre done!
}// end processSignal
};
+1 -2
Ver Arquivo
@@ -28,8 +28,7 @@ class W_template extends Widget {
widgetTemplateButton = new Button (x + w/2, y + h/2, 200, navHeight, "Design Your Own Widget!", 12);
widgetTemplateButton.setFont(p4, 14);
widgetTemplateButton.setURL("http://docs.openbci.com/OpenBCI%20Software/");
widgetTemplateButton.setURL("http://docs.openbci.com/Tutorials/15-Custom_Widgets");
}
void update(){
+13 -35
Ver Arquivo
@@ -28,7 +28,7 @@ class W_timeSeries extends Widget {
ChannelBar[] channelBars;
int[] xLimOptions = {3, 5, 8}; // number of seconds (x axis of graph)
int[] xLimOptions = {1, 3, 5, 7}; // number of seconds (x axis of graph)
int[] yLimOptions = {0, 50, 100, 200, 400, 1000, 10000}; // 0 = Autoscale ... everything else is uV
int xLim = xLimOptions[1]; //start at 5s
@@ -57,7 +57,6 @@ class W_timeSeries extends Widget {
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
addDropdown("VertScale_TS", "Vert Scale", Arrays.asList("Auto", "50 uV", "100 uV", "200 uV", "400 uV", "1000 uV", "10000 uV"), startingVertScaleIndex);
addDropdown("Duration", "Window", Arrays.asList("1 sec", "3 sec", "5 sec", "7 sec"), 2);
// addDropdown("Spillover", "Spillover", Arrays.asList("False", "True"), 0);
@@ -92,7 +91,7 @@ class W_timeSeries extends Widget {
channelBars[i] = tempBar;
}
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
hardwareSettingsButton = new Button((int)(x + 3), (int)(y + navHeight + 3), 120, navHeight - 6, "Hardware Settings", 12);
hardwareSettingsButton.setCornerRoundess((int)(navHeight-6));
hardwareSettingsButton.setFont(p6,10);
@@ -110,7 +109,6 @@ class W_timeSeries extends Widget {
int y_hsc = int(ts_y);
int w_hsc = int(ts_w); //width of montage controls (on left of montage)
int h_hsc = int(ts_h); //height of montage controls (on left of montage)
hsc = new HardwareSettingsController((int)channelBars[0].plot.getPos()[0] + 2, (int)channelBars[0].plot.getPos()[1], (int)channelBars[0].plot.getOuterDim()[0], h_hsc - 4, channelBarHeight);
}
@@ -154,7 +152,7 @@ class W_timeSeries extends Widget {
channelBars[i].draw();
}
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
hardwareSettingsButton.draw();
}
@@ -198,7 +196,7 @@ class W_timeSeries extends Widget {
hsc.screenResized((int)channelBars[0].plot.getPos()[0] + 2, (int)channelBars[0].plot.getPos()[1], (int)channelBars[0].plot.getOuterDim()[0], (int)ts_h - 4, channelBarHeight);
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
hardwareSettingsButton.setPos((int)(x0 + 3), (int)(y0 + navHeight + 3));
}
}
@@ -207,7 +205,7 @@ class W_timeSeries extends Widget {
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
//put your code here...
if (hardwareSettingsButton.isMouseHere()) {
hardwareSettingsButton.setIsActive(true);
@@ -228,7 +226,7 @@ class W_timeSeries extends Widget {
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
//put your code here...
if(hardwareSettingsButton.isActive && hardwareSettingsButton.isMouseHere()){
println("toggle...");
@@ -253,7 +251,6 @@ class W_timeSeries extends Widget {
}
}
}
};
//These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected
@@ -390,7 +387,7 @@ class ChannelBar{
onOffButton.setColorNotPressed(channelColors[(channelNumber-1)%8]);
onOffButton.hasStroke(false);
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
impButton_diameter = 22;
impCheckButton = new Button (x + 36, y + int(h/2) - int(impButton_diameter/2), impButton_diameter, impButton_diameter, "\u2126", fontInfo.buttonLabel_size);
impCheckButton.setFont(h2, 16);
@@ -400,7 +397,6 @@ class ChannelBar{
} else {
impButton_diameter = 0;
}
numSeconds = 5;
plot = new GPlot(_parent);
plot.setPos(x + 36 + 4 + impButton_diameter, y);
@@ -411,7 +407,6 @@ class ChannelBar{
plot.setYLim(-200,200);
plot.setPointSize(2);
plot.setPointColor(0);
if(channelNumber == nchan){
plot.getXAxis().setAxisLabelText("Time (s)");
}
@@ -419,14 +414,6 @@ class ChannelBar{
nPoints = nPointsBasedOnDataSource();
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
nPoints = numSeconds * (int)openBCI.fs_Hz;
}else if(eegDataSource == DATASOURCE_GANGLION || nchan == 4){
nPoints = numSeconds * (int)ganglion.fs_Hz;
}else{
nPoints = numSeconds * (int)openBCI.fs_Hz;
}
channelPoints = new GPointsArray(nPoints);
timeBetweenPoints = (float)numSeconds / (float)nPoints;
@@ -529,7 +516,7 @@ class ChannelBar{
//draw onOff Button
onOffButton.draw();
//draw impedance check Button
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
impCheckButton.draw();
}
@@ -566,16 +553,7 @@ class ChannelBar{
}
int nPointsBasedOnDataSource(){
int _nPoints;
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
_nPoints = numSeconds * (int)openBCI.fs_Hz;
}else if(eegDataSource == DATASOURCE_GANGLION || nchan == 4){
_nPoints = numSeconds * (int)ganglion.fs_Hz;
}else{
_nPoints = numSeconds * (int)openBCI.fs_Hz;
}
return _nPoints;
return numSeconds * (int)getSampleRateSafe();
}
void adjustTimeAxis(int _newTimeSize){
@@ -635,7 +613,7 @@ class ChannelBar{
onOffButton.but_x = x + 6;
onOffButton.but_y = y + int(h/2) - int(onOff_diameter/2);
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
impCheckButton.but_x = x + 36;
impCheckButton.but_y = y + int(h/2) - int(impButton_diameter/2);
}
@@ -657,7 +635,7 @@ class ChannelBar{
onOffButton.setIsActive(true);
}
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
if(impCheckButton.isMouseHere()){
println("[" + channelNumber + "] imp pressed");
impCheckButton.setIsActive(true);
@@ -683,7 +661,7 @@ class ChannelBar{
onOffButton.setIsActive(false);
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
if(impCheckButton.isMouseHere() && impCheckButton.isActive()){
println("[" + channelNumber + "] imp released");
w_timeSeries.hsc.toggleImpedanceCheck(channelNumber-1); // 'n' indicates the N inputs and '1' indicates test impedance
@@ -864,7 +842,7 @@ if(has_processed){
val_uV = processed_file.get(elm)[Ichan][startIndex];
data[Ichan][i] = (int) (0.5f+ val_uV / openBCI.get_scale_fac_uVolts_per_count()); //convert to counts, the 0.5 is to ensure roundi
data[Ichan][i] = (int) (0.5f+ val_uV / cyton.get_scale_fac_uVolts_per_count()); //convert to counts, the 0.5 is to ensure roundi
}
i++;
}
+84 -119
Ver Arquivo
@@ -98,37 +98,22 @@ class W_accelerometer extends Widget {
Z[i] = AccelWindowY + (AccelWindowHeight/4)*3; // Z at 3/4
}
if(eegDataSource == DATASOURCE_GANGLION){
// accelModeButton = new Button((int)(x + w/2), (int)(y +80), 120, navHeight - 6, "Turn Accel. On", 12);
accelModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn Accel. On", 12);
accelModeButton.setCornerRoundess((int)(navHeight-6));
accelModeButton.setFont(p6,10);
// accelModeButton.setStrokeColor((int)(color(150)));
// accelModeButton.setColorNotPressed(openbciBlue);
accelModeButton.setColorNotPressed(color(57,128,204));
accelModeButton.textColorNotActive = color(255);
// accelModeButton.setStrokeColor((int)(color(138, 182, 229, 100)));
accelModeButton.hasStroke(false);
// accelModeButton.setColorNotPressed((int)(color(138, 182, 229)));
accelModeButton.setHelpText("Click this button to activate/deactivate the accelerometer of your Ganglion board!");
}
//This is the protocol for setting up dropdowns.
//Note that these 3 dropdowns correspond to the 3 global functions below
//You just need to make sure the "id" (the 1st String) has the same name as the corresponding function
// addDropdown("Thisdrop", "Drop 1", Arrays.asList("A", "B"), 0);
// addDropdown("Dropdown2", "Drop 2", Arrays.asList("C", "D", "E"), 1);
// addDropdown("Dropdown3", "Drop 3", Arrays.asList("F", "G", "H", "I"), 3);
accelModeButton = new Button((int)(x + 3), (int)(y + 3 - navHeight), 120, navHeight - 6, "Turn Accel. On", 12);
accelModeButton.setCornerRoundess((int)(navHeight-6));
accelModeButton.setFont(p6,10);
accelModeButton.setColorNotPressed(color(57,128,204));
accelModeButton.textColorNotActive = color(255);
accelModeButton.hasStroke(false);
accelModeButton.setHelpText("Click this button to activate/deactivate the accelerometer!");
}
public void initPlayground(OpenBCI_ADS1299 _OBCI) {
public void initPlayground(Cyton _OBCI) {
OBCI_inited = true;
}
float adjustYMaxMinBasedOnSource(){
float _yMaxMin;
if(eegDataSource == DATASOURCE_NORMAL_W_AUX){
if(eegDataSource == DATASOURCE_CYTON){
_yMaxMin = 4.0;
}else if(eegDataSource == DATASOURCE_GANGLION || nchan == 4){
_yMaxMin = 2.0;
@@ -150,10 +135,10 @@ class W_accelerometer extends Widget {
currentYvalue = map(Y[Y.length-1], AccelWindowY, AccelWindowY+AccelWindowHeight, yMaxMin, -yMaxMin);
currentZvalue = map(Z[Z.length-1], AccelWindowY, AccelWindowY+AccelWindowHeight, yMaxMin, -yMaxMin);
shiftWave();
} else if (eegDataSource == DATASOURCE_NORMAL_W_AUX) {
currentXvalue = openBCI.validAuxValues[0] * openBCI.get_scale_fac_accel_G_per_count();
currentYvalue = openBCI.validAuxValues[1] * openBCI.get_scale_fac_accel_G_per_count();
currentZvalue = openBCI.validAuxValues[2] * openBCI.get_scale_fac_accel_G_per_count();
} else if (eegDataSource == DATASOURCE_CYTON) {
currentXvalue = hub.validAccelValues[0] * cyton.get_scale_fac_accel_G_per_count();
currentYvalue = hub.validAccelValues[1] * cyton.get_scale_fac_accel_G_per_count();
currentZvalue = hub.validAccelValues[2] * cyton.get_scale_fac_accel_G_per_count();
X[X.length-1] =
int(map(currentXvalue, -yMaxMin, yMaxMin, float(AccelWindowY+AccelWindowHeight), float(AccelWindowY)));
X[X.length-1] = constrain(X[X.length-1], AccelWindowY, AccelWindowY+AccelWindowHeight);
@@ -166,9 +151,9 @@ class W_accelerometer extends Widget {
shiftWave();
} else if (eegDataSource == DATASOURCE_GANGLION) {
currentXvalue = ganglion.accelArray[0] * ganglion.get_scale_fac_accel_G_per_count();
currentYvalue = ganglion.accelArray[1] * ganglion.get_scale_fac_accel_G_per_count();
currentZvalue = ganglion.accelArray[2] * ganglion.get_scale_fac_accel_G_per_count();
currentXvalue = hub.validAccelValues[0] * ganglion.get_scale_fac_accel_G_per_count();
currentYvalue = hub.validAccelValues[1] * ganglion.get_scale_fac_accel_G_per_count();
currentZvalue = hub.validAccelValues[2] * ganglion.get_scale_fac_accel_G_per_count();
X[X.length-1] =
int(map(currentXvalue, -yMaxMin, yMaxMin, float(AccelWindowY+AccelWindowHeight), float(AccelWindowY)));
X[X.length-1] = constrain(X[X.length-1], AccelWindowY, AccelWindowY+AccelWindowHeight);
@@ -194,90 +179,77 @@ class W_accelerometer extends Widget {
pushStyle();
//put your code here...
//remember to refer to x,y,w,h which are the positioning variables of the Widget class
if (true) {
// fill(graphBG);
// stroke(strokeColor);
// rect(x, y, w, h);
// textFont(f4, 24);
// textAlign(LEFT, TOP);
// fill(textColor);
// text("Acellerometer Gs", x + 10, y + 10);
fill(50);
textFont(p4, 14);
textAlign(CENTER,CENTER);
text("z", PolarWindowX, (PolarWindowY-PolarWindowHeight/2)-12);
text("x", (PolarWindowX+PolarWindowWidth/2)+8, PolarWindowY-5);
text("y", (PolarWindowX+PolarCorner)+10, (PolarWindowY-PolarCorner)-10);
fill(50);
textFont(p4, 14);
textAlign(CENTER,CENTER);
text("z", PolarWindowX, (PolarWindowY-PolarWindowHeight/2)-12);
text("x", (PolarWindowX+PolarWindowWidth/2)+8, PolarWindowY-5);
text("y", (PolarWindowX+PolarCorner)+10, (PolarWindowY-PolarCorner)-10);
fill(graphBG); // pulse window background
stroke(graphStroke);
rect(AccelWindowX, AccelWindowY, AccelWindowWidth, AccelWindowHeight);
line(AccelWindowX, AccelWindowY + AccelWindowHeight/2, AccelWindowX+AccelWindowWidth, AccelWindowY + AccelWindowHeight/2); //midline
fill(graphBG);
stroke(graphStroke);
rect(AccelWindowX, AccelWindowY, AccelWindowWidth, AccelWindowHeight);
line(AccelWindowX, AccelWindowY + AccelWindowHeight/2, AccelWindowX+AccelWindowWidth, AccelWindowY + AccelWindowHeight/2); //midline
fill(50);
textFont(p5, 12);
textAlign(CENTER,CENTER);
text("+"+(int)yMaxMin+"g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY);
text("0g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY + AccelWindowHeight/2);
text("-"+(int)yMaxMin+"g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY + AccelWindowHeight);
fill(50);
textFont(p5, 12);
textAlign(CENTER,CENTER);
text("+"+(int)yMaxMin+"g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY);
text("0g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY + AccelWindowHeight/2);
text("-"+(int)yMaxMin+"g", AccelWindowX+AccelWindowWidth + 12, AccelWindowY + AccelWindowHeight);
fill(graphBG); // pulse window background
stroke(graphStroke);
ellipse(PolarWindowX,PolarWindowY,PolarWindowWidth,PolarWindowHeight);
fill(graphBG); // pulse window background
stroke(graphStroke);
ellipse(PolarWindowX,PolarWindowY,PolarWindowWidth,PolarWindowHeight);
stroke(180);
line(PolarWindowX-PolarWindowWidth/2, PolarWindowY, PolarWindowX+PolarWindowWidth/2, PolarWindowY);
line(PolarWindowX, PolarWindowY-PolarWindowHeight/2, PolarWindowX, PolarWindowY+PolarWindowHeight/2);
line(PolarWindowX-PolarCorner, PolarWindowY+PolarCorner, PolarWindowX+PolarCorner, PolarWindowY-PolarCorner);
stroke(180);
line(PolarWindowX-PolarWindowWidth/2, PolarWindowY, PolarWindowX+PolarWindowWidth/2, PolarWindowY);
line(PolarWindowX, PolarWindowY-PolarWindowHeight/2, PolarWindowX, PolarWindowY+PolarWindowHeight/2);
line(PolarWindowX-PolarCorner, PolarWindowY+PolarCorner, PolarWindowX+PolarCorner, PolarWindowY-PolarCorner);
fill(50);
textFont(p3, 16);
fill(50);
textFont(p3, 16);
if (eegDataSource == DATASOURCE_NORMAL_W_AUX) { // LIVE
// fill(Xcolor);
// text("X " + nf(currentXvalue, 1, 3), x+10, y+40);
// fill(Ycolor);
// text("Y " + nf(currentYvalue, 1, 3), x+10, y+80);
// fill(Zcolor);
// text("Z " + nf(currentZvalue, 1, 3), x+10, y+120);
drawAccValues();
draw3DGraph();
drawAccWave();
} else if (eegDataSource == DATASOURCE_GANGLION) {
if (eegDataSource == DATASOURCE_CYTON) { // LIVE
// fill(Xcolor);
// text("X " + nf(currentXvalue, 1, 3), x+10, y+40);
// fill(Ycolor);
// text("Y " + nf(currentYvalue, 1, 3), x+10, y+80);
// fill(Zcolor);
// text("Z " + nf(currentZvalue, 1, 3), x+10, y+120);
drawAccValues();
draw3DGraph();
drawAccWave();
if (cyton.getBoardMode() != BOARD_MODE_DEFAULT) {
accelModeButton.setString("Turn Accel On");
accelModeButton.draw();
drawAccValues();
draw3DGraph();
drawAccWave();
} else if (eegDataSource == DATASOURCE_SYNTHETIC) { // SYNTHETIC
// fill(Xcolor);
// text("X "+nf(currentXvalue, 1, 3), x+10, y+40);
// fill(Ycolor);
// text("Y "+nf(currentYvalue, 1, 3), x+10, y+80);
// fill(Zcolor);
// text("Z "+nf(currentZvalue, 1, 3), x+10, y+120);
drawAccValues();
draw3DGraph();
drawAccWave();
}
else { // PLAYBACK
drawAccValues();
draw3DGraph();
drawAccWave2();
}
} else if (eegDataSource == DATASOURCE_GANGLION) {
if (ganglion.isBLE()) accelModeButton.draw();
drawAccValues();
draw3DGraph();
drawAccWave();
} else if (eegDataSource == DATASOURCE_SYNTHETIC) { // SYNTHETIC
drawAccValues();
draw3DGraph();
drawAccWave();
}
else { // PLAYBACK
drawAccValues();
draw3DGraph();
drawAccWave2();
}
// pushStyle();
// textFont(h1,24);
// fill(bgColor);
// textAlign(CENTER,CENTER);
// text(widgetTitle, x + w/2, y + h/2);
// popStyle();
popStyle();
}
void setGraphDimensions(){
println("accel w "+w);
println("accel h "+h);
println("accel x "+x);
println("accel y "+y);
AccelWindowWidth = w - padding*2;
AccelWindowHeight = int((float(h) - float(padding*3))/2.0);
AccelWindowX = x + padding;
@@ -301,18 +273,6 @@ class W_accelerometer extends Widget {
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
int dy = y - prevY;
println("dy = " + dy);
//put your code here...
// AccelWindowWidth = int(w) - 10;
// AccelWindowX = int(x)+5;
// AccelWindowY = int(y)-10+int(h)/2;
//
// PolarWindowX = x+AccelWindowWidth-90;
// PolarWindowY = y+83;
// PolarCorner = (sqrt(2)*PolarWindowWidth/2)/2;
println("Acc Widget -- Screen Resized.");
setGraphDimensions();
//empty arrays to start redrawing from scratch
@@ -320,15 +280,9 @@ class W_accelerometer extends Widget {
X[i] = AccelWindowY + AccelWindowHeight/4; // X at 1/4
Y[i] = AccelWindowY + AccelWindowHeight/2; // Y at 1/2
Z[i] = AccelWindowY + (AccelWindowHeight/4)*3; // Z at 3/4
// X[i] = X[i] + dy;
// Y[i] = Y[i] + dy;
// Z[i] = Z[i] + dy;
}
if(eegDataSource == DATASOURCE_GANGLION){
// accelModeButton.setPos((int)(x + w/2 - accelModeButton.but_dx/2), (int)(y + 80));
accelModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
accelModeButton.setPos((int)(x + 3), (int)(y + 3 - navHeight));
}
void mousePressed(){
@@ -337,6 +291,12 @@ class W_accelerometer extends Widget {
//put your code here...
if(eegDataSource == DATASOURCE_GANGLION){
//put your code here...
if (ganglion.isBLE()) {
if (accelModeButton.isMouseHere()) {
accelModeButton.setIsActive(true);
}
}
} else if (eegDataSource == DATASOURCE_CYTON) {
if (accelModeButton.isMouseHere()) {
accelModeButton.setIsActive(true);
}
@@ -350,7 +310,6 @@ class W_accelerometer extends Widget {
if(eegDataSource == DATASOURCE_GANGLION){
//put your code here...
if(accelModeButton.isActive && accelModeButton.isMouseHere()){
println("toggle...");
if(ganglion.isAccelModeActive()){
ganglion.accelStop();
@@ -361,6 +320,12 @@ class W_accelerometer extends Widget {
}
}
accelModeButton.setIsActive(false);
} else if (eegDataSource == DATASOURCE_CYTON) {
if(accelModeButton.isActive && accelModeButton.isMouseHere()){
cyton.setBoardMode(BOARD_MODE_DEFAULT);
output("Starting to read accelerometer");
}
accelModeButton.setIsActive(false);
}
}
+2 -2
Ver Arquivo
@@ -93,7 +93,7 @@ class W_ganglionImpedance extends Widget {
popStyle();
}
if(isHubInitialized && isGanglionObjectInitialized && eegDataSource == DATASOURCE_GANGLION){
if(isHubInitialized && isHubObjectInitialized && eegDataSource == DATASOURCE_GANGLION){
if(ganglion.isCheckingImpedance()){
image(loadingGIF_blue, x + padding + startStopCheck.but_dx + 15, y + padding - 8, 40, 40);
}
@@ -138,7 +138,7 @@ class W_ganglionImpedance extends Widget {
//put your code here...
if(startStopCheck.isActive && startStopCheck.isMouseHere()){
if(isHubInitialized && isGanglionObjectInitialized && eegDataSource == DATASOURCE_GANGLION){
if(isHubInitialized && isHubObjectInitialized && eegDataSource == DATASOURCE_GANGLION){
if(ganglion.isCheckingImpedance()){
ganglion.impedanceStop();
startStopCheck.but_txt = "Start Impedance Check";
+102 -9
Ver Arquivo
@@ -63,7 +63,15 @@ class W_headPlot extends Widget {
super.screenResized(); //calls the parent screenResized() method of Widget (DON'T REMOVE)
//put your code here...
headPlot.setPositionSize(x, y, w, h, width, height); //update position of headplot
headPlot.hp_x = x;
headPlot.hp_y = y;
headPlot.hp_w = w;
headPlot.hp_h = h;
headPlot.hp_win_x = x;
headPlot.hp_win_y = y;
thread("doHardCalcs");
// headPlot.setPositionSize(x, y, w, h, width, height); //update position of headplot
}
@@ -71,14 +79,21 @@ class W_headPlot extends Widget {
super.mousePressed(); //calls the parent mousePressed() method of Widget (DON'T REMOVE)
//put your code here...
headPlot.mousePressed();
}
void mouseReleased(){
super.mouseReleased(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
headPlot.mouseReleased();
}
void mouseDragged(){
super.mouseDragged(); //calls the parent mouseReleased() method of Widget (DON'T REMOVE)
//put your code here...
headPlot.mouseDragged();
}
//add custom class functions here
@@ -99,7 +114,7 @@ void Ten20(int n) { //triggered when there is an event in the Ten20 Dropdown
*/
//fft_widget.fft_plot.setXLim(0.1, fft_widget.xLimOptions[n]); //update the xLim of the FFT_Plot
println("BOOOOM!" + n);
// println("BOOOOM!" + n);
closeAllDropdowns(); // do this at the end of all widget-activated functions to ensure proper widget interactivity ... we want to make sure a click makes the menu close
}
@@ -166,6 +181,16 @@ void updateVertScale() {
vertScale_uV = default_vertScale_uV * vertScaleFactor[vertScaleFactor_ind];
w_headPlot.headPlot.setMaxIntensity_uV(vertScale_uV);
}
void doHardCalcs() {
if (!w_headPlot.headPlot.threadLock) {
w_headPlot.headPlot.threadLock = true;
w_headPlot.headPlot.setPositionSize(w_headPlot.headPlot.hp_x, w_headPlot.headPlot.hp_y, w_headPlot.headPlot.hp_w, w_headPlot.headPlot.hp_h, w_headPlot.headPlot.hp_win_x, w_headPlot.headPlot.hp_win_y);
w_headPlot.headPlot.hardCalcsDone = true;
w_headPlot.headPlot.threadLock = false;
}
}
//---------------------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////
@@ -208,8 +233,20 @@ class HeadPlot {
private boolean plot_color_as_log = true;
public float smooth_fac = 0.0f;
private boolean use_polarity = true;
private int mouse_over_elec_index = -1;
private boolean isDragging = false;
private float drag_x, drag_y;
public int hp_win_x = 0;
public int hp_win_y = 0;
public int hp_x = 0;
public int hp_y = 0;
public int hp_w = 0;
public int hp_h = 0;
public boolean hardCalcsDone = false;
public boolean threadLock = false;
HeadPlot(float x, float y, float w, float h, int win_x, int win_y, int n) {
final int n_elec = n; //8 electrodes assumed....or 16 for 16-channel? Change this!!!
nose_x = new int[3];
nose_y = new int[3];
@@ -225,7 +262,6 @@ class HeadPlot {
rel_width = w;
rel_height = h;
setWindowDimensions(win_x, win_y);
setMaxIntensity_uV(200.0f); //default intensity scaling for electrodes
}
@@ -252,7 +288,14 @@ class HeadPlot {
//rel_height = float(_h)/_win_y;
//setWindowDimensions(_win_x, _win_y);
setPositionSize(_x, _y, _w, _h, _win_x, _win_y);
hp_x = _x;
hp_y = _y;
hp_w = _w;
hp_h = _h;
hp_win_x = _win_x;
hp_win_y = _win_y;
thread("doHardCalcs");
// setPositionSize(_x, _y, _w, _h, _win_x, _win_y);
setMaxIntensity_uV(200.0f); //default intensity scaling for electrodes
}
@@ -639,17 +682,22 @@ class HeadPlot {
int toPixels[][][][] = new int[n_wide][n_tall][4][2];
int toElectrodes[][][] = new int[n_wide][n_tall][4];
//int numConnections[][] = new int[n_wide][n_tall];
// println(" HeadPlot B 2 0 -- " + millis());
//find which pixesl are within the head and which pixels are within an electrode
whereAreThePixels(pixelAddress, withinHead, withinElectrode);
// println(" HeadPlot B 2 1 -- " + millis());
//loop over the pixels and make all the connections
makeAllTheConnections(withinHead, withinElectrode, toPixels, toElectrodes);
// println(" HeadPlot B 2 3 -- " + millis());
//compute the pixel values when lighting up each electrode invididually
for (int Ielec=0; Ielec<n_elec; Ielec++) {
computeWeightFactorsGivenOneElectrode_iterative(toPixels, toElectrodes, Ielec, weightFac);
}
// println(" HeadPlot B 2 4 -- " + millis());
}
private void cleanUpTheBoundaries(int pixelAddress[][][], float weightFac[][][]) {
@@ -1223,6 +1271,38 @@ class HeadPlot {
}
}
private boolean isMouseOverElectrode(int n){
float elec_mouse_x_dist = electrode_xy[n][0] - mouseX;
float elec_mouse_y_dist = electrode_xy[n][1] - mouseY;
return elec_mouse_x_dist * elec_mouse_x_dist + elec_mouse_y_dist * elec_mouse_y_dist < elec_diam * elec_diam / 4;
}
private boolean isDraggedElecInsideHead() {
int dx = mouseX - circ_x;
int dy = mouseY - circ_y;
return dx * dx + dy * dy < (circ_diam - elec_diam) * (circ_diam - elec_diam) / 4;
}
void mousePressed() {
if (mouse_over_elec_index > -1) {
isDragging = true;
drag_x = mouseX - electrode_xy[mouse_over_elec_index][0];
drag_y = mouseY - electrode_xy[mouse_over_elec_index][1];
} else {
isDragging = false;
}
}
void mouseDragged() {
if (isDragging && mouse_over_elec_index > -1 && isDraggedElecInsideHead()) {
electrode_xy[mouse_over_elec_index][0] = mouseX - drag_x;
electrode_xy[mouse_over_elec_index][1] = mouseY - drag_y;
}
}
void mouseReleased() {
isDragging = false;
}
public boolean isPixelInsideHead(int pixel_x, int pixel_y) {
int dx = pixel_x - circ_x;
@@ -1246,8 +1326,10 @@ class HeadPlot {
if (drawHeadAsContours) updateHeadImage();
} else {
//update head voltages
updateHeadVoltages();
convertVoltagesToHeadImage();
if (!threadLock && hardCalcsDone) {
updateHeadVoltages();
convertVoltagesToHeadImage();
}
}
}
@@ -1275,14 +1357,25 @@ class HeadPlot {
}
//draw electrodes on the head
strokeWeight(1);
if (!isDragging) {
mouse_over_elec_index = -1;
}
for (int Ielec=0; Ielec < electrode_xy.length; Ielec++) {
if (drawHeadAsContours) {
noFill(); //make transparent to allow color to come through from below
} else {
fill(electrode_rgb[0][Ielec], electrode_rgb[1][Ielec], electrode_rgb[2][Ielec]);
}
ellipse(electrode_xy[Ielec][0], electrode_xy[Ielec][1], elec_diam, elec_diam); //big circle for the head
if (!isDragging && isMouseOverElectrode(Ielec)) {
//electrode with a bigger index gets priority in dragging
mouse_over_elec_index = Ielec;
strokeWeight(2);
} else if (mouse_over_elec_index == Ielec) {
strokeWeight(2);
} else{
strokeWeight(1);
}
ellipse(electrode_xy[Ielec][0], electrode_xy[Ielec][1], elec_diam, elec_diam); //electrode circle
}
//add labels to electrodes
+221 -119
Ver Arquivo
@@ -75,9 +75,11 @@ class W_networking extends Widget {
stream1 = null;
stream2 = null;
stream3 = null;
dataTypes = Arrays.asList("None", "TimeSeries", "FFT", "EMG", "PowerBands", "Widget");
defaultBaud = "9600";
baudRates = Arrays.asList("1200", "9600", "57600", "115200");
dataTypes = Arrays.asList("None", "TimeSeries", "FFT", "EMG", "BandPower", "Focus", "Widget");
defaultBaud = "115200";
// baudRates = Arrays.asList("1200", "9600", "57600", "115200");
baudRates = Arrays.asList("57600", "115200", "250000", "500000");
protocolMode = "OSC"; //default to OSC
addDropdown("Protocol", "Protocol", Arrays.asList("OSC", "UDP", "LSL", "Serial"), protocolIndex);
comPorts = new ArrayList<String>(Arrays.asList(Serial.list()));
@@ -735,9 +737,11 @@ class W_networking extends Widget {
break;
case 3 : dt1 = "EMG";
break;
case 4 : dt1 = "PowerBands";
case 4 : dt1 = "BandPower";
break;
case 5 : dt1 = "Widget";
case 5 : dt1 = "Focus";
break;
case 6 : dt1 = "Widget";
break;
}
switch ((int)cp5_networking_dropdowns.get(ScrollableList.class, "dataType2").getValue()){
@@ -749,9 +753,11 @@ class W_networking extends Widget {
break;
case 3 : dt2 = "EMG";
break;
case 4 : dt2 = "PowerBands";
case 4 : dt2 = "BandPower";
break;
case 5 : dt2 = "Widget";
case 5 : dt2 = "Focus";
break;
case 6 : dt2 = "Widget";
break;
}
switch ((int)cp5_networking_dropdowns.get(ScrollableList.class, "dataType3").getValue()){
@@ -763,9 +769,11 @@ class W_networking extends Widget {
break;
case 3 : dt3 = "EMG";
break;
case 4 : dt3 = "PowerBands";
case 4 : dt3 = "BandPower";
break;
case 5 : dt3 = "Widget";
case 5 : dt3 = "Focus";
break;
case 6 : dt3 = "Widget";
break;
}
@@ -776,7 +784,7 @@ class W_networking extends Widget {
port = Integer.parseInt(cp5_networking.get(Textfield.class, "osc_port1").getText());
address = cp5_networking.get(Textfield.class, "osc_address1").getText();
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter1").getValue();
stream1 = new Stream(dt1,ip,port,address,filt_pos);
stream1 = new Stream(dt1, ip, port, address, filt_pos, nchan);
}else{
stream1 = null;
}
@@ -785,7 +793,7 @@ class W_networking extends Widget {
port = Integer.parseInt(cp5_networking.get(Textfield.class, "osc_port2").getText());
address = cp5_networking.get(Textfield.class, "osc_address2").getText();
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter2").getValue();
stream2 = new Stream(dt2, ip,port,address,filt_pos);
stream2 = new Stream(dt2, ip, port, address, filt_pos, nchan);
}else{
stream2 = null;
}
@@ -794,7 +802,7 @@ class W_networking extends Widget {
port = Integer.parseInt(cp5_networking.get(Textfield.class, "osc_port3").getText());
address = cp5_networking.get(Textfield.class, "osc_address3").getText();
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter3").getValue();
stream3 = new Stream(dt3, ip,port,address,filt_pos);
stream3 = new Stream(dt3, ip, port, address, filt_pos, nchan);
}else{
stream3 = null;
}
@@ -805,7 +813,7 @@ class W_networking extends Widget {
ip = cp5_networking.get(Textfield.class, "udp_ip1").getText();
port = Integer.parseInt(cp5_networking.get(Textfield.class, "udp_port1").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter1").getValue();
stream1 = new Stream(dt1,ip,port,filt_pos);
stream1 = new Stream(dt1, ip, port, filt_pos, nchan);
}else{
stream1 = null;
}
@@ -813,7 +821,7 @@ class W_networking extends Widget {
ip = cp5_networking.get(Textfield.class, "udp_ip2").getText();
port = Integer.parseInt(cp5_networking.get(Textfield.class, "udp_port2").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter2").getValue();
stream2 = new Stream(dt2,ip,port,filt_pos);
stream2 = new Stream(dt2, ip, port, filt_pos, nchan);
}else{
stream2 = null;
}
@@ -821,7 +829,7 @@ class W_networking extends Widget {
ip = cp5_networking.get(Textfield.class, "udp_ip3").getText();
port = Integer.parseInt(cp5_networking.get(Textfield.class, "udp_port3").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter3").getValue();
stream3 = new Stream(dt3,ip,port,filt_pos);
stream3 = new Stream(dt3, ip, port, filt_pos, nchan);
}else{
stream3 = null;
}
@@ -833,7 +841,7 @@ class W_networking extends Widget {
type = cp5_networking.get(Textfield.class, "lsl_type1").getText();
nChanLSL = Integer.parseInt(cp5_networking.get(Textfield.class, "lsl_numchan1").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter1").getValue();
stream1 = new Stream(dt1,name,type,nChanLSL,filt_pos);
stream1 = new Stream(dt1, name, type, nChanLSL, filt_pos, nchan);
}else{
stream1 = null;
}
@@ -842,7 +850,7 @@ class W_networking extends Widget {
type = cp5_networking.get(Textfield.class, "lsl_type2").getText();
nChanLSL = Integer.parseInt(cp5_networking.get(Textfield.class, "lsl_numchan2").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter2").getValue();
stream2 = new Stream(dt2,name,type,nChanLSL,filt_pos);
stream2 = new Stream(dt2, name, type, nChanLSL, filt_pos, nchan);
}else{
stream2 = null;
}
@@ -851,7 +859,7 @@ class W_networking extends Widget {
type = cp5_networking.get(Textfield.class, "lsl_type3").getText();
nChanLSL = Integer.parseInt(cp5_networking.get(Textfield.class, "lsl_numchan3").getText());
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter3").getValue();
stream3 = new Stream(dt3,name,type,nChanLSL,filt_pos);
stream3 = new Stream(dt3, name, type, nChanLSL, filt_pos, nchan);
}else{
stream3 = null;
}
@@ -865,7 +873,7 @@ class W_networking extends Widget {
baudRate = Integer.parseInt(baudRates.get((int)(cp5_networking_baudRate.get(ScrollableList.class, "baud_rate").getValue())));
filt_pos = (int)cp5_networking.get(RadioButton.class, "filter1").getValue();
stream1 = new Stream(dt1,name,baudRate,filt_pos,pApplet); //String dataType, String portName, int baudRate, int filter, PApplet _this
stream1 = new Stream(dt1, name, baudRate, filt_pos, pApplet, nchan); //String dataType, String portName, int baudRate, int filter, PApplet _this
}else{
stream1 = null;
}
@@ -930,15 +938,15 @@ class Stream extends Thread{
String streamType;
String streamName;
int nChanLSL;
int numChan = 0;
Boolean isStreaming;
Boolean newData = false;
int numChan = nchan;
// Data buffers
int start = dataBuffY_filtY_uV[0].length-11;
int end = dataBuffY_filtY_uV[0].length-1;
int bufferLen = end-start;
float[] dataToSend = new float[numChan];
float[] dataToSend = new float[numChan*bufferLen];
//OSC Objects
OscP5 osc;
@@ -961,10 +969,17 @@ class Stream extends Thread{
PApplet pApplet;
private void updateNumChan(int _numChan) {
numChan = _numChan;
println("Stream update numChan to " + numChan);
dataToSend = new float[numChan * nPointsPerUpdate];
println("nPointsPerUpdate " + nPointsPerUpdate);
println("dataToSend len: " + numChan * nPointsPerUpdate);
}
/* OSC Stream */
Stream(String dataType, String ip, int port, String address, int filter){
Stream(String dataType, String ip, int port, String address, int filter, int _nchan){
this.protocol = "OSC";
this.dataType = dataType;
this.ip = ip;
@@ -972,19 +987,21 @@ class Stream extends Thread{
this.address = address;
this.filter = filter;
this.isStreaming = false;
updateNumChan(_nchan);
try{
closeNetwork(); //make sure everything is closed!
}catch (Exception e){
}
}
/*UDP Stream */
Stream(String dataType, String ip, int port, int filter){
Stream(String dataType, String ip, int port, int filter, int _nchan){
this.protocol = "UDP";
this.dataType = dataType;
this.ip = ip;
this.port = port;
this.filter = filter;
this.isStreaming = false;
updateNumChan(_nchan);
if(this.dataType.equals("TimeSeries")){
buffer = ByteBuffer.allocate(4*numChan);
}else{
@@ -996,7 +1013,7 @@ class Stream extends Thread{
}
}
/* LSL Stream */
Stream(String dataType, String streamName, String streamType, int nChanLSL, int filter){
Stream(String dataType, String streamName, String streamType, int nChanLSL, int filter, int _nchan){
this.protocol = "LSL";
this.dataType = dataType;
this.streamName = streamName;
@@ -1004,6 +1021,7 @@ class Stream extends Thread{
this.nChanLSL = nChanLSL;
this.filter = filter;
this.isStreaming = false;
updateNumChan(_nchan);
try{
closeNetwork(); //make sure everything is closed!
}catch (Exception e){
@@ -1011,7 +1029,7 @@ class Stream extends Thread{
}
// Serial Stream %%%%%
Stream(String dataType, String portName, int baudRate, int filter, PApplet _this){
Stream(String dataType, String portName, int baudRate, int filter, PApplet _this, int _nchan){
// %%%%%
this.protocol = "Serial";
this.dataType = dataType;
@@ -1020,6 +1038,7 @@ class Stream extends Thread{
this.filter = filter;
this.isStreaming = false;
this.pApplet = _this;
updateNumChan(_nchan);
if(this.dataType.equals("TimeSeries")){
buffer = ByteBuffer.allocate(4*numChan);
}else{
@@ -1060,8 +1079,10 @@ class Stream extends Thread{
sendFFTData();
}else if (this.dataType.equals("EMG")){
sendEMGData();
}else if (this.dataType.equals("PowerBands")){
}else if (this.dataType.equals("BandPower")){
sendPowerBandData();
}else if (this.dataType.equals("Focus")){
sendFocusData();
}else if (this.dataType.equals("WIDGET")){
sendWidgetData();
}
@@ -1090,8 +1111,10 @@ class Stream extends Thread{
sendFFTData();
}else if (this.dataType.equals("EMG")){
sendEMGData();
}else if (this.dataType.equals("PowerBands")){
}else if (this.dataType.equals("BandPower")){
sendPowerBandData();
}else if (this.dataType.equals("Focus")){
sendFocusData();
}else if (this.dataType.equals("WIDGET")){
sendWidgetData();
}
@@ -1109,7 +1132,9 @@ class Stream extends Thread{
return dataProcessing.newDataToSend;
}else if (this.dataType.equals("EMG")){
return dataProcessing.newDataToSend;
}else if (this.dataType.equals("PowerBands")){
}else if (this.dataType.equals("BandPower")){
return dataProcessing.newDataToSend;
}else if (this.dataType.equals("Focus")){
return dataProcessing.newDataToSend;
}else if (this.dataType.equals("WIDGET")){
/* ENTER YOUR WIDGET "NEW DATA" RETURN FUNCTION */
@@ -1124,7 +1149,9 @@ class Stream extends Thread{
dataProcessing.newDataToSend = false;
}else if (this.dataType.equals("EMG")){
dataProcessing.newDataToSend = false;
}else if (this.dataType.equals("PowerBands")){
}else if (this.dataType.equals("BandPower")){
dataProcessing.newDataToSend = false;
}else if (this.dataType.equals("Focus")){
dataProcessing.newDataToSend = false;
}else if (this.dataType.equals("WIDGET")){
/* ENTER YOUR WIDGET "NEW DATA" RETURN FUNCTION */
@@ -1136,7 +1163,7 @@ class Stream extends Thread{
if(filter==0){
// OSC
if(this.protocol.equals("OSC")){
for(int i=0;i<bufferLen;i++){
for(int i=0;i<nPointsPerUpdate;i++){
msg.clearArguments();
for(int j=0;j<numChan;j++){
msg.add(yLittleBuff_uV[j][i]);
@@ -1149,24 +1176,33 @@ class Stream extends Thread{
}
// UDP
}else if (this.protocol.equals("UDP")){
for(int i=0;i<bufferLen;i++){
buffer.rewind();
for(int j=0;j<numChan;j++){
buffer.putFloat(yLittleBuff_uV[j][i]);
for(int i=0;i<nPointsPerUpdate;i++){
String outputter = "{\"type\":\"eeg\",\"data\":[";
for (int j = 0; j < numChan; j++){
outputter += str(yLittleBuff_uV[j][i]);
if (j != numChan - 1) {
outputter += ",";
} else {
outputter += "]}\r\n";
}
}
this.udp.send(buffer.array(),this.ip,this.port);
}
// LSL
}else if (this.protocol.equals("LSL")){
for(int i=0;i<bufferLen;i++){
for(int j=0;j<numChan;j++){
dataToSend[j] = yLittleBuff_uV[j][i];
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
outlet_data.push_sample(dataToSend);
}
// LSL
} else if (this.protocol.equals("LSL")) {
for (int i=0; i<nPointsPerUpdate;i++){
for(int j=0;j<numChan;j++){
dataToSend[j+numChan*i] = yLittleBuff_uV[j][i];
}
}
outlet_data.push_chunk(dataToSend);
// SERIAL
}else if (this.protocol.equals("Serial")){ // Serial Output unfiltered
for(int i=0;i<bufferLen;i++){
for(int i=0;i<nPointsPerUpdate;i++){
serialMessage = "["; //clear message
for(int j=0;j<numChan;j++){
float chan_uV = yLittleBuff_uV[j][i];//get chan uV float value and truncate to 3 decimal places
@@ -1190,7 +1226,7 @@ class Stream extends Thread{
// TIME SERIES FILTERED
}else if (filter==1){
if (this.protocol.equals("OSC")){
for(int i=0;i<bufferLen;i++){
for(int i=0;i<nPointsPerUpdate;i++){
msg.clearArguments();
for(int j=0;j<numChan;j++){
msg.add(dataBuffY_filtY_uV[j][start+i]);
@@ -1201,23 +1237,32 @@ class Stream extends Thread{
println(e);
}
}
}else if (this.protocol.equals("UDP")){
for(int i=0;i<bufferLen;i++){
buffer.rewind();
for(int j=0;j<numChan;j++){
buffer.putFloat(dataBuffY_filtY_uV[j][start+i]);
} else if (this.protocol.equals("UDP")){
for(int i=0;i<nPointsPerUpdate;i++){
String outputter = "{\"type\":\"eeg\",\"data\":[";
for (int j = 0; j < numChan; j++){
outputter += str(dataBuffY_filtY_uV[j][start+i]);
if (j != numChan - 1) {
outputter += ",";
} else {
outputter += "]}\r\n";
}
}
this.udp.send(buffer.array(),this.ip,this.port);
}
}else if (this.protocol.equals("LSL")){
for(int i=0;i<bufferLen;i++){
for(int j=0;j<numChan;j++){
dataToSend[j] = dataBuffY_filtY_uV[j][i];
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
outlet_data.push_sample(dataToSend);
}
}else if (this.protocol.equals("LSL")){
for (int i=0; i<nPointsPerUpdate;i++){
for(int j=0;j<numChan;j++){
dataToSend[j+numChan*i] = dataBuffY_filtY_uV[j][i];
}
}
outlet_data.push_chunk(dataToSend);
}else if (this.protocol.equals("Serial")){
for(int i=0;i<bufferLen;i++){
for(int i=0;i<nPointsPerUpdate;i++){
serialMessage = "["; //clear message
for(int j=0;j<numChan;j++){
float chan_uV_filt = dataBuffY_filtY_uV[j][start+i];//get chan uV float value and truncate to 3 decimal places
@@ -1258,28 +1303,28 @@ class Stream extends Thread{
}
// UDP
}else if (this.protocol.equals("UDP")){
for (int i=0;i<numChan;i++){
buffer.rewind();
buffer.putFloat(i+1);
for (int j=0;j<125;j++){
buffer.putFloat(fftBuff[i].getBand(j));
String outputter = "{\"type\":\"fft\",\"data\":[[";
for (int i = 0;i < numChan; i++){
for (int j = 0; j < 125; j++) {
outputter += str(fftBuff[i].getBand(j));
if (j != 125 - 1) {
outputter += ",";
}
}
try{
this.udp.send(buffer.array(),this.ip,this.port);
}catch (Exception e){
println(e);
if (i != numChan - 1) {
outputter += "],[";
} else {
outputter += "]]}\r\n";
}
}
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
// LSL
}else if (this.protocol.equals("LSL")){
// if(filter==0){
// for(int i=0;i<bufferLen;i++){
// for(int j=0;j<numChan;j++){
// dataToSend[j] = fftBuff[j][i];
// }
// outlet_data.push_sample(dataToSend);
// }
// }
/* */
}else if (this.protocol.equals("Serial")){
// Send FFT Data over Serial ... %%%%%
// println("Sending FFT data over Serial...");
@@ -1307,7 +1352,7 @@ class Stream extends Thread{
void sendPowerBandData(){
// UNFILTERED & FILTERED ... influenced globally by the FFT filters dropdown ... just like the FFT data
int numPowerBands = 5; //DELTA, THETA, ALPHA, BETA, GAMMA
int numBandPower = 5; //DELTA, THETA, ALPHA, BETA, GAMMA
if(this.filter==0 || this.filter==1){
// OSC
@@ -1315,8 +1360,7 @@ class Stream extends Thread{
for (int i=0;i<numChan;i++){
msg.clearArguments();
msg.add(i+1);
for (int j=0;j<numPowerBands;j++){
// msg.add(fftBuff[i].getBand(j));
for (int j=0;j<numBandPower;j++){
msg.add(dataProcessing.avgPowerInBins[i][j]); // [CHAN][BAND]
}
try{
@@ -1327,36 +1371,44 @@ class Stream extends Thread{
}
// UDP
}else if (this.protocol.equals("UDP")){
for (int i=0;i<numChan;i++){
buffer.rewind();
buffer.putFloat(i+1);
for (int j=0;j<numPowerBands;j++){
buffer.putFloat(dataProcessing.avgPowerInBins[i][j]); //[CHAN][BAND]
// DELTA, THETA, ALPHA, BETA, GAMMA
String outputter = "{\"type\":\"bandPower\",\"data\":[[";
for (int i = 0;i < numChan; i++){
for (int j=0;j<numBandPower;j++){
outputter += str(dataProcessing.avgPowerInBins[i][j]); //[CHAN][BAND]
if (j != numBandPower - 1) {
outputter += ",";
}
}
try{
this.udp.send(buffer.array(),this.ip,this.port);
}catch (Exception e){
println(e);
if (i != numChan - 1) {
outputter += "],[";
} else {
outputter += "]]}\r\n";
}
}
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
// LSL
}else if (this.protocol.equals("LSL")){
// if(filter==0){
// for(int i=0;i<bufferLen;i++){
// for(int j=0;j<numChan;j++){
// dataToSend[j] = fftBuff[j][i];
// }
// outlet_data.push_sample(dataToSend);
// }
// }
float[] avgPowerLSL = new float[numChan*numBandPower];
for (int i=0; i<numChan;i++){
for(int j=0;j<numBandPower;j++){
dataToSend[j+numChan*i] = dataProcessing.avgPowerInBins[i][j];
}
}
outlet_data.push_chunk(dataToSend);
}else if (this.protocol.equals("Serial")){
for (int i=0;i<numChan;i++){
serialMessage = "[" + (i+1) + ","; //clear message
for (int j=0;j<numPowerBands;j++){
for (int j=0;j<numBandPower;j++){
float power_band = dataProcessing.avgPowerInBins[i][j];
String power_band_3dec = String.format("%.3f", power_band);
serialMessage += power_band_3dec;
if(j < numPowerBands-1){
if(j < numBandPower-1){
serialMessage += ","; //add a comma to serialMessage to separate chan values, as long as it isn't last value...
}
}
@@ -1383,7 +1435,7 @@ class Stream extends Thread{
msg.add(i+1);
//ADD NORMALIZED EMG CHANNEL DATA
msg.add(w_emg.motorWidgets[i].output_normalized);
println(i + " | " + w_emg.motorWidgets[i].output_normalized);
// println(i + " | " + w_emg.motorWidgets[i].output_normalized);
try{
this.osc.send(msg,this.netaddress);
}catch (Exception e){
@@ -1391,28 +1443,29 @@ class Stream extends Thread{
}
}
// UDP
}else if (this.protocol.equals("UDP")){
for (int i=0;i<numChan;i++){
buffer.rewind();
buffer.putFloat(i+1);
//ADD NORMALIZED EMG CHANNEL DATA
buffer.putFloat(w_emg.motorWidgets[i].output_normalized);
try{
this.udp.send(buffer.array(),this.ip,this.port);
}catch (Exception e){
println(e);
} else if (this.protocol.equals("UDP")) {
String outputter = "{\"type\":\"emg\",\"data\":[";
for (int i = 0;i < numChan; i++){
outputter += str(w_emg.motorWidgets[i].output_normalized);
if (i != numChan - 1) {
outputter += ",";
} else {
outputter += "]}\r\n";
}
}
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
// LSL
}else if (this.protocol.equals("LSL")){
// if(filter==0){
// for(int i=0;i<bufferLen;i++){
// for(int j=0;j<numChan;j++){
// dataToSend[j] = fftBuff[j][i];
// }
// outlet_data.push_sample(dataToSend);
// }
// }
if(filter==0){
for(int j=0;j<numChan;j++){
dataToSend[j] = w_emg.motorWidgets[j].output_normalized;
}
outlet_data.push_sample(dataToSend);
}
}else if (this.protocol.equals("Serial")){ // Send NORMALIZED EMG CHANNEL Data over Serial ... %%%%%
for (int i=0;i<numChan;i++){
serialMessage = "[" + (i+1) + ","; //clear message
@@ -1420,7 +1473,7 @@ class Stream extends Thread{
String emg_normalized_3dec = String.format("%.3f", emg_normalized);
serialMessage += emg_normalized_3dec + "]";
try{
println(serialMessage);
// println(serialMessage);
this.serial_networking.write(serialMessage);
}catch (Exception e){
println(e);
@@ -1430,6 +1483,55 @@ class Stream extends Thread{
}
}
void sendFocusData(){
// UNFILTERED & FILTERED ... influenced globally by the FFT filters dropdown ... just like the FFT data
if(this.filter==0 || this.filter==1){
// OSC
if (this.protocol.equals("OSC")){
msg.clearArguments();
//ADD Focus Data
msg.add(w_focus.isFocused);
println(w_focus.isFocused);
try{
this.osc.send(msg,this.netaddress);
}catch (Exception e){
println(e);
}
// UDP
}else if (this.protocol.equals("UDP")){
String outputter = "{\"type\":\"focus\",\"data\":";
outputter += str(w_focus.isFocused ? 1.0 : 0.0);
outputter += "]}\r\n";
try {
this.udp.send(outputter, this.ip, this.port);
} catch (Exception e) {
println(e);
}
// LSL
}else if (this.protocol.equals("LSL")){
// convert boolean to float and only sends the first data
float temp = w_focus.isFocused ? 1.0 : 0.0;
dataToSend[0] = temp;
outlet_data.push_chunk(dataToSend);
// Serial
}else if (this.protocol.equals("Serial")){ // Send NORMALIZED EMG CHANNEL Data over Serial ... %%%%%
for (int i=0;i<numChan;i++){
serialMessage = ""; //clear message
String isFocused = Boolean.toString(w_focus.isFocused);
serialMessage += isFocused;
try{
println(serialMessage);
this.serial_networking.write(serialMessage);
}catch (Exception e){
println(e);
}
}
}
}
}
void sendWidgetData(){
/* INSERT YOUR CODE HERE */
}
@@ -1472,18 +1574,18 @@ class Stream extends Thread{
this.msg = new OscMessage(this.address);
}else if (this.protocol.equals("UDP")){
this.udp = new UDP(this);
this.udp.setBuffer(1024);
this.udp.setBuffer(20000);
this.udp.listen(false);
this.udp.log(false);
println("UDP successfully connected");
output("UDP successfully connected");
}else if (this.protocol.equals("LSL")){
String stream_id = "q4asdgdsg";
String stream_id = "openbcieeg12345";
info_data = new LSL.StreamInfo(
this.streamName,
this.streamType,
this.nChanLSL,
openBCI.get_fs_Hz(),
getSampleRateSafe(),
LSL.ChannelFormat.float32,
stream_id
);
+6 -2
Ver Arquivo
@@ -282,6 +282,10 @@ class Widget{
}
void mouseDragged(){
}
void setTitle(String _widgetTitle){
widgetTitle = _widgetTitle;
}
@@ -313,8 +317,8 @@ class Widget{
;
}
catch (Exception e) {
println(e.getMessage());
println("widgetOptions List not built yet...");
// println(e.getMessage());
// println("widgetOptions List not built yet..."); AJK 8/22/17 because this is annoyance
}
for(int i = 0; i < dropdowns.size(); i++){
+73 -9
Ver Arquivo
@@ -14,46 +14,96 @@ int navHeight = 22;
// MAKE YOUR WIDGET GLOBALLY
W_timeSeries w_timeSeries;
W_fft w_fft;
W_headPlot w_headPlot;
W_accelerometer w_accelerometer;
W_networking w_networking;
W_BandPower w_bandPower;
W_accelerometer w_accelerometer;
W_ganglionImpedance w_ganglionImpedance;
W_headPlot w_headPlot;
W_template w_template1;
W_emg w_emg;
W_openBionics w_openbionics;
W_Focus w_focus;
W_PulseSensor w_pulsesensor;
W_AnalogRead w_analogRead;
W_DigitalRead w_digitalRead;
W_MarkerMode w_markermode;
//ADD YOUR WIDGET TO WIDGETS OF WIDGETMANAGER
void setupWidgets(PApplet _this, ArrayList<Widget> w){
// println(" setupWidgets start -- " + millis());
w_timeSeries = new W_timeSeries(_this);
w_timeSeries.setTitle("Time Series");
addWidget(w_timeSeries, w);
// println(" setupWidgets time series -- " + millis());
w_fft = new W_fft(_this);
w_fft.setTitle("FFT Plot");
addWidget(w_fft, w);
// println(" setupWidgets fft -- " + millis());
w_accelerometer = new W_accelerometer(_this);
w_accelerometer.setTitle("Accelerometer");
w_networking = new W_networking(_this);
w_networking.setTitle("Networking");
//only instantiate this widget if you are using a Ganglion board for live streaming
if(nchan == 4 && eegDataSource == DATASOURCE_GANGLION){
w_ganglionImpedance = new W_ganglionImpedance(_this);
w_ganglionImpedance.setTitle("Ganglion Signal");
addWidget(w_ganglionImpedance, w);
addWidget(w_networking, w);
addWidget(w_accelerometer, w);
} else {
addWidget(w_accelerometer, w);
addWidget(w_networking, w);
}
w_bandPower = new W_BandPower(_this);
w_bandPower.setTitle("Band Power");
addWidget(w_bandPower, w);
// println(" setupWidgets band power -- " + millis());
w_headPlot = new W_headPlot(_this);
w_headPlot.setTitle("Head Plot");
addWidget(w_headPlot, w);
// println(" setupWidgets head plot -- " + millis());
w_accelerometer = new W_accelerometer(_this);
w_accelerometer.setTitle("Accelerometer");
addWidget(w_accelerometer, w);
w_networking = new W_networking(_this);
w_networking.setTitle("Networking");
addWidget(w_networking, w);
w_emg = new W_emg(_this);
w_emg.setTitle("EMG");
addWidget(w_emg, w);
// println(" setupWidgets emg -- " + millis());
w_focus = new W_Focus(_this);
w_focus.setTitle("Focus Widget");
addWidget(w_focus, w);
// println(" setupWidgets focus widget -- " + millis());
//only instantiate this widget if you are using a Cyton board for live streaming
if(eegDataSource != DATASOURCE_GANGLION){
w_pulsesensor = new W_PulseSensor(_this);
w_pulsesensor.setTitle("Pulse Sensor");
addWidget(w_pulsesensor, w);
// println(" setupWidgets pulse sensor -- " + millis());
w_digitalRead = new W_DigitalRead(_this);
w_digitalRead.setTitle("Digital Read");
addWidget(w_digitalRead, w);
w_analogRead = new W_AnalogRead(_this);
w_analogRead.setTitle("Analog Read");
addWidget(w_analogRead, w);
w_markermode = new W_MarkerMode(_this);
w_markermode.setTitle("Marker Mode");
addWidget(w_markermode, w);
}
w_template1 = new W_template(_this);
w_template1.setTitle("Widget Template 1");
@@ -104,12 +154,14 @@ class WidgetManager{
int currentContainerLayout; //this is the Layout structure for the main body of the GUI ... refer to [PUT_LINK_HERE] for layouts/numbers image
ArrayList<Layout> layouts = new ArrayList<Layout>(); //this holds all of the different layouts ...
public boolean isWMInitialized = false;
private boolean visible = true;
private boolean updating = true;
WidgetManager(PApplet _this){
widgets = new ArrayList<Widget>();
widgetOptions = new ArrayList<String>();
isWMInitialized = false;
//DO NOT re-order the functions below
setupLayouts();
@@ -123,6 +175,10 @@ class WidgetManager{
currentContainerLayout = 4; //default layout ... tall container left and 2 shorter containers stacked on the right
setNewContainerLayout(currentContainerLayout); //sets and fills layout with widgets in order of widget index, to reorganize widget index, reorder the creation in setupWidgets()
}
delay(1000);
isWMInitialized = true;
}
public boolean isVisible() {
return visible;
@@ -214,6 +270,14 @@ class WidgetManager{
}
}
void mouseDragged(){
for(int i = 0; i < widgets.size(); i++){
if(widgets.get(i).isActive){
widgets.get(i).mouseDragged();
}
}
}
void setupLayouts(){
//refer to [PUT_LINK_HERE] for layouts/numbers image
//note that the order you create/add these layouts matters... if you reorganize these, the LayoutSelector will be out of order

Antes

Largura:  |  Altura:  |  Tamanho: 26 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 26 KiB

Antes

Largura:  |  Altura:  |  Tamanho: 23 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 23 KiB

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