Initial Kraken 2 channel, not working yet

This commit is contained in:
30hours 2024-05-11 14:03:26 +00:00
parent dd339533ce
commit 3b670a740f
7 changed files with 307 additions and 5 deletions

View file

@ -44,6 +44,7 @@ add_executable(blah2
src/capture/rspduo/RspDuo.cpp
src/capture/usrp/Usrp.cpp
src/capture/hackrf/HackRf.cpp
src/capture/kraken/Kraken.cpp
src/process/ambiguity/Ambiguity.cpp
src/process/clutter/WienerHopf.cpp
src/process/detection/CfarDetector1D.cpp
@ -71,6 +72,7 @@ target_link_libraries(blah2 PRIVATE
fftw3_threads
sdrplay
hackrf
rtlsdr
)
target_include_directories(blah2 PRIVATE RAPIDJSON_INCLUDE_DIRS "rapidjson/allocators.h")

View file

@ -12,6 +12,7 @@ RUN apt-get update && apt-get install -y software-properties-common \
libfftw3-dev pkg-config gfortran libhackrf-dev \
libuhd-dev=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 clean -y \
&& 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
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
LABEL maintainer="30hours <nathan@30hours.dev>"

84
config/config-kraken.yml Normal file
View 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.hackrf'
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/"

View file

@ -2,12 +2,13 @@
#include "rspduo/RspDuo.h"
#include "usrp/Usrp.h"
#include "hackrf/HackRf.h"
#include "kraken/Kraken.h"
#include <iostream>
#include <thread>
#include <httplib.h>
// constants
const std::string Capture::VALID_TYPE[3] = {"RspDuo", "Usrp", "HackRF"};
const std::vector<std::string> Capture::VALID_TYPE = {"RspDuo", "Usrp", "HackRF", "Kraken"};
// constructor
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)
{
// SDRplay RSPduo
if (type == VALID_TYPE[0])
{
return std::make_unique<RspDuo>(type, fc, fs, path, &saveIq);
}
// Usrp
else if (type == VALID_TYPE[1])
{
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);
config["gain"][1] >> _gain;
gain.push_back(_gain);
return std::make_unique<Usrp>(type, fc, fs, path, &saveIq,
address, subdev, antenna, gain);
}
// HackRF
else if (type == VALID_TYPE[2])
{
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);
config["amp_enable"][1] >> _ampEnable;
ampEnable.push_back(_ampEnable);
return std::make_unique<HackRf>(type, fc, fs, path, &saveIq,
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;
return nullptr;
}

View file

@ -7,6 +7,7 @@
#define CAPTURE_H
#include <string>
#include <vector>
#include <memory>
#include <ryml/ryml.hpp>
#include <ryml/ryml_std.hpp> // optional header, provided for std:: interop
@ -19,7 +20,7 @@ class Capture
{
private:
/// @brief The valid capture devices.
static const std::string VALID_TYPE[3];
static const std::vector<std::string> VALID_TYPE;
/// @brief The capture device type.
std::string type;

View file

@ -0,0 +1,111 @@
#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);
gain.push_back(std::lower_bound(validGains.begin(),
validGains.end(), adjustedGain));
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);
}
}

View 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