Updated docs.

master
Hannes Matuschek 11 years ago
parent e6a9aa6c02
commit 1ed605135a

@ -139,6 +139,7 @@ protected:
return 2*N; return 2*N;
} }
/** std::complex<uint8_t> -> std::complex<int8_t>. */
static size_t _cuint8_cint8(const RawBuffer &in, const RawBuffer &out) { static size_t _cuint8_cint8(const RawBuffer &in, const RawBuffer &out) {
size_t N = in.bytesLen()/2; size_t N = in.bytesLen()/2;
std::complex<uint8_t> *values = reinterpret_cast<std::complex<uint8_t> *>(in.data()); std::complex<uint8_t> *values = reinterpret_cast<std::complex<uint8_t> *>(in.data());

@ -75,8 +75,7 @@ Source::send(const RawBuffer &buffer, bool allow_overwrite) {
allow_overwrite = allow_overwrite && (1 == _sinks.size()); allow_overwrite = allow_overwrite && (1 == _sinks.size());
// Call sink directly // Call sink directly
item->first->handleBuffer(buffer, allow_overwrite); item->first->handleBuffer(buffer, allow_overwrite);
} } else {
else {
// otherwise, queue buffer // otherwise, queue buffer
allow_overwrite = allow_overwrite && (1 == _sinks.size()); allow_overwrite = allow_overwrite && (1 == _sinks.size());
Queue::get().send(buffer, item->first, allow_overwrite); Queue::get().send(buffer, item->first, allow_overwrite);

@ -18,6 +18,7 @@ class DelegateInterface {
public: public:
/** Call back interface. */ /** Call back interface. */
virtual void operator() () = 0; virtual void operator() () = 0;
/** Returns the instance of the delegate. */
virtual void *instance() = 0; virtual void *instance() = 0;
}; };
@ -32,6 +33,7 @@ public:
virtual ~Delegate() {} virtual ~Delegate() {}
/** Callback, simply calls the method of the instance given to the constructor. */ /** Callback, simply calls the method of the instance given to the constructor. */
virtual void operator() () { (_instance->*_function)(); } virtual void operator() () { (_instance->*_function)(); }
/** Returns the instance of the delegate. */
virtual void *instance() { return _instance; } virtual void *instance() { return _instance; }
protected: protected:
@ -126,6 +128,7 @@ public:
_idle.push_back(new Delegate<T>(instance, function)); _idle.push_back(new Delegate<T>(instance, function));
} }
/** Removes all callbacks of the given instance from the idle signal. */
template <class T> template <class T>
void remIdle(T *instance) { void remIdle(T *instance) {
std::list<DelegateInterface *>::iterator item = _idle.begin(); std::list<DelegateInterface *>::iterator item = _idle.begin();
@ -144,6 +147,7 @@ public:
_onStart.push_back(new Delegate<T>(instance, function)); _onStart.push_back(new Delegate<T>(instance, function));
} }
/** Removes all callbacks of the given instance from the start signal. */
template <class T> template <class T>
void remStart(T *instance) { void remStart(T *instance) {
std::list<DelegateInterface *>::iterator item = _onStart.begin(); std::list<DelegateInterface *>::iterator item = _onStart.begin();
@ -162,6 +166,7 @@ public:
_onStop.push_back(new Delegate<T>(instance, function)); _onStop.push_back(new Delegate<T>(instance, function));
} }
/** Removes all callbacks of the given instance from the stop signal. */
template <class T> template <class T>
void remStop(T *instance) { void remStop(T *instance) {
std::list<DelegateInterface *>::iterator item = _onStop.begin(); std::list<DelegateInterface *>::iterator item = _onStop.begin();

@ -14,37 +14,38 @@
* request further data from the sources once all present data has been processed. It also * request further data from the sources once all present data has been processed. It also
* routes the date from the sources to the sinks. * routes the date from the sources to the sinks.
* *
* \section intro A practical introduction
* The following examples shows a trivial application that recods some audio from the systems * The following examples shows a trivial application that recods some audio from the systems
* default audio source and play it back. * default audio source and play it back.
* \code * \code
* #include <libsdr/sdr.hh> * #include <libsdr/sdr.hh>
* *
* int main(int argc, char *argv[]) { * int main(int argc, char *argv[]) {
* // Initialize PortAudio system * // Initialize PortAudio system
* sdr::PortAudio::init(); * sdr::PortAudio::init();
* *
* // Create an audio source using PortAudio * // Create an audio source using PortAudio
* sdr::PortSource<int16_t> source(44.1e3); * sdr::PortSource<int16_t> source(44.1e3);
* // Create an audio sink using PortAudio * // Create an audio sink using PortAudio
* sdr::PortSink sink; * sdr::PortSink sink;
* *
* // Connect them * // Connect them
* source.connect(&sink); * source.connect(&sink);
* *
* // Read new data from audio sink if queue is idle * // Read new data from audio sink if queue is idle
* sdr::Queue::get().addIdle(&source, &sdr::PortSource<int16_t>::next); * sdr::Queue::get().addIdle(&source, &sdr::PortSource<int16_t>::next);
* *
* // Get and start queue * // Get and start queue
* sdr::Queue::get().start(); * sdr::Queue::get().start();
* // Wait for queue to stop * // Wait for queue to stop
* sdr::Queue::get().wait(); * sdr::Queue::get().wait();
* *
* // Terminate PortAudio system * // Terminate PortAudio system
* sdr::PortAudio::terminate(); * sdr::PortAudio::terminate();
* *
* // done. * // done.
* return 0; * return 0;
* } * }
* \endcode * \endcode
* *
* First, the PortAudio system gets initialized. * First, the PortAudio system gets initialized.
@ -73,6 +74,192 @@
* *
* The queue can be stopped by calling the @c sdr::Queue::stop method. This can be implemented for * The queue can be stopped by calling the @c sdr::Queue::stop method. This can be implemented for
* this example by the means of process signals. * this example by the means of process signals.
*
* \code
* #include <signal.h>
* ...
*
* static void __sigint_handler(int signo) {
* // On SIGINT -> stop queue properly
* sdr::Queue::get().stop();
* }
*
* int main(int argc, char *argv[]) {
* // Register signal handler
* signal(SIGINT, __sigint_handler);
* ...
* }
* \endcode
*
* Whenever a SIGINT is send to the process, i.e. by pressing CTRL+C, the @c sdr::Queue::stop method
* gets called. This will cause the processing thread of the queue to exit and the call to
* @c sdr::Queue::wait to return.
*
* \subsection example2 Queue less operation
* Sometimes, the queue is simply not needed. This is particularily the case if the data processing
* can happen in the main thread, i.e. if there is not GUI. The example above can be implemented
* without the Queue, as the main thread is just waiting for the processing thread to exit.
*
* \code
* #include <libsdr/sdr.hh>
*
* int main(int argc, char *argv[]) {
* // Initialize PortAudio system
* sdr::PortAudio::init();
*
* // Create an audio source using PortAudio
* sdr::PortSource<int16_t> source(44.1e3);
* // Create an audio sink using PortAudio
* sdr::PortSink sink;
*
* // Connect them directly
* source.connect(&sink, true);
*
* // Loop to infinity7
* while(true) { source.next(); }
*
* // Terminate PortAudio system
* sdr::PortAudio::terminate();
*
* // done.
* return 0;
* }
* \endcode
*
* The major difference between the first example and this one, is the way how the nodes are
* connected. The @c sdr::Source::connect method takes an optional argument specifying wheter the
* source is connected directly to the sink or not. If @c false (the default) is specified, the
* data of the source will be send to the Queue first. In a direct connection (passing @c true), the
* source will send the data directly to the sink, bypassing the queue.
*
* Instead of starting the processing thread of the queue, here the main thread is doing all the
* work by calling the @c next mehtod of the audio source.
*
* \subsection logging Log messages
* During configuration and operation, processing nodes will send log messages of different levels
* (DEBUG, INFO, WARNING, ERROR), which allow to debug the operation of the complete processing
* chain. These log messages are passed around using the build-in @c sdr::Logger class. To make
* them visible, a log handler must be installed.
*
* \code
* int main(int argc, char *argv[]) {
* ...
* // Install the log handler...
* sdr::Logger::get().addHandler(
* new sdr::StreamLogHandler(std::cerr, sdr::LOG_DEBUG));
* ...
* }
* \endcode
*
* Like the @c sdr::Queue, the logger is also a singleton object, which can be obtained by
* @c sdr::Logger::get. By calling @c sdr::Logger::addHandler, a new message handler is installed.
* In this example, a @c sdr::StreamLogHandler instance is installed, which serializes the
* log messages into @c std::cerr.
*
* \subsection intro_summary In summary
* In summary, the complete example above using the queue including a singal handler to properly
* terminate the application by SIGINT and a log handler will look like
*
* \code
* #include <libsdr/sdr.hh>
* #include <signal.h>
*
* static void __sigint_handler(int signo) {
* // On SIGINT -> stop queue properly
* sdr::Queue::get().stop();
* }
*
* int main(int argc, char *argv[]) {
* // Register signal handler
* signal(SIGINT, __sigint_handler);
*
* // Initialize PortAudio system
* sdr::PortAudio::init();
*
* // Create an audio source using PortAudio
* sdr::PortSource<int16_t> source(44.1e3);
* // Create an audio sink using PortAudio
* sdr::PortSink sink;
*
* // Connect them
* source.connect(&sink);
*
* // Read new data from audio sink if queue is idle
* sdr::Queue::get().addIdle(&source, &sdr::PortSource<int16_t>::next);
*
* // Get and start queue
* sdr::Queue::get().start();
* // Wait for queue to stop
* sdr::Queue::get().wait();
*
* // Terminate PortAudio system
* sdr::PortAudio::terminate();
*
* // done.
* return 0;
* }
* \endcode
* This may appear quiet bloated for such a simple application. I designed the library to be rather
* explicit. No feature is implicit and hidden from the user. This turns simple examples like the
* one above quite bloated but it is imediately clear how the example works whithout any knowledge
* of "hidden features" and the complexity does not suddenly increases for non-trivial examples.
*
* Finally, we may have a look at a more relaistic example implementing a FM broadcast receiver
* using a RTL2832 based USB dongle as the input source.
* \code
* #include <libsdr/sdr.hh>
* #include <signal.h>
*
* static void __sigint_handler(int signo) {
* // On SIGINT -> stop queue properly
* sdr::Queue::get().stop();
* }
*
* int main(int argc, char *argv[]) {
* // Register signal handler
* signal(SIGINT, __sigint_handler);
*
* // Initialize PortAudio system
* sdr::PortAudio::init();
*
* // Frequency of the FM station (in Hz)
* double freq = 100e6;
*
* // Create a RTL2832 input node
* sdr::RTLSource src(freq);
* // Filter 100kHz around the center frequency (0) with an 16th order FIR filter and
* // subsample the result to a sample rate of approx. 100kHz.
* sdr::IQBaseBand<int8_t> baseband(0, 100e3, 16, 0, 100e3);
* // FM demodulator, takes a complex int8_t stream and returns a real int16_t stream
* sdr::FMDemod<int8_t, int16_t> demod;
* // Deemphesize the result (actually part of the demodulation)
* sdr::FMDeemph<int16_t> deemph;
* // Playback the final signal
* sdr::PortSink sink;
*
* // Connect signals
* src.connect(&baseband, true);
* baseband.connect(&demod);
* demod.connect(&deemph);
* deemph.connect(&sink);
*
* // Connect start and stop signals of Queue to RTL2832 source
* sdr::Queue::get().addStart(&src, &sdr::RTLSource::start);
* sdr::Queue::get().addStop(&src, &sdr::RTLSource::stop);
*
* // Start queue
* sdr::Queue::get().start();
* // Wait for queue
* sdr::Queue::get().wait();
*
* // Terminate PortAudio system
* sdr::PortAudio::terminate();
*
* // Done...
* return 0;
* }
* \endcode
*
*/ */
#ifndef __SDR_HH__ #ifndef __SDR_HH__
@ -91,12 +278,18 @@
#include "options.hh" #include "options.hh"
#include "utils.hh" #include "utils.hh"
#include "demod.hh"
#include "siggen.hh" #include "siggen.hh"
#include "buffernode.hh" #include "buffernode.hh"
#include "wavfile.hh" #include "wavfile.hh"
#include "firfilter.hh" #include "firfilter.hh"
#include "autocast.hh" #include "autocast.hh"
#include "freqshift.hh"
#include "interpolate.hh"
#include "subsample.hh"
#include "baseband.hh"
#include "demod.hh"
#include "psk31.hh"
#ifdef SDR_WITH_FFTW #ifdef SDR_WITH_FFTW
#include "filternode.hh" #include "filternode.hh"

@ -104,7 +104,7 @@ public:
}; };
/** A simple node, that allows to balance the IQ signal. */ /** A simple node, that allows to balance an IQ signal. */
template <class Scalar> template <class Scalar>
class IQBalance: public Sink< std::complex<Scalar> >, public Source class IQBalance: public Sink< std::complex<Scalar> >, public Source
{ {
@ -113,6 +113,10 @@ public:
typedef typename Traits<Scalar>::SScalar SScalar; typedef typename Traits<Scalar>::SScalar SScalar;
public: public:
/** Constructor.
* @param balance Specifies the balance between the I and Q chanel. If @c balance = 1, only the
* I chanel remains, on @c balance = -1 only the Q chanel remains and on @c balance = 0
* both chanels are balanced equally. */
IQBalance(double balance=0.0) IQBalance(double balance=0.0)
: Sink< std::complex<Scalar> >(), Source(), _realFact(1), _imagFact(1) : Sink< std::complex<Scalar> >(), Source(), _realFact(1), _imagFact(1)
{ {
@ -128,10 +132,12 @@ public:
} }
} }
/** Destructor. */
virtual ~IQBalance() { virtual ~IQBalance() {
_buffer.unref(); _buffer.unref();
} }
/** Retunrs the balance. */
double balance() const { double balance() const {
if (_realFact != (1<<8)) { if (_realFact != (1<<8)) {
return (double(_realFact)/(1<<8)-1); return (double(_realFact)/(1<<8)-1);
@ -139,6 +145,7 @@ public:
return (1-double(_imagFact)/(1<<8)); return (1-double(_imagFact)/(1<<8));
} }
/** Sets the I/Q balance. */
void setBalance(double balance) { void setBalance(double balance) {
if (balance < 0) { if (balance < 0) {
// scale real part // scale real part
@ -152,6 +159,7 @@ public:
} }
} }
/** Configures the node. */
virtual void config(const Config &src_cfg) { virtual void config(const Config &src_cfg) {
// Check if config is complete // Check if config is complete
if (! src_cfg.hasBufferSize()) { return; } if (! src_cfg.hasBufferSize()) { return; }
@ -162,6 +170,7 @@ public:
this->setConfig(cfg); this->setConfig(cfg);
} }
/** Processes a buffer. */
virtual void process(const Buffer<std::complex<Scalar> > &buffer, bool allow_overwrite) { virtual void process(const Buffer<std::complex<Scalar> > &buffer, bool allow_overwrite) {
if (allow_overwrite) { if (allow_overwrite) {
_process(buffer, buffer); _process(buffer, buffer);
@ -173,6 +182,7 @@ public:
} }
protected: protected:
/** The actual implementation. */
void _process(const Buffer< std::complex<Scalar> > &in, const Buffer< std::complex<Scalar> > &out) { void _process(const Buffer< std::complex<Scalar> > &in, const Buffer< std::complex<Scalar> > &out) {
for (size_t i=0; i<in.size(); i++) { for (size_t i=0; i<in.size(); i++) {
out[i] = std::complex<Scalar>((_realFact*SScalar(in[i].real()))/(1<<8), out[i] = std::complex<Scalar>((_realFact*SScalar(in[i].real()))/(1<<8),
@ -181,8 +191,11 @@ protected:
} }
protected: protected:
/** Scaleing factor for the real part. */
int32_t _realFact; int32_t _realFact;
/** Scaleing factor for the imaginary part. */
int32_t _imagFact; int32_t _imagFact;
/** The working buffer. */
Buffer< std::complex<Scalar> > _buffer; Buffer< std::complex<Scalar> > _buffer;
}; };

Loading…
Cancel
Save