Skip to content

Latest commit

 

History

History
432 lines (350 loc) · 27.6 KB

data-gather-into-org.org

File metadata and controls

432 lines (350 loc) · 27.6 KB

Mindwave Emacs

;;; data-gather-into-org.el --- Gather Mindwave Data into an org file 

;; Copyright (C) 2012 Jonathan Arkell

;; Author: Jonathan Arkell <[email protected]>
;; Created: 16 June 2012
;; Keywords: comint mindwave
;; Version 0.1

;; This file is not part of GNU Emacs.
;; Released under the GPL     

(provide 'data-gather-into-org)

<<dg-basic-setup>>
<<dg-marks>>
<<dg-collect>>
<<dg-write>>
<<dg-results-table>>
<<dg-mark-window>>
<<dg-timed-recording>>

Changelog

v 0.1
Split from main mindwave-emacs.org file

Gather data into an org buffer

In this example, you can see how to use the base mindwave hooks to capture data into an org buffer. This actually is far more then a simple example, and is really a full working suite of tools that you can use to examine your neurological state.

However, I am not a neuroscientist, I am a computer programmer. If you happen to be a neuroscientist, psychologist or other scientist who can help out my process, I would LOVE to hear from you.

When you purchase a mindwave, it doesn’t actually come with any long-term data logging tools. There is an open source tool to show your brainwaves on a graph, but again it doesn’t provide logging.

I wanted something simple that would provide that for me.

This chunk of code here illustrates how to use mindwave-emacs. It will collect the eSense, eegPower and signal level into a table, that could theoretically be further processed into R, and then even plotted with various programs. I’ve provided some gnuplot code that will graph things pretty nicely.

data-gather works in 2 different modes. The first mode is continuous recording mode, and can be started wtih dg-mindwave/start-recording-session. This will start the recording, and won’t stop it untill the command dg-mindwave/stop-recording-session is issued.

The second mode is a little different. It is used for 45 second “calibration” sessions. The theory is that you start the session, with 15 seconds of throw away data, and then the subject is to spend the next 15 seconds relaxing, and then the final 15 seconds doing a particular task. This can be started with dg-mindwave/start-45-second-session and stops automatically after the 45 seconds.

In both modes the concept of a “mark” applies. This shows when a particular stimulus is encountered. You can use dg-mindwave/generic-mark to insert a mark called “mark”, or use dg-mindwave/mark to prompt for the name of the mark. In their chase, once input, the mark will be inserted immediately.

Output Example

This is an example of the output of a 45 second recording session. Notice the “relaxed” and “tester” marks.

Table

timesignalhighGammalowGammahighBetalowBetahighAlphalowAlphathetadeltameditationattentionmark
134022952206715883973971235894281993921762450128340
134022952305293768021524643673803645331707611688361
13402295240265911910831536064350127286604201857869
134022952504270339375360541339242114621006675769436448925374
1340229526013471792914365625782061743832568849069584451
13402295270227165186288134302868889279085511180852944
134022952804299569069737985897715999694431148121434
1340229529029686811617984718756400055889745332124
13402295300170465439922201217502309914680907023550
1340229531028092879601715968755294125696713795056
13402295320770561877244165783137912079148379609694454
134022953305949821085946521138023051839344253724769
13402295340764940279078501242731816222758381684363
134022953501678201737996433336642452976458993551
1340229536011896646308435224005698514239821984457
13402295370211297063396014244265351657723013215336056
13402295380190513918818634113640482322706121556054relaxed
1340229539018948464966944725817103511294528347066
1340229540015973099210821943878880363033666698161
134022954101861565713161532112381226515898114008157
13402295420153818996201517137246658175063859063
13402295430169230445080536856311747714533339060
1340229544022173062433265593085737521089198167860
1340229546025642950373373125809181995943103275761
134022954702400514058397216807065101313129615364
134022954801836146110593133421543532443509710695369
13402295490235835574657413519473002802114325767
13402295500166216943111329624047591545163586363
1340229551093531358643587062422730618114597060
13402295520183535104576721820362749436874808154
1340229553010213251508754832280648011058164767857tester
13402295540256514681051312150217711613021917175207860
1340229555050492925145549252827024547459157476644
1340229556022962791277925511375261429351404295037
134022955702762265965197152436010126355951855343
134022955802613140940492419478433814948100975740
1340229559043816161297413023176057128101841625056
1340229560019762660730054895101302010564136176467
1340229561035594133669659342822232078103153205770
13402295620812337331337703177266897549661434204064
1340229563066676829101652551924609850722401381981943461
13402295640295284742045480148553328251543009361552057
1340229565038753082964350956947561624947595652344
13402295660678085929355122627212622718259709613756
1340229567050225286824811726214701582025245413315163

Gnuplot code

reset
set terminal png size 1024,800

set multiplot layout 7,1


unset title

set tmargin 0
set bmargin 0
set lmargin 8
set rmargin 2

set grid

set xtics format ""

set ylabel "EEG"
set ytics 

set yrange [0 to 2000000]
plot data u 1:10 w lines title 'D' axis x1y1 lt rgb '#0000cc'
plot data u 1:9 w lines title 'T' axis x1y1 lt rgb '#0000ff'
set yrange [0 to 100000]
plot data u 1:8 w lines title '+A' lt rgb '#00ffff', data u 1:7 with lines title '-A'  lt rgb '#0088ff' 
plot data u 1:6 w lines title '+B' lt rgb '#00aa00', data u 1:5 with lines title '-B'  lt rgb '#00ff00'
plot data u 1:4 w lines title '+G' lt rgb '#ff0000', data u 1:3 with lines title '-G'  lt rgb '#ffaa00'

set xlabel "Time"

set yrange [0 to 100]

plot data u 1:11 lt rgb '#00cccc' w lines title 'eM' axis x1y1, \
     data u 1:12 lt rgb '#ffcc00' w lines title 'eA' axis x1y1 

unset multiplot

example.png

Set up the bacis and get a file for writing ready

change the dependence on Brain.org to something that can be set with customize.

Note, that the code assumes that you want everything put in a buffer called Brain.org.

(require 'mindwave-emacs)

(defvar dg-mindwave/org-buffer "Brain.org")
  

Marks

The basic concept of this data gathering scheme is the concept of ‘marks’. During the examination of brainwaves, there may be external or internal stimulus that trigger a sensation which may (or may not) trigger a change in brainwave state. that brainwave state should then be stored on the table for later analysis.

Right now a very simple interface is defined and provided. One can either insert a generic “mark” into the table, and insert a prompted for mark. A little later we will create a buffer that takes alpha characters as marks.

dg-mindwave/generic-mark
Inserts a generic mark called “mark”.
dg-mindwave/mark
Prompt for a mark name, and mark it with that mark.

Note, that the act of prompting for a mark name already skews the results, right?

(defvar dg-mindwave/mark nil)

(defun dg-mindwave/generic-mark ()
  "Used to generically mark a section of the table"
  (interactive)
  (dg-mindwave/mark "mark"))

(defun dg-mindwave/mark (mark)
  "Set a mark on the section of a table"
  (interactive "sMark: ")
  (setq dg-mindwave/mark mark))

figure out a much better interface for marks

Right now the current mark implementation is clunky at best. In my ideal work I would like to have a way to receive these mark inputs from the mindwave wearer in as unobtrusive a way as possible.

Data collection

This is where the magic happens. A hook is set up to read the various values from the mindwave output, and then write them into an org-mode table.

(defun dg-mindwave/if-assoc (key lst)
  (if (assoc key lst)
      (number-to-string (cdr (assoc key lst)))
      " "))

(defun dg-mindwave/get-in (lst key keylist)
  (let ((innerList (assoc key lst)))
    (mapconcat '(lambda (el)
                  (if (and innerList 
                           (assoc el innerList))
                       (number-to-string (cdr (assoc el innerList)))
                    "")) 
               keylist
               " | ")))

(defun dg-mindwave/collect-and-write (out)
  "Hook function to gather and write data to the table."
  (when (and (assoc 'eSense out)
             (assoc 'eegPower out))
    (let ((string-write (concat "| " 
                                (format-time-string "%s")
                                " | "
                                (dg-mindwave/if-assoc 'poorSignalLevel out) 
                                " | "
                                (dg-mindwave/get-in out 'eegPower '(highGamma lowGamma highBeta lowBeta highAlpha lowAlpha theta delta))
                                " | "
                                (dg-mindwave/get-in out 'eSense '(attention meditation))
                                " | "
                                (when dg-mindwave/mark
                                  (let ((m dg-mindwave/mark))
                                    (setq dg-mindwave/mark)
                                    m))
                                " | "                          
                                "\n")))
      (with-current-buffer dg-mindwave/org-buffer 
        (goto-char (point-max))
        (insert string-write)))))

(defun dg-mindwave/start-recording-session (name)
  "Sets up an entirely new mindwave session for recording." 
  (interactive "sMindwave Session Name: ")
  (with-current-buffer dg-mindwave/org-buffer
    (goto-char (point-max))
    (insert "\n\n")
    (insert "*** ")
    (insert (current-time-string))
    (insert "  ")
    (insert name)
    (insert "\n")
    (insert "#+TBLNAME: ")
    (insert name)
    (insert "\n")
    (insert "|------------+--------+-----------+----------+----------+---------+-----------+----------+--------+---------+------------+-----------+------|\n")
    (insert "|       time | signal | highGamma | lowGamma | highBeta | lowBeta | highAlpha | lowAlpha |  theta |   delta | meditation | attention | mark |\n")
    (insert  "|------------+--------+-----------+----------+----------+---------+-----------+----------+--------+---------+------------+-----------+------|\n"))
  (mindwave-get-buffer)
  (when (not (member 'dg-mindwave/collect-and-write 'mindwave-hook))
    (add-hook 'mindwave-hook 'dg-mindwave/collect-and-write)))

(defun dg-mindwave/stop-recording-session ()
  "Stops a recording session"
  (interactive)
  (remove-hook 'mindwave-hook 'dg-mindwave/collect-and-write))

Results Table

In my simple explorations, I found it handy to have a secondary table generated from the first that shows various simple statistical qualities.

Again, I am not a scientist, but I do find these result tables to be fairly informative. If you have any ideas on how to make them better, let me know.

Note, that for now the code formatting, especially of the org-mode calc table is kinda yucky and could be better.

#+name dg-results-table

(defun dg-mindwave/make-results-table (name)
  "Generate a results table for a mindwave session"
  (interactive "sMindwave Session Name: ")
  (insert "\n")
  (insert "#+TBLNAME: ")
  (insert name)
  (insert "_results")
  (insert "\n")
  (insert " |         |      signal | highGamma |  lowGamma |  highBeta |   lowBeta | highAlpha |  lowAlpha |     theta |     delta | meditation | attention |") (insert "\n")
  (insert " |---------+-------------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+-----------+------------+-----------|") (insert "\n")
  (insert " | vmean   |             | 		  | 		  | 		  | 		  |			  |			  | 		  | 		  |  		   |		   |") (insert "\n")
  (insert " | vmedian |             | 		  | 		  | 		  | 		  |			  |			  | 		  | 		  |  		   |		   |") (insert "\n")
  (insert " | vmax    |             | 		  | 		  | 		  | 		  |			  |			  | 		  | 		  |  		   |		   |") (insert "\n")
  (insert " | vmin    |             | 		  | 		  | 		  | 		  |			  |			  | 		  | 		  |  		   |		   |") (insert "\n")
  (insert " | vsdev   |             | 		  | 		  | 		  | 		  |			  |			  | 		  | 		  |  		   |		   |") (insert "\n")
  (insert (concat "    #+TBLFM: @2$2=vmean(remote(" name ",@II$2..@III$2))::@3$2=vmedian(remote(" name ",@II$2..@III$2))::@4$2=vmax(remote(" name ",@II$2..@III$2))::@5$2=vmin(remote(" name ",@II$2..@III$2))::@6$2=vsdev(remote(" name ",@II$2..@III$2))::@2$3=vmean(remote(" name ",@II$3..@III$3))::@3$3=vmedian(remote(" name ",@II$3..@III$3))::@4$3=vmax(remote(" name ",@II$3..@III$3))::@5$3=vmin(remote(" name ",@II$3..@III$3))::@6$3=vsdev(remote(" name ",@II$3..@III$3))::@2$4=vmean(remote(" name ",@II$4..@III$4))::@3$4=vmedian(remote(" name ",@II$4..@III$4))::@4$4=vmax(remote(" name ",@II$4..@III$4))::@5$4=vmin(remote(" name ",@II$4..@III$4))::@6$4=vsdev(remote(" name ",@II$4..@III$4))::@2$5=vmean(remote(" name ",@II$5..@III$5))::@3$5=vmedian(remote(" name ",@II$5..@III$5))::@4$5=vmax(remote(" name ",@II$5..@III$5))::@5$5=vmin(remote(" name ",@II$5..@III$5))::@6$5=vsdev(remote(" name ",@II$5..@III$5))::@2$6=vmean(remote(" name ",@II$6..@III$6))::@3$6=vmedian(remote(" name ",@II$6..@III$6))::@4$6=vmax(remote(" name ",@II$6..@III$6))::@5$6=vmin(remote(" name ",@II$6..@III$6))::@6$6=vsdev(remote(" name ",@II$6..@III$6))::@2$7=vmean(remote(" name ",@II$7..@III$7))::@3$7=vmedian(remote(" name ",@II$7..@III$7))::@4$7=vmax(remote(" name ",@II$7..@III$7))::@5$7=vmin(remote(" name ",@II$7..@III$7))::@6$7=vsdev(remote(" name ",@II$7..@III$7))::@2$8=vmean(remote(" name ",@II$8..@III$8))::@3$8=vmedian(remote(" name ",@II$8..@III$8))::@4$8=vmax(remote(" name ",@II$8..@III$8))::@5$8=vmin(remote(" name ",@II$8..@III$8))::@6$8=vsdev(remote(" name ",@II$8..@III$8))::@2$9=vmean(remote(" name ",@II$9..@III$9))::@3$9=vmedian(remote(" name ",@II$9..@III$9))::@4$9=vmax(remote(" name ",@II$9..@III$9))::@5$9=vmin(remote(" name ",@II$9..@III$9))::@6$9=vsdev(remote(" name ",@II$9..@III$9))::@2$10=vmean(remote(" name ",@II$10..@III$10))::@3$10=vmedian(remote(" name ",@II$10..@III$10))::@4$10=vmax(remote(" name ",@II$10..@III$10))::@5$10=vmin(remote(" name ",@II$10..@III$10))::@6$10=vsdev(remote(" name ",@II$10..@III$10))::@2$11=vmean(remote(" name ",@II$11..@III$11))::@3$11=vmedian(remote(" name ",@II$11..@III$11))::@4$11=vmax(remote(" name ",@II$11..@III$11))::@5$11=vmin(remote(" name ",@II$11..@III$11))::@6$11=vsdev(remote(" name ",@II$11..@III$11))::@2$12=vmean(remote(" name ",@II$12..@III$12))::@3$12=vmedian(remote(" name ",@II$12..@III$12))::@4$12=vmax(remote(" name ",@II$12..@III$12))::@5$12=vmin(remote(" name ",@II$12..@III$12))::@6$12=vsdev(remote(" name ",@II$12..@III$12))")))

Results Example (basic)

signalhighGammalowGammahighBetalowBetahighAlphalowAlphathetadeltameditationattention
vmean0.06161137412192.72015232.82019399.64215180.61617033.28722201.69976134.531270353.2553.24170653.424171
vmedian08132.51001414247.59695.58411.59076.523773.5629365456
vmax26869701521111922002607063636677990148200332920134100100
vmin0303378638342436311202530000
vsdev1.265660212190.02115797.15617531.91820699.66429733.99751731.083124792.48449634.6722.64134017.949459

fix formatting of the TBLFM line

make the lisp function re-calc the table after insertion

Window for mark input

The mark window is a very simple mark interface. It will allow you to use the lower case letters a through z to insert that letter as a mark, which can be used as a mnemonic for various situations.

Right now the buffer is just blank, but I will be working on improving it in the future.

#+name dg-mark-window

(defun dg-mindwave/create-input-buffer ()
  "Create an input buffer so that marks can be handled"
  (interactive)
  (pop-to-buffer (get-buffer-create "*mindwave-input*") )
  (local-set-key " " 'dg-mindwave/generic-mark)
  (local-set-key "a" '(lambda () (interactive) (dg-mindwave/mark "a")))
  (local-set-key "b" '(lambda () (interactive) (dg-mindwave/mark "b")))
  (local-set-key "c" '(lambda () (interactive) (dg-mindwave/mark "c")))
  (local-set-key "d" '(lambda () (interactive) (dg-mindwave/mark "d")))
  (local-set-key "e" '(lambda () (interactive) (dg-mindwave/mark "e")))
  (local-set-key "f" '(lambda () (interactive) (dg-mindwave/mark "f")))
  (local-set-key "g" '(lambda () (interactive) (dg-mindwave/mark "g")))
  (local-set-key "h" '(lambda () (interactive) (dg-mindwave/mark "h")))
  (local-set-key "i" '(lambda () (interactive) (dg-mindwave/mark "i")))
  (local-set-key "j" '(lambda () (interactive) (dg-mindwave/mark "j")))
  (local-set-key "k" '(lambda () (interactive) (dg-mindwave/mark "k")))
  (local-set-key "l" '(lambda () (interactive) (dg-mindwave/mark "l")))
  (local-set-key "m" '(lambda () (interactive) (dg-mindwave/mark "m")))
  (local-set-key "n" '(lambda () (interactive) (dg-mindwave/mark "n")))
  (local-set-key "o" '(lambda () (interactive) (dg-mindwave/mark "o")))
  (local-set-key "p" '(lambda () (interactive) (dg-mindwave/mark "p")))
  (local-set-key "q" '(lambda () (interactive) (dg-mindwave/mark "q")))
  (local-set-key "r" '(lambda () (interactive) (dg-mindwave/mark "r")))
  (local-set-key "s" '(lambda () (interactive) (dg-mindwave/mark "s")))
  (local-set-key "t" '(lambda () (interactive) (dg-mindwave/mark "t")))
  (local-set-key "u" '(lambda () (interactive) (dg-mindwave/mark "u")))
  (local-set-key "v" '(lambda () (interactive) (dg-mindwave/mark "v")))
  (local-set-key "w" '(lambda () (interactive) (dg-mindwave/mark "w")))
  (local-set-key "x" '(lambda () (interactive) (dg-mindwave/mark "x")))
  (local-set-key "y" '(lambda () (interactive) (dg-mindwave/mark "y")))
  (local-set-key "z" '(lambda () (interactive) (dg-mindwave/mark "z"))))  

Make the buffer keep a record of the marks used.

have some kind of way to input inside the mark buffer the meaning of various marks

In the mark buffer, the eeg and signal scores should be displayed.

Timed Recordings

Timed recordings are for micro-experimentation of your EEG. The idea is that you record EEG activity in 15 second chunks, which each chunk being a different activity.

  1. a ‘whatever chunk’, and is basically 15 seconds of “whatever is going on right now”.
  2. a 15 second chunk of eyes closed and relaxing
  3. a 15 second chunk of experimentation or calibration, for instance:
  • eyes closed and relaxing
  • eyes opened and relaxing
  • eyes closed and breathing deeply
  • eyes open and doing complicated math problems.

This can be used for self experimentation. At the 15 second mark, Emacs will beep at you and tell you to close your eyes. At the 30 second mark, it will beep at you and insert the name of the session as a mark. finally, it will beep at the 45 second mark and stop the recording session.

(defun dg-mindwave/start-45-second-session (name) 
  "Start a 45 second session with appropriate marks.  NAME should be a simple name."
  (interactive "s45 Second Session Name:")
  (dg-mindwave/start-recording-session name)
  (run-at-time 15 nil '(lambda ()
                         (message "Close your Eyes and Relax")
                         (beep 1) 
                         (dg-mindwave/mark "relaxed")))
  (run-at-time 30 nil `(lambda ()
                         (message ,name)
                         (beep 1)
                         (dg-mindwave/mark ,name)))
  (run-at-time 45 nil '(lambda ()
                         (beep 1)
                         (message "stop")
                         (dg-mindwave/stop-recording-session))))