Audio input: Added Fc control

This commit is contained in:
f4exb 2023-03-11 09:56:18 +01:00
parent e606805beb
commit 683bf51d0e
17 changed files with 264 additions and 40 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

View File

@ -303,6 +303,15 @@ void AudioInput::applySettings(const AudioInputSettings& settings, QList<QString
}
}
if (settingsKeys.contains("fcPos") || force)
{
if (m_worker) {
m_worker->setFcPos((int) settings.m_fcPos);
}
qDebug() << "AudioInput::applySettings: set fc pos (enum) to " << (int) settings.m_fcPos;
}
if (settingsKeys.contains("iqMapping") || force)
{
forwardChange = true;
@ -437,6 +446,9 @@ void AudioInput::webapiUpdateDeviceSettings(
if (deviceSettingsKeys.contains("iqImbalance")) {
settings.m_iqImbalance = response.getAudioInputSettings()->getIqImbalance() != 0;
}
if (deviceSettingsKeys.contains("fcPos")) {
settings.m_fcPos = (AudioInputSettings::fcPos_t) response.getAudioInputSettings()->getFcPos();
}
if (deviceSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getAudioInputSettings()->getUseReverseApi() != 0;
}
@ -460,6 +472,7 @@ void AudioInput::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& resp
response.getAudioInputSettings()->setIqMapping((int)settings.m_iqMapping);
response.getAudioInputSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
response.getAudioInputSettings()->setIqImbalance(settings.m_iqImbalance ? 1 : 0);
response.getAudioInputSettings()->setFcPos((int) settings.m_fcPos);
response.getAudioInputSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
@ -505,6 +518,9 @@ void AudioInput::webapiReverseSendSettings(const QList<QString>& deviceSettingsK
if (deviceSettingsKeys.contains("iqImbalance") || force) {
swgAudioInputSettings->setIqImbalance(settings.m_iqImbalance ? 1 : 0);
}
if (deviceSettingsKeys.contains("fcPos") || force) {
swgAudioInputSettings->setFcPos(settings.m_fcPos);
}
QString deviceSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
.arg(settings.m_reverseAPIAddress)

View File

@ -245,6 +245,18 @@ void AudioInputGui::displaySettings()
ui->dcOffset->setChecked(m_settings.m_dcBlock);
ui->iqImbalance->setChecked(m_settings.m_iqImbalance);
refreshSampleRates(ui->device->currentText());
displayFcTooltip();
}
void AudioInputGui::displayFcTooltip()
{
int32_t fShift = DeviceSampleSource::calculateFrequencyShift(
m_settings.m_log2Decim,
(DeviceSampleSource::fcPos_t) m_settings.m_fcPos,
m_settings.m_sampleRate,
DeviceSampleSource::FrequencyShiftScheme::FSHIFT_STD
);
ui->fcPos->setToolTip(tr("Relative position of device center frequency: %1 kHz").arg(QString::number(fShift / 1000.0f, 'g', 5)));
}
void AudioInputGui::on_device_currentIndexChanged(int index)
@ -260,6 +272,7 @@ void AudioInputGui::on_sampleRate_currentIndexChanged(int index)
{
(void) index;
m_settings.m_sampleRate = ui->sampleRate->currentText().toInt();
displayFcTooltip();
m_settingsKeys.append("sampleRate");
sendSettings();
}
@ -271,6 +284,7 @@ void AudioInputGui::on_decim_currentIndexChanged(int index)
}
m_settings.m_log2Decim = index;
displayFcTooltip();
m_settingsKeys.append("log2Decim");
sendSettings();
}
@ -305,6 +319,14 @@ void AudioInputGui::on_iqImbalance_toggled(bool checked)
sendSettings();
}
void AudioInputGui::on_fcPos_currentIndexChanged(int index)
{
m_settings.m_fcPos = (AudioInputSettings::fcPos_t) (index < 0 ? 0 : index > 2 ? 2 : index);
displayFcTooltip();
m_settingsKeys.append("fcPos");
sendSettings();
}
void AudioInputGui::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
@ -394,5 +416,6 @@ void AudioInputGui::makeUIConnections()
QObject::connect(ui->channels, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_channels_currentIndexChanged);
QObject::connect(ui->dcOffset, &ButtonSwitch::toggled, this, &AudioInputGui::on_dcOffset_toggled);
QObject::connect(ui->iqImbalance, &ButtonSwitch::toggled, this, &AudioInputGui::on_iqImbalance_toggled);
QObject::connect(ui->fcPos, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AudioInputGui::on_fcPos_currentIndexChanged);
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &AudioInputGui::on_startStop_toggled);
}

