Skip to content

Commit

Permalink
initial commit. Basic use cases work.
Browse files Browse the repository at this point in the history
  • Loading branch information
nat-n committed Jul 18, 2015
0 parents commit 1e7420d
Show file tree
Hide file tree
Showing 49 changed files with 32,136 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tmp
*.pyc
.DS_Store
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Python package for various streaming tasks with OpenBCI data.

The default config serves a webpage which graphs the data coming from the OpenBCI.
Empty file added __init__.py
Empty file.
35 changes: 35 additions & 0 deletions client/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>BCI Streamer</title>

<link rel="stylesheet" type="text/css" href="static/vendor/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="static/vendor/css/bootstrap-theme.css">
<link rel="stylesheet" type="text/css" href="static/css/main.css">

</head>
<body>


<div class="container">

<header></header>

<div class="well">well hello</div>

<canvas id="display" width="1275" height="400" style="border: 1px solid; background-color: #333;"></canvas>

<footer></footer>

</div>

<script type="text/javascript" src="static/vendor/js/lodash.min.js"></script>
<script type="text/javascript" src="static/vendor/js/jquery.min.js"></script>
<script type="text/javascript" src="static/vendor/js/bootstrap.min.js"></script>
<script type="text/javascript" src="static/js/ws.js"></script>
<script type="text/javascript" src="static/js/plotter.js"></script>
<script type="text/javascript" src="static/js/main.js"></script>

</body>
</html>
Empty file added client/static/css/main.css
Empty file.
41 changes: 41 additions & 0 deletions client/static/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

window.Bci = (window.Bci || {});


window.Bci.Ws.connect();
var unlisten = window.Bci.Ws.listen(function (packet) {
Bci.Plotter.plotSamples(packet.channel_data.map(function (x) { return parseInt(x / -900, 10); }));
// verifySampleOrder(packet);
});

// TODO:
// - server and/or client side, verify sample rate and packet fidelity


var latestOrdinal;
function verifySampleOrder(sample) {
if (latestOrdinal == 255) {
latestOrdinal = -1;
}
var newOrdinal = sample.ordernal_or_whatever;
if (newOrdinal != latestOrdinal + 1) {
console.log(Date.now(), latestOrdinal, newOrdinal);
}
latestOrdinal = newOrdinal;
};

// -- * == * -- * == * -- * == * -- * == * -- * == * -- * == * -- * == * -- * ==


// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,1]);}, 0);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,10]);}, 100);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,11]);}, 200);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,12]);}, 300);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,19]);}, 400);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,10]);}, 500);
// setTimeout(function() { window.Bci.Plotter.plotSamples([-101,-10,-1]);}, 600);
// setTimeout(function() { window.Bci.Plotter.plotSamples([101,-10,1]);}, 700);
// setTimeout(function() { window.Bci.Plotter.plotSamples([105,-10,1]);}, 800);
// setTimeout(function() { window.Bci.Plotter.plotSamples([111,-10,1]);}, 900);
// setTimeout(function() { window.Bci.Plotter.plotSamples([11,-10,1]);}, 1000);

120 changes: 120 additions & 0 deletions client/static/js/plotter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

window.Bci = (window.Bci || {});


// TODO: make scrollspeed depend on time (pixels per second) rather than plot speed


