Skip to content

Commit

Permalink
Add support for HackRf
Browse files Browse the repository at this point in the history
  • Loading branch information
nqbit committed Jun 28, 2019
1 parent b0700de commit 558ae3e
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 9 deletions.
14 changes: 8 additions & 6 deletions docs/commands/goesrecv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ Demodulate and decode signal into packet stream.
See :ref:`minimal_receiver` for an example setup.

You can use goesrecv with an RTL-SDR_ (make sure you have one with the
R820T tuner chip), or an Airspy_ (confirmed to work with the Mini).
The raw signal is then processed by the demodulator and turned into a
stream of 1s and 0s. This is then passed to the decoder where the
bitstream is synchronized and error correction is applied. Every valid
packet is then forwarded to downstream tools (e.g. goeslrit or
goesproc).
R820T tuner chip), an Airspy_ (confirmed to work with the Mini), or a
HackRf (confirmed to work with a HackRf One). The raw signal is then
processed by the demodulator and turned into a stream of 1s and 0s. This
is then passed to the decoder where the bitstream is synchronized and
error correction is applied. Every valid packet is then forwarded to
downstream tools (e.g. goeslrit or goesproc).

.. _rtl-sdr:
https://rtlsdr.org/
.. _airspy:
https://airspy.com/
.. _hackrf:
https://greatscottgadgets.com/hackrf/

Options
=======
Expand Down
3 changes: 2 additions & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ Install system dependencies:
If you want to run goesrecv on this machine, you also have to install
the development packages of the drivers the SDRs you want to use;
``librtlsdr-dev`` for an RTL-SDR, ``libairspy-dev`` for an Airspy.
``librtlsdr-dev`` for an RTL-SDR, ``libairspy-dev`` for an Airspy,
and ``libhackrf-dev`` for a HackRf.

Now you can build and install goestools:

Expand Down
9 changes: 8 additions & 1 deletion etc/goesrecv.conf
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ source = "airspy"
# bias_tee = false
# device_index = 0

# [hackrf]
# frequency = 1694100000
# sample_rate = 8000000
# if_gain = 32
# bb_gain = 34
# rf_amp_enabled = true
# bias_tee = false

# [nanomsg]
# sample_rate = 2400000
# connect = "tcp://1.2.3.4:5005"
Expand Down Expand Up @@ -69,4 +77,3 @@ bind = "tcp://0.0.0.0:6002"
# this enabled even if you haven't setup a statsd daemon yet.
[monitor]
statsd_address = "udp4://localhost:8125"

1 change: 1 addition & 0 deletions scripts/setup_raspbian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ urls() {
scripts/list_raspbian_urls.py \
librtlsdr-dev \
libairspy-dev \
libhackrf-dev \
libusb-1.0-0-dev \
libudev1 \
zlib1g-dev \
Expand Down
13 changes: 13 additions & 0 deletions src/goesrecv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ else()
target_link_libraries(rtlsdr_source ${RTLSDR_LIBRARIES} publisher stdc++)
endif()

pkg_check_modules(HACKRF libhackrf)
if(NOT HACKRF_FOUND)
message(WARNING "Unable to find libhackrf")
else()
add_library(hackrf_source hackrf_source.cc)
target_link_libraries(hackrf_source ${HACKRF_LIBRARIES} publisher stdc++)
endif()

add_library(nanomsg_source nanomsg_source.cc)
target_link_libraries(nanomsg_source nanomsg publisher stdc++)

Expand Down Expand Up @@ -60,6 +68,7 @@ target_link_libraries(goesrecv clock_recovery)
target_link_libraries(goesrecv quantize)
target_link_libraries(goesrecv nanomsg_source)
target_link_libraries(goesrecv version)

if(AIRSPY_FOUND)
target_compile_definitions(goesrecv PUBLIC -DBUILD_AIRSPY)
target_link_libraries(goesrecv airspy_source)
Expand All @@ -68,6 +77,10 @@ if(RTLSDR_FOUND)
target_compile_definitions(goesrecv PUBLIC -DBUILD_RTLSDR)
target_link_libraries(goesrecv rtlsdr_source)
endif()
if(HACKRF_FOUND)
target_compile_definitions(goesrecv PUBLIC -DBUILD_HACKRF)
target_link_libraries(goesrecv hackrf_source)
endif()

add_executable(benchmark benchmark.cc)
target_link_libraries(benchmark pthread)
Expand Down
52 changes: 52 additions & 0 deletions src/goesrecv/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,51 @@ void loadRTLSDRSource(Config::RTLSDR& out, const toml::Value& v) {
}
}

