0% found this document useful (0 votes)
37 views

Brain-Computer Interface Using Single-Channel Electroencephalography

Medical electronics

Uploaded by

Jeya Kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views

Brain-Computer Interface Using Single-Channel Electroencephalography

Medical electronics

Uploaded by

Jeya Kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 53

BCIusingEEG

BrainComputerInterface
CharlesMoyes(cwm55)andMengxiangJiang(mj294)

WebuiltarobustBrainComputerInterface(BCI)usingsinglechannelelectroencephalography(EEG)withanAVR
microcontroller,andwewereabletoplayPongusingourbrainwaves(andmonitor/recordoursleep).
Watchademovideo

Introduction
OurgoalwastobuildabraincomputerinterfaceusinganAVRmicrocontroller.Wedecidedthattheleastinvasivewayofmeasuringbrainwaveswouldbeusing
electroencephalography(EEG)torecordmicrovoltrangepotentialdifferencesacrosslocationsontheuser'sscalp.Inordertoaccomplishthis,weconstructedatwostage
amplificationandfilteringcircuit.Moreover,weusedthebuiltinADCfunctionalityofthemicrocontrollertodigitizethesignal.Passivesilverplatedelectrodessoakedinasaline
solutionareplacedontheuser'sheadandconnectedtotheamplifierboard.TheoptoisolatedUARTsendstheADCdigitalvaluesoverUSBtoaPCconnectedtothemicrocontroller.
ThePCrunssoftwarewritteninMATLABandCtoperformFFTandrunmachinelearningalgorithms(SVM)ontheresultantsignal.Fromthere,wewereabletocontrolourown
OpenGLimplementationoftheclassicPCgamePongusingourmind'sbrainwaves.WealsowrotesoftwaretorecordoursleepandstoretheEEGsignalinsideadatafile.

Figure:RecordingaUser'sBrainWavesUsingEEG
Source:http://www.enotes.com/electroencephalogramreference/eegmachine

HighLevelDesign
RationalandInspirationforProjectIdea
OurprojectideawasinspiredbyCharles'ssevereobstructivesleepapnea(OSA)disorder.Inordertodiagnosesleepapnea,aclinicalsleepstudyisperformedwherethepatientis
attachedtoEEGelectrodes,alongwithSpO2,EMG,andrespirationsensors.Thepatient'ssleepingpatternsarerecordedovernight,andapneas(periodsofsleepwithoutbreathing)
canbeidentifiedwithinthecollecteddata.Thisprocessiscostlyandrequiresanovernightstayatahospitalorsleeplab.Moreover,thepatientoftenisdeniedaccesstotheirown
datasincealicensedsleepspecialistinterpretsitforthem.Ourgoalwastobuildalowcostalternativethatwouldallowuserstotaketheirhealthintheirownhandsbydiagnosingand
attemptingtotreattheirownsleepdisorders.Moreover,ourprojecthasdiverseapplicationsintheareasofneurofeedback(aidingmeditationandtreatmentofADHDdisorder),along
withbraincomputerinterfaces(allowingthedisabledtocontrolwheelchairsandspellwordsonacomputerscreenusingtheirthoughts).

BackgroundMath
SupportVectorMachines
Themachinelearningalgorithmweusedwasasupportvectormachine(SVM),whichisaclassifierthatoperatesinahigherdimensionalspaceandattemptstolabelthegivenvectors
usingadividinghyperplane.Thesupervisedlearningmethodtakesasetoftrainingdataandconstructsamodelthatisabletolabelunknowntestdata.Abriefexplanationofthe
mathematicsbehindSVMsfollows.Duringtraining,theSVMisgivenasetofinstancelabelpairsoftheform{(xi, yi ) : i = 1, , l} wheretheinstancesaren dimensionalvectors
n
suchthatxi R .Then dimensionsrepresentn separate"features."Inaddition,thelabelsareintheformy {1, 1},where1and1designatetargetandnontargetinstances
respectively.To"train"thesupportvectormachinetorecognizeunknowninputvectors,thefollowingminimizationproblemissolved:
l

1
min
w,b,

w+C

i=1

subjectto:
(

) + b) 1

y (w
i

(x i ) + b) 1

Source:http://www.csie.ntu.edu.tw/~cjlin/papers/guide/guide.pdf
Notethat isafunctionthatmapsthetrainingvectorsxi intoahigherdimensionalspace,whileC

> 0

andi actaserrorterms(socalled"slackvariables").Moreover,K isthe

kernelfunctionwhichisdefinedasK((x i ) (x j )) .Forourpurposes,weusedaradialbasisfunction(RBF)kernelwhichhasaK functionofK(x i , x j )


where > 0 representsausertunableparameter.

