1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 01:39:05 -05:00

Audio Input: quarter sample rate heterodyne for mono signals

This commit is contained in:
f4exb 2020-11-13 19:55:09 +01:00
parent 6e3add76e3
commit 9cdcfeef70
7 changed files with 70 additions and 34 deletions

View File

@ -287,6 +287,7 @@ void AudioInput::applySettings(const AudioInputSettings& settings, bool force, b
if ((m_settings.m_iqMapping != settings.m_iqMapping) || force) if ((m_settings.m_iqMapping != settings.m_iqMapping) || force)
{ {
reverseAPIKeys.append("iqMapping"); reverseAPIKeys.append("iqMapping");
forwardChange = true;
if (m_worker) { if (m_worker) {
m_worker->setIQMapping(settings.m_iqMapping); m_worker->setIQMapping(settings.m_iqMapping);
@ -306,7 +307,11 @@ void AudioInput::applySettings(const AudioInputSettings& settings, bool force, b
if (forwardChange) if (forwardChange)
{ {
DSPSignalNotification *notif = new DSPSignalNotification(m_settings.m_sampleRate/(1<<m_settings.m_log2Decim), 0); qint64 dF =
((m_settings.m_iqMapping == AudioInputSettings::IQMapping::L) ||
(m_settings.m_iqMapping == AudioInputSettings::IQMapping::R)) ?
m_settings.m_sampleRate / 4 : 0;
DSPSignalNotification *notif = new DSPSignalNotification(m_settings.m_sampleRate/(1<<m_settings.m_log2Decim), dF);
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif); m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
} }
} }

View File

@ -37,7 +37,8 @@ AudioInputGui::AudioInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
m_deviceUISet(deviceUISet), m_deviceUISet(deviceUISet),
m_forceSettings(true), m_forceSettings(true),
m_settings(), m_settings(),
m_sampleSource(NULL) m_sampleSource(nullptr),
m_centerFrequency(0)
{ {
m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource(); m_sampleSource = (AudioInput*) m_deviceUISet->m_deviceAPI->getSampleSource();
@ -130,6 +131,7 @@ void AudioInputGui::handleInputMessages()
{ {
DSPSignalNotification* notif = (DSPSignalNotification*) message; DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_sampleRate = notif->getSampleRate(); m_sampleRate = notif->getSampleRate();
m_centerFrequency = notif->getCenterFrequency();
qDebug("AudioInputGui::handleInputMessages: DSPSignalNotification: SampleRate: %d", notif->getSampleRate()); qDebug("AudioInputGui::handleInputMessages: DSPSignalNotification: SampleRate: %d", notif->getSampleRate());
updateSampleRateAndFrequency(); updateSampleRateAndFrequency();
@ -160,7 +162,7 @@ void AudioInputGui::updateSampleRateAndFrequency()
*/ */
{ {
m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(0); m_deviceUISet->getSpectrum()->setCenterFrequency(m_centerFrequency);
m_deviceUISet->getSpectrum()->setSsbSpectrum(false); m_deviceUISet->getSpectrum()->setSsbSpectrum(false);
m_deviceUISet->getSpectrum()->setLsbDisplay(false); m_deviceUISet->getSpectrum()->setLsbDisplay(false);
} }

View File

@ -57,6 +57,7 @@ private:
QTimer m_updateTimer; QTimer m_updateTimer;
DeviceSampleSource* m_sampleSource; DeviceSampleSource* m_sampleSource;
int m_sampleRate; int m_sampleRate;
qint64 m_centerFrequency;
MessageQueue m_inputMessageQueue; MessageQueue m_inputMessageQueue;

View File

@ -210,6 +210,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="decimLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QComboBox" name="decim"> <widget class="QComboBox" name="decim">
<property name="maximumSize"> <property name="maximumSize">
@ -243,13 +250,6 @@
</item> </item>
</widget> </widget>
</item> </item>
<item>
<widget class="QLabel" name="decimLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -336,12 +336,12 @@
</property> </property>
<item> <item>
<property name="text"> <property name="text">
<string>I=L, Q=0</string> <string>Mono L</string>
</property> </property>
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>I=R, Q=0</string> <string>Mono R</string>
</property> </property>
</item> </item>
<item> <item>

View File

@ -33,7 +33,8 @@ AudioInputWorker::AudioInputWorker(SampleSinkFifo* sampleFifo, AudioFifo *fifo,
m_log2Decim(0), m_log2Decim(0),
m_iqMapping(AudioInputSettings::IQMapping::L), m_iqMapping(AudioInputSettings::IQMapping::L),
m_convertBuffer(m_convBufSamples), m_convertBuffer(m_convBufSamples),
m_sampleFifo(sampleFifo) m_sampleFifo(sampleFifo),
m_quNCOPhase(0)
{ {
} }
@ -56,20 +57,40 @@ void AudioInputWorker::stopWork()
void AudioInputWorker::workIQ(unsigned int nbRead) void AudioInputWorker::workIQ(unsigned int nbRead)
{ {
// Map between left and right audio channels and IQ channels // Map between left and right audio channels and IQ channels
if (m_iqMapping == AudioInputSettings::IQMapping::L) if ((m_iqMapping == AudioInputSettings::IQMapping::L) || // mono
{ (m_iqMapping == AudioInputSettings::IQMapping::R))
for (uint32_t i = 0; i < nbRead; i++)
m_buf[i*2+1] = 0;
}
else if (m_iqMapping == AudioInputSettings::IQMapping::R)
{ {
for (uint32_t i = 0; i < nbRead; i++) for (uint32_t i = 0; i < nbRead; i++)
{ {
m_buf[i*2] = m_buf[i*2+1]; qint16 r = m_buf[i*2 + (m_iqMapping == AudioInputSettings::IQMapping::R ? 1 : 0)]; // real sample
m_buf[i*2+1] = 0;
if (m_quNCOPhase == 0) // 0
{
m_buf[i*2] = r; // 1
m_buf[i*2+1] = 0; // 0
m_quNCOPhase = 1; // next phase
}
else if (m_quNCOPhase == 1) // -pi/2
{
m_buf[i*2] = 0; // 0
m_buf[i*2+1] = -r; // -1
m_quNCOPhase = 2; // next phase
}
else if (m_quNCOPhase == 2) // pi or -pi
{
m_buf[i*2] = -r; // -1
m_buf[i*2+1] = 0; // 0
m_quNCOPhase = 3; // next phase
}
else if (m_quNCOPhase == 3) // pi/2
{
m_buf[i*2] = 0; // 0
m_buf[i*2+1] = r; // 1
m_quNCOPhase = 0; // next phase
}
} }
} }
else if (m_iqMapping == AudioInputSettings::IQMapping::LR) else if (m_iqMapping == AudioInputSettings::IQMapping::LR) // stereo - reverse
{ {
for (uint32_t i = 0; i < nbRead; i++) for (uint32_t i = 0; i < nbRead; i++)
{ {
@ -79,30 +100,35 @@ void AudioInputWorker::workIQ(unsigned int nbRead)
} }
} }
decimate(m_buf, nbRead);
}
void AudioInputWorker::decimate(qint16 *buf, unsigned int nbRead)
{
SampleVector::iterator it = m_convertBuffer.begin(); SampleVector::iterator it = m_convertBuffer.begin();
switch (m_log2Decim) switch (m_log2Decim)
{ {
case 0: case 0:
m_decimatorsIQ.decimate1(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate1(&it, buf, 2*nbRead);
break; break;
case 1: case 1:
m_decimatorsIQ.decimate2_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate2_cen(&it, buf, 2*nbRead);
break; break;
case 2: case 2:
m_decimatorsIQ.decimate4_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate4_cen(&it, buf, 2*nbRead);
break; break;
case 3: case 3:
m_decimatorsIQ.decimate8_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate8_cen(&it, buf, 2*nbRead);
break; break;
case 4: case 4:
m_decimatorsIQ.decimate16_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate16_cen(&it, buf, 2*nbRead);
break; break;
case 5: case 5:
m_decimatorsIQ.decimate32_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate32_cen(&it, buf, 2*nbRead);
break; break;
case 6: case 6:
m_decimatorsIQ.decimate64_cen(&it, m_buf, 2*nbRead); m_decimatorsIQ.decimate64_cen(&it, buf, 2*nbRead);
break; break;
default: default:
break; break;

View File

@ -51,8 +51,10 @@ private:
SampleVector m_convertBuffer; SampleVector m_convertBuffer;
SampleSinkFifo* m_sampleFifo; SampleSinkFifo* m_sampleFifo;
Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 16, true> m_decimatorsIQ; Decimators<qint32, qint16, SDR_RX_SAMP_SZ, 16, true> m_decimatorsIQ;
int m_quNCOPhase; //!< Quarter sample rate pseudo NCO phase index (0, 90, 180, 270)
void workIQ(unsigned int nbRead); void workIQ(unsigned int nbRead);
void decimate(qint16 *buf, unsigned int nbRead);
private slots: private slots:
void handleAudio(); void handleAudio();

View File

@ -38,9 +38,9 @@ A control to set the input volume. This is not supported by all input audio devi
<h3>7: Channel Map</h3> <h3>7: Channel Map</h3>
This controls how the left and right stereo audio channels map on to the IQ channels. This controls how the left and right audio channels map on to the IQ channels.
* I=L, Q=0 - The left audio channel is driven to the I channel. The Q channel is set to 0. * Mono L - Real samples are taken from the left audio channel and are heterodyned by the fourth of the sample rate (fs/4) to obtain complex samples. Therefore the spectrum of the complex baseband is centered at the fourth of the sample rate (fs/4). As per Nyquist rule only a bandwidth of half of the sample rate (fs/2) is available for real signals. Frequencies outside the [0, fs/2] interval are artefacts and can be eliminated by decimating by a factor of 2.
* I=R, Q=0 - The right audio channel is driven to the I channel. The Q channel is set to 0. * Mono R - Same as above but takes the right audio channel for the real signal.
* I=L, Q=R - The left audio channel is driven to the I channel. The right audio channel is driven to the Q channel. * I=L, Q=R - The left audio channel is driven to the I channel. The right audio channel is driven to the Q channel for a complex (analytic signal)input.
* I=R, Q=L - The right audio channel is driven to the I channel. The left audio channel is driven to the Q channel. * I=R, Q=L - The right audio channel is driven to the I channel. The left audio channel is driven to the Q channel for a complex (analytic signal)input.