Merge pull request #1914 from srcejon/freq_scanner

Instant Replay
This commit is contained in:
Edouard Griffiths 2023-12-06 15:24:34 +01:00 committed by GitHub
commit cb97263129
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 787 additions and 367 deletions

View File

@ -130,6 +130,14 @@ jobs:
id: get_filename
run: echo "filename=$(grep CPACK_PACKAGE_FILE_NAME build/CMakeCache.txt | cut -d "=" -f2)" >> $GITHUB_OUTPUT
- name: Build SDRangel on Mac
run: |
cd build
make -j3
- name: Stop XProtectBehaviorService
run: |
echo "killing XProject as it can interfere with hdiutil"; sudo pkill -9 XProtect >/dev/null || true;
echo "waiting..."; while pgrep XProtect; do sleep 3; done;
- name: Build DMG
run: |
cd build
make package -j3

View File

@ -623,6 +623,11 @@ void AirspyHFGui::on_replayLoop_toggled(bool checked)
sendSettings();
}
void AirspyHFGui::setReplayTime(float time)
{
ui->replayOffset->setValue(std::ceil(time * 10.0f));
}
void AirspyHFGui::makeUIConnections()
{
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &AirspyHFGui::on_centerFrequency_changed);

View File

@ -46,6 +46,7 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue* getInputMessageQueue() { return &m_inputMessageQueue; }
void setReplayTime(float time) override;
uint32_t getDevSampleRate(unsigned int index);
int getDevSampleRateIndex(uint32_t sampleRate);

View File

@ -77,27 +77,27 @@ void AirspyHFWorker::setLog2Decimation(unsigned int log2_decim)
// Decimate according to specified log2 (ex: log2=4 => decim=16)
void AirspyHFWorker::callbackIQ(const float* inBuf, qint32 len)
{
SampleVector::iterator it = m_convertBuffer.begin();
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const float* buf = inBuf;
qint32 remaining = len;
const float* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
switch (m_log2Decim)
{
@ -135,32 +135,32 @@ void AirspyHFWorker::callbackIQ(const float* inBuf, qint32 len)
m_replayBuffer->unlock();
m_sampleFifo->write(m_convertBuffer.begin(), it);
m_sampleFifo->write(m_convertBuffer.begin(), it);
}
void AirspyHFWorker::callbackQI(const float* inBuf, qint32 len)
{
SampleVector::iterator it = m_convertBuffer.begin();
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const float* buf = inBuf;
qint32 remaining = len;
const float* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
switch (m_log2Decim)
{
@ -198,7 +198,7 @@ void AirspyHFWorker::callbackQI(const float* inBuf, qint32 len)
m_replayBuffer->unlock();
m_sampleFifo->write(m_convertBuffer.begin(), it);
m_sampleFifo->write(m_convertBuffer.begin(), it);
}
int AirspyHFWorker::rx_callback(airspyhf_transfer_t* transfer)

View File

@ -48,6 +48,7 @@ MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgGetDeviceInfo, Message)
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgReportStreamInfo, Message)
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgCalibrationResult, Message)
MESSAGE_CLASS_DEFINITION(LimeSDRInput::MsgSaveReplay, Message)
LimeSDRInput::LimeSDRInput(DeviceAPI *deviceAPI) :
m_deviceAPI(deviceAPI),
@ -422,7 +423,7 @@ bool LimeSDRInput::start()
// 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");
applySettings(m_settings, QList<QString>(), true);
@ -778,6 +779,12 @@ bool LimeSDRInput::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;
@ -977,6 +984,9 @@ bool LimeSDRInput::applySettings(const LimeSDRInputSettings& settings, const QLi
settings.m_devSampleRate,
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);
}
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;
if (LMS_GetClockFreq(m_deviceShared.m_deviceParams->getDevice(), LMS_CLOCK_CGEN, &clockGenFreqAfter) != 0)

View File

@ -29,6 +29,7 @@
#include <QNetworkRequest>
#include "dsp/devicesamplesource.h"
#include "dsp/replaybuffer.h"
#include "limesdr/devicelimesdrshared.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 void destroy();
@ -280,6 +300,7 @@ private:
lms_stream_t m_streamId;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
ReplayBuffer<qint16> m_replayBuffer;
bool openDevice();
void closeDevice();

View File

