Add doxygen comments

This commit is contained in:
30hours 2023-05-20 13:10:55 +09:30
parent d432945c0b
commit 2d325d257d
14 changed files with 2963 additions and 87 deletions

4
.gitignore vendored
View file

@ -4,3 +4,7 @@ bin/*
!bin/README.md
*.a
*.so
.vscode/
doc/html/*
doc/latex/*
!doc/html/example.png

View file

@ -4,7 +4,7 @@ MAINTAINER 30hours <nathan@30hours.dev>
WORKDIR blah2
ADD lib lib
RUN apt-get update
RUN apt-get install -y g++ make cmake libfftw3-dev liblapack-dev libopenblas-dev xz-utils libudev-dev libusb-1.0.0-dev sudo systemd
RUN apt-get install -y g++ make cmake libfftw3-dev liblapack-dev libopenblas-dev xz-utils libudev-dev libusb-1.0.0-dev sudo systemd doxygen graphviz
RUN cd lib && tar xf armadillo-12.0.1.tar.xz && cd armadillo-12.0.1 && cmake . && make install
RUN cd lib/sdrplay-3.0.7 && mkdir -p /etc/udev/rules.d && yes | ./install_lib.sh

2494
Doxyfile Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,2 +1,5 @@
## [0.1] - 04/May/2023
- Initial release
- Initial release
## [0.2] - 20/May/2023
- Add doxygen comments

1
doc/html/example.png Symbolic link
View file

@ -0,0 +1 @@
../../example.png

View file

@ -1,20 +1,16 @@
// A real-time radar.
// Author: github.com/30hours
// Date: 31/Jan/2023
// License: MIT
/// @file blah2.cpp
/// @brief A real-time radar.
/// @author 30hours
#define RYML_SINGLE_HDR_DEFINE_NOW
#include <ryml-0.5.0.hpp>
#include <asio.hpp>
#include <Capture.h>
#include <Ambiguity.h>
#include <WienerHopf.h>
#include <IqData.h>
#include <Map.h>
#include <sys/types.h>
#include <getopt.h>
#include <string>

View file

@ -1,3 +1,8 @@
/// @file Capture.h
/// @class Capture
/// @brief A class for a generic IQ capture device.
/// @author 30hours
#ifndef CAPTURE_H
#define CAPTURE_H
@ -7,20 +12,53 @@
class Capture
{
private:
/// @brief The valid capture devices.
static const std::string VALID_TYPE[2];
bool replay;
bool loop;
std::string file;
public:
std::string type;
uint32_t fs;
uint32_t fc;
bool saveIq;
std::string path;
Capture(std::string type, uint32_t fs, uint32_t fc, std::string path);
void process(IqData *buffer1, IqData *buffer2);
void set_replay(bool loop, std::string file);
/// @brief True if file replay is enabled.
bool replay;
/// @brief True if replay file should loop when complete.
bool loop;
/// @brief Absolute path of file to replay.
std::string file;
public:
/// @brief The capture device type.
std::string type;
/// @brief Sampling frequency (Hz).
uint32_t fs;
/// @brief Center frequency (Hz).
uint32_t fc;
/// @brief True if IQ data is saved to file.
bool saveIq;
/// @brief Absolute path to IQ save location.
std::string path;
/// @brief Constructor.
/// @param type The capture device type.
/// @param fs Sampling frequency (Hz).
/// @param fc Center frequency (Hz).
/// @param path Absolute path to IQ save location.
/// @return The object.
Capture(std::string type, uint32_t fs, uint32_t fc, std::string path);
/// @brief Implement the capture process.
/// @param buffer1 Buffer for reference samples.
/// @param buffer2 Buffer for surveillance samples.
/// @return Void.
void process(IqData *buffer1, IqData *buffer2);
/// @brief Set parameters to enable file replay.
/// @param loop True if replay file should loop when complete.
/// @param file Absolute path of file to replay.
/// @return Void.
void set_replay(bool loop, std::string file);
};
#endif

View file

@ -1,30 +1,3 @@
/******************************************************************************
*
* Filename: rspduo_iq_recorder.c
*
* Author: Michael Parker
*
* Created: 13 Feb 2022
*
* Description: IQ Recorder for SDRplay RSPduo
*
* Loosely based upon the sdrplay_api_sample_app.c and sdr_play.c examples
* provided in the SDRplay API V3 documentation
* This should be read in conjuction with that documentation
*
* For coherent operation the use of sdrplay_api_Tuner_Both is most important
* This clue was provided by Gustaw Mazurek of WUT
*
* Revision History:
*
* 13 Feb 2022 MCP Initial coding
*
*
* https://github.com/fventuri/gr-sdrplay/issues/2
* https://github.com/g4eev/RSPduoEME/blob/main/rspduointerface.cpp
*
******************************************************************************/
#include <RspDuo.h>
#include <stdbool.h>
#include <stdint.h>

View file

@ -1,3 +1,21 @@
/// @file RspDuo.h
/// @class RspDuo
/// @brief A class to capture data on the SDRplay RspDuo.
/// @details Loosely based upon the sdrplay_api_sample_app.c and sdr_play.c examples
/// provided in the SDRplay API V3 documentation.
/// This should be read in conjuction with that documentation
///
/// For coherent operation the use of sdrplay_api_Tuner_Both is most important
/// This clue was provided by Gustaw Mazurek of WUT
/// <https://github.com/fventuri/gr-sdrplay/issues/2>
/// <https://github.com/g4eev/RSPduoEME/blob/main/rspduointerface.cpp>
///
/// Reference for using C style callback API with a C++ wrapper:
/// <https://stackoverflow.com/questions/63768893/pointer-problem-using-functions-from-non-object-api-in-objects?rq=1>
/// @author 30hours
/// @author Michael P
/// @todo Remove max time.
#ifndef RSPDUO_H
#define RSPDUO_H
@ -6,76 +24,212 @@
#include <string>
#include <IqData.h>
#define BUFFER_SIZE_NR 1024 /* standard size of buffers */
// https://stackoverflow.com/questions/63768893/pointer-problem-using-functions-from-non-object-api-in-objects?rq=1
#define BUFFER_SIZE_NR 1024
class RspDuo
{
private:
uint32_t fc; // frequency (Hz)
int chunk_time_nr; // chunk time of recording (s)
int agc_bandwidth_nr; // agc bandwidth (Hz)
int agc_set_point_nr; // agc set point (dBfs)
int gain_reduction_nr; // gain reduction (dB)
int lna_state_nr; // lna state
int nDecimation; // decimation factor
bool rf_notch_fg; // MW and FM notch filters
bool dab_notch_fg; // DAB notch filter
bool usb_bulk_fg; // usb bulk transfer mode
bool small_verbose_fg; // debugging
bool more_verbose_fg; // debugging
std::string path; // file path
bool capture; // flag to capture
static const double MAX_FREQUENCY_NR;
static const uint8_t DEF_DECIMATION_NR;
static const int DEF_WAIT_TIME_NR; // default wait time before recording
static const int DEF_CHUNK_TIME_NR; // default chunk time of recording
static const int MAX_RUN_TIME_NR; // max run time of recording
static const int DEF_AGC_BANDWIDTH_NR; // default agc bandwidth
static const int MIN_AGC_SET_POINT_NR; // min agc set point
static const int DEF_AGC_SET_POINT_NR; // default agc set point
static const int MIN_GAIN_REDUCTION_NR; // min gain reduction
static const int DEF_GAIN_REDUCTION_NR; // default gain reduction
static const int MAX_GAIN_REDUCTION_NR; // max gain reduction
static const int DEF_LNA_STATE_NR; // default lna state
static const int MAX_LNA_STATE_NR; // max lna state
static const int DEF_SAMPLE_FREQUENCY_NR; // default sample frequency
static const int DEF_SAMPLE_RATE_NR; // default sample rate
/// @brief Center frequency (Hz)
uint32_t fc;
/// @brief chunk time of recording (s)
int chunk_time_nr;
/// @brief AGC bandwidth (Hz)
int agc_bandwidth_nr;
/// @brief AGC set point (dBfs)
int agc_set_point_nr;
/// @brief Gain reduction (dB).
int gain_reduction_nr;
/// @brief LNA state
int lna_state_nr;
/// @brief Decimation factor (integer).
int nDecimation;
/// @brief MW and FM notch filters.
bool rf_notch_fg;
/// @brief DAB notch filter.
bool dab_notch_fg;
/// @brief USB bulk transfer mode.
bool usb_bulk_fg;
/// @brief Debugging.
bool small_verbose_fg;
/// @brief Debugging.
bool more_verbose_fg;
/// @brief File path.
std::string path;
/// @brief True if capture is enabled.
bool capture;
/// @brief Maximum frequency (Hz).
static const double MAX_FREQUENCY_NR;
/// @brief Default decimation.
static const uint8_t DEF_DECIMATION_NR;
/// @brief Default wait time before recording.
static const int DEF_WAIT_TIME_NR;
/// @brief Default chunk time of recording.
static const int DEF_CHUNK_TIME_NR;
/// @brief Maximum run time of recording.
static const int MAX_RUN_TIME_NR;
/// @brief Default AGC bandwidth.
static const int DEF_AGC_BANDWIDTH_NR;
/// @brief Minimum AGC set point.
static const int MIN_AGC_SET_POINT_NR;
/// @brief Default AGC set point.
static const int DEF_AGC_SET_POINT_NR;
/// @brief Minimum gain reduction.
static const int MIN_GAIN_REDUCTION_NR;
/// @brief Default gain reduction.
static const int DEF_GAIN_REDUCTION_NR;
/// @brief Maximum gain reduction.
static const int MAX_GAIN_REDUCTION_NR;
/// @brief Default LNA state.
static const int DEF_LNA_STATE_NR;
/// @brief Max LNA state.
static const int MAX_LNA_STATE_NR;
/// @brief Default sample frequency.
static const int DEF_SAMPLE_FREQUENCY_NR;
/// @brief Default sample rate.
static const int DEF_SAMPLE_RATE_NR;
/// @brief Check parameters for valid for capture device.
/// @return The object.
void validate();
/// @brief Start API functions.
/// @return The object.
void open_api();
/// @brief Device selection function.
/// @return The object.
void get_device();
/// @brief Set device parameters.
/// @return The object.
void set_device_parameters();
/// @brief Wrapper for C style callback function for stream_a_callback().
/// @param xi Pointer to real part of sample.
/// @param xq Pointer to imag part of sample.
/// @param params As defined in SDRplay API.
/// @param numSamples Number of samples in block.
/// @param reset As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
static void _stream_a_callback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext)
{
static_cast<RspDuo *>(cbContext)->stream_a_callback(xi, xq, params, numSamples, reset, cbContext);
};
/// @brief Wrapper for C style callback function for stream_b_callback().
/// @param xi Pointer to real part of sample.
/// @param xq Pointer to imag part of sample.
/// @param params As defined in SDRplay API.
/// @param numSamples Number of samples in block.
/// @param reset As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
static void _stream_b_callback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext)
{
static_cast<RspDuo *>(cbContext)->stream_b_callback(xi, xq, params, numSamples, reset, cbContext);
};
/// @brief Wrapper for C style callback function for event_callback().
/// @param eventId As defined in SDRplay API.
/// @param tuner As defined in SDRplay API.
/// @param params As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
static void _event_callback(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext)
{
static_cast<RspDuo *>(cbContext)->event_callback(eventId, tuner, params, cbContext);
};
/// @brief Tuner a callback as defined in SDRplay API.
/// @param xi Pointer to real part of sample.
/// @param xq Pointer to imag part of sample.
/// @param params As defined in SDRplay API.
/// @param numSamples Number of samples in block.
/// @param reset As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
void stream_a_callback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
/// @brief Tuner b callback as defined in SDRplay API.
/// @param xi Pointer to real part of sample.
/// @param xq Pointer to imag part of sample.
/// @param params As defined in SDRplay API.
/// @param numSamples Number of samples in block.
/// @param reset As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
void stream_b_callback(short *xi, short *xq, sdrplay_api_StreamCbParamsT *params, unsigned int numSamples, unsigned int reset, void *cbContext);
/// @brief Event callback function as defined in SDRplay API.
/// @param eventId As defined in SDRplay API.
/// @param tuner As defined in SDRplay API.
/// @param params As defined in SDRplay API.
/// @param cbContext As defined in SDRplay API.
/// @return Void.
void event_callback(sdrplay_api_EventT eventId, sdrplay_api_TunerSelectT tuner, sdrplay_api_EventParamsT *params, void *cbContext);
/// @brief Start running capture callback function.
/// @return Void.
void initialise_device();
/// @brief Stop running capture callback function.
/// @return Void.
void uninitialise_device();
/// @brief Internal method to gracefully stop capture..
/// @return Void.
void finish();
public:
/// @brief Constructor.
/// @param fc Center frequency (Hz).
/// @param path Path to save IQ data.
/// @return The object.
RspDuo(uint32_t fc, std::string path);
/// @brief Get file name from path.
/// @return String of file name based on current time.
std::string set_file(std::string path);
/// @brief Call methods to start capture.
/// @return Void.
void start();
/// @brief Call methods to gracefully stop capture.
/// @return Void.
void stop();
/// @brief Implement capture function on RSPduo.
/// @param buffer1 Pointer to reference buffer.
/// @param buffer2 Pointer to surveillance buffer.
/// @return Void.
void process(IqData *buffer1, IqData *buffer2);
/// @brief Implement replay function on RSPduo.
/// @param buffer1 Pointer to reference buffer.
/// @param buffer2 Pointer to surveillance buffer.
/// @param file Path to file to replay data from.
/// @param loop True if samples should loop at EOF.
/// @return Void.
void replay(IqData *buffer1, IqData *buffer2, std::string file, bool loop);
/// @brief Open a new file to record IQ.
/// @return Void.
void open_file();
/// @brief Close IQ file gracefully.
/// @return Void.
void close_file();
/// @brief Setter for capture.
/// @param capture True if capture is enabled.
/// @return Void.
void set_capture(bool capture);
/// @brief Getter for capture.
/// @return True if capture is true.
bool get_capture();
};

View file

@ -1,3 +1,9 @@
/// @file IqData.h
/// @class IqData
/// @brief A class to store IQ data.
/// @details Implements a FIFO queue to store IQ samples.
/// @author 30hours
#ifndef IQDATA_H
#define IQDATA_H
@ -8,20 +14,57 @@
class IqData
{
private:
/// @brief Maximum number of samples.
uint32_t n;
/// @brief True if should not push to buffer (mutex).
bool doNotPush;
/// @brief Pointer to IQ data.
std::deque<std::complex<double>> *data;
public:
/// @brief Constructor.
/// @param n Number of samples.
/// @return The object.
IqData(uint32_t n);
/// @brief Getter for maximum number of samples.
/// @return Maximum number of samples.
uint32_t get_n();
/// @brief Getter for current data length.
/// @return Number of samples currently in data.
uint32_t get_length();
/// @brief Setter for mutex.
/// @param doNotPush True if should not push to buffer (mutex).
/// @return Void.
void set_doNotPush(bool doNotPush);
/// @brief Getter for mutex.
/// @return True if should not push to buffer (mutex).
bool get_doNotPush();
/// @brief Getter for data.
/// @return IQ data.
std::deque<std::complex<double>> get_data();
/// @brief Push a sample to the queue.
/// @param sample A single sample.
/// @return Void.
void push_back(std::complex<double> sample);
/// @brief Pop the front of the queue.
/// @return Sample from the front of the queue.
std::complex<double> pop_front();
/// @brief Print to stdout (debug).
/// @return Void.
void print();
/// @brief Clear samples from the queue.
/// @return Void.
void clear();
};

