From d8ae6fc765fb0f8ebf2932aef4a75bcf44e09f70 Mon Sep 17 00:00:00 2001
From: Jon Beniston <jon@beniston.com>
Date: Sun, 25 Oct 2020 11:57:48 +0000
Subject: [PATCH] Import USRP support.

Add LO offset support.
Only set tx/rx_bandwidth after getting tx stream, to reduce TX LO leakage for <10MHz bandwidths.
Check for reference and LO lock before getting streams.
---
 devices/usrp/deviceusrp.cpp                   |  28 +++++
 devices/usrp/deviceusrp.h                     |   2 +
 devices/usrp/deviceusrpshared.h               |   6 +
 plugins/samplesink/usrpoutput/usrpoutput.cpp  |  70 +++++++-----
 plugins/samplesink/usrpoutput/usrpoutput.h    |   2 +-
 .../samplesink/usrpoutput/usrpoutputgui.cpp   |  13 +++
 plugins/samplesink/usrpoutput/usrpoutputgui.h |   1 +
 .../samplesink/usrpoutput/usrpoutputgui.ui    |  55 +++++++++
 .../usrpoutput/usrpoutputsettings.cpp         |   5 +-
 .../usrpoutput/usrpoutputsettings.h           |   1 +
 plugins/samplesource/usrpinput/usrpinput.cpp  | 105 +++++++++++-------
 plugins/samplesource/usrpinput/usrpinput.h    |   2 +-
 .../samplesource/usrpinput/usrpinputgui.cpp   |  15 ++-
 plugins/samplesource/usrpinput/usrpinputgui.h |   1 +
 .../samplesource/usrpinput/usrpinputgui.ui    |  57 ++++++++++
 .../usrpinput/usrpinputsettings.cpp           |   5 +-
 .../usrpinput/usrpinputsettings.h             |   1 +
 17 files changed, 299 insertions(+), 70 deletions(-)

diff --git a/devices/usrp/deviceusrp.cpp b/devices/usrp/deviceusrp.cpp
index d5ef1ba4b..5d3e59307 100644
--- a/devices/usrp/deviceusrp.cpp
+++ b/devices/usrp/deviceusrp.cpp
@@ -69,3 +69,31 @@ void DeviceUSRP::enumOriginDevices(const QString& hardwareId, PluginInterface::O
         qDebug() << "DeviceUSRP::enumOriginDevices: exception: " << e.what();
     }
 }
+
+void DeviceUSRP::waitForLock(uhd::usrp::multi_usrp::sptr usrp, const QString& clockSource, int channel)
+{
+    int tries;
+    const int maxTries = 100;
+
+    // Wait for Ref lock
+    std::vector<std::string> sensor_names;
+    sensor_names = usrp->get_tx_sensor_names(channel);
+    if (clockSource == "external")
+    {
+        if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())
+        {
+            for (tries = 0; !usrp->get_mboard_sensor("ref_locked", 0).to_bool() && (tries < maxTries); tries++)
+                std::this_thread::sleep_for(std::chrono::milliseconds(10));
+            if (tries == maxTries)
+                qCritical("USRPInput::acquireChannel: Failed to lock ref");
+        }
+    }
+    // Wait for LO lock
+    if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked") != sensor_names.end())
+    {
+        for (tries = 0; !usrp->get_tx_sensor("lo_locked", channel).to_bool() && (tries < maxTries); tries++)
+            std::this_thread::sleep_for(std::chrono::milliseconds(10));
+        if (tries == maxTries)
+            qCritical("USRPInput::acquireChannel: Failed to lock LO");
+    }
+}
diff --git a/devices/usrp/deviceusrp.h b/devices/usrp/deviceusrp.h
index 497129e72..e79e1d14c 100644
--- a/devices/usrp/deviceusrp.h
+++ b/devices/usrp/deviceusrp.h
@@ -33,6 +33,8 @@ public:
     /** Enumeration of USRP hardware devices */
     static void enumOriginDevices(const QString& hardwareId, PluginInterface::OriginDevices& originDevices);
 
+    /** Wait for ref clock and LO to lock */
+    static void waitForLock(uhd::usrp::multi_usrp::sptr usrp, const QString& clockSource, int channel);
 };
 
 #endif /* DEVICES_USRP_DEVICEUSRP_H_ */
diff --git a/devices/usrp/deviceusrpshared.h b/devices/usrp/deviceusrpshared.h
index 6d71db1f2..7ee429759 100644
--- a/devices/usrp/deviceusrpshared.h
+++ b/devices/usrp/deviceusrpshared.h
@@ -36,31 +36,37 @@ public:
     public:
         int      getDevSampleRate() const { return m_devSampleRate; }
         uint64_t getCenterFrequency() const { return m_centerFrequency; }
+        int      getLOOffset() const { return m_loOffset; }
         bool getRxElseTx() const { return m_rxElseTx; }
 
         static MsgReportBuddyChange* create(
                 int devSampleRate,
                 uint64_t centerFrequency,
+                int loOffset,
                 bool rxElseTx)
         {
             return new MsgReportBuddyChange(
                     devSampleRate,
                     centerFrequency,
+                    loOffset,
                     rxElseTx);
         }
 
     private:
         int      m_devSampleRate;       //!< device/host sample rate
         uint64_t m_centerFrequency;     //!< Center frequency