= exp(||x i x j ||

DFT
ThediscreteFouriertransform(DFT)transformsasequenceofN complexnumbers(anN pointsignal)inthetimedomainintoanotherN sequenceinfrequencydomainviathe
followingformula:
N 1

X k = xn e

n=0

TheFouriertransformisdenotedbyF ,whereX

= F (x)

.Source:http://en.wikipedia.org/wiki/Discrete_Fourier_transform

Analgorithm,theFastFourierTransform(FFT)byCooleyandTukey,existstoperformDFTinO(n log n)computationalcomplexityasopposedtoO(n


speeduptoperformDFTsinrealtimeontheinputsignals.

.Wetakeadvantageofthis

Filters
InordertofilterthebrainwavedatainMATLAB,weuseafiniteimpulseresponse(FIR)filterwhichoperatesonthelastN + 1samplesreceivedfromtheADC.Insignalprocessing,
theoutputyofalineartimeinvariant(LTI)systemisobtainedthroughconvolutionoftheinputsignalxwithitsimpulseresponseh.Thishfunction"characterizes"theLTIsystem.
Thefilterequationintermsoftheoutputsequencey[n]andtheinputsequencex[n]is:
y[n] = h0 x[n] + h1 x[n 1] + + hN x[n N ]

NotethatonlyN coefficientsareusedforthisfilter(hence,"finite"impulseresponse).IfweletN
http://en.wikipedia.org/wiki/FIR_filter

,thenthefilterbecomesaninfiniteimpulseresponse(IIR)filter.Source:

EEGSignalAnalysis
TheEEGsignalitselfhasseveralcomponentsseparatedbyfrequency.Deltawavesarecharacteristicofdeepsleepandarehighamplitudewavesinthefrequencyrange0 f 4
Hz.Thetawavesoccurwithinthe48Hzfrequencybandduringmeditation,idling,ordrowsiness.Alphawaveshavefrequencyrange814Hzandtakeplacewhilerelaxingor
reflecting.Anotherwaytoboostalphawavesistoclosetheeyes.Betawavesresideinthe1330Hzfrequencybandandarecharacteristicoftheuserbeingalertoractive.They
becomepresentwhiletheuserisconcentrating.Gammawavesinthe30100Hzrangeoccurduringsensoryprocessingofsoundandsight.Lastly,muwavesoccurinthe813Hz
frequencyrangewhilemotorneuronsareatrest.Musuppressiontakesplacewhentheuserimaginesmovingoractuallymovespartsoftheirbody.AnexamplediagramoftheEEG
signaltypesfollows:

Figure:EEGWaveFrequencyRanges
Source:http://neurodevelopmentcenter.com/
EEGsignalsalsocontaineventrelatedpotentials(ERPs).AnexampleistheP300signal,whichoccurswhentheuserrecognizesaniteminasequenceofrandomlypresentedevents
occurringwithaBernoullidistribution.Itisemittedwithalatencyofaround300600msandshowsupasadeflectionintheEEGsignal:

Figure:P300EventRelatedPotential(ERP)
Source:http://en.wikipedia.org/wiki/Eventrelated_potential
OtherartifactspresentthemselvesintheEEGsignalaswellsuchaseyeblinkingandeyemovement.Anillustrationofanexamplesignalcorruptedbyeyeblinkingfollows:

Figure:EyeBlinkingwithinanEEGSignal
Source:http://psy.hull.ac.uk/Staff/m.large/Infonew/

LogicalStructure
Theoverallstructureoftheprojectconsistsofanamplifierpipelineconsistingofadifferentialinstrumentationamplifier(wherecommonmodenoiseismeasuredusingarightlegdriver
attachedtothepatient'smastoidorearlobe),alongwithanoperationalamplifierandsomefilters(toremoveDCoffsets,60Hzpowerlinenoise,andotherartifacts).Fromthere,the
signalpassestothemicrocontroller,whereitisdigitizedviaanADC.Next,itissendoveranisolatedUSBUARTconnectiontoaPCviaanFTDIchip.ThePCthenperformssignal
processingandisabletooutputtheresultstotheuser,creatinganeurofeedbackloopwhichallowstheusertocontrolthePCusingtheirbrainwaves.Afunctionalblockdiagramof
theoverallstructurefollows:

Figure:HighLevelBlockDiagramofProject

Hardware/SoftwareTradeoffs
PerformingFFTinhardwareusingafloatingpointunit(FPU)orafieldprogrammablegatearray(FPGA)wouldhaveallowedustorealizeaconsiderablespeeduphowever,ourbudget
wasonlylimitedto$75,sothiswasnotanoption.AnothertradeoffweencounteredwastheuseofMATLABversusC.MATLABisaninterprettedlanguagewhosestrengthliesin
performingvectorizedmatrixandlinearalgebraoperations.Itisveryfastwhenperformingtheseoperations,butitisaninterpretedlanguagethatdoesnotrunasnativecode.This
speedpenaltyaffecteduswhenweattemptedtocollectdatainrealtimefromtheserialportat57600baud.Tocombatthisspeedpenalty,IwroteamuchfasterOpenGLserialplotting
applicationinCthatrunsat200400framespersecondonmymachine(wellabovetheADCsamplerateof200Hz)andisabletoperformFFTsinrealtimeasthedatacomesin.
Furthermore,yetanothertradeoffwasthedecisiontousethePCtooutputtheEEGwaveformsratherthanabuiltingraphicalLCDinhardware.Onceagain,budgetconstraintslimited
us,alongwithpowerusagesinceforsafetyreasons,ourdeviceusesfourAAbatteriesinsteadofamainsACpowersupply.

RelationshipofYourDesigntoAvailableStandards
ThereexistsaModularEEGserialpacketdataformatthatistypicallyusedtotransmitEEGdataoverserialhowever,weusedASCIIterminaloutput(16bitintegervaluesinASCII
separatedbylinebreaks)forsimplicity,easeofdebugging,andcompatibilitywithMATLAB.Moreover,serialcommunicationsfollowedtheRS232/USBstandards.Another
considerationwastheIEC601standard.IEC601isamedicalsafetystandardformedicaldevicesthatensuresthattheyaresafeforpatientuse.Unfortunately,testingforIEC601
compliancewasverymuchoutofbudget.Nevertheless,wediscussthemanysafetyconsiderationsthatweabsolutelyadheredbyintheSafetysubsection(underResults)ofthis
report.

HardwareDesign
AmplifierBoardDesign
Webuiltananalogamplificationcircuitwithatotalgainof1,500basedonthedesignbyChipEpsteinathttps://sites.google.com/site/chipstein/homepage/eegwithanarduinowith
modifiedgainandfilterstages.ThefirststageusesanAD620instrumentationamplifierfordifferentialcommonmodesignalrejectiontoreducenoise.ThegainoftheAD620is
approximately23.Avoltagedivideranda3140opampbufferprovidea2.5Vvirtualgroundfortheinstrumentationamplifier.Afterpassingthroughtheinstrumentationamplifier,the
signalisfilteredusinganRChighpassfilterwithf c = 0.13 Hz(wemodifiedtheoriginaldesigntoallowtheP300ERPtoresidewithinthepassbandofthefilter).
Next,thesignalundergoesasecondstageamplification.Thegainofa3140opampissettoapproximately65.TheoutputsignalisthenfilteredusinganRClowpassfilterwithacut
offfrequencyofapproximately48Hz.ThisfrequencywaschosentopreservethelowfrequencycontentoftheEEGsignal,whileremoving5060Hzpowerlinenoisefromthesignal.
WeorderedpartsfromDigiKeyandsamplesfromAnalogDevices,TexasInstruments,andMaximSemiconductor.WealsosampledsilverplatedpassiveEEGelectrodes.From
there,wewereabletoamplifya125V Vpp ,10HzsquarewavecalibrationsignaltowellwithintheADCreferencevoltagerange(01.1V )andplotitonaPCinrealtime.We
constructedthisprototypecircuitonabreadboard.Aschematicdiagramoftheamplifierboardfollows:


Figure:EEGAmplifierBoardSchematic

MicrocontrollerBoardDesign
Themicrocontrollerboardcontainsavoltagedividerthatoutputsa125V Vpp ,10HzsquarewavecalibrationsignalfromPinD.3ofthemicrocontroller.Moreover,itcontainsa
+6V DCbatterypowersupplythatprovidesDCpowertothemicrocontrollerandtheamplifiers.Aschematicdiagramofthemicrocontrollerboardfollows:

Figure:MicrocontrollerPowerSupplySchematic

OptoIsolatedUARToverUSBDesign
Weconstructedanisolated+6VDCpowersupplyusing4AAbatteriesandconnecteditthemicrocontrollerusingthePCBtargetboard.Wecutthegroundtraceconnectingthe
microcontrollergroundtotheUSBgroundusingadremeltool.Anillustrationofthecutthatweperformedfollows:

Figure:GroundIsolationDremelCutonTargetBoard
WeusedaFairchildSemiconductor6N137optoisolatortoisolatetheUSBpowerfromthemicrocontrollerpower.ThelineofisolationisbetweenthemicrocontrollerUARTRXandTX
pins(PinD.0andPinD.1)andtheFTDIchip'sRXandTXpins.Aschematicdiagramoftheisolationcircuitfollows:

Figure:USBUARTOptoIsolationSchematic

ElectrodeCapDesign
WeconstructedanEEGhelmetconsistingofanoldbaseballcapmodifiedtocontainEEGelectrodes.WefollowedtheInternational1020SystemofElectrodePlacementbyincluding
electrodesatthedesignatedlocationsonthescalp:Occipitallobe(O),Centrallobe(Fz ,Pz ,C3 ,C4 ,Cz ),andFrontallobe(Fp ,Fp ,G ).Adiagramofthe1020systemofelectrode
placementfollows:
1

Figure:EEGElectrodePlacementDiagram
Source:http://www.immrama.org/eeg/electrode.html

SoftwareDesign

MATLABSerialCode
TheprimaryfunctionoftheMATLABserialcodeistoacquiredigitalEEGsignaldatafromthemicrocontrollerovertheserialport.Wewrotesomecodetoplotthesignalontothe
screenandtoperformrudimentarysignalprocessingtasks(FFTandfiltering).TheMATLABcodeconsistsofthreefiles:plot_samples.m,plot_samples_rt.m,andserial_test.m

Figure:CalibrationSignalPlottedwithinMATLAB
Theserial_test.mscriptopenstheserialportanddisplaysanalmostrealtimeplotoftheserialdata.Itparsestheserialdatainawhileloopviafscanfandstr2num.Additionally,
itupdatestheplotwindowcontentsusingMATLAB'sdrawnowcommand.Theloopterminatesiftheuserclosestheplotwindow,causingthescripttocleanupthefilehandleand
closetheserialport.

Figure:ActualEEGSignalPlottedwithinMATLAB(observethebetawavesandeyeblinkartifact)
Theplot_samples.mscriptopenstheserialport,andreadsexactlyN = 200 samplesofEEGdata(around2seconds).Itthenclosesandcleansuptheserialport.Next,a60Hz
notchfilterisappliedtothesignaltoremovepowerlinenoiseviatheiirnotchandfilter,andtheDCoffset(meanvalue)issubtractedfromthesignal.Finally,thetimedomain
signalisdisplayedinaplotwindow,alongwiththesinglesidedamplitudespectrumcomputedviaMATLAB'sfftfunction.
Theplot_samples_rt.mscriptperformsexactlythesameoperationsastheplot_samples.mscript,exceptitperformstheminaloop.ThescriptoperatesbysamplingN = 200
samplesrepeatedlyuntiltheuserclosestheplotwindow.Asaneffect,thesignalplotandthefrequencyspectrumarerefreshedevery2secondsgivingpsuedorealtimeoperation.

OpenGLPlotter
ArealtimeserialplotterwaswritteninOpenGLandC++.OpenGLisahighperformancegraphicslibrary,whileC++ismuchfasterthanMATLABsinceitrunsasnativecode.The
programdisplaystherealtimewaveformfromtheserialport,alongwithitsFFT.WeusetheFFTW(FastestFFTintheWest)libraryforcomputingtheFFTusingfastalgorithms.
Moreover,extensionswerelateraddedtotheplottingcodeallowPongtobeplayedusingbrainwaves,alongwithaP300ERPdetector.Adataloggingfeaturewasaddedtoallowus
torecordourEEGdatawhileasleeptoafile.TheSDLlibraryisusedtocollectuserinputfromthekeyboardandoutputtheOpenGLgraphicsbuffertoanonscreenwindow.

Figure:CalibrationSignalPlottedwithinOpenGL

InitializationandEventLoop
Themain()functioninitializestheSDLlibrary,clearstheADCbuffers,initializesOpenGL,initializesSDLTTF(forTrueTypefontrendering),openstheserialport,opensthelogfile,
andinitializestheFFTWlibrary.Fromthere,theprogramentersthemaineventloop,aninfinitewhileloopwhichchecksforandhandleskeypresses,alongwithdrawingthescreen
viaOpenGL.
TheQuit()functioncleansupSDL,closestheserialport,deinitializesFFTWfreesthefont,andquitstheprogramusingtheexitUNIXsystemcall.
TheresizeWindow()functionchangesthewindowsizeofthescreenbychangingtheviewportdimensionsandtheprojectionmatrix.Forthisproject,weuseanorthographic
projectionwithrangesx [0, X_SIZE] andy [0, ADC_RESOLUTION] .ThehandleKeyPress()functioninterceptskeypressestoquitthegame(viathe[ESCAPE]key)and
totogglefullscreenmode(usingthe[F1]key).
TheinitGL()functioninitializesOpenGLbysettingtheshadingmodel,theclearcoloranddepth,andthedepthbuffer.

DrawingCode
ThedrawGLScene()functioniscalledonceperframetoupdatethescreen.Itclearsthescreen,setsthemodelviewmatrixtotheidentitymatrixI4 ,shiftstheoscilloscopebuffer
running_buffertotheleftbyone,andfillsthenewspotinthebufferwiththenewestsamplefromtheserialport.ThissampleisobtainedbycallingthereadSerialValue()function
inserial.cpp.ThisfunctionalsocontainslogictoperformtheFFTanddrawitontothescreenaswell.ThesampleissenttotheP300moduleandloggedtotheoutputfile.
Moreover,thepowerspectrumoftheFFTiscomputedusingrfftw_one()andbysquaringthefrequencyamplitudes.
TheFFTbarsandtheoscilloscopepointsareplottedusingGL_LINESandGL_POINTSrespectively.Moreover,linesjoinadjacentoscilloscopepoints.Thefrequencyranges
correspondingtoeachbrainwaveclassification(alpha,beta,etc.)arecalculated,alongwiththeirrelativepowers.Moreover,BCIcodeisexecutedwhichwillbediscussedinthe
OpenGLPongsubsectionofthisreport.ThepongandP300modulesarethendrawnonthescreen,alongwithstatustextusingTTFfontsandtheframebufferisswapped.Lastly,
theframerateiscalculatedusingSDL_GetTicks(),anSDLlibraryfunction.

Figure:ActualEEGSignalPlottedwithinOpenGL

SerialCode
TheserialcodehandlesserialcommunicationsoverUSBandislocatedinserial.cpp.ImportantparameterssuchasthebuffersizeBAUD_RATE,theportnamePORT_NAME,andthe
baudrateB57600arestoredaspreprocessordirectives.TheopenSerial()functionopenstheserialportandsetsthebaudrateto57600(56k).ThereadSerialValue()function
readsandparsesone10bitASCIIADCvaluefromtheserialportbyscanningforanewlineterminatorandusingsscanf(readByte()isunused).Lastly,thecloseSerial()function
closestheserialportdevice.TheUNIXsystemcallsopen,read,andcloseareusedtocarryoutserialI/O,alongwiththeGNUClibrary'ssystem()function.Thedevicefilename
forUSBserialinUNIXis/dev/ttyUSB0.
NotethatiftheNULL_SERIALpreprocessordirectiveisset,thenmockserialdataisusedratherthanactuallycollectingdatafromtheserialdevice.Thisfunctionalityisusefulfor
testingpurposes.

Configuration
Theconfig.hfilecontainspreprocessordirectivesthatcanbeusedtoconfiguretheOpenGLplottingapplicationduringcompiletime.Importantparametersincludethescreenwidth
10
andheight(SCREEN_WIDTHandSCREEN_HEIGHTrespectively),thescreenbitdepth(SCREEN_BPP),ADC_RESOLUTIONtheADCresolution(thenumberofyvalues)setto2 = 1024 ,
andthelogfilenameLOG_FILENAME(defaultingto"eeg_log.csv").

Debugging
Usefulutilityfunctionsfordebuggingpurposesarefoundindebug.cpp.TheFileExists()functionreturnsabooleanindicatingwhetherthegivenfileexists.TheOpenLog()and
CloseLog()functionsareusefulforwritinglogfileswithtimeanddatestamps.Thelog_out()functioncanbepassedaformatstringwhichiswrittentothelogfile,alongwithatime
stamp.Theformat()functiontakesaformatstringandreturnstheformattedresult.Itusesthevformatutilityfunctiontogeneratetheformatstringbasedontheargumentspassed
tothefunction.Thedump()functiondumpsregionsofmemorytothescreeninahumanfriendlyhexadecimalformat.

FontRendering
TTFfontrenderingsupportwasimplementedusingNeHesamplecode(locatedintheReferencessectionofthisreport).TheglBegin2D()andglEnd2D()functionssetupan
orthographicscreenprojectionforfontrendering.Meanwhile,power_of_two()isautilityfunctionthatcalculatesthenextlargestpoweroftwoofthegivenintegerinput(usefulfor
computingOpenGLtexturedimensionswhichmustbepowersoftwo).TheSDL_GL_LoadTexture()functionconvertsanSDL_SurfacestructureinmemorytoanOpenGLtexture
object.ThisisusefulbecausetheSDL_TTFlibraryonlyreturnsanSDL_Surface,butwearerenderingthefontsusingOpenGL.TheInitFont()andFreeFont()functionsusethe
SDL_TTFlibraryfunctionstoloadandfreefontsrespectively.Lastly,glPrint()actslikeanOpenGLimplementationofprintfbyprintingstringstothescreenusingTTFfont
texturesinSDL_TTF.

OpenGLPong

Figure:ScreenshotofOpenGLPonggame
ThePonggamelogicanddrawingcodeislocatedinthepong.cppsourcefile.Moreover,asetofconstant"glyphs"islocatedintheglyphs.hheaderfile.Theseglyphsare2Darrays
ofbooleanvaluescorrespondingtopixelonandpixeloffforeachscoreboardnumberthatisdisplayedonthescreen.Theblockyfontusedforeachglyphlendsaretrosylingtothe
game(whichsuitsthePongaestheticsmuchmorethantheTTFfontrendererwould).

PongGameLogic
Thegamelogicdefinesseveralvariablesincludingthepaddlewidthandheight(storedinPADDLE_WIDTHandPADDLE_HEIGHTrespectively),thepositionsofbothpaddles,thexandy
positionsandvelocitiesoftheball(ballposx,ballposy,ballvelx,ballvely).Thesprite[]arrayincludesabooleanbitmapstoringtheballsprite.
Theupdateball()functionperformancesrudimentarynumericalintegrationtogetthenewballpositionbyaddition.Boundschecksisperformedandtheballvelocityisnegatedto
reflecttheballdirectioniftheballcollideswiththescreenedges.Collisiondetectionisperformedwithbothpaddlesforallfoursidesalsousingboundschecking.Moreover,theball's
velocityisreflectedanditsspeedisincreasedslightlybytheconstantvaluestoredinvel_thresholduponcolliding.Lastly,iftheballcollideswiththeleftorrightsideofthescreen,
theappropriateplayer'sscorevariable(score1orscore2)isincrementeddependingonwhichsidetheballcollideson.TheResetVelocity()functionisinvokedtoresettheball's
speedbacktotheinitialspeed.
ThepongInit()functiondeterminesarandomdirectionofthepongballfortheinitial"faceoff"bycallingautilityfunctionrandomNum()whichgeneratesarandomintegerintheset
0, 1.ThepongHandleKeyDownandpongHandleKeyUpupdatethegamestatebasedonkeyboardpressesanddepresses.The[UP]keymovestherightpaddleup,andthe[DOWN]
keymovestherightpaddledown.Theleftpaddleiscontrolledbytheuser'sbrainwaves.Theupdatepaddle1()andupdatepaddle2()functionsperformnumericalintegrationusing
additiontoupdatethepaddleyvelocities(paddle1yvelandpaddle2yvelrespectively).

PongGameDrawingCode
ThepongUpdateAndRender()functionisinvokedfromtheOpenGLplotterdrawingcode.Itinvokesalloftheupdatingfunctions(updateball(),updatepaddle1(),
updatepaddle2())andallofthedrawingfunctions(drawsprite(),drawpaddlesprite(),drawscore(),anddrawline()).Thedrawscore()functiondrawsbothscoreglyphsonto
thescreenattheproperpositionsbyinvokingdrawglyph().Thedrawglyph()functiontakesanintegernumber,anxandyposition,andan(r, g, b)floatingpointcolorvalue.It
usesGL_POINTStodraweachpixeloftheglyphontothescreen.Thedrawsprite()functiontakesan(x, y) positionandan(r, g, b)coloranddrawstheballspriteatthatlocation.
Thedrawline()functiondrawsastripedlinewithspacingdefinedbyLINE_SPACINGacrossthecenterofthescreendepictingthecenterofthefield.Finally,the
drawpaddlesprite()functiontakesan(x, y) positionandan(r, g, b)coloranddrawstherectangularpaddlespriteusingGL_POINTS.

PongBrainWaveControlandBrainComputerInterface(BCI)Code
ThemostimportantpartofthePongsoftwareisthecodesnippetinmain.cppthatupdatestheleftpaddlevelocitybasedontheuser'sbrainwaves.Weprovidetwomodesofcontrol.
Thefirst,alpharhythm(813Hz)modulation,providesproportionalcontrolbasedontheuser'salphawaves.TheEEGelectrodesareplacedontheuser'sforehead(nearfrontallobes)
duringalpharhythmmeasurement.Theuserconcentratestomovethepaddledownandrelaxestomovethepaddleup.
Theothermethodofcontrolisbasedonmurhythm(813Hz)suppression.Theuserimaginesmovingtheirfeetupanddown(oractuallymovesthem)tomovethepaddle,andifmu
suppressionreachesathreshold,thenthepaddlemovesdownotherwise,itmovesup.Theuserplacestheelectrodesonthetopofthescalpnearthesensorimotorcortex(1020
C

locationsC3 andC4 )duringmusuppressionmeasurement.Althoughbothmethodsworkedequallywell,wefoundduringusertestingthatuserspreferredthealphamodulationcontrol


methodoverthemurhythmsuppressionmethod.
AlphaRhythmModulation

ThealpharhythmmodulationcontrolisdeterminedbytwoboundaryvariablesALPHA_MINandALPHA_MAX.Thepaddle'sypositionposyisproportionaltothealpharhythm'srelative
powerspectrum'svaluewithinthisrange[ALPHA_MIN, ALPHA_MAX].Fromourtesting,wefoundthatvaluesof0.01and0.04workedbestforALPHA_MINandALPHA_MAX
respectively.Userswereabletocontrolthepaddlepositionquiteaccuratelyaftersomepractice.
MuRhythmSuppression

ThemurhythmsupressioncontrolisdeterminedbyonethresholdvalueMU_THRESHOLD.Thepaddle'syvelocitypaddle1yvelissettoavalueof0.1ifthemurhythm'srelativepower
spectrumisbelowMU_THRESHOLD(indicatingmusuppression,ormovementvisualizationintheuser).Thepaddle1yvelissetto0.1 ifthemurhythm'srelativepowerspectrum
exceedsormatchesMU_THRESHOLD.Userswerealsoabletousethismethodofcontrolalbeitlesssuccessfullyduetotheweakersignalreceivedfromplacingthesalineelectrodeson
theuser'shairratherthantheirforehead.Nevertheless,murhythmsuppressionwasalsoaviablecontrolscheme.
NeurofeedbackandCursorControl

ThealphamodulatiomandmusupressioncontrolschemeshavediverseapplicationsbeyondsimplyplayingthegamePonginbraincomputerinterfaces.Wheelchairandcursorcontrol
(both1Dand2D)havebeenaccomplishedbymurhythmsuppression.Inoneinstance,userscontrolledacursorin2Dbyimaginingclenchingeithertheirlefthand,theirrighthand,or
movingtheirfeet.Thiscontrolschemerequiresthreechannelsmeasurethreelocationsofthesensorimotorcortexnearthetopofthescalp:user'sleftside(C3 ),center(Cz ),and
user'srightside(C4 ).Eventhoughwehadonechannel,wecouldeasilyextendthistosupport2Dcursorcontrol,alongwithdetectingeyeblinkingartifactsfor"clicking"themouse.
Onecouldimagineapplyingthistechnologytoallowuserswithspecialneedstocontrolcomputermousemovement.
Theotherapplicationisinthefieldofneurofeedback.NeurofeedbackcreatesafeedbackloopforusersattemptingtomeditateortreatADHDdisorder.Theuservisuallyseesor
audiblyhearsthepoweroftheiralphawavesandisabletomanipulatetheiralphaintensity.Thisneurofeedbackhasapplicationsinthemilitaryandaircraftcontrolaswell,asusers
canbetrainedtofocusandarealertediftheyloseconcentration.ThePonggamecanbeviewedasaneurofeedbackdevicesincetheuser'sconcentrationlevelisvisuallydepictedon
thescreenasthepositionoftheleftpaddle.Thus,theBrainComputerInterfacecomponentofthisprojecthasdiverseapplicationsthatgofarbeyondplayingasimplecomputergame
withone'sbrainwaves.

Figure:PhotographofUserInteractionwithOpenGLPonggame

P300Detector
TheP300detectioncodewasanattempttodetectwhichcolorauseristhinkingoffromadiscretesetofrandomlyflashedcolorsdisplayedonthescreen.Thesoftwareusedmachine
learningalgorithmsforsupportvectormachines(SVMs)providedbythelibSVMClibrary.Thisattemptwasnotsuccessful.Inatrainingsetof50trials,wewereunabletoobtain
classificationaccuracybeyond64%.Nonetheless,wedocumentourcodehereandprovidesomesuggestionsforimprovementsandfuturework.
TheP300codeusesafinitestatemachine(FSM)todisplaycolorsrandomlyonthescreenineitheratrainingmodeoratestingmode.Thecolorsarechosenrandomlyfromtheset
{red,green,blue,yellow,purple},andaftereachcolorinthesethasbeendisplayedexactlyonce,atrialisconsideredtohavebeencompleted.Fivetrialsareperformedduringboth
testingandtraining,andtherecordedbrainwavesareaveraged.TheideaistoattempttoclassifyonesecondsetsofbraindataaseithercontainingaP300potential(target)ornot
(nontarget)usingtheSVM.Thetargetsetcorrespondswiththecolorthattheuseristhinkingof.Whilethecolorsareflashedonthecomputerscreen,theuserisinstructedtocount
thenumberoftimesthetargetappears.

CodeStructure
Thecodecontainspreprocessordefinitions(TRAINING_DATA_FILENAMEandTESTING_DATA_FILENAME)forthedatafilenames,alongwithconfigurationvariablesforthenumberof
colorsNUM_COLORS,thenumberoftrials(NUM_TRIALS),andthebuffersize(BUFFER_SIZE).
Thecolor_choicesarraycontains(r, g, b)floatingpointtuplesforeachoftheNUM_COLORScolors.Thecolor_namesarraycontainsthehumanreadablenamesofeachcolor(fortext
display).ThetrialBufferisa3Darrayindexedbytrialnumber,colorindex,andbufferpositioncontainingtheEEGwavesrecordedforeachcolorandeachtrial.Moreover,
targetBufferandnonTargetBuffercontaintheaveragedtargetandnontargetEEGwavesrespectivelyintrainingmode(toprovideanadditionaltargetandnontargettraining
instancetotheSVM).Meanwhile,testBuffersisa2DarrayofEEGwavebuffersforeachcolorusedduringtestingmode(totesteachcolorindividuallyusingtheSVM).
Statevariablesincludecurrent_color,thecurrentcolorbeingdisplayed,andtrainingTarget,therandomlyselectedtargetcolortobe"chosen"bytheuserduringtrainingmode.
ThebufferPtrvariablecontainsanindexintothecurrenttrialBuffer.Itgetsincrementedasadditionalsamplesarereceivedfromtheserialport.Thecurrent_trialvariable
containstheindexcorrespondingtothecurrentcolortrial [0, NUM_TRIALS).Thecolor_histogramvariablecontainsanarrayofbooleanssignifyingwhethercolori hasbeen
displayedinthecurrenttrialyetornot.
Thep300init()functioninitializestheP300modulebyclearingthehistogramsandthebuffers.ItinitiallysetsthetrainingtargetandcallsaplaceholderfunctiontotraintheSVM.For
ourpurposes,weusedlibSVM'sprovidedscriptstoprocessthedata,ratherthandirectlyintegratingitwithinourcode.Thisworkedbecauseourcodemerelygeneratesdatathatcan
beusedbylibSVMofflinefortrainingandtestingthesupportvectormachine.Thedatafilesareupdatedwheneveranewtrainingortestinginstanceisprovided,andthentheycanbe
usedlaterbytheuserwithlibSVM.

Thep300AddSample()functionaddsanewsamplecollectedfromtheserialporttothecurrenttrialbuffer.Ifthebufferhasbeenfilled,itstartsanewtrial,andifthelasttrialhas
finished,thenthestateisresettotheinitialstate.Thebackgroundcolorisresettoblack,andp300_stateissettoP300_READY).
Inbothtrainingandtestingmode,thep300StartTrial()functionisusedtostartanewtrial.Iftheclearparameterisset,thenthistrialisconsideredtobethefirsttrial,andstate
variablesareresetaccordingly.Otherwise,wecheckifallcolorshavebeendisplayed.Iftheyhavebeen,thenweincrementcurrent_trialandclearthehistogram.Wethenchoose
thenextrandomcolorandsetitsvalueinthehistogramtotrue.Lastly,weupdatetheOpenGLscreenclearcolortosetthebackgroundcolortothenewrandomlychosencolor.
UserInteractionCode

Thep300UpdateAndRender()functionupdatesthescreentocontainstatustextcorrespondingtothestateoftheP300FSM(ready,training,testingmode),alongwiththetraining
target.TheSDL_ttflibraryfunctionsfromfont.cppareusedtorenderthetextontothescreenusingOpenGL.
Thep300HandleKeyDown()functionchecksforkeypressesandhandlesthemaccordingly.Ifthe[F2]keyispressed,thentheP300FSMisswitchedto"trainingmode."Ifthe[F3]
keyispressed,thentheP300FSMisswitchedto"testingmode."Notethatifatrialisalreadyrunning,thennothinghappens.
SVMTrainingCode

Thep300setTrainingTarget()functionsetsthenexttrainingtargetofthetrainingsession.Initially,werandomlychoseacolor.However,wefoundthatuserfatigueisgreaterifthe
colorchangesduringtraining,soweinsteadfixedthecolorindextocorrespondwiththeyellowcolorforalltrainingsessions.Becausetraininginstancesareonlydistinguishedbytheir
label(i.e.targetornontarget),thisdoesnothaveaneffectonthetrainingprocedure,otherthanthefactthatitiseasiertoconcentrateonasinglecolorthroughouttheentiretraining
sessionratherthanarandomlychangingcolor.
Thep300addTrainingExample()functionconstructsatargetBufferandthenonTargetBufferfromthetrialBufferbyaveragingthetargetandnontargetbuffers.Thedatais
thenscaledforimprovedSVMperformanceintherangeof[1, +1].Last,thetraininginstanceisappendedtotheTRAINING_DATA_FILENAMEfileusingtheASCIIformatspecifiedin
thelibSVMREADMEfile.
SVMTestingCode

Thep300testandReport()functionclearsthetestBuffersandstorestheaverageEEGwaveforeachcolorthroughoutalltrialsfromthetrialBufferarrayineachtestBuffer.
Then,thedataiswrittentoatestingdatafilespecifiedbyTESTING_DATA_FILENAMEusingthelibSVMdataformat.

AVRFirmware
TheAVRfirmwarewasdevelopedusingtheAtmelAVRStudio5integrateddevelopmentenvironment(IDE)softwareprogram.ThelateststablereleaseoftheWinAVRcompilerwas
used,alongwiththeATAVRISP2programmer.

InitializationCode
Thefirmware'sCsourcecodeinitializesthemicrocontrollerADCtousea1.1V referencevoltagebysettingtheREFS1bitoftheADMUXregister.Next,theADENandADIEbitsaresetin
theADCSRAregister,enablingtheADCandtheADCinterruptrespectively.Aprescalervalueof125,000isused.TheLEDportPinD.2andPinD.3aresettooutputs.Next,Timer0is
settoa1mstimebasebysettingaprescalerof64andacomparematchISRwithanOCR0Avalueof249(implying250timeticks).
ADCsleepisenabledbysettingtheSM0bitintheSMCRsleepmodecontrolregister.TheUARTistheninitializedbycallinguart_init(),sleepisenabledviasleep_enable(),and
interruptsareenabledusingsei().
Fromthere,thefirmwareentersaninfinitewhileloop.TheCPUisputtosleepviasleep_cpu(),whichautomaticallystartsanADCconversion.WhentheCPUwakesup,thecurrent
valueoftheADCissentviaUARTusingfprintf().AdelayloopwaitsfortheUARTtransmissiontofinishbydelaying1msuntiltheUDRE0bitissetintheUCSR0Aregister.

ADCInterruptHandler
TheADCinterrupthandlerADC_vectreadsa10bitADCvaluebystoringthecontentsoftheADCLregisterinsideatemporaryvariable,thenreadingADCH,andcomputing
ADCL+ADCH*256.Notethattheregisterreadsmustbeperformedinthisorder,otherwisetheADCwilllockup(seetheMega644datasheetfordetails).

Timer0CompareISRInterruptHandler
TheTimer0compareISRvectorTIMER0_COMPA_vectgeneratesatestsquarewaveonPinD.2andPinD.3.Itexecutesatarateof10Hz,togglingthevaluesofbothpinsinthe
PORTDregister.Notethatthistaskrunsonceevery10ticksbecauseofthe1mstimebase.WelaterdisabledthisfeaturebecauseitwasintroducingextraneousnoiseintheEEG
signal.

Results

Figure:MengxiangusingtheEEGDevice

SpeedofExecution
TheMATLABserialplotterwasnotfastenoughforrealtimehowever,theOpenGLplotterrunsat200400framespersecond(FPS).CodewaswrittenintheOpenGLsoftwareto
measureandoutputtheFPSratetotheterminalconsole.ThisbenchmarkincludesrunningPonganddoingarealtimeFFTontheincomingserialdataconcurrently.The
microcontrollersamplestheADCatarateof200Hz,anditsendstheserialdatatothePCoverUSB/UARTat57,600(56k)baud.

Accuracy
TheADCcodeachieves10bitaccuracy,utilizingADCsleeptopowerdowntheCPUclockinbetweensuccessiveanalogvoltagemeasurementsinordertoreduceclocknoise.
Accuracyishighlydependentonelectrodeplacementandelectricalconnectiontotheuser'shead.WediscussthesemeasurementaccuracyissuesfurtherintheInterferencesub
section.Unfortunately,wewereunabletoattainacceptableaccuracyperformingSVMclassificationofP300targetwaveforms,butonedimensionalBCIcursorcontrolinPongworks
perfectlyforbothalpharhythmmodulation(concentratingandrelaxing)andmusuppression(visualizingmotormovement).

Safety
Becausethisisadeviceattachedtotheuser'shead,safetywasourutmostpriority.Themicrocontrollerwasonlyeverpoweredbyfour1.5VAAbatteries,ratherthanthroughanAC
powersupplyconnectedtomains.Moreover,serialcommunicationtoaPCoverUSBwasisolatedfromtheUSBusingoptocouplers,whichwetestedextensivelyusingamultimeter
toensurethatbothgroundloopswereseparated.Onlylaptopsrunningoffofbatterypowersupplies(noACadaptersconnected)wereeverconnectedtothemicrocontrolleroverUSB.
Asacorollary,themicrocontrollerwasneverconnectedtoauser'sheadwhiletheprogrammercablewasconnectedtoaPC.Wepromisedthat120VACpowerwillneverbe
connectedtothisprojectdirectlyorindirectly.Asaresult,userswereneverallowedtotouchanyotherelectricaldeviceswhilewearingtheEEGhelmet.Wetooksafetyveryseriously
throughoutthedevelopmentoftheproject.

Interference

Figure:60HzNoiseCorruptinganEEGSignal
Ourdeviceisverysusceptibletointerferencefromoutsidesources.WeconstructedDIYshieldedelectrodecablesusingaluminumfoil,butwestillencounteredproblemswith60Hz
powerlinenoiseoccasionally.Moreover,whentheelectrodesdonotmakesufficientelectricalcontactwiththeuser'sscalp,galvanicvoltagesshowupthatcorruptthesignal.These
galvanicvoltagesarelessofanissuewhileplayingPongbecauseanadditional50Hzor60Hzfrequencybanddoesnotaffecttherelevantfrequencycontentofthesignal.However,
fortimedomainanalysisoftheP300ERP,thesegalvanicvoltagescanseverelydegradetheaccuracyofthisprocess.

Figure:DIYShieldedAluminumFoilElectrodeCables
Wealsonoticedthatour10HzcalibrationtestsquarewavesignalwasintroducingnoiseintotheEEGsignalevenwhenitwasnotconnectedtotheinstrumentationamplifier,sowe
disabledthisfeatureinthefirmware.Thissolutioneliminatedthenoise.

UsabilityandSpecialNeedsConsiderations
Thisprojectwillhavegreatsocietalimpactbecauseitisspecificallydesignedforuserswithspecialneeds.Usingbraincomputerinterfaces,userswithspecialneedswillbeableto
interactincomputersinwaysthatwerenotpreviouslypossiblemerelybyusingtheirbrainwaves.Inaddition,patientswithsleepapneawillbeabletocollectandanalyzetheirown
EEGdatawhileasleepwithouthavingtoparticipateinexpensiveovernightsleepstudiesathospitals.Theywillbeabletoseetheirdataratherthanbeingshieldedfromitbyamedical
doctor.Becauseourbudgetislessthan$75,theywillbeabletodothisataverylowcost.

Conclusions
ExpectationsMet
Whenwewereinitiallybrainstormingfortheproject,wewantedafullsleepapneadiagnosticmachine.ThisincludednotonlyanEEG,butalsoheartratemonitor,bloodoxgenlevel
monitor,temperature,etc.However,weimmediatelyrealizedthatdoingjusttheEEGwasaverychallengingtaskbyitselfandsodecidedtofocusononlythat.Whenweactually
createdtheproposalfortheproject,wehadmanyambitiousideasforwhattheEEGwoulddo.SomeoftheideaswerefullyimplementedsuchasreadingtheAlphaWavesfor
checkinghowrelaxedorfocusedyouare,andtheMuWaveswhichcorrespondwiththoughtsofmotion.Someoftheideasweresomewhatimplementedsuchasthereadingofthe
P300signalforidentifyingcolor.Andsomeoftheideaswerenotimplementedatallsuchasmousecursormovement,duetotimeconstraintsandtechnicallimitationssuchasonly
having10bitresolutionandonechannel,whilemostcommercialEEGshavemuchmorechannelsandbetterresolution.Ifweweretodothisprojectagain,wemightaddmore
channelsandtrytowriteabetterP300trainingandpredictionprogram.

ConformedStandards
AlthoughthereisnotanIEEEStandardforEEGoperation,therearemedicalguidelinesforspecificationsofwhattheEEGmusthave.Theguidelinescanbefoundat
ftp://ansuk.org/pub/clinical_governance/dig_eeg.pdf
ThefirstspecificationisthattheEEGmusthaveaminimumof25channels,preferably32.
Wedidnotmeetthisspecificationbecausewecouldnotaffordthatmany.
Theinputimpedancealsomustbegreaterthan10megaohms.
OurAD620inputimpedanceis10||2gigaohms,whichmeetsthespecification.
TheNoisemustbebelow2microVppfrom0.16to100Hz.
Weusedfunctiongeneratorinputandwehadnoisearound3microVolt,sowedidnotmeetthespecification.
TheCommonModeRejectionRatiomustbegreaterthan80100dB.
TheAD620CommonModeRejectionRatiohasaminimumof110dBforthegainandtherangeoffrequenciesmeasured,sothespecificationismet.
Thesamplingratemustbeaminimumof200Hz,preferably250400Hz.
Ourswas200Hz,sowedidmeetthisspecification.
Thedynamicrangemustbebetterthan2microVolts.
Wecalculatedourdynamicrangetobearound0.7microVolts,thereforemeetingspecification.
Theremustalsobelowpassfiltersof15,30,50,70,100Hz,andhighpassfiltersof0.16,0.5,1.6,5,10Hz.
Wehadonelowpassfilterwith36Hz,andahighpassfilterof0.13Hz,thereforenotenoughfilterstomeetspecification.
ANotchFilterwithattenuationratio1:20at50and60Hzisalsorequired.
WedidnothaveahardwareNotchFilteralthoughweimplementedoneinsoftwareonMATLAB.
Theelectrodeimpedancesmustbedisplayedforeachelectrodeandsavedforreviewandplayback.
Wedonotmeasureanddisplaytheelectrodeimpedancessowedidnotmeetthisspecification.
TheADCresolutionmustbeequaltoorgreaterthan12bits.
OurADConlyhas10bitaccuracy,thereforenotmeetingthisstandard.
Theremustbeenoughmemorystorage,atleast1GB.
Ourcomputershavemorethanthatsowemeetthespecification.
InputsmustbesafelyisolatedandcomplieswiththeIEC6011/EN606011TypeBFUL544Isolatedstandard.
Wedonotbelieveweconformtothesestandardshowever,theinputsareisolated.
Theremustbeacolormonitorwithatleast17"withminimumof1280x1024resolution.
Wehavea22"colormonitorwith1680x1050resolution.
Theremustbeaprinterthatprintsatpaperspeedof1,2,5,10,15,30,60mm/sec.
Wedonothavesuchaprinter,sowedonotmeetthisspecification.
Althoughwedidnotmeetthemajorityofthesespecifications,wedidnotintendthisdeviceformedicaluseaswearesimplystudentscreatingamicrocontrollerprojectinamonth.
Therefore,itisdifficultifnotimpossibleforustocreateaproductwiththesameamountofqualityrequiredformedicaluse.

IntellectualPropertyConsiderations
WereusedsomecodefromthewebtohelpuswritetheprogramsneededforanalyzingtheEEG.
Thefollowingareallpublicdomainandopensourcethereforefreetouseandmodifyasneeded.TheADCSleepcodeforthemicrocontrollercomesfromtheECE4760Lab2example
codebyProfessorLand.ThiscodewasusedtoincreasetheresolutionoftheADCto10bitsbyputtingthemicrocontrollertosleepandthusreducenoise.UARTcode,whichwas
usedinallthepreviouslabs,iswrittenbyJoergWunsch.Thiswasusedtohelpdebugthehardwarebyallowingthemicrocontrollertosendmessagestothecomputerusingtheusb.
Thecodehasabeerwarelicense,whichrequiresonlythatthelicenseheaderremainonthefilewhenused.WhenintiallydevelopingsoftwarefortheEEG,weusedMATLAB,andone
ofthefirstproblemswefacedwassignificant60Hznoise.WelookedonlineandfoundaMATLABnotchfilterprogramwhichreducedthenoise.WhenwefoundoutthatMATLABwas
runningtooslowforrealtime,weturnedtoCinstead.ForplottinginC,weusedOpenGL,whichhastheopensourcelicense.WeusedtheexamplesfromtheNeonHelium(NeHe)
OpenGLwebsiteasguidesforcreatingprogramsinOpenGL.ForwritingtextontheOpenGLwindowweusedasprintfprogramontheflipcodewebsite.
WealsousedGNUlicensedcodeinourproject.Thelicensestatesthatwecannotmodifythecode,andwemostprovidethefullsourcecodeifwedistributeourcode.Assuch,the
fullsourcecodeofourprojectcanbefoundonGitHubathttps://github.com/TheChuckster/EEG_BCI
OneoftheGNUlicensedcodeisFFTW,aFastFourierTransformalgorithmforC,whichallowedastoquicklyfigureoutthefrequenciespresentinourEEGwhileplottinginOpenGL.

AnotheristheGNUCLibrary(glibc),whichprovidesthestandardlibaryoffunctionsforusingC.TheGNUCompilerCollectionisthecompilersystemweusedforourCprograms.
SDL_TTFisanotherGNUlicensedcodewhichwasusedfordisplayingtruetypefont.
Wedidnotreverseengineeringanydesignnordidwesignanynondisclosureagreementsforanyparts.

EthicalConsiderations
Whenwestartedtheproject,weunderstoodthatwewerehookingupelectricaldevicesuptoaperson'shead.Ifwedidnotcarefullymakethedeviceandmadesomemistakes,the
devicecouldpotentiallyharmorevenkillaperson.Therefore,safetywasoneofourmainconcernswhenstartingtheproject.Ourfirstobjectivewastomakesurethatevenifwe
madesomemistake,therewasstillnowaytoharmaperson.Thiswasdonebyoptoisolatingtheelectrodecircuitfromthemicrocontroller,andalsoprovidingthemicrocontroller's
powersourcewithAAbatteriesinsteadofanoutlet.Thisway,evenifweshortedoutthecircuitsomehow,themostvoltagethepersonwillreceivewillbe6Vfrom4AAbatteries.
WhenweweremakingtheprojectandtestingoutourEEG,wegotvarioussignals.Althoughsometimeswethoughtthesignalswegotwereactualbrainwaves,wealsounderstood
thatwecouldbewrong.Bycarefulcalculationsandprocedures,wewereabletodistinguishnoisefromwhatcouldbebrainwaves.Eventhen,wewerenotsureifthesignalwegot
wasindeedbrainwaves,sowealsoaskedaresearcherattheCornellSleepLabtoverifythatindeedwewereright.

LegalConsiderations
NOTE:Thisprojectismadebystudentsandnotmedicalprofessionals,thereforeitisnotintendedformedicaluse!
WealsomadeaPONGlikegame,andtheoriginalPONGgamewasmadebyAtari.However,wearesimplyusingthegameasademoofourEEGandnotforcommercialgain,
thereforethereshouldbenolegalimplicationsforhavingPONG.

Figure:HardatworkinsidePhillipsECELab

Appendix
SleepRecording
Becausethisdevicewasoriginallyintendedtodiagnosesleeprelateddisorders,weusedtoEEGtorecordMengxiangtakinganapanddiscoveredseveralinterestingsleeprelated
waveforms.ItookseveralscreenshotsoftheOpenGLplottingapplication.Sleepspindlesandkcomplexesoccurduringtheonsetofstage2sleepandaidinmaintainingmuscle
memoryandsleeprelatedrelaxation(Seehttp://en.wikipedia.org/wiki/Sleep_spindle).WealsoobservedthetawavesduringStage1andStage2sleep
(http://en.wikipedia.org/wiki/Stage_2_sleep),andREMsleep(http://en.wikipedia.org/wiki/REM_sleep).IwokeMengxiangupduringtheobservedREMwavesontheOpenGLplot,and
heconfirmedthathewasdreaming.
Weincludedaseriesofscreenshots(thegreenbarsshowtheFourierpowerspectrum):

Figure:SleepSpindle

Figure:KComplex( 3 ofthewaytotheright)

Figure:AnotherKComplex(neartheleft,noticethestrongdeflectionsinvoltage)

Figure:ThetaWaves(Lowerfrequency)

Figure:REMsleep(Higherfrequency)

SourceCodeListings
YoumayfindacopyofoursourcecodeonGitHubathttps://github.com/TheChuckster/EEG_BCI.

AVRMicrocontrollerFirmware
lab5.c

1.#include<inttypes.h>
2.#include<avr/io.h>
3.#include<avr/interrupt.h>
4.#include<avr/sleep.h>
5.#include<stdio.h>
6.#include<stdlib.h>
7.#include<util/delay.h>
8.#include"uart.h"
9.
10.//UARTfiledescriptor
11.//putcharandgetcharareinuart.c
12.FILEuart_str=FDEV_SETUP_STREAM(uart_putchar,uart_getchar,_FDEV_SETUP_RW)
13.
14.//timeoutvaluesforeachtask
15.#definet1200
16.#definet25
17.
18.volatileunsignedchartime1=0,time2=0//timeoutcounter
19.unsignedcharled//lightstates
20.
21.volatileintAin,AinLow//rawAtoDnumber
22.
23.ISR(ADC_vect)
24.{
25.
//programONLYgetsherewhenADCdoneflagisset
26.//whenreading10bitvalues

27.//youMUSTreadthelowbytefirst
28.AinLow=(int)ADCL
29.Ain=(int)ADCH*256
30.Ain=Ain+AinLow
31.}
32.
33.//timer0compareISR
34.ISR(TIMER0_COMPA_vect)
35.{
36.
if(time1>0)time1
37.
38.

if(time1==0)

39.
40.
41.
42.

time1=t1

//toggletheLEDandthesquarewavetestoutput

43.
44.
45.
46.
47.

led^=1
PORTD=(led<<PORTD2)|(led<<PORTD3)

if(time2>0)time2

48.}
49.
50.intmain()
51.{
52.

//inittheAtoDconverter

53.

//channelzero/rightadj/extAref

54.

//!!!DONOTCONNECTArefjumper!!!!

55.

ADMUX=(1<<REFS1)//1.1Vref

56.

//enableADCandsetprescalerto1/127*16MHz=125,000

57.

//andsetintenable

58.
59.
60.

ADCSRA=(1<<ADEN)|(1<<ADIE)+7

61.

DDRD=(1<<PORTD2)|(1<<PORTD3)//PORTD.2isanouput

62.
63.

//setuptimer0for1mSectimebase

64.

TIMSK0=1<<OCIE0A//turnontimer0cmpmatchISR

65.

OCR0A=249//setthecompareregisterto250timeticks

66.

TCCR0B=3//setprescalartodivideby64

67.

TCCR0A=1<<WGM01//turnonclearonmatch

//setuptheLEDport

68.

69.SMCR=(1<<SM0)//sleepchooseADCmode
70.
71.
72.

//setuptimerforPWM

73.

//pwm_init()

74.
75.

led=0x00//inittheLEDstatus

76.
77.

time1=t1//initthetasktimer

78.
79.
80.

time2=t2

81.
82.
83.
84.
85.

uart_init()
stdout=stdin=stderr=&uart_str
fprintf(stdout,"\n\rStartingADCISRdemo...\n\r")

86.

//BEFOREthecpugoestosleep.

87.

while(!(UCSR0A&(1<<UDRE0)))

88.
89.
90.
91.
92.
93.

_delay_ms(1)

94.

while(1)

95.
96.
97.

{
sleep_cpu()

fprintf(stdout,"%d\n\r",Ain)

98.

while(!(UCSR0A&(1<<UDRE0)))

99.
100.
101.

_delay_ms(1)

/*if(time2==0)

102.

103.

time2=t2

104.

fprintf(stdout,"%d\n\r",Ain)

//inittheUARTuart_init()isinuart.c

//NeedthenexttwostatmentssothattheUSARTfinishes

sleep_enable()
sei()
//measureanddisplayloop

105.

106.

107.

if(ADCSRA&(1<<ADSC))continue//skipprocessingifnotreadytodoADCnowwedon'tneedaninterrupt

109.

AinLow=(int)ADCL

110.

Ain=(int)ADCH*256

111.

Ain=Ain+AinLow

112.

113.

//startanotherconversion

114.

ADCSRA|=(1<<ADSC)*/

108.

115.
}
116.}
117.
uart.c

1./*
2.*
3.*"THEBEERWARELICENSE"(Revision42):
4.*<[email protected]>wrotethisfile.Aslongasyouretainthisnoticeyou
5.*candowhateveryouwantwiththisstuff.Ifwemeetsomeday,andyouthink
6.*thisstuffisworthit,youcanbuymeabeerinreturn.JoergWunsch
7.*
8.*
9.*Stdiodemo,UARTimplementation
10.*
11.*$Id:uart.c,v1.12005/12/2821:38:59joerg_wunschExp$
12.*
13.*Modformega644BRLJan2009
14.*/
15.
16.
17./*CPUfrequency*/
18.#defineF_CPU16000000UL
19.
20./*UARTbaudrate*/
21.#defineUART_BAUD57600
22.
23.
24.#include<stdint.h>
25.#include<stdio.h>
26.
27.#include<avr/io.h>
28.
29.#include"uart.h"
30.
31./*
32.*InitializetheUARTto9600Bd,tx/rx,8N1.
33.*/
34.void
35.uart_init(void)
36.{
37.#ifF_CPU<2000000UL&&defined(U2X)
38.UCSR0A=_BV(U2X)/*improvebaudrateerrorbyusing2xclk*/
39.UBRR0L=(F_CPU/(8UL*UART_BAUD))1
40.#else
41.UBRR0L=(F_CPU/(16UL*UART_BAUD))1
42.#endif
43.UCSR0B=_BV(TXEN0)|_BV(RXEN0)/*tx/rxenable*/
44.}
45.
46./*
47.*SendcharactercdowntheUARTTx,waituntiltxholdingregister
48.*isempty.
49.*/
50.int
51.uart_putchar(charc,FILE*stream)
52.{
53.
54.if(c=='\a')
55.{
56.fputs("*ring*\n",stderr)
57.return0
58.}
59.
60.if(c=='\n')
61.uart_putchar('\r',stream)
62.loop_until_bit_is_set(UCSR0A,UDRE0)

63.UDR0=c
64.
65.return0
66.}
67.
68./*
69.*ReceiveacharacterfromtheUARTRx.
70.*
71.*Thisfeaturesasimplelineeditorthatallowstodeleteand
72.*reeditthecharactersentered,untileitherCRorNLisentered.
73.*Printablecharactersenteredwillbeechoedusinguart_putchar().
74.*
75.*Editingcharacters:
76.*
77.*.\b(BS)or\177(DEL)deletethepreviouscharacter
78.*.^ukillstheentireinputbuffer
79.*.^wdeletesthepreviousword
80.*.^rsendsaCR,andthenreprintsthebuffer
81.*.\twillbereplacedbyasinglespace
82.*
83.*Allothercontrolcharacterswillbeignored.
84.*
85.*TheinternallinebufferisRX_BUFSIZE(80)characterslong,which
86.*includestheterminating\n(butnoterminating\0).Ifthebuffer
87.*isfull(i.e.,atRX_BUFSIZE1charactersinordertokeepspacefor
88.*thetrailing\n),anyfurtherinputattemptswillsenda\ato
89.*uart_putchar()(BELcharacter),althoughlineeditingisstill
90.*allowed.
91.*
92.*InputerrorswhiletalkingtotheUARTwillcauseanimmediate
93.*returnof1(errorindication).Notably,thiswillbecausedbya
94.*framingerror(e.g.serialline"break"condition),byaninput
95.*overrun,andbyaparityerror(ifparitywasenabledandautomatic
96.*parityrecognitionissupportedbyhardware).
97.*
98.*Successivecallstouart_getchar()willbesatisfiedfromthe
99.*internalbufferuntilthatbufferisemptiedagain.
100.*/
101.int
102.uart_getchar(FILE*stream)
103.{
104.uint8_tc
105.char*cp,*cp2
106.staticcharb[RX_BUFSIZE]
107.staticchar*rxp
108.
109.if(rxp==0)
110.for(cp=b)
111.{
112.
loop_until_bit_is_set(UCSR0A,RXC0)
113.
if(UCSR0A&_BV(FE0))
114.

return_FDEV_EOF

115.

if(UCSR0A&_BV(DOR0))

116.

return_FDEV_ERR

117.
118.

c=UDR0
/*behavioursimilartoUnixsttyICRNL*/

119.

if(c=='\r')

120.
121.

c='\n'
if(c=='\n')

122.
123.
124.
125.
126.

{
*cp=c
uart_putchar(c,stream)
rxp=b
break

127.
128.

}
elseif(c=='\t')

129.
130.
131.

c=''

132.

c>=(uint8_t)'\xa0')

133.
134.

{
if(cp==b+RX_BUFSIZE1)

135.
136.

uart_putchar('\a',stream)
else

137.
138.
139.
140.

*cp++=c

uart_putchar(c,stream)
}

if((c>=(uint8_t)''&&c<=(uint8_t)'\x7e')||

141.

continue

142.
143.
144.

145.
146.

{
case'c'&0x1f:

147.

return1

148.
149.

case'\b':

150.

case'\x7f':

151.

if(cp>b)

152.
153.
154.
155.
156.
157.
158.

uart_putchar('\b',stream)

uart_putchar('',stream)

uart_putchar('\b',stream)

cp
}
break

159.
160.

case'r'&0x1f:

161.
162.

uart_putchar('\r',stream)
for(cp2=bcp2<cpcp2++)

163.
164.

uart_putchar(*cp2,stream)
break

165.
166.

case'u'&0x1f:

167.

while(cp>b)

168.
169.
170.
171.
172.
173.
174.

uart_putchar('\b',stream)

uart_putchar('',stream)

uart_putchar('\b',stream)

cp
}
break

175.
176.

case'w'&0x1f:

177.

while(cp>b&&cp[1]!='')

178.
179.
180.
181.
182.
183.
184.

uart_putchar('\b',stream)

uart_putchar('',stream)

uart_putchar('\b',stream)

cp
}
break

switch(c)

185.
}
186.}
187.
188.c=*rxp++
189.if(c=='\n')
190.rxp=0
191.
192.returnc
193.}
194.
uart.h

1./*
2.*
3.*"THEBEERWARELICENSE"(Revision42):
4.*<[email protected]>wrotethisfile.Aslongasyouretainthisnoticeyou
5.*candowhateveryouwantwiththisstuff.Ifwemeetsomeday,andyouthink
6.*thisstuffisworthit,youcanbuymeabeerinreturn.JoergWunsch
7.*
8.*
9.*Stdiodemo,UARTdeclarations
10.*
11.*$Id:uart.h,v1.12005/12/2821:38:59joerg_wunschExp$
12.*/
13.
14./*
15.*PerformUARTstartupinitialization.
16.*/
17.void

uart_init(void)

18.
19./*
20.*SendonecharactertotheUART.
21.*/
22.int

uart_putchar(charc,FILE*stream)

23.
24./*
25.*Sizeofinternallinebufferusedbyuart_getchar().
26.*/
27.#defineRX_BUFSIZE80
28.
29./*
30.*ReceiveonecharacterfromtheUART.Theactualreceptionis
31.*linebuffered,andonecharacterisreturnedfromthebufferat
32.*eachinvokation.
33.*/
34.int

uart_getchar(FILE*stream)

35.

OpenGLPong/P300
config.h

1.#ifndefCONFIG_H_
2.#defineCONFIG_H_
3.
4.#defineSCREEN_WIDTH1024
5.#defineSCREEN_HEIGHT768
6.#defineSCREEN_BPP16
7.
8.#defineADC_RESOLUTION1024
9.#defineFFT_SCALE_FACTOR(1024*1024*10)
10.#defineX_SIZE1024
11.
12.#defineTRUE1
13.#defineFALSE0
14.
15.#defineLOG_FILENAME"eeg_log.csv"
16.
17.#endif
18.
debug.cpp

1.#include"debug.h"
2.#include<stdlib.h>
3.#include<iostream>
4.#include<fstream>
5.#include<time.h>
6.usingnamespacestd
7.
8.ofstreamlog_file
9.
10.#defineBPERL16//byte/linefordump
11.
12.boolFileExists(std::stringfilename)
13.{
14.
15.

std::fstreamfin
fin.open(filename.c_str(),std::ios::in)

16.
17.

if(fin.is_open())

18.
19.
20.

21.
22.
23.
24.

fin.close()
returntrue

fin.close()
returnfalse

25.}
26.
27.voidOpenLog()
28.{
29.

stringfilename=""

30.
31.

inti=0

32.

booltaken=true

33.
34.

do{

35.
36.
37.

i++
filename=format("logs/log%d.txt",i)
if(FileExists(filename))

38.

39.

else

40.

41.

}while(taken==true)

taken=true
taken=false

42.
43.
44.

printf("Usinglogfile%s...\n",filename.c_str())
log_file.open(filename.c_str(),ofstream::out|ofstream::trunc)

45.}
46.
47.voidCloseLog()
48.{
49.
log_file.close()
50.
printf("Logfileclosed.\n")
51.}
52.
53.voidlog_out(constchar*fmt...)
54.{
55.
56.
57.

va_listargList
va_start(argList,fmt)
std::stringresult=vformat(fmt,argList)

58.
59.
60.

va_end(argList)

61.

longtimeval

62.
63.
64.
65.
66.
67.
68.
69.
70.

chartimebuf[52]

time(&timeval)
strftime(timebuf,32,"%I:%M:%S",localtime(&timeval))
cout<<"["<<timebuf<<"]"<<result
if(log_file.is_open())log_file<<"["<<timebuf<<"]"<<result
return

71.}
72.
73.std::stringformat(constchar*fmt...)
74.{
75.
76.
77.
78.
79.
80.

va_listargList
va_start(argList,fmt)
std::stringresult=vformat(fmt,argList)
va_end(argList)
returnresult

81.}
82.
83.std::stringvformat(constchar*fmt,va_listargPtr)
84.{
85.

constintmaxSize=1000000

86.

constintbufSize=161

87.

charstackBuffer[bufSize]

88.
89.

intattemptedSize=bufSize1

90.
91.

intnumChars=vsnprintf(stackBuffer,attemptedSize,fmt,argPtr)

92.
93.

if(numChars>=0)

94.

95.
96.

char*heapBuffer=NULL

97.
98.

while((numChars==1)&&(attemptedSize<maxSize))

returnstd::string(stackBuffer)

//Gotitonthefirsttry.

//Nowusetheheap.

99.
100.

attemptedSize*=2

101.

heapBuffer=(char*)realloc(heapBuffer,attemptedSize+1)

102.
103.
104.
105.

numChars=vsnprintf(heapBuffer,attemptedSize,fmt,argPtr)

106.
107.
108.

free(heapBuffer)

//Tryabiggersize

std::stringresult=std::string(heapBuffer)

returnresult

109.}
110.
111.voiddump(unsignedchar*data,unsignedcount)
112.{
113.

unsignedbyte1,byte2

114.
115.

while(count!=0)

116.
117.

for(byte1=0byte1<BPERLbyte1++)

118.
119.

if(count==0)

120.

break

121.
122.
123.
124.
125.
126.

printf("%02X",data[byte1])

count
}
printf("\t")
for(byte2=0byte2<byte1byte2++)

127.
128./*

if(data[byte2]<'')

129.

130.

else

131.

132.

if(data[byte2]>='!'&&data[byte2]<='}')

133.
134.

else

135.

136.

}
137.

printf("\n")
138.

data+=BPERL
139.
}
140.}
141.

printf(".")
printf("%c",data[byte2])*/
printf("%c",data[byte2])
printf(".")

debug.h

1.#ifndefDEBUG_H_
2.#defineDEBUG_H_
3.
4.#include<string>
5.#include<stdio.h>
6.#include<cstdarg>
7.#include<stdarg.h>
8.
9.boolFileExists(std::stringfilename)
10.voidOpenLog()
11.voidCloseLog()
12.
13.std::stringformat(constchar*fmt...)
14.std::stringvformat(constchar*fmt,va_listargPtr)
15.voiddump(unsignedchar*data,unsignedcount)
16.voidlog_out(constchar*fmt...)
17.
18.#endif
19.
font.cpp

1.#include<math.h>
2.#include"font.h"
3.
4.#defineFONT_SIZE16
5.SDL_Colorred={255,0,0},blue={0,0,255}
6.
7.voidglBegin2D()
8.{
9.
glViewport(0,0,SCREEN_WIDTH,SCREEN_HEIGHT)
10.
11.
glMatrixMode(GL_PROJECTION)
12.
glPushMatrix()
13.
glLoadIdentity()
14.
15.glOrtho(0.0,(GLdouble)SCREEN_WIDTH,(GLdouble)SCREEN_HEIGHT,0.0,0.0,1.0)
16.
17.
glMatrixMode(GL_MODELVIEW)
18.
glPushMatrix()
19.
glLoadIdentity()
20.
21.
glPushAttrib(GL_ENABLE_BIT)
22.
glDisable(GL_DEPTH_TEST)
23.
glDisable(GL_CULL_FACE)
24.
glEnable(GL_TEXTURE_2D)
25.
26.
glEnable(GL_BLEND)
27.
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)
28.}
29.
30.voidglEnd2D()
31.{
32.
33.
34.

glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)

