2015년 12월 17일 목요일

AverageBandPowers

여기서 부터는 뇌파에 대한 이론 공부가 필요하다.
wikipedia 나 기존 블로거들의 공부한 내용을 참고 하면 이해에 도움이 된다.

내용을 살짝 들여다 보면,
[An EEG recording setup] 출처 : wikipedia
[An EEG recording setup] 출처 : wikipedia
좀 혐오스럽지만 위 사진 정도는 센서를 붙여줘야~ 정확한 데이터를 얻을 수 있을 거라는 느낌은 올 듯. 저 무수한 측정 Point 중에 insight 가 사용하는 5개 (AF3, AF4, T7, T8, Pz) 로 아래 alpha, beta, gamma, theta 4개의 파형을 얻어 내는 것이다. (수면파라고 하는 delta 는 측정에 빠져있다)

조금 더 인용하여 설명 하면 각각의 뇌파가 어떤 특징을 갖고 있는지 알 수 있다.

출처 : https://ko.wikipedia.org/wiki/뇌파
감마(gamma)파
- 주파수 : 30Hz 이상 - 극도의 각성과 흥분시 - 전두엽과 두정(중심)엽에서 비교적 많이 발생한다. - 2 - 20 μV
베타(beta)파
주파수 : 13 - 30 Hz
"스트레스파"라고도 한다. 불안, 긴장 등의 활동파
알파(alpha)파
주파수 : 8 - 12.99 Hz
심신이 안정을 취하고 있을 때의 뇌파. 안정파
특징: 사람 뇌파의 대표적인 성분으로, 뇌의 발달과 밀접한 관계가 있다.
세타(theta)파
주파수 : 4 - 7.99 Hz
"졸음파" 또는 "서파수면파(徐波睡眠波)"라고 불림. 잠에 빠져들 때 통과하는 뇌파
델타(delta)파[편집]
주파수 : 0.2 - 3.99 Hz
"수면파"라고도 함. 수면 시 발생.

공부는 이정도만 일단 해 두고 예제 코드를 살펴 보면,

위치 : community-sdk/example/C++/AverageBandPowers
기준 commit : 6a20813229ff56cf0c0040cfddaaa086d7e27ea4
컴파일 : 비정상 (수정가능)
실행 : 비정상  (수정 후 실행)
결과물 : 표준 출력

먼저 CMakeList.txt 가 빠져 있으므로 다른 예제에서 복사해서 작성해 주면 아래와 같다.

$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 2.6.0)
project (abp)
#For the shared library:
set ( PROJECT_LINK_LIBS libedk.so )
set ( EXAMPLES_DIR ../../../ )
link_directories( ${EXAMPLES_DIR}/bin/linux64 )
include_directories(${EXAMPLES_DIR}/include)
add_executable(abp main.cpp)
target_link_libraries(abp ${PROJECT_LINK_LIBS} )

컴파일 부터 해보면