+        int      m_loOffset;            //!< LO offset
         bool     m_rxElseTx;            //!< tells which side initiated the message
 
         MsgReportBuddyChange(
                 int devSampleRate,
                 uint64_t centerFrequency,
+                int loOffset,
                 bool rxElseTx) :
             Message(),
             m_devSampleRate(devSampleRate),
             m_centerFrequency(centerFrequency),
+            m_loOffset(loOffset),
             m_rxElseTx(rxElseTx)
         { }
     };
diff --git a/plugins/samplesink/usrpoutput/usrpoutput.cpp b/plugins/samplesink/usrpoutput/usrpoutput.cpp
index 0b2b5d33e..b175136e3 100644
--- a/plugins/samplesink/usrpoutput/usrpoutput.cpp
+++ b/plugins/samplesink/usrpoutput/usrpoutput.cpp
@@ -293,6 +293,13 @@ bool USRPOutput::acquireChannel()
     {
         try
         {
+            uhd::usrp::multi_usrp::sptr usrp = m_deviceShared.m_deviceParams->getDevice();
+
+            // Apply settings before creating stream
+            // However, don't set LPF to <10MHz at this stage, otherwise there is massive TX LO leakage
+            applySettings(m_settings, true, true);
+            usrp->set_tx_bandwidth(56000000, m_deviceShared.m_channel);
+
             // set up the stream
             std::string cpu_format("sc16");
             std::string wire_format("sc16");
@@ -302,10 +309,16 @@ bool USRPOutput::acquireChannel()
             uhd::stream_args_t stream_args(cpu_format, wire_format);
             stream_args.channels = channel_nums;
 
-            m_streamId = m_deviceShared.m_deviceParams->getDevice()->get_tx_stream(stream_args);
+            m_streamId = usrp->get_tx_stream(stream_args);
 
             // Match our transmit buffer size to what UHD uses
             m_bufSamples = m_streamId->get_max_num_samps();
+
+            // Wait for reference and LO to lock
+            DeviceUSRP::waitForLock(usrp, m_settings.m_clockSource, m_deviceShared.m_channel);
+
+            // Now we can set desired bandwidth
+            usrp->set_tx_bandwidth(m_settings.m_lpfBW, m_deviceShared.m_channel);
         }
         catch (std::exception& e)
         {
@@ -326,14 +339,8 @@ void USRPOutput::releaseChannel()
     suspendRxBuddies();
     suspendTxBuddies();
 
-    // FIXME: Currently we do not try to destroy the stream, as there seems to be
-    // an issue when we re-acquire the stream, the output spectrum will not be correct
-    // The transmitter output will be disabled when we stop sending data to it anyway
-    if (false)
-    {
-        // destroy the stream
-        m_streamId = nullptr;
-    }
+    // destroy the stream
+    m_streamId = nullptr;
 
     resumeTxBuddies();
     resumeRxBuddies();
@@ -343,7 +350,7 @@ void USRPOutput::releaseChannel()
 
 void USRPOutput::init()
 {
-    applySettings(m_settings, true);
+    applySettings(m_settings, false, true);
 }
 
 bool USRPOutput::start()
@@ -362,8 +369,6 @@ bool USRPOutput::start()
     m_usrpOutputThread = new USRPOutputThread(m_streamId, m_bufSamples, &m_sampleSourceFifo);
     qDebug("USRPOutput::start: thread created");
 
-    applySettings(m_settings, true);
-
     m_usrpOutputThread->setLog2Interpolation(m_settings.m_log2SoftInterp);
     m_usrpOutputThread->startWork();
 
@@ -494,7 +499,7 @@ bool USRPOutput::handleMessage(const Message& message)
         MsgConfigureUSRP& conf = (MsgConfigureUSRP&) message;
         qDebug() << "USRPOutput::handleMessage: MsgConfigureUSRP";
 
-        if (!applySettings(conf.getSettings(), conf.getForce()))
+        if (!applySettings(conf.getSettings(), false, conf.getForce()))
         {
             qDebug("USRPOutput::handleMessage config error");
         }
@@ -542,6 +547,7 @@ bool USRPOutput::handleMessage(const Message& message)
         {
             m_settings.m_devSampleRate   = report.getDevSampleRate();
             m_settings.m_centerFrequency = report.getCenterFrequency();
+            m_settings.m_loOffset        = report.getLOOffset();
         }
 
         DSPSignalNotification *notif = new DSPSignalNotification(
@@ -550,7 +556,7 @@ bool USRPOutput::handleMessage(const Message& message)
         m_deviceAPI->getDeviceEngineInputMessageQueue()->push(notif);
 
         DeviceUSRPShared::MsgReportBuddyChange *reportToGUI = DeviceUSRPShared::MsgReportBuddyChange::create(
-                m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
+                m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, false);
         getMessageQueueToGUI()->push(reportToGUI);
 
         return true;
@@ -604,7 +610,7 @@ bool USRPOutput::handleMessage(const Message& message)
     }
 }
 
-bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
+bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool preGetStream, bool force)
 {
     bool forwardChangeOwnDSP = false;
     bool forwardChangeTxDSP  = false;
@@ -626,7 +632,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
         {
             reverseAPIKeys.append("clockSource");
 
-            if (m_deviceShared.m_deviceParams->getDevice())
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 try
                 {
@@ -656,7 +662,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
             reverseAPIKeys.append("devSampleRate");
             forwardChangeAllDSP = true;
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_tx_rate(settings.m_devSampleRate, m_deviceShared.m_channel);
                 double actualSampleRate = m_deviceShared.m_deviceParams->getDevice()->get_tx_rate(m_deviceShared.m_channel);
@@ -667,6 +673,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
         }
 
         if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
+            || (m_settings.m_loOffset != settings.m_loOffset)
             || (m_settings.m_transverterMode != settings.m_transverterMode)
             || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)
             || force)