35.
glPopMatrix()
36.
glPopAttrib()
37.}
38.
39.staticintpower_of_two(intinput)
40.{
41.

intvalue=1

42.
43.

while(value<input)

44.
45.
46.
47.

value<<=1
}
returnvalue

48.}
49.
50.GLuintSDL_GL_LoadTexture(SDL_Surface*surface,GLfloat*texcoord)
51.{
52.

GLuinttexture

53.

intw,h

54.
55.
56.

SDL_Surface*image
SDL_Rectarea
Uint32saved_flags

57.

Uint8saved_alpha

58.
59.
60.
61.
62.

w=power_of_two(surface>w)
h=power_of_two(surface>h)
texcoord[0]=0.0f

/*MinX*/

63.

texcoord[1]=0.0f

/*MinY*/

64.

texcoord[2]=(GLfloat)surface>w/w /*MaxX*/

65.

texcoord[3]=(GLfloat)surface>h/h /*MaxY*/

66.
67.
image=SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,32,
68.#ifSDL_BYTEORDER==SDL_LIL_ENDIAN/*OpenGLRGBAmasks*/
69.
70.
71.
72.
73.#else

0x000000FF,
0x0000FF00,
0x00FF0000,
0xFF000000

74.
75.
76.
77.
78.#endif

0xFF000000,
0x00FF0000,
0x0000FF00,
0x000000FF

79.
80.
81.

if(image==NULL)return0

82.
83.
84.
85.

saved_flags=surface>flags&(SDL_SRCALPHA|SDL_RLEACCELOK)
saved_alpha=surface>format>alpha
if((saved_flags&SDL_SRCALPHA)==SDL_SRCALPHA)SDL_SetAlpha(surface,0,0)

86.
87.

/*CopythesurfaceintotheGLtextureimage*/

88.
89.
90.
91.
92.
93.
94.

area.x=0
area.y=0
area.w=surface>w
area.h=surface>h
SDL_BlitSurface(surface,&area,image,&area)

95.

if((saved_flags&SDL_SRCALPHA)==SDL_SRCALPHA)SDL_SetAlpha(surface,saved_flags,saved_alpha)

96.
97.

/*CreateanOpenGLtexturefortheimage*/

/*Restorethealphablendingattributes*/

98.
99.
100.
101.
102.
103.

glGenTextures(1,&texture)
glBindTexture(GL_TEXTURE_2D,texture)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,image>pixels)
SDL_FreeSurface(image)/*Nolongerneeded*/

