-
Notifications
You must be signed in to change notification settings - Fork 0
Integration with Emotiv Cortex API
Here's Cortex doc.
Each Octopus server has a local Cortex server (located on the same physical host) with a number of headsets. To subscribe to events from a headset you need to perform a few requests to local Cortex:
(1) login: I presume this is required for "enabling" the Cortex server API (it's not stated in the documentation, but I think that it checks credentials with the Emotiv server on the Internet) (2) authorize: this produces a temporary token for accessing the premium API, like EEG events stream; token is valid for 48 hours; as far as I get it, multiple tokens for a single username can be used simultaneously (3) create session for a particular headset; one session per headset is allowed; session may have different states, for instance, it's automatically closed by Cortex if the headset is unplugged for 1 minute (3.1) activate session to be able to receive EEG events (session can be created in active state, so 3 and 3.1 are the same single step) (4) subscribe to the session to receive events
Also, login and subscribe have opposite operations logout and unsubscribe. Currently, I'm not sure what are the implications if we don't use those.
Let's look at these steps in greater detail.
https://emotiv.github.io/cortex-docs/#login
You need to provide a username and password of user from the Emotiv Cloud, and the client id/secret for your application.
REQUEST
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"username": "...",
"password": "...",
"client_id": "...",
"client_secret": "..."
},
"id": 1
}
RESPONSE
{
"jsonrpc": "2.0",
"id":1,
"result": "...."
}
It's not clear what the result looks like, but probably receiving a successful [1] response is enough to say that we've logged in and are ready to authorize. I presume that each Octopus server may log in independently via its' local Cortex (i.e. multiple Cortex servers may be logged in with Emotiv Cloud simultaneously). I presume that multiple successive logins via the same Cortex are fine, so we may perform login each time the connection to Cortex is established (in case the Cortex server has been restarted or was not running at application startup). In case multiple logins are not allowed, there's a getUserLogin operation, that lists all currently logged in users, so we can switch to that if problems arise.
Also, there's a lot of possible authentication/authorization errors in the API, look at [1] below.
[1] All API methods may return an error:
{
"jsonrpc": "2.0",
"error": {
"code": -9999,
"message": "Don't know how to say hello"
}
"id": 1
}
Possible error codes are listed here.
https://emotiv.github.io/cortex-docs/#authorize
Authenticates a user. You can authenticate as an anonymous user, but to get access to Raw EEG data or high-resolution performance metrics or both, you need to provide a username and password of user from the Emotiv Cloud, and the client id/secret for your application.
Auth tokens are valid for 48 hours. If you want to use them after that you must call authorize again or refresh the token.
REQUEST
{
"jsonrpc": "2.0",
"method": "authorize",
"params": {
"client_id": "...",
"client_secret": "...",
"license": "...",
"debit": 0
},
"id": 1
}
RESPONSE
{
"jsonrpc": "2.0",
"id":1,
"result": {
"_auth": "..."
}
}
It's mandatory to (once again) provide client ID and secret to access EEG data. Other two params license and debit are optional. Descriptions from the doc:
- license: If you don’t set a specific license, Cortex will use oldest license which is activated
- debit: Number of session will use on local machine (if don’t set default debit = 0)
I suppose selecting the last activated license is fine for us, as we'll have only one license; so we can omit this parameter.
It's not clear how debit = 0
would work: does it prohibit creating any sessions at all or, to the contrary, permits creating any number of session? My guess would be that we need to set this value equal to the number of headsets, that would be connected to this machine.
There are two debit-related errors in the API:
-32025 | Request ‘debit’ is greater than ‘maxDebit’ on the license.
-32026 | Daily debit limit exceeded.
Not sure what daily debit limit means. Is the number of sessions that can be created per one day limited? Per license? How all of this is calculated? Do we "consume" one "point" from the overall limit each time we call the createSession
API? What if the session has been closed, and we re-opened it?
https://emotiv.github.io/cortex-docs/#createsession
Creates a new session. Sessions are used to manage live or pre-recorded streams of data from the headset. To use a session successfuly, it must be in the correct state.
https://emotiv.github.io/cortex-docs/#updatesession
Updates an existing session. You can use this to start or stop recording from the EEG (by modifying its status) or change the metadata associated with that session.
There's some versatility in how to create a session, so visit the link to see all possible options. I assume that we need to use the following variant (combining creation and activation of the session in one API call):
REQUEST
{
"jsonrpc": "2.0",
"method": "createSession",
"params": {
"_auth": "...",
"headset": "INSIGHT-59683C10",
"status": "active"
},
"id": 1
}
RESPONSE
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"client": "",
"headset": "INSIGHT-59683C10",
"id": "af349e3e-c72b-44c9-992c-5ee1905cfdaa",
"license": "",
"logs": null,
"markers": [],
"profile": "",
"started": "2017-03-23T18:25:08Z",
"status": "activated",
"stopped": "",
"streams": {},
"subject": "",
"tags": []
}
}
We need to create a separate session for each connected headset. Again, there is a lot of errors that relate to session, to name a few:
-32005 | There is an active session already with this headset.
-32006 | No valid license to activate a new session.
-32007 | Session does not exist.
-32008 | The session was closed.
-32009 | Current session has been updated.
-32010 | The headset of current session has been disconnected.
-32012 | The current session must be activated first.
-32019 | Session limit has been reached for current device.
-32028 | Session soft limit exceeded.
-32029 | Session hard limit exceeded.
-32030 | Session data already existed.
I think that we need to use querySessions API to list all existing sessions before trying to create a session.
Basically, we'll have a hard-coded list of headset IDs in the configuration, and each time we establish the connection to Cortex, we'll do the following:
query all sessions
for each headset ID from the configuration
if there is a session for this headset ID
switch (session state)
case closed: create session
case opened: activate session
case active: do nothing
else
create session
Currently, it's not clear how to react to different API errors, that may happen on session creation/update.
Also, what do we do, if we were not able to create/update session for some of the headset IDs? Do we continue or terminate?
https://emotiv.github.io/cortex-docs/#subscribe
Subscribes to a stream of data from a headset. While subscribed, you will receive events asynchronously as data comes in.
REQUEST
{
"jsonrpc": "2.0",
"method": "subscribe",
"params": {
"_auth": "myToken",
"streams": [
"eeg", "dev"
],
"session": "...",
"replay" false
},
"id": 1
}
The call should return a response immediately (containing subscription ID). The events will come afterwards:
https://emotiv.github.io/cortex-docs/#event https://emotiv.github.io/cortex-docs/#eeg-event
{
"eeg": [...],
"sid": "af349e3e-c72b-44c9-992c-5ee1905cfdaa",
"time": 1283.58928052493
}
sid
stands for Subscription ID and is equal to Session ID. Via this value we can correlate the event to a particular headset.
EEG events for EPOC/EPOC+ headsets contain 14 attributes and a timestamp (in seconds since Cortex startup (!)):
IED_COUNTER | Counter that increments by 1 each event |
---|---|
IED_INTERPOLATED | 1 if this is interpolated package, otherwise = 0 |
IED_RAW_CQ | Signal quality raw value |
IED_AF3 | AF3 channel |
IED_T7 | T7 channel |
IED_Pz | Pz channel |
IED_T8 | T8 channel |
IED_AF4 | AF4 channel |
IED_MARKER_HARDWARE | marker hardware value |
IED_MARKER | marker value |
In database, we'll store all of these + timestamp + headset ID.
There's an operation queryHeadsets, that returns all currently connected headsets (in the form of Headset Objects). There are two problems with it though:
-
label
attribute is marked as "use in the future" in the doc; I'd guess that filling labels should be possible via some Cortex UI, but, as it's marked for future use, maybe there's no such option at the moment; we need labels to correlate user-sent events to headsets (as was discussed earlier, the user will input her headset's label in UI before using the mobile app; this label will be sent by the mobile device when creating session in Octopus - obviously, it returns only connected headsets, so we'll need to poll it regularly, in case some the headsets get connected/re-connected
dev
event channel seems to provide the signal info. So we'll need to subscribe to it along with eeg
channel and re-direct incoming dev
messages to the corresponding gRPC client, if some of the values are below (?) some threshold (which?)
-
https://emotiv.github.io/cortex-docs/#querysessions uses 'activated' status for indicating that a session is active, while https://emotiv.github.io/cortex-docs/#createsession expects 'active' status (and returns error "-32013 Invalid status." if 'activated' is specified).
-
Contrary to https://emotiv.github.io/cortex-docs/#eeg-event, EEG event columns are NOT prefixed with 'IED_'.
-
There's a problem with subscribing to an active session. For instance, here's what https://emotiv.github.io/cortex-docs/#querysessions returns (don't mind the formatting):
{result=[Session{appId=com.ea481neuro.octopusync, id=12ba6c68-64b1-4549-a936-d6a009aab2a5, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:47:10.772318, stopped=2019-04-15T10:47:12.589181, headset=Headset{id=EPOCPLUS-4A2C0686}}, Session{appId=com.ea481neuro.octopusync, id=41516453-470c-4452-985c-b8ec49978fc7, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:44:48.835455, stopped=2019-04-15T10:44:57.604547, headset=Headset{id=EPOCPLUS-4A2C0686}}, Session{appId=com.emotiv.emotivpro, id=64907201-e258-4929-816d-caee659b13ec, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2018-12-17T13:01:58.833106, stopped=2018-12-17T13:26:32.129303, headset=Headset{id=EPOCPLUS-3B9AE721}}, Session{appId=com.emotiv.emotivpro, id=7161a35f-e8e8-43f2-ba90-8f5a66c02c44, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T11:03:14.472948, stopped=2019-04-15T11:03:20.771045, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=73125fb9-fe2e-48d2-a2d5-8737789e5ea6, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:22:42.285086, stopped=2019-04-15T10:22:48.668747, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=774901a2-0bae-4b76-8f9a-4012890ce03c, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T12:06:49.911050, stopped=2019-04-15T12:12:02.466365, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=8d6179a0-91b5-44d4-a98a-360d7243243e, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=activated, started=2019-04-15T12:13:53.972514, stopped=null, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.ea481neuro.octopusync, id=91efa5fa-6a4e-4a6d-bddd-cca8efa2f0aa, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:33:58.082092, stopped=2019-04-15T10:34:08.680444, headset=Headset{id=EPOCPLUS-4A2C0686}}, Session{appId=com.emotiv.emotivpro, id=938b4dcd-9d36-4efd-bc2f-907fbce37266, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=activated, started=2019-04-15T10:25:59.913530, stopped=null, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=93facbdb-091c-4185-b67c-985f9070c730, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:13:03.283156, stopped=2019-04-15T10:21:50.753739, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.ea481neuro.octopusync, id=c7644c1f-efeb-43bb-ad9e-ea1229125158, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:44:29.007568, stopped=2019-04-15T10:44:31.556583, headset=Headset{id=EPOCPLUS-4A2C0686}}, Session{appId=com.emotiv.emotivpro, id=dbf11536-0119-4e82-bc87-1d72fbcbe53d, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T11:17:44.423741, stopped=2019-04-15T12:06:25.094026, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=df665def-d6cc-41fa-a3ca-41737b9ecf2f, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2018-06-11T14:47:11.878980, stopped=2018-06-11T14:51:13.362080, headset=Headset{id=EPOCPLUS-3B9AE721}}, Session{appId=com.ea481neuro.octopusync, id=e1304dc4-f3f3-4727-89d3-da17448ae515, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T10:45:28.416254, stopped=2019-04-15T10:45:30.132751, headset=Headset{id=EPOCPLUS-4A2C0686}}, Session{appId=com.emotiv.emotivpro, id=f056c0a4-88d0-4789-b4e5-00ea7fd3aad3, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=closed, started=2019-04-15T08:38:46.021102, stopped=2019-04-15T08:39:46.521611, headset=Headset{id=EPOCPLUS-4A2C065F}}, Session{appId=com.emotiv.emotivpro, id=50879f4b-0e77-41f1-b3c5-1f138cf7a5be, license=70053b79-8342-4b4d-b9e5-a195bfcf1c7e, owner=ea481neuro, status=closed, started=2019-04-12T16:31:22.470544, stopped=2019-04-12T16:32:15.057872, headset=Headset{id=EPOCPLUS-3B9AE688}}, Session{appId=com.emotiv.emotivpro, id=8466c51e-0721-439d-9185-ea0d45528fd3, license=70053b79-8342-4b4d-b9e5-a195bfcf1c7e, owner=ea481neuro, status=closed, started=2019-04-12T16:21:08.769864, stopped=2019-04-12T16:22:51.747342, headset=Headset{id=EPOCPLUS-3B9AE688}}], jsonrpc=2.0, id=3, error=null}
You may see that there's one active session for headset EPOCPLUS-4A2C065F (other sessions for this headset are closed):
{appId=com.emotiv.emotivpro, id=8d6179a0-91b5-44d4-a98a-360d7243243e, license=2fd7c436-9283-4b5a-8348-5d48b825f558, owner=ea481neuro, status=activated, started=2019-04-15T12:13:53.972514, stopped=null, headset=Headset{id=EPOCPLUS-4A2C065F}}
When trying to subscribe to this session, API returns an error:
{id=5, params={replay=false, session=8d6179a0-91b5-44d4-a98a-360d7243243e, _auth=..., streams=[eeg]}}
=> {code=-32007, message=Session does not exist.}