mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-09-27 07:16:48 -04:00
Add replay support to Lime Input
This commit is contained in:
parent
238a40b9e0
commit
2ac7cfd800
@ -48,6 +48,7 @@ MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgGetDeviceInfo, Message)
|
|||||||
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgReportStreamInfo, Message)
|
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgReportStreamInfo, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgStartStop, Message)
|
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgStartStop, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgCalibrationResult, Message)
|
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgCalibrationResult, Message)
|
||||||
|
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgSaveReplay, Message)
|
||||||
|
|
||||||
LimeSDRInput::LimeSDRInput(DeviceAPI *deviceAPI) :
|
LimeSDRInput::LimeSDRInput(DeviceAPI *deviceAPI) :
|
||||||
m_deviceAPI(deviceAPI),
|
m_deviceAPI(deviceAPI),
|
||||||
@ -422,7 +423,7 @@ bool LimeSDRInput::start()
|
|||||||
|
|
||||||
// start / stop streaming is done in the thread.
|
// start / stop streaming is done in the thread.
|
||||||
|
|
||||||
m_limeSDRInputThread = new LimeSDRInputThread(&m_streamId, &m_sampleFifo);
|
m_limeSDRInputThread = new LimeSDRInputThread(&m_streamId, &m_sampleFifo, &m_replayBuffer);
|
||||||
qDebug("LimeSDRInput::start: thread created");
|
qDebug("LimeSDRInput::start: thread created");
|
||||||
|
|
||||||
applySettings(m_settings, QList<QString>(), true);
|
applySettings(m_settings, QList<QString>(), true);
|
||||||
@ -778,6 +779,12 @@ bool LimeSDRInput::handleMessage(const Message& message)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (MsgSaveReplay::match(message))
|
||||||
|
{
|
||||||
|
MsgSaveReplay& cmd = (MsgSaveReplay&) message;
|
||||||
|
m_replayBuffer.save(cmd.getFilename(), m_settings.m_devSampleRate, getCenterFrequency());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -977,6 +984,9 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, const QLi
|
|||||||
settings.m_devSampleRate,
|
settings.m_devSampleRate,
|
||||||
1<<settings.m_log2HardDecim);
|
1<<settings.m_log2HardDecim);
|
||||||
}
|
}
|
||||||
|
if (settings.m_devSampleRate != m_settings.m_devSampleRate) {
|
||||||
|
m_replayBuffer.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1168,6 +1178,18 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, const QLi
|
|||||||
m_settings.applySettings(settingsKeys, settings);
|
m_settings.applySettings(settingsKeys, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settingsKeys.contains("replayLength") || settingsKeys.contains("devSampleRate") || force) {
|
||||||
|
m_replayBuffer.setSize(m_settings.m_replayLength, m_settings.m_devSampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsKeys.contains("replayOffset") || settingsKeys.contains("devSampleRate") || force) {
|
||||||
|
m_replayBuffer.setReadOffset(((unsigned)(m_settings.m_replayOffset * m_settings.m_devSampleRate)) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsKeys.contains("replayLoop") || force) {
|
||||||
|
m_replayBuffer.setLoop(m_settings.m_replayLoop);
|
||||||
|
}
|
||||||
|
|
||||||
double clockGenFreqAfter;
|
double clockGenFreqAfter;
|
||||||
|
|
||||||
if (LMS_GetClockFreq(m_deviceShared.m_deviceParams->getDevice(), LMS_CLOCK_CGEN, &clockGenFreqAfter) != 0)
|
if (LMS_GetClockFreq(m_deviceShared.m_deviceParams->getDevice(), LMS_CLOCK_CGEN, &clockGenFreqAfter) != 0)
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
||||||
#include "dsp/devicesamplesource.h"
|
#include "dsp/devicesamplesource.h"
|
||||||
|
#include "dsp/replaybuffer.h"
|
||||||
#include "limesdr/devicelimesdrshared.h"
|
#include "limesdr/devicelimesdrshared.h"
|
||||||
#include "limesdrinputsettings.h"
|
#include "limesdrinputsettings.h"
|
||||||
|
|
||||||
@ -209,7 +210,26 @@ public:
|
|||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
LimeSDRInput(DeviceAPI *deviceAPI);
|
class MsgSaveReplay : public Message {
|
||||||
|
MESSAGE_CLASS_DECLARATION
|
||||||
|
|
||||||
|
public:
|
||||||
|
QString getFilename() const { return m_filename; }
|
||||||
|
|
||||||
|
static MsgSaveReplay* create(const QString& filename) {
|
||||||
|
return new MsgSaveReplay(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_filename;
|
||||||
|
|
||||||
|
MsgSaveReplay(const QString& filename) :
|
||||||
|
Message(),
|
||||||
|
m_filename(filename)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
LimeSDRInput(DeviceAPI *deviceAPI);
|
||||||
virtual ~LimeSDRInput();
|
virtual ~LimeSDRInput();
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
|
|
||||||
@ -280,6 +300,7 @@ private:
|
|||||||
lms_stream_t m_streamId;
|
lms_stream_t m_streamId;
|
||||||
QNetworkAccessManager *m_networkManager;
|
QNetworkAccessManager *m_networkManager;
|
||||||
QNetworkRequest m_networkRequest;
|
QNetworkRequest m_networkRequest;
|
||||||
|
ReplayBuffer<qint16> m_replayBuffer;
|
||||||
|
|
||||||
bool openDevice();
|
bool openDevice();
|
||||||
void closeDevice();
|
void closeDevice();
|
||||||
|
@ -469,6 +469,10 @@ void LimeSDRInputGUI::displaySettings()
|
|||||||
setNCODisplay();
|
setNCODisplay();
|
||||||
|
|
||||||
ui->ncoEnable->setChecked(m_settings.m_ncoEnable);
|
ui->ncoEnable->setChecked(m_settings.m_ncoEnable);
|
||||||
|
displayReplayLength();
|
||||||
|
displayReplayOffset();
|
||||||
|
displayReplayStep();
|
||||||
|
ui->replayLoop->setChecked(m_settings.m_replayLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimeSDRInputGUI::setNCODisplay()
|
void LimeSDRInputGUI::setNCODisplay()
|
||||||
@ -805,6 +809,9 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p)
|
|||||||
if (m_contextMenuType == ContextMenuDeviceSettings)
|
if (m_contextMenuType == ContextMenuDeviceSettings)
|
||||||
{
|
{
|
||||||
BasicDeviceSettingsDialog dialog(this);
|
BasicDeviceSettingsDialog dialog(this);
|
||||||
|
dialog.setReplayBytesPerSecond(m_settings.m_devSampleRate * 2 * sizeof(qint16));
|
||||||
|
dialog.setReplayLength(m_settings.m_replayLength);
|
||||||
|
dialog.setReplayStep(m_settings.m_replayStep);
|
||||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||||
@ -818,10 +825,17 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p)
|
|||||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||||
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
|
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
|
||||||
|
m_settings.m_replayLength = dialog.getReplayLength();
|
||||||
|
m_settings.m_replayStep = dialog.getReplayStep();
|
||||||
|
displayReplayLength();
|
||||||
|
displayReplayOffset();
|
||||||
|
displayReplayStep();
|
||||||
m_settingsKeys.append("useReverseAPI");
|
m_settingsKeys.append("useReverseAPI");
|
||||||
m_settingsKeys.append("reverseAPIAddress");
|
m_settingsKeys.append("reverseAPIAddress");
|
||||||
m_settingsKeys.append("reverseAPIPort");
|
m_settingsKeys.append("reverseAPIPort");
|
||||||
m_settingsKeys.append("reverseAPIDeviceIndex");
|
m_settingsKeys.append("reverseAPIDeviceIndex");
|
||||||
|
m_settingsKeys.append("replayLength");
|
||||||
|
m_settingsKeys.append("replayStep");
|
||||||
|
|
||||||
sendSettings();
|
sendSettings();
|
||||||
}
|
}
|
||||||
@ -829,6 +843,96 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p)
|
|||||||
resetContextMenuType();
|
resetContextMenuType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::displayReplayLength()
|
||||||
|
{
|
||||||
|
bool replayEnabled = m_settings.m_replayLength > 0.0f;
|
||||||
|
if (!replayEnabled) {
|
||||||
|
ui->replayOffset->setMaximum(0);
|
||||||
|
} else {
|
||||||
|
ui->replayOffset->setMaximum(m_settings.m_replayLength * 10 - 1);
|
||||||
|
}
|
||||||
|
ui->replayLabel->setEnabled(replayEnabled);
|
||||||
|
ui->replayOffset->setEnabled(replayEnabled);
|
||||||
|
ui->replayOffsetText->setEnabled(replayEnabled);
|
||||||
|
ui->replaySave->setEnabled(replayEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::displayReplayOffset()
|
||||||
|
{
|
||||||
|
bool replayEnabled = m_settings.m_replayLength > 0.0f;
|
||||||
|
ui->replayOffset->setValue(m_settings.m_replayOffset * 10);
|
||||||
|
ui->replayOffsetText->setText(QString("%1s").arg(m_settings.m_replayOffset, 0, 'f', 1));
|
||||||
|
ui->replayNow->setEnabled(replayEnabled && (m_settings.m_replayOffset > 0.0f));
|
||||||
|
ui->replayPlus->setEnabled(replayEnabled && (std::round(m_settings.m_replayOffset * 10) < ui->replayOffset->maximum()));
|
||||||
|
ui->replayMinus->setEnabled(replayEnabled && (m_settings.m_replayOffset > 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::displayReplayStep()
|
||||||
|
{
|
||||||
|
QString step;
|
||||||
|
float intpart;
|
||||||
|
float frac = modf(m_settings.m_replayStep, &intpart);
|
||||||
|
if (frac == 0.0f) {
|
||||||
|
step = QString::number((int)intpart);
|
||||||
|
} else {
|
||||||
|
step = QString::number(m_settings.m_replayStep, 'f', 1);
|
||||||
|
}
|
||||||
|
ui->replayPlus->setText(QString("+%1s").arg(step));
|
||||||
|
ui->replayPlus->setToolTip(QString("Add %1 seconds to time delay").arg(step));
|
||||||
|
ui->replayMinus->setText(QString("-%1s").arg(step));
|
||||||
|
ui->replayMinus->setToolTip(QString("Remove %1 seconds from time delay").arg(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replayOffset_valueChanged(int value)
|
||||||
|
{
|
||||||
|
m_settings.m_replayOffset = value / 10.0f;
|
||||||
|
displayReplayOffset();
|
||||||
|
m_settingsKeys.append("replayOffset");
|
||||||
|
sendSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replayNow_clicked()
|
||||||
|
{
|
||||||
|
ui->replayOffset->setValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replayPlus_clicked()
|
||||||
|
{
|
||||||
|
ui->replayOffset->setValue(ui->replayOffset->value() + m_settings.m_replayStep * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replayMinus_clicked()
|
||||||
|
{
|
||||||
|
ui->replayOffset->setValue(ui->replayOffset->value() - m_settings.m_replayStep * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replaySave_clicked()
|
||||||
|
{
|
||||||
|
QFileDialog fileDialog(nullptr, "Select file to save IQ data to", "", "*.wav");
|
||||||
|
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||||
|
if (fileDialog.exec())
|
||||||
|
{
|
||||||
|
QStringList fileNames = fileDialog.selectedFiles();
|
||||||
|
if (fileNames.size() > 0)
|
||||||
|
{
|
||||||
|
LimeSDRInput::MsgSaveReplay *message = LimeSDRInput::MsgSaveReplay::create(fileNames[0]);
|
||||||
|
m_limeSDRInput->getInputMessageQueue()->push(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::on_replayLoop_toggled(bool checked)
|
||||||
|
{
|
||||||
|
m_settings.m_replayLoop = checked;
|
||||||
|
m_settingsKeys.append("replayLoop");
|
||||||
|
sendSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LimeSDRInputGUI::setReplayTime(float time)
|
||||||
|
{
|
||||||
|
ui->replayOffset->setValue(std::ceil(time * 10.0f));
|
||||||
|
}
|
||||||
|
|
||||||
void LimeSDRInputGUI::makeUIConnections()
|
void LimeSDRInputGUI::makeUIConnections()
|
||||||
{
|
{
|
||||||
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_startStop_toggled);
|
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_startStop_toggled);
|
||||||
@ -852,4 +956,10 @@ void LimeSDRInputGUI::makeUIConnections()
|
|||||||
QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &LimeSDRInputGUI::on_extClock_clicked);
|
QObject::connect(ui->extClock, &ExternalClockButton::clicked, this, &LimeSDRInputGUI::on_extClock_clicked);
|
||||||
QObject::connect(ui->transverter, &TransverterButton::clicked, this, &LimeSDRInputGUI::on_transverter_clicked);
|
QObject::connect(ui->transverter, &TransverterButton::clicked, this, &LimeSDRInputGUI::on_transverter_clicked);
|
||||||
QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &LimeSDRInputGUI::on_sampleRateMode_toggled);
|
QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &LimeSDRInputGUI::on_sampleRateMode_toggled);
|
||||||
|
QObject::connect(ui->replayOffset, &QSlider::valueChanged, this, &LimeSDRInputGUI::on_replayOffset_valueChanged);
|
||||||
|
QObject::connect(ui->replayNow, &QToolButton::clicked, this, &LimeSDRInputGUI::on_replayNow_clicked);
|
||||||
|
QObject::connect(ui->replayPlus, &QToolButton::clicked, this, &LimeSDRInputGUI::on_replayPlus_clicked);
|
||||||
|
QObject::connect(ui->replayMinus, &QToolButton::clicked, this, &LimeSDRInputGUI::on_replayMinus_clicked);
|
||||||
|
QObject::connect(ui->replaySave, &QToolButton::clicked, this, &LimeSDRInputGUI::on_replaySave_clicked);
|
||||||
|
QObject::connect(ui->replayLoop, &ButtonSwitch::toggled, this, &LimeSDRInputGUI::on_replayLoop_toggled);
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ public:
|
|||||||
QByteArray serialize() const;
|
QByteArray serialize() const;
|
||||||
bool deserialize(const QByteArray& data);
|
bool deserialize(const QByteArray& data);
|
||||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||||
|
void setReplayTime(float time) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::LimeSDRInputGUI* ui;
|
Ui::LimeSDRInputGUI* ui;
|
||||||
@ -66,6 +67,9 @@ private:
|
|||||||
|
|
||||||
void displaySettings();
|
void displaySettings();
|
||||||
void displaySampleRate();
|
void displaySampleRate();
|
||||||
|
void displayReplayLength();
|
||||||
|
void displayReplayOffset();
|
||||||
|
void displayReplayStep();
|
||||||
void setNCODisplay();
|
void setNCODisplay();
|
||||||
void setCenterFrequencyDisplay();
|
void setCenterFrequencyDisplay();
|
||||||
void setCenterFrequencySetting(uint64_t kHzValue);
|
void setCenterFrequencySetting(uint64_t kHzValue);
|
||||||
@ -101,6 +105,12 @@ private slots:
|
|||||||
void on_extClock_clicked();
|
void on_extClock_clicked();
|
||||||
void on_transverter_clicked();
|
void on_transverter_clicked();
|
||||||
void on_sampleRateMode_toggled(bool checked);
|
void on_sampleRateMode_toggled(bool checked);
|
||||||
|
void on_replayOffset_valueChanged(int value);
|
||||||
|
void on_replayNow_clicked();
|
||||||
|
void on_replayPlus_clicked();
|
||||||
|
void on_replayMinus_clicked();
|
||||||
|
void on_replaySave_clicked();
|
||||||
|
void on_replayLoop_toggled(bool checked);
|
||||||
void openDeviceSettingsDialog(const QPoint& p);
|
void openDeviceSettingsDialog(const QPoint& p);
|
||||||
|
|
||||||
void updateHardware();
|
void updateHardware();
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>360</width>
|
<width>360</width>
|
||||||
<height>230</height>
|
<height>255</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -19,13 +19,13 @@
|
|||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>360</width>
|
<width>360</width>
|
||||||
<height>230</height>
|
<height>255</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>380</width>
|
<width>386</width>
|
||||||
<height>266</height>
|
<height>280</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="font">
|
<property name="font">
|
||||||
@ -1169,6 +1169,104 @@ QToolTip{background-color: white; color: black;}</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="replayLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="replayLabel">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>65</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Time Delay</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="replayOffset">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Replay time delay in seconds</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>500</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="replayOffsetText">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Replay time delay in seconds</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0s</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="replayNow">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Set time delay to 0 seconds</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Now</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="replayPlus">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Add displayed number of seconds to time delay</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>+5s</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="replayMinus">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Remove displayed number of seconds from time delay</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>-5s</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="ButtonSwitch" name="replayLoop">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Repeatedly replay data in replay buffer</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/playloop.png</normaloff>:/playloop.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="replaySave">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Save replay buffer to a file</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||||
|
<normaloff>:/save.png</normaloff>:/save.png</iconset>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
@ -51,6 +51,10 @@ void LimeSDRInputSettings::resetToDefaults()
|
|||||||
m_iqOrder = true;
|
m_iqOrder = true;
|
||||||
m_gpioDir = 0;
|
m_gpioDir = 0;
|
||||||
m_gpioPins = 0;
|
m_gpioPins = 0;
|
||||||
|
m_replayOffset = 0.0f;
|
||||||
|
m_replayLength = 20.0f;
|
||||||
|
m_replayStep = 5.0f;
|
||||||
|
m_replayLoop = false;
|
||||||
m_useReverseAPI = false;
|
m_useReverseAPI = false;
|
||||||
m_reverseAPIAddress = "127.0.0.1";
|
m_reverseAPIAddress = "127.0.0.1";
|
||||||
m_reverseAPIPort = 8888;
|
m_reverseAPIPort = 8888;
|
||||||
@ -88,6 +92,10 @@ QByteArray LimeSDRInputSettings::serialize() const
|
|||||||
s.writeU32(26, m_reverseAPIPort);
|
s.writeU32(26, m_reverseAPIPort);
|
||||||
s.writeU32(27, m_reverseAPIDeviceIndex);
|
s.writeU32(27, m_reverseAPIDeviceIndex);
|
||||||
s.writeBool(28, m_iqOrder);
|
s.writeBool(28, m_iqOrder);
|
||||||
|
s.writeFloat(29, m_replayOffset);
|
||||||
|
s.writeFloat(30, m_replayLength);
|
||||||
|
s.writeFloat(31, m_replayStep);
|
||||||
|
s.writeBool(32, m_replayLoop);
|
||||||
|
|
||||||
return s.final();
|
return s.final();
|
||||||
}
|
}
|
||||||
@ -146,6 +154,10 @@ bool LimeSDRInputSettings::deserialize(const QByteArray& data)
|
|||||||
d.readU32(27, &uintval, 0);
|
d.readU32(27, &uintval, 0);
|
||||||
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
|
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
|
||||||
d.readBool(28, &m_iqOrder, true);
|
d.readBool(28, &m_iqOrder, true);
|
||||||
|
d.readFloat(29, &m_replayOffset, 0.0f);
|
||||||
|
d.readFloat(30, &m_replayLength, 20.0f);
|
||||||
|
d.readFloat(31, &m_replayStep, 5.0f);
|
||||||
|
d.readBool(32, &m_replayLoop, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -231,6 +243,18 @@ void LimeSDRInputSettings::applySettings(const QStringList& settingsKeys, const
|
|||||||
if (settingsKeys.contains("gpioPins")) {
|
if (settingsKeys.contains("gpioPins")) {
|
||||||
m_gpioPins = settings.m_gpioPins;
|
m_gpioPins = settings.m_gpioPins;
|
||||||
}
|
}
|
||||||
|
if (settingsKeys.contains("replayOffset")) {
|
||||||
|
m_replayOffset = settings.m_replayOffset;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayLength")) {
|
||||||
|
m_replayLength = settings.m_replayLength;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayStep")) {
|
||||||
|
m_replayStep = settings.m_replayStep;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayLoop")) {
|
||||||
|
m_replayLoop = settings.m_replayLoop;
|
||||||
|
}
|
||||||
if (settingsKeys.contains("useReverseAPI")) {
|
if (settingsKeys.contains("useReverseAPI")) {
|
||||||
m_useReverseAPI = settings.m_useReverseAPI;
|
m_useReverseAPI = settings.m_useReverseAPI;
|
||||||
}
|
}
|
||||||
@ -321,6 +345,18 @@ QString LimeSDRInputSettings::getDebugString(const QStringList& settingsKeys, bo
|
|||||||
if (settingsKeys.contains("gpioPins") || force) {
|
if (settingsKeys.contains("gpioPins") || force) {
|
||||||
ostr << " m_gpioPins: " << m_gpioPins;
|
ostr << " m_gpioPins: " << m_gpioPins;
|
||||||
}
|
}
|
||||||
|
if (settingsKeys.contains("replayOffset") || force) {
|
||||||
|
ostr << " m_replayOffset: " << m_replayOffset;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayLength") || force) {
|
||||||
|
ostr << " m_replayLength: " << m_replayLength;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayStep") || force) {
|
||||||
|
ostr << " m_replayStep: " << m_replayStep;
|
||||||
|
}
|
||||||
|
if (settingsKeys.contains("replayLoop") || force) {
|
||||||
|
ostr << " m_replayLoop: " << m_replayLoop;
|
||||||
|
}
|
||||||
if (settingsKeys.contains("useReverseAPI") || force) {
|
if (settingsKeys.contains("useReverseAPI") || force) {
|
||||||
ostr << " m_useReverseAPI: " << m_useReverseAPI;
|
ostr << " m_useReverseAPI: " << m_useReverseAPI;
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,10 @@ struct LimeSDRInputSettings
|
|||||||
bool m_iqOrder;
|
bool m_iqOrder;
|
||||||
uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output
|
uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output
|
||||||
uint8_t m_gpioPins; //!< GPIO pins to write; LSB first
|
uint8_t m_gpioPins; //!< GPIO pins to write; LSB first
|
||||||
|
float m_replayOffset; //!< Replay offset in seconds
|
||||||
|
float m_replayLength; //!< Replay buffer size in seconds
|
||||||
|
float m_replayStep; //!< Replay forward/back step size in seconds
|
||||||
|
bool m_replayLoop; //!< Replay buffer repeatedly without recording new data
|
||||||
bool m_useReverseAPI;
|
bool m_useReverseAPI;
|
||||||
QString m_reverseAPIAddress;
|
QString m_reverseAPIAddress;
|
||||||
uint16_t m_reverseAPIPort;
|
uint16_t m_reverseAPIPort;
|
||||||
|
@ -18,15 +18,18 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "dsp/replaybuffer.h"
|
||||||
#include "limesdrinputsettings.h"
|
#include "limesdrinputsettings.h"
|
||||||
#include "limesdrinputthread.h"
|
#include "limesdrinputthread.h"
|
||||||
|
|
||||||
LimeSDRInputThread::LimeSDRInputThread(lms_stream_t* stream, SampleSinkFifo* sampleFifo, QObject* parent) :
|
LimeSDRInputThread::LimeSDRInputThread(lms_stream_t* stream, SampleSinkFifo* sampleFifo,
|
||||||
|
ReplayBuffer<qint16> *replayBuffer, QObject* parent) :
|
||||||
QThread(parent),
|
QThread(parent),
|
||||||
m_running(false),
|
m_running(false),
|
||||||
m_stream(stream),
|
m_stream(stream),
|
||||||
m_convertBuffer(DeviceLimeSDR::blockSize),
|
m_convertBuffer(DeviceLimeSDR::blockSize),
|
||||||
m_sampleFifo(sampleFifo),
|
m_sampleFifo(sampleFifo),
|
||||||
|
m_replayBuffer(replayBuffer),
|
||||||
m_log2Decim(0),
|
m_log2Decim(0),
|
||||||
m_iqOrder(true)
|
m_iqOrder(true)
|
||||||
{
|
{
|
||||||
@ -106,70 +109,116 @@ void LimeSDRInputThread::run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decimate according to specified log2 (ex: log2=4 => decim=16)
|
// Decimate according to specified log2 (ex: log2=4 => decim=16)
|
||||||
void LimeSDRInputThread::callbackIQ(const qint16* buf, qint32 len)
|
void LimeSDRInputThread::callbackIQ(const qint16* inBuf, qint32 len)
|
||||||
{
|
{
|
||||||
SampleVector::iterator it = m_convertBuffer.begin();
|
SampleVector::iterator it = m_convertBuffer.begin();
|
||||||
|
|
||||||
switch (m_log2Decim)
|
// Save data to replay buffer
|
||||||
{
|
m_replayBuffer->lock();
|
||||||
case 0:
|
bool replayEnabled = m_replayBuffer->getSize() > 0;
|
||||||
m_decimatorsIQ.decimate1(&it, buf, len);
|
if (replayEnabled) {
|
||||||
break;
|
m_replayBuffer->write(inBuf, len);
|
||||||
case 1:
|
}
|
||||||
m_decimatorsIQ.decimate2_cen(&it, buf, len);
|
|
||||||
break;
|
const qint16* buf = inBuf;
|
||||||
case 2:
|
qint32 remaining = len;
|
||||||
m_decimatorsIQ.decimate4_cen(&it, buf, len);
|
|
||||||
break;
|
while (remaining > 0)
|
||||||
case 3:
|
{
|
||||||
m_decimatorsIQ.decimate8_cen(&it, buf, len);
|
// Choose between live data or replayed data
|
||||||
break;
|
if (replayEnabled && m_replayBuffer->useReplay()) {
|
||||||
case 4:
|
len = m_replayBuffer->read(remaining, buf);
|
||||||
m_decimatorsIQ.decimate16_cen(&it, buf, len);
|
} else {
|
||||||
break;
|
len = remaining;
|
||||||
case 5:
|
}
|
||||||
m_decimatorsIQ.decimate32_cen(&it, buf, len);
|
remaining -= len;
|
||||||
break;
|
|
||||||
case 6:
|
switch (m_log2Decim)
|
||||||
m_decimatorsIQ.decimate64_cen(&it, buf, len);
|
{
|
||||||
break;
|
case 0:
|
||||||
default:
|
m_decimatorsIQ.decimate1(&it, buf, len);
|
||||||
break;
|
break;
|
||||||
|
case 1:
|
||||||
|
m_decimatorsIQ.decimate2_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
m_decimatorsIQ.decimate4_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
m_decimatorsIQ.decimate8_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
m_decimatorsIQ.decimate16_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
m_decimatorsIQ.decimate32_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
m_decimatorsIQ.decimate64_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_replayBuffer->unlock();
|
||||||
|
|
||||||
m_sampleFifo->write(m_convertBuffer.begin(), it);
|
m_sampleFifo->write(m_convertBuffer.begin(), it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LimeSDRInputThread::callbackQI(const qint16* buf, qint32 len)
|
void LimeSDRInputThread::callbackQI(const qint16* inBuf, qint32 len)
|
||||||
{
|
{
|
||||||
SampleVector::iterator it = m_convertBuffer.begin();
|
SampleVector::iterator it = m_convertBuffer.begin();
|
||||||
|
|
||||||
switch (m_log2Decim)
|
// Save data to replay buffer
|
||||||
{
|
m_replayBuffer->lock();
|
||||||
case 0:
|
bool replayEnabled = m_replayBuffer->getSize() > 0;
|
||||||
m_decimatorsQI.decimate1(&it, buf, len);
|
if (replayEnabled) {
|
||||||
break;
|
m_replayBuffer->write(inBuf, len);
|
||||||
case 1:
|
}
|
||||||
m_decimatorsQI.decimate2_cen(&it, buf, len);
|
|
||||||
break;
|
const qint16* buf = inBuf;
|
||||||
case 2:
|
qint32 remaining = len;
|
||||||
m_decimatorsQI.decimate4_cen(&it, buf, len);
|
|
||||||
break;
|
while (remaining > 0)
|
||||||
case 3:
|
{
|
||||||
m_decimatorsQI.decimate8_cen(&it, buf, len);
|
// Choose between live data or replayed data
|
||||||
break;
|
if (replayEnabled && m_replayBuffer->useReplay()) {
|
||||||
case 4:
|
len = m_replayBuffer->read(remaining, buf);
|
||||||
m_decimatorsQI.decimate16_cen(&it, buf, len);
|
} else {
|
||||||
break;
|
len = remaining;
|
||||||
case 5:
|
}
|
||||||
m_decimatorsQI.decimate32_cen(&it, buf, len);
|
remaining -= len;
|
||||||
break;
|
|
||||||
case 6:
|
switch (m_log2Decim)
|
||||||
m_decimatorsQI.decimate64_cen(&it, buf, len);
|
{
|
||||||
break;
|
case 0:
|
||||||
default:
|
m_decimatorsQI.decimate1(&it, buf, len);
|
||||||
break;
|
break;
|
||||||
}
|
case 1:
|
||||||
|
m_decimatorsQI.decimate2_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
m_decimatorsQI.decimate4_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
m_decimatorsQI.decimate8_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
m_decimatorsQI.decimate16_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
m_decimatorsQI.decimate32_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
m_decimatorsQI.decimate64_cen(&it, buf, len);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_replayBuffer->unlock();
|
||||||
|
|
||||||
m_sampleFifo->write(m_convertBuffer.begin(), it);
|
m_sampleFifo->write(m_convertBuffer.begin(), it);
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,8 @@ class LimeSDRInputThread : public QThread, public DeviceLimeSDRShared::ThreadInt
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LimeSDRInputThread(lms_stream_t* stream, SampleSinkFifo* sampleFifo, QObject* parent = 0);
|
LimeSDRInputThread(lms_stream_t* stream, SampleSinkFifo* sampleFifo,
|
||||||
|
ReplayBuffer<qint16> *replayBuffer, QObject* parent = 0);
|
||||||
~LimeSDRInputThread();
|
~LimeSDRInputThread();
|
||||||
|
|
||||||
virtual void startWork();
|
virtual void startWork();
|
||||||
@ -56,6 +57,7 @@ private:
|
|||||||
qint16 m_buf[2*DeviceLimeSDR::blockSize]; //must hold I+Q values of each sample hence 2xcomplex size
|
qint16 m_buf[2*DeviceLimeSDR::blockSize]; //must hold I+Q values of each sample hence 2xcomplex size
|
||||||
SampleVector m_convertBuffer;
|
SampleVector m_convertBuffer;
|
||||||
SampleSinkFifo* m_sampleFifo;
|
SampleSinkFifo* m_sampleFifo;
|
||||||
|
ReplayBuffer<qint16> *m_replayBuffer;
|
||||||
|
|
||||||
unsigned int m_log2Decim; // soft decimation
|
unsigned int m_log2Decim; // soft decimation
|
||||||
bool m_iqOrder;
|
bool m_iqOrder;
|
||||||
|
Loading…
Reference in New Issue
Block a user