104.
105.

returntexture

106.}
107.
108.TTF_Font*font
109.
110.voidInitFont()
111.{
112.
113.

font=TTF_OpenFont("fonts/arial.ttf",FONT_SIZE)
if(font==NULL)

114.
115.
116.

printf("TTF_OpenFont:%s\n",SDL_GetError())
return

117.
}
118.}
119.
120.voidFreeFont()
121.{
122.
TTF_CloseFont(font)
123.}
124.
125.voidglPrint(std::stringmessage,intx,inty,SDL_Colorcolor)
126.{
127.

intw,h

128.

GLfloattexcoord[4]

129.

GLfloattexMinX,texMinY

130.

GLfloattexMaxX,texMaxY

131.
132.

SDL_Surface*text
GLuintfonttexture

133.
134.
text=TTF_RenderText_Blended(font,message.c_str(),color)
135.
fonttexture=SDL_GL_LoadTexture(text,texcoord)
136.
SDL_FreeSurface(text)
137.
138.
w=text>w
139.
h=text>h
140.
141.
texMinX=texcoord[0]
142.
texMinY=texcoord[1]
143.
texMaxX=texcoord[2]
144.
texMaxY=texcoord[3]
145.
146.
glBegin2D()
147.
glDisable(GL_LIGHTING)
148.
glBindTexture(GL_TEXTURE_2D,fonttexture)
149.
glColor3f(1.0,1.0,1.0)
150.
glBegin(GL_TRIANGLE_STRIP)
151.

glTexCoord2f(texMinX,texMinY)glVertex2i(x,y)
152.

glTexCoord2f(texMaxX,texMinY)glVertex2i(x+w,y)
153.

glTexCoord2f(texMinX,texMaxY)glVertex2i(x,y+h)
154.

glTexCoord2f(texMaxX,texMaxY)glVertex2i(x+w,y+h)
155.
glEnd()
156.
glEnable(GL_LIGHTING)
157.
glEnd2D()
158.
159.
glDeleteTextures(1,&fonttexture)
160.}
161.
font.h