View File

@ -65,6 +65,7 @@ private:
void refreshDeviceList();
void refreshSampleRates(QString deviceName);
void displaySettings();
void displayFcTooltip();
void sendSettings();
void updateSampleRateAndFrequency();
bool handleMessage(const Message& message);
@ -79,6 +80,7 @@ private slots:
void on_channels_currentIndexChanged(int index);
void on_dcOffset_toggled(bool checked);
void on_iqImbalance_toggled(bool checked);
void on_fcPos_currentIndexChanged(int index);
void on_startStop_toggled(bool checked);
void updateHardware();
void openDeviceSettingsDialog(const QPoint& p);

View File

@ -304,6 +304,50 @@
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="fcPosLabel">
<property name="text">
<string>Fp</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fcPos">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Relative position of device center frequency</string>
</property>
<property name="currentIndex">
<number>2</number>
</property>
<item>
<property name="text">
<string>Inf</string>
</property>
</item>
<item>
<property name="text">
<string>Sup</string>
</property>
</item>
<item>
<property name="text">
<string>Cen</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>

View File

@ -35,6 +35,7 @@ void AudioInputSettings::resetToDefaults()
m_dcBlock = false;
m_iqImbalance = false;
m_useReverseAPI = false;
m_fcPos = FC_POS_CENTER;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
@ -51,6 +52,7 @@ QByteArray AudioInputSettings::serialize() const
s.writeS32(5, (int)m_iqMapping);
s.writeBool(6, m_dcBlock);
s.writeBool(7, m_iqImbalance);
s.writeS32(8, (int) m_fcPos);
s.writeBool(24, m_useReverseAPI);
s.writeString(25, m_reverseAPIAddress);
@ -73,6 +75,7 @@ bool AudioInputSettings::deserialize(const QByteArray& data)
if (d.getVersion() == 1)
{
uint32_t uintval;
int intval;
d.readString(1, &m_deviceName, "");
d.readS32(2, &m_sampleRate, 48000);
@ -81,6 +84,8 @@ bool AudioInputSettings::deserialize(const QByteArray& data)
d.readS32(5, (int *)&m_iqMapping, IQMapping::L);
d.readBool(6, &m_dcBlock, false);
d.readBool(7, &m_iqImbalance, false);
d.readS32(8, &intval, 2);
m_fcPos = (fcPos_t) intval;
d.readBool(24, &m_useReverseAPI, false);
d.readString(25, &m_reverseAPIAddress, "127.0.0.1");
@ -127,6 +132,9 @@ void AudioInputSettings::applySettings(const QStringList& settingsKeys, const Au
if (settingsKeys.contains("iqImbalance")) {
m_iqImbalance = settings.m_iqImbalance;
}
if (settingsKeys.contains("fcPos")) {
m_fcPos = settings.m_fcPos;
}
if (settingsKeys.contains("useReverseAPI")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
@ -166,6 +174,9 @@ QString AudioInputSettings::getDebugString(const QStringList& settingsKeys, bool
if (settingsKeys.contains("iqImbalance") || force) {
ostr << " m_iqImbalance: " << m_iqImbalance;
}
if (settingsKeys.contains("fcPos") || force) {
ostr << " m_fcPos: " << m_fcPos;
}
if (settingsKeys.contains("useReverseAPI") || force) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}

View File

@ -24,6 +24,12 @@
struct AudioInputSettings {
typedef enum {
FC_POS_INFRA = 0,
FC_POS_SUPRA,
FC_POS_CENTER
} fcPos_t;
QString m_deviceName; // Including realm, as from getFullDeviceName below
int m_sampleRate;
float m_volume;
@ -36,6 +42,7 @@ struct AudioInputSettings {
} m_iqMapping;
bool m_dcBlock;
bool m_iqImbalance;
fcPos_t m_fcPos;
bool m_useReverseAPI;
QString m_reverseAPIAddress;

View File

@ -107,31 +107,90 @@ void AudioInputWorker::decimate(qint16 *buf, unsigned int nbRead)
{
SampleVector::iterator it = m_convertBuffer.begin();
switch (m_log2Decim)
{
case 0:
m_decimatorsIQ.decimate1(&it, buf, 2*nbRead);
break;
case 1:
m_decimatorsIQ.decimate2_cen(&it, buf, 2*nbRead);
break;
case 2:
m_decimatorsIQ.decimate4_cen(&it, buf, 2*nbRead);
break;
case 3:
m_decimatorsIQ.decimate8_cen(&it, buf, 2*nbRead);
break;
case 4:
m_decimatorsIQ.decimate16_cen(&it, buf, 2*nbRead);
break;
case 5:
m_decimatorsIQ.decimate32_cen(&it, buf, 2*nbRead);
break;
case 6:
m_decimatorsIQ.decimate64_cen(&it, buf, 2*nbRead);
break;
default:
break;
if (m_log2Decim == 0)
{
m_decimatorsIQ.decimate1(&it, buf, 2*nbRead);
}
else
{
if (m_fcPos == 0) // Infradyne
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ.decimate2_inf(&it, buf, 2*nbRead);
break;
case 2:
m_decimatorsIQ.decimate4_inf(&it, buf, 2*nbRead);
break;
case 3:
m_decimatorsIQ.decimate8_inf(&it, buf, 2*nbRead);
break;
case 4:
m_decimatorsIQ.decimate16_inf(&it, buf, 2*nbRead);
break;
case 5:
m_decimatorsIQ.decimate32_inf(&it, buf, 2*nbRead);
break;
case 6:
m_decimatorsIQ.decimate64_inf(&it, buf, 2*nbRead);
break;
default:
break;
}
}
else if (m_fcPos == 1) // Supradyne
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ.decimate2_sup(&it, buf, 2*nbRead);
break;
case 2:
m_decimatorsIQ.decimate4_sup(&it, buf, 2*nbRead);
break;
case 3:
m_decimatorsIQ.decimate8_sup(&it, buf, 2*nbRead);
break;
case 4:
m_decimatorsIQ.decimate16_sup(&it, buf, 2*nbRead);
break;
case 5:
m_decimatorsIQ.decimate32_sup(&it, buf, 2*nbRead);
break;
case 6:
m_decimatorsIQ.decimate64_sup(&it, buf, 2*nbRead);
break;
default:
break;
}
}
else // centered
{
switch (m_log2Decim)
{
case 1:
m_decimatorsIQ.decimate2_cen(&it, buf, 2*nbRead);
break;
case 2:
m_decimatorsIQ.decimate4_cen(&it, buf, 2*nbRead);
break;
case 3:
m_decimatorsIQ.decimate8_cen(&it, buf, 2*nbRead);
break;
case 4:
m_decimatorsIQ.decimate16_cen(&it, buf, 2*nbRead);
break;
case 5:
m_decimatorsIQ.decimate32_cen(&it, buf, 2*nbRead);
break;
case 6:
m_decimatorsIQ.decimate64_cen(&it, buf, 2*nbRead);
break;
default:
break;
}
}
}
m_sampleFifo->write(m_convertBuffer.begin(), it);

View File

@ -37,6 +37,7 @@ public:
void startWork();
void stopWork();
void setLog2Decimation(unsigned int log2_decim) {m_log2Decim = log2_decim;}
void setFcPos(int fcPos) { m_fcPos = fcPos; }
void setIQMapping(AudioInputSettings::IQMapping iqMapping) {m_iqMapping = iqMapping;}
static const int m_convBufSamples = 4096;
@ -45,6 +46,7 @@ private:
bool m_running;
unsigned int m_log2Decim;
int m_fcPos;
AudioInputSettings::IQMapping m_iqMapping;
qint16 m_buf[m_convBufSamples*2]; // stereo (I, Q)

View File

@ -18,27 +18,43 @@ Device start / stop button.
- Green square icon: device is running and can be stopped
- Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again.
<h3>2: Device</h3>
<h3>2: Auto remove DC component</h3>
Software DSP auto remove DC correction. This will work in I/Q mode (stereo I/Q) only.
<h3>3: Auto make I/Q balance</h3>
Software DSP auto I/Q imbalance correction. The DC correction (8) must be enabled for this to be effective. This will work in I/Q mode (stereo I/Q) only.
<h3>4: Device</h3>
The audio device to use.
<h3>3: Refresh devices</h3>
<h3>5: Refresh devices</h3>
Refresh the list of audio devices.
<h3>4: Audio sample rate</h3>
<h3>6: Audio sample rate</h3>
Audio sample rate in Hz (Sa/s).
<h3>5: Decimation</h3>
<h3>7: Decimation</h3>
A decimation factor to apply to the audio data. The baseband sample rate will be the audio sample, divided by this decimation factor.
<h3>6: Volume</h3>
<h3>8: Decimated bandpass center frequency position relative the device center frequency</h3>
This will work in I/Q mode (stereo I/Q) only.
- **Cen**: the decimation operation takes place around the device center frequency Fs
- **Inf**: the decimation operation takes place around Fs - Fc.
- **Sup**: the decimation operation takes place around Fs + Fc.
<h3>9: Volume</h3>
A control to set the input volume. This is not supported by all input audio devices.
<h3>7: Channel Map</h3>
<h3>10: Channel Map</h3>
This controls how the left and right audio channels map on to the IQ channels.
@ -47,10 +63,3 @@ This controls how the left and right audio channels map on to the IQ channels.
* 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.
<h3>8: Auto remove DC component</h3>
Software DSP auto remove DC correction.
<h3>9: Auto make I/Q balance</h3>
Software DSP auto I/Q imbalance correction. The DC correction (8) must be enabled for this to be effective.

View File

@ -2457,6 +2457,10 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "Auto IQ balance (you need auto DC blocking active)\n * 0 - Off\n * 1 - On\n"
},
"fcPos" : {
"type" : "integer",
"description" : "Decimated bandpass center frequency position\n * 0 - Infradyne\n * 1 - Supradyne\n * 2 - Centered\n"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
@ -57256,7 +57260,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-03-11T04:40:02.823+01:00
Generated 2023-03-11T09:38:49.252+01:00
</div>
</div>
</div>

View File

@ -33,6 +33,13 @@ AudioInputSettings:
Auto IQ balance (you need auto DC blocking active)
* 0 - Off
* 1 - On
fcPos:
type: integer
description: >
Decimated bandpass center frequency position
* 0 - Infradyne
* 1 - Supradyne
* 2 - Centered
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer

View File

@ -33,6 +33,13 @@ AudioInputSettings:
Auto IQ balance (you need auto DC blocking active)
* 0 - Off
* 1 - On
fcPos:
type: integer
description: >
Decimated bandpass center frequency position
* 0 - Infradyne
* 1 - Supradyne
* 2 - Centered
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer

View File

@ -2457,6 +2457,10 @@ margin-bottom: 20px;
"type" : "integer",
"description" : "Auto IQ balance (you need auto DC blocking active)\n * 0 - Off\n * 1 - On\n"
},
"fcPos" : {
"type" : "integer",
"description" : "Decimated bandpass center frequency position\n * 0 - Infradyne\n * 1 - Supradyne\n * 2 - Centered\n"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
@ -57256,7 +57260,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-03-11T04:40:02.823+01:00
Generated 2023-03-11T09:38:49.252+01:00
</div>
</div>
</div>

View File

@ -42,6 +42,8 @@ SWGAudioInputSettings::SWGAudioInputSettings() {
m_dc_block_isSet = false;
iq_imbalance = 0;
m_iq_imbalance_isSet = false;
fc_pos = 0;
m_fc_pos_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
@ -72,6 +74,8 @@ SWGAudioInputSettings::init() {
m_dc_block_isSet = false;
iq_imbalance = 0;
m_iq_imbalance_isSet = false;
fc_pos = 0;
m_fc_pos_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
@ -94,6 +98,7 @@ SWGAudioInputSettings::cleanup() {
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
@ -126,6 +131,8 @@ SWGAudioInputSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&iq_imbalance, pJson["iqImbalance"], "qint32", "");
::SWGSDRangel::setValue(&fc_pos, pJson["fcPos"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
@ -171,6 +178,9 @@ SWGAudioInputSettings::asJsonObject() {
if(m_iq_imbalance_isSet){
obj->insert("iqImbalance", QJsonValue(iq_imbalance));
}
if(m_fc_pos_isSet){
obj->insert("fcPos", QJsonValue(fc_pos));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
@ -257,6 +267,16 @@ SWGAudioInputSettings::setIqImbalance(qint32 iq_imbalance) {
this->m_iq_imbalance_isSet = true;
}
qint32
SWGAudioInputSettings::getFcPos() {
return fc_pos;
}
void
SWGAudioInputSettings::setFcPos(qint32 fc_pos) {
this->fc_pos = fc_pos;
this->m_fc_pos_isSet = true;
}
qint32
SWGAudioInputSettings::getUseReverseApi() {
return use_reverse_api;
@ -323,6 +343,9 @@ SWGAudioInputSettings::isSet(){
if(m_iq_imbalance_isSet){
isObjectUpdated = true; break;
}
if(m_fc_pos_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}

View File

@ -63,6 +63,9 @@ public:
qint32 getIqImbalance();
void setIqImbalance(qint32 iq_imbalance);
qint32 getFcPos();
void setFcPos(qint32 fc_pos);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
@ -100,6 +103,9 @@ private:
qint32 iq_imbalance;
bool m_iq_imbalance_isSet;
qint32 fc_pos;
bool m_fc_pos_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;