@@ -676,12 +683,20 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
             reverseAPIKeys.append("transverterDeltaFrequency");
             forwardChangeTxDSP = true;
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
-                uhd::tune_request_t tune_request(deviceCenterFrequency);
-                m_deviceShared.m_deviceParams->getDevice()->set_tx_freq(tune_request, m_deviceShared.m_channel);
+                if (settings.m_loOffset != 0)
+                {
+                    uhd::tune_request_t tune_request(deviceCenterFrequency, settings.m_loOffset);
+                    m_deviceShared.m_deviceParams->getDevice()->set_tx_freq(tune_request, m_deviceShared.m_channel);
+                }
+                else
+                {
+                    uhd::tune_request_t tune_request(deviceCenterFrequency);
+                    m_deviceShared.m_deviceParams->getDevice()->set_tx_freq(tune_request, m_deviceShared.m_channel);
+                }
                 m_deviceShared.m_centerFrequency = deviceCenterFrequency; // for buddies
-                qDebug("USRPOutput::applySettings: frequency set to %lld", deviceCenterFrequency);
+                qDebug("USRPOutput::applySettings: frequency set to %lld with LO offset %d", deviceCenterFrequency, settings.m_loOffset);
             }
         }
 
@@ -707,7 +722,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
         {
             reverseAPIKeys.append("gain");
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_tx_gain(settings.m_gain, m_deviceShared.m_channel);
                 qDebug() << "USRPOutput::applySettings: Gain set to " << settings.m_gain;
@@ -718,6 +733,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
         {
             reverseAPIKeys.append("lpfBW");
 
+            // Don't set bandwidth before get_tx_stream (See above)
             if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_tx_bandwidth(settings.m_lpfBW, m_deviceShared.m_channel);
@@ -742,7 +758,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
         {
             reverseAPIKeys.append("antennaPath");
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_tx_antenna(settings.m_antennaPath.toStdString(), m_deviceShared.m_channel);
                 qDebug("USRPOutput::applySettings: set antenna path to %s on channel %d", qPrintable(settings.m_antennaPath), m_deviceShared.m_channel);
@@ -779,7 +795,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
             for (; itSink != sinkBuddies.end(); ++itSink)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, false);
                 (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
             }
 
@@ -790,7 +806,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
             for (; itSource != sourceBuddies.end(); ++itSource)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, false);
                 (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
             }
         }
@@ -811,7 +827,7 @@ bool USRPOutput::applySettings(const USRPOutputSettings& settings, bool force)
             for (; itSink != sinkBuddies.end(); ++itSink)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, false);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, false);
                 (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
             }
         }
@@ -1035,7 +1051,7 @@ void USRPOutput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response
     quint32 underflows = 0;
     quint32 droppedPackets = 0;
 
-    if ((m_streamId != nullptr) && m_channelAcquired)
+    if ((m_streamId != nullptr) && (m_usrpOutputThread != nullptr) && m_channelAcquired)
     {
         m_usrpOutputThread->getStreamStatus(active, underflows, droppedPackets);
         success = true;
diff --git a/plugins/samplesink/usrpoutput/usrpoutput.h b/plugins/samplesink/usrpoutput/usrpoutput.h
index 7ebec32d0..e61581903 100644
--- a/plugins/samplesink/usrpoutput/usrpoutput.h
+++ b/plugins/samplesink/usrpoutput/usrpoutput.h
@@ -238,7 +238,7 @@ private:
     void resumeRxBuddies();
     void suspendTxBuddies();
     void resumeTxBuddies();
-    bool applySettings(const USRPOutputSettings& settings, bool force = false);
+    bool applySettings(const USRPOutputSettings& settings, bool preGetStream, bool force = false);
     void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
     void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const USRPOutputSettings& settings, bool force);
     void webapiReverseSendStartStop(bool start);
diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp
index f4874ec24..bdf013cf2 100644
--- a/plugins/samplesink/usrpoutput/usrpoutputgui.cpp
+++ b/plugins/samplesink/usrpoutput/usrpoutputgui.cpp
@@ -57,6 +57,9 @@ USRPOutputGUI::USRPOutputGUI(DeviceUISet *deviceUISet, QWidget* parent) :
     ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
     ui->sampleRate->setValueRange(8, (uint32_t) minF, (uint32_t) maxF);
 
