From 97fee4d08adbdd0d9400d334aea164898663c836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pinkava?= Date: Sun, 6 Nov 2022 17:38:16 +0100 Subject: [PATCH 1/2] Simplify setFrequencyFocus() * Replace depreceted QMouseEvent constructor * Focus only if not already focuses * Keep the current position if focused --- src/qtgui/freqctrl.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/qtgui/freqctrl.cpp b/src/qtgui/freqctrl.cpp index f5b2c158a..da8590bc2 100644 --- a/src/qtgui/freqctrl.cpp +++ b/src/qtgui/freqctrl.cpp @@ -31,6 +31,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include + #include #include "freqctrl.h" @@ -876,13 +878,11 @@ void CFreqCtrl::cursorEnd() void CFreqCtrl::setFrequencyFocus() { - uint8_t position = floor(log10(m_freq)); - position = (uint8_t)fmax(position, 4); // restrict min to 100s of kHz + if (!hasFocus() || m_ActiveEditDigit == -1) { + // Select last digit or 5th digit (100s of kHz), whatever is bigger. + uint8_t position = MAX(int(log10(m_freq)), 5); - QMouseEvent mouseEvent(QEvent::MouseMove, - m_DigitInfo[position].dQRect.center(), - Qt::NoButton, - Qt::NoButton, - Qt::NoModifier); - mouseMoveEvent(&mouseEvent); + setFocus(Qt::ShortcutFocusReason); + setActiveDigit(position); + } } From e81b4b559352ef569f951f181270c99646615932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pinkava?= Date: Fri, 30 Sep 2022 00:10:13 +0200 Subject: [PATCH 2/2] Add IQ recording to WAV file --- src/applications/gqrx/mainwindow.cpp | 15 +++++----- src/applications/gqrx/mainwindow.h | 2 +- src/applications/gqrx/receiver.cpp | 42 +++++++++++++++++++++++---- src/applications/gqrx/receiver.h | 11 +++++-- src/qtgui/iq_tool.cpp | 35 +++++++++++++++++++--- src/qtgui/iq_tool.h | 7 ++++- src/qtgui/iq_tool.ui | 43 +++++++++++++++++++--------- 7 files changed, 121 insertions(+), 34 deletions(-) diff --git a/src/applications/gqrx/mainwindow.cpp b/src/applications/gqrx/mainwindow.cpp index 145245497..30467a6f0 100644 --- a/src/applications/gqrx/mainwindow.cpp +++ b/src/applications/gqrx/mainwindow.cpp @@ -281,7 +281,8 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent) connect(&DXCSpots::Get(), SIGNAL(dxcSpotsUpdated()), this, SLOT(updateClusterSpots())); // I/Q playback - connect(iq_tool, SIGNAL(startRecording(QString)), this, SLOT(startIqRecording(QString))); + connect(iq_tool, SIGNAL(startRecording(QString,receiver::RecordingFormat)), + this, SLOT(startIqRecording(QString,receiver::RecordingFormat))); connect(iq_tool, SIGNAL(stopRecording()), this, SLOT(stopIqRecording())); connect(iq_tool, SIGNAL(startPlayback(QString,float,qint64)), this, SLOT(startIqPlayback(QString,float,qint64))); connect(iq_tool, SIGNAL(stopPlayback()), this, SLOT(stopIqPlayback())); @@ -1540,20 +1541,21 @@ void MainWindow::stopAudioStreaming() } /** Start I/Q recording. */ -void MainWindow::startIqRecording(const QString& recdir) +void MainWindow::startIqRecording(const QString& recdir, const receiver::RecordingFormat format) { qDebug() << __func__; // generate file name using date, time, rf freq in kHz and BW in Hz - // gqrx_iq_yyyymmdd_hhmmss_freq_bw_fc.raw + // gqrx_iq_yyyymmdd_hhmmss_freq_bw_fc.format + const char * const suffix = format == receiver::RecordingFormat::RAW ? "raw" : "wav"; auto freq = (qint64)(rx->get_rf_freq()); auto sr = (qint64)(rx->get_input_rate()); auto dec = (quint32)(rx->get_input_decim()); auto lastRec = QDateTime::currentDateTimeUtc(). - toString("%1/gqrx_yyyyMMdd_hhmmss_%2_%3_fc.'raw'") - .arg(recdir).arg(freq).arg(sr/dec); + toString("%1/gqrx_yyyyMMdd_hhmmss_%2_%3_fc.%4") + .arg(recdir).arg(freq).arg(sr/dec).arg(suffix); // start recorder; fails if recording already in progress - if (rx->start_iq_recording(lastRec.toStdString())) + if (rx->start_iq_recording(lastRec.toStdString(), format)) { // reset action status ui->statusBar->showMessage(tr("Error starting I/Q recoder")); @@ -1564,7 +1566,6 @@ void MainWindow::startIqRecording(const QString& recdir) msg_box.setText(tr("There was an error starting the I/Q recorder.\n" "Check write permissions for the selected location.")); msg_box.exec(); - } else { diff --git a/src/applications/gqrx/mainwindow.h b/src/applications/gqrx/mainwindow.h index b8c24a77e..e273a21b9 100644 --- a/src/applications/gqrx/mainwindow.h +++ b/src/applications/gqrx/mainwindow.h @@ -178,7 +178,7 @@ private slots: void stopAudioStreaming(); /* I/Q playback and recording*/ - void startIqRecording(const QString& recdir); + void startIqRecording(const QString& recdir, const receiver::RecordingFormat format); void stopIqRecording(); void startIqPlayback(const QString& filename, float samprate, qint64 center_freq); void stopIqPlayback(); diff --git a/src/applications/gqrx/receiver.cpp b/src/applications/gqrx/receiver.cpp index 8f36d7818..115f389cc 100644 --- a/src/applications/gqrx/receiver.cpp +++ b/src/applications/gqrx/receiver.cpp @@ -1178,7 +1178,7 @@ receiver::status receiver::stop_udp_streaming() * @brief Start I/Q data recorder. * @param filename The filename where to record. */ -receiver::status receiver::start_iq_recording(const std::string filename) +receiver::status receiver::start_iq_recording(const std::string filename, const receiver::RecordingFormat format) { receiver::status status = STATUS_OK; @@ -1187,9 +1187,18 @@ receiver::status receiver::start_iq_recording(const std::string filename) return STATUS_ERROR; } + gr::basic_block_sptr iq_sink; try { - iq_sink = gr::blocks::file_sink::make(sizeof(gr_complex), filename.c_str(), true); + if (format == receiver::RecordingFormat::WAV) { + iq_sink_wav = gr::blocks::wavfile_sink::make( + filename.c_str(), 2, get_quad_rate(), gr::blocks::FORMAT_WAV, gr::blocks::FORMAT_FLOAT); + iq_complex_to_float = gr::blocks::complex_to_float::make(); + iq_sink = iq_complex_to_float; + } else { + iq_sink_raw = gr::blocks::file_sink::make(sizeof(gr_complex), filename.c_str(), true); + iq_sink = iq_sink_raw; + } } catch (std::runtime_error &e) { @@ -1198,6 +1207,10 @@ receiver::status receiver::start_iq_recording(const std::string filename) } tb->lock(); + if (format == receiver::RecordingFormat::WAV) { + tb->connect(iq_complex_to_float, 0, iq_sink_wav, 0); + tb->connect(iq_complex_to_float, 1, iq_sink_wav, 1); + } if (d_decim >= 2) tb->connect(input_decim, 0, iq_sink, 0); else @@ -1216,8 +1229,18 @@ receiver::status receiver::stop_iq_recording() return STATUS_ERROR; } + gr::basic_block_sptr iq_sink; tb->lock(); - iq_sink->close(); + if (iq_sink_raw) { + iq_sink_raw->close(); + iq_sink = iq_sink_raw; + } + if (iq_sink_wav) { + iq_sink_wav->close(); + iq_sink = iq_complex_to_float; + tb->disconnect(iq_complex_to_float, 0, iq_sink_wav, 0); + tb->disconnect(iq_complex_to_float, 1, iq_sink_wav, 1); + } if (d_decim >= 2) tb->disconnect(input_decim, 0, iq_sink, 0); @@ -1225,7 +1248,9 @@ receiver::status receiver::stop_iq_recording() tb->disconnect(src, 0, iq_sink, 0); tb->unlock(); - iq_sink.reset(); + iq_sink_raw.reset(); + iq_sink_wav.reset(); + iq_complex_to_float.reset(); d_recording_iq = false; return STATUS_OK; @@ -1330,7 +1355,14 @@ void receiver::connect_all(rx_chain type) if (d_recording_iq) { // We record IQ with minimal pre-processing - tb->connect(b, 0, iq_sink, 0); + if (iq_sink_raw) { + tb->connect(b, 0, iq_sink_raw, 0); + } + if (iq_sink_wav) { + tb->connect(b, 0, iq_complex_to_float, 0); + tb->connect(iq_complex_to_float, 0, iq_sink_wav, 0); + tb->connect(iq_complex_to_float, 1, iq_sink_wav, 1); + } } tb->connect(b, 0, iq_swap, 0); diff --git a/src/applications/gqrx/receiver.h b/src/applications/gqrx/receiver.h index abf827cb8..13a6f0a95 100644 --- a/src/applications/gqrx/receiver.h +++ b/src/applications/gqrx/receiver.h @@ -110,6 +110,11 @@ class receiver FILTER_SHAPE_SHARP = 2 /*!< Sharp: Transition band is TBD of width. */ }; + enum class RecordingFormat { + RAW, + WAV, + }; + receiver(const std::string input_device="", const std::string audio_device="", unsigned int decimation=1); @@ -210,7 +215,7 @@ class receiver status stop_udp_streaming(); /* I/Q recording and playback */ - status start_iq_recording(const std::string filename); + status start_iq_recording(const std::string filename, const RecordingFormat format); status stop_iq_recording(); status seek_iq_file(long pos); @@ -275,7 +280,9 @@ class receiver gr::blocks::multiply_const_ff::sptr audio_gain0; /*!< Audio gain block. */ gr::blocks::multiply_const_ff::sptr audio_gain1; /*!< Audio gain block. */ - gr::blocks::file_sink::sptr iq_sink; /*!< I/Q file sink. */ + gr::blocks::file_sink::sptr iq_sink_raw; /*!< I/Q file sink. */ + gr::blocks::wavfile_sink::sptr iq_sink_wav; /*!< I/Q file sink. */ + gr::blocks::complex_to_float::sptr iq_complex_to_float; gr::blocks::wavfile_sink::sptr wav_sink; /*!< WAV file sink for recording. */ gr::blocks::wavfile_source::sptr wav_src; /*!< WAV file source for playback. */ diff --git a/src/qtgui/iq_tool.cpp b/src/qtgui/iq_tool.cpp index 8375db8e9..fd89714a4 100644 --- a/src/qtgui/iq_tool.cpp +++ b/src/qtgui/iq_tool.cpp @@ -38,7 +38,8 @@ CIqTool::CIqTool(QWidget *parent) : QDialog(parent), - ui(new Ui::CIqTool) + ui(new Ui::CIqTool), + format(receiver::RecordingFormat::RAW) { ui->setupUi(this); @@ -51,7 +52,8 @@ CIqTool::CIqTool(QWidget *parent) : //ui->recDirEdit->setText(QDir::currentPath()); - recdir = new QDir(QDir::homePath(), "*.raw"); + recdir = new QDir(QDir::homePath(), "*.raw;*.wav"); + recdir->setFilter(QDir::Files); error_palette = new QPalette(); error_palette->setColor(QPalette::Text, Qt::red); @@ -100,6 +102,16 @@ void CIqTool::on_listWidget_currentTextChanged(const QString ¤tText) } +void CIqTool::on_wavRadioButton_clicked(bool checked) +{ + format = receiver::RecordingFormat::WAV; +} + +void CIqTool::on_rawRadioButton_clicked(bool checked) +{ + format = receiver::RecordingFormat::RAW; +} + /*! \brief Start/stop playback */ void CIqTool::on_playButton_clicked(bool checked) { @@ -173,7 +185,7 @@ void CIqTool::on_recButton_clicked(bool checked) if (checked) { ui->playButton->setEnabled(false); - emit startRecording(recdir->path()); + emit startRecording(recdir->path(), format); refreshDir(); ui->listWidget->setCurrentRow(ui->listWidget->count()-1); @@ -235,6 +247,11 @@ void CIqTool::saveSettings(QSettings *settings) else settings->remove("baseband/rec_dir"); + if (format == receiver::RecordingFormat::RAW) { + settings->setValue("baseband/rec_format", "raw"); + } else { + settings->setValue("baseband/rec_format", "wav"); + } } void CIqTool::readSettings(QSettings *settings) @@ -245,8 +262,18 @@ void CIqTool::readSettings(QSettings *settings) // Location of baseband recordings QString dir = settings->value("baseband/rec_dir", QDir::homePath()).toString(); ui->recDirEdit->setText(dir); -} + QString fmt = settings->value("baseband/rec_format", "raw").toString(); + if (fmt == "wav") { + ui->rawRadioButton->setChecked(false); + ui->wavRadioButton->setChecked(true); + format = receiver::RecordingFormat::WAV; + } else { + ui->rawRadioButton->setChecked(true); + ui->wavRadioButton->setChecked(false); + format = receiver::RecordingFormat::RAW; + } +} /*! \brief Slot called when the recordings directory has changed either * because of user input or programmatically. diff --git a/src/qtgui/iq_tool.h b/src/qtgui/iq_tool.h index cb8da0f5b..138ffa119 100644 --- a/src/qtgui/iq_tool.h +++ b/src/qtgui/iq_tool.h @@ -32,6 +32,8 @@ #include #include +#include "applications/gqrx/receiver.h" + namespace Ui { class CIqTool; } @@ -62,7 +64,7 @@ class CIqTool : public QDialog void readSettings(QSettings *settings); signals: - void startRecording(const QString recdir); + void startRecording(const QString recdir, const receiver::RecordingFormat format); void stopRecording(); void startPlayback(const QString filename, float samprate, qint64 center_freq); void stopPlayback(); @@ -79,6 +81,8 @@ private slots: void on_playButton_clicked(bool checked); void on_slider_valueChanged(int value); void on_listWidget_currentTextChanged(const QString ¤tText); + void on_wavRadioButton_clicked(bool checked); + void on_rawRadioButton_clicked(bool checked); void timeoutFunction(void); private: @@ -89,6 +93,7 @@ private slots: private: Ui::CIqTool *ui; + receiver::RecordingFormat format; QDir *recdir; QTimer *timer; QPalette *error_palette; /*!< Palette used to indicate an error. */ diff --git a/src/qtgui/iq_tool.ui b/src/qtgui/iq_tool.ui index 1d9d74eb1..f52abfe95 100644 --- a/src/qtgui/iq_tool.ui +++ b/src/qtgui/iq_tool.ui @@ -153,7 +153,7 @@ - + Qt::Horizontal @@ -166,27 +166,42 @@ - + - 00:00:00 / 00:00:00 + File format: - - Qt::AlignCenter + + + + + + Raw + + + true - - - Qt::Horizontal + + + WAV - - - 40 - 20 - + + + + + + + + + + 00:00:00 / 00:00:00 - + + Qt::AlignCenter + +