diff --git a/api/iqdata.js b/api/iqdata.js new file mode 100644 index 0000000..db879e9 --- /dev/null +++ b/api/iqdata.js @@ -0,0 +1,69 @@ +const http = require('http'); + +var nCpi = 20; +var iqdata = ''; +var spectrum = []; +frequency = []; +var timestamp = []; +var detection = ''; +var ts = ''; +var output = []; +const options_timestamp = { + host: '127.0.0.1', + path: '/api/timestamp', + port: 3000 +}; +const options_iqdata = { + host: '127.0.0.1', + path: '/api/iqdata', + port: 3000 +}; + +function update_data() { + + // check if timestamp is updated + http.get(options_timestamp, function(res) { + res.setEncoding('utf8'); + res.on('data', function (body) { + if (ts != body) + { + ts = body; + http.get(options_iqdata, function(res) { + let body_map = ''; + res.setEncoding('utf8'); + res.on('data', (chunk) => { + body_map += chunk; + }); + res.on('end', () => { + try { + output = JSON.parse(body_map); + // spectrum + spectrum.push(output.spectrum); + if (spectrum.length > nCpi) { + spectrum.shift(); + } + output.spectrum = spectrum; + // frequency + frequency.push(output.frequency); + if (frequency.length > nCpi) { + frequency.shift(); + } + output.frequency = frequency; + } catch (e) { + console.error(e.message); + } + }); + }); + } + }); + }); + +}; + +setInterval(update_data, 100); + +function get_data() { + return output; +}; + +module.exports.get_data_iqdata = get_data; \ No newline at end of file diff --git a/api/server.js b/api/server.js index 11fd3e5..0405c9f 100644 --- a/api/server.js +++ b/api/server.js @@ -4,6 +4,7 @@ const net = require("net"); var data_map = require('./maxhold.js'); var data_detection = require('./detection.js'); +var data_iqdata = require('./iqdata.js'); // constants const PORT = 3000; @@ -44,12 +45,18 @@ app.get('/api/timing', (req, res) => { app.get('/api/iqdata', (req, res) => { res.send(iqdata); }); + +// stash API app.get('/stash/map', (req, res) => { res.send(data_map.get_data_map()); }); app.get('/stash/detection', (req, res) => { res.send(data_detection.get_data_detection()); }); +app.get('/stash/iqdata', (req, res) => { + res.send(data_iqdata.get_data_iqdata()); +}); + // read state of capture app.get('/capture', (req, res) => { res.send(capture); diff --git a/html/controller/index.html b/html/controller/index.html index a3be642..55c8ff2 100644 --- a/html/controller/index.html +++ b/html/controller/index.html @@ -108,6 +108,7 @@
  • Detections in delay over time
  • Detections in Doppler over time
  • Detections in delay-Doppler over time
  • +
  • Spectrum reference
  • @@ -127,6 +128,7 @@
  • Stash
  • Map data
  • Detection data
  • +
  • IqData metadata
  • diff --git a/html/display/spectrum/index.html b/html/display/spectrum/index.html new file mode 100644 index 0000000..19a3f42 --- /dev/null +++ b/html/display/spectrum/index.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + blah2 + + + + + + + + + + + + + + + +
    + +
    + +
    + +
    + + + + + + + diff --git a/html/js/plot_spectrum.js b/html/js/plot_spectrum.js new file mode 100644 index 0000000..d4e717a --- /dev/null +++ b/html/js/plot_spectrum.js @@ -0,0 +1,124 @@ +var timestamp = -1; +var nRows = 3; +var host = window.location.hostname; +var isLocalHost = (host === "localhost" || host === "127.0.0.1" || host === "192.168.0.112"); +var range_x = []; +var range_y = []; + +// setup API +var urlTimestamp = ''; +if (isLocalHost) { + urlTimestamp = '//' + host + ':3000/api/timestamp?timestamp=' + Date.now(); +} else { + urlTimestamp = '//' + host + '/api/timestamp?timestamp=' + Date.now(); +} + +// setup plotly +var layout = { + autosize: true, + margin: { + l: 50, + r: 50, + b: 50, + t: 10, + pad: 0 + }, + hoverlabel: { + namelength: 0 + }, + plot_bgcolor: "rgba(0,0,0,0)", + paper_bgcolor: "rgba(0,0,0,0)", + annotations: [], + displayModeBar: false, + xaxis: { + title: { + text: 'Frequency (MHz)', + font: { + size: 24 + } + }, + ticks: '', + side: 'bottom' + }, + yaxis: { + title: { + text: 'Timestamp', + font: { + size: 24 + } + }, + ticks: '', + ticksuffix: ' ', + autosize: false, + categoryorder: "total descending" + } +}; +var config = { + responsive: true, + displayModeBar: false + //scrollZoom: true +} + +// setup plotly data +var data = [ + { + z: [[0, 0, 0], [0, 0, 0], [0, 0, 0]], + colorscale: 'Jet', + type: 'heatmap' + } +]; +var detection = []; + +Plotly.newPlot('data', data, layout, config); + +// callback function +var intervalId = window.setInterval(function () { + + // check if timestamp is updated + var timestampData = $.get(urlTimestamp, function () { }) + + .done(function (data) { + if (timestamp != data) { + timestamp = data; + + // get new map data + var apiData = $.getJSON(urlMap, function () { }) + .done(function (data) { + + // case draw new plot + if (data.nRows != nRows) { + nRows = data.nRows; + + var trace1 = { + z: data.spectrum, + colorscale: 'Jet', + zauto: false, + //zmin: 0, + //zmax: Math.max(13, data.maxPower), + type: 'heatmap' + }; + + var data_trace = [trace1]; + Plotly.newPlot('data', data_trace, layout, config); + } + // case update plot + else { + var trace_update = { + z: [data.spectrum] + //zmax: [Math.max(13, data.maxPower), []] + }; + Plotly.update('data', trace_update); + } + + }) + .fail(function () { + }) + .always(function () { + }); + } + }) + .fail(function () { + }) + .always(function () { + }); +}, 100); diff --git a/src/process/spectrum/SpectrumAnalyser.cpp b/src/process/spectrum/SpectrumAnalyser.cpp index 5cb7ec0..413e8d3 100644 --- a/src/process/spectrum/SpectrumAnalyser.cpp +++ b/src/process/spectrum/SpectrumAnalyser.cpp @@ -14,7 +14,8 @@ SpectrumAnalyser::SpectrumAnalyser(uint32_t _n, double _bandwidth) // compute nfft decimation = n/bandwidth; - nfft = n/decimation; + nSpectrum = n/decimation; + nfft = nSpectrum*decimation; // compute FFTW plans in constructor dataX = new std::complex[nfft]; @@ -29,26 +30,41 @@ SpectrumAnalyser::~SpectrumAnalyser() void SpectrumAnalyser::process(IqData *x) { - // decimate - int16_t iData = 0; + // load data and FFT std::deque> data = x->get_data(); - for (int i = 0; i < x->get_length(); i+=decimation) - { - dataX[iData] = data[i]; - iData++; - } - - fftw_execute(fftX); - - // update spectrum - std::vector> spectrum; for (int i = 0; i < nfft; i++) { - spectrum.push_back(dataX[i]); + dataX[i] = data[i]; + } + fftw_execute(fftX); + + // fftshift + std::vector> fftshift; + for (int i = 0; i < nfft; i++) + { + fftshift.push_back(dataX[(i + int(nfft / 2) + 1) % nfft]); + } + + // decimate + std::vector> spectrum; + for (int i = 0; i < nfft; i+=decimation) + { + spectrum.push_back(fftshift[i]); } x->update_spectrum(spectrum); // update frequency + std::vector frequency; + double offset = 0; + if (decimation % 2 == 0) + { + offset = bandwidth/2; + } + for (int i = -nSpectrum/2; i < nSpectrum/2; i++) + { + frequency.push_back(((i*bandwidth)+offset+204640000)/1000); + } + x->update_frequency(frequency); return; } diff --git a/src/process/spectrum/SpectrumAnalyser.h b/src/process/spectrum/SpectrumAnalyser.h index 80b41e5..7140cca 100644 --- a/src/process/spectrum/SpectrumAnalyser.h +++ b/src/process/spectrum/SpectrumAnalyser.h @@ -33,6 +33,9 @@ private: /// @brief Number of samples to perform FFT. uint32_t nfft; + /// @brief Number of samples in decimated spectrum. + uint32_t nSpectrum; + /// @brief Resolution of spectrum (Hz). double resolution;