Skip to content

Integration with Emotiv Cortex API

Andrei Tomashpolskiy edited this page Apr 15, 2019 · 2 revisions

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.

Login

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.

Authorize

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?

Create/activate session

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?

Subscribe

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.

Data model

EEG events for EPOC/EPOC+ headsets contain 14 attributes and a timestamp (in seconds since Cortex startup (!)):

https://emotiv.github.io/cortex-docs/#eeg-event

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.

Hard-coding the list of headsets VS querying from Cortex

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

Querying headset signal

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?)

Issues

  1. 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).

  2. Contrary to https://emotiv.github.io/cortex-docs/#eeg-event, EEG event columns are NOT prefixed with 'IED_'.

  3. 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.}
Clone this wiki locally