Test source: added AM and FM modulations

This commit is contained in:
f4exb 2018-02-11 13:48:17 +01:00
parent 4b2dfd488e
commit 1d9ab62a4a
9 changed files with 481 additions and 138 deletions

View File

@ -33,7 +33,7 @@ void DevicePerseusScan::scan(int nbDevices)
for (int deviceIndex = 0; deviceIndex < nbDevices; deviceIndex++)
{
if ((descr = perseus_open(deviceIndex)) == 0) {
qCritical("DevicePerseusScan::scan: open error: %s\n", perseus_errorstr());
qCritical("DevicePerseusScan::scan: open error: %s", perseus_errorstr());
continue;
}

View File

@ -57,6 +57,7 @@ TestSourceGui::TestSourceGui(DeviceUISet *deviceUISet, QWidget* parent) :
ui->sampleRate->setValueRange(7, 48000, 9999999);
ui->frequencyShift->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->frequencyShift->setValueRange(false, 7, -9999999, 9999999);
ui->frequencyShiftLabel->setText(QString("%1").arg(QChar(0x94, 0x03)));
displaySettings();
@ -212,6 +213,37 @@ void TestSourceGui::on_amplitudeFine_valueChanged(int value __attribute__((unuse
sendSettings();
}
void TestSourceGui::on_modulation_currentIndexChanged(int index)
{
if ((index < 0) || (index > TestSourceSettings::ModulationLast)) {
return;
}
m_settings.m_modulation = (TestSourceSettings::Modulation) index;
sendSettings();
}
void TestSourceGui::on_modulationFrequency_valueChanged(int value)
{
m_settings.m_modulationTone = value;
ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2));
sendSettings();
}
void TestSourceGui::on_amModulation_valueChanged(int value)
{
m_settings.m_amModulation = value;
ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation));
sendSettings();
}
void TestSourceGui::on_fmDeviation_valueChanged(int value)
{
m_settings.m_fmDeviation = value;
ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_fmDeviation / 10.0, 0, 'f', 1));
sendSettings();
}
void TestSourceGui::on_dcBias_valueChanged(int value)
{
ui->dcBiasText->setText(QString(tr("%1 %").arg(value)));
@ -354,6 +386,13 @@ void TestSourceGui::displaySettings()
ui->qBiasText->setText(QString(tr("%1 %").arg(qBiasPercent)));
ui->autoCorr->setCurrentIndex(m_settings.m_autoCorrOptions);
ui->sampleSize->blockSignals(false);
ui->modulation->setCurrentIndex((int) m_settings.m_modulation);
ui->modulationFrequency->setValue(m_settings.m_modulationTone);
ui->modulationFrequencyText->setText(QString("%1").arg(m_settings.m_modulationTone / 100.0, 0, 'f', 2));
ui->amModulation->setValue(m_settings.m_amModulation);
ui->amModulationText->setText(QString("%1").arg(m_settings.m_amModulation));
ui->fmDeviation->setValue(m_settings.m_fmDeviation);
ui->fmDeviationText->setText(QString("%1").arg(m_settings.m_fmDeviation / 10.0, 0, 'f', 1));
blockApplySettings(false);
}

View File

@ -88,6 +88,10 @@ private slots:
void on_sampleSize_currentIndexChanged(int index);
void on_amplitudeCoarse_valueChanged(int value);
void on_amplitudeFine_valueChanged(int value);
void on_modulation_currentIndexChanged(int index);
void on_modulationFrequency_valueChanged(int value);
void on_amModulation_valueChanged(int value);
void on_fmDeviation_valueChanged(int value);
void on_dcBias_valueChanged(int value);
void on_iBias_valueChanged(int value);
void on_qBias_valueChanged(int value);

View File

