Skip to content

Commit

Permalink
Implement new squelch_triggered audio recorder
Browse files Browse the repository at this point in the history
...while keeping in mind that gqrx-sdr#946
would be next.
Add tag processing to wavfile_sink_gqrx.
Implement event-driven GUI updates.
  • Loading branch information
vladisslav2011 committed Jan 24, 2022
1 parent aa0de46 commit ecc58dd
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 39 deletions.
14 changes: 12 additions & 2 deletions src/applications/gqrx/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockAudio, SIGNAL(audioPlayStarted(QString)), this, SLOT(startAudioPlayback(QString)));
connect(uiDockAudio, SIGNAL(audioPlayStopped()), this, SLOT(stopAudioPlayback()));
connect(uiDockAudio, SIGNAL(recDirChanged(QString)), this, SLOT(recDirChanged(QString)));
connect(uiDockAudio, SIGNAL(recSquelchTriggeredChanged(bool)), this, SLOT(recSquelchTriggeredChanged(bool)));
connect(uiDockAudio, SIGNAL(fftRateChanged(int)), this, SLOT(setAudioFftRate(int)));
connect(uiDockFft, SIGNAL(fftSizeChanged(int)), this, SLOT(setIqFftSize(int)));
connect(uiDockFft, SIGNAL(fftRateChanged(int)), this, SLOT(setIqFftRate(int)));
Expand Down Expand Up @@ -303,8 +304,8 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(remote, SIGNAL(newSquelchLevel(double)), this, SLOT(setSqlLevel(double)));
connect(remote, SIGNAL(newSquelchLevel(double)), uiDockRxOpt, SLOT(setSquelchLevel(double)));
connect(uiDockRxOpt, SIGNAL(sqlLevelChanged(double)), remote, SLOT(setSquelchLevel(double)));
connect(remote, SIGNAL(startAudioRecorderEvent()), uiDockAudio, SLOT(startAudioRecorder()));
connect(remote, SIGNAL(stopAudioRecorderEvent()), uiDockAudio, SLOT(stopAudioRecorder()));
connect(remote, SIGNAL(startAudioRecorderEvent()), this, SLOT(startAudioRec()));
connect(remote, SIGNAL(stopAudioRecorderEvent()), this, SLOT(stopAudioRec()));
connect(ui->plotter, SIGNAL(newFilterFreq(int, int)), remote, SLOT(setPassband(int, int)));
connect(remote, SIGNAL(newPassband(int)), this, SLOT(setPassband(int)));
connect(remote, SIGNAL(gainChanged(QString, double)), uiDockInputCtl, SLOT(setGain(QString,double)));
Expand Down Expand Up @@ -1539,6 +1540,15 @@ void MainWindow::recDirChanged(const QString dir)
rx->set_audio_rec_dir(dir.toStdString());
}

/**
* @brief Set audio recording squelch triggered mode.
* @param enabled New state.
*/
void MainWindow::recSquelchTriggeredChanged(const bool enabled)
{
rx->set_audio_rec_sql_triggered(enabled);
}

/**
* @brief Start audio recorder.
* @param filename The file name into which audio should be recorded.
Expand Down
1 change: 1 addition & 0 deletions src/applications/gqrx/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ private slots:

/* audio recording and playback */
void recDirChanged(const QString dir);
void recSquelchTriggeredChanged(const bool enabled);
void startAudioRec();
void stopAudioRec();
void audioRecEvent(const QString filename, bool is_running);
Expand Down
6 changes: 6 additions & 0 deletions src/applications/gqrx/receiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,12 @@ receiver::status receiver::set_audio_rec_dir(const std::string dir)
return STATUS_OK;
}

receiver::status receiver::set_audio_rec_sql_triggered(const bool enabled)
{
rx->set_audio_rec_squelch_triggered(enabled);
return STATUS_OK;
}

