Merge branch 'audio-enhance' into shuttle

half-duplex
Phil Taylor 2021-07-08 15:35:44 +01:00
commit 20f3e70d2a
21 zmienionych plików z 1462 dodań i 135 usunięć

135
CHANGELOG
Wyświetl plik

@ -1,8 +1,143 @@
# CHANGELOG
- 20210706
Local af gain now has anti-log audio pot taper.
Only start audio when stream is ready
- 20210705
Added transceiver adjustment window show code, for the debug button only
currently.
Added window title text change to show radio model. Needs to be checked
cross-platform. On Linux Mint, displays: "IC-7300 -- wfview"
Applying setup.volume within audio handler if the audio handled is an
output. Added this to the init function.
waterfall theme is now saved.
Added local af gain and wf length to the preferences.
- 20210702
fixed small error where the tx latency was not copied in the UI
- 20210626
Merge branch 'sequence' of gitlab.com:eliggett/wfview into sequence
Duplicate of existing command.
Remove unnecessary escape sequence
Fix for fix of missing __PRETTY_FUNCTION__
Add __PRETTY_FUNCTION__ for compilers that don't have it.
- 20210625
Mode changes from the combo box now use the que. There are still other
methods to change mode which will transition shortly.
Faster PTT
Added PTT to the queue.
Added unique priority insertion methods.
Changed how commands with parameter data are added.
Initial queued "set" command commit. Only the frequency set command is
used so far, and only for the "Frequency" tab and the tuning knob.
- 20210624
Quick hack for WFM forcing FIL1 always
- 20210621
Added polling button
Moving to std::deque (double-ended que).
- 20210620
IC-R8600 span is now received into the UI correctly.
New unified outgoing command queue. Tested on IC-9700 and IC-718 (to
remote wfview server). CPU usage seems higher but please check your
system.
Timing seems to be acceptable but could probably use some tweaks.
S-meter polling is 25ms for fast radios, and slower rates for slower
radios. Half-duplex serial radios receive 3x slower polling to make room
for replies.
For Freq, Mode, etc "regular" constant polling (new feature):
IC-9700 polling is 5 per second, IC-718 is 1-2 per second.
Just helps keep the UI in sync with changes taking place at the rig. The
polling is slow enough that it doesn't impact anything. But quick enough
that it catches discrepencies pretty quickly.
- 20210619
Added a few more slider things
whatsnew: improved IC-R8600
- 20210618
Additional support for the IC-R8600, including wider scope spans.
Minor change to remove some old debug code that snuck in.
If no rig caps, then don't mess with the window!
Added full duplex comms parameter to rigCaps. We assume half-duplex
until we receive a reply to rigID.
Fixed accidental s-meter timing parameter change.
- 20210617
Radios without spectrum do not show spectrum, and, the window properly
resizes for those controls. Also, a new key command, control-shift-d has
been added to run debug functions from any tab in the program.
- 20210615
Additional code to hide/show spectrum and correcting an issue with the
rig name not populating for non-spectrum radios.
Dynamic show/hide spectrum for rigs without this feature.
Additional data corruption checking.
- 20210614
Changed collision detection code so that we can more easily see what
message was missed.
Added collision detection for serial commands. Collisions are aparently
frequent for true 1-wire CI-V radios.
We now calculate polling rates immediately upon receiveCommReady for
serial connections. For network connections, we assume sane values and
modify once we receive the baud rate from the server.
Add Neon (ARM) support to resampler
Revert to using resampler directory rather than opus-tools submodule
- 20210612
Add tooltip showing percentage of TX power when slider is moved
- 20210611
adding a second path/way for the qcustomplot link if the first fails
Update udpserver.cpp
Use global watchdog rather than per-connection

Wyświetl plik

@ -1,7 +1,7 @@
The following highlights are in this dot-release:
added IC7700
added IC7700, 718, 7100. (more testing needed)
fixes and improvements to audio
ability to choose between rtaudio and qtmultimedia (default) as compile time option
fixes and lots of improvements at the usb-server end
@ -12,3 +12,5 @@ The following highlights are in this dot-release:
wf display disappears when there is no wf capable rig
IC R8600 support improved
for older rigs added a polling timing box to keep stuff keeping smooth
Local af gain now has anti-log audio pot taper.

111
aboutbox.cpp 100644
Wyświetl plik

@ -0,0 +1,111 @@
#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 1.1a");
QString head = QString("<html><head></head><body>");
QString copyright = QString("Copyright 2017-2021 Elliott H. Liggett, W6EL. All rights reserved. wfview source code is <a href=\"https://gitlab.com/eliggett/wfview/-/blob/master/LICENSE\">licensed</a> under the GNU GPLv3.");
QString nacode = QString("<br/><br/>Networking, audio, rigctl server, and much more written by Phil Taylor, M0VSE");
QString doctest = QString("<br/><br/>Testing, documentation, bug fixes, and development mentorship from<br/>Roeland Jansen, PA3MET, and Jim Nijkamp, PA8E.");
QString ssCredit = QString("<br/><br/>Stylesheet <a href=\"https://github.com/ColinDuquesnoy/QDarkStyleSheet/tree/master/qdarkstyle\" style=\"color: cyan;\">qdarkstyle</a> used under MIT license, stored in /usr/share/wfview/stylesheets/.");
QString website = QString("<br/><br/>Please visit <a href=\"https://wfview.org/\" style=\"color: cyan;\">https://wfview.org/</a> for the latest information.");
QString docs = QString("<br/><br/>Be sure to check the <a href=\"https://wfview.org/wfview-user-manual/\" style=\"color: cyan;\">User Manual</a> and <a href=\"https://forum.wfview.org/\" style=\"color: cyan;\">the Forum</a> if you have any questions.");
QString support = QString("<br/><br/>For support, please visit <a href=\"https://forum.wfview.org/\">the official wfview support forum</a>.");
QString gitcodelink = QString("<a href=\"https://gitlab.com/eliggett/wfview/-/tree/%1\" style=\"color: cyan;\">").arg(GITSHORT);
QString contact = QString("<br/>email W6EL: kilocharlie8@gmail.com");
QString buildInfo = QString("<br/><br/>Build " + gitcodelink + QString(GITSHORT) + "</a> on " + QString(__DATE__) + " at " + __TIME__ + " by " + UNAME + "@" + HOST);
QString end = QString("</body></html>");
// Short credit strings:
QString rsCredit = QString("<br/><br/><a href=\"https://www.speex.org/\" style=\"color: cyan;\">Speex</a> Resample library Copyright 2003-2008 Jean-Marc Valin");
#if defined(RTAUDIO)
QString rtaudiocredit = QString("<br/><br/>RT Audio, from <a href=\"https://www.music.mcgill.ca/~gary/rtaudio/index.html\">Gary P. Scavone</a>");
#endif
#if defined(PORTAUDIO)
QString portaudiocredit = QString("<br/><br/>Port Audio, from <a href=\"http://portaudio.com\">The Port Audio Community</a>");
#endif
QString qcpcredit = QString("<br/><br/>The waterfall and spectrum plot graphics use QCustomPlot, from <a href=\"https://www.qcustomplot.com/\">Emanuel Eichhammer</a>");
QString qtcredit = QString("<br/><br/>This copy of wfview was built against Qt version %1").arg(QT_VERSION_STR);
// Acknowledgement:
QString wfviewcommunityack = QString("<br/><br/>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("<br/><br/>Special thanks to Norbert Varga, and the <a href=\"https://github.com/nonoo/kappanhang\">nonoo/kappanhang team</a> 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("<br/><br/>");
aboutText.append("<pre>" + sxcreditcopyright + "</pre>");
aboutText.append("<br/><br/>");
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/"));
}

