mirror of https://github.com/hmatuschek/libsdr
Implemented simple cmd app.
parent
28b907dd9a
commit
7c5cfb2019
@ -0,0 +1,2 @@
|
|||||||
|
add_executable(sdr_cmd main.cc)
|
||||||
|
target_link_libraries(sdr_cmd ${LIBS} libsdr)
|
||||||
@ -0,0 +1,225 @@
|
|||||||
|
#include "options.hh"
|
||||||
|
#include "queue.hh"
|
||||||
|
#include "logger.hh"
|
||||||
|
#include "wavfile.hh"
|
||||||
|
#include "autocast.hh"
|
||||||
|
#include "portaudio.hh"
|
||||||
|
#include "rtlsource.hh"
|
||||||
|
#include "baseband.hh"
|
||||||
|
#include "demod.hh"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
using namespace sdr;
|
||||||
|
|
||||||
|
// On SIGINT -> stop queue properly
|
||||||
|
static void __sigint_handler(int signo) {
|
||||||
|
Queue::get().stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command line options
|
||||||
|
static Options::Definition options[] = {
|
||||||
|
{"rtl2832", 'R', Options::FLOAT,
|
||||||
|
"Specifies a RTL2832 USB dongle as the input source"},
|
||||||
|
{"rtl-device", 0, Options::INTEGER,
|
||||||
|
"Specifies the RTL2832 device index. (default 0)"},
|
||||||
|
{"disable-rtl-agc", 0 , Options::FLAG,
|
||||||
|
"Disables the IF AGC of the RTL2832 device, default on."},
|
||||||
|
{"rtl-agc-gain", 0, Options::INTEGER,
|
||||||
|
"In conjecture with --disable-rtl-agc, specifies the fixed IF gain of the RTL2832 device."},
|
||||||
|
{"rtl-ppm", 0, Options::FLOAT,
|
||||||
|
"Specifies the frequency correction for the RTL2832 device in parts-per-million (ppm)."},
|
||||||
|
{"audio", 'a', Options::FLAG,
|
||||||
|
"Specifies the system audio as the input source."},
|
||||||
|
{"audio-iq", 'a', Options::FLAG,
|
||||||
|
"Specifies the system audio as the input source (I/Q channels)."},
|
||||||
|
{"source-rate", 0, Options::FLOAT,
|
||||||
|
"Specifies the sample rate of the input device."},
|
||||||
|
{"file", 'f', Options::ANY,
|
||||||
|
"Specifies a WAV file as input source."},
|
||||||
|
{"demod", 'd', Options::ANY,
|
||||||
|
"Specifies the demodulator (wfm, nfm, am, usb, lsb)."},
|
||||||
|
{"demod-offset", 0, Options::FLOAT,
|
||||||
|
"Specifies the reception offset in Hz. (default 0)"},
|
||||||
|
{"loglevel", 0, Options::INTEGER,
|
||||||
|
"Specifies the log-level. 0 = DEBUG, 1 = INFO, 2 = WARNING, 3 = ERROR."},
|
||||||
|
{"help", '?', Options::FLAG,
|
||||||
|
"Prints this help."},
|
||||||
|
{0,0,Options::FLAG,0}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void print_help() {
|
||||||
|
std::cerr << "USAGE: sdr_cmd SOURCE [OPTIONS] OUTPUT" << std::endl << std::endl;
|
||||||
|
Options::print_help(std::cerr, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Parse command line options.
|
||||||
|
Options opts;
|
||||||
|
if (! Options::parse(options, argc, argv, opts)) {
|
||||||
|
print_help(); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If help flag is present -> print and done.
|
||||||
|
if (opts.has("help")) { print_help(); return 0; }
|
||||||
|
|
||||||
|
// Install log-handler
|
||||||
|
LogLevel loglevel = LOG_INFO;
|
||||||
|
if (opts.has("loglevel")) {
|
||||||
|
if (0 >= opts.get("loglevel").toInteger()) { loglevel = LOG_DEBUG; }
|
||||||
|
else if (1 >= opts.get("loglevel").toInteger()) { loglevel = LOG_INFO; }
|
||||||
|
else if (2 >= opts.get("loglevel").toInteger()) { loglevel = LOG_WARNING; }
|
||||||
|
else { loglevel = LOG_ERROR; }
|
||||||
|
}
|
||||||
|
sdr::Logger::get().addHandler(
|
||||||
|
new sdr::StreamLogHandler(std::cerr, loglevel));
|
||||||
|
|
||||||
|
// If no source has been selected -> error
|
||||||
|
if (! (opts.has("rtl2832")|opts.has("audio")|opts.has("file"))) {
|
||||||
|
std::cerr << "No source has been selected!" << std::endl;
|
||||||
|
print_help(); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no demodulator has been selected.
|
||||||
|
if (!opts.has("demod")) {
|
||||||
|
std::cerr << "No demodulator has been selected!" << std::endl;
|
||||||
|
print_help(); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bbFc = 0, bbFf = 0, bbFw = 0, bbFs = 0;
|
||||||
|
// Configure baseband node depending on the selected demodulator
|
||||||
|
if ("wfm" == opts.get("demod").toString()) {
|
||||||
|
bbFw = 100e3; bbFs = 100e3;
|
||||||
|
} else if ("nfm" == opts.get("demod").toString()) {
|
||||||
|
bbFw = 12.5e3; bbFs = 22.05e3;
|
||||||
|
} else if ("am" == opts.get("demod").toString()) {
|
||||||
|
bbFw = 10.0e3; bbFs = 22.05e3;
|
||||||
|
} else if ("usb" == opts.get("demod").toString()) {
|
||||||
|
bbFf = 1.5e3; bbFw = 3e3; bbFs = 22.05e3;
|
||||||
|
} else if ("lsb" == opts.get("demod").toString()) {
|
||||||
|
bbFf = -1.5e3; bbFw = 3e3; bbFs = 22.05e3;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown demodulator '" << opts.get("demod").toString() << "'." << std::endl;
|
||||||
|
print_help(); return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register signal handler to stop queue on SIGINT
|
||||||
|
signal(SIGINT, __sigint_handler);
|
||||||
|
|
||||||
|
// Init audio system
|
||||||
|
PortAudio::init();
|
||||||
|
|
||||||
|
// Get the global queue
|
||||||
|
Queue &queue = Queue::get();
|
||||||
|
|
||||||
|
// reference to selected source node
|
||||||
|
Source *source = 0;
|
||||||
|
// Nodes for file input
|
||||||
|
WavSource *wav_src=0;
|
||||||
|
AutoCast< std::complex<int16_t> > *wav_cast=0;
|
||||||
|
// nodes for real audio input
|
||||||
|
PortSource<int16_t> *raudio_src=0;
|
||||||
|
AutoCast< std::complex<int16_t> > *raudio_cast=0;
|
||||||
|
// nodes for IQ audio input
|
||||||
|
PortSource< std::complex<int16_t> > *iqaudio_src=0;
|
||||||
|
// nodes for RTL2832 input
|
||||||
|
RTLSource *rtl_source=0;
|
||||||
|
AutoCast< std::complex<int16_t> > *rtl_cast=0;
|
||||||
|
|
||||||
|
/* Demodulator nodes. */
|
||||||
|
Source *demod = 0;
|
||||||
|
FMDemod<int16_t> *fm_demod = 0;
|
||||||
|
FMDeemph<int16_t> *fm_deemph = 0;
|
||||||
|
AMDemod<int16_t> *am_demod = 0;
|
||||||
|
USBDemod<int16_t> *ssb_demod = 0;
|
||||||
|
|
||||||
|
// Dispatch by selected source
|
||||||
|
if (opts.has("rtl2832")) {
|
||||||
|
// Instantiate & config RTL2832 node
|
||||||
|
size_t devIdx = 0;
|
||||||
|
double sampleRate = 1e6;
|
||||||
|
if (opts.has("rtl-device")) { devIdx = opts.get("rtl-device").toInteger(); }
|
||||||
|
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||||
|
rtl_source = new RTLSource(opts.get("rtl2832").toFloat(), sampleRate, devIdx);
|
||||||
|
if (opts.has("rtl-disable-agc")) { rtl_source->enableAGC(false); }
|
||||||
|
if (opts.has("rtl-gain")) { rtl_source->setGain(opts.get("rtl-gain").toFloat()); }
|
||||||
|
if (opts.has("rtl-ppm")) { rtl_source->setFreqCorrection(opts.get("rtl-ppm").toFloat()); }
|
||||||
|
rtl_cast = new AutoCast< std::complex<int16_t> >();
|
||||||
|
rtl_source->connect(rtl_cast);
|
||||||
|
source = rtl_cast;
|
||||||
|
// Connect start & stop event to RTL2832 device
|
||||||
|
queue.addStart(rtl_source, &RTLSource::start);
|
||||||
|
queue.addStop(rtl_source, &RTLSource::stop);
|
||||||
|
} else if (opts.has("audio")) {
|
||||||
|
double sampleRate = 44100;
|
||||||
|
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||||
|
raudio_src = new PortSource<int16_t>(sampleRate, 1024);
|
||||||
|
raudio_cast = new AutoCast< std::complex<int16_t> >();
|
||||||
|
source = raudio_cast;
|
||||||
|
// Connect idle event to source
|
||||||
|
queue.addIdle(raudio_src, &PortSource<int16_t>::next);
|
||||||
|
} else if (opts.has("audio-iq")) {
|
||||||
|
double sampleRate = 44100;
|
||||||
|
if (opts.has("source-rate")) { sampleRate = opts.get("source-rate").toFloat(); }
|
||||||
|
iqaudio_src = new PortSource< std::complex<int16_t> >(sampleRate, 1024);
|
||||||
|
source = iqaudio_src;
|
||||||
|
// Connect idle event to source
|
||||||
|
queue.addIdle(iqaudio_src, &PortSource< std::complex<int16_t> >::next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift baseband by offset
|
||||||
|
if (opts.has("demod-offset")) {
|
||||||
|
bbFc += opts.get("demod-offset").toFloat();
|
||||||
|
bbFf += opts.get("demod-offset").toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
IQBaseBand< int16_t > baseband(bbFc, bbFf, bbFw, 31, 0, bbFs);
|
||||||
|
source->connect(&baseband, true);
|
||||||
|
|
||||||
|
// Dispatch by selected demodulator
|
||||||
|
if (("wfm" == opts.get("demod").toString()) || ("nfm" == opts.get("demod").toString())) {
|
||||||
|
fm_demod = new FMDemod<int16_t>();
|
||||||
|
fm_deemph = new FMDeemph<int16_t>();
|
||||||
|
source->connect(fm_demod, true);
|
||||||
|
fm_demod->connect(fm_deemph, true);
|
||||||
|
demod = fm_deemph;
|
||||||
|
} else if ("am" == opts.get("demod").toString()) {
|
||||||
|
am_demod = new AMDemod<int16_t>();
|
||||||
|
source->connect(am_demod, true);
|
||||||
|
demod = am_demod;
|
||||||
|
} else if (("usb" == opts.get("demod").toString()) || ("lsb" == opts.get("demod").toString())) {
|
||||||
|
ssb_demod = new USBDemod<int16_t>();
|
||||||
|
source->connect(ssb_demod, true);
|
||||||
|
demod = ssb_demod;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable playback
|
||||||
|
PortSink audio_sink;
|
||||||
|
demod->connect(&audio_sink, true);
|
||||||
|
|
||||||
|
// Start queue & processing
|
||||||
|
queue.start();
|
||||||
|
// Wait for queue to to stop
|
||||||
|
queue.wait();
|
||||||
|
|
||||||
|
// Free allocated input nodes
|
||||||
|
if (rtl_source) { delete rtl_source; }
|
||||||
|
if (rtl_cast) { delete rtl_cast; }
|
||||||
|
if (raudio_src) { delete raudio_src; }
|
||||||
|
if (raudio_cast) { delete raudio_cast; }
|
||||||
|
if (iqaudio_src) { delete iqaudio_src; }
|
||||||
|
if (wav_src) { delete wav_src; }
|
||||||
|
if (wav_cast) { delete wav_cast; }
|
||||||
|
if (fm_demod) { delete fm_demod; }
|
||||||
|
if (fm_deemph) { delete fm_deemph; }
|
||||||
|
if (am_demod) { delete am_demod; }
|
||||||
|
if (ssb_demod) { delete ssb_demod; }
|
||||||
|
|
||||||
|
PortAudio::terminate();
|
||||||
|
|
||||||
|
// Done.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue