31 Commits

Autor SHA1 Mensagem Data
Alex Castillo 78dd352e71 add music training component
includes phaser
2016-05-15 11:30:51 -04:00
Alex Castillo 118b480690 topo experiment 2016-05-15 11:08:08 -04:00
Alex Castillo e3e9974888 topo via plotly 2016-05-14 19:21:25 -04:00
Alex Castillo 0c237e0116 add assets for readme 2016-05-14 19:01:54 -04:00
Alex Castillo bd534b3216 add default route 2016-05-13 16:54:53 -04:00
Alex Castillo 7393c0603b migrate topo component/route 2016-05-13 16:42:23 -04:00
Alex Castillo 17ef476a32 destroy socket event listener on component deactivate 2016-05-13 15:54:48 -04:00
Alex Castillo ef5857e3a2 add constants and move defaults to service 2016-05-13 15:51:09 -04:00
Alex Castillo 92f27fb400 add frequency bands component/route 2016-05-13 15:24:02 -04:00
Alex Castillo 1e4401c68b abstract frequency chart for various types
+add radar chart
2016-05-13 13:05:06 -04:00
Alex Castillo 64460c3fe6 style fixes 2016-05-13 12:21:43 -04:00
Alex Castillo 4de6d83bf4 migrate to angular2 2016-05-11 10:23:16 -04:00
Alex Castillo efe6fe2829 readme updated 2016-05-09 20:52:51 -04:00
Alex Castillo 7810048145 add npm start script 2016-05-08 13:31:12 -06:00
Alex Castillo 77d62d797f remove duplicate charts property 2016-05-07 16:28:19 -06:00
Alex Castillo a0fc9925d9 add repository and bugs to package.json 2016-05-06 07:25:08 -06:00
Alex Castillo b1fa763845 update package keywords 2016-05-06 07:19:49 -06:00
Alex Castillo 0329ea5703 Merge pull request #18 from NeuroJS/frequency-bands
Closes #17: add gamma to the frequency plot
2016-05-04 23:07:51 -06:00
Alex Castillo 1640a96e95 remove filtered signals 2016-05-04 23:07:04 -06:00
Alex Castillo 60c0ab004c filters fixes 2016-05-04 15:17:23 -06:00
Alex Castillo 5e4ea5b750 Merge pull request #20 from teonbrooks/signalFiltered
Remove filtering from spectrum plots
2016-05-04 14:20:12 -06:00
Teon L Brooks 3fece01fd4 Remove filtering from spectrum plots
- Remove hard-coded sample rate.
- Separated the filtering from the fft
2016-05-04 15:57:08 -04:00
Alex Castillo 39de35ea28 added missing directive 2016-05-04 13:22:25 -06:00
Alex Castillo 6f9a5a260c Closes #17: add gamma to the frequency plot 2016-05-04 11:58:11 -06:00
Alex Castillo 29777e0770 Merge pull request #13 from andrewheusser/filter
bandpass, highpass, lowpass
2016-05-04 11:25:49 -06:00
andrewheusser 3ff08885f3 :) 2016-05-04 09:33:11 -04:00
andrewheusser f76ac21401 topo params 2016-05-04 09:32:19 -04:00
andrewheusser 5e41f2328b added high and low pass 2016-05-03 19:37:20 -04:00
andrewheusser 8041e70ac3 quick patch 2016-05-03 18:43:16 -04:00
andrewheusser 1c9682351c bandstop filter implemented 2016-05-03 18:38:30 -04:00
andrewheusser f41b60515f topo plot patch
topo data is now raw signal and emits on each sample. can someone test
on real data?
2016-05-03 09:48:46 -04:00
85 arquivos alterados com 2011 adições e 749 exclusões
+3
Ver Arquivo
@@ -0,0 +1,3 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 100
+14
Ver Arquivo
@@ -0,0 +1,14 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = 0
trim_trailing_whitespace = false
+28 -3
Ver Arquivo
@@ -1,3 +1,28 @@
.idea
node_modules
npm-debug.log
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
# dependencies
/node_modules
/bower_components
# IDEs and editors
/.idea
# misc
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
#System Files
.DS_Store
+3 -3
Ver Arquivo
@@ -13,15 +13,15 @@ This project is under development, this is just a first draft.
```bash
npm install
node visualizer
npm run visualize
```
* Go to: http://localhost:3060
* Go to: http://localhost:4200
## Simulating data
```bash
node visualizer simulate
npm run simulate
```
## Support
+24
Ver Arquivo
@@ -0,0 +1,24 @@
/* global require, module */
var Angular2App = require('angular-cli/lib/broccoli/angular2-app');
module.exports = function(defaults) {
return new Angular2App(defaults, {
vendorNpmFiles: [
'socket.io-client/socket.io.js',
'smoothie/smoothie.js',
'chart.js/Chart.js',
'ng2-charts/bundles/ng2-charts.js',
'chroma-js/chroma.js',
'plotly.js/dist/plotly.js',
'phaser/dist/phaser.js',
'systemjs/dist/system-polyfills.js',
'systemjs/dist/system.src.js',
'zone.js/dist/*.js',
'es6-shim/es6-shim.js',
'reflect-metadata/*.js',
'rxjs/**/*.js',
'@angular/**/*.js'
]
});
};
+25
Ver Arquivo
@@ -0,0 +1,25 @@
{
"project": {
"version": "0.1.0",
"name": "clitest"
},
"apps": [
{"main": "src/main.ts", "tsconfig": "src/tsconfig.json"}
],
"addons": [],
"packages": [],
"e2e": {
"protractor": {
"config": "config/protractor.conf.js"
}
},
"test": {
"karma": {
"config": "config/karma.conf.js"
}
},
"defaults": {
"prefix": "bci",
"sourceDir": "src"
}
}
-11
Ver Arquivo
@@ -1,11 +0,0 @@
<section ng-class="{ loading: !$ctrl.data }">
<h2>Alpha</h2>
<canvas id="alpha"
class="chart chart-bar"
chart-data="$ctrl.data"
chart-labels="$ctrl.channels"
chart-series="$ctrl.channels"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
-45
Ver Arquivo
@@ -1,45 +0,0 @@
(function () {
var BCIAlpha = {
templateUrl: 'components/alpha/alpha.html',
bindings: {
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.colors = [
{ fillColor: 'rgba(112,185,252,1)' }
];
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 15
};
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.labels = data.labels;
$ctrl.data = data.alpha;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciAlpha', BCIAlpha);
})();
-11
Ver Arquivo
@@ -1,11 +0,0 @@
<section ng-class="{ loading: !$ctrl.data }">
<h2>Beta</h2>
<canvas id="beta"
class="chart chart-bar"
chart-data="$ctrl.data"
chart-labels="$ctrl.channels"
chart-series="$ctrl.channels"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
-45
Ver Arquivo
@@ -1,45 +0,0 @@
(function () {
var BCIBeta = {
templateUrl: 'components/beta/beta.html',
bindings: {
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.colors = [
{ fillColor: 'rgba(138,219,229,1)' }
];
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 15
};
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.labels = data.labels;
$ctrl.data = data.beta;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciBeta', BCIBeta);
})();
-11
Ver Arquivo
@@ -1,11 +0,0 @@
<section ng-class="{ loading: !$ctrl.data }">
<h2>Delta</h2>
<canvas id="delta"
class="chart chart-bar"
chart-data="$ctrl.data"
chart-labels="$ctrl.channels"
chart-series="$ctrl.channels"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
-46
Ver Arquivo
@@ -1,46 +0,0 @@
(function () {
var BCIDelta = {
templateUrl: 'components/delta/delta.html',
bindings: {
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.colors = [
{ fillColor: 'rgba(162,86,178,1)' }
];
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 15,
scaleOverride: true
};
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.labels = data.labels;
$ctrl.data = data.delta;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciDelta', BCIDelta);
})();
-12
Ver Arquivo
@@ -1,12 +0,0 @@
<section ng-class="{ loading: !$ctrl.data }">
<h2>Frequency <span class="frequency-type">{{ $ctrl.type }}</span></h2>
<canvas id="frequency"
class="chart-base"
chart-type="$ctrl.type"
chart-data="$ctrl.data"
chart-labels="$ctrl.labels"
chart-series="$ctrl.channels"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
-56
Ver Arquivo
@@ -1,56 +0,0 @@
(function () {
var BCIFrequency = {
templateUrl: 'components/frequency/frequency.html',
bindings: {
type: '@',
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
// Default chart type as fallback
$ctrl.type = $ctrl.type || 'Line';
var socket = io();
$ctrl.colors = [
{ strokeColor: 'rgba(112,185,252,1)' },
{ strokeColor: 'rgba(116,150,161,1)' },
{ strokeColor: 'rgba(162,86,178,1)' },
{ strokeColor: 'rgba(144,132,246,1)' },
{ strokeColor: 'rgba(138,219,229,1)' },
{ strokeColor: 'rgba(232,223,133,1)' },
{ strokeColor: 'rgba(148,159,177,1)' },
{ strokeColor: 'rgba(182,224,53,1)' }
];
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 15
};
$ctrl.series = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.data = data.data;
$ctrl.labels = data.labels;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciFrequency', BCIFrequency);
})();
-11
Ver Arquivo
@@ -1,11 +0,0 @@
<section ng-class="{ loading: !$ctrl.data }">
<h2>Theta</h2>
<canvas id="theta"
class="chart chart-bar"
chart-data="$ctrl.data"
chart-labels="$ctrl.channels"
chart-series="$ctrl.channels"
chart-options="$ctrl.options"
chart-colours="$ctrl.colors">
</canvas>
</section>
-45
Ver Arquivo
@@ -1,45 +0,0 @@
(function () {
var BCITheta = {
templateUrl: 'components/theta/theta.html',
bindings: {
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.colors = [
{ fillColor: 'rgba(144,132,246,1)' }
];
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
$ctrl.options = {
responsive: true,
animation: true,
animationSteps: 15
};
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.labels = data.labels;
$ctrl.data = data.theta;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciTheta', BCITheta);
})();
-88
Ver Arquivo
@@ -1,88 +0,0 @@
(function () {
angular
.module('bciDashboard')
.directive('bciTimeSeries', bciTimeSeries);
function bciTimeSeries() {
var timeSeries = new SmoothieChart({
millisPerLine: 3000,
grid: {
fillStyle: '#333333',
strokeStyle: 'rgba(0,0,0,0.1)',
sharpLines: false,
verticalSections: 8,
borderVisible: true
},
labels: {
disabled: true
},
maxValue: 8 * 2,
minValue: 0
});
return {
templateUrl: 'components/time-series/time-series.html',
scope: {
eventName: '@'
},
bindToController: true,
controllerAs: '$ctrl',
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.colors = [
{ strokeColor: 'rgba(112,185,252,1)' },
{ strokeColor: 'rgba(116,150,161,1)' },
{ strokeColor: 'rgba(162,86,178,1)' },
{ strokeColor: 'rgba(144,132,246,1)' },
{ strokeColor: 'rgba(138,219,229,1)' },
{ strokeColor: 'rgba(232,223,133,1)' },
{ strokeColor: 'rgba(148,159,177,1)' },
{ strokeColor: 'rgba(182,224,53,1)' }
];
$ctrl.channels = ['CH1','CH2','CH3','CH4','CH5','CH6','CH7','CH8'];
// Construct time series array with 8 lines
var lines = Array(8).fill().map(function () {
return new TimeSeries();
});
lines.forEach(function (line, index) {
timeSeries.addTimeSeries(line, { strokeStyle: $ctrl.colors[index].strokeColor });
});
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.amplitudes = data.amplitudes;
$ctrl.timeline = data.timeline;
});
lines.forEach(function (line, index) {
data.data[index].forEach(function (amplitude) {
line.append(new Date().getTime(), amplitude);
});
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
},
link: function (scope, element) {
// 200 = 50 samples * 4 milliseconds (sample rate)
timeSeries.streamTo(element[0].querySelector('canvas'), 40);
}
}
}
})();
-19
Ver Arquivo
@@ -1,19 +0,0 @@
<section ng-class="{ loading: !$ctrl.grid }">
<h2>Topo</h2>
<section class="topoplot-wrapper">
<article class="topoplot-c1 top left"></article>
<article class="topoplot-c2 top right"></article>
<article class="topoplot-c3 middle left"></article>
<article class="topoplot-c4 middle right"></article>
<article class="topoplot-c5 third left"></article>
<article class="topoplot-c6 third right"></article>
<article class="topoplot-c7 bottom left"></article>
<article class="topoplot-c8 bottom right"></article>
<aside class="topoplot-grid">
<div ng-repeat="pixel in $ctrl.grid track by $index" ng-style="$ctrl.getColor($index,pixel,$ctrl.grid)" ng-class="$ctrl.getClass($index)"></div>
</aside>
</section>
</section>
-44
Ver Arquivo
@@ -1,44 +0,0 @@
(function () {
var BCITopo = {
templateUrl: 'components/topo/topo.html',
bindings: {
eventName: '@'
},
controller: function ($timeout) {
var $ctrl = this;
var socket = io();
$ctrl.getClass = function(index){
return 'topoplot-u' + index
};
$ctrl.getColor = function(index,pixel,grid){
var min = Math.min.apply(Math,grid);
var max = Math.max.apply(Math,grid);
var f = chroma.scale('Spectral').domain([min,max]);
return {'background-color': f(pixel)}
};
socket.on($ctrl.eventName, function (data) {
$timeout(function () {
$ctrl.grid = data.data;
});
});
$ctrl.$onDestroy = function () {
socket.removeListener($ctrl.eventName);
};
}
};
angular
.module('bciDashboard')
.component('bciTopo', BCITopo);
})();
-73
Ver Arquivo
@@ -1,73 +0,0 @@
<!doctype html>
<html ng-app="bciDashboard">
<head>
<title>BCI Dashboard</title>
<link rel="stylesheet" href="node_modules/angular-chart.js/dist/angular-chart.css">
<link rel="stylesheet" href="visualizer.css">
<link rel="stylesheet" href="components/topo/topo.css">
<script src="node_modules/socket.io-client/socket.io.js"></script>
<script src="node_modules/smoothie/smoothie.js"></script>
<script src="node_modules/angular/angular.js"></script>
<script src="node_modules/chart.js/Chart.js"></script>
<script src="node_modules/angular-chart.js/dist/angular-chart.js"></script>
<script src="visualizer.js"></script>
<script src="lib/chroma.min.js"></script>
<script src="components/frequency/frequency.js"></script>
<script src="components/time-series/time-series.js"></script>
<script src="components/alpha/alpha.js"></script>
<script src="components/delta/delta.js"></script>
<script src="components/beta/beta.js"></script>
<script src="components/theta/theta.js"></script>
<script src="components/topo/topo.js"></script>
</head>
<body>
<main>
<h1>BCI Dashboard</h1>
<nav ng-init="mode = 'timeSeries'">
<button ng-click="mode = 'timeSeries'">Time Series</button>
<button ng-click="mode = 'frequency'">Frequency</button>
<button ng-click="mode = 'frequencyRadar'">Frequency Radar</button>
<button ng-click="mode = 'filtered'">Frequency Bands</button>
<button ng-click="mode = 'topo'">Topo</button>
</nav>
<section class="row" ng-if="mode == 'timeSeries'">
<bci-time-series event-name="bci:time" class="block block-75"></bci-time-series>
</section>
<section class="row" ng-if="mode == 'frequency'">
<bci-frequency type="Line" event-name="bci:fft" class="block block-75"></bci-frequency>
</section>
<section class="row" ng-if="mode == 'frequencyRadar'">
<bci-frequency type="Radar" event-name="bci:fft" class="block block-75"></bci-frequency>
</section>
<section class="row" ng-if="mode == 'filtered'">
<bci-delta event-name="bci:fft" class="block block-33"></bci-delta>
<bci-theta event-name="bci:fft" class="block block-33"></bci-theta>
</section>
<section class="row" ng-if="mode == 'filtered'">
<bci-alpha event-name="bci:fft" class="block block-33"></bci-alpha>
<bci-beta event-name="bci:fft" class="block block-33"></bci-beta>
</section>
<section class="row" ng-if="mode == 'topo'">
<bci-topo event-name="bci:topo" class="block block-50"></bci-topo>
</section>
</main>
</body>
</html>
-33
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
-29
Ver Arquivo
@@ -1,29 +0,0 @@
(function () {
angular
.module('bciDashboard', ['chart.js'])
.config(bciDashboardConfig);
function bciDashboardConfig (ChartJsProvider) {
ChartJsProvider.setOptions({
animation: false,
responsive: true,
datasetStrokeWidth: 1,
pointDot: false,
pointDotRadius: 1,
pointDotStrokeWidth: 0,
datasetFill: false,
scaleOverride: true,
scaleStartValue: -2,
scaleStepWidth: 1,
scaleSteps: 6,
barShowStroke: false,
barValueSpacing: 1,
barShowStroke: true,
barStrokeWidth: 1,
strokeColor: 'rgba(116,150,161,1)'
});
}
})();
+3
Ver Arquivo
@@ -0,0 +1,3 @@
export const environment = {
production: false
};
+10
Ver Arquivo
@@ -0,0 +1,10 @@
/* jshint node: true */
module.exports = function(environment) {
return {
environment: environment,
baseURL: '/',
locationType: 'auto'
};
};
+3
Ver Arquivo
@@ -0,0 +1,3 @@
export const environment = {
production: true
};
+51
Ver Arquivo
@@ -0,0 +1,51 @@
/*global jasmine, __karma__, window*/
Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
__karma__.loaded = function () {
};
var distPath = '/base/dist/';
var appPath = distPath + 'app/';
function isJsFile(path) {
return path.slice(-3) == '.js';
}
function isSpecFile(path) {
return path.slice(-8) == '.spec.js';
}
function isAppFile(path) {
return isJsFile(path) && (path.substr(0, appPath.length) == appPath);
}
var allSpecFiles = Object.keys(window.__karma__.files)
.filter(isSpecFile)
.filter(isAppFile);
// Load our SystemJS configuration.
System.config({
baseURL: distPath
});
System.import('system-config.js').then(function() {
// Load and configure the TestComponentBuilder.
return Promise.all([
System.import('@angular/core/testing'),
System.import('@angular/platform-browser-dynamic/testing')
]).then(function (providers) {
var testing = providers[0];
var testingBrowser = providers[1];
testing.setBaseTestProviders(testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS);
});
}).then(function() {
// Finally, load all spec files.
// This will run the tests directly.
return Promise.all(
allSpecFiles.map(function (moduleName) {
return System.import(moduleName);
}));
}).then(__karma__.start, __karma__.error);
+42
Ver Arquivo
@@ -0,0 +1,42 @@
module.exports = function (config) {
config.set({
basePath: '..',
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher')
],
customLaunchers: {
// chrome setup for travis CI using chromium
Chrome_travis_ci: {
base: 'Chrome',
flags: ['--no-sandbox']
}
},
files: [
{ pattern: 'dist/vendor/es6-shim/es6-shim.js', included: true, watched: false },
{ pattern: 'dist/vendor/zone.js/dist/zone.js', included: true, watched: false },
{ pattern: 'dist/vendor/reflect-metadata/Reflect.js', included: true, watched: false },
{ pattern: 'dist/vendor/systemjs/dist/system-polyfills.js', included: true, watched: false },
{ pattern: 'dist/vendor/systemjs/dist/system.src.js', included: true, watched: false },
{ pattern: 'dist/vendor/zone.js/dist/async-test.js', included: true, watched: false },
{ pattern: 'config/karma-test-shim.js', included: true, watched: true },
// Distribution folder.
{ pattern: 'dist/**/*', included: false, watched: true }
],
exclude: [
// Vendor packages might include spec files. We don't want to use those.
'dist/vendor/**/*.spec.js'
],
preprocessors: {},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
+29
Ver Arquivo
@@ -0,0 +1,29 @@
/*global jasmine */
var SpecReporter = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'../e2e/**/*.e2e.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
useAllAngular2AppRoots: true,
beforeLaunch: function() {
require('ts-node').register({
project: 'e2e'
});
},
onPrepare: function() {
jasmine.getEnv().addReporter(new SpecReporter());
}
};
+14
Ver Arquivo
@@ -0,0 +1,14 @@
import { ClitestPage } from './app.po';
describe('clitest App', function() {
let page: ClitestPage;
beforeEach(() => {
page = new ClitestPage();
})
it('should display message saying app works', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('clitest works!');
});
});
+9
Ver Arquivo
@@ -0,0 +1,9 @@
export class ClitestPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('clitest-app h1')).getText();
}
}
+17
Ver Arquivo
@@ -0,0 +1,17 @@
{
"compileOnSave": false,
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"mapRoot": "",
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": false,
"rootDir": ".",
"sourceMap": true,
"sourceRoot": "/",
"target": "es5"
}
}
+1
Ver Arquivo
@@ -0,0 +1 @@
/// <reference path="../typings/main.d.ts" />
+50 -18
Ver Arquivo
@@ -1,30 +1,62 @@
{
"name": "openbci-dashboard",
"private": false,
"version": "0.0.1",
"description": "A fullstack javascript app for capturing and visualizing OpenBCI EEG data",
"main": "visualizer.js",
"name": "clitest",
"version": "0.0.0",
"license": "MIT",
"keywords": [
"openbci",
"fft",
"chartjs"
],
"author": "Alex Castillo",
"scripts": {},
"angular-cli": {},
"scripts": {
"start": "ng server",
"postinstall": "typings install",
"lint": "tslint \"src/**/*.ts\"",
"format": "clang-format -i -style=file --glob=src/**/*.ts",
"visualize": "concurrently \"ng serve\" \"node visualizer\" ",
"simulate": "concurrently \"ng serve\" \"node visualizer simulate\" ",
"pree2e": "webdriver-manager update",
"e2e": "protractor"
},
"private": true,
"dependencies": {
"angular": "^1.5.5",
"angular-chart.js": "^0.10.2",
"chart.js": "^1.1.1",
"@angular/common": "2.0.0-rc.1",
"@angular/compiler": "2.0.0-rc.1",
"@angular/core": "2.0.0-rc.1",
"@angular/platform-browser": "2.0.0-rc.1",
"@angular/platform-browser-dynamic": "2.0.0-rc.1",
"@angular/router": "2.0.0-rc.1",
"chart.js": "^1.0.2",
"chroma-js": "^1.1.1",
"dsp.js": "neurojs/dsp.js",
"es6-shim": "^0.35.0",
"express": "^4.13.4",
"fili": "^1.2.1",
"jstat": "^1.5.2",
"ng2-charts": "^1.0.3",
"nodemon": "^1.9.1",
"openbci-sdk": "^0.3.4",
"phaser": "^2.4.7",
"plotly.js": "^1.10.2",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"smoothie": "^1.27.0",
"socket.io": "^1.4.5",
"socket.io-client": "^1.4.5",
"socket.io": "^1.4.6",
"socket.io-client": "^1.4.6",
"systemjs": "0.19.26",
"topogrid": "^1.0.6",
"yargs": "^4.3.2"
"yargs": "^4.3.2",
"zone.js": "^0.6.12"
},
"devDependencies": {
"angular-cli": "0.0.*",
"clang-format": "^1.0.35",
"codelyzer": "0.0.14",
"ember-cli-inject-live-reload": "^1.4.0",
"jasmine-core": "^2.4.1",
"jasmine-spec-reporter": "^2.4.0",
"karma": "^0.13.15",
"karma-chrome-launcher": "^0.2.3",
"karma-jasmine": "^0.3.8",
"protractor": "^3.3.0",
"ts-node": "^0.5.5",
"tslint": "^3.6.0",
"typescript": "^1.8.10",
"typings": "^0.8.1"
}
}
Ver Arquivo
@@ -1,21 +1,24 @@
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
font-weight: 300;
color: #ffffff;
background-color: #222222;
:host {
display: block;
height: 100vh;
margin: 0;
}
h1 {
margin: 20px;
padding: 20px;
font-weight: 300;
margin: 0;
display: block;
}
.capitalize {
text-transform: capitalize;
}
.loading:after {
@@ -28,57 +31,6 @@ h1 {
font-size: 12px;
}
nav {
margin-left: 20px;
}
button {
appearance: none;
-webkit-appearance: none;
border: 0;
background: transparent;
color: #ffffff;
padding: 8px 20px;
text-transform: uppercase;
font-family: 'Roboto', sans-serif;
font-weight: 300;
font-size: 12px;
letter-spacing: 1px;
margin-right: 2px;
cursor: pointer;
outline: none;
}
button:hover {
opacity: 0.8;
}
nav button:nth-child(1) {
background-color: rgba(112,185,252,1);
}
nav button:nth-child(2) {
background-color: rgba(138,219,229,1);
}
nav button:nth-child(3) {
background-color: rgba(162,86,178,1);
}
nav button:nth-child(4) {
background-color: rgba(144,132,246,1);
}
nav button:nth-child(5) {
background-color: rgba(232,223,133,1);
color: #000000;
}
nav button:nth-child(6) {
background-color: rgba(148,159,177,1);
}
.row {
display: flex;
}
@@ -127,64 +79,57 @@ nav button:nth-child(6) {
min-height: 580px;
}
.time-series canvas {
margin: 40px 80px 40px 40px;
nav {
margin-left: 20px;
margin-bottom: 20px;
}
.time-series-channels,
.time-series-amplitudes {
position: absolute;
top: 60px;
height: 450px;
color: rgba(102,102,102,1);
nav a {
appearance: none;
-webkit-appearance: none;
border: 0;
background: transparent;
color: #ffffff;
padding: 8px 20px;
text-transform: uppercase;
font-family: 'Roboto', sans-serif;
font-weight: 300;
font-size: 12px;
letter-spacing: 1px;
margin-right: 2px;
cursor: pointer;
outline: none;
text-decoration: none;
display: inline-block;
}
.time-series-channels {
width: 40px;
left: 20px;
text-align: left;
nav a:hover {
opacity: 0.8;
}
nav a:nth-child(1) {
background-color: rgba(112,185,252,1);
}
nav a:nth-child(2) {
background-color: rgba(138,219,229,1);
}
nav a:nth-child(3) {
background-color: rgba(162,86,178,1);
}
nav a:nth-child(4) {
background-color: rgba(144,132,246,1);
}
nav a:nth-child(5) {
background-color: rgba(232,223,133,1);
color: #000000;
font-weight: 500;
}
.time-series-amplitudes {
right: 20px;
text-align: right;
}
.time-series-channels ul {
width: 100%;
}
.time-series-channels li {
width: 100%;
height: 100%;
padding-top: 20px;
}
.time-series-channels ul,
.time-series-amplitudes ul {
display: flex;
align-items: flex-end;
justify-content: space-around;
flex-direction: column;
height: 100%;
margin: 0;
padding: 0;
list-style: none;
}
.time-series-duration {
position: absolute;
left: 50px;
width: 98%;
bottom: 22px;
display: flex;
color: rgba(102,102,102,1);
font-size: 12px;
}
.time-series-duration time {
width: 20%;
nav a:nth-child(6) {
background-color: rgba(148,159,177,1);
}
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<main>
<h1>{{title}}</h1>
<nav>
<a [routerLink]="['/time-series']">Time Series</a>
<a [routerLink]="['/frequency/line', { type: 'Line' }]">Frequency Line</a>
<a [routerLink]="['/frequency/radar', { type: 'Radar' }]">Frequency Radar</a>
<a [routerLink]="['/frequency/bands']">Frequency Bands</a>
<a [routerLink]="['/topo']">Topo</a>
<a [routerLink]="['/music-training']">Music Training</a>
</nav>
<router-outlet></router-outlet>
</main>
+22
Ver Arquivo
@@ -0,0 +1,22 @@
import {
beforeEachProviders,
describe,
expect,
it,
inject
} from '@angular/core/testing';
import { DashboardComponent } from '../app/dashboard.component';
beforeEachProviders(() => [DashboardComponent]);
describe('App: DashboardComponent', () => {
it('should create the app',
inject([DashboardComponent], (app: DashboardComponent) => {
expect(app).toBeTruthy();
}));
it('should have as title \'clitest works!\'',
inject([DashboardComponent], (app: DashboardComponent) => {
expect(app.title).toEqual('clitest works!');
}));
});
+37
Ver Arquivo
@@ -0,0 +1,37 @@
import { Component, OnInit } from '@angular/core';
import { TimeSeriesComponent } from './time-series';
import { FrequencyComponent } from './frequency';
import { FrequencyBandsComponent } from './frequency-bands';
import { TopoComponent } from './topo';
import { MusicTrainingComponent } from './music-training';
import { Routes, Router, ROUTER_PROVIDERS, ROUTER_DIRECTIVES } from '@angular/router';
@Component({
moduleId: module.id,
selector: 'bci-dashboard',
templateUrl: 'dashboard.component.html',
styleUrls: ['dashboard.component.css'],
directives: [ROUTER_DIRECTIVES],
providers: [ROUTER_PROVIDERS]
})
@Routes([
{ path: '/', component: MusicTrainingComponent },
{ path: '/time-series', component: TimeSeriesComponent },
{ path: '/frequency/line', component: FrequencyComponent },
{ path: '/frequency/radar', component: FrequencyComponent },
{ path: '/frequency/bands', component: FrequencyBandsComponent },
{ path: '/topo', component: TopoComponent },
{ path: '/music-training', component: MusicTrainingComponent }
])
export class DashboardComponent implements OnInit {
title = 'BCI Dashboard';
constructor (private router: Router) {
}
ngOnInit () {
}
}
+7
Ver Arquivo
@@ -0,0 +1,7 @@
// The file for the current environment will overwrite this one during build
// Different environments can be found in config/environment.{dev|prod}.ts
// The build system defaults to the dev environment
export const environment = {
production: false
};
@@ -0,0 +1,24 @@
:host {
display: inline-block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: 100%;
width: 27%;
overflow: hidden;
}
h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
}
.capitalize {
text-transform: capitalize;
}
@@ -0,0 +1,11 @@
<section class="frequency-band" [ngClass]="{ 'loading': !data }">
<h2 class="capitalize">{{ band }} {{ type }}</h2>
<base-chart class="chart"
[data]="data"
[labels]="channels"
[options]="options"
[colours]="colors"
[series]="channels"
[chartType]="type">
</base-chart>
</section>
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { FrequencyBandComponent } from './frequency-band.component';
describe('Component: FrequencyBand', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [FrequencyBandComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([FrequencyBandComponent],
(component: FrequencyBandComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(FrequencyBandComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(FrequencyBandComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-frequency-band></bci-frequency-band>
`,
directives: [FrequencyBandComponent]
})
class FrequencyBandComponentTestController {
}
@@ -0,0 +1,45 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import * as io from 'socket.io-client';
import { ChartService } from '../shared';
import { CHART_DIRECTIVES } from '../shared/ng2-charts';
import { Constants } from '../shared/constants';
@Component({
moduleId: module.id,
selector: 'bci-frequency-band',
templateUrl: 'frequency-band.component.html',
styleUrls: ['frequency-band.component.css'],
directives: [CHART_DIRECTIVES],
providers: [ChartService, Constants]
})
export class FrequencyBandComponent implements OnInit {
socket: any;
constructor(private chartService: ChartService, private constants: Constants) {
this.socket = io(constants.socket.url);
}
@Input() public type:string;
@Input() public band:string;
@Input() public color:number;
private data:Array<any> = [[]];
private colors:Array<any>;
private channels:Array<string> = this.chartService.getChannels();
private options:any = this.chartService.getChartJSDefaults({
responsive: false
});
ngOnInit() {
this.colors = this.chartService.getColorByIndex(this.color);
this.socket.on(this.constants.socket.events.fft, (data) => {
this.data = data[this.band || 'data'];
});
}
ngOnDestroy () {
this.socket.removeListener(this.constants.socket.events.fft);
}
}
+1
Ver Arquivo
@@ -0,0 +1 @@
export { FrequencyBandComponent } from './frequency-band.component';
@@ -0,0 +1,9 @@
<section>
<bci-frequency-band [type]="'Bar'" [band]="'delta'" [color]="1"></bci-frequency-band>
<bci-frequency-band [type]="'Bar'" [band]="'theta'" [color]="2"></bci-frequency-band>
</section>
<section>
<bci-frequency-band [type]="'Bar'" [band]="'alpha'" [color]="3"></bci-frequency-band>
<bci-frequency-band [type]="'Bar'" [band]="'beta'" [color]="4"></bci-frequency-band>
<bci-frequency-band [type]="'Bar'" [band]="'gamma'" [color]="5"></bci-frequency-band>
</section>
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { FrequencyBandsComponent } from './frequency-bands.component';
describe('Component: FrequencyBands', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [FrequencyBandsComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([FrequencyBandsComponent],
(component: FrequencyBandsComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(FrequencyBandsComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(FrequencyBandsComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-frequency-bands></bci-frequency-bands>
`,
directives: [FrequencyBandsComponent]
})
class FrequencyBandsComponentTestController {
}
@@ -0,0 +1,19 @@
import { Component, OnInit } from '@angular/core';
import { FrequencyBandComponent } from '../frequency-band';
@Component({
moduleId: module.id,
selector: 'bci-frequency-bands',
templateUrl: 'frequency-bands.component.html',
styleUrls: ['frequency-bands.component.css'],
directives: [FrequencyBandComponent]
})
export class FrequencyBandsComponent implements OnInit {
constructor() {}
ngOnInit() {
}
}
+1
Ver Arquivo
@@ -0,0 +1 @@
export { FrequencyBandsComponent } from './frequency-bands.component';
+23
Ver Arquivo
@@ -0,0 +1,23 @@
.chart {
display: block;
}
:host {
display: block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: 100%;
overflow: hidden;
}
h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
}
+11
Ver Arquivo
@@ -0,0 +1,11 @@
<section class="frequency" [ngClass]="{ 'loading': !data }">
<h2>Frequency {{ type }}</h2>
<base-chart class="chart"
[data]="data"
[labels]="labels"
[options]="options"
[colours]="colors"
[series]="channels"
[chartType]="type">
</base-chart>
</section>
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { FrequencyComponent } from './frequency.component';
describe('Component: Frequency', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [FrequencyComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([FrequencyComponent],
(component: FrequencyComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(FrequencyComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(FrequencyComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-frequency></bci-frequency>
`,
directives: [FrequencyComponent]
})
class FrequencyComponentTestController {
}
+46
Ver Arquivo
@@ -0,0 +1,46 @@
import { Component, ElementRef, OnInit, OnDestroy, Input } from '@angular/core';
import { RouteSegment, ROUTER_PROVIDERS } from '@angular/router';
import * as io from 'socket.io-client';
import { ChartService } from '../shared';
import { CHART_DIRECTIVES } from '../shared/ng2-charts';
import { Constants } from '../shared/constants';
@Component({
moduleId: module.id,
selector: 'bci-frequency',
templateUrl: 'frequency.component.html',
styleUrls: ['frequency.component.css'],
directives: [CHART_DIRECTIVES],
providers: [ChartService, Constants, ROUTER_PROVIDERS]
})
export class FrequencyComponent implements OnInit {
socket: any;
constructor(private chartService: ChartService,
private segment: RouteSegment,
private constants: Constants) {
this.socket = io(constants.socket.url);
this.type = segment.getParam('type') || 'Line';
}
@Input() type:string;
private data:Array<any> = [[]];
private labels:Array<any> = [];
private colors:Array<any> = this.chartService.getColors();
private channels:Array<string> = this.chartService.getChannels();
private options:any = this.chartService.getChartJSDefaults();
ngOnInit() {
this.socket.on(this.constants.socket.events.fft, (data) => {
this.data = data.data;
this.labels = data.labels;
});
}
ngOnDestroy () {
this.socket.removeListener(this.constants.socket.events.fft);
}
}
+1
Ver Arquivo
@@ -0,0 +1 @@
export { FrequencyComponent } from './frequency.component';
Ver Arquivo
+2
Ver Arquivo
@@ -0,0 +1,2 @@
export {environment} from './environment';
export {DashboardComponent} from './dashboard.component';
+1
Ver Arquivo
@@ -0,0 +1 @@
export { MusicTrainingComponent } from './music-training.component';
@@ -0,0 +1,20 @@
:host {
display: block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: 100%;
width: 70%;
}
h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
z-index: 1;
}
@@ -0,0 +1,4 @@
<section class="music" [ngClass]="{ 'loading': !data }">
<h2>Music Training</h2>
<div id="phaser"></div>
</section>
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { MusicTrainingComponent } from './music-training.component';
describe('Component: MusicTraining', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [MusicTrainingComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([MusicTrainingComponent],
(component: MusicTrainingComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(MusicTrainingComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(MusicTrainingComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-music-training></bci-music-training>
`,
directives: [MusicTrainingComponent]
})
class MusicTrainingComponentTestController {
}
@@ -0,0 +1,48 @@
import { Component, OnInit, ElementRef } from '@angular/core';
import * as io from 'socket.io-client';
import { Constants } from '../shared/constants';
declare var Phaser: any;
@Component({
moduleId: module.id,
selector: 'bci-music-training',
templateUrl: 'music-training.component.html',
styleUrls: ['music-training.component.css'],
providers: [Constants]
})
export class MusicTrainingComponent implements OnInit {
game: any;
phaserElement: any;
socket: any;
constructor(private view: ElementRef, private constants: Constants) {
this.socket = io(constants.socket.url);
}
ngOnInit() {
this.phaserElement = this.view.nativeElement.querySelector('#phaser');
this.game = new Phaser.Game(480, 480, Phaser.WEBGL, this.phaserElement.id, {
preload: this.preload, create: this.create, update: this.update
});
this.socket.on(this.constants.socket.events.time, (data) => {
console.log('data from music component', data);
});
}
preload () {
this.game.scale.scaleMode = Phaser.ScaleManager.NO_SCALE;
this.game.stage.backgroundColor = '#FF0000';
}
create () {
this.game.physics.startSystem(Phaser.Physics.ARCADE);
}
update () {
}
}
+17
Ver Arquivo
@@ -0,0 +1,17 @@
import {
beforeEachProviders,
it,
describe,
expect,
inject
} from '@angular/core/testing';
import { ChartService } from './chart.service';
describe('Chart Service', () => {
beforeEachProviders(() => [ChartService]);
it('should ...',
inject([ChartService], (service: ChartService) => {
expect(service).toBeTruthy();
}));
});
+67
Ver Arquivo
@@ -0,0 +1,67 @@
import { Injectable } from '@angular/core';
@Injectable()
export class ChartService {
constructor() {}
getChartJSDefaults (overrides: any = {}): any {
return Object.assign({
responsive: true,
animation: false,
animationSteps: 15,
datasetStrokeWidth: 1,
pointDot: false,
pointDotRadius: 1,
pointDotStrokeWidth: 0,
datasetFill: false,
scaleOverride: true,
scaleStartValue: -2,
scaleStepWidth: 1,
scaleSteps: 6,
barShowStroke: false,
barValueSpacing: 1,
barStrokeWidth: 1
}, overrides);
}
getChartSmoothieDefaults (overrides: any = {}): any {
return Object.assign({
millisPerLine: 3000,
grid: {
fillStyle: '#333333',
strokeStyle: 'rgba(0,0,0,0.1)',
sharpLines: false,
verticalSections: this.getChannels().length,
borderVisible: true
},
labels: {
disabled: true
},
maxValue: this.getChannels().length * 2,
minValue: 0
}, overrides);
}
getChannels (): Array<string> {
return Array(8).fill('CH').map((item, index) => item + (index + 1));
}
getColors (): Array<any> {
return [
{ strokeColor: 'rgba(112,185,252,1)', fillColor: 'rgba(112,185,252,1)' },
{ strokeColor: 'rgba(116,150,161,1)', fillColor: 'rgba(116,150,161,1)' },
{ strokeColor: 'rgba(162,86,178,1)', fillColor: 'rgba(162,86,178,1)' },
{ strokeColor: 'rgba(144,132,246,1)', fillColor: 'rgba(144,132,246,1)' },
{ strokeColor: 'rgba(138,219,229,1)', fillColor: 'rgba(138,219,229,1)' },
{ strokeColor: 'rgba(232,223,133,1)', fillColor: 'rgba(232,223,133,1)' },
{ strokeColor: 'rgba(148,159,177,1)', fillColor: 'rgba(148,159,177,1)' },
{ strokeColor: 'rgba(77,83,96,1)', fillColor: 'rgba(77,83,96,1)' }
];
}
getColorByIndex (index:number): Array<any> {
return this.getColors().filter((c, i) => index === i);
}
}
+17
Ver Arquivo
@@ -0,0 +1,17 @@
import { Injectable } from '@angular/core';
@Injectable()
export class Constants {
socket: any;
constructor(){
this.socket = {
url: 'http://localhost:8080',
events: {
fft: 'bci:fft',
time: 'bci:time',
topo: 'bci:topo'
}
};
}
}
+2
Ver Arquivo
@@ -0,0 +1,2 @@
export * from './chart.service';
export * from './ng2-charts';
+265
Ver Arquivo
@@ -0,0 +1,265 @@
import {
Component, OnDestroy, OnInit, OnChanges,
EventEmitter, ElementRef, Input
} from '@angular/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass} from '@angular/common';
declare var Chart:any;
@Component({
selector: 'chart',
template: `<canvas></canvas>`,
directives: [CORE_DIRECTIVES, NgClass]
})
export class ChartsComponent {}
@Component({
selector: 'base-chart',
properties: [
'data',
'labels',
'series',
'colours',
'chartType',
'legend',
'options'
],
events: ['chartClick', 'chartHover'],
template: `
<canvas style="width: 100%; height: 100%;" (click)="click($event)" (mousemove)="hover($event)"></canvas>
`,
directives: [CORE_DIRECTIVES, FORM_DIRECTIVES, NgClass]
})
export class BaseChartComponent implements OnInit, OnDestroy, OnChanges {
@Input() public data:Array<any> = [];
@Input() public labels:Array<any> = [];
@Input() public options:any = {responsive: true};
@Input() public chartType:string;
@Input() public series:Array<any> = [];
@Input() public colours:Array<any> = [];
@Input() public legend:boolean;
private ctx:any;
private cvs:any;
private parent:any;
private chart:any;
private legendTemplate:any;
private initFlag:boolean = false;
private chartClick:EventEmitter<any> = new EventEmitter();
private chartHover:EventEmitter<any> = new EventEmitter();
private defaultsColours:Array<any> = [
{
fillColor: 'rgba(151,187,205,0.2)',
strokeColor: 'rgba(151,187,205,1)',
pointColor: 'rgba(151,187,205,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(151,187,205,0.8)',
color: 'rgba(151,187,205,1)',
highlight: 'rgba(151,187,205,0.8)'
}, {
fillColor: 'rgba(220,220,220,0.2)',
strokeColor: 'rgba(220,220,220,1)',
pointColor: 'rgba(220,220,220,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(220,220,220,0.8)',
color: 'rgba(220,220,220,1)',
highlight: 'rgba(220,220,220,0.8)'
}, {
fillColor: 'rgba(247,70,74,0.2)',
strokeColor: 'rgba(247,70,74,1)',
pointColor: 'rgba(247,70,74,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(247,70,74,0.8)',
color: 'rgba(247,70,74,1)',
highlight: 'rgba(247,70,74,0.8)'
}, {
fillColor: 'rgba(70,191,189,0.2)',
strokeColor: 'rgba(70,191,189,1)',
pointColor: 'rgba(70,191,189,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(70,191,189,0.8)',
color: 'rgba(70,191,189,1)',
highlight: 'rgba(70,191,189,0.8)'
}, {
fillColor: 'rgba(253,180,92,0.2)',
strokeColor: 'rgba(253,180,92,1)',
pointColor: 'rgba(253,180,92,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(253,180,92,0.8)',
color: 'rgba(253,180,92,1)',
highlight: 'rgba(253,180,92,0.8)'
}, {
fillColor: 'rgba(148,159,177,0.2)',
strokeColor: 'rgba(148,159,177,1)',
pointColor: 'rgba(148,159,177,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(148,159,177,0.8)',
color: 'rgba(148,159,177,1)',
highlight: 'rgba(148,159,177,0.8)'
}, {
fillColor: 'rgba(77,83,96,0.2)',
strokeColor: 'rgba(77,83,96,1)',
pointColor: 'rgba(77,83,96,1)',
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: 'rgba(77,83,96,0.8)',
color: 'rgba(77,83,96,1)',
highlight: 'rgba(77,83,96,0.8)'
}];
private element:ElementRef;
public constructor(element:ElementRef) {
this.element = element;
}
public ngOnInit():any {
this.ctx = this.element.nativeElement.children[0].getContext('2d');
this.cvs = this.element.nativeElement.children[0];
this.parent = this.element.nativeElement;
this.refresh();
this.initFlag = true;
}
public ngOnChanges():any {
if (this.initFlag) {
this.refresh();
}
}
public ngOnDestroy():any {
if (this.chart) {
this.chart.destroy();
this.chart = void 0;
}
if (this.legendTemplate) {
this.legendTemplate.destroy();
this.legendTemplate = void 0;
}
}
public setLegend():void {
let list = this.parent.getElementsByTagName('ul');
if (list.length) {
list[0].remove();
this.parent.insertAdjacentHTML('beforeend', this.chart.generateLegend());
} else {
this.parent.insertAdjacentHTML('beforeend', this.chart.generateLegend());
}
}
public getColour(colour:Array<number>):any {
return {
fillColor: this.rgba(colour, 0.2),
strokeColor: this.rgba(colour, 1),
pointColor: this.rgba(colour, 1),
pointStrokeColor: '#fff',
pointHighlightFill: '#fff',
pointHighlightStroke: this.rgba(colour, 0.8),
color: this.rgba(colour, 1),
highlight: this.rgba(colour, 0.8)
};
}
public getRandomInt(min:number, max:number):number {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
public rgba(colour:Array<number>, alpha:number):string {
return 'rgba(' + colour.concat(alpha).join(',') + ')';
}
public click(evt:any):void {
let atEvent = this.chart.getPointsAtEvent || this.chart.getBarsAtEvent || this.chart.getSegmentsAtEvent;
let activePoints = atEvent.call(this.chart, evt);
if (activePoints.length > 0) {
let activeLabel = activePoints[0].label;
this.chartClick.emit({activePoints: activePoints, activeLabel: activeLabel});
}
}
public hover(evt:any):void {
let atEvent = this.chart.getPointsAtEvent || this.chart.getBarsAtEvent || this.chart.getSegmentsAtEvent;
let activePoints = atEvent.call(this.chart, evt);
if (activePoints.length > 0) {
let activeLabel = activePoints[0].label;
let activePoint = activePoints[0].value;
this.chartHover.emit({activePoints: activePoints, activePoint: activePoint, activeLabel: activeLabel});
}
}
public getChartBuilder(ctx:any, data:Array<any>, options:any):any {
return new Chart(ctx)[this.chartType](data, options);
}
public getDataObject(label:string, value:any):any {
if (this.chartType === 'Line'
|| this.chartType === 'Bar'
|| this.chartType === 'Radar') {
return {
label: label,
data: value
};
}
if (this.chartType === 'Pie'
|| this.chartType === 'Doughnut'
|| this.chartType === 'PolarArea') {
return {
label: label,
value: value
};
}
return void 0;
}
public getChartData(labels:any, dataObject:any):any {
if (this.chartType === 'Line'
|| this.chartType === 'Bar'
|| this.chartType === 'Radar') {
return {
labels: labels,
datasets: dataObject
};
}
if (this.chartType === 'Pie'
|| this.chartType === 'Doughnut'
|| this.chartType === 'PolarArea') {
return dataObject;
}
}
private refresh():any {
if (this.options.responsive && this.parent.clientHeight === 0) {
return setTimeout(() => this.refresh(), 50);
}
this.ngOnDestroy();
let dataset:Array<any> = [];
for (let i = 0; i < this.data.length; i++) {
let colourDesc:Array<number> = [this.getRandomInt(0, 255), this.getRandomInt(0, 255), this.getRandomInt(0, 255)];
let colour = i < this.colours.length ? this.colours[i] : this.defaultsColours[i] || this.getColour(colourDesc);
let data:any = Object.assign(colour,
this.getDataObject(this.series[i] || this.labels[i], this.data[i]));
dataset.push(data);
}
let data:any = this.getChartData(this.labels, dataset);
this.chart = this.getChartBuilder(this.ctx, data, this.options);
if (this.legend) {
this.setLegend();
}
}
}
export const CHART_DIRECTIVES:Array<any> = [ChartsComponent, BaseChartComponent];
+1
Ver Arquivo
@@ -0,0 +1 @@
export { TimeSeriesComponent } from './time-series.component';
@@ -0,0 +1,82 @@
:host {
display: block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: 100%;
width: 1060px;
overflow: hidden;
}
h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
}
.time-series canvas {
margin: 40px 80px 40px 40px;
}
.time-series-channels,
.time-series-amplitudes {
position: absolute;
top: 60px;
height: 450px;
color: rgba(102,102,102,1);
font-size: 12px;
}
.time-series-channels {
width: 40px;
left: 20px;
text-align: left;
color: #000000;
font-weight: 500;
}
.time-series-amplitudes {
right: 20px;
text-align: right;
}
.time-series-channels ul {
width: 100%;
}
.time-series-channels li {
width: 100%;
height: 100%;
padding-top: 20px;
}
.time-series-channels ul,
.time-series-amplitudes ul {
display: flex;
align-items: flex-end;
justify-content: space-around;
flex-direction: column;
height: 100%;
margin: 0;
padding: 0;
list-style: none;
}
.time-series-duration {
position: absolute;
left: 50px;
width: 98%;
bottom: 22px;
display: flex;
color: rgba(102,102,102,1);
font-size: 12px;
}
.time-series-duration time {
width: 20%;
}
@@ -1,9 +1,9 @@
<section class="time-series" ng-class="{ loading: !$ctrl.amplitudes }">
<section class="time-series" [ngClass]="{ 'loading': !amplitudes }">
<h2>Time Series</h2>
<aside class="time-series-channels">
<ul>
<li ng-repeat="channel in $ctrl.channels track by $index"
ng-style="{ 'color': $ctrl.colors[$index].strokeColor }">
<li *ngFor="let channel of channels, let i = index"
[ngStyle]="{ 'color': colors[i].strokeColor }">
{{ channel }}
</li>
</ul>
@@ -11,14 +11,14 @@
<canvas id="timeSeries" width="950" height="450"></canvas>
<aside class="time-series-amplitudes">
<ul>
<li ng-repeat="amplitude in $ctrl.amplitudes track by $index"
ng-style="{ 'color': $ctrl.colors[$index].strokeColor }">
<li *ngFor="let amplitude of amplitudes, let i = index"
[ngStyle]="{ 'color': colors[i].strokeColor }">
{{ amplitude }}
</li>
</ul>
</aside>
<footer class="time-series-duration">
<time ng-repeat="time in $ctrl.timeline track by $index"
<time *ngFor="let time of timeline"
datetime="P1M">
{{ time }}
</time>
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { TimeSeriesComponent } from './time-series.component';
describe('Component: TimeSeries', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [TimeSeriesComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([TimeSeriesComponent],
(component: TimeSeriesComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(TimeSeriesComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(TimeSeriesComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-time-series></bci-time-series>
`,
directives: [TimeSeriesComponent]
})
class TimeSeriesComponentTestController {
}
+68
Ver Arquivo
@@ -0,0 +1,68 @@
import { Component, ElementRef, OnInit, OnDestroy } from '@angular/core';
import { SmoothieChart, TimeSeries } from 'smoothie';
import { ChartService } from '../shared';
import * as io from 'socket.io-client';
import { Constants } from '../shared/constants';
@Component({
moduleId: module.id,
selector: 'bci-time-series',
templateUrl: 'time-series.component.html',
styleUrls: ['time-series.component.css'],
providers: [ChartService, Constants]
})
export class TimeSeriesComponent implements OnInit {
socket: any;
constructor(private view: ElementRef,
private chartService: ChartService,
private constants: Constants) {
this.socket = io(constants.socket.url);
this.chartService = chartService;
}
private options = this.chartService.getChartSmoothieDefaults();
private timeSeries = new SmoothieChart(this.options);
private amplitudes = [];
private timeline = [];
private lines = Array(8).fill(0).map(() => new TimeSeries());
private channels = this.chartService.getChannels();
private colors = this.chartService.getColors();
ngOnInit() {
this.addTimeSeriesLines();
this.socket.on(this.constants.socket.events.time, (data) => {
this.amplitudes = data.amplitudes;
this.timeline = data.timeline;
this.appendTimeSeriesLines(data.data);
});
}
ngOnDestroy () {
this.socket.removeListener(this.constants.socket.events.time);
}
ngAfterViewInit () {
this.timeSeries.streamTo(this.view.nativeElement.querySelector('canvas'), 40);
}
addTimeSeriesLines () {
this.lines.forEach((line, index) => {
this.timeSeries.addTimeSeries(line, {
strokeStyle: this.colors[index].strokeColor
});
});
}
appendTimeSeriesLines (data) {
this.lines.forEach((line, index) => {
data[index].forEach((amplitude) => {
line.append(new Date().getTime(), amplitude);
});
});
}
}
+1
Ver Arquivo
@@ -0,0 +1 @@
export { TopoComponent } from './topo.component';
@@ -1,8 +1,27 @@
* {
box-sizing: border-box;
}
:host {
display: block;
margin: 20px;
padding: 20px;
box-shadow: 0 0 5px rgba(0,0,0,0.3);
background-color: #333333;
position: relative;
height: 100%;
width: 70%;
}
h2 {
position: absolute;
margin: 0;
top: 10px;
right: 20px;
font-weight: 300;
z-index: 1;
}
.topoplot-wrapper {
width: 300px;
height: 300px;
@@ -79,4 +98,4 @@
width: 9.09%;
height: 9.09%;
/*background: lightblue;*/
}
}
+6
Ver Arquivo
@@ -0,0 +1,6 @@
<section [ngClass]="{ loading: !data }">
<h2>Topo</h2>
<section class="topoplot">
<div id="topo"></div>
</section>
</section>
+46
Ver Arquivo
@@ -0,0 +1,46 @@
import {
beforeEach,
beforeEachProviders,
describe,
expect,
it,
inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { TopoComponent } from './topo.component';
describe('Component: Topo', () => {
let builder: TestComponentBuilder;
beforeEachProviders(() => [TopoComponent]);
beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
builder = tcb;
}));
it('should inject the component', inject([TopoComponent],
(component: TopoComponent) => {
expect(component).toBeTruthy();
}));
it('should create the component', inject([], () => {
return builder.createAsync(TopoComponentTestController)
.then((fixture: ComponentFixture<any>) => {
let query = fixture.debugElement.query(By.directive(TopoComponent));
expect(query).toBeTruthy();
expect(query.componentInstance).toBeTruthy();
});
}));
});
@Component({
selector: 'test',
template: `
<bci-topo></bci-topo>
`,
directives: [TopoComponent]
})
class TopoComponentTestController {
}
+130
Ver Arquivo
@@ -0,0 +1,130 @@
import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core';
import * as io from 'socket.io-client';
import { Constants } from '../shared/constants';
declare var chroma: any;
declare var Plotly: any;
@Component({
moduleId: module.id,
selector: 'bci-topo',
templateUrl: 'topo.component.html',
styleUrls: ['topo.component.css'],
providers: [Constants]
})
export class TopoComponent implements OnInit {
socket: any;
plotElement: any;
constructor(private view: ElementRef, private constants: Constants) {
this.socket = io(constants.socket.url);
}
private data: any = {
x: [],
y: [],
name: 'density',
ncontours: 15,
colorscale: [
[0, 'rgb(208, 0, 0)'],
[.50, 'rgb(247, 192, 0)'],
[.60, 'rgb(241, 255, 22)'],
[.80, 'rgb(68, 255, 250)'],
[.95, 'rgb(50, 0, 159)'],
[1, 'rgb(51, 51, 51)']
],
reversescale: true,
showscale: false,
type: 'histogram2dcontour',
line: {
width: 1
},
contours: {
//coloring: 'heatmap'
}
};
private layout: any = {
autosize: true,
width: 600,
height: 450,
bargap: 0,
paper_bgcolor: 'rgba(0,0,0,0)',
plot_bgcolor: 'rgba(0,0,0,0)',
margin: { l: 0, r: 0, b: 0, t: 0 },
xaxis: {
autorange: true,
showgrid: false,
zeroline: false,
showline: false,
autotick: true,
ticks: '',
showticklabels: false
},
yaxis: {
autorange: true,
showgrid: false,
zeroline: false,
showline: false,
autotick: true,
ticks: '',
showticklabels: false
}
};
private options: any = {
staticPlot: true
};
ngOnInit(): void {
this.plotElement = this.view.nativeElement.querySelector('#topo');
Plotly.newPlot(this.plotElement.id, [this.data], this.layout, this.options);
this.socket.on(this.constants.socket.events.topo, (data) => {
console.log(data.plot);
this.data.x = data.plot.x; //this.getRandomData().x
this.data.y = data.plot.y; //this.getRandomData().y
Plotly.redraw(this.plotElement);
//Plotly.Plots.resize(this.plotElement);
});
}
ngOnDestroy (): void {
this.socket.removeListener(this.constants.socket.events.topo);
}
getRandomData (): any {
function normal() {
var x = 0, y = 0, rds, c;
do {
x = Math.random() * 2 - 1;
y = Math.random() * 2 - 1;
rds = x * x + y * y;
} while (rds == 0 || rds > 1);
c = Math.sqrt(-2 * Math.log(rds) / rds);
return x * c;
}
var N = 2000,
a = -1,
b = Math.random();
var step = (b - a) / (N - 1);
var t = new Array(N), x = new Array(N), y = new Array(N);
for(var i = 0; i < N; i++){
t[i] = a + step * i;
x[i] = (Math.pow(t[i], 3)) + (0.3 * normal() );
y[i] = (Math.pow(t[i], 6)) + (0.3 * normal() );
}
return {
x: x,
y: y
};
}
}
BIN
Ver Arquivo
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 5.3 KiB

+51
Ver Arquivo
@@ -0,0 +1,51 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BCI Dashboard</title>
<base href="/">
{{content-for 'head'}}
<link rel="icon" type="image/x-icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700,300);
body {
font-family: 'Roboto', sans-serif;
font-weight: 300;
color: #ffffff;
background-color: #222222;
margin: 0;
}
</style>
<!-- Service worker support is disabled by default.
Install the worker script and uncomment to enable.
Only enable service workers in production.
<script type="text/javascript">
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/worker.js').catch(function(err) {
console.log('Error installing service worker: ', err);
});
}
</script>
-->
</head>
<body>
<bci-dashboard>Loading...</bci-dashboard>
<script src="vendor/chart.js/Chart.js"></script>
<script src="vendor/chroma-js/chroma.js"></script>
<script src="vendor/plotly.js/dist/plotly.js"></script>
<script src="vendor/phaser/dist/phaser.js"></script>
<script src="vendor/es6-shim/es6-shim.js"></script>
<script src="vendor/reflect-metadata/Reflect.js"></script>
<script src="vendor/systemjs/dist/system.src.js"></script>
<script src="vendor/zone.js/dist/zone.js"></script>
<script>
System.import('system-config.js').then(function () {
System.import('main');
}).catch(console.error.bind(console));
</script>
</body>
</html>
+9
Ver Arquivo
@@ -0,0 +1,9 @@
import { bootstrap } from '@angular/platform-browser-dynamic';
import { enableProdMode } from '@angular/core';
import { DashboardComponent, environment } from './app/';
if (environment.production) {
enableProdMode();
}
bootstrap(DashboardComponent);
+68
Ver Arquivo
@@ -0,0 +1,68 @@
/***********************************************************************************************
* User Configuration.
**********************************************************************************************/
/** Map relative paths to URLs. */
const map: any = {
'smoothie': 'vendor/smoothie/smoothie.js',
'ng2-charts': 'vendor/ng2-charts/bundles/ng2-charts.js',
'socket.io-client': 'vendor/socket.io-client/socket.io.js',
'chroma-js': 'vendor/chroma-js/chroma.js',
'plotly': 'vendor/plotly.js/dist/plotly.js',
'phaser': 'vendor/phaser/dist/phaser.js'
};
/** User packages configuration. */
const packages: any = {
};
////////////////////////////////////////////////////////////////////////////////////////////////
/***********************************************************************************************
* Everything underneath this line is managed by the CLI.
**********************************************************************************************/
const barrels: string[] = [
// Angular specific barrels.
'@angular/core',
'@angular/common',
'@angular/compiler',
'@angular/http',
'@angular/router',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
// Thirdparty barrels.
'rxjs',
// App specific barrels.
'app',
'app/shared',
'app/frequency',
'app/time-series',
'app/nav',
'app/frequency-bands',
'app/frequency-band',
'app/topo',
'app/music-training',
/** @cli-barrel */
];
const cliSystemConfigPackages: any = {};
barrels.forEach((barrelName: string) => {
cliSystemConfigPackages[barrelName] = { main: 'index' };
});
/** Type declaration for ambient System. */
declare var System: any;
// Apply the CLI SystemJS configuration.
System.config({
map: {
'@angular': 'vendor/@angular',
'rxjs': 'vendor/rxjs',
'main': 'main.js'
},
packages: cliSystemConfigPackages
});
// Apply the user's configuration.
System.config({ map, packages });
+23
Ver Arquivo
@@ -0,0 +1,23 @@
{
"compileOnSave": false,
"compilerOptions": {
"declaration": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"mapRoot": "",
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": false,
"outDir": "../dist/",
"rootDir": ".",
"sourceMap": true,
"target": "es5",
"inlineSources": true
},
"files": [
"main.ts",
"typings.d.ts"
]
}
+3
Ver Arquivo
@@ -0,0 +1,3 @@
/// <reference path="../typings/browser.d.ts" />
declare var module: { id: string };
+72
Ver Arquivo
@@ -0,0 +1,72 @@
{
"rulesDirectory": ["node_modules/codelyzer"],
"rules": {
"max-line-length": [true, 100],
"no-inferrable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"indent": [
true,
"spaces"
],
"eofline": true,
"no-duplicate-variable": true,
"no-eval": true,
"no-arg": true,
"no-internal-module": true,
"no-trailing-whitespace": true,
"no-bitwise": true,
"no-shadowed-variable": true,
"no-unused-expression": true,
"no-unused-variable": true,
"one-line": [
true,
"check-catch",
"check-else",
"check-open-brace",
"check-whitespace"
],
"quotemark": [
true,
"single",
"avoid-escape"
],
"semicolon": [true, "always"],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"curly": true,
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-trailing-underscore"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"component-selector-name": [true, "kebab-case"],
"component-selector-type": [true, "element"],
"host-parameter-decorator": true,
"input-parameter-decorator": true,
"output-parameter-decorator": true,
"attribute-parameter-decorator": true,
"input-property-directive": true,
"output-property-directive": true
}
}
+14
Ver Arquivo
@@ -0,0 +1,14 @@
{
"ambientDevDependencies": {
"angular-protractor": "registry:dt/angular-protractor#1.5.0+20160425143459",
"jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
"selenium-webdriver": "registry:dt/selenium-webdriver#2.44.0+20160317120654"
},
"ambientDependencies": {
"chart": "registry:dt/chart#0.0.0+20160316155526",
"chroma-js": "registry:dt/chroma-js#0.5.6+20160317120654",
"es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654",
"smoothie": "registry:dt/smoothie#1.25.0+20160316155526",
"socket.io-client": "registry:dt/socket.io-client#1.4.4+20160317120654"
}
}
+87 -28
Ver Arquivo
@@ -1,13 +1,11 @@
var express = require('express');
var app = express();
var path = require('path');
var http = require('http').Server(app);
var argv = require('yargs').argv;
var OpenBCIBoard = require('openbci-sdk');
var dsp = require('dsp.js');
var io = require('socket.io')(http);
var io = require('socket.io')(process.env.app_port || 8080);
var topogrid = require('topogrid');
var jStat = require('jstat').jStat;
var Fili = require('fili');
var globalScale = 1.5;
@@ -16,19 +14,6 @@ io.on('connection', function(socket){
console.log('A user connected');
});
// Server
app.use(express.static(path.join(__dirname, '/app')));
app.use('/node_modules', express.static(path.join(__dirname, '/node_modules')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, '/app/index.html'));
});
http.listen(3060, function () {
console.log('listening on port 3060');
});
// OpenBCI
var board = new OpenBCIBoard.OpenBCIBoard({
verbose: true
@@ -90,6 +75,47 @@ var pos_x = [3,7,2,8,0,10,3,7]; // x coordinates of the data
var pos_y = [0,0,3,3,8,8,10,10]; // y coordinates of the data
// var data = [10,0,0,0,0,0,-10,30,25]; // the data values
// Instance of a filter coefficient calculator
var iirCalculator = new Fili.CalcCascades();
// calculate filter coefficients
var notchFilterCoeffs = iirCalculator.bandstop({
order: 2, // cascade 3 biquad filters (max: 12)
characteristic: 'butterworth',
Fs: sampleRate, // sampling frequency
Fc: 60,
F1: 59,
F2: 61,
gain: 0, // gain for peak, lowshelf and highshelf
preGain: false // adds one constant multiplication for highpass and lowpass
// k = (1 + cos(omega)) * 0.5 / k = 1 with preGain == false
});
// create a filter instance from the calculated coeffs
var notchFilter = new Fili.IirFilter(notchFilterCoeffs);
var hpFilterCoeffs = iirCalculator.highpass({
order: 3, // cascade 3 biquad filters (max: 12)
characteristic: 'butterworth',
Fs: sampleRate, // sampling frequency
Fc: 1,
gain: 0, // gain for peak, lowshelf and highshelf
preGain: false // adds one constant multiplication for highpass and lowpass
// k = (1 + cos(omega)) * 0.5 / k = 1 with preGain == false
});
var hpFilter = new Fili.IirFilter(hpFilterCoeffs);
var lpFilterCoeffs = iirCalculator.lowpass({
order: 3, // cascade 3 biquad filters (max: 12)
characteristic: 'butterworth',
Fs: sampleRate, // sampling frequency
Fc: 50,
gain: 0, // gain for peak, lowshelf and highshelf
preGain: false // adds one constant multiplication for highpass and lowpass
// k = (1 + cos(omega)) * 0.5 / k = 1 with preGain == false
});
var lpFilter = new Fili.IirFilter(lpFilterCoeffs);
function onSample (sample) {
@@ -106,6 +132,7 @@ function onSample (sample) {
var spectrums = [[],[],[],[],[],[],[],[]];
signals.forEach(function (signal, index) {
signal = notchFilter.multiStep(signal);
var fft = new dsp.FFT(bufferSize, sampleRate);
fft.forward(signal);
spectrums[index] = parseObjectAsArray(fft.spectrum);
@@ -120,13 +147,14 @@ function onSample (sample) {
var spectrumsByBand = [];
var bands = {
delta : [1, 3],
theta : [4, 8],
alpha : [9, 12],
beta : [13, 30]
delta: [1, 3],
theta: [4, 8],
alpha: [8, 12],
beta: [13, 30],
gamma: [30, 100]
};
for(band in bands){
for (band in bands) {
spectrumsByBand[band] = filterBand(spectrums, labels, bands[band])
}
@@ -141,6 +169,7 @@ function onSample (sample) {
delta: spectrumsByBand.delta.spectrums,
alpha: spectrumsByBand.alpha.spectrums,
beta: spectrumsByBand.beta.spectrums,
gamma: spectrumsByBand.gamma.spectrums,
labels: labels
});
@@ -154,12 +183,6 @@ function onSample (sample) {
return jStat.mean(channel);
});
grid = topogrid.create(pos_x,pos_y,meanSpectrum,grid_params);
var grid_flat = [].concat.apply([], grid);
io.emit('bci:topo', {
data: grid_flat
});
sampleNumber = bins - windowSize;
@@ -188,6 +211,42 @@ function onSample (sample) {
});
seriesNumber = 0;
grid = topogrid.create(pos_x,pos_y,sample.channelData,grid_params);
var grid_flat = [].concat.apply([], grid);
//**********//
var Xs = signals.map(function (channel, i) {
var x = [3,7,2,8,0,10,3,7];
return channel.map(function (volt) {
return volt + (x[i]);
});
});
var Ys = signals.map(function (channel, i) {
var y = [0,0,3,3,8,8,10,10];
return channel.map(function (volt) {
return volt + (y[i]);
});
});
var plotX = [].concat.apply([], Xs).sort(function (a, b) {
return a - b;
});
var plotY = [].concat.apply([], Ys).sort(function (a, b) {
return a - b;
});
//**********//
io.emit('bci:topo', {
data: grid_flat,
plot: {
x: plotX,
y: plotY
}
});
}
}