1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-11-02 12:40:41 -05:00

SigMF file input: first commit

This commit is contained in:
f4exb 2020-06-14 23:09:19 +02:00
parent fff2855080
commit baf57d0723
23 changed files with 6742 additions and 0 deletions

View File

@ -58,4 +58,8 @@ if(ENABLE_SOAPYSDR AND SOAPYSDR_FOUND)
add_subdirectory(soapysdrinput)
endif()
if(LIBSIGMF_FOUND)
add_subdirectory(sigmffileinput)
endif()
add_subdirectory(kiwisdr)

View File

@ -0,0 +1,61 @@
project(sigmffileinput)
set(sigmffileinput_SOURCES
sigmffileinput.cpp
sigmffileinputplugin.cpp
sigmffileinputthread.cpp
sigmffileinputsettings.cpp
sigmffileinputwebapiadapter.cpp
)
set(sigmffileinput_HEADERS
sigmffileinput.h
sigmffileinputplugin.h
sigmffileinputthread.h
sigmffileinputsettings.h
sigmffileinputwebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${LIBSIGMF_INCLUDE_DIR}
)
if(NOT SERVER_MODE)
set(sigmffileinput_SOURCES
${sigmffileinput_SOURCES}
sigmffileinputgui.cpp
recordinfodialog.cpp
sigmffileinputgui.ui
recordinfodialog.ui
)
set(sigmffileinput_HEADERS
${sigmffileinput_HEADERS}
sigmffileinputgui.h
recordinfodialog.h
)
set(TARGET_NAME inputsigmffileinput)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME inputsigmffileinputsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${sigmffileinput_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
${LIBSIGMF_LIBRARIES}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,113 @@
<h1>File input plugin</h1>
<h2>Introduction</h2>
This plugin reads a file of I/Q samples that have been previously saved with the file record button of other sampling source devices. The file starts with a 32 byte header of all unsigned integer of various sizes containing meta data:
<table>
<tr>
<th>Displ.</th>
<th>Bytes</th>
<th>Description</th>
</tr>
<tr>
<td>0</td>
<td>4</td>
<td>Sample rate in S/s</td>
</tr>
<tr>
<td>4</td>
<td>8</td>
<td>Center frequency in Hz</td>
</tr>
<tr>
<td>12</td>
<td>8</td>
<td>Unix epoch (timestamp) of start</td>
</tr>
<tr>
<td>20</td>
<td>4</td>
<td>Sample size (16 or 24 bits)</td>
</tr>
<tr>
<td>24</td>
<td>4</td>
<td>Filler with zeroes</td>
</tr>
<tr>
<td>28</td>
<td>4</td>
<td>CRC32 of the previous 28 bytes</td>
</tr>
</table>
The header takes an integer number of 16 (4 bytes) or 24 (8 bytes) bits samples. To calculate CRC it is assumed that bytes are in little endian order.
<h2>Interface</h2>
![File input plugin GUI](../../../doc/img/FileInput_plugin.png)
<h3>1: Start/Stop</h3>
Device start / stop button.
- Blue triangle icon: ready to be started
- Green square icon: currently running and can be stopped
- Magenta (or pink) square icon: an error occurred. The file may not be found or this can be a header CRC error or the file is too small (less than the header length). You may stop and choose another file.
<h3>2: Stream sample rate</h3>
Baseband I/Q sample rate in kS/s. This is the sample rate present in the header.
<h3>3: Frequency</h3>
This is the center frequency of reception in kHz when the record was taken and written in the header.
<h3>4: Open file</h3>
Opens a file dialog to select the input file. It expects a default extension of `.sdriq`. This button is disabled when the stream is running. You need to pause (button 11) to make it active and thus be able to select another file.
<h3>5: File path</h3>
Absolute path of the file being read
<h3>6: File recorded sample rate</h3>
Sample rate of the record in kS/s as written in the header. The reading process is based on this sample rate.
<h3>7: Sample size</h3>
This is the sample size in bits as written in the header. The reading process is based on this sample size.
<h3>8: CRC indicator</h3>
Indicates if the header block CRC check has succeeded (green) or failed (red) or undetermined yet (grey). If the header is corrupted you may try to reconstruct a valid header using the `rescuesdriq` utility in the folder with the same name. See the [readme](../../../rescuesdriq/readme.md) for details.
<h3>9: Current timestamp</h3>
This is the timestamp of the current pointer in the file based on the start time, number of samples read and sample rate.
<h3>10: Loop</h3>
Use this button to read in a loop or read only once
<h3>11: Play/pause</h3>
This is the play/pause button
<h3>12: Playback acceleration</h3>
Use this combo to select play back acceleration to values of 1 (no acceleration), 2, 5, 10, 20, 50, 100, 200, 500, 1k (1000) times. This is useful on long recordings used in conjunction with the spectrum "Max" averaging mode in order to see the waterfall over a long period. Thus the waterfall will be filled much faster.
&#9758; Note that this control is enabled only in paused mode.
&#9888; The result when using channel plugins with acceleration is unpredictable. Use this tool to locate your signal of interest then play at normal speed to get proper demodulation or decoding.
<h3>13: Relative timestamp and record length</h3>
Left is the relative timestamp of the current pointer from the start of the record. Right is the total record time.
<h3>14: Current pointer gauge</h3>
This represents the position of the current pointer position in the complete recording. It can be used it paused mode to position the current pointer by moving the slider.

View File

@ -0,0 +1,15 @@
#include "recordinfodialog.h"
#include "ui_recordinfodialog.h"
RecordInfoDialog::RecordInfoDialog(const QString& text, QWidget* parent) :
QDialog(parent),
ui(new Ui::RecordInfoDialog)
{
ui->setupUi(this);
ui->infoText->setText(text);
}
RecordInfoDialog::~RecordInfoDialog()
{
delete ui;
}

View File

@ -0,0 +1,23 @@
#ifndef INCLUDE_ABOUTDIALOG_H
#define INCLUDE_ABOUTDIALOG_H
#include <QDialog>
#include "export.h"
namespace Ui {
class RecordInfoDialog;
}
class SDRGUI_API RecordInfoDialog : public QDialog {
Q_OBJECT
public:
explicit RecordInfoDialog(const QString& text, QWidget* parent = nullptr);
~RecordInfoDialog();
private:
Ui::RecordInfoDialog* ui;
};
#endif // INCLUDE_ABOUTDIALOG_H

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RecordInfoDialog</class>
<widget class="QDialog" name="RecordInfoDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>404</width>
<height>300</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>File information</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="header">
<property name="text">
<string>SigMF file information (meta data)</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="infoText">
<property name="minimumSize">
<size>
<width>398</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
</font>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RecordInfoDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RecordInfoDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,216 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEDATA_H
#define INCLUDE_SIGMFFILEDATA_H
#include <QString>
struct SigMFFileDataType
{
bool m_complex;
bool m_floatingPoint;
bool m_signed;
bool m_bigEndian;
bool m_swapIQ;
int m_sampleBits;
SigMFFileDataType() :
m_complex(true),
m_floatingPoint(false),
m_signed(true),
m_bigEndian(false),
m_swapIQ(false),
m_sampleBits(32)
{}
SigMFFileDataType(const SigMFFileDataType& other) :
m_complex(other.m_complex),
m_floatingPoint(other.m_floatingPoint),
m_signed(other.m_signed),
m_bigEndian(other.m_bigEndian),
m_swapIQ(other.m_swapIQ),
m_sampleBits(other.m_sampleBits)
{}
SigMFFileDataType& operator=(const SigMFFileDataType& t)
{
// Check for self assignment
if (this != &t)
{
m_complex = t.m_complex;
m_floatingPoint = t.m_floatingPoint;
m_signed = t.m_signed;
m_bigEndian = t.m_bigEndian;
m_swapIQ = t.m_swapIQ;
m_sampleBits = t.m_sampleBits;
}
return *this;
}
explicit operator SigMFFileDataType() const {
return SigMFFileDataType{static_cast<SigMFFileDataType>(*this)};
}
};
struct SigMFFileMetaInfo
{
// core
QString m_dataTypeStr;
SigMFFileDataType m_dataType;
uint64_t m_totalSamples;
uint64_t m_totalTimeMs;
double m_coreSampleRate;
QString m_sigMFVersion;
QString m_sha512;
unsigned int m_offset;
QString m_description;
QString m_author;
QString m_metaDOI;
QString m_dataDOI;
QString m_recorder;
QString m_license;
QString m_hw;
// sdrangel
QString m_sdrAngelVersion;
QString m_qtVersion;
int m_rxBits;
QString m_arch;
QString m_os;
// lists
unsigned int m_nbCaptures;
unsigned int m_nbAnnotations;
SigMFFileMetaInfo()
{}
SigMFFileMetaInfo(const SigMFFileMetaInfo& other) :
m_dataTypeStr(other.m_dataTypeStr),
m_dataType(other.m_dataType),
m_totalSamples(other.m_totalSamples),
m_totalTimeMs(other.m_totalTimeMs),
m_coreSampleRate(other.m_coreSampleRate),
m_sigMFVersion(other.m_sigMFVersion),
m_sha512(other.m_sha512),
m_offset(other.m_offset),
m_description(other.m_description),
m_author(other.m_author),
m_metaDOI(other.m_metaDOI),
m_dataDOI(other.m_dataDOI),
m_recorder(other.m_recorder),
m_license(other.m_license),
m_hw(other.m_hw),
m_sdrAngelVersion(other.m_sdrAngelVersion),
m_qtVersion(other.m_qtVersion),
m_rxBits(other.m_rxBits),
m_arch(other.m_arch),
m_os(other.m_os),
m_nbCaptures(other.m_nbCaptures),
m_nbAnnotations(other.m_nbAnnotations)
{}
SigMFFileMetaInfo& operator=(const SigMFFileMetaInfo& t)
{
// Check for self assignment
if (this != &t)
{
m_dataTypeStr = t.m_dataTypeStr;
m_dataType = t.m_dataType;
m_totalSamples = t.m_totalSamples;
m_totalTimeMs = t.m_totalTimeMs;
m_coreSampleRate = t.m_coreSampleRate;
m_sigMFVersion = t.m_sigMFVersion;
m_sha512 = t.m_sha512;
m_offset = t.m_offset;
m_description = t.m_description;
m_author = t.m_author;
m_metaDOI = t.m_metaDOI;
m_dataDOI = t.m_dataDOI;
m_recorder = t.m_recorder;
m_license = t.m_license;
m_hw = t.m_hw;
m_sdrAngelVersion = t.m_sdrAngelVersion;
m_qtVersion = t.m_qtVersion;
m_rxBits = t.m_rxBits;
m_arch = t.m_arch;
m_os = t.m_os;
m_nbCaptures = t.m_nbCaptures;
m_nbAnnotations = t.m_nbAnnotations;
}
return *this;
}
explicit operator SigMFFileMetaInfo() const {
return SigMFFileMetaInfo{static_cast<SigMFFileMetaInfo>(*this)};
}
};
struct SigMFFileCapture
{
uint64_t m_tsms; //!< Unix timestamp in milliseconds
uint64_t m_centerFrequency; //!< Center frequency in Hz
uint64_t m_sampleStart; //!< Sample index at which capture start
uint64_t m_length; //!< Length of capture in samples
uint64_t m_cumulativeTime; //!< Time since beginning of record (millisecond timestamp) at start
unsigned int m_sampleRate; //!< sdrangel extension - sample rate for this capture
SigMFFileCapture() :
m_tsms(0),
m_centerFrequency(0),
m_sampleStart(0),
m_length(0),
m_cumulativeTime(0),
m_sampleRate(1)
{}
SigMFFileCapture(const SigMFFileCapture& other) :
m_tsms(other.m_tsms),
m_centerFrequency(other.m_centerFrequency),
m_sampleStart(other.m_sampleStart),
m_length(other.m_length),
m_cumulativeTime(other.m_cumulativeTime),
m_sampleRate(other.m_sampleRate)
{}
SigMFFileCapture& operator=(const SigMFFileCapture& t)
{
// Check for self assignment
if (this != &t)
{
m_tsms = t.m_tsms;
m_centerFrequency = t.m_centerFrequency;
m_sampleStart = t.m_sampleStart;
m_length = t.m_length;
m_cumulativeTime = t.m_cumulativeTime;
m_sampleRate = t.m_sampleRate;
}
return *this;
}
explicit operator SigMFFileCapture() const {
return SigMFFileCapture{static_cast<SigMFFileCapture>(*this)};
}
explicit operator SigMFFileCapture() {
return SigMFFileCapture{static_cast<const SigMFFileCapture>(*this)};
}
};
#endif // INCLUDE_SIGMFFILEDATA_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,489 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEINPUT_H
#define INCLUDE_SIGMFFILEINPUT_H
#include <ctime>
#include <iostream>
#include <fstream>
#include <QString>
#include <QByteArray>
#include <QTimer>
#include <QNetworkRequest>
#include "dsp/sigmf_forward.h"
#include "dsp/devicesamplesource.h"
#include "sigmffileinputsettings.h"
#include "sigmffiledata.h"
class QNetworkAccessManager;
class QNetworkReply;
class SigMFFileInputThread;
class DeviceAPI;
class SigMFFileInput : public DeviceSampleSource {
Q_OBJECT
public:
/**
* Communicate settings
*/
class MsgConfigureSigMFFileInput : public Message {
MESSAGE_CLASS_DECLARATION
public:
const SigMFFileInputSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureSigMFFileInput* create(const SigMFFileInputSettings& settings, bool force)
{
return new MsgConfigureSigMFFileInput(settings, force);
}
private:
SigMFFileInputSettings m_settings;
bool m_force;
MsgConfigureSigMFFileInput(const SigMFFileInputSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
/**
* Start/stop track play
*/
class MsgConfigureTrackWork : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool isWorking() const { return m_working; }
static MsgConfigureTrackWork* create(bool working) {
return new MsgConfigureTrackWork(working);
}
private:
bool m_working;
MsgConfigureTrackWork(bool working) :
Message(),
m_working(working)
{ }
};
/**
* Start/stop full file play
*/
class MsgConfigureFileWork : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool isWorking() const { return m_working; }
static MsgConfigureFileWork* create(bool working) {
return new MsgConfigureFileWork(working);
}
private:
bool m_working;
MsgConfigureFileWork(bool working) :
Message(),
m_working(working)
{ }
};
/**
* Move to track
*/
class MsgConfigureTrackIndex : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTrackIndex() const { return m_trackIndex; }
static MsgConfigureTrackIndex* create(int trackIndex)
{
return new MsgConfigureTrackIndex(trackIndex);
}
private:
int m_trackIndex;
MsgConfigureTrackIndex(int trackIndex) :
Message(),
m_trackIndex(trackIndex)
{ }
};
/**
* Seek position in track
*/
class MsgConfigureTrackSeek : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getMillis() const { return m_seekMillis; }
static MsgConfigureTrackSeek* create(int seekMillis)
{
return new MsgConfigureTrackSeek(seekMillis);
}
protected:
int m_seekMillis; //!< millis of seek position from the beginning 0..1000
MsgConfigureTrackSeek(int seekMillis) :
Message(),
m_seekMillis(seekMillis)
{ }
};
/**
* Seek position in full file
*/
class MsgConfigureFileSeek : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getMillis() const { return m_seekMillis; }
static MsgConfigureFileSeek* create(int seekMillis)
{
return new MsgConfigureFileSeek(seekMillis);
}
protected:
int m_seekMillis; //!< millis of seek position from the beginning 0..1000
MsgConfigureFileSeek(int seekMillis) :
Message(),
m_seekMillis(seekMillis)
{ }
};
/**
* Pull stram timing information
*/
class MsgConfigureFileInputStreamTiming : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgConfigureFileInputStreamTiming* create()
{
return new MsgConfigureFileInputStreamTiming();
}
private:
MsgConfigureFileInputStreamTiming() :
Message()
{ }
};
/**
* Start/stop plugin
*/
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgStartStop* create(bool startStop) {
return new MsgStartStop(startStop);
}
protected:
bool m_startStop;
MsgStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
/**
* Push start/stop information
*/
class MsgReportStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgReportStartStop* create(bool startStop)
{
return new MsgReportStartStop(startStop);
}
protected:
bool m_startStop;
MsgReportStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
/**
* Push meta data information
*/
class MsgReportMetaData : public Message {
MESSAGE_CLASS_DECLARATION
public:
const SigMFFileMetaInfo& getMetaInfo() const { return m_metaInfo; }
const QList<SigMFFileCapture>& getCaptures() { return m_captures; }
static MsgReportMetaData* create(const SigMFFileMetaInfo& metaInfo, const QList<SigMFFileCapture>& captures) {
return new MsgReportMetaData(metaInfo, captures);
}
protected:
SigMFFileMetaInfo m_metaInfo;
QList<SigMFFileCapture> m_captures;
MsgReportMetaData(const SigMFFileMetaInfo& metaInfo, const QList<SigMFFileCapture>& captures) :
Message(),
m_metaInfo(metaInfo),
m_captures(captures)
{}
};
/**
* Push track change
*/
class MsgReportTrackChange : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTrackIndex() const { return m_trackIndex; }
static MsgReportTrackChange* create(int trackIndex) {
return new MsgReportTrackChange(trackIndex);
}
private:
int m_trackIndex;
MsgReportTrackChange(int trackIndex) :
Message(),
m_trackIndex(trackIndex)
{ }
};
/**
* Push stream timing information
*/
class MsgReportFileInputStreamTiming : public Message {
MESSAGE_CLASS_DECLARATION
public:
quint64 getSamplesCount() const { return m_samplesCount; }
quint64 getTrackSamplesCount() const { return m_trackSamplesCount; }
quint64 getTrackTimeStart() const { return m_trackTimeStart; }
int getTrackNumber() const { return m_trackNumber; }
static MsgReportFileInputStreamTiming* create(
quint64 samplesCount,
quint64 trackSamplesCount,
quint64 trackTimeStart,
int trackNumber
)
{
return new MsgReportFileInputStreamTiming(
samplesCount,
trackSamplesCount,
trackTimeStart,
trackNumber
);
}
protected:
quint64 m_samplesCount;
quint64 m_trackSamplesCount;
quint64 m_trackTimeStart;
int m_trackNumber;
MsgReportFileInputStreamTiming(
quint64 samplesCount,
quint64 trackSamplesCount,
quint64 trackTimeStart,
int trackNumber
) :
Message(),
m_samplesCount(samplesCount),
m_trackSamplesCount(trackSamplesCount),
m_trackTimeStart(trackTimeStart),
m_trackNumber(trackNumber)
{ }
};
/**
* Push CRC (SHA512) information
*/
class MsgReportCRC : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool isOK() const { return m_ok; }
static MsgReportCRC* create(bool ok) {
return new MsgReportCRC(ok);
}
protected:
bool m_ok;
MsgReportCRC(bool ok) :
Message(),
m_ok(ok)
{ }
};
/**
* Push record total check information
*/
class MsgReportTotalSamplesCheck : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool isOK() const { return m_ok; }
static MsgReportTotalSamplesCheck* create(bool ok) {
return new MsgReportTotalSamplesCheck(ok);
}
protected:
bool m_ok;
MsgReportTotalSamplesCheck(bool ok) :
Message(),
m_ok(ok)
{ }
};
SigMFFileInput(DeviceAPI *deviceAPI);
virtual ~SigMFFileInput();
virtual void destroy();
virtual void init();
virtual bool start();
virtual void stop();
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const;
virtual void setSampleRate(int sampleRate) { (void) sampleRate; }
virtual quint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
quint64 getStartingTimeStamp() const;
virtual bool handleMessage(const Message& message);
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
virtual int webapiRunGet(
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiRun(
bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGDeviceReport& response,
QString& errorMessage);
static void webapiFormatDeviceSettings(
SWGSDRangel::SWGDeviceSettings& response,
const SigMFFileInputSettings& settings);
static void webapiUpdateDeviceSettings(
SigMFFileInputSettings& settings,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response);
private:
DeviceAPI *m_deviceAPI;
QMutex m_mutex;
SigMFFileInputSettings m_settings;
std::ifstream m_metaStream;
std::ifstream m_dataStream;
SigMFFileMetaInfo m_metaInfo;
QList<SigMFFileCapture> m_captures;
std::vector<uint64_t> m_captureStarts;
bool m_trackMode;
int m_currentTrackIndex;
bool m_recordOpen;
QString m_recordSummary;
SigMFFileInputThread* m_fileInputThread;
QString m_deviceDescription;
int m_sampleRate;
unsigned int m_sampleBytes;
quint64 m_centerFrequency;
quint64 m_recordLength; //!< record length in seconds computed from file size
quint64 m_startingTimeStamp;
QTimer m_masterTimer;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
bool openFileStreams(const QString& fileName);
void extractMeta(
sigmf::SigMF<sigmf::Global<core::DescrT, sdrangel::DescrT>,
sigmf::Capture<core::DescrT, sdrangel::DescrT>,
sigmf::Annotation<core::DescrT> >* metaRecord,
uint64_t dataFileSize
);
void extractCaptures(
sigmf::SigMF<sigmf::Global<core::DescrT, sdrangel::DescrT>,
sigmf::Capture<core::DescrT, sdrangel::DescrT>,
sigmf::Annotation<core::DescrT> >* metaRecord
);
static void analyzeDataType(const std::string& dataTypeString, SigMFFileDataType& dataType);
uint64_t getTrackSampleStart(int trackIndex);
int getTrackIndex(uint64_t sampleIndex);
void seekFileStream(uint64_t sampleIndex);
void seekTrackMillis(int seekMillis);
void seekFileMillis(int seekMillis);
bool applySettings(const SigMFFileInputSettings& settings, bool force = false);
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
void webapiReverseSendSettings(QList<QString>& deviceSettingsKeys, const SigMFFileInputSettings& settings, bool force);
void webapiReverseSendStartStop(bool start);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_SIGMFFILEINPUT_H

View File

@ -0,0 +1,699 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QTime>
#include <QDateTime>
#include <QString>
#include <QFileDialog>
#include <QMessageBox>
#include "ui_sigmffileinputgui.h"
#include "plugin/pluginapi.h"
#include "gui/colormapper.h"
#include "gui/glspectrum.h"
#include "gui/crightclickenabler.h"
#include "gui/basicdevicesettingsdialog.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/filerecordinterface.h"
#include "device/deviceapi.h"
#include "device/deviceuiset.h"
#include "mainwindow.h"
#include "recordinfodialog.h"
#include "sigmffileinputgui.h"
SigMFFileInputGUI::SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent) :
QWidget(parent),
ui(new Ui::SigMFFileInputGUI),
m_deviceUISet(deviceUISet),
m_settings(),
m_currentTrackIndex(0),
m_doApplySettings(true),
m_sampleSource(0),
m_startStop(false),
m_trackMode(false),
m_metaFileName("..."),
m_sampleRate(48000),
m_centerFrequency(0),
m_recordLength(0),
m_startingTimeStamp(0),
m_samplesCount(0),
m_tickCount(0),
m_enableTrackNavTime(false),
m_enableFullNavTime(false),
m_lastEngineState(DeviceAPI::StNotStarted)
{
ui->setupUi(this);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequency->setValueRange(7, 0, pow(10,7));
ui->fileNameText->setText(m_metaFileName);
ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->captureTable->setSelectionMode(QAbstractItemView::NoSelection);
connect(&(m_deviceUISet->m_deviceAPI->getMasterTimer()), SIGNAL(timeout()), this, SLOT(tick()));
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(500);
CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop);
connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
setAccelerationCombo();
displaySettings();
updateStartStop();
ui->trackNavTimeSlider->setEnabled(false);
ui->fullNavTimeSlider->setEnabled(false);
ui->acceleration->setEnabled(false);
ui->playFull->setEnabled(false);
ui->playFull->setChecked(false);
ui->playTrack->setEnabled(false);
ui->playTrack->setChecked(false);
m_sampleSource = m_deviceUISet->m_deviceAPI->getSampleSource();
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
m_sampleSource->setMessageQueueToGUI(&m_inputMessageQueue);
}
SigMFFileInputGUI::~SigMFFileInputGUI()
{
delete ui;
}
void SigMFFileInputGUI::destroy()
{
delete this;
}
void SigMFFileInputGUI::setName(const QString& name)
{
setObjectName(name);
}
QString SigMFFileInputGUI::getName() const
{
return objectName();
}
void SigMFFileInputGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
qint64 SigMFFileInputGUI::getCenterFrequency() const
{
return m_centerFrequency;
}
void SigMFFileInputGUI::setCenterFrequency(qint64 centerFrequency)
{
m_centerFrequency = centerFrequency;
displaySettings();
sendSettings();
}
QByteArray SigMFFileInputGUI::serialize() const
{
return m_settings.serialize();
}
bool SigMFFileInputGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
sendSettings();
return true;
} else {
resetToDefaults();
return false;
}
}
void SigMFFileInputGUI::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;
m_deviceSampleRate = notif->getSampleRate();
m_deviceCenterFrequency = notif->getCenterFrequency();
qDebug("SigMFFileInputGUI::handleInputMessages: DSPSignalNotification: SampleRate:%d, CenterFrequency:%llu", notif->getSampleRate(), notif->getCenterFrequency());
updateSampleRateAndFrequency();
delete message;
}
else
{
if (handleMessage(*message))
{
delete message;
}
}
}
}
bool SigMFFileInputGUI::handleMessage(const Message& message)
{
if (SigMFFileInput::MsgConfigureSigMFFileInput::match(message))
{
const SigMFFileInput::MsgConfigureSigMFFileInput& cfg = (SigMFFileInput::MsgConfigureSigMFFileInput&) message;
m_settings = cfg.getSettings();
displaySettings();
return true;
}
else if (SigMFFileInput::MsgReportStartStop::match(message))
{
SigMFFileInput::MsgReportStartStop& report = (SigMFFileInput::MsgReportStartStop&) message;
m_startStop = report.getStartStop();
updateStartStop();
return true;
}
else if (SigMFFileInput::MsgReportMetaData::match(message))
{
SigMFFileInput::MsgReportMetaData& report = (SigMFFileInput::MsgReportMetaData&) message;
m_metaInfo = report.getMetaInfo();
m_recordInfo = QString("Meta file..: %1\n").arg(m_metaFileName);
if (m_metaInfo.m_sdrAngelVersion.size() == 0)
{
if (m_metaInfo.m_description.size() > 0) {
m_recordInfo += QString("Description: %1\n").arg(m_metaInfo.m_description);
}
if (m_metaInfo.m_author.size() > 0) {
m_recordInfo += QString("Author.....: %1\n").arg(m_metaInfo.m_author);
}
if (m_metaInfo.m_license.size() > 0) {
m_recordInfo += QString("License....: %1\n").arg(m_metaInfo.m_license);
}
if (m_metaInfo.m_sigMFVersion.size() > 0) {
m_recordInfo += QString("Version....: %1\n").arg(m_metaInfo.m_sigMFVersion);
}
m_recordInfo += QString("Data type..: %1\n").arg(m_metaInfo.m_dataTypeStr);
m_recordInfo += QString("Nb samples.: %1\n").arg(m_metaInfo.m_totalSamples);
m_recordInfo += QString("Nb captures: %1\n").arg(m_metaInfo.m_nbCaptures);
m_recordInfo += QString("Nb annot...: %1\n").arg(m_metaInfo.m_nbAnnotations);
ui->infoSummaryText->setText("Not recorded with SDRangel");
}
else
{
m_recordInfo += QString("Recorder...: %1\n").arg(m_metaInfo.m_recorder);
m_recordInfo += QString("Hardware...: %1\n").arg(m_metaInfo.m_hw);
m_recordInfo += QString("Data type..: %1\n").arg(m_metaInfo.m_dataTypeStr);
m_recordInfo += QString("Core SRate.: %1 S/s\n").arg(m_metaInfo.m_coreSampleRate);
m_recordInfo += QString("Nb samples.: %1\n").arg(m_metaInfo.m_totalSamples);
m_recordInfo += QString("Nb captures: %1\n").arg(m_metaInfo.m_nbCaptures);
m_recordInfo += QString("SDRangel application info:\n");
m_recordInfo += QString("Version....: v%1\n").arg(m_metaInfo.m_sdrAngelVersion);
m_recordInfo += QString("Qt version.: %1\n").arg(m_metaInfo.m_qtVersion);
m_recordInfo += QString("Rx bits....: %1 bits\n").arg(m_metaInfo.m_rxBits);
m_recordInfo += QString("Arch.......: %1\n").arg(m_metaInfo.m_arch);
m_recordInfo += QString("O/S........: %1\n").arg(m_metaInfo.m_os);
ui->infoSummaryText->setText(QString("%1 Rx %2 bits v%3")
.arg(m_metaInfo.m_recorder)
.arg(m_metaInfo.m_rxBits)
.arg(m_metaInfo.m_sdrAngelVersion));
}
m_captures = report.getCaptures();
addCaptures(m_captures);
m_centerFrequency = m_captures.size() > 0 ? m_captures.at(0).m_centerFrequency : 0;
m_recordLength = m_captures.size() > 0 ? m_captures.at(0).m_length : m_metaInfo.m_totalSamples;
m_startingTimeStamp = m_captures.size() > 0 ? m_captures.at(0).m_tsms : 0;
m_sampleRate = m_metaInfo.m_coreSampleRate;
m_sampleSize = m_metaInfo.m_dataType.m_sampleBits;
QTime recordLength(0, 0, 0, 0);
recordLength = recordLength.addMSecs(m_metaInfo.m_totalTimeMs);
QString s_time = recordLength.toString("HH:mm:ss");
ui->fullRecordLengthText->setText(s_time);
m_trackSamplesCount = 0;
m_trackTimeStart = 0;
ui->sampleSizeText->setText(tr("%1%2%3b")
.arg(m_metaInfo.m_dataType.m_complex ? "c" : "r")
.arg(m_metaInfo.m_dataType.m_floatingPoint ? "f" : m_metaInfo.m_dataType.m_signed ? "i" : "u")
.arg(m_sampleSize));
updateWithStreamData();
return true;
}
else if (SigMFFileInput::MsgReportTrackChange::match(message))
{
SigMFFileInput::MsgReportTrackChange& report = (SigMFFileInput::MsgReportTrackChange&) message;
m_currentTrackIndex = report.getTrackIndex();
qDebug("SigMFFileInputGUI::handleMessage MsgReportTrackChange: m_currentTrackIndex: %d", m_currentTrackIndex);
m_centerFrequency = m_captures.at(m_currentTrackIndex).m_centerFrequency;
m_sampleRate = m_captures.at(m_currentTrackIndex).m_sampleRate;
m_recordLength = m_captures.at(m_currentTrackIndex).m_length;
m_startingTimeStamp = m_captures.at(m_currentTrackIndex).m_tsms;
m_samplesCount = m_captures.at(m_currentTrackIndex).m_sampleStart;
m_trackSamplesCount = 0;
updateWithStreamData();
return true;
}
else if (SigMFFileInput::MsgReportFileInputStreamTiming::match(message))
{
SigMFFileInput::MsgReportFileInputStreamTiming& report = (SigMFFileInput::MsgReportFileInputStreamTiming&) message;
m_samplesCount = report.getSamplesCount();
m_trackSamplesCount = report.getTrackSamplesCount();
m_trackTimeStart = report.getTrackTimeStart();
m_trackNumber = report.getTrackNumber();
updateWithStreamTime();
return true;
}
else if (SigMFFileInput::MsgStartStop::match(message))
{
SigMFFileInput::MsgStartStop& notif = (SigMFFileInput::MsgStartStop&) message;
blockApplySettings(true);
ui->startStop->setChecked(notif.getStartStop());
blockApplySettings(false);
return true;
}
else if (SigMFFileInput::MsgReportCRC::match(message))
{
SigMFFileInput::MsgReportCRC& notif = (SigMFFileInput::MsgReportCRC&) message;
if (notif.isOK()) {
ui->crcLabel->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->crcLabel->setStyleSheet("QLabel { background-color : red; }");
}
return true;
}
else if (SigMFFileInput::MsgReportTotalSamplesCheck::match(message))
{
SigMFFileInput::MsgReportTotalSamplesCheck& notif = (SigMFFileInput::MsgReportTotalSamplesCheck&) message;
if (notif.isOK()) {
ui->totalLabel->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->totalLabel->setStyleSheet("QLabel { background-color : red; }");
}
return true;
}
else
{
return false;
}
}
void SigMFFileInputGUI::updateSampleRateAndFrequency()
{
m_deviceUISet->getSpectrum()->setSampleRate(m_deviceSampleRate);
m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency);
ui->deviceRateText->setText(tr("%1k").arg((float)m_deviceSampleRate / 1000));
}
void SigMFFileInputGUI::displaySettings()
{
blockApplySettings(true);
ui->playTrackLoop->setChecked(m_settings.m_trackLoop);
ui->playFullLoop->setChecked(m_settings.m_fullLoop);
ui->acceleration->setCurrentIndex(SigMFFileInputSettings::getAccelerationIndex(m_settings.m_accelerationFactor));
blockApplySettings(false);
}
QString SigMFFileInputGUI::displayScaled(uint64_t value, int precision)
{
if (value < 1000) {
return tr("%1").arg(QString::number(value, 'f', precision));
} else if (value < 1000000) {
return tr("%1k").arg(QString::number(value / 1000.0, 'f', precision));
} else if (value < 1000000000) {
return tr("%1M").arg(QString::number(value / 1000000.0, 'f', precision));
} else if (value < 1000000000000) {
return tr("%1G").arg(QString::number(value / 1000000000.0, 'f', precision));
}
}
void SigMFFileInputGUI::addCaptures(const QList<SigMFFileCapture>& captures)
{
ui->captureTable->setRowCount(captures.size());
QList<SigMFFileCapture>::const_iterator it = captures.begin();
for (int i = 0; i < captures.size(); i++)
{
QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(captures.at(i).m_tsms);
unsigned int sampleRate = captures.at(i).m_sampleRate;
ui->captureTable->setItem(i, 0, new QTableWidgetItem(dateTime.toString("yyyy-MM-ddTHH:mm:ss")));
ui->captureTable->setItem(i, 1, new QTableWidgetItem(displayScaled(captures.at(i).m_centerFrequency, 5)));
ui->captureTable->setItem(i, 2, new QTableWidgetItem(displayScaled(sampleRate, 2)));
unsigned int milliseconds = (captures.at(i).m_length * 1000) / sampleRate;
QTime t = QTime::fromMSecsSinceStartOfDay(milliseconds);
ui->captureTable->setItem(i, 3, new QTableWidgetItem(t.toString("HH:mm:ss")));
for (int j = 0; j < 4; j++)
{
ui->captureTable->item(i, j)->setFlags(ui->captureTable->item(i, j)->flags() & ~Qt::ItemIsEditable);
ui->captureTable->item(i, j)->setTextAlignment(Qt::AlignRight);
}
}
ui->captureTable->resizeRowsToContents();
ui->captureTable->resizeColumnsToContents();
}
void SigMFFileInputGUI::sendSettings()
{
if (m_doApplySettings)
{
SigMFFileInput::MsgConfigureSigMFFileInput *message = SigMFFileInput::MsgConfigureSigMFFileInput::create(m_settings, false);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void SigMFFileInputGUI::on_startStop_toggled(bool checked)
{
if (m_doApplySettings)
{
SigMFFileInput::MsgStartStop *message = SigMFFileInput::MsgStartStop::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void SigMFFileInputGUI::on_infoDetails_clicked(bool checked)
{
RecordInfoDialog infoDialog(m_recordInfo, this);
infoDialog.exec();
}
void SigMFFileInputGUI::on_captureTable_itemSelectionChanged()
{
QList<QTableWidgetItem *> selectedItems = ui->captureTable->selectedItems();
if (selectedItems.size() == 0)
{
qDebug("SigMFFileInputGUI::on_captureTable_itemSelectionChanged: no selection");
}
else
{
int trackIndex = selectedItems.front()->row();
qDebug("SigMFFileInputGUI::on_captureTable_itemSelectionChanged: row: %d", trackIndex);
SigMFFileInput::MsgConfigureTrackIndex *message = SigMFFileInput::MsgConfigureTrackIndex::create(trackIndex);
m_sampleSource->getInputMessageQueue()->push(message);
ui->trackNavTimeSlider->setValue(0);
float posRatio = (float) m_captures[trackIndex].m_sampleStart / (float) m_metaInfo.m_totalSamples;
ui->fullNavTimeSlider->setValue((int) (posRatio * 1000.0));
}
}
void SigMFFileInputGUI::on_trackNavTimeSlider_valueChanged(int value)
{
if (m_enableTrackNavTime && ((value >= 0) && (value <= 1000)))
{
SigMFFileInput::MsgConfigureTrackSeek* message = SigMFFileInput::MsgConfigureTrackSeek::create(value);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void SigMFFileInputGUI::on_fullNavTimeSlider_valueChanged(int value)
{
if (m_enableFullNavTime && ((value >= 0) && (value <= 1000)))
{
SigMFFileInput::MsgConfigureFileSeek* message = SigMFFileInput::MsgConfigureFileSeek::create(value);
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void SigMFFileInputGUI::on_playTrackLoop_toggled(bool checked)
{
m_settings.m_trackLoop = checked;
sendSettings();
}
void SigMFFileInputGUI::on_playTrack_toggled(bool checked)
{
SigMFFileInput::MsgConfigureTrackWork* message = SigMFFileInput::MsgConfigureTrackWork::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
ui->trackNavTimeSlider->setEnabled(!checked);
ui->fullNavTimeSlider->setEnabled(!checked);
ui->acceleration->setEnabled(!checked);
m_enableTrackNavTime = !checked;
m_enableFullNavTime = !checked;
ui->playFull->setEnabled(!checked);
ui->playFull->setChecked(false);
ui->captureTable->setSelectionMode(checked ? QAbstractItemView::NoSelection : QAbstractItemView::ExtendedSelection);
}
void SigMFFileInputGUI::on_playFullLoop_toggled(bool checked)
{
m_settings.m_fullLoop = checked;
sendSettings();
}
void SigMFFileInputGUI::on_playFull_toggled(bool checked)
{
SigMFFileInput::MsgConfigureFileWork* message = SigMFFileInput::MsgConfigureFileWork::create(checked);
m_sampleSource->getInputMessageQueue()->push(message);
ui->trackNavTimeSlider->setEnabled(!checked);
ui->fullNavTimeSlider->setEnabled(!checked);
ui->acceleration->setEnabled(!checked);
m_enableTrackNavTime = !checked;
m_enableFullNavTime = !checked;
ui->playTrack->setEnabled(!checked);
ui->playTrack->setChecked(false);
ui->captureTable->setSelectionMode(checked ? QAbstractItemView::NoSelection : QAbstractItemView::ExtendedSelection);
}
void SigMFFileInputGUI::on_acceleration_currentIndexChanged(int index)
{
m_settings.m_accelerationFactor = SigMFFileInputSettings::getAccelerationValue(index);
sendSettings();
}
void SigMFFileInputGUI::updateStatus()
{
int state = m_deviceUISet->m_deviceAPI->state();
if(m_lastEngineState != state)
{
switch(state)
{
case DeviceAPI::StNotStarted:
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
break;
case DeviceAPI::StIdle:
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
break;
case DeviceAPI::StRunning:
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
break;
case DeviceAPI::StError:
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
break;
default:
break;
}
m_lastEngineState = state;
}
}
void SigMFFileInputGUI::on_showFileDialog_clicked(bool checked)
{
(void) checked;
QString fileName = QFileDialog::getOpenFileName(
this,
tr("Open SigMF I/Q record file"),
".",
tr("SigMF Files (*.sigmf-meta)"),
nullptr,
QFileDialog::DontUseNativeDialog
);
if (fileName != "")
{
m_metaFileName = fileName;
ui->fileNameText->setText(m_metaFileName);
ui->crcLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
configureFileName();
}
}
void SigMFFileInputGUI::configureFileName()
{
qDebug() << "SigMFFileInputGUI::configureFileName: " << m_metaFileName.toStdString().c_str();
QString fileBase;
FileRecordInterface::RecordType recordType = FileRecordInterface::guessTypeFromFileName(m_metaFileName, fileBase);
if (recordType == FileRecordInterface::RecordTypeSigMF)
{
m_settings.m_fileName = fileBase;
sendSettings();
}
}
void SigMFFileInputGUI::updateStartStop()
{
qDebug("SigMFFileInputGUI::updateStartStop: %s", m_startStop ? "start" : "stop");
// always start in file mode
ui->playFull->setEnabled(m_startStop);
ui->playFull->setChecked(m_startStop);
ui->playTrack->setEnabled(false);
ui->playTrack->setChecked(false);
ui->trackNavTimeSlider->setEnabled(false);
ui->fullNavTimeSlider->setEnabled(false);
ui->showFileDialog->setEnabled(!m_startStop);
ui->captureTable->setSelectionMode(QAbstractItemView::NoSelection);
}
void SigMFFileInputGUI::updateWithStreamData()
{
ui->captureTable->blockSignals(true);
ui->captureTable->setRangeSelected(
QTableWidgetSelectionRange(0, 0, ui->captureTable->rowCount() - 1, ui->captureTable->columnCount() - 1), false);
ui->captureTable->setRangeSelected(
QTableWidgetSelectionRange(m_currentTrackIndex, 0, m_currentTrackIndex, ui->captureTable->columnCount() - 1), true);
ui->captureTable->blockSignals(false);
ui->trackNumberText->setText(tr("%1").arg(m_currentTrackIndex + 1, 3, 10, QChar('0')));
ui->centerFrequency->setValue(m_centerFrequency/1000);
ui->sampleRateText->setText(tr("%1k").arg((float)m_sampleRate / 1000));
QTime recordLength(0, 0, 0, 0);
recordLength = recordLength.addSecs(m_recordLength / m_sampleRate);
QString s_time = recordLength.toString("HH:mm:ss");
ui->trackRecordLengthText->setText(s_time);
updateWithStreamTime();
}
void SigMFFileInputGUI::updateWithStreamTime()
{
qint64 track_sec = 0;
qint64 track_msec = 0;
if (m_sampleRate > 0)
{
track_sec = m_trackSamplesCount / m_sampleRate;
track_msec = (m_trackSamplesCount - (track_sec * m_sampleRate)) * 1000LL / m_sampleRate;
}
QTime t(0, 0, 0, 0);
t = t.addSecs(track_sec);
t = t.addMSecs(track_msec);
QString s_timems = t.toString("HH:mm:ss.zzz");
ui->trackRelTimeText->setText(s_timems);
t = t.addMSecs(m_trackTimeStart);
s_timems = t.toString("HH:mm:ss.zzz");
ui->fullRelTimeText->setText(s_timems);
QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_startingTimeStamp);
dt = dt.addSecs(track_sec);
dt = dt.addMSecs(track_msec);
QString s_date = dt.toString("yyyy-MM-dd HH:mm:ss.zzz");
ui->absTimeText->setText(s_date);
if (!ui->trackNavTimeSlider->isEnabled())
{
float posRatio = (float) m_trackSamplesCount / (float) m_recordLength;
ui->trackNavTimeSlider->setValue((int) (posRatio * 1000.0));
}
if (!ui->fullNavTimeSlider->isEnabled())
{
float posRatio = (float) m_samplesCount / (float) m_metaInfo.m_totalSamples;
ui->fullNavTimeSlider->setValue((int) (posRatio * 1000.0));
}
}
void SigMFFileInputGUI::tick()
{
if ((++m_tickCount & 0xf) == 0) {
SigMFFileInput::MsgConfigureFileInputStreamTiming* message = SigMFFileInput::MsgConfigureFileInputStreamTiming::create();
m_sampleSource->getInputMessageQueue()->push(message);
}
}
void SigMFFileInputGUI::setAccelerationCombo()
{
ui->acceleration->blockSignals(true);
ui->acceleration->clear();
ui->acceleration->addItem(QString("1"));
for (unsigned int i = 0; i <= SigMFFileInputSettings::m_accelerationMaxScale; i++)
{
QString s;
int m = pow(10.0, i);
int x = 2*m;
setNumberStr(x, s);
ui->acceleration->addItem(s);
x = 5*m;
setNumberStr(x, s);
ui->acceleration->addItem(s);
x = 10*m;
setNumberStr(x, s);
ui->acceleration->addItem(s);
}
ui->acceleration->blockSignals(false);
}
void SigMFFileInputGUI::setNumberStr(int n, QString& s)
{
if (n < 1000) {
s = tr("%1").arg(n);
} else if (n < 100000) {
s = tr("%1k").arg(n/1000);
} else if (n < 1000000) {
s = tr("%1e5").arg(n/100000);
} else if (n < 1000000000) {
s = tr("%1M").arg(n/1000000);
} else {
s = tr("%1G").arg(n/1000000000);
}
}
void SigMFFileInputGUI::openDeviceSettingsDialog(const QPoint& p)
{
BasicDeviceSettingsDialog dialog(this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.move(p);
dialog.exec();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
sendSettings();
}

View File

@ -0,0 +1,121 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEINPUTGUI_H
#define INCLUDE_SIGMFFILEINPUTGUI_H
#include <QTimer>
#include <QWidget>
#include "plugin/plugininstancegui.h"
#include "util/messagequeue.h"
#include "sigmffileinputsettings.h"
#include "sigmffiledata.h"
#include "sigmffileinput.h"
class DeviceUISet;
namespace Ui {
class SigMFFileInputGUI;
}
class SigMFFileInputGUI : public QWidget, public PluginInstanceGUI {
Q_OBJECT
public:
explicit SigMFFileInputGUI(DeviceUISet *deviceUISet, QWidget* parent = nullptr);
virtual ~SigMFFileInputGUI();
virtual void destroy();
void setName(const QString& name);
QString getName() const;
void resetToDefaults();
virtual qint64 getCenterFrequency() const;
virtual void setCenterFrequency(qint64 centerFrequency);
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual bool handleMessage(const Message& message);
private:
Ui::SigMFFileInputGUI* ui;
DeviceUISet* m_deviceUISet;
SigMFFileInputSettings m_settings;
int m_currentTrackIndex;
bool m_doApplySettings;
QTimer m_statusTimer;
std::vector<int> m_gains;
DeviceSampleSource* m_sampleSource;
bool m_startStop;
bool m_trackMode;
QString m_metaFileName;
QString m_recordInfo;
int m_sampleRate;
quint32 m_sampleSize;
quint64 m_centerFrequency;
quint64 m_recordLength;
quint64 m_startingTimeStamp;
quint64 m_samplesCount;
quint64 m_trackSamplesCount;
quint64 m_trackTimeStart;
int m_trackNumber;
std::size_t m_tickCount;
bool m_enableTrackNavTime;
bool m_enableFullNavTime;
int m_deviceSampleRate;
quint64 m_deviceCenterFrequency; //!< Center frequency in device
int m_lastEngineState;
MessageQueue m_inputMessageQueue;
SigMFFileMetaInfo m_metaInfo;
QList<SigMFFileCapture> m_captures;
void blockApplySettings(bool block) { m_doApplySettings = !block; }
void displaySettings();
void displayTime();
QString displayScaled(uint64_t value, int precision);
void addCaptures(const QList<SigMFFileCapture>& captures);
void sendSettings();
void updateSampleRateAndFrequency();
void configureFileName();
void updateStartStop();
void updateWithStreamData();
void updateWithStreamTime();
void setAccelerationCombo();
void setNumberStr(int n, QString& s);
private slots:
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_infoDetails_clicked(bool checked);
void on_captureTable_itemSelectionChanged();
void on_trackNavTimeSlider_valueChanged(int value);
void on_playTrack_toggled(bool checked);
void on_playTrackLoop_toggled(bool checked);
void on_fullNavTimeSlider_valueChanged(int value);
void on_playFull_toggled(bool checked);
void on_playFullLoop_toggled(bool checked);
void on_showFileDialog_clicked(bool checked);
void on_acceleration_currentIndexChanged(int index);
void updateStatus();
void tick();
void openDeviceSettingsDialog(const QPoint& p);
};
#endif // INCLUDE_SIGMFFILEINPUTGUI_H

View File

@ -0,0 +1,851 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SigMFFileInputGUI</class>
<widget class="QWidget" name="SigMFFileInputGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>303</width>
<height>447</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>250</width>
<height>360</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
<weight>50</weight>
<italic>false</italic>
<bold>false</bold>
</font>
</property>
<property name="windowTitle">
<string>SigMF File Input</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_freq">
<property name="topMargin">
<number>4</number>
</property>
<item>
<layout class="QVBoxLayout" name="deviceUILayout">
<item>
<layout class="QHBoxLayout" name="deviceButtonsLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>start/stop acquisition</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="deviceRateLayout">
<item>
<widget class="QLabel" name="deviceRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>I/Q sample rate kS/s</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="freqLeftSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="ValueDial" name="centerFrequency" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Record center frequency in kHz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="freqUnits">
<property name="text">
<string> kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="freqRightlSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_freq">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="fileSelectionLayout">
<item>
<widget class="QPushButton" name="showFileDialog">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Open file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/preset-load.png</normaloff>:/preset-load.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fileNameText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>File currently opened</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_file">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="infoLayout">
<item>
<widget class="QPushButton" name="infoDetails">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Record detailed information</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/info.png</normaloff>:/info.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="infoSummaryText">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Record summary information</string>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="rateTimeLayout">
<item>
<widget class="QLabel" name="sampleRateText">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Record sample rate (kS/s)</string>
</property>
<property name="text">
<string>00000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="absTimeLine1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sampleSizeText">
<property name="minimumSize">
<size>
<width>22</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Record sample size (bits)</string>
</property>
<property name="text">
<string>00b</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="absTimeLine2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="crcLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>CRC status: Green: OK Red: KO Grey: undefined</string>
</property>
<property name="text">
<string>CRC</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="totalLabel">
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Total samples check: Green: OK Red: KO Grey: undefined</string>
</property>
<property name="text">
<string>TOT</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="absTimeLine3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="absTimeText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>160</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Record absolute time</string>
</property>
<property name="text">
<string>2015-01-01 00:00:00.000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_rate">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="captureTable">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<column>
<property name="text">
<string>Datetime</string>
</property>
</column>
<column>
<property name="text">
<string>F(Hz)</string>
</property>
</column>
<column>
<property name="text">
<string>SR(Hz)</string>
</property>
</column>
<column>
<property name="text">
<string>Time</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_trackNav">
<item>
<widget class="QSlider" name="trackNavTimeSlider">
<property name="toolTip">
<string>Track time navigator</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="trackPlayControllLayout">
<item>
<widget class="ButtonSwitch" name="playTrackLoop">
<property name="toolTip">
<string>Play track in a loop</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/playloop.png</normaloff>:/playloop.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="playTrack">
<property name="toolTip">
<string>Track Play / Pause</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/pause.png</normalon>
<disabledoff>:/stop.png</disabledoff>
<disabledon>:/stop.png</disabledon>
<activeoff>:/play.png</activeoff>
<activeon>:/pause.png</activeon>
<selectedoff>:/play.png</selectedoff>
<selectedon>:/pause.png</selectedon>:/play.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="trackPlaySpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="trackNumberText">
<property name="toolTip">
<string>Track number</string>
</property>
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="trackLinePlay1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="trackRelTimeText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Track record time from start</string>
</property>
<property name="text">
<string>00:00:00.000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="traclLinePlay2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="trackRecordLengthText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Track total record time</string>
</property>
<property name="text">
<string>00:00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_fullNav">
<item>
<widget class="QSlider" name="fullNavTimeSlider">
<property name="toolTip">
<string>Full time navigator</string>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="fullPlayControllLayout">
<item>
<widget class="ButtonSwitch" name="playFullLoop">
<property name="toolTip">
<string>Play record in a loop</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="ButtonSwitch" name="playFull">
<property name="toolTip">
<string>Record Play / Pause</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/pause.png</normalon>
<disabledoff>:/stop.png</disabledoff>
<disabledon>:/stop.png</disabledon>
<activeoff>:/play.png</activeoff>
<activeon>:/pause.png</activeon>
<selectedoff>:/play.png</selectedoff>
<selectedon>:/pause.png</selectedon>:/play.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="acceleration">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Acceleration factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>5</string>
</property>
</item>
<item>
<property name="text">
<string>10</string>
</property>
</item>
<item>
<property name="text">
<string>20</string>
</property>
</item>
<item>
<property name="text">
<string>50</string>
</property>
</item>
<item>
<property name="text">
<string>100</string>
</property>
</item>
<item>
<property name="text">
<string>200</string>
</property>
</item>
<item>
<property name="text">
<string>500</string>
</property>
</item>
<item>
<property name="text">
<string>1k</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="fullPlaySpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="fullRelTimeText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>90</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Full record time from start</string>
</property>
<property name="text">
<string>00:00:00.000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="fullPlayLine">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fullRecordLengthText">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Full record time</string>
</property>
<property name="text">
<string>00:00:00</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="padLayout">
<item>
<spacer name="verticaPadlSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</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>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,148 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2019 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#ifdef SERVER_MODE
#include "sigmffileinput.h"
#else
#include "sigmffileinputgui.h"
#endif
#include "sigmffileinputplugin.h"
#include "sigmffileinputwebapiadapter.h"
const PluginDescriptor SigMFFileInputPlugin::m_pluginDescriptor = {
QString("SigMFFileInput"),
QString("File device input (SigMF)"),
QString("5.7.0"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
const QString SigMFFileInputPlugin::m_hardwareID = "SigMFFileInput";
const QString SigMFFileInputPlugin::m_deviceTypeID = FILEINPUT_DEVICE_TYPE_ID;
SigMFFileInputPlugin::SigMFFileInputPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& SigMFFileInputPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void SigMFFileInputPlugin::initPlugin(PluginAPI* pluginAPI)
{
pluginAPI->registerSampleSource(m_deviceTypeID, this);
}
void SigMFFileInputPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
{
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
return;
}
originDevices.append(OriginDevice(
"SigMFFileInput",
m_hardwareID,
QString(),
0,
1, // nb Rx
0 // nb Tx
));
listedHwIds.append(m_hardwareID);
}
PluginInterface::SamplingDevices SigMFFileInputPlugin::enumSampleSources(const OriginDevices& originDevices)
{
SamplingDevices result;
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
{
if (it->hardwareId == m_hardwareID)
{
result.append(SamplingDevice(
it->displayableName,
m_hardwareID,
m_deviceTypeID,
it->serial,
it->sequence,
PluginInterface::SamplingDevice::BuiltInDevice,
PluginInterface::SamplingDevice::StreamSingleRx,
1,
0
));
}
}
return result;
}
#ifdef SERVER_MODE
PluginInstanceGUI* SigMFFileInputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
(void) sourceId;
(void) widget;
(void) deviceUISet;
return 0;
}
#else
PluginInstanceGUI* SigMFFileInputPlugin::createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet)
{
if(sourceId == m_deviceTypeID)
{
SigMFFileInputGUI* gui = new SigMFFileInputGUI(deviceUISet);
*widget = gui;
return gui;
}
else
{
return 0;
}
}
#endif
DeviceSampleSource *SigMFFileInputPlugin::createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI)
{
if (sourceId == m_deviceTypeID)
{
SigMFFileInput* input = new SigMFFileInput(deviceAPI);
return input;
}
else
{
return 0;
}
}
DeviceWebAPIAdapter *SigMFFileInputPlugin::createDeviceWebAPIAdapter() const
{
return new SigMFFileInputWebAPIAdapter();
}

View File

@ -0,0 +1,55 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEINPUTPLUGIN_H
#define INCLUDE_SIGMFFILEINPUTPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
#define FILEINPUT_DEVICE_TYPE_ID "sdrangel.samplesource.sigmffileinput"
class PluginAPI;
class SigMFFileInputPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID FILEINPUT_DEVICE_TYPE_ID)
public:
explicit SigMFFileInputPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
virtual SamplingDevices enumSampleSources(const OriginDevices& originDevices);
virtual PluginInstanceGUI* createSampleSourcePluginInstanceGUI(
const QString& sourceId,
QWidget **widget,
DeviceUISet *deviceUISet);
virtual DeviceSampleSource* createSampleSourcePluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
static const QString m_hardwareID;
static const QString m_deviceTypeID;
private:
static const PluginDescriptor m_pluginDescriptor;
};
#endif // INCLUDE_FILESOURCEPLUGIN_H

View File

@ -0,0 +1,159 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/simpleserializer.h"
#include "sigmffileinputsettings.h"
const unsigned int SigMFFileInputSettings::m_accelerationMaxScale = 2;
SigMFFileInputSettings::SigMFFileInputSettings()
{
resetToDefaults();
}
void SigMFFileInputSettings::resetToDefaults()
{
m_fileName = "./test.sdriq";
m_accelerationFactor = 1;
m_trackLoop = false;
m_fullLoop = true;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
}
QByteArray SigMFFileInputSettings::serialize() const
{
SimpleSerializer s(1);
s.writeString(1, m_fileName);
s.writeU32(2, m_accelerationFactor);
s.writeBool(3, m_trackLoop);
s.writeBool(3, m_fullLoop);
s.writeBool(5, m_useReverseAPI);
s.writeString(6, m_reverseAPIAddress);
s.writeU32(7, m_reverseAPIPort);
s.writeU32(8, m_reverseAPIDeviceIndex);
return s.final();
}
bool SigMFFileInputSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid()) {
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
uint32_t uintval;
d.readString(1, &m_fileName, "./test.sdriq");
d.readU32(2, &m_accelerationFactor, 1);
d.readBool(3, &m_trackLoop, false);
d.readBool(4, &m_fullLoop, true);
d.readBool(5, &m_useReverseAPI, false);
d.readString(6, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(7, &uintval, 0);
if ((uintval > 1023) && (uintval < 65535)) {
m_reverseAPIPort = uintval;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(8, &uintval, 0);
m_reverseAPIDeviceIndex = uintval > 99 ? 99 : uintval;
return true;
}
else
{
resetToDefaults();
return false;
}
}
int SigMFFileInputSettings::getAccelerationIndex(int accelerationValue)
{
if (accelerationValue <= 1) {
return 0;
}
int v = accelerationValue;
int j = 0;
for (int i = 0; i <= accelerationValue; i++)
{
if (v < 20)
{
if (v < 2) {
j = 0;
} else if (v < 5) {
j = 1;
} else if (v < 10) {
j = 2;
} else {
j = 3;
}
return 3*i + j;
}
v /= 10;
}
return 3*m_accelerationMaxScale + 3;
}
int SigMFFileInputSettings::getAccelerationValue(int accelerationIndex)
{
if (accelerationIndex <= 0) {
return 1;
}
unsigned int v = accelerationIndex - 1;
int m = pow(10.0, v/3 > m_accelerationMaxScale ? m_accelerationMaxScale : v/3);
int x = 1;
if (v % 3 == 0) {
x = 2;
} else if (v % 3 == 1) {
x = 5;
} else if (v % 3 == 2) {
x = 10;
}
return x * m;
}
int SigMFFileInputSettings::bitsToBytes(int bits)
{
if (bits <= 8) {
return 1;
} else if (bits <= 16) {
return 2;
} else if (bits <= 32) {
return 4;
} else {
return 8;
}
}

View File

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_SAMPLESOURCE_SIGMFFILEINPUT_SIGMFFILEINPUTSETTINGS_H_
#define PLUGINS_SAMPLESOURCE_SIGMFFILEINPUT_SIGMFFILEINPUTSETTINGS_H_
#include <QString>
#include <QByteArray>
struct SigMFFileInputSettings {
QString m_fileName;
quint32 m_accelerationFactor;
bool m_trackLoop;
bool m_fullLoop;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
static const unsigned int m_accelerationMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000
SigMFFileInputSettings();
~SigMFFileInputSettings() {}
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
static int getAccelerationIndex(int averaging);
static int getAccelerationValue(int averagingIndex);
static int bitsToBytes(int bits);
};
#endif /* PLUGINS_SAMPLESOURCE_SIGMFFILEINPUT_SIGMFFILEINPUTSETTINGS_H_ */

View File

@ -0,0 +1,762 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <QDebug>
#include "dsp/filerecord.h"
#include "dsp/samplesinkfifo.h"
#include "util/messagequeue.h"
#include "sigmffiledata.h"
#include "sigmffileconvert.h"
#include "sigmffileinputsettings.h"
#include "sigmffileinputthread.h"
MESSAGE_CLASS_DEFINITION(SigMFFileInputThread::MsgReportEOF, Message)
MESSAGE_CLASS_DEFINITION(SigMFFileInputThread::MsgReportTrackChange, Message)
SigMFFileInputThread::SigMFFileInputThread(std::ifstream *samplesStream,
SampleSinkFifo* sampleFifo,
const QTimer& timer,
MessageQueue *fileInputMessageQueue,
QObject* parent) :
QThread(parent),
m_running(false),
m_currentTrackIndex(0),
m_ifstream(samplesStream),
m_fileBuf(0),
m_convertBuf(0),
m_bufsize(0),
m_chunksize(0),
m_sampleFifo(sampleFifo),
m_samplesCount(0),
m_timer(timer),
m_fileInputMessageQueue(fileInputMessageQueue),
m_samplerate(48000),
m_accelerationFactor(1),
m_samplesize(16),
m_samplebytes(2),
m_throttlems(FILESOURCE_THROTTLE_MS),
m_throttleToggle(false),
m_sigMFConverter(nullptr)
{
assert(m_ifstream != 0);
}
SigMFFileInputThread::~SigMFFileInputThread()
{
if (m_running) {
stopWork();
}
if (m_fileBuf != 0) {
free(m_fileBuf);
}
if (m_convertBuf != 0) {
free(m_convertBuf);
}
}
void SigMFFileInputThread::startWork()
{
qDebug() << "SigMFFileInputThread::startWork: ";
if (m_ifstream->is_open())
{
qDebug() << "SigMFFileInputThread::startWork: file stream open, starting...";
m_startWaitMutex.lock();
m_elapsedTimer.start();
start();
while(!m_running) {
m_startWaiter.wait(&m_startWaitMutex, 100);
}
m_startWaitMutex.unlock();
connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
}
else
{
qDebug() << "SigMFFileInputThread::startWork: file stream closed, not starting.";
}
}
void SigMFFileInputThread::stopWork()
{
qDebug() << "SigMFFileInputThread::stopWork";
disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
m_running = false;
wait();
}
void SigMFFileInputThread::setMetaInformation(const SigMFFileMetaInfo *metaInfo, const QList<SigMFFileCapture> *captures)
{
m_metaInfo = metaInfo;
m_captures = captures;
m_samplerate = m_metaInfo->m_coreSampleRate;
m_samplesize = m_metaInfo->m_dataType.m_sampleBits;
setConverter();
setSampleRate();
}
void SigMFFileInputThread::setTrackIndex(int trackIndex)
{
m_currentTrackIndex = trackIndex;
m_samplesCount = m_captures->at(m_currentTrackIndex).m_sampleStart;
unsigned int sampleRate = m_captures->at(m_currentTrackIndex).m_sampleRate;
if (sampleRate != m_samplerate)
{
m_samplerate = sampleRate;
setSampleRate();
}
MsgReportTrackChange *message = MsgReportTrackChange::create(m_currentTrackIndex);
m_fileInputMessageQueue->push(message);
}
void SigMFFileInputThread::setAccelerationFactor(int accelerationFactor)
{
m_accelerationFactor = accelerationFactor;
setSampleRate();
}
void SigMFFileInputThread::setSampleRate()
{
bool running = m_running;
if (running) {
stopWork();
}
m_samplebytes = SigMFFileInputSettings::bitsToBytes(m_samplesize);
m_chunksize = (m_accelerationFactor * m_samplerate * 2 * m_samplebytes * m_throttlems) / 1000;
setBuffers(m_chunksize);
if (running) {
startWork();
}
}
void SigMFFileInputThread::setBuffers(std::size_t chunksize)
{
if (chunksize > m_bufsize)
{
m_bufsize = chunksize;
int nbSamples = m_bufsize/(2 * m_samplebytes);
if (m_fileBuf == 0)
{
qDebug() << "FileInputThread::setBuffers: Allocate file buffer";
m_fileBuf = (quint8*) malloc(m_bufsize);
}
else
{
qDebug() << "FileInputThread::setBuffers: Re-allocate file buffer";
quint8 *buf = m_fileBuf;
m_fileBuf = (quint8*) realloc((void*) m_fileBuf, m_bufsize);
if (!m_fileBuf) free(buf);
}
if (m_convertBuf == 0)
{
qDebug() << "FileInputThread::setBuffers: Allocate conversion buffer";
m_convertBuf = (quint8*) malloc(nbSamples*sizeof(Sample));
}
else
{
qDebug() << "FileInputThread::setBuffers: Re-allocate conversion buffer";
quint8 *buf = m_convertBuf;
m_convertBuf = (quint8*) realloc((void*) m_convertBuf, nbSamples*sizeof(Sample));
if (!m_convertBuf) free(buf);
}
qDebug() << "FileInputThread::setBuffers: size: " << m_bufsize
<< " #samples: " << nbSamples;
}
}
void SigMFFileInputThread::run()
{
m_running = true;
m_startWaiter.wakeAll();
while(m_running) // actual work is in the tick() function
{
sleep(1);
}
m_running = false;
}
void SigMFFileInputThread::tick()
{
if (m_running)
{
qint64 throttlems = m_elapsedTimer.restart();
if (throttlems != m_throttlems)
{
m_throttlems = throttlems;
m_chunksize = 2 * m_samplebytes * ((m_samplerate * (m_throttlems+(m_throttleToggle ? 1 : 0))) / 1000);
m_throttleToggle = !m_throttleToggle;
setBuffers(m_chunksize);
}
// read samples directly feeding the SampleFifo (no callback)
if (m_samplesCount + m_chunksize > m_totalSamples) {
m_ifstream->read(reinterpret_cast<char*>(m_fileBuf), m_totalSamples - m_samplesCount);
} else {
m_ifstream->read(reinterpret_cast<char*>(m_fileBuf), m_chunksize);
}
if ((m_samplesCount + m_chunksize > m_totalSamples) || m_ifstream->eof())
{
writeToSampleFifo(m_fileBuf, (qint32) m_ifstream->gcount()); // take what has been read
MsgReportEOF *message = MsgReportEOF::create();
m_fileInputMessageQueue->push(message);
}
else
{
writeToSampleFifo(m_fileBuf, (qint32) m_chunksize);
m_samplesCount += m_chunksize / (2 * m_samplebytes);
if ((m_currentTrackIndex + 1 < m_captures->size())
&& (m_samplesCount > m_captures->at(m_currentTrackIndex+1).m_sampleStart))
{
m_currentTrackIndex++;
unsigned int sampleRate = m_captures->at(m_currentTrackIndex).m_sampleRate;
if (sampleRate != m_samplerate)
{
m_samplerate = sampleRate;
setSampleRate();
}
MsgReportTrackChange *message = MsgReportTrackChange::create(m_currentTrackIndex);
m_fileInputMessageQueue->push(message);
}
}
}
}
void SigMFFileInputThread::setConverter()
{
if (m_metaInfo->m_dataType.m_floatingPoint) // float
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_bigEndian)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, true, true, true>();
} else {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, true, true, false>();
}
}
else
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, true, false, false>();
}
}
}
else
{
if (m_metaInfo->m_dataType.m_bigEndian) {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, false, true, false>();
} else {
m_sigMFConverter = new SigMFConverter<float, SDR_RX_SAMP_SZ, 32, false, false, false>();
}
}
}
else if ((m_metaInfo->m_dataType.m_signed) && (m_samplesize == 8)) // i8
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<int8_t, SDR_RX_SAMP_SZ, 8, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<int8_t, SDR_RX_SAMP_SZ, 8, true, false, false>();
}
}
else
{
m_sigMFConverter = new SigMFConverter<int8_t, SDR_RX_SAMP_SZ, 8, false, false, false>();
}
}
else if ((!m_metaInfo->m_dataType.m_signed) && (m_samplesize == 8)) // u8
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<uint8_t, SDR_RX_SAMP_SZ, 8, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<uint8_t, SDR_RX_SAMP_SZ, 8, true, false, false>();
}
}
else
{
m_sigMFConverter = new SigMFConverter<uint8_t, SDR_RX_SAMP_SZ, 8, false, false, false>();
}
}
else if ((m_metaInfo->m_dataType.m_signed) && (m_samplesize == 16)) // i16
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_bigEndian)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, true, true, true>();
} else {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, true, true, false>();
}
}
else
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, true, false, false>();
}
}
}
else
{
if (m_metaInfo->m_dataType.m_bigEndian) {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, false, true, false>();
} else {
m_sigMFConverter = new SigMFConverter<int16_t, SDR_RX_SAMP_SZ, 16, false, false, false>();
}
}
}
else if ((!m_metaInfo->m_dataType.m_signed) && (m_samplesize == 16)) // u16
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_bigEndian)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, true, true, true>();
} else {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, true, true, false>();
}
}
else
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, true, false, false>();
}
}
}
else
{
if (m_metaInfo->m_dataType.m_bigEndian) {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, false, true, false>();
} else {
m_sigMFConverter = new SigMFConverter<uint16_t, SDR_RX_SAMP_SZ, 16, false, false, false>();
}
}
}
else if ((m_metaInfo->m_dataType.m_signed) && (m_samplesize == 24)) // i24 (SDRangel special)
{
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 24, true, false, false>();
}
else if ((m_metaInfo->m_dataType.m_signed) && (m_samplesize == 32)) // i32
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_bigEndian)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, true, true, true>();
} else {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, true, true, false>();
}
}
else
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, true, false, false>();
}
}
}
else
{
if (m_metaInfo->m_dataType.m_bigEndian) {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, false, true, false>();
} else {
m_sigMFConverter = new SigMFConverter<int32_t, SDR_RX_SAMP_SZ, 32, false, false, false>();
}
}
}
else if ((!m_metaInfo->m_dataType.m_signed) && (m_samplesize == 32)) // u32
{
if (m_metaInfo->m_dataType.m_complex)
{
if (m_metaInfo->m_dataType.m_bigEndian)
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, true, true, true>();
} else {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, true, true, false>();
}
}
else
{
if (m_metaInfo->m_dataType.m_swapIQ) {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, true, false, true>();
} else {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, true, false, false>();
}
}
}
else
{
if (m_metaInfo->m_dataType.m_bigEndian) {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, false, true, false>();
} else {
m_sigMFConverter = new SigMFConverter<uint32_t, SDR_RX_SAMP_SZ, 32, false, false, false>();
}
}
}
}
void SigMFFileInputThread::writeToSampleFifo(const quint8* buf, qint32 nbBytes)
{
if (!m_sigMFConverter)
{
qDebug("SigMFFileInputThread::writeToSampleFifo: no converter - probably sample format is not supported");
return;
}
#if defined(__WINDOWS__) || (BYTE_ORDER == LITTLE_ENDIAN)
if ((m_metaInfo->m_dataType.m_complex) && (!m_metaInfo->m_dataType.m_bigEndian) && (!m_metaInfo->m_dataType.m_swapIQ))
{
if ((m_samplesize == 16) && (SDR_RX_SAMP_SZ == 16))
{
m_sampleFifo->write(buf, nbBytes);
return;
}
if ((m_samplesize == 24) && (SDR_RX_SAMP_SZ == 24))
{
m_sampleFifo->write(buf, nbBytes);
return;
}
}
#endif
int nbSamples = m_sigMFConverter->convert((FixReal *) m_convertBuf, buf, nbBytes);
m_sampleFifo->write(m_convertBuf, nbSamples*sizeof(Sample));
}
void SigMFFileInputThread::writeToSampleFifoBAK(const quint8* buf, qint32 nbBytes)
{
if (m_metaInfo->m_dataType.m_floatingPoint) // FP assumes 32 bit floats (float) always
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const float *fileBuf = (float *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] * SDR_RX_SCALEF;
convertBuf[2*is+1] = fileBuf[2*is+1] * SDR_RX_SCALEF;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] * SDR_RX_SCALEF;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
else if (m_metaInfo->m_dataType.m_signed) // signed integers
{
if (m_samplesize == 8)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int8_t *fileBuf = (int8_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is];
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
convertBuf[2*is+1] = fileBuf[2*is+1];
convertBuf[2*is+1] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is];
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
else if (m_samplesize == 16)
{
if (SDR_RX_SAMP_SZ == 16)
{
if (m_metaInfo->m_dataType.m_complex)
{
m_sampleFifo->write(buf, nbBytes);
}
else
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int16_t *fileBuf = (int16_t *) buf;
int nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is];
convertBuf[2*is+1] = 0;
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
else if (SDR_RX_SAMP_SZ == 24)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int16_t *fileBuf = (int16_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] << 8;
convertBuf[2*is+1] = fileBuf[2*is+1] << 8;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] << 8;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
else if (m_samplesize == 24)
{
if (SDR_RX_SAMP_SZ == 24)
{
if (m_metaInfo->m_dataType.m_complex)
{
m_sampleFifo->write(buf, nbBytes);
}
else
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int32_t *fileBuf = (int32_t *) buf;
int nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is];
convertBuf[2*is+1] = 0;
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
else if (SDR_RX_SAMP_SZ == 16)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int32_t *fileBuf = (int32_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] >> 8;
convertBuf[2*is+1] = fileBuf[2*is+1] >> 8;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] >> 8;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
if (m_samplesize == 32)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const int32_t *fileBuf = (int32_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16;
convertBuf[2*is+1] = fileBuf[2*is+1] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
else // unsigned integers
{
if (m_samplesize == 8)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const uint8_t *fileBuf = (uint8_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] - 128;
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
convertBuf[2*is+1] = fileBuf[2*is+1] - 128;
convertBuf[2*is+1] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] - 128;
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 8 : 16;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
else if (m_samplesize == 16)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const uint16_t *fileBuf = (uint16_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[2*is] - 32768;
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 0 : 8;
convertBuf[2*is+1] = fileBuf[2*is+1] - 32768;
convertBuf[2*is+1] <<= (SDR_RX_SAMP_SZ == 16) ? 0 : 8;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = fileBuf[is] - 32768;
convertBuf[2*is] <<= (SDR_RX_SAMP_SZ == 16) ? 0 : 8;
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
else if (m_samplesize == 32)
{
FixReal *convertBuf = (FixReal *) m_convertBuf;
const uint32_t *fileBuf = (uint32_t *) buf;
int nbSamples;
if (m_metaInfo->m_dataType.m_complex)
{
nbSamples = nbBytes / (2 * m_samplebytes);
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = (fileBuf[2*is] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16)
- ((SDR_RX_SAMP_SZ == 24) ? (1<<23) : (1<<15));
convertBuf[2*is+1] = (fileBuf[2*is+1] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16)
- ((SDR_RX_SAMP_SZ == 24) ? (1<<23) : (1<<15));;
}
}
else
{
nbSamples = nbBytes / m_samplebytes;
for (int is = 0; is < nbSamples; is++)
{
convertBuf[2*is] = (fileBuf[is] >> (SDR_RX_SAMP_SZ == 24) ? 8 : 16)
- ((SDR_RX_SAMP_SZ == 24) ? (1<<23) : (1<<15));
convertBuf[2*is+1] = 0;
}
}
m_sampleFifo->write((quint8*) convertBuf, nbSamples*sizeof(Sample));
}
}
}