1.#ifndefFONT_H_
2.#defineFONT_H_
3.
4.#include<string>
5.#include<GL/gl.h>
6.#include<GL/glu.h>
7.#include"SDL.h"
8.#include"SDL_ttf.h"
9.#include"config.h"
10.
11.voidglPrint(std::stringmessage,intx,inty,SDL_Colorcolor)
12.voidInitFont()
13.voidFreeFont()
14.
15.voidglBegin2D()
16.voidglEnd2D()
17.
18.externSDL_Colorred,blue
19.
20.#endif
21.
glyphs.h

1.constunsignedcharnum0[]=
2.{
3.1,1,1,1,1,1,1,1,1,1,1,1,
4.1,1,1,1,1,1,1,1,1,1,1,1,
5.1,1,1,1,1,1,1,1,1,1,1,1,
6.1,1,1,1,1,1,1,1,1,1,1,1,

7.1,1,1,1,0,0,0,0,1,1,1,1,
8.1,1,1,1,0,0,0,0,1,1,1,1,
9.1,1,1,1,0,0,0,0,1,1,1,1,
10.1,1,1,1,0,0,0,0,1,1,1,1,
11.1,1,1,1,0,0,0,0,1,1,1,1,
12.1,1,1,1,0,0,0,0,1,1,1,1,
13.1,1,1,1,0,0,0,0,1,1,1,1,
14.1,1,1,1,0,0,0,0,1,1,1,1,
15.1,1,1,1,0,0,0,0,1,1,1,1,
16.1,1,1,1,0,0,0,0,1,1,1,1,
17.1,1,1,1,0,0,0,0,1,1,1,1,
18.1,1,1,1,0,0,0,0,1,1,1,1,
19.1,1,1,1,1,1,1,1,1,1,1,1,
20.1,1,1,1,1,1,1,1,1,1,1,1,
21.1,1,1,1,1,1,1,1,1,1,1,1,
22.1,1,1,1,1,1,1,1,1,1,1,1
23.}
24.
25.constunsignedcharnum1[]=
26.{
27.0,0,0,0,0,0,0,0,1,1,1,1,
28.0,0,0,0,0,0,0,0,1,1,1,1,
29.0,0,0,0,0,0,0,0,1,1,1,1,
30.0,0,0,0,0,0,0,0,1,1,1,1,
31.0,0,0,0,0,0,0,0,1,1,1,1,
32.0,0,0,0,0,0,0,0,1,1,1,1,
33.0,0,0,0,0,0,0,0,1,1,1,1,
34.0,0,0,0,0,0,0,0,1,1,1,1,
35.0,0,0,0,0,0,0,0,1,1,1,1,
36.0,0,0,0,0,0,0,0,1,1,1,1,
37.0,0,0,0,0,0,0,0,1,1,1,1,
38.0,0,0,0,0,0,0,0,1,1,1,1,
39.0,0,0,0,0,0,0,0,1,1,1,1,
40.0,0,0,0,0,0,0,0,1,1,1,1,
41.0,0,0,0,0,0,0,0,1,1,1,1,
42.0,0,0,0,0,0,0,0,1,1,1,1,
43.0,0,0,0,0,0,0,0,1,1,1,1,
44.0,0,0,0,0,0,0,0,1,1,1,1,
45.0,0,0,0,0,0,0,0,1,1,1,1,
46.0,0,0,0,0,0,0,0,1,1,1,1
47.}
48.
49.constunsignedcharnum2[]=
50.{
51.1,1,1,1,1,1,1,1,1,1,1,1,
52.1,1,1,1,1,1,1,1,1,1,1,1,
53.1,1,1,1,1,1,1,1,1,1,1,1,
54.1,1,1,1,1,1,1,1,1,1,1,1,
55.0,0,0,0,0,0,0,0,1,1,1,1,
56.0,0,0,0,0,0,0,0,1,1,1,1,
57.0,0,0,0,0,0,0,0,1,1,1,1,
58.0,0,0,0,0,0,0,0,1,1,1,1,
59.1,1,1,1,1,1,1,1,1,1,1,1,
60.1,1,1,1,1,1,1,1,1,1,1,1,
61.1,1,1,1,1,1,1,1,1,1,1,1,
62.1,1,1,1,1,1,1,1,1,1,1,1,
63.1,1,1,1,0,0,0,0,0,0,0,0,
64.1,1,1,1,0,0,0,0,0,0,0,0,
65.1,1,1,1,0,0,0,0,0,0,0,0,
66.1,1,1,1,0,0,0,0,0,0,0,0,
67.1,1,1,1,1,1,1,1,1,1,1,1,
68.1,1,1,1,1,1,1,1,1,1,1,1,
69.1,1,1,1,1,1,1,1,1,1,1,1,
70.1,1,1,1,1,1,1,1,1,1,1,1
71.}
72.
73.constunsignedcharnum3[]=
74.{
75.1,1,1,1,1,1,1,1,1,1,1,1,
76.1,1,1,1,1,1,1,1,1,1,1,1,
77.1,1,1,1,1,1,1,1,1,1,1,1,
78.1,1,1,1,1,1,1,1,1,1,1,1,
79.0,0,0,0,0,0,0,0,1,1,1,1,
80.0,0,0,0,0,0,0,0,1,1,1,1,
81.0,0,0,0,0,0,0,0,1,1,1,1,
82.0,0,0,0,0,0,0,0,1,1,1,1,
83.1,1,1,1,1,1,1,1,1,1,1,1,
84.1,1,1,1,1,1,1,1,1,1,1,1,
85.1,1,1,1,1,1,1,1,1,1,1,1,
86.1,1,1,1,1,1,1,1,1,1,1,1,

87.0,0,0,0,0,0,0,0,1,1,1,1,
88.0,0,0,0,0,0,0,0,1,1,1,1,
89.0,0,0,0,0,0,0,0,1,1,1,1,
90.0,0,0,0,0,0,0,0,1,1,1,1,
91.1,1,1,1,1,1,1,1,1,1,1,1,
92.1,1,1,1,1,1,1,1,1,1,1,1,
93.1,1,1,1,1,1,1,1,1,1,1,1,
94.1,1,1,1,1,1,1,1,1,1,1,1
95.}
96.
97.constunsignedcharnum4[]=
98.{
99.1,1,1,1,0,0,0,0,1,1,1,1,
100.1,1,1,1,0,0,0,0,1,1,1,1,
101.1,1,1,1,0,0,0,0,1,1,1,1,
102.1,1,1,1,0,0,0,0,1,1,1,1,
103.1,1,1,1,0,0,0,0,1,1,1,1,
104.1,1,1,1,0,0,0,0,1,1,1,1,
105.1,1,1,1,0,0,0,0,1,1,1,1,
106.1,1,1,1,0,0,0,0,1,1,1,1,
107.1,1,1,1,1,1,1,1,1,1,1,1,
108.1,1,1,1,1,1,1,1,1,1,1,1,
109.1,1,1,1,1,1,1,1,1,1,1,1,
110.1,1,1,1,1,1,1,1,1,1,1,1,
111.0,0,0,0,0,0,0,0,1,1,1,1,
112.0,0,0,0,0,0,0,0,1,1,1,1,
113.0,0,0,0,0,0,0,0,1,1,1,1,
114.0,0,0,0,0,0,0,0,1,1,1,1,
115.0,0,0,0,0,0,0,0,1,1,1,1,
116.0,0,0,0,0,0,0,0,1,1,1,1,
117.0,0,0,0,0,0,0,0,1,1,1,1,
118.0,0,0,0,0,0,0,0,1,1,1,1
119.}
120.
121.constunsignedcharnum5[]=
122.{
123.1,1,1,1,1,1,1,1,1,1,1,1,
124.1,1,1,1,1,1,1,1,1,1,1,1,
125.1,1,1,1,1,1,1,1,1,1,1,1,
126.1,1,1,1,1,1,1,1,1,1,1,1,
127.1,1,1,1,0,0,0,0,0,0,0,0,
128.1,1,1,1,0,0,0,0,0,0,0,0,
129.1,1,1,1,0,0,0,0,0,0,0,0,
130.1,1,1,1,0,0,0,0,0,0,0,0,
131.1,1,1,1,1,1,1,1,1,1,1,1,
132.1,1,1,1,1,1,1,1,1,1,1,1,
133.1,1,1,1,1,1,1,1,1,1,1,1,
134.1,1,1,1,1,1,1,1,1,1,1,1,
135.0,0,0,0,0,0,0,0,1,1,1,1,
136.0,0,0,0,0,0,0,0,1,1,1,1,
137.0,0,0,0,0,0,0,0,1,1,1,1,
138.0,0,0,0,0,0,0,0,1,1,1,1,
139.1,1,1,1,1,1,1,1,1,1,1,1,
140.1,1,1,1,1,1,1,1,1,1,1,1,
141.1,1,1,1,1,1,1,1,1,1,1,1,
142.1,1,1,1,1,1,1,1,1,1,1,1
143.}
144.
145.constunsignedcharnum6[]=
146.{
147.1,1,1,1,1,1,1,1,1,1,1,1,
148.1,1,1,1,1,1,1,1,1,1,1,1,
149.1,1,1,1,1,1,1,1,1,1,1,1,
150.1,1,1,1,1,1,1,1,1,1,1,1,
151.1,1,1,1,0,0,0,0,0,0,0,0,
152.1,1,1,1,0,0,0,0,0,0,0,0,
153.1,1,1,1,0,0,0,0,0,0,0,0,
154.1,1,1,1,0,0,0,0,0,0,0,0,
155.1,1,1,1,1,1,1,1,1,1,1,1,
156.1,1,1,1,1,1,1,1,1,1,1,1,
157.1,1,1,1,1,1,1,1,1,1,1,1,
158.1,1,1,1,1,1,1,1,1,1,1,1,
159.1,1,1,1,0,0,0,0,1,1,1,1,
160.1,1,1,1,0,0,0,0,1,1,1,1,
161.1,1,1,1,0,0,0,0,1,1,1,1,
162.1,1,1,1,0,0,0,0,1,1,1,1,
163.1,1,1,1,1,1,1,1,1,1,1,1,
164.1,1,1,1,1,1,1,1,1,1,1,1,
165.1,1,1,1,1,1,1,1,1,1,1,1,
166.1,1,1,1,1,1,1,1,1,1,1,1

167.}
168.
169.constunsignedcharnum7[]=
170.{
171.1,1,1,1,1,1,1,1,1,1,1,1,
172.1,1,1,1,1,1,1,1,1,1,1,1,
173.1,1,1,1,1,1,1,1,1,1,1,1,
174.1,1,1,1,1,1,1,1,1,1,1,1,
175.0,0,0,0,0,0,0,0,1,1,1,1,
176.0,0,0,0,0,0,0,0,1,1,1,1,
177.0,0,0,0,0,0,0,0,1,1,1,1,
178.0,0,0,0,0,0,0,0,1,1,1,1,
179.0,0,0,0,0,0,0,0,1,1,1,1,
180.0,0,0,0,0,0,0,0,1,1,1,1,
181.0,0,0,0,0,0,0,0,1,1,1,1,
182.0,0,0,0,0,0,0,0,1,1,1,1,
183.0,0,0,0,0,0,0,0,1,1,1,1,
184.0,0,0,0,0,0,0,0,1,1,1,1,
185.0,0,0,0,0,0,0,0,1,1,1,1,
186.0,0,0,0,0,0,0,0,1,1,1,1,
187.0,0,0,0,0,0,0,0,1,1,1,1,
188.0,0,0,0,0,0,0,0,1,1,1,1,
189.0,0,0,0,0,0,0,0,1,1,1,1,
190.0,0,0,0,0,0,0,0,1,1,1,1
191.}
192.
193.constunsignedcharnum8[]=
194.{
195.1,1,1,1,1,1,1,1,1,1,1,1,
196.1,1,1,1,1,1,1,1,1,1,1,1,
197.1,1,1,1,1,1,1,1,1,1,1,1,
198.1,1,1,1,1,1,1,1,1,1,1,1,
199.1,1,1,1,0,0,0,0,1,1,1,1,
200.1,1,1,1,0,0,0,0,1,1,1,1,
201.1,1,1,1,0,0,0,0,1,1,1,1,
202.1,1,1,1,0,0,0,0,1,1,1,1,
203.1,1,1,1,1,1,1,1,1,1,1,1,
204.1,1,1,1,1,1,1,1,1,1,1,1,
205.1,1,1,1,1,1,1,1,1,1,1,1,
206.1,1,1,1,1,1,1,1,1,1,1,1,
207.1,1,1,1,0,0,0,0,1,1,1,1,
208.1,1,1,1,0,0,0,0,1,1,1,1,
209.1,1,1,1,0,0,0,0,1,1,1,1,
210.1,1,1,1,0,0,0,0,1,1,1,1,
211.1,1,1,1,1,1,1,1,1,1,1,1,
212.1,1,1,1,1,1,1,1,1,1,1,1,
213.1,1,1,1,1,1,1,1,1,1,1,1,
214.1,1,1,1,1,1,1,1,1,1,1,1
215.}
216.
217.constunsignedcharnum9[]=
218.{
219.1,1,1,1,1,1,1,1,1,1,1,1,
220.1,1,1,1,1,1,1,1,1,1,1,1,
221.1,1,1,1,1,1,1,1,1,1,1,1,
222.1,1,1,1,1,1,1,1,1,1,1,1,
223.1,1,1,1,0,0,0,0,1,1,1,1,
224.1,1,1,1,0,0,0,0,1,1,1,1,
225.1,1,1,1,0,0,0,0,1,1,1,1,
226.1,1,1,1,0,0,0,0,1,1,1,1,
227.1,1,1,1,1,1,1,1,1,1,1,1,
228.1,1,1,1,1,1,1,1,1,1,1,1,
229.1,1,1,1,1,1,1,1,1,1,1,1,
230.1,1,1,1,1,1,1,1,1,1,1,1,
231.0,0,0,0,0,0,0,0,1,1,1,1,
232.0,0,0,0,0,0,0,0,1,1,1,1,
233.0,0,0,0,0,0,0,0,1,1,1,1,
234.0,0,0,0,0,0,0,0,1,1,1,1,
235.0,0,0,0,0,0,0,0,1,1,1,1,
236.0,0,0,0,0,0,0,0,1,1,1,1,
237.0,0,0,0,0,0,0,0,1,1,1,1,
238.0,0,0,0,0,0,0,0,1,1,1,1
239.}
240.
main.cpp

