Skip to content

Loading…

Hi Daeken! Some embryo of a 'libepoc', if you wish. #3

Merged
4 commits merged into from

1 participant

@skadge

No description provided.

Séverin Lema... added some commits
Séverin Lemaignan Added some detection code to emotiv.py to know if we are using a syst…
…em daemon to uncode the EPOC stream
1ac4373
Séverin Lemaignan Added udev rule for the headset.
There's an issue with the idProduct/idVendor detection that prevent
for now both the research and the consumer headset to be distinguished...

Can be solved if someone send me the output of:
udevadm info --name=/dev/hidraw2 --attribute-walk for the consumer
headset.
3bd0e52
Séverin Lemaignan Added the bitmask + a getlevel() function copied from Daeken's python…
… code
a2c1112
Séverin Lemaignan Started a 'libepoc' and a 'epocd' daemon + added CMake compilation.
The library doesn't do anything really useful yet, but it shouldn't
be far away.
The 'epocd' exec should become at some point a daemon. For now, that's
the former 'decrypt_emotiv' that allow some testing of libepoc
881f0a8
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 21, 2010
  1. Added some detection code to emotiv.py to know if we are using a syst…

    Séverin Lemaignan committed
    …em daemon to uncode the EPOC stream
  2. Added udev rule for the headset.

    Séverin Lemaignan committed
    There's an issue with the idProduct/idVendor detection that prevent
    for now both the research and the consumer headset to be distinguished...
    
    Can be solved if someone send me the output of:
    udevadm info --name=/dev/hidraw2 --attribute-walk for the consumer
    headset.
  3. Added the bitmask + a getlevel() function copied from Daeken's python…

    Séverin Lemaignan committed
    … code
  4. Started a 'libepoc' and a 'epocd' daemon + added CMake compilation.

    Séverin Lemaignan committed
    The library doesn't do anything really useful yet, but it shouldn't
    be far away.
    The 'epocd' exec should become at some point a daemon. For now, that's
    the former 'decrypt_emotiv' that allow some testing of libepoc
This page is out of date. Refresh to see the latest.
Showing with 331 additions and 72 deletions.
  1. +65 −0 c/CMakeLists.txt
  2. +0 −66 c/decrypt_emotiv.c
  3. +41 −0 c/include/libepoc.h
  4. +123 −0 c/src/epoc.c
  5. +64 −0 c/src/epocd.c
  6. +21 −6 emotiv.py
  7. +17 −0 linux/epoc.rules
