mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-08-26 13:26:24 -04:00
Add instant replay
This commit is contained in:
@@ -334,6 +334,10 @@ void RTLSDRGui::displaySettings()
|
||||
ui->lowSampleRate->setChecked(m_settings.m_lowSampleRate);
|
||||
ui->offsetTuning->setChecked(m_settings.m_offsetTuning);
|
||||
ui->biasT->setChecked(m_settings.m_biasTee);
|
||||
displayReplayLength();
|
||||
displayReplayOffset();
|
||||
displayReplayStep();
|
||||
ui->replayLoop->setChecked(m_settings.m_replayLoop);
|
||||
}
|
||||
|
||||
void RTLSDRGui::sendSettings()
|
||||
@@ -568,6 +572,9 @@ void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
if (m_contextMenuType == ContextMenuDeviceSettings)
|
||||
{
|
||||
BasicDeviceSettingsDialog dialog(this);
|
||||
dialog.setReplayBytesPerSecond(m_settings.m_devSampleRate * 2);
|
||||
dialog.setReplayLength(m_settings.m_replayLength);
|
||||
dialog.setReplayStep(m_settings.m_replayStep);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
@@ -581,10 +588,17 @@ void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
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("reverseAPIAddress");
|
||||
m_settingsKeys.append("reverseAPIPort");
|
||||
m_settingsKeys.append("reverseAPIDeviceIndex");
|
||||
m_settingsKeys.append("replayLength");
|
||||
m_settingsKeys.append("replayStep");
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
@@ -592,6 +606,91 @@ void RTLSDRGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
resetContextMenuType();
|
||||
}
|
||||
|
||||
void RTLSDRGui::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 RTLSDRGui::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 RTLSDRGui::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 RTLSDRGui::on_replayOffset_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_replayOffset = value / 10.0f;
|
||||
displayReplayOffset();
|
||||
m_settingsKeys.append("replayOffset");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_replayNow_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(0);
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_replayPlus_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(ui->replayOffset->value() + m_settings.m_replayStep * 10);
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_replayMinus_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(ui->replayOffset->value() - m_settings.m_replayStep * 10);
|
||||
}
|
||||
|
||||
void RTLSDRGui::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)
|
||||
{
|
||||
RTLSDRInput::MsgSaveReplay *message = RTLSDRInput::MsgSaveReplay::create(fileNames[0]);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTLSDRGui::on_replayLoop_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_replayLoop = checked;
|
||||
m_settingsKeys.append("replayLoop");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RTLSDRGui::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &RTLSDRGui::on_centerFrequency_changed);
|
||||
@@ -611,4 +710,10 @@ void RTLSDRGui::makeUIConnections()
|
||||
QObject::connect(ui->transverter, &TransverterButton::clicked, this, &RTLSDRGui::on_transverter_clicked);
|
||||
QObject::connect(ui->sampleRateMode, &QToolButton::toggled, this, &RTLSDRGui::on_sampleRateMode_toggled);
|
||||
QObject::connect(ui->biasT, &QCheckBox::stateChanged, this, &RTLSDRGui::on_biasT_stateChanged);
|
||||
QObject::connect(ui->replayOffset, &QSlider::valueChanged, this, &RTLSDRGui::on_replayOffset_valueChanged);
|
||||
QObject::connect(ui->replayNow, &QToolButton::clicked, this, &RTLSDRGui::on_replayNow_clicked);
|
||||
QObject::connect(ui->replayPlus, &QToolButton::clicked, this, &RTLSDRGui::on_replayPlus_clicked);
|
||||
QObject::connect(ui->replayMinus, &QToolButton::clicked, this, &RTLSDRGui::on_replayMinus_clicked);
|
||||
QObject::connect(ui->replaySave, &QToolButton::clicked, this, &RTLSDRGui::on_replaySave_clicked);
|
||||
QObject::connect(ui->replayLoop, &ButtonSwitch::toggled, this, &RTLSDRGui::on_replayLoop_toggled);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,9 @@ private:
|
||||
void displaySampleRate();
|
||||
void displayFcTooltip();
|
||||
void displaySettings();
|
||||
void displayReplayLength();
|
||||
void displayReplayOffset();
|
||||
void displayReplayStep();
|
||||
void sendSettings();
|
||||
void updateSampleRateAndFrequency();
|
||||
void updateFrequencyLimits();
|
||||
@@ -96,6 +99,12 @@ private slots:
|
||||
void on_transverter_clicked();
|
||||
void on_sampleRateMode_toggled(bool checked);
|
||||
void on_biasT_stateChanged(int state);
|
||||
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 updateHardware();
|
||||
void updateStatus();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>217</height>
|
||||
<height>240</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -19,13 +19,13 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>360</width>
|
||||
<height>217</height>
|
||||
<height>240</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>380</width>
|
||||
<height>229</height>
|
||||
<height>240</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
@@ -598,29 +598,6 @@
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="agc">
|
||||
<property name="toolTip">
|
||||
<string>Toggles RTLSDR AGC</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AGC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="gainLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Gain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSlider" name="gain">
|
||||
<property name="enabled">
|
||||
@@ -640,6 +617,19 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="gainLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Gain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="gainText">
|
||||
<property name="minimumSize">
|
||||
@@ -656,6 +646,121 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QCheckBox" name="agc">
|
||||
<property name="toolTip">
|
||||
<string>Toggles RTLSDR AGC</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>AGC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(RTLSDRInput::MsgConfigureRTLSDR, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RTLSDRInput::MsgStartStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RTLSDRInput::MsgSaveReplay, Message)
|
||||
|
||||
const quint64 RTLSDRInput::frequencyLowRangeMin = 0UL;
|
||||
const quint64 RTLSDRInput::frequencyLowRangeMax = 275000UL;
|
||||
@@ -238,7 +239,7 @@ bool RTLSDRInput::start()
|
||||
|
||||
if (m_running) stop();
|
||||
|
||||
m_rtlSDRThread = new RTLSDRThread(m_dev, &m_sampleFifo);
|
||||
m_rtlSDRThread = new RTLSDRThread(m_dev, &m_sampleFifo, &m_replayBuffer);
|
||||
m_rtlSDRThread->setSamplerate(m_settings.m_devSampleRate);
|
||||
m_rtlSDRThread->setLog2Decimation(m_settings.m_log2Decim);
|
||||
m_rtlSDRThread->setFcPos((int) m_settings.m_fcPos);
|
||||
@@ -374,6 +375,12 @@ bool RTLSDRInput::handleMessage(const Message& message)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgSaveReplay::match(message))
|
||||
{
|
||||
MsgSaveReplay& cmd = (MsgSaveReplay&) message;
|
||||
m_replayBuffer.save(cmd.getFilename(), m_settings.m_devSampleRate, getCenterFrequency());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@@ -432,6 +439,9 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, const QList<QStr
|
||||
|
||||
qDebug("RTLSDRInput::applySettings: sample rate set to %d", settings.m_devSampleRate);
|
||||
}
|
||||
if (settings.m_devSampleRate != m_settings.m_devSampleRate) {
|
||||
m_replayBuffer.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,6 +584,18 @@ bool RTLSDRInput::applySettings(const RTLSDRSettings& settings, const QList<QStr
|
||||
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);
|
||||
}
|
||||
|
||||
if (forwardChange)
|
||||
{
|
||||
int sampleRate = m_settings.m_devSampleRate/(1<<m_settings.m_log2Decim);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#include "dsp/devicesamplesource.h"
|
||||
#include "dsp/replaybuffer.h"
|
||||
#include "rtlsdrsettings.h"
|
||||
#include <rtl-sdr.h>
|
||||
|
||||
@@ -83,6 +84,25 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
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)
|
||||
{ }
|
||||
};
|
||||
|
||||
RTLSDRInput(DeviceAPI *deviceAPI);
|
||||
virtual ~RTLSDRInput();
|
||||
virtual void destroy();
|
||||
@@ -161,6 +181,7 @@ private:
|
||||
bool m_running;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
ReplayBuffer<quint8> m_replayBuffer;
|
||||
|
||||
bool openDevice();
|
||||
void closeDevice();
|
||||
|
||||
@@ -43,6 +43,10 @@ void RTLSDRSettings::resetToDefaults()
|
||||
m_rfBandwidth = 2500 * 1000; // Hz
|
||||
m_offsetTuning = false;
|
||||
m_biasTee = false;
|
||||
m_replayOffset = 0.0f;
|
||||
m_replayLength = 20.0f;
|
||||
m_replayStep = 5.0f;
|
||||
m_replayLoop = false;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
@@ -73,6 +77,10 @@ QByteArray RTLSDRSettings::serialize() const
|
||||
s.writeU32(19, m_reverseAPIDeviceIndex);
|
||||
s.writeBool(20, m_iqOrder);
|
||||
s.writeBool(21, m_biasTee);
|
||||
s.writeFloat(22, m_replayOffset);
|
||||
s.writeFloat(23, m_replayLength);
|
||||
s.writeFloat(24, m_replayStep);
|
||||
s.writeBool(25, m_replayLoop);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@@ -121,6 +129,10 @@ bool RTLSDRSettings::deserialize(const QByteArray& data)
|
||||
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
|
||||
d.readBool(20, &m_iqOrder, true);
|
||||
d.readBool(21, &m_biasTee, false);
|
||||
d.readFloat(22, &m_replayOffset, 0.0f);
|
||||
d.readFloat(23, &m_replayLength, 20.0f);
|
||||
d.readFloat(24, &m_replayStep, 5.0f);
|
||||
d.readBool(25, &m_replayLoop, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -187,6 +199,18 @@ void RTLSDRSettings::applySettings(const QStringList& settingsKeys, const RTLSDR
|
||||
if (settingsKeys.contains("biasTee")) {
|
||||
m_biasTee = settings.m_biasTee;
|
||||
}
|
||||
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")) {
|
||||
m_useReverseAPI = settings.m_useReverseAPI;
|
||||
}
|
||||
@@ -256,6 +280,18 @@ QString RTLSDRSettings::getDebugString(const QStringList& settingsKeys, bool for
|
||||
if (settingsKeys.contains("biasTee") || force) {
|
||||
ostr << " m_biasTee: " << m_biasTee;
|
||||
}
|
||||
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) {
|
||||
ostr << " m_useReverseAPI: " << m_useReverseAPI;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ struct RTLSDRSettings {
|
||||
quint32 m_rfBandwidth; //!< RF filter bandwidth in Hz
|
||||
bool m_offsetTuning;
|
||||
bool m_biasTee;
|
||||
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;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include "rtlsdrthread.h"
|
||||
@@ -26,12 +28,13 @@
|
||||
|
||||
#define FCD_BLOCKSIZE 16384
|
||||
|
||||
RTLSDRThread::RTLSDRThread(rtlsdr_dev_t* dev, SampleSinkFifo* sampleFifo, QObject* parent) :
|
||||
RTLSDRThread::RTLSDRThread(rtlsdr_dev_t* dev, SampleSinkFifo* sampleFifo, ReplayBuffer<quint8> *replayBuffer, QObject* parent) :
|
||||
QThread(parent),
|
||||
m_running(false),
|
||||
m_dev(dev),
|
||||
m_convertBuffer(FCD_BLOCKSIZE),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_replayBuffer(replayBuffer),
|
||||
m_samplerate(288000),
|
||||
m_log2Decim(4),
|
||||
m_fcPos(0),
|
||||
@@ -92,192 +95,241 @@ void RTLSDRThread::run()
|
||||
}
|
||||
|
||||
// Decimate according to specified log2 (ex: log2=4 => decim=16)
|
||||
void RTLSDRThread::callbackIQ(const quint8* buf, qint32 len)
|
||||
// Len is total samples (i.e. one I and Q pair will have len=2)
|
||||
void RTLSDRThread::callbackIQ(const quint8* inBuf, qint32 len)
|
||||
{
|
||||
SampleVector::iterator it = m_convertBuffer.begin();
|
||||
|
||||
if (m_log2Decim == 0)
|
||||
{
|
||||
m_decimatorsIQ.decimate1(&it, buf, len);
|
||||
// Save data to replay buffer
|
||||
m_replayBuffer->lock();
|
||||
bool replayEnabled = m_replayBuffer->getSize() > 0;
|
||||
if (replayEnabled) {
|
||||
m_replayBuffer->write(inBuf, len);
|
||||
}
|
||||
else
|
||||
|
||||
const quint8* buf = inBuf;
|
||||
qint32 remaining = len;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
if (m_fcPos == 0) // Infradyne
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsIQ.decimate2_inf(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsIQ.decimate4_inf(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsIQ.decimate8_inf(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsIQ.decimate16_inf(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsIQ.decimate32_inf(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsIQ.decimate64_inf(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Choose between live data or replayed data
|
||||
if (replayEnabled && m_replayBuffer->useReplay()) {
|
||||
len = m_replayBuffer->read(remaining, buf);
|
||||
qDebug() << "Replay";
|
||||
} else {
|
||||
len = remaining;
|
||||
qDebug() << "Live" << m_replayBuffer->useReplay();
|
||||
}
|
||||
else if (m_fcPos == 1) // Supradyne
|
||||
remaining -= len;
|
||||
|
||||
if (m_log2Decim == 0)
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsIQ.decimate2_sup(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsIQ.decimate4_sup(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsIQ.decimate8_sup(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsIQ.decimate16_sup(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsIQ.decimate32_sup(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsIQ.decimate64_sup(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_decimatorsIQ.decimate1(&it, buf, len);
|
||||
}
|
||||
else // Centered
|
||||
else
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
if (m_fcPos == 0) // Infradyne
|
||||
{
|
||||
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;
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsIQ.decimate2_inf(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsIQ.decimate4_inf(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsIQ.decimate8_inf(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsIQ.decimate16_inf(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsIQ.decimate32_inf(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsIQ.decimate64_inf(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (m_fcPos == 1) // Supradyne
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsIQ.decimate2_sup(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsIQ.decimate4_sup(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsIQ.decimate8_sup(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsIQ.decimate16_sup(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsIQ.decimate32_sup(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsIQ.decimate64_sup(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // Centered
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
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);
|
||||
|
||||
if(!m_running)
|
||||
rtlsdr_cancel_async(m_dev);
|
||||
}
|
||||
|
||||
void RTLSDRThread::callbackQI(const quint8* buf, qint32 len)
|
||||
void RTLSDRThread::callbackQI(const quint8* inBuf, qint32 len)
|
||||
{
|
||||
SampleVector::iterator it = m_convertBuffer.begin();
|
||||
|
||||
if (m_log2Decim == 0)
|
||||
{
|
||||
m_decimatorsQI.decimate1(&it, buf, len);
|
||||
// Save data to replay buffer
|
||||
m_replayBuffer->lock();
|
||||
bool replayEnabled = m_replayBuffer->getSize() > 0;
|
||||
if (replayEnabled) {
|
||||
m_replayBuffer->write(inBuf, len);
|
||||
}
|
||||
else
|
||||
|
||||
const quint8* buf = inBuf;
|
||||
qint32 remaining = len;
|
||||
|
||||
while (remaining > 0)
|
||||
{
|
||||
if (m_fcPos == 0) // Infradyne
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsQI.decimate2_inf(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsQI.decimate4_inf(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsQI.decimate8_inf(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsQI.decimate16_inf(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsQI.decimate32_inf(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsQI.decimate64_inf(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Choose between live data or replayed data
|
||||
if (replayEnabled && m_replayBuffer->useReplay()) {
|
||||
len = m_replayBuffer->read(remaining, buf);
|
||||
} else {
|
||||
len = remaining;
|
||||
}
|
||||
else if (m_fcPos == 1) // Supradyne
|
||||
remaining -= len;
|
||||
|
||||
if (m_log2Decim == 0)
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsQI.decimate2_sup(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsQI.decimate4_sup(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsQI.decimate8_sup(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsQI.decimate16_sup(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsQI.decimate32_sup(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsQI.decimate64_sup(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_decimatorsQI.decimate1(&it, buf, len);
|
||||
}
|
||||
else // Centered
|
||||
else
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
if (m_fcPos == 0) // Infradyne
|
||||
{
|
||||
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;
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsQI.decimate2_inf(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsQI.decimate4_inf(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsQI.decimate8_inf(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsQI.decimate16_inf(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsQI.decimate32_inf(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsQI.decimate64_inf(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (m_fcPos == 1) // Supradyne
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
m_decimatorsQI.decimate2_sup(&it, buf, len);
|
||||
break;
|
||||
case 2:
|
||||
m_decimatorsQI.decimate4_sup(&it, buf, len);
|
||||
break;
|
||||
case 3:
|
||||
m_decimatorsQI.decimate8_sup(&it, buf, len);
|
||||
break;
|
||||
case 4:
|
||||
m_decimatorsQI.decimate16_sup(&it, buf, len);
|
||||
break;
|
||||
case 5:
|
||||
m_decimatorsQI.decimate32_sup(&it, buf, len);
|
||||
break;
|
||||
case 6:
|
||||
m_decimatorsQI.decimate64_sup(&it, buf, len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // Centered
|
||||
{
|
||||
switch (m_log2Decim)
|
||||
{
|
||||
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);
|
||||
|
||||
if(!m_running)
|
||||
@@ -294,4 +346,3 @@ void RTLSDRThread::callbackHelper(unsigned char* buf, uint32_t len, void* ctx)
|
||||
thread->callbackQI(buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <QWaitCondition>
|
||||
#include <rtl-sdr.h>
|
||||
|
||||
#include "dsp/replaybuffer.h"
|
||||
#include "dsp/samplesinkfifo.h"
|
||||
#include "dsp/decimatorsu.h"
|
||||
|
||||
@@ -33,7 +34,7 @@ class RTLSDRThread : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RTLSDRThread(rtlsdr_dev_t* dev, SampleSinkFifo* sampleFifo, QObject* parent = NULL);
|
||||
RTLSDRThread(rtlsdr_dev_t* dev, SampleSinkFifo* sampleFifo, ReplayBuffer<quint8> *replayBuffer, QObject* parent = NULL);
|
||||
~RTLSDRThread();
|
||||
|
||||
void startWork();
|
||||
@@ -51,6 +52,7 @@ private:
|
||||
rtlsdr_dev_t* m_dev;
|
||||
SampleVector m_convertBuffer;
|
||||
SampleSinkFifo* m_sampleFifo;
|
||||
ReplayBuffer<quint8> *m_replayBuffer;
|
||||
|
||||
int m_samplerate;
|
||||
unsigned int m_log2Decim;
|
||||
|
||||
Reference in New Issue
Block a user