@ -469,6 +469,10 @@ void LimeSDRInputGUI::displaySettings()
setNCODisplay();
ui->ncoEnable->setChecked(m_settings.m_ncoEnable);
displayReplayLength();
displayReplayOffset();
displayReplayStep();
ui->replayLoop->setChecked(m_settings.m_replayLoop);
}
void LimeSDRInputGUI::setNCODisplay()
@ -805,6 +809,9 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p)
if (m_contextMenuType == ContextMenuDeviceSettings)
{
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.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
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_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();
}
@ -829,6 +843,96 @@ void LimeSDRInputGUI::openDeviceSettingsDialog(const QPoint& p)
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()
{
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->transverter, &TransverterButton::clicked, this, &LimeSDRInputGUI::on_transverter_clicked);
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);
}

View File

@ -45,6 +45,7 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setReplayTime(float time) override;
private:
Ui::LimeSDRInputGUI* ui;
@ -66,6 +67,9 @@ private:
void displaySettings();
void displaySampleRate();
void displayReplayLength();
void displayReplayOffset();
void displayReplayStep();
void setNCODisplay();
void setCenterFrequencyDisplay();
void setCenterFrequencySetting(uint64_t kHzValue);
@ -101,6 +105,12 @@ private slots:
void on_extClock_clicked();
void on_transverter_clicked();
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 updateHardware();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>360</width>
<height>230</height>
<height>255</height>
</rect>
</property>
<property name="sizePolicy">
@ -19,13 +19,13 @@
<property name="minimumSize">
<size>
<width>360</width>
<height>230</height>
<height>255</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>380</width>
<height>266</height>
<width>386</width>
<height>280</height>
</size>
</property>
<property name="font">
@ -1169,6 +1169,104 @@ QToolTip{background-color: white; color: black;}</string>
</item>
</layout>
</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>
</widget>
<customwidgets>

View File

@ -51,6 +51,10 @@ void LimeSDRInputSettings::resetToDefaults()
m_iqOrder = true;
m_gpioDir = 0;
m_gpioPins = 0;
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;
@ -88,6 +92,10 @@ QByteArray LimeSDRInputSettings::serialize() const
s.writeU32(26, m_reverseAPIPort);
s.writeU32(27, m_reverseAPIDeviceIndex);
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();
}
@ -146,6 +154,10 @@ bool LimeSDRInputSettings::deserialize(const QByteArray& data)
d.readU32(27, &uintval, 0);
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
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;
}
@ -231,6 +243,18 @@ void LimeSDRInputSettings::applySettings(const QStringList& settingsKeys, const
if (settingsKeys.contains("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")) {
m_useReverseAPI = settings.m_useReverseAPI;
}
@ -321,6 +345,18 @@ QString LimeSDRInputSettings::getDebugString(const QStringList& settingsKeys, bo
if (settingsKeys.contains("gpioPins") || force) {
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) {
ostr << " m_useReverseAPI: " << m_useReverseAPI;
}

View File

@ -71,6 +71,10 @@ struct LimeSDRInputSettings
bool m_iqOrder;
uint8_t m_gpioDir; //!< GPIO pin direction LSB first; 0 input, 1 output
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;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;

View File

@ -18,15 +18,18 @@
#include <errno.h>
#include <algorithm>
#include "dsp/replaybuffer.h"
#include "limesdrinputsettings.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),
m_running(false),
m_stream(stream),
m_convertBuffer(DeviceLimeSDR::blockSize),
m_sampleFifo(sampleFifo),
m_replayBuffer(replayBuffer),
m_log2Decim(0),
m_iqOrder(true)
{
@ -106,70 +109,116 @@ void LimeSDRInputThread::run()
}
// 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();
switch (m_log2Decim)
{
case 0:
m_decimatorsIQ.decimate1(&it, buf, len);
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;
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const qint16* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
switch (m_log2Decim)
{
case 0:
m_decimatorsIQ.decimate1(&it, buf, len);
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);
}
void LimeSDRInputThread::callbackQI(const qint16* buf, qint32 len)
void LimeSDRInputThread::callbackQI(const qint16* inBuf, qint32 len)
{
SampleVector::iterator it = m_convertBuffer.begin();
switch (m_log2Decim)
{
case 0:
m_decimatorsQI.decimate1(&it, buf, len);
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;
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const qint16* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
switch (m_log2Decim)
{
case 0:
m_decimatorsQI.decimate1(&it, buf, len);
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);
}

View File

@ -37,7 +37,8 @@ class LimeSDRInputThread : public QThread, public DeviceLimeSDRShared::ThreadInt
Q_OBJECT
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();
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
SampleVector m_convertBuffer;
SampleSinkFifo* m_sampleFifo;
ReplayBuffer<qint16> *m_replayBuffer;
unsigned int m_log2Decim; // soft decimation
bool m_iqOrder;

View File

@ -691,6 +691,11 @@ void RTLSDRGui::on_replayLoop_toggled(bool checked)
sendSettings();
}
void RTLSDRGui::setReplayTime(float time)
{
ui->replayOffset->setValue(std::ceil(time * 10.0f));
}
void RTLSDRGui::makeUIConnections()
{
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &RTLSDRGui::on_centerFrequency_changed);

View File

@ -50,6 +50,7 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setReplayTime(float time) override;
private:
Ui::RTLSDRGui* ui;

View File

@ -98,240 +98,240 @@ void RTLSDRThread::run()
// 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();
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const quint8* buf = inBuf;
qint32 remaining = len;
const quint8* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
if (m_log2Decim == 0)
{
m_decimatorsIQ.decimate1(&it, buf, len);
}
else
{
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;
}
}
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;
}
}
}
}
if (m_log2Decim == 0)
{
m_decimatorsIQ.decimate1(&it, buf, len);
}
else
{
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;
}
}
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_replayBuffer->unlock();
m_sampleFifo->write(m_convertBuffer.begin(), it);
m_sampleFifo->write(m_convertBuffer.begin(), it);
if(!m_running)
rtlsdr_cancel_async(m_dev);
if(!m_running)
rtlsdr_cancel_async(m_dev);
}
void RTLSDRThread::callbackQI(const quint8* inBuf, qint32 len)
{
SampleVector::iterator it = m_convertBuffer.begin();
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const quint8* buf = inBuf;
qint32 remaining = len;
const quint8* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
if (m_log2Decim == 0)
{
m_decimatorsQI.decimate1(&it, buf, len);
}
else
{
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;
}
}
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;
}
}
}
}
if (m_log2Decim == 0)
{
m_decimatorsQI.decimate1(&it, buf, len);
}
else
{
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;
}
}
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_replayBuffer->unlock();
m_sampleFifo->write(m_convertBuffer.begin(), it);
m_sampleFifo->write(m_convertBuffer.begin(), it);
if(!m_running)
rtlsdr_cancel_async(m_dev);
if(!m_running)
rtlsdr_cancel_async(m_dev);
}
void RTLSDRThread::callbackHelper(unsigned char* buf, uint32_t len, void* ctx)