1.#include<stdio.h>
2.#include<stdlib.h>
3.#include<GL/gl.h>
4.#include<GL/glu.h>

5.#include<time.h>
6.#include<rfftw.h>
7.#include"SDL.h"
8.#include"font.h"
9.#include"debug.h"
10.#include"serial.h"
11.#include"config.h"
12.#include"pong.h"
13.#include"p300.h"
14.
15.//drawingstuff
16.SDL_Surface*surface
17.unsignedintpoint_buffer[X_SIZE],running_buffer[X_SIZE],xline=0
18.
19.//loggingstuff
20.FILE*logFile=NULL
21.
22.//FFTstuff
23.fftw_realin[X_SIZE],out[X_SIZE],power_spectrum[X_SIZE/2+1]
24.rfftw_planp
25.
26.voidQuit(intreturnCode)
27.{
28.
SDL_ShowCursor(SDL_ENABLE)
29.
30.printf("Quiting...\n")
31.closeSerial()
32.if(logFile)fclose(logFile)
33.rfftw_destroy_plan(p)
34.
35.FreeFont()
36.SDL_Quit()
37.exit(returnCode)
38.}
39.
40.intresizeWindow(intwidth,intheight)
41.{
42.GLfloatratio
43.
44.if(height==0)height=1
45.
46.ratio=(GLfloat)width/(GLfloat)height
47.
48.glViewport(0,0,(GLsizei)width,(GLsizei)height)
49.
50.glMatrixMode(GL_PROJECTION)
51.glLoadIdentity()
52.
53.//gluPerspective(45.0f,ratio,0.1f,100.0f)
54.gluOrtho2D(0,X_SIZE,0,ADC_RESOLUTION)
55.
56.glMatrixMode(GL_MODELVIEW)
57.
58.glLoadIdentity()
59.
60.returnTRUE
61.}
62.
63.voidhandleKeyPress(SDL_keysym*keysym)
64.{
65.switch(keysym>sym)
66.
67.

{
caseSDLK_ESCAPE:

68.

Quit(0)

69.

break

70.

caseSDLK_F1:

71.
72.

SDL_WM_ToggleFullScreen(surface)
break

73.

default:

74.

break

75.
}
76.
77.return
78.}
79.
80.intinitGL(void)
81.{
82.glShadeModel(GL_SMOOTH)
83.glClearColor(0.0f,0.0f,0.0f,0.0f)

84.glClearDepth(1.0f)
85.glEnable(GL_DEPTH_TEST)
86.glDepthFunc(GL_LEQUAL)
87.glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST)
88.
89.returnTRUE
90.}
91.
92.intdrawGLScene(void)
93.{
94.staticGLintT0=0,Frames=0
95.
96.glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
97.
98.glLoadIdentity()
99.glTranslatef(0.0f,0.0f,0.0f)
100.
101.unsignedints=point_buffer[xline]=readSerialValue()
102.for(inti=1i<X_SIZEi++)
103.{
104.running_buffer[i1]=running_buffer[i]
105.}
106.running_buffer[X_SIZE1]=point_buffer[xline]
107.
108.//logtheoutputtoafile
109.fprintf(logFile,"%d,%d\n",time(NULL),point_buffer[xline])
110.
111.//sendsampletoP300buffer
112.p300AddSample(s)
113.
114.xline++
115.xline%=X_SIZE
116.
117.for(inti=0i<X_SIZEi++)
118.{
119.in[i]=running_buffer[i]ADC_RESOLUTION/2
120.}
121.
122.rfftw_one(p,in,out)
123.power_spectrum[0]=out[0]*out[0]/*DCcomponent*/
124.for(inti=1i<(X_SIZE+1)/2i++)/*(k<N/2roundedup)*/
125.{
126.power_spectrum[i]=out[i]*out[i]+out[X_SIZEi]*out[X_SIZEi]
127.//printf("i=%dpower_spectrum[i]=%f\n",i,power_spectrum[i])
128.}
129.
130.if(X_SIZE%2==0)/*Niseven*/
131.{
132.power_spectrum[X_SIZE/2]=out[X_SIZE/2]*out[X_SIZE/2]/*Nyquistfreq.*/
133.}
134.
135.//drawFFT
136.glBegin(GL_LINES)
137.for(inti=0i<=X_SIZE/2i++)
138.{
139.glColor4f(0,1,0,1)
140.glVertex2i(i*2,0)
141.glVertex2i(i*2,power_spectrum[i]/FFT_SCALE_FACTOR)
142.glVertex2i(i*2+1,0)
143.glVertex2i(i*2+1,power_spectrum[i]/FFT_SCALE_FACTOR)
144.}
145.glEnd()
146.
147.//drawpointshere
148.glBegin(GL_POINTS)
149.for(inti=0i<X_SIZEi++)
150.{
151.glColor4f(1,0,0,1)
152.glVertex2f(i,running_buffer[i]*((float)SCREEN_HEIGHT/ADC_RESOLUTION))
153.}
154.glEnd()
155.
156.glBegin(GL_LINES)
157.for(inti=1i<X_SIZEi++)
158.{
159.glColor4f(1,0,0,1)
160.glVertex2f(i,running_buffer[i1]*((float)SCREEN_HEIGHT/ADC_RESOLUTION))
161.glVertex2f(i,running_buffer[i]*((float)SCREEN_HEIGHT/ADC_RESOLUTION))
162.}

163.glEnd()
164.
165.floatdelta=0,theta=0,alpha=0,beta=0,gamma=0,mu=0,total=0
166.
167.for(inti=0i<(X_SIZE/2)i++)total+=power_spectrum[i]
168.for(inti=0i<4i++)delta+=power_spectrum[i]
169.for(inti=4i<=8i++)theta+=power_spectrum[i]
170.for(inti=8i<=13i++)alpha+=power_spectrum[i]
171.for(inti=14i<=30i++)beta+=power_spectrum[i]
172.for(inti=30i<=100i++)gamma+=power_spectrum[i]
173.for(inti=8i<=13i++)mu+=power_spectrum[i]
174.delta/=totaltheta/=totalalpha/=totalbeta/=totalgamma/=totalmu/=total
175.
176.//doBCIpaddlecontrol
177.constfloatTHETA_MIN=0.01f,THETA_MAX=0.04f
178.posy=(SCREEN_HEIGHT64)*(thetaTHETA_MIN)/(THETA_MAXTHETA_MIN)
179.//TODO:lowpassfilter
180.
181./*//0.03BELOWTHATMOVEPADDLEDOWN,OTHERWISEMOVEUP
182.constfloatMU_THRESHOLD=0.03f
183.if(mu<MU_THRESHOLD)
184.paddle1yvel=0.1f
185.else
186.paddle1yvel=0.1f*/
187.
188.//updateanddrawponggamestate
189.
pongUpdateAndRender()
190.
191.//updateanddrawP300statemachine
192.p300UpdateAndRender()
193.
194.//drawtext
195.glPrint(format("Delta=%fTheta=%fAlpha=%f",delta,theta,alpha),10,10,blue)
196.glPrint(format("Beta=%fGamma=%fMu=%f",beta,gamma,mu),10,30,blue)
197.
198.SDL_GL_SwapBuffers()
199.
200.Frames++
201.{
202.GLintt=SDL_GetTicks()
203.if(tT0>=5000)
204.{
205.GLfloatseconds=(tT0)/1000.0
206.GLfloatfps=Frames/seconds
207.printf("%dframesin%gseconds=%gFPS\n",Frames,seconds,fps)
208.T0=t
209.Frames=0
210.}
211.}
212.
213.returnTRUE
214.}
215.
216.intmain(intargc,char**argv)
217.{
218.for(inti=0i<X_SIZEi++)
219.{
220.point_buffer[i]=ADC_RESOLUTION/2//0
221.running_buffer[i]=ADC_RESOLUTION/2//0
222.}
223.
224.intvideoFlags
225.intdone=FALSE
226.SDL_Eventevent
227.constSDL_VideoInfo*videoInfo
228.
229.if(SDL_Init(SDL_INIT_VIDEO)<0)
230.
231.
232.

{
fprintf(stderr,"Videoinitializationfailed:%s\n",SDL_GetError())
Quit(1)

233.
}
234.
235.#ifdefWIN32
236.
freopen("CON","w",stdout)
237.freopen("CON","w",stderr)
238.#endif
239.
240.videoInfo=SDL_GetVideoInfo()

241.
242.if(!videoInfo)
243.
244.
245.

{
fprintf(stderr,"Videoqueryfailed:%s\n",SDL_GetError())
Quit(1)

246.
}
247.
248.videoFlags=SDL_OPENGL
249.videoFlags|=SDL_GL_DOUBLEBUFFER
250.videoFlags|=SDL_HWPALETTE
251.videoFlags|=SDL_RESIZABLE
252.
253.if(videoInfo>hw_available)
254.videoFlags|=SDL_HWSURFACE
255.else
256.videoFlags|=SDL_SWSURFACE
257.
258.if(videoInfo>blit_hw)
259.videoFlags|=SDL_HWACCEL
260.
261.SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1)
262.
263.surface=SDL_SetVideoMode(SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_BPP,videoFlags)
264.
265.if(!surface)
266.
267.
268.

{
fprintf(stderr,"Videomodesetfailed:%s\n",SDL_GetError())
Quit(1)

269.
}
270.
271.initGL()
272.
273.resizeWindow(SCREEN_WIDTH,SCREEN_HEIGHT)
274.
275.if(TTF_Init()==1)
276.{
277.printf("UnabletoinitializeSDL_ttf:%s\n",TTF_GetError())
278.Quit(1)
279.}
280.
281.//initfont
282.InitFont()
283.
284.//openserial
285.openSerial()
286.
287.//openlogfile
288.logFile=fopen(LOG_FILENAME,"w")
289.
290.if(logFile==NULL)
291.{
292.fprintf(stderr,"gl_plotmain():Can'topenlogoutputfile%s!\n",LOG_FILENAME)
293.Quit(1)
294.}
295.
296.//initFFT
297.printf("InitializingFFTW2...\n")
298.p=rfftw_create_plan(X_SIZE,FFTW_REAL_TO_COMPLEX,FFTW_ESTIMATE)
299.printf("Done!\n")
300.
301.//initpong
302.printf("Initializingpong...\n")
303.
srand((unsigned)time(0))//InitRandomNumberGenerator
304.pongInit()
305.
306.//initP300
307.p300init()
308.
309.while(!done)
310.
311.

