spectrogram
This commit is contained in:
@@ -20,6 +20,17 @@ $().ready(function () {
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
$('input[type=number]').on('mousewheel', function(e) {
|
||||
var val = +$(this).val();
|
||||
var step = +($(this).attr('step') || 1);
|
||||
if(e.wheelDelta > 0) {
|
||||
val += step;
|
||||
} else {
|
||||
val -= step;
|
||||
}
|
||||
$(this).val(val);
|
||||
});
|
||||
|
||||
modal.init();
|
||||
notify.init();
|
||||
});
|
||||
|
||||
@@ -63,7 +63,9 @@
|
||||
|
||||
// Convert to camel case
|
||||
function cssCamel(property) {
|
||||
return property.replace(/-\w/g, function (result) {return result.charAt(1).toUpperCase(); });
|
||||
return property.replace(/-\w/g, function (result) {
|
||||
return result.charAt(1).toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
// Get computed style
|
||||
@@ -82,7 +84,8 @@
|
||||
function setCss(elm, property, value) {
|
||||
try {
|
||||
elm.style[cssCamel(property)] = value;
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Show CSS
|
||||
@@ -117,38 +120,38 @@
|
||||
|
||||
if (!subelm.disabled) {
|
||||
switch (subelm.type) {
|
||||
// Ignore buttons, unsupported XHR 1 form fields
|
||||
case 'button':
|
||||
case 'image':
|
||||
case 'file':
|
||||
case 'submit':
|
||||
case 'reset':
|
||||
break;
|
||||
// Ignore buttons, unsupported XHR 1 form fields
|
||||
case 'button':
|
||||
case 'image':
|
||||
case 'file':
|
||||
case 'submit':
|
||||
case 'reset':
|
||||
break;
|
||||
|
||||
case 'select-one':
|
||||
if (subelm.length > 0) {
|
||||
querystring += '&' + queryPair(subelm.name, subelm.value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'select-multiple':
|
||||
for (j = 0; j < subelm.length; j += 1) {
|
||||
if (subelm[j].selected) {
|
||||
querystring += '&' + queryPair(subelm.name, subelm[j].value);
|
||||
case 'select-one':
|
||||
if (subelm.length > 0) {
|
||||
querystring += '&' + queryPair(subelm.name, subelm.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
if (subelm.checked) {
|
||||
case 'select-multiple':
|
||||
for (j = 0; j < subelm.length; j += 1) {
|
||||
if (subelm[j].selected) {
|
||||
querystring += '&' + queryPair(subelm.name, subelm[j].value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
if (subelm.checked) {
|
||||
querystring += '&' + queryPair(subelm.name, subelm.value);
|
||||
}
|
||||
break;
|
||||
|
||||
// Everything else including shinny new HTML5 input types
|
||||
default:
|
||||
querystring += '&' + queryPair(subelm.name, subelm.value);
|
||||
}
|
||||
break;
|
||||
|
||||
// Everything else including shinny new HTML5 input types
|
||||
default:
|
||||
querystring += '&' + queryPair(subelm.name, subelm.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,7 +208,9 @@
|
||||
} else if (position === 'prepend') {
|
||||
elm.insertBefore(tmpnode, elm.firstChild);
|
||||
}
|
||||
} catch (e) {break; }
|
||||
} catch (e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, nodes);
|
||||
}
|
||||
@@ -347,7 +352,7 @@
|
||||
// Toggle node display
|
||||
cb.toggle = function (state) {
|
||||
if (typeof state != 'undefined') { // ADDED
|
||||
if(state)
|
||||
if (state)
|
||||
cb.show();
|
||||
else
|
||||
cb.hide();
|
||||
@@ -370,7 +375,8 @@
|
||||
// Catch error in unlikely case elm has been removed
|
||||
try {
|
||||
elm.parentNode.removeChild(elm);
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
}
|
||||
}, nodes);
|
||||
return chibi();
|
||||
};
|
||||
@@ -397,7 +403,7 @@
|
||||
cb.getClass = function () {
|
||||
if (nodes[0] && nodes[0].className.length > 0) {
|
||||
// Weak IE trim support
|
||||
return nodes[0].className.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '').replace(/\s+/,' ');
|
||||
return nodes[0].className.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '').replace(/\s+/, ' ');
|
||||
}
|
||||
};
|
||||
// Set (replaces) classes
|
||||
@@ -501,7 +507,7 @@
|
||||
// Get/Set HTML data property
|
||||
cb.data = function (key, value) {
|
||||
if (key) {
|
||||
return cb.attr('data-'+key, value);
|
||||
return cb.attr('data-' + key, value);
|
||||
}
|
||||
};
|
||||
// Get/Set form element values
|
||||
@@ -510,26 +516,26 @@
|
||||
if (value || value === '') {
|
||||
nodeLoop(function (elm) {
|
||||
switch (elm.nodeName) {
|
||||
case 'SELECT':
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
value = [value];
|
||||
}
|
||||
for (i = 0; i < elm.length; i += 1) {
|
||||
// Multiple select
|
||||
for (j = 0; j < value.length; j += 1) {
|
||||
elm[i].selected = '';
|
||||
if (elm[i].value === value[j]) {
|
||||
elm[i].selected = 'selected';
|
||||
break;
|
||||
case 'SELECT':
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
value = [value];
|
||||
}
|
||||
for (i = 0; i < elm.length; i += 1) {
|
||||
// Multiple select
|
||||
for (j = 0; j < value.length; j += 1) {
|
||||
elm[i].selected = '';
|
||||
if (elm[i].value === value[j]) {
|
||||
elm[i].selected = 'selected';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'INPUT':
|
||||
case 'TEXTAREA':
|
||||
case 'BUTTON':
|
||||
elm.value = value;
|
||||
break;
|
||||
break;
|
||||
case 'INPUT':
|
||||
case 'TEXTAREA':
|
||||
case 'BUTTON':
|
||||
elm.value = value;
|
||||
break;
|
||||
}
|
||||
}, nodes);
|
||||
|
||||
@@ -537,18 +543,18 @@
|
||||
}
|
||||
if (nodes[0]) {
|
||||
switch (nodes[0].nodeName) {
|
||||
case 'SELECT':
|
||||
values = [];
|
||||
for (i = 0; i < nodes[0].length; i += 1) {
|
||||
if (nodes[0][i].selected) {
|
||||
values.push(nodes[0][i].value);
|
||||
case 'SELECT':
|
||||
values = [];
|
||||
for (i = 0; i < nodes[0].length; i += 1) {
|
||||
if (nodes[0][i].selected) {
|
||||
values.push(nodes[0][i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (values.length > 1) ? values : values[0];
|
||||
case 'INPUT':
|
||||
case 'TEXTAREA':
|
||||
case 'BUTTON':
|
||||
return nodes[0].value;
|
||||
return (values.length > 1) ? values : values[0];
|
||||
case 'INPUT':
|
||||
case 'TEXTAREA':
|
||||
case 'BUTTON':
|
||||
return nodes[0].value;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -576,7 +582,9 @@
|
||||
elm.addEventListener(event, fn, false);
|
||||
} else if (d.attachEvent) {
|
||||
// <= IE 8 loses scope so need to apply, we add this to object so we can detach later (can't detach anonymous functions)
|
||||
elm[event + fn] = function () { return fn.apply(elm, arguments); };
|
||||
elm[event + fn] = function () {
|
||||
return fn.apply(elm, arguments);
|
||||
};
|
||||
elm.attachEvent('on' + event, elm[event + fn]);
|
||||
}
|
||||
}, nodes);
|
||||
@@ -599,11 +607,14 @@
|
||||
return cb;
|
||||
};
|
||||
// Basic XHR 1, no file support. Shakes fist at IE
|
||||
cb.ajax = function (url, method, callback, options) {
|
||||
cb.ajax = function (url, method, callback, options) { // if options is a number, it's timeout in ms
|
||||
var xhr,
|
||||
query = serializeData(nodes),
|
||||
type = (method) ? method.toUpperCase() : 'GET',
|
||||
timestamp = '_ts=' + (+new Date());
|
||||
timestamp = '_ts=' + (+new Date()),
|
||||
abortTmeo;
|
||||
|
||||
if (_.isNumber(options)) options = {timeout: options};
|
||||
|
||||
var opts = Chartist.extend({}, {
|
||||
nocache: true,
|
||||
@@ -611,7 +622,7 @@
|
||||
loader: true,
|
||||
}, options);
|
||||
|
||||
console.log('ajax to = '+opts.timeout);
|
||||
console.log('ajax to = ' + opts.timeout);
|
||||
|
||||
if (query && (type === 'GET')) {
|
||||
url += (url.indexOf('?') === -1) ? '?' + query : '&' + query;
|
||||
@@ -633,6 +644,12 @@
|
||||
|
||||
xhr.timeout = opts.timeout;
|
||||
|
||||
abortTmeo = setTimeout(function () {
|
||||
errorMsg("XHR timed out.");
|
||||
xhr.abort();
|
||||
if (opts.loader) $('#loader').removeClass('show');
|
||||
}, opts.timeout + 10); // a bit later, but still.;
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
|
||||
@@ -642,6 +659,8 @@
|
||||
if (callback && xhr.status != 0) { // xhr.status 0 means "aborted"
|
||||
callback(xhr.responseText, xhr.status);
|
||||
}
|
||||
|
||||
clearTimeout(abortTmeo);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -654,7 +673,7 @@
|
||||
xhr.send(query);
|
||||
}
|
||||
|
||||
return cb;
|
||||
return xhr;
|
||||
};
|
||||
// Alias to cb.ajax(url, 'get', callback)
|
||||
cb.get = function (url, callback, opts) {
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
var page_spectrogram = (function () {
|
||||
var sg = {};
|
||||
|
||||
var ctx;
|
||||
|
||||
// drawing area
|
||||
var plot = {
|
||||
x:0,
|
||||
y:0,
|
||||
w:860,
|
||||
h:512,
|
||||
dx: 1, // bin
|
||||
dy: 1
|
||||
};
|
||||
|
||||
var interval = 1000;
|
||||
var running = false;
|
||||
var readTimeout; // timer
|
||||
var readoutPending;
|
||||
var readXhr;
|
||||
|
||||
var sampCount = 1024;
|
||||
var binCount = sampCount/2;
|
||||
|
||||
var colormap = {
|
||||
r: [
|
||||
{x: 0, b: 0},
|
||||
{x: .7, b: 0},
|
||||
{x: 1, b: 1},
|
||||
],
|
||||
g: [
|
||||
{x: 0, b: 0},
|
||||
{x: .3, b: 0},
|
||||
{x: .7, b: 1},
|
||||
{x: 1, b: 1},
|
||||
],
|
||||
b: [
|
||||
{x: 0, b: 0},
|
||||
{x: .02, b: .3},
|
||||
{x: .3, b: 1},
|
||||
{x: 1, b: 1},
|
||||
]
|
||||
};
|
||||
|
||||
function cmResolv(db, tab) {
|
||||
var startX,endX,startC,endC;
|
||||
|
||||
db /=6;
|
||||
if (db > 1) db = 1;
|
||||
if (db < 0) db = 0;
|
||||
|
||||
for (var i = 0; i < tab.length; i++) {
|
||||
var p = tab[i];
|
||||
if (db >= p.x) {
|
||||
startX = p.x;
|
||||
startC = p.b;
|
||||
}
|
||||
|
||||
if (db <= p.x) {
|
||||
endX = p.x;
|
||||
endC = p.b;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.round((startC + (endC - startC)*((db - startX)/(endX - startX)))*255);
|
||||
}
|
||||
|
||||
function val2color(x) {
|
||||
var xx = x;//20 * Math.log(x);
|
||||
|
||||
var r = cmResolv(xx, colormap.r);
|
||||
var g = cmResolv(xx, colormap.g);
|
||||
var b = cmResolv(xx, colormap.b);
|
||||
|
||||
return 'rgb('+r+','+g+','+b+')';
|
||||
}
|
||||
|
||||
function shiftSg() {
|
||||
var imageData = ctx.getImageData(plot.x+plot.dx, plot.y, plot.w-plot.dx, plot.h);
|
||||
ctx.putImageData(imageData, plot.x, plot.y);
|
||||
}
|
||||
|
||||
function drawSg(col) {
|
||||
shiftSg();
|
||||
|
||||
for (var i = 0; i < binCount; i++) {
|
||||
// resolve color from the value
|
||||
var y = binCount - i;
|
||||
var clr;
|
||||
|
||||
if (i > col.length) {
|
||||
clr = '#000';
|
||||
} else {
|
||||
clr = val2color(col[i]);
|
||||
}
|
||||
ctx.fillStyle = clr;
|
||||
|
||||
ctx.fillRect(plot.x+plot.w-plot.dx, plot.y+y*plot.dy, plot.dx, plot.dy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onRxData(resp, status) {
|
||||
readoutPending = false;
|
||||
if (status == 200) {
|
||||
try {
|
||||
var j = JSON.parse(resp);
|
||||
if (j.success) {
|
||||
// display
|
||||
drawSg(j.samples);
|
||||
} else {
|
||||
errorMsg("Sampling failed.");
|
||||
}
|
||||
} catch(e) {
|
||||
errorMsg(e);
|
||||
}
|
||||
} else {
|
||||
errorMsg("Request failed.");
|
||||
}
|
||||
|
||||
if (running)
|
||||
readTimeout = setTimeout(requestData, interval); // TODO should actually compute time remaining, this adds interval to the request time.
|
||||
}
|
||||
|
||||
function requestData() {
|
||||
if (readoutPending) {
|
||||
errorMsg("Request already pending - aborting.");
|
||||
readXhr.abort();
|
||||
}
|
||||
readoutPending = true;
|
||||
|
||||
var fs = $('#freq').val();
|
||||
var url = _root+'/measure/fft?n='+sampCount+'&fs='+fs;
|
||||
|
||||
readXhr = $().get(url, onRxData, estimateLoadTime(fs,sampCount));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sg.init = function () {
|
||||
var canvas = $('#sg')[0];
|
||||
ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillRect(plot.x, plot.y, plot.w, plot.h);
|
||||
|
||||
$('#go-btn').on('click', function() {
|
||||
interval = +$('#interval').val(); // ms
|
||||
|
||||
running = !running;
|
||||
if (running) {
|
||||
requestData();
|
||||
} else {
|
||||
clearTimeout(readTimeout);
|
||||
}
|
||||
|
||||
$('#go-btn')
|
||||
.toggleClass('btn-green')
|
||||
.toggleClass('btn-red')
|
||||
.html(running ? 'Stop' : 'Start');
|
||||
});
|
||||
};
|
||||
|
||||
return sg;
|
||||
})();
|
||||
@@ -168,15 +168,8 @@ var page_waveform = (function () {
|
||||
var n = $('#count').val();
|
||||
var fs = $('#freq').val();
|
||||
|
||||
var url = _root+'/measure/{fmt}?n={n}&fs={fs}'.format({
|
||||
fmt: dataFormat, // fft or raw
|
||||
n: n,
|
||||
fs: fs
|
||||
});
|
||||
|
||||
$().get(url, onRxData, {
|
||||
timeout: (1000/fs)*n+1500
|
||||
});
|
||||
var url = _root+'/measure/'+dataFormat+'?n='+n+'&fs='+fs;
|
||||
$().get(url, onRxData, estimateLoadTime(fs,n));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -224,18 +217,6 @@ var page_waveform = (function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
// --- scroll the input box ---
|
||||
$('input[type=number]').on('mousewheel', function(e) {
|
||||
var val = +$(this).val();
|
||||
var step = +($(this).attr('step') || 1);
|
||||
if(e.wheelDelta > 0) {
|
||||
val += step;
|
||||
} else {
|
||||
val -= step;
|
||||
}
|
||||
$(this).val(val);
|
||||
});
|
||||
|
||||
// auto-reload button
|
||||
$('#ar-btn').on('click', toggleAutoReload);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,10 @@ function numfmt(x, places) {
|
||||
return Math.round(x*pow) / pow;
|
||||
}
|
||||
|
||||
function estimateLoadTime(fs, n) {
|
||||
return (1000/fs)*n+1500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a substitution in the given string.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user