View File

@ -0,0 +1,134 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEINPUTTHREAD_H
#define INCLUDE_SIGMFFILEINPUTTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QTimer>
#include <QElapsedTimer>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include "dsp/inthalfbandfilter.h"
#include "util/message.h"
#define FILESOURCE_THROTTLE_MS 50
class SampleSinkFifo;
class MessageQueue;
class SigMFFileCapture;
class SigMFFileMetaInfo;
class SigMFConverterInterface;
class SigMFFileInputThread : public QThread {
Q_OBJECT
public:
class MsgReportEOF : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgReportEOF* create() {
return new MsgReportEOF();
}
private:
MsgReportEOF() :
Message()
{ }
};
class MsgReportTrackChange : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getTrackIndex() const { return m_trackIndex; }
static MsgReportTrackChange* create(int trackIndex) {
return new MsgReportTrackChange(trackIndex);
}
private:
int m_trackIndex;
MsgReportTrackChange(int trackIndex) :
Message(),
m_trackIndex(trackIndex)
{ }
};
SigMFFileInputThread(std::ifstream *samplesStream,
SampleSinkFifo* sampleFifo,
const QTimer& timer,
MessageQueue *fileInputMessageQueue,
QObject* parent = NULL);
~SigMFFileInputThread();
void startWork();
void stopWork();
void setBuffers(std::size_t chunksize);
bool isRunning() const { return m_running; }
quint64 getSamplesCount() const { return m_samplesCount; }
void setSamplesCount(uint64_t samplesCount) { m_samplesCount = samplesCount; }
void setTotalSamples(uint64_t totalSamples) { m_totalSamples = totalSamples; }
void setMetaInformation(const SigMFFileMetaInfo *metaInfo, const QList<SigMFFileCapture> *captures);
void setAccelerationFactor(int accelerationFactor);
void setTrackIndex(int trackIndex);
private:
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
volatile bool m_running;
const SigMFFileMetaInfo *m_metaInfo;
const QList<SigMFFileCapture> *m_captures;
int m_currentTrackIndex;
std::ifstream* m_ifstream;
quint8 *m_fileBuf;
quint8 *m_convertBuf;
std::size_t m_bufsize;
qint64 m_chunksize;
SampleSinkFifo* m_sampleFifo;
uint64_t m_samplesCount;
uint64_t m_totalSamples;
const QTimer& m_timer;
MessageQueue *m_fileInputMessageQueue;
int m_samplerate; //!< File I/Q stream original sample rate
int m_accelerationFactor;
quint64 m_samplesize; //!< File effective sample size in bits (I or Q). Ex: 16, 24.
quint64 m_samplebytes; //!< Number of bytes used to store a I or Q sample. Ex: 2. 4.
qint64 m_throttlems;
QElapsedTimer m_elapsedTimer;
bool m_throttleToggle;
SigMFConverterInterface *m_sigMFConverter;
void run();
//void decimate1(SampleVector::iterator* it, const qint16* buf, qint32 len);
void setSampleRate();
void setConverter();
void writeToSampleFifo(const quint8* buf, qint32 nbBytes);
void writeToSampleFifoBAK(const quint8* buf, qint32 nbBytes);
private slots:
void tick();
};
#endif // INCLUDE_SIGMFFILEINPUTTHREAD_H

