Professional Documents
Culture Documents
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
= 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
.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
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