View File

@ -687,6 +687,11 @@ void SDRPlayV3Gui::on_replayLoop_toggled(bool checked)
sendSettings();
}
void SDRPlayV3Gui::setReplayTime(float time)
{
ui->replayOffset->setValue(std::ceil(time * 10.0f));
}
void SDRPlayV3Gui::makeUIConnections()
{
QObject::connect(ui->centerFrequency, &ValueDial::changed, this, &SDRPlayV3Gui::on_centerFrequency_changed);

View File

@ -46,6 +46,7 @@ public:
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setReplayTime(float time) override;
private:
Ui::SDRPlayV3Gui* ui;

View File

@ -177,24 +177,24 @@ void SDRPlayV3Thread::callbackIQ(const qint16* inBuf, qint32 len)
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const qint16* buf = inBuf;
qint32 remaining = len;
const qint16* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
if (m_log2Decim == 0)
{
@ -293,24 +293,24 @@ void SDRPlayV3Thread::callbackQI(const qint16* inBuf, qint32 len)
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const qint16* buf = inBuf;
qint32 remaining = len;
const qint16* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
if (m_log2Decim == 0)
{

View File

@ -769,6 +769,11 @@ void USRPInputGUI::on_replayLoop_toggled(bool checked)
sendSettings();
}
void USRPInputGUI::setReplayTime(float time)
{
ui->replayOffset->setValue(std::ceil(time * 10.0f));
}
void USRPInputGUI::makeUIConnections()
{
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &USRPInputGUI::on_startStop_toggled);

View File

@ -50,6 +50,7 @@ public:
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setReplayTime(float time) override;
private:
Ui::USRPInputGUI* ui;

View File

@ -875,28 +875,28 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>TransverterButton</class>
<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>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TransverterButton</class>
<extends>QPushButton</extends>
<header>gui/transverterbutton.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>

View File

@ -55,7 +55,7 @@ struct USRPInputSettings
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_replayLoop; //!< Replay buffer repeatedly without recording new data
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;

View File

@ -198,24 +198,24 @@ void USRPInputThread::callbackIQ(const qint16* inBuf, qint32 len)
SampleVector::iterator it = m_convertBuffer.begin();
// Save data to replay buffer
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
m_replayBuffer->lock();
bool replayEnabled = m_replayBuffer->getSize() > 0;
if (replayEnabled) {
m_replayBuffer->write(inBuf, len);
}
const qint16* buf = inBuf;
qint32 remaining = len;
const qint16* buf = inBuf;
qint32 remaining = len;
while (remaining > 0)
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
{
// Choose between live data or replayed data
if (replayEnabled && m_replayBuffer->useReplay()) {
len = m_replayBuffer->read(remaining, buf);
} else {
len = remaining;
}
remaining -= len;
switch (m_log2Decim)
{

View File

@ -81,6 +81,7 @@ public:
void setCurrentDeviceIndex(int index) { m_currentDeviceIndex = index; } //!< index in plugins list
void setChannelNames(const QStringList& channelNames) { m_channelAddDialog.addChannelNames(channelNames); }
DeviceUISet* getDeviceUISet() { return m_deviceUISet; }
virtual void setReplayTime(float time) { (void) time; } //!< Not supported by all devices
protected:
void closeEvent(QCloseEvent *event) override;

View File

@ -73,6 +73,8 @@ DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet)
font.setFamily(QStringLiteral("Liberation Sans"));
font.setPointSize(9);
m_spectrum->setFont(font);
connect(m_mainSpectrumGUI, &MainSpectrumGUI::timeSelected, this, &DeviceUISet::onTimeSelected);
}
DeviceUISet::~DeviceUISet()
@ -825,3 +827,12 @@ int DeviceUISet::webapiSpectrumServerDelete(SWGSDRangel::SWGSuccessResponse& res
{
return m_spectrumVis->webapiSpectrumServerDelete(response, errorMessage);
}
void DeviceUISet::onTimeSelected(int deviceSetIndex, float time)
{
(void) deviceSetIndex;
if (m_deviceGUI) {
m_deviceGUI->setReplayTime(time);
}
}

View File

@ -167,6 +167,7 @@ private:
private slots:
void handleChannelGUIClosing(ChannelGUI* channelGUI);
void handleDeleteChannel(ChannelAPI *channelAPI);
void onTimeSelected(int deviceSetIndex, float time);
};

View File

@ -4175,6 +4175,18 @@ void GLSpectrumView::mousePressEvent(QMouseEvent* event)
{
frequencyPan(event);
}
else if (event->modifiers() & Qt::ControlModifier)
{
if (!m_display3DSpectrogram && pointInWaterfallOrSpectrogram(ep))
{
QPointF pWat = ep;
pWat.rx() = (ep.x()/width() - m_waterfallRect.left()) / m_waterfallRect.width();
pWat.ry() = (ep.y()/height() - m_waterfallRect.top()) / m_waterfallRect.height();
float time = m_timeScale.getRangeMin() + pWat.y()*m_timeScale.getRange();
emit timeSelected(time);
}
return;
}
else if (m_display3DSpectrogram)
{
// Detect click and drag to rotate 3D spectrogram

View File

@ -541,6 +541,8 @@ signals:
void requestCenterFrequency(qint64 frequency);
// Emitted when annotations are changed
void updateAnnotations();
// Emitted when user ctrl-clicks on waterfall to select a time. time is in seconds.
void timeSelected(float time);
};

View File

@ -157,6 +157,8 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
connect(spectrum->getSpectrumView(), &GLSpectrumView::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency);
connect(spectrumGUI, &GLSpectrumGUI::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency);
connect(spectrum->getSpectrumView(), &GLSpectrumView::timeSelected, this, &MainSpectrumGUI::onTimeSelected);
m_resizer.enableChildMouseTracking();
shrinkWindow();
}
@ -380,3 +382,8 @@ void MainSpectrumGUI::onRequestCenterFrequency(qint64 frequency)
{
emit requestCenterFrequency(m_deviceSetIndex, frequency);
}
void MainSpectrumGUI::onTimeSelected(float time)
{
emit timeSelected(m_deviceSetIndex, time);
}

View File

@ -113,12 +113,14 @@ private slots:
void shrinkWindow();
void maximizeWindow();
void onRequestCenterFrequency(qint64 frequency);
void onTimeSelected(float time);
signals:
void closing();
void moveToWorkspace(int workspaceIndex);
void forceShrink();
void requestCenterFrequency(int deviceSetIndex, qint64 frequency); // an action from the user to move device center frequency
void timeSelected(int deviceSetIndex, float time); // user ctrl-clicked waterfall to set a time
};