{
while(SDL_PollEvent(&event))

312.
313.

{
switch(event.type)

314.
315.

{
caseSDL_VIDEORESIZE:

316.

surface=SDL_SetVideoMode(event.resize.w,event.resize.h,SCREEN_BPP,videoFlags)

317.

if(!surface)

318.
319.

{
fprintf(stderr,"Couldnotgetasurfaceafterresize:%s\n",SDL_GetError())

320.

321.
322.

}
resizeWindow(event.resize.w,event.resize.h)

323.

break

324.

caseSDL_KEYDOWN:

325.

handleKeyPress(&event.key.keysym)

326.

pongHandleKeyDown(&event.key.keysym)

327.

p300HandleKeyDown(&event.key.keysym)

328.

break

329.

caseSDL_KEYUP:

330.

pongHandleKeyUp(&event.key.keysym)

331.

p300HandleKeyUp(&event.key.keysym)

332.

break

333.

caseSDL_QUIT:

334.

done=TRUE

335.

break

336.

default:

337.

break

338.

339.

340.
341.

342.
}
343.
344.Quit(0)

Quit(1)

drawGLScene()

345.
346.return(0)
347.}
348.
Makefile

1.CXX=g++
2.
3.CFLAGS:=gI/usr/include/SDLO2pipeWallansi
4.LDFLAGS:=lrfftwlfftwlmlGLEWlGLlGLUlSDL_imagelSDL_ttf`sdlconfigcflagslibs`
5.
6.PROGRAM=gl_plot
7.
8.SRC=\
9.
serial.cpp\
10.
font.cpp\
11.
main.cpp\
12.
debug.cpp\
13.
pong.cpp\
14.
p300.cpp\
15.
16.OBJ=$(SRC:%.cpp=%.o)
17.
18..cpp.o:
19.
$(CXX)c$<$(CFLAGS)o$@
20.
21.all:
$(PROGRAM)
22.
23.$(PROGRAM):
$(OBJ)
24.
$(CXX)o$(PROGRAM)$(OBJ)$(LDFLAGS)
25.
26..PHONY:clean
27.clean:
28.
rmf$(OBJ)
29.
p300.cpp

1.#include<stdio.h>
2.#include<stdlib.h>
3.#include<GL/gl.h>
4.#include<GL/glu.h>
5.#include<time.h>
6.#include<string>
7.//#include<rfftw.h>
8.#include"font.h"
9.#include"debug.h"
10.#include"config.h"
11.
12.#include"p300.h"
13.
14.#defineTRAINING_DATA_FILENAME"p300_data"
15.#defineTESTING_DATA_FILENAME"p300_data.t"
16.

17.#defineP300_READY0
18.#defineP300_TRAINING1
19.#defineP300_TESTING2
20.
21.unsignedintp300_state=P300_READY
22.
23.#defineNUM_COLORS5
24.#defineNUM_TRIALS3
25.#defineBUFFER_SIZE512
26.
27.constfloatcolor_choices[NUM_COLORS][3]={{1,0,0},{0,1,0},{0,0,1},{1,1,0},{1,0,1}}
28.conststd::stringcolor_names[NUM_COLORS]={"Red","Green","Blue","Yellow","Purple"}
29.unsignedinttrialBuffer[NUM_TRIALS][NUM_COLORS][BUFFER_SIZE]
30.floattargetBuffer[BUFFER_SIZE],nonTargetBuffer[BUFFER_SIZE],testBuffers[NUM_COLORS][BUFFER_SIZE]
31.
32.intcurrent_color=1,trainingTarget=1//black
33.unsignedintbufferPtr=0
34.unsignedintcurrent_trial=0
35.boolcolor_histogram[NUM_COLORS],classified_histogram[NUM_COLORS]
36.booldisplayBuffers=false
37.
38.boolp300StartTrial(boolclear)
39.voidp300trainSVM()
40.
41.voidp300setTrainingTarget()
42.{
43.trainingTarget=3//rand()%NUM_COLORS
44.}
45.
46.voidp300init()
47.{
48.for(inti=0i<NUM_COLORSi++)
49.{
50.color_histogram[i]=classified_histogram[i]=false
51.for(intj=0j<BUFFER_SIZEj++)
52.{
53.testBuffers[i][j]=0
54.}
55.}
56.
57.for(inti=0i<BUFFER_SIZEi++)
58.{
59.targetBuffer[i]=nonTargetBuffer[i]=0
60.}
61.
62.p300setTrainingTarget()
63.p300trainSVM()
64.}
65.
66.voidp300trainSVM()
67.{
68.printf("TODO:TrainingSVM!\n")
69.
70.//loadbinarydatafile,useittotraintheSVMandgetcoefficients
71.}
72.
73.voidp300addTrainingExample()
74.{
75.printf("Addingtrainingexamplebuffer!\n")
76.
77.//soweknowtrainingTarget,soiteratethroughalltrialsandaddthebufferswheretrainingtargetisthecolortotargetBufferandaddthebuf
ferswheretrainingtargetisNOTthecolortonontargetbuffer,thendividetargetBufferbyNUM_TRIALS(sinceeachtrialhasexactlyonetarget)and
dividenonTargetBufferby(NUM_TRIALS*(NUM_COLORS1))
78.
79.//clearbuffersfirst!
80.for(inti=0i<BUFFER_SIZEi++)
81.{
82.targetBuffer[i]=nonTargetBuffer[i]=0
83.}
84.
85.for(inti=0i<NUM_TRIALSi++)
86.{
87.for(intj=0j<NUM_COLORSj++)
88.{
89.for(intk=0k<BUFFER_SIZEk++)
90.{
91.if(j==trainingTarget)
92.{
93.targetBuffer[k]+=trialBuffer[i][j][k]

94.}else{
95.nonTargetBuffer[k]+=trialBuffer[i][j][k]
96.}
97.}
98.}
99.}
100.
101.for(inti=0i<BUFFER_SIZEi++)
102.{
103.targetBuffer[i]/=NUM_TRIALS
104.nonTargetBuffer[i]/=(NUM_TRIALS*(NUM_COLORS1))
105.}
106.
107.//thenscalethedata
108.for(inti=0i<BUFFER_SIZEi++)//goesfrom0toADC_RESOLUTIONinitiallysosubtractADC_RESOLUTION/2thendividebyADC_RESOLUTION/2togeti
tin[1,+1]range
109.{
110.targetBuffer[i]=(targetBuffer[i]ADC_RESOLUTION/2)/(ADC_RESOLUTION/2)
111.nonTargetBuffer[i]=(nonTargetBuffer[i]ADC_RESOLUTION/2)/(ADC_RESOLUTION/2)
112.}
113.
114.//thenappendittoatrainingSVMtextfile
115.FILE*fp=fopen(TRAINING_DATA_FILENAME,"a")
116.if(fp==NULL)
117.{
118.printf("p300addTrainingExample():Unabletoopentrainingdatafile%s!\n",TRAINING_DATA_FILENAME)
119.return
120.}
121.
122.//firstdonontargetbuffer
123.fprintf(fp,"1")
124.for(inti=0i<BUFFER_SIZEi++)
125.{
126.fprintf(fp,"%d:%f",i+1,nonTargetBuffer[i])
127.}
128.fprintf(fp,"\n")
129.
130.//nowdotargetbuffer
131.fprintf(fp,"+1")
132.for(inti=0i<BUFFER_SIZEi++)
133.{
134.fprintf(fp,"%d:%f",i+1,targetBuffer[i])
135.}
136.fprintf(fp,"\n")
137.
138.fclose(fp)
139.
140.//averagethetrials,appendthetrialtoabinarydatafile,alsocallp300trainSVM()?
141.//TODO:updatetargetBuffer,nonTargetBuffer
142.}
143.
144.voidp300testAndReport()
145.{
146.printf("Testingandreporting!\n")
147.
148.//sowehaveabunchofcolorswithmultipletrialseach,soweneedtogothrougheachcolorandtheneachtrialandaddallthedataforeachc
olorintotheappropriatetestbufferthenscaleallofthetestbuffersbyNUM_TRIALS,thenspiteachoneintothetestdatafile(afterscalingthe
data,ofcourse)
149.
150.//clearbuffersfirst!
151.for(inti=0i<NUM_COLORSi++)
152.{
153.for(intj=0j<BUFFER_SIZEj++)
154.{
155.testBuffers[i][j]=0
156.}
157.}
158.
159.for(inti=0i<NUM_TRIALSi++)
160.{
161.for(intj=0j<NUM_COLORSj++)
162.{
163.for(intk=0k<BUFFER_SIZEk++)
164.{
165.testBuffers[j][k]+=trialBuffer[i][j][k]
166.}
167.}
168.}
169.

170.for(inti=0i<NUM_COLORSi++)
171.{
172.for(intj=0j<BUFFER_SIZEj++)
173.{
174.testBuffers[i][j]/=NUM_TRIALS
175.}
176.}
177.
178.//thenscalethedata
179.for(inti=0i<NUM_COLORSi++)
180.{
181.for(intj=0j<BUFFER_SIZEj++)
182.{
183.testBuffers[i][j]=(testBuffers[i][j]ADC_RESOLUTION/2)/(ADC_RESOLUTION/2)
184.}
185.}
186.
187.//thenwriteittoatestingSVMtextfile
188.FILE*fp=fopen(TESTING_DATA_FILENAME,"w")
189.if(fp==NULL)
190.{
191.printf("p300testAndReport():Unabletoopentestingdatafile%s!\n",TESTING_DATA_FILENAME)
192.return
193.}
194.
195.//doalltestbufferswith0forclassificationlabel
196.for(inti=0i<NUM_COLORSi++)
197.{
198.fprintf(fp,"0")
199.for(intj=0j<BUFFER_SIZEj++)
200.{
201.fprintf(fp,"%d:%f",j+1,testBuffers[i][j])
202.}
203.fprintf(fp,"\n")
204.}
205.
206.fclose(fp)
207.
208.//sotakethecurrenttrialandclassifyitusinglibSVM,thensettheclassificationhistogram
209.//TODO:updatetargetBuffer,nonTargetBuffer(???)
210.}
211.
212.voidp300AddSample(unsignedints)
213.{
214.if(p300_state!=P300_READY)
215.{
216.if(bufferPtr<BUFFER_SIZE)//stillroomleftinthebuffer
217.{
218.trialBuffer[current_trial][current_color][bufferPtr++]=s
219.}else{//bufferfilledup
220.//startanewtrialunlesswearefinishedthensetp300_statetoREADY
221.if(p300StartTrial(false))//finished
222.{
223.//doSVMstuffhere
224.if(p300_state==P300_TRAINING)
225.{
226.p300addTrainingExample()
227.p300setTrainingTarget()//donetraining,setanewtarget
228.}elseif(p300_state==P300_TESTING)
229.p300testAndReport()
230.else
231.printf("p300AddSample():Unknownstate%d\n",p300_state)
232.
233.//resetstate
234.p300_state=P300_READY
235.glClearColor(0.0f,0.0f,0.0f,0.0f)
236.
237.//mightaswellresetthestatevariablesjusttobesafe
238.current_color=1
239.bufferPtr=0
240.current_trial=0
241.}
242.}
243.}
244.}
245.
246.//returnstrueifallfinished
247.boolp300StartTrial(boolclear)
248.{

249.//resetbufferpointer
250.bufferPtr=0
251.
252.if(clear)
253.{
254.//settrialtozeroandclearhistogram
255.current_trial=0
256.for(inti=0i<NUM_COLORSi++)color_histogram[i]=false
257.}
258.
259.//checkifdonewithtrial
260.boolincrement_trial=true
261.for(inti=0i<NUM_COLORSi++)
262.{
263.if(color_histogram[i]==false)
264.{
265.increment_trial=false
266.break
267.}
268.}
269.
270.//donewithtrial
271.if(increment_trial)
272.{
273.if(current_trial<NUM_TRIALS1)//checkiffinishedalltogether
274.current_trial++
275.else
276.returntrue
277.
278.for(inti=0i<NUM_COLORSi++)color_histogram[i]=false
279.}
280.
281.//pickarandomcolor,setitinthehistograph
282.boolpicked=false
283.while(!picked)
284.{
285.unsignedintnext_color=rand()%NUM_COLORS
286.if(!color_histogram[next_color]&&next_color!=current_color)
287.{
288.current_color=next_color
289.color_histogram[current_color]=true
290.picked=true
291.}
292.}
293.
294.glClearColor(color_choices[current_color][0],color_choices[current_color][1],color_choices[current_color][2],0.0f)
295.returnfalse
296.}
297.
298.voidp300UpdateAndRender()
299.{
300.if(displayBuffers)//TODO:testandfix!!!
301.{
302.glBegin(GL_LINES)
303.for(inti=1i<BUFFER_SIZEi++)
304.{
305.glColor4f(0,1,0,1)
306.glVertex2f(i,targetBuffer[i1]/2.0f*((float)SCREEN_HEIGHT/ADC_RESOLUTION))
307.glVertex2f(i,targetBuffer[i]/2.0f*((float)SCREEN_HEIGHT/ADC_RESOLUTION))
308.}
309.glEnd()
310.
311.glBegin(GL_LINES)
312.for(inti=1i<BUFFER_SIZEi++)
313.{
314.glColor4f(0,0,1,1)
315.glVertex2f(i,nonTargetBuffer[i1]/2.0f*((float)SCREEN_HEIGHT/ADC_RESOLUTION)+SCREEN_HEIGHT/2.0f)
316.glVertex2f(i,nonTargetBuffer[i]/2.0f*((float)SCREEN_HEIGHT/ADC_RESOLUTION)+SCREEN_HEIGHT/2.0f)
317.}
318.glEnd()
319.}
320.
321.//drawstatustext:
322.std::stringclassified_str="Classified:{"
323.//Classified:{RedBlue}
324.for(inti=0i<NUM_COLORSi++)
325.{
326.if(classified_histogram[i])
327.{

328.classified_str+=color_names[i]+""
329.}
330.}
331.classified_str+="}"
332.
333.//addmodestringtostatustext
334.switch(p300_state)
335.{
336.caseP300_READY:
337.classified_str="P300Ready:"+classified_str
338.break
339.caseP300_TRAINING:
340.classified_str="P300TrainingMode:"+classified_str
341.break
342.caseP300_TESTING:
343.classified_str="P300TestingMode:"+classified_str
344.break
345.default:
346.classified_str="P300UnknownState!"+classified_str
347.}
348.
349.//displaytrainingtargettoo
350.classified_str+="TrainingTarget:"+color_names[trainingTarget]
351.
352.glPrint(classified_str.c_str(),10,50,blue)
353.}
354.
355.voidp300HandleKeyDown(SDL_keysym*keysym)
356.{
357.

switch(keysym>sym)

358.
{
359.caseSDLK_F2://switchto"trainingmode"
360.if(p300_state==P300_READY)
361.{
362.p300_state=P300_TRAINING
363.p300StartTrial(true)
364.}
365.
366.break
367.caseSDLK_F3://switchto"testingmode"
368.if(p300_state==P300_READY)
369.{
370.p300_state=P300_TESTING
371.p300StartTrial(true)
372.}
373.
374.break
375.caseSDLK_F5:
376.displayBuffers=!displayBuffers
377.break
378.default:
379.break
380.
}
381.}
382.
383.voidp300HandleKeyUp(SDL_keysym*keysym)
384.{
385./*

switch(keysym>sym)

386.

387.

caseSDLK_UP:

388.

paddle2yvel=0

389.

break

390.

caseSDLK_DOWN:

391.

paddle2yvel=0

392.

break

393.

caseSDLK_a:

394.

paddle1yvel=0

395.

break

396.

caseSDLK_z:

397.

paddle1yvel=0

398.

break

399.

}*/

400.}
401.
p300.h

1.#ifndefP300_H_
2.#defineP300_H_

3.
4.#include"SDL.h"
5.
6.//statevariables
7.externunsignedintp300_state
8.
9.//methods
10.voidp300UpdateAndRender()
11.voidp300HandleKeyUp(SDL_keysym*keysym)
12.voidp300HandleKeyDown(SDL_keysym*keysym)
13.voidp300AddSample(unsignedints)
14.voidp300init()
15.
16.#endif
17.
pong.cpp

1.#include<SDL/SDL.h>
2.#include<GL/gl.h>
3.#include<GL/glu.h>
4.
5.#include<stdio.h>
6.#include<math.h>
7.#include<stdlib.h>
8.#include<ctime>
9.#include<malloc.h>
10.#include<string>
11.#include"config.h"
12.#include"glyphs.h"
13.
14.voiddrawsprite(intx,inty,floatr,floatg,floatb)
15.voiddrawpaddlesprite(intx,inty,floatr,floatg,floatb)
16.voidupdatepaddle1()
17.voidupdatepaddle2()
18.voiddrawglyph(intnum,intx,inty,floatr,floatg,floatb)
19.voiddrawline()
20.voiddrawscore()
21.voidResetVelocity()
22.intrandomNum()
23.
24.#definePADDLE_WIDTH16
25.#definePADDLE_HEIGHT128
26.
27.unsignedintscore1=0,score2=0
28.floatposy=SCREEN_HEIGHT/2,paddle1yvel=0
29.floatpos2y=SCREEN_HEIGHT/2,paddle2yvel=0
30.
31.floatballposx=SCREEN_WIDTH/28,ballposy=SCREEN_HEIGHT/28
32.floatballvelx=0,ballvely=0
33.
34.constfloatvel_threshold=0.1f
35.
36.constunsignedcharsprite[]=
37.{
38.0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,
39.0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
40.0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
41.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
42.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
43.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
44.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
45.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
46.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
47.1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
48.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
49.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
50.0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
51.0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
52.0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,
53.0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0
54.}
55.
56.voidupdateball()
57.{
58.
59.
60.
61.

ballposx+=ballvelx
ballposy+=ballvely
if(ballposy<=0)

62.
63.
64.
65.

ballvely=ballvely

ballposy=0
}elseif(ballposy>=SCREEN_HEIGHT16){

66.
67.
68.
69.
70.

71.
72.
73.
74.

ballvelx=ballvelx

ballposx=0
}elseif(ballposx>=SCREEN_WIDTH16){

75.
76.
77.
78.
79.

80.

//frontcheckingforbothpaddles

81.

if((ballposx>=75)&&(ballposx<=75+PADDLE_WIDTH)&&(ballposy>=posy(PADDLE_HEIGHT1))&&(ballposy<=posy+PADDLE_HEIGHT1))

82.
83.
84.
85.
86.

ballposx=75+(PADDLE_WIDTH1)
ballvelx=ballvelx

if(ballvelx>1)

87.
88.

ballvelx+=vel_threshold
elseif(ballvelx<1)

89.
90.
91.

if(ballvely>1)

92.
93.

ballvely+=vel_threshold
elseif(ballvely<1)

94.
95.
96.
97.

ballvely=ballvely
ballposy=SCREEN_HEIGHT16

if(ballposx<=0)

ballvelx=ballvelx
ballposx=SCREEN_WIDTH16

//COLLISIONDETECTION

ballvelx=vel_threshold

ballvely=vel_threshold

if((ballposx<=(SCREEN_WIDTH91))&&(ballposx>=(SCREEN_WIDTH91)PADDLE_WIDTH)&&(ballposy>=pos2y(PADDLE_WIDTH1))&&(ballposy<=po

s2y+PADDLE_HEIGHT1))
98.
{
99.

ballposx=(SCREEN_WIDTH91)PADDLE_WIDTH
100.

ballvelx=ballvelx
101.
102.

if(ballvelx>1)
103.
104.

ballvelx+=vel_threshold
elseif(ballvelx<1)

105.
106.
107.

if(ballvely>1)

108.
109.

ballvely+=vel_threshold
elseif(ballvely<1)

110.
111.
112.
113.

114.

if((ballposx==75PADDLE_WIDTH)&&(ballposy>=posy(PADDLE_WIDTH1))&&(ballposy<=posy+PADDLE_HEIGHT1))

115.
116.
117.

118.
119.
120.

121.

if((ballposy==posyPADDLE_WIDTH)&&(ballposx>75(PADDLE_WIDTH1))&&(ballposx<75+(PADDLE_WIDTH1)))

122.
123.
124.

125.
126.
127.

128.

if((ballposy==pos2yPADDLE_WIDTH)&&(ballposx>(SCREEN_WIDTH91)(PADDLE_WIDTH1))&&(ballposx<(SCREEN_WIDTH91)+(PADDLE_WIDTH1)))

129.
130.
131.

132.
133.
134.

135.
136.
137.
138.

score1++

ResetVelocity()

139.

ballvelx=vel_threshold

ballvely=vel_threshold

//backcheckingforbothpaddles
ballvelx=ballvelx

if((ballposx==(SCREEN_WIDTH91)+PADDLE_WIDTH)&&(ballposy>=pos2y(PADDLE_WIDTH1))&&(ballposy<=pos2y+PADDLE_HEIGHT1))
ballvelx=ballvelx

//Topandbottomcheckingforpaddle1
ballvely=ballvely

if((ballposy==posy+PADDLE_HEIGHT)&&(ballposx>75(PADDLE_WIDTH1))&&(ballposx<75+(PADDLE_WIDTH1)))
ballvely=ballvely

//Topandbottomcheckingforpaddle2
ballvely=ballvely

if((ballposy==pos2y+PADDLE_HEIGHT)&&(ballposx>(SCREEN_WIDTH91)(PADDLE_WIDTH1))&&(ballposx<(SCREEN_WIDTH91)+(PADDLE_WIDTH1)))
ballvely=ballvely

if(ballposx==0)

140.
141.
142.
143.
144.
145.

if(ballposx==SCREEN_WIDTH16)
{

score2++

ResetVelocity()

146.
}
147.}
148.
149.voidResetVelocity()
150.{
151.

if(ballvelx>1)

152.
153.

ballvelx=1
elseif(ballvelx<1)

154.
155.
156.

157.
158.

ballvely=1
elseif(ballvely<1)

ballvelx=1

if(ballvely>1)

159.

ballvely=1
160.}
161.
162.voidpongUpdateAndRender()
163.{
164.

inttick=SDL_GetTicks()

165.
166.
updateball()
167.
updatepaddle1()
168.
updatepaddle2()
169.
170.
drawsprite(ballposx,ballposy,0,1,0)
171.
172.
drawpaddlesprite(75,posy,1,0,0)
173.
drawpaddlesprite((SCREEN_WIDTH91),pos2y,0,0,1)
174.
175.
drawscore()
176.
drawline()
177.}
178.
179.voiddrawscore()
180.{
181.
drawglyph(score1,SCREEN_WIDTH/2+10,25,1,1,0)
182.
drawglyph(score2,SCREEN_WIDTH/21210,25,1,1,0)
183.}
184.
185.voiddrawglyph(intnum,intx,inty,floatr,floatg,floatb)
186.{
187.

constunsignedchar*glyph

188.
189.

switch(num)

190.
191.

{
case0:

192.
193.

194.

case1:

195.
196.

197.

case2:

198.
199.

200.

case3:

201.
202.

203.

case4:

204.
205.

206.

case5:

207.
208.

209.

case6:

210.
211.

212.

case7:

213.
214.

215.

case8:

216.
217.

218.

case9:

glyph=num0
break
glyph=num1
break
glyph=num2
break
glyph=num3
break
glyph=num4
break
glyph=num5
break
glyph=num6
break
glyph=num7
break
glyph=num8
break

219.
220.

glyph=num9
break

221.

default:

222.
223.

224.
225.
226.
227.
228.
229.
230.
231.
232.
233.

234.
235.

for(intj=0j<12j++,c++)

236.
237.

glyph=num0
break

glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadIdentity()
gluOrtho2D(0,SCREEN_WIDTH,SCREEN_HEIGHT,0)
glColor4f(r,g,b,1.0f)
glBegin(GL_POINTS)

for(inti=0,c=0i<20i++)

if(glyph[c])

238.

{
239.

glVertex2i(x+j,y+i)
240.

}
241.

}
242.

}
243.
glPopMatrix()
244.}
245.
246.voiddrawsprite(intx,inty,floatr,floatg,floatb)
247.{
248.
249.
250.
251.

glBegin(GL_POINTS)
glColor4f(r,g,b,1.0f)
for(inti=0,c=0i<16i++)

252.
253.

for(intj=0j<16j++,c++)

254.
255.

256.

257.

258.

259.

}
260.
}
261.
glEnd()
262.}
263.
264.#defineLINE_SPACING5