+    ui->loOffset->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+    ui->loOffset->setValueRange(false, 5, (int32_t)-maxF/2/1000, (int32_t)maxF/2/1000); // LO offset shouldn't be greater than half the sample rate
+
     m_usrpOutput->getLPRange(minF, maxF);
     ui->lpf->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
     ui->lpf->setValueRange(5, (minF/1000)+1, maxF/1000);
@@ -181,6 +184,7 @@ bool USRPOutputGUI::handleMessage(const Message& message)
 
         if (!report.getRxElseTx()) {
             m_settings.m_centerFrequency = report.getCenterFrequency();
+            m_settings.m_loOffset        = report.getLOOffset();
         }
 
         blockApplySettings(true);
@@ -293,6 +297,8 @@ void USRPOutputGUI::updateSampleRate()
     } else {
         ui->sampleRateLabel->setText(tr("%1M").arg(QString::number(sr / 1000000.0f, 'g', 5)));
     }
+    // LO offset shouldn't be greater than half the sample rate
+    ui->loOffset->setValueRange(false, 5, -(int32_t)sr/2/1000, (int32_t)sr/2/1000);
 }
 
 void USRPOutputGUI::displaySampleRate()
@@ -343,6 +349,7 @@ void USRPOutputGUI::displaySettings()
     updateSampleRate();
 
     ui->lpf->setValue(m_settings.m_lpfBW / 1000);
+    ui->loOffset->setValue(m_settings.m_loOffset / 1000);
 
     ui->gain->setValue(m_settings.m_gain);
     ui->gainText->setText(tr("%1dB").arg(m_settings.m_gain));
@@ -497,6 +504,12 @@ void USRPOutputGUI::on_lpf_changed(quint64 value)
     sendSettings();
 }
 
+void USRPOutputGUI::on_loOffset_changed(qint64 value)
+{
+    m_settings.m_loOffset = value * 1000;
+    sendSettings();
+}
+
 void USRPOutputGUI::on_gain_valueChanged(int value)
 {
     m_settings.m_gain = value;
diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.h b/plugins/samplesink/usrpoutput/usrpoutputgui.h
index 1429b9c0a..1f337e40f 100644
--- a/plugins/samplesink/usrpoutput/usrpoutputgui.h
+++ b/plugins/samplesink/usrpoutput/usrpoutputgui.h
@@ -88,6 +88,7 @@ private slots:
     void on_sampleRate_changed(quint64 value);
     void on_swInterp_currentIndexChanged(int index);
     void on_lpf_changed(quint64 value);
+    void on_loOffset_changed(qint64 value);
     void on_gain_valueChanged(int value);
     void on_antenna_currentIndexChanged(int index);
     void on_clockSource_currentIndexChanged(int index);
diff --git a/plugins/samplesink/usrpoutput/usrpoutputgui.ui b/plugins/samplesink/usrpoutput/usrpoutputgui.ui
index 5b740bd57..49abaea36 100644
--- a/plugins/samplesink/usrpoutput/usrpoutputgui.ui
+++ b/plugins/samplesink/usrpoutput/usrpoutputgui.ui
@@ -562,6 +562,55 @@
        </property>
       </widget>
      </item>
+     <item>
+      <widget class="Line" name="line_3">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="loOffsetLabel">
+       <property name="text">
+        <string>LO</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="ValueDialZ" name="loOffset" 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>Liberation Mono</family>
+         <pointsize>12</pointsize>
+        </font>
+       </property>
+       <property name="cursor">
+        <cursorShape>PointingHandCursor</cursorShape>
+       </property>
+       <property name="toolTip">
+        <string>LO frequency offset. This should not be greater than half the sample rate.</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="loOffsetUnits">
+       <property name="text">
+        <string>kHz</string>
+       </property>
+      </widget>
+     </item>
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
@@ -684,6 +733,12 @@
    <header>gui/valuedial.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ValueDialZ</class>
+   <extends>QWidget</extends>
+   <header>gui/valuedialz.h</header>
+   <container>1</container>
+  </customwidget>
   <customwidget>
    <class>TransverterButton</class>
    <extends>QPushButton</extends>
diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp
index 4c40887a8..941962b9b 100644
--- a/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp
+++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.cpp
@@ -29,8 +29,9 @@ void USRPOutputSettings::resetToDefaults()
 {
     m_centerFrequency = 435000*1000;
     m_devSampleRate = 3000000;
+    m_loOffset = 0;
     m_log2SoftInterp = 0;
-    m_lpfBW = 5.5e6f;
+    m_lpfBW = 10e6f;
     m_gain = 50;
     m_antennaPath = "TX/RX";
     m_clockSource = "internal";
@@ -58,6 +59,7 @@ QByteArray USRPOutputSettings::serialize() const
     s.writeString(10, m_reverseAPIAddress);
     s.writeU32(11, m_reverseAPIPort);
     s.writeU32(12, m_reverseAPIDeviceIndex);
+    s.writeS32(13, m_loOffset);
 
     return s.final();
 }
@@ -97,6 +99,7 @@ bool USRPOutputSettings::deserialize(const QByteArray& data)
 
         d.readU32(12, &uintval, 0);
         m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
+        d.readS32(13, &m_loOffset, 0);
 
         return true;
     }
