diff --git a/CHANGELOG b/CHANGELOG index a10f418..a90975f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,341 @@ # CHANGELOG +- 20221129 + + bumped 1.53 + +- 20221128 + + Add missing prefs + Merge remote-tracking branch 'origin/pollprefs' into various-fixes + Added some tooltips that nobody will notice. + Added polling preferences and changed UI elements to radio buttons with + spin box. + Added program version number to the settings file. Currently it is merely + written to and not used. + Moved preferences to prefs.h, which should make it easier to add new + preferences. Moved custom types (as it seemed useful here and there) to + wfviewtypes.h. Let's use the wfviewtypes.h file for any kind of datatype + which more than one module can make use of. + Update some rig definitions to work with new band struct. + +- 20221126 + + Merge branch 'master' into various-fixes + Add some more commands + +- 20221124 + + Fixed color preset zero issue. + Fixed unusual set of conditions in which manual CI-V address would fail + to identify the connected radio. The fix is to continually poll the RigID + until one is received. Radios that don't support RigID will need to + check both boxes in wfview (use rig as model number) to manually ID the + radio. + Fixed minor typo in debug text. + Cleaned up rigidentities + Fixed annoying indentation issue. + Fixed broken RTS preference. Added preliminary (and untested) support + for the IC-703, 737, 738, and 756. + Fixed issue causing the current color preset to not be load (previously + preset zero was always used). + +- 20221101 + + added libs for suse builds; it also builds on leap 15.5 + +- 20221031 + + Fix passband warnings + Add skimmer support (setting not saved) + Key KEYSPD + Add AR Cluster support + Add more commands to rigctld + +- 20221029 + + Make bandType a struct containing frequency and default SSB mode + Remove spot display processing timer + Silly error in setting default cluster + +- 20221028 + + Send bye when disconnecting from cluster + Add passband to rigctld and allow setting + +- 20221025 + + combobox resizing fix + +- 20221024 + + Stop it cutting off some devices + +- 20221023 + + Resize according to boundingrect + Resize based on font size + Try new universal resize code + +- 20221022 + + added qt xml dev lib for suse + + +- 20221025 + + combobox: Stop it cutting off some devices + Resize according to boundingrect + Resize based on font size + Try new universal resize code + +- 20221022 + + added qt xml dev lib for suse + +- 20221014 + + bands page reorganized + + +- 20221010 + + bumped to version 1.52 + +- 20221011 + + make spots appear at the actual top of wf + + Add sendOutput() back to tcp cluster + + Add Pop-Out button for cluster screen + + Add cluster spot color picker + +- 20221008 + + Left click on spot to change freq + +- 20221006 + + Make spot window size to fit content. + + Add right-click on spot to popup dialog + +- 20221005 + + Bit of tidying + + Remove calls for QSqlDatabase when USESQL is not configured + + Add optional SQL in wfview.pro + + Make SQL db optional + + Remove old code + + Update query + + Add memory sqlite db for cluster spots. + +- 20220930 + + Delete all spots if cluster disabled + + Fix crash when adding cluster server + + Change default to isdefault! + + Add TCP spot client + +- 20220929 + + Fix for squished screen + + bump version to 1.51 + + +- 20220928 + + Only request passband when there is a scope available + + Change default passband colors. + + Fix passband colorswatch + +- 20220927 + + See if this works better for color pickers? + + Ignore second VFO scope data (for now) + + Silently ignore server audio issues if server is not enabled. + + Always use 8bit encoding for audio device names + + Make multimedia-plugins message only for Linux + +- 20220926 + + Add PSK modes to IC-7610 and to passband. + + Add passband for FM mode + + Added click-drag tuning. Needs refinement but it's a start. + + colorpicker: Move to 3 columns + + First look at a passband indicator + +- 20220925 + + Remove obsolete code + + Remove logging of audio device realm + +- 20220923 + + ready for v1.50 + +- 20220923 + + Delete quit confirmation checkbox + + Use date/time for log name if none specified + + Fix logfile/directory opening in Windows + + Fixed glitch when tabs are changed prior to rigcaps. + + Remove redundant CL args + + Additional resize fixes for non-spectrum rigs. + + Hide UI elements not needed for non-spectrum radios. + + updated to v1.48 + +- 20220921 + + Fix that was stopping users being created when none existed + +- 20220920 + + Added dialog box to the toFixed button where an edge can be selected. + + Add quick fix for rigctld fmv issue + + updated to v1.47 + +- 20220918 + + Added controls for custom scope edges, hide/show scope controls + depending upon the scope mode the radio reports. + + Fixed clear peaks button to work with the plasma underlay. + + Make sure both audio system button are disabled when connected to radio and enabled when not. + + Remove password from log! + + Fix server user handling + + updated to v1.46 + +- 20220918 + + merged bits from log branch into master + + updated to v1.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 diff --git a/INSTALL.md b/INSTALL.md index 70612cc..fd304f8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -78,7 +78,7 @@ install wfview on suse 15.3 & up, sles 15.x or tumbleweed; this was done on a cl we need to add packages to be able to build the stuff. - sudo zypper in --type pattern devel_basis -- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel eigen3-devel +- sudo zypper in libQt5Widgets-devel libqt5-qtbase-common-devel libqt5-qtserialport-devel libQt5SerialPort5 qcustomplot-devel libqcustomplot2 libQt5PrintSupport-devel libqt5-qtmultimedia-devel lv2-devel libopus-devel eigen3-devel libQt5Xml-devel portaudio-devel rtaudio-devel optional (mainly for development specifics): get and install qt5: diff --git a/WHATSNEW b/WHATSNEW index 270f5ae..527f43d 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -1,29 +1,36 @@ -The following highlights are in this 1.x-release: - - 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 - +The following highlights are in this 1.51-release ince v1.50: ++ 1.51 + Fix for squished screen + Only request passband when there is a scope available + Change default passband colors. + Fix passband colorswatch + Ignore second VFO scope data (for now) + Silently ignore server audio issues if server is not enabled. + Always use 8bit encoding for audio device names + Make multimedia-plugins message only for Linux + Add PSK modes to IC-7610 and to passband. + Add passband for FM mode + Added click-drag tuning. Needs refinement but it's a start. + colorpicker: Move to 3 columns + passband indicator + Remove logging of audio device realm ++ 1.52 added cluster spotting including color picker suport + optional SQLITE in memory for spots (disabled) ++ 1.53 modified band buttons to look more alike the layout on the rigs + audio selction combobox shows full devicenames + Fixed color preset zero issue. + Fixed unusual set of conditions in which manual CI-V address would fail + to identify the connected radio. + Fixed broken RTS preference. Added preliminary (and untested) support + for the IC-703, 737, 738, and 756. + added libs for suse builds; it also builds on leap 15.5 + Add skimmer support (setting not saved) + Add AR Cluster support ++1.54 Various wfserver fixes + logging using termbin edge case fix + Notes: - We know about high CPU usage on RPi. diff --git a/aboutbox.cpp b/aboutbox.cpp index ba8de0f..4c3549e 100644 --- a/aboutbox.cpp +++ b/aboutbox.cpp @@ -15,9 +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 scm = QString("

Source code and issues managed by Roeland Jansen, PA3MET"); + QString doctest = QString("

Testing and development mentorship from 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/."); @@ -33,7 +36,7 @@ aboutbox::aboutbox(QWidget *parent) : QString support = QString("

For support, please visit the official wfview support forum."); QString gitcodelink = QString("").arg(GITSHORT); - QString contact = QString("
email W6EL: kilocharlie8@gmail.com"); + QString contact = QString("
email W6EL: kilocharlie8 at gmail.com"); QString buildInfo = QString("

Build " + gitcodelink + QString(GITSHORT) + "
on " + QString(__DATE__) + " at " + __TIME__ + " by " + UNAME + "@" + HOST); QString end = QString(""); @@ -83,7 +86,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" + scm + "\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 398ba98..fb18088 100644 --- a/audioconverter.cpp +++ b/audioconverter.cpp @@ -186,7 +186,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 b82d62b..642f451 100644 --- a/audioconverter.h +++ b/audioconverter.h @@ -27,8 +27,7 @@ #include #endif -enum audioType { qtAudio, portAudio, rtAudio }; -enum codecType { LPCM, PCMU, OPUS }; +#include "wfviewtypes.h" #include "resampler/speex_resampler.h" @@ -40,7 +39,8 @@ struct audioPacket { quint16 sent; QByteArray data; quint8 guid[GUIDLEN]; - float amplitude; + float amplitudePeak; + float amplitudeRMS; qreal volume = 1.0; }; diff --git a/audiodevices.cpp b/audiodevices.cpp new file mode 100644 index 0000000..12be2fe --- /dev/null +++ b/audiodevices.cpp @@ -0,0 +1,330 @@ +/* + wfview class to enumerate audio devices and assist with matching saved devices + + Written by Phil Taylor M0VSE Nov 2022. + +*/ + +#include "audiodevices.h" +#include "logcategories.h" + +audioDevices::audioDevices(audioType type, QFontMetrics fm, QObject* parent) : + QObject(parent), + system(type), + fm(fm) +{ + +} + + +void audioDevices::enumerate() +{ + numInputDevices = 0; + numOutputDevices = 0; + numCharsIn = 0; + numCharsOut = 0; + inputs.clear(); + outputs.clear(); + switch (system) + { + case qtAudio: + { + Pa_Terminate(); + + foreach(const QAudioDeviceInfo & deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) + { + bool isDefault = false; + if (numInputDevices == 0) { + defaultInputDeviceName = QString(deviceInfo.deviceName()); + } +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + /* Append Input Device Here */ + if (deviceInfo.deviceName() == defaultInputDeviceName) { + isDefault = true; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0)) + inputs.append(audioDevice(deviceInfo.deviceName(), deviceInfo, deviceInfo.realm(), isDefault)); +#else + inputs.append(audioDevice(deviceInfo.deviceName(), deviceInfo, "", isDefault)); +#endif + + if (fm.boundingRect(deviceInfo.deviceName()).width() > numCharsIn) + numCharsIn = fm.boundingRect(deviceInfo.deviceName()).width(); + +#ifdef Q_OS_WIN + } +#endif + numInputDevices++; + } + + foreach(const QAudioDeviceInfo & deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) + { + bool isDefault = false; + if (numOutputDevices == 0) + { + defaultOutputDeviceName = QString(deviceInfo.deviceName()); + } +#ifdef Q_OS_WIN + if (deviceInfo.realm() == "wasapi") { +#endif + /* Append Output Device Here */ + if (deviceInfo.deviceName() == defaultOutputDeviceName) { + isDefault = true; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0)) + outputs.append(audioDevice(deviceInfo.deviceName(), deviceInfo, deviceInfo.realm(), isDefault)); +#else + outputs.append(audioDevice(deviceInfo.deviceName(), deviceInfo, "", isDefault)); +#endif + if (fm.boundingRect(deviceInfo.deviceName()).width() > numCharsOut) + numCharsOut = fm.boundingRect(deviceInfo.deviceName()).width(); + +#ifdef Q_OS_WIN + } +#endif + numOutputDevices++; + } + break; + } + case portAudio: + { + PaError err; + + err = Pa_Initialize(); + + if (err != paNoError) + { + qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; + return; + } + + qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; + + int numDevices = Pa_GetDeviceCount(); + qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; + + const PaDeviceInfo* info; + for (int i = 0; i < numDevices; i++) + { + info = Pa_GetDeviceInfo(i); + if (info->maxInputChannels > 0) { + bool isDefault = false; + numInputDevices++; + qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << QString(info->name); + if (i == Pa_GetDefaultInputDevice()) { + defaultInputDeviceName = info->name; + isDefault = true; + } + inputs.append(audioDevice(QString(info->name), i,isDefault)); + if (fm.boundingRect(QString(info->name)).width() > numCharsIn) + numCharsIn = fm.boundingRect(QString(info->name)).width(); + + } + if (info->maxOutputChannels > 0) { + bool isDefault = false; + numOutputDevices++; + qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << QString(info->name); + if (i == Pa_GetDefaultOutputDevice()) { + defaultOutputDeviceName = info->name; + isDefault = true; + } + outputs.append(audioDevice(QString(info->name), i,isDefault)); + if (fm.boundingRect(QString(info->name)).width() > numCharsOut) + numCharsOut = fm.boundingRect(QString(info->name)).width(); + } + } + break; + } + case rtAudio: + { + Pa_Terminate(); + +#if defined(Q_OS_LINUX) + RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); +#elif defined(Q_OS_WIN) + RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); +#elif defined(Q_OS_MACX) + RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); +#endif + + + // Enumerate audio devices, need to do before settings are loaded. + std::map apiMap; + apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; + apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; + apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; + apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; + apiMap[RtAudio::UNIX_JACK] = "Jack Client"; + apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; + apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; + apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; + apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; + + std::vector< RtAudio::Api > apis; + RtAudio::getCompiledApi(apis); + + qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); + + qInfo(logAudio()) << "Compiled APIs:"; + for (unsigned int i = 0; i < apis.size(); i++) { + qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); + } + + RtAudio::DeviceInfo info; + + qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); + + unsigned int devices = audio->getDeviceCount(); + qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; + + for (unsigned int i = 1; i < devices; i++) { + info = audio->getDeviceInfo(i); + if (info.inputChannels > 0) { + bool isDefault = false; + qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); + numInputDevices++; + + if (info.isDefaultInput) { + defaultInputDeviceName = QString::fromStdString(info.name); + isDefault = true; + } + + inputs.append(audioDevice(QString::fromStdString(info.name), i, isDefault)); + + if (fm.boundingRect(QString::fromStdString(info.name)).width() > numCharsIn) + numCharsIn = fm.boundingRect(QString::fromStdString(info.name)).width(); + + } + if (info.outputChannels > 0) { + bool isDefault = false; + qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); + numOutputDevices++; + + if (info.isDefaultOutput) { + defaultOutputDeviceName = QString::fromStdString(info.name); + isDefault = true; + } + + outputs.append(audioDevice(QString::fromStdString(info.name), i, isDefault)); + + if (fm.boundingRect(QString::fromStdString(info.name)).width() > numCharsOut) + numCharsOut = fm.boundingRect(QString::fromStdString(info.name)).width(); + } + } + + delete audio; + break; + } + + } + emit updated(); + +} + +audioDevices::~audioDevices() +{ + +} + +QStringList audioDevices::getInputs() +{ + QStringList list; + foreach(const audioDevice input, inputs) { + list.append(input.name); + } + + return list; +} + +QStringList audioDevices::getOutputs() +{ + QStringList list; + foreach(const audioDevice output, outputs) { + list.append(output.name); + } + + return list; +} + +int audioDevices::findInput(QString type, QString name) +{ + int ret = -1; + int def = -1; + QString msg; + QTextStream s(&msg); + for (int f = 0; f < inputs.size(); f++) + { + //qInfo(logAudio()) << "Found device" << inputs[f].name; + if (inputs[f].name.startsWith(name)) { + s << type << " Audio input device " << name << " found! "; + ret = f; + } + if (inputs[f].isDefault == true) + { + def = f; + } + } + + if (ret == -1) + { + s << type << " Audio input device " << name << " Not found: "; + + if (inputs.size() == 1) { + s << "Selecting first device " << inputs[0].name; + ret = 0; + } + else if (def > -1) + { + s << " Selecting default device " << inputs[def].name; + ret = def; + } + else { + s << " and no default device found, aborting!"; + } + } + + qInfo(logAudio()) << msg; + return ret; +} + +int audioDevices::findOutput(QString type, QString name) +{ + int ret = -1; + int def = -1; + QString msg; + QTextStream s(&msg); + for (int f = 0; f < outputs.size(); f++) + { + //qInfo(logAudio()) << "Found device" << outputs[f].name; + if (outputs[f].name.startsWith(name)) { + ret = f; + s << type << " Audio output device " << name << " found! "; + } + if (outputs[f].isDefault == true) + { + def = f; + } + } + + if (ret == -1) + { + s << type << " Audio output device " << name << " Not found: "; + + if (outputs.size() == 1) { + s << " Selecting first device " << outputs[0].name; + ret = 0; + } + else if (def > -1) + { + s << " Selecting default device " << outputs[def].name; + ret = def; + } + else { + s << " and no default device found, aborting!"; + } + } + qInfo(logAudio()) << msg; + return ret; +} \ No newline at end of file diff --git a/audiodevices.h b/audiodevices.h new file mode 100644 index 0000000..ac60624 --- /dev/null +++ b/audiodevices.h @@ -0,0 +1,73 @@ +#ifndef AUDIODEVICES_H +#define AUDIODEVICES_H +#include +#include +#include + +#include +#ifndef Q_OS_LINUX +#include "RtAudio.h" +#else +#include "rtaudio/RtAudio.h" +#endif + +#include "wfviewtypes.h" + +struct audioDevice { + audioDevice(QString name, int deviceInt, bool isDefault) : name(name), deviceInt(deviceInt), isDefault(isDefault) {}; + audioDevice(QString name, const QAudioDeviceInfo deviceInfo, QString realm, bool isDefault) : name(name), deviceInfo(deviceInfo), realm(realm), isDefault(isDefault) {}; + + QString name; + int deviceInt; + const QAudioDeviceInfo deviceInfo; + QString realm; + bool isDefault; +}; + +class audioDevices : public QObject +{ + Q_OBJECT + +public: + explicit audioDevices(audioType type, QFontMetrics fm, QObject* parent = nullptr); + ~audioDevices(); + void setAudioType(audioType type) { system = type; }; + audioType getAudioType() { return system; }; + int getNumCharsIn() { return numCharsIn; }; + int getNumCharsOut() { return numCharsOut; }; + + QString getInputName(int num) { return inputs[num].name; }; + QString getOutputName(int num) { return outputs[num].name; }; + + int getInputDeviceInt(int num) { return inputs[num].deviceInt; }; + int getOutputDeviceInt(int num) { return outputs[num].deviceInt; }; + const QAudioDeviceInfo getInputDeviceInfo(int num) { return inputs[num].deviceInfo; }; + const QAudioDeviceInfo getOutputDeviceInfo(int num) { return outputs[num].deviceInfo; }; + + void enumerate(); + + QStringList getInputs(); + QStringList getOutputs(); + + int findInput(QString type, QString name); + int findOutput(QString type, QString name); + +public slots: + +signals: + void updated(); +protected: +private: + audioType system; + QFontMetrics fm; + QString defaultInputDeviceName; + QString defaultOutputDeviceName; + int numInputDevices; + int numOutputDevices; + QList inputs; + QList outputs; + int numCharsIn = 0; + int numCharsOut = 0; +}; + +#endif \ No newline at end of file diff --git a/audiohandler.cpp b/audiohandler.cpp index 07fb26a..60f28f5 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -46,7 +46,11 @@ audioHandler::~audioHandler() qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name; if (setup.port.isNull()) { - qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; +#ifdef Q_OS_LINUX + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins."; +#else + qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device is NULL, please check device selection in settings."; +#endif return false; } @@ -275,7 +279,6 @@ void audioHandler::stop() void audioHandler::setVolume(unsigned char volume) { this->volume = audiopot[volume]; - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "setVolume: " << volume << "(" << this->volume << ")"; } @@ -315,9 +318,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); } } @@ -357,8 +359,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 3eaa686..9c25ead 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -80,7 +80,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, codecType codecIn, QAudioFormat out, codecType codecOut, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); diff --git a/cluster.cpp b/cluster.cpp new file mode 100644 index 0000000..ee17415 --- /dev/null +++ b/cluster.cpp @@ -0,0 +1,314 @@ +#include "cluster.h" +#include "logcategories.h" + + +dxClusterClient::dxClusterClient(QObject* parent): + QObject(parent) +{ + qInfo(logCluster()) << "starting dxClusterClient()"; +} + +dxClusterClient::~dxClusterClient() +{ + qInfo(logCluster()) << "closing dxClusterClient()"; + enableUdp(false); + enableTcp(false); +#ifdef USESQL + database db; + db.close(); +#else + QMap::iterator spot = allSpots.begin(); + while (spot != allSpots.end()) + { + delete spot.value(); // Stop memory leak? + spot = allSpots.erase(spot); + } +#endif +} + +void dxClusterClient::enableUdp(bool enable) +{ + udpEnable = enable; + if (enable) + { + if (udpSocket == Q_NULLPTR) + { + udpSocket = new QUdpSocket(this); + bool result = udpSocket->bind(QHostAddress::AnyIPv4, udpPort); + qInfo(logCluster()) << "Starting udpSocket() on:" << udpPort << "Result:" << result; + + connect(udpSocket, SIGNAL(readyRead()), this, SLOT(udpDataReceived()), Qt::QueuedConnection); + } + } + else { + if (udpSocket != Q_NULLPTR) + { + qInfo(logCluster()) << "Stopping udpSocket() on:" << udpPort; + + udpSocket->disconnect(); + delete udpSocket; + udpSocket = Q_NULLPTR; + } + } +} + +void dxClusterClient::enableTcp(bool enable) +{ + tcpEnable = enable; + if (enable) + { + tcpRegex = QRegularExpression("^DX de ([a-z-|A-Z|0-9|#|/]+):\\s+([0-9|.]+)\\s+([a-z|A-Z|0-9|/]+)+\\s+(.*)\\s+(\\d{4}Z)"); + + if (tcpSocket == Q_NULLPTR) + { + tcpSocket = new QTcpSocket(this); + tcpSocket->connectToHost(tcpServerName, tcpPort); + qInfo(logCluster()) << "Starting tcpSocket() on:" << tcpPort; + + connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(tcpDataReceived()), Qt::QueuedConnection); + connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(tcpDisconnected())); + + tcpCleanupTimer = new QTimer(this); + tcpCleanupTimer->setInterval(1000 * 10); // Run once a minute + connect(tcpCleanupTimer, SIGNAL(timeout()), this, SLOT(tcpCleanup())); + tcpCleanupTimer->start(); + authenticated = false; + } + } + else { + if (tcpSocket != Q_NULLPTR) + { + sendTcpData(QString("bye\n")); + qInfo(logCluster()) << "Disconnecting tcpSocket() on:" << tcpPort; + if (tcpCleanupTimer != Q_NULLPTR) + { + tcpCleanupTimer->stop(); + delete tcpCleanupTimer; + tcpCleanupTimer = Q_NULLPTR; + } + tcpSocket->disconnect(); + delete tcpSocket; + tcpSocket = Q_NULLPTR; + } + } +} + +void dxClusterClient::udpDataReceived() +{ + QHostAddress sender; + quint16 port; + QByteArray datagram; + datagram.resize(udpSocket->pendingDatagramSize()); + udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &port); + + if (udpSpotReader.setContent(datagram)) + { + QDomElement spot = udpSpotReader.firstChildElement("spot"); + if (spot.nodeName() == "spot") + { + // This is a spot? + QString action = spot.firstChildElement("action").text(); + if (action == "add") { + spotData* data = new spotData(); + data->dxcall = spot.firstChildElement("dxcall").text(); + data->frequency = spot.firstChildElement("frequency").text().toDouble() / 1000.0; + data->spottercall = spot.firstChildElement("spottercall").text(); + data->timestamp = QDateTime::fromString(spot.firstChildElement("timestamp").text(),"yyyy-MM-dd hh:mm:ss"); + data->mode = spot.firstChildElement("mode").text(); + data->comment = spot.firstChildElement("comment").text(); + +#ifdef USESQL + database db = database(); + db.query(QString("DELETE from spots where dxcall='%1'").arg(data->dxcall)); + QString query = QString("INSERT INTO spots(type,spottercall,frequency,dxcall,mode,comment,timestamp) VALUES('%1','%2',%3,'%4','%5','%6','%7')\n") + .arg("UDP").arg(data->spottercall).arg(data->frequency).arg(data->dxcall).arg(data->mode).arg(data->comment).arg(data->timestamp.toString("yyyy-MM-dd hh:mm:ss")); + db.query(query); +#else + bool found = false; + QMap::iterator spot = allSpots.find(data->dxcall); + while (spot != allSpots.end() && spot.key() == data->dxcall && spot.value()->frequency == data->frequency) { + found = true; + ++spot; + } + if (found == false) { + allSpots.insert(data->dxcall, data); + } +#endif + emit sendOutput(QString("add%1%2%3%4\n") + .arg(data->dxcall).arg(data->spottercall).arg(data->frequency).arg(data->comment)); + + } + else if (action == "delete") + { + QString dxcall = spot.firstChildElement("dxcall").text(); + double frequency = spot.firstChildElement("frequency").text().toDouble() / 1000.0; + +#ifdef USESQL + database db = database(); + QString query=QString("DELETE from spots where dxcall='%1' AND frequency=%2").arg(dxcall).arg(frequency); + db.query(query); + qInfo(logCluster()) << query; +#else + QMap::iterator spot = allSpots.find(dxcall); + while (spot != allSpots.end() && spot.key() == dxcall && spot.value()->frequency == frequency) + { + delete spot.value(); // Stop memory leak? + spot = allSpots.erase(spot); + } +#endif + emit sendOutput(QString("delete%1%3\n") + .arg(dxcall).arg(frequency)); + } + updateSpots(); + } + } +} + + +void dxClusterClient::tcpDataReceived() +{ + QString data = QString(tcpSocket->readAll()); + + emit sendOutput(data); + if (!authenticated) { + if (data.contains("login:") || data.contains("call:") || data.contains("callsign:")) { + sendTcpData(QString("%1\n").arg(tcpUserName)); + return; + } + if (data.contains("password:")) { + sendTcpData(QString("%1\n").arg(tcpPassword)); + return; + } + if (data.contains("Hello")) { + authenticated = true; + enableSkimmerSpots(skimmerSpots); + } + } + else { + QRegularExpressionMatchIterator i = tcpRegex.globalMatch(data); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + if (match.hasMatch()) { + spotData* data = new spotData(); + data->spottercall = match.captured(1); + data->frequency = match.captured(2).toDouble() / 1000.0; + data->dxcall = match.captured(3); + data->comment = match.captured(4).trimmed(); + data->timestamp = QDateTime::currentDateTimeUtc(); + +#ifdef USESQL + database db = database(); + db.query(QString("DELETE from spots where dxcall='%1'").arg(data->dxcall)); + QString query = QString("INSERT INTO spots(type,spottercall,frequency,dxcall,comment,timestamp) VALUES('%1','%2',%3,'%4','%5','%6')\n") + .arg("TCP").arg(data->spottercall).arg(data->frequency).arg(data->dxcall).arg(data->comment).arg(data->timestamp.toString("yyyy-MM-dd hh:mm:ss")); + db.query(query); +#else + bool found = false; + QMap::iterator spot = allSpots.find(data->dxcall); + while (spot != allSpots.end() && spot.key() == data->dxcall && spot.value()->frequency == data->frequency) { + found = true; + ++spot; + } + if (found == false) { + allSpots.insert(data->dxcall, data); + } +#endif + } + } + updateSpots(); + } +} + + +void dxClusterClient::sendTcpData(QString data) +{ + qInfo(logCluster()) << "Sending:" << data; + if (tcpSocket != Q_NULLPTR && tcpSocket->isValid() && tcpSocket->isOpen()) + { + tcpSocket->write(data.toLatin1()); + } + else + { + qInfo(logCluster()) << "socket not open!"; + } +} + +void dxClusterClient::tcpCleanup() +{ +#ifdef USESQL + database db = database(); + db.query(QString("DELETE FROM spots where timestamp < datetime('now', '-%1 minutes')").arg(tcpTimeout)); +#else + QMap::iterator spot = allSpots.begin();; + while (spot != allSpots.end()) { + if (spot.value()->timestamp.addSecs(tcpTimeout * 60) < QDateTime::currentDateTimeUtc()) + { + delete spot.value(); // Stop memory leak? + spot = allSpots.erase(spot); + } + else + { + ++spot; + } + } +#endif +} + +void dxClusterClient::tcpDisconnected() { + qWarning(logCluster()) << "TCP Cluster server disconnected..."; + // Need to start a timer and attempt reconnect. +} + +void dxClusterClient::freqRange(double low, double high) +{ + lowFreq = low; + highFreq = high; + //qInfo(logCluster) << "New range" << low << "-" << high; + updateSpots(); +} + +void dxClusterClient::updateSpots() +{ + QList spots; +#ifdef USESQL + // Set the required frequency range. + QString queryText = QString("SELECT * FROM spots WHERE frequency > %1 AND frequency < %2").arg(lowFreq).arg(highFreq); + //QString queryText = QString("SELECT * FROM spots"); + database db; + auto query = db.query(queryText); + + while (query.next()) { + // Step through all current spots within range + spotData s = spotData(); + s.dxcall = query.value(query.record().indexOf("dxcall")).toString(); + s.frequency = query.value(query.record().indexOf("frequency")).toDouble(); + spots.append(s); + } +#else + QMap::iterator spot = allSpots.begin();; + while (spot != allSpots.end()) { + if (spot.value()->frequency > lowFreq && spot.value()->frequency < highFreq) + { + spots.append(**spot); + } + ++spot; + } + +#endif + emit sendSpots(spots); +} + +void dxClusterClient::enableSkimmerSpots(bool enable) +{ + skimmerSpots = enable; + if (authenticated) { + if (skimmerSpots) { + sendTcpData(QString("Set Dx Filter Skimmer\n")); + } + else + { + sendTcpData(QString("Set Dx Filter Not Skimmer\n")); + } + + } +} diff --git a/cluster.h b/cluster.h new file mode 100644 index 0000000..9577a44 --- /dev/null +++ b/cluster.h @@ -0,0 +1,106 @@ +#ifndef CLUSTER_H +#define CLUSTER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USESQL +#include +#include +#endif + +#include + +#ifdef USESQL +#include "database.h" +#endif + +struct spotData { + QString dxcall; + double frequency; + QString spottercall; + QDateTime timestamp; + QString mode; + QString comment; + QCPItemText* text = Q_NULLPTR; + bool current = false; +}; + +struct clusterSettings { + QString server; + int port=7300; + QString userName; + QString password; + int timeout=30; + bool isdefault; +}; + +class dxClusterClient : public QObject +{ + Q_OBJECT + +public: + explicit dxClusterClient(QObject* parent = nullptr); + virtual ~dxClusterClient(); + +signals: + void addSpot(spotData* spot); + void deleteSpot(QString dxcall); + void deleteOldSpots(int minutes); + void sendOutput(QString text); + void sendSpots(QList spots); + +public slots: + void udpDataReceived(); + void tcpDataReceived(); + void tcpDisconnected(); + void enableUdp(bool enable); + void enableTcp(bool enable); + void setUdpPort(int p) { udpPort = p; } + void setTcpServerName(QString s) { tcpServerName = s; } + void setTcpPort(int p) { tcpPort = p; } + void setTcpUserName(QString s) { tcpUserName = s; } + void setTcpPassword(QString s) { tcpPassword = s; } + void setTcpTimeout(int p) { tcpTimeout = p; } + void tcpCleanup(); + void freqRange(double low, double high); + void enableSkimmerSpots(bool enable); + +private: + void sendTcpData(QString data); + bool databaseOpen(); + void updateSpots(); + + bool udpEnable; + bool tcpEnable; + QUdpSocket* udpSocket=Q_NULLPTR; + QTcpSocket* tcpSocket=Q_NULLPTR; + int udpPort; + QString tcpServerName; + int tcpPort; + QString tcpUserName; + QString tcpPassword; + int tcpTimeout; + QDomDocument udpSpotReader; + QRegularExpression tcpRegex; + QMutex mutex; + bool authenticated=false; + QTimer* tcpCleanupTimer=Q_NULLPTR; +#ifdef USESQL + QSqlDatabase db; +#endif + double lowFreq; + double highFreq; + QMap allSpots; + bool skimmerSpots = false; +}; + +#endif diff --git a/colorprefs.h b/colorprefs.h new file mode 100644 index 0000000..7e8ddd6 --- /dev/null +++ b/colorprefs.h @@ -0,0 +1,43 @@ +#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; + QColor passband; + + // Waterfall: + QColor wfBackground; + QColor wfGrid; + QColor wfAxis; + QColor wfText; + + // Meters: + QColor meterLevel; + QColor meterAverage; + QColor meterPeakLevel; + QColor meterPeakScale; + QColor meterLowerLine; + QColor meterLowText; + + // Assorted + QColor clusterSpots; + +}; + + +#endif // COLORPREFS_H diff --git a/database.cpp b/database.cpp new file mode 100644 index 0000000..178399b --- /dev/null +++ b/database.cpp @@ -0,0 +1,107 @@ +#ifdef USESQL + +#include "database.h" +#include "logcategories.h" + +database::database() +{ + open(); +} + +database::~database() +{ +} + +bool database::open() +{ + auto name = "my_db_" + QString::number((quint64)QThread::currentThread(), 16); + if (QSqlDatabase::contains(name)) + { + db = QSqlDatabase::database(name); + qu = QSqlQuery(db); + return true; + } + else { + qInfo(logCluster()) << "Creating new connection" << name; + db = QSqlDatabase::addDatabase("QSQLITE", name); + qu = QSqlQuery(db); + } + + //QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + "wfview.db"; + QString path = ":memory:"; + qInfo(logCluster()) << "DB Filename" << path; + db.setDatabaseName(path); + if (db.isValid()) + { + db.open(); + if (check()) { + return true; + } + } + qWarning(logCluster()) << "Database is not valid!"; + return false; +} + +void database::close() +{ + auto name = "my_db_" + QString::number((quint64)QThread::currentThread(), 16); + qInfo(logCluster()) << "Closing database connection:" << name; + db.close(); +} + + + +QSqlQuery database::query(QString query) +{ + if (!db.isOpen()) + { + qWarning(logCluster()) << "Query Database is not open!"; + db.open(); + } + qu.exec(query); + return qu; +} + + + +bool database::check() +{ + if (db.isOpen()) { + for (const auto& table : db.tables()) + { + if (table == "spots") + { + qInfo(logCluster()) << "DB Contains spots table"; + return true; + } + } + qInfo(logCluster()) << "Creating spots table"; + // Spots table does not exist, need to create it. + /* + QString dxcall; + double frequency; + QString spottercall; + QDateTime timestamp; + QString mode; + QString comment; + QCPItemText* text = Q_NULLPTR;*/ + qu.exec("CREATE TABLE spots " + "(id INTEGER PRIMARY KEY, " + "type VARCHAR(3)," + "dxcall VARCHAR(30)," + "spottercall VARCHAR(30)," + "frequency DOUBLE," + "timestamp DATETIME," + "mode VARCHAR(30)," + "comment VARCHAR(255) )"); + qu.exec("CREATE INDEX spots_index ON spots(type,dxcall,frequency,timestamp)"); + return true; + } + else { + qWarning(logCluster()) << "Database is not open"; + + } + return false; +} + +#endif \ No newline at end of file diff --git a/database.h b/database.h new file mode 100644 index 0000000..7e80e9e --- /dev/null +++ b/database.h @@ -0,0 +1,36 @@ +#ifdef USESQL + +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +class database +{ +public: + explicit database(); + virtual ~database(); + bool open(); + void close(); + QSqlQuery query(QString query); + +signals: + +public slots: +private: + bool check(); + QSqlDatabase db; + QSqlQuery qu; + +}; + +#endif + +#endif diff --git a/freqmemory.h b/freqmemory.h index 96d3c6b..0067158 100644 --- a/freqmemory.h +++ b/freqmemory.h @@ -17,6 +17,8 @@ enum mode_kind { modeFM=0x05, modeCW_R=0x07, modeRTTY_R=0x08, + modePSK = 0x12, + modePSK_R = 0x13, modeLSB_D=0x80, modeUSB_D=0x81, modeDV=0x17, @@ -29,9 +31,7 @@ enum mode_kind { modedPMR, modeNXDN_VN, modeNXDN_N, - modeDCR, - modePSK, - modePSK_R + modeDCR }; struct mode_info { diff --git a/logcategories.cpp b/logcategories.cpp index da4c6d7..88c854e 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") @@ -10,3 +12,4 @@ Q_LOGGING_CATEGORY(logUdpServer, "udp.server") Q_LOGGING_CATEGORY(logRigCtlD, "rigctld") Q_LOGGING_CATEGORY(logTcpServer, "tcpserver") Q_LOGGING_CATEGORY(logAudioConverter, "audioconverter") +Q_LOGGING_CATEGORY(logCluster, "cluster") diff --git a/logcategories.h b/logcategories.h index 8c088a7..7054c98 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) @@ -13,6 +15,7 @@ Q_DECLARE_LOGGING_CATEGORY(logUdpServer) Q_DECLARE_LOGGING_CATEGORY(logRigCtlD) Q_DECLARE_LOGGING_CATEGORY(logTcpServer) Q_DECLARE_LOGGING_CATEGORY(logAudioConverter) +Q_DECLARE_LOGGING_CATEGORY(logCluster) #if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__) diff --git a/loggingwindow.cpp b/loggingwindow.cpp new file mode 100644 index 0000000..e3667df --- /dev/null +++ b/loggingwindow.cpp @@ -0,0 +1,211 @@ +#include "loggingwindow.h" +#include "ui_loggingwindow.h" + +loggingWindow::loggingWindow(QString logFilename, QWidget *parent) : + QWidget(parent), + ui(new Ui::loggingWindow), + logFilename(logFilename) +{ + 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); + + QDir d = QFileInfo(logFilename).absoluteDir(); + logDirectory = d.absolutePath(); + + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + ui->logTextDisplay->setFont(font); + ui->userAnnotationText->setFont(font); + + 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; + qInfo(logLogger()) << "This address already copied to the clipboard. Please paste this URL in to your support questions."; + 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 https://termbin.com/ypxbthis 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; + ui->sendToPasteBtn->setDisabled(false); + 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; + bool rtn = false; + QStringList arg; + const QFileInfo dir(logDirectory); + +#ifdef Q_OS_LINUX + cmd = "xdg-open"; +#elif defined(Q_OS_WIN) + cmd = QStandardPaths::findExecutable("explorer.exe"); + if (!dir.isDir()) + arg += QLatin1String("/select,"); +#else + cmd = "open"; +#endif + arg += QDir::toNativeSeparators(dir.canonicalFilePath());; + rtn = QProcess::startDetached(cmd, arg); + if(!rtn) + qInfo(logLogger()) << "Error, open log directory" << logDirectory << "command failed"; +} + +void loggingWindow::on_openLogFileBtn_clicked() +{ + QString cmd; + bool rtn = false; +#ifdef Q_OS_LINUX + cmd = "xdg-open"; +#elif defined(Q_OS_WIN) + cmd = QStandardPaths::findExecutable("notepad.exe"); +#else + cmd = "open"; +#endif + rtn = QProcess::startDetached(cmd, { logFilename }); + if(!rtn) + qInfo(logLogger()) << "Error, open log file command failed"; +} + +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..d453eb7 --- /dev/null +++ b/loggingwindow.h @@ -0,0 +1,75 @@ +#ifndef LOGGINGWINDOW_H +#define LOGGINGWINDOW_H + +#include +#include +#include +#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(QString logFilename, QWidget *parent = 0); + ~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; + QString logFilename; + QString logDirectory; + QClipboard *clipboard; + QMessageBox URLmsgBox; + QScrollBar *vertLogScroll; + QScrollBar *horizLogScroll; + QMutex textMutex; + 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 8e945ae..f0dcf72 100644 --- a/main.cpp +++ b/main.cpp @@ -12,13 +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 m_logFile; QMutex logMutex; +#endif + bool debugMode=false; #ifdef BUILD_WFSERVER @@ -42,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[]) { @@ -65,22 +68,18 @@ int main(int argc, char *argv[]) #endif #ifdef QT_DEBUG - debugMode = true; + //debugMode = true; #endif - QString serialPortCL; - QString hostCL; - QString civCL; -#ifdef Q_OS_MAC - QString logFilename= QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log"; -#else - QString logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log"; -#endif + QDateTime date = QDateTime::currentDateTime(); + QString formattedTime = date.toString("dd.MM.yyyy hh:mm:ss"); + QString logFilename = (QString("%1/%2-%3.log").arg(QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0]).arg(a.applicationName()).arg(date.toString("yyyyMMddhhmmss"))); + QString settingsFile = NULL; QString currentArg; - const QString helpText = QString("\nUsage: -p --port /dev/port, -h --host remotehostname, -c --civ 0xAddr, -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO... + const QString helpText = QString("\nUsage: -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO... #ifdef BUILD_WFSERVER const QString version = QString("wfserver version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n") .arg(QString(WFVIEW_VERSION)) @@ -99,34 +98,10 @@ int main(int argc, char *argv[]) //qInfo() << "Argc: " << c << " argument: " << argv[c]; currentArg = QString(argv[c]); - if ((currentArg == "-p") || (currentArg == "--port")) - { - if (argc > c) - { - serialPortCL = argv[c + 1]; - c += 1; - } - } - else if ((currentArg == "-d") || (currentArg == "--debug")) + if ((currentArg == "-d") || (currentArg == "--debug")) { debugMode = true; } - else if ((currentArg == "-h") || (currentArg == "--host")) - { - if(argc > c) - { - hostCL = argv[c+1]; - c+=1; - } - } - else if ((currentArg == "-c") || (currentArg == "--civ")) - { - if (argc > c) - { - civCL = argv[c + 1]; - c += 1; - } - } else if ((currentArg == "-l") || (currentArg == "--logfile")) { if (argc > c) @@ -160,6 +135,8 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER + // Set the logging file before doing anything else. m_logFile.reset(new QFile(logFilename)); // Open the file logging @@ -168,10 +145,8 @@ int main(int argc, char *argv[]) qInstallMessageHandler(messageHandler); qInfo(logSystem()) << version; - qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL); - qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL); - qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL); +#endif #ifdef BUILD_WFSERVER #ifdef Q_OS_WIN SetConsoleCtrlHandler((PHANDLER_ROUTINE)cleanup, TRUE); @@ -180,10 +155,10 @@ int main(int argc, char *argv[]) signal(SIGTERM, cleanup); signal(SIGKILL, cleanup); #endif - w = new servermain(serialPortCL, hostCL, settingsFile); + w = new servermain(settingsFile, logFilename); #else a.setWheelScrollLines(1); // one line per wheel click - wfmain w(serialPortCL, hostCL, settingsFile); + wfmain w(settingsFile, logFilename, debugMode); w.show(); #endif @@ -191,6 +166,7 @@ int main(int argc, char *argv[]) } +#ifdef BUILD_WFSERVER void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) { @@ -201,6 +177,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } 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 "); @@ -226,8 +203,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } // Write to the output category of the message and the message itself out << context.category << ": " << msg << "\n"; -#ifdef BUILD_WFSERVER std::cout << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ").toLocal8Bit().toStdString() << msg.toLocal8Bit().toStdString() << "\n"; -#endif out.flush(); // Clear the buffered data } +#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 4fbba0b..cfcbc65 100644 --- a/pahandler.cpp +++ b/pahandler.cpp @@ -238,20 +238,23 @@ 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) { packet.volume = volume; - emit sendToConverter(packet); + if (Pa_IsStreamActive(audio) == 1) { + emit sendToConverter(packet); + } + else + { + Pa_StartStream(audio); + } return; } @@ -293,17 +296,24 @@ void paHandler::convertedOutput(audioPacket packet) { if (packet.data.size() > 0) { if (Pa_IsStreamActive(audio) == 1) { - PaError err = Pa_WriteStream(audio, (char*)packet.data.data(), packet.data.size() / sizeof(float) / outFormat.channelCount()); + if (currentLatency < (setup.latency+latencyAllowance)) { + PaError err = Pa_WriteStream(audio, (char*)packet.data.data(), packet.data.size() / sizeof(float) / outFormat.channelCount()); - if (err != paNoError) { - qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!"; + if (err != paNoError) { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Error writing audio!"; + } + } + else { + qDebug(logAudio()) << (setup.isinput ? "Input" : "Output") << "Disgarding audio data as current latency" << currentLatency << "exceeds setup latency" << setup.latency; + Pa_StopStream(audio); + latencyAllowance++; } const PaStreamInfo* info = Pa_GetStreamInfo(audio); currentLatency = packet.time.msecsTo(QTime::currentTime()) + (info->outputLatency * 1000); } - amplitude = packet.amplitude; - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + amplitude = packet.amplitudePeak; + emit haveLevels(getAmplitude(), static_cast(packet.amplitudeRMS * 255.0), setup.latency, currentLatency, isUnderrun, isOverrun); } } @@ -313,10 +323,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); } } @@ -325,6 +335,8 @@ void paHandler::convertedInput(audioPacket packet) void paHandler::changeLatency(const quint16 newSize) { qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "Changing latency to: " << newSize << " from " << setup.latency; + setup.latency = newSize; + latencyAllowance = 0; } int paHandler::getLatency() diff --git a/pahandler.h b/pahandler.h index 8349a6c..a6fe767 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, codecType codecIn, QAudioFormat out, codecType codecOut, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); @@ -91,6 +91,7 @@ private: QThread* converterThread = Q_NULLPTR; bool isUnderrun = false; bool isOverrun = false; + int latencyAllowance = 0; }; #endif // PAHANDLER_H diff --git a/prefs.h b/prefs.h new file mode 100644 index 0000000..4309e33 --- /dev/null +++ b/prefs.h @@ -0,0 +1,53 @@ +#ifndef PREFS_H +#define PREFS_H + +#include + +#include "wfviewtypes.h" + +struct preferences { + QString version; + bool useFullScreen; + bool useSystemTheme; + bool drawPeaks; + underlay_t underlayMode = underlayNone; + int underlayBufferSize = 64; + bool wfAntiAlias; + bool wfInterpolate; + QString stylesheetPath; + unsigned char radioCIVAddr; + bool CIVisRadioModel; + bool forceRTSasPTT; + QString serialPortRadio; + quint32 serialPortBaud; + int polling_ms; + bool enablePTT; + bool niceTS; + bool enableLAN; + bool enableRigCtlD; + quint16 rigCtlPort; + 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; + bool clusterUdpEnable; + bool clusterTcpEnable; + int clusterUdpPort; + QString clusterTcpServerName; + QString clusterTcpUserName; + QString clusterTcpPassword; + int clusterTimeout; + bool clickDragTuningEnable; + bool clusterSkimmerSpotsEnable; +}; + +#endif // PREFS_H diff --git a/qledlabel.cpp b/qledlabel.cpp index 640b1c4..4911dde 100644 --- a/qledlabel.cpp +++ b/qledlabel.cpp @@ -6,18 +6,20 @@ 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); } void QLedLabel::setState(State state) { - qInfo() << "setState" << state; + qDebug() << "LED: setState" << state; switch (state) { case StateOk: setStyleSheet(greenSS); @@ -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 c0d3ea6..444d08b 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; @@ -753,11 +760,11 @@ void rigCommander::setMode(mode_info m) { QByteArray payload; - if(rigCaps.model==model706) + if (rigCaps.model == model706) { m.filter = '\x01'; } - if(m.mk == modeWFM) + if (m.mk == modeWFM) { m.filter = '\x01'; } @@ -865,6 +872,49 @@ void rigCommander::getDuplexMode() prepDataAndSend(payload); } +void rigCommander::setPassband(quint16 pass) +{ + QByteArray payload; + payload.setRawData("\x1A\x03", 2); + + unsigned char calc; + /* + Mode Data Steps + SSB/CW/RTTY/PSK 0 to 9 50 ~ 500 Hz (50 Hz) + SSB/CW/PSK 10 to 40 600 Hz ~ 3.6 kHz (100 Hz) + RTTY 10 to 31 600 ~ 2.7 kHz (100 Hz) + AM 0 to 49 200 Hz ~ 10.0 kHz (200 Hz) + */ + if (state.getChar(MODE) == modeAM) { // AM 0-49 + + calc = quint16((pass / 200) - 1); + } + else if (pass >= 600 || pass <=3600) // SSB/CW/PSK 10-40 (10-31 for RTTY) + { + calc = quint16((pass / 100) + 4); + } + else { // SSB etc 0-9 + calc = quint16((pass / 50) - 1); + } + + qDebug() << "Setting rig passband" << pass << "Sending" << calc; + + char tens = (calc / 10); + char units = (calc - (10 * tens)); + + char b1 = (units) | (tens << 4); + + payload.append(b1); + prepDataAndSend(payload); +} + +void rigCommander::getPassband() +{ + QByteArray payload; + payload.setRawData("\x1A\x03", 2); + prepDataAndSend(payload); +} + void rigCommander::getTransmitFrequency() { QByteArray payload; @@ -1434,13 +1484,16 @@ void rigCommander::parseLevels() emit haveTPBFInner(level); else emit haveIFShift(level); + state.set(PBTIN, level, false); break; case '\x08': // Twin BPF Outer emit haveTPBFOuter(level); + state.set(PBTOUT, level, false); break; case '\x09': // CW Pitch - ignore for now + state.set(CWPITCH, level, false); break; case '\x0A': // TX RF level @@ -1454,9 +1507,11 @@ void rigCommander::parseLevels() break; case '\x0C': // CW Keying Speed - ignore for now + state.set(KEYSPD, level, false); break; case '\x0D': // Notch filder setting - ignore for now + state.set(NOTCHF, level, false); break; case '\x0E': // compressor level @@ -1465,6 +1520,7 @@ void rigCommander::parseLevels() break; case '\x12': // NB level - ignore for now + state.set(NB, level, false); break; case '\x15': // monitor level @@ -2498,16 +2554,41 @@ void rigCommander::parseRegisters1A() switch(payloadIn[01]) { case '\x00': + { // Memory contents break; + } case '\x01': + { // band stacking register parseBandStackReg(); break; + } + case '\x03': + { + quint16 calc; + quint8 pass = bcdHexToUChar((quint8)payloadIn[2]); + if (state.getChar(MODE) == modeAM) { + calc = 200 + (pass * 200); + } + else if (pass <= 10) + { + calc = 50 + (pass * 50); + } + else { + calc = 600 + ((pass - 10) * 100); + } + emit havePassband(calc); + state.set(PASSBAND, calc, false); + break; + } case '\x04': + { state.set(AGC, (quint8)payloadIn[2], false); break; + } case '\x06': + { // data mode // emit havedataMode( (bool) payloadIn[somebit]) // index @@ -2519,13 +2600,20 @@ void rigCommander::parseRegisters1A() emit haveDataMode((bool)payloadIn[03]); state.set(DATAMODE, (quint8)payloadIn[3], false); break; + } case '\x07': + { // IP+ status break; + } case '\x09': + { state.set(MUTEFUNC, (quint8)payloadIn[2], false); + } default: + { break; + } } } @@ -3006,12 +3094,35 @@ void rigCommander::determineRigCaps() std::vector standardHF; std::vector standardVU; - // Most commonly supported "HF" bands: - standardHF = {band6m, band10m, band10m, band12m, - band15m, band17m, band20m, band30m, - band40m, band60m, band80m, band160m}; + bandType bandDef6m = bandType(band6m, 50000000, 54000000, modeUSB); + bandType bandDef10m = bandType(band10m, 28000000, 29700000, modeUSB); + bandType bandDef12m = bandType(band12m, 24890000, 24990000, modeUSB); + bandType bandDef15m = bandType(band15m, 21000000, 21450000, modeUSB); + bandType bandDef17m = bandType(band17m, 18068000, 18168000, modeUSB); + bandType bandDef20m = bandType(band20m, 14000000, 14350000, modeUSB); + bandType bandDef30m = bandType(band30m, 10100000, 10150000, modeLSB); + bandType bandDef40m = bandType(band40m, 7000000, 7300000, modeLSB); + bandType bandDef60m = bandType(band60m, 5250000, 5450000, modeLSB); + bandType bandDef80m = bandType(band80m, 3500000, 4000000, modeLSB); + bandType bandDef160m = bandType(band160m, 1800000, 2000000, modeLSB); + bandType bandDef630m = bandType(band630m, 493000, 595000, modeLSB); + bandType bandDef2200m = bandType(band2200m, 135000, 138000, modeLSB); + bandType bandDef2m = bandType(band2m, 144000000, 148000000, modeUSB); + bandType bandDef4m = bandType(band4m, 70000000, 70500000, modeUSB); + bandType bandDef70cm = bandType(band70cm, 420000000, 450000000, modeUSB); + bandType bandDef23cm = bandType(band23cm, 1240000000, 1400000000, modeUSB); + + bandType bandDefAir(bandAir, 108000000, 137000000, modeAM); + bandType bandDefWFM(bandWFM, 88000000, 108000000, modeWFM); + bandType bandDefGen(bandGen, 10000, 30000000, modeAM); + + + standardHF = { bandDef6m, bandDef10m, bandDef12m, bandDef15m, bandDef17m, + bandDef20m, bandDef30m, bandDef40m, bandDef60m, bandDef80m, bandDef80m}; + + standardVU = { bandDef2m, bandDef70cm }; + - standardVU = {band70cm, band2m}; std::vector commonModes; commonModes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"), @@ -3112,10 +3223,7 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x01'); rigCaps.preamps.push_back('\x02'); rigCaps.bands = standardHF; - rigCaps.bands.push_back(band4m); - rigCaps.bands.push_back(bandGen); - rigCaps.bands.push_back(band630m); - rigCaps.bands.push_back(band2200m); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDef4m, bandDef630m, bandDef2200m, bandDefGen }); rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x71"); @@ -3146,7 +3254,7 @@ void rigCommander::determineRigCaps() rigCaps.antennas = {0x00, 0x01, 0x02}; rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.insert(rigCaps.bands.end(), {band23cm, band4m, band630m, band2200m, bandGen}); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDef23cm, bandDef4m, bandDef630m, bandDef2200m, bandDefGen }); rigCaps.modes = commonModes; rigCaps.modes.insert(rigCaps.modes.end(), { createMode(modeWFM, 0x06, "WFM"), createMode(modeS_AMD, 0x11, "S-AM (D)"), @@ -3178,7 +3286,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.push_back('\x10'); rigCaps.preamps.push_back('\x01'); rigCaps.bands = standardVU; - rigCaps.bands.push_back(band23cm); + rigCaps.bands.push_back(bandDef23cm); rigCaps.bsr[band23cm] = 0x03; rigCaps.bsr[band70cm] = 0x02; rigCaps.bsr[band2m] = 0x01; @@ -3203,7 +3311,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x10' , '\x20', '\x30'}); rigCaps.preamps.push_back('\x01'); rigCaps.bands = standardVU; - rigCaps.bands.push_back(band23cm); + rigCaps.bands.push_back(bandDef23cm); rigCaps.bsr[band23cm] = 0x03; rigCaps.bsr[band70cm] = 0x02; rigCaps.bsr[band2m] = 0x01; @@ -3229,11 +3337,11 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x02'); rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; - rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"), - createMode(modePSK_R, 0x13, "PSK-R")}); + rigCaps.modes.insert(rigCaps.modes.end(), { createMode(modePSK, 0x12, "PSK"), + createMode(modePSK_R, 0x13, "PSK-R") }); rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x97"); break; case model7610: @@ -3262,10 +3370,10 @@ void rigCommander::determineRigCaps() rigCaps.antennas = {0x00, 0x01}; rigCaps.hasATU = true; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); - rigCaps.bands.push_back(band630m); - rigCaps.bands.push_back(band2200m); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDef630m, bandDef2200m, bandDefGen }); rigCaps.modes = commonModes; + rigCaps.modes.insert(rigCaps.modes.end(), { createMode(modePSK, 0x12, "PSK"), + createMode(modePSK_R, 0x13, "PSK-R") }); rigCaps.hasRXAntenna = true; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x01\x12"); break; @@ -3294,9 +3402,7 @@ void rigCommander::determineRigCaps() rigCaps.hasAntennaSel = true; rigCaps.antennas = {0x00, 0x01, 0x02, 0x03}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); - rigCaps.bands.push_back(band630m); - rigCaps.bands.push_back(band2200m); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDef630m, bandDef2200m, bandDefGen }); rigCaps.modes = commonModes; rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"), createMode(modePSK_R, 0x13, "PSK-R")}); @@ -3326,16 +3432,12 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x02'); rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.push_back(bandGen); - rigCaps.bands.push_back(bandAir); - rigCaps.bands.push_back(bandWFM); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDefAir, bandDefGen, bandDefWFM, bandDef630m, bandDef2200m }); rigCaps.bsr[band70cm] = 0x14; rigCaps.bsr[band2m] = 0x13; rigCaps.bsr[bandAir] = 0x12; rigCaps.bsr[bandWFM] = 0x11; rigCaps.bsr[bandGen] = 0x15; - rigCaps.bands.push_back(band630m); - rigCaps.bands.push_back(band2200m); rigCaps.modes = commonModes; rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modeWFM, 0x06, "WFM"), createMode(modeDV, 0x17, "DV")}); @@ -3358,7 +3460,7 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x01'); rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[band2m] = 0x11; rigCaps.bsr[band70cm] = 0x12; rigCaps.bsr[bandGen] = 0x13; @@ -3383,7 +3485,7 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x02'); rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x40"); @@ -3407,8 +3509,7 @@ void rigCommander::determineRigCaps() rigCaps.preamps.push_back('\x02'); rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.push_back(band4m); - rigCaps.bands.push_back(bandGen); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDef4m, bandDefGen}); rigCaps.bsr[band2m] = 0x11; rigCaps.bsr[band70cm] = 0x12; rigCaps.bsr[bandGen] = 0x13; @@ -3434,7 +3535,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.push_back('\x20'); rigCaps.preamps.push_back('\x01'); rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x03\x48"); @@ -3459,14 +3560,33 @@ void rigCommander::determineRigCaps() rigCaps.antennas = {0x00, 0x01, 0x02, 0x03}; // not sure if 0x03 works rigCaps.hasATU = true; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); - rigCaps.bands.push_back(band630m); - rigCaps.bands.push_back(band2200m); + rigCaps.bands.insert(rigCaps.bands.end(), { bandDefGen, bandDef630m, bandDef2200m }); rigCaps.modes = commonModes; rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"), createMode(modePSK_R, 0x13, "PSK-R")}); rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x95"); break; + case model703: + rigCaps.modelName = QString("IC-703"); + rigCaps.rigctlModel = 3055; + rigCaps.hasSpectrum = false; + rigCaps.inputs.clear(); + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + rigCaps.hasFDcomms = false; + rigCaps.hasATU = true; + rigCaps.hasPTTCommand = false; + rigCaps.useRTSforPTT = true; + rigCaps.hasDataModes = false; + rigCaps.attenuators.push_back('\x20'); + rigCaps.bands = standardHF; + rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); + rigCaps.bands.push_back(bandDefGen); + rigCaps.modes = commonModes; + rigCaps.modes.insert(rigCaps.modes.end(), createMode(modeWFM, 0x06, "WFM")); + rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); + break; case model706: rigCaps.modelName = QString("IC-706"); rigCaps.rigctlModel = 3009; @@ -3483,7 +3603,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.push_back('\x20'); rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.modes = commonModes; rigCaps.modes.insert(rigCaps.modes.end(), createMode(modeWFM, 0x06, "WFM")); rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); @@ -3504,9 +3624,9 @@ void rigCommander::determineRigCaps() rigCaps.hasDataModes = false; rigCaps.attenuators.push_back('\x20'); rigCaps.preamps.push_back('\x01'); - rigCaps.bands = {band10m, band10m, band12m, - band15m, band17m, band20m, band30m, - band40m, band60m, band80m, band160m, bandGen}; + rigCaps.bands = {bandDef10m, bandDef10m, bandDef12m, + bandDef15m, bandDef17m, bandDef20m, bandDef30m, + bandDef40m, bandDef60m, bandDef80m, bandDef160m, bandDefGen}; rigCaps.modes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"), createMode(modeAM, 0x02, "AM"), createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"), @@ -3527,7 +3647,48 @@ void rigCommander::determineRigCaps() rigCaps.hasPTTCommand = false; rigCaps.useRTSforPTT = true; rigCaps.hasDataModes = false; - rigCaps.hasIFShift = true; // untested + rigCaps.attenuators.push_back('\x20'); + rigCaps.preamps.push_back('\x01'); + rigCaps.bands = standardHF; + rigCaps.modes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"), + createMode(modeAM, 0x02, "AM"), createMode(modeFM, 0x05, "FM"), + createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"), + }; + break; + case model737: + rigCaps.modelName = QString("IC-737"); + rigCaps.rigctlModel = 3021; + rigCaps.hasSpectrum = false; + rigCaps.inputs.clear(); + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + rigCaps.hasFDcomms = false; + rigCaps.hasATU = false; + rigCaps.hasPTTCommand = false; + rigCaps.useRTSforPTT = true; + rigCaps.hasDataModes = false; + rigCaps.attenuators.push_back('\x20'); + rigCaps.preamps.push_back('\x01'); + rigCaps.bands = standardHF; + rigCaps.modes = { createMode(modeLSB, 0x00, "LSB"), createMode(modeUSB, 0x01, "USB"), + createMode(modeAM, 0x02, "AM"), createMode(modeFM, 0x05, "FM"), + createMode(modeCW, 0x03, "CW"), createMode(modeCW_R, 0x07, "CW-R"), + }; + break; + case model738: + rigCaps.modelName = QString("IC-738"); + rigCaps.rigctlModel = 3022; + rigCaps.hasSpectrum = false; + rigCaps.inputs.clear(); + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + rigCaps.hasFDcomms = false; + rigCaps.hasATU = false; + rigCaps.hasPTTCommand = false; + rigCaps.useRTSforPTT = true; + rigCaps.hasDataModes = false; rigCaps.attenuators.push_back('\x20'); rigCaps.preamps.push_back('\x01'); rigCaps.bands = standardHF; @@ -3558,12 +3719,32 @@ void rigCommander::determineRigCaps() // 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.bands.push_back(bandDef2m); + rigCaps.bands.push_back(bandDefGen); + rigCaps.modes = commonModes; + rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); + break; + case model756: + rigCaps.modelName = QString("IC-756"); + rigCaps.rigctlModel = 3026; + 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.preamps.push_back('\x01'); + rigCaps.preamps.push_back('\x02'); + rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'}); + rigCaps.antennas = {0x00, 0x01}; + rigCaps.bands = standardHF; + rigCaps.bands.push_back(bandDefGen); + rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); break; - case model756pro: rigCaps.modelName = QString("IC-756 Pro"); rigCaps.rigctlModel = 3027; @@ -3580,7 +3761,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'}); rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); @@ -3601,7 +3782,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'}); rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); @@ -3622,7 +3803,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.insert(rigCaps.attenuators.end(),{ '\x06' , '\x12', '\x18'}); rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[bandGen] = 0x11; rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); @@ -3646,8 +3827,8 @@ void rigCommander::determineRigCaps() rigCaps.antennas = {0x00, 0x01}; rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.push_back(band23cm); - rigCaps.bands.push_back(bandGen); + rigCaps.bands.push_back(bandDef23cm); + rigCaps.bands.push_back(bandDefGen); rigCaps.bsr[band2m] = 0x11; rigCaps.bsr[band70cm] = 0x12; rigCaps.bsr[band23cm] = 0x13; @@ -3673,7 +3854,7 @@ void rigCommander::determineRigCaps() rigCaps.attenuators.push_back('\x20'); rigCaps.bands = standardHF; rigCaps.bands.insert(rigCaps.bands.end(), standardVU.begin(), standardVU.end()); - rigCaps.bands.insert(rigCaps.bands.end(), {band23cm, band4m, band630m, band2200m, bandGen}); + rigCaps.bands.insert(rigCaps.bands.end(), {bandDef23cm, bandDef4m, bandDef630m, bandDef2200m, bandDefGen}); rigCaps.modes = commonModes; rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x00"); qInfo(logRig()) << "Found unknown rig: 0x" << QString("%1").arg(rigCaps.modelID, 2, 16); @@ -3764,9 +3945,16 @@ void rigCommander::parseSpectrum() freqt fStart; freqt fEnd; + unsigned char vfo = bcdHexToUChar(payloadIn[02]); unsigned char sequence = bcdHexToUChar(payloadIn[03]); + //unsigned char sequenceMax = bcdHexToDecimal(payloadIn[04]); + if (vfo == 1) + { + // This is for the second VFO! + return; + } // unsigned char waveInfo = payloadIn[06]; // really just one byte? //qInfo(logRig()) << "Spectrum Data received: " << sequence << "/" << sequenceMax << " mode: " << scopeMode << " waveInfo: " << waveInfo << " length: " << payloadIn.length(); @@ -4058,9 +4246,88 @@ void rigCommander::parseMode() } else { filter = 0; } - emit haveMode((unsigned char)payloadIn[01], filter); - state.set(MODE,(unsigned char)payloadIn[01],false); - state.set(FILTER,filter,false); + unsigned char mode = (unsigned char)payloadIn[01]; + emit haveMode(mode, filter); + state.set(MODE,mode,false); + state.set(FILTER, filter, false); + quint16 pass = 0; + + if (!state.isValid(PASSBAND)) { + + /* We haven't got a valid passband from the rig so we + need to create a 'fake' one from default values + This will be replaced with a valid one if we get it */ + + if (mode == 3 || mode == 7 || mode == 12 || mode == 17) { + switch (filter) { + case 1: + pass=1200; + break; + case 2: + pass=500; + break; + case 3: + pass=250; + break; + } + } + else if (mode == 4 || mode == 8) + { + switch (filter) { + case 1: + pass=2400; + break; + case 2: + pass=500; + break; + case 3: + pass=250; + break; + } + } + else if (mode == 2) + { + switch (filter) { + case 1: + pass=9000; + break; + case 2: + pass=6000; + break; + case 3: + pass=3000; + break; + } + } + else if (mode == 5) + { + switch (filter) { + case 1: + pass=15000; + break; + case 2: + pass=10000; + break; + case 3: + pass=7000; + break; + } + } + else { // SSB or unknown mode + switch (filter) { + case 1: + pass=3000; + break; + case 2: + pass=2400; + break; + case 3: + pass=1800; + break; + } + } + } + state.set(PASSBAND, pass, false); } @@ -4286,7 +4553,7 @@ void rigCommander::setRigID(unsigned char rigID) // It can be used for radios without Rig ID commands, // or to force a specific radio model - qInfo(logRig()) << "Sending rig ID to: (int)" << (int)rigID; + qInfo(logRig()) << "Setting rig ID to: (int)" << (int)rigID; lookingForRig = true; @@ -4407,11 +4674,17 @@ void rigCommander::stateUpdated() break; case MODE: case FILTER: - if (i.value()._valid) { + if (state.isValid(MODE) && state.isValid(FILTER)) { setMode(state.getChar(MODE), state.getChar(FILTER)); } getMode(); break; + case PASSBAND: + if (i.value()._valid && state.isValid(MODE)) { + setPassband(state.getUInt16(PASSBAND)); + } + getPassband(); + break; case DUPLEX: if (i.value()._valid) { setDuplexMode(state.getDuplex(DUPLEX)); @@ -4670,13 +4943,70 @@ void rigCommander::stateUpdated() break; case SATMODEFUNC: break; - case NBLEVEL: - break; case NBDEPTH: break; case NBWIDTH: break; - case NRLEVEL: + case NB: + break; + case NR: { + if (i.value()._valid) { + QByteArray payload("\x14\x06"); + payload.append(bcdEncodeInt(state.getChar(NR))); + prepDataAndSend(payload); + } + break; + } + case PBTIN: { + if (i.value()._valid) { + QByteArray payload("\x14\x07"); + payload.append(bcdEncodeInt(state.getChar(PBTIN))); + prepDataAndSend(payload); + } + break; + } + case PBTOUT: { + if (i.value()._valid) { + QByteArray payload("\x14\x08"); + payload.append(bcdEncodeInt(state.getChar(PBTOUT))); + prepDataAndSend(payload); + } + break; + } + case CWPITCH: { + if (i.value()._valid) { + QByteArray payload("\x14\x09"); + payload.append(bcdEncodeInt(state.getChar(CWPITCH))); + prepDataAndSend(payload); + } + break; + } + case KEYSPD: { + if (i.value()._valid) { + QByteArray payload("\x14\x0c"); + payload.append(bcdEncodeInt(state.getChar(KEYSPD))); + prepDataAndSend(payload); + } + break; + } + case NOTCHF: { + if (i.value()._valid) { + QByteArray payload("\x14\x0d"); + payload.append(bcdEncodeInt(state.getChar(NOTCHF))); + prepDataAndSend(payload); + } + break; + } + case IF: { + if (i.value()._valid) { + setIFShift(state.getChar(IF)); + } + getIFShift(); + break; + } + case APF: + break; + case BAL: break; case RESUMEFUNC: break; diff --git a/rigcommander.h b/rigcommander.h index 7921b24..9f49832 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -5,7 +5,7 @@ #include #include - +#include "wfviewtypes.h" #include "commhandler.h" #include "pttyhandler.h" #include "udphandler.h" @@ -23,47 +23,6 @@ // note: using a define because switch case doesn't even work with const unsigned char. Surprised me. #define compCivAddr 0xE1 -enum meterKind { - meterNone=0, - meterS, - meterCenter, - meterSWR, - meterPower, - meterALC, - meterComp, - meterVoltage, - meterCurrent, - meterRxdB, - meterTxMod, - meterRxAudio, - meterLatency -}; - -enum spectrumMode { - spectModeCenter=0x00, - spectModeFixed=0x01, - spectModeScrollC=0x02, - spectModeScrollF=0x03, - spectModeUnknown=0xff -}; - -struct freqt { - quint64 Hz; - double MHzDouble; -}; - -struct datekind { - uint16_t year; - unsigned char month; - unsigned char day; -}; - -struct timekind { - unsigned char hours; - unsigned char minutes; - bool isMinus; -}; - class rigCommander : public QObject { Q_OBJECT @@ -122,6 +81,7 @@ public slots: void getRitValue(); void setRitValue(int ritValue); void setRitEnable(bool ritEnabled); + void setPassband(quint16 pass); // PTT, ATU, ATT, Antenna, and Preamp: void getPTT(); @@ -156,6 +116,8 @@ public slots: void setManualNotch(bool enabled); void getManualNotch(); + void getPassband(); + // Repeater: void setDuplexMode(duplexMode dm); void getDuplexMode(); @@ -277,6 +239,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 +251,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); @@ -318,6 +282,7 @@ signals: void haveBandStackReg(freqt f, char mode, char filter, bool dataOn); void haveRitEnabled(bool ritEnabled); void haveRitFrequency(int ritHz); + void havePassband(quint16 pass); // Repeater: void haveDuplexMode(duplexMode); @@ -397,6 +362,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/rigctld.cpp b/rigctld.cpp index 6b81eaf..c63fd98 100644 --- a/rigctld.cpp +++ b/rigctld.cpp @@ -133,7 +133,7 @@ void rigCtlClient::socketReadyRead() char responseCode = 0; QStringList response; bool setCommand = false; - //commands.chop(1); // Remove \n character + //commands.chop(1); // Remove \n if (commands.endsWith('\r')) { commands.chop(1); // Remove \n character @@ -144,7 +144,7 @@ void rigCtlClient::socketReadyRead() continue; } - //qDebug(logRigCtlD()) << sessionId << "command received" << commands; + qDebug(logRigCtlD()) << sessionId << "RX:" << commands; // We have a full line so process command. @@ -195,46 +195,90 @@ void rigCtlClient::socketReadyRead() } else if (command[0] == "dump_state") { - // Currently send "fake" state information until I can work out what is required! - response.append("1"); // rigctld protocol version + quint64 modes = getRadioModes(); + + // rigctld protocol version + response.append("1"); + // Radio model response.append(QString("%1").arg(rigCaps.rigctlModel)); - response.append("0"); // Print something - bandType lastBand=(bandType)-1; + // Print something (used to be ITU region) + response.append("0"); + // Supported RX bands (startf,endf,modes,low_power,high_power,vfo,ant) + quint32 lowFreq = 0; + quint32 highFreq = 0; for (bandType band : rigCaps.bands) { - if (band != lastBand) - response.append(generateFreqRange(band)); - lastBand = band; + if (lowFreq == 0 || band.lowFreq < lowFreq) + lowFreq = band.lowFreq; + if (band.highFreq > highFreq) + highFreq = band.highFreq; } + response.append(QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq) + .arg(modes, 0, 16).arg(-1).arg(-1).arg(0x16000000, 0, 16).arg(getAntennas(), 0, 16)); response.append("0 0 0 0 0 0 0"); + if (rigCaps.hasTransmit) { + // Supported TX bands (startf,endf,modes,low_power,high_power,vfo,ant) for (bandType band : rigCaps.bands) { - if (band != lastBand) - response.append(generateFreqRange(band)); - lastBand = band; + response.append(QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(band.lowFreq).arg(band.highFreq) + .arg(modes, 0, 16).arg(2000).arg(100000).arg(0x16000000, 0, 16).arg(getAntennas(), 0, 16)); } } response.append("0 0 0 0 0 0 0"); - response.append(QString("0x%1 1").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 10").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 100").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 1000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 2500").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 5000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 6125").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 8333").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 10000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 12500").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 25000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 100000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 250000").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 1000000").arg(getRadioModes(), 0, 16)); + response.append(QString("0x%1 1").arg(modes, 0, 16)); + response.append(QString("0x%1 10").arg(modes, 0, 16)); + response.append(QString("0x%1 100").arg(modes, 0, 16)); + response.append(QString("0x%1 1000").arg(modes, 0, 16)); + response.append(QString("0x%1 2500").arg(modes, 0, 16)); + response.append(QString("0x%1 5000").arg(modes, 0, 16)); + response.append(QString("0x%1 6125").arg(modes, 0, 16)); + response.append(QString("0x%1 8333").arg(modes, 0, 16)); + response.append(QString("0x%1 10000").arg(modes, 0, 16)); + response.append(QString("0x%1 12500").arg(modes, 0, 16)); + response.append(QString("0x%1 25000").arg(modes, 0, 16)); + response.append(QString("0x%1 100000").arg(modes, 0, 16)); + response.append(QString("0x%1 250000").arg(modes, 0, 16)); + response.append(QString("0x%1 1000000").arg(modes, 0, 16)); response.append("0 0"); - response.append(QString("0x%1 1200").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 2400").arg(getRadioModes(), 0, 16)); - response.append(QString("0x%1 3000").arg(getRadioModes(), 0, 16)); + + modes = getRadioModes("SB"); + if (modes) { + response.append(QString("0x%1 3000").arg(modes, 0, 16)); + response.append(QString("0x%1 2400").arg(modes, 0, 16)); + response.append(QString("0x%1 1800").arg(modes, 0, 16)); + } + modes = getRadioModes("AM"); + if (modes) { + response.append(QString("0x%1 9000").arg(modes, 0, 16)); + response.append(QString("0x%1 6000").arg(modes, 0, 16)); + response.append(QString("0x%1 3000").arg(modes, 0, 16)); + } + modes = getRadioModes("CW"); + if (modes) { + response.append(QString("0x%1 1200").arg(modes, 0, 16)); + response.append(QString("0x%1 500").arg(modes, 0, 16)); + response.append(QString("0x%1 200").arg(modes, 0, 16)); + } + modes = getRadioModes("FM"); + if (modes) { + response.append(QString("0x%1 15000").arg(modes, 0, 16)); + response.append(QString("0x%1 10000").arg(modes, 0, 16)); + response.append(QString("0x%1 7000").arg(modes, 0, 16)); + } + modes = getRadioModes("RTTY"); + if (modes) { + response.append(QString("0x%1 2400").arg(modes, 0, 16)); + response.append(QString("0x%1 500").arg(modes, 0, 16)); + response.append(QString("0x%1 250").arg(modes, 0, 16)); + } + modes = getRadioModes("PSK"); + if (modes) { + response.append(QString("0x%1 1200").arg(modes, 0, 16)); + response.append(QString("0x%1 500").arg(modes, 0, 16)); + response.append(QString("0x%1 250").arg(modes, 0, 16)); + } response.append("0 0"); response.append("9900"); response.append("9900"); @@ -242,7 +286,7 @@ void rigCtlClient::socketReadyRead() response.append("0"); QString preamps=""; if (rigCaps.hasPreamp) { - for (unsigned char pre : rigCaps.preamps) + for (quint8 pre : rigCaps.preamps) { if (pre == 0) continue; @@ -258,7 +302,7 @@ void rigCtlClient::socketReadyRead() QString attens = ""; if (rigCaps.hasAttenuator) { - for (unsigned char att : rigCaps.attenuators) + for (quint8 att : rigCaps.attenuators) { if (att == 0) continue; @@ -272,7 +316,6 @@ void rigCtlClient::socketReadyRead() } response.append(attens); - response.append("0xffffffffffffffff"); response.append("0xffffffffffffffff"); response.append("0xffffffffffffffff"); @@ -280,23 +323,6 @@ void rigCtlClient::socketReadyRead() response.append("0xffffffffffffffff"); response.append("0xffffffffffffffff"); - /* - response.append("0xffffffffffffffff"); - response.append("0xffffffffffffffff"); - response.append("0xfffffffff7ffffff"); - response.append("0xfffffff083ffffff"); - response.append("0xffffffffffffffff"); - response.append("0xffffffffffffffbf"); - */ - - /* - response.append("0x3effffff"); - response.append("0x3effffff"); - response.append("0x7fffffff"); - response.append("0x7fffffff"); - response.append("0x7fffffff"); - response.append("0x7fffffff"); - */ if (chkVfoEecuted) { response.append(QString("vfo_ops=0x%1").arg(255, 0, 16)); response.append(QString("ptt_type=0x%1").arg(rigCaps.hasTransmit, 0, 16)); @@ -313,6 +339,28 @@ void rigCtlClient::socketReadyRead() } } + else if (command[0] == "fmv") { + QString resp; + + if (rigState->getChar(CURRENTVFO) == 0) { + resp.append(QString("%1").arg(rigState->getInt64(VFOAFREQ))); + } + else { + resp.append(QString("%1").arg(rigState->getInt64(VFOBFREQ))); + } + response.append(resp); + resp = ""; + response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); + response.append(QString("%1").arg(rigState->getUInt16(PASSBAND))); + + if (rigState->getChar(CURRENTVFO) == 0) { + resp.append("VFOA"); + } + else { + resp.append("VFOB"); + } + response.append(resp); + } else if (command[0] == "f" || command[0] == "get_freq") { QString resp; @@ -335,7 +383,7 @@ void rigCtlClient::socketReadyRead() freqt freq; bool ok=false; double newFreq=0.0f; - unsigned char vfo=0; + quint8 vfo=0; if (command.length() == 2) { newFreq = command[1].toDouble(&ok); @@ -353,10 +401,10 @@ void rigCtlClient::socketReadyRead() freq.Hz = static_cast(newFreq); qDebug(logRigCtlD()) << QString("Set frequency: %1 (%2)").arg(freq.Hz).arg(command[1]); if (vfo == 0) { - rigState->set(VFOAFREQ, freq.Hz,true); + rigState->set(VFOAFREQ, (quint64)freq.Hz,true); } else { - rigState->set(VFOBFREQ, freq.Hz,true); + rigState->set(VFOBFREQ, (quint64)freq.Hz,true); } } } @@ -408,7 +456,7 @@ void rigCtlClient::socketReadyRead() } else if (command[0] == "v" || command[0] == "v\nv" || command[0] == "get_vfo") { - QString resp; + QString resp; if (longReply) { resp.append("VFO: "); } @@ -416,15 +464,18 @@ void rigCtlClient::socketReadyRead() if (rigState->getChar(CURRENTVFO) == 0) { resp.append("VFOA"); } - else { + else if (rigState->getChar(CURRENTVFO) == 1) { resp.append("VFOB"); } + else if (rigState->getChar(CURRENTVFO) == 2) { + resp.append("MEM"); + } response.append(resp); } else if (command.length() > 1 && (command[0] == "V" || command[0] == "set_vfo")) { - setCommand = true; + setCommand = true; if (command[1] == "?") { response.append("set_vfo: ?"); response.append("VFOA"); @@ -433,16 +484,22 @@ void rigCtlClient::socketReadyRead() response.append("Main"); response.append("MEM"); } - else if (command[1] == "VFOB" || command[1] == "Sub") { + else if (command[1] == "VFOA" || command[1] == "Main") + { + rigState->set(CURRENTVFO, (quint8)0, true); + } + else if (command[1] == "VFOB" || command[1] == "Sub") + { rigState->set(CURRENTVFO, (quint8)1, true); } - else { - rigState->set(CURRENTVFO, (quint8)0, true); + else if (command[1] == "MEM") + { + rigState->set(CURRENTVFO, (quint8)2, true); } } else if (command[0] == "s" || command[0] == "get_split_vfo") { - + if (longReply) { response.append(QString("Split: %1").arg(rigState->getChar(DUPLEX))); } @@ -497,7 +554,8 @@ void rigCtlClient::socketReadyRead() response.append(QString("Freq: %1").arg(rigState->getInt64(VFOAFREQ))); } response.append(QString("Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("Width: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("Width: %1").arg(rigState->getUInt16(PASSBAND))); + response.append(QString("Split: %1").arg(rigState->getDuplex(DUPLEX))); response.append(QString("SatMode: %1").arg(0)); // Need to get satmode } @@ -509,7 +567,8 @@ void rigCtlClient::socketReadyRead() response.append(QString("%1").arg(rigState->getInt64(VFOAFREQ))); } response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("%1").arg(rigState->getUInt16(PASSBAND))); + } } else if (command[0] == "i" || command[0] == "get_split_freq") @@ -548,11 +607,11 @@ void rigCtlClient::socketReadyRead() { if (longReply) { response.append(QString("TX Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("TX Passband: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("TX Passband: %1").arg(rigState->getUInt16(PASSBAND))); } else { response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("%1").arg(rigState->getUInt16(PASSBAND))); } } @@ -560,58 +619,60 @@ void rigCtlClient::socketReadyRead() { if (longReply) { response.append(QString("TX Mode: %1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("TX Passband: %1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("TX Passband: %1").arg(rigState->getUInt16(PASSBAND))); } else { response.append(QString("%1").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE)))); - response.append(QString("%1").arg(getFilter(rigState->getChar(MODE), rigState->getChar(FILTER)))); + response.append(QString("%1").arg(rigState->getUInt16(PASSBAND))); } + //qDebug(logRigCtlD()) << QString("get_mode: %1 passband: %2").arg(getMode(rigState->getChar(MODE), rigState->getBool(DATAMODE))).arg(rigState->getUInt16(PASSBAND)); } else if (command[0] == "M" || command[0] == "set_mode") { // Set mode setCommand = true; - int width = -1; + quint8 width = 0; + quint16 passband = 0; QString vfo = "VFOA"; QString mode = "USB"; if (command.length() == 3) { - width = command[2].toInt(); + passband = command[2].toInt(); mode = command[1]; } else if (command.length() == 4) { - width = command[3].toInt(); + passband = command[3].toInt(); mode = command[2]; vfo = command[1]; } - qDebug(logRigCtlD()) << "setting mode: VFO:" << vfo << getMode(mode) << mode << "width" << width; + qDebug(logRigCtlD()) << "setting mode: VFO:" << vfo << getMode(mode) << mode << "passband" << passband << "command:" << commands; - if (width != -1 && width <= 1800) - width = 2; - else - width = 1; - - rigState->set(MODE,getMode(mode),true); - rigState->set(FILTER,(quint8)width, true); - if (mode.mid(0, 3) == "PKT") { - rigState->set(DATAMODE, true, true); - } - else { - rigState->set(DATAMODE, false, true); - } - - } - else if (command[0] == "s" || command[0] == "get_split_vfo") - { - if (longReply) { - response.append(QString("Split: 1")); - response.append(QString("TX VFO: VFOB")); - } - else + if (!mode.isEmpty()) { - response.append("1"); - response.append("VFOb"); + rigState->set(MODE, getMode(mode), true); + if (mode.mid(0, 3) == "PKT") { + rigState->set(DATAMODE, true, true); + } + else { + rigState->set(DATAMODE, false, true); + } } + if (passband > 0) + { + if (passband > 1800 && passband < 2700) { + width = 1; + } + else if (passband <= 1800) + { + width = 2; + } + else if (passband >= 2700) + { + width = 0; + } + rigState->set(FILTER, width, true); + rigState->set(PASSBAND, passband, true); + } } else if (command[0] == "j" || command[0] == "get_rit") { @@ -633,13 +694,13 @@ void rigCtlClient::socketReadyRead() if (command.length() > 1) { if (longReply) { - response.append(QString("AntCurr: %1").arg(getAntName((unsigned char)command[1].toInt()))); + response.append(QString("AntCurr: %1").arg(getAntName((quint8)command[1].toInt()))); response.append(QString("Option: %1").arg(0)); response.append(QString("AntTx: %1").arg(getAntName(rigState->getChar(ANTENNA)))); response.append(QString("AntRx: %1").arg(getAntName(rigState->getChar(ANTENNA)))); } else { - response.append(QString("%1").arg(getAntName((unsigned char)command[1].toInt()))); + response.append(QString("%1").arg(getAntName((quint8)command[1].toInt()))); response.append(QString("%1").arg(0)); response.append(QString("%1").arg(getAntName(rigState->getChar(ANTENNA)))); response.append(QString("%1").arg(getAntName(rigState->getChar(ANTENNA)))); @@ -718,6 +779,33 @@ void rigCtlClient::socketReadyRead() else if (command[1] == "ATT") { resp.append(QString("%1").arg(rigState->getChar(ATTENUATOR))); } + else if (command[1] == "CWPITCH") { + resp.append(QString("%1").arg(rigState->getInt16(CWPITCH))); + } + else if (command[1] == "NOTCHF") { + resp.append(QString("%1").arg(rigState->getInt16(NOTCHF))); + } + else if (command[1] == "IF") { + resp.append(QString("%1").arg(rigState->getInt16(IF))); + } + else if (command[1] == "PBT_IN") { + resp.append(QString("%1").arg((float)rigState->getChar(PBTIN) / 255.0)); + } + else if (command[1] == "PBT_OUT") { + resp.append(QString("%1").arg((float)rigState->getChar(PBTOUT) / 255.0)); + } + else if (command[1] == "APF") { + resp.append(QString("%1").arg((float)rigState->getChar(APF) / 255.0)); + } + else if (command[1] == "NR") { + resp.append(QString("%1").arg((float)rigState->getChar(NR) / 255.0)); + } + else if (command[1] == "BAL") { + resp.append(QString("%1").arg((float)rigState->getChar(BAL) / 255.0)); + } + else if (command[1] == "KEYSPD") { + resp.append(QString("%1").arg(rigState->getChar(KEYSPD)/5.1)); + } else { resp.append(QString("%1").arg(value)); } @@ -726,55 +814,91 @@ void rigCtlClient::socketReadyRead() } else if (command.length() > 2 && (command[0] == "L" || command[0] == "set_level")) { - unsigned char value=0; + int value=0; setCommand = true; if (command[1] == "AF") { value = command[2].toFloat() * 255; - rigState->set(AFGAIN, value, true); + rigState->set(AFGAIN, quint8(value), true); } else if (command[1] == "RF") { value = command[2].toFloat() * 255; - rigState->set(RFGAIN, value, true); + rigState->set(RFGAIN, quint8(value), true); } else if (command[1] == "RFPOWER") { - value = command[2].toFloat() * 255; - rigState->set(RFPOWER, value, true); + value = command[2].toFloat() * 255; + rigState->set(RFPOWER, quint8(value), true); } else if (command[1] == "SQL") { value = command[2].toFloat() * 255; - rigState->set(SQUELCH, value, true); + rigState->set(SQUELCH, quint8(value), true); } else if (command[1] == "COMP") { value = command[2].toFloat() * 255; - rigState->set(COMPLEVEL, value, true); + rigState->set(COMPLEVEL, quint8(value), true); } else if (command[1] == "MICGAIN") { value = command[2].toFloat() * 255; - rigState->set(MICGAIN, value, true); + rigState->set(MICGAIN, quint8(value), true); } else if (command[1] == "MON") { value = command[2].toFloat() * 255; - rigState->set(MONITORLEVEL, value, true); + rigState->set(MONITORLEVEL, quint8(value), true); } else if (command[1] == "VOXGAIN") { value = command[2].toFloat() * 255; - rigState->set(VOXGAIN, value, true); + rigState->set(VOXGAIN, quint8(value), true); } else if (command[1] == "ANTIVOX") { value = command[2].toFloat() * 255; - rigState->set(ANTIVOXGAIN, value, true); + rigState->set(ANTIVOXGAIN, quint8(value), true); } else if (command[1] == "ATT") { value = command[2].toInt(); - rigState->set(ATTENUATOR, value, true); + rigState->set(ATTENUATOR, quint8(value), true); } else if (command[1] == "PREAMP") { value = command[2].toFloat() / 10; - rigState->set(PREAMP, value, true); + rigState->set(PREAMP, quint8(value), true); } else if (command[1] == "AGC") { - value = command[2].toInt();; - rigState->set(AGC, value, true); + value = command[2].toFloat() * 255; + rigState->set(AGC, quint8(value), true); + } + else if (command[1] == "CWPITCH") { + value = command[2].toInt(); + rigState->set(CWPITCH, value, true); + } + else if (command[1] == "NOTCHF") { + value = command[2].toInt(); + rigState->set(NOTCHF, value, true); + } + else if (command[1] == "IF") { + value = command[2].toInt(); + rigState->set(IF, qint16(value), true); + } + else if (command[1] == "PBT_IN") { + value = command[2].toFloat() * 255; + rigState->set(PBTIN, quint8(value), true); + } + else if (command[1] == "PBT_OUT") { + value = command[2].toFloat() * 255; + rigState->set(PBTOUT, quint8(value), true); + } + else if (command[1] == "APF") { + value = command[2].toFloat() * 255; + rigState->set(APF, quint8(value), true); + } + else if (command[1] == "NR") { + value = command[2].toFloat() * 255; + rigState->set(NR, quint8(value), true); + } + else if (command[1] == "BAL") { + value = command[2].toFloat() * 255; + rigState->set(BAL, quint8(value), true); + } + else if (command[1] == "KEYSPD") { + value = command[2].toInt() * 5.1; + rigState->set(KEYSPD, quint8(value), true); } qInfo(logRigCtlD()) << "Setting:" << command[1] << command[2] << value; @@ -1131,7 +1255,7 @@ void rigCtlClient::closeSocket() void rigCtlClient::sendData(QString data) { - //qDebug(logRigCtlD()) << "Sending:" << data; + //qDebug(logRigCtlD()) << sessionId << "TX:" << data; if (socket != Q_NULLPTR && socket->isValid() && socket->isOpen()) { socket->write(data.toLatin1()); @@ -1142,8 +1266,7 @@ void rigCtlClient::sendData(QString data) } } - -QString rigCtlClient::getFilter(unsigned char mode, unsigned char filter) { +QString rigCtlClient::getFilter(quint8 mode, quint8 filter) { if (mode == 3 || mode == 7 || mode == 12 || mode == 17) { switch (filter) { @@ -1201,51 +1324,52 @@ QString rigCtlClient::getFilter(unsigned char mode, unsigned char filter) { return QString(""); } -QString rigCtlClient::getMode(unsigned char mode, bool datamode) { +QString rigCtlClient::getMode(quint8 mode, bool datamode) { QString ret; + switch (mode) { - case 0: + case modeLSB: if (datamode) { ret = "PKT"; } ret.append("LSB"); break; - case 1: + case modeUSB: if (datamode) { ret = "PKT"; } ret.append("USB"); break; - case 2: + case modeAM: if (datamode) { ret = "PKT"; } ret.append("AM"); break; - case 3: + case modeCW: ret.append("CW"); break; - case 4: + case modeRTTY: ret.append("RTTY"); break; - case 5: + case modeFM: if (datamode) { ret = "PKT"; } ret.append("FM"); break; - case 6: + case modeWFM: ret.append("WFM"); break; - case 7: + case modeCW_R: ret.append("CWR"); break; - case 8: + case modeRTTY_R: ret.append("RTTYR"); break; - case 12: + case modePSK: if (datamode) { ret = "PKT"; } ret.append("USB"); break; - case 17: + case modeDV: if (datamode) { ret = "PKT"; } ret.append("LSB"); break; - case 22: + case 22: // We don't seem to have a mode for this? if (datamode) { ret = "PKT"; } ret.append("FM"); break; @@ -1253,7 +1377,7 @@ QString rigCtlClient::getMode(unsigned char mode, bool datamode) { return ret; } -unsigned char rigCtlClient::getMode(QString modeString) { +quint8 rigCtlClient::getMode(QString modeString) { if (modeString == QString("LSB")) { return 0; @@ -1297,140 +1421,48 @@ unsigned char rigCtlClient::getMode(QString modeString) { return 0; } -QString rigCtlClient::generateFreqRange(bandType band) -{ - unsigned int lowFreq = 0; - unsigned int highFreq = 0; - switch (band) { - case band2200m: - lowFreq = 135000; - highFreq = 138000; - break; - case band630m: - lowFreq = 493000; - highFreq = 595000; - break; - case band160m: - lowFreq = 1800000; - highFreq = 2000000; - break; - case band80m: - lowFreq = 3500000; - highFreq = 4000000; - break; - case band60m: - lowFreq = 5250000; - highFreq = 5450000; - break; - case band40m: - lowFreq = 7000000; - highFreq = 7300000; - break; - case band30m: - lowFreq = 10100000; - highFreq = 10150000; - break; - case band20m: - lowFreq = 14000000; - highFreq = 14350000; - break; - case band17m: - lowFreq = 18068000; - highFreq = 18168000; - break; - case band15m: - lowFreq = 21000000; - highFreq = 21450000; - break; - case band12m: - lowFreq = 24890000; - highFreq = 24990000; - break; - case band10m: - lowFreq = 28000000; - highFreq = 29700000; - break; - case band6m: - lowFreq = 50000000; - highFreq = 54000000; - break; - case band4m: - lowFreq = 70000000; - highFreq = 70500000; - break; - case band2m: - lowFreq = 144000000; - highFreq = 148000000; - break; - case band70cm: - lowFreq = 420000000; - highFreq = 450000000; - break; - case band23cm: - lowFreq = 1240000000; - highFreq = 1400000000; - break; - case bandAir: - lowFreq = 108000000; - highFreq = 137000000; - break; - case bandWFM: - lowFreq = 88000000; - highFreq = 108000000; - break; - case bandGen: - lowFreq = 10000; - highFreq = 30000000; - break; - } - QString ret = ""; - if (lowFreq > 0 && highFreq > 0) { - ret = QString("%1.000000 %2.000000 0x%3 %4 %5 0x%6 0x%7").arg(lowFreq).arg(highFreq).arg(getRadioModes(),0,16).arg(-1).arg(-1).arg(0x16000003,0,16).arg(getAntennas(),0,16); - } - return ret; -} - -unsigned char rigCtlClient::getAntennas() +quint8 rigCtlClient::getAntennas() { - unsigned char ant=0; - for (unsigned char i : rigCaps.antennas) + quint8 ant=0; + for (quint8 i : rigCaps.antennas) { ant |= 1< inputs; - bool hasSpectrum; + bool hasSpectrum=true; quint8 spectSeqMax; quint16 spectAmpMax; quint16 spectLenMax; diff --git a/rigstate.h b/rigstate.h index 13c66cf..478f471 100644 --- a/rigstate.h +++ b/rigstate.h @@ -12,12 +12,12 @@ #include "rigidentities.h" // Meters at the end as they are ALWAYS updated from the rig! -enum stateTypes { VFOAFREQ, VFOBFREQ, CURRENTVFO, PTT, MODE, FILTER, DUPLEX, DATAMODE, ANTENNA, RXANTENNA, CTCSS, TSQL, DTCS, CSQL, - PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, RFPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, VOXGAIN, ANTIVOXGAIN, +enum stateTypes { VFOAFREQ, VFOBFREQ, CURRENTVFO, PTT, MODE, FILTER, PASSBAND, DUPLEX, DATAMODE, ANTENNA, RXANTENNA, CTCSS, TSQL, DTCS, CSQL, + PREAMP, AGC, ATTENUATOR, MODINPUT, AFGAIN, RFGAIN, SQUELCH, RFPOWER, MICGAIN, COMPLEVEL, MONITORLEVEL, BAL, KEYSPD, + VOXGAIN, ANTIVOXGAIN, CWPITCH, NOTCHF, IF, PBTIN, PBTOUT, APF, NR, NB, NBDEPTH, NBWIDTH, RIGINPUT, POWERONOFF, RITVALUE, FAGCFUNC, NBFUNC, COMPFUNC, VOXFUNC, TONEFUNC, TSQLFUNC, SBKINFUNC, FBKINFUNC, ANFFUNC, NRFUNC, AIPFUNC, APFFUNC, MONFUNC, MNFUNC,RFFUNC, - AROFUNC, MUTEFUNC, VSCFUNC, REVFUNC, SQLFUNC, ABMFUNC, BCFUNC, MBCFUNC, RITFUNC, AFCFUNC, SATMODEFUNC, SCOPEFUNC, - NBLEVEL, NBDEPTH, NBWIDTH, NRLEVEL, RIGINPUT, POWERONOFF, RITVALUE, - RESUMEFUNC, TBURSTFUNC, TUNERFUNC, LOCKFUNC, SMETER, POWERMETER, SWRMETER, ALCMETER, COMPMETER, VOLTAGEMETER, CURRENTMETER + AROFUNC, MUTEFUNC, VSCFUNC, REVFUNC, SQLFUNC, ABMFUNC, BCFUNC, MBCFUNC, RITFUNC, AFCFUNC, SATMODEFUNC, SCOPEFUNC, + RESUMEFUNC, TBURSTFUNC, TUNERFUNC, LOCKFUNC, SMETER, POWERMETER, SWRMETER, ALCMETER, COMPMETER, VOLTAGEMETER, CURRENTMETER, }; struct value { @@ -40,7 +40,7 @@ public: void set(stateTypes s, quint64 x, bool u) { if (x != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -48,9 +48,19 @@ public: } } void set(stateTypes s, qint32 x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); + map[s]._valid = true; + map[s]._updated = u; + map[s]._dateUpdated = QDateTime::currentDateTime(); + _mutex.unlock(); + } + } + void set(stateTypes s, qint16 x, bool u) { + if (quint64(x) != map[s]._value) { + _mutex.lock(); + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -58,9 +68,9 @@ public: } } void set(stateTypes s, quint16 x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -68,9 +78,9 @@ public: } } void set(stateTypes s, quint8 x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -78,9 +88,9 @@ public: } } void set(stateTypes s, bool x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -88,9 +98,9 @@ public: } } void set(stateTypes s, duplexMode x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -99,9 +109,9 @@ public: } void set(stateTypes s, rigInput x, bool u) { - if ((quint64)x != map[s]._value) { + if (quint64(x) != map[s]._value) { _mutex.lock(); - map[s]._value = (quint64)x; + map[s]._value = quint64(x); map[s]._valid = true; map[s]._updated = u; map[s]._dateUpdated = QDateTime::currentDateTime(); @@ -110,12 +120,14 @@ public: } bool getBool(stateTypes s) { return map[s]._value != 0; } - quint8 getChar(stateTypes s) { return (quint8)map[s]._value; } - quint16 getInt16(stateTypes s) { return (qint16)map[s]._value; } - qint32 getInt32(stateTypes s) { return (qint32)map[s]._value; } + quint8 getChar(stateTypes s) { return quint8(map[s]._value); } + qint16 getInt16(stateTypes s) { return qint16(map[s]._value); } + quint16 getUInt16(stateTypes s) { return quint16(map[s]._value); } + qint32 getInt32(stateTypes s) { return qint32(map[s]._value); } + quint32 getUInt32(stateTypes s) { return quint32(map[s]._value); } quint64 getInt64(stateTypes s) { return map[s]._value; } - duplexMode getDuplex(stateTypes s) { return(duplexMode)map[s]._value; } - rigInput getInput(stateTypes s) { return(rigInput)map[s]._value; } + duplexMode getDuplex(stateTypes s) { return duplexMode(map[s]._value); } + rigInput getInput(stateTypes s) { return rigInput(map[s]._value); } QMap map; diff --git a/rthandler.cpp b/rthandler.cpp index f3943d9..ab164b1 100644 --- a/rthandler.cpp +++ b/rthandler.cpp @@ -286,10 +286,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) @@ -371,13 +368,13 @@ void rtHandler::convertedOutput(audioPacket packet) audioMutex.lock(); arrayBuffer.append(packet.data); audioMutex.unlock(); - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; #if QT_VERSION < 0x060000 currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) / 1000); #else currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * sizeof(outFormat.sampleFormat()) * outFormat.channelCount()) / 1000); #endif - emit haveLevels(getAmplitude(), setup.latency, currentLatency, isUnderrun, isOverrun); + emit haveLevels(getAmplitude(), packet.amplitudeRMS, setup.latency, currentLatency, isUnderrun, isOverrun); } @@ -386,13 +383,13 @@ void rtHandler::convertedInput(audioPacket packet) { if (packet.data.size() > 0) { emit haveAudioData(packet); - amplitude = packet.amplitude; + amplitude = packet.amplitudePeak; #if QT_VERSION < 0x060000 currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * (outFormat.sampleSize() / 8) * outFormat.channelCount()) / 1000); #else currentLatency = packet.time.msecsTo(QTime::currentTime()) + (outFormat.durationForBytes(audio->getStreamLatency() * sizeof(outFormat.sampleFormat()) * outFormat.channelCount()) / 1000); #endif - 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 b68743a..37f11fe 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, codecType codecIn, QAudioFormat out, codecType codecOut, quint8 opus, quint8 resamp); void sendToConverter(audioPacket audio); diff --git a/servermain.cpp b/servermain.cpp index c2942c5..791d8fd 100644 --- a/servermain.cpp +++ b/servermain.cpp @@ -8,10 +8,8 @@ // This code is copyright 2017-2020 Elliott H. Liggett // All rights reserved -servermain::servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile) +servermain::servermain(const QString settingsFile, const QString logFile) { - this->serialPortCL = serialPortCL; - this->hostCL = hostCL; qRegisterMetaType (); // Needs to be registered early. qRegisterMetaType (); @@ -89,7 +87,7 @@ void servermain::openRig() { //qInfo(logSystem()) << "Got rig"; QMetaObject::invokeMethod(radio->rig, [=]() { - radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),prefs.tcpPort,radio->waterfallFormat); + radio->rig->commSetup(radio->civAddr, radio->serialPort, radio->baudRate, QString("none"),0 ,radio->waterfallFormat); }, Qt::QueuedConnection); } } @@ -487,7 +485,7 @@ void servermain::loadSettings() numRadios = settings->beginReadArray("Radios"); int tempNum = numRadios; - + for (int i = 0; i < numRadios; i++) { settings->setArrayIndex(i); RIGCONFIG* tempPrefs = new RIGCONFIG(); @@ -502,23 +500,16 @@ void servermain::loadSettings() tempPrefs->rxAudioSetup.type = prefs.audioSystem; tempPrefs->txAudioSetup.type = prefs.audioSystem; - QString tempPort = "auto"; - if (tempPrefs->rigName=="") - { + if (tempPrefs->serialPort == "auto") { foreach(const QSerialPortInfo & serialPortInfo, QSerialPortInfo::availablePorts()) { qDebug(logSystem()) << "Serial Port found: " << serialPortInfo.portName() << "Manufacturer:" << serialPortInfo.manufacturer() << "Product ID" << serialPortInfo.description() << "S/N" << serialPortInfo.serialNumber(); - if ((serialPortInfo.portName() == tempPrefs->serialPort || tempPrefs->serialPort == "auto") && !serialPortInfo.serialNumber().isEmpty()) - { - if (serialPortInfo.serialNumber().startsWith("IC-")) { - tempPrefs->rigName = serialPortInfo.serialNumber(); - tempPort = serialPortInfo.portName(); - } + if (serialPortInfo.serialNumber().startsWith("IC-") && tempPrefs->serialPort == "auto") { + tempPrefs->rigName = serialPortInfo.serialNumber(); + tempPrefs->serialPort = serialPortInfo.portName(); } } } - tempPrefs->serialPort = tempPort; - QString guid = settings->value("GUID", "").toString(); if (guid.isEmpty()) { guid = QUuid::createUuid().toString(); diff --git a/servermain.h b/servermain.h index 1b7e3a8..b19d8ca 100644 --- a/servermain.h +++ b/servermain.h @@ -46,9 +46,7 @@ class servermain : public QObject Q_OBJECT public: - servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile); - QString serialPortCL; - QString hostCL; + servermain(const QString logFile, const QString settingsFile); ~servermain(); signals: diff --git a/udpaudio.cpp b/udpaudio.cpp index dc1e5c5..1326a67 100644 --- a/udpaudio.cpp +++ b/udpaudio.cpp @@ -159,13 +159,13 @@ 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() @@ -256,7 +256,7 @@ void udpAudio::startAudio() { 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(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())); @@ -290,7 +290,7 @@ void udpAudio::startAudio() { 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(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); diff --git a/udpaudio.h b/udpaudio.h index 884edef..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: @@ -85,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 f0ffd60..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() @@ -317,8 +368,8 @@ void udpHandler::dataReceived() 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))); + 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 02badd6..e29377e 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -5,23 +5,45 @@ #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 settingsFile, const QString logFile, bool debugMode, QWidget *parent ) : QMainWindow(parent), - ui(new Ui::wfmain) + ui(new Ui::wfmain), + logFilename(logFile) { QGuiApplication::setApplicationDisplayName("wfview"); 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). Operating System: %7 (%8). Build Qt Version %9. Current Qt Version: %10") + .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")); - this->serialPortCL = serialPortCL; - this->hostCL = hostCL; + logWindow = new loggingWindow(logFile); + initLogging(); + logWindow->setInitialDebugState(debugMode); + qInfo(logSystem()) << version; cal = new calibrationWindow(); rpt = new repeaterSetup(); @@ -39,6 +61,7 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType (); qRegisterMetaType (); @@ -46,22 +69,27 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s qRegisterMetaType (); qRegisterMetaType(); qRegisterMetaType>(); + qRegisterMetaType>(); + qRegisterMetaType>(); qRegisterMetaType(); + qRegisterMetaType(); haveRigCaps = false; setupKeyShortcuts(); setupMainUI(); + prepareSettingsWindow(); setSerialDevicesUI(); - setDefaultColors(); setDefPrefs(); getSettingsFilePath(settingsFile); setupPlots(); + setDefaultColorPresets(); + loadSettings(); // Look for saved preferences #if QT_VERSION >= 0x060000 @@ -86,6 +114,46 @@ wfmain::wfmain(const QString serialPortCL, const QString hostCL, const QString s rigConnections(); + cluster = new dxClusterClient(); + + clusterThread = new QThread(this); + clusterThread->setObjectName("dxcluster()"); + + cluster->moveToThread(clusterThread); + + connect(this, SIGNAL(setClusterEnableUdp(bool)), cluster, SLOT(enableUdp(bool))); + connect(this, SIGNAL(setClusterEnableTcp(bool)), cluster, SLOT(enableTcp(bool))); + connect(this, SIGNAL(setClusterUdpPort(int)), cluster, SLOT(setUdpPort(int))); + connect(this, SIGNAL(setClusterServerName(QString)), cluster, SLOT(setTcpServerName(QString))); + connect(this, SIGNAL(setClusterTcpPort(int)), cluster, SLOT(setTcpPort(int))); + connect(this, SIGNAL(setClusterUserName(QString)), cluster, SLOT(setTcpUserName(QString))); + connect(this, SIGNAL(setClusterPassword(QString)), cluster, SLOT(setTcpPassword(QString))); + connect(this, SIGNAL(setClusterTimeout(int)), cluster, SLOT(setTcpTimeout(int))); + connect(this, SIGNAL(setFrequencyRange(double, double)), cluster, SLOT(freqRange(double, double))); + connect(this, SIGNAL(setClusterSkimmerSpots(bool)), cluster, SLOT(enableSkimmerSpots(bool))); + + connect(cluster, SIGNAL(sendSpots(QList)), this, SLOT(receiveSpots(QList))); + connect(cluster, SIGNAL(sendOutput(QString)), this, SLOT(receiveClusterOutput(QString))); + + connect(clusterThread, SIGNAL(finished()), cluster, SLOT(deleteLater())); + + clusterThread->start(); + + emit setClusterUdpPort(prefs.clusterUdpPort); + emit setClusterEnableUdp(prefs.clusterUdpEnable); + + for (int f = 0; f < clusters.size(); f++) + { + if (clusters[f].isdefault) + { + emit setClusterServerName(clusters[f].server); + emit setClusterTcpPort(clusters[f].port); + emit setClusterUserName(clusters[f].userName); + emit setClusterPassword(clusters[f].password); + emit setClusterTimeout(clusters[f].timeout); + } + } + emit setClusterEnableTcp(prefs.clusterTcpEnable); setServerToPrefs(); @@ -105,6 +173,10 @@ wfmain::~wfmain() serverThread->quit(); serverThread->wait(); } + if (clusterThread != Q_NULLPTR) { + clusterThread->quit(); + clusterThread->wait(); + } if (rigCtl != Q_NULLPTR) { delete rigCtl; } @@ -125,6 +197,7 @@ void wfmain::closeEvent(QCloseEvent *event) QApplication::exit(); } QCheckBox *cb = new QCheckBox("Don't ask me again"); + cb->setToolTip("Don't ask me to confirm exit again"); QMessageBox msgbox; msgbox.setText("Are you sure you wish to exit?\n"); msgbox.setIcon(QMessageBox::Icon::Question); @@ -152,6 +225,7 @@ void wfmain::closeEvent(QCloseEvent *event) } else { event->ignore(); } + delete cb; } void wfmain::openRig() @@ -171,20 +245,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); - // TODO: Use these if they are found - if(!serialPortCL.isEmpty()) - { - qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL; - } else { - qDebug(logSystem()) << "Serial port not specified. "; - } - - if(!hostCL.isEmpty()) - { - qDebug(logSystem()) << "Remote host name specified by user: " << hostCL; - } - + ui->connectBtn->setText("Cancel connection"); // We are attempting to connect makeRig(); @@ -197,16 +261,11 @@ void wfmain::openRig() emit sendCommSetup(prefs.radioCIVAddr, udpPrefs, rxSetup, txSetup, prefs.virtualSerialPort, prefs.tcpPort); } else { ui->serialEnableBtn->setChecked(true); - if( (prefs.serialPortRadio.toLower() == QString("auto")) && (serialPortCL.isEmpty())) + if( (prefs.serialPortRadio.toLower() == QString("auto"))) { findSerialPort(); } else { - if(serialPortCL.isEmpty()) - { - serialPortRig = prefs.serialPortRadio; - } else { - serialPortRig = serialPortCL; - } + serialPortRig = prefs.serialPortRadio; } usingLAN = false; emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud,prefs.virtualSerialPort, prefs.tcpPort,prefs.waterfallFormat); @@ -225,8 +284,9 @@ void wfmain::createSettingsListItems() ui->settingsList->addItem("Radio Settings"); // 2 ui->settingsList->addItem("Radio Server"); // 3 ui->settingsList->addItem("External Control"); // 4 - ui->settingsList->addItem("Experimental"); // 5 - //ui->settingsList->addItem("Audio Processing"); // 6 + ui->settingsList->addItem("DX Cluster"); // 5 + ui->settingsList->addItem("Experimental"); // 6 + //ui->settingsList->addItem("Audio Processing"); // 7 ui->settingsStack->setCurrentIndex(0); } @@ -274,6 +334,7 @@ void wfmain::rigConnections() connect(this, SIGNAL(scopeDisplayEnable()), rig, SLOT(enableSpectrumDisplay())); connect(rig, SIGNAL(haveMode(unsigned char, unsigned char)), this, SLOT(receiveMode(unsigned char, unsigned char))); connect(rig, SIGNAL(haveDataMode(bool)), this, SLOT(receiveDataModeStatus(bool))); + connect(rig, SIGNAL(havePassband(quint16)), this, SLOT(receivePassband(quint16))); connect(rpt, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); connect(rpt, SIGNAL(setDuplexMode(duplexMode)), rig, SLOT(setDuplexMode(duplexMode))); @@ -293,6 +354,7 @@ void wfmain::rigConnections() connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode())); + connect(this, SIGNAL(getPassband()), rig, SLOT(getPassband())); connect(this, SIGNAL(getTone()), rig, SLOT(getTone())); connect(this, SIGNAL(getTSQL()), rig, SLOT(getTSQL())); connect(this, SIGNAL(getRptAccessMode()), rig, SLOT(getRptAccessMode())); @@ -429,6 +491,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))); @@ -575,10 +638,13 @@ void wfmain::receiveCommReady() if(prefs.CIVisRadioModel) { qInfo(logSystem()) << "Skipping Rig ID query, using user-supplied model from CI-V address: " << prefs.radioCIVAddr; + emit setCIVAddr(prefs.radioCIVAddr); emit setRigID(prefs.radioCIVAddr); } else { + emit setCIVAddr(prefs.radioCIVAddr); emit getRigID(); - getInitialRigState(); + issueDelayedCommand(cmdGetRigID); + delayedCommand->start(); } } } @@ -620,6 +686,45 @@ 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() { spectrumDrawLock = true; @@ -627,10 +732,25 @@ void wfmain::setupPlots() wf = ui->waterfall; + passbandIndicator = new QCPItemRect(plot); + passbandIndicator->setAntialiased(true); + passbandIndicator->setPen(QPen(Qt::red)); + passbandIndicator->setBrush(QBrush(Qt::red)); + passbandIndicator->setSelectable(true); + freqIndicatorLine = new QCPItemLine(plot); freqIndicatorLine->setAntialiased(true); freqIndicatorLine->setPen(QPen(Qt::blue)); + /* + text = new QCPItemText(plot); + text->setAntialiased(true); + text->setColor(QColor(Qt::red)); + text->setText("TEST"); + text->position->setCoords(14.195, rigCaps.spectAmpMax); + text->setFont(QFont(font().family(), 12)); + */ + ui->plot->addGraph(); // primary ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first. ui->plot->addLayer( "Top Layer", ui->plot->layer("main")); @@ -655,14 +775,19 @@ void wfmain::setupPlots() plot->graph(1)->setPen(QPen(color.lighter(200))); plot->graph(1)->setBrush(QBrush(color)); - freqIndicatorLine->start->setCoords(0.5,0); - freqIndicatorLine->end->setCoords(0.5,160); + freqIndicatorLine->start->setCoords(0.5, 0); + freqIndicatorLine->end->setCoords(0.5, 160); + + passbandIndicator->topLeft->setCoords(0.5, 0); + passbandIndicator->bottomRight->setCoords(0.5, 160); // Plot user interaction connect(plot, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handlePlotDoubleClick(QMouseEvent*))); connect(wf, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handleWFDoubleClick(QMouseEvent*))); connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handlePlotClick(QMouseEvent*))); connect(wf, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handleWFClick(QMouseEvent*))); + connect(plot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(handlePlotMouseRelease(QMouseEvent*))); + connect(plot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(handlePlotMouseMove(QMouseEvent *))); connect(wf, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handleWFScroll(QWheelEvent*))); connect(plot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handlePlotScroll(QWheelEvent*))); spectrumDrawLock = false; @@ -751,6 +876,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(); @@ -881,48 +1010,55 @@ 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) { - if(!haveRigCaps) - return; + // This function does nothing unless you are using a rig without spectrum. // This is a hack. It is not great, but it seems to work ok. if(!rigCaps.hasSpectrum) { // Set "ignore" size policy for non-selected tabs: - for(int i=0;itabWidget->count();i++) - if((i!=tabIndex) && tabIndex != 0) + for(int i=1;itabWidget->count();i++) + if((i!=tabIndex)) ui->tabWidget->widget(i)->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); // allows size to be any size that fits the tab bar - if(tabIndex==0 && !rigCaps.hasSpectrum) + if(tabIndex==0) { ui->tabWidget->widget(0)->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); ui->tabWidget->widget(0)->setMaximumSize(ui->tabWidget->widget(0)->minimumSizeHint()); ui->tabWidget->widget(0)->adjustSize(); // tab this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - this->setMaximumSize(QSize(1024,350)); - this->setMinimumSize(QSize(1024,350)); + this->setMaximumSize(QSize(940,350)); + this->setMinimumSize(QSize(940,350)); resize(minimumSize()); adjustSize(); // main window - } else if(tabIndex==0 && rigCaps.hasSpectrum) { - // At main tab (0) and we have spectrum: - ui->tabWidget->widget(0)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); - - resize(minimumSizeHint()); - adjustSize(); // Without this call, the window retains the size of the previous tab. } else { // At some other tab, with or without spectrum: ui->tabWidget->widget(tabIndex)->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); this->setMinimumSize(QSize(1024, 600)); // not large enough for settings tab this->setMaximumSize(QSize(65535,65535)); + resize(minimumSize()); adjustSize(); } } else { @@ -971,12 +1107,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); @@ -1045,16 +1175,9 @@ 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) { @@ -1085,13 +1208,24 @@ 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->colorPresetCombo->setCurrentIndex(prefs.currentColorPresetNumber); + loadColorPresetToUIandPlots(prefs.currentColorPresetNumber); + ui->wfthemeCombo->setCurrentIndex(ui->wfthemeCombo->findData(prefs.wftheme)); colorMap->setGradient(static_cast(prefs.wftheme)); @@ -1284,9 +1418,9 @@ void wfmain::setupKeyShortcuts() 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; @@ -1297,6 +1431,7 @@ void wfmain::setDefPrefs() defPrefs.forceRTSasPTT = false; defPrefs.serialPortRadio = QString("auto"); defPrefs.serialPortBaud = 115200; + defPrefs.polling_ms = 0; // 0 = Automatic defPrefs.enablePTT = false; defPrefs.niceTS = true; defPrefs.enableRigCtlD = false; @@ -1331,7 +1466,6 @@ 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(); @@ -1355,39 +1489,59 @@ void wfmain::loadSettings() prefs.confirmExit = settings->value("ConfirmExit", defPrefs.confirmExit).toBool(); prefs.confirmPowerOff = settings->value("ConfirmPowerOff", defPrefs.confirmPowerOff).toBool(); prefs.meter2Type = static_cast(settings->value("Meter2Type", defPrefs.meter2Type).toInt()); + prefs.clickDragTuningEnable = settings->value("ClickDragTuning", false).toBool(); + ui->clickDragTuningEnableChk->setChecked(prefs.clickDragTuningEnable); 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"); + prefs.currentColorPresetNumber = settings->value("currentColorPresetNumber", defPrefs.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->passband.setNamedColor(settings->value("passband", p->passband.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()); + p->clusterSpots.setNamedColor(settings->value("clusterSpots", p->clusterSpots.name(QColor::HexArgb)).toString()); + } + } + settings->endArray(); settings->endGroup(); // Radio and Comms: C-IV addr, port to use @@ -1426,6 +1580,27 @@ void wfmain::loadSettings() serverConfig.baudRate = prefs.serialPortBaud; } + prefs.polling_ms = settings->value("polling_ms", defPrefs.polling_ms).toInt(); + if(prefs.polling_ms == 0) + { + // Automatic + ui->pollingButtonGroup->blockSignals(true); + ui->autoPollBtn->setChecked(true); + ui->manualPollBtn->setChecked(false); + ui->pollingButtonGroup->blockSignals(false); + ui->pollTimeMsSpin->setEnabled(false); + } else { + // Manual + ui->pollingButtonGroup->blockSignals(true); + ui->autoPollBtn->setChecked(false); + ui->manualPollBtn->setChecked(true); + ui->pollingButtonGroup->blockSignals(false); + ui->pollTimeMsSpin->blockSignals(true); + ui->pollTimeMsSpin->setValue(prefs.polling_ms); + ui->pollTimeMsSpin->blockSignals(false); + ui->pollTimeMsSpin->setEnabled(true); + } + prefs.virtualSerialPort = settings->value("VirtualSerialPort", defPrefs.virtualSerialPort).toString(); int vspIndex = ui->vspCombo->findText(prefs.virtualSerialPort); if (vspIndex != -1) { @@ -1443,7 +1618,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); @@ -1464,6 +1642,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); @@ -1557,10 +1736,10 @@ void wfmain::loadSettings() ui->audioTXCodecCombo->setCurrentIndex(f); ui->audioRXCodecCombo->blockSignals(false); - rxSetup.name = settings->value("AudioOutput", "").toString(); + rxSetup.name = settings->value("AudioOutput", "Default Output Device").toString(); qInfo(logGui()) << "Got Audio Output from Settings: " << rxSetup.name; - txSetup.name = settings->value("AudioInput", "").toString(); + txSetup.name = settings->value("AudioInput", "Default Input Device").toString(); qInfo(logGui()) << "Got Audio Input from Settings: " << txSetup.name; @@ -1637,24 +1816,37 @@ void wfmain::loadSettings() memcpy(rigTemp->guid, QUuid::fromString(guid).toRfc4122().constData(), GUIDLEN); #endif - rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "").toString(); - rigTemp->txAudioSetup.name = settings->value("ServerAudioOutput", "").toString(); + rigTemp->rxAudioSetup.name = settings->value("ServerAudioInput", "Default Input Device").toString(); + rigTemp->txAudioSetup.name = settings->value("ServerAudioOutput", "Default Output Device").toString(); serverConfig.rigs.append(rigTemp); 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); + SERVERUSER user; + user.username = ""; + user.password = ""; + user.userType = 0; + serverConfig.users.append(user); + + ui->serverAddUserBtn->setEnabled(false); } settings->endGroup(); @@ -1693,6 +1885,72 @@ void wfmain::loadSettings() settings->endArray(); settings->endGroup(); + settings->beginGroup("Cluster"); + + prefs.clusterUdpEnable = settings->value("UdpEnabled", false).toBool(); + prefs.clusterTcpEnable = settings->value("TcpEnabled", false).toBool(); + prefs.clusterUdpPort = settings->value("UdpPort", 12060).toInt(); + ui->clusterUdpPortLineEdit->setText(QString::number(prefs.clusterUdpPort)); + ui->clusterUdpEnable->setChecked(prefs.clusterUdpEnable); + ui->clusterTcpEnable->setChecked(prefs.clusterTcpEnable); + + int numClusters = settings->beginReadArray("Servers"); + clusters.clear(); + if (numClusters > 0) { + { + for (int f = 0; f < numClusters; f++) + { + settings->setArrayIndex(f); + clusterSettings c; + c.server = settings->value("ServerName", "").toString(); + c.port = settings->value("Port", 7300).toInt(); + c.userName = settings->value("UserName", "").toString(); + c.password = settings->value("Password", "").toString(); + c.timeout = settings->value("Timeout", 0).toInt(); + c.isdefault = settings->value("Default", false).toBool(); + if (!c.server.isEmpty()) { + clusters.append(c); + } + } + int defaultCluster = 0; + ui->clusterServerNameCombo->blockSignals(true); + + for (int f = 0; f < clusters.size(); f++) + { + ui->clusterServerNameCombo->addItem(clusters[f].server); + if (clusters[f].isdefault) { + defaultCluster = f; + } + } + ui->clusterServerNameCombo->blockSignals(false); + + if (clusters.size() > defaultCluster) + { + ui->clusterServerNameCombo->setCurrentIndex(defaultCluster); + ui->clusterTcpPortLineEdit->blockSignals(true); + ui->clusterUsernameLineEdit->blockSignals(true); + ui->clusterPasswordLineEdit->blockSignals(true); + ui->clusterTimeoutLineEdit->blockSignals(true); + ui->clusterTcpPortLineEdit->setText(QString::number(clusters[defaultCluster].port)); + ui->clusterUsernameLineEdit->setText(clusters[defaultCluster].userName); + ui->clusterPasswordLineEdit->setText(clusters[defaultCluster].password); + ui->clusterTimeoutLineEdit->setText(QString::number(clusters[defaultCluster].timeout)); + ui->clusterTcpPortLineEdit->blockSignals(false); + ui->clusterUsernameLineEdit->blockSignals(false); + ui->clusterPasswordLineEdit->blockSignals(false); + ui->clusterTimeoutLineEdit->blockSignals(false); + } + } + } + else { + ui->clusterTcpPortLineEdit->setEnabled(false); + ui->clusterUsernameLineEdit->setEnabled(false); + ui->clusterPasswordLineEdit->setEnabled(false); + ui->clusterTimeoutLineEdit->setEnabled(false); + } + settings->endArray(); + + settings->endGroup(); } void wfmain::serverAddUserLine(const QString& user, const QString& pass, const int& type) @@ -1700,58 +1958,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); } @@ -1783,21 +2092,15 @@ void wfmain::on_serverRXAudioInputCombo_currentIndexChanged(int value) if (!serverConfig.rigs.isEmpty()) { if (prefs.audioSystem == qtAudio) { - QVariant v = ui->serverRXAudioInputCombo->itemData(value); -#if QT_VERSION >= 0x060000 - serverConfig.rigs.first()->rxAudioSetup.port = v.value(); -#else - serverConfig.rigs.first()->rxAudioSetup.port = v.value(); -#endif + serverConfig.rigs.first()->rxAudioSetup.port = audioDev->getInputDeviceInfo(value); } else { - serverConfig.rigs.first()->rxAudioSetup.portInt = ui->serverRXAudioInputCombo->itemData(value).toInt(); + serverConfig.rigs.first()->rxAudioSetup.portInt = audioDev->getInputDeviceInt(value); } - serverConfig.rigs.first()->rxAudioSetup.name = ui->serverRXAudioInputCombo->itemText(value); + serverConfig.rigs.first()->rxAudioSetup.name = audioDev->getInputName(value); } - } void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) @@ -1806,45 +2109,18 @@ void wfmain::on_serverTXAudioOutputCombo_currentIndexChanged(int value) if (!serverConfig.rigs.isEmpty()) { if (prefs.audioSystem == qtAudio) { - QVariant v = ui->serverTXAudioOutputCombo->itemData(value); -#if QT_VERSION >= 0x060000 - serverConfig.rigs.first()->txAudioSetup.port = v.value(); -#else - serverConfig.rigs.first()->txAudioSetup.port = v.value(); -#endif + serverConfig.rigs.first()->txAudioSetup.port = audioDev->getOutputDeviceInfo(value); } else { - serverConfig.rigs.first()->txAudioSetup.portInt = ui->serverTXAudioOutputCombo->itemData(value).toInt(); + serverConfig.rigs.first()->txAudioSetup.portInt = audioDev->getOutputDeviceInt(value); } - serverConfig.rigs.first()->txAudioSetup.name = ui->serverTXAudioOutputCombo->itemText(value); + serverConfig.rigs.first()->txAudioSetup.name = audioDev->getOutputName(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() @@ -1852,11 +2128,14 @@ void wfmain::saveSettings() qInfo(logSystem()) << "Saving settings to " << settings->fileName(); // Basic things to load: + settings->beginGroup("Program"); + settings->setValue("version", QString(WFVIEW_VERSION)); + settings->endGroup(); + // UI: (full screen, dark theme, draw peaks, colors, etc) 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); @@ -1873,6 +2152,8 @@ void wfmain::saveSettings() settings->setValue("ConfirmExit", prefs.confirmExit); settings->setValue("ConfirmPowerOff", prefs.confirmPowerOff); settings->setValue("Meter2Type", (int)prefs.meter2Type); + settings->setValue("ClickDragTuning", prefs.clickDragTuningEnable); + settings->endGroup(); // Radio and Comms: C-IV addr, port to use @@ -1880,6 +2161,7 @@ void wfmain::saveSettings() settings->setValue("RigCIVuInt", prefs.radioCIVAddr); settings->setValue("CIVisRadioModel", prefs.CIVisRadioModel); settings->setValue("ForceRTSasPTT", prefs.forceRTSasPTT); + settings->setValue("polling_ms", prefs.polling_ms); // 0 = automatic settings->setValue("SerialPortRadio", prefs.serialPortRadio); settings->setValue("SerialPortBaud", prefs.serialPortBaud); settings->setValue("VirtualSerialPort", prefs.virtualSerialPort); @@ -1938,50 +2220,40 @@ 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("passband", p->passband.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->setValue("clusterSpots", p->clusterSpots.name(QColor::HexArgb)); + } + settings->endArray(); settings->endGroup(); settings->beginGroup("Server"); @@ -2016,11 +2288,29 @@ void wfmain::saveSettings() } settings->endArray(); - qInfo() << "Server config stored"; - settings->endGroup(); + settings->beginGroup("Cluster"); + settings->setValue("UdpEnabled", prefs.clusterUdpEnable); + settings->setValue("TcpEnabled", prefs.clusterTcpEnable); + settings->setValue("UdpPort", prefs.clusterUdpPort); + settings->beginWriteArray("Servers"); + + for (int f = 0; f < clusters.count(); f++) + { + settings->setArrayIndex(f); + settings->setValue("ServerName", clusters[f].server); + settings->setValue("UserName", clusters[f].userName); + settings->setValue("Port", clusters[f].port); + settings->setValue("Password", clusters[f].password); + settings->setValue("Timeout", clusters[f].timeout); + settings->setValue("Default", clusters[f].isdefault); + } + + settings->endArray(); + + settings->endGroup(); settings->sync(); // Automatic, not needed (supposedly) } @@ -2048,6 +2338,7 @@ void wfmain::showHideSpectrum(bool show) ui->wfLengthSlider->setEnabled(show); ui->wfthemeCombo->setVisible(show); ui->toFixedBtn->setVisible(show); + ui->customEdgeBtn->setVisible(show); ui->clearPeakBtn->setVisible(show); // And the labels: @@ -2097,17 +2388,6 @@ void wfmain::prepareWf(unsigned int wfLength) QByteArray empty((int)spectWidth, '\x01'); spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); - if(spectrumPlasmaSize == 0) - spectrumPlasmaSize = 128; - - //spectrumPlasma.resize(spectrumPlasmaSize); - for(unsigned int p=0; p < spectrumPlasmaSize; p++) - { - spectrumPlasma.append(empty); - } - - //wfimage.resize(wfLengthMax); - if((unsigned int)wfimage.size() < wfLengthMax) { unsigned int i=0; @@ -2127,7 +2407,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) @@ -2579,14 +2859,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); @@ -2618,79 +2890,118 @@ 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->passband = 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); + + p->clusterSpots = QColor(Qt::red); + + //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->passband = QColor("#32ffffff"); + + 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); + p->clusterSpots = QColor(Qt::red); + 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->passband = QColor("#64000080"); + + 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); + p->clusterSpots = QColor(Qt::red); + break; + } + + case 2: + case 3: + case 4: + default: + break; + + } + ui->colorPresetCombo->setItemText(presetNumber, *(p->presetName)); } void wfmain::doCmd(commandtype cmddata) @@ -2817,10 +3128,8 @@ void wfmain::doCmd(commandtype cmddata) doCmd(cmd); break; } - } - void wfmain::doCmd(cmds cmd) { // Use this function to take action upon a command. @@ -2830,8 +3139,12 @@ void wfmain::doCmd(cmds cmd) //qInfo(logSystem()) << "NOOP"; break; case cmdGetRigID: - emit getRigID(); - break; + if(!haveRigCaps) + { + emit getRigID(); + issueDelayedCommand(cmdGetRigID); + } + break; case cmdGetRigCIV: // if(!know rig civ already) if(!haveRigCaps) @@ -2879,6 +3192,9 @@ void wfmain::doCmd(cmds cmd) case cmdGetDuplexMode: emit getDuplexMode(); break; + case cmdGetPassband: + emit getPassband(); + break; case cmdGetTone: emit getTone(); break; @@ -3255,9 +3571,31 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. //wfCeiling = rigCaps.spectAmpMax; //plotCeiling = rigCaps.spectAmpMax; - ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); + if(rigCaps.hasSpectrum) + { + ui->topLevelSlider->setVisible(true); + ui->labelTop->setVisible(true); + ui->botLevelSlider->setVisible(true); + ui->labelBot->setVisible(true); + ui->scopeRefLevelSlider->setVisible(true); + ui->refLabel->setVisible(true); + ui->wfLengthSlider->setVisible(true); + ui->lenLabel->setVisible(true); + ui->topLevelSlider->setMaximum(rigCaps.spectAmpMax); + ui->botLevelSlider->setMaximum(rigCaps.spectAmpMax); + } else { + ui->scopeRefLevelSlider->setVisible(false); + ui->refLabel->setVisible(false); + ui->wfLengthSlider->setVisible(false); + ui->lenLabel->setVisible(false); + ui->topLevelSlider->setVisible(false); + ui->labelTop->setVisible(false); + ui->botLevelSlider->setVisible(false); + ui->labelBot->setVisible(false); + } haveRigCaps = true; + // Added so that server receives rig capabilities. emit sendRigCaps(rigCaps); rpt->setRig(rigCaps); @@ -3385,16 +3723,15 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) setBandButtons(); - ui->tuneEnableChk->setEnabled(rigCaps.hasATU); ui->tuneNowBtn->setEnabled(rigCaps.hasATU); - ui->useRTSforPTTchk->blockSignals(true); - ui->useRTSforPTTchk->setChecked(rigCaps.useRTSforPTT); - ui->useRTSforPTTchk->blockSignals(false); + ui->useRTSforPTTchk->setChecked(prefs.forceRTSasPTT); - 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) @@ -3407,7 +3744,12 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) issueDelayedCommand(cmdGetFreq); issueDelayedCommand(cmdGetMode); // recalculate command timing now that we know the rig better: - calculateTimingParameters(); + if(prefs.polling_ms != 0) + { + changePollTiming(prefs.polling_ms, true); + } else { + calculateTimingParameters(); + } initPeriodicCommands(); // Set the second meter here as I suspect we need to be connected for it to work? @@ -3422,6 +3764,7 @@ void wfmain::receiveRigID(rigCapabilities rigCaps) } } } + updateSizes(ui->tabWidget->currentIndex()); } void wfmain::initPeriodicCommands() @@ -3448,6 +3791,11 @@ void wfmain::initPeriodicCommands() insertSlowPeriodicCommand(cmdGetAntenna, 128); } insertSlowPeriodicCommand(cmdGetDuplexMode, 128); + + if (rigCaps.hasSpectrum) { + // Get passband + insertPeriodicCommand(cmdGetPassband, 128); + } } void wfmain::insertPeriodicCommand(cmds cmd, unsigned char priority) @@ -3561,8 +3909,12 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e // TODO: create non-button function to do this // This will break if the button is ever moved or renamed. on_clearPeakBtn_clicked(); + } else { + plasmaPrepared = false; + preparePlasma(); } - // TODO: Add clear-out for the buffer + // Inform other threads (cluster) that the frequency range has changed. + emit setFrequencyRange(startFreq, endFreq); } oldLowerFreq = startFreq; @@ -3584,6 +3936,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; @@ -3604,35 +3957,78 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } plasmaMutex.lock(); spectrumPlasma.push_front(spectrum); - spectrumPlasma.pop_back(); - //spectrumPlasma.resize(spectrumPlasmaSize); + if(spectrumPlasma.size() > (int)spectrumPlasmaSize) + { + spectrumPlasma.pop_back(); + } plasmaMutex.unlock(); - // HACK DO NOT CHECK IN: - drawPeaks = false; - drawPlasma = true; if(!spectrumDrawLock) { if((plotFloor != oldPlotFloor) || (plotCeiling != oldPlotCeiling)) updateRange = true; - //ui->qcp->addGraph(); +#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); + freqIndicatorLine->start->setCoords(freq.MHzDouble, 0); + freqIndicatorLine->end->setCoords(freq.MHzDouble, rigCaps.spectAmpMax); + + if (currentModeInfo.mk == modeLSB || currentModeInfo.mk == modePSK_R) { + passbandIndicator->topLeft->setCoords(freq.MHzDouble - passBand - 0.0001, 0); + passbandIndicator->bottomRight->setCoords(freq.MHzDouble - 0.0001, rigCaps.spectAmpMax); + } + else if (currentModeInfo.mk == modeUSB || currentModeInfo.mk == modePSK) { + passbandIndicator->topLeft->setCoords(freq.MHzDouble + 0.0001, 0); + passbandIndicator->bottomRight->setCoords(freq.MHzDouble + 0.0001 + passBand, rigCaps.spectAmpMax); + } + else + { + if (currentModeInfo.mk == modeFM) { + if (currentModeInfo.filter == 1) + passBand = 0.015; + else if (currentModeInfo.filter == 2) + passBand = 0.010; + else + passBand = 0.007; + } + passbandIndicator->topLeft->setCoords(freq.MHzDouble - (passBand / 2), 0); + passbandIndicator->bottomRight->setCoords(freq.MHzDouble + (passBand / 2), rigCaps.spectAmpMax); + } + + } + if(underlayMode == underlayPeakHold) { plot->graph(1)->setData(x,y2, true); // peaks } else if (underlayMode != underlayNone) { computePlasma(); - plot->graph(1)->setData(x,spectrumPlasmaLine); + 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,rigCaps.spectAmpMax); + } + + 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 + } +#endif if(updateRange) plot->yAxis->setRange(prefs.plotFloor, prefs.plotCeiling); @@ -3667,6 +4063,23 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e } } +void wfmain::preparePlasma() +{ + if(plasmaPrepared) + return; + + if(spectrumPlasmaSize == 0) + spectrumPlasmaSize = 128; + + plasmaMutex.lock(); + spectrumPlasma.clear(); + + + spectrumPlasma.squeeze(); + plasmaMutex.unlock(); + plasmaPrepared = true; +} + void wfmain::computePlasma() { plasmaMutex.lock(); @@ -3679,7 +4092,7 @@ void wfmain::computePlasma() { for(int pos=0; pos < specPlasmaSize; pos++) { - spectrumPlasmaLine[col] += spectrumPlasma[pos][col]; + spectrumPlasmaLine[col] += (unsigned char)spectrumPlasma[pos][col]; } spectrumPlasmaLine[col] = spectrumPlasmaLine[col] / specPlasmaSize; } @@ -3689,13 +4102,12 @@ void wfmain::computePlasma() { for(int pos=0; pos < specPlasmaSize; pos++) { - if((double)(spectrumPlasma[pos][col]) > spectrumPlasmaLine[col]) - spectrumPlasmaLine[col] = spectrumPlasma[pos][col]; + if((double)((unsigned char)spectrumPlasma[pos][col]) > spectrumPlasmaLine[col]) + spectrumPlasmaLine[col] = (unsigned char)spectrumPlasma[pos][col]; } } } plasmaMutex.unlock(); - } void wfmain::receiveSpectrumMode(spectrumMode spectMode) @@ -3709,6 +4121,7 @@ void wfmain::receiveSpectrumMode(spectrumMode spectMode) ui->spectrumModeCombo->blockSignals(false); } } + setUISpectrumControlsToMode(spectMode); } @@ -3760,10 +4173,84 @@ void wfmain::handleWFDoubleClick(QMouseEvent *me) } } -void wfmain::handlePlotClick(QMouseEvent *me) +void wfmain::handlePlotClick(QMouseEvent* me) { - double x = plot->xAxis->pixelToCoord(me->pos().x()); - showStatusBarText(QString("Selected %1 MHz").arg(x)); + QCPAbstractItem* item = plot->itemAt(me->pos(), true); + QCPItemText* textItem = dynamic_cast (item); + if (me->button() == Qt::RightButton && textItem != nullptr) { + QMap::iterator spot = clusterSpots.find(textItem->text()); + if (spot != clusterSpots.end() && spot.key() == textItem->text()) { + /* parent and children are destroyed on close */ + QDialog* spotDialog = new QDialog(); + QVBoxLayout* vlayout = new QVBoxLayout; + //spotDialog->setFixedSize(240, 100); + spotDialog->setBaseSize(1, 1); + spotDialog->setWindowTitle(spot.value()->dxcall); + QLabel* dxcall = new QLabel(QString("DX:%1").arg(spot.value()->dxcall)); + QLabel* spotter = new QLabel(QString("Spotter:%1").arg(spot.value()->spottercall)); + QLabel* frequency = new QLabel(QString("Frequency:%1 MHz").arg(spot.value()->frequency)); + QLabel* comment = new QLabel(QString("Comment:%1").arg(spot.value()->comment)); + QAbstractButton* bExit = new QPushButton("Close"); + vlayout->addWidget(dxcall); + vlayout->addWidget(spotter); + vlayout->addWidget(frequency); + vlayout->addWidget(comment); + vlayout->addWidget(bExit); + spotDialog->setLayout(vlayout); + spotDialog->show(); + spotDialog->connect(bExit, SIGNAL(clicked()), spotDialog, SLOT(close())); + } + } + else if (textItem != nullptr) + { + QMap::iterator spot = clusterSpots.find(textItem->text()); + if (spot != clusterSpots.end() && spot.key() == textItem->text()) + { + qInfo(logGui()) << "Clicked on spot:" << textItem->text(); + freqt freqGo; + freqGo.Hz = ( spot.value()->frequency)*1E6; + freqGo.MHzDouble = spot.value()->frequency; + issueCmdUniquePriority(cmdSetFreq, freqGo); + } + } + else if (prefs.clickDragTuningEnable) + { + double x = plot->xAxis->pixelToCoord(me->pos().x()); + showStatusBarText(QString("Selected %1 MHz").arg(x)); + this->mousePressFreq = x; + } +} + +void wfmain::handlePlotMouseRelease(QMouseEvent* me) +{ + QCPAbstractItem* item = plot->itemAt(me->pos(), true); + QCPItemText* textItem = dynamic_cast (item); + + if (textItem == nullptr && prefs.clickDragTuningEnable) { + this->mouseReleaseFreq = plot->xAxis->pixelToCoord(me->pos().x()); + double delta = mouseReleaseFreq - mousePressFreq; + qInfo(logGui()) << "Mouse release delta: " << delta; + + } +} + +void wfmain::handlePlotMouseMove(QMouseEvent *me) +{ + QCPAbstractItem* item = plot->itemAt(me->pos(), true); + QCPItemText* textItem = dynamic_cast (item); + if(me->buttons() == Qt::LeftButton && textItem==nullptr && prefs.clickDragTuningEnable) + { + double delta = plot->xAxis->pixelToCoord(me->pos().x()) - mousePressFreq; + qInfo(logGui()) << "Mouse moving delta: " << delta; + if( (( delta < -0.0001 ) || (delta > 0.0001)) && ((delta < 0.501) && (delta > -0.501)) ) + { + freqt freqGo; + freqGo.Hz = ( freq.MHzDouble + delta)*1E6; + //freqGo.Hz = roundFrequency(freqGo.Hz, tsWfScrollHz); + freqGo.MHzDouble = (float)freqGo.Hz / 1E6; + issueCmdUniquePriority(cmdSetFreq, freqGo); + } + } } void wfmain::handleWFClick(QMouseEvent *me) @@ -3850,6 +4337,8 @@ void wfmain::receiveMode(unsigned char mode, unsigned char filter) } } currentModeIndex = mode; + currentModeInfo.mk = (mode_kind)mode; + currentModeInfo.filter = filter; } else { qInfo(logSystem()) << __func__ << "Invalid mode " << mode << " received. "; } @@ -3867,7 +4356,6 @@ void wfmain::receiveMode(unsigned char mode, unsigned char filter) (void)filter; - // Note: we need to know if the DATA mode is active to reach mode-D // some kind of queued query: if (rigCaps.hasDataModes && rigCaps.hasTransmit) @@ -3890,30 +4378,11 @@ void wfmain::on_clearPeakBtn_clicked() if(haveRigCaps) { spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); + clearPlasmaBuffer(); } 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) @@ -4053,7 +4522,29 @@ void wfmain::on_fCEbtn_clicked() void wfmain::on_spectrumModeCombo_currentIndexChanged(int index) { - emit setScopeMode(static_cast(ui->spectrumModeCombo->itemData(index).toInt())); + spectrumMode smode = static_cast(ui->spectrumModeCombo->itemData(index).toInt()); + emit setScopeMode(smode); + setUISpectrumControlsToMode(smode); +} + +void wfmain::setUISpectrumControlsToMode(spectrumMode smode) +{ + if((smode==spectModeCenter) || (smode==spectModeScrollC)) + { + ui->specEdgeLabel->hide(); + ui->scopeEdgeCombo->hide(); + ui->customEdgeBtn->hide(); + ui->toFixedBtn->show(); + ui->specSpanLabel->show(); + ui->scopeBWCombo->show(); + } else { + ui->specEdgeLabel->show(); + ui->scopeEdgeCombo->show(); + ui->customEdgeBtn->show(); + ui->toFixedBtn->hide(); + ui->specSpanLabel->hide(); + ui->scopeBWCombo->hide(); + } } void wfmain::on_fEnterBtn_clicked() @@ -4147,7 +4638,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; @@ -4155,12 +4646,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); @@ -4168,50 +4653,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: @@ -4221,12 +4681,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); @@ -4662,6 +5118,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) @@ -4715,19 +5172,13 @@ void wfmain::on_audioOutputCombo_currentIndexChanged(int value) { if (prefs.audioSystem == qtAudio) { - QVariant v = ui->audioOutputCombo->itemData(value); -#if QT_VERSION >= 0x060000 - rxSetup.port = v.value(); -#else - rxSetup.port = v.value(); -#endif - + rxSetup.port = audioDev->getOutputDeviceInfo(value); } else { - rxSetup.portInt = ui->audioOutputCombo->itemData(value).toInt(); + rxSetup.portInt = audioDev->getOutputDeviceInt(value); } - rxSetup.name = ui->audioOutputCombo->itemText(value); + rxSetup.name = audioDev->getOutputName(value); qDebug(logGui()) << "Changed audio output to:" << rxSetup.name; } @@ -4735,18 +5186,13 @@ void wfmain::on_audioInputCombo_currentIndexChanged(int value) { if (prefs.audioSystem == qtAudio) { - QVariant v = ui->audioInputCombo->itemData(value); -#if QT_VERSION >= 0x060000 - txSetup.port = v.value(); -#else - txSetup.port = v.value(); -#endif + txSetup.port = audioDev->getInputDeviceInfo(value); } else { - txSetup.portInt = ui->audioInputCombo->itemData(value).toInt(); + txSetup.portInt = audioDev->getInputDeviceInt(value); } - txSetup.name = ui->audioInputCombo->itemText(value); + txSetup.name = audioDev->getInputName(value); qDebug(logGui()) << "Changed audio input to:" << txSetup.name; } @@ -4788,9 +5234,28 @@ void wfmain::on_vspCombo_currentIndexChanged(int value) void wfmain::on_toFixedBtn_clicked() { - emit setScopeFixedEdge(oldLowerFreq, oldUpperFreq, ui->scopeEdgeCombo->currentIndex()+1); - emit setScopeEdge(ui->scopeEdgeCombo->currentIndex()+1); - issueDelayedCommand(cmdScopeFixedMode); + int currentEdge = ui->scopeEdgeCombo->currentIndex(); + bool dialogOk = false; + bool numOk = false; + + QStringList edges; + edges << "1" << "2" << "3" << "4"; + + QString item = QInputDialog::getItem(this, "Select Edge", "Edge to replace:", edges, currentEdge, false, &dialogOk); + + if(dialogOk) + { + int edge = QString(item).toInt(&numOk,10); + if(numOk) + { + emit setScopeFixedEdge(oldLowerFreq, oldUpperFreq, edge); + emit setScopeEdge(edge); + ui->scopeEdgeCombo->blockSignals(true); + ui->scopeEdgeCombo->setCurrentIndex(edge-1); + ui->scopeEdgeCombo->blockSignals(false); + issueDelayedCommand(cmdScopeFixedMode); + } + } } @@ -4800,16 +5265,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) @@ -5101,6 +5575,22 @@ void wfmain::receiveLANGain(unsigned char level) processModLevel(inputLAN, level); } +void wfmain::receivePassband(quint16 pass) +{ +/* int calc; + if (currentModeInfo.mk == modeAM) { + calc = 200 + (pass * 200); + } + else if (pass <= 10) + { + calc = 50 + (pass * 50); + } + else { + calc = 600 + ((pass - 10) * 100); + } */ + passBand = (double)(pass / 1000000.0); +} + void wfmain::receiveMeter(meterKind inMeter, unsigned char level) { @@ -5120,6 +5610,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; } @@ -5420,6 +5916,10 @@ void wfmain::calculateTimingParameters() qInfo(logSystem()) << "Delay command interval timing: " << delayedCommand->interval() << "ms"; + ui->pollTimeMsSpin->blockSignals(true); + ui->pollTimeMsSpin->setValue(delayedCommand->interval()); + ui->pollTimeMsSpin->blockSignals(false); + // Normal: delayedCmdIntervalLAN_ms = delayedCommand->interval(); delayedCmdIntervalSerial_ms = delayedCommand->interval(); @@ -5574,7 +6074,7 @@ void wfmain::setBandButtons() for(unsigned int i=0; i < rigCaps.bands.size(); i++) { bandSel = rigCaps.bands.at(i); - switch(bandSel) + switch(bandSel.band) { case(band23cm): showButton(ui->band23cmbtn); @@ -5701,20 +6201,6 @@ void wfmain::on_wfLengthSlider_valueChanged(int value) prepareWf(value); } -void wfmain::on_pollingBtn_clicked() -{ - bool ok; - int timing = 0; - timing = QInputDialog::getInt(this, "wfview Radio Polling Setup", "Poll Timing Interval (ms)", delayedCommand->interval(), 1, 200, 1, &ok ); - - if(ok && timing) - { - delayedCommand->setInterval( timing ); - qInfo(logSystem()) << "User changed radio polling interval to " << timing << "ms."; - showStatusBarText("User changed radio polling interval to " + QString("%1").arg(timing) + "ms."); - } -} - void wfmain::on_wfAntiAliasChk_clicked(bool checked) { colorMap->setAntialiased(checked); @@ -5789,7 +6275,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; @@ -5890,244 +6377,56 @@ void wfmain::on_radioStatusBtn_clicked() void wfmain::setAudioDevicesUI() { - // Enumerate audio devices, need to do before settings are loaded, - // First clear all existing entries - ui->audioInputCombo->blockSignals(true); - ui->audioOutputCombo->blockSignals(true); - ui->serverTXAudioOutputCombo->blockSignals(true); - ui->serverRXAudioInputCombo->blockSignals(true); - - ui->audioInputCombo->clear(); - ui->audioOutputCombo->clear(); - ui->serverTXAudioOutputCombo->clear(); - ui->serverRXAudioInputCombo->clear(); - - qDebug(logSystem()) << "Finding audio devices, output=" << rxSetup.name << "input="<audioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverRXAudioInputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - inCount++; -#ifdef Q_OS_WIN - } -#endif - } - - const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); - for (const QAudioDeviceInfo& deviceInfo : audioOutputs) { -#ifdef Q_OS_WIN - if (deviceInfo.realm() == "wasapi") { -#endif - ui->audioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.deviceName(), QVariant::fromValue(deviceInfo)); - outCount++; -#ifdef Q_OS_WIN - } -#endif - } -#else - ui->audioOutputCombo->clear(); - const auto audioOutputs = mediaDevices.audioOutputs(); - for (const QAudioDevice& deviceInfo : audioOutputs) { - ui->audioOutputCombo->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo)); - ui->serverTXAudioOutputCombo->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo)); - outCount++; - } - ui->audioInputCombo->clear(); - const auto audioInputs = mediaDevices.audioInputs(); - for (const QAudioDevice& deviceInfo : audioInputs) { - ui->audioInputCombo->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo)); - ui->serverRXAudioInputCombo->addItem(deviceInfo.description(), QVariant::fromValue(deviceInfo)); - inCount++; - } -#endif - - break; - } - case portAudio: - { - PaError err; - - err = Pa_Initialize(); - - if (err != paNoError) - { - qInfo(logAudio()) << "ERROR: Cannot initialize Portaudio"; - return; - } - - qInfo(logAudio()) << "PortAudio version: " << Pa_GetVersionInfo()->versionText; - - int numDevices = Pa_GetDeviceCount(); - qInfo(logAudio()) << "Pa_CountDevices returned" << numDevices; - - const PaDeviceInfo* info; - for (int i = 0; i < numDevices; i++) - { - info = Pa_GetDeviceInfo(i); - if (info->maxInputChannels > 0) { - qDebug(logAudio()) << (i == Pa_GetDefaultInputDevice() ? "*" : " ") << "(" << i << ") Input Device : " << info->name; - - ui->audioInputCombo->addItem(info->name, i); - ui->serverRXAudioInputCombo->addItem(info->name, i); - if (i == Pa_GetDefaultInputDevice()) { - defaultAudioInputIndex = inCount; - } - inCount++; - } - if (info->maxOutputChannels > 0) { - qDebug(logAudio()) << (i == Pa_GetDefaultOutputDevice() ? "*" : " ") << "(" << i << ") Output Device : " << info->name; - ui->audioOutputCombo->addItem(info->name, i); - ui->serverTXAudioOutputCombo->addItem(info->name, i); - if (i == Pa_GetDefaultOutputDevice()) { - defaultAudioOutputIndex = outCount; - } - outCount++; - } - } - break; - } - case rtAudio: - { - Pa_Terminate(); - -#if defined(Q_OS_LINUX) - RtAudio* audio = new RtAudio(RtAudio::Api::LINUX_ALSA); -#elif defined(Q_OS_WIN) - RtAudio* audio = new RtAudio(RtAudio::Api::WINDOWS_WASAPI); -#elif defined(Q_OS_MACX) - RtAudio* audio = new RtAudio(RtAudio::Api::MACOSX_CORE); -#endif - - - // Enumerate audio devices, need to do before settings are loaded. - std::map apiMap; - apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio"; - apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO"; - apiMap[RtAudio::WINDOWS_DS] = "Windows DirectSound"; - apiMap[RtAudio::WINDOWS_WASAPI] = "Windows WASAPI"; - apiMap[RtAudio::UNIX_JACK] = "Jack Client"; - apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA"; - apiMap[RtAudio::LINUX_PULSE] = "Linux PulseAudio"; - apiMap[RtAudio::LINUX_OSS] = "Linux OSS"; - apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy"; - - std::vector< RtAudio::Api > apis; - RtAudio::getCompiledApi(apis); - - qInfo(logAudio()) << "RtAudio Version " << QString::fromStdString(RtAudio::getVersion()); - - qInfo(logAudio()) << "Compiled APIs:"; - for (unsigned int i = 0; i < apis.size(); i++) { - qInfo(logAudio()) << " " << QString::fromStdString(apiMap[apis[i]]); - } - - RtAudio::DeviceInfo info; - - qInfo(logAudio()) << "Current API: " << QString::fromStdString(apiMap[audio->getCurrentApi()]); - - unsigned int devices = audio->getDeviceCount(); - qInfo(logAudio()) << "Found " << devices << " audio device(s) *=default"; - - for (unsigned int i = 1; i < devices; i++) { - info = audio->getDeviceInfo(i); - if (info.inputChannels > 0) { - qInfo(logAudio()) << (info.isDefaultInput ? "*" : " ") << "(" << i << ") Input Device : " << QString::fromStdString(info.name); - ui->audioInputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverRXAudioInputCombo->addItem(QString::fromStdString(info.name), i); - if (info.isDefaultInput) { - defaultAudioInputIndex = inCount; - } - inCount++; - } - if (info.outputChannels > 0) { - qInfo(logAudio()) << (info.isDefaultOutput ? "*" : " ") << "(" << i << ") Output Device : " << QString::fromStdString(info.name); - ui->audioOutputCombo->addItem(QString::fromStdString(info.name), i); - ui->serverTXAudioOutputCombo->addItem(QString::fromStdString(info.name), i); - if (info.isDefaultOutput) { - defaultAudioOutputIndex = outCount; - } - outCount++; - } - } - - delete audio; - break; - } - + if (audioDev == Q_NULLPTR) { + audioDev = new audioDevices(prefs.audioSystem, QFontMetrics(ui->audioInputCombo->font())); } - - // Stop blocking signals so we can set the current values + audioDev->setAudioType(prefs.audioSystem); + audioDev->enumerate(); + + ui->audioInputCombo->blockSignals(true); + ui->audioInputCombo->clear(); + ui->audioInputCombo->addItems(audioDev->getInputs()); + ui->audioInputCombo->setCurrentIndex(-1); + ui->audioInputCombo->setStyleSheet(QString("QComboBox QAbstractItemView {min-width: %1px;}").arg(audioDev->getNumCharsIn() + 30)); ui->audioInputCombo->blockSignals(false); - ui->audioOutputCombo->blockSignals(false); - ui->serverTXAudioOutputCombo->blockSignals(false); - ui->serverRXAudioInputCombo->blockSignals(false); + ui->audioInputCombo->setCurrentIndex(audioDev->findInput("Client", txSetup.name)); + ui->audioOutputCombo->blockSignals(true); + ui->audioOutputCombo->clear(); + ui->audioOutputCombo->addItems(audioDev->getOutputs()); + ui->audioOutputCombo->setCurrentIndex(-1); + ui->audioOutputCombo->setStyleSheet(QString("QComboBox QAbstractItemView {min-width: %1px;}").arg(audioDev->getNumCharsOut() + 30)); + ui->audioOutputCombo->blockSignals(false); + ui->audioOutputCombo->setCurrentIndex(audioDev->findOutput("Client", rxSetup.name)); + + ui->serverTXAudioOutputCombo->blockSignals(true); + ui->serverTXAudioOutputCombo->clear(); + ui->serverTXAudioOutputCombo->addItems(audioDev->getOutputs()); + ui->serverTXAudioOutputCombo->setCurrentIndex(-1); + ui->serverTXAudioOutputCombo->setStyleSheet(QString("QComboBox QAbstractItemView {min-width: %1px;}").arg(audioDev->getNumCharsOut() + 30)); + ui->serverTXAudioOutputCombo->blockSignals(false); + + ui->serverRXAudioInputCombo->blockSignals(true); + ui->serverRXAudioInputCombo->clear(); + ui->serverRXAudioInputCombo->addItems(audioDev->getInputs()); + ui->serverRXAudioInputCombo->setCurrentIndex(-1); + ui->serverRXAudioInputCombo->setStyleSheet(QString("QComboBox QAbstractItemView {min-width: %1px;}").arg(audioDev->getNumCharsIn()+30)); + ui->serverRXAudioInputCombo->blockSignals(false); rxSetup.type = prefs.audioSystem; txSetup.type = prefs.audioSystem; - int audioInputIndex = ui->audioInputCombo->findText(txSetup.name); - if (audioInputIndex != -1) { - ui->audioInputCombo->setCurrentIndex(audioInputIndex); - } - else { - qDebug(logSystem()) << "Audio input not found"; - ui->audioInputCombo->setCurrentIndex(defaultAudioInputIndex); - } - - int audioOutputIndex = ui->audioOutputCombo->findText(rxSetup.name); - if (audioOutputIndex != -1) { - ui->audioOutputCombo->setCurrentIndex(audioOutputIndex); - } - else { - qDebug(logSystem()) << "Audio output not found"; - ui->audioOutputCombo->setCurrentIndex(defaultAudioOutputIndex); - } - if (!serverConfig.rigs.isEmpty()) - { - qInfo(logGui()) << "Got Server Audio Input: " << serverConfig.rigs.first()->rxAudioSetup.name; - serverConfig.rigs.first()->rxAudioSetup.type = prefs.audioSystem; serverConfig.rigs.first()->txAudioSetup.type = prefs.audioSystem; - int serverAudioInputIndex = ui->serverRXAudioInputCombo->findText(serverConfig.rigs.first()->rxAudioSetup.name); - if (serverAudioInputIndex != -1) { - ui->serverRXAudioInputCombo->setCurrentIndex(serverAudioInputIndex); - } - else { - // Set to default - ui->serverRXAudioInputCombo->setCurrentIndex(defaultAudioInputIndex); - } - - qInfo(logGui()) << "Got Server Audio Output: " << serverConfig.rigs.first()->txAudioSetup.name; - int serverAudioOutputIndex = ui->serverTXAudioOutputCombo->findText(serverConfig.rigs.first()->txAudioSetup.name); - if (serverAudioOutputIndex != -1) { - ui->serverTXAudioOutputCombo->setCurrentIndex(serverAudioOutputIndex); - } - else { - ui->serverTXAudioOutputCombo->setCurrentIndex(defaultAudioOutputIndex); - } + ui->serverRXAudioInputCombo->setCurrentIndex(audioDev->findInput("Server", serverConfig.rigs.first()->rxAudioSetup.name)); + ui->serverTXAudioOutputCombo->setCurrentIndex(audioDev->findOutput("Server", serverConfig.rigs.first()->txAudioSetup.name)); } + qDebug(logSystem()) << "Audio devices done."; } @@ -6135,6 +6434,18 @@ 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_audioSystemServerCombo_currentIndexChanged(int value) +{ + 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) @@ -6159,6 +6470,7 @@ void wfmain::on_underlayBufferSlider_valueChanged(int value) { resizePlasmaBuffer(value); prefs.underlayBufferSize = value; + spectrumPlasmaSize = value; } void wfmain::resizePlasmaBuffer(int newSize) @@ -6187,6 +6499,18 @@ void wfmain::resizePlasmaBuffer(int newSize) plasmaMutex.unlock(); } +void wfmain::clearPlasmaBuffer() +{ + QByteArray empty((int)spectWidth, '\x01'); + plasmaMutex.lock(); + int pSize = spectrumPlasma.size(); + for(int i=0; i < pSize; i++) + { + spectrumPlasma[i] = empty; + } + plasmaMutex.unlock(); +} + void wfmain::on_underlayNone_toggled(bool checked) { ui->underlayBufferSlider->setDisabled(checked); @@ -6207,7 +6531,6 @@ void wfmain::on_underlayPeakHold_toggled(bool checked) prefs.underlayMode = underlayMode; on_clearPeakBtn_clicked(); } - } void wfmain::on_underlayPeakBuffer_toggled(bool checked) @@ -6234,26 +6557,1123 @@ void wfmain::on_underlayAverageBuffer_toggled(bool checked) 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)); + emit getRigID(); +} -// bool ok; -// int height = QInputDialog::getInt(this, "wfview window fixed height", "number: ", 350, 1, 500, 1, &ok ); +// ---------- color helper functions: ---------- // -// 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)); +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)); + passbandIndicator->setPen(QPen(cp->passband)); + passbandIndicator->setBrush(QBrush(cp->passband)); + + 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); + + clusterColor = cp->clusterSpots; +} + +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->clusterPopOutBtn->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->clusterPopOutBtn->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.passband, ui->colorEditPassband, ui->colorSwatchPassband); + + 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); + + setEditAndLedFromColor(p.clusterSpots, ui->colorEditClusterSpots, ui->colorSwatchClusterSpots); + + 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::on_colorPresetCombo_currentIndexChanged(int index) +{ + prefs.currentColorPresetNumber = index; + loadColorPresetToUIandPlots(index); +} + +void wfmain::on_colorRevertPresetBtn_clicked() +{ + int pn = ui->colorPresetCombo->currentIndex(); + setDefaultColors(pn); + loadColorPresetToUIandPlots(pn); +} + +// ---------- 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); +} + +// Passband: +void wfmain::on_colorSetBtnPassband_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor* c = &(colorPreset[pos].passband); + setColorButtonOperations(c, ui->colorEditPassband, ui->colorSwatchPassband); +} +void wfmain::on_colorEditPassband_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor* c = &(colorPreset[pos].passband); + setColorLineEditOperations(c, ui->colorEditPassband, ui->colorSwatchPassband); +} + +// 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); +} + +// Cluster Spots: +void wfmain::on_colorSetBtnClusterSpots_clicked() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor* c = &(colorPreset[pos].clusterSpots); + setColorButtonOperations(c, ui->colorEditClusterSpots, ui->colorSwatchClusterSpots); +} +void wfmain::on_colorEditClusterSpots_editingFinished() +{ + int pos = ui->colorPresetCombo->currentIndex(); + QColor* c = &(colorPreset[pos].clusterSpots); + setColorLineEditOperations(c, ui->colorEditClusterSpots, ui->colorSwatchClusterSpots); +} + +// ---------- 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("passband", p->passband.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->setValue("clusterSpots", p->clusterSpots.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() +{ + // 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(); +} + +void wfmain::on_customEdgeBtn_clicked() +{ + double lowFreq = oldLowerFreq; + double highFreq = oldUpperFreq; + QString freqstring = QString("%1, %2").arg(lowFreq).arg(highFreq); + bool ok; + + QString userFreq = QInputDialog::getText(this, "Scope Edges", + "Please enter desired scope edges, in MHz, \ +with a comma between the low and high range.", + QLineEdit::Normal, freqstring, &ok); + if(!ok) + return; + + QString clean = userFreq.trimmed().replace(" ", ""); + QStringList freqs = clean.split(","); + if(freqs.length() == 2) + { + lowFreq = QString(freqs.at(0)).toDouble(&ok); + if(ok) + { + highFreq = QString(freqs.at(1)).toDouble(&ok); + if(ok) + { + qDebug(logGui()) << "setting edge to: " << lowFreq << ", " << highFreq << ", edge num: " << ui->scopeEdgeCombo->currentIndex() + 1; + emit setScopeFixedEdge(lowFreq, highFreq, ui->scopeEdgeCombo->currentIndex() + 1); + return; + } + } + goto errMsg; + } else { + goto errMsg; + } + +errMsg: + { + QMessageBox URLmsgBox; + URLmsgBox.setText("Error, could not interpret your input.\ +
Please make sure to place a comma between the frequencies.\ +
For example: '7.200, 7.300'"); + URLmsgBox.exec(); + + return; + } -// resize(minimumSize()); -// adjustSize(); // main window -// adjustSize(); } + + +void wfmain::receiveClusterOutput(QString text) { + ui->clusterOutputTextEdit->moveCursor(QTextCursor::End); + ui->clusterOutputTextEdit->insertPlainText(text); + ui->clusterOutputTextEdit->moveCursor(QTextCursor::End); +} + +void wfmain::on_clusterUdpEnable_clicked(bool enable) +{ + prefs.clusterUdpEnable = enable; + emit setClusterEnableUdp(enable); +} + +void wfmain::on_clusterTcpEnable_clicked(bool enable) +{ + prefs.clusterTcpEnable = enable; + emit setClusterEnableTcp(enable); +} + +void wfmain::on_clusterUdpPortLineEdit_editingFinished() +{ + prefs.clusterUdpPort = ui->clusterUdpPortLineEdit->text().toInt(); + emit setClusterUdpPort(prefs.clusterUdpPort); +} + +void wfmain::on_clusterServerNameCombo_currentIndexChanged(int index) +{ + if (index < 0) + return; + + QString text = ui->clusterServerNameCombo->currentText(); + + if (clusters.size() <= index) + { + qInfo(logGui) << "Adding Cluster server" << text; + clusterSettings c; + c.server = text; + clusters.append(c); + ui->clusterTcpPortLineEdit->setEnabled(true); + ui->clusterUsernameLineEdit->setEnabled(true); + ui->clusterPasswordLineEdit->setEnabled(true); + ui->clusterTimeoutLineEdit->setEnabled(true); + + } + else { + qInfo(logGui) << "Editing Cluster server" << text; + clusters[index].server = text; + } + ui->clusterUsernameLineEdit->blockSignals(true); + ui->clusterPasswordLineEdit->blockSignals(true); + ui->clusterTimeoutLineEdit->blockSignals(true); + ui->clusterTcpPortLineEdit->setText(QString::number(clusters[index].port)); + ui->clusterUsernameLineEdit->setText(clusters[index].userName); + ui->clusterPasswordLineEdit->setText(clusters[index].password); + ui->clusterTimeoutLineEdit->setText(QString::number(clusters[index].timeout)); + ui->clusterUsernameLineEdit->blockSignals(false); + ui->clusterPasswordLineEdit->blockSignals(false); + ui->clusterTimeoutLineEdit->blockSignals(false); + + + for (int i = 0; i < clusters.size(); i++) { + if (i == index) + clusters[i].isdefault = true; + else + clusters[i].isdefault = false; + } + + emit setClusterServerName(clusters[index].server); + emit setClusterTcpPort(clusters[index].port); + emit setClusterUserName(clusters[index].userName); + emit setClusterPassword(clusters[index].password); + emit setClusterTimeout(clusters[index].timeout); + +} + +void wfmain::on_clusterServerNameCombo_currentTextChanged(QString text) +{ + if (text.isEmpty()) { + int index = ui->clusterServerNameCombo->currentIndex(); + ui->clusterServerNameCombo->removeItem(index); + clusters.removeAt(index); + if (clusters.size() == 0) + { + ui->clusterTcpPortLineEdit->setEnabled(false); + ui->clusterUsernameLineEdit->setEnabled(false); + ui->clusterPasswordLineEdit->setEnabled(false); + ui->clusterTimeoutLineEdit->setEnabled(false); + } + } +} + +void wfmain::on_clusterTcpPortLineEdit_editingFinished() +{ + int index = ui->clusterServerNameCombo->currentIndex(); + if (index < clusters.size()) + { + clusters[index].port = ui->clusterTcpPortLineEdit->displayText().toInt(); + emit setClusterTcpPort(clusters[index].port); + } +} + +void wfmain::on_clusterUsernameLineEdit_editingFinished() +{ + int index = ui->clusterServerNameCombo->currentIndex(); + if (index < clusters.size()) + { + clusters[index].userName = ui->clusterUsernameLineEdit->text(); + emit setClusterUserName(clusters[index].userName); + } +} + +void wfmain::on_clusterPasswordLineEdit_editingFinished() +{ + int index = ui->clusterServerNameCombo->currentIndex(); + if (index < clusters.size()) + { + clusters[index].password = ui->clusterPasswordLineEdit->text(); + emit setClusterPassword(clusters[index].password); + } + +} + +void wfmain::on_clusterTimeoutLineEdit_editingFinished() +{ + int index = ui->clusterServerNameCombo->currentIndex(); + if (index < clusters.size()) + { + clusters[index].timeout = ui->clusterTimeoutLineEdit->displayText().toInt(); + emit setClusterTimeout(clusters[index].timeout); + } +} + + +void wfmain::receiveSpots(QList spots) +{ + //QElapsedTimer timer; + //timer.start(); + + bool current = false; + + if (clusterSpots.size() > 0) { + current=clusterSpots.begin().value()->current; + } + + foreach(spotData s, spots) + { + bool found = false; + QMap::iterator spot = clusterSpots.find(s.dxcall); + + while (spot != clusterSpots.end() && spot.key() == s.dxcall && spot.value()->frequency == s.frequency) { + spot.value()->current = !current; + found = true; + ++spot; + } + + if (!found) + { + spotData* sp = new spotData(s); + + //qDebug(logCluster()) << "ADD:" << sp->dxcall; + sp->current = !current; + bool conflict = true; + double left = sp->frequency; + QCPRange range=plot->yAxis->range(); + double top = range.upper-15.0; + sp->text = new QCPItemText(plot); + sp->text->setAntialiased(true); + sp->text->setColor(clusterColor); + sp->text->setText(sp->dxcall); + sp->text->setFont(QFont(font().family(), 10)); + sp->text->setPositionAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + sp->text->position->setType(QCPItemPosition::ptPlotCoords); + sp->text->setSelectable(true); + QMargins margin; + int width = (sp->text->right - sp->text->left) / 2; + margin.setLeft(width); + margin.setRight(width); + sp->text->setPadding(margin); + sp->text->position->setCoords(left, top); + sp->text->setVisible(false); + while (conflict) { + QCPAbstractItem* item = plot->itemAt(sp->text->position->pixelPosition(), true); + QCPItemText* textItem = dynamic_cast (item); + if (textItem != nullptr && sp->text != textItem) { + //qInfo(logGui()) << "CONFLICT:" << textItem->text() << "SAME POSITION AS" << sp->dxcall << sp->text->position->pixelPosition(); + top = top - 5.0; + } + else { + //qInfo(logGui()) << "OK:" << sp->dxcall << sp->text->position->pixelPosition(); + conflict = false; + } + sp->text->position->setCoords(left, top); + } + + sp->text->setVisible(true); + clusterSpots.insert(sp->dxcall, sp); + } + } + + QMap::iterator spot2 = clusterSpots.begin(); + while (spot2 != clusterSpots.end()) { + if (spot2.value()->current == current) { + plot->removeItem(spot2.value()->text); + //qDebug(logCluster()) << "REMOVE:" << spot2.value()->dxcall; + delete spot2.value(); // Stop memory leak? + spot2 = clusterSpots.erase(spot2); + } + else { + ++spot2; + } + + } + + //qDebug(logCluster()) << "Processing took" << timer.nsecsElapsed() / 1000 << "us"; +} + +void wfmain::on_clusterPopOutBtn_clicked() +{ + + if (settingsTabisAttached) + { + settingsTab = ui->tabWidget->currentWidget(); + ui->tabWidget->removeTab(ui->tabWidget->indexOf(settingsTab)); + settingsWidgetTab->addTab(settingsTab, "Settings"); + settingsWidgetWindow->show(); + ui->clusterPopOutBtn->setText("Re-attach"); + 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->clusterPopOutBtn->setText("Pop-Out"); + ui->colorPopOutBtn->setText("Pop-Out"); + ui->tabWidget->setCurrentIndex(3); + settingsTabisAttached = true; + } +} + +void wfmain::on_clusterSkimmerSpotsEnable_clicked(bool enable) +{ + prefs.clusterSkimmerSpotsEnable = enable; + emit setClusterSkimmerSpots(enable); +} + +void wfmain::on_clickDragTuningEnableChk_clicked(bool checked) +{ + prefs.clickDragTuningEnable = checked; +} + + + +void wfmain::on_autoPollBtn_clicked(bool checked) +{ + ui->pollTimeMsSpin->setEnabled(!checked); + if(checked) + { + prefs.polling_ms = 0; + qInfo(logSystem()) << "User set radio polling interval to automatic."; + calculateTimingParameters(); + } +} + +void wfmain::on_manualPollBtn_clicked(bool checked) +{ + ui->pollTimeMsSpin->setEnabled(checked); + if(checked) + { + prefs.polling_ms = ui->pollTimeMsSpin->value(); + changePollTiming(prefs.polling_ms); + } +} + +void wfmain::on_pollTimeMsSpin_valueChanged(int timing_ms) +{ + if(ui->manualPollBtn->isChecked()) + { + changePollTiming(timing_ms); + } +} + +void wfmain::changePollTiming(int timing_ms, bool setUI) +{ + delayedCommand->setInterval(timing_ms); + qInfo(logSystem()) << "User changed radio polling interval to " << timing_ms << "ms."; + showStatusBarText("User changed radio polling interval to " + QString("%1").arg(timing_ms) + "ms."); + prefs.polling_ms = timing_ms; + if(setUI) + { + ui->pollTimeMsSpin->blockSignals(true); + ui->pollTimeMsSpin->setValue(timing_ms); + ui->pollTimeMsSpin->blockSignals(false); + } +} diff --git a/wfmain.h b/wfmain.h index cf02435..b2370f8 100644 --- a/wfmain.h +++ b/wfmain.h @@ -16,8 +16,12 @@ #include #include #include +#include +#include #include "logcategories.h" +#include "wfviewtypes.h" +#include "prefs.h" #include "commhandler.h" #include "rigcommander.h" #include "rigstate.h" @@ -35,6 +39,10 @@ #include "rigctld.h" #include "aboutbox.h" #include "selectradio.h" +#include "colorprefs.h" +#include "loggingwindow.h" +#include "cluster.h" +#include "audiodevices.h" #include #include @@ -49,6 +57,7 @@ #include "rtaudio/RtAudio.h" #endif +#define numColorPresetsTotal (5) namespace Ui { class wfmain; @@ -59,10 +68,10 @@ class wfmain : public QMainWindow Q_OBJECT public: - explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, QWidget *parent = 0); - QString serialPortCL; - QString hostCL; + explicit wfmain(const QString settingsFile, const QString logFile, bool debugMode, QWidget *parent = 0); ~wfmain(); + static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + void handleLogText(QString text); signals: // Basic to rig: @@ -70,7 +79,7 @@ signals: void setRigID(unsigned char rigID); void setRTSforPTT(bool enabled); - // Power + void sendPowerOn(); void sendPowerOff(); @@ -93,6 +102,7 @@ signals: // Repeater: void getDuplexMode(); + void getPassband(); void getTone(); void getTSQL(); void getDTCS(); @@ -182,6 +192,16 @@ signals: void sendRigCaps(rigCapabilities caps); void requestRigState(); void stateUpdated(); + void setClusterUdpPort(int port); + void setClusterEnableUdp(bool udp); + void setClusterEnableTcp(bool tcp); + void setClusterServerName(QString name); + void setClusterTcpPort(int port); + void setClusterUserName(QString name); + void setClusterPassword(QString pass); + void setClusterTimeout(int timeout); + void setClusterSkimmerSpots(bool enable); + void setFrequencyRange(double low, double high); private slots: void updateSizes(int tabIndex); @@ -234,7 +254,7 @@ private slots: void receiveRITValue(int ritValHz); void receiveModInput(rigInput input, bool dataOn); //void receiveDuplexMode(duplexMode dm); - + void receivePassband(quint16 pass); // Levels: @@ -275,7 +295,10 @@ private slots: void receiveFoundRigID(rigCapabilities rigCaps); void receiveSerialPortError(QString port, QString errorText); void receiveStatusUpdate(networkStatus status); + void receiveNetworkAudioLevels(networkAudioLevels l); void handlePlotClick(QMouseEvent *); + void handlePlotMouseRelease(QMouseEvent *); + void handlePlotMouseMove(QMouseEvent *); void handlePlotDoubleClick(QMouseEvent *); void handleWFClick(QMouseEvent *); void handleWFDoubleClick(QMouseEvent *); @@ -287,6 +310,8 @@ private slots: void radioSelection(QList radios); void setRadioTimeDateSend(); + void logCheck(); + void setDebugLogging(bool debugModeOn); // void on_getFreqBtn_clicked(); @@ -297,8 +322,6 @@ private slots: void on_clearPeakBtn_clicked(); - void on_drawPeakChk_clicked(bool checked); - void on_fullScreenChk_clicked(bool checked); void on_goFreqBtn_clicked(); @@ -327,14 +350,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(); @@ -383,7 +400,6 @@ private slots: void on_saveSettingsBtn_clicked(); - void on_debugBtn_clicked(); void on_pttEnableChk_clicked(bool checked); @@ -426,7 +442,6 @@ private slots: void on_dataModeBtn_toggled(bool checked); - void on_transmitBtn_clicked(); void on_adjRefBtn_clicked(); @@ -499,8 +514,6 @@ private slots: void on_wfLengthSlider_valueChanged(int value); - void on_pollingBtn_clicked(); - void on_wfAntiAliasChk_clicked(bool checked); void on_wfInterpolateChk_clicked(bool checked); @@ -528,14 +541,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); @@ -557,9 +570,135 @@ private slots: 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_colorSetBtnPassband_clicked(); + + void on_colorEditPassband_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_colorSetBtnClusterSpots_clicked(); + + void on_colorEditClusterSpots_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); + + void on_customEdgeBtn_clicked(); + + void on_clusterUdpEnable_clicked(bool enable); + void on_clusterTcpEnable_clicked(bool enable); + void on_clusterUdpPortLineEdit_editingFinished(); + void on_clusterServerNameCombo_currentTextChanged(QString text); + void on_clusterServerNameCombo_currentIndexChanged(int index); + void on_clusterTcpPortLineEdit_editingFinished(); + void on_clusterUsernameLineEdit_editingFinished(); + void on_clusterPasswordLineEdit_editingFinished(); + void on_clusterTimeoutLineEdit_editingFinished(); + void on_clusterPopOutBtn_clicked(); + void on_clusterSkimmerSpotsEnable_clicked(bool enable); + + void on_clickDragTuningEnableChk_clicked(bool checked); + + void receiveClusterOutput(QString text); + void receiveSpots(QList spots); + + void on_autoPollBtn_clicked(bool checked); + + void on_manualPollBtn_clicked(bool checked); + + void on_pollTimeMsSpin_valueChanged(int arg1); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); + QString logFilename; + bool debugMode; + QString version; QSettings *settings=Q_NULLPTR; void loadSettings(); void saveSettings(); @@ -567,14 +706,19 @@ private: void createSettingsListItems(); void connectSettingsList(); + void initLogging(); + QTimer logCheckingTimer; + int logCheckingOldPosition = 0; + QCustomPlot *plot; // line plot QCustomPlot *wf; // waterfall image QCPItemLine * freqIndicatorLine; - //commHandler *comm; + QCPItemRect* passbandIndicator; 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(); @@ -665,17 +809,14 @@ 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; - bool drawPlasma = true; QMutex plasmaMutex; void resizePlasmaBuffer(int newSize); + void clearPlasmaBuffer(); double plotFloor = 0; double plotCeiling = 160; @@ -683,14 +824,18 @@ private: double wfCeiling = 160; double oldPlotFloor = -1; double oldPlotCeiling = 999; + double passBand = 0.0; + + double mousePressFreq = 0.0; + double mouseReleaseFreq = 0.0; QVector wfimage; unsigned int wfLengthMax; bool onFullscreen; - bool drawPeaks; bool freqTextSelected; void checkFreqSel(); + void setUISpectrumControlsToMode(spectrumMode smode); double oldLowerFreq; double oldUpperFreq; @@ -704,7 +849,7 @@ private: cmdGetDataMode, cmdSetModeFilter, cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue, cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain, cmdGetSql, cmdSetSql, cmdGetIFShift, cmdSetIFShift, cmdGetTPBFInner, cmdSetTPBFInner, - cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, + cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, cmdGetPassband, cmdSetATU, cmdStartATU, cmdGetSpectrumMode, cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT, cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel, @@ -761,66 +906,10 @@ 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; - - struct preferences { - bool useFullScreen; - bool useDarkMode; - bool useSystemTheme; - bool drawPeaks; - underlay_t underlayMode = underlayNone; - int underlayBufferSize = 64; - bool wfAntiAlias; - bool wfInterpolate; - QString stylesheetPath; - unsigned char radioCIVAddr; - bool CIVisRadioModel; - bool forceRTSasPTT; - QString serialPortRadio; - quint32 serialPortBaud; - bool enablePTT; - bool niceTS; - bool enableLAN; - bool enableRigCtlD; - quint16 rigCtlPort; - colors colorScheme; - 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; - } prefs; + colorPrefsType colorPreset[numColorPresetsTotal]; + preferences prefs; preferences defPrefs; udpPreferences udpPrefs; udpPreferences udpDefPrefs; @@ -829,13 +918,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,\ @@ -869,6 +979,7 @@ private: void insertSlowPeriodicCommand(cmds cmd, unsigned char priority); void calculateTimingParameters(); + void changePollTiming(int timing_ms, bool setUI=false); void changeMode(mode_kind mode); void changeMode(mode_kind mode, bool dataOn); @@ -900,6 +1011,7 @@ private: transceiverAdjustments *trxadj; aboutbox *abtBox; selectRadio *selRad; + loggingWindow *logWindow; udpServer* udp = Q_NULLPTR; rigCtlD* rigCtl = Q_NULLPTR; @@ -935,6 +1047,15 @@ private: SERVERCONFIG serverConfig; void serverAddUserLine(const QString& user, const QString& pass, const int& type); + dxClusterClient* cluster = Q_NULLPTR; + QThread* clusterThread = Q_NULLPTR; + QMap clusterSpots; + QTimer clusterTimer; + QCPItemText* text=Q_NULLPTR; + QList clusters; + QMutex clusterMutex; + QColor clusterColor; + audioDevices* audioDev = Q_NULLPTR; }; Q_DECLARE_METATYPE(struct rigCapabilities) @@ -947,12 +1068,17 @@ 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(struct spotData) Q_DECLARE_METATYPE(enum rigInput) Q_DECLARE_METATYPE(enum meterKind) Q_DECLARE_METATYPE(enum spectrumMode) +Q_DECLARE_METATYPE(enum mode_kind) Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) Q_DECLARE_METATYPE(rigstate*) +//void (*wfmain::logthistext)(QString text) = NULL; #endif // WFMAIN_H #endif diff --git a/wfmain.ui b/wfmain.ui index 4676f61..975bc54 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -6,8 +6,8 @@ 0 0 - 1012 - 569 + 1082 + 660 @@ -31,6 +31,18 @@ Spectrum + + 0 + + + 7 + + + 0 + + + 0 + @@ -105,6 +117,16 @@ + + + + Define a custom (fixed) scope edge + + + Custom Edge + + + @@ -326,6 +348,18 @@ Tuning Dial + + 3000 + + + 4000 + + + 10 + + + 100 + true @@ -778,7 +812,7 @@ - + 16777215 @@ -832,7 +866,7 @@ - + 16777215 @@ -931,6 +965,9 @@ 80 + + Sets the floor for the waterfall and spectrum displays + 160 @@ -1035,7 +1072,19 @@ Preamp/Att - + + + 4 + + + 4 + + + 4 + + + 4 + @@ -1182,7 +1231,7 @@ 0 - + 0 @@ -1196,15 +1245,12 @@ - 23cm - - - + 2200m - + 0 @@ -1218,15 +1264,12 @@ - 70cm - - - U + 630m - + 0 @@ -1240,15 +1283,15 @@ - 2m + 160m - V + L - + 0 @@ -1262,54 +1305,10 @@ - Air + 80m - A - - - - - - - - 0 - 0 - - - - - 16777215 - 128 - - - - WFM - - - W - - - - - - - - 0 - 0 - - - - - 16777215 - 128 - - - - 4m - - - $ + 8 @@ -1318,7 +1317,7 @@ - + 0 @@ -1332,15 +1331,15 @@ - 6m + 60m - 6 + S - + 0 @@ -1354,15 +1353,15 @@ - 10m + 40m - 1 + 4 - + 0 @@ -1376,15 +1375,15 @@ - 12m + 30m - T + 3 - + 0 @@ -1398,10 +1397,10 @@ - 15m + 20m - 5 + 2 @@ -1432,7 +1431,7 @@ - + 0 @@ -1446,15 +1445,15 @@ - 20m + 15m - 2 + 5 - + 0 @@ -1468,15 +1467,15 @@ - 30m + 12m - 3 + T - + 0 @@ -1490,10 +1489,10 @@ - 40m + 10m - 4 + 1 @@ -1502,7 +1501,7 @@ - + 0 @@ -1516,15 +1515,15 @@ - 60m + 6m - S + 6 - + 0 @@ -1538,15 +1537,15 @@ - 80m + 4m - 8 + $ - + 0 @@ -1560,15 +1559,15 @@ - 160m + 2m - L + V - + 0 @@ -1582,7 +1581,32 @@ - 630m + 70cm + + + U + + + + + + + + 0 + 0 + + + + + 16777215 + 128 + + + + 23cm + + + @@ -1591,7 +1615,7 @@ - + 0 @@ -1605,7 +1629,32 @@ - 2200m + WFM + + + W + + + + + + + + 0 + 0 + + + + + 16777215 + 128 + + + + Air + + + A @@ -2149,10 +2198,10 @@ - 10 + 0 - 10 + 0 @@ -2173,7 +2222,7 @@ - 0 + 5 @@ -2316,7 +2365,19 @@ Serial Connected Radios - + + + 3 + + + 5 + + + 3 + + + 5 + @@ -2401,7 +2462,22 @@ Network Connected Radios - + + + 6 + + + 3 + + + 5 + + + 3 + + + 5 + @@ -2720,8 +2796,8 @@ - 40 - 20 + 120 + 16777215 @@ -2741,7 +2817,7 @@ - 300 + 120 16777215 @@ -2755,9 +2831,12 @@ Qt::Horizontal + + QSizePolicy::Fixed + - 40 + 15 20 @@ -2775,20 +2854,26 @@ - - - - 300 - 16777215 - - - - Audio Output Selector - + + + + Qt Audio + + + + + PortAudio + + + + + RT Audio + + - + Qt::Horizontal @@ -2812,13 +2897,6 @@ 0 - - - - Connect - - - @@ -2853,13 +2931,6 @@ - - - - Draw Peaks - - - @@ -2870,6 +2941,13 @@ + + + + Allow tuning via click and drag (experimental) + + + @@ -2887,15 +2965,11 @@ - - - - Waterfall Dark Theme - - - + + Enables interpolation between pixels. Note that this will increase CPU usage. + Interpolate Waterfall @@ -2905,16 +2979,16 @@ - + - Use System Theme + Anti-Alias Waterfall - + - Anti-Alias Waterfall + Use System Theme @@ -2963,6 +3037,9 @@ + + No underlay graphics + None @@ -2976,6 +3053,9 @@ + + Indefinite peak hold + Peak Hold @@ -2986,6 +3066,9 @@ + + Peak value within the buffer + Peak @@ -2996,6 +3079,9 @@ + + Average value within the buffer + Average @@ -3007,7 +3093,7 @@ - Uneerlay Buffer Size: + Underlay Buffer Size: @@ -3019,6 +3105,9 @@ 16777215 + + Size of buffer for spectrum data. Shorter values are more responsive. + 8 @@ -3050,6 +3139,29 @@ + + + + Enable PTT Controls + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + @@ -3061,18 +3173,51 @@ - + - Set up radio polling. The radio's meter is polled every-other interval. - - - Polling - - - + wfview will automatically calculate command polling. Recommended. - Polling + AutoPolling + + + true + + + pollingButtonGroup + + + + + + + Manual (user-defined) command polling + + + Manual Polling Inteval: + + + pollingButtonGroup + + + + + + + Sets the polling interval, in ms. Automatic polling is recommended. Serial port and USB port radios should not poll quicker than about 75ms. + + + 1 + + + 250 + + + + + + + ms @@ -3100,14 +3245,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 @@ -3121,6 +3360,752 @@ + + + + + 0 + 1 + + + + + 0 + 250 + + + + + 0 + 0 + + + + User-defined Color Editor + + + + 0 + + + 4 + + + 0 + + + 0 + + + + + QAbstractScrollArea::AdjustIgnored + + + true + + + + + 0 + 0 + 858 + 287 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 + + + 6 + + + + + + 10 + 0 + + + + + + + + Text + + + + + + + Grid + + + + + + + Waterfall Axis + + + + + + + Axis + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Plot Background + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Spectrum Line + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Waterfall Back + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Spectrum Fill + + + + + + + Waterfall Grid + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Underlay Line + + + + + + + + 10 + 0 + + + + + + + + Waterfall Text + + + + + + + + 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 + + + + + + + + 10 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 10 + 0 + + + + + + + + + 10 + 0 + + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Meter Text + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Meter Scale + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Meter High Scale + + + + + + + Meter Peak Level + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 10 + 0 + + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Meter Average + + + + + + + Meter Level + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 10 + 0 + + + + + + + + Passband + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 10 + 0 + + + + + + + + Tuning Line + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + + 10 + 0 + + + + + + + + + 10 + 0 + + + + + + + + + 90 + 16777215 + + + + #AARRGGBB + + + + + + + Underlay Fill + + + + + + + + 10 + 0 + + + + + + + + + 10 + 0 + + + + + + + + Cluster Spots + + + + + + + + 0 + 0 + + + + + 90 + 16777215 + + + + + + + + + 10 + 0 + + + + + + + + + + + + + @@ -3129,7 +4114,7 @@ 20 - 40 + 20 @@ -3513,7 +4498,7 @@ - + @@ -3531,7 +4516,7 @@ - 300 + 100 16777215 @@ -3554,12 +4539,38 @@ - 300 + 100 16777215 + + + + Audio System + + + + + + + + Qt Audio + + + + + PortAudio + + + + + RT Audio + + + + @@ -3607,7 +4618,7 @@ 0 - 3 + 4 false @@ -3645,6 +4656,11 @@ Admin + + + Delete? + + @@ -3662,6 +4678,30 @@ + + + + + + Add User + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -3904,6 +4944,250 @@ + + + true + + + + + 0 + 10 + 831 + 21 + + + + + + + This page contains configuration for DX Cluster, either UDP style broadcast (from N1MM+/DXlog) or TCP connection to your favourite cluster + + + + + + + + + 10 + 50 + 201 + 181 + + + + UDP Broadcast Connection + + + + + 20 + 40 + 161 + 121 + + + + + + + UDP Port + + + + + + + 00000 + + + + + + + Enable + + + + + + + + + + 330 + 50 + 311 + 241 + + + + TCP Cluster Connection + + + + + 20 + 40 + 271 + 186 + + + + + + + Server Name + + + + + + + Username + + + + + + + + + + Password + + + + + + + QLineEdit::PasswordEchoOnEdit + + + + + + + Enable + + + + + + + true + + + true + + + + + + + Spot Timeout (minutes) + + + + + + + 0000 + + + + + + + + + + Server Port + + + + + + + 00000 + + + + + + + + + + 10 + 320 + 811 + 141 + + + + QFrame::Raised + + + QAbstractScrollArea::AdjustIgnored + + + + + + 10 + 280 + 75 + 23 + + + + <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 + + + + + + 10 + 470 + 801 + 22 + + + + + + + Show Skimmer Spots + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + groupBox_9 + horizontalLayoutWidget + groupBox_10 + clusterOutputTextEdit + clusterPopOutBtn + horizontalLayoutWidget_2 + @@ -3962,7 +5246,11 @@ - + + + true + + @@ -4003,6 +5291,33 @@ + + + + Log + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Connect to Radio + + + @@ -4038,8 +5353,8 @@ 0 0 - 1012 - 22 + 1082 + 21 @@ -4056,11 +5371,18 @@
meter.h
1 + + QLedLabel + QWidget +
qledlabel.h
+ 1 +
- + + diff --git a/wfserver.pro b/wfserver.pro index 779a7ed..d50ffa2 100644 --- a/wfserver.pro +++ b/wfserver.pro @@ -13,7 +13,7 @@ TEMPLATE = app CONFIG += console -DEFINES += WFVIEW_VERSION=\\\"1.4\\\" +DEFINES += WFVIEW_VERSION=\\\"1.54\\\" DEFINES += BUILD_WFSERVER @@ -169,4 +169,5 @@ HEADERS += servermain.h \ ulaw.h \ tcpserver.h \ audiotaper.h \ - keyboard.h + keyboard.h \ + wfviewtypes.h diff --git a/wfview b/wfview new file mode 100644 index 0000000..e69de29 diff --git a/wfview.pro b/wfview.pro index 144da14..c9a518f 100644 --- a/wfview.pro +++ b/wfview.pro @@ -4,14 +4,17 @@ # #------------------------------------------------- -QT += core gui serialport network multimedia +QT += core gui serialport network multimedia xml + +#QT += sql +#DEFINES += USESQL greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport TARGET = wfview TEMPLATE = app -DEFINES += WFVIEW_VERSION=\\\"1.4\\\" +DEFINES += WFVIEW_VERSION=\\\"1.54\\\" DEFINES += BUILD_WFVIEW @@ -91,7 +94,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\\\" @@ -175,6 +178,7 @@ win32:INCLUDEPATH += ../r8brain-free-src INCLUDEPATH += resampler SOURCES += main.cpp\ + loggingwindow.cpp \ wfmain.cpp \ commhandler.cpp \ rigcommander.cpp \ @@ -201,10 +205,16 @@ SOURCES += main.cpp\ transceiveradjustments.cpp \ selectradio.cpp \ tcpserver.cpp \ - aboutbox.cpp + cluster.cpp \ + database.cpp \ + aboutbox.cpp \ + audiodevices.cpp HEADERS += wfmain.h \ + colorprefs.h \ commhandler.h \ + loggingwindow.h \ + prefs.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ @@ -230,16 +240,22 @@ HEADERS += wfmain.h \ repeatersetup.h \ repeaterattributes.h \ rigctld.h \ + rigstate.h \ ulaw.h \ transceiveradjustments.h \ audiotaper.h \ selectradio.h \ tcpserver.h \ - aboutbox.h + cluster.h \ + database.h \ + aboutbox.h \ + wfviewtypes.h \ + audiodevices.h FORMS += wfmain.ui \ calibrationwindow.ui \ + loggingwindow.ui \ satellitesetup.ui \ selectradio.ui \ repeatersetup.ui \ diff --git a/wfviewtypes.h b/wfviewtypes.h new file mode 100644 index 0000000..46876b8 --- /dev/null +++ b/wfviewtypes.h @@ -0,0 +1,51 @@ +#ifndef WFVIEWTYPES_H +#define WFVIEWTYPES_H + +enum underlay_t { underlayNone, underlayPeakHold, underlayPeakBuffer, underlayAverageBuffer }; + +enum meterKind { + meterNone=0, + meterS, + meterCenter, + meterSWR, + meterPower, + meterALC, + meterComp, + meterVoltage, + meterCurrent, + meterRxdB, + meterTxMod, + meterRxAudio, + meterAudio, + meterLatency +}; + +enum spectrumMode { + spectModeCenter=0x00, + spectModeFixed=0x01, + spectModeScrollC=0x02, + spectModeScrollF=0x03, + spectModeUnknown=0xff +}; + +struct freqt { + quint64 Hz; + double MHzDouble; +}; + +struct datekind { + uint16_t year; + unsigned char month; + unsigned char day; +}; + +struct timekind { + unsigned char hours; + unsigned char minutes; + bool isMinus; +}; + +enum audioType {qtAudio,portAudio,rtAudio}; + + +#endif // WFVIEWTYPES_H