mirror of
https://github.com/30hours/blah2.git
synced 2024-11-18 12:33:58 +00:00
commit
8c30e3c34b
7 changed files with 312 additions and 5 deletions
|
@ -44,6 +44,7 @@ add_executable(blah2
|
||||||
src/capture/rspduo/RspDuo.cpp
|
src/capture/rspduo/RspDuo.cpp
|
||||||
src/capture/usrp/Usrp.cpp
|
src/capture/usrp/Usrp.cpp
|
||||||
src/capture/hackrf/HackRf.cpp
|
src/capture/hackrf/HackRf.cpp
|
||||||
|
src/capture/kraken/Kraken.cpp
|
||||||
src/process/ambiguity/Ambiguity.cpp
|
src/process/ambiguity/Ambiguity.cpp
|
||||||
src/process/clutter/WienerHopf.cpp
|
src/process/clutter/WienerHopf.cpp
|
||||||
src/process/detection/CfarDetector1D.cpp
|
src/process/detection/CfarDetector1D.cpp
|
||||||
|
@ -71,6 +72,7 @@ target_link_libraries(blah2 PRIVATE
|
||||||
fftw3_threads
|
fftw3_threads
|
||||||
sdrplay
|
sdrplay
|
||||||
hackrf
|
hackrf
|
||||||
|
rtlsdr
|
||||||
)
|
)
|
||||||
target_include_directories(blah2 PRIVATE RAPIDJSON_INCLUDE_DIRS "rapidjson/allocators.h")
|
target_include_directories(blah2 PRIVATE RAPIDJSON_INCLUDE_DIRS "rapidjson/allocators.h")
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y software-properties-common \
|
||||||
libfftw3-dev pkg-config gfortran libhackrf-dev \
|
libfftw3-dev pkg-config gfortran libhackrf-dev \
|
||||||
libuhd-dev=4.6.0.0-0ubuntu1~jammy1 \
|
libuhd-dev=4.6.0.0-0ubuntu1~jammy1 \
|
||||||
uhd-host=4.6.0.0-0ubuntu1~jammy1 \
|
uhd-host=4.6.0.0-0ubuntu1~jammy1 \
|
||||||
|
libusb-dev libusb-1.0.0-dev \
|
||||||
&& apt-get autoremove -y \
|
&& apt-get autoremove -y \
|
||||||
&& apt-get clean -y \
|
&& apt-get clean -y \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
@ -35,6 +36,11 @@ RUN chmod +x /blah2/lib/sdrplay-3.14.0/SDRplay_RSP_API-Linux-3.14.0.run \
|
||||||
# install UHD API
|
# install UHD API
|
||||||
RUN uhd_images_downloader
|
RUN uhd_images_downloader
|
||||||
|
|
||||||
|
# install RTL-SDR API
|
||||||
|
RUN git clone https://github.com/krakenrf/librtlsdr /opt/librtlsdr \
|
||||||
|
&& cd /opt/librtlsdr && mkdir build && cd build \
|
||||||
|
&& cmake ../ -DINSTALL_UDEV_RULES=ON && make && make install && ldconfig
|
||||||
|
|
||||||
FROM blah2_env as blah2
|
FROM blah2_env as blah2
|
||||||
LABEL maintainer="30hours <nathan@30hours.dev>"
|
LABEL maintainer="30hours <nathan@30hours.dev>"
|
||||||
|
|
||||||
|
|
84
config/config-kraken.yml
Normal file
84
config/config-kraken.yml
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
capture:
|
||||||
|
fs: 2000000
|
||||||
|
fc: 204640000
|
||||||
|
device:
|
||||||
|
type: "Kraken"
|
||||||
|
gain: [15.0, 15.0]
|
||||||
|
array:
|
||||||
|
x: [0, 0]
|
||||||
|
y: [0, 0]
|
||||||
|
z: [0, 0]
|
||||||
|
boresight: 0.0
|
||||||
|
replay:
|
||||||
|
state: false
|
||||||
|
loop: true
|
||||||
|
file: '/opt/blah2/replay/file.kraken'
|
||||||
|
|
||||||
|
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
|
||||||
|
config: 4003
|
||||||
|
|
||||||
|
truth:
|
||||||
|
adsb:
|
||||||
|
enabled: false
|
||||||
|
tar1090: 'adsb.30hours.dev'
|
||||||
|
adsb2dd: 'adsb2dd.30hours.dev'
|
||||||
|
ais:
|
||||||
|
enabled: false
|
||||||
|
ip: 0.0.0.0
|
||||||
|
port: 30001
|
||||||
|
|
||||||
|
location:
|
||||||
|
rx:
|
||||||
|
latitude: -34.9286
|
||||||
|
longitude: 138.5999
|
||||||
|
altitude: 50
|
||||||
|
name: "Adelaide"
|
||||||
|
tx:
|
||||||
|
latitude: -34.9810
|
||||||
|
longitude: 138.7081
|
||||||
|
altitude: 750
|
||||||
|
name: "Mount Lofty"
|
||||||
|
|
||||||
|
save:
|
||||||
|
iq: true
|
||||||
|
map: false
|
||||||
|
detection: false
|
||||||
|
timing: false
|
||||||
|
path: "/blah2/save/"
|
|
@ -2,12 +2,13 @@
|
||||||
#include "rspduo/RspDuo.h"
|
#include "rspduo/RspDuo.h"
|
||||||
#include "usrp/Usrp.h"
|
#include "usrp/Usrp.h"
|
||||||
#include "hackrf/HackRf.h"
|
#include "hackrf/HackRf.h"
|
||||||
|
#include "kraken/Kraken.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <httplib.h>
|
#include <httplib.h>
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
const std::string Capture::VALID_TYPE[3] = {"RspDuo", "Usrp", "HackRF"};
|
const std::string Capture::VALID_TYPE[4] = {"RspDuo", "Usrp", "HackRF", "Kraken"};
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _path)
|
Capture::Capture(std::string _type, uint32_t _fs, uint32_t _fc, std::string _path)
|
||||||
|
@ -66,10 +67,12 @@ void Capture::process(IqData *buffer1, IqData *buffer2, c4::yml::NodeRef config,
|
||||||
|
|
||||||
std::unique_ptr<Source> Capture::factory_source(const std::string& type, c4::yml::NodeRef config)
|
std::unique_ptr<Source> Capture::factory_source(const std::string& type, c4::yml::NodeRef config)
|
||||||
{
|
{
|
||||||
|
// SDRplay RSPduo
|
||||||
if (type == VALID_TYPE[0])
|
if (type == VALID_TYPE[0])
|
||||||
{
|
{
|
||||||
return std::make_unique<RspDuo>(type, fc, fs, path, &saveIq);
|
return std::make_unique<RspDuo>(type, fc, fs, path, &saveIq);
|
||||||
}
|
}
|
||||||
|
// Usrp
|
||||||
else if (type == VALID_TYPE[1])
|
else if (type == VALID_TYPE[1])
|
||||||
{
|
{
|
||||||
std::string address, subdev;
|
std::string address, subdev;
|
||||||
|
@ -87,10 +90,10 @@ std::unique_ptr<Source> Capture::factory_source(const std::string& type, c4::yml
|
||||||
gain.push_back(_gain);
|
gain.push_back(_gain);
|
||||||
config["gain"][1] >> _gain;
|
config["gain"][1] >> _gain;
|
||||||
gain.push_back(_gain);
|
gain.push_back(_gain);
|
||||||
|
|
||||||
return std::make_unique<Usrp>(type, fc, fs, path, &saveIq,
|
return std::make_unique<Usrp>(type, fc, fs, path, &saveIq,
|
||||||
address, subdev, antenna, gain);
|
address, subdev, antenna, gain);
|
||||||
}
|
}
|
||||||
|
// HackRF
|
||||||
else if (type == VALID_TYPE[2])
|
else if (type == VALID_TYPE[2])
|
||||||
{
|
{
|
||||||
std::vector<std::string> serial;
|
std::vector<std::string> serial;
|
||||||
|
@ -120,11 +123,22 @@ std::unique_ptr<Source> Capture::factory_source(const std::string& type, c4::yml
|
||||||
ampEnable.push_back(_ampEnable);
|
ampEnable.push_back(_ampEnable);
|
||||||
config["amp_enable"][1] >> _ampEnable;
|
config["amp_enable"][1] >> _ampEnable;
|
||||||
ampEnable.push_back(_ampEnable);
|
ampEnable.push_back(_ampEnable);
|
||||||
|
|
||||||
return std::make_unique<HackRf>(type, fc, fs, path, &saveIq,
|
return std::make_unique<HackRf>(type, fc, fs, path, &saveIq,
|
||||||
serial, gainLna, gainVga, ampEnable);
|
serial, gainLna, gainVga, ampEnable);
|
||||||
}
|
}
|
||||||
// Handle unknown type
|
// Kraken
|
||||||
|
else if (type == VALID_TYPE[3])
|
||||||
|
{
|
||||||
|
std::vector<double> gain;
|
||||||
|
float _gain;
|
||||||
|
for (auto child : config["gain"].children())
|
||||||
|
{
|
||||||
|
c4::atof(child.val(), &_gain);
|
||||||
|
gain.push_back(static_cast<double>(_gain));
|
||||||
|
}
|
||||||
|
return std::make_unique<Kraken>(type, fc, fs, path, &saveIq, gain);
|
||||||
|
}
|
||||||
|
// handle unknown type
|
||||||
std::cerr << "Error: Source type does not exist." << std::endl;
|
std::cerr << "Error: Source type does not exist." << std::endl;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define CAPTURE_H
|
#define CAPTURE_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ryml/ryml.hpp>
|
#include <ryml/ryml.hpp>
|
||||||
#include <ryml/ryml_std.hpp> // optional header, provided for std:: interop
|
#include <ryml/ryml_std.hpp> // optional header, provided for std:: interop
|
||||||
|
@ -19,7 +20,7 @@ class Capture
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/// @brief The valid capture devices.
|
/// @brief The valid capture devices.
|
||||||
static const std::string VALID_TYPE[3];
|
static const std::string VALID_TYPE[4];
|
||||||
|
|
||||||
/// @brief The capture device type.
|
/// @brief The capture device type.
|
||||||
std::string type;
|
std::string type;
|
||||||
|
|
116
src/capture/kraken/Kraken.cpp
Normal file
116
src/capture/kraken/Kraken.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#include "Kraken.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <complex>
|
||||||
|
#include <thread>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Kraken::Kraken(std::string _type, uint32_t _fc, uint32_t _fs,
|
||||||
|
std::string _path, bool *_saveIq, std::vector<double> _gain)
|
||||||
|
: Source(_type, _fc, _fs, _path, _saveIq)
|
||||||
|
{
|
||||||
|
// convert gain to tenths of dB
|
||||||
|
for (int i = 0; i <= _gain.size(); i++)
|
||||||
|
{
|
||||||
|
gain.push_back(static_cast<int>(_gain[i]*10));
|
||||||
|
channelIndex.push_back(i);
|
||||||
|
}
|
||||||
|
std::vector<rtlsdr_dev_t*> devs(channelIndex.size());
|
||||||
|
|
||||||
|
// store all valid gains
|
||||||
|
std::vector<int> validGains;
|
||||||
|
int nGains;
|
||||||
|
nGains = rtlsdr_get_tuner_gains(devs[0], nullptr);
|
||||||
|
check_status(nGains, "Failed to get number of gains.");
|
||||||
|
std::unique_ptr<int[]> _validGains(new int[nGains]);
|
||||||
|
int status = rtlsdr_get_tuner_gains(devs[0], _validGains.get());
|
||||||
|
check_status(status, "Failed to get number of gains.");
|
||||||
|
validGains.assign(_validGains.get(), _validGains.get() + nGains);
|
||||||
|
|
||||||
|
// update gains to next value if invalid
|
||||||
|
for (int i = 0; i <= _gain.size(); i++)
|
||||||
|
{
|
||||||
|
int adjustedGain = static_cast<int>(_gain[i] * 10);
|
||||||
|
auto it = std::lower_bound(validGains.begin(),
|
||||||
|
validGains.end(), adjustedGain);
|
||||||
|
if (it != validGains.end()) {
|
||||||
|
gain.push_back(*it);
|
||||||
|
} else {
|
||||||
|
gain.push_back(validGains.back());
|
||||||
|
}
|
||||||
|
std::cout << "[Kraken] Gain update on channel " << i << " from " <<
|
||||||
|
adjustedGain << " to " << gain[i] << "." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::start()
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
for (size_t i = 0; i < channelIndex.size(); i++)
|
||||||
|
{
|
||||||
|
status = rtlsdr_open(&devs[i], channelIndex[i]);
|
||||||
|
check_status(status, "Failed to open device.");
|
||||||
|
status = rtlsdr_set_center_freq(devs[i], fc);
|
||||||
|
check_status(status, "Failed to set center frequency.");
|
||||||
|
status = rtlsdr_set_sample_rate(devs[i], fs);
|
||||||
|
check_status(status, "Failed to set sample rate.");
|
||||||
|
status = rtlsdr_set_dithering(devs[i], 0); // disable dither
|
||||||
|
check_status(status, "Failed to disable dithering.");
|
||||||
|
status = rtlsdr_set_tuner_gain_mode(devs[i], 1); // disable AGC
|
||||||
|
check_status(status, "Failed to disable AGC.");
|
||||||
|
status = rtlsdr_set_tuner_gain(devs[i], gain[i]);
|
||||||
|
check_status(status, "Failed to set gain.");
|
||||||
|
status = rtlsdr_reset_buffer(devs[i]);
|
||||||
|
check_status(status, "Failed to reset buffer.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::stop()
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
for (size_t i = 0; i < channelIndex.size(); i++)
|
||||||
|
{
|
||||||
|
status = rtlsdr_cancel_async(devs[i]);
|
||||||
|
check_status(status, "Failed to stop async read.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::process(IqData *buffer1, IqData *buffer2)
|
||||||
|
{
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for (size_t i = 0; i < channelIndex.size(); i++)
|
||||||
|
{
|
||||||
|
threads.emplace_back(rtlsdr_read_async, devs[i], callback, &channelIndex, 0, 16 * 16384);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join threads
|
||||||
|
for (auto& thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::callback(unsigned char *buf, uint32_t len, void *ctx)
|
||||||
|
{
|
||||||
|
int deviceIndex = *reinterpret_cast<int*>(ctx);
|
||||||
|
// buffers[i]->lock();
|
||||||
|
// for (size_t j = 0; j < n_read; j++)
|
||||||
|
// {
|
||||||
|
// buffers[i]->push_back({buffer[j].real(), buffer[j].imag()});
|
||||||
|
// }
|
||||||
|
// buffers[i]->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::replay(IqData *buffer1, IqData *buffer2, std::string _file, bool _loop)
|
||||||
|
{
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kraken::check_status(int status, std::string message)
|
||||||
|
{
|
||||||
|
if (status < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("[Kraken] " + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
84
src/capture/kraken/Kraken.h
Normal file
84
src/capture/kraken/Kraken.h
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/// @file Kraken.h
|
||||||
|
/// @class Kraken
|
||||||
|
/// @brief A class to capture data on the Kraken SDR.
|
||||||
|
/// @details Uses a custom librtlsdr API to extract samples.
|
||||||
|
/// Uses 2 channels of the Kraken to capture IQ data.
|
||||||
|
/// The noise source phase synchronisation is not required for 2 channel operation.
|
||||||
|
/// Future work is to replicate the Heimdall DAQ phase syncronisation.
|
||||||
|
/// This will enable a surveillance array of up to 4 antenna elements.
|
||||||
|
/// Requires a custom librtlsdr which includes method rtlsdr_set_dithering().
|
||||||
|
/// The original steve-m/librtlsdr does not include this method.
|
||||||
|
/// This is included in librtlsdr/librtlsdr or krakenrf/librtlsdr.
|
||||||
|
/// @author 30hours
|
||||||
|
/// @todo Add support for multiple surveillance channels.
|
||||||
|
/// @todo Replay support.
|
||||||
|
|
||||||
|
#ifndef KRAKEN_H
|
||||||
|
#define KRAKEN_H
|
||||||
|
|
||||||
|
#include "capture/Source.h"
|
||||||
|
#include "data/IqData.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <rtl-sdr.h>
|
||||||
|
|
||||||
|
class Kraken : public Source
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Individual RTL-SDR devices.
|
||||||
|
std::vector<rtlsdr_dev_t*> devs;
|
||||||
|
|
||||||
|
/// @brief Device indices for Kraken.
|
||||||
|
std::vector<int> channelIndex;
|
||||||
|
|
||||||
|
/// @brief Gain for each channel.
|
||||||
|
std::vector<int> gain;
|
||||||
|
|
||||||
|
/// @brief Check status of API returns.
|
||||||
|
/// @param status Return code of API call.
|
||||||
|
/// @param message Message if API call error.
|
||||||
|
/// @return Void.
|
||||||
|
void check_status(int status, std::string message);
|
||||||
|
|
||||||
|
/// @brief Callback function when buffer is filled.
|
||||||
|
/// @param buf Pointer to buffer of IQ data.
|
||||||
|
/// @param len Length of buffer.
|
||||||
|
/// @param ctx Context data for callback.
|
||||||
|
/// @return Void.
|
||||||
|
static void callback(unsigned char *buf, uint32_t len, void *ctx);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor.
|
||||||
|
/// @param fc Center frequency (Hz).
|
||||||
|
/// @param path Path to save IQ data.
|
||||||
|
/// @return The object.
|
||||||
|
Kraken(std::string type, uint32_t fc, uint32_t fs, std::string path,
|
||||||
|
bool *saveIq, std::vector<double> gain);
|
||||||
|
|
||||||
|
/// @brief Implement capture function on KrakenSDR.
|
||||||
|
/// @param buffer Pointers to buffers for each channel.
|
||||||
|
/// @return Void.
|
||||||
|
void process(IqData *buffer1, IqData *buffer2);
|
||||||
|
|
||||||
|
/// @brief Call methods to start capture.
|
||||||
|
/// @return Void.
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/// @brief Call methods to gracefully stop capture.
|
||||||
|
/// @return Void.
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/// @brief Implement replay function on the Kraken.
|
||||||
|
/// @param buffers Pointers to buffers for each channel.
|
||||||
|
/// @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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue