You are on page 1of 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.*<joerg@FreeBSD.ORG>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.*<joerg@FreeBSD.ORG>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@cornell.eduandMengxiangJiangviamj294@cornell.edu

Copyright2012CharlesMoyesandMengxiangJiang

You might also like