1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 09:18:54 -05: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

View File

@ -329,3 +329,4 @@ qt5_use_modules(sdrangel Widgets Multimedia)
############################################################################## ##############################################################################
add_subdirectory(plugins) add_subdirectory(plugins)
add_subdirectory(fcdhid)

View File

@ -18,11 +18,16 @@ These plugins come from the parent code base and are still present in the source
- Channels: - Channels:
- tetra - tetra
- Sample sources: - Sample sources:
- fcd
- gnuradio - gnuradio
- osmosdr - osmosdr
- v4l-msi - v4l-msi
- v4l-rtl - v4l-rtl
<h3>Funcube Dongle (fcd)</h3>
This is the old driver.
<h3>Gnuradio</h3> <h3>Gnuradio</h3>
The Gnuradio plugin source needs extra packages, including `liblog4cpp-dev libboost-system-dev gnuradio-dev libosmosdr-dev` The Gnuradio plugin source needs extra packages, including `liblog4cpp-dev libboost-system-dev gnuradio-dev libosmosdr-dev`
@ -51,11 +56,11 @@ If you use your own location for libbladeRF install directory you need to specif
`-DLIBBLADERF_LIBRARIES=/opt/install/libbladeRF/lib/libbladeRF.so -DLIBBLADERF_INCLUDE_DIR=/opt/install/libbladeRF/include` `-DLIBBLADERF_LIBRARIES=/opt/install/libbladeRF/lib/libbladeRF.so -DLIBBLADERF_INCLUDE_DIR=/opt/install/libbladeRF/include`
<h2>Funcube Dongle</h2> <h2>FunCube Dongle</h2>
Only the original Funcube Dongle Pro is supported. Funcube Dongle Pro+ will come later. At the moment only the Pro+ is supported with the plugin in fcdpro. This is a work in progress. Support of features is still limited (no IF gain, no filter settings).
The interface is built in the software and do not require additional libraries other than USB support with libusb. The control interface is based on qthid and has been built in the software in the fcdhid library. You don't need anything else than libusb support.
<h2>RTL-SDR</h2> <h2>RTL-SDR</h2>

30
fcdhid/CMakeLists.txt Normal file
View File

@ -0,0 +1,30 @@
project(fcdhid)
set(fcdhid_SOURCES
hid-libusb.c
fcdhid.c
)
set(fcdhid_HEADERS
fcdhid.h
hid-libusb.h
hidapi.h
)
include_directories(
.
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl
)
#add_definitions(-DQT_PLUGIN)
add_definitions(-DQT_SHARED)
add_library(fcdhid SHARED
${fcdhid_SOURCES}
)
target_link_libraries(fcdhid
${LIBUSB_LIBRARIES}
)

927
fcdhid/fcdhid.c Normal file
View File

@ -0,0 +1,927 @@
/***************************************************************************
* This file is part of Qthid.
*
* Copyright (C) 2010 Howard Long, G6LVB
* CopyRight (C) 2011 Alexandru Csete, OZ9AEC
* Mario Lorenz, DL5MLO
*
* Qthid 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, either version 3 of the License, or
* (at your option) any later version.
*
* Qthid 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with Qthid. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************/
#include "fcdhid.h"
/** \brief Open FCD device.
* \return Pointer to the FCD HID device or NULL if none found
*
* This function looks for FCD devices connected to the computer and
* opens the first one found.
*/
hid_device *fcdOpen(uint16_t usVID, uint16_t usPID, int whichdongle)
{
struct hid_device_info *phdi=NULL;
hid_device *phd=NULL;
char *pszPath=NULL;
phdi = hid_enumerate(usVID, usPID);
int which = whichdongle;
while (phdi && which)
{
phdi=phdi->next;
which--;
}
if (phdi==NULL)
{
return NULL; // No FCD device found
}
pszPath=strdup(phdi->path);
if (pszPath==NULL)
{
return NULL;
}
hid_free_enumeration(phdi);
phdi=NULL;
if ((phd=hid_open_path(pszPath)) == NULL)
{
free(pszPath);
pszPath=NULL;
return NULL;
}
free(pszPath);
pszPath=NULL;
return phd;
}
/** \brief Close FCD HID device. */
void fcdClose(hid_device *phd)
{
hid_close(phd);
}
/** \brief Get FCD mode.
* \return The current FCD mode.
* \sa FCD_MODE_ENUM
*/
FCD_MODE_ENUM fcdGetMode(hid_device *phd)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
FCD_MODE_ENUM fcd_mode = FCD_MODE_NONE;
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_DEAD;
}*/
/* Send a BL Query Command */
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_QUERY;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
/*
fcdClose(phd);
phd = NULL;*/
/* first check status bytes then check which mode */
if (aucBufIn[0]==FCD_CMD_BL_QUERY && aucBufIn[1]==1) {
/* In bootloader mode we have the string "FCDBL" starting at acBufIn[2] **/
if (strncmp((char *)(aucBufIn+2), "FCDBL", 5) == 0) {
fcd_mode = FCD_MODE_BL;
}
/* In application mode we have "FCDAPP_18.06" where the number is the FW version */
else if (strncmp((char *)(aucBufIn+2), "FCDAPP", 6) == 0) {
fcd_mode = FCD_MODE_APP;
}
/* either no FCD or firmware less than 18f */
else {
fcd_mode = FCD_MODE_NONE;
}
}
return fcd_mode;
}
/** \brief Get FCD firmware version as string.
* \param str The returned vesion number as a 0 terminated string (must be pre-allocated)
* \return The current FCD mode.
* \sa FCD_MODE_ENUM
*/
FCD_MODE_ENUM fcdGetFwVerStr(hid_device *phd, char *str)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
FCD_MODE_ENUM fcd_mode = FCD_MODE_NONE;
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
/* Send a BL Query Command */
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_QUERY;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
/*
fcdClose(phd);
phd = NULL;*/
/* first check status bytes then check which mode */
if (aucBufIn[0]==FCD_CMD_BL_QUERY && aucBufIn[1]==1) {
/* In bootloader mode we have the string "FCDBL" starting at acBufIn[2] **/
if (strncmp((char *)(aucBufIn+2), "FCDBL", 5) == 0) {
fcd_mode = FCD_MODE_BL;
}
/* In application mode we have "FCDAPP_18.06" where the number is the FW version */
else if (strncmp((char *)(aucBufIn+2), "FCDAPP", 6) == 0) {
strncpy(str, (char *)(aucBufIn+9), 5);
str[5] = 0;
fcd_mode = FCD_MODE_APP;
}
/* either no FCD or firmware less than 18f */
else {
fcd_mode = FCD_MODE_NONE;
}
}
return fcd_mode;
}
/** \brief Get hardware and firmware dependent FCD capabilities.
* \param fcd_caps Pointer to an FCD_CAPS_STRUCT
* \return The current FCD mode.
*
* This function queries the FCD and extracts the hardware and firmware dependent
* capabilities. Currently these capabilities are:
* - Bias T (available since S/N TBD)
* - Cellular block (the certified version of the FCD)
* When the FCD is in application mode, the string returned by the query command is
* (starting at index 2):
* FCDAPP 18.08 Brd 1.0 No blk
* 1.0 means no bias tee, 1.1 means there is a bias tee
* 'No blk' means it is not cellular blocked.
*
* Ref: http://uk.groups.yahoo.com/group/FCDevelopment/message/303
*/
FCD_MODE_ENUM fcdGetCaps(hid_device *phd, FCD_CAPS_STRUCT *fcd_caps)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
FCD_MODE_ENUM fcd_mode = FCD_MODE_NONE;
/* clear output buffer */
fcd_caps->hasBiasT = 0;
fcd_caps->hasCellBlock = 0;
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
/* Send a BL Query Command */
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_QUERY;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
/*
fcdClose(phd);
phd = NULL;*/
/* first check status bytes then check which mode */
if (aucBufIn[0]==FCD_CMD_BL_QUERY && aucBufIn[1]==1) {
/* In bootloader mode we have the string "FCDBL" starting at acBufIn[2] **/
if (strncmp((char *)(aucBufIn+2), "FCDBL", 5) == 0) {
fcd_mode = FCD_MODE_BL;
}
/* In application mode we have "FCDAPP 18.08 Brd 1.0 No blk" (see API doc) */
else if (strncmp((char *)(aucBufIn+2), "FCDAPP", 6) == 0) {
/* Bias T */
fcd_caps->hasBiasT = (aucBufIn[21] == '1') ? 1 : 0;
/* cellular block */
if (strncmp((char *)(aucBufIn+23), "No blk", 6) == 0) {
fcd_caps->hasCellBlock = 0;
} else {
fcd_caps->hasCellBlock = 1;
}
fcd_mode = FCD_MODE_APP;
}
/* either no FCD or firmware less than 18f */
else {
fcd_mode = FCD_MODE_NONE;
}
}
return fcd_mode;
}
/** \brief Get hardware and firmware dependent FCD capabilities as string.
* \param caps_str Pointer to a pre-allocated string buffer where the info will be copied.
* \return The current FCD mode.
*
* This function queries the FCD and copies the returned string into the caps_str parameter.
* THe return buffer must be at least 28 characters.
* When the FCD is in application mode, the string returned by the query command is
* (starting at index 2):
* FCDAPP 18.08 Brd 1.0 No blk
* 1.0 means no bias tee, 1.1 means there is a bias tee
* 'No blk' means it is not cellular blocked.
*
* Ref: http://uk.groups.yahoo.com/group/FCDevelopment/message/303
*/
FCD_MODE_ENUM fcdGetCapsStr(hid_device *phd, char *caps_str)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
FCD_MODE_ENUM fcd_mode = FCD_MODE_NONE;
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
/* Send a BL Query Command */
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_QUERY;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
/*
fcdClose(phd);
phd = NULL;*/
/* first check status bytes then check which mode */
if (aucBufIn[0]==FCD_CMD_BL_QUERY && aucBufIn[1]==1) {
/* In bootloader mode we have the string "FCDBL" starting at acBufIn[2] **/
if (strncmp((char *)(aucBufIn+2), "FCDBL", 5) == 0) {
fcd_mode = FCD_MODE_BL;
}
/* In application mode we have "FCDAPP 18.08 Brd 1.0 No blk" (see API doc) */
else if (strncmp((char *)(aucBufIn+2), "FCDAPP", 6) == 0) {
strncpy(caps_str, (char *)(aucBufIn+2), 27);
caps_str[27] = 0;
fcd_mode = FCD_MODE_APP;
}
/* either no FCD or firmware less than 18f */
else {
fcd_mode = FCD_MODE_NONE;
}
}
return fcd_mode;
}
/** \brief Reset FCD to bootloader mode.
* \return FCD_MODE_NONE
*
* This function is used to switch the FCD into bootloader mode in which
* various firmware operations can be performed.
*/
FCD_MODE_ENUM fcdAppReset(hid_device *phd)
{
//hid_device *phd=NULL;
//unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
// Send an App reset command
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_APP_RESET;
hid_write(phd, aucBufOut, 65);
/** FIXME: hid_read() will occasionally hang due to a pthread_cond_wait() never returning.
It seems that the read_callback() in hid-libusb.c will never receive any
data during the reconfiguration. Since the same logic works in the native
windows application, it could be a libusb thing. Anyhow, since the value
returned by this function is not used, we may as well just skip the hid_read()
and return FME_NONE.
Correct switch from APP to BL mode can be observed in /var/log/messages (linux)
(when in bootloader mode the device version includes 'BL')
*/
/*
memset(aucBufIn,0xCC,65); // Clear out the response buffer
hid_read(phd,aucBufIn,65);
if (aucBufIn[0]==FCDCMDAPPRESET && aucBufIn[1]==1)
{
FCDClose(phd);
phd=NULL;
return FME_APP;
}
FCDClose(phd);
phd=NULL;
return FME_BL;
*/
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_NONE;
}
/** \brief Set FCD frequency with kHz resolution.
* \param nFreq The new frequency in kHz.
* \return The FCD mode.
*
* This function sets the frequency of the FCD with 1 kHz resolution. The parameter
* nFreq must already contain any necessary frequency correction.
*
* \sa fcdAppSetFreq
*/
FCD_MODE_ENUM fcdAppSetFreqkHz(hid_device *phd, int nFreq)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
// Send an App reset command
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_APP_SET_FREQ_KHZ;
aucBufOut[2] = (unsigned char)nFreq;
aucBufOut[3] = (unsigned char)(nFreq>>8);
aucBufOut[4] = (unsigned char)(nFreq>>16);
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]==FCD_CMD_APP_SET_FREQ_KHZ && aucBufIn[1]==1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/** \brief Set FCD frequency with Hz resolution.
* \param nFreq The new frequency in Hz.
* \return The FCD mode.
*
* This function sets the frequency of the FCD with 1 Hz resolution. The parameter
* nFreq must already contain any necessary frequency correction.
*
* \sa fcdAppSetFreq
*/
FCD_MODE_ENUM fcdAppSetFreq(hid_device *phd, int nFreq)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
// Send an App reset command
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_APP_SET_FREQ_HZ;
aucBufOut[2] = (unsigned char)nFreq;
aucBufOut[3] = (unsigned char)(nFreq>>8);
aucBufOut[4] = (unsigned char)(nFreq>>16);
aucBufOut[5] = (unsigned char)(nFreq>>24);
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]==FCD_CMD_APP_SET_FREQ_HZ && aucBufIn[1]==1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/** \brief Reset FCD to application mode.
* \return FCD_MODE_NONE
*
* This function is used to switch the FCD from bootloader mode
* into application mode.
*/
FCD_MODE_ENUM fcdBlReset(hid_device *phd)
{
//hid_device *phd=NULL;
// unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
// Send an BL reset command
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_RESET;
hid_write(phd, aucBufOut, 65);
/** FIXME: hid_read() will hang due to a pthread_cond_wait() never returning.
It seems that the read_callback() in hid-libusb.c will never receive any
data during the reconfiguration. Since the same logic works in the native
windows application, it could be a libusb thing. Anyhow, since the value
returned by this function is not used, we may as well jsut skip the hid_read()
and return FME_NONE.
Correct switch from BL to APP mode can be observed in /var/log/messages (linux)
(when in bootloader mode the device version includes 'BL')
*/
/*
memset(aucBufIn,0xCC,65); // Clear out the response buffer
hid_read(phd,aucBufIn,65);
if (aucBufIn[0]==FCDCMDBLRESET && aucBufIn[1]==1)
{
FCDClose(phd);
phd=NULL;
return FME_BL;
}
FCDClose(phd);
phd=NULL;
return FME_APP;
*/
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_NONE;
}
/** \brief Erase firmware from FCD.
* \return The FCD mode
*
* This function deletes the firmware from the FCD. This is required
* before writing new firmware into the FCD.
*
* \sa fcdBlWriteFirmware
*/
FCD_MODE_ENUM fcdBlErase(hid_device *phd)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
// Send an App reset command
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_ERASE;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]==FCD_CMD_BL_ERASE && aucBufIn[1]==1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
/** \brief Write new firmware into the FCD.
* \param pc Pointer to the new firmware data
* \param n64size The number of bytes in the data
* \return The FCD mode
*
* This function is used to upload new firmware into the FCD flash.
*
* \sa fcdBlErase
*/
FCD_MODE_ENUM fcdBlWriteFirmware(hid_device *phd, char *pc, int64_t n64Size)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
uint32_t u32AddrStart;
uint32_t u32AddrEnd;
uint32_t u32Addr;
BOOL bFinished=FALSE;
/*
phd = fcdOpen();
if (phd==NULL)
{
return FCD_MODE_NONE;
}*/
// Get the valid flash address range
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_GET_BYTE_ADDR_RANGE;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_GET_BYTE_ADDR_RANGE || aucBufIn[1]!=1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
u32AddrStart=
aucBufIn[2]+
(((uint32_t)aucBufIn[3])<<8)+
(((uint32_t)aucBufIn[4])<<16)+
(((uint32_t)aucBufIn[5])<<24);
u32AddrEnd=
aucBufIn[6]+
(((uint32_t)aucBufIn[7])<<8)+
(((uint32_t)aucBufIn[8])<<16)+
(((uint32_t)aucBufIn[9])<<24);
// Set start address for flash
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_SET_BYTE_ADDR;
aucBufOut[2] = ((unsigned char)u32AddrStart);
aucBufOut[3] = ((unsigned char)(u32AddrStart>>8));
aucBufOut[4] = ((unsigned char)(u32AddrStart>>16));
aucBufOut[5] = ((unsigned char)(u32AddrStart>>24));
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_SET_BYTE_ADDR || aucBufIn[1]!=1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
// Write blocks
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_WRITE_FLASH_BLOCK;
for (u32Addr=u32AddrStart; u32Addr+47<u32AddrEnd && u32Addr+47<n64Size && !bFinished; u32Addr+=48)
{
memcpy(&aucBufOut[3], &pc[u32Addr], 48);
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_WRITE_FLASH_BLOCK || aucBufIn[1]!=1)
{
bFinished = TRUE;
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
}
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/** \brief Verify firmware in FCd flash.
* \param pc Pointer to firmware data to verify against.
* \param n64Size Size of the data in pc.
* \return The FCD_MODE_BL if verification was succesful.
*
* This function verifies the firmware currently in the FCd flash against the firmware
* image pointed to by pc. The function return FCD_MODE_BL if the verification is OK and
* FCD_MODE_APP otherwise.
*/
FCD_MODE_ENUM fcdBlVerifyFirmware(hid_device *phd, char *pc, int64_t n64Size)
{
//hid_device *phd=NULL;
unsigned char aucBufIn[65];
unsigned char aucBufOut[65];
uint32_t u32AddrStart;
uint32_t u32AddrEnd;
uint32_t u32Addr;
BOOL bFinished=FALSE;
/*
phd = fcdOpen();
if (phd==NULL)
{
return FCD_MODE_NONE;
}*/
// Get the valid flash address range
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_GET_BYTE_ADDR_RANGE;
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_GET_BYTE_ADDR_RANGE || aucBufIn[1]!=1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
u32AddrStart=
aucBufIn[2]+
(((uint32_t)aucBufIn[3])<<8)+
(((uint32_t)aucBufIn[4])<<16)+
(((uint32_t)aucBufIn[5])<<24);
u32AddrEnd=
aucBufIn[6]+
(((uint32_t)aucBufIn[7])<<8)+
(((uint32_t)aucBufIn[8])<<16)+
(((uint32_t)aucBufIn[9])<<24);
// Set start address for flash
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_SET_BYTE_ADDR;
aucBufOut[2] = ((unsigned char)u32AddrStart);
aucBufOut[3] = ((unsigned char)(u32AddrStart>>8));
aucBufOut[4] = ((unsigned char)(u32AddrStart>>16));
aucBufOut[5] = ((unsigned char)(u32AddrStart>>24));
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_SET_BYTE_ADDR || aucBufIn[1]!=1)
{
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
// Read blocks
aucBufOut[0] = 0; // Report ID, ignored
aucBufOut[1] = FCD_CMD_BL_READ_FLASH_BLOCK;
for (u32Addr=u32AddrStart; u32Addr+47<u32AddrEnd && u32Addr+47<n64Size && !bFinished; u32Addr+=48)
{
hid_write(phd, aucBufOut, 65);
memset(aucBufIn, 0xCC, 65); // Clear out the response buffer
hid_read(phd, aucBufIn, 65);
if (aucBufIn[0]!=FCD_CMD_BL_READ_FLASH_BLOCK || aucBufIn[1]!=1)
{
bFinished = TRUE;
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
if (memcmp(&aucBufIn[2],&pc[u32Addr],48)!=0)
{
bFinished = TRUE;
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
}
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/** \brief Write FCD parameter (e.g. gain or filter)
* \param u8Cmd The command byte / parameter ID, see FCD_CMD_APP_SET_*
* \param pu8Data The parameter value to be written
* \param u8len Length of pu8Data in bytes
* \return One of FCD_MODE_NONE, FCD_MODE_APP or FCD_MODE_BL (see description)
*
* This function can be used to set the value of a parameter in the FCD for which there is no
* high level API call. It gives access to the low level API of the FCD and the caller is expected
* to be aware of the various FCD commands, since they are required to be supplied as parameter
* to this function.
*
* The return value can be used to determine the success or failure of the command execution:
* - FCD_MODE_APP : Reply from FCD was as expected (nominal case).
* - FCD_MODE_BL : Reply from FCD was not as expected.
* - FCD_MODE_NONE : No FCD was found
*/
FCD_MODE_ENUM fcdAppSetParam(hid_device *phd, uint8_t u8Cmd, uint8_t *pu8Data, uint8_t u8len)
{
//hid_device *phd=NULL;
unsigned char aucBufOut[65];
unsigned char aucBufIn[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
aucBufOut[0]=0; // Report ID, ignored
aucBufOut[1]=u8Cmd;
memcpy(aucBufOut+2, pu8Data,u8len);
hid_write(phd,aucBufOut,65);
/* we must read after each write in order to empty FCD/HID buffer */
memset(aucBufIn,0xCC,65); // Clear out the response buffer
hid_read(phd,aucBufIn,65);
/* Check the response, if OK return FCD_MODE_APP */
if (aucBufIn[0]==u8Cmd && aucBufIn[1]==1) {
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
/* Response did not contain the expected bytes */
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}
/** \brief Read FCD parameter (e.g. gain or filter)
* \param u8Cmd The command byte / parameter ID, see FCD_CMD_APP_GET_*
* \param pu8Data TPointer to buffer where the parameter value(s) will be written
* \param u8len Length of pu8Data in bytes
* \return One of FCD_MODE_NONE, FCD_MODE_APP or FCD_MODE_BL (see description)
*
* This function can be used to read the value of a parameter in the FCD for which there is no
* high level API call. It gives access to the low level API of the FCD and the caller is expected
* to be aware of the various FCD commands, since they are required to be supplied as parameter
* to this function.
*
* The return value can be used to determine the success or failure of the command execution:
* - FCD_MODE_APP : Reply from FCD was as expected (nominal case).
* - FCD_MODE_BL : Reply from FCD was not as expected.
* - FCD_MODE_NONE : No FCD was found
*/
FCD_MODE_ENUM fcdAppGetParam(hid_device *phd, uint8_t u8Cmd, uint8_t *pu8Data, uint8_t u8len)
{
//hid_device *phd=NULL;
unsigned char aucBufOut[65];
unsigned char aucBufIn[65];
/*
phd = fcdOpen();
if (phd == NULL)
{
return FCD_MODE_NONE;
}*/
aucBufOut[0]=0; // Report ID, ignored
aucBufOut[1]=u8Cmd;
hid_write(phd,aucBufOut,65);
memset(aucBufIn,0xCC,65); // Clear out the response buffer
hid_read(phd,aucBufIn,65);
/* Copy return data to output buffer (even if cmd exec failed) */
memcpy(pu8Data,aucBufIn+2,u8len);
/* Check status bytes in returned data */
if (aucBufIn[0]==u8Cmd && aucBufIn[1]==1) {
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_APP;
}
/* Response did not contain the expected bytes */
/*
fcdClose(phd);
phd = NULL;*/
return FCD_MODE_BL;
}

234
fcdhid/fcdhid.h Normal file
View File

@ -0,0 +1,234 @@
/***************************************************************************
* This file is part of Qthid.
*
* Copyright (C) 2010 Howard Long, G6LVB
* CopyRight (C) 2011 Alexandru Csete, OZ9AEC
* Mario Lorenz, DL5MLO
*
* Qthid 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, either version 3 of the License, or
* (at your option) any later version.
*
* Qthid 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with Qthid. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************/
#ifndef _QTHID_H_
#define _QTHID_H_
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "fcdhidcmd.h"
#include "hidapi.h"
#include <inttypes.h>
#define FALSE 0
#define TRUE 1
typedef int BOOL;
/** \brief FCD mode enumeration. */
typedef enum {
FCD_MODE_NONE, /*!< No FCD detected. */
FCD_MODE_DEAD,
FCD_MODE_BL, /*!< FCD present in bootloader mode. */
FCD_MODE_APP /*!< FCD present in aplpication mode. */
} FCD_MODE_ENUM; // The current mode of the FCD: none inserted, in bootloader mode or in normal application mode
/** \brief FCD capabilities that depend on both hardware and firmware. */
typedef struct {
unsigned char hasBiasT; /*!< Whether FCD has hardware bias tee (1=yes, 0=no) */
unsigned char hasCellBlock; /*!< Whether FCD has cellular blocking. */
} FCD_CAPS_STRUCT;
//#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. */
//#else
//const unsigned short _usPID=0xFB56; /*!< USB product ID. */
//#endif
#ifdef __cplusplus
extern "C" {
#endif
/** \brief Open FCD device.
* \return Pointer to the FCD HID device or NULL if none found
*
* This function looks for FCD devices connected to the computer and
* opens the first one found.
*/
hid_device *fcdOpen(uint16_t usVID, uint16_t usPID, int whichdongle);
/** \brief Close FCD HID device. */
void fcdClose(hid_device *phd);
/** \brief Get FCD mode.
* \return The current FCD mode.
* \sa FCD_MODE_ENUM
*/
FCD_MODE_ENUM fcdGetMode(hid_device *phd);
/** \brief Get FCD firmware version as string.
* \param str The returned vesion number as a 0 terminated string (must be pre-allocated)
* \return The current FCD mode.
* \sa FCD_MODE_ENUM
*/
FCD_MODE_ENUM fcdGetFwVerStr(hid_device *phd, char *str);
/** \brief Get hardware and firmware dependent FCD capabilities.
* \param fcd_caps Pointer to an FCD_CAPS_STRUCT
* \return The current FCD mode.
*
* This function queries the FCD and extracts the hardware and firmware dependent
* capabilities. Currently these capabilities are:
* - Bias T (available since S/N TBD)
* - Cellular block (the certified version of the FCD)
* When the FCD is in application mode, the string returned by the query command is
* (starting at index 2):
* FCDAPP 18.08 Brd 1.0 No blk
* 1.0 means no bias tee, 1.1 means there is a bias tee
* 'No blk' means it is not cellular blocked.
*
* Ref: http://uk.groups.yahoo.com/group/FCDevelopment/message/303
*/
FCD_MODE_ENUM fcdGetCaps(hid_device *phd, FCD_CAPS_STRUCT *fcd_caps);
/** \brief Get hardware and firmware dependent FCD capabilities as string.
* \param caps_str Pointer to a pre-allocated string buffer where the info will be copied.
* \return The current FCD mode.
*
* This function queries the FCD and copies the returned string into the caps_str parameter.
* THe return buffer must be at least 28 characters.
* When the FCD is in application mode, the string returned by the query command is
* (starting at index 2):
* FCDAPP 18.08 Brd 1.0 No blk
* 1.0 means no bias tee, 1.1 means there is a bias tee
* 'No blk' means it is not cellular blocked.
*
* Ref: http://uk.groups.yahoo.com/group/FCDevelopment/message/303
*/
FCD_MODE_ENUM fcdGetCapsStr(hid_device *phd, char *caps_str);
/** \brief Reset FCD to bootloader mode.
* \return FCD_MODE_NONE
*
* This function is used to switch the FCD into bootloader mode in which
* various firmware operations can be performed.
*/
FCD_MODE_ENUM fcdAppReset(hid_device *phd);
/** \brief Set FCD frequency with kHz resolution.
* \param nFreq The new frequency in kHz.
* \return The FCD mode.
*
* This function sets the frequency of the FCD with 1 kHz resolution. The parameter
* nFreq must already contain any necessary frequency correction.
*
* \sa fcdAppSetFreq
*/
FCD_MODE_ENUM fcdAppSetFreqkHz(hid_device *phd, int nFreq);
/** \brief Set FCD frequency with Hz resolution.
* \param nFreq The new frequency in Hz.
* \return The FCD mode.
*
* This function sets the frequency of the FCD with 1 Hz resolution. The parameter
* nFreq must already contain any necessary frequency correction.
*
* \sa fcdAppSetFreq
*/
FCD_MODE_ENUM fcdAppSetFreq(hid_device *phd, int nFreq);
/** \brief Reset FCD to application mode.
* \return FCD_MODE_NONE
*
* This function is used to switch the FCD from bootloader mode
* into application mode.
*/
FCD_MODE_ENUM fcdBlReset(hid_device *phd);
/** \brief Erase firmware from FCD.
* \return The FCD mode
*
* This function deletes the firmware from the FCD. This is required
* before writing new firmware into the FCD.
*
* \sa fcdBlWriteFirmware
*/
FCD_MODE_ENUM fcdBlErase(hid_device *phd);
/** \brief Write new firmware into the FCD.
* \param pc Pointer to the new firmware data
* \param n64size The number of bytes in the data
* \return The FCD mode
*
* This function is used to upload new firmware into the FCD flash.
*
* \sa fcdBlErase
*/
FCD_MODE_ENUM fcdBlWriteFirmware(hid_device *phd, char *pc, int64_t n64Size);
/** \brief Verify firmware in FCd flash.
* \param pc Pointer to firmware data to verify against.
* \param n64Size Size of the data in pc.
* \return The FCD_MODE_BL if verification was succesful.
*
* This function verifies the firmware currently in the FCd flash against the firmware
* image pointed to by pc. The function return FCD_MODE_BL if the verification is OK and
* FCD_MODE_APP otherwise.
*/
FCD_MODE_ENUM fcdBlVerifyFirmware(hid_device *phd, char *pc, int64_t n64Size);
/** \brief Write FCD parameter (e.g. gain or filter)
* \param u8Cmd The command byte / parameter ID, see FCD_CMD_APP_SET_*
* \param pu8Data The parameter value to be written
* \param u8len Length of pu8Data in bytes
* \return One of FCD_MODE_NONE, FCD_MODE_APP or FCD_MODE_BL (see description)
*
* This function can be used to set the value of a parameter in the FCD for which there is no
* high level API call. It gives access to the low level API of the FCD and the caller is expected
* to be aware of the various FCD commands, since they are required to be supplied as parameter
* to this function.
*
* The return value can be used to determine the success or failure of the command execution:
* - FCD_MODE_APP : Reply from FCD was as expected (nominal case).
* - FCD_MODE_BL : Reply from FCD was not as expected.
* - FCD_MODE_NONE : No FCD was found
*/
FCD_MODE_ENUM fcdAppSetParam(hid_device *phd, uint8_t u8Cmd, uint8_t *pu8Data, uint8_t u8len);
/** \brief Read FCD parameter (e.g. gain or filter)
* \param u8Cmd The command byte / parameter ID, see FCD_CMD_APP_GET_*
* \param pu8Data TPointer to buffer where the parameter value(s) will be written
* \param u8len Length of pu8Data in bytes
* \return One of FCD_MODE_NONE, FCD_MODE_APP or FCD_MODE_BL (see description)
*
* This function can be used to read the value of a parameter in the FCD for which there is no
* high level API call. It gives access to the low level API of the FCD and the caller is expected
* to be aware of the various FCD commands, since they are required to be supplied as parameter
* to this function.
*
* The return value can be used to determine the success or failure of the command execution:
* - FCD_MODE_APP : Reply from FCD was as expected (nominal case).
* - FCD_MODE_BL : Reply from FCD was not as expected.
* - FCD_MODE_NONE : No FCD was found
*/
FCD_MODE_ENUM fcdAppGetParam(hid_device *phd, uint8_t u8Cmd, uint8_t *pu8Data, uint8_t u8len);
#ifdef __cplusplus
}
#endif
#endif // _QTHID_H_

305
fcdhid/fcdhidcmd.h Normal file
View File

@ -0,0 +1,305 @@
/***************************************************************************
* This file is part of Qthid.
*
* Copyright (C) 2010 Howard Long, G6LVB
* CopyRight (C) 2011 Alexandru Csete, OZ9AEC
* Mario Lorenz, DL5MLO
*
* Qthid 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, either version 3 of the License, or
* (at your option) any later version.
*
* Qthid 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with Qthid. If not, see <http://www.gnu.org/licenses/>.
*
***************************************************************************/
#ifndef FCDHIDCMD_H
#define FCDHIDCMD_H
/* Commands applicable in bootloader mode */
#define FCD_CMD_BL_QUERY 1 /*!< Returns string with "FCDAPP version". */
#define FCD_CMD_BL_RESET 8 /*!< Reset to application mode. */
#define FCD_CMD_BL_ERASE 24 /*!< Erase firmware from FCD flash. */
#define FCD_CMD_BL_SET_BYTE_ADDR 25 /*!< TBD */
#define FCD_CMD_BL_GET_BYTE_ADDR_RANGE 26 /*!< Get address range. */
#define FCD_CMD_BL_WRITE_FLASH_BLOCK 27 /*!< Write flash block. */
#define FCD_CMD_BL_READ_FLASH_BLOCK 28 /*!< Read flash block. */
/* Commands applicable in application mode */
#define FCD_CMD_APP_SET_FREQ_KHZ 100 /*!< Send with 3 byte unsigned little endian frequency in kHz. */
#define FCD_CMD_APP_SET_FREQ_HZ 101 /*!< Send with 4 byte unsigned little endian frequency in Hz, returns with actual frequency set in Hz */
#define FCD_CMD_APP_GET_FREQ_HZ 102 /*!< Returns 4 byte unsigned little endian frequency in Hz. */
#define FCD_CMD_APP_GET_IF_RSSI 104 /*!< Supposed to return 1 byte unsigned IF RSSI (-35dBm=0, -10dBm=70) but it is not functional. */
#define FCD_CMD_APP_GET_PLL_LOCK 105 /*!< Returns 1 bit, true if locked. */
#define FCD_CMD_APP_SET_DC_CORR 106 /*!< Send with 2 byte unsigned I DC correction followed by 2 byte unsigned Q DC correction. 32768 is the default centre value. */
#define FCD_CMD_APP_GET_DC_CORR 107 /*!< Returns 2 byte unsigned I DC correction followed by 2 byte unsigned Q DC correction. 32768 is the default centre value. */
#define FCD_CMD_APP_SET_IQ_CORR 108 /*!< Send with 2 byte signed phase correction followed by 2 byte unsigned gain correction. 0 is the default centre value for phase correction, 32768 is the default centre value for gain. */
#define FCD_CMD_APP_GET_IQ_CORR 109 /*!< Returns 2 byte signed phase correction followed by 2 byte unsigned gain correction. 0 is the default centre value for phase correction, 32768 is the default centre value for gain. */
#define FCD_CMD_APP_SET_LNA_GAIN 110 /*!< Send a 1 byte value, see enums for reference. */
#define FCD_CMD_APP_SET_LNA_ENHANCE 111
#define FCD_CMD_APP_SET_BAND 112
#define FCD_CMD_APP_SET_RF_FILTER 113
#define FCD_CMD_APP_SET_MIXER_GAIN 114
#define FCD_CMD_APP_SET_BIAS_CURRENT 115
#define FCD_CMD_APP_SET_MIXER_FILTER 116
#define FCD_CMD_APP_SET_IF_GAIN1 117
#define FCD_CMD_APP_SET_IF_GAIN_MODE 118
#define FCD_CMD_APP_SET_IF_RC_FILTER 119
#define FCD_CMD_APP_SET_IF_GAIN2 120
#define FCD_CMD_APP_SET_IF_GAIN3 121
#define FCD_CMD_APP_SET_IF_FILTER 122
#define FCD_CMD_APP_SET_IF_GAIN4 123
#define FCD_CMD_APP_SET_IF_GAIN5 124
#define FCD_CMD_APP_SET_IF_GAIN6 125
#define FCD_CMD_APP_SET_BIAS_TEE 126 /*!< Bias T for ext LNA. Send with one byte: 1=ON, 0=OFF. */
#define FCD_CMD_APP_GET_LNA_GAIN 150 // Retrieve a 1 byte value, see enums for reference
#define FCD_CMD_APP_GET_LNA_ENHANCE 151
#define FCD_CMD_APP_GET_BAND 152
#define FCD_CMD_APP_GET_RF_FILTER 153
#define FCD_CMD_APP_GET_MIXER_GAIN 154
#define FCD_CMD_APP_GET_BIAS_CURRENT 155
#define FCD_CMD_APP_GET_MIXER_FILTER 156
#define FCD_CMD_APP_GET_IF_GAIN1 157
#define FCD_CMD_APP_GET_IF_GAIN_MODE 158
#define FCD_CMD_APP_GET_IF_RC_FILTER 159
#define FCD_CMD_APP_GET_IF_GAIN2 160
#define FCD_CMD_APP_GET_IF_GAIN3 161
#define FCD_CMD_APP_GET_IF_FILTER 162
#define FCD_CMD_APP_GET_IF_GAIN4 163
#define FCD_CMD_APP_GET_IF_GAIN5 164
#define FCD_CMD_APP_GET_IF_GAIN6 165
#define FCD_CMD_APP_GET_BIAS_TEE 166 /*!< Bias T. 1=ON, 0=OFF. */
#define FCD_CMD_APP_SEND_I2C_BYTE 200
#define FCD_CMD_APP_RECV_I2C_BYTE 201
#define FCD_CMD_APP_RESET 255 // Reset to bootloader
typedef enum
{
TLGE_N5_0DB=0,
TLGE_N2_5DB=1,
TLGE_P0_0DB=4,
TLGE_P2_5DB=5,
TLGE_P5_0DB=6,
TLGE_P7_5DB=7,
TLGE_P10_0DB=8,
TLGE_P12_5DB=9,
TLGE_P15_0DB=10,
TLGE_P17_5DB=11,
TLGE_P20_0DB=12,
TLGE_P25_0DB=13,
TLGE_P30_0DB=14
} TUNER_LNA_GAIN_ENUM;
typedef enum
{
TLEE_OFF=0,
TLEE_0=1,
TLEE_1=3,
TLEE_2=5,
TLEE_3=7
} TUNER_LNA_ENHANCE_ENUM;
typedef enum
{
TBE_VHF2,
TBE_VHF3,
TBE_UHF,
TBE_LBAND
} TUNER_BAND_ENUM;
typedef enum
{
// Band 0, VHF II
TRFE_LPF268MHZ=0,
TRFE_LPF299MHZ=8,
// Band 1, VHF III
TRFE_LPF509MHZ=0,
TRFE_LPF656MHZ=8,
// Band 2, UHF
TRFE_BPF360MHZ=0,
TRFE_BPF380MHZ=1,
TRFE_BPF405MHZ=2,
TRFE_BPF425MHZ=3,
TRFE_BPF450MHZ=4,
TRFE_BPF475MHZ=5,
TRFE_BPF505MHZ=6,
TRFE_BPF540MHZ=7,
TRFE_BPF575MHZ=8,
TRFE_BPF615MHZ=9,
TRFE_BPF670MHZ=10,
TRFE_BPF720MHZ=11,
TRFE_BPF760MHZ=12,
TRFE_BPF840MHZ=13,
TRFE_BPF890MHZ=14,
TRFE_BPF970MHZ=15,
// Band 2, L band
TRFE_BPF1300MHZ=0,
TRFE_BPF1320MHZ=1,
TRFE_BPF1360MHZ=2,
TRFE_BPF1410MHZ=3,
TRFE_BPF1445MHZ=4,
TRFE_BPF1460MHZ=5,
TRFE_BPF1490MHZ=6,
TRFE_BPF1530MHZ=7,
TRFE_BPF1560MHZ=8,
TRFE_BPF1590MHZ=9,
TRFE_BPF1640MHZ=10,
TRFE_BPF1660MHZ=11,
TRFE_BPF1680MHZ=12,
TRFE_BPF1700MHZ=13,
TRFE_BPF1720MHZ=14,
TRFE_BPF1750MHZ=15
} TUNER_RF_FILTER_ENUM;
typedef enum
{
TMGE_P4_0DB=0,
TMGE_P12_0DB=1
} TUNER_MIXER_GAIN_ENUM;
typedef enum
{
TBCE_LBAND=0,
TBCE_1=1,
TBCE_2=2,
TBCE_VUBAND=3
} TUNER_BIAS_CURRENT_ENUM;
typedef enum
{
TMFE_27_0MHZ=0,
TMFE_4_6MHZ=8,
TMFE_4_2MHZ=9,
TMFE_3_8MHZ=10,
TMFE_3_4MHZ=11,
TMFE_3_0MHZ=12,
TMFE_2_7MHZ=13,
TMFE_2_3MHZ=14,
TMFE_1_9MHZ=15
} TUNER_MIXER_FILTER_ENUM;
typedef enum
{
TIG1E_N3_0DB=0,
TIG1E_P6_0DB=1
} TUNER_IF_GAIN1_ENUM;
typedef enum
{
TIGME_LINEARITY=0,
TIGME_SENSITIVITY=1
} TUNER_IF_GAIN_MODE_ENUM;
typedef enum
{
TIRFE_21_4MHZ=0,
TIRFE_21_0MHZ=1,
TIRFE_17_6MHZ=2,
TIRFE_14_7MHZ=3,
TIRFE_12_4MHZ=4,
TIRFE_10_6MHZ=5,
TIRFE_9_0MHZ=6,
TIRFE_7_7MHZ=7,
TIRFE_6_4MHZ=8,
TIRFE_5_3MHZ=9,
TIRFE_4_4MHZ=10,
TIRFE_3_4MHZ=11,
TIRFE_2_6MHZ=12,
TIRFE_1_8MHZ=13,
TIRFE_1_2MHZ=14,
TIRFE_1_0MHZ=15
} TUNER_IF_RC_FILTER_ENUM;
typedef enum
{
TIG2E_P0_0DB=0,
TIG2E_P3_0DB=1,
TIG2E_P6_0DB=2,
TIG2E_P9_0DB=3
} TUNER_IF_GAIN2_ENUM;
typedef enum
{
TIG3E_P0_0DB=0,
TIG3E_P3_0DB=1,
TIG3E_P6_0DB=2,
TIG3E_P9_0DB=3
} TUNER_IF_GAIN3_ENUM;
typedef enum
{
TIG4E_P0_0DB=0,
TIG4E_P1_0DB=1,
TIG4E_P2_0DB=2
} TUNER_IF_GAIN4_ENUM;
typedef enum
{
TIFE_5_50MHZ=0,
TIFE_5_30MHZ=1,
TIFE_5_00MHZ=2,
TIFE_4_80MHZ=3,
TIFE_4_60MHZ=4,
TIFE_4_40MHZ=5,
TIFE_4_30MHZ=6,
TIFE_4_10MHZ=7,
TIFE_3_90MHZ=8,
TIFE_3_80MHZ=9,
TIFE_3_70MHZ=10,
TIFE_3_60MHZ=11,
TIFE_3_40MHZ=12,
TIFE_3_30MHZ=13,
TIFE_3_20MHZ=14,
TIFE_3_10MHZ=15,
TIFE_3_00MHZ=16,
TIFE_2_95MHZ=17,
TIFE_2_90MHZ=18,
TIFE_2_80MHZ=19,
TIFE_2_75MHZ=20,
TIFE_2_70MHZ=21,
TIFE_2_60MHZ=22,
TIFE_2_55MHZ=23,
TIFE_2_50MHZ=24,
TIFE_2_45MHZ=25,
TIFE_2_40MHZ=26,
TIFE_2_30MHZ=27,
TIFE_2_28MHZ=28,
TIFE_2_24MHZ=29,
TIFE_2_20MHZ=30,
TIFE_2_15MHZ=31
} TUNER_IF_FILTER_ENUM;
typedef enum
{
TIG5E_P3_0DB=0,
TIG5E_P6_0DB=1,
TIG5E_P9_0DB=2,
TIG5E_P12_0DB=3,
TIG5E_P15_0DB=4
} TUNER_IF_GAIN5_ENUM;
typedef enum
{
TIG6E_P3_0DB=0,
TIG6E_P6_0DB=1,
TIG6E_P9_0DB=2,
TIG6E_P12_0DB=3,
TIG6E_P15_0DB=4
} TUNER_IF_GAIN6_ENUM;
#endif // FCDHIDCMD_H

1427
fcdhid/hid-libusb.c Normal file

File diff suppressed because it is too large Load Diff

1432
fcdhid/hid-libusb.h Normal file

File diff suppressed because it is too large Load Diff

387
fcdhid/hidapi.h Normal file
View File

@ -0,0 +1,387 @@
/*******************************************************
HIDAPI - Multi-Platform library for
communication with HID devices.
Alan Ott
Signal 11 Software
8/22/2009
Copyright 2009, All Rights Reserved.
At the discretion of the user of this library,
this software may be licensed under the terms of the
GNU General Public License v3, a BSD-Style license, or the
original HIDAPI license as outlined in the LICENSE.txt,
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
files located at the root of the source distribution.
These files may also be found in the public source
code repository located at:
http://github.com/signal11/hidapi .
********************************************************/
/** @file
* @defgroup API hidapi API
*/
#ifndef HIDAPI_H__
#define HIDAPI_H__
#include <wchar.h>
#ifdef _WIN32
#define HID_API_EXPORT __declspec(dllexport)
#define HID_API_CALL
#else
#define HID_API_EXPORT /**< API export macro */
#define HID_API_CALL /**< API call macro */
#endif
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
#ifdef __cplusplus
extern "C" {
#endif
struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
/** hidapi info structure */
struct hid_device_info {
/** Platform-specific device path */
char *path;
/** Device Vendor ID */
unsigned short vendor_id;
/** Device Product ID */
unsigned short product_id;
/** Serial Number */
wchar_t *serial_number;
/** Device Release Number in binary-coded decimal,
also known as Device Version Number */
unsigned short release_number;
/** Manufacturer String */
wchar_t *manufacturer_string;
/** Product string */
wchar_t *product_string;
/** Usage Page for this Device/Interface
(Windows/Mac only). */
unsigned short usage_page;
/** Usage for this Device/Interface
(Windows/Mac only).*/
unsigned short usage;
/** The USB interface which this logical device
represents. Valid on both Linux implementations
in all cases, and valid on the Windows implementation
only if the device contains more than one interface. */
int interface_number;
/** Pointer to the next device */
struct hid_device_info *next;
};
/** @brief Initialize the HIDAPI library.
This function initializes the HIDAPI library. Calling it is not
strictly necessary, as it will be called automatically by
hid_enumerate() and any of the hid_open_*() functions if it is
needed. This function should be called at the beginning of
execution however, if there is a chance of HIDAPI handles
being opened by different threads simultaneously.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_init(void);
/** @brief Finalize the HIDAPI library.
This function frees all of the static data associated with
HIDAPI. It should be called at the end of execution to avoid
memory leaks.
@ingroup API
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_exit(void);
/** @brief Enumerate the HID Devices.
This function returns a linked list of all the HID devices
attached to the system which match vendor_id and product_id.
If @p vendor_id is set to 0 then any vendor matches.
If @p product_id is set to 0 then any product matches.
If @p vendor_id and @p product_id are both set to 0, then
all HID devices will be returned.
@ingroup API
@param vendor_id The Vendor ID (VID) of the types of device
to open.
@param product_id The Product ID (PID) of the types of
device to open.
@returns
This function returns a pointer to a linked list of type
struct #hid_device, containing information about the HID devices
attached to the system, or NULL in the case of failure. Free
this linked list by calling hid_free_enumeration().
*/
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
/** @brief Free an enumeration Linked List
This function frees a linked list created by hid_enumerate().
@ingroup API
@param devs Pointer to a list of struct_device returned from
hid_enumerate().
*/
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
/** @brief Open a HID device using a Vendor ID (VID), Product ID
(PID) and optionally a serial number.
If @p serial_number is NULL, the first device with the
specified VID and PID is opened.
@ingroup API
@param vendor_id The Vendor ID (VID) of the device to open.
@param product_id The Product ID (PID) of the device to open.
@param serial_number The Serial Number of the device to open
(Optionally NULL).
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
/** @brief Open a HID device by its path name.
The path name be determined by calling hid_enumerate(), or a
platform-specific path name can be used (eg: /dev/hidraw0 on
Linux).
@ingroup API
@param path The path name of the device to open
@returns
This function returns a pointer to a #hid_device object on
success or NULL on failure.
*/
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
/** @brief Write an Output report to a HID device.
The first byte of @p data[] must contain the Report ID. For
devices which only support a single report, this must be set
to 0x0. The remaining bytes contain the report data. Since
the Report ID is mandatory, calls to hid_write() will always
contain one more byte than the report contains. For example,
if a hid report is 16 bytes long, 17 bytes must be passed to
hid_write(), the Report ID (or 0x0, for devices with a
single report), followed by the report data (16 bytes). In
this example, the length passed in would be 17.
hid_write() will send the data on the first OUT endpoint, if
one exists. If it does not, it will send the data through
the Control Endpoint (Endpoint 0).
@ingroup API
@param device A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send.
@returns
This function returns the actual number of bytes written and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
/** @brief Read an Input report from a HID device with timeout.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@param milliseconds timeout in milliseconds or -1 for blocking wait.
@returns
This function returns the actual number of bytes read and
-1 on error. If no packet was available to be read within
the timeout period, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
/** @brief Read an Input report from a HID device.
Input reports are returned
to the host through the INTERRUPT IN endpoint. The first byte will
contain the Report number if the device uses numbered reports.
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into.
@param length The number of bytes to read. For devices with
multiple reports, make sure to read an extra byte for
the report number.
@returns
This function returns the actual number of bytes read and
-1 on error. If no packet was available to be read and
the handle is in non-blocking mode, this function returns 0.
*/
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
/** @brief Set the device handle to be non-blocking.
In non-blocking mode calls to hid_read() will return
immediately with a value of 0 if there is no data to be
read. In blocking mode, hid_read() will wait (block) until
there is data to read before returning.
Nonblocking can be turned on and off at any time.
@ingroup API
@param device A device handle returned from hid_open().
@param nonblock enable or not the nonblocking reads
- 1 to enable nonblocking
- 0 to disable nonblocking.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);
/** @brief Send a Feature report to the device.
Feature reports are sent over the Control endpoint as a
Set_Report transfer. The first byte of @p data[] must
contain the Report ID. For devices which only support a
single report, this must be set to 0x0. The remaining bytes
contain the report data. Since the Report ID is mandatory,
calls to hid_send_feature_report() will always contain one
more byte than the report contains. For example, if a hid
report is 16 bytes long, 17 bytes must be passed to
hid_send_feature_report(): the Report ID (or 0x0, for
devices which do not use numbered reports), followed by the
report data (16 bytes). In this example, the length passed
in would be 17.
@ingroup API
@param device A device handle returned from hid_open().
@param data The data to send, including the report number as
the first byte.
@param length The length in bytes of the data to send, including
the report number.
@returns
This function returns the actual number of bytes written and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);
/** @brief Get a feature report from a HID device.
Make sure to set the first byte of @p data[] to the Report
ID of the report to be read. Make sure to allow space for
this extra byte in @p data[].
@ingroup API
@param device A device handle returned from hid_open().
@param data A buffer to put the read data into, including
the Report ID. Set the first byte of @p data[] to the
Report ID of the report to be read.
@param length The number of bytes to read, including an
extra byte for the report ID. The buffer can be longer
than the actual report.
@returns
This function returns the number of bytes read and
-1 on error.
*/
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);
/** @brief Close a HID device.
@ingroup API
@param device A device handle returned from hid_open().
*/
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);
/** @brief Get The Manufacturer String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get The Product String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get The Serial Number String from a HID device.
@ingroup API
@param device A device handle returned from hid_open().
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);
/** @brief Get a string from a HID device, based on its string index.
@ingroup API
@param device A device handle returned from hid_open().
@param string_index The index of the string to get.
@param string A wide string buffer to put the data into.
@param maxlen The length of the buffer in multiples of wchar_t.
@returns
This function returns 0 on success and -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);
/** @brief Get a string describing the last error which occurred.
@ingroup API
@param device A device handle returned from hid_open().
@returns
This function returns a string containing the last error
which occurred or NULL if none has occurred.
*/
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -18,7 +18,7 @@ if(LIBUSB_FOUND AND UNIX)
FIND_LIBRARY (LIBASOUND asound) FIND_LIBRARY (LIBASOUND asound)
endif() endif()
if(LIBASOUND AND ASOUNDH) if(LIBASOUND AND ASOUNDH)
add_subdirectory(fcd) add_subdirectory(fcdpro)
endif() endif()
find_package(LibRTLSDR) find_package(LibRTLSDR)

View File

@ -25,7 +25,7 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include-gpl ${CMAKE_SOURCE_DIR}/include-gpl
${LIBRTLSDR_INCLUDE_DIR} ${LIBBLADERF_INCLUDE_DIR}
) )
#include(${QT_USE_FILE}) #include(${QT_USE_FILE})

View File

@ -171,7 +171,7 @@ const QString& FCDInput::getDeviceDescription() const
int FCDInput::getSampleRate() const int FCDInput::getSampleRate() const
{ {
return 96000; return 192000;
} }
quint64 FCDInput::getCenterFrequency() const quint64 FCDInput::getCenterFrequency() const
@ -220,7 +220,7 @@ void FCDInput::applySettings(const Settings& settings, bool force)
if (signalChange) if (signalChange)
{ {
DSPSignalNotification *notif = new DSPSignalNotification(96000, m_settings.centerFrequency); DSPSignalNotification *notif = new DSPSignalNotification(192000, m_settings.centerFrequency);
getOutputMessageQueue()->push(notif); getOutputMessageQueue()->push(notif);
} }
} }