window.Bci.Plotter = (function () {
'use strict';

var displayCnvs = document.getElementById('display');
var displayCtx = displayCnvs.getContext('2d');

var displayWidth = displayCnvs.width;
var displayHeight = displayCnvs.height;

var tapeWidth = displayWidth * 2;
var tapeHeight = displayHeight;
var tapeScratchWidth = displayWidth * 2;
var tapeScratchHeight = displayHeight;

// var tapeCnvs = document.getElementById('tapeCnvs');
var tapeCnvs = document.createElement('canvas');
tapeCnvs.width = tapeWidth;
tapeCnvs.height = tapeHeight;
var tapeCtx = tapeCnvs.getContext('2d');

// var tapeScratchCnvs = document.getElementById('tapeScratchCnvs');
var tapeScratchCnvs = document.createElement('canvas');
tapeScratchCnvs.width = tapeScratchWidth;
tapeScratchCnvs.height = tapeScratchHeight;
var tapeScratchCtx = tapeScratchCnvs.getContext('2d');

var animationOn = true;
var tapeUpdated = false;
var tapeOffset = 0;

var pallete = [
[222, 222, 222],
[255, 0, 0],
[255, 255, 0],
[0, 255, 0],
[0, 255, 255],
[0, 0, 255],
[255, 0, 255],
[200, 200, 200]
];

var previousSamples = [0, 0, 0, 0, 0, 0, 0, 0];

function traceFromPrevious(y1, y2, color, pixels) {
// y1 is the previous point, y2 is the present
var y, i;
if (y1 < y2) {
y1 += 1;
} else if (y1 > y2) {
y1 -= 1;
y = y1;
y1 = y2;
y2 = y;
}
for (y = y1; y <= y2; y++) {
i = (displayHeight / 2 - y) * 4;
pixels.data[i] = color[0];
pixels.data[i+1] = color[1];
pixels.data[i+2] = color[2];
pixels.data[i+3] = 255;
}
}

function plotSamples(samples) {
var pixels = new ImageData(1, displayHeight);
for (var ch = 0; ch < samples.length; ch++) {
traceFromPrevious(previousSamples[ch], samples[ch], pallete[ch], pixels);
previousSamples[ch] = samples[ch];
}
tapeCtx.putImageData(pixels, tapeOffset, 0);
tapeUpdated = true;

tapeOffset += 1;
scrollTape();
}

function updateDisplay() {
if (!animationOn) return;
if (tapeUpdated) {
displayCtx.clearRect(0, 0, displayWidth, displayHeight);
var copyOffset = Math.max(0, tapeOffset - displayWidth);
displayCtx.drawImage(tapeCnvs, -copyOffset, 0);
tapeUpdated = false;
}
requestAnimationFrame(updateDisplay);
}

function scrollTape() {
var scrollDelta;
if (tapeOffset > displayWidth * 1.5) {
scrollDelta = tapeOffset - displayWidth;
tapeScratchCtx.clearRect(0, 0, tapeScratchWidth, tapeScratchHeight);
tapeScratchCtx.drawImage(tapeCnvs, -scrollDelta, 0);
tapeCtx.clearRect(0, 0, tapeWidth, tapeHeight);
tapeCtx.drawImage(tapeScratchCnvs, 0, 0);
tapeOffset = displayWidth;
}
}

function setAnimationOn(on) {
animationOn = !!on;
if (animationOn) {
requestAnimationFrame(updateDisplay);
}
}

setAnimationOn(true);

return Object.freeze({
plotSamples: plotSamples,
setAnimationOn: setAnimationOn
});
})();
61 changes: 61 additions & 0 deletions client/static/js/ws.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
window.Bci = (window.Bci || {});

window.Bci.Ws = (function () {
'use strict';

var wsEndpoint = 'ws://' + location.host + '/channels';
var listeners = [];
var ws;

function connect() {
if (ws && (ws.readyState == ws.CONNECTING || ws.readyState == ws.OPEN)) {
return ws;
}
ws = new WebSocket(wsEndpoint);
function init() {
ws.onmessage = function(msg){
var content = JSON.parse(msg.data);
listeners.forEach(function (listener) { listener(content); });
};
ws.send('{"hello": 1}');
}
var waitingForConnection = setInterval(function () {
if (ws.readyState === WebSocket.OPEN) {
init();
clearInterval(waitingForConnection);
}
}, 10);
return ws;
}

function status() {
switch (ws.readyState) {
case ws.CLOSED:
return "closed";
case ws.CLOSING:
return "closing";
case ws.CONNECTING:
return "connecting";
case ws.OPEN:
return "open";
}
}

function listen(cb) {
var n = listeners.length;
listeners.push(cb);
function unlisten() {
if (n >= 0) {
listeners.splice(n, 1);
n = -1;
}
};
return unlisten;
}

return Object.freeze({
connect: connect,
status: status,
listen: listen
});
})();
Loading

0 comments on commit 1e7420d

Please sign in to comment.