diff --git a/plugins/samplesink/usrpoutput/usrpoutputsettings.h b/plugins/samplesink/usrpoutput/usrpoutputsettings.h
index 410dbbdd5..899a60c72 100644
--- a/plugins/samplesink/usrpoutput/usrpoutputsettings.h
+++ b/plugins/samplesink/usrpoutput/usrpoutputsettings.h
@@ -33,6 +33,7 @@ struct USRPOutputSettings
     // global settings to be saved
     uint64_t m_centerFrequency;
     int      m_devSampleRate;
+    int      m_loOffset;
     // channel settings
     uint32_t m_log2SoftInterp;
     float    m_lpfBW;        //!< Analog lowpass filter bandwidth (Hz)
diff --git a/plugins/samplesource/usrpinput/usrpinput.cpp b/plugins/samplesource/usrpinput/usrpinput.cpp
index d7598aa02..cdd14a0aa 100644
--- a/plugins/samplesource/usrpinput/usrpinput.cpp
+++ b/plugins/samplesource/usrpinput/usrpinput.cpp
@@ -316,25 +316,41 @@ bool USRPInput::acquireChannel()
     suspendRxBuddies();
     suspendTxBuddies();
 
-    try
+    if (m_streamId == nullptr)
     {
-        // set up the stream
-        std::string cpu_format("sc16");
-        std::string wire_format("sc16");
-        std::vector<size_t> channel_nums;
-        channel_nums.push_back(m_deviceShared.m_channel);
+        try
+        {
+            uhd::usrp::multi_usrp::sptr usrp = m_deviceShared.m_deviceParams->getDevice();
 
-        uhd::stream_args_t stream_args(cpu_format, wire_format);
-        stream_args.channels = channel_nums;
+            // Apply settings before creating stream
+            // However, don't set LPF to <10MHz at this stage, otherwise there is massive TX LO leakage
+            applySettings(m_settings, true, true);
+            usrp->set_rx_bandwidth(56000000, m_deviceShared.m_channel);
 
-        m_streamId = m_deviceShared.m_deviceParams->getDevice()->get_rx_stream(stream_args);
+            // set up the stream
+            std::string cpu_format("sc16");
+            std::string wire_format("sc16");
+            std::vector<size_t> channel_nums;
+            channel_nums.push_back(m_deviceShared.m_channel);
 
-        // Match our receive buffer size to what UHD uses
-        m_bufSamples = m_streamId->get_max_num_samps();
-    }
-    catch (std::exception& e)
-    {
-        qDebug() << "USRPInput::acquireChannel: exception: " << e.what();
+            uhd::stream_args_t stream_args(cpu_format, wire_format);
+            stream_args.channels = channel_nums;
+
+            m_streamId = m_deviceShared.m_deviceParams->getDevice()->get_rx_stream(stream_args);
+
+            // Match our receive buffer size to what UHD uses
+            m_bufSamples = m_streamId->get_max_num_samps();
+
+            // Wait for reference and LO to lock
+            DeviceUSRP::waitForLock(usrp, m_settings.m_clockSource, m_deviceShared.m_channel);
+
+            // Now we can set desired bandwidth
+            usrp->set_rx_bandwidth(m_settings.m_lpfBW, m_deviceShared.m_channel);
+        }
+        catch (std::exception& e)
+        {
+            qDebug() << "USRPInput::acquireChannel: exception: " << e.what();
+        }
     }
 
     resumeTxBuddies();
@@ -350,7 +366,7 @@ void USRPInput::releaseChannel()
     suspendRxBuddies();
     suspendTxBuddies();
 
-    // destroy the stream - FIXME: Better way to do this?
+    // destroy the stream
     m_streamId = nullptr;
 
     resumeTxBuddies();
@@ -363,7 +379,7 @@ void USRPInput::releaseChannel()
 
 void USRPInput::init()
 {
-    applySettings(m_settings, true);
+    applySettings(m_settings, false, true);
 }
 
 bool USRPInput::start()
@@ -384,8 +400,6 @@ bool USRPInput::start()
     m_usrpInputThread = new USRPInputThread(m_streamId, m_bufSamples, &m_sampleFifo);
     qDebug("USRPInput::start: thread created");
 
-    applySettings(m_settings, true);
-
     m_usrpInputThread->setLog2Decimation(m_settings.m_log2SoftDecim);
     m_usrpInputThread->startWork();
 
@@ -521,7 +535,7 @@ bool USRPInput::handleMessage(const Message& message)
         MsgConfigureUSRP& conf = (MsgConfigureUSRP&) message;
         qDebug() << "USRPInput::handleMessage: MsgConfigureUSRP";
 
-        if (!applySettings(conf.getSettings(), conf.getForce()))
+        if (!applySettings(conf.getSettings(), false, conf.getForce()))
         {
             qDebug("USRPInput::handleMessage config error");
         }