void loadHackRfSource(Config::HackRf& out, const toml::Value& v) {
const auto& table = v.as<toml::Table>();
for (const auto& it : table) {
const auto& key = it.first;
const auto& value = it.second;

if (key == "frequency") {
out.frequency = value.as<int>();
continue;
}

if (key == "sample_rate") {
out.sampleRate = value.as<int>();
continue;
}

if (key == "if_gain") {
out.if_gain = value.as<int>();
continue;
}

if (key == "bb_gain") {
out.bb_gain = value.as<int>();
continue;
}

if (key == "rf_amp_enabled") {
out.rf_amp_enabled = value.as<bool>();
continue;
}

if (key == "sample_publisher") {
out.samplePublisher = createSamplePublisher(value);
continue;
}

if (key == "bias_tee") {
out.bias_tee = value.as<bool>();
continue;
}

throwInvalidKey(key);
}
}

void loadNanomsgSource(Config::Nanomsg& out, const toml::Value& v) {
const auto& table = v.as<toml::Table>();
for (const auto& it : table) {
Expand Down Expand Up @@ -392,6 +437,11 @@ Config Config::load(const std::string& file) {
continue;
}

if (key == "hackrf") {
loadHackRfSource(out.hackrf, value);
continue;
}

if (key == "nanomsg") {
loadNanomsgSource(out.nanomsg, value);
continue;
Expand Down Expand Up @@ -446,10 +496,12 @@ Config Config::load(const std::string& file) {
if (out.demodulator.downlinkType == "lrit") {
setIfZero(out.airspy.frequency, 1691000000u);
setIfZero(out.rtlsdr.frequency, 1691000000u);
setIfZero(out.hackrf.frequency, 1691000000u);
}
if (out.demodulator.downlinkType == "hrit") {
setIfZero(out.airspy.frequency, 1694100000u);
setIfZero(out.rtlsdr.frequency, 1694100000u);
setIfZero(out.hackrf.frequency, 1694100000u);
}

return out;
Expand Down
22 changes: 21 additions & 1 deletion src/goesrecv/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,30 @@ struct Config {
bool bias_tee = 0;

std::unique_ptr<SamplePublisher> samplePublisher;
};
};

Airspy airspy;

struct HackRf {
uint32_t frequency = 0;
uint32_t sampleRate = 0;

// Applies to the tuner IF gain setting
uint8_t if_gain = 8;

// Applies to the tuner BB gain setting
uint8_t bb_gain = 30;

// Enables or disables the RF amplifier
bool rf_amp_enabled = 0;

bool bias_tee = 0;

std::unique_ptr<SamplePublisher> samplePublisher;
};

HackRf hackrf;

struct RTLSDR {
uint32_t frequency = 0;
uint32_t sampleRate = 0;
Expand Down
151 changes: 151 additions & 0 deletions src/goesrecv/hackrf_source.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
#include "hackrf_source.h"

#include <pthread.h>

#include <cstring>
#include <iostream>

#include <util/error.h>

std::unique_ptr<HackRf> HackRf::open(uint32_t index) {
struct hackrf_device* dev = nullptr;
auto rv_init = hackrf_init();
if (rv_init < 0) {
std::cerr << "Unable to init hackrf" << std::endl;
exit(1);
}

auto rv = hackrf_open(&dev);
std::cout << "Is open?" << std::endl;

if (rv < 0) {
std::cerr << "Unable to open Hackrf device: "
<< hackrf_error_name((enum hackrf_error)rv) << std::endl;
exit(1);
}

return std::make_unique<HackRf>(dev);
}

HackRf::HackRf(struct hackrf_device* dev) : dev_(dev) {
// Load list of supported sample rates
sampleRates_ = loadSampleRates();
}

HackRf::~HackRf() {
if (dev_ != nullptr) {
hackrf_close(dev_);
hackrf_exit();
}
}

std::vector<uint32_t> HackRf::loadSampleRates() {
return {8000000, 10000000, 12500000, 16000000, 20000000};
}

void HackRf::setFrequency(uint32_t freq) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_freq(dev_, freq);
ASSERT(rv >= 0);
}

void HackRf::setSampleRate(uint32_t rate) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_sample_rate(dev_, rate);
ASSERT(rv >= 0);
sampleRate_ = rate;
}

uint32_t HackRf::getSampleRate() const { return sampleRate_; }

void HackRf::setIfGain(int gain) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_lna_gain(dev_, gain);
ASSERT(rv >= 0);
}

void HackRf::setRfAmplifier(bool on) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_amp_enable(dev_, on);
ASSERT(rv >= 0);
}

