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;