@@ -536,6 +550,7 @@ bool USRPInput::handleMessage(const Message& message)
         {
             m_settings.m_devSampleRate   = report.getDevSampleRate();
             m_settings.m_centerFrequency = report.getCenterFrequency();
+            m_settings.m_loOffset        = report.getLOOffset();
         }
         else if (m_running)
         {
@@ -556,7 +571,7 @@ bool USRPInput::handleMessage(const Message& message)
         if (getMessageQueueToGUI())
         {
             DeviceUSRPShared::MsgReportBuddyChange *reportToGUI = DeviceUSRPShared::MsgReportBuddyChange::create(
-                    m_settings.m_devSampleRate, m_settings.m_centerFrequency, true);
+                    m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, true);
             getMessageQueueToGUI()->push(reportToGUI);
         }
 
@@ -633,7 +648,7 @@ bool USRPInput::handleMessage(const Message& message)
     }
 }
 
-bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
+bool USRPInput::applySettings(const USRPInputSettings& settings, bool preGetStream, bool force)
 {
     bool forwardChangeOwnDSP = false;
     bool forwardChangeRxDSP  = false;
@@ -655,7 +670,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         {
             reverseAPIKeys.append("clockSource");
 
-            if (m_deviceShared.m_deviceParams->getDevice())
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 try
                 {
@@ -686,7 +701,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
             reverseAPIKeys.append("devSampleRate");
             forwardChangeAllDSP = true;
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_rx_rate(settings.m_devSampleRate, m_deviceShared.m_channel);
                 double actualSampleRate = m_deviceShared.m_deviceParams->getDevice()->get_rx_rate(m_deviceShared.m_channel);
@@ -698,6 +713,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         }
 
         if ((m_settings.m_centerFrequency != settings.m_centerFrequency)
+            || (m_settings.m_loOffset != settings.m_loOffset)
             || (m_settings.m_transverterMode != settings.m_transverterMode)
             || (m_settings.m_transverterDeltaFrequency != settings.m_transverterDeltaFrequency)
             || force)
@@ -707,26 +723,34 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
             reverseAPIKeys.append("transverterDeltaFrequency");
             forwardChangeRxDSP = true;
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
-                uhd::tune_request_t tune_request(deviceCenterFrequency);
-                m_deviceShared.m_deviceParams->getDevice()->set_rx_freq(tune_request, m_deviceShared.m_channel);
+                if (settings.m_loOffset != 0)
+                {
+                    uhd::tune_request_t tune_request(deviceCenterFrequency, settings.m_loOffset);
+                    m_deviceShared.m_deviceParams->getDevice()->set_rx_freq(tune_request, m_deviceShared.m_channel);
+                }
+                else
+                {
+                    uhd::tune_request_t tune_request(deviceCenterFrequency);
+                    m_deviceShared.m_deviceParams->getDevice()->set_rx_freq(tune_request, m_deviceShared.m_channel);
+                }
                 m_deviceShared.m_centerFrequency = deviceCenterFrequency; // for buddies
-                qDebug("USRPInput::applySettings: frequency set to %lld", deviceCenterFrequency);
+                qDebug("USRPInput::applySettings: frequency set to %lld with LO offset %d", deviceCenterFrequency, settings.m_loOffset);
             }
         }
 
         if ((m_settings.m_dcBlock != settings.m_dcBlock) || force)
         {
             reverseAPIKeys.append("dcBlock");
-            if (m_deviceShared.m_deviceParams->getDevice())
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
                 m_deviceShared.m_deviceParams->getDevice()->set_rx_dc_offset(settings.m_dcBlock, m_deviceShared.m_channel);
         }
 
         if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force)
         {
             reverseAPIKeys.append("iqCorrection");
-            if (m_deviceShared.m_deviceParams->getDevice())
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
                 m_deviceShared.m_deviceParams->getDevice()->set_rx_iq_balance(settings.m_iqCorrection, m_deviceShared.m_channel);
         }
 