View file

@ -8,9 +8,6 @@
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filewritestream.h"
// References:
// - https://stackoverflow.com/questions/39110263/append-to-an-existing-array-of-json-objects-on-file-using-rapidjson
// constructor
template <class T>
Map<T>::Map(uint32_t _nRows, uint32_t _nCols)

View file

@ -1,3 +1,12 @@
/// @file Map.h
/// @class Map
/// @brief A class to store an ambiguity map.
/// @details References:
/// <a href="https://stackoverflow.com/questions/39110263/append-to-an-existing-array-of-json-objects-on-file-using-rapidjson">
/// Append to an existing array using rapidjson.</a>
/// @author 30hours
/// @todo Fix memory leak in get_map_db().
#ifndef MAP_H
#define MAP_H
@ -11,26 +20,84 @@ template <typename T>
class Map
{
private:
/// @brief Number of rows.
uint32_t nRows;
/// @brief Number of columns.
uint32_t nCols;
public:
/// @brief Map data to store.
std::vector<std::vector<T>> data;
/// @brief Delay units of map data (bins).
std::deque<int> delay;
/// @brief Doppler units of map data (Hz).
std::deque<double> doppler;
/// @brief Noise power of map (dB).
double noisePower;
/// @brief Dynamic range of map (dB).
double maxPower;
/// @brief Constructor.
/// @param nRows Number of rows.
/// @param nCols Number of columns.
/// @return The object.
Map(uint32_t nRows, uint32_t nCols);
/// @brief Update a row in the 2D map.
/// @param i Index of row to update.
/// @param row Data to update.
/// @return Void.
void set_row(uint32_t i, std::vector<T> row);
/// @brief Update a column in the 2D map.
/// @param i Index of column to update.
/// @param col Data to update.
/// @return Void.
void set_col(uint32_t i, std::vector<T> col);
/// @brief Create map metrics (noise power, dynamic range).
/// @return Void.
void set_metrics();
/// @brief Get the number of rows in the map.
/// @return Number of rows.
uint32_t get_nRows();
/// @brief Get the number of columns in the map.
/// @return Number of columns.
uint32_t get_nCols();
/// @brief Get a row from the 2D map.
/// @param row Index of row to get.
/// @return Vector of data.
std::vector<T> get_row(uint32_t row);
/// @brief Get a column from the 2D map.
/// @param col Index of column to get.
/// @return Vector of data.
std::vector<T> get_col(uint32_t col);
/// @brief Get a copy of the map in dB units.
/// @return Pointer to dB map.
Map<double> *get_map_db();
/// @brief Print the map to stdout (for debugging).
/// @return Void.
void print();
/// @brief Generate JSON of the map and metadata.
/// @return JSON string.
std::string to_json();
/// @brief Append the map to a save file.
/// @param json JSON string of map and metadata.
/// @param path Path of file to save.
/// @return True is save is successful.
bool save(std::string json, std::string path);
};

