Add skeleton for USRP and add include dir

This commit is contained in:
30hours 2024-01-13 03:21:55 +00:00
parent 37bdf731d4
commit 27d8994914
6 changed files with 378 additions and 23 deletions

View file

@ -8,13 +8,16 @@ include(CMakePrintHelpers)
include(CTest)
find_package(Threads REQUIRED)
find_package(asio CONFIG REQUIRED)
find_package(asio REQUIRED)
find_path(RAPIDJSON_INCLUDE_DIRS "rapidjson/allocators.h")
find_package(ryml CONFIG REQUIRED)
find_package(httplib CONFIG REQUIRED)
find_package(Armadillo CONFIG REQUIRED)
find_package(Catch2 CONFIG REQUIRED)
set(CMAKE_PREFIX_PATH "/opt/uhd" ${CMAKE_PREFIX_PATH})
find_package(UHD "4.6.0.0" CONFIG REQUIRED)
# TODO: when release CI is finished, don't use these dirs, install target should go to prod
SET (PROJECT_ROOT "${PROJECT_SOURCE_DIR}")
SET (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_ROOT}/bin")
@ -27,7 +30,7 @@ MESSAGE ("Binary path: ${PROJECT_BINARY_DIR}")
MESSAGE ("Binary test path: ${PROJECT_BINARY_TEST_DIR}")
# include from top-level src dir
include_directories(src)
include_directories(src ${UHD_INCLUDE_DIRS})
# TODO: create FindSdrplay.cmake for this
add_library(sdrplay /usr/local/include/sdrplay_api.h)
@ -38,6 +41,7 @@ add_executable(blah2
src/blah2.cpp
src/capture/Capture.cpp
src/capture/rspduo/RspDuo.cpp
src/capture/usrp/Usrp.cpp
src/process/ambiguity/Ambiguity.cpp
src/process/clutter/WienerHopf.cpp
src/process/detection/CfarDetector1D.cpp

64
config/radar4-usrp.yml Normal file
View file

@ -0,0 +1,64 @@
capture:
fs: 2000000
fc: 204640000
type: "Usrp"
replay:
state: false
loop: true
file: '/opt/blah2/replay/file.rspduo'
process:
data:
cpi: 0.5
buffer: 1.5
overlap: 0
ambiguity:
delayMin: -10
delayMax: 400
dopplerMin: -200
dopplerMax: 200
clutter:
delayMin: -10
delayMax: 400
detection:
pfa: 0.00001
nGuard: 2
nTrain: 6
minDelay: 5
minDoppler: 15
nCentroid: 6
tracker:
initiate:
M: 3
N: 5
maxAcc: 10
delete: 10
smooth: "none"
network:
ip: 0.0.0.0
ports:
api: 3000
map: 3001
detection: 3002
track: 3003
timestamp: 4000
timing: 4001
iqdata: 4002
truth:
asdb:
enabled: false
ip: 0.0.0.0
port: 30000
ais:
enabled: false
ip: 0.0.0.0
port: 30001
save:
iq: true
map: false
detection: false
timing: false
path: "/blah2/save/"

View file

@ -1,11 +1,12 @@
#include "Capture.h"
#include "rspduo/RspDuo.h"
#include "usrp/Usrp.h"
#include <iostream>
#include <thread>
#include <httplib.h>
// constants
const std::string Capture::VALID_TYPE[2] = {"RspDuo", "HackRF"};
const std::string Capture::VALID_TYPE[2] = {"RspDuo", "Usrp"};
// constructor
Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _path)
@ -19,11 +20,22 @@ Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _pat
void Capture::process(IqData *buffer1, IqData *buffer2)
{
// case RspDuo
if (type == Capture::VALID_TYPE[0])
if (is_type_valid(type))
{
std::cout << "Setting up device " + type << std::endl;
RspDuo *rspDuo = new RspDuo(this->fc, this->path);
// RspDuo device
if (type == VALID_TYPE[0])
{
RspDuo *device = new RspDuo(this->fc, this->path);
}
// Usrp device
if (type == VALID_TYPE[1])
{
//Usrp *device = new Usrp(this->fc, this->path);
}
Usrp *device = new Usrp(this->fc, this->path);
// capture status thread
std::thread t1([&]{
@ -33,16 +45,16 @@ void Capture::process(IqData *buffer1, IqData *buffer2)
httplib::Result res = cli.Get("/capture");
// if capture status changed
if ((res->body == "true") != rspDuo->get_capture())
if ((res->body == "true") != device->get_capture())
{
rspDuo->set_capture(res->body == "true");
if (rspDuo->get_capture())
device->set_capture(res->body == "true");
if (device->get_capture())
{
rspDuo->open_file();
device->open_file();
}
else
{
rspDuo->close_file();
device->close_file();
}
}
@ -52,23 +64,14 @@ void Capture::process(IqData *buffer1, IqData *buffer2)
if (!replay)
{
rspDuo->start();
rspDuo->process(buffer1, buffer2);
device->start();
device->process(buffer1, buffer2);
}
else
{
rspDuo->replay(buffer1, buffer2, file, loop);
device->replay(buffer1, buffer2, file, loop);
}
}
// case HackRF
else if (type == Capture::VALID_TYPE[1])
{
}
else
{
std::cout << "Error: Invalid capture device" << std::endl;
exit(1);
}
}
void Capture::set_replay(bool _loop, std::string _file)
@ -76,4 +79,19 @@ void Capture::set_replay(bool _loop, std::string _file)
replay = true;
loop = _loop;
file = _file;
}
bool Capture::is_type_valid(std::string _type)
{
size_t n = sizeof(Capture::VALID_TYPE) /
sizeof(Capture::VALID_TYPE[0]);
for (size_t i = 0; i < n; i++)
{
if (_type == Capture::VALID_TYPE[i])
{
return true;
}
}
std::cerr << "Invalid capture device: " << _type << std::endl;
return false;
}

View file

@ -59,6 +59,11 @@ public:
/// @param file Absolute path of file to replay.
/// @return Void.
void set_replay(bool loop, std::string file);
/// @brief Check if capture device type is valid.
/// @param type The capture device type.
/// @return True is capture device type is valid.
bool is_type_valid(std::string type);
};
#endif

