[
  {
    "path": ".firebaserc",
    "content": "{\n  \"projects\": {\n    \"default\": \"eegedu\"\n  }\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/workflow.yml",
    "content": "# .github/workflows/nodejs.yml\nname: Build and Deploy to Firebase\n\non: \n  push: # Run on Push Requests\n    branches:\n      - master\n  \njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version: [10.x] # Only run the 10.x build\n    steps:\n      - name: Checkout Repo\n        uses: actions/checkout@master\n      \n      - name: Use Node.js ${{ matrix.node-version }}\n        uses: actions/setup-node@master\n        with:\n          node-version: ${{ matrix.node-version }}\n    \n      - name: Install Yarn\n        run: npm install -g yarn\n\n      - name: Install Dependencies\n        run: yarn install\n\n      - name: Build\n        run: yarn build\n\n      - name: Create artifact dir\n        run: |\n          mkdir -p artifacts\n\n      - name: Archive Produciton Artifacts\n        uses: actions/upload-artifact@master\n        with:\n          name: build\n          path: build\n\n  deploy:\n    name: Deploy to Firebase\n    needs: build\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repo\n        uses: actions/checkout@master\n      - name: Download Production Artifacts\n        uses: actions/download-artifact@master\n        with:\n          name: build\n      - name: Deploy to Firebase\n        uses: w9jds/firebase-action@master\n        with:\n          args: deploy --only hosting\n        env:\n          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/ignore-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# editor\n/.idea\n\n.firebase"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at kylemath@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "\n🎉 Thanks for thinking about making a contribution! 🎉\n\nThe following is a set of guidelines for contributing to `EEGEdu`. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.\n\n# Issues\n\nTo start, you can find all the open issues [here](https://github.com/kylemath/EEGEdu/issues).\n\n# Pull Requests (PR)\n\nAt any point in time you can create a pull request, so others can see your changes and give you feedback.\n\n# Setup\n\n## Fork the Repository\n\nStart with a fork of the repository so you can work worry-free on your own fork.\n\nYou will need to fork once. Then, you can call `git fetch upstream` and `git pull 'branch-name'` before you do your local changes to fetch the latest remote changes make sure your changes are being made on up-to-date source code.\n\n## Make Development Environment and Change Away\n\nConfigure your Dev Environment and make your changes on your fork.\n\n# Contributing\n\n## Creating a Pull Request\n\nOnce you have made your changes, you can create a pull request, so others can see your changes and give you feedback. Create all pull requests on the master branch. If your PR is a work-in-progress, add a [WIP] at the start of the PR title. Example:[WIP] Heart Rate Monitor Module.\n\n## Wait for Reviews\n\nAfter each PR others will look into your feature, discuss the changes in the PR, and then merge into the master branch when changes are ready. \n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Kyle Mathewson\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# EEGEdu\n\n<p align=\"center\"> \n<a href=\"https://github.com/kylemath/EEGEdu/blob/master/LICENSE\" target=\"_blank\">\n<img src=\"https://img.shields.io/badge/License-MIT-brightgreen.svg\" alt=\"MIT license\" />\n</a>\n<img src=\"https://img.shields.io/github/v/release/kylemath/EEGEdu?include_prereleases\" />\n<a href=\"https://github.com/kylemath/EEGEdu/blob/master/CONTRIBUTING.md\" target=\"_blank\">\n<img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg\" alt=\"PRs welcome!\" />\n</a>\n<img src=\"https://img.shields.io/github/issues-raw/kylemath/EEGEdu\" alt=\"issues\" />\n<img src=\"https://img.shields.io/github/issues-closed-raw/kylemath/EEGEdu\" />\n<a href=\"https://neurotechx.herokuapp.com/\" target=\"_blank\">\n<img src=\"https://neurotechx.herokuapp.com/badge.svg\">\n</a>\n<a href=\"https://neurotechx.slack.com/messages/eegedu/\" target=\"_blank\">\n<img src=\"https://img.shields.io/badge/NeurotechXChannel-%23eegedu-lightgrey\" />\n</a>\n</p>\n\n\n\n\n<p align=\"center\">\n<img src=\"logo.png\" alt=\"Interactive Brain Playground Logo\" width=500 />\n</p>\n\n`EEGEdu` is an Interactive Brain Playground. \n\n`EEGEdu` is served live at [https://eegedu.com/](https://eegedu.com). This website is served from the software in this repository. So, all you need to do to try the system out is head to [EEGEdu](https://eegedu.com/).\n\n`EEGEdu` is designed as an interactive educational website to learn/teach about working with electroencephalogram (EEG) data. It is a teaching tool that allows for students to interact with their own brain waves. \n\n`EEGEdu` has been inspired by multiple works that came before. It is inspired by [EEG101](https://github.com/NeuroTechX/eeg-101), but `EEGEdu` is web-based. Being web-based allows students to interact with EEG brain data without having to install any software. Others have used [Neurotech EEG-notebooks in python](https://github.com/NeuroTechX/eeg-notebooks) for data collection and analysis with [muse-lsl](https://github.com/alexandrebarachant/muse-lsl).  These software support the Interaxon MUSE headset but require a bluetooth low-energey (BLE) dongle to work with common operating systems (e.g. Windows or Mac OSX). These tools also required the editing `pyglatt` code to connect to Muse headsets. Thus, previous software are cumbersome and serve as a barrier to entry for many students learning about EEG. `EEGEdu` aims to provide students with an accesible introduction to working with their own brain waves.\n\n\n# EEGEdu Curriculum \n\nEEGEdu provides an step-by-step incremental tutorial for students to interact with EEG-based brain signals. So, we break down the curriculum into 10 lessons as follows:\n\n1. [Connect + hardware, Biophysics + signal viewing](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduIntro/EEGEduIntro.js)\n2. [Heart rate time domain data](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduHeartRaw/EEGEduHeartRaw.js)\n3. [Heart rate frequency domain -beats per minute](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduHeartSpectra/EEGEduHeartSpectra.js)\n4. [Raw Data + artifacts + blinks + record](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduRaw/EEGEduRaw.js)\n5. [Frequency domain explanation + Raw spectra + record](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduSpectra/EEGEduSpectra.js)\n6. [Frequency bands + record](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduBands/EEGEduBands.js)\n7. [Spectrogram](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduSpectro/EEGEduSpectro.js)\n8. [Neurofeedback p5js demos](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduAnimate/EEGEduAnimate.js)\n9. [Eyes closed eyes open experiment](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduAlpha/EEGEduAlpha.js)\n10. [SSVEP experiment](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduSsvep/EEGEduSsvep.js)\n11. [BCI trainer](https://github.com/kylemath/EEGEdu/blob/master/src/components/PageSwitcher/components/EEGEduPredict/EEGEduPredict.js)\n\n# Installation for Development \n\nIf you are interested in developing EEGEdu, here are some instructions to get the software running on your system. *Note*: Currently EEGEdu development requires a Mac OSX operating system. \n\nTo start, you will need to install [Homebrew](https://brew.sh) and [yarn](https://yarnpkg.com/lang/en/docs/install/#mac-stable). These are easy to install with the following Terminal / `bash` commands:\n\n```sh\n## Install homebrew\n/usr/bin/ruby -e \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)\"\n\n## Install yarn\n# NOTE: this will also install Node.js if it is not already installed.\nbrew install yarn \n\n# NOTE: Node.js must be version 10.x for Muse interaction\n\n# Thus, if you are getting version issues, install n, with the following command:\n# sudo npm install -g n\n\n# Then, you can switch to version 10.x with the following command:\n# sudo n 10.16.0\n```\n\nThen, in Terminal/`bash`, clone this Git repository and change directory into the newly cloned folder:\n\n```sh\ngit clone https://github.com/kylemath/EEGEdu\ncd EEGEdu\n```\n\nThen, you can install the required `yarn` packages for EEGEdu:\n\n```sh\nyarn install\n```\n\n## Local Development Environment\nThen, you can run the *Local Development Environment* of EEGEdu:\n\n```sh\nyarn start dev\n```\n\nIf it is working correctly, the EEGEdu application will automatically open in a browser window at http://localhost:3000.\n\n## Local Production Environment\n\nTo start the *Local Production Environment*, you can use the following commands: \n\n```sh\nyarn cache clean\nyarn run build\nserve -s build\n```\n\n## Local Testing of Changes\n\n1. Install any new packages `yarn install`\n1. Start the *Local Development Environment* `yarn start dev`\n1. Look for errors in terminal log\n1. Open's browser to http://localhost:3000\n1. Open Javascript console\n1. Look for errors in console\n1. Connect Mock data stream by clicking Connect button\n1. Run through the `checkFunction` below with Mock data. \n1. Disconnect Mock data stream\n1. Turn on Interaxon Muse headband\n1. Connect Muse data stream\n1. Repeat the `checkFunction` below with Muse data. \n\n```sh\n# Pseudocode for checking EEGEdu functionality\ncheckFunction = {\n    view raw data \n    change sliders \n    make sure data changes and no errors\n    click spectra\n    move sliders\n    make sure changes\n    click bands\n    move sliders\n    make sure changes\n}\n```\n\n## Deployment\n\n[EEGEdu](https://eegedu.com) is running on [Firebase](https://firebase.google.com/) and deployment happens automagically using GitHub post-commit hooks, or [Actions](https://github.com/kylemath/EEGEdu/actions), as they are commonly called. You can see how the application is build and deployed by [inspecting the workflow](https://github.com/kylemath/EEGEdu/blob/master/.github/workflows/workflow.yml). \n\nCurrently this automagic deployment is not working, so we can deploy to firebase manually:\n\nFirst, install the Firebase deployment tools:\n\n```sh\nsudo brew install firebase\nsudo yarn global add firebase-tools\nsudo yarn global add firebase\n```\n\nThe first deployment requires login and initialization once:\n\n```sh\nfirebase login\n```\n\nBrowser opens, and login to Google account authorized for Firebase deployment:\n\n```sh\nfirebase init\n```\n\n* options: Hosting Sites only\n* public directory: build\n* single-page app: No\n* Overwrite - No\n* Overwrite - No\n\nThen, deployment to Firebase happens with the following commands:\n\n```sh\n# clean the local cache to ensure most recent version is served\nyarn cache clean\n\n# build the latest version of the site\nyarn run build\n\n# deploy the latest version to firebase\nfirebase deploy\n```\n\n# References and Related Tools\n\n* [Muse 2016 EEG Headset JavaScript Library (using Web Bluetooth)](https://github.com/urish/muse-js)\n* [Muse 2016 + Web Bluetooth demo app in Angular](https://github.com/NeuroJS/angular-muse)\n* [Explore Muse headband data in frequency domain](https://github.com/tanvach/muse-fft)\n* [Pipeable RxJS operators for working with EEG data in Node and the Browser](https://github.com/neurosity/eeg-pipes)\n* [React, A JavaScript library for building user interfaces](https://reactjs.org/)\n* [Simple yet flexible JavaScript charting for designers & developers](https://www.chartjs.org/docs/latest/)\n* [Muse 2016 EEG Headset LSL (NodeJS)](https://github.com/urish/muse-lsl)\n* [Electroencephalogram (EEG) Recording Protocol for Cognitive and Affective\nHuman Neuroscience Research](https://static1.squarespace.com/static/5abefa62d274cb16de90e935/t/5df7db5956ec9170a9b402e1/1576524637645/Electrode_Application_Protocol_Final_Compressed.pdf)\n\n# Contributing\nThe guide for contributors can be found [here](https://github.com/kylemath/EEGEdu/blob/master/CONTRIBUTING.md). It covers everything you need to know to start contributing to EEGEdu.\n\n# Credits\n\n`EEGEdu` - An Interactive Electrophysiology Tutorial with the Interaxon Muse brought to you by Mathewson Sons. A [Ky](http://kylemathewson.com)[Kor](http://korymathewson.com)[Key](http://keyfer.ca) Production.\n\n# License\n\n[EEGEdu is licensed under The MIT License (MIT)](https://github.com/kylemath/EEGEdu/blob/master/LICENSE)\n\n## Preview\n\n<p align=\"center\">\n  <img src=\"screenshot.png\" alt=\"Project screenshot\" width=\"720\" />\n</p>\n\n"
  },
  {
    "path": "app.yaml",
    "content": "runtime: nodejs10\nenv: standard\n"
  },
  {
    "path": "catalogue.json",
    "content": "{\n  \"id\": \"EEGEdu\",\n  \"title\": \"EEGEdu\",\n  \"oneLiner\": \"An interactive website to learn about brain waves\",\n  \"demoUrl\": \"https://eegedu.com/\",\n  \"screenshot\": \"https://raw.githubusercontent.com/kylemath/EEGEdu/master/screenshot.png\",\n  \"kind\": \"page\",\n  \"categories\": [\"eeg\", \"education\", \"neuroscience\"],\n  \"tags\": [\"eeg\", \"education\", \"neuroscience\"],\n  \"status\": \"published\"\n}\n"
  },
  {
    "path": "firebase.json",
    "content": "{\n  \"hosting\": {\n    \"headers\": [\n      { \"source\":\"/service-worker.js\", \"headers\": [{\"key\": \"Cache-Control\", \"value\": \"no-cache\"}] }\n    ],\n    \"public\": \"build\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules/**\"\n    ],\n    \"rewrites\": [\n      {\n        \"source\": \"**\",\n        \"destination\": \"/index.html\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"engines\": {\n    \"npm\": \"5.x\",\n    \"node\": \"10.x\"\n  },\n  \"name\": \"eegedu\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@neurosity/pipes\": \"^3.0.2\",\n    \"@shopify/polaris\": \"^4.9.0\",\n    \"chart.js\": \"^2.7.2\",\n    \"file-saver\": \"^2.0.2\",\n    \"firebase\": \"^7.5.0\",\n    \"firebase-tools\": \"^7.9.0\",\n    \"handlebars\": \"^4.3.0\",\n    \"ml5\": \"^0.4.3\",\n    \"muse-js\": \"^3.0.1\",\n    \"p5\": \"^0.10.2\",\n    \"p5.js-widget\": \"https://github.com/toolness/p5.js-widget.git\",\n    \"prettier\": \"^1.19.1\",\n    \"react\": \"^16.12.0\",\n    \"react-chartjs-2\": \"^2.7.2\",\n    \"react-dom\": \"^16.4.1\",\n    \"react-p5-wrapper\": \"^2.0.0\",\n    \"react-scripts\": \"^3.2.0\",\n    \"react-youtube\": \"^7.9.0\",\n    \"rxjs\": \"^6.5.3\"\n  },\n  \"scripts\": {\n    \"start\": \"react-scripts --max_old_space_size=4096 start\",\n    \"build\": \"react-scripts --max_old_space_size=4096 build\",\n    \"test\": \"react-scripts test --env=jsdom\",\n    \"eject\": \"react-scripts eject\"\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.2%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <meta http-equiv=\"Cache-Control\" content=\"no-cache, no-store, must-revalidate\" />\n    <meta http-equiv=\"Pragma\" content=\"no-cache\" />\n    <meta http-equiv=\"Expires\" content=\"0\" />\n    <title>EEGEdu</title>\n\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/@shopify/polaris@4.9.0/styles.min.css\" />\n  </head\n  >\n  <body>\n    <div id=\"root\"></div>\n\n    <!-- The core Firebase JS SDK is always required and must be listed first -->\n    <script src=\"https://www.gstatic.com/firebasejs/7.6.1/firebase-app.js\"></script>\n    <script src=\"https://www.gstatic.com/firebasejs/7.6.1/firebase-performance.js\"></script>\n    <script src=\"https://www.gstatic.com/firebasejs/7.6.1/firebase-analytics.js\"></script>\n    <script>\n      const firebaseConfig = {\n        apiKey: \"AIzaSyDOxvc3ajTHyZLXcVPkqb7Nbeo3Tn8eFCs\",\n        authDomain: \"eegedu.firebaseapp.com\",\n        databaseURL: \"https://eegedu.firebaseio.com\",\n        projectId: \"eegedu\",\n        storageBucket: \"eegedu.appspot.com\",\n        messagingSenderId: \"987114688746\",\n        appId: \"1:987114688746:web:b13b68f24897ca97440793\",\n        measurementId: \"G-2HB7VKGKV9\"\n      };\n      // Initialize Firebase\n      firebase.initializeApp(firebaseConfig);\n      var perf = firebase.performance();\n      firebase.analytics();\n\n      document.addEventListener(\"DOMContentLoaded\", () => {\n        try {\n          firebase.app();\n        } catch (e) {\n          console.error(e);\n          console.log(\"Error loading the Firebase SDK, check the console.\");\n        }\n      });\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n  \"short_name\": \"EEGEdu\",\n  \"name\": \"EEGedu Application\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    }\n  ],\n  \"start_url\": \"./index.html\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "src/components/App/App.js",
    "content": "import React from \"react\";\nimport { PageSwitcher } from \"../PageSwitcher/PageSwitcher\";\nimport { AppProvider, Card, Page, Link } from \"@shopify/polaris\";\nimport enTranslations from \"@shopify/polaris/locales/en.json\";\nimport * as translations from \"./translations/en.json\";\n\nexport function App() {\n  return (\n    <AppProvider i18n={enTranslations}>\n      <Page title={translations.title} subtitle={translations.subtitle}>\n        <PageSwitcher />\n        <Card sectioned>\n          <p>{translations.footer}\n          A  \n          <Link url=\"http://kylemathewson.com\"> Ky</Link>\n          <Link url=\"http://korymathewson.com\">Kor</Link>\n          <Link url=\"http://keyfer.ca\">Key </Link>\n          Production.\n          <Link url=\"https://github.com/kylemath/EEGEdu/\"> Github source code </Link>\n        </p>\n        </Card>\n      </Page>\n    </AppProvider>\n  );\n}\n"
  },
  {
    "path": "src/components/App/translations/en.json",
    "content": "{\n  \"title\": \"EEGEdu\",\n  \"subtitle\": [\n    \"Welcome to the EEGEdu live EEG tutorial. \",\n    \"This tutorial will help you learn about how neurons produce electrical activity we can measure. \",\n    \"By sticking electrodes on the head we can pick up these changes in electricity. \",\n    \"The tutorial will walk you through the basics of EEG signal generation, data collection, and analysis with a focus on live control based on physiological signals. \",\n    \"All demos are done in this browser. \",\n    \"This tutorial is designed to be used with the Muse and the Muse 2 headbands from Interaxon. \",\n    \"If you do not have one handy, there is an option to stream mock data as well. \",\n    \"Muse with two auxillary ports made in 2014 will not work. \",\n    \"This tutorial has been tested on Android Pixels (Mobile) and Mac OSX (laptop) with the latest chrome browser. \",\n    \"The first step will be to turn on your Muse headband and click the connect button. \",\n    \"This will open a screen and will list available Muse devices. \",\n    \"Select the serial number written on your Muse. \",\n    \"If you do not have a Muse headband you can click the Mock Data button to use simluated data. \",\n    \"Then scroll down to see you live brain activity!\"\n  ],\n  \"footer\": \"EEGEdu - An Interactive Electrophysiology Tutorial with the Muse brought to you by Mathewson Sons. \"\n\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/PageSwitcher.js",
    "content": "import React, { useState, useCallback } from \"react\";\nimport { MuseClient } from \"muse-js\";\nimport { Select, Card, Stack, Button, ButtonGroup, Checkbox } from \"@shopify/polaris\";\n\nimport { mockMuseEEG } from \"./utils/mockMuseEEG\";\nimport * as translations from \"./translations/en.json\";\nimport * as generalTranslations from \"./components/translations/en\";\nimport { emptyAuxChannelData } from \"./components/chartOptions\";\n\nimport * as funIntro from \"./components/EEGEduIntro/EEGEduIntro\"\nimport * as funHeartRaw from \"./components/EEGEduHeartRaw/EEGEduHeartRaw\"\nimport * as funHeartSpectra from \"./components/EEGEduHeartSpectra/EEGEduHeartSpectra\"\nimport * as funRaw from \"./components/EEGEduRaw/EEGEduRaw\";\nimport * as funSpectra from \"./components/EEGEduSpectra/EEGEduSpectra\";\nimport * as funBands from \"./components/EEGEduBands/EEGEduBands\";\nimport * as funAnimate from \"./components/EEGEduAnimate/EEGEduAnimate\";\nimport * as funSpectro from \"./components/EEGEduSpectro/EEGEduSpectro\";\nimport * as funAlpha from \"./components/EEGEduAlpha/EEGEduAlpha\";\nimport * as funSsvep from \"./components/EEGEduSsvep/EEGEduSsvep\";\nimport * as funEvoked from \"./components/EEGEduEvoked/EEGEduEvoked\";\nimport * as funPredict from \"./components/EEGEduPredict/EEGEduPredict\";\n\nconst intro = translations.types.intro;\nconst heartRaw = translations.types.heartRaw;\nconst heartSpectra = translations.types.heartSpectra;\nconst raw = translations.types.raw;\nconst spectra = translations.types.spectra;\nconst bands = translations.types.bands;\nconst animate = translations.types.animate;\nconst spectro = translations.types.spectro;\nconst alpha = translations.types.alpha;\nconst ssvep = translations.types.ssvep;\nconst evoked = translations.types.evoked;\nconst predict = translations.types.predict;\n\nexport function PageSwitcher() {\n\n  // For auxEnable settings\n  const [checked, setChecked] = useState(false);\n  const handleChange = useCallback((newChecked) => setChecked(newChecked), []);\n  window.enableAux = checked;\n  if (window.enableAux) {\n    window.nchans = 5;\n  } else {\n    window.nchans = 4;\n  }\n  let showAux = true; // if it is even available to press (to prevent in some modules)\n\n  // data pulled out of multicast$\n  const [introData, setIntroData] = useState(emptyAuxChannelData)\n  const [heartRawData, setHeartRawData] = useState(emptyAuxChannelData);\n  const [heartSpectraData, setHeartSpectraData] = useState(emptyAuxChannelData);\n  const [rawData, setRawData] = useState(emptyAuxChannelData);\n  const [spectraData, setSpectraData] = useState(emptyAuxChannelData); \n  const [bandsData, setBandsData] = useState(emptyAuxChannelData);\n  const [animateData, setAnimateData] = useState(emptyAuxChannelData);\n  const [spectroData, setSpectroData] = useState(emptyAuxChannelData);\n  const [alphaData, setAlphaData] = useState(emptyAuxChannelData);\n  const [ssvepData, setSsvepData] = useState(emptyAuxChannelData);\n  const [evokedData, setEvokedData] = useState(emptyAuxChannelData);\n  const [predictData, setPredictData] = useState(emptyAuxChannelData);\n\n  // pipe settings\n  const [introSettings] = useState(funIntro.getSettings);\n  const [heartRawSettings] = useState(funHeartRaw.getSettings);\n  const [heartSpectraSettings, setHeartSpectraSettings] = useState(funHeartSpectra.getSettings);\n  const [rawSettings, setRawSettings] = useState(funRaw.getSettings); \n  const [spectraSettings, setSpectraSettings] = useState(funSpectra.getSettings); \n  const [bandsSettings, setBandsSettings] = useState(funBands.getSettings);\n  const [animateSettings, setAnimateSettings] = useState(funAnimate.getSettings);\n  const [spectroSettings, setSpectroSettings] = useState(funSpectro.getSettings);\n  const [alphaSettings, setAlphaSettings] = useState(funAlpha.getSettings);\n  const [ssvepSettings, setSsvepSettings] = useState(funSsvep.getSettings);\n  const [evokedSettings, setEvokedSettings] = useState(funEvoked.getSettings);\n  const [predictSettings, setPredictSettings] = useState(funPredict.getSettings);\n\n  // connection status\n  const [status, setStatus] = useState(generalTranslations.connect);\n\n  // for picking a new module\n  const [selected, setSelected] = useState(intro);\n  const handleSelectChange = useCallback(value => {\n    setSelected(value);\n\n    console.log(\"Switching to: \" + value);\n\n    if (window.subscriptionIntro) window.subscriptionIntro.unsubscribe();\n    if (window.subscriptionHeartRaw) window.subscriptionHeartRaw.unsubscribe();\n    if (window.subscriptionHeartSpectra) window.subscriptionHeartSpectra.unsubscribe();\n    if (window.subscriptionRaw) window.subscriptionRaw.unsubscribe();\n    if (window.subscriptionSpectra) window.subscriptionSpectra.unsubscribe();\n    if (window.subscriptionBands) window.subscriptionBands.unsubscribe();\n    if (window.subscriptionAnimate) window.subscriptionAnimate.unsubscribe();\n    if (window.subscriptionSpectro) window.subscriptionSpectro.unsubscribe();\n    if (window.subscriptionAlpha) window.subscriptionAlpha.unsubscribe();\n    if (window.subscriptionSsvep) window.subscriptionSsvep.unsubscribe();\n    if (window.subscriptionEvoked) window.subscriptionEvoked.unsubscribe();\n    if (window.subscriptionPredict) window.subscriptionPredict.unsubscribe();\n\n    subscriptionSetup(value);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  // for popup flag when recording\n  const [recordPop, setRecordPop] = useState(false);\n  const recordPopChange = useCallback(() => setRecordPop(!recordPop), [recordPop]);\n\n  // for popup flag when recording 2nd condition\n  const [recordTwoPop, setRecordTwoPop] = useState(false);\n  const recordTwoPopChange = useCallback(() => setRecordTwoPop(!recordTwoPop), [recordTwoPop]);\n\n  switch (selected) {\n    case intro:\n      showAux = false;\n      break\n    case heartRaw:\n      showAux = false;\n      break\n    case heartSpectra:\n      showAux = false;\n      break\n    case raw:\n      showAux = true;\n      break\n    case spectra:\n      showAux = true;\n      break\n    case bands: \n      showAux = true;\n      break\n    case animate: \n      showAux = false;\n      break\n    case spectro:\n      showAux = false;\n      break\n    case alpha:\n      showAux = true;\n      break\n    case ssvep:\n      showAux = true;\n      break\n    case evoked:\n      showAux = true;\n      break\n    case predict:\n      showAux = false;\n      break\n    default:\n      console.log(\"Error on showAux\");\n  }\n\n\n  const chartTypes = [\n    { label: intro, value: intro },\n    { label: heartRaw, value: heartRaw },\n    { label: heartSpectra, value: heartSpectra },\n    { label: raw, value: raw },\n    { label: spectra, value: spectra }, \n    { label: bands, value: bands },\n    { label: animate, value: animate },\n    { label: spectro, value: spectro },\n    { label: alpha, value: alpha },\n    { label: ssvep, value: ssvep },\n    { label: evoked, value: evoked },\n    { label: predict, value: predict }\n\n  ];\n\n  function buildPipes(value) {\n    funIntro.buildPipe(introSettings);\n    funHeartRaw.buildPipe(heartRawSettings);\n    funHeartSpectra.buildPipe(heartSpectraSettings);\n    funRaw.buildPipe(rawSettings);\n    funSpectra.buildPipe(spectraSettings);\n    funBands.buildPipe(bandsSettings);\n    funAnimate.buildPipe(animateSettings);\n    funSpectro.buildPipe(spectroSettings);\n    funAlpha.buildPipe(alphaSettings);\n    funSsvep.buildPipe(ssvepSettings);\n    funEvoked.buildPipe(evokedSettings);\n    funPredict.buildPipe(predictSettings);\n  }\n\n  function subscriptionSetup(value) {\n    switch (value) {\n      case intro:\n        funIntro.setup(setIntroData, introSettings);\n        break;\n      case heartRaw:\n        funHeartRaw.setup(setHeartRawData, heartRawSettings);\n        break;\n      case heartSpectra:\n        funHeartSpectra.setup(setHeartSpectraData, heartSpectraSettings);\n        break;\n      case raw:\n        funRaw.setup(setRawData, rawSettings);\n        break;\n      case spectra:\n        funSpectra.setup(setSpectraData, spectraSettings);\n        break;\n      case bands:\n        funBands.setup(setBandsData, bandsSettings);\n        break;\n      case animate:\n        funAnimate.setup(setAnimateData, animateSettings);\n        break;\n      case spectro: \n        funSpectro.setup(setSpectroData, spectroSettings);\n        break;\n      case alpha:\n        funAlpha.setup(setAlphaData, alphaSettings);\n        break;\n      case ssvep:\n        funSsvep.setup(setSsvepData, ssvepSettings);\n        break;\n      case evoked:\n        funEvoked.setup(setEvokedData, evokedSettings);\n        break;\n      case predict:\n        funPredict.setup(setPredictData, predictSettings);\n        break;\n      default:\n        console.log(\n          \"Error on handle Subscriptions. Couldn't switch to: \" + value\n        );\n    }\n  }\n\n  async function connect() {\n    try {\n      if (window.debugWithMock) {\n        // Debug with Mock EEG Data\n        setStatus(generalTranslations.connectingMock);\n        window.source = {};\n        window.source.connectionStatus = {};\n        window.source.connectionStatus.value = true;\n        window.source.eegReadings$ = mockMuseEEG(256);\n        setStatus(generalTranslations.connectedMock);\n      } else {\n        // Connect with the Muse EEG Client\n        setStatus(generalTranslations.connecting);\n        window.source = new MuseClient();\n        window.source.enableAux = window.enableAux;\n        await window.source.connect();\n        await window.source.start();\n        window.source.eegReadings$ = window.source.eegReadings;\n        setStatus(generalTranslations.connected);\n      }\n      if (\n        window.source.connectionStatus.value === true &&\n        window.source.eegReadings$\n      ) {\n        buildPipes(selected);\n        subscriptionSetup(selected);\n      }\n    } catch (err) {\n      setStatus(generalTranslations.connect);\n      console.log(\"Connection error: \" + err);\n    }\n  }\n\n  function refreshPage(){\n    window.location.reload();\n  }\n\n  function pipeSettingsDisplay() {\n    switch(selected) {\n      case intro:\n        return null\n      case heartRaw:\n        return null\n      case heartSpectra:\n        return null\n      case raw:\n        return (\n          funRaw.renderSliders(setRawData, setRawSettings, status, rawSettings)\n        );\n      case spectra:\n        return (\n          funSpectra.renderSliders(setSpectraData, setSpectraSettings, status, spectraSettings)\n        );\n      case bands: \n        return (\n          funBands.renderSliders(setBandsData, setBandsSettings, status, bandsSettings)\n        );\n      case animate:\n        return (\n          funAnimate.renderSliders(setAnimateData, setAnimateSettings, status, animateSettings)\n        );\n      case spectro:\n        return (\n          funSpectro.renderSliders(setSpectroData, setSpectroSettings, status, spectroSettings)\n        );\n      case alpha:\n        return (\n          funAlpha.renderSliders(setAlphaData, setAlphaSettings, status, alphaSettings)\n        );\n      case ssvep:\n        return (\n          funSsvep.renderSliders(setSsvepData, setSsvepSettings, status, ssvepSettings)\n        );\n      case evoked:\n        return null\n      case predict: \n        return (\n          funPredict.renderSliders(setPredictData, setPredictSettings, status, predictSettings)\n        );\n      default: console.log('Error rendering settings display');\n    }\n  }\n\n  function renderModules() {\n    switch (selected) {\n      case intro:\n        return <funIntro.renderModule data={introData} />;\n      case heartRaw:\n        return <funHeartRaw.renderModule data={heartRawData} />;\n      case heartSpectra:\n        return <funHeartSpectra.renderModule data={heartSpectraData} />;\n      case raw:\n        return <funRaw.renderModule data={rawData} />;\n      case spectra:\n        return <funSpectra.renderModule data={spectraData} />;\n      case bands:\n        return <funBands.renderModule data={bandsData} />;\n      case animate:\n        return <funAnimate.renderModule data={animateData} />;\n      case spectro:\n        return <funSpectro.renderModule data={spectroData} />;\n      case alpha: \n        return <funAlpha.renderModule data={alphaData} />;\n      case ssvep:\n        return <funSsvep.renderModule data={ssvepData} />;\n      case evoked:\n        return <funEvoked.renderModule data={evokedData} />;\n      case predict:\n        return <funPredict.renderModule data={predictData} />;\n      default:\n        console.log(\"Error on renderCharts switch.\");\n    }\n  }\n \n  function renderRecord() {\n    switch (selected) {\n      case intro: \n        return null\n      case heartRaw:\n        return (\n          funHeartRaw.renderRecord(recordPopChange, recordPop, status, heartRawSettings)\n        )\n      case heartSpectra:\n        return (\n          funHeartSpectra.renderRecord(recordPopChange, recordPop, status, heartSpectraSettings, setHeartSpectraSettings)\n        )\n      case raw: \n        return (\n          funRaw.renderRecord(recordPopChange, recordPop, status, rawSettings, setRawSettings)\n        )    \n      case spectra:\n        return (\n          funSpectra.renderRecord(recordPopChange, recordPop, status, spectraSettings, setSpectraSettings)\n        )      \n      case bands:\n        return (\n          funBands.renderRecord(recordPopChange, recordPop, status, bandsSettings, setBandsSettings)\n        ) \n      case animate:\n        return null\n      case spectro:\n        return null\n      case alpha:\n        return (\n          funAlpha.renderRecord(recordPopChange, recordPop, status, alphaSettings, recordTwoPopChange, recordTwoPop, setAlphaSettings)\n        )\n      case ssvep:\n        return (\n          funSsvep.renderRecord(recordPopChange, recordPop, status, ssvepSettings, recordTwoPopChange, recordTwoPop, setSsvepSettings)\n        )\n      case evoked:\n        return (\n          funEvoked.renderRecord(recordPopChange, recordPop, status, evokedSettings, setEvokedSettings)\n        )\n      case predict:\n        return (\n          funPredict.renderRecord(recordPopChange, status)\n        )\n      default:   \n        console.log(\"Error on renderRecord.\");\n    }\n  }\n\n  // Render the entire page using above functions\n  return (\n    <React.Fragment>\n      <Card sectioned>\n        <Stack>\n          <ButtonGroup>\n            <Button\n              primary={status === generalTranslations.connect}\n              disabled={status !== generalTranslations.connect}\n              onClick={() => {\n                window.debugWithMock = false;\n                connect();\n              }}\n            >\n              {status}\n            </Button>\n            <Button\n              disabled={status !== generalTranslations.connect}\n              onClick={() => {\n                window.debugWithMock = true;\n                connect();\n              }}\n            >\n              {status === generalTranslations.connect ? generalTranslations.connectMock : status}\n            </Button>\n            <Button\n              destructive\n              onClick={refreshPage}\n              primary={status !== generalTranslations.connect}\n              disabled={status === generalTranslations.connect}\n            >\n              {generalTranslations.disconnect}\n            </Button>     \n          </ButtonGroup>\n          <Checkbox\n            label=\"Enable Muse Auxillary Channel\"\n            checked={checked}\n            onChange={handleChange}\n            disabled={!showAux || status !== generalTranslations.connect}\n          />\n        </Stack>\n      </Card>\n      <Card title={translations.title} sectioned>\n        <Select\n          label={\"\"}\n          options={chartTypes}\n          onChange={handleSelectChange}\n          value={selected}\n        />\n      </Card>\n      {pipeSettingsDisplay()}\n      {renderModules()}\n      {renderRecord()}\n    </React.Fragment>\n  );\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAlpha/EEGEduAlpha.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal, Link } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { take, takeUntil } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\n\nimport { zipSamples } from \"muse-js\";\nimport YouTube from 'react-youtube'\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport P5Wrapper from 'react-p5-wrapper';\nimport sketchFixation from \"./sketchFixation\"\n\nexport function getSettings() {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 100,\n    bins: 256,\n    sliceFFTLow: 1,\n    sliceFFTHigh: 30,\n    duration: 1024,\n    srate: 256,\n    name: 'Alpha',\n    secondsToSave: 60\n  }\n};\n\nconst conds = ['Open', 'Closed'];\nconst thisRand = Math.floor(Math.random() * 2); \nconst firstType = conds[thisRand];\n\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionAlpha) window.subscriptionAlpha.unsubscribe();\n\n  window.pipeAlpha$ = null;\n  window.multicastAlpha$ = null;\n  window.subscriptionAlpha = null;\n\n  // Build Pipe \n  window.pipeAlpha$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n\n  window.multicastAlpha$ = window.pipeAlpha$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastAlpha$) {\n    window.subscriptionAlpha = window.multicastAlpha$.subscribe(data => {\n      setData(alphaData => {\n        Object.values(alphaData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[index];\n          channel.xLabels = data.freqs;\n        });\n\n        return {\n          ch0: alphaData.ch0,\n          ch1: alphaData.ch1,\n          ch2: alphaData.ch2,\n          ch3: alphaData.ch3,\n          ch4: alphaData.ch4\n        };\n      });\n    });\n\n    window.multicastAlpha$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      if (index === 0) {\n      const options = {\n        ...generalOptions,\n        scales: {\n          xAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.xAxes[0].scaleLabel,\n                labelString: specificTranslations.xlabel\n              }\n            }\n          ],\n          yAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.yAxes[0].scaleLabel,\n                labelString: specificTranslations.ylabel\n              },\n              ticks: {\n                max: 25,\n                min: 0\n              }\n            }\n          ]\n        },\n        elements: {\n          point: {\n            radius: 3\n          }\n        },\n        title: {\n          ...generalOptions.title,\n          text: generalTranslations.channel + channelNames[index]\n        }\n      };\n\n        return (\n          <Card.Section key={\"Card_\" + index}>\n            <Line key={\"Line_\" + index} data={channel} options={options} />\n          </Card.Section>\n        );\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n        </Stack>\n      </Card.Section>\n      <Card.Section title={'Previous Module'}>\n        <TextContainer>\n          <p> {\n            \"Last module we recorded the frequency bands while people had their eyes open or closed for 10 seconds. Although we predicted there should be larger alpha power when the eyes are closed, are results showed the opposite effect:\"\n          } </p>\n        </TextContainer>\n        <br />\n        <img \n          src={ require(\"./module5.png\")} \n          alt=\"module5\"\n          width=\"50%\"\n          height=\"auto\"\n        ></img> \n        <br />     \n        <br />     \n        <TextContainer>\n          <p> {\n            \"We pooled our estimates for alpha in eyes open vs eyes closed as a class, and computed the average and compared the results with a paired samples t-test. As you can see, even if we remove some of the outliers with extreme values that may have been artifacts, we still find that for most people we estimated larger alpha power when their eyes were open. Why could this be?\"\n          } \n        <Link url=\"https://docs.google.com/spreadsheets/d/1d4oakU6ER_fDO-t2eHk1Wrj3F99eZYZGkjVXVXVwNQs/edit?usp=sharing\"\n                external={true}\n        > You can access a read only copy of the spreadsheet we used to compute these group level effects here. </Link>  \n        </p>\n        </TextContainer>   \n      </Card.Section>\n      <Card.Section title={'Limitations'}>\n        <TextContainer>\n          <p> {\n            \"We discussed in class that we had very little control over WHAT people were doing while their eyes were open. Some were looking around, some may have been blinking, some may have been reading their screen. There was a great deal of variability in the EEG signal due to differences in peoples behaviour during our task. As we have seen in previous modules, these kind of artifacts can create artificial power in most lower frequency bands. Because we were looking just at the alpha frequency band, we did not estimate the full range of differences across frequencies. \"\n          } </p>\n        </TextContainer>\n        <br />\n        <TextContainer>\n          <p> {\n            \"Furthermore, the short recording time (10 Seconds) also allows for less reliable measurement of the EEG signal. In addition, people may not have been prepared when the button was pressed to start their recording. \"\n          } </p>\n        </TextContainer>\n      </Card.Section>\n      <Card.Section title={'New Experiment'}>\n        <TextContainer>\n          <p> {\n            \"Based on all these ideas, in this module we are going to run a more controlled experiment comparing the EEG during eyes open and eyes closed conditions. Instead of just recording averages over frequency bands, we will record the whole spectra.\"\n          } </p>\n        </TextContainer>\n        <br />\n        <TextContainer>\n          <p> {\n            \"This module, the recording sessions will be 60 seconds long. They will also begin with a 2 second delay after pressing the button. A window will pop up with a red fixation cross. In the eyes open condition participants should keep their eyes fixed on this red fixation cross the entire 60 seconds, and should try to minimize blinking as much as possible.\"\n          } </p>\n        </TextContainer>\n        <br />\n        <TextContainer>\n          <p> {\n            \"Please DO NOT change any of the settings. The module will pick a random number to tell you which condition to run first, so that we can counterbalance the order. Keep track of this order in a lab notebook you will submit.\"\n          } </p>\n        </TextContainer>\n        <br />     \n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={128} step={128} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={10} step={5} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} max={Settings.sliceFFTHigh - 1}\n        label={'Slice FFT Lower limit: ' + Settings.sliceFFTLow + ' Hz'} \n        value={Settings.sliceFFTLow} \n        onChange={handleSliceFFTLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.sliceFFTLow + 1}\n        label={'Slice FFT Upper limit: ' + Settings.sliceFFTHigh + ' Hz'} \n        value={Settings.sliceFFTHigh} \n        onChange={handleSliceFFTHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, recordTwoPopChange, recordTwoPop, setSettings) {\n  \n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n\n  const recordDelay = 2000;\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return(\n    <Card title={'Record ' + Settings.name +' Data'} sectioned>\n      <Stack>\n        <TextContainer>\n          <p> {\n            \"Each person will record two sessions, one with eyes open and one with eyes closed. The output files are identical to those we recorded in the spectra module 5. \"\n          } </p>\n        </TextContainer>   \n        <TextContainer>\n          <p> {\n            \"The random number generator suggest you run the following condition first: \" + firstType\n          } </p>\n        </TextContainer>  \n        <br />\n        <ButtonGroup>\n                <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />\n          <Button \n            onClick={() => {\n              recordPopChange();\n              setTimeout(() => {  \n                saveToCSV(Settings, \"Closed\");\n               }, recordDelay);\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect || recordPop}\n          > \n            {'Record Eyes Closed Data'}  \n          </Button>\n          <Button \n            onClick={() => {\n              recordTwoPopChange();\n              setTimeout(() => {  \n                saveToCSV(Settings, \"Open\");\n              }, recordDelay); \n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect || recordTwoPop}\n          > \n            {'Record Eyes Open Data'}  \n          </Button> \n        </ButtonGroup>\n        <TextContainer>\n          <p> {\n            \"Instead of averaging over all electrodes, we will separately average the frontal and temporal electrodes to look for differences in different locations on the head. First you will each analyze your own data to summarize what you found. We will average the spectra values over the entire 60 seconds, and also over contralateral pairs of electrodes. You can watch a video showing you how to do this if you need a reminder here: \"\n          } </p>\n        </TextContainer>          \n        <YouTube \n            videoId=\"D9skUfAOstE\"\n            opts={opts}\n          />\n        <TextContainer>\n          <p> {\n            \"Finally, we will paste the resulting average spectra in each condition, at each of the two electrode locations into a group spreadsheet that we will use for our final assignment paper in the class. To clarify, each participant will make two recordings 1) Eyes open, and 2) Eyes closed. For each, the analysis will produce two arrays of numbers (spectra), one for Frontal electrodes, and one for posterior electrodes. Therefore there are four different tabs in the following group data log:\"\n          } </p>\n        </TextContainer>\n        <Link url=\"https://docs.google.com/spreadsheets/d/1Ip8Xitp548DVXikZhL55Ll-Vgg9UAFU0-N40_3-e8sw/edit?usp=sharing\"\n                external={true}\n        > Group Data Log (4 tabs) </Link>  \n\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title=\"Recording Eye Closed Data\"\n        >\n          <Modal.Section>\n           <Card.Section>\n              <P5Wrapper sketch={sketchFixation} />          \n            </Card.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n        <Modal\n          open={recordTwoPop}\n          onClose={recordTwoPopChange}\n          title=\"Recording Eyes Open Data\"\n        >\n          <Modal.Section>\n           <Card.Section>\n              <P5Wrapper sketch={sketchFixation} />          \n            </Card.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>        \n      </Stack>\n    </Card>\n  )\n}\n\n\nfunction saveToCSV(Settings, condition) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastAlpha$.pipe(\n    take(1)\n  );\n\n  localObservable$.subscribe({ \n    next(x) { \n      let freqs = Object.values(x.freqs);\n      dataToSave.push(\n        \"Timestamp (ms),\",\n        freqs.map(function(f) {return \"ch0_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch1_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch2_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch3_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"chAux_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"f_\" + f + \"Hz\"}) + \",\" , \n        \"info\", \n        \"\\n\"\n      );   \n    }\n  });\n\n  // setup timer\n  const timer$ = timer(Settings.secondsToSave * 1000)\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastAlpha$.pipe(\n    takeUntil(timer$)\n  );   \n\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_\" +  condition +  \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAlpha/sketchFixation.js",
    "content": "export default function sketchFixation (p) {\n\n p.setup = function () {\n    p.createCanvas(300, 300);\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(300, 300);\n  }\n\n\n  p.mousePressed = function () {\n    p.background(256);\n  }\n\n  p.draw = function () {\n    p.background(255);\n    p.fill(255,0,0);\n    p.text(\"+\", p.width/2, p.height/2);\n\n  }\n\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAlpha/translations/en.json",
    "content": "{\n  \"title\": \"Eyes open vs. Eyes Closed Alpha Experiment\",\n  \"description\": [\n    \"In the next demo we run our first experiment, comparing the spectra when eyes are open vs when they are closed. \",\n    \"In this module you will first adjust the muse so the signal is good, and then record two sessions of data. \",\n    \"In one, you will keep you eyes open and stare at a single point on the screen that pops up. \", \n    \"In the other, you will close your eyes and the recording will begin. \",\n    \"Alpha oscillations are found to be larger when the eyes are closed than when they are open.\"\n  ],\n  \"xlabel\": \"Frequency (Hz)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/EEGEduAnimate.js",
    "content": "import React, { useState, useCallback } from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { Card, Stack, TextContainer, RangeSlider, Select, Link} from \"@shopify/polaris\";\nimport { Subject } from \"rxjs\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  powerByBand\n} from \"@neurosity/pipes\";\n\nimport { chartStyles } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\nimport { bandLabels } from \"../../utils/chartUtils\";\n\nimport sketchBands from './sketchBands'\nimport sketchTone from './sketchTone'\nimport sketchCube from './sketchCube'\nimport sketchFlock from './sketchFlock'\nimport sketchDraw from './sketchDraw'\nimport sketchFlock3D from './sketchFlock3D'\n\nimport P5Wrapper from 'react-p5-wrapper';\n\nexport function getSettings () {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 16,\n    bins: 256,\n    duration: 128,\n    srate: 256,\n    name: 'Animate'\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionAnimate) window.subscriptionAnimate.unsubscribe();\n\n  window.pipeAnimate$ = null;\n  window.multicastAnimate$ = null;\n  window.subscriptionAnimate = null;\n\n  // Build Pipe\n  window.pipeAnimate$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    powerByBand(),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastAnimate$ = window.pipeAnimate$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastAnimate$) {\n    window.subscriptionAnimate = window.multicastAnimate$.subscribe(data => {\n      setData(animateData => {\n        Object.values(animateData).forEach((channel, index) => {\n            channel.datasets[0].data = [\n              data.delta[index],\n              data.theta[index],\n              data.alpha[index],\n              data.beta[index],\n              data.gamma[index]\n            ];\n            channel.xLabels = bandLabels;\n        });\n\n        return {\n          ch0: animateData.ch0,\n          ch1: animateData.ch1,\n          ch2: animateData.ch2,\n          ch3: animateData.ch3,\n          ch4: animateData.ch4\n        };\n      });\n    });\n\n    window.multicastAnimate$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function RenderCharts() {\n\n    const bands = '3D Frequency Bands';\n    const tone = 'Play simple music with your frequency bands';\n    const cube = 'Control a Cube with your Alpha Power';\n    const flock = 'Control a Flock with Alpha and Beta';\n    const draw = 'Draw a picture with Alpha and Beta';\n    const flock3d = 'Control a 3d Flock with Alpha, Beta, and Theta';\n\n    const chartTypes = [\n      { label: bands, value: bands },\n      { label: tone, value: tone },\n      { label: cube, value: cube },\n      { label: flock, value: flock },\n      { label: draw, value: draw },\n      { label: flock3d, value: flock3d }\n    ];\n\n    // for picking a new animation\n    const [selectedAnimation, setSelectedAnimation] = useState(bands);\n    const handleSelectChangeAnimation = useCallback(value => {\n      setSelectedAnimation(value);\n      console.log(\"Switching to: \" + value);\n    }, []);\n\n    return Object.values(channels.data).map((channel, index) => {\n      if (channel.datasets[0].data) {\n        if (index === 1) {\n          // console.log( channel.datasets[0].data[2])\n          window.delta = channel.datasets[0].data[0];\n          window.theta = channel.datasets[0].data[1];\n          window.alpha = channel.datasets[0].data[2];\n          window.beta  = channel.datasets[0].data[3];\n          window.gamma = channel.datasets[0].data[4];\n        }\n      }   \n\n      let thisSketch = sketchTone;\n\n      switch (selectedAnimation) {\n        case bands:\n          thisSketch = sketchBands;\n          break\n        case tone:\n          thisSketch = sketchTone;\n          break\n        case cube:\n          thisSketch = sketchCube;\n          break\n        case flock:\n          thisSketch = sketchFlock;\n          break\n        case draw:\n          thisSketch = sketchDraw;\n          break\n        case flock3d:\n          thisSketch = sketchFlock3D;\n          break\n        default: console.log(\"Error on switch to \" + selectedAnimation)\n      }\n\n      //only left frontal channel\n      if (index === 1) {\n        return (\n          <React.Fragment key={'dum'}>\n            <Card.Section \n              title={\"Choice of Sketch\"}\n            >\n              <Select\n                label={\"\"}\n                options={chartTypes}\n                onChange={handleSelectChangeAnimation}\n                value={selectedAnimation}\n              />\n            </Card.Section>\n            <Card.Section>\n              <P5Wrapper sketch={thisSketch} \n                delta={window.delta}\n                theta={window.theta}\n                alpha={window.alpha}\n                beta={window.beta}\n                gamma={window.gamma}\n              />          \n            </Card.Section>\n          </React.Fragment>\n        );\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n           <img \n            src={ require(\"./electrodediagram2.png\")} \n            alt=\"F7Electrode\"\n            width=\"25%\"\n            height=\"auto\"\n          ></img>\n          <Link url=\"https://p5js.org/learn/interactivity.html\"\n              external={true}>\n              Link to learn more about making your own easy P5.js animations </Link>\n          <br />\n           <Link url=\"https://p5js.org/examples/\"\n              external={true}>\n              Examples of other P5.js animations to get started </Link>\n          <TextContainer>\n            <p>{[\n              \"We are working on allowing for people to make their own animations with a p5.js editor. \", \n              \"For now if you want us to make a new one make a github issue with your p5.js animation and describe your idea\"\n              ]}</p>\n          </TextContainer>\n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{RenderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings);\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={128} step={128} max={4096} \n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={10} step={5} max={Settings.duration} \n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={.01} step={.5} max={Settings.cutOffHigh - .5} \n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2} \n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchBands.js",
    "content": "export default function sketchBands (p) {\n  let delta = 0;\n  let theta = 0;\n  let alpha = 0;\n  let beta = 0;\n  let gamma = 0;\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.5, p.windowWidth*.5, p.WEBGL);\n  };\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.5, p.windowWidth*.5);\n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    delta = props.delta;\n    theta = props.theta;\n    alpha = props.alpha;\n    beta = props.beta;\n    gamma = props.gamma;\n  };\n\n  p.draw = function () {\n\n    let unit = p.width/5;\n\n    p.background(256);\n    p.ambientMaterial(250);\n    p.noStroke();\n\n    let locX = p.mouseX - p.height / 2;\n    let locY = p.mouseY - p.width / 2;\n\n    p.ambientLight(60, 60, 60);\n    p.pointLight(255, 255, 255, locX, locY, 100);\n\n\n    p.push();\n    p.fill(255,0,0);\n    p.translate(-unit,0);\n    p.rotateY(50);\n    p.rotateX(50);\n    p.box(unit/2, delta* 10, unit);\n    p.pop();\n\n    p.push();\n    p.fill(0,255,0);\n    p.translate(-unit/2,0);\n    p.rotateY(50);\n    p.rotateX(50);\n    p.box(unit/2, theta* 10, unit);\n    p.pop();\n\n    p.push();\n    p.fill(0,0,255);\n    p.translate(0,0);\n    p.rotateY(50);\n    p.rotateX(50);\n    p.box(unit/2, alpha* 10, unit);\n    p.pop();\n\n    p.push();\n    p.fill(0,128, 128);\n    p.translate(unit/2,0);\n    p.rotateY(50);\n    p.rotateX(50);\n    p.box(unit/2, beta* 10, unit);\n    p.pop();\n\n    p.push();\n    p.fill(128,0,128);\n    p.translate(unit,0);\n    p.rotateY(50);\n    p.rotateX(50);\n    p.box(unit/2, gamma* 10, unit);\n    p.pop();\n  };\n\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchCube.js",
    "content": "export default function sketchCube (p) {\n  let delta = 0;\n  // let theta = 0;\n  // let alpha = 0;\n  // let beta = 0;\n  // let gamma = 0;\n\n  let rotation = 0;\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.6, 800, p.WEBGL);\n  };\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.6, 800);\n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    delta = Math.floor(props.delta);\n    // theta = props.theta;\n    // alpha = props.alpha;\n    // beta = props.beta;\n    // gamma = props.gamma;\n\n    if (props.alpha){\n      rotation = props.alpha * Math.PI / 180;\n    }\n  };\n\n  p.draw = function () {\n    p.background(256);\n    p.normalMaterial();\n    p.noStroke();\n    p.push();\n    p.rotateX(25 + p.frameCount/10);\n    p.rotateY(rotation);\n    p.box(delta*10);\n    p.pop();\n  };\n\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchDraw.js",
    "content": "export default function sketchDraw (p) {\n  let delta = 0;\n  let theta = 0;\n  let alpha = 0;\n  let beta = 0;\n  let gamma = 0;\n\n  let xVar = 0;\n  let yVar = 0;\n\n  let brushWidth = 50;\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.6, 500);\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(p.windowWidth*.6, 500);\n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    delta = Math.floor((props.delta/20) * 255);\n    theta = Math.floor((props.theta/10) * 255);\n    alpha = Math.floor((props.alpha/5) * p.width);\n    beta =  Math.floor((props.beta/2) * p.height);\n    gamma = Math.floor((props.gamma/2) * 255);\n\n    xVar = alpha;\n    yVar = beta;\n\n    if (xVar > p.width) {\n      xVar = p.width-brushWidth/2;\n    }\n    if (yVar > p.height) {\n      yVar = p.height-brushWidth/2;\n    }\n\n    // console.log(xVar)\n    // console.log(yVar)\n  };\n\n\n  p.mousePressed = function () {\n    p.background(256);\n  }\n\n  p.draw = function () {\n    p.fill(theta, delta, gamma, 20);  \n    p.noStroke();\n    p.ellipse(xVar, yVar, brushWidth);\n  }\n\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchFlock.js",
    "content": "import p5 from \"p5\";\nimport \"p5/lib/addons/p5.sound\";\n\nexport default function sketchFlock (p) {\n\n  let alpha = 0;\n  let beta = 0;\n\n\n  let xVar = 0;\n  let yVar = 0;\n  var flock;\n\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.6, 500);\n    flock = new p.Flock();\n\n    for (var i = 0; i < 150; i++) {\n      var b = new p.Boid(p.width / 2, p.height / 2);\n      flock.addBoid(b)\n    }\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(p.windowWidth*.6, 500);\n  }\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n\n    alpha = Math.floor((props.alpha/5) * p.width);\n    beta =  Math.floor((props.beta/2) * p.height);\n\n\n    xVar = alpha;\n    yVar = beta;\n\n    if (xVar > p.width) {\n      xVar = p.width-5;\n    }\n    if (yVar > p.height) {\n      yVar = p.height-5;\n    }\n\n    // console.log(xVar)\n    // console.log(yVar)\n  };\n\n  p.draw = function () {\n    p.background(255);\n    p.fill(255,0,0)\n    p.push();\n    p.translate(xVar, yVar);\n    p.ellipse(0,0,10,10);\n    p.pop();\n    flock.run();\n  }\n\n  p.mouseDragged = function () {\n    flock.addBoid(new p.Boid(p.mouseX, p.mouseY));\n  }\n\n  p.Flock = function() {\n    this.boids = []; //Initialize the array\n  }\n\n  p.Flock.prototype.addBoid = function(b) {\n    this.boids.push(b);\n  }\n\n  p.Flock.prototype.run = function() {\n    for (var i=0; i < this.boids.length; i++) {\n      this.boids[i].run(this.boids);\n    }\n  }\n\n  p.Boid = function(x, y) {\n    this.acceleration = p.createVector(0, 0);\n    this.velocity = p.createVector(p.random(-1, 1), p.random(-1, 1));\n    this.position = p.createVector(x, y);\n    this.r = 2.0; //Size of object Boid\n    this.maxspeed = 5; // Maximum speed\n    this.maxforce = 0.1; // Maximum steer ing force\n  }\n\n  p.Boid.prototype.run = function(boids) {\n    this.flock(boids);\n    this.update();\n    this.borders();\n    this.render();\n  };\n\n  p.Boid.prototype.applyForce = function(force) {\n    // We could add mass here if we want A = F / M\n    this.acceleration.add(force);\n  };\n\n  p.Boid.prototype.flock = function(boids) {\n    var sep = this.separate(boids); \n    var ali = this.align(boids);\n    var coh = this.cohesion(boids); \n    var mows = this.mouuse(boids); \n\n    sep.mult(1.5);\n    ali.mult(1.0);\n    coh.mult(1.0);\n    mows.mult(3.0);\n\n    this.applyForce(sep);\n    this.applyForce(ali);\n    this.applyForce(coh);\n    this.applyForce(mows);\n  };\n\n\n  p.Boid.prototype.update = function() {\n    this.velocity.add(this.acceleration);\n    this.velocity.limit(this.maxspeed);\n    this.position.add(this.velocity);\n    this.acceleration.mult(0);\n  };\n\n\n  p.Boid.prototype.seek = function(target) {\n    var desired = p5.Vector.sub(target, this.position); \n    desired.normalize();\n    desired.mult(this.maxspeed);\n    var steer = p5.Vector.sub(desired, this.velocity);\n    steer.limit(this.maxforce); \n    return steer;\n  };\n\n  p.Boid.prototype.render = function() {\n    // Draw a triangle rotated in the direction of velocity\n    var theta = this.velocity.heading() + p.radians(90);\n    p.fill(0);\n    p.noStroke();\n    p.push();\n    p.translate(this.position.x, this.position.y);\n    // p.ellipse(0,0,5,5);\n    p.rotate(theta);\n    p.beginShape();\n    p.vertex(0, -this.r * 2);\n    p.vertex(-this.r, this.r * 2);\n    p.vertex(this.r, this.r * 2);\n    p.endShape(p.CLOSE);\n    p.pop();\n  };\n\n  p.Boid.prototype.borders = function() {\n    if (this.position.x < -this.r) this.position.x = p.width + this.r;\n    if (this.position.y < -this.r) this.position.y = p.height + this.r;\n    if (this.position.x > p.width + this.r) this.position.x = -this.r;\n    if (this.position.y > p.height + this.r) this.position.y = -this.r;\n  };\n\n  p.Boid.prototype.separate = function(boids) {\n    var desiredseparation = 25.0;\n    var steer = p.createVector(0, 0);\n    var count = 0;\n    // For every boid in the system, check if it's too close\n    for (var i = 0; i < boids.length; i++) {\n      var d = p5.Vector.dist(this.position, boids[i].position);\n      // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)\n      if ((d > 0) && (d < desiredseparation)) {\n        // Calculate vector pointing away from neighbor\n        var diff = p5.Vector.sub(this.position, boids[i].position);\n        diff.normalize();\n        diff.div(d); // Weight by distance\n        steer.add(diff);\n        count++; // Keep track of how many\n      }\n    }\n    if (count > 0) {\n      steer.div(count);\n    }\n    if (steer.mag() > 0) {\n      steer.normalize();\n      steer.mult(this.maxspeed);\n      steer.sub(this.velocity);\n      steer.limit(this.maxforce);\n    }\n    return steer;\n  };\n\n  // Alignment\n  // For every nearby boid in the system, calculate the average velocity\n  p.Boid.prototype.align = function(boids) {\n    var neighbordist = 100;\n    var sum = p.createVector(0, 0);\n    var count = 0;\n    for (var i = 0; i < boids.length; i++) {\n      var d = p5.Vector.dist(this.position, boids[i].position);\n      if ((d > 0) && (d < neighbordist)) {\n        sum.add(boids[i].velocity);\n        count++;\n      }\n    }\n    if (count > 0) {\n      sum.div(count);\n      sum.normalize();\n      sum.mult(this.maxspeed);\n      var steer = p5.Vector.sub(sum, this.velocity);\n      steer.limit(this.maxforce);\n      return steer;\n    } else {\n      return p.createVector(0, 0);\n    }\n  };\n\n  // Cohesion\n  // For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location\n  p.Boid.prototype.cohesion = function(boids) {\n    var neighbordist = 100;\n    var sum = p.createVector(0, 0); // Start with empty vector to accumulate all locations\n    var count = 0;\n    for (var i = 0; i < boids.length; i++) {\n      var d = p5.Vector.dist(this.position, boids[i].position);\n      if ((d > 0) && (d < neighbordist)) {\n        sum.add(boids[i].position); // Add location\n        count++;\n      }\n    }\n    if (count > 0) {\n      sum.div(count);\n      return this.seek(sum); // Steer towards the location\n    } else {\n      return p.createVector(0, 0);\n    }\n  };\n\n  p.Boid.prototype.mouuse = function(boids) {\n    var neighbordist = 500;\n    var m = p.createVector(xVar, yVar);\n    var d = p5.Vector.dist(this.position, m);\n    if ((d > 0) && (d < neighbordist)) {\n      return this.seek(m); // Steer towards the mouse location \n    } else {\n      return p.createVector(0, 0);\n    }\n  };\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchFlock3D.js",
    "content": "import p5 from 'p5';\n\nexport default function sketchFlock3D (p) {\n  \n  const flock = []; // Array of boids\n  let depth = 800; // The Z location of the boid tend to stay between +depth/2 and -depth/2\n  let gap = 300; // Boids can go further than the edges, this further distance is the gap\n  let quadTree; // A quad tree to minimize the cost of distance calculation\n\n  let useQuadTree = true; // Toogle the use of a quad tree\n  let showPerceptionRadius = false; // Toogle vizualization of perception radius\n\n  let boidsSlider, perceptionSlider, alignmentSlider, cohesionSlider, separationSlider; // Sliders\n  let boidsP, perceptionP, alignmentP, cohesionP, separationP; // Paragraphs\n  let startingBoids = 50; // Amount of boid at the start of the sketch\n  let startingPerception = 90; // Perception radius at the start of the sketch\n  let t = 0; // Counts the frame from the time boids go out of the middle of space\n\n  let theta = 0;\n  let alpha = 0;\n  let beta = 0;\n  let xVar = 0;\n  let yVar = 0;\n  let zVar = 0;\n\n  // SETUP FUNCTION ---------------------------------------------------\n  // Make the canvas, declare some variables, create the DOM elements and the initial boid population\n  p.setup = function () {\n    // Declaration of a canvas to allow canvas download\n    p.createCanvas(p.windowWidth*.5, p.windowWidth*.5, p.WEBGL); // You can change the resolution here\n\n    // Declaration of depth (z axis), unit vectors, and the camera\n    p.depth = p.height;\n    let cameraX = 1000 / 600 * p.width;\n    let cameraY = -800 / 600 * p.height;\n    let cameraZ = -200 / 500 * p.depth;\n    p.camera(cameraX, cameraY, cameraZ, 0, 0, 0, 0, 0, 1);\n    \n    // Create the DOM elements: sliders and paragraphs\n    createDOMs();\n\n    // Create an initial population of 100 boids\n    for (let i = 0; i < boidsSlider.value(); i++) {\n      pushRandomBoid();\n    }\n  }\n\n  p.windowResized = function() {\n    p.createCanvas(p.windowWidth*.5, p.windowWidth*.5);\n    p.depth = p.height;\n    let cameraX = 1000 / 600 * p.width;\n    let cameraY = -800 / 600 * p.height;\n    let cameraZ = -200 / 500 * p.depth;\n    p.camera(cameraX, cameraY, cameraZ, 0, 0, 0, 0, 0, 1);\n    \n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    theta = Math.floor((props.theta/10) * p.width);\n    alpha = Math.floor((props.alpha/10) * p.height);\n    beta =  Math.floor((props.beta/10) * p.depth);\n\n    xVar = theta-(p.width/2)+200;\n    yVar = alpha-(p.height/2)+200;\n    zVar = beta-(p.depth/2)+200; \n\n    if (Math.abs(xVar) > p.width/2) {\n      xVar = Math.sign(xVar) * (p.width/2);\n    }\n    if (Math.abs(yVar) > p.height/2) {\n      yVar = Math.sign(yVar) * (p.height/2);\n    }\n    if (Math.abs(zVar) > p.depth/2) {\n      zVar = Math.sign(zVar) * (p.depth/2);\n    }\n\n    // console.log(xVar + ' ' + yVar + ' ' + zVar)\n  };\n\n  // DRAW FUNCTION ---------------------------------------------------\n  p.draw = function () {\n    // Background and lightning\n    p.background(200);\n    //drag to move the world.\n    p.orbitControl();\n\n    p.directionalLight(150, 150, 150, 1, 1, 0);\n    p.ambientLight(150);\n    \n    // Draw the corners of a box showing the space where boids can fly\n    p.stroke(80);\n    p.strokeWeight(8);\n    p.noFill();\n    p.box(p.width + gap/2, p.height + gap/2, p.depth + gap/2);\n\n    p.noStroke();\n    p.fill(255);\n    p.ambientMaterial(0, 0, 255);\n    p.push()\n    p.translate(xVar, yVar, zVar);\n    p.sphere(5); // A sphere where the boid is\n    p.pop();\n      \n\n    // Make the quad tree\n    let boundary = new Cube(0, 0, 0, p.width + 2 * gap, p.height + 2 * gap, p.depth + 2 * gap);\n    quadTree = new QuadTree(boundary, 4);\n    for (let boid of flock) {\n      quadTree.insert(boid);\n    }\n    \n    // Each boid determines its acceleration for the next frame\n    for (let boid of flock) {\n      boid.flock(flock, quadTree);\n    }\n    // Each boid updates its position and velocity, and is displayed on screen\n    for (let boid of flock) {\n      boid.update(gap);\n      boid.show();\n    }\n\n    // Adjust the amount of boids on screen according to the slider value\n    let maxBoids = boidsSlider.value();\n    let difference = flock.length - maxBoids;\n    if (difference < 0) {\n      for (let i = 0; i < -difference; i++) {\n        pushRandomBoid(); // Add boids if there are less boids than the slider value\n      }\n    } else if (difference > 0) {\n      for (let i = 0; i < difference; i++) {\n        flock.pop(); // Remove boids if there are more boids than the slider value\n      }\n    }\n\n    // Update the DOM elements\n    boidsP.html(`Boids: ${boidsSlider.value()}`);\n    perceptionP.html(`Perception: ${perceptionSlider.value()}`);\n    alignmentP.html(`Alignment: ${alignmentSlider.value()}`);\n    cohesionP.html(`Cohesion: ${cohesionSlider.value()}`);\n    separationP.html(`Separation: ${separationSlider.value()}`);\n    \n    t++; // t counts the number of frames, it is used to not have cohesion in the first 40 frames\n  }\n\n\n  // Create the DOM elements\n  function createDOMs() {\n    // Create the paragraphs and sliders\n    boidsP = \t\t\tp.createP('Boids');\n    perceptionP = p.createP('Perception');\n    alignmentP = \tp.createP('Alignment');\n    cohesionP = \tp.createP('Cohesion');\n    separationP = p.createP('Separation');\n    \n    if (p.windowWidth * p.windowHeight > 1200 * 1200) startingPerception = 150; // Larger perception on a larger screen\n    boidsSlider = \t\t\tp.createSlider(1, 500, startingBoids, 1);\n    perceptionSlider = \tp.createSlider(0, 1000, startingPerception, 1);\n    alignmentSlider = \tp.createSlider(0, 5, 0.2, 0.1);\n    cohesionSlider = \t\tp.createSlider(0, 5, 0.3, 0.1);\n    separationSlider = \tp.createSlider(0, 5, 0.7, 0.1);\n\n    // Position the DOM elements on the top left corner\n    let DOMoffset = 1050; // Place the DOM elements underneath the canvas when we want to download the canvas\n    let DOMgap = 5; // Gap between the DOM elements\n    let leftGap = 200;\n    boidsSlider.position(\t    leftGap + DOMgap, DOMoffset + boidsSlider.height * 0 + 1 * DOMgap);\n    perceptionSlider.position(leftGap + DOMgap, DOMoffset + boidsSlider.height * 1 + 2 * DOMgap);\n    alignmentSlider.position(\tleftGap + DOMgap, DOMoffset + boidsSlider.height * 2 + 3 * DOMgap);\n    cohesionSlider.position(\tleftGap + DOMgap, DOMoffset + boidsSlider.height * 3 + 4 * DOMgap);\n    separationSlider.position(leftGap + DOMgap, DOMoffset + boidsSlider.height * 4 + 5 * DOMgap);\n    boidsP.position(\t\t\tleftGap + boidsSlider.width + DOMgap * 2, DOMoffset + boidsSlider.height * 0 + 0 * DOMgap + 2);\n    perceptionP.position(\tleftGap + boidsSlider.width + DOMgap * 2, DOMoffset + boidsSlider.height * 1 + 1 * DOMgap + 2);\n    alignmentP.position(\tleftGap + boidsSlider.width + DOMgap * 2, DOMoffset + boidsSlider.height * 2 + 2 * DOMgap + 2);\n    cohesionP.position(\t\tleftGap + boidsSlider.width + DOMgap * 2, DOMoffset + boidsSlider.height * 3 + 3 * DOMgap + 2);\n    separationP.position(\tleftGap + boidsSlider.width + DOMgap * 2, DOMoffset + boidsSlider.height * 4 + 4 * DOMgap + 2);\n  }\n\n  // Make a new boid\n  function pushRandomBoid() {\n    //let pos = createVector(random(width), random(height), random(-depth/2, depth/2)); // Uncomment and comment next line to create boids at random position\n    let pos = p.createVector(0, 0, 0); // Create a boid at the center of space\n    let vel = p5.Vector.random3D().mult(p.random(0.5, 3)); // Give a random velocity\n    let boid = new Boid(pos, vel); // Create a new boid\n    flock.push(boid); // Add the new boid to the flock\n  }\n\n  ///---\n  ///---\n  ///---\n\n\n\n  // Boid class with flocking behavior\n  class Boid {\n    constructor(pos, vel) {\n      this.pos = pos; // Position\n      this.vel = vel; // Velocity\n      this.acc = p.createVector(0, 0, 0); // Acceleration\n      this.maxForce = 1; // Maximum steering force for alignment, cohesion, separation\n      this.maxSpeed = 10; // Desired velocity for the steering behaviors\n      this.r = 255; // red color of the boid\n      this.g = p.floor(p.random(50, 120)); // green color of the boid\n      this.b = p.floor(p.random(50, 120)); // blue color of the boid\n    }\n    \n    // Alignment rule\n    // Steering to average neighbors velocity\n    alignment(neighbors) {\n      let steering = p.createVector();\n      for (let other of neighbors) steering.add(other.vel); // Sum of neighbor velocities \n      if (neighbors.length > 0) {\n        steering.div(neighbors.length); // Average neighbors velocity\n        steering.setMag(this.maxSpeed); // Desired velocity\n        steering.sub(this.vel); // Actual steering\n        steering.limit(this.maxForce); // Steering limited to maxForce\n      }\n      return steering;\n    }\n    \n    // Cohesion rule\n    // Steering to the average neighbors position\n    cohesion(neighbors) {\n      let steering = p.createVector();\n      for (let other of neighbors) steering.add(other.pos); // Sum of neighbor positions\n      if (neighbors.length > 0) {\n        steering.div(neighbors.length); // Average neighbors position\n        steering.sub(this.pos); // Orientation of the desired velocity\n        steering.setMag(this.maxSpeed); // Desired velocity\n        steering.sub(this.vel); // Actual steering\n        steering.limit(this.maxForce); // Steering limited to maxForce\n      }\n      return steering;\n    }\n    \n    // Separation rule\n    // Steering to avoid proximity of the neighbors\n    separation(neighbors) {\n      let steering = p.createVector();\n      for (let other of neighbors) {\n        let diff = p5.Vector.sub(this.pos, other.pos); // Vector from other boid to this boid\n        let d = p.max(other.distance, 0.01); // Distance between other boid and this boid\n        steering.add(diff.div(d)); // Magnitude inversely proportional to the distance\n      }\n      if (neighbors.length > 0) {\n        steering.div(neighbors.length); // Orientation of the desired velocity\n        steering.setMag(this.maxSpeed); // Desired velocity\n        steering.sub(this.vel); // Actual steering\n        steering.limit(this.maxForce); // Steering limited to maxForce\n      }\n      return steering;\n    }\n    \n    // Application of the rules\n    flock(boids, quadTree) {\n      // Go to the middle if goMiddle is true\n      // Create a large force towards the middle, apply it to the boid, and \"return\" to not apply other forces\n      let force = p.createVector(xVar-this.pos.x, yVar-this.pos.y, zVar-this.pos.z);\n      force.setMag(this.maxForce);\n      this.acc.add(force);\n    \n      let radius = perceptionSlider.value(); // Max distance of a neighbor\n      let neighbors = [];\n      \n      if (useQuadTree === true) {\n        // VERSION WITH QUADTREE\n        // Make an array of neighbors, i.e. all boids closer than the perception radius\n        // The array will be passed to the different flocking behaviors\n        let range = new Cube(this.pos.x, this.pos.y, this.pos.z, radius, radius, radius);\n        let maybeNeighbors = quadTree.query(range);\n        for (let other of maybeNeighbors) {\n          let distance = this.pos.dist(other.pos);\n          if (other !== this && distance < radius) {\n            other.distance = distance; // Record the distance so it can be used later\n            neighbors.push(other); // Put this neighbor in the \"neighbors\" array\n          }\n        }\n      } else {\n        // VERSION WITHOUT QUADTREE\n        // Make an array of neighbors, i.e. all boids closer than the perception radius\n        // The array will be passed to the different flocking behaviors\n        for (let other of boids) {\n          let distance = this.pos.dist(other.pos);\n          if (other !== this && distance < radius) {\n            other.distance = distance; // Record the distance so it can be used later\n            neighbors.push(other); // Put this neighbor in the \"neighbors\" array\n          }\n        }\n      }\n      \n\n      \n      // Calculate the force of alignments and apply it to the boid\n      let alignment = this.alignment(neighbors);\n      alignment.mult(alignmentSlider.value());\n      this.acc.add(alignment);\n      \n      // Calculate the force of cohesion and apply it to the boid\n      if (t > 2) { // No cohesion in the first 40 frames\n        let cohesion = this.cohesion(neighbors);\n        cohesion.mult(cohesionSlider.value());\n        this.acc.add(cohesion);\n      }\n      \n      // Calculate the force of separation and apply it to the boid\n      let separation = this.separation(neighbors);\n      separation.mult(separationSlider.value());\n      this.acc.add(separation);\n      \n      // If the boid is flies too high or too low, apply another force to make it fly around the middle of space's depth\n      if (this.pos.z < -depth/8 || this.pos.z > depth/8) {\n        let force = p.createVector(0, 0, -this.pos.z / depth * this.maxForce * 2);\n        this.acc.add(force);\n      }\n      \n      // If the boid has no neighbor, apply random forces so it can go find other boids\n      if (neighbors.length === 0) {\n        let force = p5.Vector.random3D().mult(this.maxForce/4);\n        force.z = 0; // Only go find other in an XY plane\n        this.acc.add(force);\n      }\n    }\n    \n    // Update position, velocity, and acceleration\n    update(gap) {\n      // Apply physics\n      this.pos.add(this.vel);\n      this.vel.add(this.acc);\n      this.vel.mult(0.999); // Some friction\n      this.vel.limit(this.maxSpeed);\n      this.acc.mult(0);\n      \n      // Teleport to opposite side if the boid goes further than a side of space (X and Y axis)\n      // Except for the Z axis, as there is already a force keeping the boid from getting too far\n      if (this.pos.x > p.width/2 + gap) this.pos.x -= p.width + 1.7 * gap;\n      if (this.pos.x < -(p.width/2 + gap)) this.pos.x += p.width + 1.7 * gap;\n      if (this.pos.y > p.height/2 + gap) this.pos.y -= p.height + 1.7 * gap;\n      if (this.pos.y < -(p.height/2 + gap)) this.pos.y += p.eight + 1.7 * gap;\n    }\n    \n    // Show the boid on screen\n    show() {\n      p.noStroke();\n      p.fill(255);\n      p.ambientMaterial(this.r, this.g, this.b);\n\n      p.push()\n      p.translate(this.pos.x, this.pos.y, this.pos.z);\n      p.sphere(10); // A sphere where the boid is\n      let arrow = p.createVector(this.vel.x, this.vel.y, this.vel.z).setMag(10);\n      p.translate(arrow.x, arrow.y, arrow.z);\n      p.sphere(5); // Another sphere, smaller, in the direction of the boid's velocity\n      p.pop();\n      \n      // Show perception radius, all circles are drawn at z = 0\n      if (showPerceptionRadius) {\n        p.stroke(255, 255, 255, 100);\n        p.noFill();\n        p.strokeWeight(1);\n        let perception = perceptionSlider.value() * 2;\n        p.push();\n        p.translate(0,0,this.pos.z)\n        p.ellipse(this.pos.x, this.pos.y, perception, perception);\n        p.pop();\n      }\n    }\n  }\n\n  ///\n  ///\n  ///\n\n  // This file contains the QuadTree class\n  // as well as the Cube classe used by the QuadTree\n\n  // Cube --------------------------------------------------\n  // A cube delimiting the volume of a quad tree\n  // or the volume used for asking boids from a quad tree\n  class Cube {\n    constructor(x, y, z, w, h, d) {\n      this.x = x;\n      this.y = y;\n      this.z = z;\n      this.w = w;\n      this.h = h;\n      this.d = d;\n\n      this.xMin = x - w;\n      this.xMax = x + w;\n      this.yMin = y - h;\n      this.yMax = y + h;\n      this.zMin = z - d;\n      this.zMax = z + d;\n    }\n\n    // Checks if a boid is inside the cube\n    contains(boid) {\n      let pos = boid.pos;\n      return (pos.x >= this.xMin && pos.x <= this.xMax &&\n              pos.y >= this.yMin && pos.y <= this.yMax &&\n              pos.z >= this.zMin && pos.z <= this.zMax);\n    }\n\n    // Check if two cubes intersect\n    intersects(range) {\n      return !(this.xMax < range.xMin || this.xMin > range.xMax ||\n               this.yMax < range.yMin || this.yMin > range.yMax ||\n               this.zMax < range.zMin || this.zMin > range.zMax);\n    }\n  }\n\n  // QUAD TREE --------------------------------------------------\n  // The quad tree stores points in a tree structure\n  // to minimize the cost of distance calculation\n  class QuadTree {\n    constructor(boundary, capacity) {\n      this.boundary = boundary; // cube giving the borders of the quad tree\n      this.capacity = capacity; // Maximum amount of points that can be stored in the quad tree\n      this.boids = []; // Array storing the boids in the quad tree\n      this.divided = false; // True when the quad tree subdivides\n    }\n\n    // Insert a boid in the quad tree\n    insert(boid) {\n      // Return if the boid is not in the area of this layer of quad tree\n      if (!this.boundary.contains(boid)) {\n        return false;\n      }\n\n      // Add the boid at this layer or a deeper layer depending on capacity\n      if (this.boids.length < this.capacity) {\n        // Add the point to this layer if there is still room for it\n        this.boids.push(boid);\n        return true;\n      } else {\n        // Otherwise, subdivide to make room for the new boid\n        // Subdivision divides the quad tree area into 8 new children quad trees\n        if (!this.divided) {\n          this.subdivide();\n        }\n\n        // Add the boid to the relevant subdivision\n        // N = North, S = South, E = East, W = West, B = Bottom, T = Top\n        if (this.NWT.insert(boid)) {\n          return true;\n        } else if (this.NET.insert(boid)) {\n          return true;\n        } else if (this.SET.insert(boid)) {\n          return true;\n        } else if (this.SWT.insert(boid)) {\n          return true;\n        } else if (this.NWB.insert(boid)) {\n          return true;\n        } else if (this.NEB.insert(boid)) {\n          return true;\n        } else if (this.SEB.insert(boid)) {\n          return true;\n        } else if (this.SWB.insert(boid)) {\n          return true;\n        }\n      }\n    }\n\n    // Subdivides the quad tree if it is at full capacity, creating 8 new children quad trees\n    subdivide() {\n      this.divided = true; // Informs of the subdivision to only subdivide once\n\n      let x = this.boundary.x;\n      let y = this.boundary.y;\n      let z = this.boundary.z;\n      let w = this.boundary.w / 2;\n      let h = this.boundary.h / 2;\n      let d = this.boundary.d / 2;\n\n      // Creates the 8 children quad trees with the relevant positions and area\n      // North West Top quad tree\n      let NWTBoundary = new Cube(x - w, y - h, z - d, w, h, d);\n      this.NWT = new QuadTree(NWTBoundary, this.capacity);\n\n      // North East Top quad tree\n      let NETBoundary = new Cube(x + w, y - h, z - d, w, h, d);\n      this.NET = new QuadTree(NETBoundary, this.capacity);\n\n      // South East Top quad tree\n      let SETBoundary = new Cube(x + w, y + h, z - d, w, h, d);\n      this.SET = new QuadTree(SETBoundary, this.capacity);\n\n      // South West Top quad tree\n      let SWTBoundary = new Cube(x - w, y + h, z - d, w, h, d);\n      this.SWT = new QuadTree(SWTBoundary, this.capacity);\n      \n      // North West Bot quad tree\n      let NWBBoundary = new Cube(x - w, y - h, z + d, w, h, d);\n      this.NWB = new QuadTree(NWBBoundary, this.capacity);\n\n      // North East Bot quad tree\n      let NEBBoundary = new Cube(x + w, y - h, z + d, w, h, d);\n      this.NEB = new QuadTree(NEBBoundary, this.capacity);\n\n      // South East Bot quad tree\n      let SEBBoundary = new Cube(x + w, y + h, z + d, w, h, d);\n      this.SEB = new QuadTree(SEBBoundary, this.capacity);\n\n      // South West Bot quad tree\n      let SWBBoundary = new Cube(x - w, y + h, z + d, w, h, d);\n      this.SWB = new QuadTree(SWBBoundary, this.capacity);\n    }\n\n    // Returns all the points in a given range (Cube) and put them in the \"found\" array\n    query(range, found) {\n      // The array \"found\" will check all quad trees intersecting with the range,\n      // looking for points intersecting with the range\n      if (!found) found = []; // Creates the array at the beginning of the recursion\n\n      if (!this.boundary.intersects(range)) {\n        return found; // No intersection between the quad tree and the range, no need to check for points\n      } else {\n        // If the range intersects this quad tree, check for the intersection of its points with the range\n        for (let boid of this.boids) {\n          if (range.contains(boid)) {\n            found.push(boid); // Add the points intersecting with the range to \"found\"\n          }\n        }\n\n        // This quad tree intersects with the range, now do the same for its children quad trees\n        if (this.divided) {\n          this.NWT.query(range, found);\n          this.NET.query(range, found);\n          this.SET.query(range, found);\n          this.SWT.query(range, found);\n          this.NWB.query(range, found);\n          this.NEB.query(range, found);\n          this.SEB.query(range, found);\n          this.SWB.query(range, found);\n        }\n      }\n\n      return found;\n    }\n  }\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/sketchTone.js",
    "content": "import p5 from \"p5\";\nimport \"p5/lib/addons/p5.sound\";\n\n\nexport default function sketchTone (p) {\n  let delta = 0;\n  let theta = 0;\n  let alpha = 0;\n  let beta = 0;\n  let gamma = 0;\n\n  let osc, envelope, fft;\n\n  let scaleArray = [delta+30, theta+50 , beta+60, gamma*10+70];\n  let note = 0;\n\n  p.setup = function () {\n    p.createCanvas(710, 200);\n    osc = new p5.SinOsc();\n    // Instantiate the envelope\n    envelope = new p5.Env();\n    // set attackTime, decayTime, sustainRatio, releaseTime\n    envelope.setADSR(0.001, 0.5, 0.1, 0.5);\n    // set attackLevel, releaseLevel\n    envelope.setRange(1, 0);\n    osc.start();\n    fft = new p5.FFT();\n    p.noStroke();\n  };\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    delta = Math.floor(props.delta);\n    theta = Math.floor(props.theta);\n    alpha = Math.floor(props.alpha);\n    beta = Math.floor(props.beta);\n    gamma = Math.floor(props.gamma);\n  };\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.6, 200);\n  }\n\n  p.draw = function () {\n    p.background(20);\n\n    if (p.frameCount % alpha === 0 || p.frameCount === 1) {\n      let midiValue = scaleArray[note];\n      let freqValue = p.midiToFreq(midiValue);\n      osc.freq(freqValue);\n\n      envelope.play(osc, 0, 0.1);\n      note = (note + 1) % scaleArray.length;\n    }\n\n    let spectrum = fft.analyze();\n    for (let i = 0; i < spectrum.length / 20; i++) {\n      p.fill(spectrum[i], spectrum[i] / 10, 0);\n      let x = p.map(i, 0, spectrum.length / 20, 0, p.width);\n      let h = p.map(spectrum[i], 0, 255, 0, p.height);\n      p.rect(x, p.height, spectrum.length / 20, -h);\n    }\n  };\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduAnimate/translations/en.json",
    "content": "{\n  \"title\": \"P5js integration\",\n  \"description\": [\n    \"In the next demo we look at the traditional frequency bands. \",\n    \"This time instead of graphing them we pipe them into a live web animation. \",\n    \"For simplicity for now we will only use the left frontal electrode AF7. \",\n    \"All of these animations are embedded p5js animations.\"\n  ]\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduBands/EEGEduBands.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal, Link } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { takeUntil } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { channelNames } from \"muse-js\";\nimport { Bar } from \"react-chartjs-2\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  powerByBand\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\nimport { bandLabels } from \"../../utils/chartUtils\";\n\nexport function getSettings () {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 50,\n    interval: 100,\n    bins: 256,\n    duration: 1024,\n    srate: 256,\n    name: 'Bands',\n    secondsToSave: 10 \n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionBands) window.subscriptionBands.unsubscribe();\n\n  window.pipeBands$ = null;\n  window.multicastBands$ = null;\n  window.subscriptionBands = null;\n\n  // Build Pipe\n  window.pipeBands$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    powerByBand(),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastBands$ = window.pipeBands$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastBands$) {\n    window.subscriptionBands = window.multicastBands$.subscribe(data => {\n      setData(bandsData => {\n        Object.values(bandsData).forEach((channel, index) => {\n          channel.datasets[0].data = [\n            data.delta[index],\n            data.theta[index],\n            data.alpha[index],\n            data.beta[index],\n            data.gamma[index]\n          ];\n          channel.xLabels = bandLabels;\n        });\n\n        return {\n          ch0: bandsData.ch0,\n          ch1: bandsData.ch1,\n          ch2: bandsData.ch2,\n          ch3: bandsData.ch3,\n          ch4: bandsData.ch4\n        };\n      });\n    });\n\n    window.multicastBands$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n        if (index === 0) {\n        const options = {\n          ...generalOptions,\n          scales: {\n            xAxes: [\n              {\n                scaleLabel: {\n                  ...generalOptions.scales.xAxes[0].scaleLabel,\n                  labelString: specificTranslations.xlabel\n                }\n              }\n            ],\n            yAxes: [\n              {\n                scaleLabel: {\n                  ...generalOptions.scales.yAxes[0].scaleLabel,\n                  labelString: specificTranslations.ylabel\n                },\n                ticks: {\n                  min: 0,\n                  max: 100\n                }\n              }\n            ]\n          },\n          title: {\n            ...generalOptions.title,\n            text: 'Power by Frequency Band'\n          }\n        };\n\n      if (channels.data.ch3.datasets[0].data) {\n          const newData = {\n            datasets: [{\n              label: channelNames[0],\n              backgroundColor: 'rgba(217,95,2)',\n              data: channels.data.ch0.datasets[0].data,\n              fill: false\n            }, {\n              label: channelNames[1],\n              backgroundColor: 'rgba(27,158,119)',\n              data: channels.data.ch1.datasets[0].data,\n              fill: false\n            }, {\n              label: channelNames[2],\n              backgroundColor: 'rgba(117,112,179)',\n              data: channels.data.ch2.datasets[0].data,\n              fill: false\n            }, {\n              label: channelNames[3],\n              backgroundColor: 'rgba(231,41,138)',\n              data: channels.data.ch3.datasets[0].data,\n              fill: false  \n            }, {\n              label: channelNames[4],\n              backgroundColor: 'rgba(20,20,20)',\n              data: channels.data.ch4.datasets[0].data,\n              fill: false  \n            }],\n            xLabels: channels.data.ch0.xLabels\n          }\n\n          return (\n            <Card.Section key={\"Card_\" + 1}>\n              <Bar key={\"Line_\" + 1} data={newData} options={options} />\n            </Card.Section>\n          );\n        } else {\n          return( \n            <Card.Section>\n                <TextContainer>\n                <p> {[\n                \"Press connect above to see the chart.\"  \n                ]} \n                </p>\n              </TextContainer>   \n            </Card.Section>  \n          )\n        }\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n            <TextContainer>\n              <p>{[\n                \"In the next demo we look at the traditional frequency bands. \",\n                \"Oscillations in the brain are important as a mechinasm of brain function and communication. \", \n                \"For example, within a brain area, cellular firing becomes locked to the ongoing oscillations of the local field potential: \"\n              ]}</p>\n            </TextContainer>\n            <img \n              src={ require(\"./phaseLockedFiring.png\")} \n              alt=\"phaseLocked\"\n              width=\"50%\"\n              height=\"auto\"\n            ></img> \n            </Stack>\n            <Stack> \n            <Link url=\"hhttps://en.wikipedia.org/wiki/Neural_oscillation#/media/File:SimulationNeuralOscillations.png\"\n              external={true}>\n              Image Source - Wikipedia </Link>\n              <br />\n            <TextContainer>\n              <p>{[\n                \"Since oscillations can control the timing of neural firing, they can also be used to communciate information and create distributed representations. \",\n                \"Two nearby brain regions that oscillate in sync will have cells that also fire in sync, which also means the neurons they connect to will be more influenced. \", \n                \"Different brain regions have different frequency oscillations at different times \"\n              ]}</p>\n            </TextContainer>\n          <TextContainer>\n            <p>{[\n              \"Oscillations in the brain seem to belong to a number of basic families or frequency bands that are influenced by cognition in different ways. \",\n              \"We take the same spectra that was computed in Spectra and divide into five bands. \",\n              \"Delta (1-4 Hz), Theta (4-7 Hz), Alpha (7-12 Hz), Beta (12-30 Hz), and Gamma (30+ Hz). \"\n            ]}</p>\n          </TextContainer>\n            <img \n              src={ require(\"./freqBands.jpg\")} \n              alt=\"freqBands\"\n              width=\"50%\"\n              height=\"auto\"\n            ></img> \n            </Stack>\n            <Stack> \n            <Link url=\"https://upload.wikimedia.org/wikipedia/commons/5/59/Analyse_spectrale_d%27un_EEG.jpg\"\n              external={true}>\n              Image Source - Wikipedia </Link>\n              <br />\n          \n          <TextContainer>\n            <p>{[\n              \"These different frequency bands are associated with different brain states. \", \n              \"For example, when we pay focus attention to something, our alpha goes down and our beta oscillations increase. \",\n              \"Gamma oscillations are associated with neural activity, but for the most part Gamma oscillations are very difficult to measure outside the head (very small) and so we will ignore them. \",\n              \"Theta oscillations increase during spatial navigation and are associated with learning and memory. \",\n              \"Alpha oscillations are assocaited with attention and active inhbition of neural activity, we will consider them further below. \"\n            ]}</p>\n          </TextContainer>\n\n\n              </Stack>\n            </Card.Section>\n          <Card.Section>\n          <Stack>\n\n      <TextContainer>\n            <p>{[\n              \"Connect a muse and watch the following bar chart of the frequency band power. There is bar for each electrode. \",\n              \"See if you can pick one of the frequency bands and try to control the height by relaxing.\"\n            ]}</p>\n          </TextContainer>\n         <img \n              src={ require(\"./electrodediagram.png\")} \n              alt=\"Electrodes\"\n              width=\"20%\"\n              height=\"auto\"\n            ></img> \n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\n\n// https://en.wikipedia.org/wiki/Neural_oscillation#/media/File:SimulationNeuralOscillations.png\n// https://upload.wikimedia.org/wikipedia/commons/5/59/Analyse_spectrale_d%27un_EEG.jpg\n//\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings);\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={128} step={128} max={4096} \n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={10} step={5} max={Settings.duration} \n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={.01} step={.5} max={Settings.cutOffHigh - .5} \n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2} \n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, setSettings) {\n\n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n  \n  return (\n    <Card title={'Record Data'} sectioned>\n      <Stack>\n          <TextContainer>\n            <p>{[\n              \"One of the earliest and easiest to measure changes in the EEG is that of alpha oscillations when the eyes closed. \",\n              \"We will test these changes by recording data in two conditions, and comparing the average alpha at all four electrodes between conditions. \", \n              \"We expect to replicate the following relationship: \"\n            ]}</p>\n          </TextContainer>\n            <img \n              src={ require(\"./alphaOpenClosed.png\")} \n              alt=\"closedOpen\"\n              width=\"50%\"\n              height=\"auto\"\n            ></img> \n            </Stack>\n            <Stack> \n            <Link url=\"https://www.semanticscholar.org/paper/Coupling-between-visual-alpha-oscillations-and-mode-Mo-Liu/82593c9b9662d4dc022d51607b313f851f670246\"\n              external={true}>\n              Image Source - Mo et al., 2013, Neuroimage </Link>\n              <br />\n              <br />\n              <br />\n\n        <TextContainer>\n            <p>{[\n              \"First go to the Raw module 3 and check the data and connection quality. \",\n              \"Then come back to Module 6, no need to change any settings. \",\n              \"We will record two sessions for each person in your group, one with eyes open and one with eyes closed. \",\n              \"Once recorded you can open the .csv file and observe what gets saved. Along the rows are the different frequency bands from each electrode, we are going to average over all four electrodes in this assignment. \",\n              \"We are also going to average over time, which is shown on different rows. \", \n              \"So please compute the average ALPHA power in that output file, and do the same for the other condition, make sure to keep track of which file was created during which condition. \", \n              \"Compare your values for eyes open vs eyes closed, did you find the expected difference? \",\n              \"Why do you think alpha differs when we close our eyes? \"\n             ]}</p>\n          </TextContainer>\n        <TextContainer>\n            <p>{[\n              \"Once you are complete, move on to the next Module and control live animations with the values of these frequency bands. \",\n             ]}</p>\n          </TextContainer>\n\n        <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />\n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Save to CSV'}  \n          </Button>\n        </ButtonGroup>\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title=\"Recording Data\"\n        >\n          <Modal.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n      </Stack>\n    </Card> \n)\n}\n\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n  dataToSave.push(\n    \"Timestamp (ms),\",\n    \"delta0,delta1,delta2,delta3,deltaAux,\", \n    \"theta0,theta1,theta2,theta3,thetaAux,\",  \n    \"alpha0,alpha1,alpha2,alpha3,alphaAux,\",  \n    \"beta0,beta1,beta2,beta3,betaAux,\", \n    \"gamma0,gamma1,gamma2,gamma3,gammaAux\\n\"\n  );\n\n  // Create timer \n  const timer$ = timer(Settings.secondsToSave * 1000);\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastBands$.pipe(\n    takeUntil(timer$)\n  );\n  \n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduBands/translations/en.json",
    "content": "{\n  \"title\": \"Frequency Bands Data\",\n  \"xlabel\": \"Frequency (Hz)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduEvoked/EEGEduEvoked.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { take, takeUntil } from \"rxjs/operators\";\n \n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch\n} from \"@neurosity/pipes\";\n\nimport { chartStyles } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport { generateXTics, standardDeviation } from \"../../utils/chartUtils\";\n\nimport P5Wrapper from 'react-p5-wrapper';\nimport sketchEvoked from './sketchEvoked'\n\nexport function getSettings () {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 1,\n    srate: 256,\n    duration: 1,\n    name: 'Evoked',\n    secondsToSave: 60\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionEvoked) window.subscriptionEvoked.unsubscribe();\n\n  window.pipeEvoked$ = null;\n  window.multicastEvoked$ = null;\n  window.subscriptionEvoked = null;\n\n  // Build Pipe\n  window.pipeEvoked$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastEvoked$ = window.pipeEvoked$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastEvoked$) {\n    window.subscriptionEvoked = window.multicastEvoked$.subscribe(data => {\n      setData(evokedData => {\n        Object.values(evokedData).forEach((channel, index) => {\n          channel.datasets[0].data = data.data[index];\n          channel.xLabels = generateXTics(Settings.srate, Settings.duration);\n          channel.datasets[0].qual = standardDeviation(data.data[index])          \n        });\n\n        return {\n          ch0: evokedData.ch0,\n          ch1: evokedData.ch1,\n          ch2: evokedData.ch2,\n          ch3: evokedData.ch3,\n          ch4: evokedData.ch4\n\n        };\n      });\n    });\n\n    window.multicastEvoked$.connect();\n    console.log(\"Subscribed to Evoked\");\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() { \n      return null\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n  \nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n  \n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={128} step={128} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />          \n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={10} step={1} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, setSettings) {\n\n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n\n  return (\n    <Card title={'Run ERP experiment'} sectioned>\n      <Card.Section>\n        <p>\n          {\"Clicking this button will begin the experiment so check your data quality on the raw module first. \"}\n          {\"A window will pop up when you click the button and a series of circles will appear. Stare at the cross in the center. \"}\n          {\"There will be red and blue circles, ignore the blue ones.\"}\n          {\"Whenever you see a red circle, as fast as you can either press spacebar on a keyboard, or tap the touchscreen on a tablet or phone. \"}\n          {\"This entire time the eeg data will be saved along with a column indicating which target was on the screen, and another for the responses. \"}\n          {\"The task will continue for a few minutes and once it is finished a .csv file will automatically download. \"}\n          {\"This .csv file has a row for each time point, a column for each electrode, and the columns indicating when targets appeared, and when responses were made. \"}\n          {\"It saves a marker of 20 when the target is on, a marker of 10 when the blue standards are on, and a peak when the spacebar or touchscreen are pressed, here you can see those synced with an eeg channel: \"}\n\n        </p>\n        <p>\n         <img \n            src={ require(\"./dataExample.png\")} \n            alt=\"dataExample\"\n            width=\"100%\"\n            height=\"auto\"\n          ></img>    \n        </p>     \n      </Card.Section>\n      <Stack>\n         <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />   \n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Run oddball experiment'}  \n          </Button>\n        </ButtonGroup>\n        \n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title={\"Press Spacebar or tap screen when you see RED circle\"}\n        >\n          <Modal.Section>\n           <Card.Section>\n              <P5Wrapper sketch={sketchEvoked} />          \n            </Card.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n\n      </Stack>\n    </Card>\n  )\n}\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n  window.marker = 0;\n  window.responseMarker = 0;\n  window.touchMarker = 0;\n\n  console.log('making ' + Settings.name + ' headers')\n\n  // for each module subscribe to multicast and make header\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastEvoked$.pipe(\n  take(1)\n  );\n  //take one sample to get header info\n  localObservable$.subscribe({ \n  next(x) { \n    dataToSave.push(\n      \"Timestamp (ms),\",\n      \"Marker,\",\n      \"SpaceBar,\",\n      \"TouchMarker,\",\n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch0_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch1_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch2_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch3_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"chAux_\" + f + \"ms\"}) + \",\", \n      \"info\", \n      \"\\n\"\n    );   \n  }\n  });\n\n  //create timer\n  const timer$ = timer(Settings.secondsToSave * 1000)\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastEvoked$.pipe(\n    takeUntil(timer$)\n  );\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(\n        Date.now() + \",\" + \n        window.marker + \",\" + \n        window.responseMarker + \",\" +\n        window.touchMarker + \",\" +\n        Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduEvoked/sketchEvoked.js",
    "content": "export default function sketchEvoked (p) {\n\n let x = 0;\n let thisRand = 0.5; //for random choice of target type\n let targProp = 0.25;\n let isTarget = false;\n let ellapsedTime = 0;\n let nextDelay = 1000;\n let newOnset = true;\n let startTime = 0;\n let targCount = 0;\n\n p.setup = function () {\n    p.createCanvas(300, 300);\n    p.frameRate(60);\n    p.noStroke();\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(300, 300);\n  }\n\n  p.touchStarted = function() {\n  if (window.touchMarker === 0) {\n    window.touchMarker = 255;\n  } else {\n    window.touchMarker = 0;\n  }\n}\n \n  p.draw = function () {\n\n\n    if (p.keyIsPressed === true) {\n      window.responseMarker = p.keyCode;\n    } else {\n      window.responseMarker = 0;\n    }\n    p.background(255);\n    x = x+1;\n    ellapsedTime = p.millis()-startTime;\n\n    if (ellapsedTime > nextDelay) {\n      newOnset = true;\n    } else {\n      newOnset = false;\n    }\n\n    if (newOnset) {\n      targCount++;\n      nextDelay = 500 + p.int(p.random() * 1000);\n      console.log(targCount, nextDelay)\n      startTime = p.millis();\n\n      thisRand = p.random();\n      if (thisRand < targProp) { // targets 20% of the time\n        isTarget = true;\n      } else {\n        isTarget = false;\n      }\n\n      if (isTarget) {   \n        p.fill(250, 150, 150);  \n        window.marker = 20;  \n      } else {\n        p.fill(150,150,250);\n        window.marker = 10;\n      }\n\n    } else { // during time between targets \n      p.fill(255, 255, 255);\n      window.marker = 0;\n    }\n    p.ellipse(p.width/2, p.height/2, 300);\n    p.fill(255,0,0);\n    p.text(\"+\", p.width/2, p.height/2);\n  }\n\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduEvoked/translations/en.json",
    "content": "{\n  \"title\": \"Stimulus Evoked Event-related potential (ERP)\",\n  \"description\": [\n  \t\"The electrical activity evoked by individual presentations of stimuli does create small changes in electrical voltage. \", \n  \t\"These changes are, however, very small, about 1-10 microVolts (\\u03BCV), and the noise from other things like eye movements and muscles is sometimes 10x as large. \",\n  \t\"This noise can be mitigated by averaging this evoked activity over repeated presentations of the same stimulus. \",\n  \t\"This average voltage in response to a stimulus is called an Event-related potential (ERP) or evoked potential (EP). \",\n  \t\"Here...\"\n  ],\n  \"xlabel\": \"Time (ms)\",\n  \"ylabel\": \"Votage (\\u03BCV)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduHeartRaw/EEGEduHeartRaw.js",
    "content": "import React from \"react\";\nimport { catchError, multicast, take  } from \"rxjs/operators\";\nimport { Subject } from \"rxjs\";\n\nimport { TextContainer, Card, Stack, ButtonGroup, Button, Modal, Link } from \"@shopify/polaris\";\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\nimport { saveAs } from 'file-saver';\nimport YouTube from 'react-youtube'\n \nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport { generateXTics, standardDeviation } from \"../../utils/chartUtils\";\n\nexport function getSettings () {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 10,\n    srate: 256,\n    duration: 2560,\n    name: 'HeartRaw'\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionHeartRaw) window.subscriptionHeartRaw.unsubscribe();\n\n  window.pipeHeartRaw$ = null;\n  window.multicastHeartRaw$ = null;\n  window.subscriptionHeartRaw = null;\n\n  // Build Pipe\n  window.pipeHeartRaw$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastHeartRaw$ = window.pipeHeartRaw$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastHeartRaw$) {\n    window.subscriptionHeartRaw = window.multicastHeartRaw$.subscribe(data => {\n      setData(heartRawData => {\n        Object.values(heartRawData).forEach((channel, index) => {\n          channel.datasets[0].data = data.data[index];\n          channel.xLabels = generateXTics(Settings.srate, Settings.duration).map(function(x) {return x / 1000});;\n          channel.datasets[0].qual = standardDeviation(data.data[index])       \n        });\n        return {\n          ch0: heartRawData.ch0,\n          ch1: heartRawData.ch1\n        };\n      });\n    });\n\n    window.multicastHeartRaw$.connect();\n    console.log(\"Subscribed to HeartRaw\");\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      if (index === 1) {\n        const options = {\n          ...generalOptions,\n          scales: {\n            xAxes: [{\n              scaleLabel: {\n                ...generalOptions.scales.xAxes[0].scaleLabel,\n                labelString: specificTranslations.xlabel\n              },\n              gridLines: {\n                color: \"rgba(50,50,50)\"\n              },\n              ticks: {\n                maxTicksLimit: 10\n              }\n\n            }],\n            yAxes: [\n              {\n                scaleLabel: {\n                  ...generalOptions.scales.yAxes[0].scaleLabel,\n                  labelString: specificTranslations.ylabel\n                }\n              }\n            ]\n          },\n          elements: {\n            line: {\n              borderColor: 'rgba(' + channel.datasets[0].qual + ', 128, 128)',\n              fill: false\n            },\n            point: {\n              radius: 0\n            }\n          },\n          animation: {\n            duration: 0\n          },\n          title: {\n            ...generalOptions.title,\n            text: generalTranslations.channel + channelNames[index] + ' --- SD: ' + channel.datasets[0].qual \n          },\n\n        };\n\n        return (\n          <Card.Section key={\"Card_\" + index}>\n            <Line key={\"Line_\" + index} data={channel} options={options} />\n          </Card.Section>\n        );\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <React.Fragment>\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p> {[\n              \"As a first introduction to measurement of electrical potentials from the body we will look at something accessible. \",\n              \"As the heart beats and pumps blood throuhgouts our body, a series of electrical potentials are created, which can be measured using electrodes placed around the heart. \",\n              \"This is referred to as the Electrocardiogram (ECG). You have seen this hundreds of times in movies and TV beside hospital beds. \"\n            ]} </p>\n          </TextContainer>\n        <br />\n        <br />\n        <img \n          src={ require(\"./ECG_Principle_fast.gif\")} \n          alt=\"ECGPrinciples\"\n          width=\"50%\"\n          height=\"auto\"\n        ></img> \n        <Link url=\"https://commons.wikimedia.org/wiki/File:ECG_Principle_fast.gif\"> Image Source - Wikipedia </Link>\n        <br />\n        <TextContainer>\n          <p> {[\n            \"The ECG is best measured by comparing the electrical potential accross the left vs. right side of the body. \",\n            \"Depending on where on the body the measurements are taken, they pick up a different view of the electrical potentials generated by the heart. \",\n            \"One of the easiest places to measure is comparing the voltage between the left and right hands. \",\n            \"For example, this is how treadmills can read your heart rate when you place one hand on each of the two holds. \"\n          ]} </p>\n        </TextContainer>\n        <br />\n        <img \n          src={ require(\"./LimbLead.png\")} \n          alt=\"LeftHand\"\n          width=\"50%\"\n          height=\"auto\"\n        ></img>  \n        <Link url=\"https://en.wikipedia.org/wiki/Electrocardiography#/media/File:Limb_leads_of_EKG.png\"> Image Source - Wikipedia </Link>\n        <br /> \n        <TextContainer>\n          <p> {[\n            \"Therefore, you can see your own ECG today using the muse. \",\n            \"You can take off the muse from your head and place a finger on your right hand on the muse's reference electrode (in the center of the forehead). \",\n            \"We can then place a finger of our left hand on one of the eeg electrodes. Lets use the left forehead electrode (position AF7). \",\n            \"So place your left fingers pinching the left forehead electrode, and your right fingers pinching the center electrode. \",\n            \"Otherwise try to hold the Muse as still as possible, and relax your body. \" \n         ]} </p>\n        </TextContainer>\n        <br />\n        </Stack>\n        <img \n          src={ require(\"./LeftHand.png\")} \n          alt=\"LeftHand\"\n          width=\"25%\"\n          height=\"auto\"\n        ></img>  \n        <img \n          src={ require(\"./RightHand.png\")} \n          alt=\"RightHand\"\n          width=\"25%\"\n          height=\"auto\"\n        ></img> \n        <br />   \n        <Stack>\n          <br />        \n          <img \n            src={ require(\"./electrodediagram2.png\")} \n            alt=\"F7Electrode\"\n            width=\"25%\"\n            height=\"auto\"\n          ></img>\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodediagram2.png\"> Image Source - EEG101 </Link>\n\n\n          <TextContainer>\n            <p> {[\n              \"If you haven't already, connect your muse and try to plot your heart rate before moving on to the data recording and analysis below. \",\n              \"Soon you should see spikes in voltage measured between those two electrodes each time your heart beats. \", \n              \"Watch what happens when you move your body or fingers, and notice how delicate the signal is. \" \n           ]} </p>\n          </TextContainer>\n        </Stack>\n\n        \n        <br />\n      </Card.Section>\n    </Card>\n    <Card title={\"Live Data\"}>\n      <Card.Section>\n        <TextContainer>\n          <p> {[\n            \"Here time is on the horizontal axis, and the voltage is on the vertical axis. \",\n            \"There are 10 Seconds of data shown, with the current time shown on the right. \",\n            \"There is a vertical line every second so you can estimate your heart rate roughly. \",\n            \"Below we will save data and use it to estimate your heart more accurately in a spreadsheet. \",\n            \"the signal will be red if it is noisy, and when you relax and hold still it will turn green/black\"\n            ]} </p>\n        </TextContainer>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n    </React.Fragment>\n  );\n}\n  \nexport function renderRecord(recordPopChange, recordPop, status, Settings) {\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return (\n    <Card title={'Collect Raw Heart Rate Data'} sectioned>\n      <Card.Section>\n        <Stack>\n        <TextContainer>\n        <p> {[\n          \"Clicking this button will immediately record a 10 second long segment of data just like it is shown in the plot above. \",\n          \"Therefore, make sure the chart above looks clean and you can see you heart beat clearly before pressing record. \",\n           \"We are going to compare two conditions that show a clear difference in heart rate due to blood pressure changes: \",\n           \"Standing and Sitting. \",\n           \"You will record two sessions, one standing and one sitting, pick the order randomly but keep track of which output file is which\"\n           ]} </p>\n        </TextContainer> \n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              recordPopChange()\n              saveToCSV(Settings);\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Record Raw ECG Data'}  \n          </Button>\n        </ButtonGroup>\n        <TextContainer>\n          <p> {[\n            \"A .csv file will be saved that can be opened in your favorite spreadsheet software like Microsoft Excel or in our examples, Google Sheets. \",\n            \"Remember for each person at your computer to record two files, one while they are standing and one while they are sitting. \", \n            \"Here is an example of what the data will look like once loaded. \",\n            \"The top row shows the millsecond ellapsed in the data segment from 4 to 10,000. \",\n            \"The bottom row shows the voltage measured from the difference between the two electrode, for each of those moments in time. \",\n            \"The plot is plotting the data for each time point, this time with zero on the left. \" \n\n          ]} </p>\n        </TextContainer>\n        <img \n          src={ require(\"./PlotEKG_Sheets.png\")} \n          alt=\"F7Electrode\"\n          width=\"75%\"\n          height=\"auto\"\n        ></img>\n        <TextContainer>\n          <p> {[\n            \"The following youtube video will show you how to open the file in Google Sheets, rename it, plot the data, find the peaks,  \",\n            \"Record them and find their difference in time, then take the average difference to estimate your average heart period. \",\n            \"This value is then used to estimate your heart rate in beats per minute. \"\n            ]} \n        <Link url=\"https://docs.google.com/spreadsheets/d/1v2JfPkkiSiXizY9SZOhsliSpCyWDv6oW0ESu-wAL-DY/edit?usp=sharing\">\n         Link to example google sheet from video. \n        </Link>  \n        </p>\n        </TextContainer>\n\n        <br />    \n        <br />\n        <YouTube \n          videoId=\"GyIofXQUOvE\"\n          opts={opts}\n        />\n        <br />\n        <TextContainer>\n          <p> {[\n            \"Finally each of you will enter this number for both sitting and standing into a google sheet that we are sharing as a class. \", \n            \"We will use this shared google sheet which combines all our data in order to compute group statistics. \"  \n          ]} </p>\n        </TextContainer>\n        <br />\n        </Stack>\n      </Card.Section>\n\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title={\"Data is recording\"}\n        >\n          <Modal.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n\n    </Card>\n  );\n}\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n \n  // for each module subscribe to multicast and make header\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastHeartRaw$.pipe(\n    take(1)\n  );\n  //take one sample to get header info\n  localObservable$.subscribe({ \n  next(x) { \n    dataToSave.push(\n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(t) {return t }) + \",\", \n      \"\\n\"\n    );   \n  }\n  });\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastHeartRaw$.pipe(\n    take(1)\n  );\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      console.log(x)\n      dataToSave.push(Object.values(x.data[1]).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduHeartRaw/translations/en.json",
    "content": "{\n  \"title\": \"Electrocardiogram (Heart Beats)\",\n  \"description\": [\n    \"As a first introduction to measurement of electrical potentials from the body we will look at something accessible. \",\n    \"As the heart beats and pumps blood throuhgouts our body, a series of electrical potentials are created, which can be measured using electrodes placed around the heart. \",\n    \"This is referred to as the Electrocardiogram (ECG), and is best measured comparing the potential accross the left vs. right side of the body. \",\n    \"Therefore, we can take off the muse and place a finger on one hand on the muse's reference electrode (in the center of the forehead). \",\n    \"We can then place a finger of our opposite hand on one of the eeg electrodes. For this example pick the left forehead electrode. \",\n    \"So place your left fingers pinching the left forehead electrode, and your right fingers pinching the center electrode. \",\n    \"Rest the muse on the table as you do this, and relax your body. Soon you should see spikes in voltage measured between thsoe two electrodes each time your heart beats. \" \n  ],\n  \"xlabel\": \"Time (Seconds)\",\n  \"ylabel\": \"Voltage (\\u03BCV)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduHeartSpectra/EEGEduHeartSpectra.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Link, Modal } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { takeUntil } from \"rxjs/operators\";\nimport { Subject, timer  } from \"rxjs\";\n\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\nimport YouTube from 'react-youtube'\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nexport function getSettings() {\n  return {\n    cutOffLow: .01,\n    cutOffHigh: 20,\n    interval: 100,\n    bins: 8192,\n    sliceFFTLow: 0.5,\n    sliceFFTHigh: 2.5,\n    duration: 2048,\n    srate: 256,\n    name: 'HeartSpectra',\n    secondsToSave: 10\n  }\n};\n\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionHeartHeartSpectra) window.subscriptionHeartSpectra.unsubscribe();\n\n  window.pipeHeartSpectra$ = null;\n  window.multicastHeartSpectra$ = null;\n  window.subscriptionHeartSpectra = null;\n\n  // Build Pipe \n  window.pipeHeartSpectra$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n\n  window.multicastHeartSpectra$ = window.pipeHeartSpectra$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastHeartSpectra$) {\n    window.subscriptionHeartSpectra = window.multicastHeartSpectra$.subscribe(data => {\n      setData(heartSpectraData => {\n        Object.values(heartSpectraData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[1];\n          channel.xLabels = data.freqs.map(function(x) {return x * 60});\n      });\n\n        return {\n          ch1: heartSpectraData.ch1,\n        };\n      });\n    });\n\n    window.multicastHeartSpectra$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      const options = {\n        ...generalOptions,\n        scales: {\n          xAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.xAxes[0].scaleLabel,\n                labelString: specificTranslations.xlabel\n              }\n            }\n          ],\n          yAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.yAxes[0].scaleLabel,\n                labelString: specificTranslations.ylabel\n              },\n              ticks: {\n                min: 0\n              }\n            }\n          ]\n        },\n        elements: {\n          point: {\n            radius: 8\n          }\n        },\n        animation: {\n          duration: 200\n        },\n        title: {\n          ...generalOptions.title,\n          text: generalTranslations.channel + \n            channelNames[1] + \n            \" - Estimated HR: \" +\n            channel.peakF + \" BPM\"\n        }\n      };\n\n      if (index === 0) {\n        if (channel.xLabels) {\n          channel.peakInd = indexOfMax(channel.datasets[0].data);\n          channel.peakF = channel.xLabels[channel.peakInd];\n          channel.peakVal = channel.datasets[0].data[channel.peakInd]\n          const newData = {\n            datasets: [{\n              label: 'Peak',\n              borderColor: 'rgba(0,0,0)',\n              backgroundColor: 'rgba(231,41,138)',\n              data: [{\n                x: channel.peakF,\n                y: channel.peakVal\n              }],\n              fill: false\n            }, {\n              label: channelNames[0],\n              borderColor: 'rgba(180,180,180)',\n              data: channel.datasets[0].data,\n              fill: true\n            } ],\n            xLabels: channel.xLabels\n          }\n          return (\n            <Card.Section key={\"Card_\" + index}>\n              <Line key={\"Line_\" + index} data={newData} options={options} />\n            </Card.Section>\n          );\n        } else {\n           return( \n              <Card.Section>\n                  <TextContainer>\n                  <p> {[\n                  \"Press connect above to see the chart.\"  \n                  ]} \n                  </p>\n                </TextContainer>              \n              </Card.Section>\n            )\n        }\n\n      } else {\n        return null\n      }\n    });\n  }\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return (\n    <React.Fragment>\n    <Card title={specificTranslations.title}>\n            <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p> {[\n              \"In the previous module we each estimated our heart rate in two conditions, while we were sitting, and while we were standing. \", \n              \"We used a shared google sheet which combines all our data in order to compute group statistics. \",\n              \"The following video shows how to use the data to make plots of the data, compute statistics, and test the difference. \"  \n            ]} \n          <Link url=\"https://docs.google.com/spreadsheets/d/1_R4ViDw5VQv72F-lzi9BJyRPx1-BhQsj7XFckrvSW-Y/edit?usp=sharing\"\n                external={true}>\n            A copy of the anonymized data that you can use to follow along with the video can be found here. \n          </Link>  \n            </p>\n          </TextContainer>\n          <br />\n          <br />\n          <YouTube \n            videoId=\"uyrnVdKteoU\"\n            opts={opts}\n          />\n          <br />\n          <TextContainer>\n            <p> {[\n              \"There are a few problems with the way we estimated heart rate in the previous module. \",\n              \"We used only a single 10 second segment of data which adds variability. \",\n              \"We also used peak detection to find each heart beat, which takes alot of time and is difficult with noisy data. \", \n              \"This required us to make arbirary thesholds to find peaks, and led to some poor estimates in heart rate, as indicated by the number of outliers we needed to remove (~15). \", \n              \"Therefore in this module we will use some signal processing to get a better estimate of heart rate. \"  \n            ]} </p>\n          </TextContainer>\n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p> {[\n              \"Here is an example of some ECG data from our experiment in Module 2. \",\n              \"Notice that just like many other aspects of our bodies function, our heart rate is a rhythm, that is, it repeats in time at regular intervals. \"\n            ]} </p>\n          </TextContainer>\n          <img \n            src={ require(\"./exampleECG.png\")} \n            alt=\"ECG\"\n            width=\"80%\"\n            height=\"auto\"\n          ></img>  \n          <TextContainer>\n            <p> {[\n              \"Therefore, we can use mathematical techniques such as a fourier transform to estimate what frequency is present in the ECG data. \",\n              \"A fourier transform turns any series of numbers into a summed set of sine waves of different sizes. \",\n              \"The following animation shows how a single time-series of data, can be thought of as the sum of different frequencies of sine waves, each of a different magnitude. \", \n              \"The blue line chart in the animation shows what is called the spectra, and indicates the power at each frequency.\" \n            ]} </p>\n          </TextContainer>\n          <br />\n          <img \n            src={ require(\"./fft_animation.gif\")} \n            alt=\"FFT\"\n            width=\"100%\"\n            height=\"auto\"\n          ></img>  \n          </Stack>\n          <Link url=\"https://en.wikipedia.org/wiki/Electrocardiography#/media/File:Limb_leads_of_EKG.png\"\n          external={true}>\n           Image Source - Wikipedia </Link>\n          <Stack>\n          <br />\n          <TextContainer>\n            <p> {[\n                \"Now we can use the muse to estimate our heart rates, but in different ways. Instead of looking at the voltage over time, \",\n                \"We now transform the data to show us what frequencies are present in the continuous signal. \",\n                \"In this frequency domain, we now ignore time and consider how much power of each frequency there is in a segment of data. \",\n                \"If you place your fingers the same way you did when looking at your ECG, you should start to see a peak. \",\n                \"It may help to return to Module 2 and look at the raw data first. \"\n              ]} </p>\n          </TextContainer>\n                 <TextContainer>\n          <p> {[\n            \"If you missed Module 2, here are the instructions: You can take off the muse from your head and place a finger on your right hand on the muse's reference electrode (in the center of the forehead). \",\n            \"We can then place a finger of our left hand on one of the eeg electrodes. Lets use the left forehead electrode (position AF7). \",\n            \"So place your left fingers pinching the left forehead electrode, and your right fingers pinching the center electrode. \",\n            \"Otherwise try to hold the Muse as still as possible, and relax your body. \" \n         ]} </p>\n        </TextContainer>\n        <br />\n        </Stack>\n        <img \n          src={ require(\"./LeftHand.png\")} \n          alt=\"LeftHand\"\n          width=\"25%\"\n          height=\"auto\"\n        ></img>  \n        <img \n          src={ require(\"./RightHand.png\")} \n          alt=\"RightHand\"\n          width=\"25%\"\n          height=\"auto\"\n        ></img> \n        <br />   \n        <Stack>\n          <br />        \n          <img \n            src={ require(\"./electrodediagram2.png\")} \n            alt=\"F7Electrode\"\n            width=\"25%\"\n            height=\"auto\"\n          ></img>\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodediagram2.png\"\n                external={true}>\n            Image Source - EEG101 </Link>\n \n          <TextContainer>\n            <p> {[\n                \"In this new plot, Along the horizontal axis is the beats per minute, or the frequency of the peaks in your ECG. \", \n                \"The vertical y-axis shows the power of the rhythms in the data at each frequency, or how large the changes are between peak and through of the oscillations. \",\n                \"The pink ball shows the estimated peak of the spectra, or your estimated heart rate. \",\n                \"In the following experiment, it is only this pink value that will be saved over time while we record. \",\n                \"Here is an example of what the plot should look like with a strong heart rate signal. \",\n                \"Notice that there are two peaks, 60 BPM and 120 BPM. The larger peak is called the 1st Harmonic, a multiple of the true heart rate. \",\n                \"These harmonics are common in the frequency domain: \"\n              ]} </p>\n          </TextContainer>\n           <img \n            src={ require(\"./exampleSpectra.png\")} \n            alt=\"exampleSpectra\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img>\n        </Stack>\n      </Card.Section>\n    </Card>\n    <Card title=\"Live spectra plot\">\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n    </React.Fragment>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={128} step={128} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={10} step={5} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} max={Settings.sliceFFTHigh - 1}\n        label={'Slice FFT Lower limit: ' + Settings.sliceFFTLow + ' Hz'} \n        value={Settings.sliceFFTLow} \n        onChange={handleSliceFFTLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.sliceFFTLow + 1}\n        label={'Slice FFT Upper limit: ' + Settings.sliceFFTHigh + ' Hz'} \n        value={Settings.sliceFFTHigh} \n        onChange={handleSliceFFTHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, setSettings) {\n\n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return(\n    <Card title={'Record ' + Settings.name +' Data'} sectioned>\n      <Stack>\n        <TextContainer>\n        <p> {[\n          \"Clicking this button will immediately record the value of the pink dot above for \",\n          Settings.secondsToSave,\n          \" seconds. \",\n          \"Therefore, make sure the chart above looks clean and you can see a clear peak in the spectra before pressing record. \",\n           \"We are going to compare two conditions that show a clear difference in heart rate due to blood pressure changes: \",\n           \"Standing and Sitting. \",\n           \"You will record two sessions, one standing and one sitting, pick the order randomly but keep track of which output file is which\"\n           ]} </p>\n        </TextContainer> \n       <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />\n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Save to CSV'}  \n          </Button>\n        </ButtonGroup>\n                <TextContainer>\n          <p> {[\n            \"A .csv file will be saved that can be opened in Google Sheets. \",\n            \"Remember for each person to record two files, one while  standing and one while  sitting. \", \n            \"Here is an example of what the data will look like once loaded. \",\n            \"The first column shows the time in msec of each estimate of heart rate. The second column shows the heart rate estimate in BPM. \",\n            \"Each row represents an estimate from the previous 8 seconds of data. \",\n            \"This is because you need a long segment of time to estimate frequency of rhythms over time. \",\n            \"Each subsequent row is from an 8 second chunk of data, but is taken about 400 ms after the previous row. \",\n            \"You can see the time values increase about 10000 ms during the recording, representing the 10 seconds of data. \",\n            \"So 10000 milliseconds divided into ~400 ms shifts per row gives us the rough number of rows (~25). \", \n            \"The graph shows the values plotted over time, showing that the 1st Harmonic value of 120 BPM was incorrectly recorded on a few windows. \"\n          ]} </p>\n        </TextContainer>\n        <img \n          src={ require(\"./exampleOutput.png\")} \n          alt=\"exampleOutput\"\n          width=\"75%\"\n          height=\"auto\"\n        ></img>\n        <TextContainer>\n          <p> {[\n            \"In this module the analysis will be much easier, since much of the work has been done by the webpage with the fourier transform and peak detection. \",\n            \"The following youtube video will show you how to open the file in Google Sheets, rename it, plot the data, remove any harmonics or outliers,  \",\n            \"then take the average heart rate over the window as an estimate of your heart rate. \"\n            ]} \n        <Link url=\"https://docs.google.com/spreadsheets/d/1AcFxuIZDMNfOifgql2vrFy2nSS3g7c14fOxAdptOgQY/edit?usp=sharing\"\n             external={true}>\n\n         Link to example google sheet from video. \n        </Link>  \n        </p>\n        </TextContainer>\n        <br />\n        <YouTube \n          videoId=\"6EtzwGXjB9Q\"\n          opts={opts}\n        />\n        <TextContainer>\n          <p> {[\n            \"Finally each of you will enter this estimated heart rate for both sitting and standing into a new anonymized google sheet that we are sharing as a class. \", \n            \"We will use this shared google sheet which combines all our data in order to compute group statistics and to compare the two methods of estimating our heart rate. \"  \n          ]} </p>\n\n        <ol>\n          <li>Open up the Module 2&amp;3 Anonymized Data Log and make a copy to work on:&nbsp;\n            <Link url=\"https://docs.google.com/spreadsheets/d/1Z77FCC4XhsKR85PuRD7drzD3y4ZCG1xczfrAuTJTD3k/edit?usp=sharing\" \n              external={true}>\n              Link to spreadsheet\n            </Link>\n          </li>\n          <li>This time your report will focus on comparing the results from module 2 to module 3. Start by explaining that in your report, and explain what the difference was between the two modules measurement.</li>\n          <li>I have already removed outliers from Module 2, Why are there no outliers in Module 3? Include this in your report.</li>\n          <li>Recompute the statistics you did for the Module 2 data comparing sitting vs standing, and do the same for module 3. Compute the average, count, standard deviation, and standard error for each column. In our summary Report the mean and standard deviation in each of the four conditions.&nbsp;</li>\n          <li>Notice how some participants only have a score in one module, let us ignore that for now.</li>\n          <li>Now for both module 2 and module 3, compute a paired samples t-test comparing sitting vs standing. (You have already done this for module 2). Report the two result in apa format (eg. - <i>t</i>(df) = 3.904; p = .003).&nbsp;</li>\n          <li>Include bar graphs showing the sitting and standing HR in each of the four conditions.&nbsp;&nbsp;</li>\n          <li>For both module 2 and module 3, make a new column and compute the difference in heart rate Sitting minus standing (Sitting - Standing). Report the average difference in each condition, as well as the standard deviation of the difference. Show a bar graph of the differences and their standard error.</li>\n          <li>In which module is the standard deviation larger? What does this mean</li><li>It seems we found the same effect in module 2 and module 3. Both t-tests should be significant, but we also want to test how the scores on each module are related.&nbsp;</li>\n          <li>The following two steps require individuals to have a score for all four conditions, so first REMOVE ROWS where individuals only had values from one of the modules. This should leave 65 individuals (remember the row names are on the first row).&nbsp;</li>\n          <li>First we want to test if there is a difference between the differences. This would occur if our methods in module 2 and our methods in module 3 gave different changes in heart rate. Compute a t-test comparing the two difference columns. Report the results in APA format. Interpret the results.</li>\n          <li>Compute the correlation between the difference measured in module 2 and the difference measured in module 3, show a scatter plot comparing them as well. Interpret the strength of the relationship between the change we measured in module 2 and the change we measured in module 3.</li>\n          <li>Make a final concluding statement about which methods is better for estimating heart rate and why.</li>\n        </ol>\n\n        </TextContainer>\n        <br />\n\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title=\"Recording Data\"\n        >\n          <Modal.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n      </Stack>\n    </Card>\n  )\n}\n\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n  dataToSave.push(\n    \"Timestamp (ms),\",\n    \"Estimated BPM\", \n    \"\\n\"\n  );   \n\n  // Create timer \n  const timer$ = timer(Settings.secondsToSave * 1000);\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastHeartSpectra$.pipe(\n    takeUntil(timer$)\n  );   \n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + x.freqs[indexOfMax(x.psd[1])]*60 + \"\\n\");\n      // logging is useful for debugging -yup\n      console.log();\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}\n\n// Find the index of the max value in an array\nfunction indexOfMax(arr) {\n  if (arr.length === 0) {\n      return -1;\n  }\n  var max = arr[0];\n  var maxIndex = 0;\n  for (var i = 1; i < arr.length; i++) {\n      if (arr[i] > max) {\n          maxIndex = i;\n          max = arr[i];\n      }\n  }\n  return maxIndex;\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduHeartSpectra/translations/en.json",
    "content": "{\n  \"title\": \"Heart Rate (Beats per minute)\",\n  \"xlabel\": \"Heart Frequency (BPM)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduIntro/EEGEduIntro.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\nimport { Subject } from \"rxjs\";\n\nimport { Card, Link } from \"@shopify/polaris\";\n\nimport { Line } from \"react-chartjs-2\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as specificTranslations from \"./translations/en\";\n\nimport { generateXTics, standardDeviation } from \"../../utils/chartUtils\";\n\nexport function getSettings () {\n  return {\n    name: \"Intro\",\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 2,\n    srate: 256,\n    duration: 512\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionIntro$) window.subscriptionIntro$.unsubscribe();\n\n  window.pipeIntro$ = null;\n  window.multicastIntro$ = null;\n  window.subscriptionIntro = null;\n\n  // Build Pipe\n window.pipeIntro$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastIntro$ = window.pipeIntro$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastIntro$) {\n    window.subscriptionIntro = window.multicastIntro$.subscribe(data => {\n      setData(introData => {\n        Object.values(introData).forEach((channel, index) => {\n          if (index === 0) {\n            channel.datasets[0].data = data.data[index];\n            channel.xLabels = generateXTics(Settings.srate, Settings.duration);\n            channel.datasets[0].qual = standardDeviation(data.data[index])          \n          }\n        });\n\n        return {\n          ch0: introData.ch0\n        };\n      });\n    });\n\n    window.multicastIntro$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      const options = {\n        ...generalOptions,\n        scales: {\n          xAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.xAxes[0].scaleLabel,\n                labelString: specificTranslations.xlabel\n              }\n            }\n          ],\n          yAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.yAxes[0].scaleLabel,\n                labelString: specificTranslations.ylabel\n              },\n              ticks: {\n                max: 300,\n                min: -300\n              }\n            }\n          ]\n        },\n        elements: {\n          line: {\n            borderColor: 'rgba(' + channel.datasets[0].qual*10 + ', 128, 128)',\n            fill: false\n          },\n          point: {\n            radius: 0\n          }\n        },\n        animation: {\n          duration: 0\n        },\n        title: {\n          ...generalOptions.title,\n          text: 'Voltage signal over time'\n        }\n      };\n\n      if (index === 0) {\n        return (\n          <Card.Section key={\"Card_\" + index}>\n            <Line key={\"Line_\" + index} data={channel} options={options} />\n          </Card.Section>\n        );\n      } else {\n        return null\n      };\n    });\n  }\n\n  return (\n    <React.Fragment>\n\n      <Card title={specificTranslations.title}>\n        <Card.Section>\n          <p>\n            {specificTranslations.intro1}\n          </p>       \n            <div style={chartStyles.wrapperStyle.style}>\n              {renderCharts()}\n            </div>\n          <p>\n            {specificTranslations.intro2}\n          </p>  \n        </Card.Section>\n      </Card>\n\n      <Card title={specificTranslations.neuronsHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.neurons1}\n          </p>\n          <img \n            src={ require(\"./assets/neuronarrow.png\")} \n            alt=\"Single Neuron\"\n            width=\"100%\"\n            height=\"auto\"\n          ></img>\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/neuronarrow.png\"> Image Source - EEG101 </Link>\n          <br />\n          <br />\n          <p>\n            {specificTranslations.neurons2}\n          </p>         \n          <img \n            src={ require(\"./assets/neuronmultiarrow.png\")} \n            alt=\"Multiple Neurons\"\n            width=\"100%\"\n            height=\"auto\"\n          ></img> \n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/neuronmultiarrow.png\"> Image Source - EEG101 </Link>\n          <br />\n          <br />\n          <p>\n            {specificTranslations.neurons3}\n          </p> \n        </Card.Section>\n      </Card>\n\n      <Card title={specificTranslations.oscillationsHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.oscillations1}\n          </p>\n          <img \n            src={ require(\"./assets/awakeasleep.gif\")} \n            alt=\"Awake/Asleep\"\n            width=\"100%\"\n            height=\"auto\"\n          ></img>\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/awakeasleep.gif\"> Image Source - EEG101 </Link>\n          <br />\n          <br />\n          <p>\n            {specificTranslations.oscillations2}\n          </p>         \n        </Card.Section>\n      </Card>\n\n      <Card title={specificTranslations.hardwareHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.hardware1}\n          <br />\n          <br />\n            {specificTranslations.hardware2}\n          </p>\n          <br />\n          <img \n            src={ require(\"./assets/electrodelocations.png\")} \n            alt=\"electrode locations\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img>\n          <br />\n          <br />\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodelocations.png\"> Image Source - EEG101 </Link>\n          <br />\n          <br />\n          <p>\n            {specificTranslations.hardware3}\n          <br />\n          <br />          \n            {specificTranslations.hardware4}\n          <br />\n            <img \n              src={ require(\"./assets/DigitalDAQv2.png\")} //https://upload.wikimedia.org/wikipedia/commons/9/97/DigitalDAQv2.pdf\n              alt=\"DAQ diagram\"\n              width=\"100%\"\n              height=\"auto\"\n            ></img>\n              <Link url=\"https://upload.wikimedia.org/wikipedia/commons/9/97/DigitalDAQv2.pdf\"> Image Source - Wikipedia </Link>\n          <br />\n          <br />\n            {specificTranslations.hardware5}\n          </p>      \n        </Card.Section>\n      </Card>\n\n      <Card title={specificTranslations.museHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.muse1}\n          <br />\n          <br />\n           <img \n            src={ require(\"./assets/musepicture.png\")} \n            alt=\"Awake/Asleep\"\n            width=\"75%\"\n            height=\"auto\"\n          ></img> \n          <br />\n          <br />\n          <Link url=\"https://miro.medium.com/max/2854/1*pK_tLFd8c7_xlOTm1lHdAw.png\"> Image Source - @urish </Link>\n          <br />\n          <br />\n            {specificTranslations.muse2}\n          <br />\n          <img \n            src={ require(\"./assets/electrodediagram.png\")} \n            alt=\"Muse Electrodes\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img> \n          <br />\n           <img \n            src={ require(\"./assets/electrodelegend.png\")} \n            alt=\"ElectrodeLegend\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img> \n          <br />\n          <br />\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodediagram\"> Image Source - EEG101 </Link>\n          <br />\n          <br />      \n            {specificTranslations.muse3}\n          </p>  \n        </Card.Section>\n      </Card>\n\n      <Card title={specificTranslations.signalHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.signal1}\n          <br />\n          <img \n            src={ require(\"./assets/electrodediagram1.png\")} \n            alt=\"SingleElectrode\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img> \n          <br />\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodediagram\"> Image Source - EEG101 </Link>\n          <br />\n          <br />            \n          </p>       \n            <div style={chartStyles.wrapperStyle.style}>\n              {renderCharts()}\n            </div>\n          <p>\n            {specificTranslations.signal2}\n          </p>  \n        </Card.Section>\n      </Card>\n\n\n      <Card title={specificTranslations.creditsHead}>\n        <Card.Section>\n          <p>\n            {specificTranslations.credits1}\n            <Link url=\"http://learn.neurotechedu.com/\">NeurotechEdu. </Link>\n          </p>\n          <p>\n            {specificTranslations.credits2}\n            <Link url=\"https://choosemuse.com/muse-research/\">Interaxon. </Link>\n          </p>\n          <p>\n            {specificTranslations.credits3}\n            <Link url=\"https://github.com/urish/muse-js\">muse-js </Link>\n            {specificTranslations.credits4}\n            <Link url=\"https://medium.com/neurotechx/a-techys-introduction-to-neuroscience-3f492df4d3bf\">A Techy's Introduction to Neuroscience. </Link>\n          </p>\n          <p>\n            {specificTranslations.credits5}\n            <Link url=\"https://github.com/neurosity/eeg-pipes\">eeg-pipes </Link>\n            {specificTranslations.credits6}\n            <Link url=\"https://medium.com/@castillo.io/muse-2016-headband-web-bluetooth-11ddcfa74c83\">Muse 2016 Headband + Web Bluetooth.</Link>\n          </p>\n        </Card.Section>\n      </Card>\n\n    </React.Fragment>\n  );\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduIntro/translations/en.json",
    "content": "{\n  \"title\": \"Introduction\",\n  \"intro1\": [\n    \"Below you are now looking at an live measurement of the electrical potential created by your brain. \",\n    \"Just like a AA battery stores 1.5 volts of electrical potential energy accross its positive and negative leads, \",\n    \"two points on your head also have a much smaller electrical potential when measured accross them. \",\n    \"Here we are watching that live, with the voltage on the vertical axis (\\u03BCV are microvolts, 1 million microvolts are in a volt). \",\n    \"Time is shown in milleseconds along the horizontal axis, with the right side of the chart being the current moment:\"\n  ],\n  \"intro2\": [\n    \"Read on to find out where this electrical potential comes from and what it means. \"\n  ],\n  \"neuronsHead\": \"Neurons\",\n  \"neurons1\": [\n\t\"The brain is made up of cells called neurons. \",\n    \"Neurons communicate using chemical messages that change the electrical potential of the cells they connect with. \",\n    \"This change in electrical potential, if large enough, can make those cells send messages as well, and so on. \",\n    \"For example, an excitatory neuron releases Glutamate on another neuron, which lets in positively charged Sodium ions into the cell and make its interior less negative compared to outside. \",\n    \"These changes in electrical potential create small electrical fields, which act as tiny electrical dipoles like batteries. \" \n  ],\n  \"neurons2\": [\n\t\"The electrical potential accross the cell membrane is small, around -70 mV at rest (1000 microvolts in a millivolt), and it changes around -20 mV during electrical changes in the cell. \",\n\t\"However, if a large group of these tiny dipoles are aligned in space and their electrical potentials change at the same time, \",\n\t\"they can create electrical potentials which are large enough to conduct through the brain tissue and be measurable comparing different points on the head. \"\n  ],\n  \"neurons3\": [\n    \"As you can see above, these electrical potentials measured on the outside of the head fluctuature between about -200 and 200 \\u03BCV. \",\n    \"You can also see cycles between high and low voltage, called oscillations, which can occur in the human brain at a number of frequencies. \",\n    \"You may have heard of some of these brain waves before, but what do they mean? \"\n  ],\n  \"oscillationsHead\": \"Oscillations\",\n  \"oscillations1\": [\n\t\"Large groups of aligned neurons are all becoming more and less active together in groups. \",\n\t\"And these fluctuations in activity seem to occur within certain frequency bands. \",\n\t\"It has been proposed that these different frequencies of neural activity serve functional mechanisms in the brain. \",\n\t\"That is, one of the ways the brain uses to process and communicate information is through these rhythmic processes. \",\n    \"These oscillations can change during different behaviours. One of the most drastic is the difference in the EEG when we fall asleep: \"\n  ],\n  \"oscillations2\": [\n\t\"When we are awake, our EEG signal is dominated by high frequency activity called Beta waves. \",\n\t\"When we fall asleep, our brain slows down. Larger and larger groups of neurons all fire together, in slow oscillations called Delta waves. \",\n\t\"We will learn more in the modules on the frequency spectra and frequency bands about what the various oscillations represent and how they change. \",\n\t\"We use the power of these brain waves to provide real time feedback about the state of your brain and practice Neurofeedback applications. \",\n\t\"We can also use these measures to control oscillations, or control some Brain Machine Interfaces (BMIs). \",\n\t\"But first, a little more about the tecnology we are using to measure and vizualize these signals. \"\n\t  ],\n  \"hardwareHead\": \"EEG Hardware\",\n  \"hardware1\": [\n    \"Because the brain has a great deal of salty water in it, it conducts electricity. \",\n    \"This electrical field gets smeared by the slightly electrically resistive skull and scalp. \",\n    \"Therefore the signal on the outside of the head has very little spatial information about where the signal came from. \",\n    \"Even worse, any potential measured on the head could have an infinite number of dipole configurations inside the head creating it. \",\n    \"Nonetheless, there are still difference in voltage between different parts of the head that may be interesting. \"\n  ],\n  \"hardware2\": [\n    \"To measure the spatial distribution of the voltage signals, EEG is traditionally placed in a regular grid of electrode locations covering the surface of the head. \",\n    \"Each location is given a name, with the letter indicating the location of the head (F-Frontal; C-Central; P-Parietal; T-Temporal; O-Occipital; Fp-Fronto-polar). \",\n    \"The suffix has a z if along the midline, odd numbers over the left hemisphere, and even over the right. \",\n    \"Numbers start along the midline and get larger for more lateral sites on the head. \"\n  ],\n  \"hardware3\": [\n    \"Voltage is electrical potential, and like a battery, is measured as the difference between two locations. \",\n    \"In the case of EEG, we use a reference electrode, shown here in black, to compare each of the other electrode locations against. \",\n    \"The EEG device must therefore measure the difference in voltage at each of its sensors compared to some reference location. \",\n    \"It must then amplify this very small signal, and convert this voltage to some signal that can be saved by a computer (digitization), and in the case of wireless EEG, transmit the signal. \",\n    \"A computer must then receive this signal, and display it, process it, or save it for later analysis. \"\n  ],\n  \"hardware4\": [\n    \"The amplification and digitization turns the continuous voltage into a digitized signal. \",\n    \"This signal now has descrete time steps and descrete difference in voltage. \",\n    \"The hardware's sampling rate controls how many samples of voltage per second (in Hz) are recorded. For example Muse 2 records 256 samples per second. \",\n    \"The digitization's bit depth, or how many memory bits are used to represent each voltage value, influence the smallest change in voltage that a system can measure. \",\n    \"Finally, since there are multiple electrode locations, these individual signals need to be digitized quickly one after another each recording cycle, this is called multiplexing. \"\n  ],  \n  \"hardware5\": [\n    \"One important consideration is the electrical conductivity between the head and the sensor. \",\n    \"An electrode is a conductive piece of material that takes the voltage difference between locations on the head and transmits it along a wire to the amplifier/digitizer. \",\n    \"The signal will therefore be greatly affected by the conductivity of the electrode to head connection. \",\n    \"The inverse of conductivity we call electrical resistance, and since the EEG oscillates like an alternating current power source, we call this impedance. \",\n    \"Notice that the muse uses two different types of sensor material, gold on the forehead, and conductive rubber behind the ears. \"\n  ],\n  \"museHead\": \"Interaxon Muse EEG\",\n  \"muse1\": [\n    \"A decade ago, before the revolution in wireless and battery powered electronics, EEG devices were large and combersome. \",\n    \"EEG devices reqired large amplifiers and digitizers, with dedicated power supplies, and desktop computers for data recording and analysis. \",\n    \"Computing limitations limited live data processing and experimentation. \",\n    \"Within the last decade, a series of new consumer focused EEG devices have been devleoped, drastically reducing the price and portability of the technology. \",\n    \"One of the most common is the Muse and Muse 2 created by Toronto based Interaxon Inc. \"\n  ],\n  \"muse2\": [\n    \"The Muse is sold as an interactive mediation device, for under 300$ US. \",\n    \"Researchers have compared the signals with traditional expensive EEG devices and found very positive results. \",\n    \"Therefore the muse makes for an excellent teaching tool to integrate real time brain measurement into the classroom. \",\n    \"The muse records EEG data at 256 Hz, from four electrode locations shown here: \"\n  ],\n  \"muse3\": [\n    \"In the subsequent modules in this EEGEdu tutorial, you will use the live data from these four electrode locations. \"\n  ],\n  \"signalHead\": \"EEG Signal\",\n  \"signal1\": [\n    \"So now you have some background on how this electrical signal from your brain is generated. \",\n    \"This particular signal is from behind your left ear, Electrode 1 at TP9. \",\n    \"As the amplitude of the noise in the signal decreases, the line should get darker. \"\n  ],\n  \"signal2\": [\n    \"After playing around with this live signal you are ready to move onto some modules, select one from the menu above. \"\n  ],\n  \"creditsHead\": \"Credits\",\n  \"credits1\": [\n    \"EEGEdu is an open source collaborative project with NeurotechX's \"\n  ],\n  \"credits2\": [\n    \"This is also created in collaboration with \"\n  ],\n  \"credits3\": [\n    \"This online tutorial is made using a Muse connection by Web Bluetooth using \"\n  ],\n  \"credits4\": [\n    \"by Uri Shaked who has an excellent introduction to EEG \"\n  ],\n  \"credits5\": [\n    \"The data is processed using Neurosity's \"\n  ],\n  \"credits6\": [\n    \"by Alex Castillo who also has an excellent post about EEG and the web called \"\n  ],\n  \"xlabel\": \"Time (msec)\",\n  \"ylabel\": \"Voltage (\\u03BCV)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduPredict/EEGEduPredict.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, Button, ButtonGroup } from \"@shopify/polaris\";\nimport { Subject } from \"rxjs\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport P5Wrapper from 'react-p5-wrapper';\nimport sketchPredict from './sketchPredictSound';\n\nimport ml5 from 'ml5'\n\nlet knnClassifier = ml5.KNNClassifier();\n\n\nexport function getSettings() {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 256,\n    bins: 256,\n    sliceFFTLow: 1,\n    sliceFFTHigh: 30,\n    duration: 512,\n    srate: 256,\n    name: 'Predict'\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionPredict) window.subscriptionPredict.unsubscribe();\n\n  window.pipePredict$ = null;\n  window.multicastPredict$ = null;\n  window.subscriptionPredict = null;\n\n  // Build Pipe \n  window.pipePredict$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n\n  window.multicastPredict$ = window.pipePredict$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastPredict$) {\n    window.subscriptionPredict = window.multicastPredict$.subscribe(data => {\n      setData(predictData => {\n        Object.values(predictData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[index];\n          channel.xLabels = data.freqs;      \n        });\n\n        return {\n          ch0: predictData.ch0,\n          ch1: predictData.ch1,\n          ch2: predictData.ch2,\n          ch3: predictData.ch3,\n          ch4: predictData.ch4\n        };\n      });\n    });\n\n    window.multicastPredict$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      if (index === 0) {\n\n        if (channel.datasets[0].data) {\n          window.psd = channel.datasets[0].data;\n          window.freqs = channel.xLabels;\n          if (channel.xLabels) {\n            window.bins = channel.xLabels.length;\n          }\n        }   \n        return null \n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n        </Stack>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n  return null\n}\n\n// Classification algorithm (using renderRecord function)\nwindow.exampleCounts = {A: 0, B: 0, C: 0}; \nwindow.thisLabel = 'A';\nwindow.confidences = {A: 1, B: 0, C: 0}; \n\nwindow.isPredicting = false;\nwindow.enoughLabels = false;\n\nexport function renderRecord(recordPopChange, status) {\n  \n  // Adds example from current incoming psd \n  function addExample (label) {\n    if (window.psd) {\n      knnClassifier.addExample(window.psd, label);\n      window.exampleCounts[label]++;\n\n      const numLabels = knnClassifier.getNumLabels();\n      if (numLabels === 3) {\n        window.enoughLabels = true;\n      }\n    }\n  }\n\n  // Classifies current incoming psd and outputs results \n  function classify () {\n    window.isPredicting = true;\n    knnClassifier.classify(window.psd, gotResults)\n  }\n\n  // callback from classify to assign results to window and recurse\n  function gotResults(err, result) {\n    if (result.confidencesByLabel) {\n      window.confidences = result.confidencesByLabel;\n      if (result.label) {\n        switch (result.label) {\n          case 'A':\n            window.thisLabel = 'A';\n            break;\n          case 'B':\n            window.thisLabel = 'B';\n            break;\n          case 'C':\n            window.thisLabel = 'C';\n            break;\n          default: \n            console.log('error with prediction label');\n        }\n      }\n    }\n    classify(); //recursive so it continues to run \n  }\n\n  //buttons for training at prediction\n  return(\n    <React.Fragment>\n      <Card title={'Record Training Data'} sectioned>\n        <Stack>\n          <ButtonGroup>\n            <Button \n              onClick={() => {\n                addExample('A');\n              }}\n              disabled={window.isPredicting || status === generalTranslations.connect}\n            > \n              {'Record Eyes Closed Data - 🔵 - Count: ' + window.exampleCounts['A']}  \n            </Button>\n            <Button \n              onClick={() => {\n                addExample('B');\n              }}\n              disabled={window.isPredicting || status === generalTranslations.connect}\n            > \n              {'Record Eyes Open Data - 🍏 - Count: ' + window.exampleCounts['B']}  \n            </Button> \n            <Button \n              onClick={() => {\n                addExample('C');\n              }}\n              disabled={window.isPredicting || status === generalTranslations.connect}\n            > \n              {'Record Blinking Data - ❤️ - Count: ' + window.exampleCounts['C']}  \n            </Button>       \n          </ButtonGroup>\n        </Stack>\n      </Card>\n\n       <Card title={'Predict current brain state after Training'} sectioned>\n        <Stack>\n          <ButtonGroup>\n            <Button \n              onClick={() => {\n                console.log('Attempting to classify state')\n                classify();\n              }}\n              disabled={window.isPredicting || !window.enoughLabels || status === generalTranslations.connect}\n              primary={true}\n            > \n              {'Predictiction Confidence: ' + window.confidences[window.thisLabel].toFixed(2)}  \n            </Button>\n          </ButtonGroup>\n          <br />   \n          <TextContainer>\n            <p> {'Click or tap on the rectangle to toggle sound'} </p>\n          </TextContainer>\n          <Card.Section>\n            <P5Wrapper sketch={sketchPredict} \n              label={window.thisLabel}\n              confidences={window.confidences}\n\n            />          \n            </Card.Section>\n        </Stack>        \n      </Card>\n     \n    </React.Fragment>\n  )\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduPredict/sketchPredict.js",
    "content": "export default function sketchPredict (p) {\n\n  let label;\n  let confidence;\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.6, 300);\n\n  };\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.6, 300);\n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    label = props.label;\n    confidence = props.confidences[label];\n  };\n\n  p.draw = function () {\n    p.background(250, 250, 150);\n    p.fill(0);\n    p.strokeWeight(5);\n    p.line(p.width/2, 0, p.width/2, p.height);\n    p.textSize(30);\n    p.text('A', p.width/4, 30);\n    p.text('B', p.width-p.width/4, 30)\n    if (label === 'A') {\n      p.fill(120, 120, 250);\n      if (confidence > .8) {\n        p.ellipse(p.width/6, p.height/2, 60);\n      } else {\n        p.ellipse(p.width/3, p.height/2, 20);\n      }\n    } else {\n      p.fill(120, 250, 120);      \n      if (confidence > .8) {\n        p.ellipse(p.width-p.width/6, p.height/2, 60);\n      } else {\n        p.ellipse(p.width-p.width/3, p.height/2, 20);\n      }\n    }\n  }\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduPredict/sketchPredictSound.js",
    "content": "import p5 from \"p5\";\nimport \"p5/lib/addons/p5.sound\";\n\n\n export default function sketchPredict (p) {\n\n  let label;\n  let confidence;\n  let osc;\n  let soundOn = false;\n\n  p.setup = function () {\n    p.createCanvas(p.windowWidth*.6, 300);\n    p.background(255);\n    osc = new p5.Oscillator();\n    osc.setType('sine');\n  };\n\n  p.startSound = function () {\n    osc.freq(262);\n    osc.amp(0);\n    osc.start();\n  }\n\n  p.mouseClicked = function () {\n    if (p.mouseX > 1 && p.mouseX < p.windowWidth*.6 && p.mouseY > 1 && p.mouseY < 300) {\n      if (soundOn) {\n        osc.amp(0, .05)\n        osc.stop();\n        soundOn = false;\n      } else {\n        p.startSound();\n        soundOn = true;\n      }\n    }\n  }\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.6, 300);\n  }\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    label = props.label;\n    confidence = props.confidences[label];\n  };\n\n        // The notes C, E, and G have frequencies in the ratio of 4:5:6. \n        // When they are played together, the three notes blend very well \n        // and are pleasant to the ear; these notes form a major triad or a major chord.\n\n  p.draw = function () {\n    p.fill(0);\n    p.strokeWeight(5);\n    if (label === 'A') {\n      if (confidence > .5) {\n        p.fill(120, 120, 250);\n        osc.amp(1, .1);\n        osc.freq(262);\n      } else {\n        p.fill(240, 240, 255);\n        osc.amp(.25, .1);\n        osc.freq(262);\n      }\n    } else if (label === 'B') {\n      if (confidence > .5) {\n        p.fill(120, 250, 120);  \n        osc.amp(1, .1);\n        osc.freq(327.5);\n      } else {\n        p.fill(240, 255, 240);\n        osc.amp(.25, .1)\n        osc.freq(327.5);\n      }\n    } else {\n      if (confidence > .5) {\n        p.fill(250, 120, 120);  \n        osc.amp(1, .1);\n        osc.freq(393);\n      } else {\n        p.fill(255, 240, 240);\n        osc.amp(.25, .1)\n        osc.freq(393);\n      }\n\n    }\n    p.rect(20,20,p.windowWidth*.5,200);\n\n  }\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduPredict/translations/en.json",
    "content": "{\n  \"title\": \"Predict brain states with a trained classifier\",\n  \"description\": [\n    \"In the next module we will train and test classifiers of brain data like we have been looking at so far. \",\n    \"We will collect data in two different conditions, with the goal of inducing two different brain states. \",\n    \"We will then train a classifier based on the pattern of activity over time, frequency, and space on the head. \",\n    \"We will then use the classifier to predict on real time which of the two brain states are currently happening. \",\n    \"That is, we will attempt to predict if peoples brain activity more closely resembles condition A or conditoin B \",\n    \"Of the training data. \"\n  ],\n  \"xlabel\": \"Frequency (Hz)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduRaw/EEGEduRaw.js",
    "content": " import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal, Link } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { take, takeUntil } from \"rxjs/operators\";\n\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\n\nimport { zipSamples } from \"muse-js\";\nimport YouTube from 'react-youtube'\n\nimport {\n  bandpassFilter,\n  epoch\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport { generateXTics } from \"../../utils/chartUtils\";\n\nexport function getSettings () {\n  return {\n    cutOffLow: .1,\n    cutOffHigh: 100,\n    interval: 25,\n    srate: 256,\n    duration: 1024,\n    name: 'Raw',\n    secondsToSave: 10\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionRaw) window.subscriptionRaw.unsubscribe();\n\n  window.pipeRaw$ = null;\n  window.multicastRaw$ = null;\n  window.subscriptionRaw = null;\n\n  // Build Pipe\n  window.pipeRaw$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastRaw$ = window.pipeRaw$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastRaw$) {\n    window.subscriptionRaw = window.multicastRaw$.subscribe(data => {\n      setData(rawData => {\n        Object.values(rawData).forEach((channel, index) => {\n            channel.datasets[0].data = data.data[index];\n            channel.xLabels = generateXTics(Settings.srate, Settings.duration);\n            // channel.datasets[0].qual = standardDeviation(data.data[index])          \n        });\n\n        return {\n          ch0: rawData.ch0,\n          ch1: rawData.ch1,\n          ch2: rawData.ch2,\n          ch3: rawData.ch3,\n          ch4: rawData.ch4\n        };\n      });\n    });\n\n    window.multicastRaw$.connect();\n    console.log(\"Subscribed to Raw\");\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    const options = {\n      ...generalOptions,\n      scales: {\n        xAxes: [\n          {\n            scaleLabel: {\n              ...generalOptions.scales.xAxes[0].scaleLabel,\n              labelString: specificTranslations.xlabel\n            }\n          }\n        ],\n        yAxes: [\n          {\n            scaleLabel: {\n              ...generalOptions.scales.yAxes[0].scaleLabel,\n              labelString: specificTranslations.ylabel\n            },\n          }\n        ]\n      },\n      animation: {\n        duration: 0\n      },\n      title: {\n        ...generalOptions.title,\n        text: 'Raw data from EEG electrdoes'\n      },\n      legend: {\n        display: true\n      }\n    };\n\n    if (channels.data.ch3.datasets[0].data) {\n      const newData = {\n        datasets: [{\n          label: channelNames[0],\n          borderColor: 'rgba(217,95,2)',\n          data: channels.data.ch0.datasets[0].data.map(function(x) {return x + 300}),\n          fill: false\n        }, {\n          label: channelNames[1],\n          borderColor: 'rgba(27,158,119)',\n          data: channels.data.ch1.datasets[0].data.map(function(x) {return x + 200}),\n          fill: false\n        }, {\n          label: channelNames[2],\n          borderColor: 'rgba(117,112,179)',\n          data: channels.data.ch2.datasets[0].data.map(function(x) {return x + 100}),\n          fill: false\n        }, {\n          label: channelNames[3],\n          borderColor: 'rgba(231,41,138)',\n          data: channels.data.ch3.datasets[0].data.map(function(x) {return x + 0}),\n          fill: false  \n        }, {\n          label: channelNames[4],\n          borderColor: 'rgba(20,20,20)',\n          data: channels.data.ch4.datasets[0].data.map(function(x) {return x + -100}),\n          fill: false  \n        }],\n        xLabels: channels.data.ch0.xLabels\n      }\n\n      return (\n        <Card.Section key={\"Card_\" + 1}>\n          <Line key={\"Line_\" + 1} data={newData} options={options} />\n        </Card.Section>\n      );\n    } else {\n      return( \n        <Card.Section>\n            <TextContainer>\n            <p> {[\n            \"Press connect above to see the chart.\"  \n            ]} \n            </p>\n          </TextContainer>   \n        </Card.Section>  \n      )\n    }      \n  }\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p> {\n              \"Last module we recorded the frequency bands while people had their eyes open or closed for 10 seconds. Although we predicted there should be larger alpha power when the eyes are closed, are results showed the opposite effect:\"\n            } </p>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n          <img \n            src={ require(\"./electrodelocations.png\")} \n            alt=\"exampleSpectra\"\n            width=\"50%\"\n            height=\"auto\"\n          ></img>  \n          </Stack>\n          <br />\n          <Link url=\"https://github.com/NeuroTechX/eeg-101/blob/master/EEG101/src/assets/electrodediagram2.png\"\n                external={true}\n          > Image Source - EEG101 </Link>\n          <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n\n      </Card.Section>\n      <Card.Section>\n        <TextContainer>\n          <p> {[\n            \"If you have not already, it is now time to place the muse on your head. \",\n            \"Getting a good connection between the device and your head is crucial. \",\n            \"The following video gives tips on how to get a great connection: \"\n          ]} </p>\n          <YouTube \n            videoId=\"v8xUYqqJAIg\"\n            opts={opts}\n          />\n          <p> {[\n            \"The above chart shows the data coming from each of the four eeg electrodes. \",\n            \"Time, again, is on the horizontal X-axis and voltage is on the Y-axis. \",\n            \"An offset has been added to the electrodes to separate them vertically. \",\n            \"The saturation of the lines is controlled by the amount of noise in the data. \",\n            \"That is, the cleaner the data, the more rich the colours will become. \",\n            \"Conversely, when the line gets dim, there is too much noise present in the signal. \", \n            \"I have set the filter settings above very loose (.1 to 100 Hz) to let in as much noise as possible so we can learn, we will restrict them in future modules. \"\n          ]} </p>\n        </TextContainer>      \n      </Card.Section>\n      <Card.Section title=\"Artifacts\">\n        <TextContainer>\n          <p> {[\n              \"Before we try to use the EEG to estimate brain activity, we first need to observe what other things can influence the signal. \",\n              \"Artifacts refer to non-EEG noise in the EEG recording that will cloud the results we want from the brain. \",\n              \"There are many sources of noise that the EEG can pickup because all it is doing is recording the voltage changes on the head. \",\n              \"Any other source of voltage or change in resistance of the sensor can affect the signal. \",\n              \"Here is a great overview of the range of possible EEG artifacts that you might come accross. \" \n            ]} </p>\n        </TextContainer>\n        <br />\n        <iframe src=\"//www.slideshare.net/slideshow/embed_code/key/BbKErrb1x7Xf3\" title=\"dum\" width=\"60%\" height=\"400\" frameborder=\"0\" marginwidth=\"0\" marginheight=\"0\" scrolling=\"no\"> \n        </iframe> \n        <div> \n            <Link url=\"//www.slideshare.net/SudhakarMarella/eeg-artifacts-15175461\" external={true}>EEG artifacts</Link> \n           from  \n            <Link url=\"https://www.slideshare.net/SudhakarMarella\" external={true}> Sudhakar Marella</Link>\n        </div>\n      </Card.Section>\n      <Card.Section title=\"Weekly Assignment:\">\n        <TextContainer>\n          <p> {[\n              \"Your assignment this week will be to record plots of four different types of EEG artifact. \",\n              \"I will show you examples that I made using screenshots of the plot above. \", \n              \"To take a screenshot on a mac, press (⌘Command + ⇧Shift + 4) and select the area of the screen you want to save. \",\n              \"You can also record data into a .csv file below and plot artifacts using Google Sheets. \", \n              \"After making your plots, play around with the filter settings above (cutoff Low and cutoff High) and observe how they change the plotted data. \", \n              \"How can we use these filters to help collect cleaner data free from some of these artifacts?\"\n            ]} </p>\n        </TextContainer>  \n\n      </Card.Section>\n\n      <Card.Section title=\"👁️ Eye Blinks/Movements 👀\">\n        <TextContainer>\n          <p> {[\n              \"The eye balls create an electrical field, as they move around they create electrical potentials that are picked up by the EEG sensors. \",\n              \"Also, the eye lids, as the pass over the eye, create an electrical field that is picked up by the EEG electrodes. \", \n              \"Eye movement and Blink artifacts are some of the largest and most difficult to remove from the data. \", \n              \"The best way to get clean data is to try to limit blinking and eye movements. \",\n              \"These eye movement signals can also be treated as a signal of interest, called the Electrooculogram (EOG)\"\n            ]} </p>\n        </TextContainer>\n        <br />\n        <p> {\"Eye Blinks: \"} </p>\n         <img \n            src={ require(\"./blinks.png\")} \n            alt=\"blinks\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n          <br />\n          <p> {\"Horizontal Eye Movements: \"} </p>\n          <img \n            src={ require(\"./horizontal.png\")} \n            alt=\"horizontal\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n          <br />\n          <p> {\"Vertical Eye Movements: \"} </p>\n          <img \n            src={ require(\"./vertical.png\")} \n            alt=\"vertical\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n       </Card.Section>\n\n      <Card.Section title=\"💪🏿 Muscle Activity 💪🏻\">\n        <TextContainer>\n          <p> {[\n              \"When signals are sent from our brain to our muscles the motor neurons release Acetylcholine onto the muscle fibres. \",\n              \"This release causes the muscles to contract, and these contractions create electrical potentials that are also picked up by electrodes. \",\n              \"This signal is called the Electromyogram (EMG), and can be measured over any muscle on your body. \", \n              \"EEG electrodes therefore pickup muscle signals from your face muscles when you smile or frown, and from your chewing muscles. \", \n              \"You should make sure to not be chewing gum for this reason while recording EEG. \"\n            ]} </p>\n        </TextContainer>\n        <br />\n        <p> {\"Example of Jaw Clenching: \"} </p>\n         <img \n            src={ require(\"./muscle.png\")} \n            alt=\"muscle\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n          <br />\n          <p> {\"Zoomed in chewing (notice the difference horizontal axis time range): \"} </p>\n          <img \n            src={ require(\"./muscleClose.png\")} \n            alt=\"muscleClose\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>         \n       </Card.Section>\n\n      <Card.Section title=\"🧑‍🔧 Mechanical artifacts 🧑🏾‍🔧\">\n        <TextContainer>\n          <p> {[\n              \"The EEG electrodes measure the voltage of the human body between two points. Voltage, according to Ohms' law, \",\n              \"is modified both by the current and by the resistance (V = IR). \", \n              \"Therefore, if current stays the same, changes in the electrical resistance of the electrode-body connection can change the voltage. \", \n              \"These changes in voltage will not be distinguishable from real changes in voltage from the brain. \",\n              \"This is one reason that mobile recording of EEG is difficult, movement of the electrodes against the head leads to large voltage changes not due to brain activity. \", \n              \"Therefore we want the muse to be firmly secured to the head, as tight as possible, and we want to avoid excessive movement during recording. \"\n            ]} </p>\n        </TextContainer>\n        <br />\n        <p> {\"Tapping on the muse moves the electrodes against the head: \"} </p>\n         <img \n            src={ require(\"./mechanical.png\")} \n            alt=\"mechanical\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>    \n       </Card.Section>\n\n      <Card.Section title=\"😓 Drifts 😰\">\n        <TextContainer>\n          <p> {[\n              \"Resistance between the sensor and the body leads to less current and smaller measured voltage. \", \n              \"While out salty wet skin conducts electricity well, the dry skin and air does not, nor do oils and dirt that our skin is covered in. \",\n              \"Because voltage changes as resistance changes, we can sometimes see slow drifts in our EEG data as the connection strength changes. \",\n              \"One example is the moment you place the muse on your head, as the resistance decreases the voltage decreases as well as you can see here. \", \n              \"These slow drifts can also occur due to sweating, which decreases the resistance slowly over time between the head and the sensor. \"\n            ]} </p>\n        </TextContainer>\n        <br />\n        <p> {\"When you first put on the muse or when you sweat long drifts occur: \"} </p>      \n         <img \n            src={ require(\"./drift.png\")} \n            alt=\"drift\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n       </Card.Section>              \n\n      <Card.Section title=\"🔌 Electrical Noise 💡\">\n        <TextContainer>\n          <p> {[\n              \"Finally, there is electrical noise all around you. Buildings are supplied with 120 Volts of alternating current to power our lights and electronics. \",\n              \"This electrical alternates in polarity 60 times a second, allowing for much more efficient transportation accross distance locations. \", \n              \"Most of our modern electronics convert this AC electrity to DC power (usually around 5 Volts and 2 Amps). \", \n              \"All the computers in the room take in AC power from the building and convert it to DC in their power supply. \",\n              \"Therefore, there is alot of 60 Hz electrical noise in any building. \", \n              \"Most EEG systems have filters to remove some of this noise, but even still, it is so strong that it is easily picked up by EEG electrodes. \", \n              \"We can use filtering to remove this type of noise from our data. \"\n            ]} </p>\n        </TextContainer>\n       <br />\n        <p> {\"Electical noise at 60Hz from the AC power in the room (I held my wired headphones up to one side then the other of my head): \"} </p>\n         <img \n            src={ require(\"./lineNoise.png\")} \n            alt=\"lineNoise\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img>  \n          <br />\n          <p> {\"Zoomed in 60-Hz electrical noise (notice the difference horizontal axis time range): \"} </p>\n          <img \n            src={ require(\"./lineNoiseClose.png\")} \n            alt=\"lineNoiseClose\"\n            width=\"60%\"\n            height=\"auto\"\n          ></img> \n       </Card.Section>   \n\n    </Card>\n  );\n}\n  \nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n  \n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} step={1} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />          \n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} step={1} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, setSettings) {\n  \n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n\n  return (\n    <Card title={'Record ' + Settings.name + ' Data'} sectioned>\n      <Card.Section>\n        <p>\n          {\"When you are recording raw data it is recommended you \"}\n          {\"first set the sampling point between epochs to 1, then set the epoch duration to 1. \"}\n          {\"Once the live chart disappears entirely you have done it correctly. \"}\n          {\"This will make it so every row of the output file is a single time point and make the data much easier to work with.\"}\n        </p>        \n      </Card.Section>\n      <Stack>\n        <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />\n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Save to CSV'}  \n          </Button>\n        </ButtonGroup>\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title=\"Recording Data\"\n        >\n          <Modal.Section>\n            <TextContainer>\n\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n      </Stack>\n    </Card>\n  )\n}\n\n\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n \n  // for each module subscribe to multicast and make header\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastRaw$.pipe(\n    take(1)\n  );\n  //take one sample to get header info\n  localObservable$.subscribe({ \n  next(x) { \n    dataToSave.push(\n      \"Timestamp (ms),\",\n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch0_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch1_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch2_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"ch3_\" + f + \"ms\"}) + \",\", \n      generateXTics(x.info.samplingRate,x.data[0].length,false).map(function(f) {return \"chAux_\" + f + \"ms\"}) + \",\", \n      \"info\", \n      \"\\n\"\n    );   \n  }\n  });\n\n   // Create timer \n  const timer$ = timer(Settings.secondsToSave * 1000);\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastRaw$.pipe(\n    takeUntil(timer$)\n  );\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduRaw/translations/en.json",
    "content": "{\n  \"title\": \"Raw data\",\n  \"description\": \"First we look at the raw voltage signals coming from each of the four sensors on the muse. TP9 and TP10 are on the ears, AF7 and AF8 are on the forehead. In general EEG electrodes are odd on the left hemisphere and even on the right, and have suffixed with z along the midline.\",\n  \"xlabel\": \"Time (msec)\",\n  \"ylabel\": \"Voltage (\\u03BCV)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSpectra/EEGEduSpectra.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal, Link } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { take, takeUntil } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\nimport YouTube from 'react-youtube'\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nexport function getSettings() {\n  return {\n    cutOffLow: 1,\n    cutOffHigh: 100,\n    interval: 100,\n    bins: 256,\n    sliceFFTLow: 1,\n    sliceFFTHigh: 100,\n    duration: 1024,\n    srate: 256,\n    name: 'Spectra',\n    secondsToSave: 10\n\n  }\n};\n\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionSpectra) window.subscriptionSpectra.unsubscribe();\n\n  window.pipeSpectra$ = null;\n  window.multicastSpectra$ = null;\n  window.subscriptionSpectra = null;\n\n  // Build Pipe \n  window.pipeSpectra$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n\n  window.multicastSpectra$ = window.pipeSpectra$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastSpectra$) {\n    window.subscriptionSpectra = window.multicastSpectra$.subscribe(data => {\n      setData(spectraData => {\n        Object.values(spectraData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[index];\n          channel.xLabels = data.freqs;\n        });\n\n        return {\n          ch0: spectraData.ch0,\n          ch1: spectraData.ch1,\n          ch2: spectraData.ch2,\n          ch3: spectraData.ch3,\n          ch4: spectraData.ch4\n        };\n      });\n    });\n\n    window.multicastSpectra$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    let vertLim = Math.floor(Math.max(...[].concat.apply([], [channels.data.ch0.datasets[0].data,\n                channels.data.ch1.datasets[0].data,\n                channels.data.ch2.datasets[0].data,\n                channels.data.ch3.datasets[0].data,\n                channels.data.ch4.datasets[0].data])\n    ));    \n    const options = {\n      ...generalOptions,\n      scales: {\n        xAxes: [\n          {\n            scaleLabel: {\n              ...generalOptions.scales.xAxes[0].scaleLabel,\n              labelString: specificTranslations.xlabel\n            }\n          }\n        ],\n        yAxes: [\n          {\n            scaleLabel: {\n              ...generalOptions.scales.yAxes[0].scaleLabel,\n              labelString: specificTranslations.ylabel\n            },\n            ticks: {\n              max: vertLim,\n              min: vertLim * -1\n            }\n          }\n        ]\n      },\n      elements: {\n        point: {\n          radius: 3\n        }\n      },\n      title: {\n        ...generalOptions.title,\n        text: 'Spectra data from each electrode'\n      },\n      legend: {\n        display: true\n      }\n    };\n\n\n    if (channels.data.ch3.datasets[0].data) {\n      const newData = {\n        datasets: [{\n          label: channelNames[0],\n          borderColor: 'rgba(217,95,2)',\n          data: channels.data.ch0.datasets[0].data,\n          fill: false\n        }, {\n          label: channelNames[1],\n          borderColor: 'rgba(27,158,119)',\n          data: channels.data.ch1.datasets[0].data,\n          fill: false\n        }, {\n          label: channelNames[2],\n          borderColor: 'rgba(117,112,179)',\n          data: channels.data.ch2.datasets[0].data,\n          fill: false\n        }, {\n          label: channelNames[3],\n          borderColor: 'rgba(231,41,138)',\n          data: channels.data.ch3.datasets[0].data,\n          fill: false  \n        }, {\n          label: channelNames[4],\n          borderColor: 'rgba(20,20,20)',\n          data: channels.data.ch4.datasets[0].data,\n          fill: false  \n        }],\n        xLabels: channels.data.ch0.xLabels\n      }\n\n      return (\n        <Card.Section key={\"Card_\" + 1}>\n          <Line key={\"Line_\" + 1} data={newData} options={options} />\n        </Card.Section>\n      );\n    } else {\n      return (\n        <Card.Section>\n          <Stack>\n            <TextContainer>\n              <p>{'Connect the device above to see the plot'}</p>\n            </TextContainer>\n          </Stack>\n        </Card.Section>\n      )\n    }\n\n\n  }\n\n\n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return (\n    <React.Fragment>\n      <Card title={specificTranslations.title}>\n        <Card.Section>\n          <Stack>\n            <TextContainer>\n              <p>{specificTranslations.description}</p>\n            </TextContainer>\n          </Stack>\n        </Card.Section>\n        <Card.Section>\n          <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n        </Card.Section>\n      </Card>\n      <Card title={'Fourier Transform'}>\n        <Card.Section>\n          <Stack>\n            <TextContainer>\n              <p> {[\n                \"In module 3 we saw how we can use a mathematical technique called the fourier transform to estimate what frequency is present in the ECG data, to estimate heart rate. \",\n                \"A fourier transform turns any series of numbers into a summed set of sine waves of different sizes. \",\n                \"For review, the following animation shows how a single time-series of data can be thought of as the sum of different frequencies of sine waves, each of a different magnitude. \", \n                \"The blue bar chart at the end of the animation shows what is called the frequency spectra, and indicates the power at each frequency.\" \n              ]} </p>\n            </TextContainer>\n            <br />\n            <img \n              src={ require(\"./fft_animation.gif\")} \n              alt=\"FFT\"\n              width=\"50%\"\n              height=\"auto\"\n            ></img> \n            </Stack>\n            <Stack> \n            <Link url=\"https://en.wikipedia.org/wiki/Electrocardiography#/media/File:Limb_leads_of_EKG.png\"\n              external={true}>\n              Image Source - Wikipedia </Link>\n              <br />\n              <TextContainer>\n              <p> {[\n                \"The Fourier transform is a mathematical technique originally developed in the early 1800s in order to mathematically model the movement of heat. \",\n                \"A discrete fourier transform (DFT) is this mathematical technique applied to digital data (a function sampled at different time points), like EEG data. \",\n                \"In order to compute this efficiently, a pair of psychologists and statisticians created the FAST fourier transform (FFT) in the 60's, during the cold war \",\n                \"as a signal processing technique needed to triangulate possible Soviet nuclear launches from a hypothetical array of sensors around the Soviet Union. \",\n                \"The FFT has gone on to be one of the most useful and used algorithms ever created, and is used in a wide array of digital tools. \", \n                ]} \n           <Link url=\"http://www.jezzamon.com/fourier/index.html\"\n              external={true}>\n              Follow this link to an excellent interactive tutorial on the graphical understanding of the fourier transforms using sound, drawings, and images\n             </Link>\n                </p>\n             <p> {[\n                \"We will not cover the mathematical formula or algorithm behind the DFT or FFT here. \",\n                \"A fourier transform turns any series of numbers into a summed set of sine waves of different sizes. \",\n                \"The following animation shows how a single time-series of data, can be thought of as the sum of different frequencies of sine waves, each of a different magnitude. \", \n                \"Amazingly, before digital computers in the late 1800's, in order to model details of light movement, a physical machine was constructed to perform these calculations by hand. \",\n                \"This amazing Harmonic Analyzer still provides an excellent intuition into the mechanics behind decomposing a time series signal into a sum of sin waves. \" \n              ]} </p>\n            </TextContainer>\n\n            <YouTube \n              videoId=\"NAsM30MAHLg\"\n              opts={opts}\n            />\n             <p> {[\n                \"Most computer programming languages now provide an easy way to compute the FFT on time series data. \",\n                \"Other methods of converting time series data into the frequency domain also exist, such as wavelet analysis, in which the product of the data and a family of different frequency wavelets is used to esitmate the data decomposition. \",\n                \"The resolution in frequency of the FFT depends on the NUMBER OF TIME POINTS. \",\n                \"The range of frequencies provided by the FFT depends on the sampling rate of the data , in our case 256 Hz provides frequencies up to 128 Hz (half). \" \n              ]} </p>\n            <p> {[\n                \"Importantly, the uncertainty principle applies to the FFT as well. \",\n                \"It is impossible to know both exactly when something happens, and what frequency it happens at, at the same time. \",\n                \"As an intuition, imagine that in order to estimate your heart rate you obviously need more than a single time point of data, you need multiple beats of the heart (at least one). \",\n                \"Therefore, the resultant estimate of heart rate will not be precise in time, and will apply to the entire time window you put into the FFT. \" \n              ]} </p>\n\n          </Stack>\n        </Card.Section>\n      </Card>\n      <Card title={'Artifact Assignment'}>\n        <Card.Section>\n          <Stack>\n            <TextContainer>\n              <p> {[\n                \"In module 4 we plotted the range of various artifacts that are common in EEG data. \",\n                \"Obviously, these artifacts remain (if not filtered out) in the window of data that is used to compute the FFT. \",\n                \"Therefore, NOT ALL THE DATA IN THE SPECTRA IS FROM THE BRAIN!!! \", \n                \"For instance, blinks show up as low frequencies, and you have already seen how heart artifacts in the data would show up as low frequency power as well, but these are not brain activity. \",\n                \"For your assignment this module, for each type of artifact you observed in Module 4, now you will observe what the spectra looks like, take a screenshot of the result and save these in a google doc. \",\n                \"To take a screenshot on a mac, press (⌘Command + ⇧Shift + 4) and select the area of the screen you want to save.\"\n              ]} </p>\n            </TextContainer>\n          </Stack>\n        </Card.Section>\n      </Card>\n    </React.Fragment>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={128} step={128} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={10} step={5} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} max={Settings.sliceFFTHigh - 1}\n        label={'Slice FFT Lower limit: ' + Settings.sliceFFTLow + ' Hz'} \n        value={Settings.sliceFFTLow} \n        onChange={handleSliceFFTLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.sliceFFTLow + 1}\n        label={'Slice FFT Upper limit: ' + Settings.sliceFFTHigh + ' Hz'} \n        value={Settings.sliceFFTHigh} \n        onChange={handleSliceFFTHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, setSettings) {\n\n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n \n  const opts = {\n    height: '195',\n    width: '320',\n    playerVars: { // https://developers.google.com/youtube/player_parameters\n      autoplay: false\n    }\n  };\n\n  return(\n    <Card title={'Record ' + Settings.name +' Data'} sectioned>\n      <Stack>\n         <TextContainer>\n          <p> {[\n            \"Press the following button after adjusting the settings above in order to record the live spectra over time into a .csv file. \"\n          ]} </p>\n        </TextContainer>\n        <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />\n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Save to CSV'}  \n          </Button>\n        </ButtonGroup>\n   <TextContainer>\n          <p> {[\n            \"A .csv file will be saved that can be opened in Google Sheets. \",\n            \"Here is an example of what the data will look like once loaded. \",\n            \"The first column shows the time in msec of each estimate of the spectra.\",\n            \"Each row therefore represents an estimate from the previous 8 seconds of data EEG data, and the windows used to compute each row are overlapping. \",\n            \"Again, this is because you need a long segment of time to estimate frequency of rhythms over time. \",\n            \"You can see the time values increase about 10000 ms during the recording, representing the 10 seconds of data. \",\n            \"So 10000 milliseconds divided into ~400 ms shifts per row gives us the rough number of rows (~25). \"\n          ]} </p>\n          <img \n            src={ require(\"./exampleRecording.png\")} \n            alt=\"exampleOutput\"\n            width=\"75%\"\n            height=\"auto\"\n          ></img>\n          <p> {[\n            \"The spectra are then shown on each row. Each column represents the power at a different frequency. \",\n            \"The first row shows the frequency and channel label for all the data in that column. \",\n            \"So the first 30 columns after the timestamp are the 30 frequencies from the TP9 electrode \",\n            \"(where 30 is the number of frequencies saved which can be adjusted with the FFT Slice Settings). \",\n            \"After that, the next electrode starts, with another 30 frequencies.\",\n            \"After columns for all 30 frequencies from all four electrodes, another 30 columns show zeros, this is for an optional auxillary channel we are not using here. \",\n            \"Finally columns are saved to record the exact frequencies of each bin of the FFT (redundant with the column names). \"\n          ]} </p>\n        </TextContainer>\n   \n        <TextContainer>\n          <p> {[\n            \"The second part of the assignment for this module involves parsing this output file to organize the data into a format that can be plotted like the live plot on the page. \",\n            \"Data will be averaged over time, and then data for each electrode will be organized and used to make a chart of the spectra\"\n            ]} \n        <Link url=\"https://docs.google.com/spreadsheets/d/1Zdnmti-A0kb1ru3HUNMT9rMTbZYbkStSRNtiRPu3TVU/edit?usp=sharing\"\n             external={true}>\n\n         Link to example google sheet from video. \n        </Link>  \n        </p>\n        </TextContainer>\n        <br />\n        <YouTube \n          videoId=\"YgEgi73e9OM\"\n          opts={opts}\n        />\n\n\n\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title=\"Recording Data\"\n        >\n          <Modal.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n      </Stack>\n    </Card>\n  )\n}\n\n\nfunction saveToCSV(Settings) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastSpectra$.pipe(\n    take(1)\n  );\n\n  localObservable$.subscribe({ \n    next(x) { \n      let freqs = Object.values(x.freqs);\n      dataToSave.push(\n        \"Timestamp (ms),\",\n        freqs.map(function(f) {return \"ch0_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch1_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch2_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch3_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"chAux_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"f_\" + f + \"Hz\"}) + \",\" , \n        \"info\", \n        \"\\n\"\n      );   \n    }\n  });\n\n  // Create timer \n  const timer$ = timer(Settings.secondsToSave * 1000);\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastSpectra$.pipe(\n    takeUntil(timer$)\n  );   \n\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSpectra/translations/en.json",
    "content": "{\n  \"title\": \"Frequency Domain Data\",\n  \"description\": \"In the next demo we will look at the same raw EEG data but this time in the frequency domain. We want to identify the magnitude of oscillations of different frequencies in our live signal. We use the fast fourier transform (FFT) to convert the voltage values over time to the power at each frequency. To use the FFT we pick a particular chunk of data and get an output called a spectra. Every update of the chart is based on subsequent windows of time.\",\n  \"xlabel\": \"Frequency (Hz)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSpectro/EEGEduSpectro.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { Card, Stack, TextContainer, RangeSlider} from \"@shopify/polaris\";\nimport { Subject } from \"rxjs\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport sketchSpectro from './sketchSpectro'\n\nimport P5Wrapper from 'react-p5-wrapper';\n\nexport function getSettings () {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 50,\n    interval: 16,\n    bins: 128,\n    duration: 128,\n    srate: 256,\n    name: 'Spectro',\n    sliceFFTLow: 1,\n    sliceFFTHigh: 100,\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionSpectro) window.subscriptionSpectro.unsubscribe();\n\n  window.pipeSpectro$ = null;\n  window.multicastSpectro$ = null;\n  window.subscriptionSpectro = null;\n\n  // Build Pipe\n  window.pipeSpectro$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n  window.multicastSpectro$ = window.pipeSpectro$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastSpectro$) {\n    window.subscriptionSpectro = window.multicastSpectro$.subscribe(data => {\n      setData(spectroData => {\n        Object.values(spectroData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[index];\n          channel.xLabels = data.freqs\n        });\n\n        return {\n          ch0: spectroData.ch0,\n          ch1: spectroData.ch1,\n          ch2: spectroData.ch2,\n          ch3: spectroData.ch3,\n          ch4: spectroData.ch4\n        };\n      });\n    });\n\n    window.multicastSpectro$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function RenderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      if (channel.datasets[0].data) {\n        window.psd = channel.datasets[0].data;\n        window.freqs = channel.xLabels;\n        if (channel.xLabels) {\n          window.bins = channel.xLabels.length;\n        }\n      }   \n\n      //only left frontal channel\n      if (index === 1 && window.freqs) {\n        return (\n          <React.Fragment key={'dum'}>\n            <Card.Section>\n              {window.freqs.slice(-1)[0] + ' Hz'}\n              <P5Wrapper sketch={sketchSpectro} \n                psd={window.psd}\n                bins={window.bins}\n                 />   \n              {window.freqs[0] + ' Hz'}       \n            </Card.Section>\n          </React.Fragment>\n        );\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n          <img \n            src={ require(\"./electrodediagram2.png\")} \n            alt=\"F7Electrode\"\n            width=\"25%\"\n            height=\"auto\"\n          ></img>\n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{RenderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings);\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTLow: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={128} step={128} max={4096} \n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={10} step={5} max={Settings.duration} \n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={.01} step={.5} max={Settings.cutOffHigh - .5} \n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect}\n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2} \n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} max={Settings.sliceFFTHigh - 1}\n        label={'Slice FFT Lower limit: ' + Settings.sliceFFTLow + ' Hz'} \n        value={Settings.sliceFFTLow} \n        onChange={handleSliceFFTLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.sliceFFTLow + 1}\n        label={'Slice FFT Upper limit: ' + Settings.sliceFFTHigh + ' Hz'} \n        value={Settings.sliceFFTHigh} \n        onChange={handleSliceFFTHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSpectro/sketchSpectro.js",
    "content": "import \"p5/lib/addons/p5.sound\";\n\nexport default function sketchSpectro (p) {\n  \n  let spectrum;\n  let binCount;\n  let speed = 4;\n\n  // canvas is global so we can copy it\n  let cnv;\n  \n  p.setup = function () {\n    cnv = p.createCanvas(p.windowWidth*.6, 400);\n    p.noStroke();\n    p.colorMode(p.RGB);\n  };\n\n  p.myCustomRedrawAccordingToNewPropsHandler = function (props) {\n    spectrum = props.psd;\n    binCount = props.bins;\n  };\n\n  p.windowResized = function() {\n    p.resizeCanvas(p.windowWidth*.6, 400);\n  }\n\n  p.draw = function () {\n    if (spectrum) {\n      // copy the sketch and move it over based on the speed\n      p.copy(cnv, 0, 0, p.width.toFixed(0), p.height.toFixed(0), -speed, 0, p.width.toFixed(0), p.height.toFixed(0));\n\n      // iterate thru current freq spectrum\n      for (let i = 0; i < binCount; i++) {\n        let value;\n        value = spectrum[i];\n\n        let c = (value/5)*255;\n        p.fill(c, c, c);\n        let percent = i / binCount;\n        let y = percent * p.height;\n        p.rect(p.width - speed, p.height - y, speed, p.height / binCount);\n      }\n    }\n  }\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSpectro/translations/en.json",
    "content": "{\n  \"title\": \"Spectrogram (Spectra over time)\",\n  \"description\": [\n    \"Back to the full spectra, we can also look at how this changes over time in a single image. \",\n    \"This image, with time on the horizontal x-axis and freqency on the vertical y-axis is like a heat map or topographic map. \",\n    \"The most recent data is on the right side\",\n    \"Lighter regions are frequencies with larger power. \",\n    \"Remember that on the frequency spectra power is the vertical dimension, here on the spectrogram the height of that line is shown as the lightness.\",\n    \"This plot is showing only data from the left frontal electrode AF7\"\n  ]\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSsvep/EEGEduSsvep.js",
    "content": "import React from \"react\";\nimport { catchError, multicast } from \"rxjs/operators\";\n\nimport { TextContainer, Card, Stack, RangeSlider, Button, ButtonGroup, Modal } from \"@shopify/polaris\";\nimport { saveAs } from 'file-saver';\nimport { take, takeUntil } from \"rxjs/operators\";\nimport { Subject, timer } from \"rxjs\";\n\nimport { channelNames } from \"muse-js\";\nimport { Line } from \"react-chartjs-2\";\n\nimport { zipSamples } from \"muse-js\";\n\nimport {\n  bandpassFilter,\n  epoch,\n  fft,\n  sliceFFT\n} from \"@neurosity/pipes\";\n\nimport { chartStyles, generalOptions } from \"../chartOptions\";\n\nimport * as generalTranslations from \"../translations/en\";\nimport * as specificTranslations from \"./translations/en\";\n\nimport P5Wrapper from 'react-p5-wrapper';\nimport sketchFlashSlow from './sketchFlashSlow';\nimport sketchFlashFast from './sketchFlashFast';\n\nexport function getSettings() {\n  return {\n    cutOffLow: 2,\n    cutOffHigh: 20,\n    interval: 100,\n    bins: 256,\n    sliceFFTLow: 1,\n    sliceFFTHigh: 30,\n    duration: 1024,\n    srate: 256,\n    name: 'Ssvep',\n    secondsToSave: 10\n  }\n};\n\nexport function buildPipe(Settings) {\n  if (window.subscriptionSsvep) window.subscriptionSsvep.unsubscribe();\n\n  window.pipeSsvep$ = null;\n  window.multicastSsvep$ = null;\n  window.subscriptionSsvep = null;\n\n  // Build Pipe \n  window.pipeSsvep$ = zipSamples(window.source.eegReadings$).pipe(\n    bandpassFilter({ \n      cutoffFrequencies: [Settings.cutOffLow, Settings.cutOffHigh], \n      nbChannels: window.nchans }),\n    epoch({\n      duration: Settings.duration,\n      interval: Settings.interval,\n      samplingRate: Settings.srate\n    }),\n    fft({ bins: Settings.bins }),\n    sliceFFT([Settings.sliceFFTLow, Settings.sliceFFTHigh]),\n    catchError(err => {\n      console.log(err);\n    })\n  );\n\n  window.multicastSsvep$ = window.pipeSsvep$.pipe(\n    multicast(() => new Subject())\n  );\n}\n\nexport function setup(setData, Settings) {\n  console.log(\"Subscribing to \" + Settings.name);\n\n  if (window.multicastSsvep$) {\n    window.subscriptionSsvep = window.multicastSsvep$.subscribe(data => {\n      setData(ssvepData => {\n        Object.values(ssvepData).forEach((channel, index) => {\n          channel.datasets[0].data = data.psd[index];\n          channel.xLabels = data.freqs;\n  \n        });\n\n        return {\n          ch0: ssvepData.ch0,\n          ch1: ssvepData.ch1,\n          ch2: ssvepData.ch2,\n          ch3: ssvepData.ch3,\n          ch4: ssvepData.ch4\n        };\n      });\n    });\n\n    window.multicastSsvep$.connect();\n    console.log(\"Subscribed to \" + Settings.name);\n  }\n}\n\nexport function renderModule(channels) {\n  function renderCharts() {\n    return Object.values(channels.data).map((channel, index) => {\n      const options = {\n        ...generalOptions,\n        scales: {\n          xAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.xAxes[0].scaleLabel,\n                labelString: specificTranslations.xlabel\n              }\n            }\n          ],\n          yAxes: [\n            {\n              scaleLabel: {\n                ...generalOptions.scales.yAxes[0].scaleLabel,\n                labelString: specificTranslations.ylabel\n              },\n              ticks: {\n                max: 25,\n                min: 0\n              }\n            }\n          ]\n        },\n        elements: {\n          point: {\n            radius: 3\n          }\n        },\n        title: {\n          ...generalOptions.title,\n          text: generalTranslations.channel + channelNames[index]\n        }\n      };\n\n      if (index === 0) {\n        return (\n          <Card.Section key={\"Card_\" + index}>\n            <Line key={\"Line_\" + index} data={channel} options={options} />\n          </Card.Section>\n        );\n      } else {\n        return null\n      }\n    });\n  }\n\n  return (\n    <Card title={specificTranslations.title}>\n      <Card.Section>\n        <Stack>\n          <TextContainer>\n            <p>{specificTranslations.description}</p>\n          </TextContainer>\n        </Stack>\n      </Card.Section>\n      <Card.Section>\n        <div style={chartStyles.wrapperStyle.style}>{renderCharts()}</div>\n      </Card.Section>\n    </Card>\n  );\n}\n\nexport function renderSliders(setData, setSettings, status, Settings) {\n\n  function resetPipeSetup(value) {\n    buildPipe(Settings);\n    setup(setData, Settings)\n  }\n\n  function handleIntervalRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, interval: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleCutoffHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, cutOffHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTLowRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTLow: value}));\n    resetPipeSetup();\n  }\n\n  function handleSliceFFTHighRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, sliceFFTHigh: value}));\n    resetPipeSetup();\n  }\n\n  function handleDurationRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, duration: value}));\n    resetPipeSetup();\n  }\n\n  return (\n    <Card title={Settings.name + ' Settings'} sectioned>\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={128} step={128} max={4096}\n        label={'Epoch duration (Sampling Points): ' + Settings.duration} \n        value={Settings.duration} \n        onChange={handleDurationRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={10} step={5} max={Settings.duration}\n        label={'Sampling points between epochs onsets: ' + Settings.interval} \n        value={Settings.interval} \n        onChange={handleIntervalRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={.01} step={.5} max={Settings.cutOffHigh - .5}\n        label={'Cutoff Frequency Low: ' + Settings.cutOffLow + ' Hz'} \n        value={Settings.cutOffLow} \n        onChange={handleCutoffLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.cutOffLow + .5} step={.5} max={Settings.srate/2}\n        label={'Cutoff Frequency High: ' + Settings.cutOffHigh + ' Hz'} \n        value={Settings.cutOffHigh} \n        onChange={handleCutoffHighRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={1} max={Settings.sliceFFTHigh - 1}\n        label={'Slice FFT Lower limit: ' + Settings.sliceFFTLow + ' Hz'} \n        value={Settings.sliceFFTLow} \n        onChange={handleSliceFFTLowRangeSliderChange} \n      />\n      <RangeSlider \n        disabled={status === generalTranslations.connect} \n        min={Settings.sliceFFTLow + 1}\n        label={'Slice FFT Upper limit: ' + Settings.sliceFFTHigh + ' Hz'} \n        value={Settings.sliceFFTHigh} \n        onChange={handleSliceFFTHighRangeSliderChange} \n      />\n    </Card>\n  )\n}\n\nexport function renderRecord(recordPopChange, recordPop, status, Settings, recordTwoPopChange, recordTwoPop, setSettings) {\n  const cond1 = \"Slow Frequency\";\n  const cond2 = \"Fast Frequency\";\n  \n  function handleSecondsToSaveRangeSliderChange(value) {\n    setSettings(prevState => ({...prevState, secondsToSave: value}));\n  }\n  \n  return(\n    <Card title={'Record ' + Settings.name +' Data'} sectioned>\n      <Stack>\n        <RangeSlider \n          disabled={status === generalTranslations.connect} \n          min={2}\n          max={180}\n          label={'Recording Length: ' + Settings.secondsToSave + ' Seconds'} \n          value={Settings.secondsToSave} \n          onChange={handleSecondsToSaveRangeSliderChange} \n        />        \n        <ButtonGroup>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings, cond1);\n              recordPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Record ' + cond1 +' Data'}  \n          </Button>\n          <Button \n            onClick={() => {\n              saveToCSV(Settings, cond2);\n              recordTwoPopChange();\n            }}\n            primary={status !== generalTranslations.connect}\n            disabled={status === generalTranslations.connect}\n          > \n            {'Record ' + cond2 + ' Data'}  \n          </Button> \n        </ButtonGroup>\n       \n\n        <Modal\n          open={recordPop}\n          onClose={recordPopChange}\n          title={\"Recording \" + cond1 + \" Data\"}\n        >\n          <Modal.Section>\n           <Card.Section>\n              <P5Wrapper sketch={sketchFlashSlow} />          \n            </Card.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>\n       \n        <Modal\n          open={recordTwoPop}\n          onClose={recordTwoPopChange}\n          title={\"Recording \" + cond2 + \" Data\"}\n        >\n          <Modal.Section>\n           <Card.Section>\n              <P5Wrapper sketch={sketchFlashFast} />          \n            </Card.Section>\n            <TextContainer>\n              <p>\n                Your data is currently recording, \n                once complete it will be downloaded as a .csv file \n                and can be opened with your favorite spreadsheet program. \n                Close this window once the download completes.\n              </p>\n            </TextContainer>\n          </Modal.Section>\n        </Modal>        \n      \n      </Stack>\n    </Card>\n  )\n}\n\n\nfunction saveToCSV(Settings, condition) {\n  console.log('Saving ' + Settings.secondsToSave + ' seconds...');\n  var localObservable$ = null;\n  const dataToSave = [];\n\n  console.log('making ' + Settings.name + ' headers')\n\n  // take one sample from selected observable object for headers\n  localObservable$ = window.multicastSsvep$.pipe(\n    take(1)\n  );\n\n  localObservable$.subscribe({ \n    next(x) { \n      let freqs = Object.values(x.freqs);\n      dataToSave.push(\n        \"Timestamp (ms),\",\n        freqs.map(function(f) {return \"ch0_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch1_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch2_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"ch3_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"chAux_\" + f + \"Hz\"}) + \",\", \n        freqs.map(function(f) {return \"f_\" + f + \"Hz\"}) + \",\" , \n        \"info\", \n        \"\\n\"\n      );   \n    }\n  });\n\n  //create timer\n  const timer$ = timer(Settings.secondsToSave * 1000);\n\n  // put selected observable object into local and start taking samples\n  localObservable$ = window.multicastSsvep$.pipe(\n    takeUntil(timer$)\n  );   \n\n\n  // now with header in place subscribe to each epoch and log it\n  localObservable$.subscribe({\n    next(x) { \n      dataToSave.push(Date.now() + \",\" + Object.values(x).join(\",\") + \"\\n\");\n      // logging is useful for debugging -yup\n      // console.log(x);\n    },\n    error(err) { console.log(err); },\n    complete() { \n      console.log('Trying to save')\n      var blob = new Blob(\n        dataToSave, \n        {type: \"text/plain;charset=utf-8\"}\n      );\n      saveAs(blob, Settings.name + \"_\" +  condition +  \"_Recording_\" + Date.now() + \".csv\");\n      console.log('Completed');\n    }\n  });\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSsvep/sketchFlashFast.js",
    "content": "export default function sketchFlash (p) {\n\n\n  const freq = 11; \n  let x = 0;\n  let startTime = 0;\n  let newOnset = true;\n  const delay = 1000/freq;\n\n  p.setup = function () {\n    p.createCanvas(300, 300);\n    p.frameRate(60);\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(300, 300);\n  }\n\n\n  p.mousePressed = function () {\n    p.background(256);\n  }\n\n  p.draw = function () {\n    p.background(255);\n    x = x+1;\n    if ((p.millis() - startTime) > delay) {\n      newOnset = true;\n    } else {\n      newOnset = false;\n    }\n    if (newOnset) {\n      p.fill(0, 0, 0);\n      startTime = p.millis();  \n    } else {\n      p.fill(255, 255, 255);\n    }\n    p.noStroke();\n    p.ellipse(p.width/2, p.height/2, 300);\n    p.fill(255,0,0);\n    p.text(\"+\", p.width/2, p.height/2);\n  }\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSsvep/sketchFlashSlow.js",
    "content": "export default function sketchFlash (p) {\n\n\n  const freq = 4; \n  let x = 0;\n  let startTime = 0;\n  let newOnset = true;\n  const delay = 1000/freq;\n\n  p.setup = function () {\n    p.createCanvas(300, 300);\n    p.frameRate(60);\n  };\n\n  p.windowResized = function() {\n    p.createCanvas(300, 300);\n  }\n\n\n  p.mousePressed = function () {\n    p.background(256);\n  }\n\n  p.draw = function () {\n    p.background(255);\n    x = x+1;\n    if ((p.millis() - startTime) > delay) {\n      newOnset = true;\n    } else {\n      newOnset = false;\n    }\n    if (newOnset) {\n      p.fill(0, 0, 0);\n      startTime = p.millis();  \n    } else {\n      p.fill(255, 255, 255);\n    }\n    p.noStroke();\n    p.ellipse(p.width/2, p.height/2, 300);\n    p.fill(255,0,0);\n    p.text(\"+\", p.width/2, p.height/2);\n  }\n};"
  },
  {
    "path": "src/components/PageSwitcher/components/EEGEduSsvep/translations/en.json",
    "content": "{\n  \"title\": \"Steady-State Visual Evoked Potential (SSVEP) Experiment\",\n  \"description\": [\n    \"In the next demo we run our second experiment, comparing the spectra in another two conditions. \",\n    \"When we see or hear rhythms in our environemnts (music, flashing lights, etc.), our brain picks up on those rhythms. \",\n    \"The brain starts to pick up on the rhythms and begins to oscillate at the same frequency. \", \n    \"This is called a steady-state response, or sometimes entrainement, and depends on cognitive factors like attention as well. \",\n    \"Here we will compare the spectra in two conditions of different rhythmic visual stimulation.\",\n    \"WARNING: The following experiment utilizes flashing stimuli\"\n  ],\n  \"xlabel\": \"Frequency (Hz)\",\n  \"ylabel\": \"Power (\\u03BCV\\u00B2)\"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/components/chartOptions.js",
    "content": "import * as generalTranslations from \"./translations/en\";\n\nexport const chartStyles = {\n  wrapperStyle: {\n    display: \"flex\",\n    flexWrap: \"wrap\",\n    padding: \"20px\"\n  }\n};\n\nexport const emptyChannelData = {\n  ch0: {\n    datasets: [{}]\n  },\n  ch1: {\n    datasets: [{}]\n  },\n  ch2: {\n    datasets: [{}]\n  },\n  ch3: {\n    datasets: [{}]\n  }\n};\n\nexport const emptyAuxChannelData = {\n  ch0: {\n    datasets: [{}]\n  },\n  ch1: {\n    datasets: [{}]\n  },\n  ch2: {\n    datasets: [{}]\n  },\n  ch3: {\n    datasets: [{}]\n  },\n  ch4: {\n    datasets: [{}]\n  }\n};\n\nexport const emptySingleChannelData = {\n  ch1: {\n    datasets: [{}]\n  }\n};\n\n\nexport const generalOptions = {\n  scales: {\n    xAxes: [\n      {\n        scaleLabel: {\n          display: true\n        }\n      }\n    ],\n    yAxes: [\n      {\n        scaleLabel: {\n          display: true\n        }\n      }\n    ]\n  },\n  elements: {\n    point: {\n      radius: 0\n    }\n  },\n  title: {\n    display: true,\n    text: generalTranslations.channel\n  },\n  responsive: true,\n  tooltips: { enabled: false },\n  legend: { display: false }\n};\n"
  },
  {
    "path": "src/components/PageSwitcher/components/translations/en.json",
    "content": "{\n  \"connect\": \"Connect Muse Headband\",\n  \"connectMock\": \"Connect Mock Data\",\n  \"connecting\": \"Connecting to Muse\",\n  \"connectingMock\": \"Connecting to Mock Data\",\n  \"connected\": \"Muse Headband Connected\",\n  \"connectedMock\": \"Mock Data Connected\",\n  \"connectionFailed\": \"Connection failed\",\n  \"disconnect\": \"Disconnect\",\n  \"channel\": \"Channel: \"\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/translations/en.json",
    "content": "{\n  \"title\": \"Choose your Module\",\n  \"types\": {\n  \t\"intro\": \"1. Introduction\",\n    \"heartRaw\": \"2. Electrocardiogram (Heart beats)\",\n    \"heartSpectra\": \"3. Heart Rate (Beats per minute)\", \n    \"raw\": \"4. Raw and Filtered Data\",\n    \"spectra\": \"5. Frequency Spectra\",\n    \"bands\": \"6. Frequency Bands\",\n    \"animate\": \"7. Brain Controlled Animation\",\n    \"spectro\": \"8. Spectrogram (spectra over time)\",\n    \"alpha\": \"9. Eyes open vs. Eyes closed Experiment\",\n    \"ssvep\": \"10. Steady-State Visual Evoked Potential (SSVEP) Experiment\",\n    \"evoked\": \"11. Stimulus Evoked Event-related potential (ERP)\",\n    \"predict\": \"12. Predict brain states with a trained classifier\"\n  }\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/utils/chartUtils.js",
    "content": "// Function to count by n to something\nexport function customCount(start, end, step = 1) {\n  const len = Math.floor((end - start) / step) + 1;\n  return Array(len)\n    .fill()\n    .map((_, idx) => start + idx * step);\n}\n\n// Average of values in data\nfunction average(data){\n  var sum = data.reduce(function(sum, value){\n    return sum + value;\n  }, 0);\n\n  var avg = sum / data.length;\n  return avg;\n}\n\nexport const bandLabels = [\"Delta\", \"Theta\", \"Alpha\", \"Beta\", \"Gamma\"];\n\n// Generate xTics\nexport function generateXTics(srate, duration, reverse = true) {\n  let tics = [];\n  if (reverse) {\n    tics = customCount(\n      (1000 / srate) * duration,\n      1000 / srate,\n      -(1000 / srate)\n    )\n  } else {\n    tics = customCount(\n      1000 / srate, \n      (1000 / srate) * duration, \n      1000/srate\n    )\n  }\n  return (\n    tics.map(function(each_element) {\n      return Number(each_element.toFixed(0));\n    })\n  )\n}\n\n// Standard deviation of values in values\nexport function standardDeviation(values){\n  var avg = average(values);\n  var squareDiffs = values.map(function(value){\n    var diff = value - avg;\n    var sqrDiff = diff * diff;\n    return sqrDiff;\n  });\n  \n  var avgSquareDiff = average(squareDiffs);\n  var stdDev = Math.sqrt(avgSquareDiff).toFixed(0);\n  return stdDev;\n}\n"
  },
  {
    "path": "src/components/PageSwitcher/utils/mockMuseEEG.js",
    "content": "import { customCount } from './chartUtils'\n\nconst { interval, from } = require('rxjs');\nconst { map, flatMap } = require('rxjs/operators');\n\nconst samples = () => {\n return Array(12)\n  .fill()\n  .map(_ => Math.random()).map(function(x) {return x * 100});\n};\n\nconst transform = (index) => {\n const timestamp = Date.now();\n let chanNums = customCount(0, window.nchans-1);\n return from(chanNums).pipe(\n  map(electrode => ({\n   timestamp,\n   electrode,\n   index,\n   samples: samples()\n  }))\n )\n};\n\nexport const mockMuseEEG = (sampleRate) => {\n let index = 0;\n return interval(1000 / sampleRate).pipe(\n  map(() => index += 1),\n  flatMap(transform),\n )\n};\n"
  },
  {
    "path": "src/index.js",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport { App } from \"./components/App/App\";\n\nReactDOM.render(<App />, document.getElementById(\"root\"));\n"
  }
]