|
|
|
|
|
|
|
|
class PcolorBuffer{
|
|
|
constructor({div, data, key, props}){
|
|
|
this.div = document.getElementById(div);
|
|
|
this.n = 0
|
|
|
this.divs = [];
|
|
|
this.wait = false;
|
|
|
this.key = key;
|
|
|
this.timer = (props.throttle || 30)*1000;
|
|
|
this.lastRan = Date.now();
|
|
|
this.lastFunc = null;
|
|
|
this.zbuffer = [];
|
|
|
this.xbuffer = [];
|
|
|
this.empty = Array(data.yrange.length).fill(null);
|
|
|
this.props = props;
|
|
|
this.setup(data);
|
|
|
}
|
|
|
|
|
|
setup(data){
|
|
|
this.last = data.time.slice(-1);
|
|
|
if (data.time.length == 1){
|
|
|
var values = { 'time': data.time, 'data': data[this.key].map(function(x) {return [x]})};
|
|
|
}else{
|
|
|
var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length);
|
|
|
}
|
|
|
|
|
|
var t = values.time.map(function(x) { return new Date(x*1000); });
|
|
|
|
|
|
for (var i = 0; i < data[this.key].length; i++){
|
|
|
var layout = {
|
|
|
xaxis: {
|
|
|
title: 'Time',
|
|
|
showgrid: false,
|
|
|
},
|
|
|
yaxis: {
|
|
|
title: 'km',
|
|
|
showgrid: false,
|
|
|
},
|
|
|
titlefont: {
|
|
|
size: 14,
|
|
|
}
|
|
|
};
|
|
|
var iDiv = document.createElement('div');
|
|
|
iDiv.id = 'plot-' + i;
|
|
|
this.zbuffer.push([]);
|
|
|
this.n = this.n + 1;
|
|
|
this.div.appendChild(iDiv);
|
|
|
this.divs.push(iDiv.id);
|
|
|
var trace = {
|
|
|
z: values.data[i],
|
|
|
x: t,
|
|
|
y: data.yrange,
|
|
|
colorscale: this.props.colormap || 'Viridis',
|
|
|
transpose: true,
|
|
|
type: 'heatmap'
|
|
|
};
|
|
|
|
|
|
if (this.props.zmin) {trace.zmin = this.props.zmin}
|
|
|
if (this.props.zmax) {trace.zmax = this.props.zmax}
|
|
|
|
|
|
layout.title = 'Channel ' + i + ' - ' + t.slice(-1).toLocaleString();
|
|
|
var conf = {
|
|
|
modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
|
|
|
displaylogo: false,
|
|
|
showTips: true
|
|
|
};
|
|
|
Plotly.newPlot('plot-'+i, [trace], layout, conf);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
getSize(){
|
|
|
var div = document.getElementById(this.divs[0]);
|
|
|
var t = this.xbuffer.slice(-1)[0];
|
|
|
var n = 0;
|
|
|
var timespan = (this.props.timespan || 8)*1000*60*60;
|
|
|
|
|
|
while ((t-div.data[0].x[n]) > timespan){
|
|
|
n += 1;
|
|
|
}
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
fill_gaps(xBuffer, zBuffer, interval, N){
|
|
|
|
|
|
var x = [xBuffer[0]];
|
|
|
var z = [];
|
|
|
var last;
|
|
|
|
|
|
for (var j = 0; j < N; j++){
|
|
|
z.push([zBuffer[j][0]]);
|
|
|
}
|
|
|
|
|
|
for (var i = 1; i < xBuffer.length; i++){
|
|
|
var cnt = 0;
|
|
|
last = x.slice(-1)[0];
|
|
|
while (Math.abs(parseFloat(xBuffer[i])-last-parseFloat(interval))>0.5){
|
|
|
cnt += 1;
|
|
|
last = last+interval;
|
|
|
x.push(last);
|
|
|
for (var j = 0; j < N; j++){
|
|
|
z[j].push(this.empty);
|
|
|
}
|
|
|
// Avoid infinite loop
|
|
|
if (cnt==100){break;}
|
|
|
}
|
|
|
x.push(xBuffer[i]);
|
|
|
for (var j = 0; j < N; j++){
|
|
|
z[j].push(zBuffer[j][i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return {'time':x, 'data':z};
|
|
|
}
|
|
|
|
|
|
plot(){
|
|
|
// add new data to plots and empty buffers
|
|
|
var N = this.getSize();
|
|
|
console.log('Plotting...');
|
|
|
for (var i = 0; i < this.n; i++){
|
|
|
var div = document.getElementById(this.divs[i]);
|
|
|
if (N > 0){
|
|
|
div.data[0].z = div.data[0].z.slice(N,)
|
|
|
div.data[0].x = div.data[0].x.slice(N,)
|
|
|
}
|
|
|
Plotly.extendTraces(div, {
|
|
|
z: [this.zbuffer[i]],
|
|
|
x: [this.xbuffer]
|
|
|
}, [0]);
|
|
|
this.zbuffer[i] = [];
|
|
|
}
|
|
|
this.xbuffer = [];
|
|
|
}
|
|
|
|
|
|
update(obj){
|
|
|
|
|
|
// fill data gaps
|
|
|
var cnt = 0;
|
|
|
while (Math.abs(parseFloat(obj.time[0])-this.last-parseFloat(obj.interval))>0.5){
|
|
|
cnt += 1;
|
|
|
this.last += obj.interval;
|
|
|
var newt = new Date((this.last)*1000);
|
|
|
this.xbuffer.push(newt);
|
|
|
for (var i = 0; i < obj[this.key].length; i++){
|
|
|
this.zbuffer[i].push(this.empty);
|
|
|
}
|
|
|
// Avoid infinite loop
|
|
|
if (cnt==100){break;}
|
|
|
}
|
|
|
|
|
|
// update buffers
|
|
|
this.last = parseFloat(obj.time[0]);
|
|
|
var t = new Date(obj.time[0]*1000);
|
|
|
this.xbuffer.push(t);
|
|
|
for (var i = 0; i < obj[this.key].length; i++){
|
|
|
this.zbuffer[i].push(obj[this.key][i]);
|
|
|
// update title
|
|
|
var div = document.getElementById(this.divs[i]);
|
|
|
Plotly.relayout(div, {
|
|
|
title: 'Channel ' + i + ' - ' + t.toLocaleString(),
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// plot when ready (every 10 secs)
|
|
|
if (!this.wait){
|
|
|
this.plot();
|
|
|
this.wait = true;
|
|
|
} else {
|
|
|
clearTimeout(this.lastFunc)
|
|
|
this.lastFunc = setTimeout(function(scope) {
|
|
|
if ((Date.now() - scope.lastRan) >= scope.timer) {
|
|
|
scope.plot()
|
|
|
scope.lastRan = Date.now()
|
|
|
}
|
|
|
}, this.timer - (Date.now() - this.lastRan), this)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class Pcolor{
|
|
|
constructor({div, data, props}){
|
|
|
this.div = document.getElementById(div);
|
|
|
this.n = 0
|
|
|
this.divs = [];
|
|
|
this.props = props;
|
|
|
this.setup(data);
|
|
|
}
|
|
|
|
|
|
setup(data){
|
|
|
console.log(data);
|
|
|
for (var i = 0; i < data.spc.length; i++){
|
|
|
var layout = {
|
|
|
height: 400,
|
|
|
xaxis: {
|
|
|
title: 'Velocity',
|
|
|
showgrid: false,
|
|
|
zeroline: false,
|
|
|
domain: [0, 0.7],
|
|
|
},
|
|
|
yaxis: {
|
|
|
title: 'km',
|
|
|
showgrid: false,
|
|
|
},
|
|
|
xaxis2: {
|
|
|
title: 'dB',
|
|
|
domain: [0.75, 1],
|
|
|
ticks: 'outside',
|
|
|
},
|
|
|
titlefont: {
|
|
|
size: 14,
|
|
|
}
|
|
|
};
|
|
|
var iDiv = document.createElement('div');
|
|
|
iDiv.id = 'plot-' + i;
|
|
|
iDiv.className += iDiv.className ? ' col-md-6' : 'col-md-6';
|
|
|
this.n = this.n + 1;
|
|
|
this.div.appendChild(iDiv);
|
|
|
this.divs.push(iDiv.id);
|
|
|
|
|
|
var trace1 = {
|
|
|
z: data.spc[i],
|
|
|
x: data.xrange,
|
|
|
y: data.yrange,
|
|
|
colorscale: this.props.colormap || 'Viridis',
|
|
|
transpose: true,
|
|
|
type: 'heatmap'
|
|
|
};
|
|
|
|
|
|
var trace2 = {
|
|
|
x: data.rti[i],
|
|
|
y: data.yrange,
|
|
|
xaxis: 'x2',
|
|
|
yaxis: 'y',
|
|
|
type: 'scatter',
|
|
|
};
|
|
|
|
|
|
if (this.props.zmin) {
|
|
|
trace1.zmin = this.props.zmin
|
|
|
}
|
|
|
if (this.props.zmax) {
|
|
|
trace1.zmax = this.props.zmax;
|
|
|
layout.xaxis2.range = [this.props.zmin, this.props.zmax]
|
|
|
}
|
|
|
|
|
|
var t = new Date(data.time*1000);
|
|
|
layout.title = 'Channel ' + i + ': ' + data.noise[i]+'dB - ' + t.toLocaleString();
|
|
|
var conf = {
|
|
|
modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
|
|
|
displaylogo: false,
|
|
|
showTips: true
|
|
|
};
|
|
|
Plotly.newPlot('plot-'+i, [trace1, trace2], layout, conf);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
plot(obj){
|
|
|
|
|
|
// add new data to plots and empty buffers
|
|
|
console.log('Plotting...');
|
|
|
var t = new Date(obj.time[0]*1000);
|
|
|
for (var i = 0; i < this.n; i++){
|
|
|
var div = document.getElementById(this.divs[i]);
|
|
|
Plotly.relayout(div, {
|
|
|
title: 'Channel ' + i + ': ' + obj.noise[i]+'dB - ' + t.toLocaleString(),
|
|
|
});
|
|
|
Plotly.restyle(div, {
|
|
|
z: [obj.spc[i], null],
|
|
|
x: [obj.xrange, obj.rti[i]]
|
|
|
}, [0, 1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class Scatter{
|
|
|
constructor({div, data, key, props}){
|
|
|
this.div = document.getElementById(div);
|
|
|
this.n = 0
|
|
|
this.key = key;
|
|
|
this.wait = false;
|
|
|
this.timer = (props.throttle || 30)*1000;
|
|
|
this.lastRan = Date.now();
|
|
|
this.lastFunc = null;
|
|
|
this.ybuffer = [];
|
|
|
this.xbuffer = [];
|
|
|
this.props = props;
|
|
|
this.setup(data);
|
|
|
}
|
|
|
|
|
|
setup(data){
|
|
|
|
|
|
var traces = [];
|
|
|
this.last = data.time.slice(-1);
|
|
|
if (data.time.length == 1){
|
|
|
var values = { 'time': data.time, 'data': data[this.key] };
|
|
|
}else{
|
|
|
var values = this.fill_gaps(data.time, data[this.key], data.interval, data[this.key].length);
|
|
|
}
|
|
|
|
|
|
var t = values.time.map(function(x) { return new Date(x*1000); });
|
|
|
|
|
|
for (var i = 0; i < data[this.key].length; i++){
|
|
|
|
|
|
this.n = this.n + 1;
|
|
|
this.ybuffer.push([]);
|
|
|
var trace = {
|
|
|
x: t,
|
|
|
y: data[this.key][i],
|
|
|
mode: 'lines',
|
|
|
type: 'scatter',
|
|
|
name: 'Channel ' + i
|
|
|
};
|
|
|
|
|
|
traces.push(trace);
|
|
|
}
|
|
|
var title = this.props.title || ''
|
|
|
var layout = {
|
|
|
// autosize: false,
|
|
|
// width: 800,
|
|
|
// height: 400,
|
|
|
title: title + ' - ' + t.slice(-1).toLocaleString(),
|
|
|
xaxis: {
|
|
|
title: 'Time',
|
|
|
linecolor: 'black',
|
|
|
},
|
|
|
yaxis: {
|
|
|
title: this.props.ylabel || 'dB',
|
|
|
linecolor: 'black',
|
|
|
},
|
|
|
titlefont: {
|
|
|
size: 14,
|
|
|
}
|
|
|
};
|
|
|
|
|
|
if (this.props.ymin) {layout.yaxis.range = [this.props.ymin, this.props.ymax] }
|
|
|
|
|
|
var conf = {
|
|
|
modeBarButtonsToRemove: ['sendDataToCloud', 'autoScale2d', 'hoverClosestCartesian', 'hoverCompareCartesian', 'lasso2d', 'select2d', 'zoomIn2d', 'zoomOut2d', 'toggleSpikelines'],
|
|
|
displaylogo: false,
|
|
|
showTips: true
|
|
|
};
|
|
|
Plotly.newPlot('plot', traces, layout, conf);
|
|
|
}
|
|
|
|
|
|
getSize(){
|
|
|
var t = this.xbuffer.slice(-1)[0];
|
|
|
var n = 0;
|
|
|
var timespan = (this.props.timespan || 12)*1000*60*60;
|
|
|
|
|
|
while ((t-this.div.data[0].x[n]) > timespan){
|
|
|
n += 1;
|
|
|
}
|
|
|
return n;
|
|
|
}
|
|
|
|
|
|
fill_gaps(xBuffer, yBuffer, interval, N){
|
|
|
|
|
|
var x = [xBuffer[0]];
|
|
|
var y = [];
|
|
|
|
|
|
for (var j = 0; j < N; j++){
|
|
|
y.push([yBuffer[j][0]]);
|
|
|
}
|
|
|
|
|
|
var last;
|
|
|
|
|
|
for (var i = 1; i < xBuffer.length; i++){
|
|
|
var cnt = 0;
|
|
|
last = x.slice(-1)[0];
|
|
|
while (Math.abs(parseFloat(xBuffer[i])-last-parseFloat(interval))>0.5){
|
|
|
cnt += 1;
|
|
|
last = last+interval;
|
|
|
x.push(last);
|
|
|
for (var j = 0; j < N; j++){
|
|
|
y[j].push(null);
|
|
|
}
|
|
|
// Avoid infinite loop
|
|
|
if (cnt==100){break;}
|
|
|
}
|
|
|
x.push(xBuffer[i]);
|
|
|
|
|
|
for (var j = 0; j < N; j++){
|
|
|
y[j].push(yBuffer[j][i]);
|
|
|
}
|
|
|
}
|
|
|
return {'time':x, 'data':y};
|
|
|
}
|
|
|
|
|
|
plot(){
|
|
|
// add new data to plots and empty buffers
|
|
|
var xvalues = [];
|
|
|
var yvalues = [];
|
|
|
var traces = [];
|
|
|
var N = this.getSize();
|
|
|
console.log('Plotting...');
|
|
|
for (var i = 0; i < this.n; i++){
|
|
|
if (N > 0){
|
|
|
this.div.data[i].y = this.div.data[i].y.slice(N,)
|
|
|
this.div.data[i].x = this.div.data[i].x.slice(N,)
|
|
|
}
|
|
|
yvalues.push(this.ybuffer[i]);
|
|
|
xvalues.push(this.xbuffer);
|
|
|
traces.push(i);
|
|
|
this.ybuffer[i] = [];
|
|
|
}
|
|
|
Plotly.extendTraces(this.div, {
|
|
|
y: yvalues,
|
|
|
x: xvalues
|
|
|
}, traces);
|
|
|
this.xbuffer = [];
|
|
|
}
|
|
|
|
|
|
update(obj){
|
|
|
|
|
|
// fill data gaps
|
|
|
var cnt = 0;
|
|
|
while (Math.abs(parseFloat(obj.time[0])-this.last-parseFloat(obj.interval))>0.5){
|
|
|
cnt += 1;
|
|
|
this.last += obj.interval;
|
|
|
var newt = new Date((this.last)*1000);
|
|
|
this.xbuffer.push(newt);
|
|
|
for (var i = 0; i < this.n; i++){
|
|
|
this.ybuffer[i].push(null);
|
|
|
}
|
|
|
// Avoid infinite loop
|
|
|
if (cnt==100){break;}
|
|
|
}
|
|
|
|
|
|
// update buffers
|
|
|
this.last = parseFloat(obj.time[0]);
|
|
|
var t = new Date(obj.time[0]*1000);
|
|
|
this.xbuffer.push(t);
|
|
|
for (var i = 0; i < this.n; i++){
|
|
|
this.ybuffer[i].push(obj[this.key][i][0]);
|
|
|
}
|
|
|
var title = this.props.title || ''
|
|
|
Plotly.relayout(this.div, {
|
|
|
title: title + ' - ' + t.toLocaleString(),
|
|
|
});
|
|
|
|
|
|
// plot when ready (every 10 secs)
|
|
|
if (!this.wait){
|
|
|
this.plot();
|
|
|
this.wait = true;
|
|
|
} else {
|
|
|
clearTimeout(this.lastFunc)
|
|
|
this.lastFunc = setTimeout(function(scope) {
|
|
|
if ((Date.now() - scope.lastRan) >= scope.timer) {
|
|
|
scope.plot()
|
|
|
scope.lastRan = Date.now()
|
|
|
}
|
|
|
}, this.timer - (Date.now() - this.lastRan), this)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|