View File

@ -42,7 +42,7 @@ void FCDThread::stopWork()
void FCDThread::run() 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; return;
// TODO: fallback to original fcd // 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) if (snd_pcm_open(&fcd_handle, cardname, fcd_stream, 0) < 0)
{ {
qCritical("FCDThread::OpenSource: cannot open %s", cardname);
return false; return false;
} }
@ -77,10 +78,12 @@ bool FCDThread::OpenSource(const char* cardname)
if (snd_pcm_hw_params_any(fcd_handle, params) < 0) if (snd_pcm_hw_params_any(fcd_handle, params) < 0)
{ {
qCritical("FCDThread::OpenSource: snd_pcm_hw_params_any failed");
fail = true; fail = true;
} }
else if (snd_pcm_hw_params(fcd_handle, params) < 0) else if (snd_pcm_hw_params(fcd_handle, params) < 0)
{ {
qCritical("FCDThread::OpenSource: snd_pcm_hw_params failed");
fail = true; fail = true;
// TODO: check actual samplerate, may be crippled firmware // 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) if (snd_pcm_start(fcd_handle) < 0)
{ {
qCritical("FCDThread::OpenSource: snd_pcm_start failed");
fail = true; fail = true;
} }
} }

View File

@ -34,7 +34,7 @@
typedef bool BOOL; 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. */ const unsigned short _usVID=0x04D8; /*!< USB vendor ID. */
#ifdef FCDPP #ifdef FCDPP
const unsigned short _usPID=0xFB31; /*!< USB product ID. */ const unsigned short _usPID=0xFB31; /*!< USB product ID. */

View File

@ -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)

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();
}

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

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>

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);
}

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

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;
}
}

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

View File

@ -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;
}

View File

@ -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_ */

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;
}

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