kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			
						commit
						cb97263129
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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> | ||||
|  |  | |||
|  | @ -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; | ||||
|     } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|         { | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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"/> | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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) | ||||
|         { | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -167,6 +167,7 @@ private: | |||
| private slots: | ||||
|     void handleChannelGUIClosing(ChannelGUI* channelGUI); | ||||
|     void handleDeleteChannel(ChannelAPI *channelAPI); | ||||
|     void onTimeSelected(int deviceSetIndex, float time); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
| } | ||||
|  |  | |||
|  | @ -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
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Edouard Griffiths
						Edouard Griffiths