View file

@ -1,3 +1,11 @@
/// @file Ambiguity.h
/// @class Ambiguity
/// @brief A class to implement a ambiguity map processing.
/// @details Implements a the batches algorithm as described in Principles of Modern Radar, Volume II, Chapter 17.
/// See Fundamentals of Radar Signal Processing (Richards) for more on the pulse-Doppler processing method.
/// @author 30hours
/// @todo Ambiguity maps are still offset by 1 bin.
#ifndef AMBIGUITY_H
#define AMBIGUITY_H
@ -9,30 +17,79 @@
class Ambiguity
{
private:
/// @brief Minimum delay (bins).
int32_t delayMin;
/// @brief Maximum delay (bins).
int32_t delayMax;
/// @brief Minimum Doppler (Hz).
int32_t dopplerMin;
/// @brief Maximum Doppler (Hz).
int32_t dopplerMax;
/// @brief Sampling frequency (Hz).
uint32_t fs;
/// @brief Number of samples.
uint32_t n;
/// @brief Center of Doppler bins (Hz).
double dopplerMiddle;
/// @brief Number of delay bins.
uint16_t nDelayBins;
/// @brief Number of Doppler bins.
uint16_t nDopplerBins;
/// @brief Number of correlation samples per pulse.
uint16_t nCorr;
/// @brief True CPI time (s).
double cpi;
/// @brief FFTW plans for ambiguity processing.
/// @{
fftw_plan fftXi, fftYi, fftZi, fftDoppler;
/// @}
/// @brief FFTW storage for ambiguity processing.
/// @{
std::complex<double> *dataXi, *dataYi, *dataZi, *dataCorr, *dataDoppler;
/// @}
/// @brief Number of samples to perform FFT per pulse.
uint32_t nfft;
/// @brief Vector storage for ambiguity processing
/// @{
std::vector<std::complex<double>> corr, delayProfile;
/// @}
/// @brief Pointer to map to store result.
Map<std::complex<double>> *map;
public:
/// @brief Constructor.
/// @param delayMin Minimum delay (bins).
/// @param delayMax Maximum delay (bins).
/// @param dopplerMin Minimum Doppler (Hz).
/// @param dopplerMax Maximum Doppler (Hz).
/// @param fs Sampling frequency (Hz).
/// @param n Number of samples.
/// @return The object.
Ambiguity(int32_t delayMin, int32_t delayMax, int32_t dopplerMin, int32_t dopplerMax, uint32_t fs, uint32_t n);
/// @brief Destructor.
/// @return Void.
~Ambiguity();
/// @brief Implement the ambiguity processor.
/// @param x Reference samples.
/// @param y Surveillance samples.
/// @return Ambiguity map data of IQ samples.
Map<std::complex<double>> *process(IqData *x, IqData *y);
};