void HackRf::setBbGain(int gain) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_vga_gain(dev_, gain);
ASSERT(rv >= 0);

auto rv_amp = hackrf_set_amp_enable(dev_, 1);
ASSERT(rv_amp >= 0);
}

void HackRf::setBiasTee(bool on) {
ASSERT(dev_ != nullptr);
auto rv = hackrf_set_antenna_enable(dev_, on ? 1 : 0);
ASSERT(rv >= 0);
}

static int hackrf_callback(hackrf_transfer* transfer) {
auto hackrf_context = reinterpret_cast<HackRf*>(transfer->rx_ctx);
hackrf_context->handle(transfer);
return 0;
}

void HackRf::start(const std::shared_ptr<Queue<Samples> >& queue) {
ASSERT(dev_ != nullptr);
queue_ = queue;
thread_ = std::thread([&] {
auto rv = hackrf_start_rx(dev_, &hackrf_callback, this);
ASSERT(rv == 0);
});
#ifdef __APPLE__
pthread_setname_np("hackrf");
#else
pthread_setname_np(thread_.native_handle(), "hackrf");
#endif
}

void HackRf::stop() {
ASSERT(dev_ != nullptr);
auto rv = hackrf_stop_rx(dev_);
ASSERT(rv >= 0);

// Wait for thread to terminate
thread_.join();

// Close queue to signal downstream
queue_->close();

// Clear reference to queue
queue_.reset();
}

void HackRf::process(size_t nsamples, unsigned char* buf,
std::complex<float>* fo) {
for (uint32_t i = 0; i < nsamples; i++) {
fo[i].real((static_cast<int8_t>(buf[i * 2 + 0]) / 128.0f));
fo[i].imag((static_cast<int8_t>(buf[i * 2 + 1]) / 128.0f));
}
}

void HackRf::handle(const hackrf_transfer* transfer) {
uint32_t nsamples = transfer->valid_length / 2;

// Expect multiple of 2
ASSERT((nsamples & 0x2) == 0);

// Grab buffer from queue
auto out = queue_->popForWrite();
out->resize(nsamples);

// Convert unsigned char to std::complex<float>
process(nsamples, transfer->buffer, out->data());

// Publish output if applicable
if (samplePublisher_) {
samplePublisher_->publish(*out);
}

// Return buffer to queue
queue_->pushWrite(std::move(out));
}
Loading

0 comments on commit 558ae3e

Please sign in to comment.