if(sprite[c])
{

glVertex2i(x+j,y+i)

265.voiddrawline()
266.{
267.
268.
269.
270.

glBegin(GL_LINES)
glColor4f(1.0f,1.0f,1.0f,1.0f)//whitedividerlineinthemiddle
for(inti=0i<SCREEN_HEIGHTi+=LINE_SPACING*2)

271.
{
272.

glVertex2i(SCREEN_WIDTH/2,i)
273.

glVertex2i(SCREEN_WIDTH/2,i+LINE_SPACING)
274.
}
275.
276.
glEnd()
277.}
278.
279.voidpongInit()
280.{
281.
282.

printf("SCREEN_HEIGHT=%d\n",SCREEN_HEIGHT)
inti=randomNum()

283.
284.

if(i==0)

285.
286.

else

ballvelx=1

287.
288.
289.
290.
291.

ballvelx=1

292.
293.

else

i=randomNum()
if(i==0)
ballvely=1

294.

ballvely=1
295.}
296.
297.voiddrawpaddlesprite(intx,inty,floatr,floatg,floatb)

298.{
299.
300.
301.

glBegin(GL_POINTS)
glColor4f(r,g,b,1.0f)
for(inti=0,c=0i<PADDLE_HEIGHTi++)

302.
303.

for(intj=0j<PADDLE_WIDTHj++,c++)

304.

{
305.

glVertex2i(x+j,y+i)
306.

}
307.
}
308.
glEnd()
309.}
310.
311.voidpongHandleKeyDown(SDL_keysym*keysym)
312.{
313.

switch(keysym>sym)

314.
315.

{
caseSDLK_UP:

316.
317.

318.

caseSDLK_DOWN:

319.
320.

321.

/*caseSDLK_a:

322.

paddle1yvel=1

323.

break

324.

caseSDLK_z:

325.

paddle1yvel=1

326.

break*/

327.

default:

328.

paddle2yvel=1
break
paddle2yvel=1
break

break

329.
}
330.}
331.
332.voidpongHandleKeyUp(SDL_keysym*keysym)
333.{
334.

switch(keysym>sym)

335.
336.

{
caseSDLK_UP:

337.
338.

339.

caseSDLK_DOWN:

340.
341.

342./*

caseSDLK_a:

343.

paddle1yvel=0

344.

break

345.

caseSDLK_z:

346.

paddle1yvel=0

347.

break*/

paddle2yvel=0
break
paddle2yvel=0
break

348.default:
349.break
350.
}
351.}
352.
353.voidupdatepaddle1()
354.{
355.//printf("updating...\n")
356.
357.
358.

posy+=paddle1yvel

359.
360.

posy=0
elseif(posy>(SCREEN_HEIGHT64))

if(posy<0)

361.

posy=SCREEN_HEIGHT64
362.}
363.
364.voidupdatepaddle2()
365.{
366.
367.
368.
369.
370.

pos2y+=paddle2yvel
if(pos2y<0)

pos2y=0
elseif(pos2y>(SCREEN_HEIGHT64))

371.

pos2y=SCREEN_HEIGHT64
372.}
373.
374.intrandomNum()//Obtainarandomintegerbetweendefinedrange
375.{

376.

intrange=(10)+1//Calculaterange

377.
378.

//Userand()

379.

intretval=0+int(range*rand()/(RAND_MAX+1.0))

380.
381.

returnretval//Returnthenumber

382.}
383.
pong.h

1.#ifndefPONG_H_
2.#definePONG_H_
3.
4.voidpongInit()
5.voidpongUpdateAndRender()
6.
7.voidpongHandleKeyDown(SDL_keysym*keysym)
8.voidpongHandleKeyUp(SDL_keysym*keysym)
9.
10.externfloatposy,paddle1yvel
11.
12.#endif
13.
serial.cpp

1.#include<stdio.h>//Standardinput/outputdefinitions
2.#include<stdlib.h>
3.#include<string.h>//Stringfunctiondefinitions
4.#include<unistd.h>//UNIXstandardfunctiondefinitions
5.#include<fcntl.h>//Filecontroldefinitions
6.#include<errno.h>//Errornumberdefinitions
7.#include<termios.h>//POSIXterminalcontroldefinitions
8.
9.#include"serial.h"
10.
11.#defineBUFFER_SIZE80
12.
13.charlastError[1024],serialBuffer[BUFFER_SIZE]
14.
15.intusbdev=0
16.
17.#definePORT_NAME"/dev/ttyUSB0"
18.#defineBAUD_RATEB57600
19.
20.//#defineNULL_SERIAL
21.
22.boolopenSerial()
23.{
24.#ifndefNULL_SERIAL
25.system("sttyF/dev/ttyUSB057600cs8cstopbparityicanonmin1time1")
26.usbdev=open("/dev/ttyUSB0",O_RDWR)
27.return(usbdev!=NULL)
28.#else
29.returntrue
30.#endif
31.}
32.
33.char*readByte()
34.{
35.returnNULL
36.}
37.
38.unsignedintreadSerialValue()
39.{
40.#ifndefNULL_SERIAL
41.intptr=0
42.unsignedcharlast_read=NULL
43.while(ptr<BUFFER_SIZE1&&last_read!='\n')
44.{
45.read(usbdev,&serialBuffer[ptr],1)
46.last_read=serialBuffer[ptr]
47.ptr++
48.}
49.
50.intval=0
51.sscanf(serialBuffer,"%d\n",&val)
52.returnval
53.#else

54.return0
55.#endif
56.}
57.
58.voidcloseSerial()
59.{
60.#ifndefNULL_SERIAL
61.close(usbdev)
62.#endif
63.}
64.
serial.h

1.#ifndefSERIAL_H_INCLUDED
2.#defineSERIAL_H_INCLUDED
3.
4.boolopenSerial()
5.voidcloseSerial()
6.
7.unsignedintreadSerialValue()
8.
9.#endif//SERIAL_H_INCLUDED
10.

MATLABPlottingSourceCode
plot_samples.m

1.%settings
2.SerialPort='com3'%serialport
3.N=200
4.Fs=200
5.
6.m=zeros(1,N)
7.
8.s=serial(SerialPort)
9.set(s,'BaudRate',57600)
10.fopen(s)
11.
12.fori=1:N
13.datum=fscanf(s,'%s')
14.fprintf('%s\n',datum)
15.
16.if(length(datum)>0)
17.m(i)=str2num(datum)
18.else
19.m(i)=0
20.end
21.end
22.
23.%Cleanuptheserialport
24.fclose(s)
25.delete(s)
26.clears
27.
28.%Filtermwith60Hznotch
29.Wo=60/(Fs/2)BW=Wo/35
30.[b,a]=iirnotch(Wo,BW)
31.m=filter(b,a,m)
32.
33.%RemoveDCoffset
34.mu=mean(m)
35.m=mmu+1024/2
36.
37.figure(1)
38.hLine=plot(m)
39.ylim([01024])
40.set(hLine,'YData',m)
41.
42.figure(2)
43.L=length(m)
44.NFFT=2^nextpow2(L)%Nextpowerof2fromlengthofy
45.Y=fft(m,NFFT)/L
46.f=Fs/2*linspace(0,1,NFFT/2+1)
47.
48.%Plotsinglesidedamplitudespectrum.
49.plot(f,2*abs(Y(1:NFFT/2+1)))
50.title('SingleSidedAmplitudeSpectrumofy(t)')
51.xlabel('Frequency(Hz)')

52.ylabel('|Y(f)|')
53.
plot_samples_rt.m

1.%settings
2.SerialPort='com3'%serialport
3.N=200
4.Fs=200
5.
6.m=zeros(1,N)
7.
8.s=serial(SerialPort)
9.set(s,'BaudRate',57600)
10.fopen(s)
11.
12.fori=1:N
13.datum=fscanf(s,'%s')
14.fprintf('%s\n',datum)
15.
16.if(length(datum)>0)
17.m(i)=str2num(datum)
18.else
19.m(i)=0
20.end
21.end
22.
23.%Cleanuptheserialport
24.fclose(s)
25.delete(s)
26.clears
27.
28.%Filtermwith60Hznotch
29.Wo=60/(Fs/2)BW=Wo/35
30.[b,a]=iirnotch(Wo,BW)
31.m=filter(b,a,m)
32.
33.%RemoveDCoffset
34.mu=mean(m)
35.m=mmu+1024/2
36.
37.figure(1)
38.hLine=plot(m)
39.ylim([01024])
40.set(hLine,'YData',m)
41.
42.figure(2)
43.L=length(m)
44.NFFT=2^nextpow2(L)%Nextpowerof2fromlengthofy
45.Y=fft(m,NFFT)/L
46.f=Fs/2*linspace(0,1,NFFT/2+1)
47.
48.%Plotsinglesidedamplitudespectrum.
49.plot(f,2*abs(Y(1:NFFT/2+1)))
50.title('SingleSidedAmplitudeSpectrumofy(t)')
51.xlabel('Frequency(Hz)')
52.ylabel('|Y(f)|')
53.
serial_test.m

1.SerialPort='com8'%serialport
2.N=1000
3.
4.m=zeros(1,1000)
5.figure(1)
6.hLine=plot(m)
7.ylim([01024])
8.
9.KeepRunning=1
10.whileKeepRunning
11.s=serial(SerialPort)
12.set(s,'BaudRate',57600)
13.fopen(s)
14.fori=1:N
15.datum=fscanf(s,'%s')
16.%fprintf('%s\n',datum)
17.
18.if(length(datum)>0)
19.m(i)=str2num(datum)

20.else
21.m(i)=0
22.end
23.
24.if(ishandle(1))
25.set(hLine,'YData',m)
26.else
27.KeepRunning=0
28.break
29.end
30.drawnow
31.end
32.
33.%Cleanuptheserialport
34.fclose(s)
35.delete(s)
36.clears
37.end
38.

Schematics

Figure:ProjectSchematics

CostDetailsandParts
PowerSupplyCircuit

Quantity

PartNumber

PartName

Cost

SparkFun:COM00102

SPDTMiniPowerSwitch

$1.50

LAB

GreenLED

LAB

LAB

1MResistor

LAB

LAB

1kResistor

LAB

PreOwned

100Resistor

PreOwned(ResistorKit)

LAB

JumperWire

2*1.00=$2.00

DigiKey:BC4AAWND

BatteryHolder4AACells

$1.33

PanasonicAkalinePlus

AABattery

PreOwned

UARTOptoIsolation

Quantity

PartNumber

PartName

Cost

DigiKey:1601791ND

FairchildSemiconductor6N137OptoIsolator

$1.00

LAB

0.01FCapacitor

LAB

PreOwned

220Resistor

PreOwned(ResistorKit)

PreOwned

1.1kResistor

PreOwned(ResistorKit)

LAB

JumperWire

$1.00

LAB

DIPSocket

$0.50

AmplifierBoard

Quantity

PartNumber

PartName

Cost

DigiKey:AD620ANZND

ICAMPINSTLPLN18MA8DIPAD620InstrumentationAmplifier

$8.27

DigiKey:CA3140EZND

ICOPAMP4.5MHZBIMOS8DIP3140OperationalAmplifier

2*1.89=$3.78

DigiKey:3386F105LFND

1MTrimPotentiometer

$1.12

LAB

0.01FCapacitor

LAB

DigiKey:4905362ND

0.0068FCapacitor

$0.28

DigiKey:4452851ND

1FCapacitor

$0.38

PreOwned

.22FCapacitor

PreOwned(CapacitorKit)

PreOwned

2.2kResistor

PreOwned(ResistorKit)

LAB

1MResistor

LAB

LAB

10kResistor

LAB

PreOwned

15kResistor

PreOwned(ResistorKit)

Misc/Other

Quantity

PartNumber

PartName

Cost

LAB

Mega644

$6.00

LAB

FTDIUSBCommsBoard

$4.00

LAB

TargetBoard

$4.00

LAB

TargetBoardSocket

LAB

LAB

FemaleWireHeaders

7*0.05=$0.35

LAB

FemaleWireSockets

7*0.05=$0.35

LAB

WhiteBoard

$6.00

LAB

SolderBoard(6inch)

$2.50

ContecMedicalSystems:eBay

TenPcsSilverPlatingElectrodes

$20.00

PreOwned

BaseballCap

PreOwned

Thetotalcostoftheprojectwas$64.36whichisrathercostly(owingtothemedicalgradeelectrodesthatweused)butstillwellunderourbudget.

TasksandWorkloadDivision
Thetasksweredividedamongourselvesasfollows:
Charles Softwaredevelopment
WrotetheMATLABcode,AVRfirmware,OpenGLCcode
BCIresearch,projectidea
Mengxiang Hardwaredesign,construction,circuitdebugging
Partselectionforhardware(DigiKeyordering)
Testingtheamplifierboard
Overall,wefeltthiswasafair,equaldivisionoflabor.

References
Backgroundsites
Electroencephalography(EEG)article,Wikipedia.
http://en.wikipedia.org/wiki/Electroencephalography
LibSVMALibraryforSupportVectorMachines
http://www.csie.ntu.edu.tw/~cjlin/libsvm/
SimpleDirectMediaLayer(SDL)
http://www.libsdl.org/
FFTW

http://www.fftw.org/
MATLABDocumentation
http://www.mathworks.com/help/techdoc/
OpenEEGProject
http://openeeg.sourceforge.net/

Papers
Matsuoka,G.andSugi,T.andKawana,F.andNakamura,M.AutomaticdetectionofapneaandEEGarousalsforsleepapneasyndrome.
InICCASSICE,2009,pages46514654.
F.Lotte.AreviewofclassificationalgorithmsforEEGbasedbraincomputerinterfaces.
InJournalofNeuralEngineering,2007.
HazratiMKhandEfranianA.AnonlineEEGbasedbraincomputerinterfaceforcontrollinghandgraspusinganadaptiveprobabilisitcneuralnetwork.
InPubMed,2010.
JorgeBaztarricaOchoa.EEGSignalClassificationforBrainComputerInterfaceApplications.
Thesis,colePolytechniqueFederaledeLausanne,2002.
KouhyarTavakolian,FaratashVasefi,KavehNaziripour,andSiamakReazei.Mentaltaskclassificationforbraincomputerinterfaceapplications.
InFirstCanadianStudentConferenceonBiomedicalProgramming.
YuanqingLi,ChuanchuWang,HaihongZhang,andCuntaiGuan.AnEEGbasedBCISystemfor2DCursorControl.
AliS.AlMejrad.HumanEmotionsDetectionusingBrainWaveSignals.
InEuropeanJournalofScientificResearch,2010,pages640659.
AkinariOnishi,YuZhang,QibinZhao,AndrzejCichocki.FastandReliableP300BasedBCIwithFacialImages.
ChristophGuger,etal.RapidPrototypingofanEEGbasedBrainComputerInterface(BCI).
UniversityofTechnologyGraz,Austria.
SchoreschPresentationSlides:Neurofeedbackappliedneuroscience
KompetenzzentrumfrNeurofeedback.
HideakiTouyama.EEGBasedPersonalIdentification
ToyamaPerfecturalUniversity,Japan.
ChihWeiHsu,ChihChungChang,andChihJenLin.APracticalGuidetoSupportVectorClassification
DepartmentofComputerScience,NationalTaiwanUniversity,Taiwan.
MuhammadBilalKhalid,etal.Think.Done!ABrainComputerInterface(BCI)
Thesis,NationalUniversityofSciencesandTechnology,Rawalpindi.
KanaOmori,TomonariYamaguchi,andKatsuhiroInoue.FeatureExtractionfromEEGSignalsinP300SpellingSystem
ICROSSICEInternationalJointConferent2009,FukuokaInternationalCongressCenter,Japan.
DandanHuang,etal.DecodinghumanmotoractivityfromEEGsingletrialsforadiscretetwodimensionalcursorcontrol.
IOPPublishing,JournalofNeuralEngineering,2009.
UlrichHoffmann,etal.AnefficientP300basedbraincomputerinterfacefordisabledsubjects.
Elsevier,JournalofNeuroscienceMethods2008,pages115125.

Code/designsborrowedfromothers
CornellECE4760Lab2ADCSleepExample
http://people.ece.cornell.edu/land/courses/ece4760/labs/s2012/lab2.html
chipstein:HomebrewDIYEEG,EKG,andEMG
https://sites.google.com/site/chipstein/homepage/eegwithanarduino
JoergWunsch'sUARTAVRCCode
www.nongnu.org/avrlibc/examples/stdiodemo/uart.c
MATLABNotchFilterImplementationExample
http://www.mathworks.com/matlabcentral/newsreader/view_thread/292960
NeHeProductions:OpenGLTutorials(TemplatecodeforOpenGLfontrenderingand"OpenGLwithSDL"example)
http://nehe.gamedev.net/
flipcodeSafesprintfExample
http://www.flipcode.com/archives/Safe_sprintf.shtml

Datasheets
AnalogDevicesAD620LowCostLowPowerInstrumentationAmplifier
http://www.analog.com/static/importedfiles/data_sheets/AD620.pdf
FairchildSemiconductorSingleChannel:6N137HighSpeed10MBit/sLogicGateOptocoupler
http://www.fairchildsemi.com/ds/6N/6N137.pdf
IntersilCA3140,CA3140A:4.5MHz,BiMOSOperationalAmplifierwithMOSFETInput/BipolarOutput
http://www.intersil.com/data/fn/fn957.pdf
AtmelATmega644/V:8bitAtmelMicrocontrollerwith64KBytesInSystemProgrammableFlash
http://www.atmel.com/Images/doc2593.pdf

Vendorsites

ContecMedicalSystemsCo.,LTD
http://www.contecmed.com/main/Default.asp
AnalogDevices
http://www.analog.com/en/index.html
DigiKey
http://www.digikey.com/
SparkFunElectronics
http://www.sparkfun.com/
Atmel
http://www.atmel.com/
TexasInstruments
http://www.ti.com/
FairchildSemiconductor
http://www.fairchildsemi.com/
MaximIC
http://www.maximic.com/
Intersil
http://www.intersil.com/cda/home/

ContactUs
YoumayreachCharlesMoyesviaemailatcwm55@[email protected]

Copyright2012CharlesMoyesandMengxiangJiang

You might also like