181
src/capture/usrp/Usrp.cpp Normal file
View file

@ -0,0 +1,181 @@
#include "Usrp.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include <iostream>
#include <uhd/usrp/multi_usrp.hpp>
#include <complex>
// constructor
Usrp::Usrp(uint32_t _fc, std::string _path)
{
fc = _fc;
path = _path;
capture = false;
}
std::string Usrp::set_file(std::string path)
{
char buffer[15];
gettimeofday(&start_tm, NULL);
strftime(buffer, 16, "%Y%m%d-%H%M%S", localtime(&start_tm.tv_sec));
return path + buffer + ".usrp";
}
void Usrp::start()
{
}
void Usrp::stop()
{
}
void Usrp::process(IqData *buffer1, IqData *buffer2)
{
// create a USRP object
uhd::usrp::multi_usrp::sptr usrp =
uhd::usrp::multi_usrp::make("localhost");
// Set the sample rate
double sampleRate = 2e6; // Replace with your desired sample rate
usrp->set_rx_rate(sampleRate);
// Set the center frequency
double centerFrequency = (double)fc;
usrp->set_rx_freq(centerFrequency);
// Set the gain
double gain = 20.0; // Replace with your desired gain
usrp->set_rx_gain(gain);
// Set the number of channels
size_t numChannels = 1; // Assuming one channel for simplicity
usrp->set_rx_antenna("RX2", 0); // Set antenna for channel 0
// Set the receive buffer size
size_t bufferSize = 1024 * numChannels;
std::vector<std::complex<float>> buffer(bufferSize);
// Create a receive streamer
uhd::stream_args_t streamArgs("fc32", "sc16");
uhd::rx_streamer::sptr rxStreamer = usrp->get_rx_stream(streamArgs);
// Setup streaming
uhd::rx_metadata_t metadata;
rxStreamer->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
while(true)
{
// Receive samples
size_t numReceived = rxStreamer->recv(&buffer[0], buffer.size(), metadata);
// Stop streaming
// rxStreamer->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
// Check for errors
if (metadata.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
std::cerr << "Error during reception: " << metadata.strerror() << std::endl;
return;
}
// Copy received samples to the output buffer
//iqBuffer.resize(numReceived);
//std::copy(buffer.begin(), buffer.begin() + numReceived, iqBuffer.begin());
buffer1->lock();
buffer2->lock();
for (size_t i = 0; i < buffer.size(); i++)
{
buffer1->push_back({(double)buffer[i][0], (double)buffer[i][1]})
}
buffer1->unlock();
buffer2->unlock();
}
}
void Usrp::replay(IqData *_buffer1, IqData *_buffer2, std::string _file, bool _loop)
{
buffer1 = _buffer1;
buffer2 = _buffer2;
short i1, q1, i2, q2;
int rv;
file_replay = fopen(_file.c_str(), "rb");
while (true)
{
rv = fread(&i1, 1, sizeof(short), file_replay);
rv = fread(&q1, 1, sizeof(short), file_replay);
rv = fread(&i2, 1, sizeof(short), file_replay);
rv = fread(&q2, 1, sizeof(short), file_replay);
buffer1->lock();
buffer2->lock();
if (buffer1->get_length() < buffer1->get_n())
{
buffer1->push_back({(double)i1, (double)q1});
buffer2->push_back({(double)i2, (double)q2});
}
buffer1->unlock();
buffer2->unlock();
}
}
void Usrp::open_file()
{
file = set_file(path);
char time_tx[BUFFER_SIZE_NR];
// get start date and time
gettimeofday(&start_tm, NULL);
strftime(time_tx, sizeof(time_tx), "%d %b %Y %H:%M:%S", localtime(&start_tm.tv_sec));
fprintf(stderr, "Info - start - start_tm: %s.%03ld\n", time_tx, start_tm.tv_usec / 1000);
//validate();
// open files
if (chunk_time_nr == 0)
{
out_file_fp = fopen(file.c_str(), "wb");
if (out_file_fp == NULL)
{
fprintf(stderr, "Error - start - opening output file %s\n", file.c_str());
exit(1);
}
fprintf(stderr, "Info - start - output file %s\n", file.c_str());
}
return;
}
void Usrp::close_file()
{
if (out_file_fp != NULL)
{
fclose(out_file_fp);
}
}
void Usrp::set_capture(bool _capture)
{
capture = _capture;
}
bool Usrp::get_capture()
{
return capture;
}

83
src/capture/usrp/Usrp.h Normal file
View file

@ -0,0 +1,83 @@
/// @file Usrp.h
/// @class Usrp
/// @brief A class to capture data on the Ettus Research USRP.
/// @details Uses the UHD C API to extract samples into the processing chain.
///
/// Should work on all USRP models EXCEPT discontinued (USRP1, USRP2).
/// Networked models require an IP address in the config file.
///
/// @author 30hours
#ifndef USRP_H
#define USRP_H
#include "data/IqData.h"
#include <stdint.h>
#include <string>
#define BUFFER_SIZE_NR 1024
class Usrp
{
private:
/// @brief Center frequency (Hz)
uint32_t fc;
/// @brief File path.
std::string path;
/// @brief True if capture is enabled.
bool capture;
public:
/// @brief Constructor.
/// @param fc Center frequency (Hz).
/// @param path Path to save IQ data.
/// @return The object.
Usrp(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();
};
#endif