SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
chanalyzer.cpp
Go to the documentation of this file.
1 // Copyright (C) 2017 Edouard Griffiths, F4EXB //
3 // //
4 // This program is free software; you can redistribute it and/or modify //
5 // it under the terms of the GNU General Public License as published by //
6 // the Free Software Foundation as version 3 of the License, or //
7 // (at your option) any later version. //
8 // //
9 // This program is distributed in the hope that it will be useful, //
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
12 // GNU General Public License V3 for more details. //
13 // //
14 // You should have received a copy of the GNU General Public License //
15 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
17 
18 #include <QTime>
19 #include <QDebug>
20 #include <stdio.h>
21 
22 #include "device/deviceapi.h"
23 #include "audio/audiooutput.h"
25 #include "dsp/downchannelizer.h"
26 #include "chanalyzer.h"
27 
31 
32 const QString ChannelAnalyzer::m_channelIdURI = "sdrangel.channel.chanalyzer";
33 const QString ChannelAnalyzer::m_channelId = "ChannelAnalyzer";
34 
36  ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
37  m_deviceAPI(deviceAPI),
38  m_sampleSink(0),
39  m_settingsMutex(QMutex::Recursive)
40 {
41  setObjectName(m_channelId);
42 
44  m_sum = 0;
45  m_usb = true;
46  m_magsq = 0;
47  m_useInterpolator = false;
50  m_inputSampleRate = 48000;
55  m_corr = new fftcorr(8*ssbFftLen); // 8k for 4k effective samples
56  m_pll.computeCoefficients(0.002f, 0.5f, 10.0f); // bandwidth, damping factor, loop gain
57 
60 
61  m_channelizer = new DownChannelizer(this);
65 }
66 
68 {
71  delete m_threadedChannelizer;
72  delete m_channelizer;
73  delete SSBFilter;
74  delete DSBFilter;
75  delete RRCFilter;
76 }
77 
78 void ChannelAnalyzer::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly)
79 {
80  (void) positiveOnly;
81  fftfilt::cmplx *sideband = 0;
82  Complex ci;
83 
84  m_settingsMutex.lock();
85 
86  for(SampleVector::const_iterator it = begin; it < end; ++it)
87  {
88  Complex c(it->real(), it->imag());
89  c *= m_nco.nextIQ();
90 
92  {
94  {
95  processOneSample(ci, sideband);
97  }
98  }
99  else
100  {
101  processOneSample(c, sideband);
102  }
103  }
104 
105  if(m_sampleSink != 0)
106  {
107  m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), m_settings.m_ssb); // m_ssb = positive only
108  }
109 
110  m_sampleBuffer.clear();
111 
112  m_settingsMutex.unlock();
113 }
114 
116 {
117  int n_out;
118  int decim = 1<<m_settings.m_spanLog2;
119 
120  if (m_settings.m_ssb)
121  {
122  n_out = SSBFilter->runSSB(c, &sideband, m_usb);
123  }
124  else
125  {
126  if (m_settings.m_rrc) {
127  n_out = RRCFilter->runFilt(c, &sideband);
128  } else {
129  n_out = DSBFilter->runDSB(c, &sideband);
130  }
131  }
132 
133  for (int i = 0; i < n_out; i++)
134  {
135  // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display
136  // smart decimation with bit gain using float arithmetic (23 bits significand)
137 
138  m_sum += sideband[i];
139 
140  if (!(m_undersampleCount++ & (decim - 1))) // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1)
141  {
142  m_sum /= decim;
143  Real re = m_sum.real() / SDR_RX_SCALEF;
144  Real im = m_sum.imag() / SDR_RX_SCALEF;
145  m_magsq = re*re + im*im;
147  std::complex<float> mix;
148 
149  if (m_settings.m_pll)
150  {
151  if (m_settings.m_fll)
152  {
153  m_fll.feed(re, im);
154  // Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
155  mix = m_sum * std::conj(m_fll.getComplex());
156  }
157  else
158  {
159  m_pll.feed(re, im);
160  // Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
161  mix = m_sum * std::conj(m_pll.getComplex());
162  }
163  }
164 
166  m_sum = 0;
167  }
168  }
169 }
170 
172 {
174 }
175 
177 {
178 }
179 
181 {
183  {
185  qDebug() << "ChannelAnalyzer::handleMessage: DownChannelizer::MsgChannelizerNotification:"
186  << " sampleRate: " << notif.getSampleRate()
187  << " frequencyOffset: " << notif.getFrequencyOffset();
188 
190 
191  if (getMessageQueueToGUI())
192  {
194  getMessageQueueToGUI()->push(msg);
195  }
196 
197  return true;
198  }
199  else if (MsgConfigureChannelizer::match(cmd))
200  {
202  qDebug() << "ChannelAnalyzer::handleMessage: MsgConfigureChannelizer:"
203  << " sampleRate: " << cfg.getSampleRate()
204  << " centerFrequency: " << cfg.getCenterFrequency();
205 
207  cfg.getSampleRate(),
208  cfg.getCenterFrequency());
209 
210  return true;
211  }
213  {
214  qDebug("ChannelAnalyzer::handleMessage: MsgConfigureChannelAnalyzer");
216 
217  applySettings(cfg.getSettings(), cfg.getForce());
218 
219  return true;
220  }
221  else
222  {
223  // Processed through GUI
224 // if (m_sampleSink != 0)
225 // {
226 // return m_sampleSink->handleMessage(cmd);
227 // }
228 // else
229 // {
230 // return false;
231 // }
232  return false;
233  }
234 }
235 
236 void ChannelAnalyzer::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force)
237 {
238  qDebug() << "ChannelAnalyzer::applyChannelSettings:"
239  << " inputSampleRate: " << inputSampleRate
240  << " inputFrequencyOffset: " << inputFrequencyOffset;
241 
242  if ((m_inputFrequencyOffset != inputFrequencyOffset) ||
243  (m_inputSampleRate != inputSampleRate) || force)
244  {
245  m_nco.setFreq(-inputFrequencyOffset, inputSampleRate);
246  }
247 
248  if ((m_inputSampleRate != inputSampleRate) || force)
249  {
250  m_settingsMutex.lock();
251 
252  m_interpolator.create(16, inputSampleRate, inputSampleRate / 2.2f);
255 
257  {
259  m_pll.setSampleRate(inputSampleRate / (1<<m_settings.m_spanLog2));
260  m_fll.setSampleRate(inputSampleRate / (1<<m_settings.m_spanLog2));
261  }
262 
263  m_settingsMutex.unlock();
264  }
265 
266  m_inputSampleRate = inputSampleRate;
267  m_inputFrequencyOffset = inputFrequencyOffset;
268 }
269 
270 void ChannelAnalyzer::setFilters(int sampleRate, float bandwidth, float lowCutoff)
271 {
272  qDebug("ChannelAnalyzer::setFilters: sampleRate: %d bandwidth: %f lowCutoff: %f",
273  sampleRate, bandwidth, lowCutoff);
274 
275  if (bandwidth < 0)
276  {
277  bandwidth = -bandwidth;
278  lowCutoff = -lowCutoff;
279  m_usb = false;
280  }
281  else
282  {
283  m_usb = true;
284  }
285 
286  if (bandwidth < 100.0f)
287  {
288  bandwidth = 100.0f;
289  lowCutoff = 0;
290  }
291 
292  SSBFilter->create_filter(lowCutoff / sampleRate, bandwidth / sampleRate);
293  DSBFilter->create_dsb_filter(bandwidth / sampleRate);
294  RRCFilter->create_rrc_filter(bandwidth / sampleRate, m_settings.m_rrcRolloff / 100.0);
295 }
296 
298 {
299  qDebug() << "ChannelAnalyzer::applySettings:"
300  << " m_downSample: " << settings.m_downSample
301  << " m_downSampleRate: " << settings.m_downSampleRate
302  << " m_rcc: " << settings.m_rrc
303  << " m_rrcRolloff: " << settings.m_rrcRolloff / 100.0
304  << " m_bandwidth: " << settings.m_bandwidth
305  << " m_lowCutoff: " << settings.m_lowCutoff
306  << " m_spanLog2: " << settings.m_spanLog2
307  << " m_ssb: " << settings.m_ssb
308  << " m_pll: " << settings.m_pll
309  << " m_fll: " << settings.m_fll
310  << " m_pllPskOrder: " << settings.m_pllPskOrder
311  << " m_inputType: " << (int) settings.m_inputType;
312 
313  if ((settings.m_downSampleRate != m_settings.m_downSampleRate) || force)
314  {
315  m_settingsMutex.lock();
319  m_settingsMutex.unlock();
320  }
321 
322  if ((settings.m_downSample != m_settings.m_downSample) || force)
323  {
324  int sampleRate = settings.m_downSample ? settings.m_downSampleRate : m_inputSampleRate;
325 
326  m_settingsMutex.lock();
327  m_useInterpolator = settings.m_downSample;
328  setFilters(sampleRate, settings.m_bandwidth, settings.m_lowCutoff);
329  m_pll.setSampleRate(sampleRate / (1<<settings.m_spanLog2));
330  m_fll.setSampleRate(sampleRate / (1<<settings.m_spanLog2));
331  m_settingsMutex.unlock();
332  }
333 
334  if ((settings.m_bandwidth != m_settings.m_bandwidth) ||
335  (settings.m_lowCutoff != m_settings.m_lowCutoff)|| force)
336  {
337  m_settingsMutex.lock();
338  setFilters(settings.m_downSample ? settings.m_downSampleRate : m_inputSampleRate, settings.m_bandwidth, settings.m_lowCutoff);
339  m_settingsMutex.unlock();
340  }
341 
342  if ((settings.m_rrcRolloff != m_settings.m_rrcRolloff) || force)
343  {
344  float sampleRate = settings.m_downSample ? (float) settings.m_downSampleRate : (float) m_inputSampleRate;
345  m_settingsMutex.lock();
346  RRCFilter->create_rrc_filter(settings.m_bandwidth / sampleRate, settings.m_rrcRolloff / 100.0);
347  m_settingsMutex.unlock();
348  }
349 
350  if ((settings.m_spanLog2 != m_settings.m_spanLog2) || force)
351  {
352  int sampleRate = (settings.m_downSample ? settings.m_downSampleRate : m_inputSampleRate) / (1<<m_settings.m_spanLog2);
353  m_pll.setSampleRate(sampleRate);
354  m_fll.setSampleRate(sampleRate);
355  }
356 
357  if (settings.m_pll != m_settings.m_pll || force)
358  {
359  if (settings.m_pll)
360  {
361  m_pll.reset();
362  m_fll.reset();
363  }
364  }
365 
366  if (settings.m_fll != m_settings.m_fll || force)
367  {
368  if (settings.m_fll) {
369  m_fll.reset();
370  }
371  }
372 
373  if (settings.m_pllPskOrder != m_settings.m_pllPskOrder || force)
374  {
375  if (settings.m_pllPskOrder < 32) {
376  m_pll.setPskOrder(settings.m_pllPskOrder);
377  }
378  }
379 
380  m_settings = settings;
381 }
382 
384 {
385  if (m_settings.m_fll) {
386  return m_fll.getFreq();
387  } else if (m_settings.m_pll) {
388  return m_pll.getFreq();
389  } else {
390  return 0.0;
391  }
392 }
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool positiveOnly)
Definition: chanalyzer.cpp:78
bool decimate(Real *distance, const Complex &next, Complex *result)
Definition: interpolator.h:38
void configure(MessageQueue *messageQueue, int sampleRate, int centerFrequency)
void push(Message *message, bool emitSignal=true)
Push message onto queue.
DownChannelizer * m_channelizer
Definition: chanalyzer.h:163
void removeChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:163
void addChannelSinkAPI(ChannelAPI *channelAPI, int streamIndex=0)
Definition: deviceapi.cpp:156
void create(int phaseSteps, double sampleRate, double cutoff, double nbTapsPerPhase=4.5)
MessageQueue * getInputMessageQueue()
Get the queue for asynchronous inbound communication.
MovingAverageUtil< double, double, 480 > m_channelPowerAvg
Definition: chanalyzer.h:188
Real m_interpolatorDistanceRemain
Definition: chanalyzer.h:179
virtual bool handleMessage(const Message &cmd)
Processing of a message. Returns true if message has actually been processed.
Definition: chanalyzer.cpp:180
int runFilt(const cmplx &in, cmplx **out)
Definition: fftfilt.cpp:260
fftfilt::cmplx m_sum
Definition: chanalyzer.h:169
void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force=false)
Definition: chanalyzer.cpp:236
static MsgReportChannelSampleRateChanged * create()
Definition: chanalyzer.h:97
virtual void feed(const SampleVector::const_iterator &begin, const SampleVector::const_iterator &end, bool positiveOnly)=0
std::complex< float > cmplx
Definition: fftfilt.h:21
Complex nextIQ()
Return next complex sample.
Definition: ncof.cpp:63
SampleVector m_sampleBuffer
Definition: chanalyzer.h:187
virtual ~ChannelAnalyzer()
Definition: chanalyzer.cpp:67
float getFreq() const
#define SDR_RX_SCALEF
Definition: dsptypes.h:33
static const QString m_channelId
Definition: chanalyzer.h:158
const std::complex< float > & getComplex() const
float getFreq() const
Real getPllFrequency() const
Definition: chanalyzer.cpp:383
virtual void stop()
Definition: chanalyzer.cpp:176
#define MESSAGE_CLASS_DEFINITION(Name, BaseClass)
Definition: message.h:52
fftcorr * m_corr
Definition: chanalyzer.h:184
PhaseLockComplex m_pll
Definition: chanalyzer.h:175
void create_filter(float f1, float f2)
Definition: fftfilt.cpp:107
const ChannelAnalyzerSettings & getSettings() const
Definition: chanalyzer.h:50
void applySettings(const ChannelAnalyzerSettings &settings, bool force=false)
Definition: chanalyzer.cpp:297
quint32 m_rrcRolloff
in 100ths
bool m_useInterpolator
Definition: chanalyzer.h:172
int32_t i
Definition: decimators.h:244
int m_inputFrequencyOffset
Definition: chanalyzer.h:167
void feedOneSample(const fftfilt::cmplx &s, const fftfilt::cmplx &pll)
Definition: chanalyzer.h:197
static bool match(const Message *message)
Definition: message.cpp:45
Interpolator m_interpolator
Definition: chanalyzer.h:177
void processOneSample(Complex &c, fftfilt::cmplx *sideband)
Definition: chanalyzer.cpp:115
void removeChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Remove a channel sink (Rx)
Definition: deviceapi.cpp:127
virtual void start()
Definition: chanalyzer.cpp:171
MessageQueue * getMessageQueueToGUI()
const std::complex< float > & getComplex() const
BasebandSampleSink * m_sampleSink
Definition: chanalyzer.h:186
DeviceAPI * m_deviceAPI
Definition: chanalyzer.h:161
void addChannelSink(ThreadedBasebandSampleSink *sink, int streamIndex=0)
Add a channel sink (Rx)
Definition: deviceapi.cpp:118
void create_rrc_filter(float fb, float a)
root raised cosine. fb is half the band pass
Definition: fftfilt.cpp:222
ThreadedBasebandSampleSink * m_threadedChannelizer
Definition: chanalyzer.h:162
QMutex m_settingsMutex
Definition: chanalyzer.h:189
int runSSB(const cmplx &in, cmplx **out, bool usb, bool getDC=true)
Definition: fftfilt.cpp:284
FreqLockComplex m_fll
Definition: chanalyzer.h:176
void setFreq(Real freq, Real sampleRate)
Definition: ncof.cpp:51
void feed(float re, float im)
Real m_interpolatorDistance
Definition: chanalyzer.h:178
fftfilt * RRCFilter
Definition: chanalyzer.h:183
ChannelAnalyzerSettings m_settings
Definition: chanalyzer.h:164
void computeCoefficients(Real wn, Real zeta, Real K)
int m_undersampleCount
Definition: chanalyzer.h:168
void setPskOrder(unsigned int order)
std::complex< Real > Complex
Definition: dsptypes.h:43
static const QString m_channelIdURI
Definition: chanalyzer.h:157
int runDSB(const cmplx &in, cmplx **out, bool getDC=true)
Definition: fftfilt.cpp:327
float Real
Definition: dsptypes.h:42
void setSampleRate(unsigned int sampleRate)
void feed(float re, float im)
fftfilt * SSBFilter
Definition: chanalyzer.h:181
void create_dsb_filter(float f2)
Definition: fftfilt.cpp:148
#define ssbFftLen
Definition: chanalyzer.h:38
void setSampleRate(unsigned int sampleRate)
void setFilters(int sampleRate, float bandwidth, float lowCutoff)
Definition: chanalyzer.cpp:270
ChannelAnalyzer(DeviceAPI *deviceAPI)
Definition: chanalyzer.cpp:35
fftfilt * DSBFilter
Definition: chanalyzer.h:182