Staged merge of master

half-duplex
Phil Taylor 2022-08-20 19:43:58 +01:00
commit 4574e2b7b2
74 zmienionych plików z 8799 dodań i 4266 usunięć

4
.gitignore vendored
Wyświetl plik

@ -3,4 +3,8 @@
.qmake.stash
debug
release
wfview-debug
wfserver-debug
wfview-release
wfserver-release
ui_*

Wyświetl plik

@ -1,6 +1,44 @@
# CHANGELOG
- 20220819
version bumped to 1.4
temporary squashed logs; may redo later
audio fixes at exit
introduction of peak decays at the scope
resizing of top and bottom scope/waterfall
various fixes on the spectrum display
- 20220414
this is a cherrypicked CHANGELOG add as there are loads of changes due to different
audio transport ideas etc. For a full changelog exec: git log"
About box updated to include Patreon site
added 500 Hz step for VFO
Added clock and UTC toggle.
Added forced manual RTS setting
Add RIT function and other rigctl fixes
Adjusted window size for radios without spectrum. Thanks K5TUX.
Allow dynamic restarting of server
New Settings tab
Enable High DPI Scaling
More multi-radio support
many rigctl improvements/cmpat adds
N1MM+ TCP connection added
- 20211201
Another "minor" update for RX only rigs
@ -94,7 +132,7 @@
Added an IF Shift-like control for radios with Twin PBF.
Added support for IF Shift and Twin Pass-Band Filters. Currently
accessable only via the debug button.
accessible only via the debug button.
- 20211104
Added IC-736 FM mode
@ -494,7 +532,7 @@
Just helps keep the UI in sync with changes taking place at the rig. The
polling is slow enough that it doesn't impact anything. But quick enough
that it catches discrepencies pretty quickly.
that it catches discrepancies pretty quickly.
- 20210619
@ -535,7 +573,7 @@
Changed collision detection code so that we can more easily see what
message was missed.
Added collision detection for serial commands. Collisions are aparently
Added collision detection for serial commands. Collisions are apparently
frequent for true 1-wire CI-V radios.
We now calculate polling rates immediately upon receiveCommReady for
@ -689,7 +727,7 @@
- 20210601
Stop deleting audio after last client disconects
Stop deleting audio after last client disconnects
Change udpserver packet handling to be similar to udphandler
@ -807,7 +845,7 @@
Add debugging and fix silly error in audiooutput combobox
reenable audio buffers
re-enable audio buffers
Attempt to fix crash
@ -830,7 +868,7 @@
Fix for stuttering audio on mac
Fixed missing break in switchs.
Fixed missing break in switches.
Typo in message about CI-V
@ -866,7 +904,7 @@
Fixed issue where unknown rigs were identified as 0xff. All rigs are now
identified using rigCaps.modelID, populated with the raw response from
the Rig ID query. Enumerations of known rig types continue to fall into
rigCaps.model, and unknwn rigs will match to rigCaps.model=modelUnknown,
rigCaps.model, and unknown rigs will match to rigCaps.model=modelUnknown,
as before.
Serial baud rate is in the UI now. Added some enable/disable code to
@ -1055,7 +1093,7 @@
Additional bands added, Airband, WFM
added 630/2200m for 705; probably like the 7300 hardly useable for TX though. Also added auomatic switching to the view pane after non-BSR bands are selected
added 630/2200m for 705; probably like the 7300 hardly usable for TX though. Also added auomatic switching to the view pane after non-BSR bands are selected
added 630/2200m to 7300/7610/785x
@ -1131,7 +1169,7 @@
- 20210411 Added ATU feature on 7610.
Now grabs scope state on startup. Found bug, did not fix, in the
frequency parsing code. Worked aroud it by using doubles for now.
frequency parsing code. Worked around it by using doubles for now.
- 20210410 Preamp and attenuator are now queried on startup. Additionally, the
@ -1258,7 +1296,7 @@
S-meter ballistics:
Turned up the speed. Once you see fast meters, there's no going back.
Tested at 5ms without any issues, comitting at 10ms for now. Note that
Tested at 5ms without any issues, committing at 10ms for now. Note that
50ms in the first 'fixed' meter code is the same as 25ms now due to how
the command queue is structured. So 10ms is only a bit faster than
before.

Wyświetl plik

@ -6,3 +6,12 @@ the following people currently contribute to this Project:
- Jim PA8E
- Phil M0VSE
- Roeland PA3MET
Also contributions by:
Daniele Forsi <dforsi@gmail.com>
Russ Woodman - K5TUX <k5tux@k5tux.us>
(and some others I will add too)

Wyświetl plik

@ -14,6 +14,9 @@ sudo apt-get install libqt5multimedia5-plugins
sudo apt-get install qtmultimedia5-dev
sudo apt-get install git
sudo apt-get install libopus-dev
sudo apt-get install libeigen3-dev
sudo apt-get install portaudio19-dev
sudo apt-get install librtaudio-dev
~~~
Now you need to install qcustomplot. There are two versions that are commonly found in linux distros: 1.3 and 2.0. Either will work fine. If you are not sure which version your linux install comes with, simply run both commands. One will work and the other will fail, and that's fine!
@ -70,12 +73,12 @@ sudo usermod -aG dialout $USER
### opensuse/sles/tumbleweed install
---
install wfview on suse 15.x sles 15.x or tumbleweed; this was done on a clean install/updated OS.
install wfview on suse 15.3 & up, sles 15.x or tumbleweed; this was done on a clean install/updated OS.
we need to add packages to be able to build the stuff.
- sudo zypper in --type pattern devel_basis
- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel
- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel eigen3-devel
optional (mainly for development specifics): get and install qt5:
@ -94,9 +97,9 @@ in this case, use your homedir:
- mkdir -p ~/src/build && cd src
- git clone https://gitlab.com/eliggett/wfview.git
- cd build
- qmake-qt5 ../wfview/wfview.pro
- qmake-qt5 ../wfview/wfview.pro (wfserver.pro if you build the server version)
- make -j
- sudo ./install.sh
- sudo make install
wfview is now installed in /usr/local/bin

Wyświetl plik

@ -22,8 +22,8 @@ Debian 11 (Debian 10 is outdated)
Fedora 33
Fedora 34
mint 20.1 (and up?)
openSUSE 15.2
openSUSE 15.3 (see notes at the end)
openSUSE 15.2 (outdated/deprecated)
openSUSE 15.3/15.4)
openSUSE Tumbleweed(s)
SLES 15.x
Ubuntu 20.04.2
@ -86,16 +86,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l
~~~
### openSUSE/Tumbleweed/SLES based on 15.2:
### openSUSE/Tumbleweed/SLES based on 15.3/15.4:
~~~
sudo zypper in libqcustomplot2 libQt5SerialPort5
wfview
~~~
### openSUSE/Tumbleweed/SLES based on 15.3:
~~~
SEE THE NOTES AT THE END. You need wfview153 here
sudo zypper in libqcustomplot2 libQt5SerialPort5
wfview
@ -119,14 +111,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l
### notes:
~~~
Some newer versions of mint. ubuntu, openSUSE have different kernels and such which cause wfview to segfault.
openSUSE 15.2 is deprecated; old binary has been removed
For these cases we created two binaries: one for current systems ("wfview") and one for the new systems ("wfview153")
So if you encounter a SEGFAULT at start:
go in to the dist directory, rename wfview to wfvie152; rename wfview153 to wfview and re-execute the install.sh
script
~~~

Wyświetl plik

@ -14,7 +14,7 @@ Other models to be tested/added (including the IC-705)..
website - [WFVIEW](https://wfview.org/) wfview.org
wfview supports viewing the spectrum display waterfall and most normal radio controls. Using wfview, the radio can be operated using the mouse, or just the keyboard (great for those with visual impairments), or even a touch screen display. The gorgous waterfall spectrum can be displayed on a monitor of any size, and can even projected onto a wall for a presentation. Even a VNC session can make use of wfview for interesting remote rig posibilities. wfview runs on humble hardware, ranging from the $35 Raspberry Pi, to laptops, to desktops. wfview is designed to run on GNU Linux, but can probably be adapted to run on other operating systems. In fact we do have working example in windows as well.
wfview supports viewing the spectrum display waterfall and most normal radio controls. Using wfview, the radio can be operated using the mouse, or just the keyboard (great for those with visual impairments), or even a touch screen display. The gorgous waterfall spectrum can be displayed on a monitor of any size, and can even projected onto a wall for a presentation. Even a VNC session can make use of wfview for interesting remote rig possibilities. wfview runs on humble hardware, ranging from the $35 Raspberry Pi, to laptops, to desktops. wfview is designed to run on GNU Linux, but can probably be adapted to run on other operating systems. In fact we do have working example in windows as well.
@ -29,16 +29,16 @@ wfview is copyright 2017-2020 Elliott H. Liggett. All rights reserved. wfview so
2. Double-click anywhere on the bandscope or waterfall to tune the radio.
3. Entry of frequency is permitted under the "Frequency" tab. Buttons are provided for touch-screen control
4. Bandscope parameters (span and mode) are adjustable.
5. Full [keyboard](https://gitlab.com/eliggett/wfview/-/wikis/Keystrokes) and mouse control. Operate in whichever way you like. Most radio functions can be operated from a numberic keypad! This also enables those with visual impairments to use the IC-7300.
5. Full [keyboard](https://gitlab.com/eliggett/wfview/-/wikis/Keystrokes) and mouse control. Operate in whichever way you like. Most radio functions can be operated from a numeric keypad! This also enables those with visual impairments to use the IC-7300.
6. 100 user memories stored in plain text on the computer
7. Stylable GUI using CSS
8. pseudo-terminal device, which allows for secondary program to control the radio while wfview is running
9. works for radios that support the ethernet interface with compareable waterfall speeds as on the radio itself.
9. works for radios that support the ethernet interface with comparable waterfall speeds as on the radio itself.
### Build Requirements:
1. gcc / g++ / make
2. qmake
3. qt5 (proably the package named "qt5-default")
3. qt5 (probably the package named "qt5-default")
4. libqt5serialport5-dev
5. libqcustomplot-dev

Wyświetl plik

@ -42,7 +42,7 @@ on/off switch for the scope/waterfall
wf theme
Currently fixed selections how the scope and waterfall look like
with colors. At som epoint we may add the ability to accept the
with colors. At some point we may add the ability to accept the
RGB values like the rig does.

Wyświetl plik

@ -1,22 +1,31 @@
The following highlights are in this 1.x-release:
many changes/mods/updates/enhancements to rigctld
rigctld box added in the UI
build process changed: you can add the install prefix (derSuessmann)
added "do not ask again" for switching off rig and exiting wfview
added opus as audio transport
dual meter support
rigctl basic split support
rigctl prevents switching off civ transceive
added 25 kHz step
as a temporary measure sending multiple TX/FREQ change commands to the rig
when we use rigctld.
people should use "fake it" in wsjtx as the split code is not reliable.
tidied up udp server function for better reliability
added some IC736 stuff
added portaudio (you need t change wfview.pro to select
and that lowers the latency to maybe less than 50 ms
added PBT and IF-shift
several bugs fixed
RTS as PTT for several radios like the 706/718/736…
New major change is the audio transport mechanism. Lower latencies.
About box updated to include Patreon site
added 500 Hz step for VFO
Added clock and UTC toggle.
Added forced manual RTS setting
Add RIT function and other rigctl fixes
Adjusted window size for radios without spectrum. Thanks K5TUX.
Allow dynamic restarting of server
New Settings tab
Enable High DPI Scaling
More multi-radio support (mostly working!)
Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select
split/combine (or default). Split only makes sense for LAN and Combine for USB.
added radio status display with meters for audio (speaker/mic)
selector for audio: QT, PortAudio, RealTime audio
+ version bumped to 1.4 -- rethinking of a new version schema that makes more sense
+ temporary squashed logs; may redo later
+ audio fixes at exit
+ introduction of peak decays at the scope
+ resizing of top and bottom scope/waterfall
+ various fixes on the spectrum display
Notes:
- We know about high CPU usage on RPi.
- try the audiostack you like the most/what works for you

313
audioconverter.cpp 100644
Wyświetl plik

@ -0,0 +1,313 @@
#include "audioconverter.h"
#include "logcategories.h"
#include "ulaw.h"
audioConverter::audioConverter(QObject* parent) : QObject(parent)
{
}
bool audioConverter::init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality)
{
this->inFormat = inFormat;
this->outFormat = outFormat;
this->opusComplexity = opusComplexity;
this->resampleQuality = resampleQuality;
qInfo(logAudioConverter) << "Starting audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() <<
"Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize();
if (inFormat.byteOrder() != outFormat.byteOrder()) {
qInfo(logAudioConverter) << "Byteorder mismatch in:" << inFormat.byteOrder() << "out:" << outFormat.byteOrder();
}
if (inFormat.codec() == "audio/opus")
{
// Create instance of opus decoder
int opus_err = 0;
opusDecoder = opus_decoder_create(inFormat.sampleRate(), inFormat.channelCount(), &opus_err);
qInfo(logAudioConverter()) << "Creating opus decoder: " << opus_strerror(opus_err);
}
if (outFormat.codec() == "audio/opus")
{
// Create instance of opus encoder
int opus_err = 0;
opusEncoder = opus_encoder_create(outFormat.sampleRate(), outFormat.channelCount(), OPUS_APPLICATION_AUDIO, &opus_err);
//opus_encoder_ctl(opusEncoder, OPUS_SET_LSB_DEPTH(16));
//opus_encoder_ctl(opusEncoder, OPUS_SET_INBAND_FEC(1));
//opus_encoder_ctl(opusEncoder, OPUS_SET_DTX(1));
//opus_encoder_ctl(opusEncoder, OPUS_SET_PACKET_LOSS_PERC(5));
opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(opusComplexity)); // Reduce complexity to maybe lower CPU?
qInfo(logAudioConverter()) << "Creating opus encoder: " << opus_strerror(opus_err);
}
if (inFormat.sampleRate() != outFormat.sampleRate())
{
int resampleError = 0;
unsigned int ratioNum;
unsigned int ratioDen;
// Sample rate conversion required.
resampler = wf_resampler_init(outFormat.channelCount(), inFormat.sampleRate(), outFormat.sampleRate(), resampleQuality, &resampleError);
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
resampleRatio = static_cast<double>(ratioDen) / ratioNum;
qInfo(logAudioConverter()) << "wf_resampler_init() returned: " << resampleError << " resampleRatio: " << resampleRatio;
}
return true;
}
audioConverter::~audioConverter()
{
qInfo(logAudioConverter) << "Closing audioConverter() Input:" << inFormat.channelCount() << "Channels of" << inFormat.codec() << inFormat.sampleRate() << inFormat.sampleType() << inFormat.sampleSize() <<
"Output:" << outFormat.channelCount() << "Channels of" << outFormat.codec() << outFormat.sampleRate() << outFormat.sampleType() << outFormat.sampleSize();
if (opusEncoder != Q_NULLPTR) {
qInfo(logAudioConverter()) << "Destroying opus encoder";
opus_encoder_destroy(opusEncoder);
}
if (opusDecoder != Q_NULLPTR) {
qInfo(logAudioConverter()) << "Destroying opus decoder";
opus_decoder_destroy(opusDecoder);
}
if (resampler != Q_NULLPTR) {
speex_resampler_destroy(resampler);
qDebug(logAudioConverter()) << "Resampler closed";
}
}
bool audioConverter::convert(audioPacket audio)
{
// If inFormat and outFormat are identical, just emit the data back (removed as it doesn't then process amplitude)
if (audio.data.size() > 0)
{
if (inFormat.codec() == "audio/opus")
{
unsigned char* in = (unsigned char*)audio.data.data();
//Decode the frame.
int nSamples = opus_packet_get_nb_samples(in, audio.data.size(), inFormat.sampleRate());
if (nSamples == -1) {
// No opus data yet?
return false;
}
QByteArray outPacket(nSamples * sizeof(float) * inFormat.channelCount(), (char)0xff); // Preset the output buffer size.
float* out = (float*)outPacket.data();
//if (audio.seq > lastAudioSequence + 1) {
// nSamples = opus_decode_float(opusDecoder, Q_NULLPTR, 0, out, nSamples, 1);
//}
//else {
nSamples = opus_decode_float(opusDecoder, in, audio.data.size(), out, nSamples, 0);
//}
//lastAudioSequence = audio.seq;
audio.data.clear();
audio.data = outPacket; // Replace incoming data with converted.
}
else if (inFormat.codec() == "audio/PCMU")
{
// Current packet is "technically" 8bit so need to create a new buffer that is 16bit
QByteArray outPacket((int)audio.data.length() * 2, (char)0xff);
qint16* out = (qint16*)outPacket.data();
for (int f = 0; f < audio.data.length(); f++)
{
*out++ = ulaw_decode[(quint8)audio.data[f]];
}
audio.data.clear();
audio.data = outPacket; // Replace incoming data with converted.
// Make sure that sample size/type is set correctly
}
Eigen::VectorXf samplesF;
if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 32)
{
Eigen::Ref<VectorXint32> samplesI = Eigen::Map<VectorXint32>(reinterpret_cast<qint32*>(audio.data.data()), audio.data.size() / int(sizeof(qint32)));
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint32>::max());
}
else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 16)
{
Eigen::Ref<VectorXint16> samplesI = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(audio.data.data()), audio.data.size() / int(sizeof(qint16)));
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint16>::max());
}
else if (inFormat.sampleType() == QAudioFormat::SignedInt && inFormat.sampleSize() == 8)
{
Eigen::Ref<VectorXint8> samplesI = Eigen::Map<VectorXint8>(reinterpret_cast<qint8*>(audio.data.data()), audio.data.size() / int(sizeof(qint8)));
samplesF = samplesI.cast<float>() / float(std::numeric_limits<qint8>::max());;
}
else if (inFormat.sampleType() == QAudioFormat::UnSignedInt && inFormat.sampleSize() == 8)
{
Eigen::Ref<VectorXuint8> samplesI = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(audio.data.data()), audio.data.size() / int(sizeof(quint8)));
samplesF = samplesI.cast<float>() / float(std::numeric_limits<quint8>::max());;
}
else if (inFormat.sampleType() == QAudioFormat::Float) {
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(audio.data.data()), audio.data.size() / int(sizeof(float)));
}
else
{
qInfo(logAudio()) << "Unsupported Input Sample Type:" << inFormat.sampleType() << "Size:" << inFormat.sampleSize();
}
if (samplesF.size() > 0)
{
audio.amplitude = samplesF.array().abs().maxCoeff();
// Set the volume
samplesF *= audio.volume;
/*
samplesF is now an Eigen Vector of the current samples in float format
The next step is to convert to the correct number of channels in outFormat.channelCount()
*/
if (inFormat.channelCount() == 2 && outFormat.channelCount() == 1) {
// If we need to drop one of the audio channels, do it now
Eigen::VectorXf samplesTemp(samplesF.size() / 2);
samplesTemp = Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesF.data(), samplesF.size() / 2);
samplesF = samplesTemp;
}
else if (inFormat.channelCount() == 1 && outFormat.channelCount() == 2) {
// Convert mono to stereo if required
Eigen::VectorXf samplesTemp(samplesF.size() * 2);
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data(), samplesF.size()) = samplesF;
Eigen::Map<Eigen::VectorXf, 0, Eigen::InnerStride<2> >(samplesTemp.data() + 1, samplesF.size()) = samplesF;
samplesF = samplesTemp;
}
/*
Next step is to resample (if needed)
*/
if (resampler != Q_NULLPTR && resampleRatio != 1.0)
{
quint32 outFrames = ((samplesF.size() / outFormat.channelCount()) * resampleRatio);
quint32 inFrames = (samplesF.size() / outFormat.channelCount());
QByteArray outPacket(outFrames * outFormat.channelCount() * sizeof(float), (char)0xff); // Preset the output buffer size.
const float* in = (float*)samplesF.data();
float* out = (float*)outPacket.data();
int err = 0;
if (outFormat.channelCount() == 1) {
err = wf_resampler_process_float(resampler, 0, in, &inFrames, out, &outFrames);
}
else {
err = wf_resampler_process_interleaved_float(resampler, in, &inFrames, out, &outFrames);
}
if (err) {
qInfo(logAudioConverter()) << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
}
samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
}
/*
If output is Opus so encode it now, don't do any more conversion on the output of Opus.
*/
if (outFormat.codec() == "audio/opus")
{
float* in = (float*)samplesF.data();
QByteArray outPacket(1275, (char)0xff); // Preset the output buffer size to MAXIMUM possible Opus frame size
unsigned char* out = (unsigned char*)outPacket.data();
int nbBytes = opus_encode_float(opusEncoder, in, (samplesF.size() / outFormat.channelCount()), out, outPacket.length());
if (nbBytes < 0)
{
qInfo(logAudioConverter()) << "Opus encode failed:" << opus_strerror(nbBytes) << "Num Samples:" << samplesF.size();
return false;
}
else {
outPacket.resize(nbBytes);
audio.data.clear();
audio.data = outPacket; // Copy output packet back to input buffer.
//samplesF = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(outPacket.data()), outPacket.size() / int(sizeof(float)));
}
}
else {
/*
Now convert back into the output format required
*/
audio.data.clear();
if (outFormat.sampleType() == QAudioFormat::UnSignedInt && outFormat.sampleSize() == 8)
{
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint8>::max());
samplesITemp.array() += 127;
VectorXuint8 samplesI = samplesITemp.cast<quint8>();
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(quint8)));
}
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 8)
{
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint8>::max());
VectorXint8 samplesI = samplesITemp.cast<qint8>();
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint8)));
}
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 16)
{
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint16>::max());
VectorXint16 samplesI = samplesITemp.cast<qint16>();
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint16)));
}
else if (outFormat.sampleType() == QAudioFormat::SignedInt && outFormat.sampleSize() == 32)
{
Eigen::VectorXf samplesITemp = samplesF * float(std::numeric_limits<qint32>::max());
VectorXint32 samplesI = samplesITemp.cast<qint32>();
audio.data = QByteArray(reinterpret_cast<char*>(samplesI.data()), int(samplesI.size()) * int(sizeof(qint32)));
}
else if (outFormat.sampleType() == QAudioFormat::Float)
{
audio.data = QByteArray(reinterpret_cast<char*>(samplesF.data()), int(samplesF.size()) * int(sizeof(float)));
}
else {
qInfo(logAudio()) << "Unsupported Output Sample Type:" << outFormat.sampleType() << "Size:" << outFormat.sampleSize();
}
/*
As we currently don't have a float based uLaw encoder, this must be done
after all other conversion has taken place.
*/
if (outFormat.codec() == "audio/PCMU")
{
QByteArray outPacket((int)audio.data.length() / 2, (char)0xff);
qint16* in = (qint16*)audio.data.data();
for (int f = 0; f < outPacket.length(); f++)
{
qint16 sample = *in++;
int sign = (sample >> 8) & 0x80;
if (sign)
sample = (short)-sample;
if (sample > cClip)
sample = cClip;
sample = (short)(sample + cBias);
int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF];
int mantissa = (sample >> (exponent + 3)) & 0x0F;
int compressedByte = ~(sign | (exponent << 4) | mantissa);
outPacket[f] = (quint8)compressedByte;
}
audio.data.clear();
audio.data = outPacket; // Copy output packet back to input buffer.
}
}
}
else
{
qDebug(logAudioConverter) << "Detected empty packet";
}
}
emit converted(audio);
return true;
}

137
audioconverter.h 100644
Wyświetl plik

@ -0,0 +1,137 @@
#ifndef AUDIOCONVERTER_H
#define AUDIOCONVERTER_H
#include <QObject>
#include <QByteArray>
#include <QTime>
#include <QMap>
#include <QDebug>
#include <QAudioFormat>
#include <QAudioDeviceInfo>
/* Opus and Eigen */
#ifdef Q_OS_WIN
#include "opus.h"
#include <Eigen/Eigen>
#else
#include "opus/opus.h"
#include <eigen3/Eigen/Eigen>
#endif
enum audioType {qtAudio,portAudio,rtAudio};
#include "resampler/speex_resampler.h"
#include "packettypes.h"
struct audioPacket {
quint32 seq;
QTime time;
quint16 sent;
QByteArray data;
quint8 guid[GUIDLEN];
float amplitude;
qreal volume = 1.0;
};
struct audioSetup {
audioType type;
QString name;
quint16 latency;
quint8 codec;
bool ulaw = false;
bool isinput;
quint32 sampleRate;
QAudioDeviceInfo port;
int portInt;
quint8 resampleQuality;
unsigned char localAFgain;
quint16 blockSize = 20; // Each 'block' of audio is 20ms long by default.
quint8 guid[GUIDLEN];
};
class audioConverter : public QObject
{
Q_OBJECT
public:
explicit audioConverter(QObject* parent = nullptr);;
~audioConverter();
public slots:
bool init(QAudioFormat inFormat, QAudioFormat outFormat, quint8 opusComplexity, quint8 resampleQuality);
bool convert(audioPacket audio);
signals:
void converted(audioPacket audio);
protected:
QAudioFormat inFormat;
QAudioFormat outFormat;
OpusEncoder* opusEncoder = Q_NULLPTR;
OpusDecoder* opusDecoder = Q_NULLPTR;
SpeexResamplerState* resampler = Q_NULLPTR;
quint8 opusComplexity;
quint8 resampleQuality = 4;
double resampleRatio=1.0; // Default resample ratio is 1:1
quint32 lastAudioSequence;
};
// Various audio handling functions declared inline
typedef Eigen::Matrix<quint8, Eigen::Dynamic, 1> VectorXuint8;
typedef Eigen::Matrix<qint8, Eigen::Dynamic, 1> VectorXint8;
typedef Eigen::Matrix<qint16, Eigen::Dynamic, 1> VectorXint16;
typedef Eigen::Matrix<qint32, Eigen::Dynamic, 1> VectorXint32;
static inline QAudioFormat toQAudioFormat(quint8 codec, quint32 sampleRate)
{
QAudioFormat format;
/*
0x01 uLaw 1ch 8bit
0x02 PCM 1ch 8bit
0x04 PCM 1ch 16bit
0x08 PCM 2ch 8bit
0x10 PCM 2ch 16bit
0x20 uLaw 2ch 8bit
0x40 Opus 1ch
0x80 Opus 2ch
*/
format.setByteOrder(QAudioFormat::LittleEndian);
format.setCodec("audio/pcm");
format.setSampleRate(sampleRate);
if (codec == 0x01 || codec == 0x20) {
/* Set sample to be what is expected by the encoder and the output of the decoder */
format.setSampleSize(16);
format.setSampleType(QAudioFormat::SignedInt);
format.setCodec("audio/PCMU");
}
if (codec == 0x02 || codec == 0x08) {
format.setSampleSize(8);
format.setSampleType(QAudioFormat::UnSignedInt);
}
if (codec == 0x08 || codec == 0x10 || codec == 0x20 || codec == 0x80) {
format.setChannelCount(2);
} else {
format.setChannelCount(1);
}
if (codec == 0x04 || codec == 0x10) {
format.setSampleSize(16);
format.setSampleType(QAudioFormat::SignedInt);
}
if (codec == 0x40 || codec == 0x80) {
format.setSampleSize(32);
format.setSampleType(QAudioFormat::Float);
format.setCodec("audio/opus");
}
return format;
}
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -1,23 +1,20 @@
#ifndef AUDIOHANDLER_H
#define AUDIOHANDLER_H
/* QT Headers */
#include <QObject>
#include <QByteArray>
#include <QMutex>
#include <QtEndian>
#include <QtMath>
#include <QThread>
#include <QTime>
#include <QMap>
#include <QDebug>
#include <QTimer>
#if defined(RTAUDIO)
#ifdef Q_OS_WIN
#include "RtAudio.h"
#else
#include "rtaudio/RtAudio.h"
#endif
#elif defined (PORTAUDIO)
#include "portaudio.h"
//#error "PORTAUDIO is not currently supported"
#else
/* QT Audio Headers */
#include <QAudioOutput>
#include <QAudioFormat>
#if QT_VERSION < 0x060000
@ -32,205 +29,115 @@
#endif
#include <QIODevice>
#endif
typedef signed short MY_TYPE;
#define FORMAT RTAUDIO_SINT16
#define SCALE 32767.0
#define LOG100 4.60517018599
/* wfview Packet types */
#include "packettypes.h"
#include <QThread>
#include <QTimer>
#include <QTime>
#include <QMap>
#include "resampler/speex_resampler.h"
#include "ring/ring.h"
#ifdef Q_OS_WIN
#include "opus.h"
#else
#include "opus/opus.h"
#endif
/* Logarithmic taper for volume control */
#include "audiotaper.h"
#include <QDebug>
/* Audio converter class*/
#include "audioconverter.h"
//#define BUFFER_SIZE (32*1024)
#define INTERNAL_SAMPLE_RATE 48000
#define MULAW_BIAS 33
#define MULAW_MAX 0x1fff
struct audioPacket {
quint32 seq;
QTime time;
quint16 sent;
QByteArray data;
};
struct audioSetup {
QString name;
quint8 bits;
quint8 radioChan;
quint16 samplerate;
quint16 latency;
quint8 codec;
bool ulaw;
bool isinput;
#if defined(RTAUDIO) || defined(PORTAUDIO)
int port;
#else
#if QT_VERSION < 0x060000
QAudioDeviceInfo port;
#else
QAudioDevice port;
#endif
#endif
quint8 resampleQuality;
unsigned char localAFgain;
};
// For QtMultimedia, use a native QIODevice
#if !defined(PORTAUDIO) && !defined(RTAUDIO)
class audioHandler : public QIODevice
#else
//class audioHandler : public QIODevice
class audioHandler : public QObject
#endif
{
Q_OBJECT
public:
audioHandler(QObject* parent = 0);
~audioHandler();
audioHandler(QObject* parent = nullptr);
virtual ~audioHandler();
int getLatency();
virtual int getLatency();
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
virtual void start();
virtual void stop();
void start();
void flush();
void stop();
qint64 bytesAvailable() const;
bool isSequential() const;
#endif
void getNextAudioChunk(QByteArray &data);
virtual quint16 getAmplitude();
public slots:
bool init(audioSetup setup);
void changeLatency(const quint16 newSize);
void setVolume(unsigned char volume);
void incomingAudio(const audioPacket data);
virtual bool init(audioSetup setup);
virtual void changeLatency(const quint16 newSize);
virtual void setVolume(unsigned char volume);
virtual void incomingAudio(const audioPacket data);
virtual void convertedInput(audioPacket audio);
virtual void convertedOutput(audioPacket audio);
private slots:
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
void notified();
void stateChanged(QAudio::State state);
#endif
virtual void stateChanged(QAudio::State state);
virtual void clearUnderrun();
virtual void getNextAudioChunk();
signals:
void audioMessage(QString message);
void sendLatency(quint16 newSize);
void haveAudioData(const QByteArray& data);
void haveAudioData(const audioPacket& data);
void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under,bool over);
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
void sendToConverter(audioPacket audio);
private:
#if defined(RTAUDIO)
int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
//qint64 readData(char* data, qint64 nBytes);
//qint64 writeData(const char* data, qint64 nBytes);
static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<audioHandler*>(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<audioHandler*>(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
#elif defined(PORTAUDIO)
int readData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames,
const PaStreamCallbackTimeInfo* streamTime,
PaStreamCallbackFlags status);
static int staticRead(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) {
return ((audioHandler*)userData)->readData(inputBuffer, outputBuffer, nFrames, streamTime, status);
}
int writeData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames,
const PaStreamCallbackTimeInfo* streamTime,
PaStreamCallbackFlags status);
static int staticWrite(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) {
return ((audioHandler*)userData)->writeData(inputBuffer, outputBuffer, nFrames, streamTime, status);
}
#else
qint64 readData(char* data, qint64 nBytes);
qint64 writeData(const char* data, qint64 nBytes);
#endif
void reinit();
bool isUnderrun = false;
bool isOverrun = false;
bool isInitialized=false;
bool isReady = false;
bool audioBuffered = false;
#if defined(RTAUDIO)
RtAudio* audio = Q_NULLPTR;
int audioDevice = 0;
RtAudio::StreamParameters aParams;
RtAudio::StreamOptions options;
RtAudio::DeviceInfo info;
#elif defined(PORTAUDIO)
PaStream* audio = Q_NULLPTR;
PaStreamParameters aParams;
const PaDeviceInfo *info;
#else
QAudioFormat format;
QAudioOutput* audioOutput=Q_NULLPTR;
QAudioInput* audioInput=Q_NULLPTR;
QIODevice* audioDevice=Q_NULLPTR;
QAudioFormat inFormat;
QAudioFormat outFormat;
#if QT_VERSION < 0x060000
QAudioDeviceInfo deviceInfo;
QAudioOutput* audioOutput = Q_NULLPTR;
QAudioInput* audioInput = Q_NULLPTR;
#else
QMediaDevices deviceInfo;
QAudioSink* audioOutput = Q_NULLPTR;
QAudioSource* audioInput = Q_NULLPTR;
QAudioDevice deviceInfo;
#endif
#endif
SpeexResamplerState* resampler = Q_NULLPTR;
audioConverter* converter=Q_NULLPTR;
QThread* converterThread = Q_NULLPTR;
QTime lastReceived;
//r8b::CFixedBuffer<double>* resampBufs;
//r8b::CPtrKeeper<r8b::CDSPResampler24*>* resamps;
quint16 audioLatency;
unsigned int chunkSize;
bool chunkAvailable;
quint32 lastSeq;
quint32 lastSentSeq=0;
qint64 elapsedMs = 0;
quint16 nativeSampleRate=0;
quint8 radioSampleBits;
quint8 radioChannels;
QMap<quint32, audioPacket>audioBuffer;
int delayedPackets=0;
double resampleRatio;
wilt::Ring<audioPacket> *ringBuf=Q_NULLPTR;
volatile bool ready = false;
audioPacket tempBuf;
quint16 currentLatency;
qreal volume=1.0;
int devChannels;
float amplitude=0.0;
qreal volume = 1.0;
audioSetup setup;
OpusEncoder* encoder=Q_NULLPTR;
OpusDecoder* decoder=Q_NULLPTR;
OpusEncoder* encoder = Q_NULLPTR;
OpusDecoder* decoder = Q_NULLPTR;
QTimer* underTimer;
};
#endif // AUDIOHANDLER_H

