#ifndef __SDR_DEMOD_HH__ #define __SDR_DEMOD_HH__ #include "node.hh" #include "traits.hh" #include "config.hh" #include "combine.hh" #include "logger.hh" #include "math.hh" namespace sdr { /** Amplitude modulation (AM) demodulator from an I/Q signal. * @ingroup demods */ template class AMDemod : public Sink< std::complex >, public Source { public: /** Constructor. */ AMDemod() : Sink< std::complex >(), Source() { // pass... } /** Destructor. */ virtual ~AMDemod() { // free buffers _buffer.unref(); } /** Configures the AM demod. */ virtual void config(const Config &src_cfg) { // Requires type & buffer size if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; } // Check if buffer type matches template if (Config::typeId< std::complex >() != src_cfg.type()) { ConfigError err; err << "Can not configure AMDemod: Invalid type " << src_cfg.type() << ", expected " << Config::typeId< std::complex >(); throw err; } // Unreference previous buffer _buffer.unref(); // Allocate buffer _buffer = Buffer(src_cfg.bufferSize()); LogMessage msg(LOG_DEBUG); msg << "Configure AMDemod: " << this << std::endl << " input type: " << Traits< std::complex >::scalarId << std::endl << " output type: " << Traits::scalarId << std::endl << " sample rate: " << src_cfg.sampleRate() << std::endl << " buffer size: " << src_cfg.bufferSize(); Logger::get().log(msg); // Propergate config this->setConfig(Config(Config::typeId(), src_cfg.sampleRate(), src_cfg.bufferSize(), src_cfg.numBuffers())); } /** Handles the I/Q input buffer. */ virtual void process(const Buffer > &buffer, bool allow_overwrite) { Buffer out_buffer; // If source allow to overwrite the buffer, use it otherwise rely on own buffer if (allow_overwrite) { out_buffer = Buffer(buffer); } else { out_buffer = _buffer; } // Perform demodulation for (size_t i=0; isend(out_buffer.head(buffer.size()), true); } protected: /** The output buffer. */ Buffer _buffer; }; /** SSB upper side band (USB) demodulator from an I/Q signal. * @ingroup demods */ template class USBDemod : public Sink< std::complex >, public Source { public: /** The complex input scalar. */ typedef std::complex CScalar; /** The real compute scalar. */ typedef typename Traits::SScalar SScalar; public: /** Constructor. */ USBDemod() : Sink(), Source() { // pass... } /** Destructor. */ virtual ~USBDemod() { _buffer.unref(); } /** Configures the USB demodulator. */ virtual void config(const Config &src_cfg) { // Requires type & buffer size if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; } // Check if buffer type matches template if (Config::typeId() != src_cfg.type()) { ConfigError err; err << "Can not configure USBDemod: Invalid type " << src_cfg.type() << ", expected " << Config::typeId(); throw err; } // Unreference previous buffer _buffer.unref(); // Allocate buffer _buffer = Buffer(src_cfg.bufferSize()); LogMessage msg(LOG_DEBUG); msg << "Configure USBDemod: " << this << std::endl << " input type: " << Traits< std::complex >::scalarId << std::endl << " output type: " << Traits::scalarId << std::endl << " sample rate: " << src_cfg.sampleRate() << std::endl << " buffer size: " << src_cfg.bufferSize(); Logger::get().log(msg); // Propergate config this->setConfig(Config(Config::typeId(), src_cfg.sampleRate(), src_cfg.bufferSize(), 1)); } /** Performs the demodulation. */ virtual void process(const Buffer &buffer, bool allow_overwrite) { if (allow_overwrite) { // Process in-place _process(buffer, Buffer(buffer)); } else { // Store result in buffer _process(buffer, _buffer); } } protected: /** The actual demodulation. */ void _process(const Buffer< std::complex > &in, const Buffer< Scalar> &out) { for (size_t i=0; isend(out.head(in.size())); } protected: /** The output buffer. */ Buffer _buffer; }; /** Demodulates FM from an I/Q signal. * This node only implements the demodulation of the signal, the needed post-filtering (deemphasize) * is implemented in a separate node, @c sdr::FMDeemph. * @ingroup demods */ template class FMDemod: public Sink< std::complex >, public Source { public: /** The super scalar. */ typedef typename Traits::SScalar SScalar; public: /** Constructor. */ FMDemod(): Sink< std::complex >(), Source(), _shift(0), _can_overwrite(false) { _shift = 8*(sizeof(oScalar)-sizeof(iScalar)); } /** Destructor. */ virtual ~FMDemod() { _buffer.unref(); } /** Configures the FM demodulator. */ virtual void config(const Config &src_cfg) { // Requires type & buffer size if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; } // Check if buffer type matches template if (Config::typeId< std::complex >() != src_cfg.type()) { ConfigError err; err << "Can not configure FMDemod: Invalid type " << src_cfg.type() << ", expected " << Config::typeId< std::complex >(); throw err; } // Unreference buffer if non-empty if (! _buffer.isEmpty()) { _buffer.unref(); } // Allocate buffer _buffer = Buffer(src_cfg.bufferSize()); // reset last value _last_value = 0; // Check if FM demod can be performed in-place _can_overwrite = (sizeof(std::complex) >= sizeof(oScalar)); LogMessage msg(LOG_DEBUG); msg << "Configured FMDemod node: " << this << std::endl << " sample-rate: " << src_cfg.sampleRate() << std::endl << " in-type / out-type: " << src_cfg.type() << " / " << Config::typeId() << std::endl << " in-place: " << (_can_overwrite ? "true" : "false") << std::endl << " output scale: 2^" << _shift; Logger::get().log(msg); // Propergate config this->setConfig(Config(Config::typeId(), src_cfg.sampleRate(), src_cfg.bufferSize(), 1)); } /** Performs the FM demodulation. */ virtual void process(const Buffer > &buffer, bool allow_overwrite) { if (0 == buffer.size()) { return; } if (allow_overwrite && _can_overwrite) { _process(buffer, Buffer(buffer)); } else { _process(buffer, _buffer); } } protected: /** The actual demodulation. */ void _process(const Buffer< std::complex > &in, const Buffer &out) { // The last input value std::complex last_value = _last_value; // calc first value SScalar a = (SScalar(in[0].real())*SScalar(last_value.real()))/2 + (SScalar(in[0].imag())*SScalar(last_value.imag()))/2; SScalar b = (SScalar(in[0].imag())*SScalar(last_value.real()))/2 - (SScalar(in[0].real())*SScalar(last_value.imag()))/2; a >>= Traits::shift; b >>= Traits::shift; // update last value last_value = in[0]; // calc output (prob. overwriting the last value) out[0] = fast_atan2(a, b); //out[0] = (1<<12)*(std::atan2(float(a),float(b))/M_PI); // Calc remaining values for (size_t i=1; i>= Traits::shift; b >>= Traits::shift; last_value = in[i]; out[i] = fast_atan2(a, b); //out[i] = (1<<12)*(std::atan2(float(a),float(b))/M_PI); } // Store last value _last_value = last_value; // propergate result this->send(out.head(in.size())); } protected: /** Output rescaling. */ int _shift; /** The last input value. */ std::complex _last_value; /** If true, in-place demodulation is poissible. */ bool _can_overwrite; /** The output buffer, unused if demodulation is performed in-place. */ Buffer _buffer; }; /** A tiny node to de-emphasize the higher frequencies of a FM transmitted audio signal. * @ingroup filters */ template class FMDeemph: public Sink, public Source { public: /** Constructor. */ FMDeemph(bool enabled=true) : Sink(), Source(), _enabled(enabled), _alpha(0), _avg(0), _buffer(0) { // pass... } /** Destructor. */ virtual ~FMDeemph() { _buffer.unref(); } /** Returns true if the filter node is enabled. */ inline bool isEnabled() const { return _enabled; } /** Enable/Disable the filter node. */ inline void enable(bool enabled) { _enabled = enabled; } /** Configures the node. */ virtual void config(const Config &src_cfg) { // Requires type, sample rate & buffer size if (!src_cfg.hasType() || !src_cfg.hasSampleRate() || !src_cfg.hasBufferSize()) { return; } // Check if buffer type matches template if (Config::typeId() != src_cfg.type()) { ConfigError err; err << "Can not configure FMDeemph: Invalid type " << src_cfg.type() << ", expected " << Config::typeId(); throw err; } // Determine filter constant alpha: _alpha = (int)round( 1.0/( (1.0-exp(-1.0/(src_cfg.sampleRate() * 75e-6) )) ) ); // Reset average: _avg = 0; // Unreference previous buffer _buffer.unref(); // Allocate buffer: _buffer = Buffer(src_cfg.bufferSize()); LogMessage msg(LOG_DEBUG); msg << "Configured FMDDeemph node: " << this << std::endl << " sample-rate: " << src_cfg.sampleRate() << std::endl << " type: " << src_cfg.type(); Logger::get().log(msg); // Propergate config: this->setConfig(Config(src_cfg.type(), src_cfg.sampleRate(), src_cfg.bufferSize(), 1)); } /** Dispatches in- or out-of-place filtering. */ virtual void process(const Buffer &buffer, bool allow_overwrite) { // Skip if disabled: if (!_enabled) { this->send(buffer, allow_overwrite); return; } // Process in-place or not if (allow_overwrite) { _process(buffer, buffer); this->send(buffer, allow_overwrite); } else { _process(buffer, _buffer); this->send(_buffer.head(buffer.size()), false); } } protected: /** Performs the actual filtering. */ void _process(const Buffer &in, const Buffer &out) { for (size_t i=0; i 0) { _avg += (diff + _alpha/2) / _alpha; } else { _avg += (diff - _alpha/2) / _alpha; } // Store result out[i] = _avg; } } protected: /** If true, the filter is enabled. If not, the node is a NOP. */ bool _enabled; /** Filter constant. */ int _alpha; /** Current averaged value. */ Scalar _avg; /** The output buffer. */ Buffer _buffer; }; } #endif // __SDR_DEMOD_HH__