Skip to content

Commit 87f0062

Browse files
committed
Add support for HackRF.
1 parent 9f21e52 commit 87f0062

File tree

10 files changed

+364
-7
lines changed

10 files changed

+364
-7
lines changed

docs/commands/goesrecv.rst

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ Demodulate and decode signal into packet stream.
1313
See :ref:`minimal_receiver` for an example setup.
1414

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

2323
.. _rtl-sdr:
2424
https://rtlsdr.org/
2525
.. _airspy:
2626
https://airspy.com/
27+
.. _hackrf:
28+
https://greatscottgadgets.com/hackrf/
2729

2830
Options
2931
=======

docs/installation.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ Install system dependencies:
5151
5252
If you want to run goesrecv on this machine, you also have to install
5353
the development packages of the drivers the SDRs you want to use;
54-
``librtlsdr-dev`` for an RTL-SDR, ``libairspy-dev`` for an Airspy.
54+
``librtlsdr-dev`` for an RTL-SDR, ``libairspy-dev`` for an Airspy,
55+
and ``libhackrf-dev`` for a HackRF.
5556

5657
Now you can build and install goestools:
5758

etc/goesrecv.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ source = "airspy"
3333
# bias_tee = false
3434
# device_index = 0
3535

36+
# [hackrf]
37+
# frequency = 1694100000
38+
# sample_rate = 8000000
39+
# if_gain = 32
40+
# bb_gain = 34
41+
# rf_amp_enabled = true
42+
# bias_tee = false
43+
3644
# [nanomsg]
3745
# sample_rate = 2400000
3846
# connect = "tcp://1.2.3.4:5005"

scripts/setup_raspbian.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ urls() {
2323
scripts/list_raspbian_urls.py \
2424
librtlsdr-dev \
2525
libairspy-dev \
26+
libhackrf-dev \
2627
libusb-1.0-0-dev \
2728
libudev1 \
2829
zlib1g-dev \

src/goesrecv/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ else()
2929
target_link_libraries(rtlsdr_source ${RTLSDR_LIBRARIES} publisher stdc++)
3030
endif()
3131

32+
pkg_check_modules(HACKRF libhackrf)
33+
if(NOT HACKRF_FOUND)
34+
message(WARNING "Unable to find libhackrf")
35+
else()
36+
add_library(hackrf_source hackrf_source.cc)
37+
target_link_libraries(hackrf_source ${HACKRF_LIBRARIES} publisher stdc++)
38+
endif()
39+
3240
add_library(nanomsg_source nanomsg_source.cc)
3341
target_link_libraries(nanomsg_source nanomsg publisher stdc++)
3442

@@ -60,6 +68,7 @@ target_link_libraries(goesrecv clock_recovery)
6068
target_link_libraries(goesrecv quantize)
6169
target_link_libraries(goesrecv nanomsg_source)
6270
target_link_libraries(goesrecv version)
71+
6372
if(AIRSPY_FOUND)
6473
target_compile_definitions(goesrecv PUBLIC -DBUILD_AIRSPY)
6574
target_link_libraries(goesrecv airspy_source)
@@ -68,6 +77,10 @@ if(RTLSDR_FOUND)
6877
target_compile_definitions(goesrecv PUBLIC -DBUILD_RTLSDR)
6978
target_link_libraries(goesrecv rtlsdr_source)
7079
endif()
80+
if(HACKRF_FOUND)
81+
target_compile_definitions(goesrecv PUBLIC -DBUILD_HACKRF)
82+
target_link_libraries(goesrecv hackrf_source)
83+
endif()
7184

7285
add_executable(benchmark benchmark.cc)
7386
target_link_libraries(benchmark pthread)

src/goesrecv/config.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,51 @@ void loadRTLSDRSource(Config::RTLSDR& out, const toml::Value& v) {
198198
}
199199
}
200200

