1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-21 23:55:13 -05:00

Removed old DV serial engine files. Make serialDV dependency mandatory. Updated various readmes

This commit is contained in:
f4exb 2019-07-21 10:15:49 +02:00
parent 0d26936e61
commit 71192a4130
8 changed files with 48 additions and 879 deletions

View File

@ -364,7 +364,7 @@ else()
find_package(Codec2)
find_package(CM256cc)
find_package(LibMbe)
find_package(SerialDV)
find_package(SerialDV REQUIRED)
find_package(LibDSDcc)
endif()

View File

@ -156,21 +156,16 @@ This is the `demoddsd` plugin. At present it can be used to decode the following
It is based on the [DSDcc](https://github.com/f4exb/dsdcc) C++ library which is a rewrite of the original [DSD](https://github.com/szechyjs/dsd) program. So you will need to have DSDcc installed in your system. Please follow instructions in [DSDcc readme](https://github.com/f4exb/dsdcc/blob/master/Readme.md) to build and install DSDcc.
If you have one or more serial devices interfacing the AMBE3000 chip in packet mode you can use them to decode AMBE voice frames. For that purpose you will need to compile with [SerialDV](https://github.com/f4exb/serialDV) support. Please refer to this project Readme.md to compile and install SerialDV. Note that your user must be a member of group `dialout` (Ubuntu/Debian) or `uucp` (Arch) to be able to use the dongle.
Note that you will need to compile with [SerialDV](https://github.com/f4exb/serialDV) support. This makes possible to use AMBE serial dongles or AMBE server connections. Please refer to this project Readme.md to compile and install SerialDV. Note that your user must be a member of group `dialout` (Ubuntu/Debian) or `uucp` (Arch) to be able to use the dongle.
Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices connected to your system as the number of conversations you would like to be handled in parallel.
Alternatively you can use [mbelib](https://github.com/szechyjs/mbelib) but mbelib comes with some copyright issues.
When no AMBE devices or servers are activated [mbelib](https://github.com/szechyjs/mbelib) is used to decode AMBE frames but mbelib comes with some copyright issues.
While DSDcc is intended to be patent-free, `mbelib` that it uses describes functions that may be covered by one or more U.S. patents owned by DVSI Inc. The source code itself should not be infringing as it merely describes possible methods of implementation. Compiling or using `mbelib` may infringe on patents rights in your jurisdiction and/or require licensing. It is unknown if DVSI will sell licenses for software that uses `mbelib`.
Possible copyright issues apart the audio quality with the DVSI AMBE chip is much better.
If you are not comfortable with this just do not install DSDcc and/or mbelib and the plugin will not be compiled and added to SDRangel. For packaged distributions just remove from the installation directory:
- For Linux distributions: `plugins/channel/libdemoddsd.so`
- For Windows distribution: `dsdcc.dll`, `mbelib.dll`, `plugins\channel\demoddsd.dll`
<h1>Binary distributions</h1>
In the [releases](https://github.com/f4exb/sdrangel/releases) section of the Github repository one can find binary distributions for Ubuntu and Windows

View File

@ -18,16 +18,17 @@ To enable this plugin at compile time you will need to have DSDcc installed in y
<h2>DV serial device support</h2>
You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. In order to support DV serial devices in your system you will need two things:
You can use a serial device connected to your system that implements and exposes the packet interface of the AMBE3000 chip. This can be for example a ThumbDV USB dongle. You may also connect to an AMBE server instance over the network.
- Compile with [SerialDV](https://github.com/f4exb/serialDV) support Please refer to this project Readme.md to compile and install SerialDV. If you install it in a custom location say `/opt/install/serialdv` you will need to add these defines to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv`
- Enable DV serial devices in your system by checking the option in the Preferences menu. You will need to enable the DV serial devices each time you start SDRangel.
Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices connected to your system as the number of conversations you would like to be handled in parallel.
DV serial devices are supported using the [SerialDV](https://github.com/f4exb/serialDV) library that is a mandatory requirement. Therefore you have to compile and install it in your system. Please refer to this project Readme.md to compile and install SerialDV. f you install it in a custom location say `/opt/install/serialdv` you will need to add this define to the cmake command: `-DSERIALDV_DIR=/opt/install/serialdv`
Note also that this is not supported in Windows because of trouble with COM port support (contributors welcome!).
To effectively use serial DV devices for AMBE decoding you will have to add at least one device to the list of AMBE devices in use using the `AMBE devices control` dialog opened with the `AMBE` option in the `Preferences` menu. The list of devices is saved in the program preferences so that they are persistent across program stop/start. However if the device name or server address changes in between the corresponding reference will be lost.
Alternatively you can use software decoding with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better.
Although such serial devices work with a serial interface at 400 kb in practice maybe for other reasons they are capable of handling only one conversation at a time. The software will allocate the device dynamically to a conversation with an inactivity timeout of 1 second so that conversations do not get interrupted constantly making the audio output too choppy. In practice you will have to have as many devices connected to your system as the number of conversations you would like to be handled in parallel.
Note also that hardware serial devices are not supported in Windows because of trouble with COM port support (contributors welcome!).
If no AMBE devices or servers are activated with the `AMBE devices control` AMBE decoding will take place with Mbelib. Possible copyright issues apart (see next) the audio quality with the DVSI AMBE chip is much better.
---
&#9888; With kernel 4.4.52 and maybe other 4.4 versions the default for FTDI devices (that is in the ftdi_sio kernel module) is not to set it as low latency. This results in the ThumbDV dongle not working anymore because its response is too slow to sustain the normal AMBE packets flow. The solution is to force low latency by changing the variable for your device (ex: /dev/ttyUSB0) as follows:
@ -101,7 +102,7 @@ One line per status text
<h4>A.7: Audio volume</h4>
When working with mbelib this is a linear multiplication factor. A value of zero triggers the auto gain feature.
When working with mbelib this is a linear multiplication factor. A value of zero triggers the auto gain feature.
With the DV serial device(s) amplification factor in dB is given by `(value - 3.0)*5.0`. In most practical cases the middle value of 5.0 (+10 dB) is a comfortable level.
@ -145,21 +146,21 @@ When the display is active the background turns from the surrounding gray color
<h5>A11.1.3: Informative text</h5>
When slow data can be decoded this is the 20 character string that is sent in the text frames
When slow data can be decoded this is the 20 character string that is sent in the text frames
<h5>A11.1.4: Geopositional data</h5>
When a `$$CRC` frame that carries geographical position can be successfully decoded from the slow data the geopositional information is displayed:
When a `$$CRC` frame that carries geographical position can be successfully decoded from the slow data the geopositional information is displayed:
- at the left of the colon `:` is the QTH 6 character locator a.k.a. Maidenhead locator
- at the right of the colon `:` is the bearing in degrees and distance in kilometers from the location entered in the main window `Preferences\My Position` dialog. The bearing and distance are separated by a slash `/`.
- at the right of the colon `:` is the bearing in degrees and distance in kilometers from the location entered in the main window `Preferences\My Position` dialog. The bearing and distance are separated by a slash `/`.
<h4>A11.2: DMR status display</h4>
![DSD DMR status](../../../doc/img/DSDdemod_plugin_dmr_status.png)
- Note 1: statuses are polled at ~1s rate and therefore do not reflect values instantaneously. As a consequence some block types that occur during the conversation may not appear.
- Note 2: status values remain unchanged until a new value is available for the channel or the transmissions stops then all values of both channels are cleared
- Note 2: status values remain unchanged until a new value is available for the channel or the transmissions stops then all values of both channels are cleared
<h5>A11.2.1: Station role</h5>
@ -178,16 +179,16 @@ For mobile stations on an inbound channel there is no channel identification (no
This applies to base stations and mobile stations in continuous mode that is transmissions including the CACH sequences.
- The first character is either:
- `*`: Busy. That is the AT bit on the opposite channel is on
- `.`: Clear. That is the AT bit on the opposite channel is off
- `/`: The CACH could not be decoded and information is missing
- The two next characters are either:
- The color code from 0 to 15 (4 bits)
- `--`: The color code could not be decoded and information is missing
<h5>A11.2.5: Slot type</h5>
This is either:
@ -203,7 +204,7 @@ This is either:
- `D12`: 1/2 rate data block
- `D34`: 3/4 rate data block
- `DB1`: full rate data block
- `RES`: reserved data block
- `RES`: reserved data block
- `UNK`: unknown data type or could not be decoded
<h5>A11.2.6: Addressing information</h5>
@ -231,7 +232,7 @@ String is in the form: `02223297>G00000222`
- `D2`: Data with FEC frame
- `XS`: Extended search: looking for a new payload frame when out of sequence
- `EN`: End frame
<h5>A11.3.2: Colour code</h5>
Colour code in decimal (12 bits)
@ -257,21 +258,21 @@ This displays a summary of FICH (Frame Identification CHannel) block data. From
- `C`: channel (as in the example)
- `T`: terminator
- `S`: test
- Channel type:
- `V1`: voice/data mode 1
- `V2`: voice/data mode 2 (as in the example)
- `VF`: voice full rate
- `DF`: data full rate
- Call mode:
- `GC`: group call (as in the example)
- `RI`: radio ID
- `RE`: reserved
- `IN`: individual call
- Number of total blocks and number of total frames separated by a colon `:`
- Miscellaneous information in a single string
- first character is the bandwidth mode:
- `N`: narrow band mode
@ -280,7 +281,7 @@ This displays a summary of FICH (Frame Identification CHannel) block data. From
- `I`: Internet path
- `L`: local path (as in the example)
- last three characters are the YSF squelch code (0..127) or dashes `---` if the YSF squelch is not active
<h5>A11.4.2: Origin and destination callsigns</h5>
- at the left of the `>` sign is the origin callsign
@ -289,7 +290,7 @@ This displays a summary of FICH (Frame Identification CHannel) block data. From
<h5>A11.4.3: Origin and destination repeaters callsigns</h5>
- at the left of the `>` sign is the origin repeater callsign
- at the right of the `>` sign is the destination repeater callsign.
- at the right of the `>` sign is the destination repeater callsign.
<h5>A11.4.4: Originator radio ID</h5>
@ -333,17 +334,17 @@ This is the 3 byte location Id associated to the site displayed in hexadecimal
This is a 16 bit collection of flags to indicate which services are available displayed in hexadecimal. The breakdown is listed in the NXDN documentation `NXDN TS 1-A Version 1.3` section 6.5.33. From MSB to LSB:
- first nibble (here `B`):
- `b15`: Multi-site service
- `b14`: Multi-system service
- `b13`: Location Registration service
- `b15`: Multi-site service
- `b14`: Multi-system service
- `b13`: Location Registration service
- `b12`: Group Registration Service
- second nibble (here `3`):
- `b11`: Authentication Service
- `b10`: Composite Control Channel Service
- `b9`: Voice Call Service
- `b11`: Authentication Service
- `b10`: Composite Control Channel Service
- `b9`: Voice Call Service
- `b8`: Data Call Service
- third nibble (here `C`):
- `b7`: Short Data Call Service
- `b7`: Short Data Call Service
- `b6`: Status Call & Remote Control Service
- `b5`: PSTN Network Connection Service
- `b4`: IP Network Connection Service
@ -399,7 +400,7 @@ In this case the display is simply "RU" for "unknown"
This display shows the sampled points of the demodulated FM signal in a XY plane with either:
- X as the signal at time t and Y the signal at time t minus symbol time if "transitions constellation" is selected by button (B.13)
- X as the signal and Y as the synchronization signal if "symbol synchronization" is selected by button (B.13)
- X as the signal and Y as the synchronization signal if "symbol synchronization" is selected by button (B.13)
The display shows 16 points as yellow crosses that can be used to tune the center frequency (A.1) and FM deviation (B.17) so that symbol recovery can be done with the best conditions. In the rest of the documentation they will be referenced with numbers from 0 to 15 starting at the top left corner and going from left to right and top to bottom.
@ -423,14 +424,14 @@ Depending on the type of modulation the figure will have different characteristi
This concerns the following standards:
- D-Star
There are 4 possible points corresponding to the 4 possible transitions. x represents the current symbol and y the previous symbol. The 4 points given by their (y,x) coordinates correspond to the following:
- (1, 1): upper right corner. The pointer can stay there or move to (1, -1). Ideally this should be placed at point 3.
- (1, -1): upper left corner. The pointer can move to (-1, -1) or (-1, 1). Ideally this should be placed at point 0.
- (-1, 1): lower right corner. The pointer can move to (1, -1) or (1, 1). Ideally this should be placed at point 15.
- (-1, -1): lower left corner. The pointer can stay there or move to (-1, 1). Ideally this should be placed at point 12.
As you can see the pointer can make all moves except between (-1, -1) and (1,1) hence all vertices between the 4 points can appear except the one between the lower left corner and the upper right corner.
<h6>4-FSK or 4-GFSK</h6>
@ -491,7 +492,7 @@ This can be one of the following:
- `+YSF`: non-inverted Yaesu System Fusion frame
<h4>B.4: Matched filter toggle</h4>
Normally you would always want to have a matched filter however on some strong D-Star signals more synchronization points could be obtained without. When engaged the background of the button is lit in orange.
<h4>B.5: Symbol PLL lock indicator</h4>
@ -514,12 +515,12 @@ This is the percentage per symbols for which a valid zero crossing has been dete
With the PLL engaged the figure should be 100% all the time in presence of a locked signal. Occasional small drops may occur without noticeable impact on decoding.
<h4>B.7: Zero crossing shift</h4>
This is the current (at display polling time) zero crossing shift. It should be the closest to 0 as possible. However some jitter is acceptable for good symbol synchronization:
- `2400 S/s`: +/- 5 inclusive
- `4800 S/s`: +/- 2 inclusive
<h4>B.8: Discriminator input signal median level in %</h4>
This is the estimated median level (center) of the discriminator input signal in percentage of half the total range. When the signal is correctly aligned in the input range it should be 0
@ -546,9 +547,9 @@ Toggle button to select slot #2 voice output. When on waves appear on the icon.
- When off the icon shows a single loudspeaker. It mixes slot #1 and slot #2 voice as a mono audio signal
- When on the icon shows a pair of loudspeakers. It sends slot #1 voice to the left stereo audio channel and slot #2 to the right one
For FDMA standards you may want to leave this as mono mode.
<h4>B.13: Transition constellation or symbol synchronization signal toggle</h4>
Using this button you can either:
@ -577,7 +578,7 @@ This button tunes the persistence decay of the points displayer on B.1. The trac
<h4>B.17: Maximum expected FM deviation</h4>
This is the one side deviation in kHz (&#177;) leading to maximum (100%) deviation at the discriminator output. The correct value depends on the maximum deviation imposed by the modulation scheme with some guard margin. In practice you should adjust this value to make the figure on the signal scope fill the entire screen as shown in the screenshots above. The typical deviations by mode for a unit gain (1.0 at B.18) are:
- DMR: &#177;5.4k
- dPMR: &#177;2.7k
- D-Star: &#177;3.5k

View File

@ -31,22 +31,10 @@ else(FFTW3F_FOUND)
add_definitions(-DUSE_KISSFFT)
endif(FFTW3F_FOUND)
if (LIBSERIALDV_FOUND)
set(sdrbase_SOURCES
${sdrbase_SOURCES}
dsp/dvserialworker.cpp
dsp/dvserialengine.cpp
)
set(sdrbase_HEADERS
${sdrbase_HEADERS}
dsp/dvserialworker.h
dsp/dvserialengine.h
)
message(STATUS "Will have SerialDV support")
add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
endif(LIBSERIALDV_FOUND)
# serialdv now required
add_definitions(-DDSD_USE_SERIALDV)
include_directories(${LIBSERIALDV_INCLUDE_DIR})
set(sdrbase_SERIALDV_LIB ${LIBSERIALDV_LIBRARY})
set(sdrbase_SOURCES
${sdrbase_SOURCES}

View File

@ -1,296 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#ifndef _MSC_VER
#include <dirent.h>
#include <unistd.h>
#include <libgen.h>
#endif
#ifndef __WINDOWS__
#include <termios.h>
#include <sys/ioctl.h>
#ifndef __APPLE__
#include <linux/serial.h>
#endif
#endif
#include <QDebug>
#include <QThread>
#include <QMutexLocker>
#include "audio/audiooutput.h"
#include "dvserialengine.h"
#include "dvserialworker.h"
DVSerialEngine::DVSerialEngine()
{
}
DVSerialEngine::~DVSerialEngine()
{
release();
}
#ifdef __WINDOWS__
void DVSerialEngine::getComList()
{
m_comList.clear();
m_comList8250.clear();
char comCStr[16];
// Arbitrarily set the list to the 20 first COM ports
for (int i = 1; i <= 20; i++)
{
sprintf(comCStr, "COM%d", i);
m_comList.push_back(std::string(comCStr));
}
}
#else
std::string DVSerialEngine::get_driver(const std::string& tty)
{
struct stat st;
std::string devicedir = tty;
// Append '/device' to the tty-path
devicedir += "/device";
// Stat the devicedir and handle it if it is a symlink
if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode))
{
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
// Append '/driver' and return basename of the target
devicedir += "/driver";
if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0)
{
return basename(buffer);
}
}
return "";
}
void DVSerialEngine::register_comport(std::list<std::string>& comList,
std::list<std::string>& comList8250, const std::string& dir)
{
// Get the driver the device is using
std::string driver = get_driver(dir);
// Skip devices without a driver
if (driver.size() > 0)
{
//std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl;
std::string devfile = std::string("/dev/") + basename((char *) dir.c_str());
// Put serial8250-devices in a seperate list
if (driver == "serial8250")
{
comList8250.push_back(devfile);
}
else
comList.push_back(devfile);
}
}
void DVSerialEngine::probe_serial8250_comports(std::list<std::string>& comList,
std::list<std::string> comList8250)
{
struct serial_struct serinfo;
std::list<std::string>::iterator it = comList8250.begin();
// Iterate over all serial8250-devices
while (it != comList8250.end())
{
// Try to open the device
int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd >= 0)
{
// Get serial_info
if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0)
{
// If device type is no PORT_UNKNOWN we accept the port
if (serinfo.type != PORT_UNKNOWN)
comList.push_back(*it);
}
close(fd);
}
it++;
}
}
void DVSerialEngine::getComList()
{
int n;
struct dirent **namelist;
m_comList.clear();
m_comList8250.clear();
const char* sysdir = "/sys/class/tty/";
// Scan through /sys/class/tty - it contains all tty-devices in the system
n = scandir(sysdir, &namelist, NULL, alphasort);
if (n < 0)
perror("scandir");
else
{
while (n--)
{
if (strcmp(namelist[n]->d_name, "..")
&& strcmp(namelist[n]->d_name, "."))
{
// Construct full absolute file path
std::string devicedir = sysdir;
devicedir += namelist[n]->d_name;
// Register the device
register_comport(m_comList, m_comList8250, devicedir);
}
free(namelist[n]);
}
free(namelist);
}
// Only non-serial8250 has been added to comList without any further testing
// serial8250-devices must be probe to check for validity
probe_serial8250_comports(m_comList, m_comList8250);
}
#endif // __WINDOWS__
bool DVSerialEngine::scan()
{
getComList();
std::list<std::string>::iterator it = m_comList.begin();
while (it != m_comList.end())
{
DVSerialWorker *worker = new DVSerialWorker();
if (worker->open(*it))
{
DVSerialController controller;
controller.worker = worker;
controller.device = *it;
controller.thread = new QThread();
controller.worker->moveToThread(controller.thread);
//connect(controller.thread, SIGNAL(started()), controller.worker, SLOT(process()));
connect(controller.worker, SIGNAL(finished()), controller.thread, SLOT(quit()));
connect(controller.worker, SIGNAL(finished()), controller.worker, SLOT(deleteLater()));
connect(controller.thread, SIGNAL(finished()), controller.thread, SLOT(deleteLater()));
connect(&controller.worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), controller.worker, SLOT(handleInputMessages()));
controller.thread->start();
m_controllers.push_back(controller);
qDebug() << "DVSerialEngine::scan: found device at: " << it->c_str();
}
else
{
delete worker;
}
it++;
}
return m_controllers.size() > 0;
}
void DVSerialEngine::release()
{
qDebug("DVSerialEngine::release");
std::vector<DVSerialController>::iterator it = m_controllers.begin();
while (it != m_controllers.end())
{
disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages()));
it->worker->stop();
it->thread->wait(100);
it->worker->m_inputMessageQueue.clear();
it->worker->close();
qDebug() << "DVSerialEngine::release: closed device at: " << it->device.c_str();
++it;
}
m_controllers.clear();
}
void DVSerialEngine::getDevicesNames(std::vector<std::string>& deviceNames)
{
std::vector<DVSerialController>::iterator it = m_controllers.begin();
while (it != m_controllers.end())
{
deviceNames.push_back(it->device);
++it;
}
}
void DVSerialEngine::pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useLP,
int upsampling,
AudioFifo *audioFifo)
{
std::vector<DVSerialController>::iterator it = m_controllers.begin();
std::vector<DVSerialController>::iterator itAvail = m_controllers.end();
bool done = false;
QMutexLocker locker(&m_mutex);
while (it != m_controllers.end())
{
if (it->worker->hasFifo(audioFifo))
{
it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
done = true;
}
else if (it->worker->isAvailable())
{
itAvail = it;
}
++it;
}
if (!done)
{
if (itAvail != m_controllers.end())
{
int wNum = itAvail - m_controllers.begin();
qDebug("DVSerialEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum);
itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
}
else
{
qDebug("DVSerialEngine::pushMbeFrame: no DV serial device available. MBE frame dropped");
}
}
}

View File

@ -1,79 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DSP_DVSERIALENGINE_H_
#define SDRBASE_DSP_DVSERIALENGINE_H_
#include <QObject>
#include <QMutex>
#include <vector>
#include <string>
#include <list>
#include "export.h"
class QThread;
class DVSerialWorker;
class AudioFifo;
class SDRBASE_API DVSerialEngine : public QObject
{
Q_OBJECT
public:
DVSerialEngine();
~DVSerialEngine();
bool scan();
void release();
int getNbDevices() const { return m_controllers.size(); }
void getDevicesNames(std::vector<std::string>& devicesNames);
void pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
int upsampling,
AudioFifo *audioFifo);
private:
struct DVSerialController
{
QThread *thread;
DVSerialWorker *worker;
std::string device;
};
#ifndef __WINDOWS__
static std::string get_driver(const std::string& tty);
static void register_comport(std::list<std::string>& comList, std::list<std::string>& comList8250, const std::string& dir);
static void probe_serial8250_comports(std::list<std::string>& comList, std::list<std::string> comList8250);
#endif
void getComList();
std::list<std::string> m_comList;
std::list<std::string> m_comList8250;
std::vector<DVSerialController> m_controllers;
QMutex m_mutex;
};
#endif /* SDRBASE_DSP_DVSERIALENGINE_H_ */

View File

@ -1,279 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <chrono>
#include <thread>
#include "dsp/dvserialworker.h"
#include "audio/audiofifo.h"
MESSAGE_CLASS_DEFINITION(DVSerialWorker::MsgMbeDecode, Message)
MESSAGE_CLASS_DEFINITION(DVSerialWorker::MsgTest, Message)
DVSerialWorker::DVSerialWorker() :
m_running(false),
m_currentGainIn(0),
m_currentGainOut(0),
m_upsamplerLastValue(0.0f),
m_phase(0),
m_upsampling(1),
m_volume(1.0f)
{
m_audioBuffer.resize(48000);
m_audioBufferFill = 0;
m_audioFifo = 0;
memset(m_dvAudioSamples, 0, SerialDV::MBE_AUDIO_BLOCK_SIZE*sizeof(short));
setVolumeFactors();
}
DVSerialWorker::~DVSerialWorker()
{
}
bool DVSerialWorker::open(const std::string& serialDevice)
{
return m_dvController.open(serialDevice);
}
void DVSerialWorker::close()
{
m_dvController.close();
}
void DVSerialWorker::process()
{
m_running = true;
qDebug("DVSerialWorker::process: started");
while (m_running)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
}
qDebug("DVSerialWorker::process: stopped");
emit finished();
}
void DVSerialWorker::stop()
{
m_running = false;
}
void DVSerialWorker::handleInputMessages()
{
Message* message;
m_audioBufferFill = 0;
AudioFifo *audioFifo = 0;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (MsgMbeDecode::match(*message))
{
MsgMbeDecode *decodeMsg = (MsgMbeDecode *) message;
int dBVolume = (decodeMsg->getVolumeIndex() - 30) / 4;
float volume = pow(10.0, dBVolume / 10.0f);
int upsampling = decodeMsg->getUpsampling();
upsampling = upsampling > 6 ? 6 : upsampling < 1 ? 1 : upsampling;
if ((volume != m_volume) || (upsampling != m_upsampling))
{
m_volume = volume;
m_upsampling = upsampling;
setVolumeFactors();
}
m_upsampleFilter.useHP(decodeMsg->getUseHP());
if (m_dvController.decode(m_dvAudioSamples, decodeMsg->getMbeFrame(), decodeMsg->getMbeRate()))
{
if (upsampling > 1) {
upsample(upsampling, m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels());
} else {
noUpsample(m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels());
}
audioFifo = decodeMsg->getAudioFifo();
}
else
{
qDebug("DVSerialWorker::handleInputMessages: MsgMbeDecode: decode failed");
}
}
delete message;
}
if (audioFifo)
{
uint res = audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill);
if (res != m_audioBufferFill)
{
qDebug("DVSerialWorker::handleInputMessages: %u/%u audio samples written", res, m_audioBufferFill);
}
}
m_timestamp = QDateTime::currentDateTime();
}
void DVSerialWorker::pushMbeFrame(const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
int upsampling,
AudioFifo *audioFifo)
{
m_audioFifo = audioFifo;
m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, upsampling, audioFifo));
}
bool DVSerialWorker::isAvailable()
{
if (m_audioFifo == 0) {
return true;
}
return m_timestamp.time().msecsTo(QDateTime::currentDateTime().time()) > 1000; // 1 second inactivity timeout
}
bool DVSerialWorker::hasFifo(AudioFifo *audioFifo)
{
return m_audioFifo == audioFifo;
}
void DVSerialWorker::upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels)
{
for (int i = 0; i < nbSamplesIn; i++)
{
//float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) m_compressor.compress(in[i])) : (float) m_compressor.compress(in[i]);
float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i];
float prev = m_upsamplerLastValue;
qint16 upsample;
for (int j = 1; j <= upsampling; j++)
{
upsample = (qint16) m_upsampleFilter.runLP(cur*m_upsamplingFactors[j] + prev*m_upsamplingFactors[upsampling-j]);
m_audioBuffer[m_audioBufferFill].l = channels & 1 ? m_compressor.compress(upsample) : 0;
m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? m_compressor.compress(upsample) : 0;
if (m_audioBufferFill < m_audioBuffer.size() - 1)
{
++m_audioBufferFill;
}
else
{
qDebug("DVSerialWorker::upsample6: audio buffer is full check its size");
}
}
m_upsamplerLastValue = cur;
}
}
void DVSerialWorker::noUpsample(short *in, int nbSamplesIn, unsigned char channels)
{
for (int i = 0; i < nbSamplesIn; i++)
{
float cur = m_upsampleFilter.usesHP() ? m_upsampleFilter.runHP((float) in[i]) : (float) in[i];
m_audioBuffer[m_audioBufferFill].l = channels & 1 ? cur*m_upsamplingFactors[0] : 0;
m_audioBuffer[m_audioBufferFill].r = (channels>>1) & 1 ? cur*m_upsamplingFactors[0] : 0;
if (m_audioBufferFill < m_audioBuffer.size() - 1)
{
++m_audioBufferFill;
}
else
{
qDebug("DVSerialWorker::noUpsample: audio buffer is full check its size");
}
}
}
void DVSerialWorker::setVolumeFactors()
{
m_upsamplingFactors[0] = m_volume;
for (int i = 1; i <= m_upsampling; i++) {
m_upsamplingFactors[i] = (i*m_volume) / (float) m_upsampling;
}
}
//void DVSerialWorker::upsample6(short *in, short *out, int nbSamplesIn)
//{
// for (int i = 0; i < nbSamplesIn; i++)
// {
// int cur = (int) in[i];
// int prev = (int) m_upsamplerLastValue;
// short up;
//
//// DEBUG:
//// for (int j = 0; j < 6; j++)
//// {
//// up = 32768.0f * cos(m_phase);
//// *out = up;
//// out ++;
//// *out = up;
//// out ++;
//// m_phase += M_PI / 6.0;
//// }
////
//// if ((i % 2) == 1)
//// {
//// m_phase = 0.0f;
//// }
//
// up = m_upsampleFilter.run((cur*1 + prev*5) / 6);
// *out = up;
// out++;
// *out = up;
// out++;
//
// up = m_upsampleFilter.run((cur*2 + prev*4) / 6);
// *out = up;
// out++;
// *out = up;
// out++;
//
// up = m_upsampleFilter.run((cur*3 + prev*3) / 6);
// *out = up;
// out++;
// *out = up;
// out++;
//
// up = m_upsampleFilter.run((cur*4 + prev*2) / 6);
// *out = up;
// out++;
// *out = up;
// out++;
//
// up = m_upsampleFilter.run((cur*5 + prev*1) / 6);
// *out = up;
// out++;
// *out = up;
// out++;
//
// up = m_upsampleFilter.run(in[i]);
// *out = up;
// out++;
// *out = up;
// out++;
//
// m_upsamplerLastValue = in[i];
// }
//}

