kopia lustrzana https://gitlab.com/eliggett/wfview
Staged merge of master
commit
4574e2b7b2
|
@ -3,4 +3,8 @@
|
|||
.qmake.stash
|
||||
debug
|
||||
release
|
||||
wfview-debug
|
||||
wfserver-debug
|
||||
wfview-release
|
||||
wfserver-release
|
||||
ui_*
|
58
CHANGELOG
58
CHANGELOG
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
11
INSTALL.md
11
INSTALL.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
~~~
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
47
WHATSNEW
47
WHATSNEW
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
1102
audiohandler.cpp
1102
audiohandler.cpp
Plik diff jest za duży
Load Diff
213
audiohandler.h
213
audiohandler.h
|
@ -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
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Load the calibration data fromthe indicated slot in the preference file. </p></body></html></string>
|
||||
<string><html><head/><body><p>Load the calibration data from the indicated slot in the preference file. </p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load</string>
|
||||
|
|
137
commhandler.cpp
137
commhandler.cpp
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "freqmemory.h"
|
||||
#include "logcategories.h"
|
||||
|
||||
// Copytight 2017-2020 Elliott H. Liggett
|
||||
// Copyright 2017-2020 Elliott H. Liggett
|
||||
|
||||
freqMemory::freqMemory()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QObject>
|
||||
#include <QThread>
|
||||
class keyboard : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
keyboard(void);
|
||||
~keyboard(void);
|
||||
void run();
|
||||
};
|
|
@ -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")
|
||||
|
|
|
@ -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
104
main.cpp
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
202
packettypes.h
202
packettypes.h
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
134
rigcommander.cpp
134
rigcommander.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
21
ring/LICENSE
21
ring/LICENSE
|
@ -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.
|
|
@ -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.
|
290
ring/ring.cpp
290
ring/ring.cpp
|
@ -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
|
||||
}
|
440
ring/ring.h
440
ring/ring.h
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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!";
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 -----";
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
1362
udphandler.cpp
1362
udphandler.cpp
Plik diff jest za duży
Load Diff
207
udphandler.h
207
udphandler.h
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
1111
udpserver.cpp
1111
udpserver.cpp
Plik diff jest za duży
Load Diff
73
udpserver.h
73
udpserver.h
|
@ -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
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Contol Port</string>
|
||||
<string>Control Port</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
1184
wfmain.cpp
1184
wfmain.cpp
Plik diff jest za duży
Load Diff
79
wfmain.h
79
wfmain.h
|
@ -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
353
wfmain.ui
|
@ -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><html><head/><body><p>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. </p><p><br/></p><p>The currently-selected edge slot will be overriden.</p></body></html></string>
|
||||
<string><html><head/><body><p>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. </p><p><br/></p><p>The currently-selected edge slot will be overridden.</p></body></html></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><html><head/><body><p>Enter the address in as hexidecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p>
|
||||
<string><html><head/><body><p>Enter the address in as hexadecimal, without any prefix, just as the radio presents the address in the menu. </p><p>Here are some common examples:</p>
|
||||
<p>IC-706: 58
|
||||
<br/>IC-756: 50
|
||||
<br/>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>
|
||||
|
|
|
@ -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
|
|
@ -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>NUL >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>NUL >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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 */
|
||||
|
113
wfview.pro
113
wfview.pro
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
161
wfview.vcxproj
161
wfview.vcxproj
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
Plik binarny nie jest wyświetlany.
Ładowanie…
Reference in New Issue