@ -216,79 +216,6 @@
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="frequencyShiftLayout">
<item>
<widget class="QLabel" name="frequencyShiftLabel">
<property name="text">
<string>Shift</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="frequencyShift" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
<italic>false</italic>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Shift from center frequency</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="frequencyShiftUnits">
<property name="text">
<string>Hz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="decimationLabel">
<property name="text">
@ -382,15 +309,66 @@
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleSzieLabel">
<property name="text">
<string>Sz</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sampleSize">
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Sample size</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>12</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleSizeUnits">
<property name="text">
<string>bits</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="sampleRateLayout">
<property name="topMargin">
@ -409,7 +387,7 @@
</property>
<property name="minimumSize">
<size>
<width>28</width>
<width>16</width>
<height>0</height>
</size>
</property>
@ -454,59 +432,7 @@
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleSzieLabel">
<property name="text">
<string>size</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sampleSize">
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Sample size</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>12</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleSizeUnits">
<property name="text">
<string>bits</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -518,8 +444,245 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="modulationLabel">
<property name="text">
<string>Mod</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="modulation">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Modulation</string>
</property>
<item>
<property name="text">
<string>No</string>
</property>
</item>
<item>
<property name="text">
<string>AM</string>
</property>
</item>
<item>
<property name="text">
<string>FM</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QDial" name="modulationFrequency">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Modulation tone (kHz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="modulationFrequencyText">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Modulation tone value (kHz)</string>
</property>
<property name="text">
<string>0.00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="frequencyShiftLayout">
<item>
<widget class="QLabel" name="frequencyShiftLabel">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>D</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="frequencyShift" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
<italic>false</italic>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Shift from center frequency</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="frequencyShiftUnits">
<property name="text">
<string>Hz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="amModulationLabel">
<property name="text">
<string>AM</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="amModulation">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>AM modulation (%)</string>
</property>
<property name="pageStep">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="amModulationText">
<property name="toolTip">
<string>AM modulation value (%)</string>
</property>
<property name="text">
<string>00</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fmDeviationLabel">
<property name="text">
<string>FM</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="fmDeviation">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>FM deviation (kHz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fmDeviationText">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>FM deviation value (kHz)</string>
</property>
<property name="text">
<string>00.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">

View File

@ -350,6 +350,34 @@ bool TestSourceInput::applySettings(const TestSourceSettings& settings, bool for
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
}
if ((m_settings.m_modulationTone != settings.m_modulationTone) || force)
{
if (m_testSourceThread != 0) {
m_testSourceThread->setToneFrequency(settings.m_modulationTone * 10);
}
}
if ((m_settings.m_modulation != settings.m_modulation) || force)
{
if (m_testSourceThread != 0) {
m_testSourceThread->setModulation(settings.m_modulation);
}
}
if ((m_settings.m_amModulation != settings.m_amModulation) || force)
{
if (m_testSourceThread != 0) {
m_testSourceThread->setAMModulation(settings.m_amModulation / 100.0f);
}
}
if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force)
{
if (m_testSourceThread != 0) {
m_testSourceThread->setFMDeviation(settings.m_fmDeviation * 100.0f);
}
}
m_settings = settings;
return true;
}

View File

@ -33,6 +33,10 @@ void TestSourceSettings::resetToDefaults()
m_sampleSizeIndex = 0;
m_amplitudeBits = 127;
m_autoCorrOptions = AutoCorrNone;
m_modulation = ModulationNone;
m_modulationTone = 44; // 440 Hz
m_amModulation = 50; // 50%
m_fmDeviation = 50; // 5 kHz
m_dcFactor = 0.0f;
m_iFactor = 0.0f;
m_qFactor = 0.0f;
@ -54,6 +58,10 @@ QByteArray TestSourceSettings::serialize() const
s.writeFloat(11, m_iFactor);
s.writeFloat(12, m_qFactor);
s.writeFloat(13, m_phaseImbalance);
s.writeS32(14, (int) m_modulation);
s.writeS32(15, m_modulationTone);
s.writeS32(16, m_amModulation);
s.writeS32(17, m_fmDeviation);
return s.final();
}
@ -91,6 +99,17 @@ bool TestSourceSettings::deserialize(const QByteArray& data)
d.readFloat(11, &m_iFactor, 0.0f);
d.readFloat(12, &m_qFactor, 0.0f);
d.readFloat(13, &m_phaseImbalance, 0.0f);
d.readS32(14, &intval, 0);
if (intval < 0 || intval > (int) ModulationLast) {
m_modulation = ModulationNone;
} else {
m_modulation = (Modulation) intval;
}
d.readS32(15, &m_modulationTone, 44);
d.readS32(16, &m_amModulation, 50);
d.readS32(17, &m_fmDeviation, 50);
return true;
}

View File

