libsdr  0.1.0
A simple SDR library
psk31.hh
1 #ifndef __SDR_PSK31_HH__
2 #define __SDR_PSK31_HH__
3 
4 #include "node.hh"
5 #include "traits.hh"
6 #include "interpolate.hh"
7 
8 
9 namespace sdr {
10 
16 template <class Scalar>
17 class BPSK31: public Sink< std::complex<Scalar> >, public Source
18 {
19 public:
35  BPSK31(double dF=0.1)
36  : Sink< std::complex<Scalar> >(), Source(),
37  _P(0), _F(0), _Fmin(-dF), _Fmax(dF)
38  {
39  // Assemble carrier PLL gains:
40  double damping = std::sqrt(2)/2;
41  double bw = M_PI/100;
42  double tmp = 1. + 2*damping*bw + bw*bw;
43  _alpha = 4*damping*bw/tmp;
44  _beta = 4*bw*bw/tmp;
45 
46  // Init delay line for the interpolating sub-sampler
48  for (size_t i=0; i<16; i++) { _dl[i] = 0; }
49  _mu = 0.25; _gain_mu = 0.01;
50 
51  // constant phase shift of the signal
52  _theta = 0;
53 
54  // Phase detection:
56  _omega_rel = 0.001; _gain_omega = 0.001;
57  _p_0T = _p_1T = _p_2T = 0;
58  _c_0T = _c_1T = _c_2T = 0;
59 
60  // Super-sample (64 phase samples per symbol)
61  _superSample = 64;
62  }
63 
65  virtual ~BPSK31() {
66  // unreference buffers
67  _dl.unref();
68  _hist.unref();
69  _buffer.unref();
70  }
71 
72  virtual void config(const Config &src_cfg)
73  {
74  // Requires type, sample rate & buffer size
75  if (!src_cfg.hasType() || !src_cfg.hasSampleRate() || !src_cfg.hasBufferSize()) { return; }
76 
77  // Check buffer type
78  if (Config::typeId< std::complex<Scalar> >() != src_cfg.type()) {
79  ConfigError err;
80  err << "Can not configure BPSK31: Invalid type " << src_cfg.type()
81  << ", expected " << Config::typeId< std::complex<Scalar> >();
82  throw err;
83  }
84 
85  // Check input sample rate
86  double Fs = src_cfg.sampleRate();
87  if (2000 > Fs) {
88  ConfigError err;
89  err << "Can not configure BPSK31: Input sample rate too low! The BPSK31 node requires at "
90  << "least a sample rate of 2000Hz, got " << Fs << "Hz";
91  throw err;
92  }
93 
94  // Compute samples per symbol
95  _omega = Fs/(_superSample*31.25);
96  // Obtain limits for the sub-sample rate
97  _min_omega = _omega*(1.0 - _omega_rel);
98  _max_omega = _omega*(1.0 + _omega_rel);
99 
100  // Decoding history, contains up to 64 phases (if in sync)
102  _hist_idx = 0;
104 
105  // Output buffer
106  size_t bsize = 1 + int(Fs/31.25);
107  _buffer = Buffer<uint8_t>(bsize);
108 
109  // This node sends a bit-stream with 31.25 baud.
110  this->setConfig(Config(Traits<uint8_t>::scalarId, 31.25, src_cfg.bufferSize(), 1));
111  }
112 
113 
114  virtual void process(const Buffer< std::complex<Scalar> > &buffer, bool allow_overwrite) {
115  size_t i=0, o=0;
116  while (i<buffer.size()) {
117  // First, fill sampler...
118  while ( (_mu > 1) && (i<buffer.size()) ) {
119  _updateSampler(buffer[i]); i++;
120  }
121  // Then, ...
122  if (i<buffer.size()) {
123  // Subsample
124  std::complex<float> sample = interpolate(_dl.sub(_dl_idx, 8), _mu);
125  // Update error tracking
126  _errorTracking(sample);
127  // Update carrier PLL
128  _updatePLL(sample);
129  // done. real(sample) constains phase of symbol
130 
131  // Now fill history with phase constellation and try to decode a bit
132  _hist[_hist_idx] = std::real(sample);
133  // Check if there is a constellation transition at the current time
134  if ((_hist_idx>1) && (_hasTransition())) {
135  if (_hist_idx<(_superSample/2)) {
136  // If less than superSample values in hist -> drop
137  _hist_idx = 0;
138  } else {
139  // Otherwise decode
140  int cconst = _currentContellation();
141  _buffer[o++] = (_last_constellation == cconst);
142  _last_constellation = cconst;
143  _hist_idx = 0;
144  }
145  } else if (_hist_idx == (_superSample-1)) {
146  // If the symbol is complete:
147  int cconst = _currentContellation();
148  _buffer[o++] = (_last_constellation == cconst);
149  _last_constellation = cconst;
150  _hist_idx = 0;
151  } else {
152  // Next sample
153  _hist_idx++;
154  }
155  }
156  }
157  // If at least 1 bit was decoded -> send result
158  if (o>0) { this->send(_buffer.head(o)); }
159  }
160 
161 
162 protected:
164  inline bool _hasTransition() const {
165  return ((_hist[_hist_idx-1]>=0) && (_hist[_hist_idx]<=0)) ||
166  ((_hist[_hist_idx-1]<=0) && (_hist[_hist_idx]>=0));
167  }
168 
170  inline int _currentContellation() const {
171  float value = 0;
172  for (size_t i=0; i<=_hist_idx; i++) { value += _hist[i]; }
173  return (value > 0) ? 1 : -1;
174  }
175 
177  inline float _phaseError(const std::complex<float> &value) const {
178  float r2 = value.real()*value.real();
179  float i2 = value.imag()*value.imag();
180  float nrm2 = r2+i2;
181  if (0 == nrm2) { return 0; }
182  return -value.real()*value.imag()/nrm2;
183  }
184 
186  inline void _updatePLL(const std::complex<float> &sample) {
187  float phi = _phaseError(sample);
188  _F += _beta*phi;
189  _P += _F + _alpha*phi;
190  while (_P>( 2*M_PI)) { _P -= 2*M_PI; }
191  while (_P<(-2*M_PI)) { _P += 2*M_PI; }
192  _F = std::min(_Fmax, std::max(_Fmin, _F));
193  //std::cerr << "Update PLL: P=" << _P << "; F=" << _F << ", err= " << phi << std::endl;
194  }
195 
197  inline void _updateSampler(const std::complex<Scalar> &value) {
198  // decrease fractional sub-sample counter
199  _mu-=1;
200  // Update phase
201  _P += _F;
202  // Limit phase
203  while (_P>( 2*M_PI)) { _P -= 2*M_PI; }
204  while (_P<(-2*M_PI)) { _P += 2*M_PI; }
205  // Calc down-conversion factor (consider look-up table)
206  std::complex<float> fac = std::exp(std::complex<float>(0, _P+_theta));
207  // Down-convert singal
208  std::complex<float> sample = fac * std::complex<float>(value.real(), value.imag());
209  // store sample into delay line
210  _dl[_dl_idx] = sample;
211  _dl[_dl_idx+8] = sample;
212  _dl_idx = (_dl_idx + 1) % 8;
213  }
214 
216  inline void _errorTracking(const std::complex<float> &sample) {
217  // Update last 2 constellation and phases
218  _p_2T = _p_1T; _p_1T = _p_0T; _p_0T = sample;
219  _c_2T = _c_1T; _c_1T = _c_0T; _c_0T = (sample.real() > 0) ? -1 : 1;
220  // Compute difference between phase and constellation
221  std::complex<float> x = (_c_0T - _c_2T) * std::conj(_p_1T);
222  std::complex<float> y = (_p_0T - _p_2T) * std::conj(_c_1T);
223  // Get phase error
224  float err = std::real(y-x);
225  if (err > 1.0) { err = 1.0; }
226  if (err < -1.0) { err = -1.0; }
227  // Update symbol rate (approx 31.25*64)
228  _omega += _gain_omega * err;
229  // Limit omega on _omega_mid +/- _omega*_omega_rel
230  _omega = std::max(_min_omega, std::min(_max_omega, _omega));
231  // Update mu as +_omega (samples per symbol) and a small correction term
232  _mu += _omega + _gain_mu * err;
233  }
234 
235 
236 protected:
238  size_t _superSample;
240  float _P;
242  float _F;
244  float _Fmin;
246  float _Fmax;
248  float _alpha;
250  float _beta;
254  size_t _dl_idx;
256  float _mu;
258  float _gain_mu;
260  float _theta;
262  float _omega;
264  float _omega_rel;
266  float _min_omega;
268  float _max_omega;
270  float _gain_omega;
272  std::complex<float> _p_0T;
274  std::complex<float> _p_1T;
276  std::complex<float> _p_2T;
278  std::complex<float> _c_0T;
280  std::complex<float> _c_1T;
282  std::complex<float> _c_2T;
286  size_t _hist_idx;
291 };
292 
293 
294 
299 class Varicode: public Sink<uint8_t>, public Source
300 {
301 public:
303  Varicode();
305  virtual ~Varicode();
307  virtual void config(const Config &src_cfg);
309  virtual void process(const Buffer<uint8_t> &buffer, bool allow_overwrite);
310 
311 protected:
313  uint16_t _value;
317  std::map<uint16_t, char> _code_table;
318 };
319 
320 
321 }
322 
323 
324 #endif // __SDR_PSK31_HH__
float _theta
Constant phase shift between real axis and first constellation.
Definition: psk31.hh:260
A collection of configuration information that is send by a source to all connected sinks to properga...
Definition: node.hh:35
Buffer< float > _hist
The last _superSample phases.
Definition: psk31.hh:284
size_t _superSample
Holds the number of phase constellations per bit.
Definition: psk31.hh:238
std::complex< float > _c_2T
Constellation at T=-2 (samples).
Definition: psk31.hh:282
std::complex< float > _p_0T
Phase at T = 0 (samples).
Definition: psk31.hh:272
float _phaseError(const std::complex< float > &value) const
Computes the phase error.
Definition: psk31.hh:177
float _gain_mu
Gain factor of the sub-sampler.
Definition: psk31.hh:258
std::map< uint16_t, char > _code_table
The conversion table.
Definition: psk31.hh:317
std::complex< float > _c_0T
Constellation at T=0 (samples).
Definition: psk31.hh:278
int _currentContellation() const
Returns the current constellation.
Definition: psk31.hh:170
virtual void config(const Config &src_cfg)
Needs to be implemented by any sub-type to check and perform the configuration of the node...
Definition: psk31.hh:72
virtual void send(const RawBuffer &buffer, bool allow_overwrite=false)
Sends the given buffer to all connected sinks.
Definition: node.cc:67
Typed sink.
Definition: node.hh:192
std::complex< float > _p_1T
Phase at T=-1 (samples).
Definition: psk31.hh:274
Definition: autocast.hh:8
Definition: operators.hh:9
bool hasSampleRate() const
If true, the configuration has a sample rate.
Definition: node.hh:75
Generic source class.
Definition: node.hh:213
float _omega_rel
Relative error of the subsample rate.
Definition: psk31.hh:264
A simple BPSK31 "demodulator".
Definition: psk31.hh:17
float _omega
Current sub-sample rate.
Definition: psk31.hh:262
float _Fmax
Upper frequency limit of the carrier PLL.
Definition: psk31.hh:246
Buffer< uint8_t > _buffer
The output buffer.
Definition: psk31.hh:315
Buffer< std::complex< float > > _dl
The delay line for the interpolating sub-sampler.
Definition: psk31.hh:252
int _last_constellation
The last output constellation.
Definition: psk31.hh:288
void _updatePLL(const std::complex< float > &sample)
Updates the PLL (_F and _P).
Definition: psk31.hh:186
bool hasType() const
If true, the configuration has a type.
Definition: node.hh:69
float _Fmin
Lower frequency limit of the carrier PLL.
Definition: psk31.hh:244
float _max_omega
Maximum of the sub-sample rate.
Definition: psk31.hh:268
std::complex< float > _c_1T
Constellation at T=-1 (samples).
Definition: psk31.hh:280
virtual void setConfig(const Config &config)
Stores the configuration and propergates it if the configuration has been changed.
Definition: node.cc:98
virtual ~Varicode()
Destructor.
Definition: psk31.cc:47
virtual void config(const Config &src_cfg)
Configures the node.
Definition: psk31.cc:52
Buffer< uint8_t > _buffer
Output buffer.
Definition: psk31.hh:290
Buffer< T > head(size_t n) const
Returns a new view on this buffer.
Definition: buffer.hh:237
float _mu
Holds the fractional sub-sampling counter.
Definition: psk31.hh:256
virtual ~BPSK31()
Destructor.
Definition: psk31.hh:65
float _F
Frequency of the carrier PLL.
Definition: psk31.hh:242
float _min_omega
Minimum of the sub-sample rate.
Definition: psk31.hh:266
void _errorTracking(const std::complex< float > &sample)
Updates the PPL state (_mu and _omega).
Definition: psk31.hh:216
Type type() const
Returns the type.
Definition: node.hh:71
float _beta
Gain factor of the carrier PLL.
Definition: psk31.hh:250
uint16_t _value
The shift register of the last received bits.
Definition: psk31.hh:313
static Type typeId()
Returns the type-id of the template type.
void _updateSampler(const std::complex< Scalar > &value)
Updates the sub-sampler.
Definition: psk31.hh:197
float _alpha
Gain factor of the carrier PLL.
Definition: psk31.hh:248
Buffer< T > sub(size_t offset, size_t len) const
Returns a new view on this buffer.
Definition: buffer.hh:231
virtual void process(const Buffer< uint8_t > &buffer, bool allow_overwrite)
Converts the input bit stream to ASCII chars.
Definition: psk31.cc:69
The configuration error class.
Definition: exception.hh:24
size_t bufferSize() const
Returns the max.
Definition: node.hh:83
Simple varicode (Huffman code) decoder node.
Definition: psk31.hh:299
size_t _hist_idx
Current phase history index.
Definition: psk31.hh:286
bool _hasTransition() const
Returns true if there is a phase transition at the current sample.
Definition: psk31.hh:164
float _gain_omega
Gain of the sub-sample rate correction.
Definition: psk31.hh:270
float _P
Phase of the carrier PLL.
Definition: psk31.hh:240
std::complex< float > _p_2T
Phase at T=-2 (samples).
Definition: psk31.hh:276
void unref()
Dereferences the buffer.
Definition: buffer.cc:63
Forward declaration of type tratis template.
Definition: traits.hh:20
BPSK31(double dF=0.1)
Constructs a new BPSK31 demodulator.
Definition: psk31.hh:35
virtual void process(const Buffer< std::complex< Scalar > > &buffer, bool allow_overwrite)
Needs to be implemented by any sub-type to process the received data.
Definition: psk31.hh:114
Varicode()
Constructor.
Definition: psk31.cc:7
A typed buffer.
Definition: buffer.hh:111
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
size_t _dl_idx
The current index of the delay line.
Definition: psk31.hh:254