View file

@ -1,3 +1,11 @@
/// @file WienerHopf.h
/// @class WienerHopf
/// @brief A class to implement a Wiener-Hopf clutter filter.
/// @details Implements a <a href="https://en.wikipedia.org/wiki/Wiener_filter#Finite_impulse_response_Wiener_filter_for_discrete_series">Wiener-Hopf filter</a>.
/// Uses <a href="https://en.wikipedia.org/wiki/Cholesky_decomposition">Cholesky decomposition</a> to speed up matrix inversion, as the Toeplitz matrix is positive-definite and Hermitian.
/// @author 30hours
/// @todo Fix the segmentation fault from clutter filter numerical instability.
#ifndef WIENERHOPF_H
#define WIENERHOPF_H
@ -9,23 +17,64 @@
class WienerHopf
{
private:
/// @brief Minimum clutter filter delay (bins).
int32_t delayMin;
/// @brief Maximum clutter filter delay (bins).
int32_t delayMax;
/// @brief Number of bins (delayMax - delayMin + 1).
uint32_t nBins;
/// @brief Number of samples per CPI.
uint32_t nSamples;
/// @brief True if clutter filter processing is successful.
bool success;
/// @brief FFTW plans for clutter filter processing.
/// @{
fftw_plan fftX, fftY, fftA, fftB, fftFiltX, fftFiltW, fftFilt;
std::complex<double> *dataX, *dataY, *dataOutX, *dataOutY, *dataA, *dataB, *filtX, *filtW, *filt;
std::deque<std::complex<double>> xData, yData;
/// @}
/// @brief FFTW storage for clutter filter processing.
/// @{
std::complex<double> *dataX, *dataY, *dataOutX, *dataOutY, *dataA, *dataB, *filtX, *filtW, *filt;
/// @}
/// @brief Deque storage for clutter filter processing.
/// @{
std::deque<std::complex<double>> xData, yData;
/// @}
/// @brief Autocorrelation toeplitz matrix.
arma::cx_mat A;
arma::cx_vec a, b, w;
/// @brief Autocorrelation vector.
arma::cx_vec a;
/// @brief Cross-correlation vector.
arma::cx_vec b;
/// @brief Weights vector.
arma::cx_vec w;
public:
/// @brief Constructor.
/// @param delayMin Minimum clutter filter delay (bins).
/// @param delayMax Maximum clutter filter delay (bins).
/// @param nSamples Number of samples per CPI.
/// @return The object.
WienerHopf(int32_t delayMin, int32_t delayMax, uint32_t nSamples);
/// @brief Destructor.
/// @return Void.
~WienerHopf();
/// @brief Implement the clutter filter.
/// @param x Reference samples.
/// @param y Surveillance samples.
/// @return True if clutter filter successful.
bool process(IqData *x, IqData *y);
};