libsdr  0.1.0
A simple SDR library
demod.hh
1 #ifndef __SDR_DEMOD_HH__
2 #define __SDR_DEMOD_HH__
3 
4 #include "node.hh"
5 #include "traits.hh"
6 #include "config.hh"
7 #include "combine.hh"
8 #include "logger.hh"
9 #include "math.hh"
10 
11 
12 namespace sdr {
13 
14 
17 template <class Scalar>
18 class AMDemod
19  : public Sink< std::complex<Scalar> >, public Source
20 {
21 public:
23  AMDemod() : Sink< std::complex<Scalar> >(), Source()
24  {
25  // pass...
26  }
27 
29  virtual ~AMDemod() {
30  // free buffers
31  _buffer.unref();
32  }
33 
35  virtual void config(const Config &src_cfg) {
36  // Requires type & buffer size
37  if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; }
38  // Check if buffer type matches template
39  if (Config::typeId< std::complex<Scalar> >() != src_cfg.type()) {
40  ConfigError err;
41  err << "Can not configure AMDemod: Invalid type " << src_cfg.type()
42  << ", expected " << Config::typeId< std::complex<Scalar> >();
43  throw err;
44  }
45 
46  // Unreference previous buffer
47  _buffer.unref();
48  // Allocate buffer
49  _buffer = Buffer<Scalar>(src_cfg.bufferSize());
50 
51  LogMessage msg(LOG_DEBUG);
52  msg << "Configure AMDemod: " << this << std::endl
53  << " input type: " << Traits< std::complex<Scalar> >::scalarId << std::endl
54  << " output type: " << Traits<Scalar>::scalarId << std::endl
55  << " sample rate: " << src_cfg.sampleRate() << std::endl
56  << " buffer size: " << src_cfg.bufferSize();
57  Logger::get().log(msg);
58 
59  // Propergate config
60  this->setConfig(Config(Config::typeId<Scalar>(), src_cfg.sampleRate(),
61  src_cfg.bufferSize(), src_cfg.numBuffers()));
62  }
63 
65  virtual void process(const Buffer<std::complex<Scalar> > &buffer, bool allow_overwrite)
66  {
67  Buffer<Scalar> out_buffer;
68  // If source allow to overwrite the buffer, use it otherwise rely on own buffer
69  if (allow_overwrite) { out_buffer = Buffer<Scalar>(buffer); }
70  else { out_buffer = _buffer; }
71 
72  // Perform demodulation
73  for (size_t i=0; i<buffer.size(); i++) {
74  out_buffer[i] = std::sqrt(buffer[i].real()*buffer[i].real() +
75  buffer[i].imag()*buffer[i].imag());
76  }
77 
78  // If the source allowed to overwrite the buffer, this source will allow it too.
79  // If this source used the internal buffer (_buffer), it allows to overwrite it anyway.
80  this->send(out_buffer.head(buffer.size()), true);
81  }
82 
83 protected:
86 };
87 
88 
91 template <class Scalar>
92 class USBDemod
93  : public Sink< std::complex<Scalar> >, public Source
94 {
95 public:
97  typedef std::complex<Scalar> CScalar;
99  typedef typename Traits<Scalar>::SScalar SScalar;
100 
101 public:
103  USBDemod() : Sink<CScalar>(), Source()
104  {
105  // pass...
106  }
107 
109  virtual ~USBDemod() {
110  _buffer.unref();
111  }
112 
114  virtual void config(const Config &src_cfg) {
115  // Requires type & buffer size
116  if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; }
117  // Check if buffer type matches template
118  if (Config::typeId<CScalar>() != src_cfg.type()) {
119  ConfigError err;
120  err << "Can not configure USBDemod: Invalid type " << src_cfg.type()
121  << ", expected " << Config::typeId<CScalar>();
122  throw err;
123  }
124 
125  // Unreference previous buffer
126  _buffer.unref();
127  // Allocate buffer
128  _buffer = Buffer<Scalar>(src_cfg.bufferSize());
129 
130  LogMessage msg(LOG_DEBUG);
131  msg << "Configure USBDemod: " << this << std::endl
132  << " input type: " << Traits< std::complex<Scalar> >::scalarId << std::endl
133  << " output type: " << Traits<Scalar>::scalarId << std::endl
134  << " sample rate: " << src_cfg.sampleRate() << std::endl
135  << " buffer size: " << src_cfg.bufferSize();
136  Logger::get().log(msg);
137 
138  // Propergate config
139  this->setConfig(Config(Config::typeId<Scalar>(), src_cfg.sampleRate(),
140  src_cfg.bufferSize(), 1));
141  }
142 
144  virtual void process(const Buffer<CScalar> &buffer, bool allow_overwrite) {
145  if (allow_overwrite) {
146  // Process in-place
147  _process(buffer, Buffer<Scalar>(buffer));
148  } else {
149  // Store result in buffer
150  _process(buffer, _buffer);
151  }
152  }
153 
154 protected:
156  void _process(const Buffer< std::complex<Scalar> > &in, const Buffer< Scalar> &out) {
157  for (size_t i=0; i<in.size(); i++) {
158  out[i] = (SScalar(std::real(in[i])) + SScalar(std::imag(in[i])))/2;
159  }
160  this->send(out.head(in.size()));
161  }
162 
163 protected:
166 };
167 
168 
169 
174 template <class iScalar, class oScalar=iScalar>
175 class FMDemod: public Sink< std::complex<iScalar> >, public Source
176 {
177 public:
180 
181 public:
184  Sink< std::complex<iScalar> >(), Source(), _shift(0), _can_overwrite(false)
185  {
186  _shift = 8*(sizeof(oScalar)-sizeof(iScalar));
187  }
188 
190  virtual ~FMDemod() {
191  _buffer.unref();
192  }
193 
195  virtual void config(const Config &src_cfg) {
196  // Requires type & buffer size
197  if (!src_cfg.hasType() || !src_cfg.hasBufferSize()) { return; }
198  // Check if buffer type matches template
199  if (Config::typeId< std::complex<iScalar> >() != src_cfg.type()) {
200  ConfigError err;
201  err << "Can not configure FMDemod: Invalid type " << src_cfg.type()
202  << ", expected " << Config::typeId< std::complex<iScalar> >();
203  throw err;
204  }
205  // Unreference buffer if non-empty
206  if (! _buffer.isEmpty()) { _buffer.unref(); }
207  // Allocate buffer
208  _buffer = Buffer<oScalar>(src_cfg.bufferSize());
209  // reset last value
210  _last_value = 0;
211  // Check if FM demod can be performed in-place
212  _can_overwrite = (sizeof(std::complex<iScalar>) >= sizeof(oScalar));
213 
214  LogMessage msg(LOG_DEBUG);
215  msg << "Configured FMDemod node: " << this << std::endl
216  << " sample-rate: " << src_cfg.sampleRate() << std::endl
217  << " in-type / out-type: " << src_cfg.type()
218  << " / " << Config::typeId<oScalar>() << std::endl
219  << " in-place: " << (_can_overwrite ? "true" : "false") << std::endl
220  << " output scale: 2^" << _shift;
221  Logger::get().log(msg);
222 
223  // Propergate config
224  this->setConfig(Config(Config::typeId<oScalar>(), src_cfg.sampleRate(),
225  src_cfg.bufferSize(), 1));
226  }
227 
229  virtual void process(const Buffer<std::complex<iScalar> > &buffer, bool allow_overwrite)
230  {
231  if (0 == buffer.size()) { return; }
232 
233  if (allow_overwrite && _can_overwrite) {
234  _process(buffer, Buffer<oScalar>(buffer));
235  } else {
236  _process(buffer, _buffer);
237  }
238  }
239 
240 protected:
242  void _process(const Buffer< std::complex<iScalar> > &in, const Buffer<oScalar> &out)
243  {
244  // The last input value
245  std::complex<iScalar> last_value = _last_value;
246  // calc first value
247  SScalar a = (SScalar(in[0].real())*SScalar(last_value.real()))/2
248  + (SScalar(in[0].imag())*SScalar(last_value.imag()))/2;
249  SScalar b = (SScalar(in[0].imag())*SScalar(last_value.real()))/2
250  - (SScalar(in[0].real())*SScalar(last_value.imag()))/2;
252  // update last value
253  last_value = in[0];
254  // calc output (prob. overwriting the last value)
255  out[0] = fast_atan2<iScalar, oScalar>(a, b);
256  //out[0] = (1<<12)*(std::atan2(float(a),float(b))/M_PI);
257 
258  // Calc remaining values
259  for (size_t i=1; i<in.size(); i++) {
260  a = (SScalar(in[i].real())*SScalar(last_value.real()))/2
261  + (SScalar(in[i].imag())*SScalar(last_value.imag()))/2;
262  b = (SScalar(in[i].imag())*SScalar(last_value.real()))/2
263  - (SScalar(in[i].real())*SScalar(last_value.imag()))/2;
265  last_value = in[i];
266  out[i] = fast_atan2<iScalar,oScalar>(a, b);
267  //out[i] = (1<<12)*(std::atan2(float(a),float(b))/M_PI);
268  }
269 
270  // Store last value
271  _last_value = last_value;
272  // propergate result
273  this->send(out.head(in.size()));
274  }
275 
276 
277 protected:
279  int _shift;
281  std::complex<iScalar> _last_value;
286 };
287 
288 
291 template <class Scalar>
292 class FMDeemph: public Sink<Scalar>, public Source
293 {
294 public:
296  FMDeemph(bool enabled=true)
297  : Sink<Scalar>(), Source(), _enabled(enabled), _alpha(0), _avg(0), _buffer(0)
298  {
299  // pass...
300  }
301 
303  virtual ~FMDeemph() {
304  _buffer.unref();
305  }
306 
308  inline bool isEnabled() const { return _enabled; }
309 
311  inline void enable(bool enabled) { _enabled = enabled; }
312 
314  virtual void config(const Config &src_cfg) {
315  // Requires type, sample rate & buffer size
316  if (!src_cfg.hasType() || !src_cfg.hasSampleRate() || !src_cfg.hasBufferSize()) { return; }
317  // Check if buffer type matches template
318  if (Config::typeId<Scalar>() != src_cfg.type()) {
319  ConfigError err;
320  err << "Can not configure FMDeemph: Invalid type " << src_cfg.type()
321  << ", expected " << Config::typeId<Scalar>();
322  throw err;
323  }
324  // Determine filter constant alpha:
325  _alpha = (int)round(
326  1.0/( (1.0-exp(-1.0/(src_cfg.sampleRate() * 75e-6) )) ) );
327  // Reset average:
328  _avg = 0;
329  // Unreference previous buffer
330  _buffer.unref();
331  // Allocate buffer:
332  _buffer = Buffer<Scalar>(src_cfg.bufferSize());
333 
334  LogMessage msg(LOG_DEBUG);
335  msg << "Configured FMDDeemph node: " << this << std::endl
336  << " sample-rate: " << src_cfg.sampleRate() << std::endl
337  << " type: " << src_cfg.type();
338  Logger::get().log(msg);
339 
340  // Propergate config:
341  this->setConfig(Config(src_cfg.type(), src_cfg.sampleRate(), src_cfg.bufferSize(), 1));
342  }
343 
345  virtual void process(const Buffer<Scalar> &buffer, bool allow_overwrite)
346  {
347  // Skip if disabled:
348  if (!_enabled) { this->send(buffer, allow_overwrite); return; }
349 
350  // Process in-place or not
351  if (allow_overwrite) {
352  _process(buffer, buffer);
353  this->send(buffer, allow_overwrite);
354  } else {
355  _process(buffer, _buffer);
356  this->send(_buffer.head(buffer.size()), false);
357  }
358  }
359 
360 protected:
362  void _process(const Buffer<Scalar> &in, const Buffer<Scalar> &out) {
363  for (size_t i=0; i<in.size(); i++) {
364  // Update average:
365  Scalar diff = in[i] - _avg;
366  if (diff > 0) { _avg += (diff + _alpha/2) / _alpha; }
367  else { _avg += (diff - _alpha/2) / _alpha; }
368  // Store result
369  out[i] = _avg;
370  }
371  }
372 
373 protected:
375  bool _enabled;
377  int _alpha;
379  Scalar _avg;
382 };
383 
384 }
385 
386 #endif // __SDR_DEMOD_HH__
bool isEnabled() const
Returns true if the filter node is enabled.
Definition: demod.hh:308
A collection of configuration information that is send by a source to all connected sinks to properga...
Definition: node.hh:35
virtual void config(const Config &src_cfg)
Configures the USB demodulator.
Definition: demod.hh:114
std::complex< Scalar > CScalar
The complex input scalar.
Definition: demod.hh:97
virtual ~FMDemod()
Destructor.
Definition: demod.hh:190
virtual ~USBDemod()
Destructor.
Definition: demod.hh:109
void _process(const Buffer< Scalar > &in, const Buffer< Scalar > &out)
Performs the actual filtering.
Definition: demod.hh:362
virtual void send(const RawBuffer &buffer, bool allow_overwrite=false)
Sends the given buffer to all connected sinks.
Definition: node.cc:67
Traits< iScalar >::SScalar SScalar
The super scalar.
Definition: demod.hh:179
virtual void config(const Config &src_cfg)
Configures the FM demodulator.
Definition: demod.hh:195
Typed sink.
Definition: node.hh:192
Definition: autocast.hh:8
size_t numBuffers() const
Returns the max.
Definition: node.hh:89
A tiny node to de-emphasize the higher frequencies of a FM transmitted audio signal.
Definition: demod.hh:292
bool isEmpty() const
Returns true if the buffer is invalid/empty.
Definition: buffer.hh:77
Definition: operators.hh:9
Buffer< Scalar > _buffer
The output buffer.
Definition: demod.hh:165
bool hasSampleRate() const
If true, the configuration has a sample rate.
Definition: node.hh:75
virtual ~FMDeemph()
Destructor.
Definition: demod.hh:303
Generic source class.
Definition: node.hh:213
int _shift
Output rescaling.
Definition: demod.hh:279
size_t size() const
Returns the number of elements of type T in this buffer.
Definition: buffer.hh:166
bool hasType() const
If true, the configuration has a type.
Definition: node.hh:69
bool _enabled
If true, the filter is enabled.
Definition: demod.hh:375
virtual void config(const Config &src_cfg)
Configures the node.
Definition: demod.hh:314
int _alpha
Filter constant.
Definition: demod.hh:377
virtual void process(const Buffer< CScalar > &buffer, bool allow_overwrite)
Performs the demodulation.
Definition: demod.hh:144
virtual void process(const Buffer< Scalar > &buffer, bool allow_overwrite)
Dispatches in- or out-of-place filtering.
Definition: demod.hh:345
virtual void setConfig(const Config &config)
Stores the configuration and propergates it if the configuration has been changed.
Definition: node.cc:98
Amplitude modulation (AM) demodulator from an I/Q signal.
Definition: demod.hh:18
virtual ~AMDemod()
Destructor.
Definition: demod.hh:29
Buffer< Scalar > _buffer
The output buffer.
Definition: demod.hh:381
Demodulates FM from an I/Q signal.
Definition: demod.hh:175
Buffer< T > head(size_t n) const
Returns a new view on this buffer.
Definition: buffer.hh:237
void log(const LogMessage &message)
Logs a message.
Definition: logger.cc:100
FMDemod()
Constructor.
Definition: demod.hh:183
Buffer< oScalar > _buffer
The output buffer, unused if demodulation is performed in-place.
Definition: demod.hh:285
bool _can_overwrite
If true, in-place demodulation is poissible.
Definition: demod.hh:283
Type type() const
Returns the type.
Definition: node.hh:71
static Type typeId()
Returns the type-id of the template type.
virtual void process(const Buffer< std::complex< Scalar > > &buffer, bool allow_overwrite)
Handles the I/Q input buffer.
Definition: demod.hh:65
static Logger & get()
Returns the singleton instance of the logger.
Definition: logger.cc:89
The configuration error class.
Definition: exception.hh:24
A log message.
Definition: logger.hh:22
size_t bufferSize() const
Returns the max.
Definition: node.hh:83
USBDemod()
Constructor.
Definition: demod.hh:103
virtual void process(const Buffer< std::complex< iScalar > > &buffer, bool allow_overwrite)
Performs the FM demodulation.
Definition: demod.hh:229
void _process(const Buffer< std::complex< iScalar > > &in, const Buffer< oScalar > &out)
The actual demodulation.
Definition: demod.hh:242
void unref()
Dereferences the buffer.
Definition: buffer.cc:63
FMDeemph(bool enabled=true)
Constructor.
Definition: demod.hh:296
void enable(bool enabled)
Enable/Disable the filter node.
Definition: demod.hh:311
Buffer< Scalar > _buffer
The output buffer.
Definition: demod.hh:85
SSB upper side band (USB) demodulator from an I/Q signal.
Definition: demod.hh:92
Forward declaration of type tratis template.
Definition: traits.hh:20
virtual void config(const Config &src_cfg)
Configures the AM demod.
Definition: demod.hh:35
void _process(const Buffer< std::complex< Scalar > > &in, const Buffer< Scalar > &out)
The actual demodulation.
Definition: demod.hh:156
AMDemod()
Constructor.
Definition: demod.hh:23
Traits< Scalar >::SScalar SScalar
The real compute scalar.
Definition: demod.hh:99
bool hasBufferSize() const
If true, the configuration has a buffer size.
Definition: node.hh:81
double sampleRate() const
Returns the sample rate.
Definition: node.hh:77
std::complex< iScalar > _last_value
The last input value.
Definition: demod.hh:281
Scalar _avg
Current averaged value.
Definition: demod.hh:379