Added acceleration recorder
parent
5ae38e02a3
commit
51a3c01fe1
14
apps.json
14
apps.json
|
|
@ -2047,6 +2047,20 @@
|
||||||
{"name":"widancs.settings.js","url":"settings.js"}
|
{"name":"widancs.settings.js","url":"settings.js"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{ "id": "accelrec",
|
||||||
|
"name": "Acceleration Recorder",
|
||||||
|
"shortName":"Accel Rec",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"interface": "interface.html",
|
||||||
|
"description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.",
|
||||||
|
"tags": "",
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"accelrec.app.js","url":"app.js"},
|
||||||
|
{"name":"accelrec.img","url":"app-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "cprassist",
|
"id": "cprassist",
|
||||||
"name":"CPR Assist",
|
"name":"CPR Assist",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Acceleration Recorder
|
||||||
|
|
||||||
|
This app records a short period of acceleration data from the accelerometer
|
||||||
|
and
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
* Start the `Accel Rec` App
|
||||||
|
* Select `Start` and place the Bangle with its rear face pointing downwards (screen up)
|
||||||
|
* After the counter counts down, it will display `Waiting`
|
||||||
|
* Now move the Bangle upwards (in the direction of `N`)
|
||||||
|
|
||||||
|
At this point the 2 second recording will start at 100 samples per second,
|
||||||
|
with a maximum of 8g.
|
||||||
|
|
||||||
|
After the 2 seconds you'll see a graph with calculated maximum acceleration
|
||||||
|
and velocity.
|
||||||
|
|
||||||
|
* Press BTN2 (labelled `FINISH`)
|
||||||
|
* Not choose `Save` and choose a slot, from 1 to 6. Slots already used
|
||||||
|
are marked with a `*`
|
||||||
|
|
||||||
|
## Getting data
|
||||||
|
|
||||||
|
* Go to the App Loader: https://banglejs.com/apps/
|
||||||
|
* Click `Connect` up the Top Right
|
||||||
|
* Click `My Apps`
|
||||||
|
* Click the Downward pointing arrow next to `Acceleration Recorder`
|
||||||
|
* After it loads, you'll see the recorded Acceleration values
|
||||||
|
* You can now either save them to a CSV file, or delete them
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AC1rAAQrrF9QuHF8tBoIvsFwIwIF04wHF1AwGF1IwFF1QwDF0llGBAuki0QiwvnFwoABGAqMlFwQABSQbqmL44umX44uoSQS7oGRAukRAouqdAoubiwAFnaMDFwQABXbk6FxRfJF0y/HF1CSCdTwuOdTpjGF1gACF11rtYuKnYvhsowKncWF8K+BGBAuBF8IiBGBAuCF8YwHFwYvf4XCz06GAwuEF7283nB4IwEegJoDF8G74XO5owBEoo2CF8PI53O4O8ZJIufF4PI4QEBF5LteFQIADBo6QBFzwvDBxc6F74A/AH4A/AFQ"))
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
var acc;
|
||||||
|
var HZ = 100;
|
||||||
|
var SAMPLES = 2*HZ; // 2 seconds
|
||||||
|
var SCALE = 5000;
|
||||||
|
var THRESH = 1.01;
|
||||||
|
var accelx = new Int16Array(SAMPLES);
|
||||||
|
var accely = new Int16Array(SAMPLES); // North
|
||||||
|
var accelz = new Int16Array(SAMPLES); // Into clock face
|
||||||
|
var accelIdx = 0;
|
||||||
|
var lastAccel = undefined;
|
||||||
|
function accelHandlerTrigger(a) {"ram"
|
||||||
|
if (a.mag*2>THRESH) { // *2 because 8g mode
|
||||||
|
tStart = getTime();
|
||||||
|
g.drawString("Recording",g.getWidth()/2,g.getHeight()/2,1);
|
||||||
|
Bangle.removeListener('accel',accelHandlerTrigger);
|
||||||
|
Bangle.on('accel',accelHandlerRecord);
|
||||||
|
if (lastAccel) accelHandlerRecord(lastAccel);
|
||||||
|
accelHandlerRecord(a);
|
||||||
|
}
|
||||||
|
lastAccel = a;
|
||||||
|
}
|
||||||
|
function accelHandlerRecord(a) {"ram"
|
||||||
|
var i = accelIdx++;
|
||||||
|
accelx[i] = a.x*SCALE*2;
|
||||||
|
accely[i] = -a.y*SCALE*2;
|
||||||
|
accelz[i] = a.z*SCALE*2;
|
||||||
|
if (accelIdx>=SAMPLES) recordStop();
|
||||||
|
}
|
||||||
|
function recordStart() {"ram"
|
||||||
|
Bangle.setLCDTimeout(0); // force LCD on
|
||||||
|
accelIdx = 0;
|
||||||
|
lastAccel = undefined;
|
||||||
|
Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter
|
||||||
|
Bangle.accelWr(0x18,0b11110100); // +-8g
|
||||||
|
Bangle.setPollInterval(10); // 100hz input
|
||||||
|
setTimeout(function() {
|
||||||
|
Bangle.on('accel',accelHandlerTrigger);
|
||||||
|
g.clear(1).setFont("6x8",2).setFontAlign(0,0);
|
||||||
|
g.drawString("Waiting",g.getWidth()/2,g.getHeight()/2);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function recordStop() {"ram"
|
||||||
|
console.log("Length:",getTime()-tStart);
|
||||||
|
Bangle.setPollInterval(80); // default poll interval
|
||||||
|
Bangle.accelWr(0x1B,0x0); // default 12.5hz output
|
||||||
|
Bangle.accelWr(0x18,0b11101100); // +-4g
|
||||||
|
Bangle.removeListener('accel',accelHandlerRecord);
|
||||||
|
E.showMessage("Finished");
|
||||||
|
showData();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function showData() {
|
||||||
|
g.clear(1);
|
||||||
|
var w = g.getWidth()-20; // width
|
||||||
|
var m = g.getHeight()/2; // middle
|
||||||
|
var s = 12; // how many pixels per G
|
||||||
|
g.fillRect(9,0,9,g.getHeight());
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
for (var l=-8;l<=8;l++)
|
||||||
|
g.drawString(l, 5, m - l*s);
|
||||||
|
|
||||||
|
function plot(a) {
|
||||||
|
g.moveTo(10,m - a[0]*s/SCALE);
|
||||||
|
for (var i=0;i<SAMPLES;i++)
|
||||||
|
g.lineTo(10+i*w/SAMPLES, m - a[i]*s/SCALE);
|
||||||
|
}
|
||||||
|
g.setColor("#0000ff");
|
||||||
|
plot(accelz);
|
||||||
|
g.setColor("#ff0000");
|
||||||
|
plot(accelx);
|
||||||
|
g.setColor("#00ff00");
|
||||||
|
plot(accely);
|
||||||
|
|
||||||
|
// work out stats
|
||||||
|
var maxAccel = 0;
|
||||||
|
var vel = 0, maxVel = 0;
|
||||||
|
for (var i=0;i<SAMPLES;i++) {
|
||||||
|
var a = accely[i]/SCALE;
|
||||||
|
if (a>maxAccel) maxAccel=a;
|
||||||
|
vel += a/HZ;
|
||||||
|
if (vel>maxVel) maxVel=vel;
|
||||||
|
}
|
||||||
|
g.reset();
|
||||||
|
g.setFont("6x8").setFontAlign(1,0);
|
||||||
|
g.drawString("Max Y Accel: "+maxAccel.toFixed(2)+" g",g.getWidth()-14,g.getHeight()-50);
|
||||||
|
g.drawString("Max Y Vel: "+maxVel.toFixed(2)+" m/s",g.getWidth()-14,g.getHeight()-40);
|
||||||
|
//console.log("End Velocity "+vel);
|
||||||
|
g.setFont("6x8").setFontAlign(0,0,1);
|
||||||
|
g.drawString("FINISH",g.getWidth()-4,g.getHeight()/2);
|
||||||
|
setWatch(function() {
|
||||||
|
showMenu();
|
||||||
|
}, BTN2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBig(txt) {
|
||||||
|
g.clear(1);
|
||||||
|
g.setFontVector(80).setFontAlign(0,0);
|
||||||
|
g.drawString(txt,g.getWidth()/2, g.getHeight()/2);
|
||||||
|
g.flip();
|
||||||
|
}
|
||||||
|
|
||||||
|
function countDown() {
|
||||||
|
showBig(3);
|
||||||
|
setTimeout(function() {
|
||||||
|
showBig(2);
|
||||||
|
setTimeout(function() {
|
||||||
|
showBig(1);
|
||||||
|
setTimeout(function() {
|
||||||
|
recordStart();
|
||||||
|
}, 800);
|
||||||
|
}, 1000);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMenu() {
|
||||||
|
Bangle.setLCDTimeout(10); // set timeout for LCD in menu
|
||||||
|
var menu = {
|
||||||
|
"" : { title : "Acceleration Rec" },
|
||||||
|
"Start" : function() {
|
||||||
|
E.showMenu();
|
||||||
|
if (accelIdx==0) countDown();
|
||||||
|
else E.showPrompt("Overwrite Recording?").then(ok=>{
|
||||||
|
if (ok) countDown(); else showMenu();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"Plot" : function() {
|
||||||
|
E.showMenu();
|
||||||
|
if (accelIdx) showData();
|
||||||
|
else E.showAlert("No Data").then(()=>{
|
||||||
|
showMenu();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"Save" : function() {
|
||||||
|
E.showMenu();
|
||||||
|
if (accelIdx) showSaveMenu();
|
||||||
|
else E.showAlert("No Data").then(()=>{
|
||||||
|
showMenu();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"Exit" : function() {
|
||||||
|
load();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSaveMenu() {
|
||||||
|
var menu = {
|
||||||
|
"" : { title : "Save" }
|
||||||
|
};
|
||||||
|
[1,2,3,4,5,6].forEach(i=>{
|
||||||
|
var fn = "accelrec."+i+".csv";
|
||||||
|
var exists = require("Storage").read(fn)!==undefined;
|
||||||
|
menu["Recording "+i+(exists?" *":"")] = function() {
|
||||||
|
var csv = "";
|
||||||
|
for (var i=0;i<SAMPLES;i++)
|
||||||
|
csv += `${accelx[i]/SCALE},${accely[i]/SCALE},${accelz[i]/SCALE}\n`;
|
||||||
|
require("Storage").write(fn,csv);
|
||||||
|
showMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
menu["< Back"] = function() {showMenu();};
|
||||||
|
E.showMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
showMenu();
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,109 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="data"></div>
|
||||||
|
|
||||||
|
<script src="../../lib/interface.js"></script>
|
||||||
|
<script>
|
||||||
|
var dataElement = document.getElementById("data");
|
||||||
|
var fileData = {};
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
fileData = [];
|
||||||
|
// show loading window
|
||||||
|
Util.showModal("Loading...");
|
||||||
|
// get the data
|
||||||
|
dataElement.innerHTML = "";
|
||||||
|
var promise = Promise.resolve();
|
||||||
|
Puck.eval('require("Storage").list(/accelrec\\..\\.csv/)',files=>{
|
||||||
|
if (files.length==0) {
|
||||||
|
dataElement.innerHTML = "<p>No saved data</p>";
|
||||||
|
} else {
|
||||||
|
files.forEach(fn => {
|
||||||
|
dataElement.innerHTML += `
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-title h5">${fn}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body" fn="${fn}">
|
||||||
|
<p>Loading...</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<button class="btn btn-primary" fn="${fn}" act="save">Save</button>
|
||||||
|
<button class="btn" fn="${fn}" act="delete">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
promise = promise.then(function() {
|
||||||
|
document.querySelector(`.btn[fn='${fn}'][act='save']`).addEventListener("click", function() {
|
||||||
|
Util.saveCSV(fn.slice(0,-4), "X,Y,Z\n"+fileData[fn]);
|
||||||
|
});
|
||||||
|
document.querySelector(`.btn[fn='${fn}'][act='delete']`).addEventListener("click", function() {
|
||||||
|
Util.showModal("Deleting...");
|
||||||
|
Util.eraseStorage(fn, function() {
|
||||||
|
Util.hideModal();
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return new Promise(resolve=>{
|
||||||
|
Puck.eval(`require("Storage").read(${JSON.stringify(fn)})`,csv=>{
|
||||||
|
fileData[fn] = csv.trim();
|
||||||
|
var el = document.querySelector(`.card-body[fn='${fn}']`);
|
||||||
|
el.innerHTML = '<canvas width="400" height="100"></canvas>';
|
||||||
|
var c = el.firstChild;
|
||||||
|
var ctx = c.getContext("2d");
|
||||||
|
var lines = csv.split("\n");
|
||||||
|
var y = 50, sx = 400/lines.length, sy = 50/8;
|
||||||
|
function plot(n) {
|
||||||
|
var last;
|
||||||
|
ctx.beginPath();
|
||||||
|
lines.map((l,x)=>{
|
||||||
|
l = l.split(",");
|
||||||
|
var yc = y + parseFloat(l[n])*sy;
|
||||||
|
if (!last) {
|
||||||
|
ctx.moveTo(0, yc);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x*sx, yc);
|
||||||
|
}
|
||||||
|
last = l;
|
||||||
|
});
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
ctx.strokeStyle = 'red';
|
||||||
|
plot(0);
|
||||||
|
ctx.strokeStyle = 'green';
|
||||||
|
plot(1);
|
||||||
|
ctx.strokeStyle = 'blue';
|
||||||
|
plot(2);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// remove window
|
||||||
|
Util.hideModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can call a utility function to save the data
|
||||||
|
/*document.getElementById("btnSave").addEventListener("click", function() {
|
||||||
|
Util.saveCSV("gpsdata", csvData);
|
||||||
|
});
|
||||||
|
// Or you can also delete the file
|
||||||
|
document.getElementById("btnDelete").addEventListener("click", function() {
|
||||||
|
Util.showModal("Deleting...");
|
||||||
|
Util.eraseStorageFile("gpspoilog.csv", function() {
|
||||||
|
Util.hideModal();
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
});*/
|
||||||
|
// Called when app starts
|
||||||
|
function onInit() {
|
||||||
|
getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue