1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-07-25 11:34:09 -04:00

Deep redesign: Better support for FCD dongles #1

This commit is contained in:
f4exb
2015-09-03 03:57:54 +02:00
parent 3a6f2b5481
commit a909c4c40c
26 changed files with 5953 additions and 9 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ if(LIBUSB_FOUND AND UNIX)
FIND_LIBRARY (LIBASOUND asound)
endif()
if(LIBASOUND AND ASOUNDH)
add_subdirectory(fcd)
add_subdirectory(fcdpro)
endif()
find_package(LibRTLSDR)
+1 -1
View File
@@ -25,7 +25,7 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl
${LIBRTLSDR_INCLUDE_DIR}
${LIBBLADERF_INCLUDE_DIR}
)
#include(${QT_USE_FILE})
+2 -2
View File
@@ -171,7 +171,7 @@ const QString& FCDInput::getDeviceDescription() const
int FCDInput::getSampleRate() const
{
return 96000;
return 192000;
}
quint64 FCDInput::getCenterFrequency() const
@@ -220,7 +220,7 @@ void FCDInput::applySettings(const Settings& settings, bool force)
if (signalChange)
{
DSPSignalNotification *notif = new DSPSignalNotification(96000, m_settings.centerFrequency);
DSPSignalNotification *notif = new DSPSignalNotification(192000, m_settings.centerFrequency);
getOutputMessageQueue()->push(notif);
}
}
+5 -1
View File
@@ -42,7 +42,7 @@ void FCDThread::stopWork()
void FCDThread::run()
{
if ( !OpenSource("hw:CARD=V10") ) // FIXME: original is V10 pro is V20. Make it an option
if ( !OpenSource("hw:CARD=V20") ) // FIXME: pro is V10 pro+ is V20. Make it an option
return;
// TODO: fallback to original fcd
@@ -70,6 +70,7 @@ bool FCDThread::OpenSource(const char* cardname)
if (snd_pcm_open(&fcd_handle, cardname, fcd_stream, 0) < 0)
{
qCritical("FCDThread::OpenSource: cannot open %s", cardname);
return false;
}
@@ -77,10 +78,12 @@ bool FCDThread::OpenSource(const char* cardname)
if (snd_pcm_hw_params_any(fcd_handle, params) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_hw_params_any failed");
fail = true;
}
else if (snd_pcm_hw_params(fcd_handle, params) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_hw_params failed");
fail = true;
// TODO: check actual samplerate, may be crippled firmware
}
@@ -88,6 +91,7 @@ bool FCDThread::OpenSource(const char* cardname)
{
if (snd_pcm_start(fcd_handle) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_start failed");
fail = true;
}
}
+1 -1
View File
@@ -34,7 +34,7 @@
typedef bool BOOL;
//#define FCDPP // FIXME: the Pro / Pro+ switch should be handled better than this!
#define FCDPP // FIXME: the Pro / Pro+ switch should be handled better than this!
const unsigned short _usVID=0x04D8; /*!< USB vendor ID. */
#ifdef FCDPP
const unsigned short _usPID=0xFB31; /*!< USB product ID. */
@@ -0,0 +1,53 @@
project(fcdpro)
set(fcdpro_SOURCES
fcdgui.cpp
fcdinput.cpp
fcdplugin.cpp
fcdserializer.cpp
fcdthread.cpp
)
set(fcdpro_HEADERS
fcdgui.h
fcdinput.h
fcdplugin.h
fcdserializer.h
fcdthread.h
)
set(fcdpro_FORMS
fcdgui.ui
)
include_directories(
.
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl
${CMAKE_SOURCE_DIR}/fcdhid
)
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
#qt4_wrap_cpp(fcdpro_HEADERS_MOC ${fcdpro_HEADERS})
qt5_wrap_ui(fcdpro_FORMS_HEADERS ${fcdpro_FORMS})
add_library(inputfcdpro SHARED
${fcdpro_SOURCES}
${fcdpro_HEADERS_MOC}
${fcdpro_FORMS_HEADERS}
)
target_link_libraries(inputfcdpro
${QT_LIBRARIES}
${LIBUSB_LIBRARIES}
asound
fcdhid
sdrbase
)
qt5_use_modules(inputfcdpro Core Widgets OpenGL Multimedia)
+149
View File
@@ -0,0 +1,149 @@
#include "fcdgui.h"
#include "ui_fcdgui.h"
#include "plugin/pluginapi.h"
#include "gui/colormapper.h"
#include "dsp/dspengine.h"
FCDGui::FCDGui(PluginAPI* pluginAPI, QWidget* parent) :
QWidget(parent),
ui(new Ui::FCDGui),
m_pluginAPI(pluginAPI),
m_settings(),
m_sampleSource(NULL)
{
ui->setupUi(this);
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::ReverseGold));
ui->centerFrequency->setValueRange(7, 64000U, 1700000U);
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
displaySettings();
m_sampleSource = new FCDInput();
DSPEngine::instance()->setSource(m_sampleSource);
}
FCDGui::~FCDGui()
{
delete ui;
}
void FCDGui::destroy()
{
delete this;
}
void FCDGui::setName(const QString& name)
{
setObjectName(name);
}
QString FCDGui::getName() const
{
return objectName();
}
void FCDGui::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
sendSettings();
}
QByteArray FCDGui::serialize() const
{
return m_settings.serialize();
}
bool FCDGui::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data))
{
displaySettings();
sendSettings();
return true;
}
else
{
resetToDefaults();
return false;
}
}
bool FCDGui::handleMessage(const Message& message)
{
return false;
}
void FCDGui::displaySettings()
{
ui->centerFrequency->setValue(m_settings.centerFrequency / 1000);
ui->checkBoxR->setChecked(m_settings.range);
ui->checkBoxG->setChecked(m_settings.gain);
ui->checkBoxB->setChecked(m_settings.bias);
}
void FCDGui::sendSettings()
{
if(!m_updateTimer.isActive())
m_updateTimer.start(100);
}
void FCDGui::on_centerFrequency_changed(quint64 value)
{
m_settings.centerFrequency = value * 1000;
sendSettings();
}
void FCDGui::updateHardware()
{
FCDInput::MsgConfigureFCD* message = FCDInput::MsgConfigureFCD::create(m_settings);
m_sampleSource->getInputMessageQueue()->push(message);
m_updateTimer.stop();
}
void FCDGui::on_checkBoxR_stateChanged(int state)
{
if (state == Qt::Checked) // FIXME: this is for the Pro+ version only!
{
ui->centerFrequency->setValueRange(7, 150U, 240000U);
ui->centerFrequency->setValue(7000);
m_settings.centerFrequency = 7000 * 1000;
m_settings.range = 1;
}
else
{
ui->centerFrequency->setValueRange(7, 64000U, 1900000U);
ui->centerFrequency->setValue(435000);
m_settings.centerFrequency = 435000 * 1000;
m_settings.range = 0;
}
sendSettings();
}
void FCDGui::on_checkBoxG_stateChanged(int state)
{
if (state == Qt::Checked)
{
m_settings.gain = 1;
}
else
{
m_settings.gain = 0;
}
sendSettings();
}
void FCDGui::on_checkBoxB_stateChanged(int state)
{
if (state == Qt::Checked)
{
m_settings.bias = 1;
}
else
{
m_settings.bias = 0;
}
sendSettings();
}
+51
View File
@@ -0,0 +1,51 @@
#ifndef INCLUDE_FCDGUI_H
#define INCLUDE_FCDGUI_H
#include <QTimer>
#include "plugin/plugingui.h"
#include "fcdinput.h"
class PluginAPI;
namespace Ui {
class FCDGui;
}
class FCDGui : public QWidget, public PluginGUI {
Q_OBJECT
public:
explicit FCDGui(PluginAPI* pluginAPI, QWidget* parent = NULL);
virtual ~FCDGui();
void destroy();
void setName(const QString& name);
QString getName() const;
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual bool handleMessage(const Message& message);
private:
Ui::FCDGui* ui;
PluginAPI* m_pluginAPI;
FCDInput::Settings m_settings;
QTimer m_updateTimer;
std::vector<int> m_gains;
SampleSource* m_sampleSource;
void displaySettings();
void sendSettings();
private slots:
void on_centerFrequency_changed(quint64 value);
void on_checkBoxR_stateChanged(int state);
void on_checkBoxG_stateChanged(int state);
void on_checkBoxB_stateChanged(int state);
void updateHardware();
};
#endif // INCLUDE_FCDGUI_H
+144
View File
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FCDGui</class>
<widget class="QWidget" name="FCDGui">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>132</width>
<height>119</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>FunCubeDongle</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_3">
<item>
<spacer name="horizontalSpacer">
<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="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>Monospace</family>
<pointsize>20</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Tuner center frequency in kHz</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutR">
<item>
<widget class="QCheckBox" name="checkBoxR">
<property name="text">
<string>Low Range</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutG">
<item>
<widget class="QCheckBox" name="checkBoxG">
<property name="text">
<string>LNA Gain</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBoxB">
<property name="text">
<string>Bias T</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDial</class>
<extends>QWidget</extends>
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+286
View File
@@ -0,0 +1,286 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// //
// 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 //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
// FIXME: FCD is handled very badly!
#include <QDebug>
#include <string.h>
#include <errno.h>
#include "fcdinput.h"
#include "fcdthread.h"
#include "fcdgui.h"
#include "dsp/dspcommands.h"
#include "fcdserializer.h"
MESSAGE_CLASS_DEFINITION(FCDInput::MsgConfigureFCD, Message)
//MESSAGE_CLASS_DEFINITION(FCDInput::MsgReportFCD, Message)
const uint16_t FCDInput::m_vendorId = 0x04D8;
const uint16_t FCDInput::m_productId = 0xFB31;
const int FCDInput::m_sampleRate = 192000;
const std::string FCDInput::m_deviceName("hw:CARD=V20");
//const uint16_t FCDInput::m_productId = 0xFB56;
//const int FCDInput::m_sampleRate = 96000;
//const std::string FCDInput::m_deviceName("hw:CARD=V10");
FCDInput::Settings::Settings() :
centerFrequency(435000000),
range(0),
gain(0),
bias(0)
{
}
void FCDInput::Settings::resetToDefaults()
{
centerFrequency = 435000000;
range = 0;
gain = 0;
bias = 0;
}
QByteArray FCDInput::Settings::serialize() const
{
FCDSerializer::FCDData data;
data.m_data.m_lnaGain = gain;
data.m_data.m_frequency = centerFrequency;
data.m_range = range;
data.m_bias = bias;
QByteArray byteArray;
FCDSerializer::writeSerializedData(data, byteArray);
return byteArray;
/*
SimpleSerializer s(1);
s.writeU64(1, centerFrequency);
s.writeS32(2, range);
s.writeS32(3, gain);
s.writeS32(4, bias);
return s.final();*/
}
bool FCDInput::Settings::deserialize(const QByteArray& serializedData)
{
FCDSerializer::FCDData data;
bool valid = FCDSerializer::readSerializedData(serializedData, data);
gain = data.m_data.m_lnaGain;
centerFrequency = data.m_data.m_frequency;
range = data.m_range;
bias = data.m_bias;
return valid;
/*
SimpleDeserializer d(data);
if (d.isValid() && d.getVersion() == 1)
{
d.readU64(1, &centerFrequency, 435000000);
d.readS32(2, &range, 0);
d.readS32(3, &gain, 0);
d.readS32(4, &bias, 0);
return true;
}
resetToDefaults();
return true;*/
}
FCDInput::FCDInput() :
m_dev(0),
m_settings(),
m_FCDThread(0),
m_deviceDescription()
{
}
FCDInput::~FCDInput()
{
stop();
}
bool FCDInput::init(const Message& cmd)
{
return false;
}
bool FCDInput::start(int device)
{
qDebug() << "FCDInput::start with device #" << device;
QMutexLocker mutexLocker(&m_mutex);
if (m_FCDThread)
{
return false;
}
m_dev = fcdOpen(m_vendorId, m_productId, device);
if (m_dev == 0)
{
qCritical("FCDInput::start: could not open FCD");
return false;
}
/* Apply settings before streaming to avoid bus contention;
* there is very little spare bandwidth on a full speed USB device.
* Failure is harmless if no device is found */
applySettings(m_settings, true);
if(!m_sampleFifo.setSize(96000*4))
{
qCritical("Could not allocate SampleFifo");
return false;
}
if ((m_FCDThread = new FCDThread(&m_sampleFifo)) == NULL)
{
qFatal("out of memory");
return false;
}
m_deviceDescription = QString("Funcube Dongle");
qDebug("FCDInput::started");
return true;
}
void FCDInput::stop()
{
QMutexLocker mutexLocker(&m_mutex);
if (m_FCDThread)
{
m_FCDThread->stopWork();
// wait for thread to quit ?
delete m_FCDThread;
m_FCDThread = 0;
}
fcdClose(m_dev);
m_dev = 0;
m_deviceDescription.clear();
}
const QString& FCDInput::getDeviceDescription() const
{
return m_deviceDescription;
}
int FCDInput::getSampleRate() const
{
return m_sampleRate;
}
quint64 FCDInput::getCenterFrequency() const
{
return m_settings.centerFrequency;
}
bool FCDInput::handleMessage(const Message& message)
{
if(MsgConfigureFCD::match(message))
{
qDebug() << "FCDInput::handleMessage: MsgConfigureFCD";
MsgConfigureFCD& conf = (MsgConfigureFCD&) message;
applySettings(conf.getSettings(), false);
return true;
}
else
{
return false;
}
}
void FCDInput::applySettings(const Settings& settings, bool force)
{
bool signalChange = false;
if ((m_settings.centerFrequency != settings.centerFrequency))
{
qDebug() << "FCDInput::applySettings: fc: " << settings.centerFrequency;
m_settings.centerFrequency = settings.centerFrequency;
if (m_dev != 0)
{
set_center_freq((double) m_settings.centerFrequency);
}
signalChange = true;
}
if ((m_settings.gain != settings.gain) || force)
{
m_settings.gain = settings.gain;
if (m_dev != 0)
{
set_lna_gain(settings.gain > 0);
}
}
if ((m_settings.bias != settings.bias) || force)
{
m_settings.bias = settings.bias;
if (m_dev != 0)
{
set_bias_t(settings.bias > 0);
}
}
if (signalChange)
{
DSPSignalNotification *notif = new DSPSignalNotification(m_sampleRate, m_settings.centerFrequency);
getOutputMessageQueue()->push(notif);
}
}
void FCDInput::set_center_freq(double freq)
{
if (fcdAppSetFreq(m_dev, freq) == FCD_MODE_NONE)
{
qDebug("No FCD HID found for frquency change");
}
}
void FCDInput::set_bias_t(bool on)
{
quint8 cmd = on ? 1 : 0;
fcdAppSetParam(m_dev, FCD_CMD_APP_SET_BIAS_TEE, &cmd, 1);
}
void FCDInput::set_lna_gain(bool on)
{
quint8 cmd = on ? 1 : 0;
fcdAppSetParam(m_dev, FCD_CMD_APP_SET_LNA_GAIN, &cmd, 1);
}
+98
View File
@@ -0,0 +1,98 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// //
// 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 //
// //
// 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_FCDINPUT_H
#define INCLUDE_FCDINPUT_H
#include "dsp/samplesource.h"
#include "fcdhid.h"
#include <QString>
#include <inttypes.h>
struct fcd_buffer {
void *start;
std::size_t length;
};
class FCDThread;
class FCDInput : public SampleSource {
public:
struct Settings {
Settings();
quint64 centerFrequency;
qint32 range;
qint32 gain;
qint32 bias;
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
class MsgConfigureFCD : public Message {
MESSAGE_CLASS_DECLARATION
public:
const Settings& getSettings() const { return m_settings; }
static MsgConfigureFCD* create(const Settings& settings)
{
return new MsgConfigureFCD(settings);
}
private:
Settings m_settings;
MsgConfigureFCD(const Settings& settings) :
Message(),
m_settings(settings)
{ }
};
FCDInput();
virtual ~FCDInput();
virtual bool init(const Message& cmd);
virtual bool start(int device);
virtual void stop();
virtual const QString& getDeviceDescription() const;
virtual int getSampleRate() const;
virtual quint64 getCenterFrequency() const;
virtual bool handleMessage(const Message& message);
void set_center_freq(double freq);
void set_bias_t(bool on);
void set_lna_gain(bool on);
static const uint16_t m_vendorId; //!< USB vendor ID.
static const uint16_t m_productId; //!< USB product ID.
static const int m_sampleRate;
static const std::string m_deviceName;
private:
void applySettings(const Settings& settings, bool force);
hid_device *m_dev;
QMutex m_mutex;
Settings m_settings;
FCDThread* m_FCDThread;
QString m_deviceDescription;
};
#endif // INCLUDE_FCD_H
+71
View File
@@ -0,0 +1,71 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 //
// //
// 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 <QAction>
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "fcdplugin.h"
#include "fcdgui.h"
const PluginDescriptor FCDPlugin::m_pluginDescriptor = {
QString("FunCube Pro Input"),
QString("---"),
QString("(c) Edouard Griffiths, F4EXB"),
QString("https://github.com/f4exb/sdrangel"),
true,
QString("https://github.com/f4exb/sdrangel")
};
FCDPlugin::FCDPlugin(QObject* parent) :
QObject(parent)
{
}
const PluginDescriptor& FCDPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void FCDPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerSampleSource("org.osmocom.sdr.samplesource.fcdpro", this);
}
PluginInterface::SampleSourceDevices FCDPlugin::enumSampleSources()
{
SampleSourceDevices result;
QString displayedName(QString("Funcube Dongle Pro #1"));
SimpleSerializer s(1);
s.writeS32(1, 0);
result.append(SampleSourceDevice(displayedName, "org.osmocom.sdr.samplesource.fcdpro", s.final()));
return result;
}
PluginGUI* FCDPlugin::createSampleSourcePluginGUI(const QString& sourceName, const QByteArray& address)
{
if(sourceName == "org.osmocom.sdr.samplesource.fcdpro") {
FCDGui* gui = new FCDGui(m_pluginAPI);
m_pluginAPI->setInputGUI(gui);
return gui;
} else {
return NULL;
}
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef INCLUDE_FCDPLUGIN_H
#define INCLUDE_FCDPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FCDPlugin : public QObject, public PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "org.osmocom.sdr.samplesource.fcdpro")
public:
explicit FCDPlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
SampleSourceDevices enumSampleSources();
PluginGUI* createSampleSourcePluginGUI(const QString& sourceName, const QByteArray& address);
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FCDPLUGIN_H
@@ -0,0 +1,68 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 //
// //
// 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 "fcdserializer.h"
void FCDSerializer::writeSerializedData(const FCDData& data, QByteArray& serializedData)
{
QByteArray sampleSourceSerialized;
SampleSourceSerializer::writeSerializedData(data.m_data, sampleSourceSerialized);
SimpleSerializer s(1);
s.writeBlob(1, sampleSourceSerialized);
s.writeS32(2, data.m_bias);
s.writeS32(3, data.m_range);
serializedData = s.final();
}
bool FCDSerializer::readSerializedData(const QByteArray& serializedData, FCDData& data)
{
bool valid = SampleSourceSerializer::readSerializedData(serializedData, data.m_data);
QByteArray sampleSourceSerialized;
SimpleDeserializer d(serializedData);
if (!d.isValid())
{
setDefaults(data);
return false;
}
if (d.getVersion() == SampleSourceSerializer::getSerializerVersion())
{
int intval;
d.readBlob(1, &sampleSourceSerialized);
d.readS32(2, &data.m_bias);
d.readS32(3, &data.m_range);
return SampleSourceSerializer::readSerializedData(sampleSourceSerialized, data.m_data);
}
else
{
setDefaults(data);
return false;
}
}
void FCDSerializer::setDefaults(FCDData& data)
{
data.m_range = 0;
data.m_bias = 0;
}
@@ -0,0 +1,39 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 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 //
// //
// 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_FCD_FCDSERIALIZER_H_
#define PLUGINS_SAMPLESOURCE_FCD_FCDSERIALIZER_H_
#include "util/samplesourceserializer.h"
class FCDSerializer
{
public:
struct FCDData
{
SampleSourceSerializer::Data m_data;
qint32 m_bias;
qint32 m_range;
};
static void writeSerializedData(const FCDData& data, QByteArray& serializedData);
static bool readSerializedData(const QByteArray& serializedData, FCDData& data);
static void setDefaults(FCDData& data);
};
#endif /* PLUGINS_SAMPLESOURCE_FCD_FCDSERIALIZER_H_ */
+150
View File
@@ -0,0 +1,150 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// //
// 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 //
// //
// 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 <stdio.h>
#include <errno.h>
#include "fcdthread.h"
#include "fcdinput.h"
#include "dsp/samplefifo.h"
FCDThread::FCDThread(SampleFifo* sampleFifo, QObject* parent) :
QThread(parent),
fcd_handle(NULL),
m_running(false),
m_convertBuffer(FCD_BLOCKSIZE),
m_sampleFifo(sampleFifo)
{
start();
}
FCDThread::~FCDThread()
{
}
void FCDThread::stopWork()
{
m_running = false;
wait();
}
void FCDThread::run()
{
if ( !OpenSource(FCDInput::m_deviceName.c_str()) )
{
qCritical() << "FCDThread::run: cannot open FCD sound card";
return;
}
// TODO: fallback to original fcd
m_running = true;
while(m_running)
{
if (work(FCD_BLOCKSIZE) < 0)
{
break;
}
}
CloseSource();
}
bool FCDThread::OpenSource(const char* cardname)
{
bool fail = false;
snd_pcm_hw_params_t* params;
//fcd_rate = FCDPP_RATE;
//fcd_channels =2;
//fcd_format = SND_PCM_SFMT_U16_LE;
snd_pcm_stream_t fcd_stream = SND_PCM_STREAM_CAPTURE;
if (fcd_handle)
{
return false;
}
if (snd_pcm_open(&fcd_handle, cardname, fcd_stream, 0) < 0)
{
qCritical("FCDThread::OpenSource: cannot open %s", cardname);
return false;
}
snd_pcm_hw_params_alloca(&params);
if (snd_pcm_hw_params_any(fcd_handle, params) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_hw_params_any failed");
fail = true;
}
else if (snd_pcm_hw_params(fcd_handle, params) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_hw_params failed");
fail = true;
// TODO: check actual samplerate, may be crippled firmware
}
else
{
if (snd_pcm_start(fcd_handle) < 0)
{
qCritical("FCDThread::OpenSource: snd_pcm_start failed");
fail = true;
}
}
if (fail)
{
snd_pcm_close( fcd_handle );
return false;
}
else
{
qDebug("FCDThread::OpenSource: Funcube stream started");
}
return true;
}
void FCDThread::CloseSource()
{
if (fcd_handle)
{
snd_pcm_close( fcd_handle );
}
fcd_handle = NULL;
}
int FCDThread::work(int n_items)
{
int l;
SampleVector::iterator it;
void *out;
it = m_convertBuffer.begin();
out = (void *)&it[0];
l = snd_pcm_mmap_readi(fcd_handle, out, (snd_pcm_uframes_t)n_items);
if (l > 0)
m_sampleFifo->write(it, it + l);
if (l == -EPIPE) {
qDebug("FCD: Overrun detected");
return 0;
}
return l;
}
+56
View File
@@ -0,0 +1,56 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// //
// 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 //
// //
// 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_FCDTHREAD_H
#define INCLUDE_FCDTHREAD_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include "dsp/samplefifo.h"
#include "dsp/inthalfbandfilter.h"
#include <alsa/asoundlib.h>
#define FCDPP_RATE 192000 // FIXME: The Pro / Pro+ switch should be handled better than this!
#define FCD_BLOCKSIZE (1<<11)
class FCDThread : public QThread {
Q_OBJECT
public:
FCDThread(SampleFifo* sampleFifo, QObject* parent = NULL);
~FCDThread();
void stopWork();
bool OpenSource(const char *filename);
void CloseSource();
int work(int n_items);
private:
snd_pcm_format_t fcd_format;
snd_pcm_t* fcd_handle;
QMutex m_startWaitMutex;
QWaitCondition m_startWaiter;
bool m_running;
SampleVector m_convertBuffer;
SampleFifo* m_sampleFifo;
void run();
};
#endif // INCLUDE_FCDTHREAD_H