Add spectrum processing and API

This commit is contained in:
30hours 2023-12-11 12:11:54 +00:00
parent 386c65280e
commit f1576d40b7
10 changed files with 244 additions and 2 deletions

View file

@ -24,6 +24,7 @@ add_executable(blah2
${PROJECT_SOURCE_DIR}/process/detection/CfarDetector1D.cpp
${PROJECT_SOURCE_DIR}/process/detection/Centroid.cpp
${PROJECT_SOURCE_DIR}/process/detection/Interpolate.cpp
${PROJECT_SOURCE_DIR}/process/spectrum/SpectrumAnalyser.cpp
${PROJECT_SOURCE_DIR}/data/IqData.cpp
${PROJECT_SOURCE_DIR}/data/Map.cpp
${PROJECT_SOURCE_DIR}/data/Detection.cpp
@ -67,5 +68,6 @@ include_directories("${PROJECT_SOURCE_DIR}/capture/rspduo/")
include_directories("${PROJECT_SOURCE_DIR}/process/ambiguity/")
include_directories("${PROJECT_SOURCE_DIR}/process/clutter/")
include_directories("${PROJECT_SOURCE_DIR}/process/detection/")
include_directories("${PROJECT_SOURCE_DIR}/process/spectrum/")
include_directories("${PROJECT_SOURCE_DIR}/data/")
include_directories("${PROJECT_SOURCE_DIR}/data/meta/")

View file

@ -12,6 +12,7 @@ var map = '';
var detection = '';
var timestamp = '';
var timing = '';
var iqdata = '';
var data = '';
var capture = false;
@ -40,6 +41,9 @@ app.get('/api/timestamp', (req, res) => {
app.get('/api/timing', (req, res) => {
res.send(timing);
});
app.get('/api/iqdata', (req, res) => {
res.send(iqdata);
});
app.get('/stash/map', (req, res) => {
res.send(data_map.get_data_map());
});
@ -123,3 +127,20 @@ const server_timing = net.createServer((socket)=>{
})
});
server_timing.listen(4001);
// tcp listener iqdata metadata
const server_iqdata = net.createServer((socket)=>{
socket.write("Hello From Server!")
socket.on("data",(msg)=>{
data = data + msg.toString();
if (data.slice(-1) === "}")
{
iqdata = data;
data = '';
}
});
socket.on("close",()=>{
console.log("Connection closed.");
})
});
server_iqdata.listen(4002);

View file

@ -36,6 +36,7 @@ network:
detection: 3002
timestamp: 4000
timing: 4001
iqdata: 4002
save:
iq: true

View file

@ -36,6 +36,7 @@ network:
detection: 3002
timestamp: 4000
timing: 4001
iqdata: 4002
save:
iq: true

View file

@ -107,7 +107,7 @@
<li class="py-1"><a class="text-reset text-decoration-none" href="/display/maxhold">Max-hold delay Doppler map</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/display/detection/delay">Detections in delay over time</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/display/detection/doppler">Detections in Doppler over time</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/display/detection/delay-Doppler">Detections in delay-Doppler over time</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/display/detection/delay-doppler">Detections in delay-Doppler over time</a></li>
</ul>
</div>
@ -117,6 +117,7 @@
<li class="py-1"><a class="text-reset text-decoration-none" href="/api/map">Map data</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/api/detection">Detection data</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/api/timing">Timing data</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/api/iqdata">IQ metadata</a></li>
<li class="py-1"><a class="text-reset text-decoration-none" href="/api/timestamp">Latest timestamp</a></li>
</ul>
</div>

View file

@ -16,6 +16,7 @@
#include <Centroid.h>
#include <Interpolate.h>
#include <Timing.h>
#include <SpectrumAnalyser.h>
#include <sys/types.h>
#include <getopt.h>
#include <string>
@ -100,22 +101,25 @@ int main(int argc, char **argv)
fftw_plan_with_nthreads(4);
// setup socket
uint16_t port_map, port_detection, port_timestamp, port_timing;
uint16_t port_map, port_detection, port_timestamp, port_timing, port_iqdata;
std::string ip;
tree["network"]["ports"]["map"] >> port_map;
tree["network"]["ports"]["detection"] >> port_detection;
tree["network"]["ports"]["timestamp"] >> port_timestamp;
tree["network"]["ports"]["timing"] >> port_timing;
tree["network"]["ports"]["iqdata"] >> port_iqdata;
tree["network"]["ip"] >> ip;
asio::io_service io_service;
asio::ip::tcp::socket socket_map(io_service);
asio::ip::tcp::socket socket_detection(io_service);
asio::ip::tcp::socket socket_timestamp(io_service);
asio::ip::tcp::socket socket_timing(io_service);
asio::ip::tcp::socket socket_iqdata(io_service);
asio::ip::tcp::endpoint endpoint_map;
asio::ip::tcp::endpoint endpoint_detection;
asio::ip::tcp::endpoint endpoint_timestamp;
asio::ip::tcp::endpoint endpoint_timing;
asio::ip::tcp::endpoint endpoint_iqdata;
endpoint_map = asio::ip::tcp::endpoint(
asio::ip::address::from_string(ip), port_map);
endpoint_detection = asio::ip::tcp::endpoint(
@ -124,10 +128,13 @@ int main(int argc, char **argv)
asio::ip::address::from_string(ip), port_timestamp);
endpoint_timing = asio::ip::tcp::endpoint(
asio::ip::address::from_string(ip), port_timing);
endpoint_iqdata = asio::ip::tcp::endpoint(
asio::ip::address::from_string(ip), port_iqdata);
socket_map.connect(endpoint_map);
socket_detection.connect(endpoint_detection);
socket_timestamp.connect(endpoint_timestamp);
socket_timing.connect(endpoint_timing);
socket_iqdata.connect(endpoint_iqdata);
asio::error_code err;
std::string subdata;
uint32_t MTU = 1024;
@ -165,6 +172,10 @@ int main(int argc, char **argv)
tree["process"]["detection"]["nCentroid"] >> nCentroid;
Centroid *centroid = new Centroid(nCentroid, nCentroid, 1/tCpi);
// setup process spectrum analyser
double spectrumBandwidth = 2000;
SpectrumAnalyser *spectrumAnalyser = new SpectrumAnalyser(nSamples, spectrumBandwidth);
// setup output data
bool saveMap;
tree["save"]["map"] >> saveMap;
@ -189,6 +200,9 @@ int main(int argc, char **argv)
std::vector<double> timing_time;
std::string jsonTiming;
// setup output signal
std::string jsonIqData;
// run process
std::thread t2([&]{
while (true)
@ -212,6 +226,9 @@ int main(int argc, char **argv)
timing_name.push_back("extract_buffer");
timing_time.push_back(delta_t1);
// spectrum
spectrumAnalyser->process(x);
// clutter filter
if (!filter->process(x, y))
{
@ -240,6 +257,14 @@ int main(int argc, char **argv)
timing_name.push_back("detector");
timing_time.push_back(delta_t4);
// output IqData meta data
jsonIqData = x->to_json(t0/1000);
for (int i = 0; i < (jsonIqData.size() + MTU - 1) / MTU; i++)
{
subdata = jsonIqData.substr(i * MTU, MTU);
socket_iqdata.write_some(asio::buffer(subdata, subdata.size()), err);
}
// output map data
mapJson = map->to_json();
mapJson = map->delay_bin_to_km(mapJson, fs);

View file

@ -2,6 +2,11 @@
#include <iostream>
#include <cstdlib>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filewritestream.h"
// constructor
IqData::IqData(uint32_t _n)
{
@ -72,3 +77,48 @@ void IqData::clear()
data->pop_front();
}
}
void IqData::update_spectrum(std::vector<std::complex<double>> _spectrum)
{
spectrum = _spectrum;
}
void IqData::update_frequency(std::vector<double> _frequency)
{
frequency = _frequency;
}
std::string IqData::to_json(uint64_t timestamp)
{
rapidjson::Document document;
document.SetObject();
rapidjson::Document::AllocatorType &allocator = document.GetAllocator();
// store frequency array
rapidjson::Value arrayFrequency(rapidjson::kArrayType);
for (int i = 0; i < frequency.size(); i++)
{
arrayFrequency.PushBack(frequency[i], allocator);
}
// store spectrum array
rapidjson::Value arraySpectrum(rapidjson::kArrayType);
for (int i = 0; i < spectrum.size(); i++)
{
arraySpectrum.PushBack(10 * std::log10(std::abs(spectrum[i])), allocator);
}
document.AddMember("timestamp", timestamp, allocator);
document.AddMember("min", min, allocator);
document.AddMember("max", max, allocator);
document.AddMember("mean", mean, allocator);
document.AddMember("frequency", arrayFrequency, allocator);
document.AddMember("spectrum", arraySpectrum, allocator);
rapidjson::StringBuffer strbuf;
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
writer.SetMaxDecimalPlaces(2);
document.Accept(writer);
return strbuf.GetString();
}

View file

@ -9,6 +9,7 @@
#include <stdint.h>
#include <deque>
#include <vector>
#include <complex>
#include <mutex>
@ -24,6 +25,21 @@ private:
/// @brief Pointer to IQ data.
std::deque<std::complex<double>> *data;
/// @brief Minimum value.
double min;
/// @brief Maximum value.
double max;
/// @brief Mean value.
double mean;
/// @brief Spectrum vector.
std::vector<std::complex<double>> spectrum;
/// @brief Frequency vector (Hz).
std::vector<double> frequency;
public:
/// @brief Constructor.
/// @param n Number of samples.
@ -66,6 +82,21 @@ public:
/// @brief Clear samples from the queue.
/// @return Void.
void clear();
/// @brief Update the time differences and names.
/// @param spectrum Spectrum vector.
/// @return Void.
void update_spectrum(std::vector<std::complex<double>> spectrum);
/// @brief Update the time differences and names.
/// @param frequency Frequency vector.
/// @return Void.
void update_frequency(std::vector<double> frequency);
/// @brief Generate JSON of the signal and metadata.
/// @param timestamp Current time (POSIX ms).
/// @return JSON string.
std::string to_json(uint64_t timestamp);
};
#endif

View file

@ -0,0 +1,54 @@
#include "SpectrumAnalyser.h"
#include <complex>
#include <iostream>
#include <deque>
#include <vector>
#include <math.h>
// constructor
SpectrumAnalyser::SpectrumAnalyser(uint32_t _n, double _bandwidth)
{
// input
n = _n;
bandwidth = _bandwidth;
// compute nfft
decimation = n/bandwidth;
nfft = n/decimation;
// compute FFTW plans in constructor
dataX = new std::complex<double>[nfft];
fftX = fftw_plan_dft_1d(nfft, reinterpret_cast<fftw_complex *>(dataX),
reinterpret_cast<fftw_complex *>(dataX), FFTW_FORWARD, FFTW_ESTIMATE);
}
SpectrumAnalyser::~SpectrumAnalyser()
{
fftw_destroy_plan(fftX);
}
void SpectrumAnalyser::process(IqData *x)
{
// decimate
int16_t iData = 0;
std::deque<std::complex<double>> 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<std::complex<double>> spectrum;
for (int i = 0; i < nfft; i++)
{
spectrum.push_back(dataX[i]);
}
x->update_spectrum(spectrum);
// update frequency
return;
}

View file

@ -0,0 +1,56 @@
/// @file SpectrumAnalyser.h
/// @class SpectrumAnalyser
/// @brief A class to generate frequency spectrum plots.
/// @details Simple decimate and FFT on CPI IQ data for frequency spectrum.
/// @author 30hours
/// @todo Potentially create k spectrum plots from sub-CPIs.
#ifndef SPECTRUMANALYSER_H
#define SPECTRUMANALYSER_H
#include <IqData.h>
#include <stdint.h>
#include <fftw3.h>
class SpectrumAnalyser
{
private:
/// @brief Number of samples on input.
uint32_t n;
/// @brief Minimum bandwidth of frequency bin (Hz).
double bandwidth;
/// @brief Decimation factor.
uint32_t decimation;
/// @brief FFTW plans for ambiguity processing.
fftw_plan fftX;
/// @brief FFTW storage for ambiguity processing.
std::complex<double> *dataX;
/// @brief Number of samples to perform FFT.
uint32_t nfft;
/// @brief Resolution of spectrum (Hz).
double resolution;
public:
/// @brief Constructor.
/// @param n Number of samples on input.
/// @param bandwidth Minimum bandwidth of frequency bin (Hz).
/// @return The object.
SpectrumAnalyser(uint32_t n, double bandwidth);
/// @brief Destructor.
/// @return Void.
~SpectrumAnalyser();
/// @brief Process spectrum data.
/// @param x Reference samples.
/// @return Void.
void process(IqData *x);
};
#endif