201+
void loadHackRFSource(Config::HackRF& out, const toml::Value& v) {
202+
const auto& table = v.as<toml::Table>();
203+
for (const auto& it : table) {
204+
const auto& key = it.first;
205+
const auto& value = it.second;
206+
207+
if (key == "frequency") {
208+
out.frequency = value.as<int>();
209+
continue;
210+
}
211+
212+
if (key == "sample_rate") {
213+
out.sampleRate = value.as<int>();
214+
continue;
215+
}
216+
217+
if (key == "if_gain") {
218+
out.if_gain = value.as<int>();
219+
continue;
220+
}
221+
222+
if (key == "bb_gain") {
223+
out.bb_gain = value.as<int>();
224+
continue;
225+
}
226+
227+
if (key == "rf_amp_enabled") {
228+
out.rf_amp_enabled = value.as<bool>();
229+
continue;
230+
}
231+
232+
if (key == "sample_publisher") {
233+
out.samplePublisher = createSamplePublisher(value);
234+
continue;
235+
}
236+
237+
if (key == "bias_tee") {
238+
out.bias_tee = value.as<bool>();
239+
continue;
240+
}
241+
242+
throwInvalidKey(key);
243+
}
244+
}
245+
201246
void loadNanomsgSource(Config::Nanomsg& out, const toml::Value& v) {
202247
const auto& table = v.as<toml::Table>();
203248
for (const auto& it : table) {
@@ -392,6 +437,11 @@ Config Config::load(const std::string& file) {
392437
continue;
393438
}
394439

440+
if (key == "hackrf") {
441+
loadHackRFSource(out.hackrf, value);
442+
continue;
443+
}
444+
395445
if (key == "nanomsg") {
396446
loadNanomsgSource(out.nanomsg, value);
397447
continue;
@@ -446,10 +496,12 @@ Config Config::load(const std::string& file) {
446496
if (out.demodulator.downlinkType == "lrit") {
447497
setIfZero(out.airspy.frequency, 1691000000u);
448498
setIfZero(out.rtlsdr.frequency, 1691000000u);
499+
setIfZero(out.hackrf.frequency, 1691000000u);
449500
}
450501
if (out.demodulator.downlinkType == "hrit") {
451502
setIfZero(out.airspy.frequency, 1694100000u);
452503
setIfZero(out.rtlsdr.frequency, 1694100000u);
504+
setIfZero(out.hackrf.frequency, 1694100000u);
453505
}
454506

455507
return out;

src/goesrecv/config.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ struct Config {
4848

4949
Airspy airspy;
5050

51+
struct HackRF {
52+
uint32_t frequency = 0;
53+
uint32_t sampleRate = 0;
54+
55+
// Applies to the tuner IF gain setting
56+
uint8_t if_gain = 8;
57+
58+
// Applies to the tuner BB gain setting
59+
uint8_t bb_gain = 30;
60+
61+
// Enables or disables the RF amplifier
62+
bool rf_amp_enabled = 0;
63+
64+
bool bias_tee = 0;
65+
66+
std::unique_ptr<SamplePublisher> samplePublisher;
67+
};
68+
69+
HackRF hackrf;
70+
5171
struct RTLSDR {
5272
uint32_t frequency = 0;
5373
uint32_t sampleRate = 0;

src/goesrecv/hackrf_source.cc

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include "hackrf_source.h"
2+
3+
#include <pthread.h>
4+
5+
#include <cstring>
6+
#include <iostream>
7+
8+
#include <util/error.h>
9+
10+
std::unique_ptr<HackRF> HackRF::open(uint32_t index) {
11+
struct hackrf_device* dev = nullptr;
12+
auto rv_init = hackrf_init();
13+
if (rv_init < 0) {
14+
std::cerr << "Unable to init hackrf" << std::endl;
15+
exit(1);
16+
}
17+
18+
auto rv = hackrf_open(&dev);
19+
20+
if (rv < 0) {
21+
std::cerr << "Unable to open HackRF device: "
22+
<< hackrf_error_name((enum hackrf_error)rv) << std::endl;
23+
exit(1);
24+
}
25+
26+
return std::make_unique<HackRF>(dev);
27+
}
28+
29+
HackRF::HackRF(struct hackrf_device* dev) : dev_(dev) {
30+
// Load list of supported sample rates
31+
sampleRates_ = loadSampleRates();
32+
}
33+
34+
HackRF::~HackRF() {
35+
if (dev_ != nullptr) {
36+
hackrf_close(dev_);
37+
hackrf_exit();
38+
}
39+
}
40+
41+
std::vector<uint32_t> HackRF::loadSampleRates() {
42+
return {8000000, 10000000, 12500000, 16000000, 20000000};
43+
}
44+
45+
void HackRF::setFrequency(uint32_t freq) {
46+
ASSERT(dev_ != nullptr);
47+
auto rv = hackrf_set_freq(dev_, freq);
48+
ASSERT(rv >= 0);
49+
}
50+
51+
void HackRF::setSampleRate(uint32_t rate) {
52+
ASSERT(dev_ != nullptr);
53+
auto rv = hackrf_set_sample_rate(dev_, rate);
54+
ASSERT(rv >= 0);
55+
sampleRate_ = rate;
56+
}
57+
58+
uint32_t HackRF::getSampleRate() const {
59+
return sampleRate_;
60+
}
61+
62+
void HackRF::setIfGain(int gain) {
63+
ASSERT(dev_ != nullptr);
64+
auto rv = hackrf_set_lna_gain(dev_, gain);
65+
ASSERT(rv >= 0);
66+
}
67+
68+
void HackRF::setRfAmplifier(bool on) {
69+
ASSERT(dev_ != nullptr);
70+
auto rv = hackrf_set_amp_enable(dev_, on);
71+
ASSERT(rv >= 0);
72+
}
73+
74+
void HackRF::setBbGain(int gain) {
75+
ASSERT(dev_ != nullptr);
76+
auto rv = hackrf_set_vga_gain(dev_, gain);
77+
ASSERT(rv >= 0);
78+
}
79+
80+
void HackRF::setBiasTee(bool on) {
81+
ASSERT(dev_ != nullptr);
82+
auto rv = hackrf_set_antenna_enable(dev_, on ? 1 : 0);
83+
ASSERT(rv >= 0);
84+
}
85+
86+
static int hackrf_callback(hackrf_transfer* transfer) {
87+
auto hackrf_context = reinterpret_cast<HackRF*>(transfer->rx_ctx);
88+
hackrf_context->handle(transfer);
89+
return 0;
90+
}
91+
92+
void HackRF::start(const std::shared_ptr<Queue<Samples> >& queue) {
93+
ASSERT(dev_ != nullptr);
94+
queue_ = queue;
95+
thread_ = std::thread([&] {
96+
auto rv = hackrf_start_rx(dev_, &hackrf_callback, this);
97+
ASSERT(rv == 0);
98+
});
99+
#ifdef __APPLE__
100+
pthread_setname_np("hackrf");
101+
#else
102+
pthread_setname_np(thread_.native_handle(), "hackrf");
103+
#endif
104+
}
105+
106+
void HackRF::stop() {
107+
ASSERT(dev_ != nullptr);
108+
auto rv = hackrf_stop_rx(dev_);
109+
ASSERT(rv >= 0);
110+
111+
// Wait for thread to terminate
112+
thread_.join();
113+
114+
// Close queue to signal downstream
115+
queue_->close();
116+
117+
// Clear reference to queue
118+
queue_.reset();
119+
}
120+
121+
void HackRF::process(size_t nsamples, unsigned char* buf, std::complex<float>* fo) {
122+
for (uint32_t i = 0; i < nsamples; i++) {
123+
fo[i].real((static_cast<int8_t>(buf[i * 2 + 0]) / 128.0f));
124+
fo[i].imag((static_cast<int8_t>(buf[i * 2 + 1]) / 128.0f));
125+
}
126+
}
127+
128+
void HackRF::handle(const hackrf_transfer* transfer) {
129+
uint32_t nsamples = transfer->valid_length / 2;
130+
131+
// Expect multiple of 2
132+
ASSERT((nsamples & 0x2) == 0);
133+
134+
// Grab buffer from queue
135+
auto out = queue_->popForWrite();
136+
out->resize(nsamples);
137+
138+
// Convert unsigned char to std::complex<float>
139+
process(nsamples, transfer->buffer, out->data());
140+
141+
// Publish output if applicable
142+
if (samplePublisher_) {
143+
samplePublisher_->publish(*out);
144+
}
145+
146+
// Return buffer to queue
147+
queue_->pushWrite(std::move(out));
148+
}

0 commit comments

Comments
 (0)