Audio Input: quarter sample rate heterodyne for mono signals

pull/714/head
f4exb 2020-11-13 19:55:09 +01:00
rodzic 6e3add76e3
commit 9cdcfeef70
7 zmienionych plików z 70 dodań i 34 usunięć

Wyświetl plik

@ -287,6 +287,7 @@ void AudioInput::applySettings(const AudioInputSettings& settings, bool force, b
if ((m_settings.m_iqMapping != settings.m_iqMapping) || force)
{
reverseAPIKeys.append("iqMapping");
forwardChange = true;
if (m_worker) {
m_worker->setIQMapping(settings.m_iqMapping);
@ -306,7 +307,11 @@ void AudioInput::applySettings(const AudioInputSettings& settings, bool force, b
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);
}
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -33,7 +33,8 @@ AudioInputWorker::AudioInputWorker(SampleSinkFifo* sampleFifo, AudioFifo *fifo,
m_log2Decim(0),
m_iqMapping(AudioInputSettings::IQMapping::L),
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)
{
// Map between left and right audio channels and IQ channels
if (m_iqMapping == AudioInputSettings::IQMapping::L)
{
for (uint32_t i = 0; i < nbRead; i++)
m_buf[i*2+1] = 0;
}
else if (m_iqMapping == AudioInputSettings::IQMapping::R)
if ((m_iqMapping == AudioInputSettings::IQMapping::L) || // mono
(m_iqMapping == AudioInputSettings::IQMapping::R))
{
for (uint32_t i = 0; i < nbRead; i++)
{
m_buf[i*2] = m_buf[i*2+1];
m_buf[i*2+1] = 0;
qint16 r = m_buf[i*2 + (m_iqMapping == AudioInputSettings::IQMapping::R ? 1 : 0)]; // real sample
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++)
{
@ -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();
switch (m_log2Decim)
{
case 0:
m_decimatorsIQ.decimate1(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate1(&it, buf, 2*nbRead);
break;
case 1:
m_decimatorsIQ.decimate2_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate2_cen(&it, buf, 2*nbRead);
break;
case 2:
m_decimatorsIQ.decimate4_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate4_cen(&it, buf, 2*nbRead);
break;
case 3:
m_decimatorsIQ.decimate8_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate8_cen(&it, buf, 2*nbRead);
break;
case 4:
m_decimatorsIQ.decimate16_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate16_cen(&it, buf, 2*nbRead);
break;
case 5:
m_decimatorsIQ.decimate32_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate32_cen(&it, buf, 2*nbRead);
break;
case 6:
m_decimatorsIQ.decimate64_cen(&it, m_buf, 2*nbRead);
m_decimatorsIQ.decimate64_cen(&it, buf, 2*nbRead);
break;
default:
break;

Wyświetl plik

@ -51,8 +51,10 @@ private:
SampleVector m_convertBuffer;
SampleSinkFifo* m_sampleFifo;
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 decimate(qint16 *buf, unsigned int nbRead);
private slots:
void handleAudio();

Wyświetl plik

@ -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>
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.
* I=R, Q=0 - The right audio channel is driven to the I channel. The Q channel is set to 0.
* 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=R, Q=L - The right audio channel is driven to the I channel. The left audio channel is driven to the Q channel.
* 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.
* 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 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 for a complex (analytic signal)input.