View File

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Implementation of static web API adapters used for preset serialization and //
// deserialization //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGDeviceSettings.h"
#include "sigmffileinput.h"
#include "sigmffileinputwebapiadapter.h"
SigMFFileInputWebAPIAdapter::SigMFFileInputWebAPIAdapter()
{}
SigMFFileInputWebAPIAdapter::~SigMFFileInputWebAPIAdapter()
{}
int SigMFFileInputWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setAirspyHfSettings(new SWGSDRangel::SWGAirspyHFSettings());
response.getAirspyHfSettings()->init();
SigMFFileInput::webapiFormatDeviceSettings(response, m_settings);
return 200;
}
int SigMFFileInputWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage)
{
(void) errorMessage;
SigMFFileInput::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Implementation of static web API adapters used for preset serialization and //
// deserialization //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIGMFFILEWEBAPIADAPTER_H
#define INCLUDE_SIGMFFILEWEBAPIADAPTER_H
#include "device/devicewebapiadapter.h"
#include "sigmffileinputsettings.h"
class SigMFFileInputWebAPIAdapter : public DeviceWebAPIAdapter
{
public:
SigMFFileInputWebAPIAdapter();
virtual ~SigMFFileInputWebAPIAdapter();
virtual QByteArray serialize() { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGDeviceSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& deviceSettingsKeys,
SWGSDRangel::SWGDeviceSettings& response, // query + response
QString& errorMessage);
private:
SigMFFileInputSettings m_settings;
};
#endif // INCLUDE_SIGMFFILEWEBAPIADAPTER_H

BIN
sdrgui/resources/info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>info.png</file>
<file>gridpolar.png</file>
<file>gridrect.png</file>
<file>double_arrow_up.png</file>