26
aboutbox.h 100644
Wyświetl plik

@ -0,0 +1,26 @@
#ifndef ABOUTBOX_H
#define ABOUTBOX_H
#include <QWidget>
#include <QDesktopServices>
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

74
aboutbox.ui 100644
Wyświetl plik

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>aboutbox</class>
<widget class="QWidget" name="aboutbox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>567</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="logoBtn">
<property name="minimumSize">
<size>
<width>0</width>
<height>128</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="iconSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="topText">
<property name="text">
<string>wfview version</string>
</property>
</widget>
</item>
<item>
<widget class="QTextBrowser" name="midTextBox">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Detailed text here&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bottomText">
<property name="text">
<string>Build String</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Wyświetl plik

@ -76,7 +76,10 @@ bool audioHandler::init(audioSetup setupIn)
tempBuf.sent = 0;
if(!setup.isinput)
{
this->setVolume(setup.localAFgain);
}
#if defined(RTAUDIO)
#if !defined(Q_OS_MACX)
@ -285,7 +288,9 @@ void audioHandler::start()
void audioHandler::setVolume(unsigned char volume)
{
this->volume = (qreal)volume/255.0;
//this->volume = (qreal)volume/255.0;
this->volume = audiopot[volume];
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")";
}

Wyświetl plik

@ -33,7 +33,7 @@ typedef signed short MY_TYPE;
#include <QMap>
#include "resampler/speex_resampler.h"
#include "ring/ring.h"
#include "audiotaper.h"
#include <QDebug>
@ -67,6 +67,7 @@ struct audioSetup {
QAudioDeviceInfo port;
#endif
quint8 resampleQuality;
unsigned char localAFgain;
};
// For QtMultimedia, use a native QIODevice

273
audiotaper.h 100644
Wyświetl plik

@ -0,0 +1,273 @@
#ifndef AUDIOTAPER_H
#define AUDIOTAPER_H
#include <QtMath>
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

Wyświetl plik

@ -37,6 +37,7 @@ enum mode_kind {
struct mode_info {
mode_kind mk;
unsigned char reg;
unsigned char filter;
QString name;
};

Wyświetl plik

@ -12,4 +12,9 @@ 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

Wyświetl plik

@ -117,7 +117,7 @@ void pttyHandler::receiveDataFromRigToPtty(const QByteArray& data)
{
int fePos=data.lastIndexOf((char)0xfe);
if (fePos>0)
if (fePos > 0 && data.length() > fePos+2)
fePos=fePos-1;
else
{
@ -125,7 +125,14 @@ void pttyHandler::receiveDataFromRigToPtty(const QByteArray& data)
printHex(data,false,true);
}
if (isConnected && (unsigned char)data[fePos+2] != 0xE1 && (unsigned char)data[fePos+3] != 0xE1)
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.
@ -133,7 +140,7 @@ void pttyHandler::receiveDataFromRigToPtty(const QByteArray& data)
// 0xE1 = wfview
// 0xE0 = pseudo-term host
// 0x00 = broadcast to all
//qInfo(logSerial()) << "Sending data from radio to pseudo-terminal";
//qInfo(logSerial()) << "Sending data from radio to pseudo-terminal";
sendDataOut(data);
}
}
@ -221,12 +228,16 @@ void pttyHandler::receiveDataIn(int fd) {
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);
//qInfo(logSerial()) << "Data from pseudo term:";
//printHex(inPortData, false, true);
qDebug(logSerial()) << "Data from pseudo term:";
printHex(inPortData, false, true);
}
if (rolledBack)

Wyświetl plik

@ -65,7 +65,7 @@ private:
bool isConnected=false; // port opened
mutable QMutex mutex;
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
bool disableTransceive = false;
QSocketNotifier *ptReader = nullptr;
quint8 civId=0;
};

Wyświetl plik

@ -133,7 +133,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud
connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString)));
connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis()));
emit haveAfGain(255);
emit haveAfGain(rxSetup.localAFgain);
}
// data from the comm port to the program:
@ -665,6 +666,28 @@ void rigCommander::setRitValue(int ritValue)
prepDataAndSend(payload);
}
void rigCommander::setMode(mode_info m)
{
QByteArray payload;
if(rigCaps.model==model706)
{
m.filter = '\x01';
}
if(m.mk == modeWFM)
{
m.filter = '\x01';
}
payload.setRawData("\x06", 1);
payload.append(m.reg);
payload.append(m.filter);
prepDataAndSend(payload);
rigState.mode = m.reg;
rigState.filter = m.filter;
}
void rigCommander::setMode(unsigned char mode, unsigned char modeFilter)
{
QByteArray payload;
@ -683,7 +706,12 @@ void rigCommander::setMode(unsigned char mode, unsigned char modeFilter)
{
payload.append("\x01"); // "normal" on IC-706
} else {
payload.append(modeFilter);
if(mode == 0x06)
{
payload.append(0x01);
} else {
payload.append(modeFilter);
}
}
prepDataAndSend(payload);

Wyświetl plik

@ -119,6 +119,7 @@ public slots:
void setFrequency(freqt freq);
void getFrequency();
void setMode(unsigned char mode, unsigned char modeFilter);
void setMode(mode_info);
void getMode();
void setDataMode(bool dataOn, unsigned char filter);
void getDataMode();

Wyświetl plik

@ -0,0 +1,16 @@
#include "transceiveradjustments.h"
#include "ui_transceiveradjustments.h"
transceiverAdjustments::transceiverAdjustments(QWidget *parent) :
QWidget(parent),
ui(new Ui::transceiverAdjustments)
{
ui->setupUi(this);
// request level updates
}
transceiverAdjustments::~transceiverAdjustments()
{
delete ui;
}

Wyświetl plik

@ -0,0 +1,22 @@
#ifndef TRANSCEIVERADJUSTMENTS_H
#define TRANSCEIVERADJUSTMENTS_H
#include <QWidget>
namespace Ui {
class transceiverAdjustments;
}
class transceiverAdjustments : public QWidget
{
Q_OBJECT
public:
explicit transceiverAdjustments(QWidget *parent = 0);
~transceiverAdjustments();
private:
Ui::transceiverAdjustments *ui;
};
#endif // TRANSCEIVERADJUSTMENTS_H

Wyświetl plik

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>transceiverAdjustments</class>
<widget class="QWidget" name="transceiverAdjustments">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>680</width>
<height>339</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Transmitter</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="verticalSlider">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Comp</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QSlider" name="verticalSlider_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Bass</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QSlider" name="verticalSlider_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Trebble</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QSlider" name="verticalSlider_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>TBD</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Receiver</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QSlider" name="verticalSlider_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Bass</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSlider" name="verticalSlider_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Trebble</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QSlider" name="verticalSlider_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>IF Shift</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QSlider" name="verticalSlider_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_2">
<property name="text">
<string>NR</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QSlider" name="verticalSlider_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_3">
<property name="text">
<string>NB</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QSlider" name="verticalSlider_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_4">
<property name="text">
<string>Notch</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Bandwidth</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Low</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_2"/>
</item>
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>High</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox"/>
</item>
<item>
<widget class="QLabel" name="label_9">
<property name="text">
<string>Filter</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBox_3"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

Wyświetl plik

@ -195,7 +195,7 @@ void udpHandler::dataReceived()
if (txSetup.codec == 0) {
txString = "(no tx)";
}
emit haveNetworkStatus(QString("<pre>%1 rx latency: %2 ms / rtt: %3 ms / loss: %4/%5</pre>").arg(txString).arg(tempLatency).arg(latency, 3).arg(totallost, 3).arg(totalsent, 3));
emit haveNetworkStatus(QString("<pre>%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5</pre>").arg(txString).arg(tempLatency).arg(latency, 3).arg(totallost, 3).arg(totalsent, 3));
}
break;
}
@ -1133,6 +1133,10 @@ void udpBase::dataReceived(QByteArray r)
{
rxBufferMutex.lock();
if (rxSeqBuf.isEmpty()) {
if (rxSeqBuf.size() > 400)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
else
@ -1153,6 +1157,10 @@ void udpBase::dataReceived(QByteArray r)
{
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
rxSeqBuf.insert(in->seq, QTime::currentTime());
if (rxSeqBuf.size() > 400)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
}
else {
// This is probably one of our missing packets!
@ -1208,7 +1216,15 @@ void udpBase::sendRetransmitRequest()
{
// We haven't seen this missing packet before
qDebug(logUdp()) << this->metaObject()->className() << ": Adding to missing buffer (len=" << rxMissing.size() << "): " << j;
if (rxMissing.size() > 25)
{
rxMissing.erase(rxMissing.begin());
}
rxMissing.insert(j, 0);
if (rxSeqBuf.size() > 400)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(j, QTime::currentTime()); // Add this missing packet to the rxbuffer as we now long about it.
packetsLost++;
}
@ -1351,11 +1367,17 @@ void udpBase::sendTrackedPacket(QByteArray d)
congestion = 0;
}
txSeqBuf.insert(sendSeq,s);
if (txSeqBuf.size() > 400)
{
txSeqBuf.erase(txSeqBuf.begin());
}
txBufferMutex.unlock();
} else {
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
}
purgeOldEntries(); // Delete entries older than PURGE_SECONDS seconds (currently 5)
// Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet.
//qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <<txSeqBuf.size() << "MISS:" << rxMissing.size();
//purgeOldEntries(); // Delete entries older than PURGE_SECONDS seconds (currently 5)
sendSeq++;
udpMutex.lock();

Wyświetl plik

@ -25,7 +25,9 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
cal = new calibrationWindow();
rpt = new repeaterSetup();
sat = new satelliteSetup();
trxadj = new transceiverAdjustments();
srv = new udpServerSetup();
abtBox = new aboutbox();
connect(this, SIGNAL(sendServerConfig(SERVERCONFIG)), srv, SLOT(receiveServerConfig(SERVERCONFIG)));
connect(srv, SIGNAL(serverConfig(SERVERCONFIG, bool)), this, SLOT(serverConfigRequested(SERVERCONFIG, bool)));
@ -38,6 +40,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s
qRegisterMetaType<meterKind>();
qRegisterMetaType<spectrumMode>();
qRegisterMetaType<freqt>();
qRegisterMetaType<mode_info>();
qRegisterMetaType<audioPacket>();
qRegisterMetaType <audioSetup>();
@ -267,6 +270,7 @@ void wfmain::rigConnections()
connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char)));
connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char)));
connect(this, SIGNAL(setMode(mode_info)), rig, SLOT(setMode(mode_info)));
// Levels (read and write)
// Levels: Query:
@ -880,6 +884,12 @@ void wfmain::setUIToPrefs()
ui->drawPeakChk->setChecked(prefs.drawPeaks);
on_drawPeakChk_clicked(prefs.drawPeaks);
drawPeaks = prefs.drawPeaks;
ui->wfLengthSlider->setValue(prefs.wflength);
prepareWf(prefs.wflength);
ui->wfthemeCombo->setCurrentIndex(ui->wfthemeCombo->findData(prefs.wftheme));
colorMap->setGradient(static_cast<QCPColorGradient::GradientPreset>(prefs.wftheme));
}
void wfmain::setAudioDevicesUI()
@ -1217,6 +1227,9 @@ void wfmain::setDefPrefs()
defPrefs.enableRigCtlD = false;
defPrefs.rigCtlPort = 4533;
defPrefs.virtualSerialPort = QString("none");
defPrefs.localAFgain = 255;
defPrefs.wflength = 160;
defPrefs.wftheme = static_cast<int>(QCPColorGradient::gpJet);
udpDefPrefs.ipAddress = QString("");
udpDefPrefs.controlLANPort = 50001;
@ -1238,7 +1251,9 @@ void wfmain::loadSettings()
prefs.useFullScreen = settings->value("UseFullScreen", defPrefs.useFullScreen).toBool();
prefs.useDarkMode = settings->value("UseDarkMode", defPrefs.useDarkMode).toBool();
prefs.useSystemTheme = settings->value("UseSystemTheme", defPrefs.useSystemTheme).toBool();
prefs.wftheme = settings->value("WFTheme", defPrefs.wftheme).toInt();
prefs.drawPeaks = settings->value("DrawPeaks", defPrefs.drawPeaks).toBool();
prefs.wflength = (unsigned int) settings->value("WFLength", defPrefs.wflength).toInt();
prefs.stylesheetPath = settings->value("StylesheetPath", defPrefs.stylesheetPath).toString();
ui->splitter->restoreState(settings->value("splitter").toByteArray());
@ -1322,7 +1337,8 @@ void wfmain::loadSettings()
ui->vspCombo->setCurrentIndex(ui->vspCombo->count()-1);
}
prefs.localAFgain = (unsigned char) settings->value("localAFgain", defPrefs.localAFgain).toUInt();
rxSetup.localAFgain = prefs.localAFgain;
settings->endGroup();
// Misc. user settings (enable PTT, draw peaks, etc)
@ -1378,7 +1394,7 @@ void wfmain::loadSettings()
txSetup.latency = settings->value("AudioTXLatency", "150").toInt();
ui->txLatencySlider->setEnabled(ui->lanEnableBtn->isChecked());
ui->txLatencySlider->setValue(rxSetup.latency);
ui->txLatencySlider->setValue(txSetup.latency);
ui->txLatencySlider->setTracking(false); // Stop it sending value on every change.
ui->audioSampleRateCombo->blockSignals(true);
@ -1527,10 +1543,12 @@ void wfmain::saveSettings()
settings->setValue("UseSystemTheme", prefs.useSystemTheme);
settings->setValue("UseDarkMode", prefs.useDarkMode);
settings->setValue("DrawPeaks", prefs.drawPeaks);
settings->setValue("WFTheme", prefs.wftheme);
settings->setValue("StylesheetPath", prefs.stylesheetPath);
settings->setValue("splitter", ui->splitter->saveState());
settings->setValue("windowGeometry", saveGeometry());
settings->setValue("windowState", saveState());
settings->setValue("WFLength", prefs.wflength);
settings->endGroup();
// Radio and Comms: C-IV addr, port to use
@ -1539,6 +1557,7 @@ void wfmain::saveSettings()
settings->setValue("SerialPortRadio", prefs.serialPortRadio);
settings->setValue("SerialPortBaud", prefs.serialPortBaud);
settings->setValue("VirtualSerialPort", prefs.virtualSerialPort);
settings->setValue("localAFgain", prefs.localAFgain);
settings->endGroup();
// Misc. user settings (enable PTT, draw peaks, etc)
@ -1967,7 +1986,8 @@ void wfmain::shortcutMinus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -1980,7 +2000,8 @@ void wfmain::shortcutPlus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2019,7 +2040,8 @@ void wfmain::shortcutShiftMinus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2032,7 +2054,8 @@ void wfmain::shortcutShiftPlus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2045,7 +2068,8 @@ void wfmain::shortcutControlMinus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2058,7 +2082,8 @@ void wfmain::shortcutControlPlus()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2071,7 +2096,8 @@ void wfmain::shortcutPageUp()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2084,7 +2110,8 @@ void wfmain::shortcutPageDown()
f.MHzDouble = f.Hz / (double)1E6;
setUIFreq();
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
}
@ -2311,6 +2338,90 @@ void wfmain::setPlotTheme(QCustomPlot *plot, bool isDark)
}
}
void wfmain::doCmd(commandtype cmddata)
{
cmds cmd = cmddata.cmd;
std::shared_ptr<void> data = cmddata.data;
// This switch is for commands with parameters.
// the "default" for non-parameter commands is to call doCmd(cmd).
switch (cmd)
{
case cmdSetFreq:
{
freqt f = (*std::static_pointer_cast<freqt>(data));
emit setFrequency(f);
break;
}
case cmdSetMode:
{
mode_info m = (*std::static_pointer_cast<mode_info>(data));
emit setMode(m);
break;
}
case cmdSetTxPower:
{
unsigned char txpower = (*std::static_pointer_cast<unsigned char>(data));
emit setTxPower(txpower);
break;
}
case cmdSetMicGain:
{
unsigned char micgain = (*std::static_pointer_cast<unsigned char>(data));
emit setTxPower(micgain);
break;
}
case cmdSetRxRfGain:
{
unsigned char rfgain = (*std::static_pointer_cast<unsigned char>(data));
emit setRfGain(rfgain);
break;
}
case cmdSetModLevel:
{
unsigned char modlevel = (*std::static_pointer_cast<unsigned char>(data));
rigInput currentIn;
if(usingDataMode)
{
currentIn = currentModDataSrc;
} else {
currentIn = currentModSrc;
}
emit setModLevel(currentIn, modlevel);
break;
}
case cmdSetAfGain:
{
unsigned char afgain = (*std::static_pointer_cast<unsigned char>(data));
emit setAfGain(afgain);
break;
}
case cmdSetSql:
{
unsigned char sqlLevel = (*std::static_pointer_cast<unsigned char>(data));
emit setSql(sqlLevel);
break;
}
case cmdSetPTT:
{
bool pttrequest = (*std::static_pointer_cast<bool>(data));
emit setPTT(pttrequest);
break;
}
case cmdSetATU:
{
bool atuOn = (*std::static_pointer_cast<bool>(data));
emit setATU(atuOn);
break;
}
default:
doCmd(cmd);
break;
}
}
void wfmain::doCmd(cmds cmd)
{
// Use this function to take action upon a command.
@ -2420,6 +2531,9 @@ void wfmain::doCmd(cmds cmd)
case cmdGetATUStatus:
emit getATUStatus();
break;
case cmdStartATU:
emit startATU();
break;
case cmdGetAttenuator:
emit getAttenuator();
break;
@ -2484,6 +2598,7 @@ void wfmain::doCmd(cmds cmd)
}
break;
default:
qInfo(logSystem()) << __PRETTY_FUNCTION__ << "WARNING: Command fell through of type: " << (unsigned int)cmd;
break;
}
}
@ -2498,9 +2613,9 @@ void wfmain::sendRadioCommandLoop()
// if ther's a command waiting, run it.
if(!delayedCmdQue.empty())
{
cmds cmd = delayedCmdQue.front();
commandtype cmddata = delayedCmdQue.front();
delayedCmdQue.pop_front();
doCmd(cmd);
doCmd(cmddata);
} else if(!(loopTickCounter % 10))
{
// pick from useful queries to make now and then
@ -2528,14 +2643,20 @@ void wfmain::sendRadioCommandLoop()
void wfmain::issueDelayedCommand(cmds cmd)
{
// Append to end of command queue
delayedCmdQue.push_back(cmd);
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = NULL;
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueDelayedCommandPriority(cmds cmd)
{
// Places the new command at the top of the queue
// Use only when needed.
delayedCmdQue.push_front(cmd);
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = NULL;
delayedCmdQue.push_front(cmddata);
}
void wfmain::issueDelayedCommandUnique(cmds cmd)
@ -2543,11 +2664,133 @@ void wfmain::issueDelayedCommandUnique(cmds cmd)
// Use this function to insert commands where
// multiple (redundant) commands don't make sense.
if( std::find(delayedCmdQue.begin(), delayedCmdQue.end(), cmd ) == delayedCmdQue.end())
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = NULL;
// The following is both expensive and not that great,
// since it does not check if the arguments are the same.
bool found = false;
for(unsigned int i=0; i < delayedCmdQue.size(); i++)
{
delayedCmdQue.push_front(cmd);
if(delayedCmdQue.at(i).cmd == cmd)
{
found = true;
break;
}
}
if(!found)
{
delayedCmdQue.push_front(cmddata);
}
// if( std::find(delayedCmdQue.begin(), delayedCmdQue.end(), cmddata ) == delayedCmdQue.end())
// {
// delayedCmdQue.push_front(cmddata);
// }
}
void wfmain::issueCmd(cmds cmd, mode_info m)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<mode_info>(new mode_info(m));
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmd(cmds cmd, freqt f)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<freqt>(new freqt(f));
//*static_cast<freqt*>(cmddata.data.get()) = f;
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmd(cmds cmd, int i)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<int>(new int(i));
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmd(cmds cmd, char c)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<char>(new char(c));
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmd(cmds cmd, bool b)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<bool>(new bool(b));
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmd(cmds cmd, unsigned char c)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<unsigned char>(new unsigned char(c));
delayedCmdQue.push_back(cmddata);
}
void wfmain::issueCmdUniquePriority(cmds cmd, bool b)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<bool>(new bool(b));
delayedCmdQue.push_front(cmddata);
removeSimilarCommand(cmd);
}
void wfmain::issueCmdUniquePriority(cmds cmd, unsigned char c)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<unsigned char>(new unsigned char(c));
delayedCmdQue.push_front(cmddata);
removeSimilarCommand(cmd);
}
void wfmain::issueCmdUniquePriority(cmds cmd, char c)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<char>(new char(c));
delayedCmdQue.push_front(cmddata);
removeSimilarCommand(cmd);
}
void wfmain::issueCmdUniquePriority(cmds cmd, freqt f)
{
commandtype cmddata;
cmddata.cmd = cmd;
cmddata.data = std::shared_ptr<freqt>(new freqt(f));
delayedCmdQue.push_front(cmddata);
removeSimilarCommand(cmd);
}
void wfmain::removeSimilarCommand(cmds cmd)
{
// pop anything out that is of the same kind of command:
// pop anything out that is of the same kind of command:
// Start at 1 since we put one in at zero that we want to keep.
for(unsigned int i=1; i < delayedCmdQue.size(); i++)
{
if(delayedCmdQue.at(i).cmd == cmd)
{
//delayedCmdQue[i].cmd = cmdNone;
delayedCmdQue.erase(delayedCmdQue.begin()+i);
// i -= 1;
}
}
}
void wfmain::receiveRigID(rigCapabilities rigCaps)
@ -2570,6 +2813,7 @@ void wfmain::receiveRigID(rigCapabilities rigCaps)
this->rigCaps = rigCaps;
rigName->setText(rigCaps.modelName);
setWindowTitle(rigCaps.modelName);
this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true.
haveRigCaps = true;
// Added so that server receives rig capabilities.
@ -2691,14 +2935,17 @@ void wfmain::receiveRigID(rigCapabilities rigCaps)
}
ui->scopeBWCombo->blockSignals(false);
setBandButtons();
ui->tuneEnableChk->setEnabled(rigCaps.hasATU);
ui->tuneNowBtn->setEnabled(rigCaps.hasATU);
ui->connectBtn->setText("Disconnect"); // We must be connected now.
prepareWf();
prepareWf(ui->wfLengthSlider->value());
if(usingLAN)
{
ui->afGainSlider->setValue(prefs.localAFgain);
}
// Adding these here because clearly at this point we have valid
// rig comms. In the future, we should establish comms and then
// do all the initial grabs. For now, this hack of adding them here and there:
@ -2915,7 +3162,8 @@ void wfmain::handlePlotDoubleClick(QMouseEvent *me)
freq.Hz = roundFrequency(freq.Hz, tsWfScrollHz);
emit setFrequency(freq);
//emit setFrequency(freq);
issueCmd(cmdSetFreq, freq);
issueDelayedCommand(cmdGetFreq);
showStatusBarText(QString("Going to %1 MHz").arg(x));
}
@ -2936,7 +3184,8 @@ void wfmain::handleWFDoubleClick(QMouseEvent *me)
freq.Hz = roundFrequency(freq.Hz, tsWfScrollHz);
emit setFrequency(freq);
//emit setFrequency(freq);
issueCmd(cmdSetFreq, freq);
issueDelayedCommand(cmdGetFreq);
showStatusBarText(QString("Going to %1 MHz").arg(x));
}
@ -2989,7 +3238,8 @@ void wfmain::handleWFScroll(QWheelEvent *we)
f.MHzDouble = f.Hz / (double)1E6;
freq = f;
emit setFrequency(f);
//emit setFrequency(f);
issueCmdUniquePriority(cmdSetFreq, f);
ui->freqLabel->setText(QString("%1").arg(f.MHzDouble, 0, 'f'));
issueDelayedCommandUnique(cmdGetFreq);
}
@ -3120,7 +3370,9 @@ void wfmain::on_goFreqBtn_clicked()
if(ok)
{
f.Hz = freq*1E6;
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
//issueCmdSetFreq(f);
issueDelayedCommand(cmdGetFreq);
}
} else {
@ -3128,7 +3380,9 @@ void wfmain::on_goFreqBtn_clicked()
if(ok)
{
f.Hz = KHz*1E3;
emit setFrequency(f);
//issueCmdSetFreq(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommand(cmdGetFreq);
}
}
@ -3288,17 +3542,35 @@ void wfmain::on_modeSelectCombo_activated(int index)
// The "acticvated" signal means the user initiated a mode change.
// This function is not called if code initated the change.
mode_info mode;
unsigned char newMode = static_cast<unsigned char>(ui->modeSelectCombo->itemData(index).toUInt());
currentModeIndex = newMode;
mode.reg = newMode;
int filterSelection = ui->modeFilterCombo->currentData().toInt();
if(filterSelection == 99)
{
// oops, we forgot to reset the combo box
return;
} else {
//qInfo(logSystem()) << __func__ << " at index " << index << " has newMode: " << newMode;
currentMode = (mode_kind)newMode;
emit setMode(newMode, filterSelection);
mode.filter = filterSelection;
mode.name = ui->modeSelectCombo->currentText(); // for debug
for(unsigned int i=0; i < rigCaps.modes.size(); i++)
{
if(rigCaps.modes.at(i).reg == newMode)
{
mode.mk = rigCaps.modes.at(i).mk;
break;
}
}
issueCmd(cmdSetMode, mode);
currentModeInfo = mode;
//emit setMode(newMode, filterSelection);
}
}
@ -3383,9 +3655,8 @@ void wfmain::on_freqDial_valueChanged(int value)
ui->freqLabel->setText(QString("%1").arg(f.MHzDouble, 0, 'f'));
emit setFrequency(f);
}
else {
issueCmdUniquePriority(cmdSetFreq, f);
} else {
ui->freqDial->blockSignals(true);
ui->freqDial->setValue(oldFreqDialVal);
ui->freqDial->blockSignals(false);
@ -3398,7 +3669,8 @@ void wfmain::receiveBandStackReg(freqt freq, char mode, char filter, bool dataOn
// read the band stack and apply by sending out commands
qInfo(logSystem()) << __func__ << "BSR received into main: Freq: " << freq.Hz << ", mode: " << (unsigned int)mode << ", filter: " << (unsigned int)filter << ", data mode: " << dataOn;
emit setFrequency(freq);
//emit setFrequency(freq);
issueCmd(cmdSetFreq, freq);
setModeVal = (unsigned char) mode;
setFilterVal = (unsigned char) filter;
@ -3464,9 +3736,10 @@ void wfmain::on_band4mbtn_clicked()
} else {
f.Hz = (70.200) * 1E6;
}
emit setFrequency(f);
issueDelayedCommandUnique(cmdGetFreq);
ui->tabWidget->setCurrentIndex(0);
issueCmd(cmdSetFreq, f);
//emit setFrequency(f);
issueDelayedCommandUnique(cmdGetFreq);
ui->tabWidget->setCurrentIndex(0);
}
void wfmain::on_band6mbtn_clicked()
@ -3530,7 +3803,8 @@ void wfmain::on_band60mbtn_clicked()
// clutter the UI with 60M channel buttons...
freqt f;
f.Hz = (5.3305) * 1E6;
emit setFrequency(f);
issueCmd(cmdSetFreq, f);
//emit setFrequency(f);
issueDelayedCommandUnique(cmdGetFreq);
ui->tabWidget->setCurrentIndex(0);
}
@ -3551,7 +3825,8 @@ void wfmain::on_band630mbtn_clicked()
{
freqt f;
f.Hz = 475 * 1E3;
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
ui->tabWidget->setCurrentIndex(0);
}
@ -3560,7 +3835,8 @@ void wfmain::on_band2200mbtn_clicked()
{
freqt f;
f.Hz = 136 * 1E3;
emit setFrequency(f);
//emit setFrequency(f);
issueCmd(cmdSetFreq, f);
issueDelayedCommandUnique(cmdGetFreq);
ui->tabWidget->setCurrentIndex(0);
}
@ -3575,58 +3851,9 @@ void wfmain::on_bandGenbtn_clicked()
void wfmain::on_aboutBtn_clicked()
{
QMessageBox msgBox(this);
msgBox.setWindowTitle("Abou wfview");
msgBox.setTextFormat(Qt::RichText);
msgBox.setWindowIcon(QIcon(":resources/wfview.png"));
// TODO: change style of link color based on current CSS sheet.
abtBox->show();
QString head = QString("<html><head></head><body>");
QString copyright = QString("Copyright 2017-2021 Elliott H. Liggett, W6EL. All rights reserved.");
QString nacode = QString("<br/><br/>Networking and audio code written by Phil Taylor, M0VSE");
QString doctest = QString("<br/><br/>Testing, documentation, bug fixes, and development mentorship from<br/>Roeland Jansen, PA3MET, and Jim Nijkamp, PA8E.");
QString ssCredit = QString("<br/><br/>Stylesheet <a href=\"https://github.com/ColinDuquesnoy/QDarkStyleSheet/tree/master/qdarkstyle\" style=\"color: cyan;\">qdarkstyle</a> used under MIT license, stored in /usr/share/wfview/stylesheets/.");
QString rsCredit = QString("<br/><br/><a href=\"https://www.speex.org/\" style=\"color: cyan;\">Speex</a> Resample library Copyright 2003-2008 Jean-Marc Valin");
QString website = QString("<br/><br/>Please visit <a href=\"https://wfview.org/\" style=\"color: cyan;\">https://wfview.org/</a> for the latest information.");
QString docs = QString("<br/><br/>Be sure to check the <a href=\"https://wfview.org/wfview-user-manual/\" style=\"color: cyan;\">User Manual</a> and <a href=\"https://forum.wfview.org/\" style=\"color: cyan;\">the Forum</a> if you have any questions.");
QString gitcodelink = QString("<a href=\"https://gitlab.com/eliggett/wfview/-/tree/%1\" style=\"color: cyan;\">").arg(GITSHORT);
QString contact = QString("<br/>email the author: kilocharlie8@gmail.com or W6EL on the air!");
QString buildInfo = QString("<br/><br/>Build " + gitcodelink + QString(GITSHORT) + "</a> on " + QString(__DATE__) + " at " + __TIME__ + " by " + UNAME + "@" + HOST);
QString end = QString("</body></html>");
QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + "\n" + ssCredit + "\n" + rsCredit + "\n";
aboutText.append(website + "\n"+ docs + contact +"\n" + buildInfo + end);
msgBox.setText(aboutText);
msgBox.exec();
volatile QString sxcreditcopyright = QString("Speex copyright notice:\
Copyright (C) 2003 Jean-Marc Valin\n\
Redistribution and use in source and binary forms, with or without\
modification, are permitted provided that the following conditions\
are met:\n\
- Redistributions of source code must retain the above copyright\
notice, this list of conditions and the following disclaimer.\n\
- Redistributions in binary form must reproduce the above copyright\
notice, this list of conditions and the following disclaimer in the\
documentation and/or other materials provided with the distribution.\n\
- Neither the name of the Xiph.org Foundation nor the names of its\
contributors may be used to endorse or promote products derived from\
this software without specific prior written permission.\n\
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR\
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.");
}
@ -3688,13 +3915,17 @@ void wfmain::on_fRclBtn_clicked()
void wfmain::on_rfGainSlider_valueChanged(int value)
{
emit setRfGain((unsigned char) value);
issueCmdUniquePriority(cmdSetRxRfGain, (unsigned char) value);
}
void wfmain::on_afGainSlider_valueChanged(int value)
{
// qInfo(logSystem()) << "Setting AF gain to " << value;
emit setAfGain((unsigned char)value);
issueCmdUniquePriority(cmdSetAfGain, (unsigned char)value);
if(usingLAN)
{
rxSetup.localAFgain = (unsigned char)(value);
prefs.localAFgain = (unsigned char)(value);
}
}
void wfmain::receiveRfGain(unsigned char level)
@ -3720,14 +3951,14 @@ void wfmain::receiveSql(unsigned char level)
void wfmain::on_tuneNowBtn_clicked()
{
emit startATU();
issueDelayedCommand(cmdStartATU);
showStatusBarText("Starting ATU tuning cycle...");
issueDelayedCommand(cmdGetATUStatus);
}
void wfmain::on_tuneEnableChk_clicked(bool checked)
{
emit setATU(checked);
issueCmd(cmdSetATU, checked);
if(checked)
{
showStatusBarText("Turning on ATU");
@ -3754,7 +3985,7 @@ void wfmain::on_pttOnBtn_clicked()
// Are we already PTT? Not a big deal, just send again anyway.
showStatusBarText("Sending PTT ON command. Use Control-R to receive.");
emit setPTT(true);
issueCmdUniquePriority(cmdSetPTT, true);
// send PTT
// Start 3 minute timer
pttTimer->start();
@ -3765,7 +3996,7 @@ void wfmain::on_pttOffBtn_clicked()
{
// Send the PTT OFF command (more than once?)
showStatusBarText("Sending PTT OFF command");
emit setPTT(false);
issueCmdUniquePriority(cmdSetPTT, false);
// Stop the 3 min timer
pttTimer->stop();
@ -3776,7 +4007,7 @@ void wfmain::handlePttLimit()
{
// transmission time exceeded!
showStatusBarText("Transmit timeout at 3 minutes. Sending PTT OFF command now.");
emit setPTT(false);
issueCmdUniquePriority(cmdSetPTT, false);
issueDelayedCommand(cmdGetPTT);
}
@ -3976,7 +4207,8 @@ void wfmain::on_udpServerSetupBtn_clicked()
}
void wfmain::on_sqlSlider_valueChanged(int value)
{
emit setSql((unsigned char)value);
issueCmd(cmdSetSql, (unsigned char)value);
//emit setSql((unsigned char)value);
}
void wfmain::on_modeFilterCombo_activated(int index)
@ -4029,18 +4261,18 @@ void wfmain::on_transmitBtn_clicked()
// Are we already PTT? Not a big deal, just send again anyway.
showStatusBarText("Sending PTT ON command. Use Control-R to receive.");
emit setPTT(true);
issueCmdUniquePriority(cmdSetPTT, true);
// send PTT
// Start 3 minute timer
pttTimer->start();
issueDelayedCommandPriority(cmdGetPTT);
issueDelayedCommand(cmdGetPTT);
//changeTxBtn();
} else {
// Currently transmitting
emit setPTT(false);
issueCmdUniquePriority(cmdSetPTT, false);
pttTimer->stop();
issueDelayedCommandPriority(cmdGetPTT);
issueDelayedCommand(cmdGetPTT);
}
}
@ -4068,7 +4300,7 @@ void wfmain::statusFromSliderRaw(QString name, int rawValue)
void wfmain::statusFromSliderPercent(QString name, int rawValue)
{
showStatusBarText(name + QString(": %1\%").arg((int)(100*rawValue/255.0)));
showStatusBarText(name + QString(": %1%").arg((int)(100*rawValue/255.0)));
}
void wfmain::receiveTxPower(unsigned char power)
@ -4267,7 +4499,8 @@ void wfmain::receiveAntiVoxGain(unsigned char antiVoxGain)
void wfmain::on_txPowerSlider_valueChanged(int value)
{
emit setTxPower(value);
issueCmdUniquePriority(cmdSetTxPower, (unsigned char)value);
//emit setTxPower(value);
}
void wfmain::on_micGainSlider_valueChanged(int value)
@ -4326,7 +4559,6 @@ void wfmain::on_modInputDataCombo_activated(int index)
(void)index;
}
void wfmain::changeModLabelAndSlider(rigInput source)
{
changeModLabel(source, true);
@ -4337,7 +4569,6 @@ void wfmain::changeModLabel(rigInput input)
changeModLabel(input, false);
}
void wfmain::changeModLabel(rigInput input, bool updateLevel)
{
QString inputName;
@ -4384,16 +4615,7 @@ void wfmain::changeModLabel(rigInput input, bool updateLevel)
void wfmain::processChangingCurrentModLevel(unsigned char level)
{
// slider moved, so find the current mod and issue the level set command.
rigInput currentIn;
if(usingDataMode)
{
currentIn = currentModDataSrc;
} else {
currentIn = currentModSrc;
}
//qInfo(logSystem()) << __func__ << ": setting current level: " << level;
emit setModLevel(currentIn, level);
issueCmd(cmdSetModLevel, level);
}
void wfmain::on_tuneLockChk_clicked(bool checked)
@ -4465,6 +4687,7 @@ void wfmain::on_antennaSelCombo_activated(int index)
void wfmain::on_wfthemeCombo_activated(int index)
{
colorMap->setGradient(static_cast<QCPColorGradient::GradientPreset>(ui->wfthemeCombo->itemData(index).toInt()));
prefs.wftheme = ui->wfthemeCombo->itemData(index).toInt();
}
void wfmain::receivePreamp(unsigned char pre)
@ -4797,6 +5020,7 @@ void wfmain::on_baudRateCombo_activated(int index)
void wfmain::on_wfLengthSlider_valueChanged(int value)
{
prefs.wflength = (unsigned int)(value);
prepareWf(value);
}
@ -4818,6 +5042,6 @@ void wfmain::on_pollingBtn_clicked()
void wfmain::on_debugBtn_clicked()
{
qInfo(logSystem()) << "Debug button pressed.";
emit getFrequency();
trxadj->show();
}

Wyświetl plik

@ -22,16 +22,19 @@
#include "calibrationwindow.h"
#include "repeatersetup.h"
#include "satellitesetup.h"
#include "transceiveradjustments.h"
#include "udpserversetup.h"
#include "udpserver.h"
#include "qledlabel.h"
#include "rigctld.h"
#include "aboutbox.h"
#include <qcustomplot.h>
#include <qserialportinfo.h>
#include "shuttle.h"
#include <deque>
#include <memory>
namespace Ui {
class wfmain;
@ -60,6 +63,7 @@ signals:
void setFrequency(freqt freq);
void getMode();
void setMode(unsigned char modeIndex, unsigned char modeFilter);
void setMode(mode_info);
void setDataMode(bool dataOn, unsigned char filter);
void getDataMode();
void getModInput(bool dataOn);
@ -591,19 +595,41 @@ private:
unsigned char setModeVal=0;
unsigned char setFilterVal=0;
enum cmds {cmdNone, cmdGetRigID, cmdGetRigCIV, cmdGetFreq, cmdGetMode, cmdGetDataMode, cmdSetModeFilter,
enum cmds {cmdNone, cmdGetRigID, cmdGetRigCIV, cmdGetFreq, cmdSetFreq, cmdGetMode, cmdSetMode, cmdGetDataMode, cmdSetModeFilter,
cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue,
cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdGetAfGain,
cmdGetSql, cmdGetATUStatus, cmdGetSpectrumMode, cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT,
cmdGetTxPower, cmdGetMicGain, cmdGetSpectrumRefLevel, cmdGetDuplexMode, cmdGetModInput, cmdGetModDataInput,
cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain,
cmdGetSql, cmdSetSql, cmdGetATUStatus, cmdSetATU, cmdStartATU, cmdGetSpectrumMode, cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT,
cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel, cmdGetSpectrumRefLevel, cmdGetDuplexMode, cmdGetModInput, cmdGetModDataInput,
cmdGetCurrentModLevel, cmdStartRegularPolling, cmdStopRegularPolling, cmdQueNormalSpeed,
cmdGetVdMeter, cmdGetIdMeter, cmdGetSMeter, cmdGetPowerMeter, cmdGetALCMeter, cmdGetCompMeter, cmdGetTxRxMeter,
cmdGetTone, cmdGetTSQL, cmdGetDTCS, cmdGetRptAccessMode, cmdGetPreamp, cmdGetAttenuator, cmdGetAntenna};
std::deque <cmds> delayedCmdQue;
std::deque <cmds> periodicCmdQueue;
std::deque <cmds> slowPollCmdQueue;
struct commandtype {
cmds cmd;
std::shared_ptr<void> data;
};
std::deque <commandtype> delayedCmdQue; // rapid que for commands to the radio
std::deque <cmds> periodicCmdQueue; // rapid que for metering
std::deque <cmds> slowPollCmdQueue; // slow, regular checking for UI sync
void doCmd(cmds cmd);
void doCmd(commandtype cmddata);
void issueCmd(cmds cmd, freqt f);
void issueCmd(cmds cmd, mode_info m);
void issueCmd(cmds cmd, int i);
void issueCmd(cmds cmd, unsigned char c);
void issueCmd(cmds cmd, char c);
void issueCmd(cmds cmd, bool b);
// These commands pop_front and remove similar commands:
void issueCmdUniquePriority(cmds cmd, bool b);
void issueCmdUniquePriority(cmds cmd, unsigned char c);
void issueCmdUniquePriority(cmds cmd, char c);
void issueCmdUniquePriority(cmds cmd, freqt f);
void removeSimilarCommand(cmds cmd);
int pCmdNum = 0;
int delayedCmdIntervalLAN_ms = 100;
int delayedCmdIntervalSerial_ms = 100;
@ -653,6 +679,10 @@ private:
quint16 rigCtlPort;
colors colorScheme;
QString virtualSerialPort;
unsigned char localAFgain;
unsigned int wflength;
int wftheme;
// plot scheme
} prefs;
preferences defPrefs;
@ -708,6 +738,7 @@ private:
rigInput currentModSrc = inputUnknown;
rigInput currentModDataSrc = inputUnknown;
mode_kind currentMode = modeUSB;
mode_info currentModeInfo;
bool haveRigCaps;
bool amTransmitting;
@ -723,7 +754,10 @@ private:
calibrationWindow *cal;
repeaterSetup *rpt;
satelliteSetup *sat;
transceiverAdjustments *trxadj;
udpServerSetup *srv;
aboutbox *abtBox;
udpServer* udp = Q_NULLPTR;
rigCtlD* rigCtl = Q_NULLPTR;
@ -763,6 +797,7 @@ private:
Q_DECLARE_METATYPE(struct rigCapabilities)
Q_DECLARE_METATYPE(struct freqt)
Q_DECLARE_METATYPE(struct mode_info)
Q_DECLARE_METATYPE(struct udpPreferences)
Q_DECLARE_METATYPE(struct rigStateStruct)
Q_DECLARE_METATYPE(struct audioPacket)

Wyświetl plik

@ -134,6 +134,8 @@ SOURCES += main.cpp\
rigctld.cpp \
ring/ring.cpp \
shuttle.cpp
transceiveradjustments.cpp \
aboutbox.cpp
HEADERS += wfmain.h \
commhandler.h \
@ -160,13 +162,17 @@ HEADERS += wfmain.h \
ulaw.h \
ring/ring.h \
shuttle.h
transceiveradjustments.h \
audiotaper.h \
aboutbox.h
FORMS += wfmain.ui \
calibrationwindow.ui \
satellitesetup.ui \
udpserversetup.ui \
repeatersetup.ui
repeatersetup.ui \
transceiveradjustments.ui \
aboutbox.ui