@@ -734,7 +758,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         {
             reverseAPIKeys.append("gainMode");
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 if (settings.m_gainMode == USRPInputSettings::GAIN_AUTO)
                 {
@@ -754,7 +778,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         {
             reverseAPIKeys.append("gain");
 
-            if ((settings.m_gainMode != USRPInputSettings::GAIN_AUTO) && m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if ((settings.m_gainMode != USRPInputSettings::GAIN_AUTO) && m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_rx_gain(settings.m_gain, m_deviceShared.m_channel);
                 qDebug() << "USRPInput::applySettings: Gain set to " << settings.m_gain << " for channel " << m_deviceShared.m_channel;
@@ -764,8 +788,13 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         if ((m_settings.m_lpfBW != settings.m_lpfBW) || force)
         {
             reverseAPIKeys.append("lpfBW");
-            m_deviceShared.m_deviceParams->getDevice()->set_rx_bandwidth(settings.m_lpfBW, m_deviceShared.m_channel);
-            qDebug("USRPOutput::applySettings: LPF BW: %f for channel %d", settings.m_lpfBW, m_deviceShared.m_channel);
+
+            // Don't set bandwidth before get_rx_stream (See above)
+            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            {
+                m_deviceShared.m_deviceParams->getDevice()->set_rx_bandwidth(settings.m_lpfBW, m_deviceShared.m_channel);
+                qDebug("USRPInput::applySettings: LPF BW: %f for channel %d", settings.m_lpfBW, m_deviceShared.m_channel);
+            }
         }
 
         if ((m_settings.m_log2SoftDecim != settings.m_log2SoftDecim) || force)
@@ -785,7 +814,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
         {
             reverseAPIKeys.append("antennaPath");
 
-            if (m_deviceShared.m_deviceParams->getDevice() && m_channelAcquired)
+            if (m_deviceShared.m_deviceParams->getDevice() && (m_channelAcquired || preGetStream))
             {
                 m_deviceShared.m_deviceParams->getDevice()->set_rx_antenna(settings.m_antennaPath.toStdString(), m_deviceShared.m_channel);
                 qDebug("USRPInput::applySettings: set antenna path to %s on channel %d", qPrintable(settings.m_antennaPath), m_deviceShared.m_channel);
@@ -835,7 +864,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
             for (; itSource != sourceBuddies.end(); ++itSource)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, true);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, true);
                 (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
             }
 
@@ -846,7 +875,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
             for (; itSink != sinkBuddies.end(); ++itSink)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, true);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, true);
                 (*itSink)->getSamplingDeviceInputMessageQueue()->push(report);
             }
         }
@@ -867,7 +896,7 @@ bool USRPInput::applySettings(const USRPInputSettings& settings, bool force)
             for (; itSource != sourceBuddies.end(); ++itSource)
             {
                 DeviceUSRPShared::MsgReportBuddyChange *report = DeviceUSRPShared::MsgReportBuddyChange::create(
-                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, true);
+                        m_settings.m_devSampleRate, m_settings.m_centerFrequency, m_settings.m_loOffset, true);
                 (*itSource)->getSamplingDeviceInputMessageQueue()->push(report);
             }
         }
diff --git a/plugins/samplesource/usrpinput/usrpinput.h b/plugins/samplesource/usrpinput/usrpinput.h
index 82cbefa85..0190f5ab6 100644
--- a/plugins/samplesource/usrpinput/usrpinput.h
+++ b/plugins/samplesource/usrpinput/usrpinput.h
@@ -239,7 +239,7 @@ private:
     void resumeRxBuddies();
     void suspendTxBuddies();
     void resumeTxBuddies();
-    bool applySettings(const USRPInputSettings& settings, bool force = false);
+    bool applySettings(const USRPInputSettings& settings, bool preGetStream, bool force = false);
     void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
     void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const USRPInputSettings& settings, bool force);
     void webapiReverseSendStartStop(bool start);
diff --git a/plugins/samplesource/usrpinput/usrpinputgui.cpp b/plugins/samplesource/usrpinput/usrpinputgui.cpp
index ab107e7a2..e63b48f3b 100644
--- a/plugins/samplesource/usrpinput/usrpinputgui.cpp
+++ b/plugins/samplesource/usrpinput/usrpinputgui.cpp
@@ -61,6 +61,9 @@ USRPInputGUI::USRPInputGUI(DeviceUISet *deviceUISet, QWidget* parent) :
     ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
     ui->sampleRate->setValueRange(8, (uint32_t) minF, (uint32_t) maxF);
 
+    ui->loOffset->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
+    ui->loOffset->setValueRange(false, 5, (int32_t)-maxF/2/1000, (int32_t)maxF/2/1000); // LO offset shouldn't be greater than half the sample rate
+
     m_usrpInput->getLPRange(minF, maxF);
     ui->lpf->setColorMapper(ColorMapper(ColorMapper::GrayYellow));
     ui->lpf->setValueRange(5, (minF/1000)+1, maxF/1000);
@@ -164,6 +167,7 @@ bool USRPInputGUI::handleMessage(const Message& message)
 
         if (report.getRxElseTx()) {
             m_settings.m_centerFrequency = report.getCenterFrequency();
+            m_settings.m_loOffset        = report.getLOOffset();
         }
 
         blockApplySettings(true);
@@ -174,7 +178,7 @@ bool USRPInputGUI::handleMessage(const Message& message)
     }
     else if (DeviceUSRPShared::MsgReportClockSourceChange::match(message))
     {
-qDebug("USRPInputGUI::handleMessage MsgReportClockSourceChange");
+        qDebug("USRPInputGUI::handleMessage MsgReportClockSourceChange");
         DeviceUSRPShared::MsgReportClockSourceChange& report = (DeviceUSRPShared::MsgReportClockSourceChange&) message;
         m_settings.m_clockSource = report.getClockSource();
 
@@ -289,6 +293,8 @@ void USRPInputGUI::updateSampleRate()
     } else {
         ui->sampleRateLabel->setText(tr("%1M").arg(QString::number(sr / 1000000.0f, 'g', 5)));
     }
+    // LO offset shouldn't be greater than half the sample rate
+    ui->loOffset->setValueRange(false, 5, -(int32_t)sr/2/1000, (int32_t)sr/2/1000);
 }
 
 void USRPInputGUI::updateSampleRateAndFrequency()
@@ -350,6 +356,7 @@ void USRPInputGUI::displaySettings()
     updateSampleRate();
 
     ui->lpf->setValue(m_settings.m_lpfBW / 1000);
