├── .gitignore ├── AUTHOR ├── CHANGELOG ├── LICENSE ├── README-SUBVERSION ├── README.md ├── doc ├── FeedbackBase │ ├── EventDrivenFeedback.rst │ ├── Feedback.rst │ ├── MainloopFeedback.rst │ ├── PygameFeedback.rst │ ├── VisionEggFeedback.rst │ ├── VisualP300.rst │ └── index.rst ├── Makefile ├── conf.py ├── index.rst └── lib │ ├── bcinetwork.rst │ ├── bcixml.rst │ ├── eyetracker.rst │ ├── feedbackcontroller.rst │ ├── feedbackprocesscontroller.rst │ ├── gstimbox.rst │ ├── index.rst │ ├── ipc.rst │ ├── marker.rst │ ├── plugincontroller.rst │ └── rollbackimporter.rst ├── src ├── FeedbackBase │ ├── EventDrivenFeedback.py │ ├── Feedback.py │ ├── MainloopFeedback.py │ ├── PygameFeedback.py │ ├── VisionEggFeedback.py │ ├── VisualP300.py │ ├── __init__.py │ └── test │ │ ├── __init__.py │ │ ├── test_feedback.py │ │ └── test_mainloopfeedback.py ├── FeedbackController.py ├── Feedbacks │ ├── BrainPong │ │ ├── BrainPong.py │ │ ├── __init__.py │ │ ├── ball_smaller2.bmp │ │ ├── ball_smaller3.bmp │ │ ├── ball_smaller4.bmp │ │ ├── bar.png │ │ ├── bar_metallic3.png │ │ ├── bg.png │ │ ├── bowl_metallic_blue.png │ │ ├── feedbacks.list │ │ ├── stars.jpg │ │ ├── wall_metal.png │ │ ├── wall_metal_blue.png │ │ └── wall_metal_dark.png │ ├── EyetrackerFeedback │ │ ├── EyetrackerFeedback.py │ │ ├── EyetrackerRawdata.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── FeedbackCursorArrow │ │ ├── FeedbackCursorArrow.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── GazeIndependentSpeller │ │ ├── CakeSpellerVE.py │ │ ├── CenterSpellerVE.py │ │ ├── HexoSpellerVE.py │ │ ├── VEShapes.py │ │ ├── VisualSpellerVE.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── GoalKeeper │ │ ├── GoalKeeper.py │ │ ├── __init__.py │ │ ├── ball.png │ │ ├── ball_miss.png │ │ ├── ball_missCircle2.png │ │ ├── ball_missCircle3.png │ │ ├── bowl copy.bmp │ │ ├── bowl.psd │ │ ├── bowls_small.bmp │ │ ├── bowls_smaller.bmp │ │ ├── classifierbar.png │ │ ├── feedbacks.list │ │ ├── frame.bmp │ │ ├── frame_blue.bmp │ │ ├── frame_blue_grad.bmp │ │ ├── halfball_left.png │ │ ├── halfball_left_green.png │ │ ├── halfball_left_red.png │ │ ├── halfball_right.png │ │ ├── halfball_right_green.png │ │ ├── halfball_right_red.png │ │ └── keeper.png │ ├── HexoSpeller │ │ ├── GraphicComponents │ │ │ ├── Arrow.py │ │ │ ├── ColorSchemes.py │ │ │ ├── ControlSignalBar.py │ │ │ ├── GraphicComponentUtils.py │ │ │ ├── Hexagon.py │ │ │ ├── TestScript.py │ │ │ ├── TextBoard.py │ │ │ └── __init__.py │ │ ├── HexoModel.py │ │ ├── HexoSpeller.py │ │ ├── HexoViz.py │ │ ├── LanguageModel.py │ │ ├── LanguageModels │ │ │ ├── german.mat │ │ │ ├── german.pckl │ │ │ ├── lm1to8.mat │ │ │ └── lm1to8.pckl │ │ ├── Utils.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── LibetClock │ │ ├── LibetClock.py │ │ ├── __init__.py │ │ ├── background.jpg │ │ ├── clockhand_straight.bmp │ │ └── feedbacks.list │ ├── MovingRhomb │ │ ├── MovingRhomb.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── MovingRhombGL │ │ ├── MovingRhombGL.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── Oddball │ │ ├── Auditory │ │ │ ├── AuditoryOddball.py │ │ │ └── __init__.py │ │ ├── CheckerboardVEP.py │ │ ├── MultiVisualOddball.py │ │ ├── Oddball.py │ │ ├── P300_Rectangle.py │ │ ├── P300_Rectangle2.py │ │ ├── Tactile │ │ │ ├── TactileOddball.py │ │ │ └── __init__.py │ │ ├── Visual │ │ │ ├── VisualOddball.py │ │ │ ├── VisualOddballVE.py │ │ │ ├── VisualOddballVE_CNV.py │ │ │ └── __init__.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── RSVPSpeller │ │ ├── __init__.py │ │ ├── bin │ │ │ ├── gui.py │ │ │ └── test.py │ │ ├── burst.py │ │ ├── config.py │ │ ├── control.py │ │ ├── data │ │ │ └── sound.ogg │ │ ├── experiment.py │ │ ├── feedbacks.list │ │ ├── input.py │ │ ├── model │ │ │ ├── __init__.py │ │ │ ├── character_sequence.py │ │ │ ├── palette.py │ │ │ └── target_word.py │ │ ├── sequence_algorithm.py │ │ ├── test │ │ │ ├── __init__.py │ │ │ ├── character_sequence.py │ │ │ └── sequence_algorithm.py │ │ ├── trial.py │ │ ├── util │ │ │ ├── __init__.py │ │ │ ├── error.py │ │ │ ├── list.py │ │ │ ├── metadata.py │ │ │ └── trigger.py │ │ └── view.py │ ├── Stroop │ │ ├── StroopFeedback.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── TestD2 │ │ ├── TestD2.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── TobiQLAdapter │ │ ├── TobiQLAdapter.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── TrivialPong │ │ ├── TrivialPong.py │ │ ├── __init__.py │ │ ├── ball.png │ │ ├── bar.png │ │ └── feedbacks.list │ ├── Tutorial │ │ ├── Lesson01.py │ │ ├── Lesson01b.py │ │ ├── Lesson02.py │ │ ├── Lesson03.py │ │ ├── Lesson04.py │ │ ├── Lesson05.py │ │ ├── Lesson06.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── Vigilance │ │ ├── BoringClock.py │ │ ├── __init__.py │ │ ├── feedbacks.list │ │ └── sound18.wav │ ├── VisualSpeller │ │ ├── ERPHex.py │ │ ├── ERPMatrix.py │ │ ├── __init__.py │ │ └── feedbacks.list │ ├── __init__.py │ └── nback │ │ ├── __init__.py │ │ ├── feedbacks.list │ │ ├── nback_verbal.py │ │ └── sound18.wav ├── GUI.py ├── Makefile ├── __init__.py ├── bci2000pyff.py ├── emulator.py ├── external │ ├── RecorderRemoteControl │ │ ├── DCOM-recorder-config.txt │ │ ├── RecorderRemoteControl.py │ │ ├── __init__.py │ │ ├── bvr_matlab_python.py │ │ └── bvr_sendcommand_python.m │ └── __init__.py ├── gui │ ├── Makefile │ ├── __init__.py │ ├── gui.py │ ├── gui.ui │ ├── icons.qrc │ ├── icons.rcc │ ├── icons │ │ ├── PyffLogoNeu.png │ │ ├── clear.png │ │ ├── connect.png │ │ ├── exit.png │ │ ├── get.png │ │ ├── open.png │ │ ├── pause.png │ │ ├── play.png │ │ ├── quit.png │ │ ├── save.png │ │ ├── saveas.png │ │ ├── sendall.png │ │ ├── sendinit.png │ │ ├── sendmodified.png │ │ └── stop.png │ └── icons_rc.py ├── lib │ ├── CEtAPI.dll │ ├── CEtAPI.h │ ├── ExperimentalDesign │ │ ├── OrthogonalDesign.py │ │ └── __init__.py │ ├── P300Aux │ │ ├── P300Functions.py │ │ └── __init__.py │ ├── P300Layout │ │ ├── CircularLayout.py │ │ ├── MatrixLayout.py │ │ ├── README.TXT │ │ └── __init__.py │ ├── P300VisualElement │ │ ├── Circle.py │ │ ├── Hexagon.py │ │ ├── Image.py │ │ ├── README.TXT │ │ ├── Rectangle.py │ │ ├── TestIt.py │ │ ├── Text.py │ │ ├── Textbox.py │ │ ├── Textrow.py │ │ ├── VisualElement.py │ │ └── __init__.py │ ├── PluginController.py │ ├── RollbackImporter.py │ ├── __init__.py │ ├── bcinetwork.py │ ├── bcixml.py │ ├── eyetracker.py │ ├── feedbackcontroller.py │ ├── feedbackprocesscontroller.py │ ├── gstimbox.py │ ├── ipc.py │ ├── marker.py │ ├── pylibtobiic.py │ ├── serialport.py │ ├── speller │ │ ├── __init__.py │ │ ├── experiment.py │ │ ├── input.py │ │ └── trial.py │ ├── test │ │ ├── __init__.py │ │ ├── mod_w_imports.py │ │ ├── mod_wo_imports.py │ │ ├── test_bcixml.py │ │ ├── test_eyetracker.py │ │ └── test_rollbackimporter.py │ └── vision_egg │ │ ├── __init__.py │ │ ├── model │ │ ├── __init__.py │ │ ├── color_word.py │ │ ├── stimulus.py │ │ ├── target_word.py │ │ └── text_list.py │ │ ├── util │ │ ├── __init__.py │ │ ├── frame_counter.py │ │ ├── stimulus.py │ │ └── switcherator.py │ │ └── view.py ├── stresstest.py └── test_all.py └── tools ├── README ├── inpout32.dll └── x64 ├── DLPortIO.txt ├── ReadMe.txt ├── Win32 ├── InstallDriver.exe ├── inpout32.dll ├── inpout32.h ├── inpout32.lib └── vssver2.scc └── x64 ├── inpout32.h ├── inpoutx64.dll ├── inpoutx64.lib └── vssver2.scc /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | ThumbsDB 4 | \#*# 5 | .#* 6 | *pyc 7 | doc/_build 8 | -------------------------------------------------------------------------------- /AUTHOR: -------------------------------------------------------------------------------- 1 | Bastian Venthur 2 | 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Changes in 2012.6 2 | ================= 3 | 4 | * Replaced prints in Feedback base class with logger messages 5 | * -a/--addition-feedback-path can be used several times to add more than one 6 | extra path 7 | * Added CMD_QUIT_FEEDBACK_CONTROLLER to bcixml to allow remote killing of the 8 | Feedback Controller 9 | * Closing the GUI also closes the Feedback Controller now 10 | * Automatic Subclass finding finally removed 11 | * Replaced all print-stacktraces with logging.exception 12 | * Shutdown logger properly to flush all buffers before exiting 13 | * Several Feedbacks migrated to PygameFeedback base class 14 | * Several Fixes on PygameFeedback base class 15 | * Preliminary BCI2000 support 16 | * Major documentation overhaul 17 | 18 | Changes in 2011.5 19 | ================= 20 | 21 | * Support for VisionEgg feedbacks 22 | * Added TOBI interface 23 | * Added support for commands in the XML protocol 24 | * Added support for Unicode in the XML protocol 25 | * Added support for load/save of feedback variables 26 | * Major GUI overhaul 27 | * Added Pyff logo 28 | * Added support for logging server 29 | * Added parallel port driver for Windows 64 Bit 30 | 31 | 32 | Changes in 2010.7 33 | ================= 34 | 35 | Features: 36 | 37 | * FeedbackController shows a stack trace instead of an error when import or 38 | load of a Feedback fails 39 | * Added new Hex-o-Speller Feedback 40 | * Added module for the g-STIMbox by g.tec 41 | * New EventDrivenFeedback Baseclass 42 | 43 | Bugfixes: 44 | 45 | * Fixed automatic sorting of the table in the GUI 46 | 47 | Other: 48 | 49 | * Updated to Python 2.6 which is now the required Python version 50 | * Pyprocessing is no longer required 51 | 52 | 53 | Changes in 2009.12 54 | ================== 55 | 56 | Features: 57 | 58 | * Added PygameFeedback base class 59 | * Added fast pluginfinder XXX 60 | * Added various feedbacks.list 61 | * Feedbacks are now re-imported in the new process 62 | * Added D2 Test Feedback 63 | * Added Stroop Feedback 64 | * Parallel Port reset time is now a configurable variable 65 | * Feedback can now send triggers via UDP (thanks Marton) 66 | 67 | Bugfixes: 68 | 69 | * Fixed Logging support under Windows 70 | * Improved cleanup when Feedback Controller quits 71 | * Second trigger is not blanked anymore when send shortly after the first one 72 | 73 | Other: 74 | 75 | * Merged all FCPlugins into brainvisionrecorderplugin 76 | * Removed FC-Signal support 77 | * Converted not-implemented-warnings into debugs in Feedback base class 78 | * Reorganized Feedbacks a bit 79 | * Emulater produces calmer signals now 80 | * Clarified inpout32.dll installation and usage 81 | * Removed benchmark files 82 | * Updated README 83 | 84 | -------------------------------------------------------------------------------- /README-SUBVERSION: -------------------------------------------------------------------------------- 1 | Pyff has moved from SVN to GIT! 2 | =============================== 3 | 4 | The old googlecode repository is no longer in use. The new repository is 5 | 6 | http://github.com/bbci/pyff 7 | 8 | You can still use SVN in read-only mode with the git repository if you must, 9 | but I encourage you to use git instead! 10 | 11 | http://bbci.de/pyff 12 | 13 | 14 | How to use SVN with the git repository: 15 | ======================================= 16 | 17 | Github also support limited support for accessing the GIT repository with SVN: 18 | 19 | https://github.com/blog/966-improved-subversion-client-support 20 | 21 | Do the initial checkout: 22 | 23 | svn checkout https://github.com/bbci/pyff/trunk pyff 24 | 25 | Basic work cycle: 26 | 27 | # update your repository 28 | svn update 29 | 30 | # your actual work.. 31 | 32 | svn add 33 | svn delete 34 | svn copy 35 | svn move 36 | 37 | # examine your changes 38 | svn status 39 | svn diff 40 | svn revert 41 | 42 | # create a patch and send it to me 43 | svn diff > my-changes.patch 44 | 45 | The last step is necessary since you cannot commit changes into the git 46 | repository. So you collect all the changes in one file and send it to me. 47 | 48 | 49 | -- 2011-11-17 Bastian Venthur 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | How to use the FeedbackController 2 | ================================= 3 | 4 | Enter the src directory and start FeedbackController: 5 | 6 | cd src 7 | python FeedbackController.py 8 | 9 | 10 | If you made changes to the gui.ui or just checked out the tree from SVN, you 11 | may have to rebuild the gui.py: 12 | 13 | cd src 14 | make 15 | 16 | How to use the parallel port under linux 17 | ======================================== 18 | 19 | ```bash 20 | $ sudo modprobe -r lp 21 | $ sudo chmod 666 /dev/parport0 22 | ``` 23 | 24 | Citing Us 25 | ========= 26 | 27 | If you use Pyff for anything that results in a publication, We humbly ask you to 28 | cite us: 29 | 30 | ```bibtex 31 | @ARTICLE{venthur2010, 32 | author={Venthur, Bastian and Scholler, Simon and Williamson, John and Dähne, Sven and Treder, Matthias S and Kramarek, Maria T and Müller, Klaus-Robert and Blankertz, Benjamin}, 33 | title={Pyff---A Pythonic Framework for Feedback Applications and Stimulus Presentation in Neuroscience}, 34 | journal={Frontiers in Neuroinformatics}, 35 | volume={4}, 36 | year={2010}, 37 | number={100}, 38 | url={http://www.frontiersin.org/neuroinformatics/10.3389/fninf.2010.00100/abstract}, 39 | doi={10.3389/fninf.2010.00100}, 40 | issn={1662-5196}, 41 | } 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /doc/FeedbackBase/EventDrivenFeedback.rst: -------------------------------------------------------------------------------- 1 | :mod:`EventDrivenFeedback` --- Event Driven Feedback Base Class 2 | =============================================================== 3 | 4 | .. automodule:: FeedbackBase.EventDrivenFeedback 5 | :synopsis: Baseclass for Event Driven Feedbacks 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Bastian Venthur 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/Feedback.rst: -------------------------------------------------------------------------------- 1 | :mod:`Feedback` --- Feedback Base Class 2 | ======================================= 3 | 4 | .. automodule:: FeedbackBase.Feedback 5 | :synopsis: Baseclass of all Feedbacks 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Bastian Venthur 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/MainloopFeedback.rst: -------------------------------------------------------------------------------- 1 | :mod:`MainloopFeedback` --- Base Class for Feedbacks with a Mainloop 2 | ==================================================================== 3 | 4 | .. automodule:: FeedbackBase.MainloopFeedback 5 | :synopsis: Provides mainloop functionality for Feedbacks. 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Bastian Venthur 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/PygameFeedback.rst: -------------------------------------------------------------------------------- 1 | :mod:`PygameFeedback` --- Pygame Feedback Base Class 2 | ==================================================== 3 | 4 | .. automodule:: FeedbackBase.PygameFeedback 5 | :synopsis: Baseclass for Feedbacks using Pygame 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Bastian Venthur 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/VisionEggFeedback.rst: -------------------------------------------------------------------------------- 1 | :mod:`VisionEggFeedback` --- Base Class for Feedbacks using VisionEgg 2 | ===================================================================== 3 | 4 | .. automodule:: FeedbackBase.VisionEggFeedback 5 | :synopsis: Provides VisionEgg functionality for Feedbacks. 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Torsten Schmits 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/VisualP300.rst: -------------------------------------------------------------------------------- 1 | :mod:`VisualP300` --- VisualP300 Base Class 2 | ============================================ 3 | 4 | .. automodule:: FeedbackBase.VisualP300 5 | :synopsis: Baseclass for Feedbacks using the Visual P300 paradigm 6 | :members: 7 | :show-inheritance: 8 | 9 | .. moduleauthor:: Bastian Venthur 10 | 11 | -------------------------------------------------------------------------------- /doc/FeedbackBase/index.rst: -------------------------------------------------------------------------------- 1 | .. _FeedbackBase: 2 | 3 | Feedback Base Classes 4 | ===================== 5 | 6 | This section provides documentation for the Feedback base classes available in 7 | Pyff. :mod:`Feedback` is the Base Class for all Feedbacks and provides 8 | means for communication with the underlying Pyff infrastructure. The other 9 | Base Classes are Sub Classes of :mod:`Feedback`. 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :glob: 16 | 17 | * 18 | 19 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Pyff.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Pyff.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. Pyff documentation master file, created by 2 | sphinx-quickstart on Thu Nov 12 15:08:03 2009. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Pyff's documentation! 7 | ================================ 8 | 9 | This is Pyff's documentation. Feedback developers are probably interested in 10 | the documentation of :ref:`Feedback base classes ` 11 | 12 | Contents: 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | FeedbackBase/index 18 | lib/index 19 | 20 | Indices and tables 21 | ================== 22 | 23 | * :ref:`genindex` 24 | * :ref:`modindex` 25 | * :ref:`search` 26 | 27 | -------------------------------------------------------------------------------- /doc/lib/bcinetwork.rst: -------------------------------------------------------------------------------- 1 | :mod:`bcinetwork` --- Networking between the different Pyff components. 2 | ======================================================================= 3 | 4 | .. automodule:: lib.bcinetwork 5 | :synopsis: Networking between the different Pyff components. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/bcixml.rst: -------------------------------------------------------------------------------- 1 | :mod:`bcixml` --- Encoding and Decoding of BCI XML messages. 2 | ============================================================ 3 | 4 | .. automodule:: lib.bcixml 5 | :synopsis: De- and encoding of BCI XML. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/eyetracker.rst: -------------------------------------------------------------------------------- 1 | :mod:`eyetracker` --- Alea Eye Tracker Support 2 | ============================================== 3 | 4 | .. automodule:: lib.eyetracker 5 | :synopsis: Alea Eye Tracker Support 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | -------------------------------------------------------------------------------- /doc/lib/feedbackcontroller.rst: -------------------------------------------------------------------------------- 1 | :mod:`feedbackcontroller` --- Feedback Controller. 2 | ================================================== 3 | 4 | .. automodule:: lib.feedbackcontroller 5 | :synopsis: Feedback Controller. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/feedbackprocesscontroller.rst: -------------------------------------------------------------------------------- 1 | :mod:`feedbackprocesscontroller` --- Controls the Feedback Processes. 2 | ===================================================================== 3 | 4 | .. automodule:: lib.feedbackprocesscontroller 5 | :synopsis: Start and stop Feedback processes. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/gstimbox.rst: -------------------------------------------------------------------------------- 1 | :mod:`gstimbox` --- Driver for g-STIMbox 2 | ======================================== 3 | 4 | .. automodule:: lib.gstimbox 5 | :synopsis: Hardware driver for g-STIMbox 6 | :members: 7 | 8 | moduleauthor:: Mirko Dietrich 9 | -------------------------------------------------------------------------------- /doc/lib/index.rst: -------------------------------------------------------------------------------- 1 | .. _lib: 2 | 3 | Library Documentation 4 | ===================== 5 | 6 | This section contains the documentation for the various Python modules 7 | provided by Pyff. Developers of Feedbacks are probably interested only in the 8 | documentation of the Feedback base classes. 9 | 10 | Contents: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | :glob: 15 | 16 | * 17 | 18 | -------------------------------------------------------------------------------- /doc/lib/ipc.rst: -------------------------------------------------------------------------------- 1 | :mod:`ipc` --- Inter Process Communication. 2 | =========================================== 3 | 4 | .. automodule:: lib.ipc 5 | :synopsis: Inter Process Communication between Feedback and Feedback Controller. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/marker.rst: -------------------------------------------------------------------------------- 1 | :mod:`marker` --- Common Marker Definitions 2 | =========================================== 3 | 4 | .. automodule:: lib.marker 5 | :synopsis: Common marker definitions 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | -------------------------------------------------------------------------------- /doc/lib/plugincontroller.rst: -------------------------------------------------------------------------------- 1 | :mod:`PluginController` --- Finding and Loading Feedbacks. 2 | ========================================================== 3 | 4 | .. automodule:: lib.PluginController 5 | :synopsis: Finding and loading Feedbacks. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /doc/lib/rollbackimporter.rst: -------------------------------------------------------------------------------- 1 | :mod:`RollbackImporter` --- Importing and Unloading of Modules. 2 | =============================================================== 3 | 4 | .. automodule:: lib.RollbackImporter 5 | :synopsis: Importing and Unloading of Modules. 6 | :members: 7 | 8 | .. moduleauthor:: Bastian Venthur 9 | 10 | -------------------------------------------------------------------------------- /src/FeedbackBase/MainloopFeedback.py: -------------------------------------------------------------------------------- 1 | # MainloopFeedback.py - 2 | # Copyright (C) 2008-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from Feedback import Feedback 20 | 21 | 22 | class MainloopFeedback(Feedback): 23 | """Mainloop Feedback Base Class. 24 | 25 | This feedback derives from the Feedback Base Class and implements a main 26 | loop. More specifically it implements the following methods from it's base: 27 | 28 | * :func:`on_init` 29 | * :func:`on_play` 30 | * :func:`on_pause` 31 | * :func:`on_stop` 32 | * :func:`on_quit` 33 | 34 | which means that you should not need to re-implement those methods. If you 35 | choose to do so anyways, make sure to call MainloopFeedback's version 36 | first:: 37 | 38 | def on_play(): 39 | MainloopFeedback.on_play(self) 40 | # your code goes here 41 | 42 | MainloopFeedback provides the following new methods: 43 | 44 | * :func:`init` 45 | * :func:`pre_mainloop` 46 | * :func:`post_mainloop` 47 | * :func:`tick` 48 | * :func:`pause_tick` 49 | * :func:`play_tick` 50 | 51 | the class takes care of the typical steps needed to run a feedback with a 52 | mainloop, starting, pausing, stopping, quiting, etc. 53 | 54 | While running it's internal mainloop it calls :func:`tick` repeatedly. 55 | Additionally it calls either :func:`play_tick` or :func:`pause_tick` 56 | repeatedly afterwards, depending if the Feedback is paused or not. 57 | 58 | """ 59 | 60 | def on_init(self): 61 | self._running = False 62 | self._paused = False 63 | self._inMainloop = False 64 | self.init() 65 | 66 | def on_play(self): 67 | self.pre_mainloop() 68 | self._mainloop() 69 | self.post_mainloop() 70 | 71 | def on_pause(self): 72 | self._paused = not self._paused 73 | 74 | def on_stop(self): 75 | self._running = False 76 | 77 | def on_quit(self): 78 | self._running = False 79 | while self._inMainloop: 80 | pass 81 | 82 | def _mainloop(self): 83 | """ 84 | Calls tick repeatedly. 85 | 86 | Additionally it calls either :func:`pause_tick` or :func:`play_tick`, 87 | depending if the Feedback is paused or not. 88 | """ 89 | self._running = True 90 | self._inMainloop = True 91 | while self._running: 92 | self.tick() 93 | if self._paused: 94 | self.pause_tick() 95 | else: 96 | self.play_tick() 97 | self._inMainloop = False 98 | 99 | def init(self): 100 | """Called at the beginning of the Feedback's lifecycle. 101 | 102 | More specifically: in :func:`on_init`. 103 | """ 104 | pass 105 | 106 | def pre_mainloop(self): 107 | """Called before entering the mainloop, e.g. after :func:`on_play`.""" 108 | pass 109 | 110 | def post_mainloop(self): 111 | """Called after leaving the mainloop, e.g. after stop or quit.""" 112 | pass 113 | 114 | def tick(self): 115 | """ 116 | Called repeatedly in the mainloop no matter if the Feedback is paused 117 | or not. 118 | """ 119 | pass 120 | 121 | def pause_tick(self): 122 | """ 123 | Called repeatedly in the mainloop if the Feedback is paused. 124 | """ 125 | pass 126 | 127 | def play_tick(self): 128 | """ 129 | Called repeatedly in the mainloop if the Feedback is not paused. 130 | """ 131 | pass 132 | 133 | -------------------------------------------------------------------------------- /src/FeedbackBase/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/FeedbackBase/__init__.py -------------------------------------------------------------------------------- /src/FeedbackBase/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/FeedbackBase/test/__init__.py -------------------------------------------------------------------------------- /src/FeedbackBase/test/test_feedback.py: -------------------------------------------------------------------------------- 1 | # test_feedback.py - 2 | # Copyright (C) 2008-2011 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import unittest 20 | 21 | from FeedbackBase.Feedback import Feedback 22 | 23 | 24 | class FeedbackTestCase(unittest.TestCase): 25 | 26 | def testFeedbackInitWithoutArguments(self): 27 | """Feedback should instantiate without arguments.""" 28 | try: 29 | fb = Feedback() 30 | except: 31 | self.fail() 32 | 33 | def testFeedbackInitWithArguments(self): 34 | """Feedback should instantiate with one argument.""" 35 | try: 36 | fb = Feedback(None) 37 | except: 38 | self.fail() 39 | 40 | def testFeedbackCallbacks(self): 41 | """_on_play, _pause, _stop and _quit should work.""" 42 | fb = Feedback() 43 | try: 44 | fb._on_play() 45 | fb._on_pause() 46 | fb._on_stop() 47 | fb._on_quit() 48 | except: 49 | self.fail() 50 | 51 | def suite(): 52 | testSuite = unittest.makeSuite(FeedbacksTestCase) 53 | return testSuite 54 | 55 | def main(): 56 | runner = unittest.TextTestRunner() 57 | runner.run(suite()) 58 | 59 | if __name__ == "__main__": 60 | main() 61 | -------------------------------------------------------------------------------- /src/FeedbackBase/test/test_mainloopfeedback.py: -------------------------------------------------------------------------------- 1 | # test_mainloopfeedback.py - 2 | # Copyright (C) 2008-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import unittest 20 | from threading import Thread 21 | 22 | from FeedbackBase.MainloopFeedback import MainloopFeedback 23 | 24 | 25 | class MainloopFeedbackTestCase(unittest.TestCase): 26 | 27 | def setUp(self): 28 | self.fb = MainloopFeedback() 29 | self.init() 30 | 31 | def tearDown(self): 32 | self.quit(1) 33 | 34 | def testPlayQuit(self): 35 | """Mainloop Feedback should handle: init + play + quit.""" 36 | self.play() 37 | self.assertTrue(self.quit()) 38 | 39 | def testQuit(self): 40 | """Mainloop Feedback should handle: init + quit.""" 41 | self.assertTrue(self.quit()) 42 | 43 | def testQuitQuit(self): 44 | """Mainloop Feedback should handle: init + quit + quit.""" 45 | self.assertTrue(self.quit()) 46 | self.assertTrue(self.quit()) 47 | 48 | def testInitInit(self): 49 | """Mainloop Feedback should handle: init + init.""" 50 | self.assertTrue(self.init()) 51 | self.assertTrue(self.init()) 52 | 53 | def testPlayPauseStopQuit(self): 54 | """Mainloop Feedback should handle: init + play + pause + stop + quit.""" 55 | self.play() 56 | self.pause() 57 | self.stop() 58 | self.assertTrue(self.quit()) 59 | 60 | def testPlayPausePauseStopQuit(self): 61 | """Mainloop Feedback should handle: init + play + pause + pause + stop + quit.""" 62 | self.play() 63 | self.pause() 64 | self.pause() 65 | self.stop() 66 | self.assertTrue(self.quit()) 67 | 68 | 69 | def init(self, timeout=None): 70 | return self.call_async(self.fb.on_init, timeout) 71 | 72 | def play(self, timeout=-1): 73 | return self.call_async(self.fb.on_play, timeout) 74 | 75 | def pause(self, timeout=None): 76 | return self.call_async(self.fb.on_pause, timeout) 77 | 78 | def stop(self, timeout=None): 79 | return self.call_async(self.fb.on_stop, timeout) 80 | 81 | def quit(self, timeout=None): 82 | return self.call_async(self.fb.on_quit, timeout) 83 | 84 | def call_async(self, mytarget, timeout=None): 85 | """Call the method in a seperate thread and join. 86 | 87 | If a timeout is given join waits timeout seconds otherwise it waits 88 | until the thread quits itself. 89 | 90 | If the timeout is -1 the method returns without joining. 91 | """ 92 | thread = Thread(target=mytarget) 93 | thread.start() 94 | if timeout == -1: 95 | return True 96 | thread.join(timeout) 97 | return not thread.isAlive() 98 | 99 | 100 | def suite(): 101 | testSuite = unittest.makeSuite(MainloopFeedbackTestCase) 102 | return testSuite 103 | 104 | def main(): 105 | runner = unittest.TextTestRunner() 106 | runner.run(suite()) 107 | 108 | if __name__ == "__main__": 109 | main() -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/ball_smaller2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/ball_smaller2.bmp -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/ball_smaller3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/ball_smaller3.bmp -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/ball_smaller4.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/ball_smaller4.bmp -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/bar.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/bar_metallic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/bar_metallic3.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/bg.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/bowl_metallic_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/bowl_metallic_blue.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/feedbacks.list: -------------------------------------------------------------------------------- 1 | BrainPong.BrainPong 2 | -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/stars.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/stars.jpg -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/wall_metal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/wall_metal.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/wall_metal_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/wall_metal_blue.png -------------------------------------------------------------------------------- /src/Feedbacks/BrainPong/wall_metal_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/BrainPong/wall_metal_dark.png -------------------------------------------------------------------------------- /src/Feedbacks/EyetrackerFeedback/EyetrackerRawdata.py: -------------------------------------------------------------------------------- 1 | # 2009 Matthias Sebastian Treder 2 | 3 | from FeedbackBase.MainloopFeedback import MainloopFeedback 4 | from lib.eyetracker import EyeTracker 5 | 6 | class EyetrackerRawdata(MainloopFeedback): 7 | 8 | def pre_mainloop(self): 9 | # Start eye tracker 10 | self.et = EyeTracker() 11 | self.et.start() 12 | 13 | def play_tick(self): 14 | print self.et.x, self.et.y, self.et.duration 15 | 16 | def post_mainloop(self): 17 | # Stop eyetracker 18 | self.et.stop() 19 | -------------------------------------------------------------------------------- /src/Feedbacks/EyetrackerFeedback/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/EyetrackerFeedback/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/EyetrackerFeedback/feedbacks.list: -------------------------------------------------------------------------------- 1 | EyetrackerFeedback.EyetrackerFeedback 2 | EyetrackerRawdata.EyetrackerRawdata 3 | -------------------------------------------------------------------------------- /src/Feedbacks/FeedbackCursorArrow/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/FeedbackCursorArrow/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/FeedbackCursorArrow/feedbacks.list: -------------------------------------------------------------------------------- 1 | FeedbackCursorArrow.FeedbackCursorArrow 2 | -------------------------------------------------------------------------------- /src/Feedbacks/GazeIndependentSpeller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GazeIndependentSpeller/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/GazeIndependentSpeller/feedbacks.list: -------------------------------------------------------------------------------- 1 | CenterSpellerVE.CenterSpellerVE 2 | CakeSpellerVE.CakeSpellerVE 3 | HexoSpellerVE.HexoSpellerVE -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/ball.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/ball_miss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/ball_miss.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/ball_missCircle2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/ball_missCircle2.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/ball_missCircle3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/ball_missCircle3.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/bowl copy.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/bowl copy.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/bowl.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/bowl.psd -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/bowls_small.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/bowls_small.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/bowls_smaller.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/bowls_smaller.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/classifierbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/classifierbar.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/feedbacks.list: -------------------------------------------------------------------------------- 1 | GoalKeeper.GoalKeeper 2 | -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/frame.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/frame.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/frame_blue.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/frame_blue.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/frame_blue_grad.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/frame_blue_grad.bmp -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_left.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_left_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_left_green.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_left_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_left_red.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_right.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_right_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_right_green.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/halfball_right_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/halfball_right_red.png -------------------------------------------------------------------------------- /src/Feedbacks/GoalKeeper/keeper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/GoalKeeper/keeper.png -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/GraphicComponents/ColorSchemes.py: -------------------------------------------------------------------------------- 1 | # ColorSchemes.py - 2 | # Copyright (C) 2009-2010 Sven Daehne 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | 20 | white = (1, 1, 1) 21 | black = (0, 0, 0) 22 | red = (1, 0, 0) 23 | green = (0, 1, 0) 24 | blue = (0, 0, 1) 25 | yellow = (1, 1, 0) 26 | 27 | ice_color_1 = ( 0/255.0, 39/255.0, 167/255.0) 28 | ice_color_2 = ( 32/255.0, 53/255.0, 125/255.0) 29 | ice_color_3 = ( 0/255.0, 25/255.0, 108/255.0) 30 | ice_color_4 = (128/255.0, 157/255.0, 255/255.0) 31 | ice_color_5 = (230/255.0, 235/255.0, 255/255.0) 32 | ICE = { 33 | "hexagon_default_color": ice_color_1, 34 | "hexagon_highlight_color": ice_color_5, 35 | "hexagon_text_color": ice_color_3, 36 | "arrow_color": ice_color_4, 37 | "arrow_locked_color": ice_color_1, 38 | "control_signal_bar_color": ice_color_4, 39 | "control_signal_bar_frame_color": ice_color_3, 40 | "textboard_background_color": ice_color_4, 41 | "textboard_frame_color": ice_color_3, 42 | "textboard_text_color": ice_color_3, 43 | "background_color": ice_color_4, 44 | } 45 | 46 | desert_color_1 = (230/255.0, 186/255.0, 23/255.0) 47 | desert_color_2 = (143/255.0, 127/255.0, 63/255.0) 48 | desert_color_3 = (109/255.0, 87/255.0, 3/255.0) 49 | desert_color_4 = (249/255.0, 219/255.0, 109/255.0) 50 | desert_color_5 = (249/255.0, 233/255.0, 176/255.0) 51 | DESERT = { 52 | "hexagon_default_color": desert_color_1, 53 | "hexagon_highlight_color": desert_color_5, 54 | "hexagon_text_color": desert_color_3, 55 | "arrow_color": desert_color_4, 56 | "arrow_locked_color": desert_color_3, 57 | "control_signal_bar_color": desert_color_4, 58 | "control_signal_bar_frame_color": desert_color_3, 59 | "textboard_background_color": desert_color_4, 60 | "textboard_frame_color": desert_color_3, 61 | "textboard_text_color": desert_color_3, 62 | "background_color": desert_color_1, 63 | } 64 | 65 | 66 | tree_color_1 = (100/255.0, 204/255.0, 20/255.0) 67 | tree_color_2 = ( 19/255.0, 115/255.0, 143/255.0) 68 | tree_color_3 = (230/255.0, 224/255.0, 23/255.0) 69 | tree_color_4 = (246/255.0, 243/255.0, 98/255.0) 70 | tree_color_5 = (204/255.0, 21/255.0, 74/255.0) 71 | tree_color_6 = (123/255.0, 120/255.0, 4/255.0) 72 | TREE = { 73 | "hexagon_default_color": tree_color_3, 74 | "hexagon_highlight_color": tree_color_4, 75 | "hexagon_text_color": tree_color_3, 76 | "arrow_color": white, 77 | "arrow_locked_color": tree_color_6, 78 | "control_signal_bar_color": tree_color_4, 79 | "control_signal_bar_frame_color": white, 80 | "textboard_background_color": tree_color_6, 81 | "textboard_frame_color": white, 82 | "textboard_text_color": white, 83 | "background_color": tree_color_1, 84 | } 85 | -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/GraphicComponents/TestScript.py: -------------------------------------------------------------------------------- 1 | 2 | if __name__ == "__main__": 3 | #import Arrow 4 | #from direct.directbase import DirectStart 5 | #from showbase.DirectObject import DirectObject 6 | from direct.showbase import ShowBase 7 | sb = ShowBase.ShowBase() 8 | 9 | sb.userExit() 10 | 11 | 12 | print "done" -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/GraphicComponents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/HexoSpeller/GraphicComponents/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/LanguageModels/german.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/HexoSpeller/LanguageModels/german.mat -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/LanguageModels/lm1to8.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/HexoSpeller/LanguageModels/lm1to8.mat -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/Utils.py: -------------------------------------------------------------------------------- 1 | # Utils.py - 2 | # Copyright (C) 2009-2010 Sven Daehne 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Utility methods for the classes involved in the Hexospeller feedback. 20 | """ 21 | from math import sin, cos, pi 22 | 23 | def rotate_phi_degrees_clockwise(phi, x_y): 24 | """ Rotates the point given by the tuple x_y for phi degrees clockwise and returns the 25 | resulting coordinates in a tuple. """ 26 | phi_rad = degrees_to_radians(phi) 27 | return rotate_phi_radians_clockwise(phi_rad, x_y) 28 | 29 | def rotate_phi_radians_clockwise(phi, x_y): 30 | """ Rotates the point given by the tuple x_y for phi radians clockwise and returns the 31 | resulting coordinates in a tuple. """ 32 | x, y = x_y 33 | x_new = cos(phi)*x + sin(phi)*y 34 | y_new = -sin(phi)*x + cos(phi)*y 35 | return (x_new, y_new) 36 | 37 | def rotate_phi_degrees_counter_clockwise(phi, x_y): 38 | """ Rotates the point given by the tuple x_y for phi degrees counter clockwise and returns the 39 | resulting coordinates in a tuple. """ 40 | return rotate_phi_degrees_clockwise(-phi, x_y) 41 | 42 | def rotate_phi_radians_counter_clockwise(phi, x_y): 43 | """ Rotates the point given by the tuple x_y for phi radians counter clockwise and returns the 44 | resulting coordinates in a tuple. """ 45 | return rotate_phi_radians_clockwise(-phi, x_y) 46 | 47 | def degrees_to_radians(phi_degrees): 48 | """ Converts the angle phi given in degrees to radians. """ 49 | return (phi_degrees/360.0) * 2.0*pi 50 | 51 | def radians_to_degrees(phi_radians): 52 | """ Converts the angle phi given in radians to degrees. """ 53 | return (phi_radians/(2.0*pi)) * 360.0 54 | 55 | def copy_list(orig_list): 56 | """ Creates a shallow copy of the given list and all its sublists. """ 57 | c_list = [] 58 | for elem in orig_list: 59 | if type(elem) == type([]): 60 | c_list.append(copy_list(elem)) 61 | else: 62 | c_list.append(elem) 63 | return c_list 64 | 65 | def array_to_list(array): 66 | li = [] 67 | for elem in array: 68 | li.append(elem) 69 | return li 70 | 71 | def max_with_idx(iter): 72 | max_value = max(iter) 73 | idx = 0 74 | for elem in iter: 75 | if max_value == elem: 76 | return max_value, idx 77 | idx += 1 78 | return max_value, None 79 | 80 | def sort_list_according_to_values(list, values): 81 | sorted_list = [] 82 | while len(list) > 0: 83 | # find the position of the max value in values 84 | idx = max_with_idx(values)[1] 85 | sorted_list.append(list.pop(idx)) 86 | values.pop(idx) 87 | return sorted_list 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/HexoSpeller/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/HexoSpeller/feedbacks.list: -------------------------------------------------------------------------------- 1 | HexoSpeller.HexoSpeller 2 | 3 | -------------------------------------------------------------------------------- /src/Feedbacks/LibetClock/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/LibetClock/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/LibetClock/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/LibetClock/background.jpg -------------------------------------------------------------------------------- /src/Feedbacks/LibetClock/clockhand_straight.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/LibetClock/clockhand_straight.bmp -------------------------------------------------------------------------------- /src/Feedbacks/LibetClock/feedbacks.list: -------------------------------------------------------------------------------- 1 | LibetClock.LibetClock 2 | -------------------------------------------------------------------------------- /src/Feedbacks/MovingRhomb/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/MovingRhomb/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/MovingRhomb/feedbacks.list: -------------------------------------------------------------------------------- 1 | MovingRhomb.MovingRhomb 2 | -------------------------------------------------------------------------------- /src/Feedbacks/MovingRhombGL/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/MovingRhombGL/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/MovingRhombGL/feedbacks.list: -------------------------------------------------------------------------------- 1 | MovingRhombGL.MovingRhombGL 2 | -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Auditory/AuditoryOddball.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # NOTE: This class makes use of the python module 'audiere' (http://pyaudiere.org/) 4 | # 5 | # Copyright (C) 2008-2009 Simon Scholler 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | """Class for Auditory Oddball Experiments.""" 22 | 23 | import audiere 24 | 25 | from Feedbacks.Oddball import Oddball 26 | 27 | class AuditoryOddball(Oddball.Oddball): 28 | 29 | def init(self): 30 | super(AuditoryOddball,self).init() 31 | self.DIR_DEV = 'C:/stim_test/dev' 32 | self.DIR_STD = 'C:/stim_test/std' 33 | self.stimuli = 'predefined' 34 | self.nStim = 15 35 | self.au = audiere.open_device() 36 | self.dev_perc = 0.3 37 | 38 | def load_stimulus(self,filename): 39 | """ 40 | Loads a stimulus from a file and returns it. 41 | """ 42 | return self.au.open_file(filename) 43 | 44 | def define_stimuli(self): 45 | """ 46 | Creates standard and deviant stimuli. 47 | """ 48 | dev1 = self.au.create_tone(500) 49 | std1 = self.au.create_tone(700) 50 | return [std1], [dev1] 51 | 52 | def start_stimulus(self, stim): 53 | """ 54 | Start audio file. 55 | """ 56 | stim.play() 57 | 58 | def stop_stimulus(self, stim): 59 | """ 60 | Stop audio file. 61 | """ 62 | stim.pause() #stim.stop() 63 | 64 | if __name__ == '__main__': 65 | vo = AuditoryOddball() 66 | vo.on_init() 67 | vo.on_play() -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Auditory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Oddball/Auditory/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/CheckerboardVEP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | # Copyright (C) 2009 Simon Scholler 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | """Checkerboard-flipping used to generate a visual evoked potential (VEP).""" 21 | 22 | import pygame 23 | import math 24 | 25 | from Feedbacks.Oddball.Visual import VisualOddball 26 | 27 | 28 | class CheckerboardVEP(VisualOddball.VisualOddball): 29 | 30 | def init(self): 31 | super(CheckerboardVEP,self).init() 32 | self.dev_perc = 0.5 33 | self.nStim = 40 34 | self.nStim_per_block = 20 # number of stimuli until a pause 35 | self.dd_dist = 1 36 | self.squaresPerSide = 6 37 | self.squaresize = 20 38 | self.fixdotsize = 10 39 | self.response = 'none' 40 | self.give_feedback = False 41 | self.feedback_duration, self.responsetime_duration = 0,0 42 | self.stim_duration = 1500 43 | self.beforestim_ival = [0,0] 44 | self.backgroundColor = (50,50,50) 45 | self.offset = (50,-50) 46 | 47 | def load_stimulus(self,filename): 48 | """ 49 | Loads a stimulus from a file. 50 | """ 51 | raise Exception('Not implemented yet') 52 | 53 | 54 | def define_stimuli(self): 55 | """ 56 | Creates standard and deviant stimuli. 57 | """ 58 | #size = (self.screen_pos[-1]*2/3,self.screen_pos[-1]*2/3) 59 | size = (self.squaresPerSide*self.squaresize,self.squaresPerSide*self.squaresize) 60 | cb1 = pygame.Surface(size) 61 | cb2 = pygame.Surface(size) 62 | white = (255,255,255) 63 | black = (1,1,1) 64 | red = (200,0,0) 65 | colors = [white,black] 66 | #squaresize = size[1]*1.0/self.squaresPerSide 67 | sign = 1 68 | for y in range(self.squaresPerSide): 69 | for x in range(self.squaresPerSide): 70 | rect = (x*self.squaresize,y*self.squaresize,self.squaresize,self.squaresize) 71 | if not(self.squaresPerSide%2==0 and x==0): 72 | sign = -sign 73 | pygame.draw.rect(cb1, colors[(sign*1+1)/2], rect) 74 | pygame.draw.rect(cb2, colors[(sign*-1+1)/2], rect) 75 | pygame.draw.circle(cb1, red, (size[0]/2, size[0]/2), self.fixdotsize) 76 | pygame.draw.circle(cb2, red, (size[0]/2, size[0]/2), self.fixdotsize) 77 | return [cb1], [cb2] 78 | 79 | def start_stimulus(self, stim): 80 | """ 81 | Draw the stimulus onto the screen. 82 | """ 83 | middle = self.screen.get_rect().center 84 | c = (self.offset[0]+middle[0],self.offset[1]+middle[1]) 85 | stimRect = stim.get_rect(center=c) 86 | self.screen.blit(stim, stimRect) 87 | pygame.display.update() 88 | 89 | def stop_stimulus(self, stim): 90 | """ 91 | Remove the stimulus from the screen. 92 | """ 93 | self.draw_initial() 94 | 95 | if __name__ == '__main__': 96 | cbvep = CheckerboardVEP() 97 | cbvep.on_init() 98 | cbvep.on_play() 99 | 100 | -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Tactile/TactileOddball.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # NOTE: This class makes use of the python module 'audiere' (http://pyaudiere.org/) 4 | # 5 | # Copyright (C) 2008-2009 Simon Scholler 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | """Class for Tactile Oddball Experiments.""" 22 | 23 | import audiere 24 | 25 | from Feedbacks.Oddball import Oddball 26 | 27 | class TactileOddball(Oddball.Oddball): 28 | 29 | def init(self): 30 | super(TactileOddball,self).init() 31 | self.au = audiere.open_device() 32 | 33 | def load_stimulus(self,filename): 34 | """ 35 | Loads a stimulus from a file and returns it. 36 | """ 37 | return self.au.open_file(filename) 38 | 39 | def define_stimuli(self): 40 | """ 41 | Creates standard and deviant stimuli. 42 | """ 43 | dev1 = self.au.create_tone(50) 44 | std1 = self.au.create_tone(100) 45 | return [std1], [dev1] 46 | 47 | def start_stimulus(self, stim): 48 | """ 49 | Start audio file. 50 | """ 51 | stim.play() 52 | 53 | def stop_stimulus(self, stim): 54 | """ 55 | Stop audio file. 56 | """ 57 | stim.stop() 58 | 59 | if __name__ == '__main__': 60 | vo = TactileOddball() 61 | vo.on_init() 62 | vo.on_play() -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Tactile/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Oddball/Tactile/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Visual/VisualOddball.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | # Methods that have to implemented by a subclass 5 | # - init: if some default settings should be changed 6 | # - load_stimulus: if stimuli are files (e.g. images, audio-files) 7 | # - get_predefined_stimuli: if stimuli are created by a python module 8 | # - present_stimulus: determines how the stimuli are presented 9 | # 10 | # 11 | # Copyright (C) 2008-2009 Simon Scholler 12 | # 13 | # This program is free software; you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License as published by 15 | # the Free Software Foundation; either version 2 of the License, or 16 | # (at your option) any later version. 17 | # 18 | # This program is distributed in the hope that it will be useful, 19 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | # GNU General Public License for more details. 22 | # 23 | # You should have received a copy of the GNU General Public License along 24 | # with this program; if not, write to the Free Software Foundation, Inc., 25 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | 27 | """Class for Visual Oddball Experiments.""" 28 | 29 | import pygame 30 | 31 | from Feedbacks.Oddball import Oddball 32 | 33 | 34 | class VisualOddball(Oddball.Oddball): 35 | 36 | def init(self): 37 | super(VisualOddball,self).init() 38 | self.dev_perc = 0.25 39 | self.nStim = 21 40 | self.dd_dist = 3 41 | 42 | 43 | def load_stimulus(self,filename): 44 | """ 45 | Loads a stimulus from a file. 46 | """ 47 | raise Exception('Not implemented yet') 48 | 49 | 50 | def define_stimuli(self): 51 | """ 52 | Creates standard and deviant stimuli. 53 | """ 54 | size = (self.screen_pos[-1]/3,self.screen_pos[-1]/3) 55 | # create deviant stimulus 56 | dev1 = pygame.Surface(size) 57 | dev1.fill((255,0,0)) 58 | # create standard stimulus 59 | std1 = pygame.Surface(size) 60 | std1Rect = std1.get_rect(center=(320,240)) 61 | hs = int(size[0]/2) 62 | pygame.draw.circle(std1, (0,255,0), (hs,hs), hs) 63 | return [std1], [dev1] 64 | 65 | def start_stimulus(self, stim): 66 | """ 67 | Draw the stimulus onto the screen. 68 | """ 69 | stimRect = stim.get_rect(center=self.screen.get_rect().center) 70 | stim.set_colorkey((0,0,0)) 71 | self.screen.blit(stim, stimRect) 72 | pygame.display.update() 73 | 74 | def stop_stimulus(self, stim): 75 | """ 76 | Remove the stimulus from the screen. 77 | """ 78 | self.draw_initial() 79 | 80 | if __name__ == '__main__': 81 | vo = VisualOddball() 82 | vo.on_init() 83 | vo.on_play() -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/Visual/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Oddball/Visual/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Oddball/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Oddball/feedbacks.list: -------------------------------------------------------------------------------- 1 | Oddball.Oddball 2 | CheckerboardVEP.CheckerboardVEP 3 | MultiVisualOddball.MultiVisualOddball 4 | P300_Rectangle.P300_Rectangle 5 | Visual.VisualOddball.VisualOddball 6 | Visual.VisualOddballVE.VisualOddballVE 7 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/RSVPSpeller/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/bin/gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ Copyright (c) 2010 Torsten Schmits 4 | 5 | This file is part of the pyff framework. pyff is free software; 6 | you can redistribute it and/or modify it under the terms of the GNU General 7 | Public License version 2, as published by the Free Software Foundation. 8 | 9 | pyff is distributed in the hope that it will be useful, but WITHOUT ANY 10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | """ 19 | 20 | import logging 21 | 22 | from Feedbacks.RSVPSpeller.control import Control 23 | 24 | if __name__ == '__main__': 25 | logging.basicConfig(level=logging.DEBUG) 26 | c = Control() 27 | c.on_init() 28 | c.on_play() 29 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/bin/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ Copyright (c) 2010 Torsten Schmits 4 | 5 | This file is part of the pyff framework. pyff is free software; 6 | you can redistribute it and/or modify it under the terms of the GNU General 7 | Public License version 2, as published by the Free Software Foundation. 8 | 9 | pyff is distributed in the hope that it will be useful, but WITHOUT ANY 10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 12 | details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple 16 | Place, Suite 330, Boston, MA 02111-1307 USA 17 | 18 | """ 19 | 20 | from unittest import main 21 | import logging 22 | 23 | from Feedbacks.RSVPSpeller.test import * 24 | 25 | if __name__ == '__main__': 26 | logging.basicConfig(level=logging.DEBUG) 27 | main() 28 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/burst.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from time import sleep 17 | 18 | from RSVPSpeller.util.trigger import TRIG_BURST_START, TRIG_BURST_END 19 | 20 | class BurstConstraints(object): 21 | def __init__(self, fix, ask, view, asker, sleep_interval, trigger): 22 | dummy = lambda: None 23 | self._clear_symbol = view.clear_center_word 24 | self._fixation_cross = view.show_fixation_cross if fix else dummy 25 | self._ask = asker if ask else dummy 26 | self._sleep_interval = sleep_interval 27 | self._trigger = trigger 28 | 29 | def __enter__(self): 30 | self._fixation_cross() 31 | self._trigger(TRIG_BURST_START, wait=True) 32 | 33 | def __exit__(self, exc_type, exc_val, exc_tb): 34 | self._clear_symbol() 35 | self._trigger(TRIG_BURST_END, wait=True) 36 | self._ask() 37 | sleep(self._sleep_interval) 38 | return False 39 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/config.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | class Config(object): 17 | def init(self): 18 | # 1: Count, 2: YesNo, 3: Calibration, 4: FreeSpelling, 5: CopySpelling 19 | self.trial_type = 5 20 | self.alternating_colors = True 21 | self.sequences_per_trial = 10 22 | self.geometry = [0, 0, 1280, 1024] 23 | self.word_font_size = 300 24 | self.alphabet_font_size = 200 25 | self.word_target_font_size = 252 26 | self.word_vpos = 100 27 | self.alphabet_vpos = 1400 28 | self.symbol_vpos = 800 29 | self.symbol_colors = ['red', 'white', 'blue', 'green', 'black'] 30 | self.words = ['LUXUS', 'WINKT', 'FJORD', 'HYBRID', 'SPHINX', 'QUARZ', 31 | 'VODKA', 'YACHT', 'GEBOT', 'MEMME'] 32 | self.color_groups = ['fRyGk<', 'pJUX!E', 'iSwc-N','TBMqAH','LdvOz.'] 33 | self.nonalpha_trigger = [['-', 57], ['.', 58], ['!', 59], ['<', 60]] 34 | #self.meaningless = '*+&%?;' 35 | self.meaningless = '' 36 | self.custom_pre_sequences = [] 37 | self.custom_post_sequences = [] 38 | # The time for that one single letter is displayed 39 | self.symbol_duration = .083 40 | # Pause time directly after each burst 41 | self.inter_burst = 0 42 | # Pause time directly after one sequence of bursts or the 43 | # following user input if set 44 | self.inter_sequence = 0 45 | # Pause time directly after one set of sequences for one letter 46 | # or the following user input if set (count, spelling) 47 | self.inter_trial = 1 48 | # Pause time after all targets in one word have been processed 49 | self.inter_word = .1 50 | # Display time of a new word in the center 51 | self.present_word_time = .2 52 | self.show_trial_fix_cross = True 53 | self.show_burst_fix_cross = False 54 | self.show_word_fix_cross = False 55 | # Display time of the next target in the word 56 | self.present_target_time = 4 57 | # Display time of the classifier-selected letter 58 | self.present_eeg_input_time = 1 59 | self.key_yes = 'j' 60 | self.key_no = 'k' 61 | # What's considered as valid count input discrepancy 62 | self.max_diff = 20 63 | self.sound = False 64 | self.delete_symbol = '<' 65 | # Display a frame around the current target 66 | self.show_target_frame = True 67 | self.target_frame_width = 2 68 | # Display the current alphabet in corresponding colors at the 69 | # bottom 70 | self.show_alphabet = False 71 | # Allow the eeg input to be simulated by keyboard (for debug) 72 | self.allow_keyboard_input = True 73 | # Display the countdown before each new word 74 | self.show_word_countdown = True 75 | # Display the countdown before each new target 76 | self.show_trial_countdown = False 77 | self.countdown_start = 3 78 | self.fullscreen = False 79 | self.headline_vpos = 150 80 | self.font_color_name = 'black' 81 | self.bg_color = 'grey' 82 | self.fixation_cross_time = 3 83 | self.countdown_symbol_duration = .5 84 | self._view_parameters += ['symbol_duration', 'present_word_time', 85 | 'present_target_time', 'color_groups', 86 | 'alternating_colors', 'word_font_size', 87 | 'word_target_font_size', 'symbol_colors', 88 | 'word_vpos', 'symbol_vpos', 89 | 'present_eeg_input_time', 'show_target_frame', 90 | 'target_frame_width', 'alphabet_vpos', 91 | 'alphabet_font_size', 'show_alphabet'] 92 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/control.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | 3 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 4 | 5 | This program is free software; you can redistribute it and/or modify it under 6 | the terms of the GNU General Public License as published by the Free Software 7 | Foundation; either version 3 of the License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License along with this 14 | program; if not, see . 15 | 16 | """ 17 | 18 | from os import path 19 | 20 | import pygame 21 | 22 | from FeedbackBase.VisionEggFeedback import VisionEggFeedback 23 | 24 | from RSVPSpeller.config import Config 25 | from RSVPSpeller.view import View 26 | from RSVPSpeller.model.palette import Palette 27 | from RSVPSpeller.util.metadata import datadir 28 | from RSVPSpeller.trial import * 29 | from RSVPSpeller.input import * 30 | from RSVPSpeller.experiment import * 31 | from RSVPSpeller.util.error import RSVPSpellerException 32 | 33 | class Control(VisionEggFeedback, Config): 34 | def __init__(self, *args, **kwargs): 35 | pygame.mixer.init() 36 | self.__init_attributes() 37 | VisionEggFeedback.__init__(self, *args, **kwargs) 38 | 39 | def init(self): 40 | Config.init(self) 41 | self.update_parameters() 42 | 43 | def __init_attributes(self): 44 | self._sound = pygame.mixer.Sound(path.join(datadir, 'sound.ogg')) 45 | self._palette = Palette() 46 | self._trial_types = ['Count', 'YesNo', 'Calibration', 'FreeSpelling', 47 | 'CopySpelling'] 48 | 49 | def _create_view(self): 50 | return View(self._palette) 51 | 52 | def update_parameters(self): 53 | VisionEggFeedback.update_parameters(self) 54 | self._palette.set(self.symbol_colors, self.color_groups) 55 | self.alphabet = ''.join(self.color_groups) 56 | self._sorted_alphabet = sorted(self.alphabet, key=lambda s: s.lower()) 57 | self.eeg_alphabet = ''.join(filter(lambda c: c.isalpha(), 58 | self._sorted_alphabet) 59 | + [e[0] for e in self.nonalpha_trigger]) 60 | self._trial_name = self._trial_types[self.trial_type - 1] 61 | self._setup_input_handler() 62 | self._setup_trial() 63 | self._setup_experiment() 64 | 65 | def _setup_input_handler(self): 66 | input_handler_type = self._trial_name + 'InputHandler' 67 | self._input_handler = eval(input_handler_type)(self) 68 | 69 | def _setup_trial(self): 70 | trial_type = self._trial_name + 'Trial' 71 | self._trial = eval(trial_type)(self._view, self._trigger, self._iter, 72 | self.stimulus_sequence, self) 73 | 74 | def _setup_experiment(self): 75 | experiment_type = self._trial_name + 'Experiment' 76 | self._experiment = eval(experiment_type)(self._view, self._trial, 77 | self._input_handler, 78 | self._flag, self._iter, 79 | self._palette, self) 80 | 81 | def run(self): 82 | try: 83 | self._view.alphabet(self.eeg_alphabet) 84 | self._experiment.run() 85 | except RSVPSpellerException as e: 86 | self.logger.error(e) 87 | self.quit() 88 | 89 | def keyboard_input(self, event): 90 | if self._trial.asking: 91 | self._input_handler.keyboard(event) 92 | VisionEggFeedback.keyboard_input(self, event) 93 | 94 | def quit(self): 95 | self._view.answered() 96 | VisionEggFeedback.quit(self) 97 | 98 | def on_control_event(self, data): 99 | cls = data.get('cl_output', None) 100 | if cls is not None: 101 | self._input_handler.eeg_select(cls) 102 | 103 | class RSVPSpeller(Control): 104 | pass 105 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/data/sound.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/RSVPSpeller/data/sound.ogg -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/experiment.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | from time import sleep 18 | from itertools import count 19 | 20 | from RSVPSpeller.model.character_sequence import CharacterSequenceFactory 21 | 22 | __all__ = ['CountExperiment', 'YesNoExperiment', 'CopySpellingExperiment', 23 | 'CalibrationExperiment', 'FreeSpellingExperiment'] 24 | 25 | class Experiment(object): 26 | def __init__(self, view, trial, input_handler, flag, iter, 27 | palette, config): 28 | self._view = view 29 | self._trial = trial 30 | self._input_handler = input_handler 31 | self._flag = flag 32 | self._iter = iter 33 | self._palette = palette 34 | self._redundance = config.meaningless 35 | self._words = config.words 36 | self._inter_trial = config.inter_trial 37 | self._inter_word = config.inter_word 38 | self._alternating_colors = config.alternating_colors 39 | self._sequences_per_trial = config.sequences_per_trial 40 | self._custom_pre_sequences = config.custom_pre_sequences 41 | self._custom_post_sequences = config.custom_post_sequences 42 | self._word_countdown = config.show_word_countdown 43 | self._word_fix_cross = config.show_word_fix_cross 44 | self._current_target = '' 45 | 46 | def run(self): 47 | self._input_handler.start_experiment(self) 48 | 49 | def trial(self): 50 | factory = CharacterSequenceFactory(self._redundance, 51 | self._alternating_colors, 52 | self._current_target, 53 | self._palette) 54 | sequences = factory.sequences(self._sequences_per_trial, 55 | self._custom_pre_sequences, 56 | self._custom_post_sequences) 57 | self._input_handler.start_trial(self._trial) 58 | self._trial.run(sequences) 59 | if self._flag: 60 | self._trial.evaluate(self._input_handler) 61 | 62 | def delete(self): 63 | pass 64 | 65 | class GuidedExperiment(Experiment): 66 | def run(self): 67 | super(GuidedExperiment, self).run() 68 | for word in self._iter(self._words): 69 | if self._word_countdown: 70 | self._view.countdown() 71 | self._view.word(word) 72 | if self._word_fix_cross: 73 | self._view.show_fixation_cross() 74 | for target in self._iter(enumerate(word)): 75 | self.trial(*target) 76 | sleep(self._inter_trial) 77 | sleep(self._inter_word) 78 | 79 | def trial(self, index, target): 80 | self._view.target(index) 81 | self._trial.target(target) 82 | Experiment.trial(self) 83 | 84 | YesNoExperiment = GuidedExperiment 85 | CopySpellingExperiment = GuidedExperiment 86 | CalibrationExperiment = GuidedExperiment 87 | 88 | class CountExperiment(GuidedExperiment): 89 | def trial(self, *a, **kw): 90 | self.detections = [] 91 | GuidedExperiment.trial(self, *a, **kw) 92 | 93 | class FreeSpellingExperiment(Experiment): 94 | def run(self): 95 | super(FreeSpellingExperiment, self).run() 96 | for i in self._iter(count()): 97 | self.trial() 98 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/feedbacks.list: -------------------------------------------------------------------------------- 1 | control.RSVPSpeller 2 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/model/__init__.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/model/palette.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from pygame import Color 17 | 18 | from RSVPSpeller.util.error import RSVPSpellerException 19 | 20 | class NoMatchingSymbolColor(RSVPSpellerException): 21 | """ The color for a symbol has been queried that does not exist 22 | in a present color group. """ 23 | def __init__(self, symbol): 24 | text = 'Symbol not found in color groups: %s' % symbol 25 | super(NoMatchingSymbolColor, self).__init__(text) 26 | 27 | class Palette(object): 28 | def __init__(self): 29 | self.set(['black'], ['A']) 30 | 31 | def set(self, colors, groups): 32 | self._alt_colors = colors 33 | self._color_count = len(colors) 34 | self.groups = groups 35 | 36 | def __call__(self, color): 37 | """ Convert the argument into a (list of) pygame Color. 38 | If color is a single character, return the color of the 39 | corresponding color group. 40 | If color is a literal name, return the pygame color by that 41 | name. 42 | If color is an integer, return the color of that position, 43 | alternating the specified colors. 44 | """ 45 | if isinstance(color, list): 46 | return map(self, color) 47 | elif isinstance(color, basestring): 48 | return self.symbol_color(color) if len(color) == 1 else \ 49 | Color(color).normalize() 50 | elif isinstance(color, int): 51 | return self.alt_color(color) 52 | else: 53 | return color 54 | 55 | def alt_color(self, i): 56 | """ Return a positional color, alternating over alt_colors. """ 57 | return self(self._alt_colors[i % self._color_count]) 58 | 59 | def symbol_color(self, symbol): 60 | try: 61 | index = (i for i, g in enumerate(self.groups) if symbol in g).next() 62 | except StopIteration: 63 | raise NoMatchingSymbolColor(symbol) 64 | return self(index) 65 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/model/target_word.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | import operator 18 | 19 | from pygame import Color 20 | 21 | import VisionEgg.ParameterTypes as ve_types 22 | import VisionEgg.GL as gl 23 | from VisionEgg.Core import Stimulus 24 | 25 | from lib.vision_egg.model.color_word import ColorWord 26 | 27 | class Frame(Stimulus): 28 | parameters_and_defaults = { 29 | 'on':(True, ve_types.Boolean), 30 | 'blending_enabled':(False, ve_types.Boolean), 31 | 'depth_test':(False, ve_types.Boolean) 32 | } 33 | 34 | def __init__(self, position, size, line_width=2, **kw): 35 | Stimulus.__init__(self, **kw) 36 | ul = (position[0] - size[0] / 2., position[1]) 37 | add = lambda a, b: map(operator.add, a, b) 38 | self._vertices = [[ul[0], ul[1], 0], 39 | [ul[0] + size[0], ul[1], 0], 40 | [ul[0] + size[0], ul[1] + size[1], 0], 41 | [ul[0], ul[1] + size[1], 0]] 42 | self._color = Color('red') 43 | self._gl_color = gl.glColor3f if len(self._color) == 3 else gl.glColor4f 44 | self._line_width = line_width 45 | 46 | def draw(self): 47 | p = self.parameters 48 | if p.on: 49 | self._gl_color(*self._color) 50 | gl.glDisable(gl.GL_TEXTURE_2D) 51 | if p.depth_test: 52 | gl.glEnable(gl.GL_DEPTH_TEST) 53 | else: 54 | gl.glDisable(gl.GL_DEPTH_TEST) 55 | if p.blending_enabled: 56 | gl.glEnable(gl.GL_BLEND) 57 | else: 58 | gl.glDisable(gl.GL_BLEND) 59 | gl.glLineWidth(self._line_width) 60 | gl.glBegin(gl.GL_LINE_LOOP) 61 | for vertex in self._vertices: 62 | gl.glVertex(*vertex) 63 | gl.glEnd() 64 | 65 | class TargetWord(ColorWord): 66 | def __init__(self, target_frame=False, target_frame_width=2, 67 | center_at_target=False, **kw): 68 | self._target_frame = target_frame 69 | self._target_frame_width = target_frame_width 70 | self._center_at_target = center_at_target 71 | ColorWord.__init__(self, **kw) 72 | 73 | def set(self, **kw): 74 | ColorWord.set(self, **kw) 75 | if self._center_at_target: 76 | self._center_target() 77 | if self._target_frame: 78 | self._add_frame() 79 | 80 | def _add_frame(self): 81 | """ Add a rectangle around the target symbol. """ 82 | if self._target_index is not None: 83 | target = self[self._target_index] 84 | pos = target.parameters.position 85 | size = target.parameters.size 86 | frame = Frame(pos, size, line_width=self._target_frame_width) 87 | self.insert(0, frame) 88 | 89 | def _center_target(self): 90 | """ Move the target symbol to the word's position by moving all 91 | symbols by the difference. 92 | """ 93 | if self._target_index is not None: 94 | target = self[self._target_index] 95 | diff = self._position[0] - target.parameters.position[0] 96 | for symbol in self: 97 | pos = symbol.parameters.position 98 | symbol.set(position=(pos[0] + diff, pos[1])) 99 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/sequence_algorithm.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Laura Acqualagna 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | import copy 17 | import random 18 | 19 | class RSVP(object): 20 | def __init__(self, groups): 21 | self.groups = map(list, groups) 22 | self.alphabet = sum(self.groups, []) 23 | 24 | def color_algorithm(self,n_seq): 25 | self.col_trial=[] 26 | n_col = len(self.groups) 27 | len_group=len(self.alphabet)/n_col 28 | i=0 29 | for i in range(0,n_seq): 30 | j=0 31 | for k in range (0,n_col): 32 | random.shuffle(self.groups[k]) 33 | while (j. 13 | 14 | """ 15 | 16 | from .character_sequence import CharacterSequenceTest 17 | from .sequence_algorithm import SequenceAlgorithmTest 18 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/test/character_sequence.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from string import ascii_uppercase 17 | from unittest import TestCase 18 | 19 | from Feedbacks.RSVPSpeller.model.character_sequence import CharacterSequenceFactory 20 | from Feedbacks.RSVPSpeller.model.palette import Palette 21 | 22 | class CharacterSequenceTest(TestCase): 23 | def test_color(self): 24 | symbol_colors = ['red', 'yellow', 'green', 'blue', 'black'] 25 | color_groups = ["ABCDEFGHIJ", "KLMNOPQRST", "UVWXYZ.,:<"] 26 | palette = Palette() 27 | palette.set(symbol_colors, color_groups) 28 | factory = CharacterSequenceFactory('!@#$%^?', True, 'E', palette) 29 | s = factory.sequences(4, [], []) 30 | print [seq.burst_sequence for seq in s] 31 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/RSVPSpeller/util/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/util/error.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | class RSVPSpellerException(Exception): 18 | """ Base class for custom errors. """ 19 | pass 20 | 21 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/util/list.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | def slices(seq, length): 17 | count = len(seq) / length 18 | return [seq[i * length:(i + 1) * length] for i in xrange(count)] 19 | 20 | def remove_all(seq, elements): 21 | for e in elements: 22 | try: 23 | seq.remove(e) 24 | except ValueError: 25 | pass 26 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/util/metadata.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This file is part of the pyff framework. pyff is free software; 4 | you can redistribute it and/or modify it under the terms of the GNU General 5 | Public License version 2, as published by the Free Software Foundation. 6 | 7 | pyff is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10 | details. 11 | 12 | You should have received a copy of the GNU General Public License along with 13 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple 14 | Place, Suite 330, Boston, MA 02111-1307 USA 15 | 16 | """ 17 | 18 | from os import path, pardir 19 | 20 | basedir = path.join(path.dirname(__file__) , pardir) 21 | datadir = path.join(basedir, 'data') 22 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/util/trigger.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | import time 17 | 18 | TRIG_RUN_START = 252 19 | TRIG_RUN_END = 253 20 | TRIG_COUNTDOWN_START = 200 21 | TRIG_COUNTDOWN_END = 201 22 | TRIG_BURST_START = 105 23 | TRIG_BURST_END = 106 24 | TRIG_LETTER = 31 25 | TRIG_TARGET_ADD = 40 26 | TRIG_COUNTED_OFFSET = 150 27 | TRIG_TARGET_ABSENT_OFFSET = 11 28 | TRIG_TARGET_PRESENT_OFFSET = 21 29 | TRIG_EEG = 131 30 | 31 | def add_target_offset_if(value, symbol, target): 32 | if symbol == target: 33 | value += TRIG_TARGET_ADD 34 | return value 35 | 36 | def burst_symbol(symbol, target, base=TRIG_LETTER): 37 | value = base + ord(symbol.lower()) - ord('a') 38 | return add_target_offset_if(value, symbol, target) 39 | 40 | def eeg_symbol(symbol): 41 | return burst_symbol(symbol, None, base=TRIG_EEG) 42 | 43 | class Triggerer(object): 44 | def __init__(self, nonalpha_trigger, trigger, wait=False): 45 | self._nonalpha_trigger = dict(nonalpha_trigger) 46 | self._trigger = trigger 47 | self._wait = wait 48 | self._target = '' 49 | self.symbol('') 50 | 51 | def symbol(self, symbol): 52 | self._symbol = symbol 53 | 54 | def target(self, target): 55 | self._target = target 56 | 57 | def __call__(self): 58 | try: 59 | if self._symbol.isalpha(): 60 | trigger = self._symbol_trigger() 61 | else: 62 | value = self._nonalpha_trigger[self._symbol] 63 | trigger = add_target_offset_if(value, self._symbol, 64 | self._target) 65 | except KeyError: 66 | # redundant symbol 67 | pass 68 | else: 69 | self._trigger(trigger) 70 | if self._wait: 71 | time.sleep(0.02) 72 | 73 | class BurstTriggerer(Triggerer): 74 | def _symbol_trigger(self): 75 | return burst_symbol(self._symbol, self._target) 76 | 77 | class EEGTriggerer(Triggerer): 78 | def __init__(self, nonalpha_trigger, trigger): 79 | Triggerer.__init__(self, nonalpha_trigger, trigger, wait=True) 80 | 81 | def _symbol_trigger(self): 82 | return eeg_symbol(self._symbol) 83 | -------------------------------------------------------------------------------- /src/Feedbacks/RSVPSpeller/view.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | import logging 17 | 18 | from VisionEgg.Core import * 19 | 20 | from lib import marker 21 | from lib.vision_egg import VisionEggView 22 | 23 | from RSVPSpeller.model.target_word import TargetWord 24 | 25 | class View(VisionEggView): 26 | def __init__(self, palette): 27 | self._palette = palette 28 | self.__init_attributes() 29 | VisionEggView.__init__(self) 30 | 31 | def __init_attributes(self): 32 | self._logger = logging.getLogger('View') 33 | self._symbol_duration = 0.05 34 | self._font_size = 150 35 | 36 | def reinit(self): 37 | VisionEggView.reinit(self) 38 | sz = self.screen.size 39 | self._center_text.set_position((sz[0] / 2., sz[1] - self._symbol_vpos)) 40 | 41 | def init(self): 42 | self.__init_text() 43 | 44 | def __init_text(self): 45 | """ Initialize the target word and alphabet texts and adjust 46 | the symbol position. 47 | """ 48 | sz = self.screen.size 49 | self._word = TargetWord(position=(sz[0] / 2., sz[1] - 50 | self._word_vpos), 51 | symbol_size=self._word_font_size, 52 | target_size=self._word_target_font_size, 53 | target_frame=self._show_target_frame, 54 | target_frame_width=self._target_frame_width, 55 | center_at_target=True) 56 | self.add_stimuli(self._word) 57 | if self._show_alphabet: 58 | self._alphabet = self.add_color_word(position=(sz[0] / 2., sz[1] - 59 | self._alphabet_vpos), 60 | font_size= 61 | self._alphabet_font_size) 62 | 63 | def _symbol_color(self, symbol): 64 | return self._palette(symbol) if self._alternating_colors else \ 65 | self._font_color 66 | 67 | def alphabet(self, alphabet): 68 | if self._show_alphabet: 69 | colors = map(self._symbol_color, alphabet) 70 | self._alphabet.set(text=alphabet, colors=colors) 71 | 72 | def word(self, word): 73 | """ Introduce a new word, optionally with colored symbols. """ 74 | self._word.set_all(on=False) 75 | colors = map(self._symbol_color, word) 76 | self.center_word(word, colors) 77 | self.present(self._present_word_time) 78 | self._word.set(text=word, colors=colors) 79 | 80 | def target(self, index): 81 | """ Introduce a new target symbol by increasing its size and 82 | presenting the word in the headline. 83 | """ 84 | self._word.set(target=index) 85 | self._word.set_all(on=True) 86 | self._center_text.set_all(on=False) 87 | self.present(self._present_target_time) 88 | self._center_text.set_all(on=True) 89 | 90 | def eeg_letter(self, text, symbol, update_word=True): 91 | self._trigger(marker.FEEDBACK_START, wait=True) 92 | colors = map(self._symbol_color, text) 93 | self.symbol(symbol, self._symbol_color(symbol)) 94 | self.present(self._present_eeg_input_time) 95 | self._center_text.set_all(on=False) 96 | self._trigger(marker.FEEDBACK_END, wait=True) 97 | if update_word: 98 | self._word.set(text=text, target=len(text)-1, colors=colors) 99 | 100 | def symbol(self, symbol, color=None): 101 | """ Display a single symbol, either in the standard font color 102 | or using the function parameter. 103 | """ 104 | if color is None: 105 | color = self._font_color 106 | self.center_word(symbol, (color,)) 107 | -------------------------------------------------------------------------------- /src/Feedbacks/Stroop/StroopFeedback.py: -------------------------------------------------------------------------------- 1 | 2 | import random 3 | 4 | from FeedbackBase import PygameFeedback 5 | 6 | 7 | def stroop_iterator(classes): 8 | last_elem = classes[0] 9 | while True: 10 | elem = random.choice(classes) 11 | while elem[0] == last_elem[0]: 12 | elem = random.choice(classes) 13 | last_elem = elem 14 | yield elem 15 | 16 | 17 | class StroopFeedback(PygameFeedback.PygameFeedback): 18 | 19 | def init(self): 20 | PygameFeedback.PygameFeedback.init(self) 21 | 22 | # all possible colors 23 | colors = ["red", "blue", "green", "brown", "purple"] 24 | self.colors = {"red" : [255,0,0], 25 | "green" : [0, 191, 0], 26 | "blue" : [0,0,255], 27 | "brown" : [150, 75, 0], 28 | "purple" : [128, 0, 128]} 29 | 30 | # all possible combinations of [color1, color2] where color1 != color2 31 | # color1 is the printed word, color2 the actual color 32 | classes = [[i,j] for i in colors for j in colors if i != j] 33 | 34 | self.si = stroop_iterator(classes) 35 | self.timer = 0 36 | self.current = self.si.next() 37 | 38 | 39 | def play_tick(self): 40 | self.do_print(self.current[0], self.colors[self.current[1]], 200, superimpose=False) 41 | self.timer += self.elapsed 42 | if self.timer > 1000: 43 | self.timer = 0 44 | self.current = self.si.next() 45 | 46 | 47 | def pause_tick(self): 48 | self.do_print("pause", (128,128,128), 100) 49 | 50 | 51 | if __name__ == "__main__": 52 | fb = StroopFeedback() 53 | fb.on_init() 54 | fb.on_play() 55 | 56 | -------------------------------------------------------------------------------- /src/Feedbacks/Stroop/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Stroop/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Stroop/feedbacks.list: -------------------------------------------------------------------------------- 1 | StroopFeedback.StroopFeedback 2 | -------------------------------------------------------------------------------- /src/Feedbacks/TestD2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/TestD2/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/TestD2/feedbacks.list: -------------------------------------------------------------------------------- 1 | TestD2.TestD2 2 | -------------------------------------------------------------------------------- /src/Feedbacks/TobiQLAdapter/TobiQLAdapter.py: -------------------------------------------------------------------------------- 1 | 2 | from xml.dom.minidom import Document 3 | import socket 4 | import time 5 | 6 | from FeedbackBase.MainloopFeedback import MainloopFeedback 7 | 8 | LEFT = -1. 9 | RIGHT = 1. 10 | NONE = 0. 11 | 12 | class TobiQLAdapter(MainloopFeedback): 13 | 14 | def init(self): 15 | self.server_address = ("127.0.0.1", 10001) 16 | self.left_signal = "l" 17 | self.right_signal = "r" 18 | 19 | # control_signal = bias + gain * control_signal 20 | self.gain = 1.0 21 | self.bias = 0.0 22 | 23 | # Last selected, value 24 | self.last = NONE 25 | 26 | # current control signal 27 | self.cs = 0.0 28 | 29 | 30 | def play_tick(self): 31 | time.sleep(0.5) 32 | if self.cs <= LEFT and (self.last == RIGHT or self.last == NONE): 33 | self.send_signal(self.left_signal) 34 | self.last = LEFT 35 | elif self.cs >= RIGHT and (self.last == LEFT or self.last == NONE): 36 | self.send_signal(self.right_signal) 37 | self.last = RIGHT 38 | print "%i \t %f\r" % (self.last, self.cs) 39 | 40 | 41 | def on_control_event(self, data): 42 | cs = data["cl_output"] 43 | self.cs = self.bias + self.gain * cs 44 | 45 | 46 | # The stuff below is taken from the Tobi API client example 47 | def send_signal(self, signal): 48 | """Sends the signal to the server, I guess.""" 49 | try: 50 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 51 | server.connect(self.get_server_address()) 52 | except socket.error, msg: 53 | self.logger.critical("Error connecting to server (%s)" % str(msg)) 54 | return 55 | 56 | request = self.build_request(signal) 57 | server.send(request) 58 | 59 | data = server.recv(1024) 60 | string = "" 61 | while len(data): 62 | string = string + data 63 | data = server.recv(1024) 64 | server.close() 65 | self.logger.debug("Server replied with: %s" % str(string)) 66 | 67 | 68 | def build_request(self, signal): 69 | """Builds the XML message from the signal.""" 70 | doc = Document() 71 | e1 = doc.createElement("TOBI_communication"); 72 | e2 = doc.createElement("signal") 73 | a = doc.createAttribute("Type") 74 | a.value = str(signal) 75 | e2.attributes[a.name] = a.value 76 | e1.appendChild(e2) 77 | doc.appendChild(e1) 78 | return doc.toxml() 79 | 80 | 81 | def parse_response(self, response): 82 | """Parses the response or throws an exception.""" 83 | pass 84 | 85 | 86 | def get_server_address(self): 87 | """Reads the configuration file and returns the IPendPoint object or 88 | throws an exception.""" 89 | return self.server_address 90 | -------------------------------------------------------------------------------- /src/Feedbacks/TobiQLAdapter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/TobiQLAdapter/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/TobiQLAdapter/feedbacks.list: -------------------------------------------------------------------------------- 1 | TobiQLAdapter.TobiQLAdapter 2 | -------------------------------------------------------------------------------- /src/Feedbacks/TrivialPong/TrivialPong.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # TrivialPong.py - 4 | # Copyright (C) 2007-2009 Bastian Venthur 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | """Trivial Pong BCI Feedback.""" 21 | 22 | 23 | import os 24 | 25 | import pygame 26 | 27 | from FeedbackBase.PygameFeedback import PygameFeedback 28 | 29 | 30 | class TrivialPong(PygameFeedback): 31 | 32 | def on_control_event(self, data): 33 | self.val = data["clout"] 34 | 35 | def init(self): 36 | PygameFeedback.init(self) 37 | self.caption = "Trivial Pong" 38 | # set the initial speeds for ball and bar 39 | self.barspeed = [3, 0] 40 | self.speed = [2, 2] 41 | # set initial value for cl output 42 | self.val = 0.0 43 | 44 | def init_graphics(self): 45 | # load graphics 46 | path = os.path.dirname( globals()["__file__"] ) 47 | self.ball = pygame.image.load(os.path.join(path, "ball.png")) 48 | self.ballrect = self.ball.get_rect() 49 | self.bar = pygame.image.load(os.path.join(path, "bar.png")) 50 | self.barrect = self.bar.get_rect() 51 | 52 | def play_tick(self): 53 | width, height = self.screenSize 54 | w_half = width / 2. 55 | # move bar and ball 56 | pos = w_half + w_half * self.val 57 | self.barrect.center = pos, height - 20 58 | self.ballrect = self.ballrect.move(self.speed) 59 | # collision detection walls 60 | if self.ballrect.left < 0 or self.ballrect.right > width: 61 | self.speed[0] = -self.speed[0] 62 | if self.ballrect.top < 0 or self.ballrect.bottom > height: 63 | self.speed[1] = -self.speed[1] 64 | if self.barrect.left < 0 or self.barrect.right > width: 65 | self.barspeed[0] = -self.barspeed[0] 66 | if self.barrect.top < 0 or self.barrect.bottom > height: 67 | self.barspeed[1] = -self.barspeed[1] 68 | # collision detection for bar vs ball 69 | if self.barrect.colliderect(self.ballrect): 70 | self.speed[0] = -self.speed[0] 71 | self.speed[1] = -self.speed[1] 72 | # update the screen 73 | self.screen.fill(self.backgroundColor) 74 | self.screen.blit(self.ball, self.ballrect) 75 | self.screen.blit(self.bar, self.barrect) 76 | pygame.display.flip() 77 | 78 | 79 | if __name__ == "__main__": 80 | fb = TrivialPong() 81 | fb.on_init() 82 | fb.on_play() 83 | -------------------------------------------------------------------------------- /src/Feedbacks/TrivialPong/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/TrivialPong/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/TrivialPong/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/TrivialPong/ball.png -------------------------------------------------------------------------------- /src/Feedbacks/TrivialPong/bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/TrivialPong/bar.png -------------------------------------------------------------------------------- /src/Feedbacks/TrivialPong/feedbacks.list: -------------------------------------------------------------------------------- 1 | TrivialPong.TrivialPong 2 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson01.py: -------------------------------------------------------------------------------- 1 | # Lesson01 - Trivial Feedback without functionality 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | 20 | from FeedbackBase.Feedback import Feedback 21 | 22 | class Lesson01(Feedback): 23 | 24 | def on_init(self): 25 | print "Feedback successfully loaded." 26 | 27 | def on_quit(self): 28 | print "Feedback quit." 29 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson01b.py: -------------------------------------------------------------------------------- 1 | # Lesson01b - Trivial Feedback without functionality - alternative with __init__ overwritten 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from FeedbackBase.Feedback import Feedback 20 | 21 | class Lesson01b(Feedback): 22 | 23 | def __init__(self, pp): 24 | Feedback.__init__(self, pp) 25 | # Your own stuff goes here 26 | 27 | def on_init(self): 28 | print "Feedback successfully loaded." 29 | 30 | def on_quit(self): 31 | print "Feedback quit." 32 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson02.py: -------------------------------------------------------------------------------- 1 | # Lesson02 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | # - Starting a main loop in a thread when the feedback gets the start signal 20 | # - Pausing and unpausing it 21 | # - Quitting the main loop 22 | 23 | 24 | import time 25 | 26 | from FeedbackBase.Feedback import Feedback 27 | 28 | 29 | class Lesson02(Feedback): 30 | 31 | def on_init(self): 32 | print "Feedback successfully loaded." 33 | self.quitting, self.quit = False, False 34 | self.pause = False 35 | 36 | def on_quit(self): 37 | self.quitting = True 38 | # Make sure we don't return on_quit until the main_loop (which runs in 39 | # a different thread!) quit. 40 | print "Waiting for main loop to quit." 41 | while not self.quit: 42 | pass 43 | # Now the main loop quit and we can safely return 44 | 45 | def on_play(self): 46 | # Start the main loop. Note that on_play runs in a different thread than 47 | # all the other Feedback methods, and so does the main loop. 48 | self.quitting, self.quit = False, False 49 | self.main_loop() 50 | 51 | def on_pause(self): 52 | self.pause = not self.pause 53 | 54 | def main_loop(self): 55 | i = 0 56 | while 1: 57 | time.sleep(0.5) 58 | if self.pause: 59 | print "Feedback Paused." 60 | continue 61 | elif self.quitting: 62 | print "Leaving main loop." 63 | break 64 | i = i+1 65 | print "Iteration Number %i" % i 66 | 67 | print "Left main loop." 68 | self.quit = True 69 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson03.py: -------------------------------------------------------------------------------- 1 | # Lesson03 - Working with data send by control- and interaction signals 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from FeedbackBase.Feedback import Feedback 20 | 21 | import time 22 | 23 | class Lesson03(Feedback): 24 | 25 | def on_init(self): 26 | print "Feedback successfully loaded." 27 | self.quitting, self.quit = False, False 28 | self.pause = False 29 | 30 | def on_quit(self): 31 | self.quitting = True 32 | # Make sure we don't return on_quit until the main_loop (which runs in 33 | # a different thread!) quit. 34 | print "Waiting for main loop to quit." 35 | while not self.quit: 36 | pass 37 | # Now the main loop quit and we can safely return 38 | 39 | def on_play(self): 40 | # Start the main loop. Note that on_play runs in a different thread than 41 | # all the other Feedback methods, and so does the main loop. 42 | self.quitting, self.quit = False, False 43 | self.main_loop() 44 | 45 | def on_pause(self): 46 | self.pause = not self.pause 47 | 48 | def main_loop(self): 49 | i = 0 50 | while 1: 51 | time.sleep(0.5) 52 | if self.pause: 53 | print "Feedback Paused." 54 | continue 55 | elif self.quitting: 56 | print "Leaving main loop." 57 | break 58 | print self._data 59 | 60 | print "Left main loop." 61 | self.quit = True -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson04.py: -------------------------------------------------------------------------------- 1 | # Lesson04 - Reacting on control- and interaction events 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from FeedbackBase.Feedback import Feedback 20 | 21 | class Lesson04(Feedback): 22 | 23 | def on_init(self): 24 | self.myVariable = None 25 | self.eegTuple = None 26 | 27 | def on_interaction_event(self, data): 28 | # this one is equivalent to: 29 | # self.myVariable = self._someVariable 30 | self.myVariable = data.get("someVariable") 31 | print self.myVariable 32 | 33 | def on_control_event(self, data): 34 | # this one is equivalent to: 35 | # self.eegTuple = self._data 36 | self.eegTuple = data 37 | print self.eegTuple 38 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson05.py: -------------------------------------------------------------------------------- 1 | # Lesson05 - Sending Markers 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from FeedbackBase.Feedback import Feedback 20 | 21 | class Lesson05(Feedback): 22 | 23 | def on_init(self): 24 | self.send_parallel(0x1) 25 | 26 | def on_play(self): 27 | self.send_parallel(0x2) 28 | 29 | def on_pause(self): 30 | self.send_parallel(0x4) 31 | 32 | def on_quit(self): 33 | self.send_parallel(0x8) 34 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/Lesson06.py: -------------------------------------------------------------------------------- 1 | # Lesson06 - Logging 2 | # Copyright (C) 2007-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | from FeedbackBase.Feedback import Feedback 20 | 21 | class Lesson06(Feedback): 22 | 23 | def on_init(self): 24 | self.logger.debug("Feedback successfully loaded.") 25 | 26 | def on_quit(self): 27 | self.logger.debug("Feedback quit.") 28 | -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Tutorial/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Tutorial/feedbacks.list: -------------------------------------------------------------------------------- 1 | Lesson01.Lesson01 2 | Lesson01b.Lesson01b 3 | Lesson02.Lesson02 4 | Lesson03.Lesson03 5 | Lesson04.Lesson04 6 | Lesson05.Lesson05 7 | Lesson06.Lesson06 8 | 9 | -------------------------------------------------------------------------------- /src/Feedbacks/Vigilance/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Vigilance/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/Vigilance/feedbacks.list: -------------------------------------------------------------------------------- 1 | BoringClock.BoringClock 2 | -------------------------------------------------------------------------------- /src/Feedbacks/Vigilance/sound18.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/Vigilance/sound18.wav -------------------------------------------------------------------------------- /src/Feedbacks/VisualSpeller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/VisualSpeller/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/VisualSpeller/feedbacks.list: -------------------------------------------------------------------------------- 1 | ERPHex.ERPHex 2 | ERPMatrix.ERPMatrix 3 | 4 | -------------------------------------------------------------------------------- /src/Feedbacks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/nback/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/nback/__init__.py -------------------------------------------------------------------------------- /src/Feedbacks/nback/feedbacks.list: -------------------------------------------------------------------------------- 1 | nback_verbal.nback_verbal 2 | -------------------------------------------------------------------------------- /src/Feedbacks/nback/sound18.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/Feedbacks/nback/sound18.wav -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | DIRS = gui 2 | 3 | all: 4 | for i in $(DIRS); do \ 5 | $(MAKE) all -C $$i; \ 6 | done 7 | 8 | clean: 9 | for i in $(DIRS); do \ 10 | $(MAKE) clean -C $$i; \ 11 | done 12 | rm -f *.pyc 13 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/__init__.py -------------------------------------------------------------------------------- /src/emulator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # emulator.py - a simple BCI system emulator 4 | # Copyright (C) 2007-2011 Bastian Venthur 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | """A BCI-system emulator.""" 21 | 22 | 23 | import cmd 24 | import threading 25 | import math 26 | import time 27 | 28 | from lib import bcinetwork 29 | from lib.bcinetwork import BciNetwork 30 | from lib import bcixml 31 | from lib.bcixml import BciSignal 32 | 33 | 34 | class Emulator(cmd.Cmd): 35 | 36 | prompt = "> " 37 | intro = "Welcome to the BCI emulator. Type help to see a list of available commands." 38 | 39 | def __init__(self): 40 | cmd.Cmd.__init__(self) 41 | self.stopping = True 42 | 43 | def do_quit(self, line): 44 | """Quit the Emulator.""" 45 | return True 46 | 47 | def postloop(self): 48 | print 49 | 50 | def do_generate_cs(self, line): 51 | """Generates a control signal and sends it to the Feedback Controller.""" 52 | self._do_generate_cs(line, 1) 53 | 54 | def do_generate_cs2(self, line): 55 | """Generates a control signal and sends it to the Feedback Controller.""" 56 | self._do_generate_cs(line, 2) 57 | 58 | def do_generate_cs3(self, line): 59 | """Generates a control signal and sends it to the Feedback Controller.""" 60 | self._do_generate_cs(line, 3) 61 | 62 | def do_generate_cs4(self, line): 63 | """Generates a control signal and sends it to the Feedback Controller.""" 64 | self._do_generate_cs(line, 4) 65 | 66 | def do_generate_cs5(self, line): 67 | """Generates a control signal and sends it to the Feedback Controller.""" 68 | self._do_generate_cs(line, 5) 69 | 70 | def do_generate_cs6(self, line): 71 | """Generates a control signal and sends it to the Feedback Controller.""" 72 | self._do_generate_cs(line, 6) 73 | 74 | 75 | def _do_generate_cs(self, line, numbers): 76 | self.net = BciNetwork("localhost", bcinetwork.FC_PORT) 77 | self.signal = BciSignal(None, None, bcixml.CONTROL_SIGNAL) 78 | self.stopping = False 79 | self.t = threading.Thread(target=self._cs_loop, args=(numbers,)) 80 | self.t.start() 81 | print "Enter: stop_cs to stop the signal." 82 | 83 | 84 | def _cs_loop(self, numbers=1): 85 | c = 0 86 | while not self.stopping: 87 | time.sleep(0.04) 88 | c += 1 89 | r = math.radians(c) 90 | sample1 = math.sin(r) 91 | sample2 = math.sin(r/2-90.0) 92 | sample3 = math.sin(r/3-180.0) 93 | sample4 = math.sin(r-270.0) 94 | sample5 = math.sin(r-45.0) 95 | sample6 = math.sin(r-135.0) 96 | 97 | samples = [sample1, sample2, sample3, sample4, sample5, sample6] 98 | 99 | #self.signal.data = {"data" : [sample1, sample2, sample3]} 100 | if numbers == 1: 101 | self.signal.data = {"cl_output" : samples[0]} 102 | elif numbers <= 6: 103 | self.signal.data = {"cl_output" : samples[:numbers]} 104 | else: 105 | print "Error don't know how to handle %i numbers." % numbers 106 | 107 | self.net.send_signal(self.signal) 108 | 109 | def do_stop_cs(self, line): 110 | """Stops the loop which sends data to the Feedback Controller.""" 111 | self.stopping = True 112 | self.t.join() 113 | 114 | def do_status(self, line): 115 | """Get the status of the emulator.""" 116 | for var, val in self.__dict__.iteritems(): 117 | print str(var), str(val) 118 | 119 | 120 | 121 | if __name__ == "__main__": 122 | Emulator().cmdloop() 123 | -------------------------------------------------------------------------------- /src/external/RecorderRemoteControl/DCOM-recorder-config.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/external/RecorderRemoteControl/DCOM-recorder-config.txt -------------------------------------------------------------------------------- /src/external/RecorderRemoteControl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/external/RecorderRemoteControl/__init__.py -------------------------------------------------------------------------------- /src/external/RecorderRemoteControl/bvr_matlab_python.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | 4 | 5 | from RecorderRemoteControl import * 6 | 7 | if (len(sys.argv) > 1): 8 | fcn = sys.argv[1] 9 | if(fcn == 'loadworkspace'): 10 | if(len(sys.argv) > 2): 11 | stopViewing() 12 | loadWorkspace(sys.argv[2]) 13 | else: 14 | print 'Couldn''t load workspace: not enough arguments' 15 | print 'You have to specify a filename.' 16 | sys.exit(1) 17 | elif(fcn == 'startrecording'): 18 | if(len(sys.argv) > 2): 19 | viewData() 20 | startRecording(sys.argv[2]) 21 | else: 22 | print 'Couldn''t start Recording: not enough arguments' 23 | print 'You have to specify a filename.' 24 | sys.exit(1) 25 | elif(fcn == 'startimprecording'): 26 | if(len(sys.argv) > 2): 27 | viewImpedance() 28 | time.sleep(10) 29 | viewData() 30 | startRecording(sys.argv[2]) 31 | else: 32 | print 'Couldn''t start impedance recording: not enough arguments' 33 | print 'You have to specify a filename.' 34 | sys.exit(1) 35 | 36 | elif(fcn == 'stoprecording'): 37 | stopRecording() 38 | elif(fcn == 'viewsignals'): 39 | viewData() 40 | elif(fcn == 'viewsignalsandwait'): 41 | waitTime = 2 42 | if(len(sys.argv) > 2): 43 | waitTime = float(sys.argv[2]) / 1000 44 | 45 | if(getState() != 1): 46 | viewData() 47 | time.sleep(waitTime) 48 | elif(fcn == 'checkimpedances'): 49 | viewImpedance() 50 | elif(fcn == 'pauserecording'): 51 | pauseRecording() 52 | elif(fcn == 'resumerecording'): 53 | continueRecording() 54 | elif(fcn == 'getstate'): 55 | print getState() 56 | else: 57 | print 'Warning: The command ' + fcn + ' is not supported' 58 | sys.exit(1) 59 | else: 60 | print 'No command specified' 61 | sys.exit(1) 62 | -------------------------------------------------------------------------------- /src/external/RecorderRemoteControl/bvr_sendcommand_python.m: -------------------------------------------------------------------------------- 1 | function r = bvr_sendcommand_python(fcn, varargin) 2 | %BVR_SENDCOMMAND - Control the BrainVision Recorder software 3 | % 4 | %Synopsis: 5 | % bvr_sendcommand(FCN, ) 6 | % 7 | % state = bvr_sendcommand('getstate') 8 | % 9 | %Arguements: 10 | % FCN: Name of the function to be executed. See in the bvr_*.vbs files in 11 | % acquistion_tools folder for a list of options. Here are some: 12 | % %'loadworkspace' - load BV workspace into the recorder; ARG: name of 13 | % the workspace (extension '.rwsp' may be left out) 14 | % 'startrecording' - Start EEG recording; ARG: name of the file with 15 | % full path, without extension. 16 | % 'startimprecording' - Make Impedance measurement first and start 17 | % recording afterward (impedance values are saved into the EEG 18 | % file); ARG as above. 19 | % 'stoprecording' - Stops the recording. 20 | % 'viewsignals' - Switch to monitoring state 21 | % 'viewsignalsandwait' - Switch to monitoring mode and wait (unless monitoring 22 | % mode is already active). Example: bvr_sendcommand('viewsignalsandwait','3000'); 23 | % 'checkimpedances' - Swithc to impedance check 24 | % 'pauserecording' 25 | % 'resumerecording' 26 | % 'getstate' - Returns the operational state of the brain vision recorder 27 | % The following part was copied from RecorderRemoteControl.py 28 | %""" The return value will describe the following state: 29 | % 0 - nothing 30 | % 1 - view data 31 | % 2 - view test signal 32 | % 3 - view impedance 33 | % 4 - record data 34 | % 5 - record test data 35 | % 6 - pause record data 36 | % 7 - pause record test data 37 | % 38 | % the list may be incomplete""" 39 | % 40 | % AUTHOR 41 | % Max Sagebaum 42 | % 43 | % 2008/09/08 - Max Sagebaum 44 | % - file created 45 | % (c) 2005 Fraunhofer FIRST 46 | 47 | % blanker@cs.tu-berlin.de, Jul-2007 48 | 49 | global BCI_DIR 50 | 51 | [status, result] = system([BCI_DIR 'acquisition/tools/bvr_matlab_python.py' sprintf(' %s %s',fcn,varargin{:})]); 52 | if (status == 0) 53 | if(strcmp(fcn,'getstate')) 54 | r = str2double(result); 55 | end 56 | else 57 | disp(result) 58 | end 59 | 60 | -------------------------------------------------------------------------------- /src/external/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/external/__init__.py -------------------------------------------------------------------------------- /src/gui/Makefile: -------------------------------------------------------------------------------- 1 | PYUIC = pyuic4 2 | RCC = rcc 3 | PYRCC = pyrcc4 4 | 5 | GUI_CLASSES = gui.py 6 | RCCS = icons.rcc 7 | PYRCCS = icons_rc.py 8 | 9 | all: $(GUI_CLASSES) $(RCCS) $(PYRCCS) 10 | 11 | %.rcc: %.qrc 12 | $(RCC) $< -o $@ 13 | 14 | %_rc.py: %.qrc 15 | $(PYRCC) $< -o $@ 16 | 17 | %.py: %.ui 18 | $(PYUIC) $< -o $@ 19 | 20 | clean: 21 | rm -f $(GUI_CLASSES) $(RCCS) $(PYRCCS) 22 | rm -f *.pyc 23 | -------------------------------------------------------------------------------- /src/gui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/__init__.py -------------------------------------------------------------------------------- /src/gui/icons.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/PyffLogoNeu.png 4 | icons/clear.png 5 | icons/connect.png 6 | icons/exit.png 7 | icons/get.png 8 | icons/open.png 9 | icons/pause.png 10 | icons/play.png 11 | icons/quit.png 12 | icons/saveas.png 13 | icons/save.png 14 | icons/sendinit.png 15 | icons/sendall.png 16 | icons/sendmodified.png 17 | icons/stop.png 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/gui/icons/PyffLogoNeu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/PyffLogoNeu.png -------------------------------------------------------------------------------- /src/gui/icons/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/clear.png -------------------------------------------------------------------------------- /src/gui/icons/connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/connect.png -------------------------------------------------------------------------------- /src/gui/icons/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/exit.png -------------------------------------------------------------------------------- /src/gui/icons/get.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/get.png -------------------------------------------------------------------------------- /src/gui/icons/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/open.png -------------------------------------------------------------------------------- /src/gui/icons/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/pause.png -------------------------------------------------------------------------------- /src/gui/icons/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/play.png -------------------------------------------------------------------------------- /src/gui/icons/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/quit.png -------------------------------------------------------------------------------- /src/gui/icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/save.png -------------------------------------------------------------------------------- /src/gui/icons/saveas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/saveas.png -------------------------------------------------------------------------------- /src/gui/icons/sendall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/sendall.png -------------------------------------------------------------------------------- /src/gui/icons/sendinit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/sendinit.png -------------------------------------------------------------------------------- /src/gui/icons/sendmodified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/sendmodified.png -------------------------------------------------------------------------------- /src/gui/icons/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/gui/icons/stop.png -------------------------------------------------------------------------------- /src/lib/CEtAPI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/CEtAPI.dll -------------------------------------------------------------------------------- /src/lib/CEtAPI.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/CEtAPI.h -------------------------------------------------------------------------------- /src/lib/ExperimentalDesign/OrthogonalDesign.py: -------------------------------------------------------------------------------- 1 | # OrthogonalDesign.py - 2 | # Copyright (C) 2010 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Given some factors and the total number of trials, orthogonal design builds 20 | a list of length nTrials wherein all factor combinations appear with the same 21 | frequency. 22 | 23 | Example: 24 | OrthogonalDesign([(1,2),(3,4)],nTrials=8) 25 | specifies a 2x2 design. The output is [(1,3),(1,4),(2,3),(2,4),(1,3),(1,4),(2,3),(2,4)]. 26 | """ 27 | 28 | 29 | def orthogonalDesign(factors,nTrials,buildup=[],trials=[]): 30 | 31 | # recursion exit strategy 32 | if factors==[]: 33 | trials.append(buildup) 34 | return 35 | # Start recursion 36 | if nTrials is not None: 37 | trials = [] 38 | nSubconditions = 1 39 | for ii in range(len(factors)): 40 | nSubconditions *= len(factors[ii]) 41 | for ii in range(nTrials/nSubconditions): 42 | orthogonalDesign(factors,None,[],trials) 43 | return trials 44 | else: 45 | ff = factors[0] 46 | for ii in range(len(ff)): 47 | orthogonalDesign(factors[1:],None,buildup+[ff[ii]],trials) 48 | 49 | -------------------------------------------------------------------------------- /src/lib/ExperimentalDesign/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/ExperimentalDesign/__init__.py -------------------------------------------------------------------------------- /src/lib/P300Aux/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/P300Aux/__init__.py -------------------------------------------------------------------------------- /src/lib/P300Layout/CircularLayout.py: -------------------------------------------------------------------------------- 1 | # CircularLayout.py 2 | # Copyright (C) 2009 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Implements a circular layout. All elements are placed on 20 | a circle with a given radius in pixels. The first element 21 | is placed on the top. The next elements are placed in 22 | clockwise fashion. 23 | 24 | Provide the number of elements (nr_elements) and the radius 25 | (radius) when creating an intance of this layout. You can 26 | also provide the angular position of the first element (start) 27 | if you do not want it to be placed on the top. 28 | """ 29 | 30 | import math 31 | 32 | class CircularLayout(object): 33 | 34 | def __init__(self, nr_elements=20, radius=200, start= - math.pi / 2): 35 | self.positions = [] 36 | step = 2 * math.pi / nr_elements 37 | for i in range(nr_elements): 38 | phi = start + i * step 39 | x = round (radius * math.cos(phi)) 40 | y = round (radius * math.sin(phi)) 41 | self.positions.append((x, y)) 42 | -------------------------------------------------------------------------------- /src/lib/P300Layout/MatrixLayout.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 Matthias Treder 2 | # 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 2 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License along 14 | # with this program; if not, write to the Free Software Foundation, Inc., 15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 | 17 | """ 18 | Implements a matrix layout. A matrix is defined by its 19 | size (width,height) and the number of rows and columns. 20 | The positions are defined row-wise, starting in the top 21 | left corner. 22 | """ 23 | 24 | class MatrixLayout(object): 25 | 26 | def __init__(self, size=(200, 200), rows=6, cols=6): 27 | self.positions = [] 28 | self.rows = rows 29 | self.cols = cols 30 | width, height = size 31 | # Determine positions 32 | distx = width / (cols - 1) # x distance between elements 33 | disty = height / (rows - 1) # y distance between elements 34 | 35 | for r in range(rows): 36 | for c in range(cols): 37 | x = round(distx * c - width / 2) 38 | y = round(disty * r - height / 2) 39 | self.positions.append((x, y)) 40 | 41 | def get_rows_cols(self): 42 | """ 43 | Just a handy method. 44 | It returns a list of lists. Each (sub)list contains the indices 45 | of the elements in one single row or column. The order in which 46 | the rows/columns are given is: First, the rows are given, starting 47 | at the top; then, the columns are given, starting left. 48 | You can call this method from your VisualP300 speller implementation 49 | to define your groups very easily. 50 | """ 51 | rows_cols = [] 52 | # Get rows 53 | for r in range(self.rows): 54 | rows_cols.append(range(r * self.cols, (r + 1) * self.cols)) 55 | # Get columns 56 | for c in range(self.cols): 57 | column = [] 58 | for r in range(self.rows): 59 | column.append(c + r * self.cols) 60 | rows_cols.append(column) 61 | return rows_cols 62 | 63 | 64 | 65 | # Test 66 | #m = MatrixLayout(rows=4,cols=4) 67 | #for i in range( len(m.positions) ): 68 | # print m.positions[i] 69 | #print m.get_rows_cols() -------------------------------------------------------------------------------- /src/lib/P300Layout/README.TXT: -------------------------------------------------------------------------------- 1 | The P300Layout folder contains a number of classes specifying a 2 | particular spatial layout (i.e., screen coordinates) of the elements 3 | in your visual P300 speller. 4 | 5 | Some standard layouts (e.g., matrix, circular) are provided here, but 6 | you can easily make your own layout by defining a member variable 7 | termed 'positions', which is just a list of xy-tuples. Each tuple 8 | represents the screen coordinates of a single element. 9 | 10 | If you define your own layout, please keep in mind that the center of 11 | your layout has to be (0,0). Later on, your speller is centered on 12 | the screen by your VisualP300 instance. -------------------------------------------------------------------------------- /src/lib/P300Layout/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/P300Layout/__init__.py -------------------------------------------------------------------------------- /src/lib/P300VisualElement/Image.py: -------------------------------------------------------------------------------- 1 | # Image.py 2 | # Copyright (C) 2009 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Loads one or more images from a file as for use in the P300 speller. 20 | P300 Image objects have a number of visual features: 21 | * file - give path to the file 22 | * fliplr - flip image horizontally 23 | * flipud - flip image vertically 24 | * scale - provide a tupel (new_width,new_height) for smoothscaling the element 25 | 26 | Please always provide your path using forward slahes. 27 | 28 | The features can be used to specify different states. If some of 29 | these features are constant (eg, the file is used in all 30 | states), you should provide a common value for this feature when you 31 | instantiate a P300Text. 32 | 33 | """ 34 | 35 | import os.path 36 | 37 | import pygame 38 | 39 | from VisualElement import VisualElement 40 | 41 | 42 | class Image(VisualElement): 43 | 44 | DEFAULT_FILE = None 45 | DEFAULT_FLIPLR = False 46 | DEFAULT_FLIPUD = False 47 | DEFAULT_SCALE = None 48 | 49 | 50 | def __init__(self, nr_states=2, pos=(0, 0), file=DEFAULT_FILE, fliplr=DEFAULT_FLIPLR, flipud=DEFAULT_FLIPUD, scale=DEFAULT_SCALE): 51 | VisualElement.__init__(self, nr_states, pos) 52 | self.file = os.path.normpath(file) 53 | self.fliplr = self.DEFAULT_FLIPLR 54 | self.flipud = self.DEFAULT_FLIPUD 55 | self.scale = self.DEFAULT_SCALE 56 | self.image = self.load_image(self.file) 57 | self.rect = self.image.get_rect() 58 | 59 | def refresh(self): 60 | # For each state, generate image and rect 61 | for i in range(self.nr_states): 62 | if self.states[i].has_key("file"): file = self.states[i]["file"] 63 | else: file = self.file # Take standard value 64 | if self.states[i].has_key("fliplr"): fliplr = self.states[i]["fliplr"] 65 | else: fliplr = self.fliplr # Take standard value 66 | if self.states[i].has_key("flipud"): flipud = self.states[i]["flipud"] 67 | else: flipud = self.flipud # Take standard value 68 | if self.states[i].has_key("scale"): scale = self.states[i]["scale"] 69 | else: scale = self.scale # Take standard value 70 | image = self.load_image(file) 71 | if fliplr or flipud: 72 | image = pygame.transform.flip(image, fliplr, flipud) 73 | if scale is not None: 74 | image = pygame.transform.smoothscale(image, scale) 75 | image = image.convert() 76 | self.images[i] = image 77 | self.rects[i] = self.images[i].get_rect(center=self.pos) 78 | 79 | def load_image(self, file): 80 | image = None 81 | # try: 82 | image = pygame.image.load_basic(file) 83 | # except pygame.error, message: 84 | # print 'Cannot load image:', file 85 | #image = image.convert() 86 | return image 87 | 88 | -------------------------------------------------------------------------------- /src/lib/P300VisualElement/README.TXT: -------------------------------------------------------------------------------- 1 | The P300VisualElement folder contains a number of classes specifying a 2 | particular visual element (such as a letter or a bitmap) that can be used 3 | in your P300 speller. 4 | 5 | The standard elements provided here should be fine for most applications. 6 | If you want to define your own element, though, use the suggestions provided 7 | below. 8 | 9 | All elements are derived from the base class VisualElement. This class 10 | implements the basic functionality for any kind of element that is used in a 11 | P300 paradigm. If you define your own subclass, it should inherit from 12 | VisualElement. 13 | Note that each instance of VisualElement (or one of its subclasses) represents 14 | exactly one element (eg, the letter "A"). If your P300 speller consists of a 15 | 6x6 matrix of elements, you will thus need 36 instances. 16 | 17 | VisualElement uses the pygame engine. It is derived from the pygame class Sprite. 18 | A Sprite is a very versatile graphical object that has an image property (self.image) 19 | specifying how it looks like and a rect property (self.rect) specifying its size 20 | and position. 21 | 22 | Starting from this, VisualElement introduces the concept of a STATE. In 23 | each P300 paradigm, the elements change from one state into another when they 24 | are highlighted. For instance, in the classical P300 paradigm, elements consist 25 | of letters and each letter can take one of two different states. Usually, the 26 | letter is in state 0, which means that it is not flashed and has a dark 27 | color. When it is flashed, the letter turns to state 1 for the duration of the 28 | flash. 29 | 30 | The VisualElement class provides a generalization of this principle. It allows 31 | to use 32 | 33 | * any kind of element (not only letters) 34 | * any number and combination of features (not only color, but also size, orientation etc) 35 | * any number of states (in particular, more than the two classical states) 36 | 37 | ### For each state, a separate image and rect is provided. If the state 38 | is changed, self.image and and self.rect are simply set to the values of 39 | the corresponding state. 40 | 41 | 42 | How to use a subclass in your P300 speller 43 | ------------------------------------------ 44 | Most functionality is automatized. In fact, all you need to do is to before using 45 | an element is to specify the states. 46 | 47 | To USE these elements in your own P300 speller, the steps are as follows: 48 | 49 | * Make a new instance of your visual element. 50 | Specify the position of the center of the element and some basic features 51 | that are not changed during the flashes, for instance: 52 | my_letter = P300Text(text="A",size=10,pos=(100,100)) 53 | * Specify the distinct states by using set_states, for instance: 54 | Read the description of the function set_states in the 55 | VisualElement class for more details 56 | 57 | How to define your own subclass 58 | ------------------------------- 59 | If you define your own subclass, you can best start off by copying one of the 60 | existing classes. Then you have to 61 | * Override the __init__ method, provided standard values and setting your 62 | own member variables 63 | * Override the refresh method wherein you specify the graphical object according 64 | to specific features 65 | If you finished your new class, run TestIt.py to test it ;) 66 | -------------------------------------------------------------------------------- /src/lib/P300VisualElement/TestIt.py: -------------------------------------------------------------------------------- 1 | # TestIt.py 2 | # Copyright (C) 2009 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Use TestIt to test your new visual elements. 20 | Test it opens a test screen, displays the element and goes through 21 | its states. 22 | """ 23 | 24 | 25 | import sys 26 | 27 | import pygame 28 | 29 | import Textrow 30 | 31 | 32 | bgcolor = 0, 0, 0 33 | screenSize = 500, 500 34 | 35 | """ Import & define your element here""" 36 | #import Hexagon,math 37 | #e = Hexagon.Hexagon(color=(255,255,255),radius=60,text="ABCD",edgecolor=(255,255,255),textcolor=(10,10,100),textsize=10,colorkey=(0,0,0),antialias=True) 38 | 39 | text = "LUXUS" 40 | 41 | 42 | 43 | """ Init pygame, open screen etc """ 44 | pygame.init() 45 | screen = pygame.display.set_mode(screenSize) 46 | background = pygame.Surface(screenSize) 47 | background.fill(bgcolor) 48 | screen.blit(background, [0, 0]) 49 | pygame.display.update() 50 | 51 | """ Loop between the states and pause in between """ 52 | width, height = screenSize 53 | e.pos = (width / 2, height / 2) 54 | e.refresh() 55 | e.update(0) 56 | pos = 0 57 | while 1: 58 | screen.blit(background, [0, 0]) 59 | screen.blit(e.image, e.rect) 60 | pygame.display.flip() 61 | e.update() 62 | pygame.time.delay(400) 63 | e.highlight = [pos] 64 | e.refresh() 65 | pos = (pos + 1) % len(text) 66 | for event in pygame.event.get(): 67 | if event.type in (pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN): 68 | sys.exit(0) 69 | break 70 | -------------------------------------------------------------------------------- /src/lib/P300VisualElement/Text.py: -------------------------------------------------------------------------------- 1 | # Text.py 2 | # Copyright (C) 2009 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | Implements a Text/Letter Element for use in the visual P300 paradigm. 20 | P300 Text objects have a number of visual features: 21 | * text - the text itself (eg "A") 22 | * color - the color of the text (eg (255,255,255) for white) 23 | * size - the size of the text (eg 30) 24 | 25 | These features can be used to specify different states. If some of 26 | these features are constant (eg, the same letter size is used in all 27 | states), you should provide a common value for this feature when you 28 | instantiate a P300Text. 29 | 30 | Example: 31 | t = P300Text(text="A",size=30) 32 | """ 33 | 34 | 35 | import pygame 36 | 37 | from VisualElement import VisualElement 38 | 39 | 40 | class Text(VisualElement): 41 | 42 | 43 | DEFAULT_TEXT = "#" 44 | DEFAULT_COLOR = 255, 255, 255 45 | DEFAULT_SIZE = 30 46 | 47 | 48 | def __init__(self, nr_states=2, pos=(0, 0), text=DEFAULT_TEXT, color=DEFAULT_COLOR, size=DEFAULT_SIZE): 49 | VisualElement.__init__(self, nr_states, pos) 50 | self.text = text 51 | self.color = color 52 | self.size = size 53 | 54 | def refresh(self): 55 | # For each state, generate image and rect 56 | for i in range(self.nr_states): 57 | if self.states[i].has_key("text"): text = self.states[i]["text"] 58 | else: text = self.text # Take standard value 59 | if self.states[i].has_key("color"): color = self.states[i]["color"] 60 | else: color = self.color # Take standard value 61 | if self.states[i].has_key("size"): size = self.states[i]["size"] 62 | else: size = self.size # Take standard value 63 | font = pygame.font.Font(None, size) 64 | self.images[i] = font.render(text, True, color); 65 | self.rects[i] = self.images[i].get_rect(center=self.pos) -------------------------------------------------------------------------------- /src/lib/P300VisualElement/VisualElement.py: -------------------------------------------------------------------------------- 1 | # VisualElement.py 2 | # Copyright (C) 2009 Matthias Treder 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | """ 19 | VisualElement.py - 20 | Copyright (c) 2009 Matthias Sebastian Treder 21 | 22 | The base class for elements in a P300 speller 23 | """ 24 | 25 | import random 26 | 27 | import pygame 28 | 29 | 30 | class VisualElement(pygame.sprite.Sprite): 31 | 32 | DEFAULT_PICK_RANDOM_STATE = False 33 | 34 | def __init__(self, nr_states=2, pos=(0, 0)): 35 | pygame.sprite.Sprite.__init__(self) 36 | self.state = 0 # Holds the current state of the element as a number (0,1,...) 37 | self.nr_states = nr_states # Total number of states the element can have (default 2) 38 | self.pick_random_state = self.DEFAULT_PICK_RANDOM_STATE # If true, a random new state will be picked upon each call of update 39 | self.states = [] # A list of dictionaries specifying the states 40 | self.pos = pos # Position of the element's center in pixel coordinates 41 | """ The images and rectangles corresponding to the different states """ 42 | self.images = [None] * nr_states 43 | self.rects = [None] * nr_states 44 | self.states = [] 45 | " Each state is specified by a dictionary of (feature,value) combinations" 46 | for i in range(nr_states): self.states.append(dict()) 47 | 48 | def update(self, new_state=None): 49 | """ 50 | This is a sprite method automatically called when the sprite is to be 51 | updated. Each call makes the element progress to another state. When 52 | the last state had been reached, the element is set back to its original 53 | state via the modulus operator. 54 | Usually, you do not have to override this method. 55 | If you provide a value >= 0 for new_state, this will be the new state 56 | of the element. If you provide the value -1 for new_state, the number 57 | of the state will be decreased by 1. 58 | If pick_random_state is True and no new state is specified, a random 59 | new state will be picked. 60 | """ 61 | 62 | if self.pick_random_state and new_state is None: # Pick a random state 63 | old_state = self.state 64 | rnd = random.Random() 65 | # Pick 2 random states: if first one is equal to current state, pick second one 66 | s = range(self.nr_states) 67 | state2 = rnd.sample(s, 2) 68 | new_state = (state2[1] if state2[0] == self.state else state2[0]) 69 | else: 70 | if new_state is None: 71 | new_state = self.state + 1 72 | elif new_state == - 1: 73 | new_state = self.state - 1 74 | self.state = new_state % self.nr_states 75 | self.image = self.images[self.state] 76 | self.rect = self.rects[self.state] 77 | 78 | def refresh(self): 79 | """ 80 | This method should be called after new states have been defined. 81 | In this method, the images corresponding to the states (self.images()) 82 | and the new rects (self.rects) should be produced. Have a look at the 83 | subclasses of VisualElement for some examples. 84 | """ 85 | pass 86 | 87 | def set_states(self, nr, features): 88 | """ 89 | Sets the features belonging each state. Upon each call, provide the number 90 | of the state you want to change and a dictionary of features you want to 91 | change. The dictionary contains key/value pairs specifying the name of the 92 | feature you want to change and the corresponding value. Details on which 93 | features are allowed for each subclass are given in the description of the 94 | subclass. 95 | """ 96 | for key, value in features.iteritems(): 97 | self.states[nr][key] = value 98 | -------------------------------------------------------------------------------- /src/lib/P300VisualElement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/P300VisualElement/__init__.py -------------------------------------------------------------------------------- /src/lib/PluginController.py: -------------------------------------------------------------------------------- 1 | # PluginController.py - 2 | # Copyright (C) 2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import sys 20 | import os 21 | import logging 22 | 23 | 24 | def import_module_and_get_class(modname, classname): 25 | """Import the module and return modname.classname. 26 | 27 | :param modname: Module name 28 | :type modname: str 29 | :param classname: Class name 30 | :type classname: str 31 | 32 | """ 33 | mod = __import__(modname, fromlist=[None]) 34 | return getattr(mod, classname) 35 | 36 | 37 | class PluginController(object): 38 | """Finds, loads and unloads plugins.""" 39 | 40 | 41 | def __init__(self, plugindirs, baseclass): 42 | self.logger = logging.getLogger("PluginController") 43 | self.plugindirs = map(os.path.normpath, map(os.path.abspath, plugindirs)) 44 | self.baseclass = baseclass 45 | self.availablePlugins = dict() 46 | self.oldModules = None 47 | 48 | for dir in plugindirs: 49 | if os.path.exists(dir): 50 | sys.path.append(dir) 51 | else: 52 | self.logger.warning("Path %s does not exist, ignoring it" % str(dir)) 53 | 54 | 55 | def find_plugins(self): 56 | """Find Plugins. 57 | 58 | :returns: list of available plugins. 59 | 60 | """ 61 | for plugindir in self.plugindirs: 62 | for root, dirs, files in os.walk(plugindir): 63 | if 'feedbacks.list' in files: 64 | self.logger.info("Found feedbacks.list in %s" % root) 65 | del dirs[:] 66 | fbdict = self.load_feedback_list(root+os.path.sep+'feedbacks.list', plugindir) 67 | for fb, module in fbdict.iteritems(): 68 | self.availablePlugins[fb] = module 69 | continue 70 | 71 | 72 | def load_feedback_list(self, filename, plugindir): 73 | """Load classnames from file and construct modulename relative to 74 | plugindir from plugindir, filename and file entries. 75 | 76 | :param filename: filename 77 | :type filename: str 78 | :param plugindir: Plugin directory 79 | :type plugindir: str 80 | :returns: dictionary: classname -> module. 81 | 82 | """ 83 | # Read the lines from file 84 | fh = open(filename, "r") 85 | lines = fh.readlines() 86 | fh.close() 87 | # Construct absolute module name from plugindir, filename and file entry 88 | base = os.path.dirname(filename) 89 | base = base.replace(plugindir, "", 1) 90 | base = base.split(os.path.sep) 91 | fbdict = dict() 92 | for line in lines: 93 | line = line.strip() 94 | # Ignore empty lines or comments 95 | if len(line) == 0 or line.startswith("#"): 96 | continue 97 | line = line.split(".") 98 | full_path_to_class = base + line 99 | while("" in full_path_to_class): 100 | full_path_to_class.remove("") 101 | fbdict[full_path_to_class[-1]] = ".".join(full_path_to_class[:-1]) 102 | return fbdict 103 | 104 | 105 | def unload_plugin(self): 106 | """Unload currently loaded plugin.""" 107 | if self.oldModules: 108 | for mod in sys.modules.keys(): 109 | if not self.oldModules.has_key(mod): 110 | del sys.modules[mod] 111 | self.oldModules = None 112 | 113 | 114 | def main(): 115 | import sys 116 | #sys.path.append("../") 117 | import FeedbackBase.Feedback 118 | pc = PluginController(["../Feedbacks", "../../../pyff-tu/src/Feedbacks"], FeedbackBase.Feedback.Feedback) 119 | pc.find_plugins() 120 | for key in pc.availablePlugins: 121 | print key, pc.availablePlugins[key] 122 | 123 | if __name__ == "__main__": 124 | main() 125 | 126 | -------------------------------------------------------------------------------- /src/lib/RollbackImporter.py: -------------------------------------------------------------------------------- 1 | # RollbackImporter.py - inspired by RollbackImporter from pyunit.sf.net 2 | # Copyright (C) 2008-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import __builtin__ 20 | import sys 21 | 22 | 23 | class RollbackImporter(object): 24 | """ 25 | RollbackImporter. 26 | 27 | RollbackImporter instances install themselves as a proxy for the built-in 28 | :func:`__import__` function that lies behind the 'import' statement. Once 29 | installed, they note all imported modules, and when uninstalled, they 30 | delete those modules from the system module list; this ensures that the 31 | modules will be freshly loaded from their source code when next imported. 32 | 33 | Usage:: 34 | 35 | if self.rollbackImporter: 36 | self.rollbackImporter.uninstall() 37 | self.rollbackImporter = RollbackImporter() 38 | # import some modules 39 | 40 | """ 41 | 42 | def __init__(self): 43 | """Init the RollbackImporter and setup the import proxy.""" 44 | self.oldmodules = sys.modules.copy() 45 | self.realimport = __builtin__.__import__ 46 | __builtin__.__import__ = self._import 47 | 48 | def uninstall(self): 49 | """Unload all modules since __init__ and restore the original import.""" 50 | for module in sys.modules.keys(): 51 | if not self.oldmodules.has_key(module): 52 | del sys.modules[module] 53 | __builtin__.__import__ = self.realimport 54 | 55 | def _import(self, name, globals={}, locals={}, fromlist=[], level=-1): 56 | """Our import method.""" 57 | return apply(self.realimport, (name, globals, locals, fromlist, level)) 58 | 59 | -------------------------------------------------------------------------------- /src/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/__init__.py -------------------------------------------------------------------------------- /src/lib/marker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # trigger.py - Common trigger definitions. 4 | # Copyright (C) 2010 Bastian Venthur 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | 21 | """Common trigger definitions. 22 | 23 | This file contains common trigger definitions. You should use those here 24 | instead of defining them in your Feedback or Stimulus applications whenever 25 | possible. 26 | 27 | This module is executable. When called as a script it will check for duplicate 28 | trigger definitions and will warn you about it. You should call this module 29 | whenever you modified or added new trigger definitions. 30 | """ 31 | 32 | 33 | # Markers specifying the start and the end of the run. They should be sent 34 | # when the feedback is started resp. stopped. 35 | RUN_START, RUN_END = 254, 255 36 | 37 | # Start resp. end of a trial 38 | TRIAL_START, TRIAL_END = 250, 251 39 | 40 | # Start resp. end of a countdown 41 | COUNTDOWN_START, COUNTDOWN_END = 240, 241 42 | 43 | # Onset resp. offset of a fixation marker 44 | FIXATION_START,FIXATION_END = 242, 243 45 | 46 | # Onset resp. offset of a cue 47 | CUE_START, CUE_END = 244, 245 48 | 49 | # Onset resp. offset of feedback 50 | FEEDBACK_START, FEEDBACK_END = 246, 247 51 | 52 | # Start resp. end of a short break during the run 53 | PAUSE_START, PAUSE_END = 248, 249 54 | 55 | 56 | if __name__ == '__main__': 57 | _tmp = list() 58 | for name, value in globals().items(): 59 | # ignore magic variables 60 | if name.startswith('__') and name.endswith('__'): 61 | print "Ignoring magic %s" % name 62 | continue 63 | if name == '_tmp': 64 | continue 65 | if not isinstance(value, int): 66 | print "Ignoring non-int %s (%s)" % (name, str(value)) 67 | _tmp.append([value, name]) 68 | _tmp.sort() 69 | for i in range(1, len(_tmp)): 70 | if _tmp[i][0] == _tmp[i-1][0]: 71 | print "Found duplicate triggers (%i) %s and %s" % (_tmp[i][0], 72 | _tmp[i-1][1], _tmp[i][1]) 73 | 74 | -------------------------------------------------------------------------------- /src/lib/serialport.py: -------------------------------------------------------------------------------- 1 | # pyffserial.py - 2 | # Copyright (C) 2010 - 2014 Chris Hausler, Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | # 20 | # This file can be imported to allow reading and writing from a serial port< 21 | # 22 | # other properties of the port such as Blockin, parity, byte size etc can also 23 | # be set if needed 24 | # 25 | # Requires pySerial - http://pyserial.sourceforge.net/ There are also some 26 | # useful test python files on the sourceforge site for testing the serial port 27 | # functionality 28 | 29 | 30 | import serial 31 | from threading import Timer 32 | 33 | 34 | class SerialPort(object): 35 | 36 | def __init__(self, port, baudrate=57600): 37 | """Open the serial port. 38 | 39 | Parameters 40 | ---------- 41 | port : int 42 | baudrate : int 43 | 44 | """ 45 | self.port = serial.Serial(port=port, baudrate=baudrate) 46 | self.trigger_reset_time = 0.01 47 | self.reset_timer = Timer(0, None) 48 | 49 | 50 | def send(self, data, reset=True): 51 | """Send data to serial port. 52 | 53 | Parameters 54 | ---------- 55 | data : bytevalue 56 | 57 | """ 58 | if reset == True: 59 | self.reset_timer.cancel() 60 | self.port.write(chr(data)) 61 | if reset == True: 62 | self.reset_timer = Timer(self.trigger_reset_time, self.send, (0, False)) 63 | self.reset_timer.start() 64 | 65 | def close(self): 66 | self.port.close() 67 | 68 | 69 | def scan(): 70 | """Scan for available ports. 71 | 72 | Returns 73 | ------- 74 | 75 | ports : list of (int, str) 76 | the elements of the tuples are the number and the name of the 77 | port. 78 | 79 | """ 80 | available = [] 81 | for i in range(256): 82 | try: 83 | s = serial.Serial(i) 84 | available.append( (i, s.portstr)) 85 | s.close() 86 | except serial.SerialException: 87 | pass 88 | return available 89 | 90 | -------------------------------------------------------------------------------- /src/lib/speller/experiment.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | from time import sleep 18 | from itertools import count 19 | import logging 20 | 21 | __all__ = ['CopySpellingExperiment', 'CalibrationExperiment', 22 | 'FreeSpellingExperiment'] 23 | 24 | class Experiment(object): 25 | def __init__(self, view, trial, phrases, input_handler, flag, iter, config): 26 | self._view = view 27 | self._trial = trial 28 | self._phrases = phrases 29 | self._input_handler = input_handler 30 | self._flag = flag 31 | self._iter = iter 32 | self._current_target = '' 33 | self._inter_trial = config.inter_trial 34 | self._inter_phrase = config.inter_phrase 35 | self._countdown = config.phrase_countdown 36 | self._target_present_time = config.target_present_time 37 | self._sequences = lambda: [] 38 | 39 | def run(self): 40 | self._input_handler.start_experiment(self) 41 | 42 | def trial(self): 43 | self._input_handler.start_trial(self._trial) 44 | self._trial.run(self._sequences()) 45 | if self._flag: 46 | self._trial.evaluate(self._input_handler) 47 | 48 | def delete(self): 49 | pass 50 | 51 | def sequences(self): 52 | pass 53 | 54 | class GuidedExperiment(Experiment): 55 | def run(self): 56 | super(GuidedExperiment, self).run() 57 | for word in self._iter(self._phrases): 58 | self._view.word(word) 59 | if self._countdown: 60 | self._view.countdown() 61 | for target in self._iter(enumerate(word)): 62 | self.trial(*target) 63 | sleep(self._inter_trial) 64 | sleep(self._inter_phrase) 65 | 66 | def trial(self, index, target): 67 | self._trial.target(target) 68 | self._view.present(self._target_present_time) 69 | Experiment.trial(self) 70 | if self._flag: 71 | self._view.next_target() 72 | 73 | CopySpellingExperiment = GuidedExperiment 74 | CalibrationExperiment = GuidedExperiment 75 | 76 | class FreeSpellingExperiment(Experiment): 77 | def run(self): 78 | super(FreeSpellingExperiment, self).run() 79 | for i in self._iter(count()): 80 | self.trial() 81 | -------------------------------------------------------------------------------- /src/lib/speller/input.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | import pygame 18 | 19 | __all__ = ['CalibrationInputHandler', 'CopySpellingInputHandler', 20 | 'FreeSpellingInputHandler'] 21 | 22 | class InputHandler(object): 23 | def __init__(self, control, triggerer=None): 24 | self._alphabet = list(control.symbols) 25 | self._view = control._view 26 | self._trigger = control._trigger 27 | self._triggerer = triggerer 28 | self._symbol = '' 29 | 30 | def start_experiment(self, experiment): 31 | self._experiment = experiment 32 | 33 | def start_trial(self, trial): 34 | pass 35 | 36 | CalibrationInputHandler = InputHandler 37 | 38 | class SpellingInputHandler(InputHandler): 39 | def __init__(self, control, update_word=False, *a, **kw): 40 | self._input = '' 41 | self._delete_symbol = control.delete_symbol 42 | self._update_word = update_word 43 | self._allow_keyboard = control.allow_keyboard_input 44 | InputHandler.__init__(self, control, *a, **kw) 45 | 46 | def keyboard(self, event): 47 | s = event.unicode 48 | if (self._allow_keyboard and s in self._alphabet): 49 | self.eeg_select(self._alphabet.index(s)) 50 | 51 | def eeg_select(self, cls): 52 | if 0 <= cls < len(self._alphabet): 53 | symbol = self._alphabet[cls] 54 | if symbol == self._delete_symbol: 55 | self._delete() 56 | else: 57 | self._input += symbol 58 | self._set_eeg_input(symbol) 59 | return True 60 | 61 | def _set_eeg_input(self, symbol): 62 | self._symbol = symbol 63 | self._view.answered() 64 | 65 | def process_eeg_input(self): 66 | if self._triggerer: 67 | self._triggerer(self._symbol) 68 | self._view.eeg_letter(self._input, self._symbol, 69 | update_word=self._update_word) 70 | 71 | def _delete(self): 72 | self._input += self._delete_symbol 73 | 74 | class FreeSpellingInputHandler(SpellingInputHandler): 75 | def __init__(self, *a, **kw): 76 | SpellingInputHandler.__init__(self, update_word=True, *a, **kw) 77 | 78 | def _delete(self): 79 | self._input = self._input[:-1] 80 | 81 | CopySpellingInputHandler = SpellingInputHandler 82 | -------------------------------------------------------------------------------- /src/lib/speller/trial.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | from lib import marker 18 | 19 | __all__ = ['CalibrationTrial', 'FreeSpellingTrial', 'CopySpellingTrial'] 20 | 21 | class Trial(object): 22 | def __init__(self, view, trigger, iter, config, trial_input=False): 23 | self._view = view 24 | self._trigger = trigger 25 | self._iter = iter 26 | self._countdown = config.trial_countdown 27 | self._trial_input = trial_input 28 | self.__init_attributes() 29 | 30 | def __init_attributes(self): 31 | self.asking = False 32 | self.current_target = '' 33 | 34 | def _ask(self): 35 | self.asking = True 36 | self._view.ask() 37 | self.asking = False 38 | 39 | def _sequence(self): 40 | pass 41 | 42 | def run(self, sequences): 43 | self._trigger(marker.TRIAL_START, wait=True) 44 | self._run(sequences) 45 | self._trigger(marker.TRIAL_END, wait=True) 46 | 47 | def _run(self, sequences): 48 | if self._countdown: 49 | self._view.countdown() 50 | for seq in self._iter(sequences): 51 | self._sequence(seq) 52 | if self._trial_input: 53 | self._ask() 54 | 55 | def target(self, target): 56 | self.current_target = target 57 | 58 | def evaluate(self, input_handler): 59 | pass 60 | 61 | class SpellingTrial(Trial): 62 | def __init__(self, *a, **kw): 63 | Trial.__init__(self, trial_input=True, *a, **kw) 64 | 65 | def evaluate(self, input_handler): 66 | input_handler.process_eeg_input() 67 | 68 | CalibrationTrial = Trial 69 | FreeSpellingTrial = SpellingTrial 70 | CopySpellingTrial = SpellingTrial 71 | -------------------------------------------------------------------------------- /src/lib/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/test/__init__.py -------------------------------------------------------------------------------- /src/lib/test/mod_w_imports.py: -------------------------------------------------------------------------------- 1 | import lib.test.mod_wo_imports 2 | 3 | class Bar(object): 4 | pass 5 | -------------------------------------------------------------------------------- /src/lib/test/mod_wo_imports.py: -------------------------------------------------------------------------------- 1 | 2 | class Foo(object): 3 | pass -------------------------------------------------------------------------------- /src/lib/test/test_eyetracker.py: -------------------------------------------------------------------------------- 1 | # test_eyetracker.py - 2 | # Copyright (C) 2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import unittest 20 | import socket 21 | import time 22 | 23 | from lib.eyetracker import EyeTracker 24 | 25 | 26 | class EyeTrackerTestCase(unittest.TestCase): 27 | 28 | def setUp(self): 29 | self.et = EyeTracker() 30 | self.et.start() 31 | self.etserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 32 | time.sleep(0.1) 33 | try: 34 | self.etserver.connect(("", 1111)) 35 | except: 36 | # We pretend everything is working when no eyetracker is available 37 | # for testing purposes 38 | self.etserver = None 39 | 40 | def tearDown(self): 41 | self.et.stop() 42 | if self.etserver is None: 43 | return 44 | self.etserver.close() 45 | 46 | def testParser(self): 47 | """EyeTracker should correctly parse valid data from the eyetracker.""" 48 | if self.etserver is None: 49 | return 50 | self.etserver.send(r"17\48\56\437:607:322:F:F:180\r\n") 51 | time.sleep(0.1) 52 | self.assertEqual(self.et.time_h, 17) 53 | self.assertEqual(self.et.time_m, 48) 54 | self.assertEqual(self.et.time_s, 56) 55 | self.assertEqual(self.et.time_ms, 437) 56 | self.assertEqual(self.et.x, 607) 57 | self.assertEqual(self.et.y, 322) 58 | self.assertEqual(self.et.duration, 180) 59 | 60 | 61 | 62 | #suite = unittest.makeSuite(BcixmlTestCase) 63 | def suite(): 64 | testSuite = unittest.makeSuite(EyeTrackerTestCase) 65 | return testSuite 66 | 67 | def main(): 68 | runner = unittest.TextTestRunner() 69 | runner.run(suite()) 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /src/lib/test/test_rollbackimporter.py: -------------------------------------------------------------------------------- 1 | # test_rollbackimporter.py - 2 | # Copyright (C) 2008-2009 Bastian Venthur 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | 19 | import unittest 20 | import sys 21 | 22 | from lib.RollbackImporter import RollbackImporter 23 | 24 | 25 | class RollbackImporterTestCase(unittest.TestCase): 26 | 27 | def testSimpleImport(self): 28 | """Should remove simple import.""" 29 | modname = "lib.test.mod_wo_imports" 30 | self._del_if_existent(modname) 31 | rbi = RollbackImporter() 32 | import lib.test.mod_wo_imports 33 | self.assertTrue(sys.modules.has_key(modname)) 34 | rbi.uninstall() 35 | self.assertFalse(sys.modules.has_key(modname)) 36 | 37 | def testComplexImport(self): 38 | """Should remove import and import(s) of import.""" 39 | modname1 = "lib.test.mod_w_imports" 40 | modname2 = "lib.test.mod_wo_imports" 41 | self._del_if_existent(modname1) 42 | self._del_if_existent(modname2) 43 | rbi = RollbackImporter() 44 | import lib.test.mod_w_imports 45 | self.assertTrue(sys.modules.has_key(modname2)) 46 | rbi.uninstall() 47 | self.assertFalse(sys.modules.has_key(modname2)) 48 | 49 | def testRelativeImport(self): 50 | """Should remove relative import.""" 51 | modname = "lib.test.mod_wo_imports" 52 | self._del_if_existent(modname) 53 | rbi = RollbackImporter() 54 | import mod_wo_imports 55 | self.assertTrue(sys.modules.has_key(modname)) 56 | rbi.uninstall() 57 | self.assertFalse(sys.modules.has_key(modname)) 58 | 59 | def testSysModulesEqualBeforeAndAfter(self): 60 | """Modules before and after usage of RBI should be equal.""" 61 | before = sys.modules.copy() 62 | rbi = RollbackImporter() 63 | import mod_w_imports 64 | rbi.uninstall() 65 | self.assertEqual(before, sys.modules) 66 | 67 | def _del_if_existent(self, modname): 68 | if sys.modules.has_key(modname): 69 | del(sys.modules[modname]) 70 | 71 | 72 | #suite = unittest.makeSuite(BcixmlTestCase) 73 | def suite(): 74 | testSuite = unittest.makeSuite(RollbackImporterTestCase) 75 | return testSuite 76 | 77 | def main(): 78 | runner = unittest.TextTestRunner() 79 | runner.run(suite()) 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /src/lib/vision_egg/__init__.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | 16 | """ 17 | 18 | from view import VisionEggView 19 | -------------------------------------------------------------------------------- /src/lib/vision_egg/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/vision_egg/model/__init__.py -------------------------------------------------------------------------------- /src/lib/vision_egg/model/color_word.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from itertools import izip 17 | from random import uniform 18 | import logging 19 | 20 | from text_list import TextList 21 | 22 | class ColorWord(TextList): 23 | def __init__(self, position=(0, 0), text='', target=None, symbol_size=72, 24 | target_size=None, colors=[], **kw): 25 | TextList.__init__(self, position, **kw) 26 | self.set_size(symbol_size, target_size) 27 | self._target = None 28 | self._target_index = None 29 | self.set(text=text, target=target, colors=colors) 30 | 31 | def set(self, text=None, target=None, colors=None): 32 | if text is not None: 33 | self.set_text(text) 34 | if colors is not None: 35 | self._colors = colors 36 | if target is not None: 37 | self.set_target(target) 38 | self.rebuild(target) 39 | 40 | def rebuild(self, target=None): 41 | self.clear() 42 | sizes = [self._symbol_size] * len(self.text) 43 | if self._target_index is not None: 44 | sizes[self._target_index] = self._target_size 45 | for letter, size in izip(self.text, sizes): 46 | self.add(letter, size) 47 | self._set_colors() 48 | 49 | def set_text(self, text): 50 | self.text = text 51 | self._target_index = None 52 | 53 | def set_target(self, target): 54 | if isinstance(target, int) and 0 <= target <= len(self.text): 55 | self._target = self.text[target] 56 | self._target_index = target 57 | elif isinstance(target, basestring) and target in self.text: 58 | self._target = target 59 | self._target_index = self.text.index(target) 60 | else: 61 | self._target = None 62 | self._target_index = None 63 | 64 | def set_size(self, symbol_size=72, target_size=None): 65 | self._symbol_size = symbol_size 66 | self._target_size = target_size or symbol_size 67 | 68 | def shuffle_colors(self): 69 | colors = ([uniform(0, 1) for i in xrange(3)] for e in self) 70 | self.set_colors(colors) 71 | 72 | def _set_colors(self): 73 | for color, symbol in izip(self._colors, self): 74 | symbol.set(color=color) 75 | -------------------------------------------------------------------------------- /src/lib/vision_egg/model/text_list.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from copy import copy 17 | 18 | from VisionEgg.Text import Text 19 | 20 | class TextList(list): 21 | def __init__(self, position, spacing=0.): 22 | list.__init__([]) 23 | self._position = position 24 | self._spacing = spacing 25 | 26 | def add(self, text, size): 27 | new = Text(font_size=size, text=text, anchor='bottom') 28 | self.append(new) 29 | self._rearrange() 30 | 31 | def _rearrange(self): 32 | height = self._max_height 33 | width = self._width 34 | pos = list(self._position) 35 | pos[0] -= width / 2. 36 | pos[1] -= height / 2. 37 | for t in self: 38 | s = t.parameters.size 39 | w = s[0] / 2. 40 | pos[0] += w 41 | t.set(position=copy(pos)) 42 | pos[0] += w + self._spacing 43 | 44 | @property 45 | def _max_height(self): 46 | heights = [t.parameters.size[1] for t in self] 47 | return max([0] + heights) 48 | 49 | @property 50 | def _width(self): 51 | spacings = self._spacing * (len(self) - 1) 52 | return reduce(lambda l, t: l + t.parameters.size[0], self, spacings) 53 | 54 | def clear(self): 55 | del self[:] 56 | 57 | def set_all(self, **kwargs): 58 | for t in self: 59 | t.set(**kwargs) 60 | 61 | def set_position(self, position): 62 | self._position = position 63 | -------------------------------------------------------------------------------- /src/lib/vision_egg/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/src/lib/vision_egg/util/__init__.py -------------------------------------------------------------------------------- /src/lib/vision_egg/util/frame_counter.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010-2011 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by the 5 | Free Software Foundation; either version 3 of the License, or (at your 6 | option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, but 9 | WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, see . 15 | """ 16 | 17 | import logging 18 | import threading 19 | 20 | import pygame 21 | 22 | class FrameCounter(threading.Thread): 23 | """ Runs a thread that calls flip() repeatedly, which waits for 24 | vsync and thus indicates real display redraws. """ 25 | def __init__(self, flag): 26 | threading.Thread.__init__(self) 27 | self._flag = flag 28 | self.frame = 0 29 | self._locked_frame = 0 30 | 31 | def run(self): 32 | try: 33 | while self._flag: 34 | self.step() 35 | except pygame.error as e: 36 | logging.getLogger('FrameCounter').error(unicode(e)) 37 | 38 | def step(self): 39 | self.sync() 40 | self.frame += 1 41 | 42 | def sync(self): 43 | pygame.display.flip() 44 | 45 | def lock(self): 46 | self._locked_frame = self.frame 47 | 48 | @property 49 | def last_interval(self): 50 | return self.frame - self._locked_frame 51 | -------------------------------------------------------------------------------- /src/lib/vision_egg/util/switcherator.py: -------------------------------------------------------------------------------- 1 | __copyright__ = """ Copyright (c) 2010 Torsten Schmits 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU General Public License as published by the Free Software 5 | Foundation; either version 3 of the License, or (at your option) any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 8 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 9 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 | 11 | You should have received a copy of the GNU General Public License along with this 12 | program; if not, see . 13 | 14 | """ 15 | 16 | from time import sleep 17 | 18 | class Flag(object): 19 | def __init__(self): 20 | self.reset() 21 | 22 | def __nonzero__(self): 23 | return self._flag 24 | 25 | def off(self): 26 | self._flag = False 27 | self.suspended = False 28 | 29 | def reset(self): 30 | self._flag = True 31 | self.suspended = False 32 | 33 | def toggle_suspension(self): 34 | self.suspended = not self.suspended 35 | 36 | def wait(self): 37 | while self.suspended: 38 | sleep(0.1) 39 | 40 | class Switcherator(object): 41 | def __init__(self, flag, itr, suspendable=False): 42 | self._flag = flag 43 | self._iter = iter(itr) 44 | self._suspendable = suspendable 45 | 46 | def __iter__(self): 47 | return self 48 | 49 | def next(self): 50 | if self._suspendable: 51 | self._flag.wait() 52 | if not self._flag: 53 | raise StopIteration() 54 | return self._iter.next() 55 | -------------------------------------------------------------------------------- /src/stresstest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import time 5 | 6 | from lib.feedbackcontroller import FeedbackController 7 | import lib.bcixml 8 | 9 | 10 | play_signal = lib.bcixml.BciSignal(None, [lib.bcixml.CMD_PLAY], lib.bcixml.INTERACTION_SIGNAL) 11 | 12 | 13 | 14 | def main(): 15 | fc = FeedbackController() 16 | feedbacks = fc.fbProcCtrl.get_feedbacks() 17 | 18 | for fb in feedbacks: 19 | print "Starting %s" % fb 20 | fc.fbProcCtrl.start_feedback(fb) 21 | 22 | fc.handle_signal(play_signal) 23 | time.sleep(1) 24 | 25 | print "Stopping %s" % fb 26 | fc.fbProcCtrl.stop_feedback() 27 | 28 | failstop = [] 29 | try: 30 | if fc.fbProcCtrl.currentProc.is_alive(): 31 | failstop.append(fb) 32 | except: 33 | pass 34 | 35 | 36 | print "Done testing Feedbacks, the following feedbacks failed to stop correctly:" 37 | for fb in failstop: 38 | print fb 39 | 40 | 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /src/test_all.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # testsuite.py 4 | # Copyright (C) 2007-2011 Bastian Venthur 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | import unittest 21 | import logging 22 | import os 23 | 24 | # Setup the loggger 25 | logging.basicConfig(level=logging.WARNING, 26 | format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s' 27 | ) 28 | logging.disable(100) 29 | 30 | # Walk through every subdir and search for folders named "test". 31 | # For every python file in those folders should contain TestCases by convention. 32 | allTests = unittest.TestSuite() 33 | 34 | for root, dirs, files in os.walk("."): 35 | if root.split(os.sep)[-1] == "test": 36 | for file in files: 37 | if file.endswith(".py"): 38 | module = root.replace(os.sep, ".")+"."+file[:-3] 39 | while module.startswith("."): 40 | module = module[1:] 41 | try: 42 | suite = unittest.TestLoader().loadTestsFromName(module) 43 | allTests.addTest(suite) 44 | except Exception, e: 45 | logging.warning("Unable to add %s" % module) 46 | logging.warning(e) 47 | 48 | # Run the tests 49 | unittest.TextTestRunner(verbosity=2).run(allTests) 50 | -------------------------------------------------------------------------------- /tools/README: -------------------------------------------------------------------------------- 1 | 2 | Windows 32 Bit (inpout32.dll) 3 | ============================= 4 | 5 | Use this file to use the parallel port on Windows. Copy this file to 6 | Windows\System, a restart is not needed. 7 | 8 | Sources and Binaries are from: 9 | http://logix4u.net/Legacy_Ports/Parallel_Port/Inpout32.dll_for_Windows_98/2000/NT/XP.html 10 | 11 | 12 | Windows 64 Bit (x64) 13 | ==================== 14 | 15 | Apparently you have to execute the installer in x64/Win32/InstallDriver.exe 16 | with administrator rights and that's it. 17 | 18 | Binaries are from: 19 | http://www.highrez.co.uk/Downloads/InpOut32/default.htm 20 | 21 | -------------------------------------------------------------------------------- /tools/inpout32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/inpout32.dll -------------------------------------------------------------------------------- /tools/x64/DLPortIO.txt: -------------------------------------------------------------------------------- 1 | From Verison 1.0.0.7, InpOut provides basic DLPortIO compatibility, for use with LCDSmartie etc. 2 | 3 | It is however, not fully tested as my development machines (DFI motherboard) does not have a parallel port!!! 4 | To use with LCD smartie, take the 32bit DLL (in \Win32) and rename it to DLPortIO.dll 5 | 6 | Copy this DLL to your LCD Smartie folder and then give LCDSmartie a go. 7 | I know that LCD Smartie loads the DLL and talks to it properly - I'm just unsure if it reads/writes the port. 8 | 9 | Let me know if you have any problems. 10 | 11 | Thanks, 12 | Phil (Phil@Highrez.co.uk) -------------------------------------------------------------------------------- /tools/x64/ReadMe.txt: -------------------------------------------------------------------------------- 1 | InpOut32Drv Driver Interface DLL 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | Modified for x64 compatibility and built by Phillip Gibbons (Phil@highrez.co.uk). 5 | See http://www.highrez.co.uk/Downloads/InpOut32 or the Highrez Forums (http://forums.highrez.co.uk) for information. 6 | Many thanks to Red Fox UK for supporting the community and providing Driver signatures allowing Vista/7 x64 compatibility. 7 | 8 | 9 | 10 | Based on the original written by Logix4U (www.logix4u.net). 11 | 12 | 13 | Notes: 14 | 15 | The InpOut32 device driver supports writing to "old fashioned" hardware port addresses. 16 | It does NOT support USB devices such as USB Parallel ports or even PCI parallel ports (as I am lead to believe). 17 | 18 | 19 | The device driver is installed at runtime. To do this however needs administrator privileges. 20 | On Vista & later, using UAC, you can run the InstallDriver.exe in the \Win32 folder to install the driver 21 | appropriate for your OS. Doing so will request elevation and ask for your permission (or for the administrator 22 | password). Once the driver is installed for the first time, it can then be used by any user *without* 23 | administrator privileges 24 | 25 | -------------------------------------------------------------------------------- /tools/x64/Win32/InstallDriver.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/Win32/InstallDriver.exe -------------------------------------------------------------------------------- /tools/x64/Win32/inpout32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/Win32/inpout32.dll -------------------------------------------------------------------------------- /tools/x64/Win32/inpout32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //Functions exported from DLL. 4 | //For easy inclusion is user projects. 5 | //Original InpOut32 function support 6 | void _stdcall Out32(short PortAddress, short data); 7 | short _stdcall Inp32(short PortAddress); 8 | 9 | //My extra functions for making life easy 10 | BOOL _stdcall IsInpOutDriverOpen(); //Returns TRUE if the InpOut driver was opened successfully 11 | BOOL _stdcall IsXP64Bit(); //Returns TRUE if the OS is 64bit (x64) Windows. 12 | 13 | //DLLPortIO function support 14 | UCHAR _stdcall DlPortReadPortUchar (USHORT port); 15 | void _stdcall DlPortWritePortUchar(USHORT port, UCHAR Value); 16 | 17 | USHORT _stdcall DlPortReadPortUshort (USHORT port); 18 | void _stdcall DlPortWritePortUshort(USHORT port, USHORT Value); 19 | 20 | ULONG _stdcall DlPortReadPortUlong(ULONG port); 21 | void _stdcall DlPortWritePortUlong(ULONG port, ULONG Value); 22 | 23 | //WinIO function support (Untested and probably does NOT work - esp. on x64!) 24 | PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle); 25 | BOOL _stdcall UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle, PBYTE pbLinAddr); 26 | BOOL _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal); 27 | BOOL _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal); 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tools/x64/Win32/inpout32.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/Win32/inpout32.lib -------------------------------------------------------------------------------- /tools/x64/Win32/vssver2.scc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/Win32/vssver2.scc -------------------------------------------------------------------------------- /tools/x64/x64/inpout32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //Functions exported from DLL. 4 | //For easy inclusion is user projects. 5 | //Original InpOut32 function support 6 | void _stdcall Out32(short PortAddress, short data); 7 | short _stdcall Inp32(short PortAddress); 8 | 9 | //My extra functions for making life easy 10 | BOOL _stdcall IsInpOutDriverOpen(); //Returns TRUE if the InpOut driver was opened successfully 11 | BOOL _stdcall IsXP64Bit(); //Returns TRUE if the OS is 64bit (x64) Windows. 12 | 13 | //DLLPortIO function support 14 | UCHAR _stdcall DlPortReadPortUchar (USHORT port); 15 | void _stdcall DlPortWritePortUchar(USHORT port, UCHAR Value); 16 | 17 | USHORT _stdcall DlPortReadPortUshort (USHORT port); 18 | void _stdcall DlPortWritePortUshort(USHORT port, USHORT Value); 19 | 20 | ULONG _stdcall DlPortReadPortUlong(ULONG port); 21 | void _stdcall DlPortWritePortUlong(ULONG port, ULONG Value); 22 | 23 | //WinIO function support (Untested and probably does NOT work - esp. on x64!) 24 | PBYTE _stdcall MapPhysToLin(PBYTE pbPhysAddr, DWORD dwPhysSize, HANDLE *pPhysicalMemoryHandle); 25 | BOOL _stdcall UnmapPhysicalMemory(HANDLE PhysicalMemoryHandle, PBYTE pbLinAddr); 26 | BOOL _stdcall GetPhysLong(PBYTE pbPhysAddr, PDWORD pdwPhysVal); 27 | BOOL _stdcall SetPhysLong(PBYTE pbPhysAddr, DWORD dwPhysVal); 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tools/x64/x64/inpoutx64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/x64/inpoutx64.dll -------------------------------------------------------------------------------- /tools/x64/x64/inpoutx64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/x64/inpoutx64.lib -------------------------------------------------------------------------------- /tools/x64/x64/vssver2.scc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bbci/pyff/d0b04d92960b98a1eed466d4edb045c66198ede6/tools/x64/x64/vssver2.scc --------------------------------------------------------------------------------