/**
* @brief Start WAV file recorder.
* @param filename The filename where to record.
Expand Down
1 change: 1 addition & 0 deletions src/applications/gqrx/receiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ class receiver

/* Audio parameters */
status set_audio_rec_dir(const std::string dir);
status set_audio_rec_sql_triggered(const bool enabled);
status start_audio_recording();
status stop_audio_recording();
std::string get_last_audio_filename();
Expand Down
50 changes: 44 additions & 6 deletions src/interfaces/wav_sink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ wavfile_sink_gqrx::wavfile_sink_gqrx(const char* filename,
d_updated(false),
d_center_freq(0),
d_offset(0),
d_rec_dir("")
d_rec_dir(""),
d_squelch_triggered(false)
{
int bits_per_sample = 16;

Expand Down Expand Up @@ -105,6 +106,9 @@ wavfile_sink_gqrx::wavfile_sink_gqrx(const char* filename,
if (!open(filename)) {
throw std::runtime_error("Can't open WAV file.");
}
//FIXME Make this configurable?
d_sob_key = pmt::intern("squelch_sob");
d_eob_key = pmt::intern("squelch_eob");
}

void wavfile_sink_gqrx::set_center_freq(double center_freq)
Expand All @@ -123,12 +127,15 @@ void wavfile_sink_gqrx::set_rec_dir(std::string dir)
}



bool wavfile_sink_gqrx::open(const char* filename)
{
SF_INFO sfinfo;

std::unique_lock<std::mutex> guard(d_mutex);
return open_unlocked(filename);
}

bool wavfile_sink_gqrx::open_unlocked(const char* filename)
{
SF_INFO sfinfo;

if (d_new_fp) { // if we've already got a new one open, close it
sf_close(d_new_fp);
Expand Down Expand Up @@ -235,12 +242,18 @@ bool wavfile_sink_gqrx::open(const char* filename)
}

int wavfile_sink_gqrx::open_new()
{
std::unique_lock<std::mutex> guard(d_mutex);
return open_new_unlocked();
}

int wavfile_sink_gqrx::open_new_unlocked()
{
// FIXME: option to use local time
// use toUTC() function compatible with older versions of Qt.
QString file_name = QDateTime::currentDateTime().toUTC().toString("gqrx_yyyyMMdd_hhmmss");
QString filename = QString("%1/%2_%3.wav").arg(QString(d_rec_dir.data())).arg(file_name).arg(qint64(d_center_freq + d_offset));
if(open(filename.toStdString().data()))
if(open_unlocked(filename.toStdString().data()))
{
if(d_rec_event)
d_rec_event(d_filename = filename.toStdString(), true);
Expand Down Expand Up @@ -293,8 +306,27 @@ int wavfile_sink_gqrx::work(int noutput_items,
int n_in_chans = input_items.size();
int nwritten;
int errnum;

//FIXME: reimplement with history and min_time, max_gap
uint64_t abs_N, end_N;
std::vector<gr::tag_t> work_tags;
std::unique_lock<std::mutex> guard(d_mutex); // hold mutex for duration of this block
sql_action last_action = ACT_NONE;

abs_N = nitems_read(0);
end_N = abs_N + (uint64_t)(noutput_items);

get_tags_in_range(work_tags, 0, abs_N, end_N);


for (const auto& tag : work_tags)
{
if(tag.key == d_sob_key)
last_action = ACT_OPEN;
if(tag.key == d_eob_key)
last_action = ACT_CLOSE;
}
if(d_squelch_triggered && (last_action == ACT_OPEN) && !d_fp)
open_new_unlocked();
do_update(); // update: d_fp is read
if (!d_fp) { // drop output on the floor
return noutput_items;
Expand All @@ -321,9 +353,15 @@ int wavfile_sink_gqrx::work(int noutput_items,
close();
throw std::runtime_error("File I/O error.");
}
if(d_squelch_triggered && (last_action == ACT_CLOSE) && d_fp)
close_wav();

return nwritten;
}
void wavfile_sink_gqrx::set_squelch_triggered(const bool enabled)
{
d_squelch_triggered = enabled;
}

void wavfile_sink_gqrx::set_bits_per_sample(int bits_per_sample)
{
Expand Down
11 changes: 11 additions & 0 deletions src/interfaces/wav_sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,19 @@ enum wavfile_subformat_t {
};

#include <gnuradio/blocks/wavfile_sink.h>
#include <gnuradio/block.h>
#include <sndfile.h> // for SNDFILE
#include <thread>
#include <condition_variable>

class wavfile_sink_gqrx : public gr::blocks::wavfile_sink
{
private:
typedef enum{
ACT_NONE=0,
ACT_OPEN,
ACT_CLOSE
} sql_action;
wav_header_info d_h;
int d_bytes_per_sample_new;
bool d_append;
Expand Down Expand Up @@ -157,8 +163,13 @@ typedef std::function<void(std::string, bool)> rec_event_handler_t;
int work(int noutput_items,
gr_vector_const_void_star& input_items,
gr_vector_void_star& output_items) override;
void set_squelch_triggered(const bool enabled);
private:
bool open_unlocked(const char* filename);
int open_new_unlocked();
rec_event_handler_t d_rec_event;
bool d_squelch_triggered;
pmt::pmt_t d_sob_key, d_eob_key;
};

#endif /* GQRX_WAVFILE_SINK_C_H */
13 changes: 13 additions & 0 deletions src/qtgui/audio_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ void CAudioOptions::setUdpStereo(bool stereo)
ui->udpStereo->setChecked(stereo);
}

void CAudioOptions::setSquelchTriggered(bool value)
{
ui->squelchTriggered->setChecked(value);
}


void CAudioOptions::setFftSplit(int pct_2d)
{
Expand Down Expand Up @@ -212,6 +217,14 @@ void CAudioOptions::on_recDirEdit_textChanged(const QString &dir)
}
}

/**
*/
void CAudioOptions::on_squelchTriggered_stateChanged(int state)
{

emit newSquelchTriggered(state == Qt::Checked);
}

/** Slot called when the user clicks on the "Select" button. */
void CAudioOptions::on_recDirButton_clicked()
{
Expand Down
3 changes: 3 additions & 0 deletions src/qtgui/audio_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CAudioOptions : public QDialog
void setUdpHost(const QString &host);
void setUdpPort(int port);
void setUdpStereo(bool stereo);
void setSquelchTriggered(bool value);

void setFftSplit(int pct_2d);
int getFftSplit(void) const;
Expand Down Expand Up @@ -74,6 +75,7 @@ public slots:
void newUdpHost(const QString text);
void newUdpPort(int port);
void newUdpStereo(bool enabled);
void newSquelchTriggered(bool enabled);

private slots:
void on_fftSplitSlider_valueChanged(int value);
Expand All @@ -85,6 +87,7 @@ private slots:
void on_udpHost_textChanged(const QString &text);
void on_udpPort_valueChanged(int port);
void on_udpStereo_stateChanged(int state);
void on_squelchTriggered_stateChanged(int state);

private:
Ui::CAudioOptions *ui; /*!< The user interface widget. */
Expand Down
7 changes: 7 additions & 0 deletions src/qtgui/audio_options.ui
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,13 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="squelchTriggered">
<property name="text">
<string>Squelch triggered</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
Expand Down
50 changes: 22 additions & 28 deletions src/qtgui/dockaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ DockAudio::DockAudio(QWidget *parent) :
connect(audioOptions, SIGNAL(newUdpHost(QString)), this, SLOT(setNewUdpHost(QString)));
connect(audioOptions, SIGNAL(newUdpPort(int)), this, SLOT(setNewUdpPort(int)));
connect(audioOptions, SIGNAL(newUdpStereo(bool)), this, SLOT(setNewUdpStereo(bool)));
connect(audioOptions, SIGNAL(newSquelchTriggered(bool)), this, SLOT(setNewSquelchTriggered(bool)));

connect(ui->audioSpectrum, SIGNAL(pandapterRangeChanged(float,float)), audioOptions, SLOT(setPandapterSliderValues(float,float)));

Expand Down Expand Up @@ -151,32 +152,9 @@ void DockAudio::setFftFill(bool enabled)
ui->audioSpectrum->setFftFill(enabled);
}

/*! Public slot to trig audio recording by external events (e.g. satellite AOS).
*
* If a recording is already in progress we ignore the event.
*/
void DockAudio::startAudioRecorder(void)
bool DockAudio::getSquelchTriggered()
{
if (ui->audioRecButton->isChecked())
{
qDebug() << __func__ << "An audio recording is already in progress";
return;
}

// emulate a button click
ui->audioRecButton->click();
}

/*! Public slot to stop audio recording by external events (e.g. satellite LOS).
*
* The event is ignored if no audio recording is in progress.
*/
void DockAudio::stopAudioRecorder(void)
{
if (ui->audioRecButton->isChecked())
ui->audioRecButton->click(); // emulate a button click
else
qDebug() << __func__ << "No audio recording in progress";
return squelch_triggered;
}

/*! Public slot to set new RX frequency in Hz. */
Expand Down Expand Up @@ -373,6 +351,11 @@ void DockAudio::saveSettings(QSettings *settings)
else
settings->remove("udp_stereo");

if (squelch_triggered != false)
settings->setValue("squelch_triggered_recording", squelch_triggered);
else
settings->remove("squelch_triggered_recording");

settings->endGroup();
}

Expand Down Expand Up @@ -423,10 +406,12 @@ void DockAudio::readSettings(QSettings *settings)
if (!conv_ok)
udp_port = 7355;
udp_stereo = settings->value("udp_stereo", false).toBool();
squelch_triggered = settings->value("squelch_triggered_recording", false).toBool();

audioOptions->setUdpHost(udp_host);
audioOptions->setUdpPort(udp_port);
audioOptions->setUdpStereo(udp_stereo);
audioOptions->setSquelchTriggered(squelch_triggered);

settings->endGroup();
}
Expand Down Expand Up @@ -479,16 +464,25 @@ void DockAudio::audioRecStarted(const QString filename)
ui->audioRecLabel->setText(info.fileName());
ui->audioRecButton->setToolTip(tr("Stop audio recorder"));
ui->audioPlayButton->setEnabled(false); /* prevent playback while recording */
setAudioRecButtonState(true);
}

void DockAudio::audioRecStopped()
{
ui->audioRecLabel->setText("<i>DSP</i>");
ui->audioRecButton->setToolTip(tr("Start audio recorder"));
ui->audioPlayButton->setEnabled(true);
ui->audioRecLabel->setText("<i>DSP</i>");
ui->audioRecButton->setToolTip(tr("Start audio recorder"));
ui->audioPlayButton->setEnabled(true);
setAudioRecButtonState(false);
}


void DockAudio::setNewSquelchTriggered(bool enabled)
{
squelch_triggered = enabled;
ui->audioRecButton->setStyleSheet(enabled?"color: rgb(255,0,0)":"");
emit recSquelchTriggeredChanged(enabled);
}

void DockAudio::recordToggleShortcut() {
ui->audioRecButton->click();
}
Expand Down
Loading

0 comments on commit ecc58dd

Please sign in to comment.