View File

@ -1,161 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DSP_DVSERIALWORKER_H_
#define SDRBASE_DSP_DVSERIALWORKER_H_
#include <QObject>
#include <QDebug>
#include <QTimer>
#include <QDateTime>
#include <vector>
#include "dvcontroller.h"
#include "util/message.h"
#include "util/syncmessenger.h"
#include "util/messagequeue.h"
#include "export.h"
#include "dsp/filtermbe.h"
#include "dsp/dsptypes.h"
#include "audio/audiocompressor.h"
class AudioFifo;
class SDRBASE_API DVSerialWorker : public QObject {
Q_OBJECT
public:
class MsgTest : public Message
{
MESSAGE_CLASS_DECLARATION
public:
static MsgTest* create() { return new MsgTest(); }
private:
MsgTest() {}
};
class MsgMbeDecode : public Message
{
MESSAGE_CLASS_DECLARATION
public:
const unsigned char *getMbeFrame() const { return m_mbeFrame; }
SerialDV::DVRate getMbeRate() const { return m_mbeRate; }
int getVolumeIndex() const { return m_volumeIndex; }
unsigned char getChannels() const { return m_channels % 4; }
bool getUseHP() const { return m_useHP; }
int getUpsampling() const { return m_upsampling; }
AudioFifo *getAudioFifo() { return m_audioFifo; }
static MsgMbeDecode* create(
const unsigned char *mbeFrame,
int mbeRateIndex,
int volumeIndex,
unsigned char channels,
bool useHP,
int upsampling,
AudioFifo *audioFifo)
{
return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, useHP, upsampling, audioFifo);
}
private:
unsigned char m_mbeFrame[SerialDV::MBE_FRAME_MAX_LENGTH_BYTES];
SerialDV::DVRate m_mbeRate;
int m_volumeIndex;
unsigned char m_channels;
bool m_useHP;
int m_upsampling;
AudioFifo *m_audioFifo;
MsgMbeDecode(const unsigned char *mbeFrame,
SerialDV::DVRate mbeRate,
int volumeIndex,
unsigned char channels,
bool useHP,
int upsampling,
AudioFifo *audioFifo) :
Message(),
m_mbeRate(mbeRate),
m_volumeIndex(volumeIndex),
m_channels(channels),
m_useHP(useHP),
m_upsampling(upsampling),
m_audioFifo(audioFifo)
{
memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::DVController::getNbMbeBytes(m_mbeRate));
}
};
DVSerialWorker();
~DVSerialWorker();
void pushMbeFrame(const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
int upsampling,
AudioFifo *audioFifo);
bool open(const std::string& serialDevice);
void close();
void process();
void stop();
bool isAvailable();
bool hasFifo(AudioFifo *audioFifo);
void postTest()
{
//emit inputMessageReady();
m_inputMessageQueue.push(MsgTest::create());
}
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
AudioFifo *m_audioFifo;
QDateTime m_timestamp;
signals:
void finished();
public slots:
void handleInputMessages();
private:
//void upsample6(short *in, short *out, int nbSamplesIn);
void upsample(int upsampling, short *in, int nbSamplesIn, unsigned char channels);
void noUpsample(short *in, int nbSamplesIn, unsigned char channels);
void setVolumeFactors();
SerialDV::DVController m_dvController;
volatile bool m_running;
int m_currentGainIn;
int m_currentGainOut;
short m_dvAudioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE];
//short m_audioSamples[SerialDV::MBE_AUDIO_BLOCK_SIZE * 6 * 2]; // upsample to 48k and duplicate channel
AudioVector m_audioBuffer;
uint m_audioBufferFill;
float m_upsamplerLastValue;
float m_phase;
MBEAudioInterpolatorFilter m_upsampleFilter;
int m_upsampling;
float m_volume;
float m_upsamplingFactors[7];
AudioCompressor m_compressor;
};
#endif /* SDRBASE_DSP_DVSERIALWORKER_H_ */