Wyświetl plik

@ -173,7 +173,7 @@
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load the calibration data fromthe indicated slot in the preference file. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Load the calibration data from the indicated slot in the preference file. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Load</string>

Wyświetl plik

@ -3,17 +3,11 @@
#include <QDebug>
// Copytight 2017-2020 Elliott H. Liggett
// Copyright 2017-2020 Elliott H. Liggett
commHandler::commHandler()
commHandler::commHandler(QObject* parent) : QObject(parent)
{
//constructor
// grab baud rate and other comm port details
// if they need to be changed later, please
// destroy this and create a new one.
port = new QSerialPort();
// TODO: The following should become arguments and/or functions
// Add signal/slot everywhere for comm port setup.
// Consider how to "re-setup" and how to save the state for next time.
@ -22,23 +16,21 @@ commHandler::commHandler()
portName = "/dev/ttyUSB0";
this->PTTviaRTS = false;
setupComm(); // basic parameters
openPort();
//qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
//port->setReadBufferSize(1024); // manually. 256 never saw any return from the radio. why...
//qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn()));
init();
}
commHandler::commHandler(QString portName, quint32 baudRate)
commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent)
{
//constructor
// grab baud rate and other comm port details
// if they need to be changed later, please
// destroy this and create a new one.
port = new QSerialPort();
if (wfFormat == 1) { // Single waterfall packet
combineWf = true;
qDebug(logSerial()) << "*********** Combine Waterfall Mode Enabled!";
}
// TODO: The following should become arguments and/or functions
// Add signal/slot everywhere for comm port setup.
@ -47,7 +39,19 @@ commHandler::commHandler(QString portName, quint32 baudRate)
stopbits = 1;
this->portName = portName;
this->PTTviaRTS = false;
init();
}
void commHandler::init()
{
if (port != Q_NULLPTR) {
delete port;
port = Q_NULLPTR;
isConnected = false;
}
port = new QSerialPort();
setupComm(); // basic parameters
openPort();
// qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
@ -55,12 +59,17 @@ commHandler::commHandler(QString portName, quint32 baudRate)
//qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn()));
connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));
lastDataReceived = QTime::currentTime();
}
commHandler::~commHandler()
{
this->closePort();
qInfo(logSerial()) << "Closing serial port: " << port->portName();
if (isConnected) {
this->closePort();
}
delete port;
}
void commHandler::setupComm()
@ -78,6 +87,13 @@ void commHandler::receiveDataFromUserToRig(const QByteArray &data)
void commHandler::sendDataOut(const QByteArray &writeData)
{
// Recycle port to attempt reconnection.
if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) {
qDebug(logSerial()) << "Serial port error? Attempting reconnect...";
lastDataReceived = QTime::currentTime();
QTimer::singleShot(500, this, SLOT(init()));
return;
}
mutex.lock();
@ -127,7 +143,7 @@ void commHandler::sendDataOut(const QByteArray &writeData)
}
bytesWritten = port->write(writeData);
if(bytesWritten != (qint64)writeData.size())
{
qDebug(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\
@ -146,6 +162,7 @@ void commHandler::receiveDataIn()
// because we know what constitutes a valid "frame" of data.
// new code:
lastDataReceived = QTime::currentTime();
port->startTransaction();
inPortData = port->readAll();
@ -165,7 +182,7 @@ void commHandler::receiveDataIn()
}
if(inPortData.startsWith("\xFE\xFE"))
if (inPortData.startsWith("\xFE\xFE"))
{
if(inPortData.contains("\xFC"))
{
@ -178,17 +195,74 @@ void commHandler::receiveDataIn()
{
// good!
port->commitTransaction();
//payloadIn = data.right(data.length() - 4);
// Do we need to combine waterfall into single packet?
int combined = 0;
if (combineWf) {
int pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00"));
int fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
//printHex(inPortData, false, true);
while (pos > -1 && fdPos > -1) {
combined++;
spectrumDivisionNumber = 0;
spectrumDivisionNumber = inPortData[pos + 3] & 0x0f;
spectrumDivisionNumber += ((inPortData[pos + 3] & 0xf0) >> 4) * 10;
if (spectrumDivisionNumber == 1) {
// This is the first waveform data.
spectrumDivisionMax = 0;
spectrumDivisionMax = inPortData[pos + 4] & 0x0f;
spectrumDivisionMax += ((inPortData[pos + 4] & 0xf0) >> 4) * 10;
spectrumData.clear();
spectrumData = inPortData.mid(pos - 4, fdPos+4); // Don't include terminating FD
spectrumData[8] = spectrumData[7]; // Make max = current;
//qDebug() << "New Spectrum seq:" << spectrumDivisionNumber << "pos = " << pos << "len" << fdPos;
}
else if (spectrumDivisionNumber > lastSpectrum && spectrumDivisionNumber <= spectrumDivisionMax) {
spectrumData.insert(spectrumData.length(), inPortData.mid(pos + 4, fdPos-5));
//qDebug() << "Added spectrum seq:" << spectrumDivisionNumber << "len" << fdPos-5;
//printHex(inPortData.mid((pos+4),fdPos - (pos+5)), false, true);
}
else {
qDebug() << "Invalid Spectrum Division received" << spectrumDivisionNumber << "last Spectrum" << lastSpectrum;
}
lastSpectrum = spectrumDivisionNumber;
if (spectrumDivisionNumber == spectrumDivisionMax) {
//qDebug() << "Got Spectrum! length=" << spectrumData.length();
spectrumData.append("\xfd"); // Need to add FD on the end.
//printHex(spectrumData, false, true);
emit haveDataFromPort(spectrumData);
lastSpectrum = 0;
}
inPortData = inPortData.remove(pos-4, fdPos+5);
pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00"));
fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
}
// If we still have data left, let the main function deal with it, any spectrum data has been removed
if (inPortData.length() == 0)
{
return;
}
// qDebug() << "Got extra data!";
//printHex(inPortData, false, true);
}
//
emit haveDataFromPort(inPortData);
if(rolledBack)
{
// qInfo(logSerial()) << "Rolled back and was successfull. Length: " << inPortData.length();
// qInfo(logSerial()) << "Rolled back and was successful. Length: " << inPortData.length();
//printHex(inPortData, false, true);
rolledBack = false;
}
} else {
// did not receive the entire thing so roll back:
// qInfo(logSerial()) << "Rolling back transaction. End not detected. Lenth: " << inPortData.length();
// qInfo(logSerial()) << "Rolling back transaction. End not detected. Length: " << inPortData.length();
//printHex(inPortData, false, true);
port->rollbackTransaction();
rolledBack = true;
@ -230,7 +304,6 @@ void commHandler::setUseRTSforPTT(bool PTTviaRTS)
void commHandler::openPort()
{
bool success;
// port->open();
success = port->open(QIODevice::ReadWrite);
if(success)
{
@ -253,7 +326,7 @@ void commHandler::closePort()
if(port)
{
port->close();
delete port;
//delete port;
}
isConnected = false;
}
@ -299,3 +372,17 @@ void commHandler::printHex(const QByteArray &pdata, bool printVert, bool printHo
}
void commHandler::handleError(QSerialPort::SerialPortError err)
{
switch (err) {
case QSerialPort::NoError:
break;
default:
if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) {
qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect";
lastDataReceived = QTime::currentTime();
QTimer::singleShot(500, this, SLOT(init()));
}
break;
}
}

Wyświetl plik

@ -6,6 +6,8 @@
#include <QMutex>
#include <QDataStream>
#include <QtSerialPort/QSerialPort>
#include <QTime>
#include <QTimer>
// This class abstracts the comm port in a useful way and connects to
// the command creator and command parser.
@ -15,8 +17,8 @@ class commHandler : public QObject
Q_OBJECT
public:
commHandler();
commHandler(QString portName, quint32 baudRate);
commHandler(QObject* parent = nullptr);
commHandler(QString portName, quint32 baudRate, quint8 wfFormat,QObject* parent = nullptr);
bool serialError;
bool rtsStatus();
@ -25,6 +27,8 @@ public:
public slots:
void setUseRTSforPTT(bool useRTS);
void setRTS(bool rtsOn);
void handleError(QSerialPort::SerialPortError error);
void init();
private slots:
void receiveDataIn(); // from physical port
@ -58,7 +62,7 @@ private:
unsigned char buffer[256];
QString portName;
QSerialPort *port;
QSerialPort *port=Q_NULLPTR;
qint32 baudrate;
unsigned char stopbits;
bool rolledBack;
@ -74,7 +78,15 @@ private:
bool isConnected; // port opened
mutable QMutex mutex;
void printHex(const QByteArray &pdata, bool printVert, bool printHoriz);
bool combineWf = false;
QByteArray spectrumData;
quint8 spectrumDivisionNumber;
quint8 spectrumDivisionMax;
quint8 spectrumCenterOrFixed;
quint8 spectrumInformation;
quint8 spectrumOutOfRange;
quint8 lastSpectrum = 0;
QTime lastDataReceived;
};
#endif // COMMHANDLER_H

Wyświetl plik

@ -1,7 +1,7 @@
#include "freqmemory.h"
#include "logcategories.h"
// Copytight 2017-2020 Elliott H. Liggett
// Copyright 2017-2020 Elliott H. Liggett
freqMemory::freqMemory()
{

26
keyboard.cpp 100644
Wyświetl plik

@ -0,0 +1,26 @@
#include <QCoreApplication>
#include <QObject>
#include <QThread>
#include <QFile>
#include <QTextStream>
#include "keyboard.h"
keyboard::keyboard(void)
{
}
keyboard::~keyboard(void)
{
}
void keyboard::run()
{
while (true)
{
char key = getchar();
if (key == 'q') {
QCoreApplication::quit();
}
}
return;
}

11
keyboard.h 100644
Wyświetl plik

@ -0,0 +1,11 @@
#include <QCoreApplication>
#include <QObject>
#include <QThread>
class keyboard : public QThread
{
Q_OBJECT
public:
keyboard(void);
~keyboard(void);
void run();
};

Wyświetl plik

@ -8,3 +8,5 @@ Q_LOGGING_CATEGORY(logAudio, "audio")
Q_LOGGING_CATEGORY(logUdp, "udp")
Q_LOGGING_CATEGORY(logUdpServer, "udp.server")
Q_LOGGING_CATEGORY(logRigCtlD, "rigctld")
Q_LOGGING_CATEGORY(logTcpServer, "tcpserver")
Q_LOGGING_CATEGORY(logAudioConverter, "audioconverter")

Wyświetl plik

@ -11,6 +11,8 @@ Q_DECLARE_LOGGING_CATEGORY(logAudio)
Q_DECLARE_LOGGING_CATEGORY(logUdp)
Q_DECLARE_LOGGING_CATEGORY(logUdpServer)
Q_DECLARE_LOGGING_CATEGORY(logRigCtlD)
Q_DECLARE_LOGGING_CATEGORY(logTcpServer)
Q_DECLARE_LOGGING_CATEGORY(logAudioConverter)
#if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__)

104
main.cpp
Wyświetl plik

@ -1,25 +1,68 @@
#ifdef BUILD_WFSERVER
#include <QtCore/QCoreApplication>
#include "keyboard.h"
#else
#include <QApplication>
#endif
#ifdef Q_OS_WIN
#include <windows.h>
#include <csignal>
#endif
#include <iostream>
#include "wfmain.h"
#include "logcategories.h"
// Copytight 2017-2021 Elliott H. Liggett
// Copyright 2017-2021 Elliott H. Liggett
// Smart pointer to log file
QScopedPointer<QFile> m_logFile;
QMutex logMutex;
bool debugMode=false;
#ifdef BUILD_WFSERVER
servermain* w=Q_NULLPTR;
#ifdef Q_OS_WIN
bool __stdcall cleanup(DWORD sig)
#else
static void cleanup(int sig)
#endif
{
Q_UNUSED(sig)
qDebug() << "Exiting via SIGNAL";
if (w!=Q_NULLPTR) w->deleteLater();
QCoreApplication::quit();
#ifdef Q_OS_WIN
return true;
#else
return;
#endif
}
#endif
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//a.setStyle( "Fusion" );
#ifdef BUILD_WFSERVER
QCoreApplication a(argc, argv);
a.setOrganizationName("wfview");
a.setOrganizationDomain("wfview.org");
a.setApplicationName("wfserver");
keyboard* kb = new keyboard();
kb->start();
#else
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv);
a.setOrganizationName("wfview");
a.setOrganizationDomain("wfview.org");
a.setApplicationName("wfview");
#endif
#ifdef QT_DEBUG
debugMode = true;
@ -38,12 +81,19 @@ int main(int argc, char *argv[])
const QString helpText = QString("\nUsage: -p --port /dev/port, -h --host remotehostname, -c --civ 0xAddr, -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO...
#ifdef BUILD_WFSERVER
const QString version = QString("wfserver version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n")
.arg(QString(WFVIEW_VERSION))
.arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST)
.arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture())
.arg(QT_VERSION_STR).arg(qVersion());
#else
const QString version = QString("wfview version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n")
.arg(QString(WFVIEW_VERSION))
.arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST)
.arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture())
.arg(QT_VERSION_STR).arg(qVersion());
.arg(QString(WFVIEW_VERSION))
.arg(GITSHORT).arg(__DATE__).arg(__TIME__).arg(UNAME).arg(HOST)
.arg(QSysInfo::prettyProductName()).arg(QSysInfo::buildCpuArchitecture())
.arg(QT_VERSION_STR).arg(qVersion());
#endif
for(int c=1; c<argc; c++)
{
//qInfo() << "Argc: " << c << " argument: " << argv[c];
@ -95,31 +145,17 @@ int main(int argc, char *argv[])
}
else if ((currentArg == "-?") || (currentArg == "--help"))
{
#ifdef Q_OS_WIN
QMessageBox::information(0, "wfview help", helpText);
#else
std::cout << helpText.toStdString();
#endif
return 0;
}
else if ((currentArg == "-v") || (currentArg == "--version"))
{
#ifdef Q_OS_WIN
QMessageBox::information(0, "wfview version", version);
#else
std::cout << version.toStdString();
#endif
return 0;
} else {
#ifdef Q_OS_WIN
QMessageBox::information(0, "wfview unrecognised argument", helpText);
#else
std::cout << "Unrecognized option: " << currentArg.toStdString();
std::cout << helpText.toStdString();
#endif
return -1;
return -1;
}
}
@ -135,13 +171,22 @@ int main(int argc, char *argv[])
qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL);
qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL);
qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL);
#ifdef BUILD_WFSERVER
#ifdef Q_OS_WIN
SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE);
#else
signal(SIGINT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGKILL, cleanup);
#endif
w = new servermain(serialPortCL, hostCL, settingsFile);
#else
a.setWheelScrollLines(1); // one line per wheel click
wfmain w( serialPortCL, hostCL, settingsFile);
wfmain w(serialPortCL, hostCL, settingsFile);
w.show();
#endif
return a.exec();
}
@ -181,5 +226,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt
}
// Write to the output category of the message and the message itself
out << context.category << ": " << msg << "\n";
#ifdef BUILD_WFSERVER
std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ").toLocal8Bit().toStdString() << msg.toLocal8Bit().toStdString() << "\n";
#endif
out.flush(); // Clear the buffered data
}

Wyświetl plik

@ -51,7 +51,7 @@ meter::meter(QWidget *parent) : QWidget(parent)
void meter::clearMeterOnPTTtoggle()
{
// When a meter changes type, such as the fixed S -- TxPo meter,
// there is automatic clearing. However, some meters do not switch on thier own,
// there is automatic clearing. However, some meters do not switch on their own,
// and thus we are providing this clearing method. We are careful
// not to clear meters that don't make sense to clear (such as Vd and Id)
@ -110,7 +110,7 @@ void meter::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// This next line sets up a canvis within the
// space of the widget, and gives it corrdinates.
// space of the widget, and gives it coordinates.
// The end effect, is that the drawing functions will all
// scale to the window size.
painter.setRenderHint(QPainter::SmoothPixmapTransform);

Wyświetl plik

@ -4,6 +4,22 @@
#pragma pack(push, 1)
// Various settings used by both client and server
#define PURGE_SECONDS 10
#define TOKEN_RENEWAL 60000
#define PING_PERIOD 500
#define IDLE_PERIOD 100
#define AREYOUTHERE_PERIOD 500
#define WATCHDOG_PERIOD 500
#define RETRANSMIT_PERIOD 100 // How often to attempt retransmit
#define LOCK_PERIOD 10 // How long to try to lock mutex (ms)
#define STALE_CONNECTION 15 // Not heard from in this many seconds
#define BUFSIZE 500 // Number of packets to buffer
#define MAX_MISSING 50 // More than this indicates serious network problem
#define AUDIO_PERIOD 20
#define GUIDLEN 16
// Fixed Size Packets
#define CONTROL_SIZE 0x10
#define WATCHDOG_SIZE 0x14
@ -15,13 +31,15 @@
#define LOGIN_RESPONSE_SIZE 0x60
#define LOGIN_SIZE 0x80
#define CONNINFO_SIZE 0x90
#define CAPABILITIES_SIZE 0xA8
#define CAPABILITIES_SIZE 0x42
#define RADIO_CAP_SIZE 0x66
// Variable size packets + payload
#define CIV_SIZE 0x15
#define AUDIO_SIZE 0x18
#define DATA_SIZE 0x15
// 0x10 length control packet (connect/disconnect/idle.)
typedef union control_packet {
struct {
@ -103,6 +121,7 @@ typedef union audio_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
quint16 ident; // 0x10
quint16 sendseq; // 0x12
quint16 unused; // 0x14
@ -147,19 +166,24 @@ typedef union token_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedb; // 0x18
char unusedc; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[7]; // 0x20
quint16 commoncap; // 0x27
char unuseddd[2]; // 0x29
char identa; // 0x2b
quint32 identb; // 0x2c
union {
struct {
quint16 authstartid; // 0x20
char unusedg[5]; // 0x22
quint16 commoncap; // 0x27
char unusedh; // 0x29
quint8 macaddress[6]; // 0x2a
};
quint8 guid[GUIDLEN]; // 0x20
};
quint32 response; // 0x30
char unusede[12]; // 0x34
};
@ -175,20 +199,24 @@ typedef union status_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedb; // 0x18
char unusedc; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[6]; // 0x20
quint16 unknown; // 0x26
char unusede; // 0x28
char unusedf[2]; // 0x29
char identa; // 0x2b
quint32 identb; // 0x2c
union {
struct {
quint16 authstartid; // 0x20
char unusedd[5]; // 0x22
quint16 commoncap; // 0x27
char unusede; // 0x29
quint8 macaddress[6]; // 0x2a
};
quint8 guid[GUIDLEN]; // 0x20
};
quint32 error; // 0x30
char unusedg[12]; // 0x34
char disc; // 0x40
@ -210,12 +238,12 @@ typedef union login_response_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedb; // 0x18
char unusedc; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
quint16 authstartid; // 0x20
@ -237,12 +265,12 @@ typedef union login_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedaa; // 0x18;
char unusedb; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedc[32]; // 0x20
@ -263,22 +291,26 @@ typedef union conninfo_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedaa; // 0x18
char unusedb; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
quint16 authstartid; // 0x20
char unusedd[5]; // 0x22
quint32 commoncap; // 0x27
char identa; // 0x2b
quint32 identb; // 0x2c
char unusedf[16]; // 0x30
char name[16]; // 0x40
char unusedg[16]; // 0x50
union {
struct {
quint16 authstartid; // 0x20
char unusedg[5]; // 0x22
quint16 commoncap; // 0x27
char unusedh; // 0x29
quint8 macaddress[6]; // 0x2a
};
quint8 guid[GUIDLEN]; // 0x20
};
char unusedab[16]; // 0x30
char name[32]; // 0x40
union { // This contains differences between the send/receive packet
struct { // Receive
quint32 busy; // 0x60
@ -306,6 +338,41 @@ typedef union conninfo_packet {
char packet[CONNINFO_SIZE];
} *conninfo_packet_t;
// 0x64 length radio capabilities part of cap packet.
typedef union radio_cap_packet {
struct
{
union {
struct {
quint8 unusede[7]; // 0x00
quint16 commoncap; // 0x07
quint8 unused; // 0x09
quint8 macaddress[6]; // 0x0a
};
quint8 guid[GUIDLEN]; // 0x0
};
char name[32]; // 0x10
char audio[32]; // 0x30
quint16 conntype; // 0x50
char civ; // 0x52
quint16 rxsample; // 0x53
quint16 txsample; // 0x55
quint8 enablea; // 0x57
quint8 enableb; // 0x58
quint8 enablec; // 0x59
quint32 baudrate; // 0x5a
quint16 capf; // 0x5e
char unusedi; // 0x60
quint16 capg; // 0x61
char unusedj[3]; // 0x63
};
char packet[RADIO_CAP_SIZE];
} *radio_cap_packet_t;
// 0xA8 length capabilities packet
typedef union capabilities_packet {
struct
@ -315,41 +382,22 @@ typedef union capabilities_packet {
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char unuseda[3]; // 0x10
quint16 code; // 0x13
quint16 res; // 0x15
quint8 innerseq; // 0x17
char unusedb; // 0x18
char unusedc; // 0x19
char unuseda[2]; // 0x10
quint16 payloadsize; // 0x12
quint8 requestreply; // 0x13
quint8 requesttype; // 0x14
quint16 innerseq; // 0x16
char unusedb[2]; // 0x18
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[33]; // 0x20
char capa; // 0x41
char unusede[7]; // 0x42
quint16 commoncap; // 0x49
char unused; // 0x4b
char macaddress[6]; // 0x4c
char name[32]; // 0x52
char audio[32]; // 0x72
quint16 conntype; // 0x92
char civ; // 0x94
quint16 rxsample; // 0x95
quint16 txsample; // 0x97
quint8 enablea; // 0x99
quint8 enableb; // 0x9a
quint8 enablec; // 0x9b
quint32 baudrate; // 0x9c
quint16 capf; // 0xa0
char unusedi; // 0xa2
quint16 capg; // 0xa3
char unusedj[3]; // 0xa5
char unusedd[32]; // 0x20
quint16 numradios; // 0x40
};
char packet[CAPABILITIES_SIZE];
} *capabilities_packet_t;
#pragma pack(pop)

315
pahandler.cpp 100644
Wyświetl plik

@ -0,0 +1,315 @@
#include "pahandler.h"
#include "logcategories.h"
#if defined(Q_OS_WIN)
#include <objbase.h>
#endif
paHandler::paHandler(QObject* parent)
{
Q_UNUSED(parent)
}
paHandler::~paHandler()
{
if (converterThread != Q_NULLPTR) {
converterThread->quit();
converterThread->wait();
}
if (isInitialized) {
Pa_StopStream(audio);
Pa_CloseStream(audio);
}
}
bool paHandler::init(audioSetup setup)
{
if (isInitialized) {
return false;
}
this->setup = setup;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "PortAudio handler starting:" << setup.name;
if (setup.portInt == -1)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found.";
return false;
}
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
", bits" << inFormat.sampleSize() <<
", codec" << setup.codec <<
", latency" << setup.latency <<
", localAFGain" << setup.localAFgain <<
", radioChan" << inFormat.channelCount() <<
", resampleQuality" << setup.resampleQuality <<
", samplerate" << inFormat.sampleRate() <<
", uLaw" << setup.ulaw;
PaError err;
#ifdef Q_OS_WIN
CoInitialize(0);
#endif
//err = Pa_Initialize();
//if (err != paNoError)
//{
// qDebug(logAudio()) << "Portaudio initialized";
//}
memset(&aParams, 0, sizeof(PaStreamParameters));
aParams.device = setup.portInt;
info = Pa_GetDeviceInfo(aParams.device);
qDebug(logAudio()) << "PortAudio" << (setup.isinput ? "Input" : "Output") << setup.portInt << "Input Channels" << info->maxInputChannels << "Output Channels" << info->maxOutputChannels;
if (setup.isinput) {
outFormat.setChannelCount(info->maxInputChannels);
}
else {
outFormat.setChannelCount(info->maxOutputChannels);
}
aParams.suggestedLatency = (float)setup.latency / 1000.0f;
outFormat.setSampleRate(info->defaultSampleRate);
aParams.sampleFormat = paFloat32;
outFormat.setSampleSize(32);
outFormat.setSampleType(QAudioFormat::Float);
outFormat.setByteOrder(QAudioFormat::LittleEndian);
outFormat.setCodec("audio/pcm");
if (outFormat.channelCount() > 2) {
outFormat.setChannelCount(2);
}
else if (outFormat.channelCount() < 1)
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
return false;
}
if (inFormat.channelCount() < outFormat.channelCount()) {
outFormat.setChannelCount(inFormat.channelCount());
}
aParams.channelCount = outFormat.channelCount();
if (outFormat.sampleRate() < 44100) {
outFormat.setSampleRate(48000);
}
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
// We "hopefully" now have a valid format that is supported so try connecting
converter = new audioConverter();
converterThread = new QThread(this);
if (setup.isinput) {
converterThread->setObjectName("audioConvIn()");
}
else {
converterThread->setObjectName("audioConvOut()");
}
converter->moveToThread(converterThread);
connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8)));
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
converterThread->start(QThread::TimeCriticalPriority);
aParams.hostApiSpecificStreamInfo = NULL;
// Per channel chunk size.
this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / sizeof(float)) * outFormat.channelCount();
// Check the format is supported
if (setup.isinput) {
err = Pa_IsFormatSupported(&aParams, NULL, outFormat.sampleRate());
}
else
{
err = Pa_IsFormatSupported(NULL,&aParams, outFormat.sampleRate());
}
if (err != paNoError) {
if (err == paInvalidChannelCount)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported channel count" << aParams.channelCount;
if (aParams.channelCount == 2) {
aParams.channelCount = 1;
outFormat.setChannelCount(1);
}
else {
aParams.channelCount = 2;
outFormat.setChannelCount(2);
}
}
else if (err == paInvalidSampleRate)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unsupported sample rate" << outFormat.sampleRate();
outFormat.setSampleRate(44100);
}
else if (err == paSampleFormatNotSupported)
{
aParams.sampleFormat = paInt16;
outFormat.setSampleType(QAudioFormat::SignedInt);
outFormat.setSampleSize(16);
}
if (setup.isinput) {
err = Pa_IsFormatSupported(&aParams, NULL, outFormat.sampleRate());
}
else
{
err = Pa_IsFormatSupported(NULL, &aParams, outFormat.sampleRate());
}
if (err != paNoError) {
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Cannot find suitable format, aborting:" << Pa_GetErrorText(err);
return false;
}
}
if (setup.isinput) {
err = Pa_OpenStream(&audio, &aParams, 0, outFormat.sampleRate(), this->chunkSize, paNoFlag, &paHandler::staticWrite, (void*)this);
emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
}
else {
err = Pa_OpenStream(&audio, 0, &aParams, outFormat.sampleRate(), this->chunkSize, paNoFlag, NULL, NULL);
emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
}
if (err == paNoError) {
err = Pa_StartStream(audio);
}
if (err == paNoError) {
isInitialized = true;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
}
else {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "failed to open device" << Pa_GetErrorText(err);
}
this->setVolume(setup.localAFgain);
return isInitialized;
}
void paHandler::setVolume(unsigned char volume)
{
#ifdef Q_OS_WIN
this->volume = audiopot[volume] * 5;
#else
this->volume = audiopot[volume];
#endif
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
}
void paHandler::incomingAudio(audioPacket packet)
{
packet.volume = volume;
emit sendToConverter(packet);
return;
}
int paHandler::writeData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime,
PaStreamCallbackFlags status)
{
Q_UNUSED(outputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
audioPacket packet;
packet.time = QTime::currentTime();
packet.sent = 0;
packet.volume = volume;
memcpy(&packet.guid, setup.guid, GUIDLEN);
packet.data.append((char*)inputBuffer, nFrames*inFormat.channelCount()*sizeof(float));
emit sendToConverter(packet);
if (status == paInputUnderflow) {
isUnderrun = true;
}
else if (status == paInputOverflow) {
isOverrun = true;
}
else
{
isUnderrun = false;
isOverrun = false;
}
return paContinue;
}
void paHandler::convertedOutput(audioPacket packet) {
if (packet.data.size() > 0) {
if (Pa_IsStreamActive(audio) == 1) {
PaError err = Pa_WriteStream(audio, (char*)packet.data.data(), packet.data.size() / sizeof(float) / outFormat.channelCount());
if (err != paNoError) {
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!";
}
const PaStreamInfo* info = Pa_GetStreamInfo(audio);
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000);
}
amplitude = packet.amplitude;
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
}
}
void paHandler::convertedInput(audioPacket packet)
{
if (packet.data.size() > 0) {
emit haveAudioData(packet);
amplitude = packet.amplitude;
const PaStreamInfo* info = Pa_GetStreamInfo(audio);
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->inputLatency * 1000);
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
}
}
void paHandler::changeLatency(const quint16 newSize)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
}
int paHandler::getLatency()
{
return currentLatency;
}
quint16 paHandler::getAmplitude()
{
return static_cast<quint16>(amplitude * 255.0);
}

