.
wfview-1.2d/README.md 0000664 0000000 0000000 00000013736 14151646264 0014331 0 ustar 00root root 0000000 0000000 # wfview
[wfview](https://gitlab.com/eliggett/wfview) is an open-source front-end application for the
- [Icom IC-705 ](https://www.icomamerica.com/en/products/amateur/hf/705/default.aspx) HF portable SDR Amateur Radio
- [Icom IC-7300](https://www.icomamerica.com/en/products/amateur/hf/7300/default.aspx) HF SDR Amateur Radio
- [Icom IC-7610](https://www.icomamerica.com/en/products/amateur/hf/7610/default.aspx) HF SDR Amateur Radio
- [Icom IC-7850](https://www.icomamerica.com/en/products/amateur/hf/7850/default.aspx) HF Hybrid SDR Amateur Radio
- [Icom IC-7851](https://www.icomamerica.com/en/products/amateur/hf/7851/default.aspx) HF Hybrid SDR Amateur Radio
- [Icom IC-9700](https://www.icomamerica.com/en/products/amateur/hf/9700/default.aspx) VHF/UHF SDR Amateur Radio
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 is unique in the radio control ecosystem in that it is free and open-source software and can take advantage of modern radio features (such as the waterfall). wfview also does not "eat the serial port", and can allow a second program, such as fldigi, access to the radio via a pseudo-terminal device.
**For screenshots, documentation, User FAQ, Programmer FAQ, and more, please [see the project's wiki](https://gitlab.com/eliggett/wfview/-/wikis/home).**
wfview is copyright 2017-2020 Elliott H. Liggett. All rights reserved. wfview source code is licensed via the GNU GPLv3.
### Features:
1. Plot bandscope and bandscope waterfall. Optionally, also plot a "peak hold". A splitter lets the user adjust the space used for the waterfall and bandscope plots.
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.
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.
### Build Requirements:
1. gcc / g++ / make
2. qmake
3. qt5 (proably the package named "qt5-default")
4. libqt5serialport5-dev
5. libqcustomplot-dev
### Recommended:
* Debian-based Linux system (Debian Linux, Linux Mint, Ubuntu, etc) or opensuse 15.x. Any recent Linux system will do though!
* QT Creator for building, designing, and debugging w/gdb
### Build directions:
See [INSTALL.md](https://gitlab.com/eliggett/wfview/-/blob/master/INSTALL.md) for directions.
### Rig setting:
1. CI-V Baud rate: Auto
2. CI-V address: 94h (default)
3. CI-V Transceive ON
4. CI-V USB-> REMOTE Transceive Address: 00h
5. CI-V Output (for ANT): OFF
6. CI-V USB Port: Unlink from REMOTE
7. CI-V USB Baud Rate: 15200
8. CI-V USB Echo Back: OFF
9. Turn on the bandscope on the rig screen
* Note: The program currently assumes the radio is on a device like this:
~~~
/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_IC-7300_02010092-if00-port0
~~~
This is symlinked to a device like /dev/ttyUSB0 typically. Make sure the port is writable by your username. You can accomplish this using udev rules, or if you are in a hurry:
~~~
sudo chown `whoami` /dev/ttyUSB*
~~~
### TODO (for developers and contributors):
1. Re-work pseudo term code into separate thread
2. Consider XML RPC to make flrig/fldigi interface easier
3. Add hide/show for additional controls: SWR, ALC, Power, S-Meter interface
4. Fix crash on close (order of delete operations is important)
5. Add support for other compatible CI-V radios (IC-706, IC-7100, IC-7610, etc)
6. Better settings panel (select serial port, CI-V address, more obvious exit button)
7. Add support for festival or other text-to-speech method using the computer (as apposed to the radio's speech module)
see also the wiki:
- [bugs](https://gitlab.com/eliggett/wfview/-/wikis/Bugs)
- [feature requests](https://gitlab.com/eliggett/wfview/-/wikis/Feature-requests)
- [raspberry pi server](https://gitlab.com/eliggett/wfview/-/wikis/raspi-server-functionality-for-7300,7100-etc)
### THIRD PARTY code/hardware:
the following projects we would like to thank in alphabetical order:
- ICOM for their well designed rigs
see ICOM Japan (https://www.icomjapan.com/)
- ICOM for their well written RS-BA1 software
see ICOM JAPAN products page (https://www.icomjapan.com/lineup/options/RS-BA1_Version2/)
- kappanhang which inspired us to enhance the original wfview project:
Akos Marton ES1AKOS
Elliot LiggettW6EL (passcode algorithm)
Norbert Varga HA2NON nonoo@nonoo.hu
see for their fine s/w here [kappanhang](https://github.com/nonoo/kappanhang)
- resampling code from the opus project:
[Xiph.Org Foundation] (https://xiph.org/)
see [sources] (https://github.com/xiph/opus/tree/master/silk)
- QCP: the marvellous qt custom plot code
Emanuel Eichhammer
see [QCP] (https://www.qcustomplot.com/)
If you feel that we forgot ayone, just drop a mail.
wfview-1.2d/USERMANUAL/ 0000775 0000000 0000000 00000000000 14151646264 0014514 5 ustar 00root root 0000000 0000000 wfview-1.2d/USERMANUAL/BAND 0000664 0000000 0000000 00000000630 14151646264 0015142 0 ustar 00root root 0000000 0000000 The Band tab:
The band tab reflects what bands the rig supports. It directly will
switch the rig to that band and you will be switched back to the View tab
the bands offered follow generally what the rig's capabilities are.
Some bands will either be RX only for some people (4m/60m/630m/2200m)
Band stacking gives you the opportunity to use the band stacking registers
which is filled by the rig itself.
wfview-1.2d/USERMANUAL/README 0000664 0000000 0000000 00000000551 14151646264 0015375 0 ustar 00root root 0000000 0000000 I started to describe in text what all the controls are and what they
actually are supposed to do. This is a very rough start of UI documentation
and will beautify that of course. Initially want just to be sure that the
definitions are right and everyone can edit/push it. So feel free.
Note that the markers that certainly are incomplete are marked TODO.
wfview-1.2d/USERMANUAL/wfview-1.0.txt 0000664 0000000 0000000 00000006500 14151646264 0017061 0 ustar 00root root 0000000 0000000
View:
buttons
spectrum scope/waterfall:
spectrum mode
depending on the rig you can select:
center
fixed
scroll-center
scroll-fixed
spectrum span
the spectrum span is active in center modes and you can select
here the span you like, just as on the rig.
spectrum edge
here you can select the edge number as programmed in the rig.
Most rigs will accept four edges.
tofixed
clear peaks
here you can clear the peaks. Currently we only support persistent
peaks and at some point we are changing that to be able to have it
go away after 10 seconds, like the rig does.
enable/disable wf
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
RGB values like the rig does.
mode
here you can select the mode used for tx and rx. To enable data mode there is
a separate button to select.
Data mode
switch the D mode on/off on the selected mode.
receive (and tx) filter
Select the predefined filter settings of the rig.
Rigs also will change the TX width accordingly.
transmit/receive
this button alternates between TX and RX; Note that to
enable TX, you need to do that first on the Settings tab
enable/disable atu
if your rig supports an internal ATU, you can enable
disable it here. Note that we have not tested external
ATU's yet. It may follow, it may not...
enable/disable rit
Enable the RIT function; currently no feedback on the rx shift.
tune
Button to initiate the ATU. Note that we have not tested external
ATU's yet. It may follow, it may not...
repeater setup (expand)
TODO
preamp
You can select the preamp mode(s). Some rigs can only accept
preamp OR att.
attenuator
you can select the rig attenuator here. Some rigs can only accept
preamp OR att.
antenna selection
TODO
controls
main dial (there is no sub dial yet)
by turning the dial, or using mouse wheel or clicking on the scope
you can change the current frequency. The step size is below the
main dial and you can select most known stepsizes there.
An Flock button will effectively lock the freq to prevent accidental
mis clicking/rotating mouse actions.
rit dial
the rit dial will effectively modify the offset in RX; useful on the VHF
bands and up. Note that you need to switch on RIT for that
You can use the mouse wheel to change the offset/shift.
rf gain
This slider controls the RF gain of the rig
af gain (defaults to 100%
This controls the AF Gain locally, defaults to 100% and does not
increase/decrease the AF gain on the rig itself.
(Else, a remotely controlled rig could make a lot of noise ;-))
sq
The squelch control
tx power
Control that sets the power in % with the same accuracy as the rig
so if your rig is 50 Watt at 47%, this slider will too.
mic gain
The mic gain slider sets the modulation level on the rig.
scope reference level
The scope reference level can be set here and TODO because
need to check if this follows per band or not.
=============================
Frequency
On this tab you can insert a free frequency in kHz.
examples:
7100 --> 7.1 kHz
430. --> 430 MHz
e.g. the dot itself defines currently that you specify MHz.
It will not accept anything beyond the dot. E.g. 430.125
will end up doing nothing.
After entering, you will be switched back to the View tab.
STO/RCL: TODO
Settings
wfview-1.2d/WHATSNEW 0000664 0000000 0000000 00000001312 14151646264 0014220 0 ustar 00root root 0000000 0000000
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
wfview-1.2d/aboutbox.cpp 0000664 0000000 0000000 00000013527 14151646264 0015377 0 ustar 00root root 0000000 0000000 #include "aboutbox.h"
#include "ui_aboutbox.h"
aboutbox::aboutbox(QWidget *parent) :
QWidget(parent),
ui(new Ui::aboutbox)
{
ui->setupUi(this);
setWindowTitle("About wfview");
setWindowIcon(QIcon(":resources/wfview.png"));
ui->logoBtn->setIcon(QIcon(":resources/wfview.png"));
ui->logoBtn->setStyleSheet("Text-align:left");
ui->topText->setText("wfview version " + QString(WFVIEW_VERSION));
QString head = QString("");
QString copyright = QString("Copyright 2017-2021 Elliott H. Liggett, W6EL. All rights reserved. wfview source code is licensed under the GNU GPLv3.");
QString nacode = QString("
Networking, audio, rigctl server, and much more written by Phil Taylor, M0VSE");
QString doctest = QString("
Testing, documentation, bug fixes, and development mentorship from
Roeland Jansen, PA3MET, and Jim Nijkamp, PA8E.");
QString ssCredit = QString("
Stylesheet qdarkstyle used under MIT license, stored in /usr/share/wfview/stylesheets/.");
QString website = QString("
Please visit https://wfview.org/ for the latest information.");
QString docs = QString("
Be sure to check the User Manual and the Forum if you have any questions.");
QString support = QString("
For support, please visit the official wfview support forum.");
QString gitcodelink = QString("").arg(GITSHORT);
QString contact = QString("
email W6EL: kilocharlie8@gmail.com");
QString buildInfo = QString("
Build " + gitcodelink + QString(GITSHORT) + " on " + QString(__DATE__) + " at " + __TIME__ + " by " + UNAME + "@" + HOST);
QString end = QString("");
// Short credit strings:
QString rsCredit = QString("
Speex Resample library Copyright 2003-2008 Jean-Marc Valin");
#if defined(RTAUDIO)
QString rtaudiocredit = QString("
RT Audio, from Gary P. Scavone");
#endif
#if defined(PORTAUDIO)
QString portaudiocredit = QString("
Port Audio, from The Port Audio Community");
#endif
QString qcpcredit = QString("
The waterfall and spectrum plot graphics use QCustomPlot, from Emanuel Eichhammer");
QString qtcredit = QString("
This copy of wfview was built against Qt version %1").arg(QT_VERSION_STR);
// Acknowledgement:
QString wfviewcommunityack = QString("
The developers of wfview wish to thank the many contributions from the wfview community at-large, including ideas, bug reports, and fixes.");
QString kappanhangack = QString("
Special thanks to Norbert Varga, and the nonoo/kappanhang team for their initial work on the OEM protocol.");
QString sxcreditcopyright = QString("Speex copyright notice: \
Copyright (C) 2003 Jean-Marc Valin\n\
Redistribution and use in source and binary forms, with or without\n\
modification, are permitted provided that the following conditions\n\
are met:\n\
- Redistributions of source code must retain the above copyright\n\
notice, this list of conditions and the following disclaimer.\n\
- Redistributions in binary form must reproduce the above copyright\n\
notice, this list of conditions and the following disclaimer in the\n\
documentation and/or other materials provided with the distribution.\n\
- Neither the name of the Xiph.org Foundation nor the names of its\n\
contributors may be used to endorse or promote products derived from\n\
this software without specific prior written permission.\n\
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n\
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n\
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR\n\
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n\
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n\
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n\
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n\
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n\
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n\
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.");
// String it all together:
QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + wfviewcommunityack;
aboutText.append(website + "\n"+ docs + support + contact +"\n");
aboutText.append("\n" + ssCredit + "\n" + rsCredit + "\n");
#if defined(RTAUDIO)
aboutText.append(rtaudiocredit);
#endif
#if defined(PORTAUDIO)
aboutText.append(portaudiocredit);
#endif
aboutText.append(kappanhangack + qcpcredit + qtcredit);
aboutText.append("
");
aboutText.append("" + sxcreditcopyright + "
");
aboutText.append("
");
aboutText.append(end);
ui->midTextBox->setText(aboutText);
ui->bottomText->setText(buildInfo);
ui->midTextBox->setFocus();
}
aboutbox::~aboutbox()
{
delete ui;
}
void aboutbox::on_logoBtn_clicked()
{
QDesktopServices::openUrl(QUrl("https://www.wfview.org/"));
}
wfview-1.2d/aboutbox.h 0000664 0000000 0000000 00000000526 14151646264 0015037 0 ustar 00root root 0000000 0000000 #ifndef ABOUTBOX_H
#define ABOUTBOX_H
#include
#include
namespace Ui {
class aboutbox;
}
class aboutbox : public QWidget
{
Q_OBJECT
public:
explicit aboutbox(QWidget *parent = 0);
~aboutbox();
private slots:
void on_logoBtn_clicked();
private:
Ui::aboutbox *ui;
};
#endif // ABOUTBOX_H
wfview-1.2d/aboutbox.ui 0000664 0000000 0000000 00000004251 14151646264 0015224 0 ustar 00root root 0000000 0000000
aboutbox
0
0
700
567
Form
-
0
128
0
0
128
128
true
-
wfview version
-
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;">
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Detailed text here</p></body></html>
true
-
Build String
wfview-1.2d/audiohandler.cpp 0000664 0000000 0000000 00000067623 14151646264 0016221 0 ustar 00root root 0000000 0000000 /*
This class handles both RX and TX audio, each is created as a seperate instance of the class
but as the setup/handling if output (RX) and input (TX) devices is so similar I have combined them.
*/
#include "audiohandler.h"
#include "logcategories.h"
#include "ulaw.h"
#if defined(Q_OS_WIN) && defined(PORTAUDIO)
#include
#endif
audioHandler::audioHandler(QObject* parent)
{
Q_UNUSED(parent)
}
audioHandler::~audioHandler()
{
if (isInitialized) {
#if defined(RTAUDIO)
try {
audio->abortStream();
audio->closeStream();
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
}
delete audio;
#elif defined(PORTAUDIO)
Pa_StopStream(audio);
Pa_CloseStream(audio);
#else
stop();
#endif
}
if (ringBuf != Q_NULLPTR) {
delete ringBuf;
}
if (resampler != Q_NULLPTR) {
speex_resampler_destroy(resampler);
qDebug(logAudio()) << "Resampler closed";
}
if (encoder != Q_NULLPTR) {
qInfo(logAudio()) << "Destroying opus encoder";
opus_encoder_destroy(encoder);
}
if (decoder != Q_NULLPTR) {
qInfo(logAudio()) << "Destroying opus decoder";
opus_decoder_destroy(decoder);
}
}
bool audioHandler::init(audioSetup setupIn)
{
if (isInitialized) {
return false;
}
/*
0x01 uLaw 1ch 8bit
0x02 PCM 1ch 8bit
0x04 PCM 1ch 16bit
0x08 PCM 2ch 8bit
0x10 PCM 2ch 16bit
0x20 uLaw 2ch 8bit
*/
setup = setupIn;
setup.radioChan = 1;
setup.bits = 8;
if (setup.codec == 0x01 || setup.codec == 0x20) {
setup.ulaw = true;
}
if (setup.codec == 0x08 || setup.codec == 0x10 || setup.codec == 0x20 || setup.codec == 0x80) {
setup.radioChan = 2;
}
if (setup.codec == 0x04 || setup.codec == 0x10 || setup.codec == 0x40 || setup.codec == 0x80) {
setup.bits = 16;
}
ringBuf = new wilt::Ring(setupIn.latency / 8 + 1); // Should be customizable.
tempBuf.sent = 0;
if(!setup.isinput)
{
this->setVolume(setup.localAFgain);
}
#if defined(RTAUDIO)
#if !defined(Q_OS_MACX)
options.flags = ((!RTAUDIO_HOG_DEVICE) | (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
if (setup.port > 0) {
aParams.deviceId = setup.port;
}
else if (setup.isinput) {
aParams.deviceId = audio->getDefaultInputDevice();
}
else {
aParams.deviceId = audio->getDefaultOutputDevice();
}
aParams.firstChannel = 0;
try {
info = audio->getDeviceInfo(aParams.deviceId);
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Device error:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage());
return isInitialized;
}
if (info.probed)
{
// Always use the "preferred" sample rate
// We can always resample if needed
this->nativeSampleRate = info.preferredSampleRate;
// Per channel chunk size.
this->chunkSize = (this->nativeSampleRate / 50);
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed";
if (info.nativeFormats == 0)
{
qInfo(logAudio()) << " No natively supported data formats!";
return false;
}
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) {
devChannels = info.inputChannels;
}
else {
devChannels = info.outputChannels;
}
qInfo(logAudio()) << " Channels:" << devChannels;
if (devChannels > 2) {
devChannels = 2;
}
aParams.nChannels = devChannels;
}
qInfo(logAudio()) << " chunkSize: " << chunkSize;
try {
if (setup.isinput) {
audio->openStream(NULL, &aParams, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticWrite, this, &options);
}
else {
audio->openStream(&aParams, NULL, RTAUDIO_SINT16, this->nativeSampleRate, &this->chunkSize, &staticRead, this, &options);
}
audio->startStream();
isInitialized = true;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened";
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency();
}
catch (RtAudioError& e) {
qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage());
}
}
else
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") could not be probed, check audio configuration!";
}
#elif defined(PORTAUDIO)
PaError err;
#ifdef Q_OS_WIN
CoInitialize(0);
#endif
memset(&aParams, 0,sizeof(PaStreamParameters));
if (setup.port > 0) {
aParams.device = setup.port;
}
else if (setup.isinput) {
aParams.device = Pa_GetDefaultInputDevice();
}
else {
aParams.device = Pa_GetDefaultOutputDevice();
}
info = Pa_GetDeviceInfo(aParams.device);
aParams.channelCount = 2;
aParams.hostApiSpecificStreamInfo = NULL;
aParams.sampleFormat = paInt16;
if (setup.isinput) {
aParams.suggestedLatency = info->defaultLowInputLatency;
}
else {
aParams.suggestedLatency = info->defaultLowOutputLatency;
}
aParams.hostApiSpecificStreamInfo = NULL;
// Always use the "preferred" sample rate (unless it is 44100)
// We can always resample if needed
if (info->defaultSampleRate == 44100) {
this->nativeSampleRate = 48000;
}
else {
this->nativeSampleRate = info->defaultSampleRate;
}
// Per channel chunk size.
this->chunkSize = (this->nativeSampleRate / 50);
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << info->name << "(" << aParams.device << ") successfully probed";
if (setup.isinput) {
devChannels = info->maxInputChannels;
}
else {
devChannels = info->maxOutputChannels;
}
if (devChannels > 2) {
devChannels = 2;
}
aParams.channelCount = devChannels;
qInfo(logAudio()) << " Channels:" << devChannels;
qInfo(logAudio()) << " chunkSize: " << chunkSize;
qInfo(logAudio()) << " sampleRate: " << nativeSampleRate;
if (setup.isinput) {
err=Pa_OpenStream(&audio, &aParams, 0, this->nativeSampleRate, this->chunkSize, paNoFlag, &audioHandler::staticWrite, (void*)this);
}
else {
err=Pa_OpenStream(&audio, 0, &aParams, this->nativeSampleRate, this->chunkSize, paNoFlag, &audioHandler::staticRead, (void*)this);
}
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);
}
#else
format.setSampleSize(16);
format.setChannelCount(2);
format.setSampleRate(INTERNAL_SAMPLE_RATE);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
if (setup.port.isNull())
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
return false;
}
else if (!setup.port.isFormatSupported(format))
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Format not supported, choosing nearest supported format - which may not work!";
format=setup.port.nearestFormat(format);
}
if (format.channelCount() > 2) {
format.setChannelCount(2);
}
else if (format.channelCount() < 1)
{
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No channels found, aborting setup.";
return false;
}
devChannels = format.channelCount();
nativeSampleRate = format.sampleRate();
// chunk size is always relative to Internal Sample Rate.
this->chunkSize = (nativeSampleRate / 50);
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Internal: sample rate" << format.sampleRate() << "channel count" << format.channelCount();
// We "hopefully" now have a valid format that is supported so try connecting
if (setup.isinput) {
audioInput = new QAudioInput(setup.port, format, this);
connect(audioInput, SIGNAL(notify()), SLOT(notified()));
connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
isInitialized = true;
}
else {
audioOutput = new QAudioOutput(setup.port, format, this);
#ifdef Q_OS_MAC
audioOutput->setBufferSize(chunkSize*4);
#endif
connect(audioOutput, SIGNAL(notify()), SLOT(notified()));
connect(audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
isInitialized = true;
}
#endif
// Setup resampler and opus if they are needed.
int resample_error = 0;
int opus_err = 0;
if (setup.isinput) {
resampler = wf_resampler_init(devChannels, nativeSampleRate, setup.samplerate, setup.resampleQuality, &resample_error);
if (setup.codec == 0x40 || setup.codec == 0x80) {
// Opus codec
encoder = opus_encoder_create(setup.samplerate, setup.radioChan, OPUS_APPLICATION_AUDIO, &opus_err);
opus_encoder_ctl(encoder, OPUS_SET_LSB_DEPTH(16));
opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1));
opus_encoder_ctl(encoder, OPUS_SET_DTX(1));
opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(5));
qInfo(logAudio()) << "Creating opus encoder: " << opus_strerror(opus_err);
}
}
else {
resampler = wf_resampler_init(devChannels, setup.samplerate, this->nativeSampleRate, setup.resampleQuality, &resample_error);
if (setup.codec == 0x40 || setup.codec == 0x80) {
// Opus codec
decoder = opus_decoder_create(setup.samplerate, setup.radioChan, &opus_err);
qInfo(logAudio()) << "Creating opus decoder: " << opus_strerror(opus_err);
}
}
unsigned int ratioNum;
unsigned int ratioDen;
wf_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
resampleRatio = static_cast(ratioDen) / ratioNum;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "wf_resampler_init() returned: " << resample_error << " resampleRatio: " << resampleRatio;
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "thread id" << QThread::currentThreadId();
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
if (isInitialized) {
this->start();
}
#endif
return isInitialized;
}
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
void audioHandler::start()
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "start() running";
if ((audioOutput == Q_NULLPTR || audioOutput->state() != QAudio::StoppedState) &&
(audioInput == Q_NULLPTR || audioInput->state() != QAudio::StoppedState)) {
return;
}
if (setup.isinput) {
#ifndef Q_OS_WIN
this->open(QIODevice::WriteOnly);
#else
this->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
#endif
audioInput->start(this);
}
else {
#ifndef Q_OS_WIN
this->open(QIODevice::ReadOnly);
#else
this->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
#endif
audioOutput->start(this);
}
}
#endif
void audioHandler::setVolume(unsigned char volume)
{
//this->volume = (qreal)volume/255.0;
this->volume = audiopot[volume];
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
}
///
/// This function processes the incoming audio FROM the radio and pushes it into the playback buffer *data
///
///
///
///
#if defined(RTAUDIO)
int audioHandler::readData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(inputBuffer);
Q_UNUSED(streamTime);
if (status == RTAUDIO_OUTPUT_UNDERFLOW)
qDebug(logAudio()) << "Underflow detected";
int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels
quint8* buffer = (quint8*)outputBuffer;
#elif defined(PORTAUDIO)
int audioHandler::readData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime, PaStreamCallbackFlags status)
{
Q_UNUSED(inputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels
quint8* buffer = (quint8*)outputBuffer;
#else
qint64 audioHandler::readData(char* buffer, qint64 nBytes)
{
#endif
// Calculate output length, always full samples
int sentlen = 0;
if (!isReady) {
isReady = true;
}
if (ringBuf->size()>0)
{
// Output buffer is ALWAYS 16 bit.
//qDebug(logAudio()) << "Read: nFrames" << nFrames << "nBytes" << nBytes;
while (sentlen < nBytes)
{
audioPacket packet;
if (!ringBuf->try_read(packet))
{
qDebug(logAudio()) << "No more data available but buffer is not full! sentlen:" << sentlen << " nBytes:" << nBytes ;
break;
}
currentLatency = packet.time.msecsTo(QTime::currentTime());
// This shouldn't be required but if we did output a partial packet
// This will add the remaining packet data to the output buffer.
if (tempBuf.sent != tempBuf.data.length())
{
int send = qMin((int)nBytes - sentlen, tempBuf.data.length() - tempBuf.sent);
memcpy(buffer + sentlen, tempBuf.data.constData() + tempBuf.sent, send);
tempBuf.sent = tempBuf.sent + send;
sentlen = sentlen + send;
if (tempBuf.sent != tempBuf.data.length())
{
// We still don't have enough buffer space for this?
break;
}
//qDebug(logAudio()) << "Adding partial:" << send;
}
if (currentLatency > setup.latency) {
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq <<
" arrived too late (increase output latency!) " <<
dec << packet.time.msecsTo(QTime::currentTime()) << "ms";
while (currentLatency > setup.latency/2) {
if (!ringBuf->try_read(packet)) {
break;
}
currentLatency = packet.time.msecsTo(QTime::currentTime());
}
}
int send = qMin((int)nBytes - sentlen, packet.data.length());
memcpy(buffer + sentlen, packet.data.constData(), send);
sentlen = sentlen + send;
if (send < packet.data.length())
{
//qDebug(logAudio()) << "Asking for partial, sent:" << send << "packet length" << packet.data.length();
tempBuf = packet;
tempBuf.sent = tempBuf.sent + send;
lastSeq = packet.seq;
break;
}
/*
if (packet.seq <= lastSeq) {
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Duplicate/early audio packet: " << hex << lastSeq << " got " << hex << packet.seq;
}
else if (packet.seq != lastSeq + 1) {
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Missing audio packet(s) from: " << hex << lastSeq + 1 << " to " << hex << packet.seq - 1;
}
*/
lastSeq = packet.seq;
}
}
//qDebug(logAudio()) << "looking for: " << nBytes << " got: " << sentlen;
// fill the rest of the buffer with silence
if (nBytes > sentlen) {
memset(buffer+sentlen,0,nBytes-sentlen);
}
#if defined(RTAUDIO)
return 0;
#elif defined(PORTAUDIO)
return 0;
#else
return nBytes;
#endif
}
#if defined(RTAUDIO)
int audioHandler::writeData(void* outputBuffer, void* inputBuffer,
unsigned int nFrames, double streamTime, RtAudioStreamStatus status)
{
Q_UNUSED(outputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels
const char* data = (const char*)inputBuffer;
#elif defined(PORTAUDIO)
int audioHandler::writeData(const void* inputBuffer, void* outputBuffer,
unsigned long nFrames, const PaStreamCallbackTimeInfo * streamTime,
PaStreamCallbackFlags status)
{
Q_UNUSED(outputBuffer);
Q_UNUSED(streamTime);
Q_UNUSED(status);
int nBytes = nFrames * devChannels * 2; // This is ALWAYS 2 bytes per sample and 2 channels
const char* data = (const char*)inputBuffer;
#else
qint64 audioHandler::writeData(const char* data, qint64 nBytes)
{
#endif
if (!isReady) {
isReady = true;
}
int sentlen = 0;
//qDebug(logAudio()) << "nFrames" << nFrames << "nBytes" << nBytes;
int chunkBytes = chunkSize * devChannels * 2;
while (sentlen < nBytes) {
if (tempBuf.sent != chunkBytes)
{
int send = qMin((int)(nBytes - sentlen), chunkBytes - tempBuf.sent);
tempBuf.data.append(QByteArray::fromRawData(data + sentlen, send));
sentlen = sentlen + send;
tempBuf.seq = 0; // Not used in TX
tempBuf.time = QTime::currentTime();
tempBuf.sent = tempBuf.sent + send;
}
else {
//ringBuf->write(tempBuf);
if (!ringBuf->try_write(tempBuf))
{
qDebug(logAudio()) << "outgoing audio buffer full!";
break;
}
tempBuf.data.clear();
tempBuf.sent = 0;
}
}
//qDebug(logAudio()) << "sentlen" << sentlen;
#if defined(RTAUDIO)
return 0;
#elif defined(PORTAUDIO)
return 0;
#else
return nBytes;
#endif
}
void audioHandler::incomingAudio(audioPacket inPacket)
{
// No point buffering audio until stream is actually running.
// Regardless of the radio stream format, the buffered audio will ALWAYS be
// 16bit sample interleaved stereo 48K (or whatever the native sample rate is)
if (!isInitialized && !isReady)
{
qDebug(logAudio()) << "Packet received when stream was not ready";
return;
}
if (setup.codec == 0x40 || setup.codec == 0x80) {
unsigned char* in = (unsigned char*)inPacket.data.data();
/* Decode the frame. */
QByteArray outPacket((setup.samplerate / 50) * sizeof(qint16) * setup.radioChan, (char)0xff); // Preset the output buffer size.
qint16* out = (qint16*)outPacket.data();
int nSamples = opus_packet_get_nb_samples(in, inPacket.data.size(),setup.samplerate);
if (nSamples != setup.samplerate / 50)
{
qInfo(logAudio()) << "Opus nSamples=" << nSamples << " expected:" << (setup.samplerate / 50);
return;
}
nSamples = opus_decode(decoder, in, inPacket.data.size(), out, (setup.samplerate / 50), 0);
if (nSamples < 0)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decode failed:" << opus_strerror(nSamples) << "packet size" << inPacket.data.length();
return;
}
else {
if (int(nSamples * sizeof(qint16) * setup.radioChan) != outPacket.size())
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoder mismatch: nBytes:" << nSamples * sizeof(qint16) * setup.radioChan << "outPacket:" << outPacket.size();
outPacket.resize(nSamples * sizeof(qint16) * setup.radioChan);
}
//qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus decoded" << inPacket.data.size() << "bytes, into" << outPacket.length() << "bytes";
inPacket.data.clear();
inPacket.data = outPacket; // Replace incoming data with converted.
}
}
//qDebug(logAudio()) << "Got" << setup.bits << "bits, length" << inPacket.data.length();
// Incoming data is 8bits?
if (setup.bits == 8)
{
// Current packet is 8bit so need to create a new buffer that is 16bit
QByteArray outPacket((int)inPacket.data.length() * 2 * (devChannels / setup.radioChan), (char)0xff);
qint16* out = (qint16*)outPacket.data();
for (int f = 0; f < inPacket.data.length(); f++)
{
int samp = (quint8)inPacket.data[f];
for (int g = setup.radioChan; g <= devChannels; g++)
{
if (setup.ulaw)
*out++ = ulaw_decode[samp] * this->volume;
else
*out++ = (qint16)((samp - 128) << 8) * this->volume;
}
}
inPacket.data.clear();
inPacket.data = outPacket; // Replace incoming data with converted.
}
else
{
// This is already a 16bit stream, do we need to convert to stereo?
if (setup.radioChan == 1 && devChannels > 1) {
// Yes
QByteArray outPacket(inPacket.data.length() * 2, (char)0xff); // Preset the output buffer size.
qint16* in = (qint16*)inPacket.data.data();
qint16* out = (qint16*)outPacket.data();
for (int f = 0; f < inPacket.data.length() / 2; f++)
{
*out++ = (qint16)*in * this->volume;
*out++ = (qint16)*in++ * this->volume;
}
inPacket.data.clear();
inPacket.data = outPacket; // Replace incoming data with converted.
}
else
{
// We already have the same number of channels so just update volume.
qint16* in = (qint16*)inPacket.data.data();
for (int f = 0; f < inPacket.data.length() / 2; f++)
{
*in = *in * this->volume;
in++;
}
}
}
/* We now have an array of 16bit samples in the NATIVE samplerate of the radio
If the radio sample rate is below 48000, we need to resample.
*/
//qDebug(logAudio()) << "Now 16 bit stereo, length" << inPacket.data.length();
if (resampleRatio != 1.0) {
// We need to resample
// We have a stereo 16bit stream.
quint32 outFrames = ((inPacket.data.length() / 2 / devChannels) * resampleRatio);
quint32 inFrames = (inPacket.data.length() / 2 / devChannels);
QByteArray outPacket(outFrames * 4, (char)0xff); // Preset the output buffer size.
const qint16* in = (qint16*)inPacket.data.constData();
qint16* out = (qint16*)outPacket.data();
int err = 0;
err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames);
if (err) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
}
inPacket.data.clear();
inPacket.data = outPacket; // Replace incoming data with converted.
}
//qDebug(logAudio()) << "Adding packet to buffer:" << inPacket.seq << ": " << inPacket.data.length();
lastSentSeq = inPacket.seq;
if (!ringBuf->try_write(inPacket))
{
qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Buffer full! capacity:" << ringBuf->capacity() << "length" << ringBuf->size();
}
return;
}
void audioHandler::changeLatency(const quint16 newSize)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency;
setup.latency = newSize;
delete ringBuf;
ringBuf = new wilt::Ring(setup.latency / 8 + 1); // Should be customizable.
}
int audioHandler::getLatency()
{
return currentLatency;
}
void audioHandler::getNextAudioChunk(QByteArray& ret)
{
audioPacket packet;
packet.sent = 0;
if (isInitialized && ringBuf != Q_NULLPTR && ringBuf->try_read(packet))
{
currentLatency = packet.time.msecsTo(QTime::currentTime());
if (currentLatency > setup.latency) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Packet " << hex << packet.seq <<
" arrived too late (increase output latency!) " <<
dec << packet.time.msecsTo(QTime::currentTime()) << "ms";
// if (!ringBuf->try_read(packet))
// break;
// currentLatency = packet.time.msecsTo(QTime::currentTime());
}
//qDebug(logAudio) << "Chunksize" << this->chunkSize << "Packet size" << packet.data.length();
// Packet will arrive as stereo interleaved 16bit 48K
if (resampleRatio != 1.0)
{
quint32 outFrames = ((packet.data.length() / 2 / devChannels) * resampleRatio);
quint32 inFrames = (packet.data.length() / 2 / devChannels);
QByteArray outPacket((int)outFrames * 2 * devChannels, (char)0xff);
const qint16* in = (qint16*)packet.data.constData();
qint16* out = (qint16*)outPacket.data();
int err = 0;
err = wf_resampler_process_interleaved_int(resampler, in, &inFrames, out, &outFrames);
if (err) {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Resampler error " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
}
//qInfo(logAudio()) << "Resampler run " << err << " inFrames:" << inFrames << " outFrames:" << outFrames;
//qInfo(logAudio()) << "Resampler run inLen:" << packet->datain.length() << " outLen:" << packet->dataout.length();
packet.data.clear();
packet.data = outPacket; // Copy output packet back to input buffer.
}
//qDebug(logAudio()) << "Now resampled, length" << packet.data.length();
// Do we need to convert mono to stereo?
if (setup.radioChan == 1 && devChannels > 1)
{
// Strip out right channel?
QByteArray outPacket(packet.data.length()/2, (char)0xff);
const qint16* in = (qint16*)packet.data.constData();
qint16* out = (qint16*)outPacket.data();
for (int f = 0; f < outPacket.length()/2; f++)
{
*out++ = *in++;
in++; // Skip each even channel.
}
packet.data.clear();
packet.data = outPacket; // Copy output packet back to input buffer.
}
//qDebug(logAudio()) << "Now mono, length" << packet.data.length();
if (setup.codec == 0x40 || setup.codec == 0x80)
{
//Are we using the opus codec?
qint16* in = (qint16*)packet.data.data();
/* Encode the frame. */
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(encoder, in, (setup.samplerate / 50), out, outPacket.length());
if (nbBytes < 0)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Opus encode failed:" << opus_strerror(nbBytes);
return;
}
else {
outPacket.resize(nbBytes);
packet.data.clear();
packet.data = outPacket; // Replace incoming data with converted.
}
}
else if (setup.bits == 8)
{
// Do we need to convert 16-bit to 8-bit?
QByteArray outPacket((int)packet.data.length() / 2, (char)0xff);
qint16* in = (qint16*)packet.data.data();
for (int f = 0; f < outPacket.length(); f++)
{
qint16 sample = *in++;
if (setup.ulaw) {
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;
}
else {
int compressedByte = (((sample + 32768) >> 8) & 0xff);
outPacket[f] = (quint8)compressedByte;
}
}
packet.data.clear();
packet.data = outPacket; // Copy output packet back to input buffer.
}
ret = packet.data;
//qDebug(logAudio()) << "Now radio format, length" << packet.data.length();
}
return;
}
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
qint64 audioHandler::bytesAvailable() const
{
return 0;
}
bool audioHandler::isSequential() const
{
return true;
}
void audioHandler::notified()
{
}
void audioHandler::stateChanged(QAudio::State state)
{
// Process the state
switch (state)
{
case QAudio::IdleState:
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in idle state: " << audioBuffer.size() << " packets in buffer";
if (audioOutput != Q_NULLPTR && audioOutput->error() == QAudio::UnderrunError)
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "buffer underrun";
//audioOutput->suspend();
}
break;
}
case QAudio::ActiveState:
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in active state: " << audioBuffer.size() << " packets in buffer";
break;
}
case QAudio::SuspendedState:
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in suspended state: " << audioBuffer.size() << " packets in buffer";
break;
}
case QAudio::StoppedState:
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio now in stopped state: " << audioBuffer.size() << " packets in buffer";
break;
}
default: {
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Unhandled audio state: " << audioBuffer.size() << " packets in buffer";
}
}
}
void audioHandler::stop()
{
if (audioOutput != Q_NULLPTR && audioOutput->state() != QAudio::StoppedState) {
// Stop audio output
audioOutput->stop();
this->stop();
this->close();
delete audioOutput;
audioOutput = Q_NULLPTR;
}
if (audioInput != Q_NULLPTR && audioInput->state() != QAudio::StoppedState) {
// Stop audio output
audioInput->stop();
this->stop();
this->close();
delete audioInput;
audioInput = Q_NULLPTR;
}
isInitialized = false;
}
#endif
wfview-1.2d/audiohandler.h 0000664 0000000 0000000 00000013007 14151646264 0015651 0 ustar 00root root 0000000 0000000 #ifndef AUDIOHANDLER_H
#define AUDIOHANDLER_H
#include
#include
#include
#include
#include
#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
#include
#include
#include
#include
#include
#endif
typedef signed short MY_TYPE;
#define FORMAT RTAUDIO_SINT16
#define SCALE 32767.0
#define LOG100 4.60517018599
#include
#include
#include
#include
#include "resampler/speex_resampler.h"
#include "ring/ring.h"
#ifdef Q_OS_WIN
#include "opus.h"
#else
#include "opus/opus.h"
#endif
#include "audiotaper.h"
#include
//#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
QAudioDeviceInfo port;
#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 QObject
#endif
{
Q_OBJECT
public:
audioHandler(QObject* parent = 0);
~audioHandler();
int getLatency();
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
bool setDevice(QAudioDeviceInfo deviceInfo);
void start();
void flush();
void stop();
qint64 bytesAvailable() const;
bool isSequential() const;
#endif
void getNextAudioChunk(QByteArray &data);
public slots:
bool init(audioSetup setup);
void changeLatency(const quint16 newSize);
void setVolume(unsigned char volume);
void incomingAudio(const audioPacket data);
private slots:
#if !defined (RTAUDIO) && !defined(PORTAUDIO)
void notified();
void stateChanged(QAudio::State state);
#endif
signals:
void audioMessage(QString message);
void sendLatency(quint16 newSize);
void haveAudioData(const QByteArray& data);
private:
#if defined(RTAUDIO)
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(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(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 isInitialized=false;
bool isReady = 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
QAudioOutput* audioOutput=Q_NULLPTR;
QAudioInput* audioInput=Q_NULLPTR;
QAudioFormat format;
QAudioDeviceInfo deviceInfo;
#endif
SpeexResamplerState* resampler = Q_NULLPTR;
quint16 audioLatency;
unsigned int chunkSize;
bool chunkAvailable;
quint32 lastSeq;
quint32 lastSentSeq=0;
quint16 nativeSampleRate=0;
quint8 radioSampleBits;
quint8 radioChannels;
QMapaudioBuffer;
double resampleRatio;
wilt::Ring *ringBuf=Q_NULLPTR;
volatile bool ready = false;
audioPacket tempBuf;
quint16 currentLatency;
qreal volume=1.0;
int devChannels;
audioSetup setup;
OpusEncoder* encoder=Q_NULLPTR;
OpusDecoder* decoder=Q_NULLPTR;
};
#endif // AUDIOHANDLER_H
wfview-1.2d/audiotaper.h 0000664 0000000 0000000 00000006575 14151646264 0015363 0 ustar 00root root 0000000 0000000 #ifndef AUDIOTAPER_H
#define AUDIOTAPER_H
#include
const qreal audiopot[256] =
{
0.00000,
0.00101,
0.00202,
0.00305,
0.00409,
0.00513,
0.00619,
0.00725,
0.00832,
0.00941,
0.01050,
0.01160,
0.01272,
0.01384,
0.01497,
0.01612,
0.01727,
0.01843,
0.01961,
0.02080,
0.02199,
0.02320,
0.02442,
0.02565,
0.02689,
0.02814,
0.02940,
0.03068,
0.03196,
0.03326,
0.03457,
0.03589,
0.03723,
0.03857,
0.03993,
0.04130,
0.04268,
0.04408,
0.04548,
0.04690,
0.04834,
0.04978,
0.05124,
0.05272,
0.05420,
0.05570,
0.05721,
0.05874,
0.06028,
0.06184,
0.06341,
0.06499,
0.06659,
0.06820,
0.06982,
0.07146,
0.07312,
0.07479,
0.07648,
0.07818,
0.07990,
0.08163,
0.08338,
0.08514,
0.08692,
0.08872,
0.09053,
0.09236,
0.09421,
0.09607,
0.09795,
0.09984,
0.10176,
0.10369,
0.10564,
0.10760,
0.10959,
0.11159,
0.11361,
0.11565,
0.11770,
0.11978,
0.12187,
0.12399,
0.12612,
0.12827,
0.13044,
0.13263,
0.13484,
0.13707,
0.13933,
0.14160,
0.14389,
0.14620,
0.14854,
0.15089,
0.15327,
0.15567,
0.15809,
0.16053,
0.16299,
0.16548,
0.16799,
0.17052,
0.17307,
0.17565,
0.17825,
0.18088,
0.18353,
0.18620,
0.18889,
0.19162,
0.19436,
0.19713,
0.19993,
0.20275,
0.20560,
0.20847,
0.21137,
0.21429,
0.21725,
0.22022,
0.22323,
0.22626,
0.22932,
0.23241,
0.23553,
0.23867,
0.24184,
0.24504,
0.24828,
0.25153,
0.25482,
0.25814,
0.26149,
0.26487,
0.26828,
0.27172,
0.27520,
0.27870,
0.28224,
0.28580,
0.28941,
0.29304,
0.29670,
0.30040,
0.30414,
0.30790,
0.31170,
0.31554,
0.31941,
0.32331,
0.32725,
0.33123,
0.33524,
0.33929,
0.34338,
0.34750,
0.35166,
0.35586,
0.36009,
0.36437,
0.36868,
0.37303,
0.37742,
0.38185,
0.38633,
0.39084,
0.39539,
0.39999,
0.40462,
0.40930,
0.41402,
0.41878,
0.42359,
0.42844,
0.43333,
0.43827,
0.44326,
0.44828,
0.45336,
0.45848,
0.46364,
0.46886,
0.47412,
0.47943,
0.48478,
0.49019,
0.49564,
0.50115,
0.50670,
0.51230,
0.51796,
0.52366,
0.52942,
0.53523,
0.54110,
0.54701,
0.55298,
0.55900,
0.56508,
0.57122,
0.57741,
0.58365,
0.58995,
0.59631,
0.60273,
0.60920,
0.61574,
0.62233,
0.62898,
0.63570,
0.64247,
0.64931,
0.65620,
0.66316,
0.67019,
0.67727,
0.68442,
0.69164,
0.69892,
0.70627,
0.71368,
0.72116,
0.72871,
0.73633,
0.74402,
0.75178,
0.75960,
0.76750,
0.77547,
0.78351,
0.79163,
0.79981,
0.80808,
0.81641,
0.82483,
0.83332,
0.84188,
0.85053,
0.85925,
0.86805,
0.87693,
0.88590,
0.89494,
0.90407,
0.91327,
0.92257,
0.93194,
0.94140,
0.95095,
0.96058,
0.97030,
0.98011,
0.99001,
1.00000
};
#endif // AUDIOTAPER_H
wfview-1.2d/calibrationwindow.cpp 0000664 0000000 0000000 00000006305 14151646264 0017267 0 ustar 00root root 0000000 0000000 #include "calibrationwindow.h"
#include "ui_calibrationwindow.h"
#include "logcategories.h"
calibrationWindow::calibrationWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::calibrationWindow)
{
ui->setupUi(this);
ui->calCourseSlider->setDisabled(true);
ui->calCourseSpinbox->setDisabled(true);
ui->calFineSlider->setDisabled(true);
ui->calFineSpinbox->setDisabled(true);
}
calibrationWindow::~calibrationWindow()
{
delete ui;
}
void calibrationWindow::handleCurrentFreq(double tunedFreq)
{
(void)tunedFreq;
}
void calibrationWindow::handleSpectrumPeak(double peakFreq)
{
(void)peakFreq;
}
void calibrationWindow::handleRefAdjustCourse(unsigned char value)
{
ui->calCourseSlider->setDisabled(false);
ui->calCourseSpinbox->setDisabled(false);
ui->calCourseSlider->blockSignals(true);
ui->calCourseSpinbox->blockSignals(true);
ui->calCourseSlider->setValue((int) value);
ui->calCourseSpinbox->setValue((int) value);
ui->calCourseSlider->blockSignals(false);
ui->calCourseSpinbox->blockSignals(false);
}
void calibrationWindow::handleRefAdjustFine(unsigned char value)
{
ui->calFineSlider->setDisabled(false);
ui->calFineSpinbox->setDisabled(false);
ui->calFineSlider->blockSignals(true);
ui->calFineSpinbox->blockSignals(true);
ui->calFineSlider->setValue((int) value);
ui->calFineSpinbox->setValue((int) value);
ui->calFineSlider->blockSignals(false);
ui->calFineSpinbox->blockSignals(false);
}
void calibrationWindow::on_calReadRigCalBtn_clicked()
{
emit requestRefAdjustCourse();
emit requestRefAdjustFine();
}
void calibrationWindow::on_calCourseSlider_valueChanged(int value)
{
ui->calCourseSpinbox->blockSignals(true);
ui->calCourseSpinbox->setValue((int) value);
ui->calCourseSpinbox->blockSignals(false);
emit setRefAdjustCourse((unsigned char) value);
}
void calibrationWindow::on_calFineSlider_valueChanged(int value)
{
ui->calFineSpinbox->blockSignals(true);
ui->calFineSpinbox->setValue((int) value);
ui->calFineSpinbox->blockSignals(false);
emit setRefAdjustFine((unsigned char) value);
}
void calibrationWindow::on_calCourseSpinbox_valueChanged(int value)
{
// this one works with the up and down arrows,
// however, if typing in a value, say "128",
// this will get called three times with these values:
// 1
// 12
// 128
//int value = ui->calFineSpinbox->value();
ui->calCourseSlider->blockSignals(true);
ui->calCourseSlider->setValue(value);
ui->calCourseSlider->blockSignals(false);
emit setRefAdjustCourse((unsigned char) value);
}
void calibrationWindow::on_calFineSpinbox_valueChanged(int value)
{
//int value = ui->calFineSpinbox->value();
ui->calFineSlider->blockSignals(true);
ui->calFineSlider->setValue(value);
ui->calFineSlider->blockSignals(false);
emit setRefAdjustFine((unsigned char) value);
}
void calibrationWindow::on_calFineSpinbox_editingFinished()
{
}
void calibrationWindow::on_calCourseSpinbox_editingFinished()
{
// This function works well for typing in values
// but the up and down arrows on the spinbox will not
// trigger this function, until the enter key is pressed.
}
wfview-1.2d/calibrationwindow.h 0000664 0000000 0000000 00000002210 14151646264 0016723 0 ustar 00root root 0000000 0000000 #ifndef CALIBRATIONWINDOW_H
#define CALIBRATIONWINDOW_H
#include
namespace Ui {
class calibrationWindow;
}
class calibrationWindow : public QDialog
{
Q_OBJECT
public:
explicit calibrationWindow(QWidget *parent = 0);
~calibrationWindow();
public slots:
void handleSpectrumPeak(double peakFreq);
void handleCurrentFreq(double tunedFreq);
void handleRefAdjustCourse(unsigned char);
void handleRefAdjustFine(unsigned char);
signals:
void requestSpectrumPeak(double peakFreq);
void requestCurrentFreq(double tunedFreq);
void requestRefAdjustCourse();
void requestRefAdjustFine();
void setRefAdjustCourse(unsigned char);
void setRefAdjustFine(unsigned char);
private slots:
void on_calReadRigCalBtn_clicked();
void on_calCourseSlider_valueChanged(int value);
void on_calFineSlider_valueChanged(int value);
void on_calCourseSpinbox_valueChanged(int arg1);
void on_calFineSpinbox_valueChanged(int arg1);
void on_calFineSpinbox_editingFinished();
void on_calCourseSpinbox_editingFinished();
private:
Ui::calibrationWindow *ui;
};
#endif // CALIBRATIONWINDOW_H
wfview-1.2d/calibrationwindow.ui 0000664 0000000 0000000 00000015066 14151646264 0017126 0 ustar 00root root 0000000 0000000
calibrationWindow
0
0
400
300
0
0
400
300
400
300
Reference Adjustment
9
9
381
281
-
-
IC-9700
-
Reference Adjustment
-
-
-
Course
-
Fine
-
10
-
-
255
Qt::Horizontal
-
255
Qt::Horizontal
-
10
10
-
255
-
255
-
Qt::Vertical
20
40
-
0
-
false
<html><head/><body><p>Save the calibration data to the indicated slot in the preference file. </p></body></html>
Save
-
false
<html><head/><body><p>Load the calibration data fromthe indicated slot in the preference file. </p></body></html>
Load
-
false
Slot:
-
false
-
1
-
2
-
3
-
4
-
5
-
Qt::Horizontal
40
20
-
Read Current Rig Calibration
wfview-1.2d/commhandler.cpp 0000664 0000000 0000000 00000021101 14151646264 0016030 0 ustar 00root root 0000000 0000000 #include "commhandler.h"
#include "logcategories.h"
#include
// Copytight 2017-2020 Elliott H. Liggett
commHandler::commHandler()
{
//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.
baudrate = 115200;
stopbits = 1;
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()));
}
commHandler::commHandler(QString portName, quint32 baudRate)
{
//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.
baudrate = baudRate;
stopbits = 1;
this->portName = portName;
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()));
}
commHandler::~commHandler()
{
this->closePort();
}
void commHandler::setupComm()
{
serialError = false;
port->setPortName(portName);
port->setBaudRate(baudrate);
port->setStopBits(QSerialPort::OneStop);// OneStop is other option
}
void commHandler::receiveDataFromUserToRig(const QByteArray &data)
{
sendDataOut(data);
}
void commHandler::sendDataOut(const QByteArray &writeData)
{
mutex.lock();
qint64 bytesWritten;
if(PTTviaRTS)
{
// Size: 1 2 3 4 5 6 7 8
//index: 0 1 2 3 4 5 6 7
//Query: FE FE TO FROM 0x1C 0x00 0xFD
//PTT On: FE FE TO FROM 0x1C 0x00 0x01 0xFD
//PTT Off: FE FE TO FROM 0x1C 0x00 0x00 0xFD
if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\xFD")))
{
// Query
//qDebug(logSerial()) << "Looks like PTT Query";
bool pttOn = this->rtsStatus();
QByteArray pttreturncmd = QByteArray("\xFE\xFE");
pttreturncmd.append(writeData.at(3));
pttreturncmd.append(writeData.at(2));
pttreturncmd.append(QByteArray("\x1C\x00", 2));
pttreturncmd.append((char)pttOn);
pttreturncmd.append("\xFD");
//qDebug(logSerial()) << "Sending fake PTT query result: " << (bool)pttOn;
printHex(pttreturncmd, false, true);
emit haveDataFromPort(pttreturncmd);
mutex.unlock();
return;
} else if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\x01\xFD")))
{
// PTT ON
//qDebug(logSerial()) << "Looks like PTT ON";
setRTS(true);
mutex.unlock();
return;
} else if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\x00\xFD")))
{
// PTT OFF
//qDebug(logSerial()) << "Looks like PTT OFF";
setRTS(false);
mutex.unlock();
return;
}
}
bytesWritten = port->write(writeData);
if(bytesWritten != (qint64)writeData.size())
{
qDebug(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\
<< " size of byte array: " << writeData.size()\
<< " Wrote all bytes? " << (bool) (bytesWritten == (qint64)writeData.size());
}
mutex.unlock();
}
void commHandler::receiveDataIn()
{
// connected to comm port data signal
// Here we get a little specific to CIV radios
// because we know what constitutes a valid "frame" of data.
// new code:
port->startTransaction();
inPortData = port->readAll();
if(inPortData.size() == 1)
{
// Generally for baud <= 9600
if (inPortData == "\xFE")
{
// This will get hit twice.
// After the FE FE, we transition into
// the normal .startsWith FE FE block
// where the normal rollback code can handle things.
port->rollbackTransaction();
rolledBack = true;
return;
}
}
if(inPortData.startsWith("\xFE\xFE"))
{
if(inPortData.contains("\xFC"))
{
//qInfo(logSerial()) << "Transaction contains collision data. Dumping.";
//printHex(inPortData, false, true);
port->commitTransaction();
return;
}
if(inPortData.endsWith("\xFD"))
{
// good!
port->commitTransaction();
emit haveDataFromPort(inPortData);
if(rolledBack)
{
// qInfo(logSerial()) << "Rolled back and was successfull. 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();
//printHex(inPortData, false, true);
port->rollbackTransaction();
rolledBack = true;
}
} else {
port->commitTransaction(); // do not emit data, do not keep data.
//qInfo(logSerial()) << "Warning: received data with invalid start. Dropping data.";
//qInfo(logSerial()) << "THIS SHOULD ONLY HAPPEN ONCE!!";
// THIS SHOULD ONLY HAPPEN ONCE!
// unrecoverable. We did not receive the start and must
// have missed it earlier because we did not roll back to
// preserve the beginning.
//printHex(inPortData, false, true);
}
}
void commHandler::setRTS(bool rtsOn)
{
bool success = port->setRequestToSend(rtsOn);
if(!success)
{
qInfo(logSerial()) << "Error, could not set RTS on port " << portName;
}
}
bool commHandler::rtsStatus()
{
return port->isRequestToSend();
}
void commHandler::setUseRTSforPTT(bool PTTviaRTS)
{
this->PTTviaRTS = PTTviaRTS;
}
void commHandler::openPort()
{
bool success;
// port->open();
success = port->open(QIODevice::ReadWrite);
if(success)
{
port->setDataTerminalReady(false);
port->setRequestToSend(false);
isConnected = true;
qInfo(logSerial()) << "Opened port: " << portName;
return;
} else {
qInfo(logSerial()) << "Could not open serial port " << portName << " , please restart.";
isConnected = false;
serialError = true;
emit haveSerialPortError(portName, "Could not open port. Please restart.");
return;
}
}
void commHandler::closePort()
{
if(port)
{
port->close();
delete port;
}
isConnected = false;
}
void commHandler::debugThis()
{
// Do not use, function is for debug only and subject to change.
qInfo(logSerial()) << "comm debug called.";
inPortData = port->readAll();
emit haveDataFromPort(inPortData);
}
void commHandler::printHex(const QByteArray &pdata, bool printVert, bool printHoriz)
{
qDebug(logSerial()) << "---- 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++)
{
qDebug(logSerial()) << strings.at(i);
}
}
if(printHoriz)
{
qDebug(logSerial()) << index;
qDebug(logSerial()) << sdata;
}
qDebug(logSerial()) << "----- End hex dump -----";
}
wfview-1.2d/commhandler.h 0000664 0000000 0000000 00000003502 14151646264 0015502 0 ustar 00root root 0000000 0000000 #ifndef COMMHANDLER_H
#define COMMHANDLER_H
#include
#include
#include
#include
// This class abstracts the comm port in a useful way and connects to
// the command creator and command parser.
class commHandler : public QObject
{
Q_OBJECT
public:
commHandler();
commHandler(QString portName, quint32 baudRate);
bool serialError;
bool rtsStatus();
~commHandler();
public slots:
void setUseRTSforPTT(bool useRTS);
void setRTS(bool rtsOn);
private slots:
void receiveDataIn(); // from physical port
void receiveDataFromUserToRig(const QByteArray &data);
void debugThis();
signals:
void haveTextMessage(QString message); // status, debug only
void sendDataOutToPort(const QByteArray &writeData); // not used
void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander
void haveSerialPortError(const QString port, const QString error);
void haveStatusUpdate(const QString text);
private:
void setupComm();
void openPort();
void closePort();
void sendDataOut(const QByteArray &writeData); // out to radio
void debugMe();
void hexPrint();
//QDataStream stream;
QByteArray outPortData;
QByteArray inPortData;
//QDataStream outStream;
//QDataStream inStream;
unsigned char buffer[256];
QString portName;
QSerialPort *port;
qint32 baudrate;
unsigned char stopbits;
bool rolledBack;
QSerialPort *pseudoterm;
int ptfd; // pseudo-terminal file desc.
mutable QMutex ptMutex;
bool havePt;
QString ptDevSlave;
bool PTTviaRTS = false;
bool isConnected; // port opened
mutable QMutex mutex;
void printHex(const QByteArray &pdata, bool printVert, bool printHoriz);
};
#endif // COMMHANDLER_H
wfview-1.2d/freqmemory.cpp 0000664 0000000 0000000 00000002702 14151646264 0015733 0 ustar 00root root 0000000 0000000 #include "freqmemory.h"
#include "logcategories.h"
// Copytight 2017-2020 Elliott H. Liggett
freqMemory::freqMemory()
{
// NOTE: These are also present in the header and
// the array must be changed if these are changed.
numPresets = 100;
maxIndex = numPresets - 1;
initializePresets();
}
void freqMemory::initializePresets()
{
// qInfo() << "Initializing " << numPresets << " memory channels";
for(unsigned int p=0; p < numPresets; p++)
{
presets[p].frequency = 14.1 + p/1000.0;
presets[p].mode = modeUSB;
presets[p].isSet = true;
}
}
void freqMemory::setPreset(unsigned int index, double frequency, mode_kind mode)
{
if(index <= maxIndex)
{
presets[index].frequency = frequency;
presets[index].mode = mode;
presets[index].isSet = true;
}
}
preset_kind freqMemory::getPreset(unsigned int index)
{
//validate then return
if(index <= maxIndex)
{
return presets[index];
}
// else, return something obviously wrong
preset_kind temp;
temp.frequency=12.345;
temp.mode = modeUSB;
temp.isSet = false;
return temp;
}
unsigned int freqMemory::getNumPresets()
{
return numPresets;
}
void freqMemory::dumpMemory()
{
for(unsigned int p=0; p < numPresets; p++)
{
qInfo(logSystem()) << "Index: " << p << " freq: " << presets[p].frequency << " Mode: " << presets[p].mode << " isSet: " << presets[p].isSet;
}
}
wfview-1.2d/freqmemory.h 0000664 0000000 0000000 00000003062 14151646264 0015400 0 ustar 00root root 0000000 0000000 #ifndef FREQMEMORY_H
#define FREQMEMORY_H
#include
#include
// 0 1 2 3 4
//modes << "LSB" << "USB" << "AM" << "CW" << "RTTY";
// 5 6 7 8 9
// modes << "FM" << "CW-R" << "RTTY-R" << "LSB-D" << "USB-D";
enum mode_kind {
modeLSB=0x00,
modeUSB=0x01,
modeAM=0x02,
modeCW=0x03,
modeRTTY=0x04,
modeFM=0x05,
modeCW_R=0x07,
modeRTTY_R=0x08,
modeLSB_D=0x80,
modeUSB_D=0x81,
modeDV=0x17,
modeDD=0x27,
modeWFM,
modeS_AMD,
modeS_AML,
modeS_AMU,
modeP25,
modedPMR,
modeNXDN_VN,
modeNXDN_N,
modeDCR,
modePSK,
modePSK_R
};
struct mode_info {
mode_kind mk;
unsigned char reg;
unsigned char filter;
QString name;
};
struct preset_kind {
// QString name;
// QString comment;
// unsigned int index; // channel number
double frequency;
mode_kind mode;
bool isSet;
};
class freqMemory
{
public:
freqMemory();
void setPreset(unsigned int index, double frequency, mode_kind mode);
void setPreset(unsigned int index, double frequency, mode_kind mode, QString name);
void setPreset(unsigned int index, double frequency, mode_kind mode, QString name, QString comment);
void dumpMemory();
unsigned int getNumPresets();
preset_kind getPreset(unsigned int index);
private:
void initializePresets();
unsigned int numPresets;
unsigned int maxIndex;
//QVector presets;
preset_kind presets[100];
};
#endif // FREQMEMORY_H
wfview-1.2d/logcategories.cpp 0000664 0000000 0000000 00000000521 14151646264 0016371 0 ustar 00root root 0000000 0000000 #include "logcategories.h"
Q_LOGGING_CATEGORY(logSystem, "system")
Q_LOGGING_CATEGORY(logSerial, "serial")
Q_LOGGING_CATEGORY(logGui, "gui")
Q_LOGGING_CATEGORY(logRig, "rig")
Q_LOGGING_CATEGORY(logAudio, "audio")
Q_LOGGING_CATEGORY(logUdp, "udp")
Q_LOGGING_CATEGORY(logUdpServer, "udp.server")
Q_LOGGING_CATEGORY(logRigCtlD, "rigctld")
wfview-1.2d/logcategories.h 0000664 0000000 0000000 00000000773 14151646264 0016047 0 ustar 00root root 0000000 0000000 #ifndef LOGCATEGORIES_H
#define LOGCATEGORIES_H
#include
Q_DECLARE_LOGGING_CATEGORY(logSystem)
Q_DECLARE_LOGGING_CATEGORY(logSerial)
Q_DECLARE_LOGGING_CATEGORY(logGui)
Q_DECLARE_LOGGING_CATEGORY(logRig)
Q_DECLARE_LOGGING_CATEGORY(logAudio)
Q_DECLARE_LOGGING_CATEGORY(logUdp)
Q_DECLARE_LOGGING_CATEGORY(logUdpServer)
Q_DECLARE_LOGGING_CATEGORY(logRigCtlD)
#if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__)
#define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
#endif // LOGCATEGORIES_H
wfview-1.2d/main.cpp 0000664 0000000 0000000 00000012415 14151646264 0014473 0 ustar 00root root 0000000 0000000 #include
#include
#include "wfmain.h"
#include "logcategories.h"
// Copytight 2017-2021 Elliott H. Liggett
// Smart pointer to log file
QScopedPointer m_logFile;
QMutex logMutex;
bool debugMode=false;
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//a.setStyle( "Fusion" );
a.setOrganizationName("wfview");
a.setOrganizationDomain("wfview.org");
a.setApplicationName("wfview");
#ifdef QT_DEBUG
debugMode = true;
#endif
QString serialPortCL;
QString hostCL;
QString civCL;
#ifdef Q_OS_MAC
QString logFilename= QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log";
#else
QString logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log";
#endif
QString settingsFile = NULL;
QString currentArg;
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...
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());
for(int c=1; c c)
{
serialPortCL = argv[c + 1];
c += 1;
}
}
else if ((currentArg == "-d") || (currentArg == "--debug"))
{
debugMode = true;
}
else if ((currentArg == "-h") || (currentArg == "--host"))
{
if(argc > c)
{
hostCL = argv[c+1];
c+=1;
}
}
else if ((currentArg == "-c") || (currentArg == "--civ"))
{
if (argc > c)
{
civCL = argv[c + 1];
c += 1;
}
}
else if ((currentArg == "-l") || (currentArg == "--logfile"))
{
if (argc > c)
{
logFilename = argv[c + 1];
c += 1;
}
}
else if ((currentArg == "-s") || (currentArg == "--settings"))
{
if (argc > c)
{
settingsFile = argv[c + 1];
c += 1;
}
}
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;
}
}
// Set the logging file before doing anything else.
m_logFile.reset(new QFile(logFilename));
// Open the file logging
m_logFile.data()->open(QFile::WriteOnly | QFile::Truncate | QFile::Text);
// Set handler
qInstallMessageHandler(messageHandler);
qInfo(logSystem()) << version;
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);
a.setWheelScrollLines(1); // one line per wheel click
wfmain w( serialPortCL, hostCL, settingsFile);
w.show();
return a.exec();
}
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
// Open stream file writes
if (type == QtDebugMsg && !debugMode)
{
return;
}
QMutexLocker locker(&logMutex);
QTextStream out(m_logFile.data());
// Write the date of recording
out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
// By type determine to what level belongs message
switch (type)
{
case QtDebugMsg:
out << "DBG ";
break;
case QtInfoMsg:
out << "INF ";
break;
case QtWarningMsg:
out << "WRN ";
break;
case QtCriticalMsg:
out << "CRT ";
break;
case QtFatalMsg:
out << "FTL ";
break;
}
// Write to the output category of the message and the message itself
out << context.category << ": " << msg << "\n";
out.flush(); // Clear the buffered data
}
wfview-1.2d/meter.cpp 0000664 0000000 0000000 00000037775 14151646264 0014703 0 ustar 00root root 0000000 0000000 #include "meter.h"
#include
meter::meter(QWidget *parent) : QWidget(parent)
{
//QPainter painter(this);
// Colors from qdarkstylesheet:
// $COLOR_BACKGROUND_LIGHT: #505F69;
// $COLOR_BACKGROUND_NORMAL: #32414B;
// $COLOR_BACKGROUND_DARK: #19232D;
// $COLOR_FOREGROUND_LIGHT: #F0F0F0; // grey
// $COLOR_FOREGROUND_NORMAL: #AAAAAA; // grey
// $COLOR_FOREGROUND_DARK: #787878; // grey
// $COLOR_SELECTION_LIGHT: #148CD2;
// $COLOR_SELECTION_NORMAL: #1464A0;
// $COLOR_SELECTION_DARK: #14506E;
// Colors I found that I liked from VFD images:
// 3FB7CD
// 3CA0DB
//
// Text in qdarkstylesheet seems to be #EFF0F1
if(drawLabels)
{
mXstart = 32;
} else {
mXstart = 0;
}
meterType = meterS;
currentColor.setNamedColor("#148CD2");
currentColor = currentColor.darker();
peakColor.setNamedColor("#3CA0DB");
peakColor = peakColor.lighter();
averageColor.setNamedColor("#3FB7CD");
lowTextColor.setNamedColor("#eff0f1");
lowLineColor = lowTextColor;
avgLevels.resize(averageBalisticLength, 0);
peakLevels.resize(peakBalisticLength, 0);
}
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,
// 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)
if( (meterType == meterALC) || (meterType == meterSWR)
|| (meterType == meterComp) || (meterType == meterTxMod)
|| (meterType == meterCenter ))
{
clearMeter();
}
}
void meter::clearMeter()
{
current = 0;
average = 0;
peak = 0;
avgLevels.clear();
peakLevels.clear();
avgLevels.resize(averageBalisticLength, 0);
peakLevels.resize(peakBalisticLength, 0);
peakPosition = 0;
avgPosition = 0;
// re-draw scale:
update();
}
void meter::setMeterType(meterKind type)
{
if(type == meterType)
return;
meterType = type;
// clear average and peak vectors:
this->clearMeter();
}
meterKind meter::getMeterType()
{
return meterType;
}
void meter::setMeterShortString(QString s)
{
meterShortString = s;
}
QString meter::getMeterShortString()
{
return meterShortString;
}
void meter::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// This next line sets up a canvis within the
// space of the widget, and gives it corrdinates.
// The end effect, is that the drawing functions will all
// scale to the window size.
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setFont(QFont(this->fontInfo().family(), fontSize));
widgetWindowHeight = this->height();
painter.setWindow(QRect(0, 0, 255+mXstart+15, widgetWindowHeight));
barHeight = widgetWindowHeight / 2;
switch(meterType)
{
case meterS:
label = "S";
peakRedLevel = 120; // S9+
drawScaleS(&painter);
break;
case meterPower:
label = "PWR";
peakRedLevel = 210; // 100%
drawScalePo(&painter);
break;
case meterALC:
label = "ALC";
peakRedLevel = 100;
drawScaleALC(&painter);
break;
case meterSWR:
label = "SWR";
peakRedLevel = 100; // SWR 2.5
drawScaleSWR(&painter);
break;
case meterCenter:
label = "CTR";
peakRedLevel = 256; // No need for red here
drawScaleCenter(&painter);
break;
case meterVoltage:
label = "Vd";
peakRedLevel = 241;
drawScaleVd(&painter);
break;
case meterCurrent:
label = "Id";
peakRedLevel = 120;
drawScaleId(&painter);
break;
case meterComp:
label = "CMP";
peakRedLevel = 100;
drawScaleComp(&painter);
break;
case meterNone:
return;
break;
default:
label = "DN";
peakRedLevel = 241;
drawScaleRaw(&painter);
break;
}
// Current: the most-current value.
// Draws a bar from start to value.
painter.setPen(currentColor);
painter.setBrush(currentColor);
if(meterType == meterCenter)
{
painter.drawRect(mXstart+128,mYstart,current-128,barHeight);
// Average:
painter.setPen(averageColor);
painter.setBrush(averageColor);
painter.drawRect(mXstart+average-1,mYstart,1,barHeight); // bar is 1 pixel wide, height = meter start?
// Peak:
painter.setPen(peakColor);
painter.setBrush(peakColor);
if((peak > 191) || (peak < 63))
{
painter.setBrush(Qt::red);
painter.setPen(Qt::red);
}
painter.drawRect(mXstart+peak-1,mYstart,1,barHeight);
} else {
// X, Y, Width, Height
painter.drawRect(mXstart,mYstart,current,barHeight);
// Average:
painter.setPen(averageColor);
painter.setBrush(averageColor);
painter.drawRect(mXstart+average-1,mYstart,1,barHeight); // bar is 1 pixel wide, height = meter start?
// Peak:
painter.setPen(peakColor);
painter.setBrush(peakColor);
if(peak > peakRedLevel)
{
painter.setBrush(Qt::red);
painter.setPen(Qt::red);
}
painter.drawRect(mXstart+peak-1,mYstart,2,barHeight);
}
if(drawLabels)
{
drawLabel(&painter);
}
}
void meter::drawLabel(QPainter *qp)
{
qp->setPen(lowLineColor);
qp->drawText(0,scaleTextYstart, label );
}
void meter::setLevel(int current)
{
this->current = current;
avgLevels[(avgPosition++)%averageBalisticLength] = current;
peakLevels[(peakPosition++)%peakBalisticLength] = current;
int sum=0;
for(unsigned int i=0; i < (unsigned int)std::min(avgPosition, (int)avgLevels.size()); i++)
{
sum += avgLevels.at(i);
}
this->average = sum / std::min(avgPosition, (int)avgLevels.size());
this->peak = 0;
for(unsigned int i=0; i < peakLevels.size(); i++)
{
if( peakLevels.at(i) > this->peak)
this->peak = peakLevels.at(i);
}
this->update();
}
void meter::setLevels(int current, int peak, int average)
{
this->current = current;
this->peak = peak;
this->average = average;
this->update();
}
void meter::updateDrawing(int num)
{
fontSize = num;
length = num;
}
// The drawScale functions draw the numbers and number unerline for each type of meter
void meter::drawScaleRaw(QPainter *qp)
{
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
int i=mXstart;
for(; idrawText(i,scaleTextYstart, QString("%1").arg(i) );
}
// Now the lines:
qp->setPen(lowLineColor);
// Line: X1, Y1 -->to--> X2, Y2
qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScaleVd(QPainter *qp)
{
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
// 7300/9700 and others:
int midPointDn = 13;
int midPointVd = 10;
// 705:
//int midPointDn = 75;
//int midPointVd = 5;
int highPointDn = 241;
int highPointVd = 16;
float VdperDn = (float)(highPointVd-midPointVd) / float(highPointDn-midPointDn);
int i=mXstart;
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int)((i-mXstart) * (float(midPointVd) / float(midPointDn)) )) );
}
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int) std::round( ((i-mXstart-midPointDn) * (VdperDn) ) + (midPointVd) )));
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
// Now the lines:
qp->setPen(lowLineColor);
// Line: X1, Y1 -->to--> X2, Y2
qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScaleCenter(QPainter *qp)
{
// No known units
qp->setPen(lowLineColor);
qp->drawText(60+mXstart,scaleTextYstart, QString("-"));
qp->setPen(Qt::green);
// Attempt to draw the zero at the actual center
qp->drawText(128-2+mXstart,scaleTextYstart, QString("0"));
qp->setPen(lowLineColor);
qp->drawText(195+mXstart,scaleTextYstart, QString("+"));
qp->setPen(lowLineColor);
qp->drawLine(mXstart,scaleLineYstart,128-32+mXstart,scaleLineYstart);
qp->setPen(Qt::green);
qp->drawLine(128-32+mXstart,scaleLineYstart,128+32+mXstart,scaleLineYstart);
qp->setPen(lowLineColor);
qp->drawLine(128+32+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScalePo(QPainter *qp)
{
//From the manual: "0000=0% to 0143=50% to 0213=100%"
float dnPerWatt = 143.0f / 50.0f;
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
int i=mXstart;
// 13.3 DN per s-unit:
int p=0;
for(; idrawText(i,scaleTextYstart, QString("%1").arg(10*(p++)) );
}
// Modify current scale position:
// Here, P is now 60 watts:
// Higher scale:
i = i - (int)(10*dnPerWatt); // back one tick first. Otherwise i starts at 178.
//qDebug() << "meter i: " << i;
dnPerWatt = (213-143.0f) / 50.0f; // 1.4 dn per watt
// P=5 here.
qp->setPen(Qt::yellow);
int k=0;
for(i=mXstart+143; idrawText(i,scaleTextYstart, QString("%1").arg(k) );
}
// Now we're out past 100:
qp->setPen(Qt::red);
for(i=mXstart+213; idrawText(i,scaleTextYstart, QString("%1").arg(k) );
}
// Now the lines:
qp->setPen(lowLineColor);
// Line: X1, Y1 -->to--> X2, Y2
qp->drawLine(mXstart,scaleLineYstart,213+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(213+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
(void)qp;
}
void meter::drawScaleRxdB(QPainter *qp)
{
(void)qp;
}
void meter::drawScaleALC(QPainter *qp)
{
// From the manual: 0000=Minimum to 0120=Maximum
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
int i=mXstart;
int alc=0;
for(; idrawText(i,scaleTextYstart, QString("%1").arg(alc) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
alc +=20;
}
qp->setPen(Qt::red);
for(; idrawText(i,scaleTextYstart, QString("+%1").arg(alc) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
alc +=10;
}
qp->setPen(lowLineColor);
qp->drawLine(mXstart,scaleLineYstart,100+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(100+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
(void)qp;
}
void meter::drawScaleComp(QPainter *qp)
{
//
// 0000=0 dB, 0130=15 dB,0241=30 dB
//
qp->setPen(lowTextColor);
int midPointDn = 130;
int midPointdB = 15;
int highPointDn = 241;
int highPointdB = 30;
float dBperDn = (float)(highPointdB-midPointdB) / float(highPointDn-midPointDn);
int i=mXstart;
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int)((i-mXstart) * (float(midPointdB) / float(midPointDn)) )) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
i = midPointDn+60;
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int) std::round( ((i-mXstart-midPointDn) * (dBperDn) ) + (midPointdB) )));
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
// Now the lines:
qp->setPen(lowLineColor);
// Line: X1, Y1 -->to--> X2, Y2
qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScaleSWR(QPainter *qp)
{
// From the manual:
// 0000=SWR1.0,
// 0048=SWR1.5,
// 0080=SWR2.0,
// 0120=SWR3.0
qp->drawText(mXstart,scaleTextYstart, QString("1.0"));
qp->drawText(24+mXstart,scaleTextYstart, QString("1.3"));
qp->drawText(48+mXstart,scaleTextYstart, QString("1.5"));
qp->drawText(80+mXstart,scaleTextYstart, QString("2.0"));
qp->drawText(100+mXstart,scaleTextYstart, QString("2.5"));
qp->drawText(120+mXstart,scaleTextYstart, QString("3.0"));
qp->drawLine( 0+mXstart,scaleTextYstart, 0+mXstart, scaleTextYstart+5);
qp->drawLine( 24+mXstart,scaleTextYstart, 24+mXstart, scaleTextYstart+5);
qp->drawLine( 48+mXstart,scaleTextYstart, 48+mXstart, scaleTextYstart+5);
qp->drawLine( 80+mXstart,scaleTextYstart, 80+mXstart, scaleTextYstart+5);
qp->drawLine(100+mXstart,scaleTextYstart,100+mXstart, scaleTextYstart+5); // does not draw?
qp->drawLine(120+mXstart,scaleTextYstart,120+mXstart, scaleTextYstart+5);
qp->setPen(lowLineColor);
qp->drawLine(mXstart,scaleLineYstart,100+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(100+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScaleId(QPainter *qp)
{
// IC-7300:
// 0000=0, 0097=10, 0146=15, 0241=25
//
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
// 7300/9700 and others:
int midPointDn = 97;
int midPointId = 10;
int highPointDn = 146;
int highPointId = 15;
float IdperDn = (float)(highPointId-midPointId) / float(highPointDn-midPointDn);
int i=mXstart;
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int)((i-mXstart) * (float(midPointId) / float(midPointDn)) )) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int) std::round( ((i-mXstart-midPointDn) * (IdperDn) ) + (midPointId) )));
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
// Now the lines:
qp->setPen(lowLineColor);
// Line: X1, Y1 -->to--> X2, Y2
qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
void meter::drawScaleS(QPainter *qp)
{
//
// 0000=S0, 0120=S9, 0241=S9+60dB
// 120 / 9 = 13.333 steps per S-unit
qp->setPen(lowTextColor);
//qp->setFont(QFont("Arial", fontSize));
int i=mXstart;
// 13.3 DN per s-unit:
int s=0;
for(; idrawText(i,scaleTextYstart, QString("%1").arg(s++) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
}
// 2 DN per 1 dB now:
// 20 DN per 10 dB
// 40 DN per 20 dB
// Modify current scale position:
s = 20;
i+=20;
qp->setPen(Qt::red);
for(; idrawText(i,scaleTextYstart, QString("+%1").arg(s) );
qp->drawLine(i,scaleTextYstart, i, scaleTextYstart+5);
s = s + 20;
}
qp->setPen(lowLineColor);
qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart);
qp->setPen(Qt::red);
qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart);
}
wfview-1.2d/meter.h 0000664 0000000 0000000 00000004060 14151646264 0014325 0 ustar 00root root 0000000 0000000 #ifndef METER_H
#define METER_H
#include
#include
#include
#include
#include
#include
#include "rigcommander.h" // for meter types
class meter : public QWidget
{
Q_OBJECT
public:
explicit meter(QWidget *parent = nullptr);
signals:
public slots:
void paintEvent(QPaintEvent *);
void updateDrawing(int num);
void setLevels(int current, int peak, int average);
void setLevel(int current);
void clearMeterOnPTTtoggle();
void clearMeter();
void setMeterType(meterKind type);
void setMeterShortString(QString);
QString getMeterShortString();
meterKind getMeterType();
private:
//QPainter painter;
meterKind meterType;
QString meterShortString;
int fontSize = 10;
int length=30;
int current=0;
int peak = 0;
int average = 0;
int averageBalisticLength = 30;
int peakBalisticLength = 30;
int avgPosition=0;
int peakPosition=0;
std::vector avgLevels;
std::vector peakLevels;
int peakRedLevel=0;
bool drawLabels = true;
int mXstart = 0; // Starting point for S=0.
int mYstart = 14; // height, down from top, where the drawing starts
int barHeight = 10; // Height of meter "bar" indicators
int scaleLineYstart = 12;
int scaleTextYstart = 10;
int widgetWindowHeight = mYstart + barHeight + 0; // height of drawing canvis.
void drawScaleS(QPainter *qp);
void drawScaleCenter(QPainter *qp);
void drawScalePo(QPainter *qp);
void drawScaleRxdB(QPainter *qp);
void drawScaleALC(QPainter *qp);
void drawScaleSWR(QPainter *qp);
void drawScaleVd(QPainter *qp);
void drawScaleId(QPainter *qp);
void drawScaleComp(QPainter *qp);
void drawScaleRaw(QPainter *qp);
void drawLabel(QPainter *qp);
QString label;
QColor currentColor;
QColor averageColor;
QColor peakColor;
// S0-S9:
QColor lowTextColor;
QColor lowLineColor;
// S9+:
QColor highTextColor;
QColor highLineColor;
};
#endif // METER_H
wfview-1.2d/packettypes.h 0000664 0000000 0000000 00000027125 14151646264 0015554 0 ustar 00root root 0000000 0000000 #ifndef PACKETTYPES_H
#define PACKETTYPES_H
#include
#pragma pack(push, 1)
// Fixed Size Packets
#define CONTROL_SIZE 0x10
#define WATCHDOG_SIZE 0x14
#define PING_SIZE 0x15
#define OPENCLOSE_SIZE 0x16
#define RETRANSMIT_RANGE_SIZE 0x18
#define TOKEN_SIZE 0x40
#define STATUS_SIZE 0x50
#define LOGIN_RESPONSE_SIZE 0x60
#define LOGIN_SIZE 0x80
#define CONNINFO_SIZE 0x90
#define CAPABILITIES_SIZE 0xA8
// 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 {
quint32 len;
quint16 type;
quint16 seq;
quint32 sentid;
quint32 rcvdid;
};
char packet[CONTROL_SIZE];
} *control_packet_t;
// 0x14 length watchdog packet
typedef union watchdog_packet {
struct {
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
quint16 secondsa; // 0x10
quint16 secondsb; // 0x12
};
char packet[WATCHDOG_SIZE];
} *watchdog_packet_t;
// 0x15 length ping packet
// Also used for the slightly different civ header packet.
typedef union ping_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
char reply; // 0x10
union { // This contains differences between the send/receive packet
struct { // Ping
quint32 time; // 0x11
};
struct { // Send
quint16 datalen; // 0x11
quint16 sendseq; //0x13
};
};
};
char packet[PING_SIZE];
} *ping_packet_t, * data_packet_t, data_packet;
// 0x16 length open/close packet
typedef union openclose_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
quint16 data; // 0x10
char unused; // 0x11
quint16 sendseq; //0x13
char magic; // 0x15
};
char packet[OPENCLOSE_SIZE];
} *startstop_packet_t;
// 0x18 length audio packet
typedef union audio_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
quint16 ident; // 0x10
quint16 sendseq; // 0x12
quint16 unused; // 0x14
quint16 datalen; // 0x16
};
char packet[AUDIO_SIZE];
} *audio_packet_t;
// 0x18 length retransmit_range packet
typedef union retransmit_range_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
quint16 seq; // 0x06
quint32 sentid; // 0x08
quint32 rcvdid; // 0x0c
quint16 first; // 0x10
quint16 second; // 0x12
quint16 third; // 0x14
quint16 fourth; // 0x16
};
char packet[RETRANSMIT_RANGE_SIZE];
} *retransmit_range_packet_t;
// 0x18 length txaudio packet
/* tx[0] = static_cast(tx.length() & 0xff);
tx[1] = static_cast(tx.length() >> 8 & 0xff);
tx[18] = static_cast(sendAudioSeq >> 8 & 0xff);
tx[19] = static_cast(sendAudioSeq & 0xff);
tx[22] = static_cast(partial.length() >> 8 & 0xff);
tx[23] = static_cast(partial.length() & 0xff);*/
// 0x40 length token packet
typedef union token_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedd[7]; // 0x20
quint16 commoncap; // 0x27
char unuseddd[2]; // 0x29
char identa; // 0x2b
quint32 identb; // 0x2c
quint32 response; // 0x30
char unusede[12]; // 0x34
};
char packet[TOKEN_SIZE];
} *token_packet_t;
// 0x50 length login status packet
typedef union status_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
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
quint32 error; // 0x30
char unusedg[12]; // 0x34
char disc; // 0x40
char unusedh; // 0x41
quint16 civport; // 0x42 // Sent bigendian
quint16 unusedi; // 0x44 // Sent bigendian
quint16 audioport; // 0x46 // Sent bigendian
char unusedj[7]; // 0x49
};
char packet[STATUS_SIZE];
} *status_packet_t;
// 0x60 length login status packet
typedef union login_response_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
quint16 authstartid; // 0x20
char unusedd[14]; // 0x22
quint32 error; // 0x30
char unusede[12]; // 0x34
char connection[16]; // 0x40
char unusedf[16]; // 0x50
};
char packet[LOGIN_RESPONSE_SIZE];
} *login_response_packet_t;
// 0x80 length login packet
typedef union login_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
quint16 tokrequest; // 0x1a
quint32 token; // 0x1c
char unusedc[32]; // 0x20
char username[16]; // 0x40
char password[16]; // 0x50
char name[16]; // 0x60
char unusedf[16]; // 0x70
};
char packet[LOGIN_SIZE];
} *login_packet_t;
// 0x90 length conninfo and stream request packet
typedef union conninfo_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
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 { // This contains differences between the send/receive packet
struct { // Receive
quint32 busy; // 0x60
char computer[16]; // 0x64
char unusedi[16]; // 0x74
quint32 ipaddress; // 0x84
char unusedj[8]; // 0x78
};
struct { // Send
char username[16]; // 0x60
char rxenable; // 0x70
char txenable; // 0x71
char rxcodec; // 0x72
char txcodec; // 0x73
quint32 rxsample; // 0x74
quint32 txsample; // 0x78
quint32 civport; // 0x7c
quint32 audioport; // 0x80
quint32 txbuffer; // 0x84
quint8 convert; // 0x88
char unusedl[7]; // 0x89
};
};
};
char packet[CONNINFO_SIZE];
} *conninfo_packet_t;
// 0xA8 length capabilities packet
typedef union capabilities_packet {
struct
{
quint32 len; // 0x00
quint16 type; // 0x04
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
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 packet[CAPABILITIES_SIZE];
} *capabilities_packet_t;
#pragma pack(pop)
#endif // PACKETTYPES_H
wfview-1.2d/pttyhandler.cpp 0000664 0000000 0000000 00000023610 14151646264 0016104 0 ustar 00root root 0000000 0000000 #include "pttyhandler.h"
#include "logcategories.h"
#include
#include
#ifndef Q_OS_WIN
#include
#include
#include
#include
#endif
// Copyright 2017-2021 Elliott H. Liggett & Phil Taylor
pttyHandler::pttyHandler(QString pty)
{
//constructor
if (pty == "" || pty.toLower() == "none")
{
// Just return if pty is not configured.
return;
}
portName = pty;
#ifdef Q_OS_WIN
// 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.
baudRate = 115200;
stopBits = 1;
portName = pty;
#endif
openPort();
}
void pttyHandler::openPort()
{
serialError = false;
bool success=false;
#ifdef Q_OS_WIN
port = new QSerialPort();
port->setPortName(portName);
port->setBaudRate(baudRate);
port->setStopBits(QSerialPort::OneStop);// OneStop is other option
success = port->open(QIODevice::ReadWrite);
if (success) {
connect(port, &QSerialPort::readyRead, this, std::bind(&pttyHandler::receiveDataIn, this, (int)0));
}
#else
// Generic method in Linux/MacOS to find a pty
ptfd = ::posix_openpt(O_RDWR | O_NONBLOCK);
if (ptfd >=0)
{
qInfo(logSerial()) << "Opened pt device: " << ptfd << ", attempting to grant pt status";
if (grantpt(ptfd))
{
qInfo(logSerial()) << "Failed to grantpt";
return;
}
if (unlockpt(ptfd))
{
qInfo(logSerial()) << "Failed to unlock pt";
return;
}
// we're good!
qInfo(logSerial()) << "Opened pseudoterminal, slave name :" << ptsname(ptfd);
// Open the slave device to keep alive.
ptKeepAlive = open(ptsname(ptfd), O_RDONLY);
ptReader = new QSocketNotifier(ptfd, QSocketNotifier::Read, this);
connect(ptReader, &QSocketNotifier::activated,
this, &pttyHandler::receiveDataIn);
success=true;
}
#endif
if (!success)
{
ptfd = 0;
qInfo(logSerial()) << "Could not open pseudo terminal port, please restart.";
isConnected = false;
serialError = true;
emit haveSerialPortError(portName, "Could not open pseudo terminal port. Please restart.");
return;
}
#ifndef Q_OS_WIN
ptDevSlave = QString::fromLocal8Bit(ptsname(ptfd));
if (portName != "" && portName.toLower() != "none")
{
if (!QFile::link(ptDevSlave, portName))
{
qInfo(logSerial()) << "Error creating link to" << ptDevSlave << "from" << portName;
} else {
qInfo(logSerial()) << "Created link to" << ptDevSlave << "from" << portName;
}
}
#endif
isConnected = true;
}
pttyHandler::~pttyHandler()
{
this->closePort();
}
void pttyHandler::receiveDataFromRigToPtty(const QByteArray& data)
{
int fePos=data.lastIndexOf((char)0xfe);
if (fePos > 0 && data.length() > fePos+2)
fePos=fePos-1;
else
{
qDebug(logSerial()) << "Invalid command";
printHex(data,false,true);
}
if (disableTransceive && ((unsigned char)data[fePos + 2] == 0x00 || (unsigned char)data[fePos + 3] == 0x00))
{
// Ignore data that is sent to/from transceive address as client has requested transceive disabled.
qDebug(logSerial()) << "Transceive command filtered";
return;
}
if (isConnected && (unsigned char)data[fePos + 2] != 0xE1 && (unsigned char)data[fePos + 3] != 0xE1)
{
// send to the pseudo port as well
// index 2 is dest, 0xE1 is wfview, 0xE0 is assumed to be the other device.
// Changed to "Not 0xE1"
// 0xE1 = wfview
// 0xE0 = pseudo-term host
// 0x00 = broadcast to all
//qInfo(logSerial()) << "Sending data from radio to pseudo-terminal";
sendDataOut(data);
}
}
void pttyHandler::sendDataOut(const QByteArray& writeData)
{
qint64 bytesWritten = 0;
//qInfo(logSerial()) << "Data to pseudo term:";
//printHex(writeData, false, true);
if (isConnected) {
mutex.lock();
#ifdef Q_OS_WIN
bytesWritten = port->write(writeData);
#else
bytesWritten = ::write(ptfd, writeData.constData(), writeData.size());
#endif
if (bytesWritten != writeData.length()) {
qInfo(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\
<< " size of byte array: " << writeData.size()\
<< " Wrote all bytes? " << (bool)(bytesWritten == (qint64)writeData.size());
}
mutex.unlock();
}
}
void pttyHandler::receiveDataIn(int fd) {
#ifndef Q_OS_WIN
ssize_t available = 255; // Read up to 'available' bytes
#else
Q_UNUSED(fd);
#endif
// Linux will correctly return the number of available bytes with the FIONREAD ioctl
// Sadly MacOS always returns zero!
#ifdef Q_OS_LINUX
int ret = ::ioctl(fd, FIONREAD, (char *) &available);
if (ret != 0)
return;
#endif
#ifdef Q_OS_WIN
port->startTransaction();
inPortData = port->readAll();
#else
inPortData.resize(available);
ssize_t got = ::read(fd, inPortData.data(), available);
int err = errno;
if (got < 0) {
qInfo(logSerial()) << tr("Read failed: %1").arg(QString::fromLatin1(strerror(err)));
return;
}
inPortData.resize(got);
#endif
if (inPortData.startsWith("\xFE\xFE"))
{
if (inPortData.endsWith("\xFD"))
{
// good!
#ifdef Q_OS_WIN
port->commitTransaction();
#endif
int lastFE = inPortData.lastIndexOf((char)0xfe);
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;
}
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;
}
// filter C-IV transceive command before forwarding on.
if (inPortData.contains(rigCaps.transceiveCommand))
{
//qInfo(logSerial()) << "Filtered transceive command";
//printHex(inPortData, false, true);
QByteArray reply= QByteArrayLiteral("\xfe\xfe\x00\x00\xfb\xfd");
reply[2] = inPortData[3];
reply[3] = inPortData[2];
sendDataOut(inPortData); // Echo command back
sendDataOut(reply);
if (!disableTransceive) {
qInfo(logSerial()) << "pty requested CI-V Transceive disable";
disableTransceive = true;
}
}
else if (inPortData.length() > lastFE + 2 && ((quint8)inPortData[lastFE + 1] == civId || (quint8)inPortData[lastFE + 2] == civId))
{
emit haveDataFromPort(inPortData);
qDebug(logSerial()) << "Data from pseudo term:";
printHex(inPortData, false, true);
}
if (rolledBack)
{
// qInfo(logSerial()) << "Rolled back and was successfull. 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();
//printHex(inPortData, false, true);
rolledBack = true;
#ifdef Q_OS_WIN
port->rollbackTransaction();
}
}
else {
port->commitTransaction(); // do not emit data, do not keep data.
//qInfo(logSerial()) << "Warning: received data with invalid start. Dropping data.";
//qInfo(logSerial()) << "THIS SHOULD ONLY HAPPEN ONCE!!";
// THIS SHOULD ONLY HAPPEN ONCE!
}
#else
}
}
#endif
}
void pttyHandler::closePort()
{
#ifdef Q_OS_WIN
if (port != Q_NULLPTR)
{
port->close();
delete port;
}
#else
if (isConnected && portName != "" && portName.toLower() != "none")
{
QFile::remove(portName);
}
if (ptKeepAlive > 0) {
close(ptKeepAlive);
}
#endif
isConnected = false;
}
void pttyHandler::debugThis()
{
// Do not use, function is for debug only and subject to change.
qInfo(logSerial()) << "comm debug called.";
inPortData = port->readAll();
emit haveDataFromPort(inPortData);
}
void pttyHandler::printHex(const QByteArray& pdata, bool printVert, bool printHoriz)
{
qDebug(logSerial()) << "---- 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(logSerial()) << strings.at(i);
}
}
if (printHoriz)
{
qDebug(logSerial()) << index;
qDebug(logSerial()) << sdata;
}
qDebug(logSerial()) << "----- End hex dump -----";
}
void pttyHandler::receiveFoundRigID(rigCapabilities rigCaps) {
this->rigCaps = rigCaps;
qInfo(logSerial) << "Received rigCapabilities for" << rigCaps.modelName;
}
wfview-1.2d/pttyhandler.h 0000664 0000000 0000000 00000003663 14151646264 0015557 0 ustar 00root root 0000000 0000000 #ifndef PTTYHANDLER_H
#define PTTYHANDLER_H
#include
#include
#include
#include
#include
#include
#include "rigidentities.h"
// This class abstracts the comm port in a useful way and connects to
// the command creator and command parser.
class pttyHandler : public QObject
{
Q_OBJECT
public:
pttyHandler(QString portName);
pttyHandler(QString portName, quint32 baudRate);
bool serialError;
~pttyHandler();
private slots:
void receiveDataIn(int fd); // from physical port
void receiveDataFromRigToPtty(const QByteArray& data);
void debugThis();
void receiveFoundRigID(rigCapabilities rigCaps);
signals:
void haveTextMessage(QString message); // status, debug only
void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander
void haveSerialPortError(const QString port, const QString error);
void haveStatusUpdate(const QString text);
private:
void setupPtty();
void openPort();
void closePort();
void sendDataOut(const QByteArray& writeData); // out to radio
void debugMe();
void hexPrint();
//QDataStream stream;
QByteArray outPortData;
QByteArray inPortData;
//QDataStream outStream;
//QDataStream inStream;
unsigned char buffer[256];
QString portName;
QSerialPort* port = Q_NULLPTR;
qint32 baudRate;
unsigned char stopBits;
bool rolledBack;
int ptfd; // pseudo-terminal file desc.
int ptKeepAlive=0; // Used to keep the pty alive after client disconects.
bool havePt;
QString ptDevSlave;
bool isConnected=false; // port opened
mutable QMutex mutex;
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
bool disableTransceive = false;
QSocketNotifier *ptReader = Q_NULLPTR;
quint8 civId=0;
rigCapabilities rigCaps;
};
#endif // PTTYHANDLER_H
wfview-1.2d/qdarkstyle/ 0000775 0000000 0000000 00000000000 14151646264 0015223 5 ustar 00root root 0000000 0000000 wfview-1.2d/qdarkstyle/rc/ 0000775 0000000 0000000 00000000000 14151646264 0015627 5 ustar 00root root 0000000 0000000 wfview-1.2d/qdarkstyle/rc/Hmovetoolbar.png 0000664 0000000 0000000 00000000334 14151646264 0020776 0 ustar 00root root 0000000 0000000 PNG
IHDR @ } bKGD y yS pHYs tIME-J iTXtComment Created with GIMPd.e @IDATX1
@}[_SE2v^%"ft 6ABxWD IENDB` wfview-1.2d/qdarkstyle/rc/Hsepartoolbar.png 0000664 0000000 0000000 00000000254 14151646264 0021143 0 ustar 00root root 0000000 0000000 PNG
IHDR ? ,{ bKGD y yS pHYs tIME.Į 9IDAT8c` 3ǣ1aSSΑ(ɂf
QQQI|h*I> K9.? IENDB` wfview-1.2d/qdarkstyle/rc/Vmovetoolbar.png 0000664 0000000 0000000 00000000344 14151646264 0021015 0 ustar 00root root 0000000 0000000 PNG
IHDR 6
sRGB bKGD ަ pHYs tIME *+\ dIDATHc0<4Oͪs]9H+5yȈ$|O-5sl&}MTRM&ZjFhRIzLKl4c z0J q-n IENDB` wfview-1.2d/qdarkstyle/rc/Vsepartoolbar.png 0000664 0000000 0000000 00000000273 14151646264 0021162 0 ustar 00root root 0000000 0000000 PNG
IHDR ? v sRGB bKGD pHYs tIME 5+URj ;IDAT8c`#0}*%v;s_9Si4 M!xc~T idkX IENDB` wfview-1.2d/qdarkstyle/rc/branch_closed-on.png 0000664 0000000 0000000 00000000223 14151646264 0021532 0 ustar 00root root 0000000 0000000 PNG
IHDR bKGD ӵW\ pHYs tIME+J<0t $IDATc`@XL\&dY&dp##Ȉa
+u IENDB` wfview-1.2d/qdarkstyle/rc/branch_closed.png 0000664 0000000 0000000 00000000240 14151646264 0021117 0 ustar 00root root 0000000 0000000 PNG
IHDR sRGB bKGD S4] pHYs tIME)G $IDATc`@s>XL\&dY&dpN