$ cmake .
$ make
Scanning dependencies of target abp
[100%] Building CXX object CMakeFiles/abp.dir/main.cpp.o
/home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:29:11: error: ‘::main’ must return ‘int’
void main() {
^
/home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp: In function ‘int main()’:
/home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:42:46: error: no matching function for call to ‘std::basic_ofstream<char>::basic_ofstream(std::__cxx11::string&, const openmode&)’
std::ofstream ofs(ouputFile, std::ios::trunc);
^
In file included from /home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:12:0:
/usr/include/c++/5/fstream:697:7: note: candidate: std::basic_ofstream<_CharT, _Traits>::basic_ofstream(const char*, std::ios_base::openmode) [with _CharT = char; _Traits = std::char_traits<char>; std::ios_base::openmode = std::_Ios_Openmode]
basic_ofstream(const char* __s,
^
/usr/include/c++/5/fstream:697:7: note: no known conversion for argument 1 from ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ to ‘const char*’
In file included from /home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:12:0:
/usr/include/c++/5/fstream:682:7: note: candidate: std::basic_ofstream<_CharT, _Traits>::basic_ofstream() [with _CharT = char; _Traits = std::char_traits<char>]
basic_ofstream(): __ostream_type(), _M_filebuf()
^
/usr/include/c++/5/fstream:682:7: note: candidate expects 0 arguments, 2 provided
In file included from /home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:12:0:
/usr/include/c++/5/fstream:656:11: note: candidate: std::basic_ofstream<char>::basic_ofstream(const std::basic_ofstream<char>&)
class basic_ofstream : public basic_ostream<_CharT,_Traits>
^
/usr/include/c++/5/fstream:656:11: note: candidate expects 1 argument, 2 provided
/home/user/community-sdk/examples/C++/AverageBandPowers/main.cpp:58:17: error: ‘_kbhit’ was not declared in this scope
while (!_kbhit())
^
CMakeFiles/abp.dir/build.make:54: recipe for target 'CMakeFiles/abp.dir/main.cpp.o' failed
make[2]: *** [CMakeFiles/abp.dir/main.cpp.o] Error 1
CMakeFiles/Makefile2:60: recipe for target 'CMakeFiles/abp.dir/all' failed
make[1]: *** [CMakeFiles/abp.dir/all] Error 2
Makefile:75: recipe for target 'all' failed
make: *** [all] Error 2

에러 부분이 대부분 단순 오류이므로 간단히 수정해 주고 다시 컴파일.
참고로 file 출력을 표준 출력으로 바꿨다.

diff --git a/examples/C++/AverageBandPowers/main.cpp b/examples/C++/AverageBandPowers/main.cpp
index 7927da8..483729e 100644
--- a/examples/C++/AverageBandPowers/main.cpp
+++ b/examples/C++/AverageBandPowers/main.cpp
@@ -9,7 +9,6 @@

#include <iostream>
#include <string>
-#include <fstream>

#ifdef _WIN32
#include <conio.h>
@@ -26,7 +25,7 @@

using namespace std;

-void main() {
+int main() {

EmoEngineEventHandle eEvent = IEE_EmoEngineEventCreate();
EmoStateHandle eState = IEE_EmoStateCreate();
@@ -35,27 +34,24 @@ void main() {
bool ready = false;
int state = 0;

+ int hon = 0;
+ int af3 = 0;
+ int t7 = 0;
+ int pz = 0;
+ int t8 = 0;
+ int af4 = 0;
+
IEE_DataChannel_t channelList[] = { IED_AF3, IED_AF4, IED_T7, IED_T8, IED_Pz };

- std::string ouputFile = "AverageBandPowers.txt";
- const char header[] = "Theta, Alpha, Low_beta, High_beta, Gamma";
- std::ofstream ofs(ouputFile, std::ios::trunc);
- ofs << header << std::endl;
+ const char header[] = "Headset on, Theta, Alpha, Low_beta, High_beta, Gamma, AF3, T7, Pz, T8, AF4";
+ std::cout << header << std::endl;

if (IEE_EngineConnect() != EDK_OK) {
throw std::runtime_error(
"Emotiv Driver start up failed.");
}

- std::cout << "==================================================================="
- << std::endl;
- std::cout << " Example to get the average band power for a specific channel from \n"
- "the latest epoch "
- << std::endl;
- std::cout << "==================================================================="
- << std::endl;
-
- while (!_kbhit())
+ while (true)
{
state = IEE_EngineGetNextEvent(eEvent);

@@ -65,7 +61,6 @@ void main() {
IEE_EmoEngineEventGetUserId(eEvent, &engineUserID);

if (eventType == IEE_UserAdded) {
- std::cout << "User added" << std::endl;
engineUserID = 0;
IEE_FFTSetWindowingType(engineUserID, IEE_HAMMING);

@@ -74,6 +69,27 @@ void main() {
}
}

+ state = IEE_EngineGetNextEvent(eEvent);
+ if (state == EDK_OK) {
+ IEE_Event_t eventType = IEE_EmoEngineEventGetType(eEvent);
+ switch (eventType) {
+ case IEE_EmoStateUpdated: {
+ //std::cout << std::endl << "Emostate updated" << std::endl;
+ IEE_EmoEngineEventGetEmoState(eEvent, eState);
+ hon = IS_GetHeadsetOn (eState);
+
+ af3 = IS_GetContactQuality(eState, IEE_CHAN_AF3);
+ t7 = IS_GetContactQuality(eState, IEE_CHAN_T7);
+ pz = IS_GetContactQuality(eState, IEE_CHAN_Pz);
+ t8 = IS_GetContactQuality(eState, IEE_CHAN_T8);
+ af4 = IS_GetContactQuality(eState, IEE_CHAN_AF4);
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
if (ready)
{
double alpha, low_beta, high_beta, gamma, theta;
@@ -84,15 +100,9 @@ void main() {
int result = IEE_GetAverageBandPowers(engineUserID, channelList[i], &theta, &alpha, 
&low_beta, &high_beta, &gamma);
if(result == EDK_OK){
- ofs << theta << ",";
- ofs << alpha << ",";
- ofs << low_beta << ",";
- ofs << high_beta << ",";
- ofs << gamma << ",";
- ofs << std::endl;
-
- std::cout << theta << "," << alpha << "," << low_beta << ",";
- std::cout << high_beta << "," << gamma << std::endl;
+ std::cout << hon << "," << theta << "," << alpha << "," << low_beta << ",";
+ std::cout << high_beta << "," << gamma << "," << af3 << ",";
+ std::cout << t7 << "," << pz << "," << t8 << "," << af4 << std::endl;
}
}
}
@@ -105,9 +115,9 @@ void main() {
#endif
}

- ofs.close();
-
IEE_EngineDisconnect();
IEE_EmoStateFree(eState);
IEE_EmoEngineEventFree(eEvent);
-}
\ No newline at end of file
+
+ return 0;
+}

여기 까지 해 놓고 소스 코드를 살펴 보면, (지금부터는 init 절차는 생략한다)

...
44 IEE_DataChannel_t channelList[] = { IED_AF3, IED_AF4, IED_T7, IED_T8, IED_Pz };
...
65 IEE_FFTSetWindowingType(engineUserID, IEE_HAMMING);
...
95 double alpha, low_beta, high_beta, gamma, theta;
..
98 for(int i=0 ; i< sizeof(channelList)/sizeof(channelList[0]) ; ++i)
99 {
100 int result = IEE_GetAverageBandPowers(engineUserID, channelList[i], &theta, &alpha,
101 &low_beta, &high_beta, &gamma);
...

1. 읽어들일 채널 리스트를 정의하고 (어차피 다 읽을거 왜 매번 설정을 할까?)
2. FFT window Type 을 설정 한다.
  - 어이쿠.. Fast Fourier Fransform 이 나왔다. 내용은 모르니 일단 Pass
3. IEE_GetAverageBandPowers() 로 값을 읽어 온다.

[결과물]
Theta, Alpha, Low_beta, High_beta, Gamma
48458,24315.4,66590.6,27574.5,40978.4,
32995.3,35488.1,56518,29273.1,29289.1,
48599.8,40455.4,37045.8,21105.8,44495.5,
10313.9,26966.5,47016.1,38151.5,40650.3,
52163.3,41597.3,45387,31473.6,36107.1,
26772.2,19018.6,76126.5,31645.1,39417.3,
20847.2,35387.8,33549,52477.6,41923.5,....

대략 이런 식인데, 보기 좋게 그래프화 하면 아래처럼 그려진다.






Battery status 나 EmoStateLogger 에서 갖게 된 data 에 대한 불신 때문인지 위 그래프가 무의미 한 것이 아닌가 의심도 되지만, 우선 '0' 이 아닌 값이 규칙적으로 들어오니 의미있는 data 라고 생각해야 겠다

정리하면,
이 예제는 아직 Linux 용 cmake 를 지원하지 않고 컴파일 오류도 있으나 API 테스트는 가능해 보임.

댓글 없음: