diff --git a/CHANGELOG b/CHANGELOG index 25ba84e..6335c22 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,109 @@ # CHANGELOG +- 20220918 + + merged bits from log branch into master + + updated to v.1.45 + + +- 20220917 + + Log will automatically scroll to bottom on show. + + Remove focus from connect button once clicked. + + Move connect button + + Update wfmain.cpp + + Add enable/disable audioSystemServerCombo + + Add "Cancel" to connect button + + Fixed bug where the frequency dial skipped extra when crossing zero. + +- 20220915 + + Merge branch log: + + Minor edit to margins + + Removed some additional padding that was left in parts of the UI. + + Changed windows 'open log' command to launch notepad. + + Removed extra disconnect message from logging host. + + Added flush to the text stream to termbin. + + Better scrolling behavior. The log is now aware if you are not at the + bottom and will not force scroll if so. Otherwise, it will bring the + horizontal scroll back to the left-edge. + + Renamed URL message box in case we add an additional message box later. + + Clarified control label to Scroll Down versus To Bottom. + + Changed font for better compatibility. Added some controls for scrolling + behavior. + + Debug mode can now be toggled from the logging window. + + Removed extra output on non-server builds. + + Added debug logging button, however, it doesn't propagate the debug + signal through all the modules yet. So, for now, it is hidden. + + Restored version to log file, fixed reversed log entries. + + updated to v.1.44 +- 20220914 + + Removed word wrapping from the log + + Cut log window polling rate in half for lower CPU. 100ms is plenty fast. + + Removed audio volume logging. + + Keep the logging window up after the message box. Added "log" logging + category. + + Added logging window capability with termbin support. + +- 20220909 + Merge branch resize: + + Found an extra 10 pixels due to some layout padding I left in + accidently. + + Main window may now be made smaller. Settings combo box sizes now + limited. + + Fixed issue where build paths with a space in the name would cause the + git hash to fail capture. + + updated to v1.43 + +- 20220826 + Merge branch 'audiometer' + This adds audiometer for the second meter TxAudio, RxAudio and TxRxAudio + updated to v.1.42 +- 20220824 + Merge branch 'color' + This adds color picker support so that you can define several UI element colors + updated to v1.41 + +- 20220819 + + version bumped to 1.4 + temporary squashed logs; may redo later + audio fixes at exit + introduction of peak decays at the scope + resizing of top and bottom scope/waterfall + various fixes on the spectrum display + + - 20220414 this is a cherrypicked CHANGELOG add as there are loads of changes due to different diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 901803c..7804ed1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,3 +6,12 @@ the following people currently contribute to this Project: - Jim PA8E - Phil M0VSE - Roeland PA3MET + + +Also contributions by: + +Daniele Forsi +Russ Woodman - K5TUX +(and some others I will add too) + + diff --git a/INSTALL.md b/INSTALL.md index 589eab7..70612cc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -14,6 +14,9 @@ sudo apt-get install libqt5multimedia5-plugins sudo apt-get install qtmultimedia5-dev sudo apt-get install git sudo apt-get install libopus-dev +sudo apt-get install libeigen3-dev +sudo apt-get install portaudio19-dev +sudo apt-get install librtaudio-dev ~~~ Now you need to install qcustomplot. There are two versions that are commonly found in linux distros: 1.3 and 2.0. Either will work fine. If you are not sure which version your linux install comes with, simply run both commands. One will work and the other will fail, and that's fine! diff --git a/INSTALL_PREBUILT_BINARY.md b/INSTALL_PREBUILT_BINARY.md index 6c8fc7e..6f0dd57 100644 --- a/INSTALL_PREBUILT_BINARY.md +++ b/INSTALL_PREBUILT_BINARY.md @@ -22,8 +22,8 @@ Debian 11 (Debian 10 is outdated) Fedora 33 Fedora 34 mint 20.1 (and up?) -openSUSE 15.2 -openSUSE 15.3 (see notes at the end) +openSUSE 15.2 (outdated/deprecated) +openSUSE 15.3/15.4) openSUSE Tumbleweed(s) SLES 15.x Ubuntu 20.04.2 @@ -86,16 +86,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l ~~~ -### openSUSE/Tumbleweed/SLES based on 15.2: +### openSUSE/Tumbleweed/SLES based on 15.3/15.4: ~~~ -sudo zypper in libqcustomplot2 libQt5SerialPort5 -wfview -~~~ - -### openSUSE/Tumbleweed/SLES based on 15.3: -~~~ - -SEE THE NOTES AT THE END. You need wfview153 here sudo zypper in libqcustomplot2 libQt5SerialPort5 wfview @@ -119,14 +111,8 @@ sudo ln -s /lib/x86_64-linux-gnu/libqcustomplot.so.2.0.1 /lib/x86_64-linux-gnu/l ### notes: ~~~ -Some newer versions of mint. ubuntu, openSUSE have different kernels and such which cause wfview to segfault. +openSUSE 15.2 is deprecated; old binary has been removed -For these cases we created two binaries: one for current systems ("wfview") and one for the new systems ("wfview153") - -So if you encounter a SEGFAULT at start: - -go in to the dist directory, rename wfview to wfvie152; rename wfview153 to wfview and re-execute the install.sh -script ~~~ diff --git a/WHATSNEW b/WHATSNEW index 45ee36c..f40f71c 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -1,37 +1,39 @@ -The following highlights are in this 1.x-release: +The following highlights are in this 1.41-release: - many changes/mods/updates/enhancements to rigctld - rigctld box added in the UI - build process changed: you can add the install prefix (derSuessmann) - added "do not ask again" for switching off rig and exiting wfview - added opus as audio transport - dual meter support - rigctl basic split support - rigctl prevents switching off civ transceive - added 25 kHz step - as a temporary measure sending multiple TX/FREQ change commands to the rig - when we use rigctld. - people should use "fake it" in wsjtx as the split code is not reliable. - tidied up udp server function for better reliability - added some IC736 stuff - added portaudio (you need t change wfview.pro to select - and that lowers the latency to maybe less than 50 ms - added PBT and IF-shift - several bugs fixed - RTS as PTT for several radios like the 706/718/736… -+ New major change is the audio transport mechanism. Lower latencies. -+ Author: Daniele Forsi /Author: Daniele Forsi -+ Author: Russ Woodman - K5TUX -+ About box updated to include Patreon site -+ added 500 Hz step for VFO -+ Added clock and UTC toggle. -+ Added forced manual RTS setting -+ Add RIT function and other rigctl fixes -+ Adjusted window size for radios without spectrum. Thanks K5TUX. -+ Allow dynamic restarting of server -+ New Settings tab -+ Enable High DPI Scaling -+ More multi-radio support (nearly working!) -+ N1MM+ TCP add. + New major change is the audio transport mechanism. Lower latencies. + About box updated to include Patreon site + added 500 Hz step for VFO + Added clock and UTC toggle. + Added forced manual RTS setting + Add RIT function and other rigctl fixes + Adjusted window size for radios without spectrum. Thanks K5TUX. + Allow dynamic restarting of server + New Settings tab + Enable High DPI Scaling + More multi-radio support (mostly working!) + Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select + split/combine (or default). Split only makes sense for LAN and Combine for USB. + added radio status display with meters for audio (speaker/mic) + selector for audio: QT, PortAudio, RealTime audio + version bumped to 1.4 -- rethinking of a new version schema that makes more sense + temporary squashed logs; may redo later + audio fixes at exit + introduction of peak decays at the scope + resizing of top and bottom scope/waterfall + various fixes on the spectrum display ++ 1.41 added color picker support for all kinds of vsual elements ++ 1.42 added three additional second meter choices. RxAudio, TxAdio, and TxRxAudio ++ 1.43 fixed resizing issues. ++ 1.44 added logging window. you can send the logs to termbin.com and share the link on the forum ++ 1.45 some more log enhancements + moved connect button and added cancel option + add enable/disable audioSystemServerCombo + Fixed bug where the frequency dial skipped extra when crossing zero. + + +Notes: + +- We know about high CPU usage on RPi. +- try the audiostack you like the most/what works for you diff --git a/aboutbox.cpp b/aboutbox.cpp index ba8de0f..03715c1 100644 --- a/aboutbox.cpp +++ b/aboutbox.cpp @@ -15,10 +15,12 @@ aboutbox::aboutbox(QWidget *parent) : 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 copyright = QString("Copyright 2017-2022 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 dedication = QString("

This version of wfview is dedicated to the ones we lost."); + #if defined(Q_OS_LINUX) QString ssCredit = QString("

Stylesheet qdarkstyle used under MIT license, stored in /usr/share/wfview/stylesheets/."); #else @@ -83,7 +85,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."); // String it all together: - QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + wfviewcommunityack; + QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + dedication + wfviewcommunityack; aboutText.append(website + "\n" + donate + "\n"+ docs + support + contact +"\n"); aboutText.append("\n" + ssCredit + "\n" + rsCredit + "\n"); diff --git a/audioconverter.cpp b/audioconverter.cpp index 2316229..58fd58f 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -157,7 +157,14 @@ bool audioConverter::convert(audioPacket audio) if (samplesF.size() > 0) { - audio.amplitude = samplesF.array().abs().maxCoeff(); + audio.amplitudePeak = samplesF.array().abs().maxCoeff(); + //audio.amplitudeRMS = samplesF.array().abs().mean(); // zero for tx audio + //audio.amplitudeRMS = samplesF.norm() / sqrt(samplesF.size()); // too high values. Zero for tx audio. + //audio.amplitudeRMS = samplesF.squaredNorm(); // tx not zero. Values higher than peak sometimes + //audio.amplitudeRMS = samplesF.norm(); // too small values. also too small on TX + //audio.amplitudeRMS = samplesF.blueNorm(); // scale same as norm, too small. + + // Set the volume samplesF *= audio.volume; diff --git a/audioconverter.h b/audioconverter.h index a0d2953..b3a1492 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -29,7 +29,8 @@ struct audioPacket { quint16 sent; QByteArray data; quint8 guid[GUIDLEN]; - float amplitude; + float amplitudePeak; + float amplitudeRMS; qreal volume = 1.0; }; diff --git a/audiohandler.cpp b/audiohandler.cpp index b274545..2244960 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -18,23 +18,24 @@ audioHandler::audioHandler(QObject* parent) : QObject(parent) audioHandler::~audioHandler() { + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + if (isInitialized) { stop(); } if (audioInput != Q_NULLPTR) { - audioInput = Q_NULLPTR; delete audioInput; + audioInput = Q_NULLPTR; } if (audioOutput != Q_NULLPTR) { delete audioOutput; audioOutput = Q_NULLPTR; } - if (converterThread != Q_NULLPTR) { - converterThread->quit(); - converterThread->wait(); - } }bool audioHandler::init(audioSetup setup) { if (isInitialized) { @@ -229,7 +230,6 @@ void audioHandler::stop() void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -269,9 +269,8 @@ void audioHandler::convertedOutput(audioPacket packet) { } */ lastSentSeq = packet.seq; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); - - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; + emit haveLevels(getAmplitude(), static_cast(packet.amplitudeRMS * 255.0), setup.latency, currentLatency, isUnderrun, isOverrun); } } @@ -311,8 +310,8 @@ void audioHandler::convertedInput(audioPacket audio) qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Time since last audio packet" << lastReceived.msecsTo(QTime::currentTime()) << "Expected around" << setup.blockSize ; } lastReceived = QTime::currentTime(); - amplitude = audio.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + amplitude = audio.amplitudePeak; + emit haveLevels(getAmplitude(), audio.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun); } } diff --git a/audiohandler.h b/audiohandler.h index a793973..760b215 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -70,7 +70,7 @@ signals: void audioMessage(QString message); void sendLatency(quint16 newSize); void haveAudioData(const audioPacket& data); - void haveLevels(quint16 amplitude,quint16 latency,quint16 current,bool under,bool over); + void haveLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency,quint16 current,bool under,bool over); void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); diff --git a/colorprefs.h b/colorprefs.h new file mode 100644 index 0000000..7db11fc --- /dev/null +++ b/colorprefs.h @@ -0,0 +1,38 @@ +#ifndef COLORPREFS_H +#define COLORPREFS_H + +#include +#include + +struct colorPrefsType{ + int presetNum = -1; + QString *presetName = Q_NULLPTR; + + // Spectrum line plot: + QColor gridColor; + QColor axisColor; + QColor textColor; + QColor spectrumLine; + QColor spectrumFill; + QColor underlayLine; + QColor underlayFill; + QColor plotBackground; + QColor tuningLine; + + // Waterfall: + QColor wfBackground; + QColor wfGrid; + QColor wfAxis; + QColor wfText; + + // Meters: + QColor meterLevel; + QColor meterAverage; + QColor meterPeakLevel; + QColor meterPeakScale; + QColor meterLowerLine; + QColor meterLowText; +}; + + +#endif // COLORPREFS_H diff --git a/logcategories.cpp b/logcategories.cpp index 64b9f81..19e183e 100644 --- a/logcategories.cpp +++ b/logcategories.cpp @@ -3,6 +3,8 @@ Q_LOGGING_CATEGORY(logSystem, "system") Q_LOGGING_CATEGORY(logSerial, "serial") Q_LOGGING_CATEGORY(logGui, "gui") +Q_LOGGING_CATEGORY(logLogger, "log") +Q_LOGGING_CATEGORY(logUser, "user") Q_LOGGING_CATEGORY(logRig, "rig") Q_LOGGING_CATEGORY(logAudio, "audio") Q_LOGGING_CATEGORY(logUdp, "udp") diff --git a/logcategories.h b/logcategories.h index 5143d38..f5b0a12 100644 --- a/logcategories.h +++ b/logcategories.h @@ -6,6 +6,8 @@ Q_DECLARE_LOGGING_CATEGORY(logSystem) Q_DECLARE_LOGGING_CATEGORY(logSerial) Q_DECLARE_LOGGING_CATEGORY(logGui) +Q_DECLARE_LOGGING_CATEGORY(logLogger) +Q_DECLARE_LOGGING_CATEGORY(logUser) Q_DECLARE_LOGGING_CATEGORY(logRig) Q_DECLARE_LOGGING_CATEGORY(logAudio) Q_DECLARE_LOGGING_CATEGORY(logUdp) diff --git a/loggingwindow.cpp b/loggingwindow.cpp new file mode 100644 index 0000000..c9ad5be --- /dev/null +++ b/loggingwindow.cpp @@ -0,0 +1,211 @@ +#include "loggingwindow.h" +#include "ui_loggingwindow.h" + +loggingWindow::loggingWindow(QWidget *parent) : + QWidget(parent), + ui(new Ui::loggingWindow) +{ + ui->setupUi(this); + this->setWindowTitle("Log"); + ui->logTextDisplay->setReadOnly(true); + ui->userAnnotationText->setFocus(); + ui->annotateBtn->setDefault(true); + ui->logTextDisplay->setFocusPolicy(Qt::NoFocus); + ui->annotateBtn->setFocusPolicy(Qt::NoFocus); + + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + ui->logTextDisplay->setFont(font); + ui->userAnnotationText->setFont(font); + +#ifdef Q_OS_MAC + logFilename = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log"; + logDirectory = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0]; + +#else + logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log"; + logDirectory = QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0]; +#endif + clipboard = QApplication::clipboard(); + socket = new QTcpSocket(this); + connect(socket, SIGNAL(connected()), this, SLOT(connectedToHost())); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnectedFromHost())); + connect(socket, SIGNAL(readyRead()), this, SLOT(handleDataFromLoggingHost())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(handleLoggingHostError(QAbstractSocket::SocketError))); + + vertLogScroll = ui->logTextDisplay->verticalScrollBar(); + horizLogScroll = ui->logTextDisplay->horizontalScrollBar(); + + vertLogScroll->setValue(vertLogScroll->maximum()); + horizLogScroll->setValue(horizLogScroll->minimum()); +} + +loggingWindow::~loggingWindow() +{ + QMutexLocker lock(&textMutex); + delete ui; +} + +void loggingWindow::showEvent(QShowEvent *event) +{ + (void)event; + on_toBottomBtn_clicked(); +} + +void loggingWindow::setInitialDebugState(bool debugModeEnabled) +{ + ui->debugBtn->blockSignals(true); + ui->debugBtn->setChecked(debugModeEnabled); + ui->debugBtn->blockSignals(false); +} + +void loggingWindow::acceptLogText(QString text) +{ + QMutexLocker lock(&textMutex); + ui->logTextDisplay->appendPlainText(text); + if(vertLogScroll->value() == vertLogScroll->maximum()) + { + horizLogScroll->setValue(horizLogScroll->minimum()); + } +} + +void loggingWindow::sendToTermbin() +{ + qInfo(logLogger()) << "Sending data to termbin.com. Standby."; + socket->connectToHost("termbin.com", 9999); + ui->sendToPasteBtn->setDisabled(true); +} + +void loggingWindow::handleDataFromLoggingHost() +{ + qInfo(logLogger()) << "Receiving data from logging host."; + QString URL; + QByteArray data = socket->readAll(); + if(data.length() < 256) + { + URL = QString(data).trimmed(); + if(!URL.isEmpty()) + { + clipboard->setText(URL); + qInfo(logLogger()) << "Sent log to URL: " << URL; + URLmsgBox.setText("Your log has been posted, and the URL has been copied to the clipboard."); + URLmsgBox.setInformativeText("" + URL + ""); + URLmsgBox.exec(); + // For whatever reason, showing the message box hides this window. + this->show(); + this->raise(); + this->activateWindow(); + } + } else { + qDebug(logLogger()) << "Error, return from logging host too large. Received " << data.length() << " bytes."; + } +} + +void loggingWindow::disconnectedFromHost() +{ + qInfo(logLogger()) << "Disconnected from logging host"; + ui->sendToPasteBtn->setDisabled(false); +} + +void loggingWindow::connectedToHost() +{ + qInfo(logLogger()) << "Connected to logging host"; + QMutexLocker lock(&textMutex); + QTextStream outText(socket); + outText << ui->logTextDisplay->toPlainText(); + outText << "\n----------\nSent from wfview version "; + outText << WFVIEW_VERSION << "\n----------\n"; + outText.flush(); +} + +void loggingWindow::handleLoggingHostError(QAbstractSocket::SocketError error) +{ + switch(error) + { + case QAbstractSocket::RemoteHostClosedError: + //qInfo(logLogger()) << "Disconnected from logging host."; + break; + + default: + qWarning(logLogger()) << "Error connecting to logging host. Check internet connection. Error code: " << error; + break; + } +} + +void loggingWindow::on_clearDisplayBtn_clicked() +{ + QMutexLocker lock(&textMutex); + // Only clears the displayed text, not the log file. + ui->logTextDisplay->clear(); +} + +void loggingWindow::on_openDirBtn_clicked() +{ + QString cmd; + int rtnval = 0; +#ifdef Q_OS_MAC + cmd = "open " + logDirectory; +#endif +#ifdef Q_OS_LINUX + cmd = "xdg-open " + logDirectory; +#endif +#ifdef Q_OS_WIN + cmd = "start " + logDirectory; +#endif + rtnval = system(cmd.toLocal8Bit().data()); + if(rtnval) + qInfo(logLogger()) << "Error, open log directory command returned error code " << rtnval; +} + +void loggingWindow::on_openLogFileBtn_clicked() +{ + QString cmd; + int rtnval = 0; +#ifdef Q_OS_MAC + cmd = "open " + logFilename; +#endif +#ifdef Q_OS_LINUX + cmd = "xdg-open " + logFilename; +#endif +#ifdef Q_OS_WIN + cmd = "notepad " + logFilename; +#endif + rtnval = system(cmd.toLocal8Bit().data()); + if(rtnval) + qInfo(logLogger()) << "Error, open log file command returned error code " << rtnval; +} + +void loggingWindow::on_sendToPasteBtn_clicked() +{ + sendToTermbin(); +} + +void loggingWindow::on_annotateBtn_clicked() +{ + QMutexLocker lock(&textMutex); + if(ui->userAnnotationText->text().isEmpty()) + return; + qInfo(logUser()) << ui->userAnnotationText->text(); + ui->userAnnotationText->clear(); +} + +void loggingWindow::on_userAnnotationText_returnPressed() +{ + on_annotateBtn_clicked(); +} + +void loggingWindow::on_copyPathBtn_clicked() +{ + clipboard->setText(logFilename); +} + +void loggingWindow::on_debugBtn_clicked(bool checked) +{ + emit setDebugMode(checked); +} + +void loggingWindow::on_toBottomBtn_clicked() +{ + vertLogScroll->setValue(vertLogScroll->maximum()); + horizLogScroll->setValue(horizLogScroll->minimum()); +} diff --git a/loggingwindow.h b/loggingwindow.h new file mode 100644 index 0000000..dda0efa --- /dev/null +++ b/loggingwindow.h @@ -0,0 +1,72 @@ +#ifndef LOGGINGWINDOW_H +#define LOGGINGWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logcategories.h" + +namespace Ui { +class loggingWindow; +} + +class loggingWindow : public QWidget +{ + Q_OBJECT + +public: + explicit loggingWindow(QWidget *parent = nullptr); + ~loggingWindow(); + void acceptLogText(QString text); + +public slots: + void setInitialDebugState(bool debugModeEnabled); + +private slots: + void connectedToHost(); + void disconnectedFromHost(); + void handleDataFromLoggingHost(); + void handleLoggingHostError(QAbstractSocket::SocketError); + void showEvent(QShowEvent* event); + void on_clearDisplayBtn_clicked(); + + void on_openDirBtn_clicked(); + + void on_openLogFileBtn_clicked(); + + void on_sendToPasteBtn_clicked(); + + void on_annotateBtn_clicked(); + + void on_userAnnotationText_returnPressed(); + + void on_copyPathBtn_clicked(); + + void on_debugBtn_clicked(bool checked); + + void on_toBottomBtn_clicked(); + +signals: + void setDebugMode(bool debugOn); + +private: + Ui::loggingWindow *ui; + QClipboard *clipboard; + QMessageBox URLmsgBox; + QScrollBar *vertLogScroll; + QScrollBar *horizLogScroll; + QMutex textMutex; + QString logFilename; + QString logDirectory; + QTcpSocket *socket; + void sendToTermbin(); +}; + +#endif // LOGGINGWINDOW_H diff --git a/loggingwindow.ui b/loggingwindow.ui new file mode 100644 index 0000000..717366c --- /dev/null +++ b/loggingwindow.ui @@ -0,0 +1,191 @@ + + + loggingWindow + + + + 0 + 0 + 625 + 300 + + + + Form + + + + + + false + + + QPlainTextEdit::NoWrap + + + true + + + + + + + 0 + + + 0 + + + + + + 75 + 0 + + + + + 75 + 16777215 + + + + Annotation: + + + + + + + + 290 + 0 + + + + You may enter your own log notes here. + + + + + + + + 85 + 0 + + + + + 85 + 16777215 + + + + Adds user-text to the log. + + + Annotate + + + + + + + + + 0 + + + 0 + + + + + <html><head/><body><p>Enable or disable debug logging. Use the &quot;-d&quot; or &quot;--debug&quot; flag to open wfview with debug logging enabled on startup. </p></body></html> + + + Debug + + + + + + + Scroll to bottom + + + Scroll Down + + + + + + + Clears the display. Does not clear the log file. + + + Clear + + + + + + + Makes a best-effort to ask the host system to open the log file directory. + + + Open Log Directory + + + + + + + Makes a best-effort to ask the host system to open the logfile. + + + Open Log + + + + + + + Copy the path of the log file to your clipboard. + + + Copy Path + + + + + + + <html><head/><body><p>Sends text to termbin.com. Some personal information (such as your username) is in the log file, so do not click this button unless you are ok sharing your log file. This is a quick way to receive a URL, pointing to your log file text, that you can send to other people. </p></body></html> + + + Send to termbin.com + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + diff --git a/main.cpp b/main.cpp index b407b8c..a16b010 100644 --- a/main.cpp +++ b/main.cpp @@ -12,12 +12,16 @@ #include #include "wfmain.h" + +// Copyright 2017-2022 Elliott H. Liggett #include "logcategories.h" -// Copyright 2017-2021 Elliott H. Liggett - +#ifdef BUILD_WFSERVER // Smart pointer to log file -QScopedPointer logFile; +QScopedPointer m_logFile; +QMutex logMutex; +#endif + bool debugMode=false; #ifdef BUILD_WFSERVER @@ -32,7 +36,7 @@ bool debugMode=false; Q_UNUSED(sig) qDebug() << "Exiting via SIGNAL"; if (w!=Q_NULLPTR) w->deleteLater(); - qApp->quit(); + QCoreApplication::quit(); #ifdef Q_OS_WIN return true; @@ -41,9 +45,9 @@ bool debugMode=false; #endif } -#endif void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); +#endif int main(int argc, char *argv[]) { @@ -64,7 +68,7 @@ int main(int argc, char *argv[]) #endif #ifdef QT_DEBUG - debugMode = true; + //debugMode = true; #endif QString serialPortCL; @@ -159,6 +163,8 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER + // Set the logging file before doing anything else. logFile.reset(new QFile(logFilename)); // Open the file logging @@ -170,17 +176,19 @@ int main(int argc, char *argv[]) qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL); qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL); qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); - +#endif #ifdef BUILD_WFSERVER #ifdef Q_OS_WIN SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); #else signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + signal(SIGKILL, cleanup); #endif w = new servermain(serialPortCL, hostCL, settingsFile); #else a.setWheelScrollLines(1); // one line per wheel click - wfmain w(serialPortCL, hostCL, settingsFile); + wfmain w(serialPortCL, hostCL, settingsFile, debugMode); w.show(); #endif @@ -188,6 +196,7 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -196,9 +205,9 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt { return; } - static QMutex mutex; - QMutexLocker locker(&mutex); - QTextStream out(logFile.data()); + QMutexLocker locker(&logMutex); + QTextStream out(m_logFile.data()); + QString text; // Write the date of recording out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); @@ -227,5 +236,8 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt #ifdef BUILD_WFSERVER std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ").toLocal8Bit().toStdString() << msg.toLocal8Bit().toStdString() << "\n"; #endif + text = out.readAll(); out.flush(); // Clear the buffered data + //mainwindow.handleLogText(test); } +#endif diff --git a/meter.cpp b/meter.cpp index bc606ef..25990e0 100644 --- a/meter.cpp +++ b/meter.cpp @@ -48,6 +48,26 @@ meter::meter(QWidget *parent) : QWidget(parent) } +void meter::setColors(QColor current, QColor peakScale, QColor peakLevel, + QColor average, QColor lowLine, + QColor lowText) +{ + currentColor = current; + + peakColor = peakLevel; // color for the peak level indicator + highLineColor = peakScale; // color for the red side of the scale + highTextColor = peakScale; // color for the red side of the scale's text + + averageColor = average; + + midScaleColor = QColor(Qt::yellow); + centerTuningColor = QColor(Qt::green); + + lowLineColor = lowLine; + lowTextColor = lowText; + this->update(); +} + void meter::clearMeterOnPTTtoggle() { // When a meter changes type, such as the fixed S -- TxPo meter, @@ -157,13 +177,29 @@ void meter::paintEvent(QPaintEvent *) drawScaleId(&painter); break; case meterComp: - label = "CMP"; + label = "CMP(dB)"; peakRedLevel = 100; drawScaleComp(&painter); break; case meterNone: return; break; + case meterAudio: + label = "dBfs"; + peakRedLevel = 241; + drawScale_dBFs(&painter); + break; + case meterRxAudio: + label = "Rx(dBfs)"; + peakRedLevel = 241; + drawScale_dBFs(&painter); + break; + case meterTxMod: + label = "Tx(dBfs)"; + peakRedLevel = 241; + drawScale_dBFs(&painter); + break; + default: label = "DN"; peakRedLevel = 241; @@ -196,8 +232,37 @@ void meter::paintEvent(QPaintEvent *) painter.drawRect(mXstart+peak-1,mYstart,1,barHeight); + } else if ( (meterType == meterAudio) || + (meterType == meterTxMod) || + (meterType == meterRxAudio)) + { + // Log scale but still 0-255: + int logCurrent = (int)((1-audiopot[255-current])*255); + int logAverage = (int)((1-audiopot[255-average])*255); + int logPeak = (int)((1-audiopot[255-peak])*255); + + // X, Y, Width, Height + painter.drawRect(mXstart,mYstart,logCurrent,barHeight); + + // Average: + painter.setPen(averageColor); + painter.setBrush(averageColor); + painter.drawRect(mXstart+logAverage-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+logPeak-1,mYstart,2,barHeight); + } else { + // X, Y, Width, Height painter.drawRect(mXstart,mYstart,current,barHeight); @@ -225,7 +290,7 @@ void meter::paintEvent(QPaintEvent *) void meter::drawLabel(QPainter *qp) { - qp->setPen(lowLineColor); + qp->setPen(lowTextColor); qp->drawText(0,scaleTextYstart, label ); } @@ -255,6 +320,22 @@ void meter::setLevel(int current) this->update(); } +void meter::setLevels(int current, int peak) +{ + this->current = current; + this->peak = peak; + + avgLevels[(avgPosition++)%averageBalisticLength] = 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->update(); +} + void meter::setLevels(int current, int peak, int average) { this->current = current; @@ -287,9 +368,43 @@ void meter::drawScaleRaw(QPainter *qp) // Line: X1, Y1 -->to--> X2, Y2 qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); +} +void meter::drawScale_dBFs(QPainter *qp) +{ + qp->setPen(lowTextColor); + peakRedLevel = 193; + + if(meterType==meterAudio) + qp->drawText(20+mXstart,scaleTextYstart, QString("-30")); + qp->drawText(38+mXstart+2,scaleTextYstart, QString("-24")); + qp->drawText(71+mXstart,scaleTextYstart, QString("-18")); + qp->drawText(124+mXstart,scaleTextYstart, QString("-12")); + qp->drawText(193+mXstart,scaleTextYstart, QString("-6")); + qp->drawText(255+mXstart,scaleTextYstart, QString("0")); + + // Low ticks: + qp->setPen(lowLineColor); + qp->drawLine(20+mXstart,scaleTextYstart, 20+mXstart, scaleTextYstart+5); + qp->drawLine(38+mXstart,scaleTextYstart, 38+mXstart, scaleTextYstart+5); + qp->drawLine(71+mXstart,scaleTextYstart, 71+mXstart, scaleTextYstart+5); + qp->drawLine(124+mXstart,scaleTextYstart, 124+mXstart, scaleTextYstart+5); + + + // High ticks: + qp->setPen(highLineColor); + qp->drawLine(193+mXstart,scaleTextYstart, 193+mXstart, scaleTextYstart+5); + qp->drawLine(255+mXstart,scaleTextYstart, 255+mXstart, scaleTextYstart+5); + + // Now the lines: + qp->setPen(lowLineColor); + + // Line: X1, Y1 -->to--> X2, Y2 + qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); + qp->setPen(highLineColor); + qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); } void meter::drawScaleVd(QPainter *qp) @@ -326,7 +441,7 @@ void meter::drawScaleVd(QPainter *qp) // Line: X1, Y1 -->to--> X2, Y2 qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); } @@ -337,7 +452,7 @@ void meter::drawScaleCenter(QPainter *qp) qp->setPen(lowLineColor); qp->drawText(60+mXstart,scaleTextYstart, QString("-")); - qp->setPen(Qt::green); + qp->setPen(centerTuningColor); // Attempt to draw the zero at the actual center qp->drawText(128-2+mXstart,scaleTextYstart, QString("0")); @@ -348,7 +463,7 @@ void meter::drawScaleCenter(QPainter *qp) qp->setPen(lowLineColor); qp->drawLine(mXstart,scaleLineYstart,128-32+mXstart,scaleLineYstart); - qp->setPen(Qt::green); + qp->setPen(centerTuningColor); qp->drawLine(128-32+mXstart,scaleLineYstart,128+32+mXstart,scaleLineYstart); qp->setPen(lowLineColor); @@ -382,7 +497,7 @@ void meter::drawScalePo(QPainter *qp) //qDebug() << "meter i: " << i; dnPerWatt = (213-143.0f) / 50.0f; // 1.4 dn per watt // P=5 here. - qp->setPen(Qt::yellow); + qp->setPen(midScaleColor); int k=0; for(i=mXstart+143; isetPen(Qt::red); + qp->setPen(highTextColor); for(i=mXstart+213; ito--> X2, Y2 qp->drawLine(mXstart,scaleLineYstart,213+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(213+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); (void)qp; @@ -432,7 +547,7 @@ void meter::drawScaleALC(QPainter *qp) alc +=20; } - qp->setPen(Qt::red); + qp->setPen(highTextColor); for(; isetPen(lowLineColor); qp->drawLine(mXstart,scaleLineYstart,100+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(100+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); (void)qp; @@ -465,6 +580,7 @@ void meter::drawScaleComp(QPainter *qp) float dBperDn = (float)(highPointdB-midPointdB) / float(highPointDn-midPointDn); int i=mXstart; + i+=midPointDn/4; // skip the 0 for cleaner label space for(; idrawText(i,scaleTextYstart, QString("%1").arg( (int)((i-mXstart) * (float(midPointdB) / float(midPointDn)) )) ); @@ -483,7 +599,7 @@ void meter::drawScaleComp(QPainter *qp) // Line: X1, Y1 -->to--> X2, Y2 qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); } @@ -495,27 +611,29 @@ void meter::drawScaleSWR(QPainter *qp) // 0080=SWR2.0, // 0120=SWR3.0 + qp->setPen(lowTextColor); 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->setPen(highTextColor); qp->drawText(120+mXstart,scaleTextYstart, QString("3.0")); + qp->setPen(lowLineColor); 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->setPen(highLineColor); qp->drawLine(120+mXstart,scaleTextYstart,120+mXstart, scaleTextYstart+5); qp->setPen(lowLineColor); qp->drawLine(mXstart,scaleLineYstart,100+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(100+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); - - } void meter::drawScaleId(QPainter *qp) @@ -553,7 +671,7 @@ void meter::drawScaleId(QPainter *qp) // Line: X1, Y1 -->to--> X2, Y2 qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); } @@ -582,7 +700,7 @@ void meter::drawScaleS(QPainter *qp) s = 20; i+=20; - qp->setPen(Qt::red); + qp->setPen(highTextColor); for(; isetPen(lowLineColor); - qp->drawLine(mXstart,scaleLineYstart,peakRedLevel+mXstart,scaleLineYstart); - qp->setPen(Qt::red); + qp->setPen(highLineColor); qp->drawLine(peakRedLevel+mXstart,scaleLineYstart,255+mXstart,scaleLineYstart); - } diff --git a/meter.h b/meter.h index d94657c..4e03ced 100644 --- a/meter.h +++ b/meter.h @@ -9,6 +9,7 @@ #include #include "rigcommander.h" // for meter types +#include "audiotaper.h" class meter : public QWidget { @@ -23,6 +24,7 @@ public slots: void updateDrawing(int num); void setLevels(int current, int peak, int average); + void setLevels(int current, int peak); // calculate avg void setLevel(int current); void clearMeterOnPTTtoggle(); void clearMeter(); @@ -30,6 +32,9 @@ public slots: void setMeterShortString(QString); QString getMeterShortString(); meterKind getMeterType(); + void setColors(QColor current, QColor peakScale, QColor peakLevel, + QColor average, QColor lowLine, + QColor lowText); private: @@ -68,6 +73,7 @@ private: void drawScaleVd(QPainter *qp); void drawScaleId(QPainter *qp); void drawScaleComp(QPainter *qp); + void drawScale_dBFs(QPainter *qp); void drawScaleRaw(QPainter *qp); void drawLabel(QPainter *qp); @@ -84,6 +90,9 @@ private: QColor highTextColor; QColor highLineColor; + QColor midScaleColor; + QColor centerTuningColor; + }; #endif // METER_H diff --git a/pahandler.cpp b/pahandler.cpp index e0de0eb..a3c4860 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -15,17 +15,15 @@ paHandler::paHandler(QObject* parent) paHandler::~paHandler() { - if (isInitialized) { - Pa_StopStream(audio); - Pa_CloseStream(audio); - } - if (converterThread != Q_NULLPTR) { converterThread->quit(); converterThread->wait(); } - //Pa_Terminate(); + if (isInitialized) { + Pa_StopStream(audio); + Pa_CloseStream(audio); + } } @@ -216,14 +214,11 @@ bool paHandler::init(audioSetup setup) void paHandler::setVolume(unsigned char volume) { - #ifdef Q_OS_WIN this->volume = audiopot[volume] * 5; #else this->volume = audiopot[volume]; #endif - - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } void paHandler::incomingAudio(audioPacket packet) @@ -280,8 +275,8 @@ void paHandler::convertedOutput(audioPacket packet) { currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000); } - amplitude = packet.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + amplitude = packet.amplitudePeak; + emit haveLevels(getAmplitude(), static_cast(packet.amplitudeRMS * 255.0), setup.latency, currentLatency, isUnderrun, isOverrun); } } @@ -291,10 +286,10 @@ void paHandler::convertedInput(audioPacket packet) { if (packet.data.size() > 0) { emit haveAudioData(packet); - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; const PaStreamInfo* info = Pa_GetStreamInfo(audio); currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->inputLatency * 1000); - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + emit haveLevels(getAmplitude(), packet.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun); } } diff --git a/pahandler.h b/pahandler.h index a8fac05..c9bd474 100644 --- a/pahandler.h +++ b/pahandler.h @@ -55,7 +55,7 @@ signals: void audioMessage(QString message); void sendLatency(quint16 newSize); void haveAudioData(const audioPacket& data); - void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void haveLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); diff --git a/qledlabel.cpp b/qledlabel.cpp index 640b1c4..d85ca8a 100644 --- a/qledlabel.cpp +++ b/qledlabel.cpp @@ -6,12 +6,14 @@ static const QString greenSS = QString("color: white;border-radius: %1;backgroun static const QString redSS = QString("color: white;border-radius: %1;background-color: qlineargradient(spread:pad, x1:0.145, y1:0.16, x2:0.92, y2:0.988636, stop:0 rgba(255, 12, 12, 255), stop:0.869347 rgba(103, 0, 0, 255));").arg(SIZE / 2); static const QString orangeSS = QString("color: white;border-radius: %1;background-color: qlineargradient(spread:pad, x1:0.232, y1:0.272, x2:0.98, y2:0.959773, stop:0 rgba(255, 113, 4, 255), stop:1 rgba(91, 41, 7, 255))").arg(SIZE / 2); static const QString blueSS = QString("color: white;border-radius: %1;background-color: qlineargradient(spread:pad, x1:0.04, y1:0.0565909, x2:0.799, y2:0.795, stop:0 rgba(203, 220, 255, 255), stop:0.41206 rgba(0, 115, 255, 255), stop:1 rgba(0, 49, 109, 255));").arg(SIZE / 2); +static const QString blankSS = QString("color: white;border-radius: %1;background-color: qlineargradient(spread:pad, x1:0.04, y1:0.0565909, x2:0.799, y2:0.795, stop:0 rgba(203, 220, 255, 0), stop:0.41206 rgba(0, 115, 255, 0), stop:1 rgba(0, 49, 109, 0));").arg(SIZE / 2); QLedLabel::QLedLabel(QWidget* parent) : QLabel(parent) { //Set to ok by default setState(StateOkBlue); + baseColor = QColor(0, 115, 255, 255); setFixedSize(SIZE, SIZE); } @@ -29,6 +31,11 @@ void QLedLabel::setState(State state) setStyleSheet(redSS); break; case StateOkBlue: + setStyleSheet(blueSS); + break; + case StateBlank: + setStyleSheet(blankSS); + break; default: setStyleSheet(blueSS); break; @@ -38,4 +45,62 @@ void QLedLabel::setState(State state) void QLedLabel::setState(bool state) { setState(state ? StateOk : StateError); -} \ No newline at end of file +} + +void QLedLabel::setColor(QColor customColor, bool applyGradient=true) +{ + QColor top,middle,bottom; + this->baseColor = customColor; + + if(applyGradient) + { + top = customColor.lighter(200); + middle = customColor; + bottom = customColor.darker(200); + } else { + top = customColor; + middle = customColor; + bottom = customColor; + } + + // Stop 0 is the upper corner color, white-ish + // Stop 0.4 is the middle color + // Stop 1 is the bottom-right corner, darker. + + QString colorSS = QString("color: white;\ + border-radius: %1;background-color: \ + qlineargradient(spread:pad, x1:0.04, \ + y1:0.0565909, x2:0.799, y2:0.795, \ + stop:0 \ + rgba(%2, %3, %4, %5), \ + stop:0.41206 \ + rgba(%6, %7, %8, %9), \ + stop:1 \ + rgba(%10, %11, %12, %13));").arg(SIZE / 2) + .arg(top.red()).arg(top.green()).arg(top.blue()).arg(top.alpha()) + .arg(middle.red()).arg(middle.green()).arg(middle.blue()).arg(middle.alpha()) + .arg(bottom.red()).arg(bottom.green()).arg(bottom.blue()).arg(bottom.alpha()); + + setStyleSheet(colorSS); +} + +void QLedLabel::setColor(QString colorString, bool applyGradient=true) +{ + QColor c = QColor(colorString); + setColor(c, applyGradient); +} + +void QLedLabel::setColor(QColor c) +{ + this->setColor(c, true); +} + +void QLedLabel::setColor(QString s) +{ + this->setColor(s, true); +} + +QColor QLedLabel::getColor() +{ + return baseColor; +} diff --git a/qledlabel.h b/qledlabel.h index b5dc706..d4117c9 100644 --- a/qledlabel.h +++ b/qledlabel.h @@ -13,7 +13,8 @@ public: StateOk, StateOkBlue, StateWarning, - StateError + StateError, + StateBlank }; @@ -22,6 +23,14 @@ signals: public slots: void setState(State state); void setState(bool state); + void setColor(QColor customColor, bool applyGradient); + void setColor(QString colorString, bool applyGradient); + void setColor(QColor c); + void setColor(QString s); + QColor getColor(); + +private: + QColor baseColor; }; #endif // QLEDLABEL_H diff --git a/rigcommander.cpp b/rigcommander.cpp index 60efd9f..d6ea80c 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -166,6 +166,8 @@ void rigCommander::commSetup(unsigned char rigCivAddr, udpPreferences prefs, aud // Connect for errors/alerts connect(udp, SIGNAL(haveNetworkError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString))); connect(udp, SIGNAL(haveNetworkStatus(networkStatus)), this, SLOT(handleStatusUpdate(networkStatus))); + connect(udp, SIGNAL(haveNetworkAudioLevels(networkAudioLevels)), this, SLOT(handleNetworkAudioLevels(networkAudioLevels))); + connect(ptty, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(handleSerialPortError(QString, QString))); connect(this, SIGNAL(getMoreDebug()), ptty, SLOT(debugThis())); @@ -245,6 +247,11 @@ void rigCommander::handleStatusUpdate(const networkStatus status) emit haveStatusUpdate(status); } +void rigCommander::handleNetworkAudioLevels(networkAudioLevels l) +{ + emit haveNetworkAudioLevels(l); +} + bool rigCommander::usingLAN() { return usingNativeLAN; @@ -3536,6 +3543,34 @@ void rigCommander::determineRigCaps() createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"), }; break; + case model746: + rigCaps.modelName = QString("IC-746"); + rigCaps.rigctlModel = 3023; + rigCaps.hasSpectrum = false; + rigCaps.inputs.clear(); + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + rigCaps.hasFDcomms = false; + rigCaps.hasATU = true; + rigCaps.hasTBPF = true; + rigCaps.hasIFShift = true; + rigCaps.hasCTCSS = true; + rigCaps.hasDTCS = true; + rigCaps.hasAntennaSel = true; + rigCaps.preamps.push_back('\x01'); + rigCaps.preamps.push_back('\x02'); + rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x20'}); + // There are two HF and VHF ant, 12-01 adn 12-02 select the HF, the VHF is auto selected + // this incorrectly shows up as 2 and 3 in the drop down. + rigCaps.antennas = {0x01, 0x02}; + rigCaps.bands = standardHF; + rigCaps.bands.push_back(band2m); + rigCaps.bands.push_back(bandGen); + rigCaps.modes = commonModes; + rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); + break; + case model756pro: rigCaps.modelName = QString("IC-756 Pro"); rigCaps.rigctlModel = 3027; diff --git a/rigcommander.h b/rigcommander.h index 7921b24..e26941e 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -36,6 +36,7 @@ enum meterKind { meterRxdB, meterTxMod, meterRxAudio, + meterAudio, meterLatency }; @@ -277,6 +278,7 @@ public slots: // Housekeeping: void handleStatusUpdate(const networkStatus status); + void handleNetworkAudioLevels(networkAudioLevels); void radioSelection(QList radios); void radioUsage(quint8 radio, quint8 busy, QString name, QString ip); void setCurrentRadio(quint8 radio); @@ -288,6 +290,7 @@ signals: void commReady(); void haveSerialPortError(const QString port, const QString errorText); void haveStatusUpdate(const networkStatus status); + void haveNetworkAudioLevels(const networkAudioLevels l); void dataForComm(const QByteArray &outData); void toggleRTS(bool rtsOn); @@ -397,6 +400,11 @@ private: quint16 decodeTone(QByteArray eTone); quint16 decodeTone(QByteArray eTone, bool &tinv, bool &rinv); + unsigned char audioLevelRxMean[50]; + unsigned char audioLevelRxPeak[50]; + unsigned char audioLevelTxMean[50]; + unsigned char audioLevelTxPeak[50]; + void parseMode(); void parseSpectrum(); void parseWFData(); diff --git a/rigidentities.cpp b/rigidentities.cpp index 1a94836..62ea5aa 100644 --- a/rigidentities.cpp +++ b/rigidentities.cpp @@ -58,8 +58,8 @@ model_kind determineRadioModel(unsigned char rigID) case model736: rig = model736; break; - case model910h: - rig = model910h; + case model746: + rig = model746; break; case model756pro: rig = model756pro; @@ -70,6 +70,9 @@ model_kind determineRadioModel(unsigned char rigID) case model756proiii: rig = model756proiii; break; + case model910h: + rig = model910h; + break; case model9100: rig = model9100; break; diff --git a/rigidentities.h b/rigidentities.h index cad0fc2..a668df5 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -32,6 +32,7 @@ enum model_kind { model706 = 0x58, model718 = 0x5E, model736 = 0x40, + model746 = 0x56, model756pro = 0x5C, model756proii = 0x64, model756proiii = 0x6E, diff --git a/rthandler.cpp b/rthandler.cpp index b7438f5..f1d7313 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -6,6 +6,7 @@ #include #endif +#define RT_EXCEPTION rtHandler::rtHandler(QObject* parent) { @@ -15,22 +16,27 @@ rtHandler::rtHandler(QObject* parent) rtHandler::~rtHandler() { + if (converterThread != Q_NULLPTR) { + converterThread->quit(); + converterThread->wait(); + } + if (isInitialized) { + +#ifdef RT_EXCEPTION try { +#endif audio->abortStream(); audio->closeStream(); +#ifdef RT_EXCEPTION } catch (RtAudioError& e) { qInfo(logAudio()) << "Error closing stream:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); } +#endif delete audio; } - - if (converterThread != Q_NULLPTR) { - converterThread->quit(); - converterThread->wait(); - } } @@ -87,14 +93,17 @@ bool rtHandler::init(audioSetup setup) } aParams.firstChannel = 0; +#ifdef RT_EXCEPTION try { +#endif info = audio->getDeviceInfo(aParams.deviceId); +#ifdef RT_EXCEPTION } catch (RtAudioError e) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Device exception:" << aParams.deviceId << ":" << QString::fromStdString(e.getMessage()); goto errorHandler; } - +#endif if (info.probed) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << QString::fromStdString(info.name) << "(" << aParams.deviceId << ") successfully probed"; @@ -190,7 +199,9 @@ bool rtHandler::init(audioSetup setup) // Per channel chunk size. this->chunkSize = (outFormat.bytesForDuration(setup.blockSize * 1000) / (outFormat.sampleSize()/8) / outFormat.channelCount()); +#ifdef RT_EXCEPTION try { +#endif if (setup.isinput) { audio->openStream(NULL, &aParams, sampleFormat, outFormat.sampleRate(), &this->chunkSize, &staticWrite, this, &options); emit setupConverter(outFormat, inFormat, 7, setup.resampleQuality); @@ -205,12 +216,14 @@ bool rtHandler::init(audioSetup setup) isInitialized = true; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "device successfully opened"; qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "detected latency:" << audio->getStreamLatency(); +#ifdef RT_EXCEPTION } catch (RtAudioError& e) { qInfo(logAudio()) << "Error opening:" << QString::fromStdString(e.getMessage()); // Try again? goto errorHandler; } +#endif } else { @@ -240,10 +253,7 @@ errorHandler: void rtHandler::setVolume(unsigned char volume) { - this->volume = audiopot[volume]; - - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } void rtHandler::incomingAudio(audioPacket packet) @@ -318,9 +328,9 @@ void rtHandler::convertedOutput(audioPacket packet) audioMutex.lock(); arrayBuffer.append(packet.data); audioMutex.unlock(); - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000); - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + emit haveLevels(getAmplitude(), packet.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun); } @@ -329,9 +339,9 @@ void rtHandler::convertedInput(audioPacket packet) { if (packet.data.size() > 0) { emit haveAudioData(packet); - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount())/1000); - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + emit haveLevels(getAmplitude(), static_cast(packet.amplitudeRMS * 255.0), setup.latency, currentLatency, isUnderrun, isOverrun); } } diff --git a/rthandler.h b/rthandler.h index c48a2b4..2b73b12 100644 --- a/rthandler.h +++ b/rthandler.h @@ -61,7 +61,7 @@ signals: void audioMessage(QString message); void sendLatency(quint16 newSize); void haveAudioData(const audioPacket& data); - void haveLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void haveLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); void setupConverter(QAudioFormat in, QAudioFormat out, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); diff --git a/udpaudio.cpp b/udpaudio.cpp index b49359d..1326a67 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -8,6 +8,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint this->localIP = local; this->port = audioPort; this->radioIP = ip; + this->rxSetup = rxSetup; + this->txSetup = txSetup; if (txSetup.sampleRate == 0) { enableTx = false; @@ -16,74 +18,8 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint init(lport); // Perform connection QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived); - if (rxSetup.type == qtAudio) { - rxaudio = new audioHandler(); - } - else if (rxSetup.type == portAudio) { - rxaudio = new paHandler(); - } - else if (rxSetup.type == rtAudio) { - rxaudio = new rtHandler(); - } - else - { - qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; - } - - rxAudioThread = new QThread(this); - rxAudioThread->setObjectName("rxAudio()"); - - rxaudio->moveToThread(rxAudioThread); - - rxAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); - - // signal/slot not currently used. - connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); - connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); - connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); - connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool,bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool,bool))); - connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); - - - sendControl(false, 0x03, 0x00); // First connect packet - - pingTimer = new QTimer(); - connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); - pingTimer->start(PING_PERIOD); // send ping packets every 100ms - - if (enableTx) { - if (txSetup.type == qtAudio) { - txaudio = new audioHandler(); - } - else if (txSetup.type == portAudio) { - txaudio = new paHandler(); - } - else if (txSetup.type == rtAudio) { - txaudio = new rtHandler(); - } - else - { - qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; - } - - txAudioThread = new QThread(this); - rxAudioThread->setObjectName("txAudio()"); - - txaudio->moveToThread(txAudioThread); - - txAudioThread->start(QThread::TimeCriticalPriority); - - connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); - connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); - connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool, bool))); - - connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); - emit setupTxAudio(txSetup); - } - - emit setupRxAudio(rxSetup); + + startAudio(); watchdogTimer = new QTimer(); connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog); @@ -96,6 +32,7 @@ udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint udpAudio::~udpAudio() { + if (pingTimer != Q_NULLPTR) { qDebug(logUdp()) << "Stopping pingTimer"; @@ -145,6 +82,21 @@ void udpAudio::watchdog() qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?"; alerted = true; + if (rxAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping rxaudio thread"; + rxAudioThread->quit(); + rxAudioThread->wait(); + rxAudioThread = Q_NULLPTR; + rxaudio = Q_NULLPTR; + } + + if (txAudioThread != Q_NULLPTR) { + qDebug(logUdp()) << "Stopping txaudio thread"; + txAudioThread->quit(); + txAudioThread->wait(); + txAudioThread = Q_NULLPTR; + txaudio = Q_NULLPTR; + } } } else @@ -207,17 +159,18 @@ void udpAudio::setVolume(unsigned char value) emit haveSetVolume(value); } -void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) { +void udpAudio::getRxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over) { - emit haveRxLevels(amplitude, latency, current, under, over); + emit haveRxLevels(amplitudePeak, amplitudeRMS, latency, current, under, over); } -void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over) { - emit haveTxLevels(amplitude, latency, current, under, over); +void udpAudio::getTxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over) { + emit haveTxLevels(amplitudePeak, amplitudeRMS, latency, current, under, over); } void udpAudio::dataReceived() { + while (udp->hasPendingDatagrams()) { QNetworkDatagram datagram = udp->receiveDatagram(); //qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10); @@ -258,6 +211,10 @@ void udpAudio::dataReceived() // Prefer signal/slot to forward audio as it is thread/safe // Need to do more testing but latency appears fine. //rxaudio->incomingAudio(tempAudio); + if (rxAudioThread == Q_NULLPTR) + { + startAudio(); + } emit haveAudioData(tempAudio); } break; @@ -270,4 +227,75 @@ void udpAudio::dataReceived() } } +void udpAudio::startAudio() { + if (rxSetup.type == qtAudio) { + rxaudio = new audioHandler(); + } + else if (rxSetup.type == portAudio) { + rxaudio = new paHandler(); + } + else if (rxSetup.type == rtAudio) { + rxaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Receive Audio Handler selected!"; + } + + rxAudioThread = new QThread(this); + rxAudioThread->setObjectName("rxAudio()"); + + rxaudio->moveToThread(rxAudioThread); + + rxAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup))); + + // signal/slot not currently used. + connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket))); + connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16))); + connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char))); + connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, quint16, bool, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, quint16, bool, bool))); + connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater())); + + + sendControl(false, 0x03, 0x00); // First connect packet + + pingTimer = new QTimer(); + connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing); + pingTimer->start(PING_PERIOD); // send ping packets every 100ms + + if (enableTx) { + if (txSetup.type == qtAudio) { + txaudio = new audioHandler(); + } + else if (txSetup.type == portAudio) { + txaudio = new paHandler(); + } + else if (txSetup.type == rtAudio) { + txaudio = new rtHandler(); + } + else + { + qCritical(logAudio()) << "Unsupported Transmit Audio Handler selected!"; + } + + txAudioThread = new QThread(this); + rxAudioThread->setObjectName("txAudio()"); + + txaudio->moveToThread(txAudioThread); + + txAudioThread->start(QThread::TimeCriticalPriority); + + connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup))); + connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); + connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, quint16, bool, bool))); + + connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater())); + emit setupTxAudio(txSetup); + } + + emit setupRxAudio(rxSetup); + +} diff --git a/udpaudio.h b/udpaudio.h index 87ae9cf..3ab6d42 100644 --- a/udpaudio.h +++ b/udpaudio.h @@ -51,14 +51,14 @@ signals: void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); - void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); - void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void haveRxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); + void haveTxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); public slots: void changeLatency(quint16 value); void setVolume(unsigned char value); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); + void getRxLevels(quint16 amplitude, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); + void getTxLevels(quint16 amplitude, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); void receiveAudioData(audioPacket audio); private: @@ -66,6 +66,9 @@ private: void sendTxAudio(); void dataReceived(); void watchdog(); + void startAudio(); + audioSetup rxSetup; + audioSetup txSetup; uint16_t sendAudioSeq = 0; @@ -82,4 +85,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/udpbase.h b/udpbase.h index f042b4d..e5c67cf 100644 --- a/udpbase.h +++ b/udpbase.h @@ -37,6 +37,15 @@ struct udpPreferences { quint8 waterfallFormat; }; +struct networkAudioLevels { + bool haveTxLevels = false; + bool haveRxLevels = false; + quint8 rxAudioRMS = 0; + quint8 txAudioRMS = 0; + quint8 rxAudioPeak = 0; + quint8 txAudioPeak = 0; +}; + struct networkStatus { quint8 rxAudioBufferPercent; quint8 txAudioBufferPercent; diff --git a/udphandler.cpp b/udphandler.cpp index 93fd3f9..a666b90 100644 --- a/udphandler.cpp +++ b/udphandler.cpp @@ -143,20 +143,71 @@ void udpHandler::receiveDataFromUserToRig(QByteArray data) } } -void udpHandler::getRxLevels(quint16 amplitude,quint16 latency,quint16 current, bool under, bool over) { - status.rxAudioLevel = amplitude; +void udpHandler::getRxLevels(quint16 amplitudePeak, quint16 amplitudeRMS,quint16 latency,quint16 current, bool under, bool over) { + status.rxAudioLevel = amplitudePeak; status.rxLatency = latency; status.rxCurrentLatency = current; status.rxUnderrun = under; status.rxOverrun = over; + audioLevelsRxPeak[(audioLevelsRxPosition)%audioLevelBufferSize] = amplitudePeak; + audioLevelsRxRMS[(audioLevelsRxPosition)%audioLevelBufferSize] = amplitudeRMS; + + if((audioLevelsRxPosition)%4 == 0) + { + // calculate mean and emit signal + unsigned char meanPeak = findMax(audioLevelsRxPeak); + unsigned char meanRMS = findMean(audioLevelsRxRMS); + networkAudioLevels l; + l.haveRxLevels = true; + l.rxAudioPeak = meanPeak; + l.rxAudioRMS = meanRMS; + emit haveNetworkAudioLevels(l); + } + audioLevelsRxPosition++; } -void udpHandler::getTxLevels(quint16 amplitude,quint16 latency, quint16 current, bool under, bool over) { - status.txAudioLevel = amplitude; +void udpHandler::getTxLevels(quint16 amplitudePeak, quint16 amplitudeRMS ,quint16 latency, quint16 current, bool under, bool over) { + status.txAudioLevel = amplitudePeak; status.txLatency = latency; status.txCurrentLatency = current; status.txUnderrun = under; status.txOverrun = over; + audioLevelsTxPeak[(audioLevelsTxPosition)%audioLevelBufferSize] = amplitudePeak; + audioLevelsTxRMS[(audioLevelsTxPosition)%audioLevelBufferSize] = amplitudeRMS; + + if((audioLevelsTxPosition)%4 == 0) + { + // calculate mean and emit signal + unsigned char meanPeak = findMax(audioLevelsTxPeak); + unsigned char meanRMS = findMean(audioLevelsTxRMS); + networkAudioLevels l; + l.haveTxLevels = true; + l.txAudioPeak = meanPeak; + l.txAudioRMS = meanRMS; + emit haveNetworkAudioLevels(l); + } + audioLevelsTxPosition++; +} + +unsigned char udpHandler::findMean(unsigned char *data) +{ + unsigned int sum=0; + for(int p=0; p < audioLevelBufferSize; p++) + { + sum += data[p]; + } + return sum / audioLevelBufferSize; +} + +unsigned char udpHandler::findMax(unsigned char *data) +{ + unsigned int max=0; + for(int p=0; p < audioLevelBufferSize; p++) + { + if(data[p] > max) + max = data[p]; + } + return max; } void udpHandler::dataReceived() @@ -302,23 +353,25 @@ void udpHandler::dataReceived() if (!streamOpened) { civ = new udpCivData(localIP, radioIP, civPort, splitWf, civLocalPort); + QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); // TX is not supported if (txSampleRates < 2) { txSetup.sampleRate=0; txSetup.codec = 0; } + streamOpened = true; + } + if (audio == Q_NULLPTR) { audio = new udpAudio(localIP, radioIP, audioPort, audioLocalPort, rxSetup, txSetup); - QObject::connect(civ, SIGNAL(receive(QByteArray)), this, SLOT(receiveFromCivStream(QByteArray))); QObject::connect(audio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket))); QObject::connect(this, SIGNAL(haveChangeLatency(quint16)), audio, SLOT(changeLatency(quint16))); QObject::connect(this, SIGNAL(haveSetVolume(unsigned char)), audio, SLOT(setVolume(unsigned char))); - QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16,bool,bool)), this, SLOT(getRxLevels(quint16, quint16,quint16,bool,bool))); - QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16,quint16,bool,bool)), this, SLOT(getTxLevels(quint16, quint16,quint16,bool,bool))); - - streamOpened = true; + QObject::connect(audio, SIGNAL(haveRxLevels(quint16, quint16, quint16, quint16, bool, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, quint16, bool, bool))); + QObject::connect(audio, SIGNAL(haveTxLevels(quint16, quint16, quint16, quint16, bool, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, quint16, bool, bool))); } + qInfo(logUdp()) << this->metaObject()->className() << "Got serial and audio request success, device name: " << devName; diff --git a/udphandler.h b/udphandler.h index 8300805..157006e 100644 --- a/udphandler.h +++ b/udphandler.h @@ -29,7 +29,7 @@ #include "udpcivdata.h" #include "udpaudio.h" - +#define audioLevelBufferSize (4) // Class to handle the connection/disconnection of the radio. class udpHandler: public udpBase @@ -56,9 +56,8 @@ public slots: void setVolume(unsigned char value); void init(); void setCurrentRadio(quint8 radio); - void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); - void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under, bool over); - + void getRxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); + void getTxLevels(quint16 amplitudePeak, quint16 amplitudeRMS, quint16 latency, quint16 current, bool under, bool over); signals: void haveDataFromPort(QByteArray data); // emit this when we have data, connect to rigcommander @@ -67,6 +66,7 @@ signals: void haveChangeLatency(quint16 value); void haveSetVolume(unsigned char value); void haveNetworkStatus(networkStatus); + void haveNetworkAudioLevels(networkAudioLevels); void haveBaudRate(quint32 baudrate); void requestRadioSelection(QList radios); void setRadioUsage(quint8, quint8 busy, QString name, QString mac); @@ -122,6 +122,19 @@ private: quint16 txSampleRates = 0; networkStatus status; bool splitWf = false; + + unsigned char audioLevelsTxPeak[audioLevelBufferSize]; + unsigned char audioLevelsRxPeak[audioLevelBufferSize]; + + unsigned char audioLevelsTxRMS[audioLevelBufferSize]; + unsigned char audioLevelsRxRMS[audioLevelBufferSize]; + + unsigned char audioLevelsTxPosition = 0; + unsigned char audioLevelsRxPosition = 0; + unsigned char findMean(unsigned char *d); + unsigned char findMax(unsigned char *d); + + }; diff --git a/wfmain.cpp b/wfmain.cpp index ebc7918..2deaa6f 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -5,10 +5,22 @@ #include "rigidentities.h" #include "logcategories.h" -// This code is copyright 2017-2020 Elliott H. Liggett +// This code is copyright 2017-2022 Elliott H. Liggett // All rights reserved -wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, QWidget *parent ) : +// Log support: +//static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); +QScopedPointer m_logFile; +QMutex logMutex; +QMutex logTextMutex; +QVector logStringBuffer; +#ifdef QT_DEBUG +bool debugModeLogging = true; +#else +bool debugModeLogging = false; +#endif + +wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, bool debugMode, QWidget *parent ) : QMainWindow(parent), ui(new Ui::wfmain) { @@ -16,9 +28,21 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s QGuiApplication::setApplicationName(QString("wfview")); setWindowIcon(QIcon( QString(":resources/wfview.png"))); + this->debugMode = debugMode; + debugModeLogging = debugMode; + 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()); ui->setupUi(this); setWindowTitle(QString("wfview")); + logWindow = new loggingWindow(); + initLogging(); + logWindow->setInitialDebugState(debugMode); + qInfo(logSystem()) << version; + this->serialPortCL = serialPortCL; this->hostCL = hostCL; @@ -50,25 +74,25 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType*>(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); haveRigCaps = false; setupKeyShortcuts(); setupMainUI(); + prepareSettingsWindow(); setSerialDevicesUI(); - setAudioDevicesUI(); - setupUsbControllerDevice(); - - setDefaultColors(); setDefPrefs(); getSettingsFilePath(settingsFile); setupPlots(); + setDefaultColorPresets(); + loadSettings(); // Look for saved preferences setAudioDevicesUI(); @@ -78,6 +102,8 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qDebug(logSystem()) << "Running setUIToPrefs()"; setUIToPrefs(); + loadColorPresetToUIandPlots(0); + qDebug(logSystem()) << "Running setInititalTiming()"; setInitialTiming(); @@ -176,6 +202,10 @@ void wfmain::openRig() // showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc // TODO: How do we know if the setting was loaded? + ui->audioSystemServerCombo->setEnabled(false); + ui->audioSystemCombo->setEnabled(false); + + ui->connectBtn->setText("Cancel connection"); // We are attempting to connect // TODO: Use these if they are found if(!serialPortCL.isEmpty()) @@ -434,6 +464,7 @@ void wfmain::makeRig() // Rig status and Errors: connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString))); connect(rig, SIGNAL(haveStatusUpdate(networkStatus)), this, SLOT(receiveStatusUpdate(networkStatus))); + connect(rig, SIGNAL(haveNetworkAudioLevels(networkAudioLevels)), this, SLOT(receiveNetworkAudioLevels(networkAudioLevels))); connect(rig, SIGNAL(requestRadioSelection(QList)), this, SLOT(radioSelection(QList))); connect(rig, SIGNAL(setRadioUsage(quint8, quint8, QString, QString)), selRad, SLOT(setInUse(quint8, quint8, QString, QString))); connect(selRad, SIGNAL(selectedRadio(quint8)), rig, SLOT(setCurrentRadio(quint8))); @@ -625,22 +656,62 @@ void wfmain::receiveStatusUpdate(networkStatus status) //qInfo(logSystem()) << "Got Status Update" << status.rxAudioLevel; } +void wfmain::receiveNetworkAudioLevels(networkAudioLevels l) +{ + /* + meterKind m2mtr = ui->meter2Widget->getMeterType(); + + if(m2mtr == meterAudio) + { + if(amTransmitting) + { + if(l.haveTxLevels) + ui->meter2Widget->setLevels(l.txAudioRMS, l.txAudioPeak); + } else { + if(l.haveRxLevels) + ui->meter2Widget->setLevels(l.rxAudioRMS, l.rxAudioPeak); + } + } else if (m2mtr == meterTxMod) { + if(l.haveTxLevels) + ui->meter2Widget->setLevels(l.txAudioRMS, l.txAudioPeak); + } else if (m2mtr == meterRxAudio) { + if(l.haveRxLevels) + ui->meter2Widget->setLevels(l.rxAudioRMS, l.rxAudioPeak); + } + */ + + + meterKind m = meterNone; + if(l.haveRxLevels) + { + m = meterRxAudio; + receiveMeter(m, l.rxAudioPeak); + } + if(l.haveTxLevels) + { + m = meterTxMod; + receiveMeter(m, l.txAudioPeak); + } + +} + void wfmain::setupPlots() { - -// Line 290-- spectrumDrawLock = true; - plot = ui->plot; // rename it waterfall. + plot = ui->plot; wf = ui->waterfall; freqIndicatorLine = new QCPItemLine(plot); freqIndicatorLine->setAntialiased(true); freqIndicatorLine->setPen(QPen(Qt::blue)); -// ui->plot->addGraph(); // primary - ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first? + ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first. + ui->plot->addLayer( "Top Layer", ui->plot->layer("main")); + ui->plot->graph(0)->setLayer("Top Layer"); + + ui->waterfall->addGraph(); colorMap = new QCPColorMap(wf->xAxis, wf->yAxis); @@ -755,6 +826,10 @@ void wfmain::setupMainUI() ui->meter2selectionCombo->addItem("Voltage", meterVoltage); ui->meter2selectionCombo->addItem("Current", meterCurrent); ui->meter2selectionCombo->addItem("Center", meterCenter); + ui->meter2selectionCombo->addItem("TxRxAudio", meterAudio); + ui->meter2selectionCombo->addItem("RxAudio", meterRxAudio); + ui->meter2selectionCombo->addItem("TxAudio", meterTxMod); + ui->meter2Widget->hide(); ui->meter2selectionCombo->show(); @@ -885,9 +960,22 @@ void wfmain::setupMainUI() connect(this->trxadj, &transceiverAdjustments::setTPBFOuter, [=](const unsigned char &newValue) { issueCmdUniquePriority(cmdSetTPBFOuter, newValue);} ); +} +void wfmain::prepareSettingsWindow() +{ + settingsTabisAttached = true; + settingsWidgetWindow = new QWidget; + settingsWidgetLayout = new QGridLayout; + settingsWidgetTab = new QTabWidget; + settingsWidgetWindow->setLayout(settingsWidgetLayout); + settingsWidgetLayout->addWidget(settingsWidgetTab); + settingsWidgetWindow->setWindowFlag(Qt::WindowCloseButtonHint, false); + //settingsWidgetWindow->setWindowFlag(Qt::WindowMinimizeButtonHint, false); + //settingsWidgetWindow->setWindowFlag(Qt::WindowMaximizeButtonHint, false); + // TODO: Capture an event when the window closes and handle accordingly. } void wfmain::updateSizes(int tabIndex) @@ -975,12 +1063,6 @@ void wfmain::setInitialTiming() delayedCommand->setSingleShot(false); connect(delayedCommand, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - // TODO: Remove this: -// periodicPollingTimer = new QTimer(this); -// periodicPollingTimer->setInterval(10); -// periodicPollingTimer->setSingleShot(false); - //connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(sendRadioCommandLoop())); - pttTimer = new QTimer(this); pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms pttTimer->setSingleShot(true); @@ -1049,15 +1131,30 @@ void wfmain::setUIToPrefs() ui->fullScreenChk->setChecked(prefs.useFullScreen); on_fullScreenChk_clicked(prefs.useFullScreen); - ui->useDarkThemeChk->setChecked(prefs.useDarkMode); - on_useDarkThemeChk_clicked(prefs.useDarkMode); - ui->useSystemThemeChk->setChecked(prefs.useSystemTheme); on_useSystemThemeChk_clicked(prefs.useSystemTheme); - ui->drawPeakChk->setChecked(prefs.drawPeaks); - on_drawPeakChk_clicked(prefs.drawPeaks); - drawPeaks = prefs.drawPeaks; + underlayMode = prefs.underlayMode; + switch(underlayMode) + { + case underlayNone: + ui->underlayNone->setChecked(true); + break; + case underlayPeakHold: + ui->underlayPeakHold->setChecked(true); + break; + case underlayPeakBuffer: + ui->underlayPeakBuffer->setChecked(true); + break; + case underlayAverageBuffer: + ui->underlayAverageBuffer->setChecked(true); + break; + default: + break; + } + + ui->underlayBufferSlider->setValue(prefs.underlayBufferSize); + on_underlayBufferSlider_valueChanged(prefs.underlayBufferSize); ui->wfAntiAliasChk->setChecked(prefs.wfAntiAlias); on_wfAntiAliasChk_clicked(prefs.wfAntiAlias); @@ -1067,6 +1164,20 @@ void wfmain::setUIToPrefs() ui->wfLengthSlider->setValue(prefs.wflength); prepareWf(prefs.wflength); + preparePlasma(); + ui->topLevelSlider->setValue(prefs.plotCeiling); + ui->botLevelSlider->setValue(prefs.plotFloor); + + plot->yAxis->setRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + colorMap->setDataRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + + colorPrefsType p; + for(int pn=0; pn < numColorPresetsTotal; pn++) + { + p = colorPreset[pn]; + if(p.presetName != Q_NULLPTR) + ui->colorPresetCombo->setItemText(pn, *p.presetName); + } ui->wfthemeCombo->setCurrentIndex(ui->wfthemeCombo->findData(prefs.wftheme)); colorMap->setGradient(static_cast(prefs.wftheme)); @@ -1350,9 +1461,11 @@ void wfmain::changeFrequency(int value) { void wfmain::setDefPrefs() { defPrefs.useFullScreen = false; - defPrefs.useDarkMode = true; defPrefs.useSystemTheme = false; defPrefs.drawPeaks = true; + defPrefs.currentColorPresetNumber = 0; + defPrefs.underlayMode = underlayNone; + defPrefs.underlayBufferSize = 64; defPrefs.wfAntiAlias = false; defPrefs.wfInterpolate = true; defPrefs.stylesheetPath = QString("qdarkstyle/style.qss"); @@ -1369,6 +1482,8 @@ void wfmain::setDefPrefs() defPrefs.localAFgain = 255; defPrefs.wflength = 160; defPrefs.wftheme = static_cast(QCPColorGradient::gpJet); + defPrefs.plotFloor = 0; + defPrefs.plotCeiling = 160; defPrefs.confirmExit = true; defPrefs.confirmPowerOff = true; defPrefs.meter2Type = meterNone; @@ -1393,10 +1508,17 @@ void wfmain::loadSettings() // UI: (full screen, dark theme, draw peaks, colors, etc) settings->beginGroup("Interface"); 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.plotFloor = settings->value("plotFloor", defPrefs.plotFloor).toInt(); + prefs.plotCeiling = settings->value("plotCeiling", defPrefs.plotCeiling).toInt(); + plotFloor = prefs.plotFloor; + plotCeiling = prefs.plotCeiling; + wfFloor = prefs.plotFloor; + wfCeiling = prefs.plotCeiling; prefs.drawPeaks = settings->value("DrawPeaks", defPrefs.drawPeaks).toBool(); + prefs.underlayBufferSize = settings->value("underlayBufferSize", defPrefs.underlayBufferSize).toInt(); + prefs.underlayMode = static_cast(settings->value("underlayMode", defPrefs.underlayMode).toInt()); prefs.wfAntiAlias = settings->value("WFAntiAlias", defPrefs.wfAntiAlias).toBool(); prefs.wfInterpolate = settings->value("WFInterpolate", defPrefs.wfInterpolate).toBool(); prefs.wflength = (unsigned int)settings->value("WFLength", defPrefs.wflength).toInt(); @@ -1411,37 +1533,53 @@ void wfmain::loadSettings() prefs.meter2Type = static_cast(settings->value("Meter2Type", defPrefs.meter2Type).toInt()); settings->endGroup(); - // Load color schemes: - // Per this bug: https://forum.qt.io/topic/24725/solved-qvariant-will-drop-alpha-value-when-save-qcolor/5 - // the alpha channel is dropped when converting raw qvariant of QColor. Therefore, we are storing as unsigned int and converting back. + // Load in the color presets. The default values are already loaded. - settings->beginGroup("DarkColors"); - prefs.colorScheme.Dark_PlotBackground = QColor::fromRgba(settings->value("Dark_PlotBackground", defaultColors.Dark_PlotBackground.rgba()).toUInt()); - prefs.colorScheme.Dark_PlotAxisPen = QColor::fromRgba(settings->value("Dark_PlotAxisPen", defaultColors.Dark_PlotAxisPen.rgba()).toUInt()); + settings->beginGroup("ColorPresets"); + settings->value("currentColorPresetNumber", prefs.currentColorPresetNumber).toInt(); + if(prefs.currentColorPresetNumber > numColorPresetsTotal-1) + prefs.currentColorPresetNumber = 0; - prefs.colorScheme.Dark_PlotLegendTextColor = QColor::fromRgba(settings->value("Dark_PlotLegendTextColor", defaultColors.Dark_PlotLegendTextColor.rgba()).toUInt()); - prefs.colorScheme.Dark_PlotLegendBorderPen = QColor::fromRgba(settings->value("Dark_PlotLegendBorderPen", defaultColors.Dark_PlotLegendBorderPen.rgba()).toUInt()); - prefs.colorScheme.Dark_PlotLegendBrush = QColor::fromRgba(settings->value("Dark_PlotLegendBrush", defaultColors.Dark_PlotLegendBrush.rgba()).toUInt()); - - prefs.colorScheme.Dark_PlotTickLabel = QColor::fromRgba(settings->value("Dark_PlotTickLabel", defaultColors.Dark_PlotTickLabel.rgba()).toUInt()); - prefs.colorScheme.Dark_PlotBasePen = QColor::fromRgba(settings->value("Dark_PlotBasePen", defaultColors.Dark_PlotBasePen.rgba()).toUInt()); - prefs.colorScheme.Dark_PlotTickPen = QColor::fromRgba(settings->value("Dark_PlotTickPen", defaultColors.Dark_PlotTickPen.rgba()).toUInt()); - - prefs.colorScheme.Dark_PeakPlotLine = QColor::fromRgba(settings->value("Dark_PeakPlotLine", defaultColors.Dark_PeakPlotLine.rgba()).toUInt()); - prefs.colorScheme.Dark_TuningLine = QColor::fromRgba(settings->value("Dark_TuningLine", defaultColors.Dark_TuningLine.rgba()).toUInt()); - settings->endGroup(); - - settings->beginGroup("LightColors"); - prefs.colorScheme.Light_PlotBackground = QColor::fromRgba(settings->value("Light_PlotBackground", defaultColors.Light_PlotBackground.rgba()).toUInt()); - prefs.colorScheme.Light_PlotAxisPen = QColor::fromRgba(settings->value("Light_PlotAxisPen", defaultColors.Light_PlotAxisPen.rgba()).toUInt()); - prefs.colorScheme.Light_PlotLegendTextColor = QColor::fromRgba(settings->value("Light_PlotLegendTextColo", defaultColors.Light_PlotLegendTextColor.rgba()).toUInt()); - prefs.colorScheme.Light_PlotLegendBorderPen = QColor::fromRgba(settings->value("Light_PlotLegendBorderPen", defaultColors.Light_PlotLegendBorderPen.rgba()).toUInt()); - prefs.colorScheme.Light_PlotLegendBrush = QColor::fromRgba(settings->value("Light_PlotLegendBrush", defaultColors.Light_PlotLegendBrush.rgba()).toUInt()); - prefs.colorScheme.Light_PlotTickLabel = QColor::fromRgba(settings->value("Light_PlotTickLabel", defaultColors.Light_PlotTickLabel.rgba()).toUInt()); - prefs.colorScheme.Light_PlotBasePen = QColor::fromRgba(settings->value("Light_PlotBasePen", defaultColors.Light_PlotBasePen.rgba()).toUInt()); - prefs.colorScheme.Light_PlotTickPen = QColor::fromRgba(settings->value("Light_PlotTickPen", defaultColors.Light_PlotTickPen.rgba()).toUInt()); - prefs.colorScheme.Light_PeakPlotLine = QColor::fromRgba(settings->value("Light_PeakPlotLine", defaultColors.Light_PeakPlotLine.rgba()).toUInt()); - prefs.colorScheme.Light_TuningLine = QColor::fromRgba(settings->value("Light_TuningLine", defaultColors.Light_TuningLine.rgba()).toUInt()); + int numPresetsInFile = settings->beginReadArray("ColorPreset"); + // We will use the number of presets that the working copy of wfview + // supports, as we must never exceed the available number. + if(numPresetsInFile > 0) + { + colorPrefsType *p; + QString tempName; + for(int pn=0; pn < numColorPresetsTotal; pn++) + { + settings->setArrayIndex(pn); + p = &(colorPreset[pn]); + p->presetNum = settings->value("presetNum", p->presetNum).toInt(); + tempName = settings->value("presetName", *p->presetName).toString(); + if((!tempName.isEmpty()) && tempName.length() < 11) + { + p->presetName->clear(); + p->presetName->append(tempName); + } + p->gridColor.setNamedColor(settings->value("gridColor", p->gridColor.name(QColor::HexArgb)).toString()); + p->axisColor.setNamedColor(settings->value("axisColor", p->axisColor.name(QColor::HexArgb)).toString()); + p->textColor.setNamedColor(settings->value("textColor", p->textColor.name(QColor::HexArgb)).toString()); + p->spectrumLine.setNamedColor(settings->value("spectrumLine", p->spectrumLine.name(QColor::HexArgb)).toString()); + p->spectrumFill.setNamedColor(settings->value("spectrumFill", p->spectrumFill.name(QColor::HexArgb)).toString()); + p->underlayLine.setNamedColor(settings->value("underlayLine", p->underlayLine.name(QColor::HexArgb)).toString()); + p->underlayFill.setNamedColor(settings->value("underlayFill", p->underlayFill.name(QColor::HexArgb)).toString()); + p->plotBackground.setNamedColor(settings->value("plotBackground", p->plotBackground.name(QColor::HexArgb)).toString()); + p->tuningLine.setNamedColor(settings->value("tuningLine", p->tuningLine.name(QColor::HexArgb)).toString()); + p->wfBackground.setNamedColor(settings->value("wfBackground", p->wfBackground.name(QColor::HexArgb)).toString()); + p->wfGrid.setNamedColor(settings->value("wfGrid", p->wfGrid.name(QColor::HexArgb)).toString()); + p->wfAxis.setNamedColor(settings->value("wfAxis", p->wfAxis.name(QColor::HexArgb)).toString()); + p->wfText.setNamedColor(settings->value("wfText", p->wfText.name(QColor::HexArgb)).toString()); + p->meterLevel.setNamedColor(settings->value("meterLevel", p->meterLevel.name(QColor::HexArgb)).toString()); + p->meterAverage.setNamedColor(settings->value("meterAverage", p->meterAverage.name(QColor::HexArgb)).toString()); + p->meterPeakLevel.setNamedColor(settings->value("meterPeakLevel", p->meterPeakLevel.name(QColor::HexArgb)).toString()); + p->meterPeakScale.setNamedColor(settings->value("meterPeakScale", p->meterPeakScale.name(QColor::HexArgb)).toString()); + p->meterLowerLine.setNamedColor(settings->value("meterLowerLine", p->meterLowerLine.name(QColor::HexArgb)).toString()); + p->meterLowText.setNamedColor(settings->value("meterLowText", p->meterLowText.name(QColor::HexArgb)).toString()); + } + } + settings->endArray(); settings->endGroup(); // Radio and Comms: C-IV addr, port to use @@ -1497,7 +1635,10 @@ void wfmain::loadSettings() prefs.audioSystem = static_cast(settings->value("AudioSystem", defPrefs.audioSystem).toInt()); ui->audioSystemCombo->blockSignals(true); + ui->audioSystemServerCombo->blockSignals(true); ui->audioSystemCombo->setCurrentIndex(prefs.audioSystem); + ui->audioSystemServerCombo->setCurrentIndex(prefs.audioSystem); + ui->audioSystemServerCombo->blockSignals(false); ui->audioSystemCombo->blockSignals(false); @@ -1518,6 +1659,7 @@ void wfmain::loadSettings() // If LAN is enabled, server gets its audio straight from the LAN ui->serverRXAudioInputCombo->setEnabled(!prefs.enableLAN); ui->serverTXAudioOutputCombo->setEnabled(!prefs.enableLAN); + ui->audioSystemServerCombo->setEnabled(!prefs.enableLAN); ui->baudRateCombo->setEnabled(!prefs.enableLAN); ui->serialDeviceListCombo->setEnabled(!prefs.enableLAN); @@ -1698,17 +1840,24 @@ void wfmain::loadSettings() int row = 0; ui->serverUsersTable->setRowCount(0); - foreach(SERVERUSER user, serverConfig.users) + QList::iterator user = serverConfig.users.begin(); + + while (user != serverConfig.users.end()) { - if (user.username != "" && user.password != "") + if (user->username != "" && user->password != "") { - serverAddUserLine(user.username, user.password, user.userType); + serverAddUserLine(user->username, user->password, user->userType); row++; + user++; + } + else { + user = serverConfig.users.erase(user); } } if (row == 0) { serverAddUserLine("", "", 0); + ui->serverAddUserBtn->setEnabled(false); } settings->endGroup(); @@ -1799,58 +1948,109 @@ void wfmain::serverAddUserLine(const QString& user, const QString& pass, const i ui->serverUsersTable->blockSignals(true); ui->serverUsersTable->insertRow(ui->serverUsersTable->rowCount()); - ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 0, new QTableWidgetItem(user)); + + ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 0, new QTableWidgetItem()); ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 1, new QTableWidgetItem()); ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 2, new QTableWidgetItem()); + ui->serverUsersTable->setItem(ui->serverUsersTable->rowCount() - 1, 3, new QTableWidgetItem()); + + + + QLineEdit* username = new QLineEdit(); + username->setProperty("row", (int)ui->serverUsersTable->rowCount() - 1); + username->setProperty("col", (int)0); + username->setText(user); + connect(username, SIGNAL(editingFinished()), this, SLOT(onServerUserFieldChanged())); + ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 0, username); QLineEdit* password = new QLineEdit(); password->setProperty("row", (int)ui->serverUsersTable->rowCount() - 1); + password->setProperty("col", (int)1); password->setEchoMode(QLineEdit::PasswordEchoOnEdit); password->setText(pass); - connect(password, SIGNAL(editingFinished()), this, SLOT(onServerPasswordChanged())); + connect(password, SIGNAL(editingFinished()), this, SLOT(onServerUserFieldChanged())); ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 1, password); QComboBox* comboBox = new QComboBox(); comboBox->insertItems(0, { "Full User","Full with no TX","Monitor only" }); + comboBox->setProperty("row", (int)ui->serverUsersTable->rowCount() - 1); + comboBox->setProperty("col", (int)2); comboBox->setCurrentIndex(type); + connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onServerUserFieldChanged())); ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 2, comboBox); + + QPushButton* button = new QPushButton(); + button->setText("Delete"); + button->setProperty("row", (int)ui->serverUsersTable->rowCount() - 1); + button->setProperty("col", (int)3); + connect(button, SIGNAL(clicked()), this, SLOT(onServerUserFieldChanged())); + ui->serverUsersTable->setCellWidget(ui->serverUsersTable->rowCount() - 1, 3, button); + ui->serverUsersTable->blockSignals(false); } -void wfmain::onServerPasswordChanged() +void wfmain::onServerUserFieldChanged() { + int row = sender()->property("row").toInt(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 1); - QByteArray pass; - passcode(password->text(), pass); - password->setText(pass); - qInfo() << "password row" << row << "changed"; - serverConfig.users.clear(); - for (int rows = 0; rows < ui->serverUsersTable->model()->rowCount(); rows++) + int col = sender()->property("col").toInt(); + qDebug() << "Server User field col" << col << "row" << row << "changed"; + + // This is a new user line so add to serverUsersTable + if (serverConfig.users.length() <= row) { - if (ui->serverUsersTable->item(rows, 0) != NULL) + qInfo() << "Something bad has happened, serverConfig.users is shorter than table!"; + } + else + { + if (col == 0) { - SERVERUSER user; - user.username = ui->serverUsersTable->item(rows, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(rows, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(rows, 2); - user.userType = comboBox->currentIndex(); - serverConfig.users.append(user); + QLineEdit* username = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 0); + if (username->text() != serverConfig.users[row].username) { + serverConfig.users[row].username = username->text(); + } } - else { - ui->serverUsersTable->removeRow(rows); + else if (col == 1) + { + QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(row, 1); + QByteArray pass; + passcode(password->text(), pass); + if (QString(pass) != serverConfig.users[row].password) { + serverConfig.users[row].password = pass; + } } + else if (col == 2) + { + QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(row, 2); + serverConfig.users[row].userType = comboBox->currentIndex(); + } + else if (col == 3) + { + serverConfig.users.removeAt(row); + ui->serverUsersTable->setRowCount(0); + foreach(SERVERUSER user, serverConfig.users) + { + serverAddUserLine(user.username, user.password, user.userType); + } + } + if (row == ui->serverUsersTable->rowCount() - 1) { + ui->serverAddUserBtn->setEnabled(true); + } + } } -void wfmain::on_serverUsersTable_cellClicked(int row, int col) +void wfmain::on_serverAddUserBtn_clicked() { - qInfo() << "Clicked on " << row << "," << col; - if (row == ui->serverUsersTable->model()->rowCount() - 1 && ui->serverUsersTable->item(row, 0) != NULL) { - serverAddUserLine("", "", 0); - } + serverAddUserLine("", "", 0); + SERVERUSER user; + user.username = ""; + user.password = ""; + user.userType = 0; + serverConfig.users.append(user); + + ui->serverAddUserBtn->setEnabled(false); } @@ -1889,9 +2089,9 @@ void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) serverConfig.rigs.first()->rxAudioSetup.portInt = ui->serverRXAudioInputCombo->itemData(value).toInt(); } + serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value); } - serverConfig.rigs.first()->rxAudioSetup.name = ui->audioInputCombo->itemText(value); } @@ -1908,34 +2108,12 @@ void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) serverConfig.rigs.first()->txAudioSetup.portInt = ui->serverTXAudioOutputCombo->itemData(value).toInt(); } + serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value); } - serverConfig.rigs.first()->txAudioSetup.name = ui->audioInputCombo->itemText(value); } -void wfmain::on_serverUsersTable_cellChanged(int row, int column) -{ - qInfo() << "Cell Changed:" << row << "," << column; - - serverConfig.users.clear(); - for (int rows = 0; rows < ui->serverUsersTable->model()->rowCount(); rows++) - { - if (ui->serverUsersTable->item(rows, 0) != NULL) - { - SERVERUSER user; - user.username = ui->serverUsersTable->item(rows, 0)->text(); - QLineEdit* password = (QLineEdit*)ui->serverUsersTable->cellWidget(rows, 1); - user.password = password->text(); - QComboBox* comboBox = (QComboBox*)ui->serverUsersTable->cellWidget(rows, 2); - user.userType = comboBox->currentIndex(); - serverConfig.users.append(user); - } - else { - ui->serverUsersTable->removeRow(rows); - } - } -} void wfmain::saveSettings() @@ -1947,11 +2125,14 @@ void wfmain::saveSettings() settings->beginGroup("Interface"); settings->setValue("UseFullScreen", prefs.useFullScreen); settings->setValue("UseSystemTheme", prefs.useSystemTheme); - settings->setValue("UseDarkMode", prefs.useDarkMode); settings->setValue("DrawPeaks", prefs.drawPeaks); + settings->setValue("underlayMode", prefs.underlayMode); + settings->setValue("underlayBufferSize", prefs.underlayBufferSize); settings->setValue("WFAntiAlias", prefs.wfAntiAlias); settings->setValue("WFInterpolate", prefs.wfInterpolate); settings->setValue("WFTheme", prefs.wftheme); + settings->setValue("plotFloor", prefs.plotFloor); + settings->setValue("plotCeiling", prefs.plotCeiling); settings->setValue("StylesheetPath", prefs.stylesheetPath); settings->setValue("splitter", ui->splitter->saveState()); settings->setValue("windowGeometry", saveGeometry()); @@ -2025,50 +2206,38 @@ void wfmain::saveSettings() settings->endArray(); settings->endGroup(); - // Note: X and Y get the same colors. See setPlotTheme() function - - settings->beginGroup("DarkColors"); - settings->setValue("Dark_PlotBackground", prefs.colorScheme.Dark_PlotBackground.rgba()); - settings->setValue("Dark_PlotAxisPen", prefs.colorScheme.Dark_PlotAxisPen.rgba()); - settings->setValue("Dark_PlotLegendTextColor", prefs.colorScheme.Dark_PlotLegendTextColor.rgba()); - settings->setValue("Dark_PlotLegendBorderPen", prefs.colorScheme.Dark_PlotLegendBorderPen.rgba()); - settings->setValue("Dark_PlotLegendBrush", prefs.colorScheme.Dark_PlotLegendBrush.rgba()); - settings->setValue("Dark_PlotTickLabel", prefs.colorScheme.Dark_PlotTickLabel.rgba()); - settings->setValue("Dark_PlotBasePen", prefs.colorScheme.Dark_PlotBasePen.rgba()); - settings->setValue("Dark_PlotTickPen", prefs.colorScheme.Dark_PlotTickPen.rgba()); - settings->setValue("Dark_PeakPlotLine", prefs.colorScheme.Dark_PeakPlotLine.rgba()); - settings->setValue("Dark_TuningLine", prefs.colorScheme.Dark_TuningLine.rgba()); - settings->endGroup(); - - settings->beginGroup("LightColors"); - settings->setValue("Light_PlotBackground", prefs.colorScheme.Light_PlotBackground.rgba()); - settings->setValue("Light_PlotAxisPen", prefs.colorScheme.Light_PlotAxisPen.rgba()); - settings->setValue("Light_PlotLegendTextColor", prefs.colorScheme.Light_PlotLegendTextColor.rgba()); - settings->setValue("Light_PlotLegendBorderPen", prefs.colorScheme.Light_PlotLegendBorderPen.rgba()); - settings->setValue("Light_PlotLegendBrush", prefs.colorScheme.Light_PlotLegendBrush.rgba()); - settings->setValue("Light_PlotTickLabel", prefs.colorScheme.Light_PlotTickLabel.rgba()); - settings->setValue("Light_PlotBasePen", prefs.colorScheme.Light_PlotBasePen.rgba()); - settings->setValue("Light_PlotTickPen", prefs.colorScheme.Light_PlotTickPen.rgba()); - settings->setValue("Light_PeakPlotLine", prefs.colorScheme.Light_PeakPlotLine.rgba()); - settings->setValue("Light_TuningLine", prefs.colorScheme.Light_TuningLine.rgba()); - - settings->endGroup(); - - // This is a reference to see how the preference file is encoded. - settings->beginGroup("StandardColors"); - - settings->setValue("white", QColor(Qt::white).rgba()); - settings->setValue("black", QColor(Qt::black).rgba()); - - settings->setValue("red_opaque", QColor(Qt::red).rgba()); - settings->setValue("red_translucent", QColor(255,0,0,128).rgba()); - settings->setValue("green_opaque", QColor(Qt::green).rgba()); - settings->setValue("green_translucent", QColor(0,255,0,128).rgba()); - settings->setValue("blue_opaque", QColor(Qt::blue).rgba()); - settings->setValue("blue_translucent", QColor(0,0,255,128).rgba()); - settings->setValue("cyan", QColor(Qt::cyan).rgba()); - settings->setValue("magenta", QColor(Qt::magenta).rgba()); - settings->setValue("yellow", QColor(Qt::yellow).rgba()); + // Color presets: + settings->beginGroup("ColorPresets"); + settings->setValue("currentColorPresetNumber", prefs.currentColorPresetNumber); + settings->beginWriteArray("ColorPreset", numColorPresetsTotal); + colorPrefsType *p; + for(int pn=0; pn < numColorPresetsTotal; pn++) + { + p = &(colorPreset[pn]); + settings->setArrayIndex(pn); + settings->setValue("presetNum", p->presetNum); + settings->setValue("presetName", *(p->presetName)); + settings->setValue("gridColor", p->gridColor.name(QColor::HexArgb)); + settings->setValue("axisColor", p->axisColor.name(QColor::HexArgb)); + settings->setValue("textColor", p->textColor.name(QColor::HexArgb)); + settings->setValue("spectrumLine", p->spectrumLine.name(QColor::HexArgb)); + settings->setValue("spectrumFill", p->spectrumFill.name(QColor::HexArgb)); + settings->setValue("underlayLine", p->underlayLine.name(QColor::HexArgb)); + settings->setValue("underlayFill", p->underlayFill.name(QColor::HexArgb)); + settings->setValue("plotBackground", p->plotBackground.name(QColor::HexArgb)); + settings->setValue("tuningLine", p->tuningLine.name(QColor::HexArgb)); + settings->setValue("wfBackground", p->wfBackground.name(QColor::HexArgb)); + settings->setValue("wfGrid", p->wfGrid.name(QColor::HexArgb)); + settings->setValue("wfAxis", p->wfAxis.name(QColor::HexArgb)); + settings->setValue("wfText", p->wfText.name(QColor::HexArgb)); + settings->setValue("meterLevel", p->meterLevel.name(QColor::HexArgb)); + settings->setValue("meterAverage", p->meterAverage.name(QColor::HexArgb)); + settings->setValue("meterPeakScale", p->meterPeakScale.name(QColor::HexArgb)); + settings->setValue("meterPeakLevel", p->meterPeakLevel.name(QColor::HexArgb)); + settings->setValue("meterLowerLine", p->meterLowerLine.name(QColor::HexArgb)); + settings->setValue("meterLowText", p->meterLowText.name(QColor::HexArgb)); + } + settings->endArray(); settings->endGroup(); settings->beginGroup("Server"); @@ -2203,8 +2372,6 @@ void wfmain::prepareWf(unsigned int wfLength) QByteArray empty((int)spectWidth, '\x01'); spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); - //wfimage.resize(wfLengthMax); - if((unsigned int)wfimage.size() < wfLengthMax) { unsigned int i=0; @@ -2224,7 +2391,7 @@ void wfmain::prepareWf(unsigned int wfLength) colorMap->data()->setValueRange(QCPRange(0, wfLength-1)); colorMap->data()->setKeyRange(QCPRange(0, spectWidth-1)); - colorMap->setDataRange(QCPRange(0, rigCaps.spectAmpMax)); + colorMap->setDataRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); colorMap->setGradient(static_cast(ui->wfthemeCombo->currentData().toInt())); if(colorMapData == Q_NULLPTR) @@ -2702,14 +2869,6 @@ void wfmain::showStatusBarText(QString text) ui->statusBar->showMessage(text, 5000); } -void wfmain::on_useDarkThemeChk_clicked(bool checked) -{ - //setAppTheme(checked); - setPlotTheme(wf, checked); - setPlotTheme(plot, checked); - prefs.useDarkMode = checked; -} - void wfmain::on_useSystemThemeChk_clicked(bool checked) { setAppTheme(!checked); @@ -2741,79 +2900,111 @@ void wfmain::setAppTheme(bool isCustom) } } -void wfmain::setDefaultColors() +void wfmain::setDefaultColors(int presetNumber) { - defaultColors.Dark_PlotBackground = QColor(0,0,0,255); - defaultColors.Dark_PlotAxisPen = QColor(75,75,75,255); - defaultColors.Dark_PlotLegendTextColor = QColor(255,255,255,255); - defaultColors.Dark_PlotLegendBorderPen = QColor(255,255,255,255); - defaultColors.Dark_PlotLegendBrush = QColor(0,0,0,200); - defaultColors.Dark_PlotTickLabel = QColor(Qt::white); - defaultColors.Dark_PlotBasePen = QColor(Qt::white); - defaultColors.Dark_PlotTickPen = QColor(Qt::white); - defaultColors.Dark_PeakPlotLine = QColor(Qt::yellow); - defaultColors.Dark_TuningLine = QColor(Qt::cyan); + // These are the default color schemes + if(presetNumber > numColorPresetsTotal-1) + return; - defaultColors.Light_PlotBackground = QColor(255,255,255,255); - defaultColors.Light_PlotAxisPen = QColor(200,200,200,255); - defaultColors.Light_PlotLegendTextColor = QColor(0,0,0,255); - defaultColors.Light_PlotLegendBorderPen = QColor(0,0,0,255); - defaultColors.Light_PlotLegendBrush = QColor(255,255,255,200); - defaultColors.Light_PlotTickLabel = QColor(Qt::black); - defaultColors.Light_PlotBasePen = QColor(Qt::black); - defaultColors.Light_PlotTickPen = QColor(Qt::black); - defaultColors.Light_PeakPlotLine = QColor(Qt::blue); - defaultColors.Light_TuningLine = QColor(Qt::blue); -} + colorPrefsType *p = &colorPreset[presetNumber]; -void wfmain::setPlotTheme(QCustomPlot *plot, bool isDark) -{ - if(isDark) + // Begin every parameter with these safe defaults first: + if(p->presetName == Q_NULLPTR) { - plot->setBackground(prefs.colorScheme.Dark_PlotBackground); - //plot->setBackground(QColor(0,0,0,255)); - - plot->xAxis->grid()->setPen(prefs.colorScheme.Dark_PlotAxisPen); - plot->yAxis->grid()->setPen(prefs.colorScheme.Dark_PlotAxisPen); - - plot->legend->setTextColor(prefs.colorScheme.Dark_PlotLegendTextColor); - plot->legend->setBorderPen(prefs.colorScheme.Dark_PlotLegendBorderPen); - plot->legend->setBrush(prefs.colorScheme.Dark_PlotLegendBrush); - - plot->xAxis->setTickLabelColor(prefs.colorScheme.Dark_PlotTickLabel); - plot->xAxis->setLabelColor(prefs.colorScheme.Dark_PlotTickLabel); - plot->yAxis->setTickLabelColor(prefs.colorScheme.Dark_PlotTickLabel); - plot->yAxis->setLabelColor(prefs.colorScheme.Dark_PlotTickLabel); - - plot->xAxis->setBasePen(prefs.colorScheme.Dark_PlotBasePen); - plot->xAxis->setTickPen(prefs.colorScheme.Dark_PlotTickPen); - plot->yAxis->setBasePen(prefs.colorScheme.Dark_PlotBasePen); - plot->yAxis->setTickPen(prefs.colorScheme.Dark_PlotTickPen); - plot->graph(0)->setPen(prefs.colorScheme.Dark_PeakPlotLine); - freqIndicatorLine->setPen(prefs.colorScheme.Dark_TuningLine); - } else { - //color = ui->groupBox->palette().color(QPalette::Button); - - plot->setBackground(prefs.colorScheme.Light_PlotBackground); - plot->xAxis->grid()->setPen(prefs.colorScheme.Light_PlotAxisPen); - plot->yAxis->grid()->setPen(prefs.colorScheme.Light_PlotAxisPen); - - plot->legend->setTextColor(prefs.colorScheme.Light_PlotLegendTextColor); - plot->legend->setBorderPen(prefs.colorScheme.Light_PlotLegendBorderPen); - plot->legend->setBrush(prefs.colorScheme.Light_PlotLegendBrush); - - plot->xAxis->setTickLabelColor(prefs.colorScheme.Light_PlotTickLabel); - plot->xAxis->setLabelColor(prefs.colorScheme.Light_PlotTickLabel); - plot->yAxis->setTickLabelColor(prefs.colorScheme.Light_PlotTickLabel); - plot->yAxis->setLabelColor(prefs.colorScheme.Light_PlotTickLabel); - - plot->xAxis->setBasePen(prefs.colorScheme.Light_PlotBasePen); - plot->xAxis->setTickPen(prefs.colorScheme.Light_PlotTickPen); - plot->yAxis->setBasePen(prefs.colorScheme.Light_PlotBasePen); - plot->yAxis->setTickPen(prefs.colorScheme.Light_PlotTickLabel); - plot->graph(0)->setPen(prefs.colorScheme.Light_PeakPlotLine); - freqIndicatorLine->setPen(prefs.colorScheme.Light_TuningLine); + p->presetName = new QString(); } + p->presetName->clear(); + p->presetName->append(QString("%1").arg(presetNumber)); + p->presetNum = presetNumber; + p->gridColor = QColor(0,0,0,255); + p->axisColor = QColor(Qt::white); + p->textColor = QColor(Qt::white); + p->spectrumLine = QColor(Qt::yellow); + p->spectrumFill = QColor("transparent"); + p->underlayLine = QColor(20+200/4.0*1,70*(1.6-1/4.0), 150, 150).lighter(200); + p->underlayFill = QColor(20+200/4.0*1,70*(1.6-1/4.0), 150, 150); + p->plotBackground = QColor(Qt::black); + p->tuningLine = QColor(Qt::blue); + + p->meterLevel = QColor("#148CD2").darker(); + p->meterAverage = QColor("#3FB7CD"); + p->meterPeakLevel = QColor("#3CA0DB").lighter(); + p->meterPeakScale = QColor(Qt::red); + p->meterLowerLine = QColor("#eff0f1"); + p->meterLowText = QColor("#eff0f1"); + + p->wfBackground = QColor(Qt::black); + p->wfAxis = QColor(Qt::white); + p->wfGrid = QColor(Qt::white); + p->wfText = QColor(Qt::white); + + //qInfo(logSystem()) << "default color preset [" << pn << "] set to pn.presetNum index [" << p->presetNum << "]" << ", with name " << *(p->presetName); + + switch (presetNumber) + { + case 0: + { + // Dark + p->presetName->clear(); + p->presetName->append("Dark"); + p->plotBackground = QColor(0,0,0,255); + p->axisColor = QColor(Qt::white); + p->textColor = QColor(255,255,255,255); + p->gridColor = QColor(0,0,0,255); + p->spectrumFill = QColor("transparent"); + p->spectrumLine = QColor(Qt::yellow); + p->underlayLine = QColor("#9633ff55"); + p->underlayFill = QColor(20+200/4.0*1,70*(1.6-1/4.0), 150, 150); + p->tuningLine = QColor("#ff55ffff"); + + p->meterLevel = QColor("#148CD2").darker(); + p->meterAverage = QColor("#3FB7CD"); + p->meterPeakScale = QColor(Qt::red); + p->meterPeakLevel = QColor("#3CA0DB").lighter(); + p->meterLowerLine = QColor("#eff0f1"); + p->meterLowText = QColor("#eff0f1"); + + p->wfBackground = QColor(Qt::black); + p->wfAxis = QColor(Qt::white); + p->wfGrid = QColor("transparent"); + p->wfText = QColor(Qt::white); + break; + } + case 1: + { + // Bright + p->presetName->clear(); + p->presetName->append("Bright"); + p->plotBackground = QColor(Qt::white); + p->axisColor = QColor(200,200,200,255); + p->gridColor = QColor(255,255,255,0); + p->textColor = QColor(Qt::black); + p->spectrumFill = QColor("transparent"); + p->spectrumLine = QColor(Qt::black); + p->underlayLine = QColor(Qt::blue); + p->tuningLine = QColor(Qt::darkBlue); + + p->meterAverage = QColor("#3FB7CD"); + p->meterPeakLevel = QColor("#3CA0DB"); + p->meterPeakScale = QColor(Qt::darkRed); + p->meterLowerLine = QColor(Qt::black); + p->meterLowText = QColor(Qt::black); + + p->wfBackground = QColor(Qt::white); + p->wfAxis = QColor(200,200,200,255); + p->wfGrid = QColor("transparent"); + p->wfText = QColor(Qt::black); + break; + } + + case 2: + case 3: + case 4: + default: + break; + + } + ui->colorPresetCombo->setItemText(presetNumber, *(p->presetName)); } void wfmain::doCmd(commandtype cmddata) @@ -2948,10 +3139,8 @@ void wfmain::doCmd(commandtype cmddata) doCmd(cmd); break; } - } - void wfmain::doCmd(cmds cmd) { // Use this function to take action upon a command. @@ -3384,7 +3573,12 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) } setWindowTitle(rigCaps.modelName); this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. + //wfCeiling = rigCaps.spectAmpMax; + //plotCeiling = rigCaps.spectAmpMax; + ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); + haveRigCaps = true; + // Added so that server receives rig capabilities. emit sendRigCaps(rigCaps); rpt->setRig(rigCaps); @@ -3503,6 +3697,8 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) { ui->scopeBWCombo->addItem(rigCaps.scopeCenterSpans.at(i).name, (int)rigCaps.scopeCenterSpans.at(i).cstype); } + plot->yAxis->setRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); + colorMap->setDataRange(QCPRange(prefs.plotFloor, prefs.plotCeiling)); } else { ui->scopeBWCombo->setHidden(true); } @@ -3518,8 +3714,10 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) ui->useRTSforPTTchk->setChecked(rigCaps.useRTSforPTT); ui->useRTSforPTTchk->blockSignals(false); - ui->connectBtn->setText("Disconnect"); // We must be connected now. ui->audioSystemCombo->setEnabled(false); + ui->audioSystemServerCombo->setEnabled(false); + + ui->connectBtn->setText("Disconnect from Radio"); // We must be connected now. prepareWf(ui->wfLengthSlider->value()); if(usingLAN) @@ -3675,15 +3873,19 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e return; } + QElapsedTimer performanceTimer; + bool updateRange = false; + if((startFreq != oldLowerFreq) || (endFreq != oldUpperFreq)) { // If the frequency changed and we were drawing peaks, now is the time to clearn them - if(drawPeaks) + if(underlayMode == underlayPeakHold) { // TODO: create non-button function to do this // This will break if the button is ever moved or renamed. on_clearPeakBtn_clicked(); } + // TODO: Add clear-out for the buffer } oldLowerFreq = startFreq; @@ -3705,6 +3907,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e QVector x(spectWidth), y(spectWidth), y2(spectWidth); + // TODO: Keep x around unless the frequency range changes. Should save a little time. for(int i=0; i < spectWidth; i++) { x[i] = (i * (endFreq-startFreq)/spectWidth) + startFreq; @@ -3714,7 +3917,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e { //x[i] = (i * (endFreq-startFreq)/specLen) + startFreq; y[i] = (unsigned char)spectrum.at(i); - if(drawPeaks) + if(underlayMode == underlayPeakHold) { if((unsigned char)spectrum.at(i) > (unsigned char)spectrumPeaks.at(i)) { @@ -3722,46 +3925,139 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } y2[i] = (unsigned char)spectrumPeaks.at(i); } - } + plasmaMutex.lock(); + spectrumPlasma.push_front(spectrum); + spectrumPlasma.pop_back(); + //spectrumPlasma.resize(spectrumPlasmaSize); + plasmaMutex.unlock(); + if(!spectrumDrawLock) { - //ui->qcp->addGraph(); + if((plotFloor != oldPlotFloor) || (plotCeiling != oldPlotCeiling)) + updateRange = true; + +#if QCUSTOMPLOT_VERSION >= 0x020000 + + plot->graph(0)->setData(x,y, true); + if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) + { + freqIndicatorLine->start->setCoords(freq.MHzDouble,0); + freqIndicatorLine->end->setCoords(freq.MHzDouble,rigCaps.spectAmpMax); + } + + if(underlayMode == underlayPeakHold) + { + plot->graph(1)->setData(x,y2, true); // peaks + } else if (underlayMode != underlayNone) { + computePlasma(); + plot->graph(1)->setData(x,spectrumPlasmaLine, true); + } else { + plot->graph(1)->setData(x,y2, true); // peaks, but probably cleared out + } +#else plot->graph(0)->setData(x,y); if((freq.MHzDouble < endFreq) && (freq.MHzDouble > startFreq)) { freqIndicatorLine->start->setCoords(freq.MHzDouble,0); - freqIndicatorLine->end->setCoords(freq.MHzDouble,160); + freqIndicatorLine->end->setCoords(freq.MHzDouble,rigCaps.spectAmpMax); } - if(drawPeaks) + + if(underlayMode == underlayPeakHold) { plot->graph(1)->setData(x,y2); // peaks + } else if (underlayMode != underlayNone) { + computePlasma(); + plot->graph(1)->setData(x,spectrumPlasmaLine); + } else { + plot->graph(1)->setData(x,y2); // peaks, but probably cleared out } - plot->yAxis->setRange(0, rigCaps.spectAmpMax+1); +#endif + + if(updateRange) + plot->yAxis->setRange(prefs.plotFloor, prefs.plotCeiling); + plot->xAxis->setRange(startFreq, endFreq); plot->replot(); if(specLen == spectWidth) { wfimage.prepend(spectrum); - wfimage.resize(wfLengthMax); - wfimage.squeeze(); - + wfimage.pop_back(); + QByteArray wfRow; // Waterfall: for(int row = 0; row < wfLength; row++) { + wfRow = wfimage.at(row); for(int col = 0; col < spectWidth; col++) { - colorMap->data()->setCell( col, row, wfimage.at(row).at(col)); + colorMap->data()->setCell( col, row, wfRow.at(col)); } } + if(updateRange) + { + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); + } + wf->yAxis->setRange(0,wfLength - 1); + wf->xAxis->setRange(0, spectWidth-1); + wf->replot(); + } + oldPlotFloor = plotFloor; + oldPlotCeiling = plotCeiling; + } +} - wf->yAxis->setRange(0,wfLength - 1); - wf->xAxis->setRange(0, spectWidth-1); - wf->replot(); +void wfmain::preparePlasma() +{ + if(plasmaPrepared) + return; + QByteArray empty((int)spectWidth, '\x01'); + + if(spectrumPlasmaSize == 0) + spectrumPlasmaSize = 128; + + plasmaMutex.lock(); + spectrumPlasma.clear(); + + for(unsigned int p=0; p < spectrumPlasmaSize; p++) + { + spectrumPlasma.append(empty); + } + + spectrumPlasma.squeeze(); + plasmaMutex.unlock(); + plasmaPrepared = true; +} + +void wfmain::computePlasma() +{ + plasmaMutex.lock(); + spectrumPlasmaLine.clear(); + spectrumPlasmaLine.resize(spectWidth); + int specPlasmaSize = spectrumPlasma.size(); + if(underlayMode == underlayAverageBuffer) + { + for(int col=0; col < spectWidth; col++) + { + for(int pos=0; pos < specPlasmaSize; pos++) + { + spectrumPlasmaLine[col] += (unsigned char)spectrumPlasma[pos][col]; + } + spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / specPlasmaSize; + } + } else if (underlayMode == underlayPeakBuffer){ + // peak mode, running peak display + for(int col=0; col < spectWidth; col++) + { + for(int pos=0; pos < specPlasmaSize; pos++) + { + if((double)((unsigned char)spectrumPlasma[pos][col]) > spectrumPlasmaLine[col]) + spectrumPlasmaLine[col] = (unsigned char)spectrumPlasma[pos][col]; + } } } + plasmaMutex.unlock(); } void wfmain::receiveSpectrumMode(spectrumMode spectMode) @@ -3960,26 +4256,6 @@ void wfmain::on_clearPeakBtn_clicked() return; } -void wfmain::on_drawPeakChk_clicked(bool checked) -{ - if(checked) - { - on_clearPeakBtn_clicked(); // clear - drawPeaks = true; - - } else { - drawPeaks = false; - -#if QCUSTOMPLOT_VERSION >= 0x020000 - plot->graph(1)->data()->clear(); -#else - plot->graph(1)->clearData(); -#endif - - } - prefs.drawPeaks = checked; -} - void wfmain::on_fullScreenChk_clicked(bool checked) { if(checked) @@ -4213,7 +4489,7 @@ void wfmain::on_modeSelectCombo_activated(int index) void wfmain::on_freqDial_valueChanged(int value) { - int maxVal = ui->freqDial->maximum(); + int fullSweep = ui->freqDial->maximum() - ui->freqDial->minimum(); freqt f; f.Hz = 0; @@ -4221,12 +4497,6 @@ void wfmain::on_freqDial_valueChanged(int value) volatile int delta = 0; - int directPath = 0; - int crossingPath = 0; - - int distToMaxNew = 0; - int distToMaxOld = 0; - if(freqLock) { ui->freqDial->blockSignals(true); @@ -4234,50 +4504,25 @@ void wfmain::on_freqDial_valueChanged(int value) ui->freqDial->blockSignals(false); return; } - - if(value == 0) + + delta = (value - oldFreqDialVal); + + if(delta > fullSweep/2) { - distToMaxNew = 0; - } else { - distToMaxNew = maxVal - value; + // counter-clockwise past the zero mark + // ie, from +3000 to 3990, old=3000, new = 3990, new-old = 990 + // desired delta here would actually be -10 + delta = delta - fullSweep; + } else if (delta < -fullSweep/2) + { + // clock-wise past the zero mark + // ie, from +3990 to 3000, old=3990, new = 3000, new-old = -990 + // desired delta here would actually be +10 + delta = fullSweep + delta; } - if(oldFreqDialVal != 0) - { - distToMaxOld = maxVal - oldFreqDialVal; - } else { - distToMaxOld = 0; - } - - directPath = abs(value - oldFreqDialVal); - if(value < maxVal / 2) - { - crossingPath = value + distToMaxOld; - } else { - crossingPath = distToMaxNew + oldFreqDialVal; - } - - if(directPath > crossingPath) - { - // use crossing path, it is shorter - delta = crossingPath; - // now calculate the direction: - if( value > oldFreqDialVal) - { - // CW - delta = delta; - } else { - // CCW - delta *= -1; - } - - } else { - // use direct path - // crossing path is larger than direct path, use direct path - //delta = directPath; - // now calculate the direction - delta = value - oldFreqDialVal; - } + // The step size is 10, which forces the knob to not skip a step crossing zero. + delta = delta / ui->freqDial->singleStep(); // With the number of steps and direction of steps established, // we can now adjust the frequency: @@ -4287,12 +4532,8 @@ void wfmain::on_freqDial_valueChanged(int value) if (f.Hz > 0) { freq = f; - oldFreqDialVal = value; - ui->freqLabel->setText(QString("%1").arg(f.MHzDouble, 0, 'f')); - - //emit setFrequency(0,f); issueCmdUniquePriority(cmdSetFreq, f); } else { ui->freqDial->blockSignals(true); @@ -4734,6 +4975,7 @@ void wfmain::on_serialEnableBtn_clicked(bool checked) ui->serialDeviceListCombo->setEnabled(checked); ui->serverRXAudioInputCombo->setEnabled(checked); ui->serverTXAudioOutputCombo->setEnabled(checked); + } void wfmain::on_lanEnableBtn_clicked(bool checked) @@ -4865,16 +5107,25 @@ void wfmain::on_connectBtn_clicked() if (haveRigCaps) { emit sendCloseComm(); - ui->connectBtn->setText("Connect"); + ui->connectBtn->setText("Connect to Radio"); ui->audioSystemCombo->setEnabled(true); + ui->audioSystemServerCombo->setEnabled(true); haveRigCaps = false; rigName->setText("NONE"); } - else + else { emit sendCloseComm(); // Just in case there is a failed connection open. - openRig(); + if (ui->connectBtn->text() != "Cancel connection") { + openRig(); + } + else { + ui->connectBtn->setText("Connect to Radio"); + ui->audioSystemCombo->setEnabled(true); + ui->audioSystemServerCombo->setEnabled(true); + } } + ui->connectBtn->clearFocus(); } void wfmain::on_sqlSlider_valueChanged(int value) @@ -5185,6 +5436,12 @@ void wfmain::receiveMeter(meterKind inMeter, unsigned char level) if(ui->meter2Widget->getMeterType() == inMeter) { ui->meter2Widget->setLevel(level); + } else if ( (ui->meter2Widget->getMeterType() == meterAudio) && + (inMeter == meterTxMod) && amTransmitting) { + ui->meter2Widget->setLevel(level); + } else if ( (ui->meter2Widget->getMeterType() == meterAudio) && + (inMeter == meterRxAudio) && !amTransmitting) { + ui->meter2Widget->setLevel(level); } break; } @@ -5854,7 +6111,8 @@ void wfmain::on_meter2selectionCombo_activated(int index) } else { ui->meter2Widget->show(); ui->meter2Widget->setMeterType(newMeterType); - insertPeriodicCommandUnique(newCmd); + if((newMeterType!=meterRxAudio) && (newMeterType!=meterTxMod) && (newMeterType!=meterAudio)) + insertPeriodicCommandUnique(newCmd); } prefs.meter2Type = newMeterType; @@ -5952,31 +6210,6 @@ void wfmain::on_radioStatusBtn_clicked() } } -// --- DEBUG FUNCTION --- -void wfmain::on_debugBtn_clicked() -{ - qInfo(logSystem()) << "Debug button pressed."; - shut->show(); - //trxadj->show(); - //setRadioTimeDatePrep(); - //wf->setInteraction(QCP::iRangeZoom, true); - //wf->setInteraction(QCP::iRangeDrag, true); - //bool ok; - //int height = QInputDialog::getInt(this, "wfview Radio Polling Setup", "Poll Timing Interval (ms)", 350, 1, 500, 1, &ok ); - - //this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - //this->setMaximumSize(QSize(1025,height)); - //this->setMinimumSize(QSize(1025,height)); - //this->setMaximumSize(QSize(929, 270)); - //this->setMinimumSize(QSize(929, 270)); - - //resize(minimumSize()); - //adjustSize(); // main window - //adjustSize(); - -} - - void wfmain::setAudioDevicesUI() { @@ -6145,6 +6378,14 @@ void wfmain::setAudioDevicesUI() } + // Make the audio comboboxes expand when clicked (only needed for Windows) +#ifdef Q_OS_WIN + ui->audioInputCombo->setStyleSheet("QComboBox QAbstractItemView {min-width: 300px;}"); + ui->audioOutputCombo->setStyleSheet("QComboBox QAbstractItemView {min-width: 300px;}"); + ui->serverTXAudioOutputCombo->setStyleSheet("QComboBox QAbstractItemView {min-width: 300px;}"); + ui->serverRXAudioInputCombo->setStyleSheet("QComboBox QAbstractItemView {min-width: 300px;}"); +#endif + // Stop blocking signals so we can set the current values ui->audioInputCombo->blockSignals(false); ui->audioOutputCombo->blockSignals(false); @@ -6207,34 +6448,870 @@ void wfmain::on_audioSystemCombo_currentIndexChanged(int value) { prefs.audioSystem = static_cast(value); setAudioDevicesUI(); // Force all audio devices to update + ui->audioSystemServerCombo->blockSignals(true); + ui->audioSystemServerCombo->setCurrentIndex(value); + ui->audioSystemServerCombo->blockSignals(false); } -void wfmain::on_usbControllerBtn_clicked() +void wfmain::on_audioSystemServerCombo_currentIndexChanged(int value) { - if (shut != Q_NULLPTR) { - if (shut->isVisible()) { - shut->hide(); + prefs.audioSystem = static_cast(value); + setAudioDevicesUI(); // Force all audio devices to update + ui->audioSystemCombo->blockSignals(true); + ui->audioSystemCombo->setCurrentIndex(value); + ui->audioSystemCombo->blockSignals(false); +} + +void wfmain::on_topLevelSlider_valueChanged(int value) +{ + wfCeiling = value; + plotCeiling = value; + prefs.plotCeiling = value; + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); +} + +void wfmain::on_botLevelSlider_valueChanged(int value) +{ + wfFloor = value; + plotFloor = value; + prefs.plotFloor = value; + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); +} + +void wfmain::on_underlayBufferSlider_valueChanged(int value) +{ + resizePlasmaBuffer(value); + prefs.underlayBufferSize = value; +} + +void wfmain::resizePlasmaBuffer(int newSize) +{ + + QByteArray empty((int)spectWidth, '\x01'); + plasmaMutex.lock(); + + int oldSize = spectrumPlasma.size(); + + if(oldSize < newSize) + { + spectrumPlasma.resize(newSize); + for(int p=oldSize; p < newSize; p++) + { + spectrumPlasma.append(empty); } - else { - shut->show(); + } else if (oldSize > newSize){ + for(int p = oldSize; p > newSize; p--) + { + spectrumPlasma.pop_back(); + } + } + + spectrumPlasma.squeeze(); + plasmaMutex.unlock(); +} + +void wfmain::on_underlayNone_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(checked); + if(checked) + { + underlayMode = underlayNone; + prefs.underlayMode = underlayMode; + on_clearPeakBtn_clicked(); + } +} + +void wfmain::on_underlayPeakHold_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(checked); + if(checked) + { + underlayMode = underlayPeakHold; + prefs.underlayMode = underlayMode; + on_clearPeakBtn_clicked(); + } +} + +void wfmain::on_underlayPeakBuffer_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(!checked); + if(checked) + { + underlayMode = underlayPeakBuffer; + prefs.underlayMode = underlayMode; + } +} + +void wfmain::on_underlayAverageBuffer_toggled(bool checked) +{ + ui->underlayBufferSlider->setDisabled(!checked); + if(checked) + { + underlayMode = underlayAverageBuffer; + prefs.underlayMode = underlayMode; + } +} + +// --- DEBUG FUNCTION --- +void wfmain::on_debugBtn_clicked() +{ + qInfo(logSystem()) << "Debug button pressed."; + // issueDelayedCommand(cmdGetRigID); + //emit getRigCIV(); + //trxadj->show(); + //setRadioTimeDatePrep(); + //wf->setInteraction(QCP::iRangeZoom, true); + //wf->setInteraction(QCP::iRangeDrag, true); + plot->yAxis->setRange(QCPRange(plotFloor, plotCeiling)); + colorMap->setDataRange(QCPRange(wfFloor, wfCeiling)); + +// bool ok; +// int height = QInputDialog::getInt(this, "wfview window fixed height", "number: ", 350, 1, 500, 1, &ok ); + +// this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); +// this->setMaximumSize(QSize(1025,height)); +// this->setMinimumSize(QSize(1025,height)); +// //this->setMaximumSize(QSize(929, 270)); +// //this->setMinimumSize(QSize(929, 270)); + +// resize(minimumSize()); +// adjustSize(); // main window +// adjustSize(); + +} + +// ---------- color helper functions: ---------- // + +void wfmain::setColorElement(QColor color, + QLedLabel *led, + QLabel *label, + QLineEdit *lineText) +{ + if(led != Q_NULLPTR) + { + led->setColor(color, true); + } + if(label != Q_NULLPTR) + { + label->setText(color.name(QColor::HexArgb)); + } + if(lineText != Q_NULLPTR) + { + lineText->setText(color.name(QColor::HexArgb)); + } +} + +void wfmain::setColorElement(QColor color, QLedLabel *led, QLabel *label) +{ + setColorElement(color, led, label, Q_NULLPTR); +} + +void wfmain::setColorElement(QColor color, QLedLabel *led, QLineEdit *lineText) +{ + setColorElement(color, led, Q_NULLPTR, lineText); +} + +QColor wfmain::getColorFromPicker(QColor initialColor) +{ + QColorDialog::ColorDialogOptions options; + options.setFlag(QColorDialog::ShowAlphaChannel, true); + options.setFlag(QColorDialog::DontUseNativeDialog, true); + QColor selColor = QColorDialog::getColor(initialColor, this, "Select Color", options); + int alphaVal = 0; + bool ok = false; + + if(selColor.isValid()) + { + if(selColor.alpha() == 0) + { + alphaVal = QInputDialog::getInt(this, tr("Specify Opacity"), + tr("You specified an opacity value of 0. \nDo you want to change it? (0=transparent, 255=opaque)"), 0, 0, 255, 1, + &ok); + if(!ok) + { + return selColor; + } else { + selColor.setAlpha(alphaVal); + return selColor; + } + } + return selColor; + } + else + return initialColor; +} + +void wfmain::getSetColor(QLedLabel *led, QLabel *label) +{ + QColor selColor = getColorFromPicker(led->getColor()); + setColorElement(selColor, led, label); +} + +void wfmain::getSetColor(QLedLabel *led, QLineEdit *line) +{ + QColor selColor = getColorFromPicker(led->getColor()); + setColorElement(selColor, led, line); +} + +QString wfmain::setColorFromString(QString colorstr, QLedLabel *led) +{ + if(led==Q_NULLPTR) + return "ERROR"; + + if(!colorstr.startsWith("#")) + { + colorstr.prepend("#"); + } + if(colorstr.length() != 9) + { + // TODO: Tell user about AA RR GG BB + return led->getColor().name(QColor::HexArgb); + } + led->setColor(colorstr, true); + return led->getColor().name(QColor::HexArgb); +} + +void wfmain::useCurrentColorPreset() +{ + int pos = ui->colorPresetCombo->currentIndex(); + useColorPreset(&colorPreset[pos]); +} + +void wfmain::useColorPreset(colorPrefsType *cp) +{ + // Apply the given preset to the UI elements + // prototyped from setPlotTheme() + if(cp == Q_NULLPTR) + return; + + //qInfo(logSystem()) << "Setting plots to color preset number " << cp->presetNum << ", with name " << *(cp->presetName); + + plot->setBackground(cp->plotBackground); + + plot->xAxis->grid()->setPen(cp->gridColor); + plot->yAxis->grid()->setPen(cp->gridColor); + + plot->legend->setTextColor(cp->textColor); + plot->legend->setBorderPen(cp->gridColor); + plot->legend->setBrush(cp->gridColor); + + plot->xAxis->setTickLabelColor(cp->textColor); + plot->xAxis->setLabelColor(cp->gridColor); + plot->yAxis->setTickLabelColor(cp->textColor); + plot->yAxis->setLabelColor(cp->gridColor); + + plot->xAxis->setBasePen(cp->axisColor); + plot->xAxis->setTickPen(cp->axisColor); + plot->yAxis->setBasePen(cp->axisColor); + plot->yAxis->setTickPen(cp->axisColor); + + freqIndicatorLine->setPen(QPen(cp->tuningLine)); + + plot->graph(0)->setPen(QPen(cp->spectrumLine)); + plot->graph(0)->setBrush(QBrush(cp->spectrumFill)); + + plot->graph(1)->setPen(QPen(cp->underlayLine)); + plot->graph(1)->setBrush(QBrush(cp->underlayFill)); + + wf->yAxis->setBasePen(cp->wfAxis); + wf->yAxis->setTickPen(cp->wfAxis); + wf->xAxis->setBasePen(cp->wfAxis); + wf->xAxis->setTickPen(cp->wfAxis); + + wf->xAxis->setLabelColor(cp->wfGrid); + wf->yAxis->setLabelColor(cp->wfGrid); + + wf->xAxis->setTickLabelColor(cp->wfText); + wf->yAxis->setTickLabelColor(cp->wfText); + + wf->setBackground(cp->wfBackground); + + ui->meterSPoWidget->setColors(cp->meterLevel, cp->meterPeakScale, cp->meterPeakLevel, cp->meterAverage, cp->meterLowerLine, cp->meterLowText); + ui->meter2Widget->setColors(cp->meterLevel, cp->meterPeakScale, cp->meterPeakLevel, cp->meterAverage, cp->meterLowerLine, cp->meterLowText); +} + +void wfmain::setColorButtonOperations(QColor *colorStore, + QLineEdit *e, QLedLabel *d) +{ + // Call this function with a pointer into the colorPreset color you + // wish to edit. + + if(colorStore==Q_NULLPTR) + { + qInfo(logSystem()) << "ERROR, invalid pointer to color received."; + return; + } + getSetColor(d, e); + QColor t = d->getColor(); + colorStore->setNamedColor(t.name(QColor::HexArgb)); + useCurrentColorPreset(); +} + +void wfmain::setColorLineEditOperations(QColor *colorStore, + QLineEdit *e, QLedLabel *d) +{ + // Call this function with a pointer into the colorPreset color you + // wish to edit. + if(colorStore==Q_NULLPTR) + { + qInfo(logSystem()) << "ERROR, invalid pointer to color received."; + return; + } + + QString colorStrValidated = setColorFromString(e->text(), d); + e->setText(colorStrValidated); + colorStore->setNamedColor(colorStrValidated); + useCurrentColorPreset(); +} + +void wfmain::on_colorPopOutBtn_clicked() +{ + + if(settingsTabisAttached) + { + settingsTab = ui->tabWidget->currentWidget(); + ui->tabWidget->removeTab(ui->tabWidget->indexOf(settingsTab)); + settingsWidgetTab->addTab(settingsTab, "Settings"); + settingsWidgetWindow->show(); + ui->colorPopOutBtn->setText("Re-attach"); + ui->tabWidget->setCurrentIndex(0); + settingsTabisAttached = false; + } else { + settingsTab = settingsWidgetTab->currentWidget(); + + settingsWidgetTab->removeTab(settingsWidgetTab->indexOf(settingsTab)); + ui->tabWidget->addTab(settingsTab, "Settings"); + settingsWidgetWindow->close(); + + ui->colorPopOutBtn->setText("Pop-Out"); + ui->tabWidget->setCurrentIndex(3); + settingsTabisAttached = true; + } +} + +void wfmain::setDefaultColorPresets() +{ + // Default wfview colors in each preset + // gets overridden after preferences are loaded + for(int pn=0; pn < numColorPresetsTotal; pn++) + { + setDefaultColors(pn); + } +} + +void wfmain::setEditAndLedFromColor(QColor c, QLineEdit *e, QLedLabel *d) +{ + bool blockSignals = true; + if(e != Q_NULLPTR) + { + e->blockSignals(blockSignals); + e->setText(c.name(QColor::HexArgb)); + e->blockSignals(false); + } + if(d != Q_NULLPTR) + { + d->setColor(c); + } +} + +void wfmain::loadColorPresetToUIandPlots(int presetNumber) +{ + if(presetNumber >= numColorPresetsTotal) + { + qDebug(logSystem()) << "WARNING: asked for preset number [" << presetNumber << "], which is out of range."; + return; + } + + colorPrefsType p = colorPreset[presetNumber]; + //qInfo(logSystem()) << "color preset number [" << presetNumber << "] requested for UI load, which has internal index of [" << p.presetNum << "]"; + setEditAndLedFromColor(p.gridColor, ui->colorEditGrid, ui->colorSwatchGrid); + setEditAndLedFromColor(p.axisColor, ui->colorEditAxis, ui->colorSwatchAxis); + setEditAndLedFromColor(p.textColor, ui->colorEditText, ui->colorSwatchText); + setEditAndLedFromColor(p.spectrumLine, ui->colorEditSpecLine, ui->colorSwatchSpecLine); + setEditAndLedFromColor(p.spectrumFill, ui->colorEditSpecFill, ui->colorSwatchSpecFill); + setEditAndLedFromColor(p.underlayLine, ui->colorEditUnderlayLine, ui->colorSwatchUnderlayLine); + setEditAndLedFromColor(p.underlayFill, ui->colorEditUnderlayFill, ui->colorSwatchUnderlayFill); + setEditAndLedFromColor(p.plotBackground, ui->colorEditPlotBackground, ui->colorSwatchPlotBackground); + setEditAndLedFromColor(p.tuningLine, ui->colorEditTuningLine, ui->colorSwatchTuningLine); + + setEditAndLedFromColor(p.meterLevel, ui->colorEditMeterLevel, ui->colorSwatchMeterLevel); + setEditAndLedFromColor(p.meterAverage, ui->colorEditMeterAvg, ui->colorSwatchMeterAverage); + setEditAndLedFromColor(p.meterPeakLevel, ui->colorEditMeterPeakLevel, ui->colorSwatchMeterPeakLevel); + setEditAndLedFromColor(p.meterPeakScale, ui->colorEditMeterPeakScale, ui->colorSwatchMeterPeakScale); + setEditAndLedFromColor(p.meterLowerLine, ui->colorEditMeterScale, ui->colorSwatchMeterScale); + setEditAndLedFromColor(p.meterLowText, ui->colorEditMeterText, ui->colorSwatchMeterText); + + setEditAndLedFromColor(p.wfBackground, ui->colorEditWfBackground, ui->colorSwatchWfBackground); + setEditAndLedFromColor(p.wfGrid, ui->colorEditWfGrid, ui->colorSwatchWfGrid); + setEditAndLedFromColor(p.wfAxis, ui->colorEditWfAxis, ui->colorSwatchWfAxis); + setEditAndLedFromColor(p.wfText, ui->colorEditWfText, ui->colorSwatchWfText); + + useColorPreset(&p); +} + +void wfmain::on_colorRenamePresetBtn_clicked() +{ + int p = ui->colorPresetCombo->currentIndex(); + QString newName; + QMessageBox msgBox; + + bool ok = false; + newName = QInputDialog::getText(this, tr("Rename Preset"), + tr("Preset Name (10 characters max):"), QLineEdit::Normal, + ui->colorPresetCombo->currentText(), &ok); + if(!ok) + return; + + if(ok && (newName.length() < 11) && !newName.isEmpty()) + { + colorPreset[p].presetName->clear(); + colorPreset[p].presetName->append(newName); + ui->colorPresetCombo->setItemText(p, *(colorPreset[p].presetName)); + } else { + if(newName.isEmpty() || (newName.length() > 10)) + { + msgBox.setText("Error, name must be at least one character and not exceed 10 characters."); + msgBox.exec(); } } } - -void wfmain::getButtonCommand(QString device, BUTTON* but) +void wfmain::on_colorPresetCombo_currentIndexChanged(int index) { - if (device != usbDeviceName) { - // New or unknown device? - usbDeviceName = device; - qDebug(logUsbControl()) << "New controller:" << device; - usbButtons.clear(); - } - qDebug(logUsbControl()) << "Adding" << "Commands for" << device << "button" << but->num << "onCommand" << but->onCommand->text << "offCommand" << but->offCommand->text; - usbButtons.insert(but->num, but); + prefs.currentColorPresetNumber = index; + loadColorPresetToUIandPlots(index); } -void wfmain::updateUsbButtons() +void wfmain::on_colorRevertPresetBtn_clicked() { + int pn = ui->colorPresetCombo->currentIndex(); + setDefaultColors(pn); + loadColorPresetToUIandPlots(pn); +} -} \ No newline at end of file +// ---------- end color helper functions ---------- // + +// ---------- Color UI slots ----------// + +// Grid: +void wfmain::on_colorSetBtnGrid_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].gridColor); + setColorButtonOperations(c, ui->colorEditGrid, ui->colorSwatchGrid); +} +void wfmain::on_colorEditGrid_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].gridColor); + setColorLineEditOperations(c, ui->colorEditGrid, ui->colorSwatchGrid); +} + +// Axis: +void wfmain::on_colorSetBtnAxis_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].axisColor); + setColorButtonOperations(c, ui->colorEditAxis, ui->colorSwatchAxis); +} +void wfmain::on_colorEditAxis_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].axisColor); + setColorLineEditOperations(c, ui->colorEditAxis, ui->colorSwatchAxis); +} + +// Text: +void wfmain::on_colorSetBtnText_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].textColor); + setColorButtonOperations(c, ui->colorEditText, ui->colorSwatchText); +} +void wfmain::on_colorEditText_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].textColor); + setColorLineEditOperations(c, ui->colorEditText, ui->colorSwatchText); +} + +// SpecLine: +void wfmain::on_colorEditSpecLine_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].spectrumLine); + setColorLineEditOperations(c, ui->colorEditSpecLine, ui->colorSwatchSpecLine); +} +void wfmain::on_colorSetBtnSpecLine_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].spectrumLine); + setColorButtonOperations(c, ui->colorEditSpecLine, ui->colorSwatchSpecLine); +} + +// SpecFill: +void wfmain::on_colorSetBtnSpecFill_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].spectrumFill); + setColorButtonOperations(c, ui->colorEditSpecFill, ui->colorSwatchSpecFill); +} +void wfmain::on_colorEditSpecFill_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].spectrumFill); + setColorLineEditOperations(c, ui->colorEditSpecFill, ui->colorSwatchSpecFill); +} + +// PlotBackground: +void wfmain::on_colorEditPlotBackground_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].plotBackground); + setColorLineEditOperations(c, ui->colorEditPlotBackground, ui->colorSwatchPlotBackground); +} +void wfmain::on_colorSetBtnPlotBackground_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].plotBackground); + setColorButtonOperations(c, ui->colorEditPlotBackground, ui->colorSwatchPlotBackground); +} + +// Underlay Line: +void wfmain::on_colorSetBtnUnderlayLine_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].underlayLine); + setColorButtonOperations(c, ui->colorEditUnderlayLine, ui->colorSwatchUnderlayLine); +} + +void wfmain::on_colorEditUnderlayLine_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].underlayLine); + setColorLineEditOperations(c, ui->colorEditUnderlayLine, ui->colorSwatchUnderlayLine); +} + +// Underlay Fill: +void wfmain::on_colorSetBtnUnderlayFill_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].underlayFill); + setColorButtonOperations(c, ui->colorEditUnderlayFill, ui->colorSwatchUnderlayFill); +} +void wfmain::on_colorEditUnderlayFill_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].underlayFill); + setColorLineEditOperations(c, ui->colorEditUnderlayFill, ui->colorSwatchUnderlayFill); +} + +// WF Background: +void wfmain::on_colorSetBtnwfBackground_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfBackground); + setColorButtonOperations(c, ui->colorEditWfBackground, ui->colorSwatchWfBackground); +} +void wfmain::on_colorEditWfBackground_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfBackground); + setColorLineEditOperations(c, ui->colorEditWfBackground, ui->colorSwatchWfBackground); +} + +// WF Grid: +void wfmain::on_colorSetBtnWfGrid_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfGrid); + setColorButtonOperations(c, ui->colorEditWfGrid, ui->colorSwatchWfGrid); +} +void wfmain::on_colorEditWfGrid_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfGrid); + setColorLineEditOperations(c, ui->colorEditWfGrid, ui->colorSwatchWfGrid); +} + +// WF Axis: +void wfmain::on_colorSetBtnWfAxis_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfAxis); + setColorButtonOperations(c, ui->colorEditWfAxis, ui->colorSwatchWfAxis); +} +void wfmain::on_colorEditWfAxis_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfAxis); + setColorLineEditOperations(c, ui->colorEditWfAxis, ui->colorSwatchWfAxis); +} + +// WF Text: +void wfmain::on_colorSetBtnWfText_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfText); + setColorButtonOperations(c, ui->colorEditWfText, ui->colorSwatchWfText); +} +void wfmain::on_colorEditWfText_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].wfText); + setColorLineEditOperations(c, ui->colorEditWfText, ui->colorSwatchWfText); +} + +// Tuning Line: +void wfmain::on_colorSetBtnTuningLine_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].tuningLine); + setColorButtonOperations(c, ui->colorEditTuningLine, ui->colorSwatchTuningLine); +} +void wfmain::on_colorEditTuningLine_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].tuningLine); + setColorLineEditOperations(c, ui->colorEditTuningLine, ui->colorSwatchTuningLine); +} + +// Meter Level: +void wfmain::on_colorSetBtnMeterLevel_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLevel); + setColorButtonOperations(c, ui->colorEditMeterLevel, ui->colorSwatchMeterLevel); +} +void wfmain::on_colorEditMeterLevel_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLevel); + setColorLineEditOperations(c, ui->colorEditMeterLevel, ui->colorSwatchMeterLevel); +} + +// Meter Average: +void wfmain::on_colorSetBtnMeterAvg_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterAverage); + setColorButtonOperations(c, ui->colorEditMeterAvg, ui->colorSwatchMeterAverage); +} +void wfmain::on_colorEditMeterAvg_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterAverage); + setColorLineEditOperations(c, ui->colorEditMeterAvg, ui->colorSwatchMeterAverage); +} + +// Meter Peak Level: +void wfmain::on_colorSetBtnMeterPeakLevel_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterPeakLevel); + setColorButtonOperations(c, ui->colorEditMeterPeakLevel, ui->colorSwatchMeterPeakLevel); +} +void wfmain::on_colorEditMeterPeakLevel_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterPeakLevel); + setColorLineEditOperations(c, ui->colorEditMeterPeakLevel, ui->colorSwatchMeterPeakLevel); +} + +// Meter Peak Scale: +void wfmain::on_colorSetBtnMeterPeakScale_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterPeakScale); + setColorButtonOperations(c, ui->colorEditMeterPeakScale, ui->colorSwatchMeterPeakScale); +} +void wfmain::on_colorEditMeterPeakScale_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterPeakScale); + setColorLineEditOperations(c, ui->colorEditMeterPeakScale, ui->colorSwatchMeterPeakScale); +} + +// Meter Scale (line): +void wfmain::on_colorSetBtnMeterScale_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLowerLine); + setColorButtonOperations(c, ui->colorEditMeterScale, ui->colorSwatchMeterScale); +} +void wfmain::on_colorEditMeterScale_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLowerLine); + setColorLineEditOperations(c, ui->colorEditMeterScale, ui->colorSwatchMeterScale); +} + +// Meter Text: +void wfmain::on_colorSetBtnMeterText_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLowText); + setColorButtonOperations(c, ui->colorEditMeterText, ui->colorSwatchMeterText); +} +void wfmain::on_colorEditMeterText_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor *c = &(colorPreset[pos].meterLowText); + setColorLineEditOperations(c, ui->colorEditMeterText, ui->colorSwatchMeterText); +} + +// ---------- End color UI slots ----------// + +void wfmain::on_colorSavePresetBtn_clicked() +{ + int pn = ui->colorPresetCombo->currentIndex(); + + settings->beginGroup("ColorPresets"); + settings->setValue("currentColorPresetNumber", prefs.currentColorPresetNumber); + settings->beginWriteArray("ColorPreset", numColorPresetsTotal); + + colorPrefsType *p; + p = &(colorPreset[pn]); + + settings->setArrayIndex(pn); + settings->setValue("presetNum", p->presetNum); + settings->setValue("presetName", *(p->presetName)); + settings->setValue("gridColor", p->gridColor.name(QColor::HexArgb)); + settings->setValue("axisColor", p->axisColor.name(QColor::HexArgb)); + settings->setValue("textColor", p->textColor.name(QColor::HexArgb)); + settings->setValue("spectrumLine", p->spectrumLine.name(QColor::HexArgb)); + settings->setValue("spectrumFill", p->spectrumFill.name(QColor::HexArgb)); + settings->setValue("underlayLine", p->underlayLine.name(QColor::HexArgb)); + settings->setValue("underlayFill", p->underlayFill.name(QColor::HexArgb)); + settings->setValue("plotBackground", p->plotBackground.name(QColor::HexArgb)); + settings->setValue("tuningLine", p->tuningLine.name(QColor::HexArgb)); + settings->setValue("wfBackground", p->wfBackground.name(QColor::HexArgb)); + settings->setValue("wfGrid", p->wfGrid.name(QColor::HexArgb)); + settings->setValue("wfAxis", p->wfAxis.name(QColor::HexArgb)); + settings->setValue("wfText", p->wfText.name(QColor::HexArgb)); + settings->setValue("meterLevel", p->meterLevel.name(QColor::HexArgb)); + settings->setValue("meterAverage", p->meterAverage.name(QColor::HexArgb)); + settings->setValue("meterPeakScale", p->meterPeakScale.name(QColor::HexArgb)); + settings->setValue("meterPeakLevel", p->meterPeakLevel.name(QColor::HexArgb)); + settings->setValue("meterLowerLine", p->meterLowerLine.name(QColor::HexArgb)); + settings->setValue("meterLowText", p->meterLowText.name(QColor::HexArgb)); + + settings->endArray(); + settings->endGroup(); + settings->sync(); +} + +void wfmain::on_showLogBtn_clicked() +{ + if(logWindow->isMinimized()) + { + logWindow->raise(); + logWindow->activateWindow(); + return; + } + logWindow->show(); + logWindow->raise(); + logWindow->activateWindow(); +} + +void wfmain::initLogging() +{ +#ifdef Q_OS_MAC + logFilename= QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log"; +#else + logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log"; +#endif + // 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); + + connect(logWindow, SIGNAL(setDebugMode(bool)), this, SLOT(setDebugLogging(bool))); + + // Interval timer for log window updates: + logCheckingTimer.setInterval(100); + connect(&logCheckingTimer, SIGNAL(timeout()), this, SLOT(logCheck())); + logCheckingTimer.start(); +} + +void wfmain::logCheck() +{ + // This is called by a timer to check for new log messages and copy + // the messages into the logWindow. + QMutexLocker locker(&logTextMutex); + int size = logStringBuffer.size(); + for(int i=0; i < size; i++) + { + handleLogText(logStringBuffer.back()); + logStringBuffer.pop_back(); + } +} + +void wfmain::handleLogText(QString text) +{ + // This function is just a pass-through + logWindow->acceptLogText(text); +} + +void wfmain::setDebugLogging(bool debugModeOn) +{ + this->debugMode = debugModeOn; + debugModeLogging = debugModeOn; +} + +void wfmain::messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + // Open stream file writes + + if (type == QtDebugMsg && !debugModeLogging) + { + return; + } + + QMutexLocker locker(&logMutex); + QTextStream out(m_logFile.data()); + QString text; + + // Write the date of recording + out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz "); + text.append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ")); + // By type determine to what level belongs message + + switch (type) + { + case QtDebugMsg: + out << "DBG "; + text.append("DBG "); + break; + case QtInfoMsg: + out << "INF "; + text.append("INF "); + break; + case QtWarningMsg: + out << "WRN "; + text.append("WRN "); + break; + case QtCriticalMsg: + out << "CRT "; + text.append("CRT "); + break; + case QtFatalMsg: + out << "FTL "; + text.append("FLT "); + break; + } + // Write to the output category of the message and the message itself + out << context.category << ": " << msg << "\n"; + out.flush(); // Clear the buffered data + + text.append(context.category); + text.append(": "); + text.append(msg); + logTextMutex.lock(); + logStringBuffer.push_front(text); + logTextMutex.unlock(); +} diff --git a/wfmain.h b/wfmain.h index 409e8ce..2ae019a 100644 --- a/wfmain.h +++ b/wfmain.h @@ -15,6 +15,10 @@ #include #include #include +#include +#include +#include +#include #include "logcategories.h" #include "commhandler.h" @@ -34,6 +38,8 @@ #include "rigctld.h" #include "aboutbox.h" #include "selectradio.h" +#include "colorprefs.h" +#include "loggingwindow.h" #include #include @@ -50,6 +56,7 @@ #include "rtaudio/RtAudio.h" #endif +#define numColorPresetsTotal (5) namespace Ui { class wfmain; @@ -60,10 +67,12 @@ class wfmain : public QMainWindow Q_OBJECT public: - explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, QWidget *parent = 0); + explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, bool debugMode, QWidget *parent = 0); QString serialPortCL; QString hostCL; ~wfmain(); + static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + void handleLogText(QString text); signals: // Basic to rig: @@ -282,6 +291,7 @@ private slots: void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); void receiveStatusUpdate(networkStatus status); + void receiveNetworkAudioLevels(networkAudioLevels l); void handlePlotClick(QMouseEvent *); void handlePlotDoubleClick(QMouseEvent *); void handleWFClick(QMouseEvent *); @@ -301,6 +311,8 @@ private slots: void setBand(int band); void setRadioTimeDateSend(); + void logCheck(); + void setDebugLogging(bool debugModeOn); void buttonControl(const COMMAND* cmd); @@ -315,8 +327,6 @@ private slots: void on_clearPeakBtn_clicked(); - void on_drawPeakChk_clicked(bool checked); - void on_fullScreenChk_clicked(bool checked); void on_goFreqBtn_clicked(); @@ -346,14 +356,8 @@ private slots: void on_scopeEdgeCombo_currentIndexChanged(int index); - // void on_modeSelectCombo_currentIndexChanged(int index); - - void on_useDarkThemeChk_clicked(bool checked); - void on_modeSelectCombo_activated(int index); - // void on_freqDial_actionTriggered(int action); - void on_freqDial_valueChanged(int value); void on_band6mbtn_clicked(); @@ -402,7 +406,6 @@ private slots: void on_saveSettingsBtn_clicked(); - void on_debugBtn_clicked(); void on_pttEnableChk_clicked(bool checked); @@ -445,7 +448,6 @@ private slots: void on_dataModeBtn_toggled(bool checked); - void on_transmitBtn_clicked(); void on_adjRefBtn_clicked(); @@ -545,14 +547,14 @@ private slots: void on_setClockBtn_clicked(); void on_serverEnableCheckbox_clicked(bool checked); - void on_serverUsersTable_cellClicked(int row, int col); void on_serverControlPortText_textChanged(QString text); void on_serverCivPortText_textChanged(QString text); void on_serverAudioPortText_textChanged(QString text); void on_serverTXAudioOutputCombo_currentIndexChanged(int value); void on_serverRXAudioInputCombo_currentIndexChanged(int value); - void onServerPasswordChanged(); - void on_serverUsersTable_cellChanged(int row, int column); + void onServerUserFieldChanged(); + + void on_serverAddUserBtn_clicked(); void on_useRTSforPTTchk_clicked(bool checked); @@ -560,9 +562,115 @@ private slots: void on_audioSystemCombo_currentIndexChanged(int value); + void on_topLevelSlider_valueChanged(int value); + + void on_botLevelSlider_valueChanged(int value); + + void on_underlayBufferSlider_valueChanged(int value); + + void on_underlayNone_toggled(bool checked); + + void on_underlayPeakHold_toggled(bool checked); + + void on_underlayPeakBuffer_toggled(bool checked); + + void on_underlayAverageBuffer_toggled(bool checked); + + void on_colorSetBtnGrid_clicked(); + + void on_colorSetBtnPlotBackground_clicked(); + + void on_colorSetBtnText_clicked(); + + void on_colorSetBtnSpecLine_clicked(); + + void on_colorSetBtnSpecFill_clicked(); + + void on_colorEditPlotBackground_editingFinished(); + + void on_colorPopOutBtn_clicked(); + + void on_colorPresetCombo_currentIndexChanged(int index); + + void on_colorEditSpecLine_editingFinished(); + + void on_colorEditGrid_editingFinished(); + + void on_colorEditText_editingFinished(); + + void on_colorEditSpecFill_editingFinished(); + + void on_colorSetBtnAxis_clicked(); + + void on_colorEditAxis_editingFinished(); + + void on_colorSetBtnUnderlayLine_clicked(); + + void on_colorEditUnderlayLine_editingFinished(); + + void on_colorSetBtnUnderlayFill_clicked(); + + void on_colorEditUnderlayFill_editingFinished(); + + void on_colorSetBtnwfBackground_clicked(); + + void on_colorEditWfBackground_editingFinished(); + + void on_colorSetBtnWfGrid_clicked(); + + void on_colorEditWfGrid_editingFinished(); + + void on_colorSetBtnWfAxis_clicked(); + + void on_colorEditWfAxis_editingFinished(); + + void on_colorSetBtnWfText_clicked(); + + void on_colorEditWfText_editingFinished(); + + void on_colorSetBtnTuningLine_clicked(); + + void on_colorEditTuningLine_editingFinished(); + + void on_colorSetBtnMeterLevel_clicked(); + + void on_colorEditMeterLevel_editingFinished(); + + void on_colorSetBtnMeterAvg_clicked(); + + void on_colorEditMeterAvg_editingFinished(); + + void on_colorSetBtnMeterScale_clicked(); + + void on_colorEditMeterScale_editingFinished(); + + void on_colorSetBtnMeterText_clicked(); + + void on_colorEditMeterText_editingFinished(); + + void on_colorRenamePresetBtn_clicked(); + + void on_colorRevertPresetBtn_clicked(); + + void on_colorSetBtnMeterPeakLevel_clicked(); + + void on_colorEditMeterPeakLevel_editingFinished(); + + void on_colorSetBtnMeterPeakScale_clicked(); + + void on_colorEditMeterPeakScale_editingFinished(); + + void on_colorSavePresetBtn_clicked(); + + void on_showLogBtn_clicked(); + + void on_audioSystemServerCombo_currentIndexChanged(int index); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); + bool debugMode; + QString version; QSettings *settings=Q_NULLPTR; void loadSettings(); void saveSettings(); @@ -570,14 +678,20 @@ private: void createSettingsListItems(); void connectSettingsList(); + void initLogging(); + QTimer logCheckingTimer; + int logCheckingOldPosition = 0; + QString logFilename; + QCustomPlot *plot; // line plot QCustomPlot *wf; // waterfall image QCPItemLine * freqIndicatorLine; - //commHandler *comm; void setAppTheme(bool isCustom); - void setPlotTheme(QCustomPlot *plot, bool isDark); void prepareWf(); void prepareWf(unsigned int wfLength); + void preparePlasma(); + bool plasmaPrepared = false; + void computePlasma(); void showHideSpectrum(bool show); void getInitialRigState(); void setBandButtons(); @@ -669,13 +783,28 @@ private: quint16 wfLength; bool spectrumDrawLock; + enum underlay_t { underlayNone, underlayPeakHold, underlayPeakBuffer, underlayAverageBuffer }; + + QByteArray spectrumPeaks; + QVector spectrumPlasmaLine; + QVector spectrumPlasma; + unsigned int spectrumPlasmaSize = 64; + underlay_t underlayMode = underlayNone; + QMutex plasmaMutex; + void resizePlasmaBuffer(int newSize); + + double plotFloor = 0; + double plotCeiling = 160; + double wfFloor = 0; + double wfCeiling = 160; + double oldPlotFloor = -1; + double oldPlotCeiling = 999; QVector wfimage; unsigned int wfLengthMax; bool onFullscreen; - bool drawPeaks; bool freqTextSelected; void checkFreqSel(); @@ -750,36 +879,15 @@ private: datekind datesetpoint; freqMemory mem; - struct colors { - QColor Dark_PlotBackground; - QColor Dark_PlotAxisPen; - QColor Dark_PlotLegendTextColor; - QColor Dark_PlotLegendBorderPen; - QColor Dark_PlotLegendBrush; - QColor Dark_PlotTickLabel; - QColor Dark_PlotBasePen; - QColor Dark_PlotTickPen; - QColor Dark_PeakPlotLine; - QColor Dark_TuningLine; - QColor Light_PlotBackground; - QColor Light_PlotAxisPen; - QColor Light_PlotLegendTextColor; - QColor Light_PlotLegendBorderPen; - QColor Light_PlotLegendBrush; - QColor Light_PlotTickLabel; - QColor Light_PlotBasePen; - QColor Light_PlotTickPen; - QColor Light_PeakPlotLine; - QColor Light_TuningLine; - - } colorScheme; + colorPrefsType colorPreset[numColorPresetsTotal]; struct preferences { bool useFullScreen; - bool useDarkMode; bool useSystemTheme; bool drawPeaks; + underlay_t underlayMode = underlayNone; + int underlayBufferSize = 64; bool wfAntiAlias; bool wfInterpolate; QString stylesheetPath; @@ -793,18 +901,19 @@ private: bool enableLAN; bool enableRigCtlD; quint16 rigCtlPort; - colors colorScheme; + int currentColorPresetNumber = 0; QString virtualSerialPort; unsigned char localAFgain; unsigned int wflength; int wftheme; + int plotFloor; + int plotCeiling; bool confirmExit; bool confirmPowerOff; meterKind meter2Type; quint16 tcpPort; quint8 waterfallFormat; audioType audioSystem; - // plot scheme } prefs; preferences defPrefs; @@ -815,13 +924,34 @@ private: audioSetup rxSetup; audioSetup txSetup; + void setDefaultColors(int presetNumber); // populate with default values - colors defaultColors; - - void setDefaultColors(); // populate with default values void useColors(); // set the plot up void setDefPrefs(); // populate default values to default prefs void setTuningSteps(); + void setColorElement(QColor color, QLedLabel *led, QLabel *label); + void setColorElement(QColor color, QLedLabel *led, QLineEdit *lineText); + void setColorElement(QColor color, QLedLabel *led, QLabel *label, QLineEdit *lineText); + QColor getColorFromPicker(QColor initialColor); + void getSetColor(QLedLabel *led, QLabel *label); + void getSetColor(QLedLabel *led, QLineEdit *line); + QString setColorFromString(QString aarrggbb, QLedLabel *led); + void setDefaultColorPresets(); + void loadColorPresetToUIandPlots(int presetNumber); + void useColorPreset(colorPrefsType *cp); + void useCurrentColorPreset(); + void setEditAndLedFromColor(QColor c, QLineEdit *e, QLedLabel *d); + void setColorButtonOperations(QColor *colorStore, QLineEdit *e, QLedLabel *d); + void setColorLineEditOperations(QColor *colorStore, QLineEdit *e, QLedLabel *d); + + void detachSettingsTab(); + void reattachSettingsTab(); + void prepareSettingsWindow(); + QWidget *settingsWidgetWindow; + QWidget *settingsTab; + QGridLayout *settingsWidgetLayout; + QTabWidget *settingsWidgetTab; + bool settingsTabisAttached = true; quint64 roundFrequency(quint64 frequency, unsigned int tsHz); quint64 roundFrequencyWithStep(quint64 oldFreq, int steps,\ @@ -889,6 +1019,7 @@ private: shuttleSetup* shut; aboutbox *abtBox; selectRadio *selRad; + loggingWindow *logWindow; udpServer* udp = Q_NULLPTR; rigCtlD* rigCtl = Q_NULLPTR; @@ -941,6 +1072,7 @@ Q_DECLARE_METATYPE(struct SERVERCONFIG) Q_DECLARE_METATYPE(struct timekind) Q_DECLARE_METATYPE(struct datekind) Q_DECLARE_METATYPE(struct networkStatus) +Q_DECLARE_METATYPE(struct networkAudioLevels) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) diff --git a/wfmain.ui b/wfmain.ui index 7b98f6e..b765db3 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,8 +6,8 @@ 0 0 - 940 - 569 + 1032 + 582 @@ -31,6 +31,18 @@ Spectrum + + 0 + + + 7 + + + 0 + + + 0 + @@ -326,6 +338,18 @@ Tuning Dial + + 3000 + + + 4000 + + + 10 + + + 100 + true @@ -846,6 +870,117 @@ + + + + 0 + + + 0 + + + + + + 0 + 70 + + + + + 16777215 + 80 + + + + Sets the ceiling for the waterfall and spectrum displays + + + Ceiling for waterfall and spectrum display + + + 1 + + + 160 + + + 160 + + + Qt::Vertical + + + + + + + + 16777215 + 15 + + + + Top + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 70 + + + + + 16777215 + 80 + + + + Sets the floor for the waterfall and spectrum displays + + + 160 + + + Qt::Vertical + + + + + + + + 16777215 + 15 + + + + Bot + + + + + @@ -927,7 +1062,19 @@ Preamp/Att - + + + 4 + + + 4 + + + 4 + + + 4 + @@ -2042,10 +2189,10 @@ - 10 + 0 - 10 + 0 @@ -2209,7 +2356,19 @@ Serial Connected Radios - + + + 3 + + + 5 + + + 3 + + + 5 + @@ -2294,7 +2453,22 @@ Network Connected Radios - + + + 6 + + + 3 + + + 5 + + + 3 + + + 5 + @@ -2591,7 +2765,7 @@ - 300 + 120 16777215 @@ -2611,7 +2785,7 @@ - 300 + 120 16777215 @@ -2625,9 +2799,12 @@ Qt::Horizontal + + QSizePolicy::Fixed + - 40 + 15 20 @@ -2644,7 +2821,7 @@ - QT Audio + Qt Audio @@ -2659,6 +2836,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -2671,13 +2861,6 @@ 0 - - - - Connect - - - @@ -2712,13 +2895,6 @@ - - - - Draw Peaks - - - @@ -2746,15 +2922,11 @@ - - - - Waterfall Dark Theme - - - + + Enables interpolation between pixels. Note that this will increase CPU usage. + Interpolate Waterfall @@ -2764,16 +2936,16 @@ - + - Use System Theme + Anti-Alias Waterfall - + - Anti-Alias Waterfall + Use System Theme @@ -2799,8 +2971,154 @@ + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Underlay Mode + + + + + + + No underlay graphics + + + None + + + true + + + underlayButtonGroup + + + + + + + Indefinite peak hold + + + Peak Hold + + + underlayButtonGroup + + + + + + + Peak value within the buffer + + + Peak + + + underlayButtonGroup + + + + + + + Average value within the buffer + + + Average + + + underlayButtonGroup + + + + + + + Underlay Buffer Size: + + + + + + + + 100 + 16777215 + + + + Size of buffer for spectrum data. Shorter values are more responsive. + + + 8 + + + 128 + + + 64 + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Enable PTT Controls + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + @@ -2851,14 +3169,108 @@ 0 - + - Enable PTT Controls + Color scheme - + + + Preset: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 90 + 0 + + + + + 90 + 16777215 + + + + Select a color preset here. + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + + + + Saves the current preset to the settings file. + + + Save Preset + + + + + + + Revert the selected color preset to the default. + + + Revert + + + + + + + Rename the selected color preset. Max length is 10 characters. + + + Rename Preset + + + + + + + <html><head/><body><p>Pop out (or pop back in) the entire Settings tab. </p><p>NOTE: Press this button again to re-insert the tab when finished. </p></body></html> + + + Pop-Out + + + + + Qt::Horizontal @@ -2872,6 +3284,520 @@ + + + + + 0 + 1 + + + + + 0 + 250 + + + + + 0 + 0 + + + + User-defined Color Editor + + + + 0 + + + 4 + + + 0 + + + 0 + + + + + QAbstractScrollArea::AdjustToContents + + + true + + + + + 0 + 0 + 194 + 563 + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Waterfall Grid + + + + + + + + + + Spectrum Fill + + + + + + + Underlay Fill + + + + + + + + + + Meter Average + + + + + + + Meter High Scale + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + Waterfall Back + + + + + + + Plot Background + + + + + + + + 90 + 16777215 + + + + Color text format is #AARRGGBB, where AA is the "alpha" channel, and value "00" is totally transparent, and "ff" is totally opaque. + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Waterfall Text + + + + + + + + + + Axis + + + + + + + + + + + + + Waterfall Axis + + + + + + + + + + Grid + + + + + + + + + + + + + + + + Tuning Line + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Text + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Meter Peak Level + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Spectrum Line + + + + + + + + + + Meter Text + + + + + + + Underlay Line + + + + + + + Meter Scale + + + + + + + Meter Level + + + + + + + + + + @@ -2880,7 +3806,7 @@ 20 - 40 + 20 @@ -3264,7 +4190,7 @@ - + @@ -3282,7 +4208,7 @@ - 300 + 100 16777215 @@ -3305,12 +4231,38 @@ - 300 + 100 16777215 + + + + Audio System + + + + + + + + Qt Audio + + + + + PortAudio + + + + + RT Audio + + + + @@ -3358,7 +4310,7 @@ 0 - 3 + 4 false @@ -3396,6 +4348,11 @@ Admin + + + Delete? + + @@ -3413,6 +4370,30 @@ + + + + + + Add User + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -3761,6 +4742,33 @@ + + + + Log + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Connect to Radio + + + @@ -3797,7 +4805,7 @@ 0 0 - 940 + 1032 21 @@ -3815,10 +4823,17 @@
meter.h
1 + + QLedLabel + QWidget +
qledlabel.h
+ 1 +
+ diff --git a/wfserver.pro b/wfserver.pro index bbd4fee..658d0a0 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -13,7 +13,7 @@ TEMPLATE = app CONFIG += console -DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" +DEFINES += WFVIEW_VERSION=\\\"1.45\\\" DEFINES += BUILD_WFSERVER @@ -22,7 +22,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -ole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -31,7 +31,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -76,34 +76,6 @@ equals(QT_ARCH, x86_64): DEFINES += EIGEN_VECTORIZE_SSE3 DEFINES += PREFIX=\\\"$$PREFIX\\\" -# Choose audio system, uses QTMultimedia if both are commented out. -# DEFINES += RTAUDIO -# DEFINES += PORTAUDIO - -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib diff --git a/wfview.pro b/wfview.pro index 309d267..4bfcd2c 100644 --- a/wfview.pro +++ b/wfview.pro @@ -11,7 +11,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport TARGET = wfview TEMPLATE = app -DEFINES += WFVIEW_VERSION=\\\"1.2e\\\" +DEFINES += WFVIEW_VERSION=\\\"1.45\\\" DEFINES += BUILD_WFVIEW @@ -19,7 +19,7 @@ CONFIG(debug, release|debug) { # For Debug builds only: QMAKE_CXXFLAGS += -faligned-new win32:DESTDIR = wfview-release - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 -lole32 } else { # For Release builds only: linux:QMAKE_CXXFLAGS += -s @@ -28,7 +28,7 @@ CONFIG(debug, release|debug) { QMAKE_CXXFLAGS += -faligned-new linux:QMAKE_LFLAGS += -O2 -s win32:DESTDIR = wfview-debug - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 + win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 -lole32 } # RTAudio defines @@ -77,30 +77,6 @@ isEmpty(PREFIX) { DEFINES += PREFIX=\\\"$$PREFIX\\\" -contains(DEFINES, RTAUDIO) { - # RTAudio defines - win32:DEFINES += __WINDOWS_WASAPI__ - #win32:DEFINES += __WINDOWS_DS__ # Requires DirectSound libraries - linux:DEFINES += __LINUX_ALSA__ - #linux:DEFINES += __LINUX_OSS__ - #linux:DEFINES += __LINUX_PULSE__ - macx:DEFINES += __MACOSX_CORE__ - win32:SOURCES += ../rtaudio/RTAudio.cpp - win32:HEADERS += ../rtaudio/RTAUdio.h - !linux:INCLUDEPATH += ../rtaudio - linux:LIBS += -lpulse -lpulse-simple -lrtaudio -lpthread -} - -contains(DEFINES, PORTAUDIO) { - CONFIG(debug, release|debug) { - win32:LIBS += -L../portaudio/msvc/Win32/Debug/ -lportaudio_x86 - } else { - win32:LIBS += -L../portaudio/msvc/Win32/Release/ -lportaudio_x86 - } - win32:INCLUDEPATH += ../portaudio/include - !win32:LIBS += -lportaudio -} - macx:INCLUDEPATH += /usr/local/include /opt/local/include macx:LIBS += -L/usr/local/lib -L/opt/local/lib @@ -115,7 +91,7 @@ QMAKE_INFO_PLIST = ../wfview/resources/Info.plist !win32:DEFINES += HOST=\\\"`hostname`\\\" UNAME=\\\"`whoami`\\\" -!win32:DEFINES += GITSHORT="\\\"$(shell git -C $$PWD rev-parse --short HEAD)\\\"" +!win32:DEFINES += GITSHORT="\\\"$(shell git -C \"$$PWD\" rev-parse --short HEAD)\\\"" win32:DEFINES += GITSHORT=\\\"$$system(git -C $$PWD rev-parse --short HEAD)\\\" win32:DEFINES += HOST=\\\"wfview.org\\\" @@ -177,6 +153,7 @@ win32:INCLUDEPATH += ../r8brain-free-src INCLUDEPATH += resampler SOURCES += main.cpp\ + loggingwindow.cpp \ wfmain.cpp \ commhandler.cpp \ rigcommander.cpp \ @@ -208,7 +185,9 @@ SOURCES += main.cpp\ aboutbox.cpp HEADERS += wfmain.h \ + colorprefs.h \ commhandler.h \ + loggingwindow.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ @@ -245,6 +224,7 @@ HEADERS += wfmain.h \ FORMS += wfmain.ui \ calibrationwindow.ui \ + loggingwindow.ui \ satellitesetup.ui \ selectradio.ui \ repeatersetup.ui \