96
pahandler.h 100644
Wyświetl plik

@ -0,0 +1,96 @@
#ifndef PAHANDLER_H
#define PAHANDLER_H
#include <QObject>
#include <QByteArray>
#include <QThread>
#include "portaudio.h"
#include <QAudioFormat>
#include <QTime>
#include <QMap>
/* wfview Packet types */
#include "packettypes.h"
/* Logarithmic taper for volume control */
#include "audiotaper.h"
#include "audiohandler.h"
/* Audio converter class*/
#include "audioconverter.h"
#include <QDebug>
class paHandler : public audioHandler
{
Q_OBJECT
public:
paHandler(QObject* parent = 0);
~paHandler();
int getLatency();
void getNextAudioChunk(QByteArray& data);
quint16 getAmplitude();
public slots:
bool init(audioSetup setup);
void changeLatency(const quint16 newSize);
void setVolume(unsigned char volume);
void convertedInput(audioPacket audio);
void convertedOutput(audioPacket audio);
void incomingAudio(const audioPacket data);
private slots:
signals:
void audioMessage(QString message);
void sendLatency(quint16 newSize);
void haveAudioData(const audioPacket& data);
void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
void sendToConverter(audioPacket audio);
private:
int writeData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames,
const PaStreamCallbackTimeInfo* streamTime,
PaStreamCallbackFlags status);
static int staticWrite(const void* inputBuffer, void* outputBuffer, unsigned long nFrames, const PaStreamCallbackTimeInfo* streamTime, PaStreamCallbackFlags status, void* userData) {
return ((paHandler*)userData)->writeData(inputBuffer, outputBuffer, nFrames, streamTime, status);
}
bool isInitialized = false;
PaStream* audio = Q_NULLPTR;
PaStreamParameters aParams;
const PaDeviceInfo* info;
quint16 audioLatency;
unsigned int chunkSize;
quint32 lastSeq;
quint32 lastSentSeq = 0;
quint16 currentLatency;
float amplitude=0.0;
qreal volume = 1.0;
audioSetup setup;
QAudioFormat inFormat;
QAudioFormat outFormat;
audioConverter* converter = Q_NULLPTR;
QThread* converterThread = Q_NULLPTR;
bool isUnderrun = false;
bool isOverrun = false;
};
#endif // PAHANDLER_H

Wyświetl plik

@ -13,7 +13,7 @@
// Copyright 2017-2021 Elliott H. Liggett & Phil Taylor
pttyHandler::pttyHandler(QString pty)
pttyHandler::pttyHandler(QString pty, QObject* parent) : QObject(parent)
{
//constructor
if (pty == "" || pty.toLower() == "none")
@ -214,12 +214,12 @@ void pttyHandler::receiveDataIn(int fd) {
if (civId == 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] > (quint8)0xdf && (quint8)inPortData[lastFE + 2] < (quint8)0xef) {
// This is (should be) the remotes CIV id.
civId = (quint8)inPortData[lastFE + 2];
qInfo(logSerial()) << "pty detected remote CI-V:" << hex << civId;
qInfo(logSerial()) << "pty detected remote CI-V:" << QString("0x%1").arg(civId,0,16);
}
else if (civId != 0 && inPortData.length() > lastFE + 2 && (quint8)inPortData[lastFE + 2] != civId)
{
civId = (quint8)inPortData[lastFE + 2];
qInfo(logSerial()) << "pty remote CI-V changed:" << hex << (quint8)civId;
qInfo(logSerial()) << "pty remote CI-V changed:" << QString("0x%1").arg((quint8)civId,0,16);
}
// filter C-IV transceive command before forwarding on.
if (inPortData.contains(rigCaps.transceiveCommand))
@ -245,14 +245,14 @@ void pttyHandler::receiveDataIn(int fd) {
if (rolledBack)
{
// qInfo(logSerial()) << "Rolled back and was successfull. Length: " << inPortData.length();
// qInfo(logSerial()) << "Rolled back and was successful. Length: " << inPortData.length();
//printHex(inPortData, false, true);
rolledBack = false;
}
}
else {
// did not receive the entire thing so roll back:
// qInfo(logSerial()) << "Rolling back transaction. End not detected. Lenth: " << inPortData.length();
// qInfo(logSerial()) << "Rolling back transaction. End not detected. Length: " << inPortData.length();
//printHex(inPortData, false, true);
rolledBack = true;
#ifdef Q_OS_WIN

Wyświetl plik

@ -19,7 +19,7 @@ class pttyHandler : public QObject
Q_OBJECT
public:
pttyHandler(QString portName);
explicit pttyHandler(QString portName, QObject* parent = nullptr);
pttyHandler(QString portName, quint32 baudRate);
bool serialError;
@ -62,7 +62,7 @@ private:
bool rolledBack;
int ptfd; // pseudo-terminal file desc.
int ptKeepAlive=0; // Used to keep the pty alive after client disconects.
int ptKeepAlive=0; // Used to keep the pty alive after client disconnects.
bool havePt;
QString ptDevSlave;

Wyświetl plik

@ -25,7 +25,7 @@ repeaterSetup::repeaterSetup(QWidget *parent) :
repeaterSetup::~repeaterSetup()
{
// Trying this for more consistant destruction
// Trying this for more consistent destruction
rig.inputs.clear();
rig.preamps.clear();
rig.attenuators.clear();

Wyświetl plik

@ -104,7 +104,7 @@ static void speex_free(void* ptr) { free(ptr); }
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
/* Number of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else

Wyświetl plik

@ -4,7 +4,7 @@
#include "rigidentities.h"
#include "logcategories.h"
// Copytight 2017-2020 Elliott H. Liggett
// Copyright 2017-2020 Elliott H. Liggett
// This file parses data from the radio and also forms commands to the radio.
// The radio physical interface is handled by the commHandler() instance "comm"
@ -20,18 +20,27 @@
// Note: When sending \x00, must use QByteArray.setRawData()
rigCommander::rigCommander()
rigCommander::rigCommander(QObject* parent) : QObject(parent)
{
state.set(SCOPEFUNC,true,false);
qInfo(logRig()) << "creating instance of rigCommander()";
state.set(SCOPEFUNC, true, false);
}
rigCommander::rigCommander(quint8 guid[GUIDLEN], QObject* parent) : QObject(parent)
{
qInfo(logRig()) << "creating instance of rigCommander()";
state.set(SCOPEFUNC, true, false);
memcpy(this->guid, guid, GUIDLEN);
}
rigCommander::~rigCommander()
{
qInfo(logRig()) << "closing instance of rigCommander()";
closeComm();
}
void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp)
void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp,quint16 tcpPort, quint8 wf)
{
// construct
// TODO: Bring this parameter and the comm port from the UI.
@ -41,23 +50,37 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu
civAddr = rigCivAddr; // address of the radio. Decimal is 148.
usingNativeLAN = false;
//qInfo(logRig()) << "Opening connection to Rig:" << QString("0x%1").arg((unsigned char)rigCivAddr,0,16) << "on serial port" << rigSerialPort << "at baud rate" << rigBaudRate;
// ---
setup();
// ---
this->rigSerialPort = rigSerialPort;
this->rigBaudRate = rigBaudRate;
rigCaps.baudRate = rigBaudRate;
comm = new commHandler(rigSerialPort, rigBaudRate,wf,this);
ptty = new pttyHandler(vsp,this);
if (tcpPort > 0) {
tcp = new tcpServer(this);
tcp->startServer(tcpPort);
}
comm = new commHandler(rigSerialPort, rigBaudRate);
ptty = new pttyHandler(vsp);
// data from the comm port to the program:
connect(comm, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray)));
// data from the ptty to the rig:
connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray)));
// data from the program to the comm port:
connect(this, SIGNAL(dataForComm(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray)));
if (tcpPort > 0) {
// data from the tcp port to the rig:
connect(tcp, SIGNAL(receiveData(QByteArray)), comm, SLOT(receiveDataFromUserToRig(QByteArray)));
connect(comm, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray)));
}
connect(this, SIGNAL(toggleRTS(bool)), comm, SLOT(setRTS(bool)));
// data from the rig to the ptty:
@ -76,7 +99,7 @@ void rigCommander::commSetup(unsigned char rigCivAddr, QString rigSerialPort, qu
}
void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp)
void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcpPort)
{
// construct
// TODO: Bring this parameter and the comm port from the UI.
@ -85,16 +108,17 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
// civAddr = 0x94; // address of the radio. Decimal is 148.
civAddr = rigCivAddr; // address of the radio. Decimal is 148.
usingNativeLAN = true;
// ---
// --
setup();
// ---
if (udp == Q_NULLPTR) {
udp = new udpHandler(prefs,rxSetup,txSetup);
udpHandlerThread = new QThread(this);
udpHandlerThread->setObjectName("udpHandler()");
udp->moveToThread(udpHandlerThread);
@ -108,8 +132,12 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
//this->rigSerialPort = rigSerialPort;
//this->rigBaudRate = rigBaudRate;
ptty = new pttyHandler(vsp);
ptty = new pttyHandler(vsp,this);
if (tcpPort > 0) {
tcp = new tcpServer(this);
tcp->startServer(tcpPort);
}
// Data from UDP to the program
connect(udp, SIGNAL(haveDataFromPort(QByteArray)), this, SLOT(handleNewData(QByteArray)));
@ -125,19 +153,28 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
// data from the ptty to the rig:
connect(ptty, SIGNAL(haveDataFromPort(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));
if (tcpPort > 0) {
// data from the tcp port to the rig:
connect(tcp, SIGNAL(receiveData(QByteArray)), udp, SLOT(receiveDataFromUserToRig(QByteArray)));
connect(udp, SIGNAL(haveDataFromPort(QByteArray)), tcp, SLOT(sendData(QByteArray)));
}
connect(this, SIGNAL(haveChangeLatency(quint16)), udp, SLOT(changeLatency(quint16)));
connect(this, SIGNAL(haveSetVolume(unsigned char)), udp, SLOT(setVolume(unsigned char)));
connect(udp, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32)));
// Connect for errors/alerts
connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
connect(udp, SIGNAL(haveNetworkStatus(QString)), this, SLOT(handleStatusUpdate(QString)));
connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(handleStatusUpdate(networkStatus)));
connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis()));
connect(this, SIGNAL(discoveredRigID(rigCapabilities)), ptty, SLOT(receiveFoundRigID(rigCapabilities)));
connect(udp, SIGNAL(requestRadioSelection(QList<radio_cap_packet>)), this, SLOT(radioSelection(QList<radio_cap_packet>)));
connect(udp, SIGNAL(setRadioUsage(quint8, quint8, QString, QString)), this, SLOT(radioUsage(quint8, quint8, QString, QString)));
connect(this, SIGNAL(selectedRadio(quint8)), udp, SLOT(setCurrentRadio(quint8)));
emit haveAfGain(rxSetup.localAFgain);
localVolume = rxSetup.localAFgain;
}
@ -203,9 +240,9 @@ void rigCommander::handleSerialPortError(const QString port, const QString error
emit haveSerialPortError(port, errorText);
}
void rigCommander::handleStatusUpdate(const QString text)
void rigCommander::handleStatusUpdate(const networkStatus status)
{
emit haveStatusUpdate(text);
emit haveStatusUpdate(status);
}
bool rigCommander::usingLAN()
@ -214,6 +251,7 @@ bool rigCommander::usingLAN()
}
void rigCommander::receiveBaudRate(quint32 baudrate) {
rigCaps.baudRate = baudrate;
emit haveBaudRate(baudrate);
}
@ -1407,7 +1445,7 @@ void rigCommander::parseLevels()
case '\x0A':
// TX RF level
emit haveTxPower(level);
state.set(TXPOWER, level, false);
state.set(RFPOWER, level, false);
break;
case '\x0B':
// Mic Gain
@ -1456,6 +1494,9 @@ void rigCommander::parseLevels()
{
switch(payloadIn[1])
{
case '\x01':
// noise or s-meter sequelch status
break;
case '\x02':
// S-Meter
emit haveMeter(meterS, level);
@ -1466,6 +1507,9 @@ void rigCommander::parseLevels()
emit haveMeter(meterCenter, level);
state.set(SMETER, level, false);
break;
case '\x05':
// Various squelch (tone etc.)
break;
case '\x11':
// RF-Power meter
emit haveMeter(meterPower, level);
@ -3492,6 +3536,34 @@ void rigCommander::determineRigCaps()
createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"),
};
break;
case model746:
rigCaps.modelName = QString("IC-746");
rigCaps.rigctlModel = 3023;
rigCaps.hasSpectrum = false;
rigCaps.inputs.clear();
rigCaps.hasLan = false;
rigCaps.hasEthernet = false;
rigCaps.hasWiFi = false;
rigCaps.hasFDcomms = false;
rigCaps.hasATU = true;
rigCaps.hasTBPF = true;
rigCaps.hasIFShift = true;
rigCaps.hasCTCSS = true;
rigCaps.hasDTCS = true;
rigCaps.hasAntennaSel = true;
rigCaps.preamps.push_back('\x01');
rigCaps.preamps.push_back('\x02');
rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x20'});
// There are two HF and VHF ant, 12-01 adn 12-02 select the HF, the VHF is auto selected
// this incorrectly shows up as 2 and 3 in the drop down.
rigCaps.antennas = {0x01, 0x02};
rigCaps.bands = standardHF;
rigCaps.bands.push_back(band2m);
rigCaps.bands.push_back(bandGen);
rigCaps.modes = commonModes;
rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00");
break;
case model756pro:
rigCaps.modelName = QString("IC-756 Pro");
rigCaps.rigctlModel = 3027;
@ -3609,6 +3681,9 @@ void rigCommander::determineRigCaps()
}
haveRigCaps = true;
// Copy received guid so we can recognise this radio.
memcpy(rigCaps.guid, this->guid, GUIDLEN);
if(!usingNativeLAN)
{
if(useRTSforPTT_isSet)
@ -3629,7 +3704,7 @@ void rigCommander::determineRigCaps()
payloadPrefix.append(civAddr);
payloadPrefix.append((char)compCivAddr);
// if there is a compile-time error, remove the following line, the "hex" part is the issue:
qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << hex << this->civAddr;
qInfo(logRig()) << "Using incomingCIVAddr: (int): " << this->civAddr << " hex: " << QString("0x%1").arg(this->civAddr,0,16);
emit discoveredRigID(rigCaps);
} else {
if(!foundRig)
@ -3730,7 +3805,7 @@ void rigCommander::parseSpectrum()
spectrumEndFreq = fEnd.MHzDouble;
if(scopeMode == spectModeCenter)
{
// "center" mode, start is actuall center, end is bandwidth.
// "center" mode, start is actual center, end is bandwidth.
spectrumStartFreq -= spectrumEndFreq;
spectrumEndFreq = spectrumStartFreq + 2*(spectrumEndFreq);
// emit haveSpectrumCenterSpan(span);
@ -4029,7 +4104,7 @@ void rigCommander::getPreamp()
void rigCommander::getAntenna()
{
// This one might neet some thought
// This one might need some thought
// as it seems each antenna has to be checked.
// Maybe 0x12 alone will do it.
QByteArray payload("\x12");
@ -4273,6 +4348,19 @@ void rigCommander::sendState()
emit stateInfo(&state);
}
void rigCommander::radioSelection(QList<radio_cap_packet> radios)
{
emit requestRadioSelection(radios);
}
void rigCommander::radioUsage(quint8 radio, quint8 busy, QString user, QString ip) {
emit setRadioUsage(radio, busy, user, ip);
}
void rigCommander::setCurrentRadio(quint8 radio) {
emit selectedRadio(radio);
}
void rigCommander::stateUpdated()
{
// A remote process has updated the rigState
@ -4397,9 +4485,9 @@ void rigCommander::stateUpdated()
}
getSql();
break;
case TXPOWER:
case RFPOWER:
if (i.value()._valid) {
setTxPower(state.getChar(TXPOWER));
setTxPower(state.getChar(RFPOWER));
}
getTxLevel();
break;
@ -4539,6 +4627,7 @@ void rigCommander::stateUpdated()
break;
// All meters can only be updated from the rig end.
case SMETER:
case SWRMETER:
case POWERMETER:
case ALCMETER:
case COMPMETER:
@ -4655,14 +4744,17 @@ void rigCommander::printHex(const QByteArray &pdata, bool printVert, bool printH
void rigCommander::dataFromServer(QByteArray data)
{
//qInfo(logRig()) << "emit dataForComm()";
//qInfo(logRig()) << "***************** emit dataForComm()" << data;
emit dataForComm(data);
}
quint8* rigCommander::getGUID() {
return guid;
}

Wyświetl plik

@ -12,6 +12,7 @@
#include "rigidentities.h"
#include "repeaterattributes.h"
#include "freqmemory.h"
#include "tcpserver.h"
#include "rigstate.h"
@ -68,15 +69,18 @@ class rigCommander : public QObject
Q_OBJECT
public:
rigCommander();
explicit rigCommander(QObject* parent=nullptr);
explicit rigCommander(quint8 guid[GUIDLEN], QObject* parent = nullptr);
~rigCommander();
bool usingLAN();
quint8* getGUID();
public slots:
void process();
void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp);
void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp);
void commSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate, QString vsp, quint16 tcp, quint8 wf);
void commSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp);
void closeComm();
void stateUpdated();
void setRTSforPTT(bool enabled);
@ -272,7 +276,10 @@ public slots:
void sayAll();
// Housekeeping:
void handleStatusUpdate(const QString text);
void handleStatusUpdate(const networkStatus status);
void radioSelection(QList<radio_cap_packet> radios);
void radioUsage(quint8 radio, quint8 busy, QString name, QString ip);
void setCurrentRadio(quint8 radio);
void sendState();
void getDebug();
@ -280,7 +287,7 @@ signals:
// Communication:
void commReady();
void haveSerialPortError(const QString port, const QString errorText);
void haveStatusUpdate(const QString text);
void haveStatusUpdate(const networkStatus status);
void dataForComm(const QByteArray &outData);
void toggleRTS(bool rtsOn);
@ -365,6 +372,9 @@ signals:
void stateInfo(rigstate* state);
// Housekeeping:
void requestRadioSelection(QList<radio_cap_packet> radios);
void setRadioUsage(quint8 radio, quint8 busy, QString user, QString ip);
void selectedRadio(quint8 radio);
void getMoreDebug();
void finished();
@ -416,6 +426,7 @@ private:
commHandler* comm = Q_NULLPTR;
pttyHandler* ptty = Q_NULLPTR;
tcpServer* tcp = Q_NULLPTR;
udpHandler* udp=Q_NULLPTR;
QThread* udpHandlerThread = Q_NULLPTR;
@ -469,7 +480,7 @@ private:
QString serialPortError;
unsigned char localVolume=0;
quint8 guid[GUIDLEN] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
};

Wyświetl plik

@ -199,7 +199,7 @@ void rigCtlClient::socketReadyRead()
response.append("1"); // rigctld protocol version
response.append(QString("%1").arg(rigCaps.rigctlModel));
response.append("0"); // Print something
bandType lastBand;
bandType lastBand=(bandType)-1;
for (bandType band : rigCaps.bands)
{
if (band != lastBand)
@ -710,7 +710,7 @@ void rigCtlClient::socketReadyRead()
resp.append(QString("%1").arg((float)rigState->getChar(ANTIVOXGAIN) / 255.0));
}
else if (command[1] == "RFPOWER") {
resp.append(QString("%1").arg((float)rigState->getChar(TXPOWER) / 255.0));
resp.append(QString("%1").arg((float)rigState->getChar(RFPOWER) / 255.0));
}
else if (command[1] == "PREAMP") {
resp.append(QString("%1").arg(rigState->getChar(PREAMP)*10));
@ -736,6 +736,10 @@ void rigCtlClient::socketReadyRead()
value = command[2].toFloat() * 255;
rigState->set(RFGAIN, value, true);
}
else if (command[1] == "RFPOWER") {
value = command[2].toFloat() * 255;
rigState->set(RFPOWER, value, true);
}
else if (command[1] == "SQL") {
value = command[2].toFloat() * 255;
rigState->set(SQUELCH, value, true);

Wyświetl plik

@ -326,7 +326,7 @@ class rigCtlD : public QTcpServer
Q_OBJECT
public:
explicit rigCtlD(QObject *parent=Q_NULLPTR);
explicit rigCtlD(QObject *parent=nullptr);
virtual ~rigCtlD();
int startServer(qint16 port);

Wyświetl plik

@ -1,7 +1,7 @@
#include "rigidentities.h"
#include "logcategories.h"
// Copytight 2017-2021 Elliott H. Liggett
// Copyright 2017-2021 Elliott H. Liggett
model_kind determineRadioModel(unsigned char rigID)
{
@ -58,8 +58,8 @@ model_kind determineRadioModel(unsigned char rigID)
case model736:
rig = model736;
break;
case model910h:
rig = model910h;
case model746:
rig = model746;
break;
case model756pro:
rig = model756pro;
@ -70,6 +70,9 @@ model_kind determineRadioModel(unsigned char rigID)
case model756proiii:
rig = model756proiii;
break;
case model910h:
rig = model910h;
break;
case model9100:
rig = model9100;
break;

Wyświetl plik

@ -7,6 +7,7 @@
#include <vector>
#include "freqmemory.h"
#include "packettypes.h"
// Credit for parts of CIV list:
// http://www.docksideradio.com/Icom%20Radio%20Hex%20Addresses.htm
@ -30,6 +31,7 @@ enum model_kind {
model706 = 0x58,
model718 = 0x5E,
model736 = 0x40,
model746 = 0x56,
model756pro = 0x5C,
model756proii = 0x64,
model756proiii = 0x6E,
@ -138,6 +140,8 @@ struct rigCapabilities {
std::vector <mode_info> modes;
QByteArray transceiveCommand;
quint8 guid[GUIDLEN] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
quint32 baudRate;
};

Wyświetl plik

@ -13,7 +13,7 @@
// Meters at the end as they are ALWAYS updated from the rig!
enum stateTypes { VFOAFREQ, VFOBFREQ, CURRENTVFO, PTT, MODE, FILTER, DUPLEX, DATAMODE, ANTENNA, RXANTENNA, CTCSS, TSQL, DTCS, CSQL,
PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, TXPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN,
PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, RFPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN,
FAGCFUNC, NBFUNC, COMPFUNC, VOXFUNC, TONEFUNC, TSQLFUNC, SBKINFUNC, FBKINFUNC, ANFFUNC, NRFUNC, AIPFUNC, APFFUNC, MONFUNC, MNFUNC,RFFUNC,
AROFUNC, MUTEFUNC, VSCFUNC, REVFUNC, SQLFUNC, ABMFUNC, BCFUNC, MBCFUNC, RITFUNC, AFCFUNC, SATMODEFUNC, SCOPEFUNC,
NBLEVEL, NBDEPTH, NBWIDTH, NRLEVEL, RIGINPUT, POWERONOFF, RITVALUE,
@ -124,4 +124,4 @@ private:
QMutex _mutex;
};
#endif
#endif

Wyświetl plik

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018 Trevor Wilson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Wyświetl plik

@ -1,10 +0,0 @@
# Ring Library
## Overview
This library provides source for a multi-producer multi-consumer lock-free ring buffer. It provides a very simple interface for writing and reading from the buffer. The source includes a `Ring_` class, that provides the raw implementation and C-like facilities, as well as a templated `Ring<T>` class for typed reads and writes.
## Contact
If you have any questions, concerns, or recommendations please feel free to e-mail me at kmdreko@gmail.com. If you notice a bug or defect, create an issue to report it.

Wyświetl plik

@ -1,290 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// FILE: ring.cpp
// DATE: 2016-02-25
// AUTH: Trevor Wilson
// DESC: Implements a lock-free, multi-consumer, multi-producer ring buffer
// class
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Trevor Wilson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "ring.h"
using namespace wilt;
#include <cstring>
// - std::memcpy
Ring_::Ring_()
: beg_(nullptr)
, end_(nullptr)
{
std::atomic_init(&used_, static_cast<std::ptrdiff_t>(0));
std::atomic_init(&free_, static_cast<std::ptrdiff_t>(0));
std::atomic_init(&rbuf_, static_cast<char*>(0));
std::atomic_init(&rptr_, static_cast<char*>(0));
std::atomic_init(&wptr_, static_cast<char*>(0));
std::atomic_init(&wbuf_, static_cast<char*>(0));
}
Ring_::Ring_(std::size_t size)
: beg_(new char[size])
, end_(beg_ + size)
{
std::atomic_init(&used_, static_cast<std::ptrdiff_t>(0));
std::atomic_init(&free_, static_cast<std::ptrdiff_t>(size));
std::atomic_init(&rbuf_, beg_);
std::atomic_init(&rptr_, beg_);
std::atomic_init(&wptr_, beg_);
std::atomic_init(&wbuf_, beg_);
}
Ring_::Ring_(Ring_&& ring)
: beg_(ring.beg_)
, end_(ring.end_)
{
std::atomic_init(&used_, ring.used_.load());
std::atomic_init(&free_, ring.free_.load());
std::atomic_init(&rbuf_, ring.rbuf_.load());
std::atomic_init(&rptr_, ring.rptr_.load());
std::atomic_init(&wptr_, ring.wptr_.load());
std::atomic_init(&wbuf_, ring.wbuf_.load());
ring.beg_ = nullptr;
ring.end_ = nullptr;
ring.used_.store(0);
ring.free_.store(0);
ring.rbuf_.store(nullptr);
ring.rptr_.store(nullptr);
ring.wptr_.store(nullptr);
ring.wbuf_.store(nullptr);
}
Ring_& Ring_::operator= (Ring_&& ring)
{
delete[] beg_;
beg_ = ring.beg_;
end_ = ring.end_;
used_.store(ring.used_.load());
free_.store(ring.free_.load());
rbuf_.store(ring.rbuf_.load());
rptr_.store(ring.rptr_.load());
wptr_.store(ring.wptr_.load());
wbuf_.store(ring.wbuf_.load());
ring.beg_ = nullptr;
ring.end_ = nullptr;
ring.used_.store(0);
ring.free_.store(0);
ring.rbuf_.store(nullptr);
ring.rptr_.store(nullptr);
ring.wptr_.store(nullptr);
ring.wbuf_.store(nullptr);
return *this;
}
Ring_::~Ring_()
{
delete[] beg_;
}
std::size_t Ring_::size() const
{
// The 'used' space can be negative in an over-reserved case, but it can be
// clamped to 0 for simplicity.
auto s = used_.load();
return s < 0 ? 0 : static_cast<std::size_t>(s);
}
std::size_t Ring_::capacity() const
{
return static_cast<std::size_t>(end_ - beg_);
}
void Ring_::read(void* data, std::size_t length) noexcept
{
auto block = acquire_read_block_(length);
copy_read_block_(block, (char*)data, length);
release_read_block_(block, length);
}
void Ring_::write(const void* data, std::size_t length) noexcept
{
auto block = acquire_write_block_(length);
copy_write_block_(block, (const char*)data, length);
release_write_block_(block, length);
}
bool Ring_::try_read(void* data, std::size_t length) noexcept
{
auto block = try_acquire_read_block_(length);
if (block == nullptr)
return false;
copy_read_block_(block, (char*)data, length);
release_read_block_(block, length);
return true;
}
bool Ring_::try_write(const void* data, std::size_t length) noexcept
{
auto block = try_acquire_write_block_(length);
if (block == nullptr)
return false;
copy_write_block_(block, (const char*)data, length);
release_write_block_(block, length);
return true;
}
char* Ring_::normalize_(char* ptr)
{
return ptr < end_ ? ptr : ptr - capacity();
}
char* Ring_::acquire_read_block_(std::size_t length)
{
auto size = static_cast<std::ptrdiff_t>(length);
while (true) // loop while conflict
{
auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr
while (used_.load(std::memory_order_consume) < size) // check for data
; // spin until success
auto new_rptr = normalize_(old_rptr + size); // get block end
used_.fetch_sub(size); // reserve
if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit
return old_rptr; // committed
used_.fetch_add(size, std::memory_order_relaxed); // un-reserve
}
}
char* Ring_::try_acquire_read_block_(std::size_t length)
{
auto size = static_cast<std::ptrdiff_t>(length);
while (true) // loop while conflict
{
auto old_rptr = rptr_.load(std::memory_order_consume); // read rptr
if (used_.load(std::memory_order_consume) < size) // check for data
return nullptr; // return failure
auto new_rptr = normalize_(old_rptr + size); // get block end
used_.fetch_sub(size); // reserve
if (rptr_.compare_exchange_strong(old_rptr, new_rptr)) // try commit
return old_rptr; // committed
used_.fetch_add(size, std::memory_order_relaxed); // un-reserve
}
}
void Ring_::copy_read_block_(const char* block, char* data, std::size_t length)
{
if (block + length < end_)
{
std::memcpy(data, block, length);
}
else
{
auto first = end_ - block;
std::memcpy(data, block, first);
std::memcpy(data + first, beg_, length - first);
}
}
void Ring_::release_read_block_(char* old_rptr, std::size_t length)
{
auto new_rptr = normalize_(old_rptr + length); // get block end
while (rbuf_.load() != old_rptr) // check for earlier reads
; // spin until reads complete
rbuf_.store(new_rptr); // finish commit
free_.fetch_add(length, std::memory_order_relaxed); // add to free space
}
char* Ring_::acquire_write_block_(std::size_t length)
{
auto size = static_cast<std::ptrdiff_t>(length);
while (true) // loop while conflict
{
auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf
while (free_.load(std::memory_order_consume) < size) // check for space
; // spin until success
auto new_wbuf = normalize_(old_wbuf + size); // get block end
free_.fetch_sub(size); // reserve
if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit
return old_wbuf; // committed
free_.fetch_add(size, std::memory_order_relaxed); // un-reserve
}
}
char* Ring_::try_acquire_write_block_(std::size_t length)
{
auto size = static_cast<std::ptrdiff_t>(length);
while (true) // loop while conflict
{
auto old_wbuf = wbuf_.load(std::memory_order_consume); // read wbuf
if (free_.load(std::memory_order_consume) < size) // check for space
return nullptr; // return failure
auto new_wbuf = normalize_(old_wbuf + size); // get block end
free_.fetch_sub(size); // reserve
if (wbuf_.compare_exchange_strong(old_wbuf, new_wbuf)) // try commit
return old_wbuf; // committed
free_.fetch_add(size, std::memory_order_relaxed); // un-reserve
}
}
void Ring_::copy_write_block_(char* block, const char* data, std::size_t length)
{
if (block + length < end_)
{
std::memcpy(block, data, length);
}
else
{
auto first = end_ - block;
std::memcpy(block, data, first);
std::memcpy(beg_, data + first, length - first);
}
}
void Ring_::release_write_block_(char* old_wbuf, std::size_t length)
{
auto new_wbuf = normalize_(old_wbuf + length); // get block end
while (wptr_.load() != old_wbuf) // wait for earlier writes
; // spin until writes complete
wptr_.store(new_wbuf); // finish commit
used_.fetch_add(length, std::memory_order_relaxed); // add to used space
}

Wyświetl plik

@ -1,440 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
// FILE: ring.h
// DATE: 2016-02-25
// AUTH: Trevor Wilson
// DESC: Defines a lock-free, multi-consumer, multi-producer ring buffer class
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016 Trevor Wilson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef WILT_RING_H
#define WILT_RING_H
#include <atomic>
// - std::atomic
#include <cstddef>
// - std::size_t
// - std::ptrdiff_t
#include <new>
// - ::new(ptr)
#include <type_traits>
// - std::is_nothrow_copy_constructible
// - std::is_nothrow_move_constructible
// - std::is_nothrow_move_assignable
// - std::is_nothrow_destructible
#include <utility>
// - std::move
namespace wilt
{
//////////////////////////////////////////////////////////////////////////////
// This structure aims to access elements in a ring buffer from multiple
// concurrent readers and writers in a lock-free manner.
//
// The class works by allocating the array and storing two pointers (for the
// beginning and end of the allocated space). Two atomic pointers are used to
// track the beginning and end of the currently used storage space. To
// facilitate concurrent reads and writes, theres a read buffer pointer before
// the read pointer for data currently being read, and a corresponding write
// buffer pointer beyond the write pointer for data currently being written.
// These buffer pointers cannot overlap. Just using these pointers suffer from
// some minute inefficiencies and a few ABA problems. Therfore, atomic
// integers are used to store the currently used and currently free sizes.
//
// It allows multiple readers and multiple writers by implementing a reserve-
// commit system. A thread wanting to read will check the used size to see if
// there's enough data. If there is, it subtracts from the used size to
// 'reserve' the read. It then does a compare-exchange to 'commit' by
// increasing the read pointer. If that fails, then it backs out ('un-
// reserves') by adding back to the used size and tries again. If it
// succeeds, then it proceeds to read the data. In order to complete, the
// reader must update the read buffer pointer to where it just finished
// reading from. However, because other readers that started before may not be
// done yet, the reader must wait until the read buffer pointer points to
// where the read started. Only, then is the read buffer pointer updated, and
// the free size increased. So while this implementation is lock-free, it is
// not wait-free. This same principle works the same when writing (ammended
// for the appropriate pointers).
//
// If two readers try to read at the same time and there is only enough data
// for one of them. The used size MAY be negative because they both 'reserve'
// the data. This is an over-reserved state. But the compare-exchange will
// only allow one reader to 'commit' to the read and the other will 'un-
// reserve' the read.
//
// |beg |rptr used=5 |wbuf - unused
// |----|----|++++|====|====|====|====|====|++++|----| + modifying
// free=3 |rbuf |wptr |end = used
//
// The diagram above shows a buffer of size 10 storing 5 bytes with a reader
// reading one byte and one writer reading one byte.
//
// Out of the box, the class works by reading and writing raw bytes from POD
// data types and arrays. A wrapper could allow for a nicer interface for
// pushing and popping elements. As it stands, this structure cannot be easily
// modified to store types of variable size.
class Ring_
{
private:
////////////////////////////////////////////////////////////////////////////
// TYPE DEFINITIONS
////////////////////////////////////////////////////////////////////////////
typedef char* data_ptr;
typedef std::atomic<std::ptrdiff_t> size_type;
typedef std::atomic<char*> atom_ptr;
private:
////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
////////////////////////////////////////////////////////////////////////////
// Beginning and end pointers don't need to be atomic because they don't
// change. used_ and free_ can be negative in certain cases (and that's ok).
data_ptr beg_; // pointer to beginning of data block
data_ptr end_; // pointer to end of data block
alignas(64)
size_type used_; // size of unreserved used space
alignas(64)
size_type free_; // size of unreserved free space
alignas(64)
atom_ptr rbuf_; // pointer to beginning of data being read
atom_ptr rptr_; // pointer to beginning of data
alignas(64)
atom_ptr wptr_; // pointer to end of data
atom_ptr wbuf_; // pointer to end of data being written
public:
////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS AND DESTRUCTORS
////////////////////////////////////////////////////////////////////////////
// Constructs a ring without a buffer (capacity() == 0)
Ring_();
// Constructs a ring with a buffer with a size
Ring_(std::size_t size);
// Moves the buffer between rings, assumes no concurrent operations
Ring_(Ring_&& ring);
// Moves the buffer between rings, assumes no concurrent operations on
// either ring. Deallocates the buffer
Ring_& operator= (Ring_&& ring);
// No copying
Ring_(const Ring_&) = delete;
Ring_& operator= (const Ring_&) = delete;
// Deallocates the buffer
~Ring_();
public:
////////////////////////////////////////////////////////////////////////////
// QUERY FUNCTIONS
////////////////////////////////////////////////////////////////////////////
// Functions only report on the state of the ring
// Returns the current amount of non-reserved used space (amount of written
// data that a read hasn't yet reserved). Over-reserved scenarios mean this
// number is not the ultimate source of truth with concurrent operations,
// but its the closest safe approximation. This, of course, doesn't report
// writes that have not completed.
std::size_t size() const;
// Maximum amount of data that can be held
std::size_t capacity() const;
public:
////////////////////////////////////////////////////////////////////////////
// ACCESSORS AND MODIFIERS
////////////////////////////////////////////////////////////////////////////
// All operations assume object has not been moved. Blocking operations run
// until operation is completed. Non-blocking operations fail if there is
// not enough space
void read(void* data, std::size_t length) noexcept;
void write(const void* data, std::size_t length) noexcept;
bool try_read(void* data, std::size_t length) noexcept;
bool try_write(const void* data, std::size_t length) noexcept;
protected:
////////////////////////////////////////////////////////////////////////////
// PROTECTED FUNCTIONS
////////////////////////////////////////////////////////////////////////////
// Helper functions
// Wraps a pointer within the array. Assumes 'beg_ <= ptr < end_+capacity()'
char* normalize_(char*);
char* acquire_read_block_(std::size_t length);
char* try_acquire_read_block_(std::size_t length);
void copy_read_block_(const char* block, char* data, std::size_t length);
void release_read_block_(char* block, std::size_t length);
char* acquire_write_block_(std::size_t length);
char* try_acquire_write_block_(std::size_t length);
void copy_write_block_(char* block, const char* data, std::size_t length);
void release_write_block_(char* block, std::size_t length);
char* begin_alloc_() { return beg_; }
const char* begin_alloc_() const { return beg_; }
char* end_alloc_() { return end_; }
const char* end_alloc_() const { return end_; }
char* begin_data_() { return rptr_; }
const char* begin_data_() const { return rptr_; }
char* end_data_() { return wptr_; }
const char* end_data_() const { return wptr_; }
}; // class Ring_
template <class T>
class Ring : protected Ring_
{
public:
////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS AND DESTRUCTORS
////////////////////////////////////////////////////////////////////////////
// Constructs a ring without a buffer (capacity() == 0)
Ring();
// Constructs a ring with a buffer with a size
Ring(std::size_t size);
// Moves the buffer between rings, assumes no concurrent operations
Ring(Ring<T>&& ring);
// Moves the buffer between rings, assumes no concurrent operations on
// either ring. Deallocates the buffer
Ring<T>& operator= (Ring<T>&& ring);
// No copying
Ring(const Ring_&) = delete;
Ring& operator= (const Ring_&) = delete;
// Deallocates the buffer, destructs stored data.
~Ring();
public:
////////////////////////////////////////////////////////////////////////////
// QUERY FUNCTIONS
////////////////////////////////////////////////////////////////////////////
// Functions only report on the state of the ring
// Returns the current amount of non-reserved used space (amount of written
// data that a read hasn't yet reserved). Over-reserved scenarios mean this
// number is not the ultimate source of truth with concurrent operations,
// but its the closest safe approximation. This, of course, doesn't report
// writes that have not completed.
std::size_t size() const;
// Maximum amount of data that can be held
std::size_t capacity() const;
public:
////////////////////////////////////////////////////////////////////////////
// ACCESSORS AND MODIFIERS
////////////////////////////////////////////////////////////////////////////
// All operations assume object has not been moved. Blocking operations run
// until operation is completed. Non-blocking operations fail if there is
// not enough space
void read(T& data) noexcept; // blocking read
void write(const T& data) noexcept; // blocking write
void write(T&& data) noexcept; // blocking write
bool try_read(T& data) noexcept; // non-blocking read
bool try_write(const T& data) noexcept; // non-blocking write
bool try_write(T&& data) noexcept; // non-blocking write
private:
////////////////////////////////////////////////////////////////////////////
// PRIVATE HELPER FUNCTIONS
////////////////////////////////////////////////////////////////////////////
void destruct_();
}; // class Ring<T>
template <class T>
Ring<T>::Ring()
: Ring_()
{ }
template <class T>
Ring<T>::Ring(std::size_t size)
: Ring_(size * sizeof(T))
{ }
template <class T>
Ring<T>::Ring(Ring<T>&& ring)
: Ring_(std::move(ring))
{ }
template <class T>
Ring<T>& Ring<T>::operator= (Ring<T>&& ring)
{
destruct_();
Ring_::operator= (ring);
return *this;
}
template <class T>
Ring<T>::~Ring()
{
destruct_();
}
template <class T>
void Ring<T>::destruct_()
{
if (size() == 0)
return;
auto itr = begin_data_();
auto end = end_data_();
do
{
auto t = reinterpret_cast<T*>(itr);
t->~T();
itr = normalize_(itr + sizeof(T));
} while (itr != end);
}
template <class T>
std::size_t Ring<T>::size() const
{
return Ring_::size() / sizeof(T);
}
template <class T>
std::size_t Ring<T>::capacity() const
{
return Ring_::capacity() / sizeof(T);
}
template <class T>
void Ring<T>::read(T& data) noexcept
{
static_assert(std::is_nothrow_move_assignable<T>::value, "T move assignment must not throw");
static_assert(std::is_nothrow_destructible<T>::value, "T destructor must not throw");
auto block = acquire_read_block_(sizeof(T));
// critical section
auto t = reinterpret_cast<T*>(block);
data = std::move(*t);
t->~T();
release_read_block_(block, sizeof(T));
}
template <class T>
void Ring<T>::write(const T& data) noexcept
{
static_assert(std::is_nothrow_copy_constructible<T>::value, "T copy constructor must not throw");
auto block = acquire_write_block_(sizeof(T));
// critical section
new(block) T(data);
release_write_block_(block, sizeof(T));
}
template <class T>
void Ring<T>::write(T&& data) noexcept
{
static_assert(std::is_nothrow_move_constructible<T>::value, "T move constructor must not throw");
auto block = acquire_write_block_(sizeof(T));
// critical section
new(block) T(std::move(data));
release_write_block_(block, sizeof(T));
}
template <class T>
bool Ring<T>::try_read(T& data) noexcept
{
static_assert(std::is_nothrow_move_assignable<T>::value, "T move assignment must not throw");
static_assert(std::is_nothrow_destructible<T>::value, "T destructor must not throw");
auto block = try_acquire_read_block_(sizeof(T));
if (block == nullptr)
return false;
// critical section
auto t = reinterpret_cast<T*>(block);
data = std::move(*t);
t->~T();
release_read_block_(block, sizeof(T));
return true;
}
template <class T>
bool Ring<T>::try_write(const T& data) noexcept
{
static_assert(std::is_nothrow_copy_constructible<T>::value, "T copy constructor must not throw");
auto block = try_acquire_write_block_(sizeof(T));
if (block == nullptr)
return false;
// critical section
new(block) T(data);
release_write_block_(block, sizeof(T));
return true;
}
template <class T>
bool Ring<T>::try_write(T&& data) noexcept
{
static_assert(std::is_nothrow_move_constructible<T>::value, "T move constructor must not throw");
auto block = try_acquire_write_block_(sizeof(T));
if (block == nullptr)
return false;
// critical section
new(block) T(std::move(data));
release_write_block_(block, sizeof(T));
return true;
}
} // namespace wilt
#endif // !WILT_RING_H

367
rthandler.cpp 100644
Wyświetl plik

@ -0,0 +1,367 @@
#include "rthandler.h"
#include "logcategories.h"
#if defined(Q_OS_WIN)
#include <objbase.h>
#endif
#define RT_EXCEPTION
rtHandler::rtHandler(QObject* parent)
{
Q_UNUSED(parent)
}
rtHandler::~rtHandler()
{
if (converterThread != Q_NULLPTR) {
converterThread->quit();
converterThread->wait();
}
if (isInitialized) {
#ifdef RT_EXCEPTION
try {
#endif
audio->abortStream();
audio->closeStream();
#ifdef RT_EXCEPTION
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
}
#endif
delete audio;
}
}
bool rtHandler::init(audioSetup setup)
{
if (isInitialized) {
return false;
}
this->setup = setup;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "RTAudio handler starting:" << setup.name;
if (setup.portInt==-1)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found.";
return false;
}
inFormat = toQAudioFormat(setup.codec, setup.sampleRate);
qDebug(logAudio()) << "Creating" << (setup.isinput ? "Input" : "Output") << "audio device:" << setup.name <<
", bits" << inFormat.sampleSize() <<
", codec" << setup.codec <<
", latency" << setup.latency <<
", localAFGain" << setup.localAFgain <<
", radioChan" << inFormat.channelCount() <<
", resampleQuality" << setup.resampleQuality <<
", samplerate" << inFormat.sampleRate() <<
", uLaw" << setup.ulaw;
#if !defined(Q_OS_MACX)
options.flags = ((!RTAUDIO_HOG_DEVICE) | (RTAUDIO_MINIMIZE_LATENCY));
//options.flags = RTAUDIO_MINIMIZE_LATENCY;
#endif
#if defined(Q_OS_LINUX)
audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
#elif defined(Q_OS_WIN)
audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
#elif defined(Q_OS_MACX)
audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
#endif
options.numberOfBuffers = int(setup.latency/setup.blockSize);
if (setup.portInt > 0) {
aParams.deviceId = setup.portInt;
}
else if (setup.isinput) {
aParams.deviceId = audio->getDefaultInputDevice();
}
else {
aParams.deviceId = audio->getDefaultOutputDevice();
}
aParams.firstChannel = 0;
#ifdef RT_EXCEPTION
try {
#endif
info = audio->getDeviceInfo(aParams.deviceId);
#ifdef RT_EXCEPTION
}
catch (RtAudioError e) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
goto errorHandler;
}
#endif
if (info.probed)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
RtAudioFormat sampleFormat;
outFormat.setByteOrder(QAudioFormat::LittleEndian);
outFormat.setCodec("audio/pcm");
if (info.nativeFormats == 0)
{
qCritical(logAudio()) << " No natively supported data formats!";
goto errorHandler;
}
else {
qDebug(logAudio()) << " Supported formats:" <<
(info.nativeFormats & RTAUDIO_SINT8 ? "8-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT16 ? "16-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT24 ? "24-bit int," : "") <<
(info.nativeFormats & RTAUDIO_SINT32 ? "32-bit int," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT32 ? "32-bit float," : "") <<
(info.nativeFormats & RTAUDIO_FLOAT64 ? "64-bit float," : "");
qInfo(logAudio()) << " Preferred sample rate:" << info.preferredSampleRate;
if (setup.isinput) {
outFormat.setChannelCount(info.inputChannels);
}
else {
outFormat.setChannelCount(info.outputChannels);
}
qInfo(logAudio()) << " Channels:" << outFormat.channelCount();
if (outFormat.channelCount() > 2) {
outFormat.setChannelCount(2);
}
else if (outFormat.channelCount() < 1)
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
goto errorHandler;
}
aParams.nChannels = outFormat.channelCount();
outFormat.setSampleRate(info.preferredSampleRate);
if (outFormat.sampleRate() < 44100) {
outFormat.setSampleRate(48000);
}
if (info.nativeFormats & RTAUDIO_FLOAT32) {
outFormat.setSampleType(QAudioFormat::Float);
outFormat.setSampleSize(32);
sampleFormat = RTAUDIO_FLOAT32;
}
else if (info.nativeFormats & RTAUDIO_SINT32) {
outFormat.setSampleType(QAudioFormat::SignedInt);
outFormat.setSampleSize(32);
sampleFormat = RTAUDIO_SINT32;
}
else if (info.nativeFormats & RTAUDIO_SINT16) {
outFormat.setSampleType(QAudioFormat::SignedInt);
outFormat.setSampleSize(16);
sampleFormat = RTAUDIO_SINT16;
}
else {
qCritical(logAudio()) << "Cannot find supported sample format!";
goto errorHandler;
}
}
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Selected format: SampleSize" << outFormat.sampleSize() << "Channel Count" << outFormat.channelCount() <<
"Sample Rate" << outFormat.sampleRate() << "Codec" << outFormat.codec() << "Sample Type" << outFormat.sampleType();
// We "hopefully" now have a valid format that is supported so try connecting
converter = new audioConverter();
converterThread = new QThread(this);
if (setup.isinput) {
converterThread->setObjectName("audioConvIn()");
}
else {
converterThread->setObjectName("audioConvOut()");
}
converter->moveToThread(converterThread);
connect(this, SIGNAL(setupConverter(QAudioFormat, QAudioFormat, quint8, quint8)), converter, SLOT(init(QAudioFormat, QAudioFormat, quint8, quint8)));
connect(converterThread, SIGNAL(finished()), converter, SLOT(deleteLater()));
connect(this, SIGNAL(sendToConverter(audioPacket)), converter, SLOT(convert(audioPacket)));
converterThread->start(QThread::TimeCriticalPriority);
// Per channel chunk size.
this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / (outFormat.sampleSize()/8) / outFormat.channelCount());
#ifdef RT_EXCEPTION
try {
#endif
if (setup.isinput) {
audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options);
emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedInput(audioPacket)));
}
else {
audio->openStream(&aParams, NULL, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticRead, this , &options);
emit setupConverter(inFormat, outFormat, 7, setup.resampleQuality);
connect(converter, SIGNAL(converted(audioPacket)), this, SLOT(convertedOutput(audioPacket)));
}
audio->startStream();
isInitialized = true;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency();
#ifdef RT_EXCEPTION
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
// Try again?
goto errorHandler;
}
#endif
}
else
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
goto errorHandler;
}
this->setVolume(setup.localAFgain);
return isInitialized;
errorHandler:
if (retryConnectCount < 10) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Attempting to reconnect to audio device in 500ms";
QTimer::singleShot(500, this, std::bind(&rtHandler::init, this, setup));
retryConnectCount++;
}
else
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "*** Retry count exceeded, giving up!";
}
return false;
}
void rtHandler::setVolume(unsigned char volume)
{
this->volume = audiopot[volume];
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
}
void rtHandler::incomingAudio(audioPacket packet)
{
packet.volume = volume;
emit sendToConverter(packet);
return;
}
int rtHandler::readData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(inputBuffer);
Q_UNUSED(streamTime);
int nBytes = nFrames * outFormat.channelCount() * (outFormat.sampleSize()/8);
//lastSentSeq = packet.seq;
if (arrayBuffer.length() >= nBytes) {
if (audioMutex.tryLock(0)) {
std::memcpy(outputBuffer, arrayBuffer.constData(), nBytes);
arrayBuffer.remove(0, nBytes);
audioMutex.unlock();
}
}
if (status == RTAUDIO_INPUT_OVERFLOW) {
isUnderrun = true;
}
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
isOverrun = true;
}
else
{
isUnderrun = false;
isOverrun = false;
}
return 0;
}
int rtHandler::writeData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(outputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
audioPacket packet;
packet.time = QTime::currentTime();
packet.sent = 0;
packet.volume = volume;
memcpy(&packet.guid, setup.guid, GUIDLEN);
packet.data.append((char*)inputBuffer, nFrames *outFormat.channelCount() * (outFormat.sampleSize()/8));
emit sendToConverter(packet);
if (status == RTAUDIO_INPUT_OVERFLOW) {
isUnderrun = true;
}
else if (status == RTAUDIO_OUTPUT_UNDERFLOW) {
isOverrun = true;
}
else
{
isUnderrun = false;
isOverrun = false;
}
return 0;
}
void rtHandler::convertedOutput(audioPacket packet)
{
audioMutex.lock();
arrayBuffer.append(packet.data);
audioMutex.unlock();
amplitude = packet.amplitude;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000);
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
}
void rtHandler::convertedInput(audioPacket packet)
{
if (packet.data.size() > 0) {
emit haveAudioData(packet);
amplitude = packet.amplitude;
currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000);
emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun);
}
}
void rtHandler::changeLatency(const quint16 newSize)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
}
int rtHandler::getLatency()
{
return currentLatency;
}
quint16 rtHandler::getAmplitude()
{
return static_cast<quint16>(amplitude * 255.0);
}

115
rthandler.h 100644
Wyświetl plik

@ -0,0 +1,115 @@
#ifndef rtHandler_H
#define rtHandler_H
#include <QObject>
#include <QByteArray>
#include <QThread>
#include <QMutex>
#ifndef Q_OS_LINUX
#include "RtAudio.h"
#else
#include "rtaudio/RtAudio.h"
#endif
#include <QAudioFormat>
#include <QTime>
#include <QMap>
#include <QTimer>
/* wfview Packet types */
#include "packettypes.h"
/* Logarithmic taper for volume control */
#include "audiotaper.h"
#include "audiohandler.h"
/* Audio converter class*/
#include "audioconverter.h"
#include <QDebug>
class rtHandler : public audioHandler
{
Q_OBJECT
public:
rtHandler(QObject* parent = 0);
~rtHandler();
int getLatency();
void getNextAudioChunk(QByteArray& data);
quint16 getAmplitude();
public slots:
bool init(audioSetup setup);
void changeLatency(const quint16 newSize);
void setVolume(unsigned char volume);
void convertedInput(audioPacket audio);
void convertedOutput(audioPacket audio);
void incomingAudio(const audioPacket data);
private slots:
signals:
void audioMessage(QString message);
void sendLatency(quint16 newSize);
void haveAudioData(const audioPacket& data);
void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp);
void sendToConverter(audioPacket audio);
private:
int readData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
static int staticRead(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<rtHandler*>(userData)->readData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
int writeData(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status);
static int staticWrite(void* outputBuffer, void* inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void* userData) {
return static_cast<rtHandler*>(userData)->writeData(outputBuffer, inputBuffer, nFrames, streamTime, status);
}
bool isInitialized = false;
RtAudio* audio = Q_NULLPTR;
int audioDevice = 0;
RtAudio::StreamParameters aParams;
RtAudio::StreamOptions options;
RtAudio::DeviceInfo info;
quint16 audioLatency;
unsigned int chunkSize;
quint32 lastSeq;
quint32 lastSentSeq = 0;
quint16 currentLatency;
float amplitude = 0.0;
qreal volume = 1.0;
audioSetup setup;
QAudioFormat inFormat;
QAudioFormat outFormat;
audioConverter* converter = Q_NULLPTR;
QThread* converterThread = Q_NULLPTR;
QByteArray arrayBuffer;
bool isUnderrun = false;
bool isOverrun = false;
QMutex audioMutex;
int retryConnectCount = 0;
};
#endif // rtHandler_H

81
selectradio.cpp 100644
Wyświetl plik

@ -0,0 +1,81 @@
#include "logcategories.h"
#include "selectradio.h"
#include "ui_selectradio.h"
selectRadio::selectRadio(QWidget* parent) :
QDialog(parent),
ui(new Ui::selectRadio)
{
ui->setupUi(this);
}
selectRadio::~selectRadio()
{
delete ui;
}
void selectRadio::populate(QList<radio_cap_packet> radios)
{
ui->table->clearContents();
for (int row = ui->table->rowCount() - 1;row>=0; row--) {
ui->table->removeRow(row);
}
for (int row = 0; row < radios.count(); row++) {
ui->table->insertRow(ui->table->rowCount());
ui->table->setItem(row, 0, new QTableWidgetItem(QString(radios[row].name)));
ui->table->setItem(row, 1, new QTableWidgetItem(QString("%1").arg((unsigned char)radios[row].civ, 2, 16, QLatin1Char('0')).toUpper()));
ui->table->setItem(row, 2, new QTableWidgetItem(QString::number(qFromBigEndian(radios[row].baudrate))));
}
if (radios.count() > 1) {
this->setVisible(true);
}
}
void selectRadio::setInUse(quint8 radio, quint8 busy, QString user, QString ip)
{
//if ((radio > 0)&& !this->isVisible()) {
// qInfo() << "setInUse: radio:" << radio <<"busy" << busy << "user" << user << "ip"<<ip;
// this->setVisible(true);
//}
ui->table->setItem(radio, 3, new QTableWidgetItem(user));
ui->table->setItem(radio, 4, new QTableWidgetItem(ip));
for (int f = 0; f < 5; f++) {
if (busy == 1)
{
ui->table->item(radio, f)->setBackground(Qt::darkGreen);
}
else if (busy == 2)
{
ui->table->item(radio, f)->setBackground(Qt::red);
}
else
{
ui->table->item(radio, f)->setBackground(Qt::black);
}
}
}
void selectRadio::on_table_cellClicked(int row, int col) {
qInfo() << "Clicked on " << row << "," << col;
if (ui->table->item(row, col)->backgroundColor() != Qt::darkGreen) {
ui->table->selectRow(row);
emit selectedRadio(row);
this->setVisible(false);
}
}
void selectRadio::on_cancelButton_clicked() {
this->setVisible(false);
}
void selectRadio::audioOutputLevel(quint16 level) {
ui->afLevel->setValue(level);
}
void selectRadio::audioInputLevel(quint16 level) {
ui->modLevel->setValue(level);
}

38
selectradio.h 100644
Wyświetl plik

@ -0,0 +1,38 @@
#ifndef SELECTRADIO_H
#define SELECTRADIO_H
#include <QDialog>
#include <QList>
#include <QtEndian>
#include <QHostInfo>
#include "packettypes.h"
namespace Ui {
class selectRadio;
}
class selectRadio : public QDialog
{
Q_OBJECT
public:
explicit selectRadio(QWidget* parent = 0);
~selectRadio();
void populate(QList<radio_cap_packet> radios);
void audioOutputLevel(quint16 level);
void audioInputLevel(quint16 level);
public slots:
void on_table_cellClicked(int row, int col);
void setInUse(quint8 radio, quint8 busy, QString user, QString ip);
void on_cancelButton_clicked();
signals:
void selectedRadio(quint8 radio);
private:
Ui::selectRadio* ui;
};
#endif // SELECTRADIO_H

157
selectradio.ui 100644
Wyświetl plik

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>selectRadio</class>
<widget class="QDialog" name="selectRadio">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>590</width>
<height>206</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Radio From List</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="table">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Rig Name</string>
</property>
</column>
<column>
<property name="text">
<string>CI-V</string>
</property>
</column>
<column>
<property name="text">
<string>Baud Rate</string>
</property>
</column>
<column>
<property name="text">
<string>Current User</string>
</property>
</column>
<column>
<property name="text">
<string>User IP Address</string>
</property>
</column>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>AF</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="afLevel">
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>MOD</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="modLevel">
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

780
servermain.cpp 100644
Wyświetl plik

@ -0,0 +1,780 @@
#include "servermain.h"
#include "commhandler.h"
#include "rigidentities.h"
#include "logcategories.h"
#include <iostream>
// This code is copyright 2017-2020 Elliott H. Liggett
// All rights reserved
servermain::servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile)
{
this->serialPortCL = serialPortCL;
this->hostCL = hostCL;
qRegisterMetaType <udpPreferences>(); // Needs to be registered early.
qRegisterMetaType <rigCapabilities>();
qRegisterMetaType <duplexMode>();
qRegisterMetaType <rptAccessTxRx>();
qRegisterMetaType <rigInput>();
qRegisterMetaType <meterKind>();
qRegisterMetaType <spectrumMode>();
qRegisterMetaType <freqt>();
qRegisterMetaType <mode_info>();
qRegisterMetaType <audioPacket>();
qRegisterMetaType <audioSetup>();
qRegisterMetaType <SERVERCONFIG>();
qRegisterMetaType <timekind>();
qRegisterMetaType <datekind>();
qRegisterMetaType<rigstate*>();
qRegisterMetaType<QList<radio_cap_packet>>();
qRegisterMetaType<networkStatus>();
setDefPrefs();
getSettingsFilePath(settingsFile);
loadSettings(); // Look for saved preferences
setInitialTiming();
openRig();
setServerToPrefs();
amTransmitting = false;
}
servermain::~servermain()
{
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio->rigThread != Q_NULLPTR)
{
radio->rigThread->quit();
radio->rigThread->wait();
}
delete radio; // This has been created by new in loadSettings();
}
serverConfig.rigs.clear();
if (serverThread != Q_NULLPTR) {
serverThread->quit();
serverThread->wait();
}
delete settings;
#if defined(PORTAUDIO)
Pa_Terminate();
#endif
}
void servermain::openRig()
{
// This function is intended to handle opening a connection to the rig.
// the connection can be either serial or network,
// and this function is also responsible for initiating the search for a rig model and capabilities.
// Any errors, such as unable to open connection or unable to open port, are to be reported to the user.
makeRig();
for (RIGCONFIG* radio : serverConfig.rigs)
{
//qInfo(logSystem()) << "Opening rig";
if (radio->rigThread != Q_NULLPTR)
{
//qInfo(logSystem()) << "Got rig";
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort,radio->waterfallFormat);
}, Qt::QueuedConnection);
}
}
}
void servermain::makeRig()
{
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio->rigThread == Q_NULLPTR)
{
qInfo(logSystem()) << "Creating new rigThread()";
radio->rig = new rigCommander(radio->guid);
radio->rigThread = new QThread(this);
radio->rigThread->setObjectName("rigCommander()");
// Thread:
radio->rig->moveToThread(radio->rigThread);
connect(radio->rigThread, SIGNAL(started()), radio->rig, SLOT(process()));
connect(radio->rigThread, SIGNAL(finished()), radio->rig, SLOT(deleteLater()));
radio->rigThread->start();
// Rig status and Errors:
connect(radio->rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
connect(radio->rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus)));
// Rig comm setup:
connect(this, SIGNAL(setRTSforPTT(bool)), radio->rig, SLOT(setRTSforPTT(bool)));
connect(radio->rig, SIGNAL(haveBaudRate(quint32)), this, SLOT(receiveBaudRate(quint32)));
connect(this, SIGNAL(sendCloseComm()), radio->rig, SLOT(closeComm()));
connect(this, SIGNAL(sendChangeLatency(quint16)), radio->rig, SLOT(changeLatency(quint16)));
//connect(this, SIGNAL(getRigCIV()), radio->rig, SLOT(findRigs()));
//connect(this, SIGNAL(setRigID(unsigned char)), radio->rig, SLOT(setRigID(unsigned char)));
connect(radio->rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
connect(radio->rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
connect(this, SIGNAL(requestRigState()), radio->rig, SLOT(sendState()));
connect(this, SIGNAL(stateUpdated()), radio->rig, SLOT(stateUpdated()));
connect(radio->rig, SIGNAL(stateInfo(rigstate*)), this, SLOT(receiveStateInfo(rigstate*)));
//Other connections
connect(this, SIGNAL(setCIVAddr(unsigned char)), radio->rig, SLOT(setCIVAddr(unsigned char)));
connect(radio->rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool)));
connect(this, SIGNAL(setPTT(bool)), radio->rig, SLOT(setPTT(bool)));
connect(this, SIGNAL(getPTT()), radio->rig, SLOT(getPTT()));
connect(this, SIGNAL(getDebug()), radio->rig, SLOT(getDebug()));
if (radio->rigThread->isRunning()) {
qInfo(logSystem()) << "Rig thread is running";
}
else {
qInfo(logSystem()) << "Rig thread is not running";
}
}
}
}
void servermain::removeRig()
{
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio->rigThread != Q_NULLPTR)
{
radio->rigThread->disconnect();
radio->rig->disconnect();
delete radio->rigThread;
delete radio->rig;
radio->rig = Q_NULLPTR;
}
}
}
void servermain::findSerialPort()
{
// Find the ICOM radio connected, or, if none, fall back to OS default.
// qInfo(logSystem()) << "Searching for serial port...";
QDirIterator it73("/dev/serial/by-id", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it7610("/dev/serial", QStringList() << "*IC-7610*A", QDir::Files, QDirIterator::Subdirectories);
QDirIterator itR8600("/dev/serial", QStringList() << "*IC-R8600*A", QDir::Files, QDirIterator::Subdirectories);
if(!it73.filePath().isEmpty())
{
// IC-7300
serialPortRig = it73.filePath(); // first
} else if(!it97.filePath().isEmpty())
{
// IC-9700
serialPortRig = it97.filePath();
} else if(!it785x.filePath().isEmpty())
{
// IC-785x
serialPortRig = it785x.filePath();
} else if(!it705.filePath().isEmpty())
{
// IC-705
serialPortRig = it705.filePath();
} else if(!it7610.filePath().isEmpty())
{
// IC-7610
serialPortRig = it7610.filePath();
} else if(!itR8600.filePath().isEmpty())
{
// IC-R8600
serialPortRig = itR8600.filePath();
} else {
//fall back:
qInfo(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences.";
#ifdef Q_OS_MAC
serialPortRig = QString("/dev/tty.SLAB_USBtoUART");
#endif
#ifdef Q_OS_LINUX
serialPortRig = QString("/dev/ttyUSB0");
#endif
#ifdef Q_OS_WIN
serialPortRig = QString("COM1");
#endif
}
}
void servermain::receiveStatusUpdate(networkStatus status)
{
if (status.message != lastMessage) {
std::cout << status.message.toLocal8Bit().toStdString() << "\n";
lastMessage = status.message;
}
}
void servermain::receiveCommReady()
{
rigCommander* sender = qobject_cast<rigCommander*>(QObject::sender());
// Use the GUID to determine which radio the response is from
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !memcmp(sender->getGUID(), radio->guid, GUIDLEN))
{
qInfo(logSystem()) << "Received CommReady!! ";
if (radio->civAddr == 0)
{
// tell rigCommander to broadcast a request for all rig IDs.
// qInfo(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)";
if (!radio->rigAvailable) {
if (radio->connectTimer == Q_NULLPTR) {
radio->connectTimer = new QTimer();
connect(radio->connectTimer, &QTimer::timeout, this, std::bind(&servermain::connectToRig, this, radio));
}
radio->connectTimer->start(500);
}
}
else {
// don't bother, they told us the CIV they want, stick with it.
// We still query the rigID to find the model, but at least we know the CIV.
qInfo(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << radio->civAddr;
QMetaObject::invokeMethod(radio->rig, [=]() {
radio->rig->setRigID(radio->civAddr);
}, Qt::QueuedConnection);
}
}
}
}
void servermain::connectToRig(RIGCONFIG* rig)
{
if (!rig->rigAvailable) {
qDebug(logSystem()) << "Searching for rig on" << rig->serialPort;
QMetaObject::invokeMethod(rig->rig, [=]() {
rig->rig->findRigs();
}, Qt::QueuedConnection);
}
else {
rig->connectTimer->stop();
}
}
void servermain::receiveFoundRigID(rigCapabilities rigCaps)
{
// Entry point for unknown rig being identified at the start of the program.
//now we know what the rig ID is:
rigCommander* sender = qobject_cast<rigCommander*>(QObject::sender());
// Use the GUID to determine which radio the response is from
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (sender != Q_NULLPTR && radio->rig != Q_NULLPTR && !radio->rigAvailable && !memcmp(sender->getGUID(), radio->guid, GUIDLEN))
{
qDebug(logSystem()) << "Rig name: " << rigCaps.modelName;
qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan;
qDebug(logSystem()) << "Rig ID received into servermain: spectLenMax: " << rigCaps.spectLenMax;
qDebug(logSystem()) << "Rig ID received into servermain: spectAmpMax: " << rigCaps.spectAmpMax;
qDebug(logSystem()) << "Rig ID received into servermain: spectSeqMax: " << rigCaps.spectSeqMax;
qDebug(logSystem()) << "Rig ID received into servermain: hasSpectrum: " << rigCaps.hasSpectrum;
qDebug(logSystem()).noquote() << QString("Rig ID received into servermain: GUID: {%1%2%3%4-%5%6-%7%8-%9%10-%11%12%13%14%15%16}")
.arg(rigCaps.guid[0], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[1], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[2], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[3], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[4], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[5], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[6], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[7], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[8], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[9], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[10], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[11], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[12], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[13], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[14], 2, 16, QLatin1Char('0'))
.arg(rigCaps.guid[15], 2, 16, QLatin1Char('0'))
;
radio->rigCaps = rigCaps;
// Added so that server receives rig capabilities.
emit sendRigCaps(rigCaps);
}
}
return;
}
void servermain::receiveSerialPortError(QString port, QString errorText)
{
qInfo(logSystem()) << "servermain: received serial port error for port: " << port << " with message: " << errorText;
// TODO: Dialog box, exit, etc
}
void servermain::getSettingsFilePath(QString settingsFile)
{
if (settingsFile.isNull()) {
settings = new QSettings();
}
else
{
QString file = settingsFile;
QFile info(settingsFile);
QString path="";
if (!QFileInfo(info).isAbsolute())
{
path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (path.isEmpty())
{
path = QDir::homePath();
}
path = path + "/";
file = info.fileName();
}
qInfo(logSystem()) << "Loading settings from:" << path + file;
settings = new QSettings(path + file, QSettings::Format::IniFormat);
}
}
void servermain::setInitialTiming()
{
loopTickCounter = 0;
pttTimer = new QTimer(this);
pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms
pttTimer->setSingleShot(true);
connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit()));
}
void servermain::setServerToPrefs()
{
// Start server if enabled in config
if (serverThread != Q_NULLPTR) {
serverThread->quit();
serverThread->wait();
serverThread = Q_NULLPTR;
udp = Q_NULLPTR;
}
udp = new udpServer(&serverConfig);
serverThread = new QThread(this);
serverThread->setObjectName("udpServer()");
udp->moveToThread(serverThread);
connect(this, SIGNAL(initServer()), udp, SLOT(init()));
connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater()));
// Step through all radios and connect them to the server,
// server will then use GUID to determine which actual radio it belongs to.
for (RIGCONFIG* radio : serverConfig.rigs)
{
if (radio->rigThread != Q_NULLPTR)
{
if (radio->rig != Q_NULLPTR) {
connect(radio->rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
connect(radio->rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
//connect(udp, SIGNAL(haveDataFromServer(QByteArray)), radio->rig, SLOT(dataFromServer(QByteArray)));
connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities)));
}
}
}
connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus)));
serverThread->start();
emit initServer();
}
void servermain::setDefPrefs()
{
defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300.
defPrefs.CIVisRadioModel = false;
defPrefs.forceRTSasPTT = false;
defPrefs.serialPortRadio = QString("auto");
defPrefs.serialPortBaud = 115200;
defPrefs.localAFgain = 255;
defPrefs.tcpPort = 0;
defPrefs.audioSystem = qtAudio;
defPrefs.rxAudio.name = QString("default");
defPrefs.txAudio.name = QString("default");
udpDefPrefs.ipAddress = QString("");
udpDefPrefs.controlLANPort = 50001;
udpDefPrefs.serialLANPort = 50002;
udpDefPrefs.audioLANPort = 50003;
udpDefPrefs.username = QString("");
udpDefPrefs.password = QString("");
udpDefPrefs.clientName = QHostInfo::localHostName();
}
void servermain::loadSettings()
{
qInfo(logSystem()) << "Loading settings from " << settings->fileName();
prefs.audioSystem = static_cast<audioType>(settings->value("AudioSystem", defPrefs.audioSystem).toInt());
int numRadios = settings->beginReadArray("Radios");
if (numRadios == 0) {
settings->endArray();
// We assume that QSettings is empty as there are no radios configured, create new:
qInfo(logSystem()) << "Creating new settings file " << settings->fileName();
settings->setValue("AudioSystem", defPrefs.audioSystem);
numRadios = 1;
settings->beginWriteArray("Radios");
for (int i = 0; i < numRadios; i++)
{
settings->setArrayIndex(i);
settings->setValue("RigCIVuInt", defPrefs.radioCIVAddr);
settings->setValue("ForceRTSasPTT", defPrefs.forceRTSasPTT);
settings->setValue("SerialPortRadio", defPrefs.serialPortRadio);
settings->setValue("RigName", "<NONE>");
settings->setValue("SerialPortBaud", defPrefs.serialPortBaud);
settings->setValue("AudioInput", "default");
settings->setValue("AudioOutput", "default");
settings->setValue("WaterfallFormat", 0);
}
settings->endArray();
settings->beginGroup("Server");
settings->setValue("ServerEnabled", true);
settings->setValue("ServerControlPort", udpDefPrefs.controlLANPort);
settings->setValue("ServerCivPort", udpDefPrefs.serialLANPort);
settings->setValue("ServerAudioPort", udpDefPrefs.audioLANPort);
settings->beginWriteArray("Users");
settings->setArrayIndex(0);
settings->setValue("Username", "user");
QByteArray pass;
passcode("password", pass);
settings->setValue("Password", QString(pass));
settings->setValue("UserType", 0);
settings->endArray();
settings->endGroup();
settings->sync();
} else {
settings->endArray();
}
numRadios = settings->beginReadArray("Radios");
int tempNum = numRadios;
for (int i = 0; i < numRadios; i++) {
settings->setArrayIndex(i);
RIGCONFIG* tempPrefs = new RIGCONFIG();
tempPrefs->civAddr = (unsigned char)settings->value("RigCIVuInt", defPrefs.radioCIVAddr).toInt();
tempPrefs->forceRTSasPTT = (bool)settings->value("ForceRTSasPTT", defPrefs.forceRTSasPTT).toBool();
tempPrefs->serialPort = settings->value("SerialPortRadio", defPrefs.serialPortRadio).toString();
tempPrefs->rigName = settings->value("RigName", "<NONE>").toString();
tempPrefs->baudRate = (quint32)settings->value("SerialPortBaud", defPrefs.serialPortBaud).toInt();
tempPrefs->rxAudioSetup.name = settings->value("AudioInput", "default").toString();
tempPrefs->txAudioSetup.name = settings->value("AudioOutput", "default").toString();
tempPrefs->waterfallFormat = settings->value("WaterfallFormat", 0).toInt();
tempPrefs->rxAudioSetup.type = prefs.audioSystem;
tempPrefs->txAudioSetup.type = prefs.audioSystem;
QString tempPort = "auto";
if (tempPrefs->rigName=="<NONE>")
{
foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts())
{
qDebug(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber();
if ((serialPortInfo.portName() == tempPrefs->serialPort || tempPrefs->serialPort == "auto") && !serialPortInfo.serialNumber().isEmpty())
{
if (serialPortInfo.serialNumber().startsWith("IC-")) {
tempPrefs->rigName = serialPortInfo.serialNumber();
tempPort = serialPortInfo.portName();
}
}
}
}
tempPrefs->serialPort = tempPort;
QString guid = settings->value("GUID", "").toString();
if (guid.isEmpty()) {
guid = QUuid::createUuid().toString();
settings->setValue("GUID", guid);
}
#if (QT_VERSION >= QT_VERSION_CHECK(5,10,0))
memcpy(tempPrefs->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN);
#endif
tempPrefs->rxAudioSetup.isinput = true;
tempPrefs->txAudioSetup.isinput = false;
tempPrefs->rxAudioSetup.localAFgain = 255;
tempPrefs->txAudioSetup.localAFgain = 255;
tempPrefs->rxAudioSetup.resampleQuality = 4;
tempPrefs->txAudioSetup.resampleQuality = 4;
tempPrefs->rig = Q_NULLPTR;
tempPrefs->rigThread = Q_NULLPTR;
serverConfig.rigs.append(tempPrefs);
if (tempNum == 0) {
settings->endGroup();
}
}
if (tempNum > 0) {
settings->endArray();
}
/*
Now we have an array of rig objects, we need to match the configured audio devices with physical devices
*/
switch (prefs.audioSystem)
{
case rtAudio:
{
#if defined(Q_OS_LINUX)
RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA);
// RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_PULSE);
#elif defined(Q_OS_WIN)
RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI);
#elif defined(Q_OS_MACX)
RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE);
#endif
// Enumerate audio devices, need to do before settings are loaded.
std::map<int, std::string> apiMap;
apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound";
apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI";
apiMap[RtAudio::UNIX_JACK] = "Jack Client";
apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio";
apiMap[RtAudio::LINUX_OSS] = "Linux OSS";
apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy";
std::vector< RtAudio::Api > apis;
RtAudio::getCompiledApi(apis);
qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion());
qInfo(logAudio()) << "Compiled APIs:";
for (unsigned int i = 0; i < apis.size(); i++) {
qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]);
}
RtAudio::DeviceInfo info;
qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]);
unsigned int devices = audio->getDeviceCount();
qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default";
for (unsigned int i = 1; i < devices; i++) {
info = audio->getDeviceInfo(i);
for (RIGCONFIG* rig : serverConfig.rigs)
{
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
if (info.outputChannels > 0)
{
qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name);
if (rig->txAudioSetup.name.toStdString() == info.name) {
rig->txAudioSetup.portInt = i;
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info.name.c_str());
}
}
if (info.inputChannels > 0)
{
qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name);
if (rig->rxAudioSetup.name.toStdString() == info.name) {
rig->rxAudioSetup.portInt = i;
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info.name.c_str());
}
}
}
}
break;
}
case portAudio:
{
// Use PortAudio device enumeration
PaError err;
err = Pa_Initialize();
if (err != paNoError)
{
qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio";
}
qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText;
int numDevices;
numDevices = Pa_GetDeviceCount();
qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices;
const PaDeviceInfo* info;
for (int i = 0; i < numDevices; i++)
{
info = Pa_GetDeviceInfo(i);
for (RIGCONFIG* rig : serverConfig.rigs)
{
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
if (info->maxInputChannels > 0) {
qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name;
if (rig->rxAudioSetup.name == info->name) {
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << QString(info->name);
rig->rxAudioSetup.portInt = i;
}
}
if (info->maxOutputChannels > 0) {
if (rig->txAudioSetup.name == info->name) {
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << QString(info->name);
rig->txAudioSetup.portInt = i;
}
}
}
}
break;
}
case qtAudio:
{
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
//qInfo(logAudio()) << "Looking for audio input devices";
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
qDebug(logSystem()) << "Found Audio input: " << deviceInfo.deviceName();
for (RIGCONFIG* rig : serverConfig.rigs)
{
qDebug(logAudio()) << "Rig" << rig->rigName << "rxAudio device:" << rig->rxAudioSetup.name;
qDebug(logAudio()) << "Rig" << rig->rigName << "txAudio device:" << rig->txAudioSetup.name;
if (deviceInfo.deviceName() == rig->rxAudioSetup.name
#ifdef Q_OS_WIN
&& deviceInfo.realm() == "wasapi"
#endif
)
{
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected rxAudio device:" << deviceInfo.deviceName();
rig->rxAudioSetup.port = deviceInfo;
}
}
}
//qInfo(logAudio()) << "Looking for audio output devices";
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
qDebug(logSystem()) << "Found Audio output: " << deviceInfo.deviceName();
for (RIGCONFIG* rig : serverConfig.rigs)
{
if (deviceInfo.deviceName() == rig->txAudioSetup.name
#ifdef Q_OS_WIN
&& deviceInfo.realm() == "wasapi"
#endif
)
{
qDebug(logAudio()) << "Rig" << rig->rigName << "Selected txAudio device:" << deviceInfo.deviceName();
rig->txAudioSetup.port = deviceInfo;
}
}
}
break;
}
}
settings->beginGroup("Server");
serverConfig.enabled = settings->value("ServerEnabled", false).toBool();
serverConfig.controlPort = settings->value("ServerControlPort", 50001).toInt();
serverConfig.civPort = settings->value("ServerCivPort", 50002).toInt();
serverConfig.audioPort = settings->value("ServerAudioPort", 50003).toInt();
serverConfig.users.clear();
int numUsers = settings->beginReadArray("Users");
if (numUsers > 0) {
{
for (int f = 0; f < numUsers; f++)
{
settings->setArrayIndex(f);
SERVERUSER user;
user.username = settings->value("Username", "").toString();
user.password = settings->value("Password", "").toString();
user.userType = settings->value("UserType", 0).toInt();
serverConfig.users.append(user);
}
}
settings->endArray();
}
settings->endGroup();
settings->sync();
#if defined(RTAUDIO)
delete audio;
#endif
}
void servermain::receivePTTstatus(bool pttOn)
{
// This is the only place where amTransmitting and the transmit button text should be changed:
//qInfo(logSystem()) << "PTT status: " << pttOn;
amTransmitting = pttOn;
}
void servermain::handlePttLimit()
{
// ptt time exceeded!
std::cout << "Transmit timeout at 3 minutes. Sending PTT OFF command now.\n";
emit setPTT(false);
emit getPTT();
}
void servermain::receiveBaudRate(quint32 baud)
{
qInfo() << "Received serial port baud rate from remote server:" << baud;
prefs.serialPortBaud = baud;
}
void servermain::powerRigOn()
{
emit sendPowerOn();
}
void servermain::powerRigOff()
{
emit sendPowerOff();
}
void servermain::receiveStateInfo(rigstate* state)
{
qInfo("Received rig state for wfmain");
Q_UNUSED(state);
//rigState = state;
}

307
servermain.h 100644
Wyświetl plik

@ -0,0 +1,307 @@
#ifndef WFMAIN_H
#define WFMAIN_H
#include <QtCore/QCoreApplication>
#include <QtCore/QDirIterator>
#include <QtCore/QStandardPaths>
#include <QThread>
#include <QString>
#include <QVector>
#include <QTimer>
#include <QSettings>
#include <QShortcut>
#include <QMetaType>
#include "logcategories.h"
#include "commhandler.h"
#include "rigcommander.h"
#include "rigstate.h"
#include "freqmemory.h"
#include "rigidentities.h"
#include "repeaterattributes.h"
#include "udpserver.h"
#include "rigctld.h"
#include "signal.h"
#include <qserialportinfo.h>
#include <deque>
#include <memory>
#include <portaudio.h>
#ifdef Q_OS_WIN
#include "RtAudio.h"
#else
#include "rtaudio/RtAudio.h"
#endif
namespace Ui {
class wfmain;
}
class servermain : public QObject
{
Q_OBJECT
public:
servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile);
QString serialPortCL;
QString hostCL;
~servermain();
signals:
// Basic to rig:
void setCIVAddr(unsigned char newRigCIVAddr);
void setRigID(unsigned char rigID);
void setRTSforPTT(bool enabled);
// Power
void sendPowerOn();
void sendPowerOff();
// Frequency, mode, band:
void getFrequency();
void setFrequency(unsigned char vfo, freqt freq);
void getMode();
void setMode(unsigned char modeIndex, unsigned char modeFilter);
void setMode(mode_info);
void setDataMode(bool dataOn, unsigned char filter);
void getDataMode();
void getModInput(bool dataOn);
void setModInput(rigInput input, bool dataOn);
void getBandStackReg(char band, char regCode);
void getDebug();
void getRitEnabled();
void getRitValue();
void setRitValue(int ritValue);
void setRitEnable(bool ritEnabled);
// Repeater:
void getDuplexMode();
void getTone();
void getTSQL();
void getDTCS();
void getRptAccessMode();
// Level get:
void getLevels(); // get all levels
void getRfGain();
void getAfGain();
void getSql();
void getIfShift();
void getTPBFInner();
void getTPBFOuter();
void getTxPower();
void getMicGain();
void getSpectrumRefLevel();
void getModInputLevel(rigInput input);
// Level set:
void setRfGain(unsigned char level);
void setAfGain(unsigned char level);
void setSql(unsigned char level);
void setIFShift(unsigned char level);
void setTPBFInner(unsigned char level);
void setTPBFOuter(unsigned char level);
void setIFShiftWindow(unsigned char level);
void setTPBFInnerWindow(unsigned char level);
void setTPBFOuterWindow(unsigned char level);
void setMicGain(unsigned char);
void setCompLevel(unsigned char);
void setTxPower(unsigned char);
void setMonitorLevel(unsigned char);
void setVoxGain(unsigned char);
void setAntiVoxGain(unsigned char);
void setSpectrumRefLevel(int);
void setModLevel(rigInput input, unsigned char level);
void setACCGain(unsigned char level);
void setACCAGain(unsigned char level);
void setACCBGain(unsigned char level);
void setUSBGain(unsigned char level);
void setLANGain(unsigned char level);
void getMeters(meterKind meter);
// PTT, ATU, ATT, Antenna, Preamp:
void getPTT();
void setPTT(bool pttOn);
void getAttenuator();
void getPreamp();
void getAntenna();
void setAttenuator(unsigned char att);
void setPreamp(unsigned char pre);
void setAntenna(unsigned char ant, bool rx);
void startATU();
void setATU(bool atuEnabled);
void getATUStatus();
// Time and date:
void setTime(timekind t);
void setDate(datekind d);
void setUTCOffset(timekind t);
void getRigID(); // this is the model of the rig
void getRigCIV(); // get the rig's CIV addr
void spectOutputEnable();
void spectOutputDisable();
void scopeDisplayEnable();
void scopeDisplayDisable();
void setScopeMode(spectrumMode spectMode);
void setScopeSpan(char span);
void setScopeEdge(char edge);
void setScopeFixedEdge(double startFreq, double endFreq, unsigned char edgeNumber);
void getScopeMode();
void getScopeEdge();
void getScopeSpan();
void sayFrequency();
void sayMode();
void sayAll();
void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp, quint8 wf);
void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp);
void sendCloseComm();
void sendChangeLatency(quint16 latency);
void initServer();
void sendRigCaps(rigCapabilities caps);
void requestRigState();
void stateUpdated();
private slots:
void receiveCommReady();
void receivePTTstatus(bool pttOn);
void receiveFoundRigID(rigCapabilities rigCaps);
void receiveSerialPortError(QString port, QString errorText);
void receiveBaudRate(quint32 baudrate);
void handlePttLimit();
void receiveStatusUpdate(networkStatus status);
void receiveStateInfo(rigstate* state);
void connectToRig(RIGCONFIG* rig);
private:
QSettings *settings=Q_NULLPTR;
void loadSettings();
void openRig();
void powerRigOff();
void powerRigOn();
QStringList portList;
QString serialPortRig;
QTimer * delayedCommand;
QTimer * pttTimer;
uint16_t loopTickCounter;
uint16_t slowCmdNum;
QString lastMessage="";
void makeRig();
void removeRig();
void findSerialPort();
void setServerToPrefs();
void setInitialTiming();
void getSettingsFilePath(QString settingsFile);
QStringList modes;
int currentModeIndex;
QStringList spans;
QStringList edges;
QStringList commPorts;
quint16 spectWidth;
quint16 wfLength;
bool spectrumDrawLock;
QByteArray spectrumPeaks;
QVector <QByteArray> wfimage;
unsigned int wfLengthMax;
bool onFullscreen;
bool drawPeaks;
bool freqTextSelected;
void checkFreqSel();
double oldLowerFreq;
double oldUpperFreq;
freqt freq;
float tsKnobMHz;
unsigned char setModeVal=0;
unsigned char setFilterVal=0;
bool usingLAN = false;
struct preferences {
unsigned char radioCIVAddr;
bool CIVisRadioModel;
bool forceRTSasPTT;
QString serialPortRadio;
quint32 serialPortBaud;
unsigned char localAFgain;
audioSetup rxAudio;
audioSetup txAudio;
rigCapabilities rigCaps;
bool haveRigCaps = false;
quint16 tcpPort;
audioType audioSystem;
} prefs;
preferences defPrefs;
udpPreferences udpPrefs;
udpPreferences udpDefPrefs;
// Configuration for audio output and input.
audioSetup rxSetup;
audioSetup txSetup;
audioSetup serverRxSetup;
audioSetup serverTxSetup;
void setDefPrefs(); // populate default values to default prefs
bool amTransmitting;
bool usingDataMode = false;
unsigned char micGain=0;
unsigned char accAGain=0;
unsigned char accBGain=0;
unsigned char accGain=0;
unsigned char usbGain=0;
unsigned char lanGain=0;
udpServer* udp = Q_NULLPTR;
rigCtlD* rigCtl = Q_NULLPTR;
QThread* serverThread = Q_NULLPTR;
rigstate* rigState = Q_NULLPTR;
SERVERCONFIG serverConfig;
};
Q_DECLARE_METATYPE(struct rigCapabilities)
Q_DECLARE_METATYPE(struct freqt)
Q_DECLARE_METATYPE(struct mode_info)
Q_DECLARE_METATYPE(struct udpPreferences)
Q_DECLARE_METATYPE(struct audioPacket)
Q_DECLARE_METATYPE(struct audioSetup)
Q_DECLARE_METATYPE(struct SERVERCONFIG)
Q_DECLARE_METATYPE(struct timekind)
Q_DECLARE_METATYPE(struct datekind)
Q_DECLARE_METATYPE(struct networkStatus)
Q_DECLARE_METATYPE(enum rigInput)
Q_DECLARE_METATYPE(QList<radio_cap_packet>)
Q_DECLARE_METATYPE(enum meterKind)
Q_DECLARE_METATYPE(enum spectrumMode)
Q_DECLARE_METATYPE(rigstate*)
#endif // WFMAIN_H

99
tcpserver.cpp 100644
Wyświetl plik

@ -0,0 +1,99 @@
#include "tcpserver.h"
#include "logcategories.h"
tcpServer::tcpServer(QObject* parent) : QTcpServer(parent)
{
}
tcpServer::~tcpServer()
{
qInfo(logTcpServer()) << "closing tcpServer";
}
int tcpServer::startServer(qint16 port) {
if (!this->listen(QHostAddress::Any, port)) {
qInfo(logTcpServer()) << "could not start on port " << port;
return -1;
}
else
{
qInfo(logTcpServer()) << "started on port " << port;
}
return 0;
}
void tcpServer::incomingConnection(qintptr socket) {
tcpServerClient* client = new tcpServerClient(socket, this);
connect(this, SIGNAL(onStopped()), client, SLOT(closeSocket()));
emit newClient(socket); // Signal par
}
void tcpServer::stopServer()
{
qInfo(logTcpServer()) << "stopping server";
emit onStopped();
}
void tcpServer::receiveDataFromClient(QByteArray data)
{
emit receiveData(data);
}
void tcpServer::sendData(QByteArray data) {
emit sendDataToClient(data);
}
tcpServerClient::tcpServerClient(int socketId, tcpServer* parent) : QObject(parent)
{
sessionId = socketId;
socket = new QTcpSocket(this);
this->parent = parent;
if (!socket->setSocketDescriptor(sessionId))
{
qInfo(logTcpServer()) << " error binding socket: " << sessionId;
return;
}
connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()), Qt::DirectConnection);
connect(parent, SIGNAL(sendDataToClient(QByteArray)), this, SLOT(receiveDataToClient(QByteArray)), Qt::DirectConnection);
connect(this, SIGNAL(sendDataFromClient(QByteArray)), parent, SLOT(receiveDataFromClient(QByteArray)), Qt::DirectConnection);
qInfo(logTcpServer()) << " session connected: " << sessionId;
}
void tcpServerClient::socketReadyRead() {
QByteArray data;
if (socket->bytesAvailable()) {
data=socket->readAll();
emit sendDataFromClient(data);
}
}
void tcpServerClient::socketDisconnected() {
qInfo(logTcpServer()) << sessionId << "disconnected";
socket->deleteLater();
this->deleteLater();
}
void tcpServerClient::closeSocket()
{
socket->close();
}
void tcpServerClient::receiveDataToClient(QByteArray data) {
if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen())
{
socket->write(data);
}
else
{
qInfo(logTcpServer()) << "socket not open!";
}
}

63
tcpserver.h 100644
Wyświetl plik

@ -0,0 +1,63 @@
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QObject>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include <QSet>
#include <QDataStream>
#include <map>
#include <vector>
#include <typeindex>
class tcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit tcpServer(QObject* parent = Q_NULLPTR);
~tcpServer();
int startServer(qint16 port);
void stopServer();
public slots:
virtual void incomingConnection(qintptr socketDescriptor);
void receiveDataFromClient(QByteArray data);
void sendData(QByteArray data);
signals:
void onStarted();
void onStopped();
void receiveData(QByteArray data); // emit this when we have data from tcp client, connect to rigcommander
void sendDataToClient(QByteArray data);
void newClient(int socketId);
private:
QTcpServer* server;
QTcpSocket* socket = Q_NULLPTR;
};
class tcpServerClient : public QObject
{
Q_OBJECT
public:
explicit tcpServerClient(int socket, tcpServer* parent = Q_NULLPTR);
public slots:
void socketReadyRead();
void socketDisconnected();
void closeSocket();
void receiveDataToClient(QByteArray);
signals:
void sendDataFromClient(QByteArray data);
protected:
int sessionId;
QTcpSocket* socket = Q_NULLPTR;
private:
tcpServer* parent;
};
#endif

301
udpaudio.cpp 100644
Wyświetl plik

@ -0,0 +1,301 @@
#include "udpaudio.h"
#include "logcategories.h"
// Audio stream
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup)
{
qInfo(logUdp()) << "Starting udpAudio";
this->localIP = local;
this->port = audioPort;
this->radioIP = ip;
this->rxSetup = rxSetup;
this->txSetup = txSetup;
if (txSetup.sampleRate == 0) {
enableTx = false;
}
init(lport); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived);
startAudio();
watchdogTimer = new QTimer();
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
watchdogTimer->start(WATCHDOG_PERIOD);
areYouThereTimer = new QTimer();
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
areYouThereTimer->start(AREYOUTHERE_PERIOD);
}
udpAudio::~udpAudio()
{
if (pingTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping pingTimer";
pingTimer->stop();
delete pingTimer;
pingTimer = Q_NULLPTR;
}
if (idleTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping idleTimer";
idleTimer->stop();
delete idleTimer;
idleTimer = Q_NULLPTR;
}
if (watchdogTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping watchdogTimer";
watchdogTimer->stop();
delete watchdogTimer;
watchdogTimer = Q_NULLPTR;
}
if (rxAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping rxaudio thread";
rxAudioThread->quit();
rxAudioThread->wait();
}
if (txAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping txaudio thread";
txAudioThread->quit();
txAudioThread->wait();
}
qDebug(logUdp()) << "udpHandler successfully closed";
}
void udpAudio::watchdog()
{
static bool alerted = false;
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
{
if (!alerted) {
/* Just log it at the moment, maybe try signalling the control channel that it needs to
try requesting civ/audio again? */
qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?";
alerted = true;
if (rxAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping rxaudio thread";
rxAudioThread->quit();
rxAudioThread->wait();
rxAudioThread = Q_NULLPTR;
rxaudio = Q_NULLPTR;
}
if (txAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping txaudio thread";
txAudioThread->quit();
txAudioThread->wait();
txAudioThread = Q_NULLPTR;
txaudio = Q_NULLPTR;
}
}
}
else
{
alerted = false;
}
}
void udpAudio::sendTxAudio()
{
if (txaudio == Q_NULLPTR) {
return;
}
}
void udpAudio::receiveAudioData(audioPacket audio) {
// I really can't see how this could be possible but a quick sanity check!
if (txaudio == Q_NULLPTR) {
return;
}
if (audio.data.length() > 0) {
int counter = 1;
int len = 0;
while (len < audio.data.length()) {
QByteArray partial = audio.data.mid(len, 1364);
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + partial.length();
p.sentid = myId;
p.rcvdid = remoteId;
if (partial.length() == 0xa0) {
p.ident = 0x9781;
}
else {
p.ident = 0x0080; // TX audio is always this?
}
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial);
len = len + partial.length();
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
sendTrackedPacket(tx);
sendAudioSeq++;
counter++;
}
}
}
void udpAudio::changeLatency(quint16 value)
{
emit haveChangeLatency(value);
}
void udpAudio::setVolume(unsigned char value)
{
emit haveSetVolume(value);
}
void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) {
emit haveRxLevels(amplitude, latency, current, under, over);
}
void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) {
emit haveTxLevels(amplitude, latency, current, under, over);
}
void udpAudio::dataReceived()
{
while (udp->hasPendingDatagrams()) {
QNetworkDatagram datagram = udp->receiveDatagram();
//qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10);
QByteArray r = datagram.data();
switch (r.length())
{
case (16): // Response to control packet handled in udpBase
{
//control_packet_t in = (control_packet_t)r.constData();
break;
}
default:
{
/* Audio packets start as follows:
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third
*/
control_packet_t in = (control_packet_t)r.constData();
if (in->type != 0x01 && in->len >= 0x20) {
if (in->seq == 0)
{
// Seq number has rolled over.
seqPrefix++;
}
// 0xac is the smallest possible audio packet.
lastReceived = QTime::currentTime();
audioPacket tempAudio;
tempAudio.seq = (quint32)seqPrefix << 16 | in->seq;
tempAudio.time = lastReceived;
tempAudio.sent = 0;
tempAudio.data = r.mid(0x18);
// Prefer signal/slot to forward audio as it is thread/safe
// Need to do more testing but latency appears fine.
//rxaudio->incomingAudio(tempAudio);
if (rxAudioThread == Q_NULLPTR)
{
startAudio();
}
emit haveAudioData(tempAudio);
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
datagram.clear();
}
}
void udpAudio::startAudio() {
if (rxSetup.type == qtAudio) {
rxaudio = new audioHandler();
}
else if (rxSetup.type == portAudio) {
rxaudio = new paHandler();
}
else if (rxSetup.type == rtAudio) {
rxaudio = new rtHandler();
}
else
{
qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!";
}
rxAudioThread = new QThread(this);
rxAudioThread->setObjectName("rxAudio()");
rxaudio->moveToThread(rxAudioThread);
rxAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup)));
// signal/slot not currently used.
connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket)));
connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16)));
connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char)));
connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool, bool)));
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
sendControl(false, 0x03, 0x00); // First connect packet
pingTimer = new QTimer();
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
if (enableTx) {
if (txSetup.type == qtAudio) {
txaudio = new audioHandler();
}
else if (txSetup.type == portAudio) {
txaudio = new paHandler();
}
else if (txSetup.type == rtAudio) {
txaudio = new rtHandler();
}
else
{
qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!";
}
txAudioThread = new QThread(this);
rxAudioThread->setObjectName("txAudio()");
txaudio->moveToThread(txAudioThread);
txAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
emit setupTxAudio(txSetup);
}
emit setupRxAudio(rxSetup);
}

88
udpaudio.h 100644
Wyświetl plik

@ -0,0 +1,88 @@
#ifndef UDPAUDIO_H
#define UDPAUDIO_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
#include "udpbase.h"
#include "audiohandler.h"
#include "pahandler.h"
#include "rthandler.h"
// Class for all audio communications.
class udpAudio : public udpBase
{
Q_OBJECT
public:
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
~udpAudio();
int audioLatency = 0;
signals:
void haveAudioData(audioPacket data);
void setupTxAudio(audioSetup setup);
void setupRxAudio(audioSetup setup);
void haveChangeLatency(quint16 value);
void haveSetVolume(unsigned char value);
void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
public slots:
void changeLatency(quint16 value);
void setVolume(unsigned char value);
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void receiveAudioData(audioPacket audio);
private:
void sendTxAudio();
void dataReceived();
void watchdog();
void startAudio();
audioSetup rxSetup;
audioSetup txSetup;
uint16_t sendAudioSeq = 0;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* txAudioTimer = Q_NULLPTR;
bool enableTx = true;
QMutex audioMutex;
};
#endif

545
udpbase.cpp 100644
Wyświetl plik

@ -0,0 +1,545 @@
#include "udpbase.h"
#include "logcategories.h"
void udpBase::init(quint16 lport)
{
//timeStarted.start();
udp = new QUdpSocket(this);
udp->bind(lport); // Bind to random port.
localPort = udp->localPort();
qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
uint32_t addr = localIP.toIPv4Address();
myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff);
retransmitTimer = new QTimer();
connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest);
retransmitTimer->start(RETRANSMIT_PERIOD);
}
udpBase::~udpBase()
{
qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port;
if (udp != Q_NULLPTR) {
sendControl(false, 0x05, 0x00); // Send disconnect
udp->close();
delete udp;
}
if (areYouThereTimer != Q_NULLPTR)
{
areYouThereTimer->stop();
delete areYouThereTimer;
}
if (pingTimer != Q_NULLPTR)
{
pingTimer->stop();
delete pingTimer;
}
if (idleTimer != Q_NULLPTR)
{
idleTimer->stop();
delete idleTimer;
}
if (retransmitTimer != Q_NULLPTR)
{
retransmitTimer->stop();
delete retransmitTimer;
}
pingTimer = Q_NULLPTR;
idleTimer = Q_NULLPTR;
areYouThereTimer = Q_NULLPTR;
retransmitTimer = Q_NULLPTR;
}
// Base class!
void udpBase::dataReceived(QByteArray r)
{
if (r.length() < 0x10)
{
return; // Packet too small do to anything with?
}
switch (r.length())
{
case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests.
{
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x01 && in->len == 0x10)
{
// Single packet request
packetsLost++;
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
txBufferMutex.lock();
auto match = txSeqBuf.find(in->seq);
if (match != txSeqBuf.end()) {
// Found matching entry?
// Send "untracked" as it has already been sent once.
// Don't constantly retransmit the same packet, give-up eventually
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
match->retransmitCount++;
udpMutex.lock();
udp->writeDatagram(match->data, radioIP, port);
udpMutex.unlock();
}
else {
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
<< QString("0x%1").arg(in->seq, 0, 16) <<
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
}
txBufferMutex.unlock();
}
if (in->type == 0x04) {
qInfo(logUdp()) << this->metaObject()->className() << ": Received I am here ";
areYouThereCounter = 0;
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
remoteId = in->sentid;
if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) {
// send ping packets every second
areYouThereTimer->stop();
}
sendControl(false, 0x06, 0x01); // Send Are you ready - untracked.
}
else if (in->type == 0x06)
{
// Just get the seqnum and ignore the rest.
}
break;
}
case (PING_SIZE): // ping packet
{
ping_packet_t in = (ping_packet_t)r.constData();
if (in->type == 0x07)
{
// It is a ping request/response
if (in->reply == 0x00)
{
ping_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = 0x07;
p.sentid = myId;
p.rcvdid = remoteId;
p.reply = 0x01;
p.seq = in->seq;
p.time = in->time;
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else if (in->reply == 0x01) {
if (in->seq == pingSendSeq)
{
// This is response to OUR request so increment counter
pingSendSeq++;
}
}
else {
qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
}
}
break;
}
default:
{
// All packets "should" be added to the incoming buffer.
// First check that we haven't already received it.
}
break;
}
// All packets except ping and retransmit requests should trigger this.
control_packet_t in = (control_packet_t)r.constData();
// This is a variable length retransmit request!
if (in->type == 0x01 && in->len != 0x10)
{
for (quint16 i = 0x10; i < r.length(); i = i + 2)
{
quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8;
auto match = txSeqBuf.find(seq);
if (match == txSeqBuf.end()) {
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
<< QString("0x%1").arg(seq, 0, 16) <<
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
// Just send idle packet.
sendControl(false, 0, seq);
}
else {
// Found matching entry?
// Send "untracked" as it has already been sent once.
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
match->retransmitCount++;
udpMutex.lock();
udp->writeDatagram(match->data, radioIP, port);
udpMutex.unlock();
packetsLost++;
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
}
}
}
else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00)
{
rxBufferMutex.lock();
if (rxSeqBuf.isEmpty()) {
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
else
{
if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING)
{
qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " <<
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
//seqPrefix++;
// Looks like it has rolled over so clear buffer and start again.
rxSeqBuf.clear();
// Add this packet to the incoming buffer
rxSeqBuf.insert(in->seq, QTime::currentTime());
rxBufferMutex.unlock();
missingMutex.lock();
rxMissing.clear();
missingMutex.unlock();
return;
}
if (!rxSeqBuf.contains(in->seq))
{
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
if (in->seq > rxSeqBuf.lastKey() + 1) {
qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " <<
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
// We are likely missing packets then!
missingMutex.lock();
//int missCounter = 0;
// Sanity check!
for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++)
{
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(f, QTime::currentTime());
if (f != in->seq && !rxMissing.contains(f))
{
rxMissing.insert(f, 0);
}
}
missingMutex.unlock();
}
else {
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
}
else {
// This is probably one of our missing packets!
missingMutex.lock();
auto s = rxMissing.find(in->seq);
if (s != rxMissing.end())
{
qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq, 0, 16);
s = rxMissing.erase(s);
}
missingMutex.unlock();
}
}
rxBufferMutex.unlock();
}
}
void udpBase::sendRetransmitRequest()
{
// Find all gaps in received packets and then send requests for them.
// This will run every 100ms so out-of-sequence packets will not trigger a retransmit request.
if (rxMissing.isEmpty()) {
return;
}
else if (rxMissing.size() > MAX_MISSING) {
qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers";
missingMutex.lock();
rxMissing.clear();
missingMutex.unlock();
rxBufferMutex.lock();
rxSeqBuf.clear();
rxBufferMutex.unlock();
return;
}
QByteArray missingSeqs;
missingMutex.lock();
auto it = rxMissing.begin();
while (it != rxMissing.end())
{
if (it.key() != 0x0)
{
if (it.value() < 4)
{
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
it.value()++;
it++;
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting";
it = rxMissing.erase(it);
}
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer";
it++;
}
}
missingMutex.unlock();
if (missingSeqs.length() != 0)
{
control_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = 0x01;
p.seq = 0x0000;
p.sentid = myId;
p.rcvdid = remoteId;
if (missingSeqs.length() == 4) // This is just a single missing packet so send using a control.
{
p.seq = (missingSeqs[0] & 0xff) | (quint16)(missingSeqs[1] << 8);
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq, 0, 16);
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else
{
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':');
missingMutex.lock();
p.len = sizeof(p) + missingSeqs.size();
missingSeqs.insert(0, p.packet, sizeof(p));
missingMutex.unlock();
udpMutex.lock();
udp->writeDatagram(missingSeqs, radioIP, port);
udpMutex.unlock();
}
}
}
// Used to send idle and other "control" style messages
void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0)
{
control_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = type;
p.sentid = myId;
p.rcvdid = remoteId;
if (!tracked) {
p.seq = seq;
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else {
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
}
return;
}
// Send periodic ping packets
void udpBase::sendPing()
{
ping_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.type = 0x07;
p.sentid = myId;
p.rcvdid = remoteId;
p.seq = pingSendSeq;
QTime now = QTime::currentTime();
p.time = (quint32)now.msecsSinceStartOfDay();
lastPingSentTime = QDateTime::currentDateTime();
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
return;
}
void udpBase::sendTrackedPacket(QByteArray d)
{
// As the radio can request retransmission of these packets, store them in a buffer
d[6] = sendSeq & 0xff;
d[7] = (sendSeq >> 8) & 0xff;
SEQBUFENTRY s;
s.seqNum = sendSeq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = d;
if (txBufferMutex.tryLock(100))
{
if (sendSeq == 0) {
// We are either the first ever sent packet or have rolled-over so clear the buffer.
txSeqBuf.clear();
congestion = 0;
}
if (txSeqBuf.size() > BUFSIZE)
{
txSeqBuf.erase(txSeqBuf.begin());
}
txSeqBuf.insert(sendSeq, s);
txBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
}
// Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet.
//qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <<txSeqBuf.size() << "MISS:" << rxMissing.size();
//purgeOldEntries(); // Delete entries older than PURGE_SECONDS seconds (currently 5)
sendSeq++;
udpMutex.lock();
udp->writeDatagram(d, radioIP, port);
if (congestion > 10) { // Poor quality connection?
udp->writeDatagram(d, radioIP, port);
if (congestion > 20) // Even worse so send again.
udp->writeDatagram(d, radioIP, port);
}
if (idleTimer != Q_NULLPTR && idleTimer->isActive()) {
idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running
}
udpMutex.unlock();
packetsSent++;
return;
}
/// <summary>
/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use.
/// </summary>
void udpBase::purgeOldEntries()
{
// Erase old entries from the tx packet buffer
if (txBufferMutex.tryLock(100))
{
if (!txSeqBuf.isEmpty())
{
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
for (auto it = txSeqBuf.begin(); it != txSeqBuf.end();) {
if (it.value().timeSent.secsTo(QTime::currentTime()) > PURGE_SECONDS) {
txSeqBuf.erase(it++);
}
else {
break;
}
}
}
txBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
}
if (rxBufferMutex.tryLock(100))
{
if (!rxSeqBuf.isEmpty()) {
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
for (auto it = rxSeqBuf.begin(); it != rxSeqBuf.end();) {
if (it.value().secsTo(QTime::currentTime()) > PURGE_SECONDS) {
rxSeqBuf.erase(it++);
}
else {
break;
}
}
}
rxBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked";
}
if (missingMutex.tryLock(100))
{
// Erase old entries from the missing packets buffer
if (!rxMissing.isEmpty() && rxMissing.size() > 50) {
for (size_t i = 0; i < 25; ++i) {
rxMissing.erase(rxMissing.begin());
}
}
missingMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked";
}
}
void udpBase::printHex(const QByteArray& pdata)
{
printHex(pdata, false, true);
}
void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz)
{
qDebug(logUdp()) << "---- Begin hex dump -----:";
QString sdata("DATA: ");
QString index("INDEX: ");
QStringList strings;
for (int i = 0; i < pdata.length(); i++)
{
strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0'));
sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')));
index.append(QString("%1 ").arg(i, 2, 10, QChar('0')));
}
if (printVert)
{
for (int i = 0; i < strings.length(); i++)
{
//sdata = QString(strings.at(i));
qDebug(logUdp()) << strings.at(i);
}
}
if (printHoriz)
{
qDebug(logUdp()) << index;
qDebug(logUdp()) << sdata;
}
qDebug(logUdp()) << "----- End hex dump -----";
}

207
udpbase.h 100644
Wyświetl plik

@ -0,0 +1,207 @@
#ifndef UDPBASE_H
#define UDPBASE_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
struct udpPreferences {
QString ipAddress;
quint16 controlLANPort;
quint16 serialLANPort;
quint16 audioLANPort;
QString username;
QString password;
QString clientName;
quint8 waterfallFormat;
};
struct networkStatus {
quint8 rxAudioBufferPercent;
quint8 txAudioBufferPercent;
quint8 rxAudioLevel;
quint8 txAudioLevel;
quint16 rxLatency;
quint16 txLatency;
bool rxUnderrun;
bool txUnderrun;
bool rxOverrun;
bool txOverrun;
quint16 rxCurrentLatency;
quint16 txCurrentLatency;
quint32 packetsSent = 0;
quint32 packetsLost = 0;
quint16 rtt = 0;
quint32 networkLatency = 0;
QString message;
};
// Parent class that contains all common items.
class udpBase : public QObject
{
public:
~udpBase();
void init(quint16 local);
void reconnect();
void dataReceived(QByteArray r);
void sendPing();
void sendRetransmitRange(quint16 first, quint16 second, quint16 third, quint16 fourth);
void sendControl(bool tracked, quint8 id, quint16 seq);
void printHex(const QByteArray& pdata);
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
//QTime timeStarted;
QUdpSocket* udp = Q_NULLPTR;
uint32_t myId = 0;
uint32_t remoteId = 0;
uint16_t authSeq = 0x30;
uint16_t sendSeqB = 0;
uint16_t sendSeq = 1;
uint16_t lastReceivedSeq = 1;
uint16_t pkt0SendSeq = 0;
uint16_t periodicSeq = 0;
quint64 latency = 0;
QString username = "";
QString password = "";
QHostAddress radioIP;
QHostAddress localIP;
bool isAuthenticated = false;
quint16 localPort = 0;
quint16 port = 0;
bool periodicRunning = false;
bool sentPacketConnect2 = false;
QTime lastReceived = QTime::currentTime();
QMutex udpMutex;
QMutex txBufferMutex;
QMutex rxBufferMutex;
QMutex missingMutex;
struct SEQBUFENTRY {
QTime timeSent;
uint16_t seqNum;
QByteArray data;
quint8 retransmitCount;
};
QMap<quint16, QTime> rxSeqBuf;
QMap<quint16, SEQBUFENTRY> txSeqBuf;
QMap<quint16, int> rxMissing;
void sendTrackedPacket(QByteArray d);
void purgeOldEntries();
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
QTimer* watchdogTimer = Q_NULLPTR;
QTimer* retransmitTimer = Q_NULLPTR;
QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0;
quint16 areYouThereCounter = 0;
quint32 packetsSent = 0;
quint32 packetsLost = 0;
quint16 seqPrefix = 0;
QString connectionType = "";
int congestion = 0;
private:
void sendRetransmitRequest();
};
/// <summary>
/// passcode function used to generate secure (ish) code
/// </summary>
/// <param name="str"></param>
/// <returns>pointer to encoded username or password</returns>
static inline void passcode(QString in, QByteArray& out)
{
const quint8 sequence[] =
{
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0x47,0x5d,0x4c,0x42,0x66,0x20,0x23,0x46,0x4e,0x57,0x45,0x3d,0x67,0x76,0x60,0x41,0x62,0x39,0x59,0x2d,0x68,0x7e,
0x7c,0x65,0x7d,0x49,0x29,0x72,0x73,0x78,0x21,0x6e,0x5a,0x5e,0x4a,0x3e,0x71,0x2c,0x2a,0x54,0x3c,0x3a,0x63,0x4f,
0x43,0x75,0x27,0x79,0x5b,0x35,0x70,0x48,0x6b,0x56,0x6f,0x34,0x32,0x6c,0x30,0x61,0x6d,0x7b,0x2f,0x4b,0x64,0x38,
0x2b,0x2e,0x50,0x40,0x3f,0x55,0x33,0x37,0x25,0x77,0x24,0x26,0x74,0x6a,0x28,0x53,0x4d,0x69,0x22,0x5c,0x44,0x31,
0x36,0x58,0x3b,0x7a,0x51,0x5f,0x52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
QByteArray ba = in.toLocal8Bit();
uchar* ascii = (uchar*)ba.constData();
for (int i = 0; i < in.length() && i < 16; i++)
{
int p = ascii[i] + i;
if (p > 126)
{
p = 32 + p % 127;
}
out.append(sequence[p]);
}
return;
}
/// <summary>
/// returns a QByteArray of a null terminated string
/// </summary>
/// <param name="c"></param>
/// <param name="s"></param>
/// <returns></returns>
static inline QByteArray parseNullTerminatedString(QByteArray c, int s)
{
//QString res = "";
QByteArray res;
for (int i = s; i < c.length(); i++)
{
if (c[i] != '\0')
{
res.append(c[i]);
}
else
{
break;
}
}
return res;
}
#endif

268
udpcivdata.cpp 100644
Wyświetl plik

@ -0,0 +1,268 @@
#include "udpcivdata.h"
#include "logcategories.h"
// Class that manages all Civ Data to/from the rig
udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort = 0)
{
qInfo(logUdp()) << "Starting udpCivData";
localIP = local;
port = civPort;
radioIP = ip;
splitWaterfall = splitWf;
udpBase::init(localPort); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived);
sendControl(false, 0x03, 0x00); // First connect packet
/*
Connect various timers
*/
pingTimer = new QTimer();
idleTimer = new QTimer();
areYouThereTimer = new QTimer();
startCivDataTimer = new QTimer();
watchdogTimer = new QTimer();
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog);
connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0));
connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false));
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
watchdogTimer->start(WATCHDOG_PERIOD);
// Start sending are you there packets - will be stopped once "I am here" received
// send ping packets every 100 ms (maybe change to less frequent?)
pingTimer->start(PING_PERIOD);
// Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent.
idleTimer->start(IDLE_PERIOD);
areYouThereTimer->start(AREYOUTHERE_PERIOD);
}
udpCivData::~udpCivData()
{
sendOpenClose(true);
if (startCivDataTimer != Q_NULLPTR)
{
startCivDataTimer->stop();
delete startCivDataTimer;
startCivDataTimer = Q_NULLPTR;
}
if (pingTimer != Q_NULLPTR)
{
pingTimer->stop();
delete pingTimer;
pingTimer = Q_NULLPTR;
}
if (idleTimer != Q_NULLPTR)
{
idleTimer->stop();
delete idleTimer;
idleTimer = Q_NULLPTR;
}
if (watchdogTimer != Q_NULLPTR)
{
watchdogTimer->stop();
delete watchdogTimer;
watchdogTimer = Q_NULLPTR;
}
}
void udpCivData::watchdog()
{
static bool alerted = false;
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
{
if (!alerted) {
qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start.";
if (startCivDataTimer != Q_NULLPTR)
{
startCivDataTimer->start(100);
}
alerted = true;
}
}
else
{
alerted = false;
}
}
void udpCivData::send(QByteArray d)
{
//qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d;
data_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + d.length();
p.sentid = myId;
p.rcvdid = remoteId;
p.reply = (char)0xc1;
p.datalen = d.length();
p.sendseq = qToBigEndian(sendSeqB); // THIS IS BIG ENDIAN!
QByteArray t = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
t.append(d);
sendTrackedPacket(t);
sendSeqB++;
return;
}
void udpCivData::sendOpenClose(bool close)
{
uint8_t magic = 0x04;
if (close)
{
magic = 0x00;
}
openclose_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p);
p.sentid = myId;
p.rcvdid = remoteId;
p.data = 0x01c0; // Not sure what other values are available:
p.sendseq = qToBigEndian(sendSeqB);
p.magic = magic;
sendSeqB++;
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
return;
}
void udpCivData::dataReceived()
{
while (udp->hasPendingDatagrams())
{
QNetworkDatagram datagram = udp->receiveDatagram();
//qInfo(logUdp()) << "Received: " << datagram.data();
QByteArray r = datagram.data();
switch (r.length())
{
case (CONTROL_SIZE): // Control packet
{
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x04)
{
areYouThereTimer->stop();
}
else if (in->type == 0x06)
{
// Update remoteId
remoteId = in->sentid;
// Manually send a CIV start request and start the timer if it isn't received.
// The timer will be stopped as soon as valid CIV data is received.
sendOpenClose(false);
if (startCivDataTimer != Q_NULLPTR) {
startCivDataTimer->start(100);
}
}
break;
}
default:
{
if (r.length() > 21) {
data_packet_t in = (data_packet_t)r.constData();
if (in->type != 0x01) {
// Process this packet, any re-transmit requests will happen later.
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
// We have received some Civ data so stop sending Start packets!
if (startCivDataTimer != Q_NULLPTR) {
startCivDataTimer->stop();
}
lastReceived = QTime::currentTime();
if (quint16(in->datalen + 0x15) == (quint16)in->len)
{
//if (r.mid(0x15).length() != 157)
// Find data length
int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00")) + 2;
int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
//splitWaterfall = false;
if (splitWaterfall && pos > 1 && len > 100) {
// We need to split waterfall data into its component parts
// There are only 2 types that we are currently aware of
int numDivisions = 0;
if (len == 490) // IC705, IC9700, IC7300(LAN)
{
numDivisions = 11;
}
else if (len == 704) // IC7610, IC7851, ICR8600
{
numDivisions = 15;
}
else {
qInfo(logUdp()) << "Unknown spectrum size" << len;
break;
}
// (sequence #1) includes center/fixed mode at [05]. No pixels.
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 "
// "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd "
// (sequences 2-10, 50 pixels)
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 "
// "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd "
// ^--^--(seq 7/11)
// ^-- start waveform data 0x00 to 0xA0, index 05 to 54
// (sequence #11)
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "
// "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd "
int divSize = (len / numDivisions) + 6;
QByteArray wfPacket;
for (int i = 0; i < numDivisions; i++) {
wfPacket = r.mid(pos - 6, 9); // First part of packet
wfPacket = r.mid(pos - 6, 9); // First part of packet
char tens = ((i + 1) / 10);
char units = ((i + 1) - (10 * tens));
wfPacket[7] = units | (tens << 4);
tens = (numDivisions / 10);
units = (numDivisions - (10 * tens));
wfPacket[8] = units | (tens << 4);
if (i == 0) {
//Just send initial data, first BCD encode the max number:
wfPacket.append(r.mid(pos + 3, 12));
}
else
{
wfPacket.append(r.mid((pos + 15) + ((i - 1) * divSize), divSize));
}
if (i < numDivisions - 1) {
wfPacket.append('\xfd');
}
//printHex(wfPacket, false, true);
emit receive(wfPacket);
wfPacket.clear();
}
//qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize;
}
else {
// Not waterfall data or split not enabled.
emit receive(r.mid(0x15));
}
//qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length();
}
}
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
datagram.clear();
}
}

56
udpcivdata.h 100644
Wyświetl plik

@ -0,0 +1,56 @@
// Class for all (pseudo) serial communications
#ifndef UDPCIVDATA_H
#define UDPCIVDATA_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
#include "udpbase.h"
class udpCivData : public udpBase
{
Q_OBJECT
public:
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport);
~udpCivData();
QMutex serialmutex;
signals:
int receive(QByteArray);
public slots:
void send(QByteArray d);
private:
void watchdog();
void dataReceived();
void sendOpenClose(bool close);
QTimer* startCivDataTimer = Q_NULLPTR;
bool splitWaterfall = false;
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -1,6 +1,7 @@
#ifndef UDPHANDLER_H
#define UDPHANDLER_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
@ -11,6 +12,7 @@
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
@ -21,186 +23,11 @@
#include <QDebug>
#include "audiohandler.h"
#include "packettypes.h"
#define PURGE_SECONDS 10
#define TOKEN_RENEWAL 60000
#define PING_PERIOD 100
#define IDLE_PERIOD 100
#define TXAUDIO_PERIOD 10
#define AREYOUTHERE_PERIOD 500
#define WATCHDOG_PERIOD 500
#define RETRANSMIT_PERIOD 100
#define LOCK_PERIOD 100
struct udpPreferences {
QString ipAddress;
quint16 controlLANPort;
quint16 serialLANPort;
quint16 audioLANPort;
QString username;
QString password;
QString clientName;
};
void passcode(QString in, QByteArray& out);
QByteArray parseNullTerminatedString(QByteArray c, int s);
// Parent class that contains all common items.
class udpBase : public QObject
{
public:
~udpBase();
void init();
void dataReceived(QByteArray r);
void sendPing();
void sendRetransmitRange(quint16 first, quint16 second, quint16 third,quint16 fourth);
void sendControl(bool tracked,quint8 id, quint16 seq);
QTime timeStarted;
QUdpSocket* udp=Q_NULLPTR;
uint32_t myId = 0;
uint32_t remoteId = 0;
uint8_t authSeq = 0x00;
uint16_t sendSeqB = 0;
uint16_t sendSeq = 1;
uint16_t lastReceivedSeq = 1;
uint16_t pkt0SendSeq = 0;
uint16_t periodicSeq = 0;
quint64 latency = 0;
QString username = "";
QString password = "";
QHostAddress radioIP;
QHostAddress localIP;
bool isAuthenticated = false;
quint16 localPort=0;
quint16 port=0;
bool periodicRunning = false;
bool sentPacketConnect2 = false;
QTime lastReceived =QTime::currentTime();
QMutex udpMutex;
QMutex txBufferMutex;
QMutex rxBufferMutex;
QMutex missingMutex;
struct SEQBUFENTRY {
QTime timeSent;
uint16_t seqNum;
QByteArray data;
quint8 retransmitCount;
};
QMap<quint16, QTime> rxSeqBuf;
QMap<quint16, SEQBUFENTRY> txSeqBuf;
QMap<quint16, int> rxMissing;
void sendTrackedPacket(QByteArray d);
void purgeOldEntries();
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
QTimer* watchdogTimer = Q_NULLPTR;
QTimer* retransmitTimer = Q_NULLPTR;
QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0;
quint16 areYouThereCounter=0;
quint32 packetsSent=0;
quint32 packetsLost=0;
quint16 seqPrefix = 0;
QString connectionType="";
int congestion = 0;
private:
void sendRetransmitRequest();
};
// Class for all (pseudo) serial communications
class udpCivData : public udpBase
{
Q_OBJECT
public:
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort);
~udpCivData();
QMutex serialmutex;
signals:
int receive(QByteArray);
public slots:
void send(QByteArray d);
private:
void watchdog();
void dataReceived();
void sendOpenClose(bool close);
QTimer* startCivDataTimer = Q_NULLPTR;
};
// Class for all audio communications.
class udpAudio : public udpBase
{
Q_OBJECT
public:
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, audioSetup rxSetup, audioSetup txSetup);
~udpAudio();
int audioLatency = 0;
signals:
void haveAudioData(audioPacket data);
void setupTxAudio(audioSetup setup);
void setupRxAudio(audioSetup setup);
void haveChangeLatency(quint16 value);
void haveSetVolume(unsigned char value);
public slots:
void changeLatency(quint16 value);
void setVolume(unsigned char value);
private:
void sendTxAudio();
void dataReceived();
void watchdog();
uint16_t sendAudioSeq = 0;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* txAudioTimer=Q_NULLPTR;
bool enableTx = true;
QMutex audioMutex;
};
#include "audiohandler.h"
#include "udpbase.h"
#include "udpcivdata.h"
#include "udpaudio.h"
@ -218,6 +45,8 @@ public:
udpCivData* civ = Q_NULLPTR;
udpAudio* audio = Q_NULLPTR;
unsigned char numRadios;
QList<radio_cap_packet> radios;
public slots:
void receiveDataFromUserToRig(QByteArray); // This slot will send data on to
@ -226,6 +55,10 @@ public slots:
void changeLatency(quint16 value);
void setVolume(unsigned char value);
void init();
void setCurrentRadio(quint8 radio);
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over);
signals:
void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander
@ -233,9 +66,10 @@ signals:
void haveNetworkError(QString, QString);
void haveChangeLatency(quint16 value);
void haveSetVolume(unsigned char value);
void haveNetworkStatus(QString);
void haveNetworkStatus(networkStatus);
void haveBaudRate(quint32 baudrate);
void requestRadioSelection(QList<radio_cap_packet> radios);
void setRadioUsage(quint8, quint8 busy, QString name, QString mac);
private:
void sendAreYouThere();
@ -259,6 +93,9 @@ private:
quint16 civPort;
quint16 audioPort;
quint16 civLocalPort;
quint16 audioLocalPort;
audioSetup rxSetup;
audioSetup txSetup;
@ -270,9 +107,9 @@ private:
quint16 tokRequest;
quint32 token;
// These are for stream ident info.
char identa;
quint32 identb;
quint8 macaddress[8];
quint8 guid[GUIDLEN];
bool useGuid = false;
QByteArray usernameEncoded;
QByteArray passwordEncoded;
@ -283,6 +120,8 @@ private:
quint8 civId = 0;
quint16 rxSampleRates = 0;
quint16 txSampleRates = 0;
networkStatus status;
bool splitWf = false;
};

Plik diff jest za duży Load Diff

Wyświetl plik

@ -21,7 +21,9 @@
#include "packettypes.h"
#include "rigidentities.h"
#include "udphandler.h"
#include "audiohandler.h"
#include "rigcommander.h"
extern void passcode(QString in,QByteArray& out);
extern QByteArray parseNullTerminatedString(QByteArray c, int s);
@ -40,6 +42,44 @@ struct SERVERUSER {
quint8 userType;
};
struct RIGCONFIG {
QString serialPort;
quint32 baudRate;
unsigned char civAddr;
bool civIsRadioModel;
bool forceRTSasPTT;
bool hasWiFi = false;
bool hasEthernet=false;
audioSetup rxAudioSetup;
audioSetup txAudioSetup;
QString modelName;
QString rigName;
#pragma pack(push, 1)
union {
struct {
quint8 unused[7]; // 0x22
quint16 commoncap; // 0x27
quint8 unusedb; // 0x29
quint8 macaddress[6]; // 0x2a
};
quint8 guid[GUIDLEN]; // 0x20
};
#pragma pack(pop)
bool rigAvailable=false;
rigCapabilities rigCaps;
rigCommander* rig = Q_NULLPTR;
QThread* rigThread = Q_NULLPTR;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* rxAudioTimer = Q_NULLPTR;
QTimer* connectTimer = Q_NULLPTR;
quint8 waterfallFormat;
};
struct SERVERCONFIG {
bool enabled;
bool lan;
@ -50,8 +90,8 @@ struct SERVERCONFIG {
int audioInput;
quint8 resampleQuality;
quint32 baudRate;
QList <SERVERUSER> users;
QList <RIGCONFIG*> rigs;
};
@ -60,7 +100,7 @@ class udpServer : public QObject
Q_OBJECT
public:
udpServer(SERVERCONFIG config,audioSetup outAudio, audioSetup inAudio);
explicit udpServer(SERVERCONFIG* config, QObject* parent = nullptr);
~udpServer();
public slots:
@ -72,7 +112,7 @@ public slots:
signals:
void haveDataFromServer(QByteArray);
void haveAudioData(audioPacket data);
void haveNetworkStatus(QString);
void haveNetworkStatus(networkStatus);
void setupTxAudio(audioSetup);
void setupRxAudio(audioSetup);
@ -100,12 +140,11 @@ private:
quint16 connSeq;
quint16 pingSeq;
quint32 rxPingTime; // 32bit as has other info
quint8 authInnerSeq;
quint16 authInnerSeq;
quint16 authSeq;
quint16 innerSeq;
quint16 sendAudioSeq;
quint8 identa;
quint32 identb;
quint8 macaddress[6];
quint16 tokenRx;
quint32 tokenTx;
quint32 commonCap;
@ -140,6 +179,7 @@ private:
CLIENT* controlClient = Q_NULLPTR;
CLIENT* civClient = Q_NULLPTR;
CLIENT* audioClient = Q_NULLPTR;
quint8 guid[GUIDLEN];
};
void controlReceived();
@ -151,28 +191,25 @@ private:
void sendControl(CLIENT* c, quint8 type, quint16 seq);
void sendLoginResponse(CLIENT* c, bool allowed);
void sendCapabilities(CLIENT* c);
void sendConnectionInfo(CLIENT* c);
void sendConnectionInfo(CLIENT* c,quint8 guid[GUIDLEN]);
void sendTokenResponse(CLIENT* c,quint8 type);
void sendStatus(CLIENT* c);
void sendRetransmitRequest(CLIENT* c);
void watchdog();
void sendRxAudio();
void deleteConnection(QList<CLIENT*> *l, CLIENT* c);
SERVERCONFIG config;
SERVERCONFIG *config;
QUdpSocket* udpControl = Q_NULLPTR;
QUdpSocket* udpCiv = Q_NULLPTR;
QUdpSocket* udpAudio = Q_NULLPTR;
QHostAddress localIP;
QString macAddress;
quint8 macAddress[6];
quint32 controlId = 0;
quint32 civId = 0;
quint32 audioId = 0;
quint8 rigciv = 0xa2;
QMutex udpMutex; // Used for critical operations.
QMutex connMutex;
QMutex audioMutex;
@ -180,19 +217,13 @@ private:
QList <CLIENT*> controlClients = QList<CLIENT*>();
QList <CLIENT*> civClients = QList<CLIENT*>();
QList <CLIENT*> audioClients = QList<CLIENT*>();
QTime timeStarted;
rigCapabilities rigCaps;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
//QTime timeStarted;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
audioSetup outAudio;
audioSetup inAudio;
QTimer* rxAudioTimer=Q_NULLPTR;
quint16 rxSampleRate = 0;
quint16 txSampleRate = 0;
quint8 rxCodec = 0;
@ -200,7 +231,9 @@ private:
QHostAddress hasTxAudio;
QTimer* wdTimer;
networkStatus status;
};
#endif // UDPSERVER_H
#endif // UDPSERVER_H

Wyświetl plik

@ -139,7 +139,7 @@
</size>
</property>
<property name="text">
<string>Contol Port</string>
<string>Control Port</string>
</property>
</widget>
</item>

1184
wfmain.cpp

Plik diff jest za duży Load Diff

Wyświetl plik

@ -1,3 +1,7 @@
#ifdef BUILD_WFSERVER
#include "servermain.h"
#else
#ifndef WFMAIN_H
#define WFMAIN_H
@ -10,6 +14,8 @@
#include <QSettings>
#include <QShortcut>
#include <QMetaType>
#include <QMutex>
#include <QMutexLocker>
#include "logcategories.h"
#include "commhandler.h"
@ -19,6 +25,7 @@
#include "rigidentities.h"
#include "repeaterattributes.h"
#include "packettypes.h"
#include "calibrationwindow.h"
#include "repeatersetup.h"
#include "satellitesetup.h"
@ -27,6 +34,7 @@
#include "qledlabel.h"
#include "rigctld.h"
#include "aboutbox.h"
#include "selectradio.h"
#include <qcustomplot.h>
#include <qserialportinfo.h>
@ -34,6 +42,14 @@
#include <deque>
#include <memory>
#include <portaudio.h>
#ifndef Q_OS_LINUX
#include "RtAudio.h"
#else
#include "rtaudio/RtAudio.h"
#endif
namespace Ui {
class wfmain;
}
@ -158,8 +174,8 @@ signals:
void sayFrequency();
void sayMode();
void sayAll();
void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp);
void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp);
void sendCommSetup(unsigned char rigCivAddr, QString rigSerialPort, quint32 rigBaudRate,QString vsp, quint16 tcp, quint8 wf);
void sendCommSetup(unsigned char rigCivAddr, udpPreferences prefs, audioSetup rxSetup, audioSetup txSetup, QString vsp, quint16 tcp);
void sendCloseComm();
void sendChangeLatency(quint16 latency);
void initServer();
@ -258,7 +274,7 @@ private slots:
void receiveRigID(rigCapabilities rigCaps);
void receiveFoundRigID(rigCapabilities rigCaps);
void receiveSerialPortError(QString port, QString errorText);
void receiveStatusUpdate(QString errorText);
void receiveStatusUpdate(networkStatus status);
void handlePlotClick(QMouseEvent *);
void handlePlotDoubleClick(QMouseEvent *);
void handleWFClick(QMouseEvent *);
@ -268,6 +284,7 @@ private slots:
void sendRadioCommandLoop();
void showStatusBarText(QString text);
void receiveBaudRate(quint32 baudrate);
void radioSelection(QList<radio_cap_packet> radios);
void setRadioTimeDateSend();
@ -490,11 +507,13 @@ private slots:
void on_meter2selectionCombo_activated(int index);
void on_waterfallFormatCombo_activated(int index);
void on_enableRigctldChk_clicked(bool checked);
void on_rigctldPortTxt_editingFinished();
void setAudioDevicesUI();
void on_tcpServerPortTxt_editingFinished();
void on_moreControlsBtn_clicked();
@ -518,6 +537,24 @@ private slots:
void on_useRTSforPTTchk_clicked(bool checked);
void on_radioStatusBtn_clicked();
void on_audioSystemCombo_currentIndexChanged(int value);
void on_topLevelSlider_valueChanged(int value);
void on_botLevelSlider_valueChanged(int value);
void on_underlayBufferSlider_valueChanged(int value);
void on_underlayNone_toggled(bool checked);
void on_underlayPeakHold_toggled(bool checked);
void on_underlayPeakBuffer_toggled(bool checked);
void on_underlayAverageBuffer_toggled(bool checked);
private:
Ui::wfmain *ui;
void closeEvent(QCloseEvent *event);
@ -536,6 +573,7 @@ private:
void setPlotTheme(QCustomPlot *plot, bool isDark);
void prepareWf();
void prepareWf(unsigned int wfLength);
void computePlasma();
void showHideSpectrum(bool show);
void getInitialRigState();
void setBandButtons();
@ -625,7 +663,24 @@ private:
quint16 wfLength;
bool spectrumDrawLock;
enum underlay_t { underlayNone, underlayPeakHold, underlayPeakBuffer, underlayAverageBuffer };
QByteArray spectrumPeaks;
QVector <double> spectrumPlasmaLine;
QVector <QByteArray> spectrumPlasma;
unsigned int spectrumPlasmaSize = 64;
underlay_t underlayMode = underlayNone;
bool drawPlasma = true;
QMutex plasmaMutex;
void resizePlasmaBuffer(int newSize);
double plotFloor = 0;
double plotCeiling = 160;
double wfFloor = 0;
double wfCeiling = 160;
double oldPlotFloor = -1;
double oldPlotCeiling = 999;
QVector <QByteArray> wfimage;
unsigned int wfLengthMax;
@ -734,6 +789,8 @@ private:
bool useDarkMode;
bool useSystemTheme;
bool drawPeaks;
underlay_t underlayMode = underlayNone;
int underlayBufferSize = 64;
bool wfAntiAlias;
bool wfInterpolate;
QString stylesheetPath;
@ -752,10 +809,14 @@ private:
unsigned char localAFgain;
unsigned int wflength;
int wftheme;
int plotFloor;
int plotCeiling;
bool confirmExit;
bool confirmPowerOff;
meterKind meter2Type;
// plot scheme
quint16 tcpPort;
quint8 waterfallFormat;
audioType audioSystem;
} prefs;
preferences defPrefs;
@ -766,8 +827,6 @@ private:
audioSetup rxSetup;
audioSetup txSetup;
audioSetup serverRxSetup;
audioSetup serverTxSetup;
colors defaultColors;
@ -838,7 +897,7 @@ private:
satelliteSetup *sat;
transceiverAdjustments *trxadj;
aboutbox *abtBox;
selectRadio *selRad;
udpServer* udp = Q_NULLPTR;
rigCtlD* rigCtl = Q_NULLPTR;
@ -882,12 +941,16 @@ Q_DECLARE_METATYPE(struct mode_info)
Q_DECLARE_METATYPE(struct udpPreferences)
Q_DECLARE_METATYPE(struct audioPacket)
Q_DECLARE_METATYPE(struct audioSetup)
Q_DECLARE_METATYPE(struct SERVERCONFIG)
Q_DECLARE_METATYPE(struct timekind)
Q_DECLARE_METATYPE(struct datekind)
Q_DECLARE_METATYPE(struct networkStatus)
Q_DECLARE_METATYPE(enum rigInput)
Q_DECLARE_METATYPE(enum meterKind)
Q_DECLARE_METATYPE(enum spectrumMode)
Q_DECLARE_METATYPE(QList<radio_cap_packet>)
Q_DECLARE_METATYPE(rigstate*)
#endif // WFMAIN_H
#endif

353
wfmain.ui
Wyświetl plik

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>946</width>
<width>1002</width>
<height>569</height>
</rect>
</property>
@ -108,7 +108,7 @@
<item>
<widget class="QPushButton" name="toFixedBtn">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Press button to convert center mode spectrum to fixed mode, preserving the range. This allows you to tune without the spectrum moving, in the same currently-visible range that you see now. &lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;The currently-selected edge slot will be overriden.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Press button to convert center mode spectrum to fixed mode, preserving the range. This allows you to tune without the spectrum moving, in the same currently-visible range that you see now. &lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;The currently-selected edge slot will be overridden.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>ToFixed</string>
@ -148,7 +148,7 @@
<string>Waterfall display color theme</string>
</property>
<property name="accessibleDescription">
<string>Selects the theme for the color waterfall dispaly</string>
<string>Selects the theme for the color waterfall display</string>
</property>
</widget>
</item>
@ -846,6 +846,114 @@
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="topSliderLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="topLevelSlider">
<property name="minimumSize">
<size>
<width>0</width>
<height>70</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="toolTip">
<string>Sets the ceiling for the waterfall and spectrum displays</string>
</property>
<property name="accessibleName">
<string>Ceiling for waterfall and spectrum display</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>160</number>
</property>
<property name="value">
<number>160</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelTop">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>15</height>
</size>
</property>
<property name="text">
<string>Top</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="botAdjustVertLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="botLevelSlider">
<property name="minimumSize">
<size>
<width>0</width>
<height>70</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="maximum">
<number>160</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelBot">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>15</height>
</size>
</property>
<property name="text">
<string>Bot</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_16">
<property name="leftMargin">
@ -2066,7 +2174,7 @@
<item>
<widget class="QStackedWidget" name="settingsStack">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="radioAccess">
<layout class="QVBoxLayout" name="verticalLayout_21">
@ -2163,7 +2271,7 @@
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. &lt;/p&gt;&lt;p&gt;Here are some common examples:&lt;/p&gt;
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the address in as hexadecimal, without any prefix, just as the radio presents the address in the menu. &lt;/p&gt;&lt;p&gt;Here are some common examples:&lt;/p&gt;
&lt;p&gt;IC-706: 58
&lt;br/&gt;IC-756: 50
&lt;br/&gt;IC-756 Pro: 5C
@ -2633,6 +2741,32 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>Audio System</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="audioSystemCombo">
<item>
<property name="text">
<string>QT Audio</string>
</property>
</item>
<item>
<property name="text">
<string>PortAudio</string>
</property>
</item>
<item>
<property name="text">
<string>RT Audio</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
@ -2773,6 +2907,114 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_43">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="underlayLabel">
<property name="text">
<string>Underlay Mode</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="underlayNone">
<property name="text">
<string>None</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">underlayButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="underlayPeakHold">
<property name="text">
<string>Peak Hold</string>
</property>
<attribute name="buttonGroup">
<string notr="true">underlayButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="underlayPeakBuffer">
<property name="text">
<string>Peak</string>
</property>
<attribute name="buttonGroup">
<string notr="true">underlayButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="underlayAverageBuffer">
<property name="text">
<string>Average</string>
</property>
<attribute name="buttonGroup">
<string notr="true">underlayButtonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QLabel" name="label_42">
<property name="text">
<string>Uneerlay Buffer Size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="underlayBufferSlider">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>128</number>
</property>
<property name="value">
<number>64</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_27">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_28">
<item>
@ -3099,7 +3341,7 @@
</size>
</property>
<property name="text">
<string>Contol Port</string>
<string>Control Port</string>
</property>
</widget>
</item>
@ -3527,6 +3769,93 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_42">
<item>
<widget class="QLabel" name="label_26">
<property name="text">
<string>TCP Server Port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="tcpServerPortTxt">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_39">
<property name="text">
<string>Enter port for TCP server, 0 = disabled (restart required if changed)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_26">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_44">
<item>
<widget class="QLabel" name="label_41">
<property name="text">
<string>Waterfall Format</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="waterfallFormatCombo">
<item>
<property name="text">
<string>Default</string>
</property>
</item>
<item>
<property name="text">
<string>Single (network)</string>
</property>
</item>
<item>
<property name="text">
<string>Multi (serial)</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_28">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
@ -3634,6 +3963,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="radioStatusBtn">
<property name="text">
<string>Radio Status</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
@ -3670,8 +4006,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>946</width>
<height>22</height>
<width>1002</width>
<height>21</height>
</rect>
</property>
</widget>
@ -3693,5 +4029,6 @@
<connections/>
<buttongroups>
<buttongroup name="buttonGroup"/>
<buttongroup name="underlayButtonGroup"/>
</buttongroups>
</ui>

172
wfserver.pro 100644
Wyświetl plik

@ -0,0 +1,172 @@
#-------------------------------------------------
#
# Project created by QtCreator 2018-05-26T16:57:32
#
#-------------------------------------------------
QT += core serialport network multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
TARGET = wfserver
TEMPLATE = app
CONFIG += console
DEFINES += WFVIEW_VERSION=\\\"1.4\\\"
DEFINES += BUILD_WFSERVER
CONFIG(debug, release|debug) {
# For Debug builds only:
QMAKE_CXXFLAGS += -faligned-new
win32:DESTDIR = wfview-release
win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -ole32
} else {
# For Release builds only:
linux:QMAKE_CXXFLAGS += -s
QMAKE_CXXFLAGS += -fvisibility=hidden
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
QMAKE_CXXFLAGS += -faligned-new
linux:QMAKE_LFLAGS += -O2 -s
win32:DESTDIR = wfview-debug
win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32
}
# RTAudio defines
win32:DEFINES += __WINDOWS_WASAPI__
#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries
#linux:DEFINES += __LINUX_ALSA__
#linux:DEFINES += __LINUX_OSS__
linux:DEFINES += __LINUX_PULSE__
macx:DEFINES += __MACOSX_CORE__
!linux:SOURCES += ../rtaudio/RTAudio.cpp
!linux:HEADERS += ../rtaudio/RTAUdio.h
!linux:INCLUDEPATH += ../rtaudio
linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread
win32:INCLUDEPATH += ../portaudio/include
!win32:LIBS += -lportaudio
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
DEFINES += QCUSTOMPLOT_COMPILE_LIBRARY
# These defines are used for the resampler
equals(QT_ARCH, i386): DEFINES += USE_SSE
equals(QT_ARCH, i386): DEFINES += USE_SSE2
equals(QT_ARCH, arm): DEFINES += USE_NEON
DEFINES += OUTSIDE_SPEEX
DEFINES += RANDOM_PREFIX=wf
isEmpty(PREFIX) {
PREFIX = /usr/local
}
# These defines are used for the Eigen library
DEFINES += EIGEN_MPL2_ONLY
DEFINES += EIGEN_DONT_VECTORIZE #Clear vector flags
equals(QT_ARCH, i386): win32:DEFINES += EIGEN_VECTORIZE_SSE3
equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3
DEFINES += PREFIX=\\\"$$PREFIX\\\"
macx:INCLUDEPATH += /usr/local/include /opt/local/include
macx:LIBS += -L/usr/local/lib -L/opt/local/lib
macx:ICON = ../wfview/resources/wfview.icns
win32:RC_ICONS = ../wfview/resources/wfview.ico
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.13
QMAKE_TARGET_BUNDLE_PREFIX = org.wfview
MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
MY_ENTITLEMENTS.value = ../wfview/resources/wfview.entitlements
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS
QMAKE_INFO_PLIST = ../wfview/resources/Info.plist
!win32:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\"
!win32:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\""
win32:DEFINES += GITSHORT=\\\"$$system(git -C $$PWD rev-parse --short HEAD)\\\"
win32:DEFINES += HOST=\\\"wfview.org\\\"
win32:DEFINES += UNAME=\\\"build\\\"
RESOURCES += qdarkstyle/style.qrc \
resources/resources.qrc
unix:target.path = $$PREFIX/bin
INSTALLS += target
# Do not do this, it will hang on start:
# CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT
CONFIG(debug, release|debug) {
win32:LIBS += -L../opus/win32/VS2015/Win32/Debug/ -lopus
} else {
win32:LIBS += -L../opus/win32/VS2015/Win32/Release/ -lopus
}
linux:LIBS += -L./ -lopus
macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus
!linux:INCLUDEPATH += ../opus/include
!linux:INCLUDEPATH += ../eigen
!linux:INCLUDEPATH += ../r8brain-free-src
INCLUDEPATH += resampler
SOURCES += main.cpp\
servermain.cpp \
commhandler.cpp \
rigcommander.cpp \
freqmemory.cpp \
rigidentities.cpp \
udpbase.cpp \
udphandler.cpp \
udpcivdata.cpp \
udpaudio.cpp \
logcategories.cpp \
pahandler.cpp \
rthandler.cpp \
audiohandler.cpp \
audioconverter.cpp \
udpserver.cpp \
pttyhandler.cpp \
resampler/resample.c \
rigctld.cpp \
tcpserver.cpp \
keyboard.cpp
HEADERS += servermain.h \
commhandler.h \
rigcommander.h \
freqmemory.h \
rigidentities.h \
udpbase.h \
udphandler.h \
udpcivdata.h \
udpaudio.h \
logcategories.h \
pahandler.h \
rthandler.h \
audiohandler.h \
audioconverter.h \
udpserver.h \
packettypes.h \
pttyhandler.h \
resampler/speex_resampler.h \
resampler/arch.h \
resampler/resample_sse.h \
repeaterattributes.h \
rigctld.h \
ulaw.h \
tcpserver.h \
audiotaper.h \
keyboard.h

440
wfserver.vcxproj 100644
Wyświetl plik

@ -0,0 +1,440 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}</ProjectGuid>
<RootNamespace>wfserver</RootNamespace>
<Keyword>QtVS_v304</Keyword>
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
<QtMsBuild Condition="'$(QtMsBuild)'=='' or !Exists('$(QtMsBuild)\qt.targets')">$(MSBuildProjectDirectory)\QtMsBuild</QtMsBuild></PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<OutputDirectory>wfview-release\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
<IntermediateDirectory>release\</IntermediateDirectory>
<PrimaryOutput>wfserver</PrimaryOutput>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<OutputDirectory>wfview-debug\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
<IntermediateDirectory>debug\</IntermediateDirectory>
<PrimaryOutput>wfserver</PrimaryOutput>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /><Target Name="QtMsBuildNotFound" BeforeTargets="CustomBuild;ClCompile" Condition="!Exists('$(QtMsBuild)\qt.targets') or !Exists('$(QtMsBuild)\qt.props')"><Message Importance="High" Text="QtMsBuild: could not locate qt.targets, qt.props; project may not build correctly." /></Target>
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>wfview-debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfserver</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>wfview-release\</OutDir><IntDir>release\</IntDir><TargetName>wfserver</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>release\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
<DebugInformationFormat>None</DebugInformationFormat>
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>release\</ObjectFileName>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<ProgramDataBaseFileName></ProgramDataBaseFileName>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
<Link>
<AdditionalDependencies>..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>false</GenerateDebugInformation>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<LinkIncremental>false</LinkIncremental>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>$(OutDir)\wfserver.exe</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SubSystem>Console</SubSystem>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Link>
<Midl>
<DefaultCharType>Unsigned</DefaultCharType>
<EnableErrorChecks>None</EnableErrorChecks>
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>debug\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>debug\</ObjectFileName>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="44f6ec2";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
<Link>
<AdditionalDependencies>..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<OutputFile>$(OutDir)\wfserver.exe</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SubSystem>Console</SubSystem>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Link>
<Midl>
<DefaultCharType>Unsigned</DefaultCharType>
<EnableErrorChecks>None</EnableErrorChecks>
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_CONSOLE;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFSERVER;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"44f6ec2\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc></ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\rtaudio\RTAudio.cpp" />
<ClCompile Include="audioconverter.cpp" />
<ClCompile Include="audiohandler.cpp" />
<ClCompile Include="commhandler.cpp" />
<ClCompile Include="freqmemory.cpp" />
<ClCompile Include="keyboard.cpp" />
<ClCompile Include="logcategories.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="pahandler.cpp" />
<ClCompile Include="pttyhandler.cpp" />
<ClCompile Include="resampler\resample.c" />
<ClCompile Include="rigcommander.cpp" />
<ClCompile Include="rigctld.cpp" />
<ClCompile Include="rigidentities.cpp" />
<ClCompile Include="rthandler.cpp" />
<ClCompile Include="servermain.cpp" />
<ClCompile Include="tcpserver.cpp" />
<ClCompile Include="udpaudio.cpp" />
<ClCompile Include="udpbase.cpp" />
<ClCompile Include="udpcivdata.cpp" />
<ClCompile Include="udphandler.cpp" />
<ClCompile Include="udpserver.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rtaudio\RTAUdio.h" />
<ClInclude Include="resampler\arch.h" />
<QtMoc Include="audioconverter.h">
</QtMoc>
<QtMoc Include="audiohandler.h">
</QtMoc>
<ClInclude Include="audiotaper.h" />
<QtMoc Include="commhandler.h">
</QtMoc>
<ClInclude Include="freqmemory.h" />
<QtMoc Include="keyboard.h">
</QtMoc>
<ClInclude Include="logcategories.h" />
<ClInclude Include="packettypes.h" />
<QtMoc Include="pahandler.h">
</QtMoc>
<QtMoc Include="pttyhandler.h">
</QtMoc>
<ClInclude Include="repeaterattributes.h" />
<ClInclude Include="resampler\resample_sse.h" />
<QtMoc Include="rigcommander.h">
</QtMoc>
<QtMoc Include="rigctld.h">
</QtMoc>
<ClInclude Include="rigidentities.h" />
<QtMoc Include="rthandler.h">
</QtMoc>
<QtMoc Include="servermain.h">
</QtMoc>
<ClInclude Include="resampler\speex_resampler.h" />
<QtMoc Include="tcpserver.h">
</QtMoc>
<QtMoc Include="udpaudio.h">
</QtMoc>
<ClInclude Include="udpbase.h" />
<QtMoc Include="udpcivdata.h">
</QtMoc>
<QtMoc Include="udphandler.h">
</QtMoc>
<QtMoc Include="udpserver.h">
</QtMoc>
<ClInclude Include="ulaw.h" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
<FileType>Document</FileType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -faligned-new -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;debug\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">debug\moc_predefs.h;%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="release\moc_predefs.h.cbt">
<FileType>Document</FileType>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -fvisibility=hidden -fvisibility-inlines-hidden -faligned-new -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;release\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">release\moc_predefs.h;%(Outputs)</Outputs>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<None Include="qdarkstyle\rc\Hmovetoolbar.png" />
<None Include="qdarkstyle\rc\Hsepartoolbar.png" />
<None Include="qdarkstyle\rc\Vmovetoolbar.png" />
<None Include="qdarkstyle\rc\Vsepartoolbar.png" />
<None Include="qdarkstyle\rc\branch_closed-on.png" />
<None Include="qdarkstyle\rc\branch_closed.png" />
<None Include="qdarkstyle\rc\branch_open-on.png" />
<None Include="qdarkstyle\rc\branch_open.png" />
<None Include="qdarkstyle\rc\checkbox_checked.png" />
<None Include="qdarkstyle\rc\checkbox_checked_disabled.png" />
<None Include="qdarkstyle\rc\checkbox_checked_focus.png" />
<None Include="qdarkstyle\rc\checkbox_indeterminate.png" />
<None Include="qdarkstyle\rc\checkbox_indeterminate_focus.png" />
<None Include="qdarkstyle\rc\checkbox_unchecked.png" />
<None Include="qdarkstyle\rc\checkbox_unchecked_disabled.png" />
<None Include="qdarkstyle\rc\checkbox_unchecked_focus.png" />
<None Include="qdarkstyle\rc\close-hover.png" />
<None Include="qdarkstyle\rc\close-pressed.png" />
<None Include="qdarkstyle\rc\close.png" />
<None Include="qdarkstyle\rc\down_arrow.png" />
<None Include="qdarkstyle\rc\down_arrow_disabled.png" />
<None Include="qdarkstyle\rc\left_arrow.png" />
<None Include="qdarkstyle\rc\left_arrow_disabled.png" />
<None Include="qdarkstyle\rc\radio_checked.png" />
<None Include="qdarkstyle\rc\radio_checked_disabled.png" />
<None Include="qdarkstyle\rc\radio_checked_focus.png" />
<None Include="qdarkstyle\rc\radio_unchecked.png" />
<None Include="qdarkstyle\rc\radio_unchecked_disabled.png" />
<None Include="qdarkstyle\rc\radio_unchecked_focus.png" />
<QtRcc Include="resources\resources.qrc">
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">resources</InitFuncName><InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">resources</InitFuncName></QtRcc>
<None Include="qdarkstyle\rc\right_arrow.png" />
<None Include="qdarkstyle\rc\right_arrow_disabled.png" />
<None Include="qdarkstyle\rc\sizegrip.png" />
<QtRcc Include="qdarkstyle\style.qrc">
<InitFuncName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">style</InitFuncName><InitFuncName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">style</InitFuncName></QtRcc>
<None Include="qdarkstyle\style.qss" />
<None Include="qdarkstyle\rc\stylesheet-branch-end.png" />
<None Include="qdarkstyle\rc\stylesheet-branch-more.png" />
<None Include="qdarkstyle\rc\stylesheet-vline.png" />
<None Include="qdarkstyle\rc\transparent.png" />
<None Include="qdarkstyle\rc\undock.png" />
<None Include="qdarkstyle\rc\up_arrow.png" />
<None Include="qdarkstyle\rc\up_arrow_disabled.png" />
<None Include="resources\wfview.png" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include=".\wfserver_resource.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')"><Import Project="$(QtMsBuild)\qt.targets" /></ImportGroup>
<ImportGroup Label="ExtensionTargets" />
</Project>

Wyświetl plik

@ -0,0 +1,366 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Generated Files">
<UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier>
<Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11}</UniqueIdentifier>
<Extensions>cpp;c;cxx;moc;h;def;odl;idl;res;</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier>
<Extensions>qrc;*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E}</UniqueIdentifier>
<Extensions>qrc;*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\rtaudio\RTAudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audioconverter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audiohandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="commhandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="freqmemory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="keyboard.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="logcategories.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pahandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pttyhandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="resampler\resample.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rigcommander.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rigctld.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rigidentities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="rthandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="servermain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tcpserver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpaudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpbase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpcivdata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udphandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpserver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rtaudio\RTAUdio.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resampler\arch.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="audioconverter.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="audiohandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="audiotaper.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="commhandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="freqmemory.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="keyboard.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="logcategories.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="packettypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="pahandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="pttyhandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="repeaterattributes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resampler\resample_sse.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="rigcommander.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="rigctld.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="rigidentities.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="rthandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="servermain.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="resampler\speex_resampler.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="tcpserver.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udpaudio.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="udpbase.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="udpcivdata.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udphandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udpserver.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="ulaw.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
<Filter>Generated Files</Filter>
</CustomBuild>
<CustomBuild Include="release\moc_predefs.h.cbt">
<Filter>Generated Files</Filter>
</CustomBuild>
</ItemGroup>
<ItemGroup>
<None Include="qdarkstyle\rc\Hmovetoolbar.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\Hsepartoolbar.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\Vmovetoolbar.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\Vsepartoolbar.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\branch_closed-on.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\branch_closed.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\branch_open-on.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\branch_open.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_checked.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_checked_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_checked_focus.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_indeterminate.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_indeterminate_focus.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_unchecked.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_unchecked_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\checkbox_unchecked_focus.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\close-hover.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\close-pressed.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\close.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\down_arrow.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\down_arrow_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\left_arrow.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\left_arrow_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_checked.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_checked_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_checked_focus.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_unchecked.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_unchecked_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\radio_unchecked_focus.png">
<Filter>Resource Files</Filter>
</None>
<QtRcc Include="resources\resources.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<None Include="qdarkstyle\rc\right_arrow.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\right_arrow_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\sizegrip.png">
<Filter>Resource Files</Filter>
</None>
<QtRcc Include="qdarkstyle\style.qrc">
<Filter>Resource Files</Filter>
</QtRcc>
<None Include="qdarkstyle\style.qss">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\stylesheet-branch-end.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\stylesheet-branch-more.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\stylesheet-vline.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\transparent.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\undock.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\up_arrow.png">
<Filter>Resource Files</Filter>
</None>
<None Include="qdarkstyle\rc\up_arrow_disabled.png">
<Filter>Resource Files</Filter>
</None>
<None Include="resources\wfview.png">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="C:\Users\Phil\source\repos\wfview\wfserver_resource.rc" />
</ItemGroup>
</Project>

Wyświetl plik

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<QtLastBackgroundBuild>2022-04-13T11:33:50.3607712Z</QtLastBackgroundBuild>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<QtLastBackgroundBuild>2022-04-13T11:33:53.0745117Z</QtLastBackgroundBuild>
</PropertyGroup>
</Project>

Wyświetl plik

@ -0,0 +1,37 @@
#include <windows.h>
IDI_ICON1 ICON DISCARDABLE "C:\\Users\\Phil\\source\\repos\\wfview\\resources\\wfview.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,0,0
PRODUCTVERSION 0,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "\0"
VALUE "FileVersion", "0.0.0.0\0"
VALUE "LegalCopyright", "\0"
VALUE "OriginalFilename", "wfserver.exe\0"
VALUE "ProductName", "wfserver\0"
VALUE "ProductVersion", "0.0.0.0\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0409, 1200
END
END
/* End of Version info */

Wyświetl plik

@ -11,21 +11,42 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
TARGET = wfview
TEMPLATE = app
DEFINES += WFVIEW_VERSION=\\\"1.2d\\\"
DEFINES += WFVIEW_VERSION=\\\"1.4\\\"
DEFINES += BUILD_WFVIEW
CONFIG(debug, release|debug) {
# For Debug builds only:
QMAKE_CXXFLAGS += -faligned-new
# For Debug builds only:
QMAKE_CXXFLAGS += -faligned-new
win32:DESTDIR = wfview-release
win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -lole32
} else {
# For Release builds only:
linux:QMAKE_CXXFLAGS += -s
QMAKE_CXXFLAGS += -fvisibility=hidden
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
QMAKE_CXXFLAGS += -faligned-new
linux:QMAKE_LFLAGS += -O2 -s
# For Release builds only:
linux:QMAKE_CXXFLAGS += -s
QMAKE_CXXFLAGS += -fvisibility=hidden
QMAKE_CXXFLAGS += -fvisibility-inlines-hidden
QMAKE_CXXFLAGS += -faligned-new
linux:QMAKE_LFLAGS += -O2 -s
win32:DESTDIR = wfview-debug
win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32
}
# RTAudio defines
win32:DEFINES += __WINDOWS_WASAPI__
#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries
#linux:DEFINES += __LINUX_ALSA__
#linux:DEFINES += __LINUX_OSS__
linux:DEFINES += __LINUX_PULSE__
macx:DEFINES += __MACOSX_CORE__
!linux:SOURCES += ../rtaudio/RTAudio.cpp
!linux:HEADERS += ../rtaudio/RTAUdio.h
!linux:INCLUDEPATH += ../rtaudio
linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread
win32:INCLUDEPATH += ../portaudio/include
!win32:LIBS += -lportaudio
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
@ -35,48 +56,28 @@ DEFINES += QCUSTOMPLOT_USE_LIBRARY
# These defines are used for the resampler
equals(QT_ARCH, i386): DEFINES += USE_SSE
equals(QT_ARCH, i386): DEFINES += USE_SSE2
equals(QT_ARCH, i386): win32:DEFINES += USE_SSE
equals(QT_ARCH, i386): win32:DEFINES += USE_SSE2
equals(QT_ARCH, x86_64): DEFINES += USE_SSE
equals(QT_ARCH, x86_64): DEFINES += USE_SSE2
equals(QT_ARCH, arm): DEFINES += USE_NEON
DEFINES += OUTSIDE_SPEEX
DEFINES += RANDOM_PREFIX=wf
# These defines are used for the Eigen library
DEFINES += EIGEN_MPL2_ONLY
DEFINES += EIGEN_DONT_VECTORIZE #Clear vector flags
equals(QT_ARCH, i386): win32:DEFINES += EIGEN_VECTORIZE_SSE3
equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3
isEmpty(PREFIX) {
PREFIX = /usr/local
}
DEFINES += PREFIX=\\\"$$PREFIX\\\"
# Choose audio system, uses QTMultimedia if both are commented out.
# DEFINES += RTAUDIO
# DEFINES += PORTAUDIO
contains(DEFINES, RTAUDIO) {
# RTAudio defines
win32:DEFINES += __WINDOWS_WASAPI__
#win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries
linux:DEFINES += __LINUX_ALSA__
#linux:DEFINES += __LINUX_OSS__
#linux:DEFINES += __LINUX_PULSE__
macx:DEFINES += __MACOSX_CORE__
win32:SOURCES += ../rtaudio/RTAudio.cpp
win32:HEADERS += ../rtaudio/RTAUdio.h
!linux:INCLUDEPATH += ../rtaudio
linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread
}
contains(DEFINES, PORTAUDIO) {
CONFIG(debug, release|debug) {
win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86
} else {
win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86
}
win32:INCLUDEPATH += ../portaudio/include
!win32:LIBS += -lportaudio
}
macx:INCLUDEPATH += /usr/local/include /opt/local/include
macx:INCLUDEPATH += /usr/local/include /opt/local/include
macx:LIBS += -L/usr/local/lib -L/opt/local/lib
macx:ICON = ../wfview/resources/wfview.icns
@ -104,9 +105,9 @@ unix:target.path = $$PREFIX/bin
INSTALLS += target
# Why doesn't this seem to do anything?
DISTFILES += resources/wfview.png \
unix:DISTFILES += resources/wfview.png \
resources/install.sh
DISTFILES += resources/wfview.desktop
unix:DISTFILES += resources/wfview.desktop
unix:applications.files = resources/wfview.desktop
unix:applications.path = $$PREFIX/share/applications
@ -160,6 +161,9 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread -lopus
!linux:INCLUDEPATH += ../qcustomplot
!linux:INCLUDEPATH += ../opus/include
win32:INCLUDEPATH += ../eigen
win32:INCLUDEPATH += ../r8brain-free-src
INCLUDEPATH += resampler
SOURCES += main.cpp\
@ -168,9 +172,15 @@ SOURCES += main.cpp\
rigcommander.cpp \
freqmemory.cpp \
rigidentities.cpp \
udpbase.cpp \
udphandler.cpp \
udpcivdata.cpp \
udpaudio.cpp \
logcategories.cpp \
pahandler.cpp \
rthandler.cpp \
audiohandler.cpp \
audioconverter.cpp \
calibrationwindow.cpp \
satellitesetup.cpp \
udpserver.cpp \
@ -180,18 +190,25 @@ SOURCES += main.cpp\
resampler/resample.c \
repeatersetup.cpp \
rigctld.cpp \
ring/ring.cpp \
transceiveradjustments.cpp \
aboutbox.cpp
selectradio.cpp \
tcpserver.cpp \
aboutbox.cpp
HEADERS += wfmain.h \
commhandler.h \
rigcommander.h \
freqmemory.h \
rigidentities.h \
udpbase.h \
udphandler.h \
udpcivdata.h \
udpaudio.h \
logcategories.h \
pahandler.h \
rthandler.h \
audiohandler.h \
audioconverter.h \
calibrationwindow.h \
satellitesetup.h \
udpserver.h \
@ -206,15 +223,17 @@ HEADERS += wfmain.h \
repeaterattributes.h \
rigctld.h \
ulaw.h \
ring/ring.h \
transceiveradjustments.h \
audiotaper.h \
selectradio.h \
tcpserver.h \
aboutbox.h
FORMS += wfmain.ui \
calibrationwindow.ui \
satellitesetup.ui \
selectradio.ui \
repeatersetup.ui \
transceiveradjustments.ui \
aboutbox.ui

Wyświetl plik

@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.30804.86
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfview", "wfview.vcxproj", "{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wfserver", "wfserver.vcxproj", "{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -19,6 +21,10 @@ Global
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x64.ActiveCfg = Release|Win32
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.ActiveCfg = Release|Win32
{326108AD-FA9D-3AAF-8D3E-062C4DDC34E2}.Release|x86.Build.0 = Release|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.ActiveCfg = Debug|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Debug|x86.Build.0 = Debug|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.ActiveCfg = Release|Win32
{00E054F8-A1D4-3ECA-A8D6-DFC8A68AFD56}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Wyświetl plik

@ -20,7 +20,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<OutputDirectory>release\</OutputDirectory>
<OutputDirectory>wfview-release\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
@ -29,7 +29,7 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<OutputDirectory>debug\</OutputDirectory>
<OutputDirectory>wfview-debug\</OutputDirectory>
<ATLMinimizesCRunTimeLibraryUsage>false</ATLMinimizesCRunTimeLibraryUsage>
<CharacterSet>NotSet</CharacterSet>
<ConfigurationType>Application</ConfigurationType>
@ -44,11 +44,11 @@
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><PreLinkEventUseInBuild>true</PreLinkEventUseInBuild></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>release\</OutDir><IntDir>release\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental><PreLinkEventUseInBuild>true</PreLinkEventUseInBuild></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
<PropertyGroup Label="UserMacros" /><ImportGroup Condition="Exists('$(QtMsBuild)\qt_defaults.props')"><Import Project="$(QtMsBuild)\qt_defaults.props" /></ImportGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><OutDir>wfview-debug\</OutDir><IntDir>debug\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><OutDir>wfview-release\</OutDir><IntDir>release\</IntDir><TargetName>wfview</TargetName><IgnoreImportLibrary>true</IgnoreImportLibrary><LinkIncremental>false</LinkIncremental></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"><QtInstall>msvc2019</QtInstall><QtModules>core;network;gui;multimedia;widgets;serialport;printsupport</QtModules></PropertyGroup><ImportGroup Condition="Exists('$(QtMsBuild)\qt.props')"><Import Project="$(QtMsBuild)\qt.props" /></ImportGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>.;..\qcustomplot;..\opus\include;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;release;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>release\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
@ -57,7 +57,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>release\</ObjectFileName>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="4203b62";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="1aa45dc";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<ProgramDataBaseFileName></ProgramDataBaseFileName>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
@ -66,8 +66,8 @@
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
<Link>
<AdditionalDependencies>..\qcustomplot\win32\qcustomplot2.lib;..\opus\win32\VS2015\win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\opus\win32\VS2015\win32\Release;..\qcustomplot\win32;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>..\portaudio\msvc\Win32\Release\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Release\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Release;..\opus\win32\VS2015\Win32\Release;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>false</GenerateDebugInformation>
@ -85,16 +85,12 @@
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"4203b62\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"1aa45dc\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<PreLinkEvent>
<Command>copy /Y ..\qcustomplot\win32\qcustomplot2.dll release</Command>
<Message>copy /Y ..\qcustomplot\win32\qcustomplot2.dll release</Message>
</PreLinkEvent>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc><QtUic><ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription><QtUicDir>$(ProjectDir)</QtUicDir><QtUicFileName>ui_%(Filename).h</QtUicFileName></QtUic></ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>.;..\qcustomplot;..\opus\include;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>.;..\rtaudio;..\portaudio\include;..\qcustomplot;..\opus\include;..\eigen;..\r8brain-free-src;resampler;debug;/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>-Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>debug\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation>
@ -103,7 +99,7 @@
<ExceptionHandling>Sync</ExceptionHandling>
<ObjectFileName>debug\</ObjectFileName>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2d";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX="/usr/local";GITSHORT="4203b62";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION="1.2e";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX="/usr/local";GITSHORT="1aa45dc";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessToFile>false</PreprocessToFile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<SuppressStartupBanner>true</SuppressStartupBanner>
@ -111,8 +107,8 @@
<WarningLevel>Level3</WarningLevel>
<MultiProcessorCompilation>true</MultiProcessorCompilation></ClCompile>
<Link>
<AdditionalDependencies>..\qcustomplot\win32\qcustomplotd2.lib;..\opus\win32\VS2015\win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\opus\win32\VS2015\win32\Debug;..\qcustomplot\win32;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>..\portaudio\msvc\Win32\Debug\portaudio_x86.lib;..\opus\win32\VS2015\Win32\Debug\opus.lib;shell32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\portaudio\msvc\Win32\Debug;..\opus\win32\VS2015\Win32\Debug;C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -128,15 +124,13 @@
<WarningLevel>0</WarningLevel>
</Midl>
<ResourceCompile>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2d\";QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_USE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;PREFIX=\"/usr/local\";GITSHORT=\"4203b62\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WFVIEW_VERSION=\"1.2e\";BUILD_WFVIEW;__WINDOWS_WASAPI__;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;USE_SSE2;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;EIGEN_MPL2_ONLY;EIGEN_DONT_VECTORIZE;EIGEN_VECTORIZE_SSE3;PREFIX=\"/usr/local\";GITSHORT=\"1aa45dc\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
<PreLinkEvent>
<Command>copy /Y ..\qcustomplot\win32\qcustomplotd2.dll debug</Command>
<Message>copy /Y ..\qcustomplot\win32\qcustomplotd2.dll debug</Message>
</PreLinkEvent>
<QtMoc><CompilerFlavor>msvc</CompilerFlavor><Include>./$(Configuration)/moc_predefs.h</Include><ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription><DynamicSource>output</DynamicSource><QtMocDir>$(Configuration)</QtMocDir><QtMocFileName>moc_%(Filename).cpp</QtMocFileName></QtMoc><QtRcc><Compression>default</Compression><ExecutionDescription>Rcc'ing %(Identity)...</ExecutionDescription><QtRccDir>$(Configuration)</QtRccDir><QtRccFileName>qrc_%(Filename).cpp</QtRccFileName></QtRcc><QtUic><ExecutionDescription>Uic'ing %(Identity)...</ExecutionDescription><QtUicDir>$(ProjectDir)</QtUicDir><QtUicFileName>ui_%(Filename).h</QtUicFileName></QtUic></ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\rtaudio\RTAudio.cpp" />
<ClCompile Include="aboutbox.cpp" />
<ClCompile Include="audioconverter.cpp" />
<ClCompile Include="audiohandler.cpp" />
<ClCompile Include="calibrationwindow.cpp" />
<ClCompile Include="commhandler.cpp" />
@ -144,21 +138,29 @@
<ClCompile Include="logcategories.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="meter.cpp" />
<ClCompile Include="pahandler.cpp" />
<ClCompile Include="pttyhandler.cpp" />
<ClCompile Include="..\qcustomplot\qcustomplot.cpp" />
<ClCompile Include="qledlabel.cpp" />
<ClCompile Include="repeatersetup.cpp" />
<ClCompile Include="resampler\resample.c" />
<ClCompile Include="rigcommander.cpp" />
<ClCompile Include="rigctld.cpp" />
<ClCompile Include="rigidentities.cpp" />
<ClCompile Include="ring\ring.cpp" />
<ClCompile Include="rthandler.cpp" />
<ClCompile Include="satellitesetup.cpp" />
<ClCompile Include="selectradio.cpp" />
<ClCompile Include="tcpserver.cpp" />
<ClCompile Include="transceiveradjustments.cpp" />
<ClCompile Include="udpaudio.cpp" />
<ClCompile Include="udpbase.cpp" />
<ClCompile Include="udpcivdata.cpp" />
<ClCompile Include="udphandler.cpp" />
<ClCompile Include="udpserver.cpp" />
<ClCompile Include="wfmain.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rtaudio\RTAUdio.h" />
<QtMoc Include="aboutbox.h">
@ -170,6 +172,16 @@
</QtMoc>
<ClInclude Include="resampler\arch.h" />
<QtMoc Include="audioconverter.h">
</QtMoc>
<QtMoc Include="audiohandler.h">
@ -214,6 +226,16 @@
</QtMoc>
<ClInclude Include="packettypes.h" />
<QtMoc Include="pahandler.h">
</QtMoc>
<QtMoc Include="pttyhandler.h">
@ -223,6 +245,16 @@
</QtMoc>
<QtMoc Include="..\qcustomplot\qcustomplot.h">
</QtMoc>
<QtMoc Include="qledlabel.h">
@ -267,7 +299,16 @@
</QtMoc>
<ClInclude Include="rigidentities.h" />
<ClInclude Include="ring\ring.h" />
<QtMoc Include="rthandler.h">
</QtMoc>
<QtMoc Include="satellitesetup.h">
@ -277,8 +318,28 @@
</QtMoc>
<QtMoc Include="selectradio.h">
</QtMoc>
<ClInclude Include="resampler\speex_resampler.h" />
<QtMoc Include="tcpserver.h">
</QtMoc>
<QtMoc Include="transceiveradjustments.h">
@ -288,6 +349,27 @@
</QtMoc>
<QtMoc Include="udpaudio.h">
</QtMoc>
<ClInclude Include="udpbase.h" />
<QtMoc Include="udpcivdata.h">
</QtMoc>
<QtMoc Include="udphandler.h">
@ -332,6 +414,10 @@
<CustomBuild Include="debug\moc_predefs.h.cbt">
<FileType>Document</FileType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -368,6 +454,19 @@
@ -423,6 +522,17 @@
</QtUic>
<QtUic Include="selectradio.ui">
</QtUic>
<QtUic Include="transceiveradjustments.ui">
@ -512,11 +622,6 @@
<None Include="qdarkstyle\rc\up_arrow_disabled.png" />
<None Include="resources\wfview.png" />
</ItemGroup>
<ItemGroup>
<None Include="resources\install.sh" />
<None Include="resources\wfview.desktop" />
<None Include="resources\wfview.png" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include=".\wfview_resource.rc" />
</ItemGroup>

Wyświetl plik

@ -45,21 +45,17 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Distribution Files">
<UniqueIdentifier>{B83CAF91-C7BF-462F-B76C-EA11631F866C}</UniqueIdentifier>
<Extensions>*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
<Filter Include="Distribution Files">
<UniqueIdentifier>{B83CAF91-C7BF-462F-B76C-EA11631F866C}</UniqueIdentifier>
<Extensions>*</Extensions>
<ParseFiles>false</ParseFiles>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\rtaudio\RTAudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="aboutbox.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audioconverter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="audiohandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -81,9 +77,15 @@
<ClCompile Include="meter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pahandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pttyhandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\qcustomplot\qcustomplot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="qledlabel.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -102,15 +104,30 @@
<ClCompile Include="rigidentities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ring\ring.cpp">
<ClCompile Include="rthandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="satellitesetup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="selectradio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="tcpserver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="transceiveradjustments.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpaudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpbase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpcivdata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udphandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -122,12 +139,18 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\rtaudio\RTAUdio.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="aboutbox.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="resampler\arch.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="audioconverter.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="audiohandler.h">
<Filter>Header Files</Filter>
</QtMoc>
@ -152,9 +175,15 @@
<ClInclude Include="packettypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="pahandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="pttyhandler.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="..\qcustomplot\qcustomplot.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="qledlabel.h">
<Filter>Header Files</Filter>
</QtMoc>
@ -176,18 +205,33 @@
<ClInclude Include="rigidentities.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ring\ring.h">
<QtMoc Include="rthandler.h">
<Filter>Header Files</Filter>
</ClInclude>
</QtMoc>
<QtMoc Include="satellitesetup.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="selectradio.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="resampler\speex_resampler.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="tcpserver.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="transceiveradjustments.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udpaudio.h">
<Filter>Header Files</Filter>
</QtMoc>
<ClInclude Include="udpbase.h">
<Filter>Header Files</Filter>
</ClInclude>
<QtMoc Include="udpcivdata.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udphandler.h">
<Filter>Header Files</Filter>
</QtMoc>
@ -212,6 +256,10 @@
<CustomBuild Include="debug\moc_predefs.h.cbt">
<Filter>Generated Files</Filter>
</CustomBuild>
@ -238,6 +286,19 @@
@ -262,6 +323,9 @@
<QtUic Include="satellitesetup.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="selectradio.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="transceiveradjustments.ui">
<Filter>Form Files</Filter>
</QtUic>
@ -400,17 +464,6 @@
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<None Include="resources\install.sh">
<Filter>Distribution Files</Filter>
</None>
<None Include="resources\wfview.desktop">
<Filter>Distribution Files</Filter>
</None>
<None Include="resources\wfview.png">
<Filter>Distribution Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="C:\Users\Phil\source\repos\wfview\wfview_resource.rc" />
</ItemGroup>

Wyświetl plik

@ -7,9 +7,9 @@
<LocalDebuggerEnvironment>PATH=$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(QTDIR)\bin%3bC:\QT\5.15.2\MSVC2019\bin%3b$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<QtLastBackgroundBuild>2021-11-22T18:24:33.3752914Z</QtLastBackgroundBuild>
<QtLastBackgroundBuild>2022-04-18T13:23:03.5252168Z</QtLastBackgroundBuild>
</PropertyGroup>
<PropertyGroup Label="QtSettings" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<QtLastBackgroundBuild>2021-11-22T18:24:41.6960953Z</QtLastBackgroundBuild>
<QtLastBackgroundBuild>2022-04-18T13:23:05.0598803Z</QtLastBackgroundBuild>
</PropertyGroup>
</Project>

BIN
wfview_resource.aps 100644

Plik binarny nie jest wyświetlany.