From 7c5cfb201941dd567d44dac658b138661e3e22a8 Mon Sep 17 00:00:00 2001 From: Hannes Matuschek Date: Fri, 5 Jun 2015 13:35:29 +0200 Subject: [PATCH] Implemented simple cmd app. --- CMakeLists.txt | 7 ++ cmd/CMakeLists.txt | 2 + cmd/main.cc | 225 +++++++++++++++++++++++++++++++++++++++++++ examples/sdr_ax25.cc | 2 +- src/ax25.cc | 4 +- src/logger.hh | 2 +- 6 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 cmd/CMakeLists.txt create mode 100644 cmd/main.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a35154d..14653dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ include(InstallHeadersWithDirectory) OPTION(BUILD_EXAMPLES "Build examples" OFF) OPTION(BUILD_UNIT_TESTS "Build unit tests" OFF) +OPTION(BUILD_COMMANDLINETOOL "Build command line tool" ON) SET(libsdr_VERSION_MAJOR "0") SET(libsdr_VERSION_MINOR "1") @@ -84,13 +85,19 @@ ENDIF(UNIX AND APPLE) # Add core library, and unit tests add_subdirectory(src) + IF(BUILD_UNIT_TESTS) add_subdirectory(test) ENDIF(BUILD_UNIT_TESTS) + IF(BUILD_EXAMPLES) add_subdirectory(examples) ENDIF(BUILD_EXAMPLES) +IF(BUILD_COMMANDLINETOOL) + add_subdirectory(cmd) +endif(BUILD_COMMANDLINETOOL) + # Source distribution packages: set(CPACK_PACKAGE_VERSION_MAJOR ${libsdr_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${libsdr_VERSION_MINOR}) diff --git a/cmd/CMakeLists.txt b/cmd/CMakeLists.txt new file mode 100644 index 0000000..ca963fc --- /dev/null +++ b/cmd/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(sdr_cmd main.cc) +target_link_libraries(sdr_cmd ${LIBS} libsdr) diff --git a/cmd/main.cc b/cmd/main.cc new file mode 100644 index 0000000..6ccd0fd --- /dev/null +++ b/cmd/main.cc @@ -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 +#include + +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 > *wav_cast=0; + // nodes for real audio input + PortSource *raudio_src=0; + AutoCast< std::complex > *raudio_cast=0; + // nodes for IQ audio input + PortSource< std::complex > *iqaudio_src=0; + // nodes for RTL2832 input + RTLSource *rtl_source=0; + AutoCast< std::complex > *rtl_cast=0; + + /* Demodulator nodes. */ + Source *demod = 0; + FMDemod *fm_demod = 0; + FMDeemph *fm_deemph = 0; + AMDemod *am_demod = 0; + USBDemod *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 >(); + 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(sampleRate, 1024); + raudio_cast = new AutoCast< std::complex >(); + source = raudio_cast; + // Connect idle event to source + queue.addIdle(raudio_src, &PortSource::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 >(sampleRate, 1024); + source = iqaudio_src; + // Connect idle event to source + queue.addIdle(iqaudio_src, &PortSource< std::complex >::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(); + fm_deemph = new FMDeemph(); + 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(); + source->connect(am_demod, true); + demod = am_demod; + } else if (("usb" == opts.get("demod").toString()) || ("lsb" == opts.get("demod").toString())) { + ssb_demod = new USBDemod(); + 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; +} diff --git a/examples/sdr_ax25.cc b/examples/sdr_ax25.cc index be875db..caa069e 100644 --- a/examples/sdr_ax25.cc +++ b/examples/sdr_ax25.cc @@ -114,7 +114,7 @@ int main(int argc, char *argv[]) rtl_source->setFreqCorrection(opts.get("correction").toFloat()); } rtl_cast = new AutoCast< std::complex >(); - rtl_baseband = new IQBaseBand(0, 12.5e3, 21, 0, 22050.0); + rtl_baseband = new IQBaseBand(0, 15.0e3, 21, 0, 22050.0); rtl_demod = new FMDemod(); rtl_deemph = new FMDeemph(); // Connect nodes diff --git a/src/ax25.cc b/src/ax25.cc index 67cb983..ef55236 100644 --- a/src/ax25.cc +++ b/src/ax25.cc @@ -109,9 +109,9 @@ AX25::process(const Buffer &buffer, bool allow_overwrite) if ((1==_state) && ((_ptr - _rxbuffer) > 2)) { *_ptr = 0; if (! check_crc_ccitt(_rxbuffer, _ptr-_rxbuffer)) { - LogMessage msg(LOG_DEBUG); + /*LogMessage msg(LOG_DEBUG); msg << "AX.25: Received invalid buffer: " << _rxbuffer; - Logger::get().log(msg); + Logger::get().log(msg); */ } else { // Assemble message Message msg(_rxbuffer, _ptr-_rxbuffer-2); diff --git a/src/logger.hh b/src/logger.hh index b54f0da..99008c6 100644 --- a/src/logger.hh +++ b/src/logger.hh @@ -10,7 +10,7 @@ namespace sdr { /** Specifies the possible log levels. */ typedef enum { - LOG_DEBUG, ///< Every thing that may be of interest. + LOG_DEBUG = 0, ///< Every thing that may be of interest. LOG_INFO, ///< Messages about state changes. LOG_WARNING, ///< Non critical errors (i.e. data loss). LOG_ERROR ///< Critical errors.