mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-26 01:39:05 -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/fftfilt.cxx
|
||||
sdrbase/dsp/fftwindow.cpp
|
||||
sdrbase/dsp/filesink.cpp
|
||||
sdrbase/dsp/interpolator.cpp
|
||||
sdrbase/dsp/inthalfbandfilter.cpp
|
||||
sdrbase/dsp/lowpass.cpp
|
||||
@ -131,6 +132,7 @@ set(sdrbase_HEADERS
|
||||
include-gpl/dsp/fftfilt.h
|
||||
include-gpl/dsp/fftwengine.h
|
||||
include-gpl/dsp/fftwindow.h
|
||||
include-gpl/dsp/filesink.h
|
||||
include-gpl/dsp/gfft.h
|
||||
include-gpl/dsp/interpolator.h
|
||||
include-gpl/dsp/inthalfbandfilter.h
|
||||
|
@ -127,6 +127,7 @@ Done since the fork
|
||||
- Spectrum histogram clear
|
||||
- Trigger line display for all trigger modes
|
||||
- Coarse and fine trigger level sliders
|
||||
- Minimalist recording
|
||||
|
||||
=====
|
||||
To Do
|
||||
@ -134,7 +135,6 @@ To Do
|
||||
|
||||
- Level calibration
|
||||
- Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON)
|
||||
- recording capability
|
||||
- Tx channels for Rx/Tx boards like BladeRF
|
||||
- Enhance WFM (stereo, RDS?)
|
||||
- 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 Indicator;
|
||||
class SpectrumVis;
|
||||
class FileSink;
|
||||
class SampleSource;
|
||||
class PluginAPI;
|
||||
class PluginGUI;
|
||||
@ -76,6 +77,7 @@ private:
|
||||
Settings m_settings;
|
||||
|
||||
SpectrumVis* m_spectrumVis;
|
||||
FileSink *m_fileSink;
|
||||
|
||||
DSPEngine* m_dspEngine;
|
||||
|
||||
@ -83,6 +85,7 @@ private:
|
||||
int m_lastEngineState;
|
||||
|
||||
QLabel* m_sampleRateWidget;
|
||||
Indicator* m_recording;
|
||||
Indicator* m_engineIdle;
|
||||
Indicator* m_engineRunning;
|
||||
Indicator* m_engineError;
|
||||
@ -93,6 +96,7 @@ private:
|
||||
|
||||
int m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
std::string m_sampleFileName;
|
||||
|
||||
PluginManager* m_pluginManager;
|
||||
|
||||
@ -114,6 +118,8 @@ private slots:
|
||||
void updateStatus();
|
||||
void on_action_Start_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_iqImbalance_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->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->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 "dsp/dspengine.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "dsp/filesink.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "plugin/plugingui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "plugin/plugingui.h"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) :
|
||||
@ -49,6 +51,7 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
m_inputGUI(0),
|
||||
m_sampleRate(0),
|
||||
m_centerFrequency(0),
|
||||
m_sampleFileName(std::string("./test.sdriq")),
|
||||
m_pluginManager(new PluginManager(this, m_dspEngine))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@ -94,6 +97,9 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
m_spectrumVis = new SpectrumVis(ui->glSpectrum);
|
||||
m_dspEngine->addSink(m_spectrumVis);
|
||||
|
||||
m_fileSink = new FileSink();
|
||||
m_dspEngine->addSink(m_fileSink);
|
||||
|
||||
ui->glSpectrumGUI->setBuddies(m_dspEngine->getMessageQueue(), m_spectrumVis, ui->glSpectrum);
|
||||
|
||||
loadSettings();
|
||||
@ -119,7 +125,9 @@ MainWindow::~MainWindow()
|
||||
|
||||
m_pluginManager->freeAll();
|
||||
|
||||
m_dspEngine->removeSink(m_fileSink);
|
||||
m_dspEngine->removeSink(m_spectrumVis);
|
||||
delete m_fileSink;
|
||||
delete m_spectrumVis;
|
||||
delete m_pluginManager;
|
||||
|
||||
@ -221,6 +229,10 @@ void MainWindow::createStatusBar()
|
||||
m_sampleRateWidget->setToolTip(tr("Sample Rate"));
|
||||
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->setToolTip(tr("DSP engine is idle"));
|
||||
statusBar()->addPermanentWidget(m_engineIdle);
|
||||
@ -309,6 +321,8 @@ void MainWindow::handleMessages()
|
||||
updateCenterFreqDisplay();
|
||||
updateSampleRate();
|
||||
message->completed();
|
||||
std::cerr << "MainWindow::handleMessages: m_fileSink->configure" << std::endl;
|
||||
m_fileSink->configure(m_dspEngine->getMessageQueue(), m_sampleFileName, m_sampleRate, m_centerFrequency);
|
||||
} else {
|
||||
if(!m_pluginManager->handleMessage(message))
|
||||
message->completed();
|
||||
@ -363,6 +377,18 @@ void MainWindow::on_action_Stop_triggered()
|
||||
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)
|
||||
{
|
||||
m_settings.getCurrent()->setDCOffsetCorrection(checked);
|
||||
|
@ -75,6 +75,9 @@
|
||||
</property>
|
||||
<addaction name="action_Start"/>
|
||||
<addaction name="action_Stop"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_Start_Recording"/>
|
||||
<addaction name="action_Stop_Recording"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
<property name="title">
|
||||
@ -456,6 +459,22 @@
|
||||
<string>Loaded &Plugins...</string>
|
||||
</property>
|
||||
</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>channelDock</zorder>
|
||||
</widget>
|
||||
|
Loading…
Reference in New Issue
Block a user