+    ui->loOffset->setValue(m_settings.m_loOffset / 1000);
 
     ui->gain->setValue(m_settings.m_gain);
     ui->gainText->setText(tr("%1").arg(m_settings.m_gain));
@@ -527,6 +534,12 @@ void USRPInputGUI::on_lpf_changed(quint64 value)
     sendSettings();
 }
 
+void USRPInputGUI::on_loOffset_changed(qint64 value)
+{
+    m_settings.m_loOffset = value * 1000;
+    sendSettings();
+}
+
 void USRPInputGUI::on_gainMode_currentIndexChanged(int index)
 {
     m_settings.m_gainMode = (USRPInputSettings::GainMode) index;
diff --git a/plugins/samplesource/usrpinput/usrpinputgui.h b/plugins/samplesource/usrpinput/usrpinputgui.h
index cfa1e8aa7..9790d7fb2 100644
--- a/plugins/samplesource/usrpinput/usrpinputgui.h
+++ b/plugins/samplesource/usrpinput/usrpinputgui.h
@@ -89,6 +89,7 @@ private slots:
     void on_sampleRate_changed(quint64 value);
     void on_swDecim_currentIndexChanged(int index);
     void on_lpf_changed(quint64 value);
+    void on_loOffset_changed(qint64 value);
     void on_gainMode_currentIndexChanged(int index);
     void on_gain_valueChanged(int value);
     void on_antenna_currentIndexChanged(int index);
diff --git a/plugins/samplesource/usrpinput/usrpinputgui.ui b/plugins/samplesource/usrpinput/usrpinputgui.ui
index 959d08a75..c599ad659 100644
--- a/plugins/samplesource/usrpinput/usrpinputgui.ui
+++ b/plugins/samplesource/usrpinput/usrpinputgui.ui
@@ -626,6 +626,57 @@
        </property>
       </widget>
      </item>
+     <item>
+      <widget class="Line" name="line_2">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="loOffsetLabel">
+       <property name="text">
+        <string>LO</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="ValueDialZ" name="loOffset" 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>Liberation Mono</family>
+         <pointsize>12</pointsize>
+         <weight>50</weight>
+         <bold>false</bold>
+        </font>
+       </property>
+       <property name="cursor">
+        <cursorShape>PointingHandCursor</cursorShape>
+       </property>
+       <property name="toolTip">
+        <string>LO offset (kHz)</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="loOffsetUnits">
+       <property name="text">
+        <string>kHz</string>
+       </property>
+      </widget>
+     </item>
      <item>
       <spacer name="horizontalSpacer">
        <property name="orientation">
@@ -753,6 +804,12 @@
    <extends>QPushButton</extends>
    <header>gui/transverterbutton.h</header>
   </customwidget>
+  <customwidget>
+   <class>ValueDialZ</class>
+   <extends>QWidget</extends>
+   <header>gui/valuedialz.h</header>
+   <container>1</container>
+  </customwidget>
  </customwidgets>
  <resources>
   <include location="../../../sdrgui/resources/res.qrc"/>
diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.cpp b/plugins/samplesource/usrpinput/usrpinputsettings.cpp
index e16f9b94f..0a8adf6b4 100644
--- a/plugins/samplesource/usrpinput/usrpinputsettings.cpp
+++ b/plugins/samplesource/usrpinput/usrpinputsettings.cpp
@@ -28,10 +28,11 @@ void USRPInputSettings::resetToDefaults()
 {
     m_centerFrequency = 435000*1000;
     m_devSampleRate = 3000000;
+    m_loOffset = 0;
     m_dcBlock = false;
     m_iqCorrection = false;
     m_log2SoftDecim = 0;
-    m_lpfBW = 5.5e6f;
+    m_lpfBW = 10e6f;
     m_gain = 50;
     m_antennaPath = "TX/RX";
     m_gainMode = GAIN_AUTO;
@@ -63,6 +64,7 @@ QByteArray USRPInputSettings::serialize() const
     s.writeString(13, m_reverseAPIAddress);
     s.writeU32(14, m_reverseAPIPort);
     s.writeU32(15, m_reverseAPIDeviceIndex);
+    s.writeS32(16, m_loOffset);
     return s.final();
 }
 
@@ -105,6 +107,7 @@ bool USRPInputSettings::deserialize(const QByteArray& data)
 
         d.readU32(15, &uintval, 0);
         m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
+        d.readS32(16, &m_loOffset, 0);
 
         return true;
     }
diff --git a/plugins/samplesource/usrpinput/usrpinputsettings.h b/plugins/samplesource/usrpinput/usrpinputsettings.h
index 3774db555..a485d2e80 100644
--- a/plugins/samplesource/usrpinput/usrpinputsettings.h
+++ b/plugins/samplesource/usrpinput/usrpinputsettings.h
@@ -37,6 +37,7 @@ struct USRPInputSettings
     // global settings to be saved
     uint64_t m_centerFrequency;
     int      m_devSampleRate;
+    int      m_loOffset;
     // channel settings
     bool     m_dcBlock;
     bool     m_iqCorrection;