@ -31,6 +31,13 @@ struct TestSourceSettings {
AutoCorrLast,
} AutoCorrOptions;
typedef enum {
ModulationNone,
ModulationAM,
ModulationFM,
ModulationLast
} Modulation;
quint64 m_centerFrequency;
qint32 m_frequencyShift;
quint32 m_sampleRate;
@ -39,6 +46,10 @@ struct TestSourceSettings {
quint32 m_sampleSizeIndex;
qint32 m_amplitudeBits;
AutoCorrOptions m_autoCorrOptions;
Modulation m_modulation;
int m_modulationTone; //!< 10'Hz
int m_amModulation; //!< percent
int m_fmDeviation; //!< 100'Hz
float m_dcFactor; //!< -1.0 < x < 1.0
float m_iFactor; //!< -1.0 < x < 1.0
float m_qFactor; //!< -1.0 < x < 1.0

View File

@ -31,6 +31,11 @@ TestSourceThread::TestSourceThread(SampleSinkFifo* sampleFifo, QObject* parent)
m_convertBuffer(TESTSOURCE_BLOCKSIZE),
m_sampleFifo(sampleFifo),
m_frequencyShift(0),
m_toneFrequency(440),
m_modulation(TestSourceSettings::ModulationNone),
m_amModulation(0.5f),
m_fmDeviationUnit(0.0f),
m_fmPhasor(0.0f),
m_samplerate(48000),
m_log2Decim(4),
m_fcPos(0),
@ -81,6 +86,7 @@ void TestSourceThread::setSamplerate(int samplerate)
m_chunksize = 4 * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
m_throttleToggle = !m_throttleToggle;
m_nco.setFreq(m_frequencyShift, m_samplerate);
m_toneNco.setFreq(m_toneFrequency, m_samplerate);
}
void TestSourceThread::setLog2Decimation(unsigned int log2_decim)
@ -149,6 +155,28 @@ void TestSourceThread::setFrequencyShift(int shift)
m_nco.setFreq(shift, m_samplerate);
}
void TestSourceThread::setToneFrequency(int toneFrequency)
{
m_toneNco.setFreq(toneFrequency, m_samplerate);
}
void TestSourceThread::setModulation(TestSourceSettings::Modulation modulation)
{
m_modulation = modulation;
}
void TestSourceThread::setAMModulation(float amModulation)
{
m_amModulation = amModulation < 0.0f ? 0.0f : amModulation > 1.0f ? 1.0f : amModulation;
}
void TestSourceThread::setFMDeviation(float deviation)
{
float fmDeviationUnit = deviation / (float) m_samplerate;
m_fmDeviationUnit = fmDeviationUnit < 0.0f ? 0.0f : fmDeviationUnit > 1.0f ? 1.0f : fmDeviationUnit;
qDebug("TestSourceThread::setFMDeviation: m_fmDeviationUnit: %f", m_fmDeviationUnit);
}
void TestSourceThread::run()
{
m_running = true;
@ -195,14 +223,52 @@ void TestSourceThread::generate(quint32 chunksize)
for (int i = 0; i < n-1;)
{
Complex c = m_nco.nextIQ(m_phaseImbalance);
m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ);
switch (m_modulation)
{
case TestSourceSettings::ModulationAM:
{
Complex c = m_nco.nextIQ();
Real t, re, im;
pullAF(t);
t = (t*m_amModulation + 1.0f)*0.5f;
re = c.real()*t;
im = c.imag()*t + m_phaseImbalance*re;
m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
}
break;
case TestSourceSettings::ModulationFM:
{
Complex c = m_nco.nextIQ();
Real t, re, im;
pullAF(t);
m_fmPhasor += m_fmDeviationUnit * t;
m_fmPhasor = m_fmPhasor < -1.0f ? -m_fmPhasor - 1.0f : m_fmPhasor > 1.0f ? m_fmPhasor - 1.0f : m_fmPhasor;
re = c.real()*cos(m_fmPhasor*M_PI) - c.imag()*sin(m_fmPhasor*M_PI);
im = (c.real()*sin(m_fmPhasor*M_PI) + c.imag()*cos(m_fmPhasor*M_PI)) + m_phaseImbalance*re;
m_buf[i++] = (int16_t) (re * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
m_buf[i++] = (int16_t) (im * (float) m_amplitudeBitsQ);
}
break;
case TestSourceSettings::ModulationNone:
default:
{
Complex c = m_nco.nextIQ(m_phaseImbalance);
m_buf[i++] = (int16_t) (c.real() * (float) m_amplitudeBitsI) + m_amplitudeBitsDC;
m_buf[i++] = (int16_t) (c.imag() * (float) m_amplitudeBitsQ);
}
break;
}
}
callback(m_buf, n);
}
void TestSourceThread::pullAF(Real& afSample)
{
afSample = m_toneNco.next();
}
// call appropriate conversion (decimation) routine depending on the number of sample bits
void TestSourceThread::callback(const qint16* buf, qint32 len)
{

View File

@ -28,6 +28,8 @@
#include "dsp/decimators.h"
#include "dsp/ncof.h"
#include "testsourcesettings.h"
#define TESTSOURCE_THROTTLE_MS 50
class TestSourceThread : public QThread {
@ -49,6 +51,10 @@ public:
void setQFactor(float qFactor);
void setPhaseImbalance(float phaseImbalance);
void setFrequencyShift(int shift);
void setToneFrequency(int toneFrequency);
void setModulation(TestSourceSettings::Modulation modulation);
void setAMModulation(float amModulation);
void setFMDeviation(float deviation);
void connectTimer(const QTimer& timer);
@ -63,7 +69,13 @@ private:
SampleVector m_convertBuffer;
SampleSinkFifo* m_sampleFifo;
NCOF m_nco;
NCOF m_toneNco;
int m_frequencyShift;
int m_toneFrequency;
TestSourceSettings::Modulation m_modulation;
float m_amModulation;
float m_fmDeviationUnit;
float m_fmPhasor;
int m_samplerate;
unsigned int m_log2Decim;
@ -101,6 +113,7 @@ private:
void callback(const qint16* buf, qint32 len);
void setBuffers(quint32 chunksize);
void generate(quint32 chunksize);
void pullAF(Real& afSample);
// Decimate according to specified log2 (ex: log2=4 => decim=16)
inline void convert_8(SampleVector::iterator* it, const qint16* buf, qint32 len)