View
65 c/CMakeLists.txt
@@ -0,0 +1,65 @@
+# The name of the project is "LIBORO". CMakeLists files in this project can
+# refer to the root source directory of the project as ${LIBORO_SOURCE_DIR} and
+# to the root binary directory of the project as ${LIBORO_BINARY_DIR}.
+cmake_minimum_required (VERSION 2.6)
+PROJECT(libepoc)
+
+set(LIBS mcrypt)
+
+SET(BUILD_SHARED_LIBS true)
+
+include_directories(${libepoc_SOURCE_DIR}/include)
+
+set(LIBEPOC_HEADERS include/libepoc.h)
+
+##########################################
+## libepoc ###
+##########################################
+
+# Create a library called "libepoc"
+
+ADD_LIBRARY (epoc src/epoc.c)
+
+INSTALL (TARGETS epoc
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+)
+
+INSTALL(FILES
+ ${LIBEPOC_HEADERS}
+ DESTINATION include/libepoc
+)
+
+##################################################
+# epoc daemon #
+##################################################
+
+add_executable (epocd src/epocd.c)
+
+target_link_libraries (epocd epoc ${LIBS})
+
+SET_TARGET_PROPERTIES(epocd PROPERTIES
+ INSTALL_RPATH_USE_LINK_PATH TRUE
+ INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib
+ BUILD_WITH_INSTALL_RPATH TRUE )
+
+INSTALL (TARGETS epocd
+ RUNTIME DESTINATION bin
+)
+
+##########################################
+## PKGCONFIG file ###
+##########################################
+
+IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libepoc.pc.in")
+ MESSAGE(STATUS "configured ${CMAKE_CURRENT_SOURCE_DIR}/libepoc.pc.in --> ${CMAKE_CURRENT_BINARY_DIR}/libepoc.pc")
+ CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libepoc.pc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/libepoc.pc
+ @ONLY )
+
+ INSTALL(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/libepoc.pc
+ DESTINATION lib/pkgconfig
+ )
+ENDIF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libepoc.pc.in")
+
View
66 c/decrypt_emotiv.c
@@ -1,66 +0,0 @@
-/* Decrypt Emotic EPOC stream using ECB and RIJNDAEL-128 cipher
- *
- * Usage: decrypt_emotiv (consumer/research) /dev/emotiv/raw > decoded
- * Make sure to pick the right type of device, as this determins the key
- * */
-
-#include <mcrypt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define KEYSIZE 16 /* 128 bits == 16 bytes */
-
-unsigned char CONSUMERKEY[KEYSIZE] = {0x31,0x00,0x35,0x54,0x38,0x10,0x37,0x42,0x31,0x00,0x35,0x48,0x38,0x00,0x37,0x50};
-unsigned char RESEARCHKEY[KEYSIZE] = {0x31,0x00,0x39,0x54,0x38,0x10,0x37,0x42,0x31,0x00,0x39,0x48,0x38,0x00,0x37,0x50};
-
-int main(int argc, char **argv)
-{
- MCRYPT td;
- int i;
- unsigned char key[KEYSIZE];
- char *block_buffer;
- int blocksize;
-
- FILE *input;
- if (argc < 3)
- {
- fputs("Missing argument\nExpected: decrypt_emotiv (consumer/research) source\n", stderr);
- return 1;
- }
-
- if(strcmp(argv[1], "research") == 0)
- memcpy(key, RESEARCHKEY, KEYSIZE);
- else
- memcpy(key, CONSUMERKEY, KEYSIZE);
-
- input = fopen(argv[2], "rb");
- if (input == NULL)
- {
- fputs("File read error", stderr);
- return 1;
- }
-
- td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, NULL, MCRYPT_ECB, NULL);
- blocksize = mcrypt_enc_get_block_size(td); //should return a 16bits blocksize
- //printf( "%d", blocksize);
-
- block_buffer = malloc(blocksize);
-
- mcrypt_generic_init( td, key, KEYSIZE, NULL);
-
- while ( fread (block_buffer, 1, blocksize, input) == blocksize ) {
-//for (i=0; i<1024 ; i++){
-// fread (block_buffer, 1, blocksize, input);
- //mcrypt_generic (td, block_buffer, blocksize);
- mdecrypt_generic (td, block_buffer, blocksize);
- fwrite ( block_buffer, 1, blocksize, stdout);
- }
-
- mcrypt_generic_deinit (td);
- mcrypt_module_close(td);
-
- fclose(input);
-
- return 0;
-}
View
41 c/include/libepoc.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010, Daeken and Skadge
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef LIBEPOC_H_
+#define LIBEPOC_H_
+
+#include <stdio.h>
+
+enum headset_type {CONSUMER_HEADSET, RESEARCH_HEADSET};
+
+struct epoc_contact_quality {
+ char F3, FC6, P7, T8, F7, F8, T7, P8, AF4, F4, AF3, O2, O1, FC5;
+};
+
+struct epoc_frame {
+ int F3, FC6, P7, T8, F7, F8, T7, P8, AF4, F4, AF3, O2, O1, FC5;
+ struct epoc_contact_quality cq;
+ char gyroX, gyroY;
+ char battery;
+};
+
+int epoc_init(FILE* source, enum headset_type type);
+int epoc_close();
+
+int epoc_get_next_raw(char raw_frame[32]);
+int epoc_get_next_frame(struct epoc_frame* frame);
+
+
+#endif //LIBEPOC_H_
View
123 c/src/epoc.c
@@ -0,0 +1,123 @@
+#include <mcrypt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libepoc.h"
+
+#define KEYSIZE 16 /* 128 bits == 16 bytes */
+
+const unsigned char CONSUMERKEY[KEYSIZE] = {0x31,0x00,0x35,0x54,0x38,0x10,0x37,0x42,0x31,0x00,0x35,0x48,0x38,0x00,0x37,0x50};
+const unsigned char RESEARCHKEY[KEYSIZE] = {0x31,0x00,0x39,0x54,0x38,0x10,0x37,0x42,0x31,0x00,0x39,0x48,0x38,0x00,0x37,0x50};
+
+const unsigned char F3_MASK[14] = {10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
+const unsigned char FC6_MASK[14] = {214, 215, 200, 201, 202, 203, 204, 205, 206, 207, 192, 193, 194, 195};
+const unsigned char P7_MASK[14] = {84, 85, 86, 87, 72, 73, 74, 75, 76, 77, 78, 79, 64, 65};
+const unsigned char T8_MASK[14] = {160, 161, 162, 163, 164, 165, 166, 167, 152, 153, 154, 155, 156, 157};
+const unsigned char F7_MASK[14] = {48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45};
+const unsigned char F8_MASK[14] = {178, 179, 180, 181, 182, 183, 168, 169, 170, 171, 172, 173, 174, 175};
+const unsigned char T7_MASK[14] = {66, 67, 68, 69, 70, 71, 56, 57, 58, 59, 60, 61, 62, 63};
+const unsigned char P8_MASK[14] = {158, 159, 144, 145, 146, 147, 148, 149, 150, 151, 136, 137, 138, 139};
+const unsigned char AF4_MASK[14] = {196, 197, 198, 199, 184, 185, 186, 187, 188, 189, 190, 191, 176, 177};
+const unsigned char F4_MASK[14] = {216, 217, 218, 219, 220, 221, 222, 223, 208, 209, 210, 211, 212, 213};
+const unsigned char AF3_MASK[14] = {46, 47, 32, 33, 34, 35, 36, 37, 38, 39, 24, 25, 26, 27};
+const unsigned char O2_MASK[14] = {140, 141, 142, 143, 128, 129, 130, 131, 132, 133, 134, 135, 120, 121};
+const unsigned char O1_MASK[14] = {102, 103, 88, 89, 90, 91, 92, 93, 94, 95, 80, 81, 82, 83};
+const unsigned char FC5_MASK[14] = {28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 8, 9};
+
+MCRYPT td;
+unsigned char key[KEYSIZE];
+char *block_buffer;
+int blocksize;
+
+unsigned char frame[32];
+
+FILE *input;
+
+int epoc_init(FILE* source, enum headset_type type) {
+
+ input = source;
+
+ if(type == RESEARCH_HEADSET)
+ memcpy(key, RESEARCHKEY, KEYSIZE);
+ else
+ memcpy(key, CONSUMERKEY, KEYSIZE);
+
+ //libmcrypt initialization
+ td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, NULL, MCRYPT_ECB, NULL);
+ blocksize = mcrypt_enc_get_block_size(td); //should return a 16bits blocksize
+
+ block_buffer = malloc(blocksize);
+
+ mcrypt_generic_init( td, key, KEYSIZE, NULL);
+}
+
+int epoc_close() {
+ mcrypt_generic_deinit (td);
+ mcrypt_module_close(td);
+
+ fclose(input);
+}
+
+int get_level(unsigned char frame[32], const unsigned char bits[14]) {
+ char i;
+ char b,o;
+ int level = 0;
+
+ for (i= 13; i == -1; --i){
+ level <<= 1;
+ b = (bits[i] / 8) + 1;
+ o = bits[i] % 8;
+
+ level |= (frame[b] >> o) & 1;
+ }
+
+ return level;
+}
+
+int epoc_get_next_raw(char frame[32]) {
+ //Two blocks of 16 bytes must be read.
+ if (fread (block_buffer, 1, blocksize, input) == blocksize) {
+ mdecrypt_generic (td, block_buffer, blocksize);
+ memcpy(frame, block_buffer, 16);
+ }
+ else {
+ return -1;
+ }
+
+ if (fread (block_buffer, 1, blocksize, input) == blocksize) {
+ mdecrypt_generic (td, block_buffer, blocksize);
+ memcpy(frame + 16, block_buffer, 16);
+ }
+ else {
+ return -1;
+ }
+ return 0;
+}
+
+int epoc_get_next_frame(struct epoc_frame* frame) {
+ char raw_frame[32];
+
+ epoc_get_next_raw(raw_frame);
+
+ frame->F3 = get_level(raw_frame, F3_MASK);
+ frame->FC6 = get_level(raw_frame, FC6_MASK);
+ frame->P7 = get_level(raw_frame, P7_MASK);
+ frame->T8 = get_level(raw_frame, T8_MASK);
+ frame->F7 = get_level(raw_frame, F7_MASK);
+ frame->F8 = get_level(raw_frame, F8_MASK);
+ frame->T7 = get_level(raw_frame, T7_MASK);
+ frame->P8 = get_level(raw_frame, P8_MASK);
+ frame->AF4 = get_level(raw_frame, AF4_MASK);
+ frame->F4 = get_level(raw_frame, F4_MASK);
+ frame->AF3 = get_level(raw_frame, AF3_MASK);
+ frame->O2 = get_level(raw_frame, O2_MASK);
+ frame->O1 = get_level(raw_frame, O1_MASK);
+ frame->FC5 = get_level(raw_frame, FC5_MASK);
+
+ //TODO!
+ frame->gyroX = 0;
+ frame->gyroY = 0;
+
+ frame->battery = 0;
+}
View
64 c/src/epocd.c
@@ -0,0 +1,64 @@
+/* Emotic EPOC daemon that decrypt stream using ECB and RIJNDAEL-128 cipher
+ * (well, not yet a daemon...)
+ *
+ * Usage: epocd (consumer/research) /dev/emotiv/encrypted output_file
+ *
+ * Make sure to pick the right type of device, as this determins the key
+ * */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libepoc.h"
+
+
+int main(int argc, char **argv)
+{
+ FILE *input;
+ FILE *output;
+ enum headset_type type;
+
+ char raw_frame[32];
+ struct epoc_frame frame;
+
+ if (argc < 3)
+ {
+ fputs("Missing argument\nExpected: decrypt_emotiv [consumer|research] source [dest]\n", stderr);
+ fputs("By default, dest = stdout\n", stderr);
+ return 1;
+ }
+
+ if(strcmp(argv[1], "research") == 0)
+ type = RESEARCH_HEADSET;
+ else
+ type = CONSUMER_HEADSET;
+
+ input = fopen(argv[2], "rb");
+ if (input == NULL)
+ {
+ fputs("File read error: couldn't open the EEG source!", stderr);
+ return 1;
+ }
+
+ epoc_init(input, type);
+
+ if (argc == 3) {
+ output = stdout;
+ } else {
+ output = fopen(argv[3], "wb");
+ if (input == NULL)
+ {
+ fputs("File write error: couldn't open the destination file for uncrypted data", stderr);
+ return 1;
+ }
+ }
+
+ while ( 1 ) {
+ epoc_get_next_frame(&frame);
+ printf("F3: %d\n", frame.F3);
+ fflush(stdout);
+ }
+
+ epoc_close();
+ return 0;
+}
View
27 emotiv.py
@@ -4,7 +4,7 @@
except:
windows = False
-import sys
+import sys, os
import logging
logger = logging.getLogger("emotiv")
@@ -58,7 +58,7 @@ def __repr__(self):
)
class Emotiv(object):
- def __init__(self, headsetId=0, research_headset = False):
+ def __init__(self, headsetId=0, research_headset = True):
if research_headset:
self.rijn = rijndael(research_key, 16)
@@ -69,9 +69,9 @@ def __init__(self, headsetId=0, research_headset = False):
self.packets = []
if self.setupWin(headsetId) if windows else self.setupPosix(headsetId):
- logger.info("Fine, connected to the Emotiv receiver")
+ logger.info("Fine, connected to the Emotiv EPOC receiver")
else:
- logger.error("Unable to connect to the Emotiv receiver :-(")
+ logger.error("Unable to connect to the Emotiv EPOC receiver :-(")
sys.exit(1)
def setupWin(self, headsetId):
@@ -88,11 +88,26 @@ def handle(data):
def setupPosix(self, headsetId):
def reader():
- self.hidraw = open("/dev/hidraw1")
+ _os_decryption = False
+ if os.path.exists('/dev/eeg/raw'):
+ #The decrpytion is handled by the Linux epoc daemon. We don't need to handle it there.
+ _os_decryption = True
+ self.hidraw = open("/dev/eeg/raw")
+ else:
+ if os.path.exists("/dev/hidraw2"):
+ self.hidraw = open("/dev/hidraw2")
+ else:
+ self.hidraw = open("/dev/hidraw2")
+
while self._goOn:
data = self.hidraw.read(32)
if data != "":
- self.gotData(data)
+ if _os_decryption:
+ self.packets.append(EmotivPacket(data))
+ else:
+ #Decrypt it!
+ self.gotData(data)
+
self._dataReader = Thread(target=reader)
self._dataReader.start()
return True
View
17 linux/epoc.rules
@@ -0,0 +1,17 @@
+#UDEV rule for the Emotiv EPOC headset
+#
+#Once installed into /etc/udev/rules.d, udev will recognize the headset and
+#create a /dev/eeg/encrypted device where you can access the headset encryted
+#stream.
+#
+#It will as well attempt to start a daemon in charge of decrypting the stream.
+#If the daemon successfully starts, you will find the data under /dev/eeg/raw
+
+ATTR{manufacturer}=="Emotiv Systems Pty Ltd", ATTRS{product}=="Receiver Dongle L01", NAME="eeg/epoc%2", SYMLINK+="epoc%n"
+
+#Consumer headset
+#SUBSYSTEM=="hidraw", ATTRS{idVendor}=="21a1", ATTRS{idProduct}=="0001", ATTRS{interface}=="Emotiv RAW DATA", NAME="eeg/encrypted%n", SYMLINK+="eeg/encrypted", MODE="0444", RUN +="decrypt_emotiv.sh consumer"
+
+#Research headset
+SUBSYSTEM=="hidraw", ATTRS{interface}=="Emotiv RAW DATA", NAME="eeg/encrypted%n", SYMLINK+="eeg/encrypted", MODE="0444", RUN +="decrypt_emotiv.sh research"
+
Something went wrong with that request. Please try again.