mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 16:08:39 -05:00
Implemented minimalist recording
This commit is contained in:
parent
4649a1627d
commit
3dba9a0ea9
@ -63,6 +63,7 @@ set(sdrbase_SOURCES
|
|||||||
sdrbase/dsp/fftengine.cpp
|
sdrbase/dsp/fftengine.cpp
|
||||||
sdrbase/dsp/fftfilt.cxx
|
sdrbase/dsp/fftfilt.cxx
|
||||||
sdrbase/dsp/fftwindow.cpp
|
sdrbase/dsp/fftwindow.cpp
|
||||||
|
sdrbase/dsp/filesink.cpp
|
||||||
sdrbase/dsp/interpolator.cpp
|
sdrbase/dsp/interpolator.cpp
|
||||||
sdrbase/dsp/inthalfbandfilter.cpp
|
sdrbase/dsp/inthalfbandfilter.cpp
|
||||||
sdrbase/dsp/lowpass.cpp
|
sdrbase/dsp/lowpass.cpp
|
||||||
@ -131,6 +132,7 @@ set(sdrbase_HEADERS
|
|||||||
include-gpl/dsp/fftfilt.h
|
include-gpl/dsp/fftfilt.h
|
||||||
include-gpl/dsp/fftwengine.h
|
include-gpl/dsp/fftwengine.h
|
||||||
include-gpl/dsp/fftwindow.h
|
include-gpl/dsp/fftwindow.h
|
||||||
|
include-gpl/dsp/filesink.h
|
||||||
include-gpl/dsp/gfft.h
|
include-gpl/dsp/gfft.h
|
||||||
include-gpl/dsp/interpolator.h
|
include-gpl/dsp/interpolator.h
|
||||||
include-gpl/dsp/inthalfbandfilter.h
|
include-gpl/dsp/inthalfbandfilter.h
|
||||||
|
@ -127,6 +127,7 @@ Done since the fork
|
|||||||
- Spectrum histogram clear
|
- Spectrum histogram clear
|
||||||
- Trigger line display for all trigger modes
|
- Trigger line display for all trigger modes
|
||||||
- Coarse and fine trigger level sliders
|
- Coarse and fine trigger level sliders
|
||||||
|
- Minimalist recording
|
||||||
|
|
||||||
=====
|
=====
|
||||||
To Do
|
To Do
|
||||||
@ -134,7 +135,6 @@ To Do
|
|||||||
|
|
||||||
- Level calibration
|
- Level calibration
|
||||||
- Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON)
|
- Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON)
|
||||||
- recording capability
|
|
||||||
- Tx channels for Rx/Tx boards like BladeRF
|
- Tx channels for Rx/Tx boards like BladeRF
|
||||||
- Enhance WFM (stereo, RDS?)
|
- Enhance WFM (stereo, RDS?)
|
||||||
- Even more demods ...
|
- Even more demods ...
|
||||||
|
79
include-gpl/dsp/filesink.h
Normal file
79
include-gpl/dsp/filesink.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#ifndef INCLUDE_FILESINK_H
|
||||||
|
#define INCLUDE_FILESINK_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include "dsp/samplesink.h"
|
||||||
|
#include "util/export.h"
|
||||||
|
#include "util/message.h"
|
||||||
|
|
||||||
|
class MessageQueue;
|
||||||
|
|
||||||
|
class SDRANGELOVE_API FileSink : public SampleSink {
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Header
|
||||||
|
{
|
||||||
|
int sampleRate;
|
||||||
|
quint64 centerFrequency;
|
||||||
|
std::time_t startTimeStamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
FileSink();
|
||||||
|
~FileSink();
|
||||||
|
|
||||||
|
quint64 getByteCount() const { return m_byteCount; }
|
||||||
|
|
||||||
|
void configure(MessageQueue* msgQueue, const std::string& filename, int sampleRate, quint64 centerFrequency);
|
||||||
|
|
||||||
|
void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void startRecording();
|
||||||
|
void stopRecording();
|
||||||
|
bool handleMessage(Message* message);
|
||||||
|
static void readHeader(std::ifstream& samplefile, Header& header);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class MsgConfigureFileSink : public Message {
|
||||||
|
MESSAGE_CLASS_DECLARATION
|
||||||
|
|
||||||
|
public:
|
||||||
|
const std::string& getFileName() const { return m_fileName; }
|
||||||
|
int getSampleRate() const { return m_sampleRate; }
|
||||||
|
quint64 getCenterFrequency() const { return m_centerFrequency; }
|
||||||
|
|
||||||
|
static MsgConfigureFileSink* create(const std::string& fileName, int sampleRate, quint64 centerFrequency)
|
||||||
|
{
|
||||||
|
return new MsgConfigureFileSink(fileName, sampleRate, centerFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_fileName;
|
||||||
|
int m_sampleRate;
|
||||||
|
quint64 m_centerFrequency;
|
||||||
|
|
||||||
|
MsgConfigureFileSink(const std::string& fileName, int sampleRate, quint64 centerFrequency) :
|
||||||
|
Message(),
|
||||||
|
m_fileName(fileName),
|
||||||
|
m_sampleRate(sampleRate),
|
||||||
|
m_centerFrequency(centerFrequency)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string m_fileName;
|
||||||
|
int m_sampleRate;
|
||||||
|
quint64 m_centerFrequency;
|
||||||
|
bool m_recordOn;
|
||||||
|
bool m_recordStart;
|
||||||
|
std::ofstream m_sampleFile;
|
||||||
|
quint64 m_byteCount;
|
||||||
|
|
||||||
|
void handleConfigure(const std::string& fileName, int sampleRate, quint64 centerFrequency);
|
||||||
|
void writeHeader();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDE_FILESINK_H
|
@ -31,6 +31,7 @@ class AudioDeviceInfo;
|
|||||||
class DSPEngine;
|
class DSPEngine;
|
||||||
class Indicator;
|
class Indicator;
|
||||||
class SpectrumVis;
|
class SpectrumVis;
|
||||||
|
class FileSink;
|
||||||
class SampleSource;
|
class SampleSource;
|
||||||
class PluginAPI;
|
class PluginAPI;
|
||||||
class PluginGUI;
|
class PluginGUI;
|
||||||
@ -76,6 +77,7 @@ private:
|
|||||||
Settings m_settings;
|
Settings m_settings;
|
||||||
|
|
||||||
SpectrumVis* m_spectrumVis;
|
SpectrumVis* m_spectrumVis;
|
||||||
|
FileSink *m_fileSink;
|
||||||
|
|
||||||
DSPEngine* m_dspEngine;
|
DSPEngine* m_dspEngine;
|
||||||
|
|
||||||
@ -83,6 +85,7 @@ private:
|
|||||||
int m_lastEngineState;
|
int m_lastEngineState;
|
||||||
|
|
||||||
QLabel* m_sampleRateWidget;
|
QLabel* m_sampleRateWidget;
|
||||||
|
Indicator* m_recording;
|
||||||
Indicator* m_engineIdle;
|
Indicator* m_engineIdle;
|
||||||
Indicator* m_engineRunning;
|
Indicator* m_engineRunning;
|
||||||
Indicator* m_engineError;
|
Indicator* m_engineError;
|
||||||
@ -93,6 +96,7 @@ private:
|
|||||||
|
|
||||||
int m_sampleRate;
|
int m_sampleRate;
|
||||||
quint64 m_centerFrequency;
|
quint64 m_centerFrequency;
|
||||||
|
std::string m_sampleFileName;
|
||||||
|
|
||||||
PluginManager* m_pluginManager;
|
PluginManager* m_pluginManager;
|
||||||
|
|
||||||
@ -114,6 +118,8 @@ private slots:
|
|||||||
void updateStatus();
|
void updateStatus();
|
||||||
void on_action_Start_triggered();
|
void on_action_Start_triggered();
|
||||||
void on_action_Stop_triggered();
|
void on_action_Stop_triggered();
|
||||||
|
void on_action_Start_Recording_triggered();
|
||||||
|
void on_action_Stop_Recording_triggered();
|
||||||
void on_dcOffset_toggled(bool checked);
|
void on_dcOffset_toggled(bool checked);
|
||||||
void on_iqImbalance_toggled(bool checked);
|
void on_iqImbalance_toggled(bool checked);
|
||||||
void on_action_View_Fullscreen_toggled(bool checked);
|
void on_action_View_Fullscreen_toggled(bool checked);
|
||||||
|
@ -140,7 +140,7 @@ void BladerfGui::displaySettings()
|
|||||||
ui->vga1Text->setText(tr("%1dB").arg(m_settings.m_vga1));
|
ui->vga1Text->setText(tr("%1dB").arg(m_settings.m_vga1));
|
||||||
ui->vga1->setValue(m_settings.m_vga1);
|
ui->vga1->setValue(m_settings.m_vga1);
|
||||||
|
|
||||||
ui->vga2Text->setText(tr("%1dBS").arg(m_settings.m_vga2));
|
ui->vga2Text->setText(tr("%1dB").arg(m_settings.m_vga2));
|
||||||
ui->vga2->setValue(m_settings.m_vga2);
|
ui->vga2->setValue(m_settings.m_vga2);
|
||||||
|
|
||||||
ui->xb200->setCurrentIndex(getXb200Index(m_settings.m_xb200, m_settings.m_xb200Path, m_settings.m_xb200Filter));
|
ui->xb200->setCurrentIndex(getXb200Index(m_settings.m_xb200, m_settings.m_xb200Path, m_settings.m_xb200Filter));
|
||||||
|
126
sdrbase/dsp/filesink.cpp
Normal file
126
sdrbase/dsp/filesink.cpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#include "dsp/filesink.h"
|
||||||
|
#include "util/simpleserializer.h"
|
||||||
|
#include "util/messagequeue.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
MESSAGE_CLASS_DEFINITION(FileSink::MsgConfigureFileSink, Message)
|
||||||
|
|
||||||
|
FileSink::FileSink() :
|
||||||
|
SampleSink(),
|
||||||
|
m_fileName(std::string("test.sdriq")),
|
||||||
|
m_sampleRate(0),
|
||||||
|
m_centerFrequency(0),
|
||||||
|
m_recordOn(false),
|
||||||
|
m_recordStart(false),
|
||||||
|
m_byteCount(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSink::~FileSink()
|
||||||
|
{
|
||||||
|
stopRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::configure(MessageQueue* msgQueue, const std::string& filename, int sampleRate, quint64 centerFrequency)
|
||||||
|
{
|
||||||
|
Message* cmd = MsgConfigureFileSink::create(filename, sampleRate, centerFrequency);
|
||||||
|
cmd->submit(msgQueue, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly)
|
||||||
|
{
|
||||||
|
// if no recording is active, send the samples to /dev/null
|
||||||
|
if(!m_recordOn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (begin < end) // if there is something to put out
|
||||||
|
{
|
||||||
|
if (m_recordStart)
|
||||||
|
{
|
||||||
|
writeHeader();
|
||||||
|
m_recordStart = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sampleFile.write(reinterpret_cast<const char*>(&*(begin)), (end - begin)*sizeof(Sample));
|
||||||
|
m_byteCount += end - begin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::start()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::stop()
|
||||||
|
{
|
||||||
|
stopRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::startRecording()
|
||||||
|
{
|
||||||
|
if (!m_sampleFile.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "FileSink::startRecording" << std::endl;
|
||||||
|
m_sampleFile.open(m_fileName.c_str(), std::ios::binary);
|
||||||
|
m_recordOn = true;
|
||||||
|
m_recordStart = true;
|
||||||
|
m_byteCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::stopRecording()
|
||||||
|
{
|
||||||
|
if (m_sampleFile.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "FileSink::stopRecording" << std::endl;
|
||||||
|
m_sampleFile.close();
|
||||||
|
m_recordOn = false;
|
||||||
|
m_recordStart = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileSink::handleMessage(Message* message)
|
||||||
|
{
|
||||||
|
if (MsgConfigureFileSink::match(message))
|
||||||
|
{
|
||||||
|
MsgConfigureFileSink* conf = (MsgConfigureFileSink*) message;
|
||||||
|
handleConfigure(conf->getFileName(), conf->getSampleRate(), conf->getCenterFrequency());
|
||||||
|
std::cerr << "FileSink::handleMessage:"
|
||||||
|
<< " fileName: " << m_fileName
|
||||||
|
<< " sampleRate: " << m_sampleRate
|
||||||
|
<< " centerFrequency: " << m_centerFrequency << std::endl;
|
||||||
|
message->completed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::handleConfigure(const std::string& fileName, int sampleRate, quint64 centerFrequency)
|
||||||
|
{
|
||||||
|
if ((fileName != m_fileName) || (m_sampleRate != sampleRate) || (m_centerFrequency != centerFrequency))
|
||||||
|
{
|
||||||
|
stopRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fileName = fileName;
|
||||||
|
m_sampleRate = sampleRate;
|
||||||
|
m_centerFrequency = centerFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::writeHeader()
|
||||||
|
{
|
||||||
|
m_sampleFile.write((const char *) &m_sampleRate, sizeof(int));
|
||||||
|
m_sampleFile.write((const char *) &m_centerFrequency, sizeof(quint64));
|
||||||
|
std::time_t ts = time(0);
|
||||||
|
m_sampleFile.write((const char *) &ts, sizeof(std::time_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSink::readHeader(std::ifstream& sampleFile, Header& header)
|
||||||
|
{
|
||||||
|
sampleFile.read((char *) &(header.sampleRate), sizeof(int));
|
||||||
|
sampleFile.read((char *) &(header.centerFrequency), sizeof(quint64));
|
||||||
|
sampleFile.read((char *) &(header.startTimeStamp), sizeof(std::time_t));
|
||||||
|
}
|
@ -30,11 +30,13 @@
|
|||||||
#include "gui/rollupwidget.h"
|
#include "gui/rollupwidget.h"
|
||||||
#include "dsp/dspengine.h"
|
#include "dsp/dspengine.h"
|
||||||
#include "dsp/spectrumvis.h"
|
#include "dsp/spectrumvis.h"
|
||||||
|
#include "dsp/filesink.h"
|
||||||
#include "dsp/dspcommands.h"
|
#include "dsp/dspcommands.h"
|
||||||
#include "plugin/plugingui.h"
|
#include "plugin/plugingui.h"
|
||||||
#include "plugin/pluginapi.h"
|
#include "plugin/pluginapi.h"
|
||||||
#include "plugin/plugingui.h"
|
#include "plugin/plugingui.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget* parent) :
|
MainWindow::MainWindow(QWidget* parent) :
|
||||||
@ -49,6 +51,7 @@ MainWindow::MainWindow(QWidget* parent) :
|
|||||||
m_inputGUI(0),
|
m_inputGUI(0),
|
||||||
m_sampleRate(0),
|
m_sampleRate(0),
|
||||||
m_centerFrequency(0),
|
m_centerFrequency(0),
|
||||||
|
m_sampleFileName(std::string("./test.sdriq")),
|
||||||
m_pluginManager(new PluginManager(this, m_dspEngine))
|
m_pluginManager(new PluginManager(this, m_dspEngine))
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
@ -94,6 +97,9 @@ MainWindow::MainWindow(QWidget* parent) :
|
|||||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||||
m_dspEngine->addSink(m_spectrumVis);
|
m_dspEngine->addSink(m_spectrumVis);
|
||||||
|
|
||||||
|
m_fileSink = new FileSink();
|
||||||
|
m_dspEngine->addSink(m_fileSink);
|
||||||
|
|
||||||
ui->glSpectrumGUI->setBuddies(m_dspEngine->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
ui->glSpectrumGUI->setBuddies(m_dspEngine->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
@ -119,7 +125,9 @@ MainWindow::~MainWindow()
|
|||||||
|
|
||||||
m_pluginManager->freeAll();
|
m_pluginManager->freeAll();
|
||||||
|
|
||||||
|
m_dspEngine->removeSink(m_fileSink);
|
||||||
m_dspEngine->removeSink(m_spectrumVis);
|
m_dspEngine->removeSink(m_spectrumVis);
|
||||||
|
delete m_fileSink;
|
||||||
delete m_spectrumVis;
|
delete m_spectrumVis;
|
||||||
delete m_pluginManager;
|
delete m_pluginManager;
|
||||||
|
|
||||||
@ -221,6 +229,10 @@ void MainWindow::createStatusBar()
|
|||||||
m_sampleRateWidget->setToolTip(tr("Sample Rate"));
|
m_sampleRateWidget->setToolTip(tr("Sample Rate"));
|
||||||
statusBar()->addPermanentWidget(m_sampleRateWidget);
|
statusBar()->addPermanentWidget(m_sampleRateWidget);
|
||||||
|
|
||||||
|
m_recording = new Indicator(tr("Rec"), this);
|
||||||
|
m_recording->setToolTip(tr("Recording"));
|
||||||
|
statusBar()->addPermanentWidget(m_recording);
|
||||||
|
|
||||||
m_engineIdle = new Indicator(tr("Idle"), this);
|
m_engineIdle = new Indicator(tr("Idle"), this);
|
||||||
m_engineIdle->setToolTip(tr("DSP engine is idle"));
|
m_engineIdle->setToolTip(tr("DSP engine is idle"));
|
||||||
statusBar()->addPermanentWidget(m_engineIdle);
|
statusBar()->addPermanentWidget(m_engineIdle);
|
||||||
@ -309,6 +321,8 @@ void MainWindow::handleMessages()
|
|||||||
updateCenterFreqDisplay();
|
updateCenterFreqDisplay();
|
||||||
updateSampleRate();
|
updateSampleRate();
|
||||||
message->completed();
|
message->completed();
|
||||||
|
std::cerr << "MainWindow::handleMessages: m_fileSink->configure" << std::endl;
|
||||||
|
m_fileSink->configure(m_dspEngine->getMessageQueue(), m_sampleFileName, m_sampleRate, m_centerFrequency);
|
||||||
} else {
|
} else {
|
||||||
if(!m_pluginManager->handleMessage(message))
|
if(!m_pluginManager->handleMessage(message))
|
||||||
message->completed();
|
message->completed();
|
||||||
@ -363,6 +377,18 @@ void MainWindow::on_action_Stop_triggered()
|
|||||||
m_dspEngine->stopAcquistion();
|
m_dspEngine->stopAcquistion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_action_Start_Recording_triggered()
|
||||||
|
{
|
||||||
|
m_recording->setColor(Qt::red);
|
||||||
|
m_fileSink->startRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_action_Stop_Recording_triggered()
|
||||||
|
{
|
||||||
|
m_recording->setColor(Qt::gray);
|
||||||
|
m_fileSink->stopRecording();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::on_dcOffset_toggled(bool checked)
|
void MainWindow::on_dcOffset_toggled(bool checked)
|
||||||
{
|
{
|
||||||
m_settings.getCurrent()->setDCOffsetCorrection(checked);
|
m_settings.getCurrent()->setDCOffsetCorrection(checked);
|
||||||
|
@ -75,6 +75,9 @@
|
|||||||
</property>
|
</property>
|
||||||
<addaction name="action_Start"/>
|
<addaction name="action_Start"/>
|
||||||
<addaction name="action_Stop"/>
|
<addaction name="action_Stop"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Start_Recording"/>
|
||||||
|
<addaction name="action_Stop_Recording"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_View">
|
<widget class="QMenu" name="menu_View">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -456,6 +459,22 @@
|
|||||||
<string>Loaded &Plugins...</string>
|
<string>Loaded &Plugins...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Start_Recording">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start Recording</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F7</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Stop_Recording">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop Recording</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>F8</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<zorder>presetDock</zorder>
|
<zorder>presetDock</zorder>
|
||||||
<zorder>channelDock</zorder>
|
<zorder>channelDock</zorder>
|
||||||
</widget>
|
</widget>
|
||||||
|
Loading…
Reference in New Issue
Block a user