Added support for use of "Standard" locations for writable files.
This allows writable files to be located in the "correct" location for each platform rather than in the directory of the executable which, in general, is not recommended or allowed in some cases. A preprocessor macro WSJT_STANDARD_FILE_LOCATIONS is used to switch be tween old and new functionality, currently it is on by default. It can be turned off by defining it to a false value (0) or more simply with cmake-gui setting the option with the same name. JTAlert can only work with the old non-standard file locations until Laurie VK3AMA chooses to support the new file locations. Even if the above is not enabled; the QSettings file is written to a user specific location so it will be shared by all instances of the program (i.e. across upgrades). See below for multiple concurrent instance support changes. Added a command line parser module for Fortran. Added 'lib/options.f90' to facilitate more complex argument passing to jt9 to cover explicit file locations. Changed the way multiple concurrent instances are handled. This is to allow the program to be run multiple times from the same installation directory. A new wsjtx command line optional argument is available "-r" or "--rig" which enables multiple concurrent instance support. The parameter of the new option is a unique name signifying a rig or equivalent. The name is used as the shared memory segment key and in window titles. The name is also used to access unique settings files and writable data files like ALL.TXT and log files. No attempt has been made to share these files between concurrent instances. If "-r" or "--rig" is used without a parameter it still enables multiple concurrent instance support for that instance. All instances must use a unique parameter, one of which may be empty. The rig name is appended the QCoreApplication::applicationName() for convenient usage like window titles. Set non Qt locale to "C". This ensures that C library functions give consistent results whatever the system locale is set to. QApplication follows the system locale as before. Thus using QApplication and its descendants like widgets and QString for all user visible formating will give correct l10n and using C/C++ library will give consistent formatting across locales. Added top level C++ exception handling to main.cpp. Because the new transceiver framework uses exceptions internally, the main function now handles any exceptions that aren't caught. Retired devsetup, replaced with Configuration. Configuration is a class that encapsulates most of the configuration behavior. Because rig configuration is so closely coupled with rig operation, Configuration serves as a proxy for access to the rig control functions. See Configuration.hpp for more details of the Configuration interface. Menu changes. Various checkable menu actions moved from main menu to the Configuration dialog. The whole settings menu has been retired with the single "Settings..." action moved to the file menu for consistency on Mac where it appears as "Preferences" in line with Mac guidelines. New data models for data used by the application. ADIF amateur band parameters, free text message macros, spot working frequencies and, station information (station descriptions and transverter offsets per band) each implement the QAbstractItemModel interface allowing them to be used directly with Qt view widgets (Bands.hpp, FrequencyList.hpp and, StationList.hpp). Configuration manages maintenance of an instance of all but the former of the above models. The ADIF band model is owned by Configuration but requires no user maintenance as it is immutable. Band combo box gets more functionality. This widget is now an editable QComboBox with some extra input capabilities. The popup list is still the list of spot working frequencies, now showing the actual frequency decorated with the band name. This allows multiple spot frequencies on a band if required. The line edit allows direct frequency entry in mega-Hertz with a completer built in to suggest the available spot working frequencies. It also allows band name entry where the first available spot working frequency is selected. Recognized band names are those that are defined by the ADIF specification and can be found in in the implementation of the ADIF bands model (Bands.cpp). If an out of band frequency is chosen, the line edit shows a warning red background and the text "OOB". Out of band is only defined by the ADIF band limits which in general are wider than any entities regulations. Qt 5.2 now supports default audio i/p and o/p devices. These devices are placeholders for whatever the user defines as the default device. Because of this they need special treatment as the actual device used is chosen at open time behind the scenes. Close-down behavior is simplified. The close-down semantics were broken such that some objects were not being shut down cleanly, this required amendments to facilitate correct close down of threads. User font selection added to Configuration UI. Buttons to set the application font and the font for the band and Rx frequency activity widgets have been added to the Configuration UI to replace the file based font size control. Free text macros now selected directly. The free text line edit widgets are now editable combo boxes that have the current free text macro definitions as their popup list. The old context menu to do this has been retired. Astronomical data window dynamically formatted and has font a chooser. This window is now autonomous, has its own font chooser and, dynamically resizes to cover the contents. Double click to Tx enabled now has its own widget in the status bar. QDir used for portable path and file name handling throughout. The "Monitor", "Decode", "Enable Tx" and, "Tune" buttons are now checkable. Being checkable allows these buttons control their own state and rendering. Calls to PSK Reporter interface simplified. In mainwindow.cpp the calls to this interface are rationalized to just 3 locations. Manipulation of ALL.TXT simplified. Moved, where possible, to common functions. Elevated frequency types to be Qt types. Frequency and FrequencyDelta defined as Qt types in their meta-type system (Radio.hpp). They are integral types for maximum accuracy. Re-factored rig control calls in mainwindow.cpp. The new Configuration proxy access to rig control required many changes (mostly simplifications) to the MainWindow rig control code. Some common code has been gathered in member functions like qsy(), monitor(), band_changed() and auto_tx_mode(). Rig control enhancements. The rig control for clients interface is declared as an abstract interface (See Transceiver.hpp). Concrete implementations of this interface are provided for the Hamlib rig control library, DX Lab Suite Commander via a TCP/IP command channel, Ham Radio Deluxe also via a TCP/IP command channel and, OmniRig via its Windows COM server interface. Concrete Transceiver implementations are expected to be moved to a separate thread after construction since many operations are blocking and not suitable for running in a GUI thread. To facilitate this all instantiation of concrete Transceiver instances are handled by Configuration using a factory class (TransceiverFactory) for configuration parameter based instantiation. Various common functionality shared by different rig interface implementations are factored out into helper base classes that implement or delegate parts of the Transceiver interface. They are TransceiverBase which caches state to minimize expensive rig commands, it also maps the Transceiver interface into a more convenient form for implementation (template methods). PollingTransceiver that provides a state polling mechanism that only reports actual changes. EmulateSplitTransceiver that provides split operation by QSYing on PTT state changes. EmulateSplitTransceiver can be used with any implementation as it follows the GoF Decorator pattern and can wrap any Transceiver implementation. OmniRigTransceiver is derived directly from TransceiverBase since it doesn't require polling due to its asynchronous nature. OmniRigTransceiver is only built on Windows as it is a COM server client. To build it you must first install the OmniRig client on the development machine (http://www.dxatlas.com/omnirig/). DXLabSuiteCommanderTransceiver derives from PollingTransceiver since it is a synchronous communications channel. No third party library is required for this interface. HRDTransceiver also derives from PollingTransceiver. The HRD interface library has been reverse engineered to provide functionality with all available versions of HRD. No third party libraries are required. HamlibTransceiver likewise derives from PollingTransceiver since the Hamlib asynchronous interface is non-functional. Although this class will interface with the release version of Hamlib (1.2.15.3); for correct operation on most rigs it needs to run with the latest master branch code of Hamlib. During development many changes to Hamlib have been submitted and accepted, hence this requirement. Hamlib source can be obtained from git://git.code.sf.net/p/hamlib/code and at the time of writing he master branch was at SHA 6e4432. The Hamlib interface directly calls the "C" interface and the modified rigclass.{h,cpp} files have been retired. There is a rig type selection of "None" which may be used for non-CAT rigs, this is actually a connection to the dummy Hamlib device. PollingTransvceiver derives from TransceiverBase and TransceiverBase derives from the Transceiver interface. Each interface implementation offers some possibility of PTT control via a different serial port than the CAT port. We also support PTT control directly via a second serial port. This is done by delegating to a dummy Hamlib instance which is only used for PTT control. This means that DXLabSuiteCommanderTransceiver, HRDTransceiver and OmniRigTransceiver always wrap a dummy HamlibTransceiver instance. The factory class TransceiverFactory manages all these constructional complexities. Serial port selection combo boxes are now editable with a manually entered value being saved to the settings file. This allows a non-standard port device to be used without having to edit the settings file manually. For TCP/IP network CAT interfaces; the network address and port may be specified allowing the target device to be located on a different machine from the one running wsjtx if required. The default used when the address field is left blank is the correct one for normal usage on the local host. Selecting a polling interval of zero is no longer possible, this is because the rig control capability can no longer support one way connection. This is in line with most other CAT control software. In the Configuration dialog there are options to select split mode control by the software and mode control by the software. For the former "None", "Rig" and "Fake it" are available, for the latter "None", "USB" and, "Data" are available. Because tone generation is implicitly linked to split mode operation; it is no longer possible to have the software in split mode and the rig not or vice versa. This may mean some rigs cannot be used in split mode and therefore not in dual JT65+JT9 until issues with CAT control with that rig are resolved. Single mode with VOX keying and no CAT control are still possible so even the most basic transceiver setup is supported as before. Configuration now supports a frequency offset suitable for transverter operation. The station details model (StationList.hpp) includes a column to store an offset for each band if required. CMake build script improvements. The CMakeLists.txt from the 'lib' directory has been retired with its contents merged into the top level CMakeLists.txt. Install target support has been greatly improved with the Release build configuration now building a fully standalone installation on Mac and Windows. The Debug configuration still builds an installation that has environment dependencies for external libraries, which is desirable for testing and debugging. Package target support is largely complete for Mac, Windows and, Linux, it should be possible to build release installers directly from CMake/CPack. Cmake FindXXXX.cmake modules have been added to improve the location of fftw-3 and Hamlib packages. Version numbers are now stored in Versions.cmake and work in concert with automatic svn revision lookup during build. The version string becomes 'rlocal'± if there are any uncommitted changes in the build source tree. Moved resource like files to Qt resources. Because location of resource files (when they cannot go into the installation directory because of packaging rules) is hard to standardize. I have used the Qt resource system for all ancillary data files. Some like kvasd.dat are dumped out to the temp (working directory) because they are accessed by an external program, others like the audio samples are copied out so they appear in the data directory under the default save directory. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3929 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
23
AudioDevice.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "AudioDevice.hpp"
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct init
|
||||
{
|
||||
init ()
|
||||
{
|
||||
qRegisterMetaType<AudioDevice::Channel> ("AudioDevice::Channel");
|
||||
}
|
||||
} static_initializer;
|
||||
}
|
||||
|
||||
bool AudioDevice::open (OpenMode mode, Channel channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
|
||||
// ensure we are unbuffered
|
||||
return QIODevice::open (mode | QIODevice::Unbuffered);
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ public:
|
||||
return "both" == s ? Both : "right" == s ? Right : "left" == s ? Left : Mono;
|
||||
}
|
||||
|
||||
bool open (OpenMode mode, Channel channel)
|
||||
{
|
||||
m_channel = channel;
|
||||
|
||||
// ensure we are unbuffered
|
||||
return QIODevice::open (mode | QIODevice::Unbuffered);
|
||||
}
|
||||
bool open (OpenMode mode, Channel channel);
|
||||
|
||||
bool isSequential () const {return true;}
|
||||
|
||||
|
196
Bands.cpp
Normal file
@ -0,0 +1,196 @@
|
||||
#include "Bands.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
namespace
|
||||
{
|
||||
// Local structure to hold a single ADIF band definition.
|
||||
struct ADIF_band
|
||||
{
|
||||
char const * const name_;
|
||||
Radio::Frequency lower_bound_;
|
||||
Radio::Frequency upper_bound_;
|
||||
};
|
||||
|
||||
// Table of ADIF band definitions as defined in the ADIF
|
||||
// specification.
|
||||
ADIF_band constexpr ADIF_bands[] = {
|
||||
{"2190m", 136000u, 137000u},
|
||||
{"630m", 472000u, 479000u},
|
||||
{"560m", 501000u, 504000u},
|
||||
{"160m", 1800000u, 2000000u},
|
||||
{"80m", 3500000u, 4000000u},
|
||||
{"60m", 5102000u, 5406500u},
|
||||
{"40m", 7000000u, 7300000u},
|
||||
{"30m", 10000000u, 10150000u},
|
||||
{"20m", 14000000u, 14350000u},
|
||||
{"17m", 18068000u, 18168000u},
|
||||
{"15m", 21000000u, 21450000u},
|
||||
{"12m", 24890000u, 24990000u},
|
||||
{"10m", 28000000u, 29700000u},
|
||||
{"6m", 50000000u, 54000000u},
|
||||
{"4m", 70000000u, 71000000u},
|
||||
{"2m", 144000000u, 148000000u},
|
||||
{"1.25m", 222000000u, 225000000u},
|
||||
{"70cm", 420000000u, 450000000u},
|
||||
{"33cm", 902000000u, 928000000u},
|
||||
{"23cm", 1240000000u, 1300000000u},
|
||||
{"13cm", 2300000000u, 2450000000u},
|
||||
{"9cm", 3300000000u, 3500000000u},
|
||||
{"6cm", 5650000000u, 5925000000u},
|
||||
{"3cm", 10000000000u, 10500000000u},
|
||||
{"1.25cm", 24000000000u, 24250000000u},
|
||||
{"6mm", 47000000000u, 47200000000u},
|
||||
{"4mm", 75500000000u, 81000000000u},
|
||||
{"2.5mm", 119980000000u, 120020000000u},
|
||||
{"2mm", 142000000000u, 149000000000u},
|
||||
{"1mm", 241000000000u, 250000000000u},
|
||||
};
|
||||
|
||||
auto constexpr out_of_band = "OOB";
|
||||
|
||||
int constexpr table_rows ()
|
||||
{
|
||||
return sizeof (ADIF_bands) / sizeof (ADIF_bands[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Bands::Bands (QObject * parent)
|
||||
: QAbstractTableModel {parent}
|
||||
{
|
||||
}
|
||||
|
||||
QModelIndex Bands::find (QVariant const& v) const
|
||||
{
|
||||
auto f = v.value<Radio::Frequency> ();
|
||||
auto end_iter = ADIF_bands + table_rows ();
|
||||
auto row_iter = std::find_if (ADIF_bands, end_iter, [f] (ADIF_band const& band) {
|
||||
return band.lower_bound_ <= f && f <= band.upper_bound_;
|
||||
});
|
||||
if (row_iter != end_iter)
|
||||
{
|
||||
return index (row_iter - ADIF_bands, 0); // return the band row index
|
||||
}
|
||||
|
||||
return QModelIndex {};
|
||||
}
|
||||
|
||||
int Bands::rowCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : table_rows ();
|
||||
}
|
||||
|
||||
int Bands::columnCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : 3;
|
||||
}
|
||||
|
||||
Qt::ItemFlags Bands::flags (QModelIndex const& index) const
|
||||
{
|
||||
return QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
|
||||
QVariant Bands::data (QModelIndex const& index, int role) const
|
||||
{
|
||||
QVariant item;
|
||||
|
||||
if (!index.isValid ())
|
||||
{
|
||||
// Hijack root for OOB string.
|
||||
if (Qt::DisplayRole == role)
|
||||
{
|
||||
item = out_of_band;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
|
||||
if (row < table_rows ())
|
||||
{
|
||||
switch (role)
|
||||
{
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
switch (column)
|
||||
{
|
||||
case 0: item = tr ("Band name"); break;
|
||||
case 1: item = tr ("Lower frequency limit"); break;
|
||||
case 2: item = tr ("Upper frequency limit"); break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SortRole:
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
if (SortRole == role)
|
||||
{
|
||||
// band name sorts by lower bound
|
||||
item = ADIF_bands[row].lower_bound_;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = ADIF_bands[row].name_;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: item = ADIF_bands[row].lower_bound_; break;
|
||||
case 2: item = ADIF_bands[row].upper_bound_; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::AccessibleTextRole:
|
||||
switch (column)
|
||||
{
|
||||
case 0: item = ADIF_bands[row].name_; break;
|
||||
case 1: item = ADIF_bands[row].lower_bound_; break;
|
||||
case 2: item = ADIF_bands[row].upper_bound_; break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
item = Qt::AlignHCenter + Qt::AlignVCenter;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
QVariant Bands::headerData (int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant result;
|
||||
|
||||
if (Qt::DisplayRole == role && Qt::Horizontal == orientation)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case 0: result = tr ("Band"); break;
|
||||
case 1: result = tr ("Lower Limit"); break;
|
||||
case 2: result = tr ("Upper Limit"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = QAbstractTableModel::headerData (section, orientation, role);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
58
Bands.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
#ifndef BANDS_HPP__
|
||||
#define BANDS_HPP__
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
//
|
||||
// Class Bands
|
||||
//
|
||||
// Encapsulates information about amateur radio bands as defined by
|
||||
// the ADIF specification. The model is immutable. The rows are
|
||||
// stored in asscending order of frequency.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Provides a well known band name mapped to lower and upper
|
||||
// frequency limits. Also provides a convenience operation to
|
||||
// determine the band details for any given frequency, the result of
|
||||
// which may be invalid if the given frequency doesn't lie within a
|
||||
// recognised band.
|
||||
//
|
||||
// Collaborations
|
||||
//
|
||||
// Implements the QAbstractTableModel interface as an immutable table
|
||||
// where rows are bands and columns are band name, lower frequency
|
||||
// limit and, upper ferquency limit respectively.
|
||||
//
|
||||
class Bands final
|
||||
: public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
explicit Bands (QObject * parent = nullptr);
|
||||
|
||||
//
|
||||
// Model API
|
||||
//
|
||||
QModelIndex find (QVariant const&) const; // find band Frequency is in
|
||||
|
||||
// Custom role for sorting.
|
||||
static int constexpr SortRole = Qt::UserRole;
|
||||
|
||||
// Implement the QAbstractTableModel interface
|
||||
int rowCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
int columnCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override;
|
||||
QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override;
|
||||
|
||||
// The value return for the Qt::DisplayRole role for the root of the
|
||||
// model (invalid index) is a special string representing out of
|
||||
// band.
|
||||
//
|
||||
// All columns return a number for the custom role SortRole, this
|
||||
// number defines a strict frequency order for the rows.
|
||||
QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override;
|
||||
};
|
||||
|
||||
#endif
|
93
CMake/Modules/FindFFTW3.cmake
Normal file
@ -0,0 +1,93 @@
|
||||
# - Try to find FFTW3.
|
||||
# Usage: find_package(FFTW3 [COMPONENTS [single double long-double threads]])
|
||||
#
|
||||
# Variables used by this module:
|
||||
# FFTW3_ROOT_DIR - FFTW3 root directory
|
||||
# Variables defined by this module:
|
||||
# FFTW3_FOUND - system has FFTW3
|
||||
# FFTW3_INCLUDE_DIR - the FFTW3 include directory (cached)
|
||||
# FFTW3_INCLUDE_DIRS - the FFTW3 include directories
|
||||
# (identical to FFTW3_INCLUDE_DIR)
|
||||
# FFTW3[FL]?_LIBRARY - the FFTW3 library - double, single(F),
|
||||
# long-double(L) precision (cached)
|
||||
# FFTW3[FL]?_THREADS_LIBRARY - the threaded FFTW3 library - double, single(F),
|
||||
# long-double(L) precision (cached)
|
||||
# FFTW3_LIBRARIES - list of all FFTW3 libraries found
|
||||
|
||||
# Copyright (C) 2009-2010
|
||||
# ASTRON (Netherlands Institute for Radio Astronomy)
|
||||
# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
|
||||
#
|
||||
# This file is part of the LOFAR software suite.
|
||||
# The LOFAR software suite is free software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# The LOFAR software suite is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# $Id: FindFFTW3.cmake 15918 2010-06-25 11:12:42Z loose $
|
||||
|
||||
# Use double precision by default.
|
||||
if (FFTW3_FIND_COMPONENTS MATCHES "^$")
|
||||
set (_components double)
|
||||
else ()
|
||||
set (_components ${FFTW3_FIND_COMPONENTS})
|
||||
endif ()
|
||||
|
||||
# Loop over each component.
|
||||
set (_libraries)
|
||||
foreach (_comp ${_components})
|
||||
if (_comp STREQUAL "single")
|
||||
list (APPEND _libraries fftw3f)
|
||||
elseif (_comp STREQUAL "double")
|
||||
list (APPEND _libraries fftw3)
|
||||
elseif (_comp STREQUAL "long-double")
|
||||
list (APPEND _libraries fftw3l)
|
||||
elseif (_comp STREQUAL "threads")
|
||||
set (_use_threads ON)
|
||||
else (_comp STREQUAL "single")
|
||||
message (FATAL_ERROR "FindFFTW3: unknown component `${_comp}' specified. "
|
||||
"Valid components are `single', `double', `long-double', and `threads'.")
|
||||
endif (_comp STREQUAL "single")
|
||||
endforeach (_comp ${_components})
|
||||
|
||||
# If using threads, we need to link against threaded libraries as well.
|
||||
if (_use_threads)
|
||||
set (_thread_libs)
|
||||
foreach (_lib ${_libraries})
|
||||
list (APPEND _thread_libs ${_lib}_threads)
|
||||
endforeach (_lib ${_libraries})
|
||||
set (_libraries ${_thread_libs} ${_libraries})
|
||||
endif (_use_threads)
|
||||
|
||||
# Keep a list of variable names that we need to pass on to
|
||||
# find_package_handle_standard_args().
|
||||
set (_check_list)
|
||||
|
||||
# Search for all requested libraries.
|
||||
foreach (_lib ${_libraries})
|
||||
string (TOUPPER ${_lib} _LIB)
|
||||
find_library (${_LIB}_LIBRARY NAMES ${_lib} ${_lib}-3
|
||||
HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES lib)
|
||||
mark_as_advanced (${_LIB}_LIBRARY)
|
||||
list (APPEND FFTW3_LIBRARIES ${${_LIB}_LIBRARY})
|
||||
list (APPEND _check_list ${_LIB}_LIBRARY)
|
||||
endforeach (_lib ${_libraries})
|
||||
|
||||
# Search for the header file.
|
||||
find_path (FFTW3_INCLUDE_DIR fftw3.h
|
||||
HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES include)
|
||||
mark_as_advanced (FFTW3_INCLUDE_DIR)
|
||||
list(APPEND _check_list FFTW3_INCLUDE_DIR)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include (FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args (FFTW3 DEFAULT_MSG ${_check_list})
|
28
CMake/Modules/Findhamlib.cmake
Normal file
@ -0,0 +1,28 @@
|
||||
# - Try to find hamlib
|
||||
# Once done, this will define:
|
||||
#
|
||||
# hamlib_FOUND - system has Hamlib-2
|
||||
# hamlib_INCLUDE_DIRS - the Hamlib-2 include directories
|
||||
# hamlib_LIBRARIES - link these to use Hamlib-2
|
||||
|
||||
include (LibFindMacros)
|
||||
|
||||
# Use pkg-config to get hints about paths
|
||||
libfind_pkg_check_modules (hamlib_PKGCONF hamlib)
|
||||
|
||||
# Include dirs
|
||||
find_path (hamlib_INCLUDE_DIR
|
||||
NAMES hamlib/rig.h
|
||||
PATHS ${hamlib_PKGCONF_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# The library
|
||||
find_library (hamlib_LIBRARY
|
||||
NAMES hamlib hamlib-2
|
||||
PATHS ${hamlib_PKGCONF_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
# Set the include dir variables and the libraries and let libfind_process do the rest
|
||||
set (hamlib_PROCESS_INCLUDES hamlib_INCLUDE_DIR)
|
||||
set (hamlib_PROCESS_LIBS hamlib_LIBRARY)
|
||||
libfind_process (hamlib)
|
112
CMake/Modules/LibFindMacros.cmake
Normal file
@ -0,0 +1,112 @@
|
||||
# Version 1.0 (2013-04-12)
|
||||
# Public Domain, originally written by Lasse Kärkkäinen <tronic@zi.fi>
|
||||
# Published at http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries
|
||||
|
||||
# If you improve the script, please modify the forementioned wiki page because
|
||||
# I no longer maintain my scripts (hosted as static files at zi.fi). Feel free
|
||||
# to remove this entire header if you use real version control instead.
|
||||
|
||||
# Changelog:
|
||||
# 2013-04-12 Added version number (1.0) and this header, no other changes
|
||||
# 2009-10-08 Originally published
|
||||
|
||||
|
||||
# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments
|
||||
# used for the current package. For this to work, the first parameter must be the
|
||||
# prefix of the current package, then the prefix of the new package etc, which are
|
||||
# passed to find_package.
|
||||
macro (libfind_package PREFIX)
|
||||
set (LIBFIND_PACKAGE_ARGS ${ARGN})
|
||||
if (${PREFIX}_FIND_QUIETLY)
|
||||
set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET)
|
||||
endif (${PREFIX}_FIND_QUIETLY)
|
||||
if (${PREFIX}_FIND_REQUIRED)
|
||||
set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED)
|
||||
endif (${PREFIX}_FIND_REQUIRED)
|
||||
find_package(${LIBFIND_PACKAGE_ARGS})
|
||||
endmacro (libfind_package)
|
||||
|
||||
# CMake developers made the UsePkgConfig system deprecated in the same release (2.6)
|
||||
# where they added pkg_check_modules. Consequently I need to support both in my scripts
|
||||
# to avoid those deprecated warnings. Here's a helper that does just that.
|
||||
# Works identically to pkg_check_modules, except that no checks are needed prior to use.
|
||||
macro (libfind_pkg_check_modules PREFIX PKGNAME)
|
||||
if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
include(UsePkgConfig)
|
||||
pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS)
|
||||
else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(${PREFIX} ${PKGNAME})
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
|
||||
endmacro (libfind_pkg_check_modules)
|
||||
|
||||
# Do the final processing once the paths have been detected.
|
||||
# If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain
|
||||
# all the variables, each of which contain one include directory.
|
||||
# Ditto for ${PREFIX}_PROCESS_LIBS and library files.
|
||||
# Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES.
|
||||
# Also handles errors in case library detection was required, etc.
|
||||
macro (libfind_process PREFIX)
|
||||
# Skip processing if already processed during this run
|
||||
if (NOT ${PREFIX}_FOUND)
|
||||
# Start with the assumption that the library was found
|
||||
set (${PREFIX}_FOUND TRUE)
|
||||
|
||||
# Process all includes and set _FOUND to false if any are missing
|
||||
foreach (i ${${PREFIX}_PROCESS_INCLUDES})
|
||||
if (${i})
|
||||
set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}})
|
||||
mark_as_advanced(${i})
|
||||
else (${i})
|
||||
set (${PREFIX}_FOUND FALSE)
|
||||
endif (${i})
|
||||
endforeach (i)
|
||||
|
||||
# Process all libraries and set _FOUND to false if any are missing
|
||||
foreach (i ${${PREFIX}_PROCESS_LIBS})
|
||||
if (${i})
|
||||
set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}})
|
||||
mark_as_advanced(${i})
|
||||
else (${i})
|
||||
set (${PREFIX}_FOUND FALSE)
|
||||
endif (${i})
|
||||
endforeach (i)
|
||||
|
||||
# Print message and/or exit on fatal error
|
||||
if (${PREFIX}_FOUND)
|
||||
if (NOT ${PREFIX}_FIND_QUIETLY)
|
||||
message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}")
|
||||
endif (NOT ${PREFIX}_FIND_QUIETLY)
|
||||
else (${PREFIX}_FOUND)
|
||||
if (${PREFIX}_FIND_REQUIRED)
|
||||
foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS})
|
||||
message("${i}=${${i}}")
|
||||
endforeach (i)
|
||||
message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.")
|
||||
endif (${PREFIX}_FIND_REQUIRED)
|
||||
endif (${PREFIX}_FOUND)
|
||||
endif (NOT ${PREFIX}_FOUND)
|
||||
endmacro (libfind_process)
|
||||
|
||||
macro(libfind_library PREFIX basename)
|
||||
set(TMP "")
|
||||
if(MSVC80)
|
||||
set(TMP -vc80)
|
||||
endif(MSVC80)
|
||||
if(MSVC90)
|
||||
set(TMP -vc90)
|
||||
endif(MSVC90)
|
||||
set(${PREFIX}_LIBNAMES ${basename}${TMP})
|
||||
if(${ARGC} GREATER 2)
|
||||
set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2})
|
||||
string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES})
|
||||
set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP})
|
||||
endif(${ARGC} GREATER 2)
|
||||
find_library(${PREFIX}_LIBRARY
|
||||
NAMES ${${PREFIX}_LIBNAMES}
|
||||
PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}
|
||||
)
|
||||
endmacro(libfind_library)
|
||||
|
36
CMake/Modules/QtAxMacros.cmake
Normal file
@ -0,0 +1,36 @@
|
||||
#
|
||||
# Macros for processing ActiveX and COM controls with ActiveQt
|
||||
#
|
||||
|
||||
if (WIN32)
|
||||
include (CMakeParseArguments)
|
||||
|
||||
find_program (DUMPCPP_Executable dumpcpp.exe)
|
||||
|
||||
# wrap_ax_server (outfiles inputfile ...)
|
||||
|
||||
function (WRAP_AX_SERVER outfiles)
|
||||
set (options)
|
||||
set (oneValueArgs)
|
||||
set (multiValueArgs OPTIONS)
|
||||
|
||||
cmake_parse_arguments (_WRAP_AX_SERVER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
set (ax_server_files ${_WRAP_AX_SERVER_UNPARSED_ARGUMENTS})
|
||||
set (ax_server_options ${_WRAP_AX_SERVER_OPTIONS})
|
||||
|
||||
foreach (it ${ax_server_files})
|
||||
get_filename_component (outfile ${it} NAME_WE)
|
||||
get_filename_component (infile ${it} ABSOLUTE)
|
||||
set (outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfile})
|
||||
add_custom_command (
|
||||
OUTPUT ${outfile}.h ${outfile}.cpp
|
||||
COMMAND ${DUMPCPP_Executable}
|
||||
ARGS ${AX_SERVER_options} -o "${outfile}" "${infile}"
|
||||
MAIN_DEPENDENCY ${infile} VERBATIM)
|
||||
list (APPEND ${outfiles} ${outfile}.cpp)
|
||||
endforeach()
|
||||
set(${outfiles} ${${outfiles}} PARENT_SCOPE)
|
||||
endfunction ()
|
||||
|
||||
endif (WIN32)
|
8
CMake/VersionCompute.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
# Load version number components.
|
||||
include (${wsjtx_SOURCE_DIR}/Versions.cmake)
|
||||
|
||||
# Compute the full version string.
|
||||
set (wsjtx_VERSION ${WSJTX_VERSION_MAJOR}.${WSJTX_VERSION_MINOR}.${WSJTX_VERSION_PATCH})
|
||||
if (WSJTX_RC)
|
||||
set (wsjtx_VERSION ${wsjtx_VERSION}-rc${WSJTX_RC})
|
||||
endif ()
|
22
CMake/cmake_uninstall.cmake.in
Normal file
@ -0,0 +1,22 @@
|
||||
if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||
message (FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||
endif ()
|
||||
|
||||
file (READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||
string (REGEX REPLACE "\n" ";" files "${files}")
|
||||
foreach (file ${files})
|
||||
message (STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
|
||||
if (EXISTS "$ENV{DESTDIR}${file}")
|
||||
exec_program (
|
||||
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||
OUTPUT_VARIABLE rm_out
|
||||
RETURN_VALUE rm_retval
|
||||
)
|
||||
if ("${rm_retval}" STREQUAL 0)
|
||||
else ()
|
||||
message (FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||
endif ()
|
||||
else ()
|
||||
message (STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
|
||||
endif ()
|
||||
endforeach ()
|
15
CMake/getsvn.cmake
Normal file
@ -0,0 +1,15 @@
|
||||
find_package (Subversion)
|
||||
if (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn)
|
||||
# the FindSubversion.cmake module is part of the standard distribution
|
||||
include (FindSubversion)
|
||||
# extract working copy information for SOURCE_DIR into MY_XXX variables
|
||||
Subversion_WC_INFO (${SOURCE_DIR} MY)
|
||||
# write a file with the SVNVERSION define
|
||||
file (WRITE svnversion.h.txt "#define SVNVERSION r${MY_WC_REVISION}\n")
|
||||
else (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn)
|
||||
file (WRITE svnversion.h.txt "#define SVNVERSION local\n")
|
||||
endif (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn)
|
||||
|
||||
# copy the file to the final header only if the version changes
|
||||
# reduces needless rebuilds
|
||||
execute_process (COMMAND ${CMAKE_COMMAND} -E copy_if_different svnversion.h.txt svnversion.h)
|
81
CMakeCPackOptions.cmake.in
Normal file
@ -0,0 +1,81 @@
|
||||
# This file is configured at cmake time, and loaded at cpack time.
|
||||
# To pass variables to cpack from cmake, they must be configured
|
||||
# in this file.
|
||||
|
||||
set (CPACK_PACKAGE_VENDOR "Joe Taylor, K1JT")
|
||||
set (CPACK_PACKAGE_CONTACT "k1jt@arrl.net")
|
||||
set (CPACK_PACKAGE_DESCRIPTION_FILE "@PROJECT_SOURCE_DIR@/Copyright.txt")
|
||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "WSJT-X - JT9 and JT65 Modes for LF, MF and HF Amateur Radio")
|
||||
set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/LICENSE_WHEATLEY.txt")
|
||||
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
||||
set (CPACK_PACKAGE_EXECUTABLES wsjtx "WSJT-X")
|
||||
set (CPACK_CREATE_DESKTOP_LINKS wsjtx)
|
||||
set (CPACK_STRIP_FILES TRUE)
|
||||
|
||||
#
|
||||
# components
|
||||
#
|
||||
#set (CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "WSJT-X Application")
|
||||
#set (CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "@WSJTX_DESCRIPTION_SUMMARY@")
|
||||
|
||||
if (CPACK_GENERATOR MATCHES "NSIS")
|
||||
set (CPACK_STRIP_FILES FALSE) # breaks Qt packaging on Windows
|
||||
|
||||
set (CPACK_NSIS_INSTALL_ROOT "C:\\WSJT")
|
||||
|
||||
# set the install/unistall icon used for the installer itself
|
||||
# There is a bug in NSI that does not handle full unix paths properly.
|
||||
set (CPACK_NSIS_MUI_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\wsjtx.ico")
|
||||
set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\wsjtx.ico")
|
||||
# set the package header icon for MUI
|
||||
set (CPACK_PACKAGE_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\installer_logo.bmp")
|
||||
# tell cpack to create links to the doc files
|
||||
set (CPACK_NSIS_MENU_LINKS
|
||||
"@PROJECT_MANUAL@" "WSJT-X Documentation"
|
||||
"@PROJECT_HOMEPAGE@" "WSJT-X Web Site"
|
||||
)
|
||||
# Use the icon from wsjtx for add-remove programs
|
||||
set (CPACK_NSIS_INSTALLED_ICON_NAME "@PROJECT_BINARY_DIR@\\wsjtx.exe")
|
||||
|
||||
set (CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
|
||||
set (CPACK_NSIS_HELP_LINK "@PROJECT_MANUAL@")
|
||||
set (CPACK_NSIS_URL_INFO_ABOUT "@PROJECT_HOMEPAGE@")
|
||||
set (CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}")
|
||||
set (CPACK_NSIS_MUI_FINISHPAGE_RUN "wsjtx.exe")
|
||||
set (CPACK_NSIS_MODIFY_PATH ON)
|
||||
endif ()
|
||||
|
||||
if ("${CPACK_GENERATOR}" STREQUAL "PackageMaker")
|
||||
set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-pkg")
|
||||
set (CPACK_PACKAGE_DEFAULT_LOCATION "/Applications")
|
||||
set (CPACK_PACKAGING_INSTALL_PREFIX "/")
|
||||
endif ()
|
||||
|
||||
if ("${CPACK_GENERATOR}" STREQUAL "DragNDrop")
|
||||
set (CPACK_DMG_BACKGROUND_IMAGE "@PROJECT_SOURCE_DIR@/icons/Darwin/DragNDrop Background.png")
|
||||
set (CPACK_DMG_DS_STORE "@PROJECT_SOURCE_DIR@/wsjtx_DMG.DS_Store")
|
||||
set (CPACK_BUNDLE_NAME "@WSJTX_BUNDLE_NAME@")
|
||||
set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/wsjtx.icns")
|
||||
set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/wsjtx.icns")
|
||||
set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-wsjtx-startup.sh")
|
||||
endif ()
|
||||
|
||||
if ("${CPACK_GENERATOR}" STREQUAL "WIX")
|
||||
# Reset CPACK_PACKAGE_VERSION to deal with WiX restriction.
|
||||
# But the file names still use the full CMake_VERSION value:
|
||||
set (CPACK_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}-@wsjtx_VERSION@-${CPACK_SYSTEM_NAME}")
|
||||
set (CPACK_SOURCE_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}-@wsjtx_VERSION@-Source")
|
||||
|
||||
if (NOT CPACK_WIX_SIZEOF_VOID_P)
|
||||
set (CPACK_WIX_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if ("${CPACK_GENERATOR}" STREQUAL "DEB")
|
||||
set (CPACK_PACKAGE_FILE_NAME "@DEBIAN_PACKAGE_FILE_NAME@")
|
||||
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "@PROJECT_HOMEPAGE@")
|
||||
endif ("${CPACK_GENERATOR}" STREQUAL "DEB")
|
||||
|
||||
message (STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
|
852
CMakeLists.txt
@ -1,9 +1,314 @@
|
||||
cmake_minimum_required (VERSION 2.8.9)
|
||||
cmake_minimum_required (VERSION 2.8.9 FATAL_ERROR)
|
||||
|
||||
project (wsjtx C CXX Fortran)
|
||||
|
||||
set (WSJTX_VERSION_MAJOR 1)
|
||||
set (WSJTX_VERSION_MINOR 3)
|
||||
set (WSJTX_COPYRIGHT "Copyright (C) 2001-2014 by Joe Taylor, K1JT")
|
||||
|
||||
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMake/Modules)
|
||||
|
||||
include (${PROJECT_SOURCE_DIR}/CMake/VersionCompute.cmake)
|
||||
|
||||
|
||||
#
|
||||
# Options & features
|
||||
#
|
||||
# Some of these directly effect compilation by being defined in
|
||||
# wsjtx_config.h.in which makes them available to the C/C++
|
||||
# pre-processor.
|
||||
#
|
||||
option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts
|
||||
files (WARNING: make clean will delete the source .ts files! Danger!)")
|
||||
option (WSJT_SHARED_RUNTIME "Debugging option that allows running from a shared Cloud directory.")
|
||||
option (WSJT_QDEBUG_IN_RELEASE "Leave qDebug statements in Release configuration.")
|
||||
option (WSJT_TRACE_CAT "Debugging option that turns on CAT diagnostics.")
|
||||
option (WSJT_TRACE_CAT_POLLS "Debugging option that turns on CAT diagnostics during polling.")
|
||||
option (WSJT_HAMLIB_TRACE "Debugging option that turns on full Hamlib internal diagnostics.")
|
||||
option (WSJT_STANDARD_FILE_LOCATIONS "All non-installation files located in \"Standard\" platfom specific locations." ON)
|
||||
|
||||
|
||||
#
|
||||
# install locations
|
||||
#
|
||||
set (WSJT_BIN_DESTINATION bin)
|
||||
set (WSJT_LIB_DESTINATION lib)
|
||||
set (WSJT_SHARE_DESTINATION share/wsjtx)
|
||||
set (WSJT_DOC_DESTINATION share/wsjtx/doc)
|
||||
set (WSJT_PLUGIN_DESTINATION lib/plugins)
|
||||
set (WSJT_QT_CONF_DESTINATION bin)
|
||||
|
||||
|
||||
#
|
||||
# project information
|
||||
#
|
||||
set (PROJECT_HOMEPAGE "http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html")
|
||||
set (PROJECT_MANUAL "http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjtx-main-toc2.html")
|
||||
|
||||
|
||||
#
|
||||
# Project sources
|
||||
#
|
||||
set (wsjt_qt_CXXSRCS
|
||||
NetworkServerLookup.cpp
|
||||
Radio.cpp
|
||||
Bands.cpp
|
||||
FrequencyList.cpp
|
||||
StationList.cpp
|
||||
FrequencyLineEdit.cpp
|
||||
FrequencyItemDelegate.cpp
|
||||
ForeignKeyDelegate.cpp
|
||||
LiveFrequencyValidator.cpp
|
||||
GetUserId.cpp
|
||||
TraceFile.cpp
|
||||
AudioDevice.cpp
|
||||
Transceiver.cpp
|
||||
TransceiverBase.cpp
|
||||
EmulateSplitTransceiver.cpp
|
||||
TransceiverFactory.cpp
|
||||
PollingTransceiver.cpp
|
||||
HamlibTransceiver.cpp
|
||||
HRDTransceiver.cpp
|
||||
DXLabSuiteCommanderTransceiver.cpp
|
||||
)
|
||||
|
||||
set (ConfigTest_CXXSRCS
|
||||
Configuration.cpp
|
||||
TestConfiguration.cpp
|
||||
ConfigTest.cpp
|
||||
)
|
||||
|
||||
set (jt9_CXXSRCS
|
||||
lib/ipcomm.cpp
|
||||
)
|
||||
|
||||
set (wsjtx_CXXSRCS
|
||||
logbook/adif.cpp
|
||||
logbook/countrydat.cpp
|
||||
logbook/countriesworked.cpp
|
||||
logbook/logbook.cpp
|
||||
psk_reporter.cpp
|
||||
Modulator.cpp
|
||||
Detector.cpp
|
||||
logqso.cpp
|
||||
displaytext.cpp
|
||||
decodedtext.cpp
|
||||
getfile.cpp
|
||||
soundout.cpp
|
||||
soundin.cpp
|
||||
meterwidget.cpp
|
||||
signalmeter.cpp
|
||||
plotter.cpp
|
||||
widegraph.cpp
|
||||
about.cpp
|
||||
astro.cpp
|
||||
mainwindow.cpp
|
||||
Configuration.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
set (ConfigTest_CXXSRCS
|
||||
${ConfigTest_CXXSRCS}
|
||||
OmniRigTransceiver.cpp
|
||||
)
|
||||
|
||||
set (wsjt_CXXSRCS
|
||||
${wsjt_CXXSRCS}
|
||||
killbyname.cpp
|
||||
)
|
||||
|
||||
set (wsjt_qt_CXXSRCS
|
||||
${wsjt_qt_CXXSRCS}
|
||||
OmniRigTransceiver.cpp
|
||||
)
|
||||
endif (WIN32)
|
||||
|
||||
set (wsjt_FSRCS
|
||||
lib/afc65b.f90
|
||||
lib/afc9.f90
|
||||
lib/analytic.f90
|
||||
lib/astro.f90
|
||||
lib/astrosub.f90
|
||||
lib/astro0.f90
|
||||
lib/azdist.f90
|
||||
lib/baddata.f90
|
||||
lib/ccf2.f90
|
||||
lib/ccf65.f90
|
||||
lib/chkhist.f90
|
||||
lib/chkss2.f90
|
||||
lib/coord.f90
|
||||
lib/db.f90
|
||||
lib/dcoord.f90
|
||||
lib/decode65a.f90
|
||||
lib/decode65b.f90
|
||||
lib/decode9.f90
|
||||
lib/decoder.f90
|
||||
lib/deg2grid.f90
|
||||
lib/demod64a.f90
|
||||
lib/determ.f90
|
||||
lib/dot.f90
|
||||
lib/downsam9.f90
|
||||
lib/encode232.f90
|
||||
lib/entail.f90
|
||||
lib/extract.F90
|
||||
lib/geocentric.f90
|
||||
lib/f77_wisdom.f90
|
||||
lib/fano232.f90
|
||||
lib/fchisq.f90
|
||||
lib/fchisq65.f90
|
||||
lib/fil3.f90
|
||||
lib/fil4.f90
|
||||
lib/fil6521.f90
|
||||
lib/filbig.f90
|
||||
lib/fillcom.f90
|
||||
lib/flat1.f90
|
||||
lib/flat2.f90
|
||||
lib/flat3.f90
|
||||
lib/flat65.f90
|
||||
lib/four2a.f90
|
||||
lib/gen65.f90
|
||||
lib/genjt9.f90
|
||||
lib/geodist.f90
|
||||
lib/getlags.f90
|
||||
lib/getpfx1.f90
|
||||
lib/getpfx2.f90
|
||||
lib/graycode.f90
|
||||
lib/graycode65.f90
|
||||
lib/grid2deg.f90
|
||||
lib/grid2k.f90
|
||||
lib/grid2n.f90
|
||||
lib/indexx.f90
|
||||
lib/interleave63.f90
|
||||
lib/interleave9.f90
|
||||
lib/jt65a.f90
|
||||
lib/k2grid.f90
|
||||
lib/moon2.f90
|
||||
lib/moondop.f90
|
||||
lib/morse.f90
|
||||
lib/move.f90
|
||||
lib/n2grid.f90
|
||||
lib/nchar.f90
|
||||
lib/options.f90
|
||||
lib/packbits.f90
|
||||
lib/packcall.f90
|
||||
lib/packgrid.f90
|
||||
lib/packmsg.f90
|
||||
lib/packtext.f90
|
||||
lib/pctile.f90
|
||||
lib/peakdt9.f90
|
||||
lib/pfxdump.f90
|
||||
lib/polfit.f90
|
||||
lib/prog_args.f90
|
||||
lib/sec_midn.f90
|
||||
lib/setup65.f90
|
||||
lib/sleep_msec.f90
|
||||
lib/smo.f90
|
||||
lib/smo121.f90
|
||||
lib/softsym.f90
|
||||
lib/sort.f90
|
||||
lib/ssort.f90
|
||||
lib/stdmsg.f90
|
||||
lib/sun.f90
|
||||
lib/symspec.f90
|
||||
lib/symspec2.f90
|
||||
lib/symspec65.f90
|
||||
lib/sync9.f90
|
||||
lib/timer.f90
|
||||
lib/tm2.f90
|
||||
lib/toxyz.f90
|
||||
lib/twkfreq.f90
|
||||
lib/twkfreq65.f90
|
||||
lib/unpackbits.f90
|
||||
lib/unpackcall.f90
|
||||
lib/unpackgrid.f90
|
||||
lib/unpackmsg.f90
|
||||
lib/unpacktext.f90
|
||||
lib/zplot9.f90
|
||||
)
|
||||
|
||||
set (wsjt_CSRCS
|
||||
lib/decode_rs.c
|
||||
lib/encode_rs.c
|
||||
lib/gran.c
|
||||
lib/igray.c
|
||||
lib/init_rs.c
|
||||
lib/tmoonsub.c
|
||||
lib/usleep.c
|
||||
lib/wrapkarn.c
|
||||
)
|
||||
|
||||
set (ConfigTest_UISRCS
|
||||
TestConfiguration.ui
|
||||
Configuration.ui
|
||||
)
|
||||
|
||||
set (wsjtx_UISRCS
|
||||
mainwindow.ui
|
||||
about.ui
|
||||
astro.ui
|
||||
widegraph.ui
|
||||
logqso.ui
|
||||
Configuration.ui
|
||||
)
|
||||
|
||||
set (all_C_and_CXXSRCS
|
||||
${wsjt_CSRCS}
|
||||
${wsjt_CXXSRCS}
|
||||
${wsjt_qt_CXXSRCS}
|
||||
${jt9_CXXSRCS}
|
||||
${ConfigTest_CXXSRCS}
|
||||
${wsjtx_CXXSRCS}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
set (WSJTX_ICON_FILE ${CMAKE_PROJECT_NAME}.icns)
|
||||
set (ICONSRCS
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_16x16.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_16x16@2x.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_32x32.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_32x32@2x.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_128x128.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_128x128@2x.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_256x256.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_256x256@2x.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_512x512.png
|
||||
icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_512x512@2x.png
|
||||
)
|
||||
add_custom_command (
|
||||
OUTPUT ${WSJTX_ICON_FILE}
|
||||
COMMAND iconutil -c icns --output "${CMAKE_BINARY_DIR}/${WSJTX_ICON_FILE}" "${CMAKE_SOURCE_DIR}/icons/Darwin/${CMAKE_PROJECT_NAME}.iconset"
|
||||
DEPENDS ${ICONSRCS}
|
||||
COMMENT "Building Icons"
|
||||
)
|
||||
endif (APPLE)
|
||||
|
||||
set_source_files_properties (${WSJTX_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
|
||||
if (NOT WSJT_QDEBUG_IN_RELEASE)
|
||||
set_directory_properties (PROPERTIES COMPILE_DEFINITIONS_RELEASE "QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT")
|
||||
set_directory_properties (PROPERTIES COMPILE_DEFINITIONS_MINSIZEREL "QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT")
|
||||
endif (NOT WSJT_QDEBUG_IN_RELEASE)
|
||||
|
||||
set_property (SOURCE ${all_C_and_CXXSRCS} APPEND PROPERTY COMPILE_FLAGS "-include wsjtx_config.h")
|
||||
set_property (SOURCE ${all_C_and_CXXSRCS} APPEND PROPERTY OBJECT_DEPENDS wsjtx_config.h)
|
||||
|
||||
if (WIN32)
|
||||
# generate the OmniRig COM interface source
|
||||
find_program (DUMPCPP dumpcpp)
|
||||
if (DUMPCPP-NOTFOUND)
|
||||
message (FATAL_ERROR "dumpcpp tool not found")
|
||||
endif (DUMPCPP-NOTFOUND)
|
||||
execute_process (
|
||||
COMMAND ${DUMPCPP} -getfile {4FE359C5-A58F-459D-BE95-CA559FB4F270}
|
||||
OUTPUT_VARIABLE AXSERVER
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
string (STRIP "${AXSERVER}" AXSERVER)
|
||||
if (NOT AXSERVER)
|
||||
message (FATAL_ERROR "You need to install OmniRig on this computer")
|
||||
endif (NOT AXSERVER)
|
||||
string (REPLACE "\"" "" AXSERVER ${AXSERVER})
|
||||
file (TO_CMAKE_PATH ${AXSERVER} AXSERVERSRCS)
|
||||
endif (WIN32)
|
||||
|
||||
if (POLICY CMP0020)
|
||||
cmake_policy (SET CMP0020 NEW) # link to Qt winmain on Windows
|
||||
@ -18,79 +323,142 @@ endif (NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
|
||||
#
|
||||
# C++ setup
|
||||
# decide on platform specifc packing and fixing up
|
||||
#
|
||||
if (APPLE)
|
||||
set (WSJTX_BUNDLE_VERSION ${wsjtx_VERSION})
|
||||
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
# make sure CMAKE_INSTALL_PREFIX ends in /
|
||||
string (LENGTH "${CMAKE_INSTALL_PREFIX}" LEN)
|
||||
math (EXPR LEN "${LEN} -1" )
|
||||
string (SUBSTRING "${CMAKE_INSTALL_PREFIX}" ${LEN} 1 ENDCH)
|
||||
if (NOT "${ENDCH}" STREQUAL "/")
|
||||
set (CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/")
|
||||
endif ()
|
||||
# install inside bundle
|
||||
# set (CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}wsjtx.app")
|
||||
|
||||
set (WSJT_PLUGIN_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/PlugIns)
|
||||
set (WSJT_QT_CONF_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/Resources)
|
||||
set (WSJT_SHARE_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/Resources)
|
||||
set (WSJT_BIN_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/MacOS)
|
||||
endif (APPLE)
|
||||
|
||||
#set (QT_NEED_RPATH FALSE)
|
||||
#if (NOT "${QT_LIBRARY_DIR}" STREQUAL "/lib" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/usr/lib" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/lib64" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/usr/lib64")
|
||||
# set (QT_NEED_RPATH TRUE)
|
||||
#endif ()
|
||||
|
||||
|
||||
#
|
||||
# C & C++ setup
|
||||
#
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++11 -fexceptions -frtti")
|
||||
if (WIN32)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-keep-inline-dllexport")
|
||||
endif (WIN32)
|
||||
if (APPLE)
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
else (APPLE)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
endif (APPLE)
|
||||
|
||||
|
||||
set (CXXSRCS
|
||||
logbook/adif.cpp
|
||||
logbook/countrydat.cpp
|
||||
logbook/countriesworked.cpp
|
||||
logbook/logbook.cpp
|
||||
rigclass.cpp
|
||||
psk_reporter.cpp
|
||||
Modulator.cpp
|
||||
Detector.cpp
|
||||
logqso.cpp
|
||||
displaytext.cpp
|
||||
decodedtext.cpp
|
||||
getfile.cpp
|
||||
soundout.cpp
|
||||
soundin.cpp
|
||||
meterwidget.cpp
|
||||
signalmeter.cpp
|
||||
plotter.cpp
|
||||
widegraph.cpp
|
||||
devsetup.cpp
|
||||
about.cpp
|
||||
astro.cpp
|
||||
mainwindow.cpp
|
||||
main.cpp
|
||||
)
|
||||
#
|
||||
# Fortran setup
|
||||
#
|
||||
set (General_FFLAGS "-fbounds-check -Wall -Wno-conversion -fno-second-underscore")
|
||||
|
||||
if (WIN32)
|
||||
set (CXXSRCS ${CXXSRCS} killbyname.cpp)
|
||||
endif (WIN32)
|
||||
# FFLAGS depend on the compiler
|
||||
get_filename_component (Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME)
|
||||
|
||||
set_property (SOURCE ${CXXSRCS} APPEND PROPERTY COMPILE_FLAGS "-include wsjtx_config.h")
|
||||
if (Fortran_COMPILER_NAME MATCHES "gfortran.*")
|
||||
# gfortran
|
||||
set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3 ${General_FFLAGS}")
|
||||
set (CMAKE_Fortran_FLAGS_DEBUG "-fno-f2c -O0 -g ${General_FFLAGS}")
|
||||
elseif (Fortran_COMPILER_NAME MATCHES "ifort.*")
|
||||
# ifort (untested)
|
||||
set (CMAKE_Fortran_FLAGS_RELEASE "-f77rtl -O3 ${General_FFLAGS}")
|
||||
set (CMAKE_Fortran_FLAGS_DEBUG "-f77rtl -O0 -g ${General_FFLAGS}")
|
||||
elseif (Fortran_COMPILER_NAME MATCHES "g77")
|
||||
# g77
|
||||
set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3 -m32 ${General_FFLAGS}")
|
||||
set (CMAKE_Fortran_FLAGS_DEBUG "-fno-f2c -O0 -g -m32 ${General_FFLAGS}")
|
||||
else (Fortran_COMPILER_NAME MATCHES "gfortran.*")
|
||||
message ("CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER})
|
||||
message ("Fortran compiler: " ${Fortran_COMPILER_NAME})
|
||||
message ("No optimized Fortran compiler flags are known, we just try -O2...")
|
||||
set (CMAKE_Fortran_FLAGS_RELEASE "-O2 ${General_FFLAGS}")
|
||||
set (CMAKE_Fortran_FLAGS_DEBUG "-O0 -g ${General_FFLAGS}")
|
||||
endif (Fortran_COMPILER_NAME MATCHES "gfortran.*")
|
||||
|
||||
|
||||
#
|
||||
# setup and test Fortran C/C++ interaction
|
||||
#
|
||||
|
||||
include (FortranCInterface)
|
||||
FortranCInterface_VERIFY (CXX QUIET)
|
||||
FortranCInterface_HEADER (FC.h MACRO_NAMESPACE "FC_" SYMBOL_NAMESPACE "FC_"
|
||||
SYMBOLS )
|
||||
|
||||
set (UISRCS
|
||||
mainwindow.ui
|
||||
about.ui
|
||||
astro.ui
|
||||
devsetup.ui
|
||||
widegraph.ui
|
||||
logqso.ui
|
||||
)
|
||||
|
||||
#
|
||||
# sort out pre-requisites
|
||||
#
|
||||
|
||||
#
|
||||
# libfftw3 setup
|
||||
# Setup RPATH so that built executable targets will run in both the
|
||||
# build tree and the install location without having to set a
|
||||
# (DYLD|LD)_LIBRARY_PATH override.
|
||||
#
|
||||
find_path (fftw3f_INCLUDES fftw3.f)
|
||||
find_library (fftw3f_LIBRARIES NAMES fftw3f fftw3f-3)
|
||||
include_directories (${fftw3f_INCLUDES})
|
||||
|
||||
# use the full RPATH of the build tree
|
||||
set (CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
|
||||
# when building, don't use the install RPATH, it will still be used
|
||||
# later on in the install phase
|
||||
set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
|
||||
set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}")
|
||||
|
||||
# add teh automaticaly determined parts of the RPATH which point to
|
||||
# directories outside of the build tree to the install RPATH
|
||||
set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
# the RPATH to be used when installing, but only if it's not a system
|
||||
# directory
|
||||
list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}" isSystemDir)
|
||||
if ("${isSystemDir}" STREQUAL "-1")
|
||||
set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}")
|
||||
endif ("${isSystemDir}" STREQUAL "-1")
|
||||
|
||||
|
||||
#
|
||||
# fftw3 single precsion library
|
||||
#
|
||||
find_package (FFTW3 COMPONENTS single REQUIRED)
|
||||
if (NOT FFTW3_FOUND)
|
||||
message (FATAL_ERROR "fftw3 single precsion library not found")
|
||||
endif (NOT FFTW3_FOUND)
|
||||
include_directories (${FFTW3_INCLUDE_DIRS})
|
||||
|
||||
#
|
||||
# libhamlib setup
|
||||
#
|
||||
find_path (hamlib_INCLUDES hamlib/rig.h)
|
||||
find_library (hamlib_LIBRARIES NAMES hamlib hamlib-2 PATH_SUFFIXES msvc gcc)
|
||||
include_directories (${hamlib_INCLUDES})
|
||||
find_package (hamlib REQUIRED)
|
||||
if (NOT hamlib_FOUND)
|
||||
message (FATAL_ERROR "hamlib library not found")
|
||||
endif (NOT hamlib_FOUND)
|
||||
include_directories (${hamlib_INCLUDE_DIRS})
|
||||
|
||||
if (WIN32)
|
||||
find_library (hamlib_RUNTIME NAMES hamlib hamlib-2 PATH_SUFFIXES bin)
|
||||
get_filename_component (_hamlib_runtime_path "${hamlib_RUNTIME}" PATH)
|
||||
file (GLOB hamlib_BACKENDS ${_hamlib_runtime_path}/hamlib*.dll)
|
||||
find_library (usb_RUNTIME NAMES usb0 PATH_SUFFIXES bin)
|
||||
find_library (hamlib_RUNTIME NAMES hamlib hamlib-2 PATH_SUFFIXES bin)
|
||||
get_filename_component (_hamlib_runtime_path "${hamlib_RUNTIME}" PATH)
|
||||
file (GLOB hamlib_BACKENDS ${_hamlib_runtime_path}/hamlib*.dll)
|
||||
find_library (usb_RUNTIME NAMES usb0 PATH_SUFFIXES bin)
|
||||
endif (WIN32)
|
||||
|
||||
|
||||
@ -102,117 +470,373 @@ endif (WIN32)
|
||||
find_package (Qt5Widgets REQUIRED)
|
||||
find_package (Qt5Multimedia REQUIRED)
|
||||
|
||||
if (WIN32)
|
||||
add_definitions (-DQT_NEEDS_QTMAIN)
|
||||
find_package (Qt5AxContainer REQUIRED)
|
||||
endif (WIN32)
|
||||
|
||||
#
|
||||
# stuff only qmake can tell us
|
||||
#
|
||||
get_target_property (QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
||||
function (QUERY_QMAKE VAR RESULT)
|
||||
exec_program (${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output)
|
||||
if (NOT return_code)
|
||||
file (TO_CMAKE_PATH "${output}" output)
|
||||
set (${RESULT} ${output} PARENT_SCOPE)
|
||||
endif (NOT return_code)
|
||||
endfunction (QUERY_QMAKE)
|
||||
|
||||
query_qmake (QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
|
||||
query_qmake (QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
|
||||
query_qmake (QT_HOST_DATA QT_DATA_DIR)
|
||||
set (QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
||||
|
||||
# Tell CMake to run moc when necessary
|
||||
set (CMAKE_AUTOMOC ON)
|
||||
|
||||
# don't use Qt "keywords" signal, slot, emit in generated files to
|
||||
# avoid compatability issue with other libraries
|
||||
#ADD_DEFINITIONS (-DQT_NO_KEYWORDS)
|
||||
# ADD_DEFINITIONS (-DQT_NO_KEYWORDS)
|
||||
# ADD_DEFINITIONS (-DUNICODE) #as per qmake
|
||||
|
||||
# As moc files are generated in the binary dir, tell CMake to always
|
||||
# look for includes there:
|
||||
set (CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# project definitions
|
||||
add_definitions (-DQT5)
|
||||
add_definitions (-DQT5 -DCMAKE_BUILD -DBIGSYM=1)
|
||||
if (CMAKE_HOST_UNIX)
|
||||
add_definitions (-DUNIX)
|
||||
elseif (CMAKE_HOST_WIN32)
|
||||
add_definitions (-DWIN32)
|
||||
endif ()
|
||||
|
||||
# add_definitions (-DWSJT_SOFT_KEYING)
|
||||
set_property (DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_RELEASE QT_NO_DEBUG_OUTPUT QT_NO_WARNING_OUTPUT)
|
||||
|
||||
|
||||
#
|
||||
# build the subdirectories
|
||||
#
|
||||
add_subdirectory (lib)
|
||||
|
||||
|
||||
#
|
||||
# fetch kvasd
|
||||
#
|
||||
if (APPLE)
|
||||
set (kvasd_NAME http://svn.berlios.de/wsvn/wsjt/trunk/KVASD_gfortran_Mac${CMAKE_EXECUTABLE_SUFFIX})
|
||||
else ()
|
||||
set (kvasd_NAME http://www.physics.princeton.edu/pulsar/K1JT/kvasd${CMAKE_EXECUTABLE_SUFFIX})
|
||||
endif ()
|
||||
set (kvasd_NAME https://svn.code.sf.net/p/wsjt/wsjt/trunk/kvasd-binary/${CMAKE_SYSTEM_NAME}/kvasd${CMAKE_EXECUTABLE_SUFFIX})
|
||||
file (
|
||||
DOWNLOAD ${kvasd_NAME} contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX}
|
||||
TIMEOUT 10
|
||||
STATUS kvasd_STATUS
|
||||
LOG kvasd_LOG
|
||||
SHOW_PROGRESS
|
||||
)
|
||||
message (${kvasd_LOG})
|
||||
list (GET kvasd_STATUS 0 kvasd_RC)
|
||||
if (!kvasd_RC)
|
||||
message (FATAL_ERROR "${kvasd_STATUS}")
|
||||
endif (!kvasd_RC)
|
||||
if (kvasd_RC)
|
||||
message (WARNING "${kvasd_STATUS}")
|
||||
endif (kvasd_RC)
|
||||
add_custom_target (kvasd DEPENDS contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX})
|
||||
|
||||
|
||||
# UI generation
|
||||
qt5_wrap_ui (GENUISRCS ${UISRCS})
|
||||
qt5_wrap_ui (GENUISRCS ${ConfigTest_UISRCS} ${wsjtx_UISRCS})
|
||||
|
||||
add_executable (wsjtx ${CXXSRCS} ${GENUISRCS} wsjtx.rc)
|
||||
target_link_libraries (wsjtx jt9impl ${hamlib_LIBRARIES} ${fftw3f_LIBRARIES})
|
||||
# Resource generation
|
||||
qt5_add_resources (wsjtx_RESOURCES_RCC wsjtx.qrc)
|
||||
|
||||
# AX COM servers
|
||||
if (WIN32)
|
||||
target_link_libraries (wsjtx ${CMAKE_CURRENT_SOURCE_DIR}/libHRDInterface001.a)
|
||||
set_target_properties (wsjtx PROPERTIES LINK_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} -mwindows")
|
||||
include (QtAxMacros)
|
||||
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
||||
endif (WIN32)
|
||||
|
||||
|
||||
#
|
||||
# targets
|
||||
#
|
||||
|
||||
# build a library of package functionality
|
||||
add_library (wsjt STATIC ${wsjt_CSRCS} ${wsjt_CXXSRCS} ${wsjt_FSRCS})
|
||||
|
||||
# build a library of package Qt functionality
|
||||
add_library (wsjt_qt STATIC ${wsjt_qt_CXXSRCS} ${GENAXSRCS})
|
||||
qt5_use_modules (wsjt_qt Widgets OpenGL Network)
|
||||
if (WIN32)
|
||||
qt5_use_modules (wsjt_qt AxContainer AxBase)
|
||||
endif (WIN32)
|
||||
|
||||
add_executable (jt9sim lib/jt9sim.f90 wsjtx.rc)
|
||||
target_link_libraries (jt9sim wsjt)
|
||||
|
||||
add_executable (jt9code lib/jt9code.f90 wsjtx.rc)
|
||||
target_link_libraries (jt9code wsjt)
|
||||
|
||||
add_executable (jt9 lib/jt9.f90 lib/jt9a.f90 lib/jt9b.f90 lib/jt9c.f90 ${jt9_CXXSRCS} wsjtx.rc)
|
||||
target_link_libraries (jt9 wsjt ${FFTW3F_LIBRARY})
|
||||
qt5_use_modules (jt9 Core)
|
||||
|
||||
# build configuration dialog and transceiver interface test application
|
||||
#add_executable (ConfigTest ${ConfigTest_CXXSRCS} ${ConfigTest_UISRCS} wsjtx.rc)
|
||||
#target_link_libraries (ConfigTest wsjt wsjt_qt ${hamlib_LIBRARIES})
|
||||
#qt5_use_modules (ConfigTest Widgets OpenGL Network Multimedia)
|
||||
|
||||
# build the main application
|
||||
add_executable (wsjtx WIN32 MACOSX_BUNDLE ${wsjtx_CXXSRCS} ${wsjtx_UISRCS} wsjtx.rc ${WSJTX_ICON_FILE} ${wsjtx_RESOURCES_RCC})
|
||||
qt5_use_modules (wsjtx Widgets OpenGL Network Multimedia)
|
||||
|
||||
set_target_properties (wsjtx PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
|
||||
MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${wsjtx_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "v${wsjtx_VERSION}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${wsjtx_VERSION}"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "WSJT-X"
|
||||
MACOSX_BUNDLE_COPYRIGHT "${WSJTX_COPYRIGHT}"
|
||||
)
|
||||
|
||||
target_link_libraries (wsjtx wsjt wsjt_qt ${hamlib_LIBRARIES} ${FFTW3F_LIBRARY})
|
||||
add_dependencies (wsjtx kvasd)
|
||||
qt5_use_modules (wsjtx Widgets Multimedia OpenGL)
|
||||
|
||||
|
||||
install (
|
||||
TARGETS wsjtx
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
)
|
||||
|
||||
install (DIRECTORY Palettes DESTINATION bin PATTERN *.pal)
|
||||
install (DIRECTORY samples DESTINATION bin/save)
|
||||
install (FILES
|
||||
shortcuts.txt
|
||||
mouse_commands.txt
|
||||
prefixes.txt
|
||||
cty.dat
|
||||
kvasd.dat
|
||||
DESTINATION bin
|
||||
)
|
||||
|
||||
install (
|
||||
PROGRAMS ${CMAKE_BINARY_DIR}/contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX}
|
||||
DESTINATION bin
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
install (
|
||||
FILES ${hamlib_RUNTIME} ${hamlib_BACKENDS} ${fftw3f_LIBRARIES} ${usb_RUNTIME} contrib/HRDInterface001.dll
|
||||
DESTINATION bin COMPONENT Runtime
|
||||
set_target_properties (
|
||||
wsjtx
|
||||
# ConfigTest
|
||||
PROPERTIES LINK_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} -mwindows"
|
||||
)
|
||||
endif (WIN32)
|
||||
|
||||
|
||||
# a custom target that is always built
|
||||
ADD_CUSTOM_TARGET (revisiontag ALL)
|
||||
#
|
||||
# installation
|
||||
#
|
||||
install (TARGETS wsjtx
|
||||
RUNTIME DESTINATION ${WSJT_BIN_DESTINATION}
|
||||
LIBRARY DESTINATION ${WSJT_LIB_DESTINATION}
|
||||
BUNDLE DESTINATION .
|
||||
# COMPONENT Runtime
|
||||
)
|
||||
|
||||
# creates svnversion.h using cmake script
|
||||
ADD_CUSTOM_COMMAND (TARGET revisiontag COMMAND ${CMAKE_COMMAND}
|
||||
-DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake)
|
||||
install (TARGETS jt9
|
||||
RUNTIME DESTINATION ${WSJT_BIN_DESTINATION}
|
||||
LIBRARY DESTINATION ${WSJT_LIB_DESTINATION}
|
||||
BUNDLE DESTINATION ${WSJT_BIN_DESTINATION}
|
||||
# COMPONENT Runtime
|
||||
)
|
||||
|
||||
install (PROGRAMS
|
||||
${CMAKE_BINARY_DIR}/contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX}
|
||||
DESTINATION ${WSJT_BIN_DESTINATION}
|
||||
# COMPONENT Runtime
|
||||
)
|
||||
|
||||
# explicitly say that the executable depends on custom target
|
||||
add_dependencies(wsjtx revisiontag)
|
||||
|
||||
#
|
||||
# versioning
|
||||
# uninstall support
|
||||
#
|
||||
configure_file (
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
@ONLY)
|
||||
add_custom_target (uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
|
||||
|
||||
# creates svnversion.h using cmake script
|
||||
add_custom_target (revisiontag
|
||||
COMMAND ${CMAKE_COMMAND} -D SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/getsvn.cmake
|
||||
COMMENT "Generating Subversion revision information"
|
||||
VERBATIM
|
||||
)
|
||||
# explicitly say that the executable depends on custom target, this is
|
||||
# done indirectly so that the revisiontag target gets built exactly
|
||||
# once per build
|
||||
add_dependencies(wsjt revisiontag)
|
||||
add_dependencies(jt9 revisiontag)
|
||||
add_dependencies(wsjtx revisiontag)
|
||||
|
||||
|
||||
#
|
||||
# versioning and configuration
|
||||
#
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/wsjtx_config.h.in"
|
||||
"${PROJECT_BINARY_DIR}/wsjtx_config.h"
|
||||
)
|
||||
|
||||
include_directories ("${PROJECT_BINARY_DIR}")
|
||||
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
# install a desktop file so wsjtx appears in the application start
|
||||
# menu with an icon
|
||||
install (FILES wsjtx.desktop DESTINATION share/applications)
|
||||
install (FILES icons/Unix/wsjtx_icon.png DESTINATION share/pixmaps)
|
||||
endif (NOT WIN32 AND NOT APPLE)
|
||||
|
||||
|
||||
#
|
||||
# bundle fixup only done in Release or MinSizeRel configurations
|
||||
#
|
||||
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
# get_target_property (QtCore_location Qt5::Core LOCATION)
|
||||
# get_filename_component (QtCore_location ${QtCore_location} PATH)
|
||||
# list (APPEND fixup_library_dirs ${QtCore_location})
|
||||
|
||||
if (APPLE)
|
||||
set (CMAKE_POSTFLIGHT_SCRIPT
|
||||
"${wsjtx_BINARY_DIR}/postflight.sh")
|
||||
set (CMAKE_POSTUPGRADE_SCRIPT
|
||||
"${wsjtx_BINARY_DIR}/postupgrade.sh")
|
||||
configure_file ("${wsjtx_SOURCE_DIR}/postflight.sh.in"
|
||||
"${wsjtx_BINARY_DIR}/postflight.sh")
|
||||
configure_file ("${wsjtx_SOURCE_DIR}/postupgrade.sh.in"
|
||||
"${wsjtx_BINARY_DIR}/postupgrade.sh")
|
||||
endif ()
|
||||
|
||||
if (APPLE OR WIN32)
|
||||
# install rules for including 3rd party libs such as Qt
|
||||
|
||||
# install a qt.conf file
|
||||
install (CODE "
|
||||
file (WRITE \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\"
|
||||
\"[Paths]
|
||||
\")
|
||||
" #COMPONENT Runtime
|
||||
)
|
||||
|
||||
# if a system Qt is used (e.g. installed in /usr/lib/), it will not be included in the installation
|
||||
set (fixup_exe "\${CMAKE_INSTALL_PREFIX}/${WSJT_BIN_DESTINATION}/${CMAKE_PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
get_filename_component (hamlib_lib_dir ${hamlib_LIBRARIES} PATH)
|
||||
|
||||
if (APPLE)
|
||||
# install required Qt plugins
|
||||
install (
|
||||
DIRECTORY
|
||||
${QT_PLUGINS_DIR}/platforms
|
||||
${QT_PLUGINS_DIR}/audio
|
||||
DESTINATION ${WSJT_PLUGIN_DESTINATION}
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
# COMPONENT Runtime
|
||||
FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}"
|
||||
PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
PATTERN "*_debug${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
)
|
||||
# install (
|
||||
# DIRECTORY
|
||||
# ${QT_PLUGINS_DIR}/platforms
|
||||
# ${QT_PLUGINS_DIR}/audio
|
||||
# DESTINATION ${WSJT_PLUGIN_DESTINATION}
|
||||
# CONFIGURATIONS Debug
|
||||
# # COMPONENT Runtime
|
||||
# FILES_MATCHING PATTERN "*_debug${CMAKE_SHARED_LIBRARY_SUFFIX}"
|
||||
# PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
# PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
# )
|
||||
|
||||
# add plugins path for Mac Bundle
|
||||
install (CODE "
|
||||
file (APPEND \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\"
|
||||
\"Plugins = Plugins
|
||||
\")
|
||||
" #COMPONENT Runtime
|
||||
)
|
||||
endif (APPLE)
|
||||
|
||||
if (WIN32)
|
||||
# DLL directory
|
||||
set (hamlib_lib_dir ${hamlib_lib_dir}/../bin)
|
||||
|
||||
get_filename_component (fftw_lib_dir ${FFTW3F_LIBRARY} PATH)
|
||||
list (APPEND fixup_library_dirs ${fftw_lib_dir})
|
||||
|
||||
# install required Qt plugins
|
||||
install (
|
||||
DIRECTORY
|
||||
${QT_PLUGINS_DIR}/platforms
|
||||
DESTINATION ${WSJT_PLUGIN_DESTINATION}
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
# COMPONENT Runtime
|
||||
FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}"
|
||||
PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
PATTERN "*d${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
)
|
||||
# install (
|
||||
# DIRECTORY
|
||||
# ${QT_PLUGINS_DIR}/platforms
|
||||
# DESTINATION ${WSJT_PLUGIN_DESTINATION}
|
||||
# CONFIGURATIONS Debug
|
||||
# # COMPONENT Runtime
|
||||
# FILES_MATCHING PATTERN "*d${CMAKE_SHARED_LIBRARY_SUFFIX}"
|
||||
# PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
# PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE
|
||||
# )
|
||||
|
||||
# add plugins path for WIN32
|
||||
file (RELATIVE_PATH _plugins_path "${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}" "${CMAKE_INSTALL_PREFIX}/${WSJT_PLUGIN_DESTINATION}")
|
||||
install (CODE "
|
||||
file (APPEND \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\"
|
||||
\"Plugins = ${_plugins_path}
|
||||
\")
|
||||
" #COMPONENT Runtime
|
||||
)
|
||||
|
||||
# set (gp_tool "objdump") # we want MinGW tool - not MSVC (See GetPrerequisites.cmake)
|
||||
endif (WIN32)
|
||||
|
||||
list (APPEND fixup_library_dirs ${hamlib_lib_dir})
|
||||
|
||||
install (CODE "
|
||||
file (GLOB_RECURSE QTPLUGINS
|
||||
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_PLUGIN_DESTINATION}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
||||
include (BundleUtilities)
|
||||
set (BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
set (gp_tool ${gp_tool})
|
||||
message (STATUS \"fixup_exe: ${fixup_exe}\")
|
||||
fixup_bundle (\"${fixup_exe}\" \"\${QTPLUGINS}\" \"${fixup_library_dirs}\")
|
||||
"
|
||||
#COMPONENT Runtime
|
||||
)
|
||||
endif (APPLE OR WIN32)
|
||||
|
||||
endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
|
||||
|
||||
#
|
||||
# packaging
|
||||
#
|
||||
set (CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
|
||||
set (CPACK_PACKAGE_VERSION_MAJOR ${WSJTX_VERSION_MAJOR})
|
||||
set (CPACK_PACKAGE_VERSION_MINOR ${WSJTX_VERSION_MINOR})
|
||||
set (CPACK_PACKAGE_VERSION_PATCH ${WSJTX_VERSION_PATCH})
|
||||
|
||||
if (WIN32)
|
||||
set (CPACK_GENERATOR "NSIS")
|
||||
elseif (APPLE)
|
||||
set (CPACK_GENERATOR "DragNDrop" "PackageMaker")
|
||||
else ()
|
||||
#
|
||||
# Derive the correct filename for a Debian package because the DEB
|
||||
# generator doesn't do this correctly at present.
|
||||
#
|
||||
string (TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
|
||||
find_program (DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
if (DPKG_PROGRAM)
|
||||
execute_process (
|
||||
COMMAND ${DPKG_PROGRAM} --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
set (DEBIAN_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME_LOWERCASE}_${wsjtx_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
else (DPKG_PROGRAM)
|
||||
set (DEBIAN_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME_LOWERCASE}_${wsjtx_VERSION}_${CMAKE_SYSTEM_NAME}")
|
||||
endif (DPKG_PROGRAM)
|
||||
|
||||
set (CPACK_DEBIAN_PACKAGE_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
set (CPACK_GENERATOR "DEB" "RPM" "TGZ")
|
||||
endif ()
|
||||
|
||||
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake" @ONLY)
|
||||
set (CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake")
|
||||
|
||||
include (CPack)
|
||||
|
63
ConfigTest.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include "GetUserId.hpp"
|
||||
#include "TraceFile.hpp"
|
||||
#include "TestConfiguration.hpp"
|
||||
#include "AudioDevice.hpp"
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "Configuration.hpp"
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
QApplication application {argc, argv};
|
||||
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
|
||||
// consistent format, do this after
|
||||
// instantiating QApplication so
|
||||
// that GUI has correct l18n
|
||||
|
||||
// get a unique id from the user
|
||||
auto id = get_user_id ();
|
||||
|
||||
// open a user specific trace file
|
||||
TraceFile trace_file {QDir {QApplication::applicationDirPath () + "/logs"}.absoluteFilePath (id + "_config_test.log")};
|
||||
|
||||
// announce to log file
|
||||
qDebug () << "Configuration Test v" WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MAJOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MINOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_PATCH) ", " WSJTX_STRINGIZE (SVNVERSION) " - Program startup";
|
||||
|
||||
// open user specific settings
|
||||
QSettings settings {QDir {QApplication::applicationDirPath () + "/settings"}.absoluteFilePath (id + "_config_test.ini"), QSettings::IniFormat};
|
||||
|
||||
// the test GUI
|
||||
TestConfiguration main_window ("ConfigTest", &settings);
|
||||
|
||||
// hook up close down mechanism
|
||||
QObject::connect (&application, SIGNAL (lastWindowClosed ()), &application, SLOT (quit ()));
|
||||
|
||||
// start event loop
|
||||
auto status = application.exec();
|
||||
qDebug () << "Normal exit with status: " << status;
|
||||
return status;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
qDebug () << "Error exit: " << e.what () << '\n';
|
||||
std::cerr << "Error: " << e.what () << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
qDebug () << "Unknown error exit\n";
|
||||
std::cerr << "Unexpected error\n";
|
||||
throw; // hoping the runtime might tell us more about the exception
|
||||
}
|
||||
return -1;
|
||||
}
|
2128
Configuration.cpp
Normal file
188
Configuration.hpp
Normal file
@ -0,0 +1,188 @@
|
||||
#ifndef CONFIGURATION_HPP_
|
||||
#define CONFIGURATION_HPP_
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "Radio.hpp"
|
||||
#include "AudioDevice.hpp"
|
||||
#include "Transceiver.hpp"
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QSettings;
|
||||
class QWidget;
|
||||
class QAudioDeviceInfo;
|
||||
class QString;
|
||||
class QDir;
|
||||
class QFont;
|
||||
class Bands;
|
||||
class FrequencyList;
|
||||
class StationList;
|
||||
class QStringListModel;
|
||||
|
||||
//
|
||||
// Class Configuration
|
||||
//
|
||||
// Encapsulates the control, access and, persistence of user defined
|
||||
// settings for the wsjtx GUI. Setting values are accessed through a
|
||||
// QDialog window containing concept orientated tab windows.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Provides management of the CAT and PTT rig interfaces, providing
|
||||
// control access via a minimal generic set of Qt slots and status
|
||||
// updates via Qt signals. Internally the rig control capability is
|
||||
// farmed out to a separate thread since many of the rig control
|
||||
// functions are blocking.
|
||||
//
|
||||
// All user settings required by the wsjtx GUI are exposed through
|
||||
// query methods.
|
||||
//
|
||||
// The QSettings instance passed to the constructor is used to read
|
||||
// and write user settings.
|
||||
//
|
||||
// Pointers to three QAbstractItemModel objects are provided to give
|
||||
// access to amateur band information, user working frequencies and,
|
||||
// user operating band information. These porovide consistent data
|
||||
// models that can be used in GUI lists or tables or simply queried
|
||||
// for user defined bands, default operating frequencies and, station
|
||||
// descriptions.
|
||||
//
|
||||
class Configuration final
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
Q_ENUMS (DataMode);
|
||||
|
||||
public:
|
||||
using MODE = Transceiver::MODE;
|
||||
using TransceiverState = Transceiver::TransceiverState;
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
enum DataMode {data_mode_none, data_mode_USB, data_mode_data};
|
||||
|
||||
explicit Configuration (QString const& instance_key, QSettings * settings, QWidget * parent = nullptr);
|
||||
~Configuration ();
|
||||
|
||||
int exec ();
|
||||
|
||||
QDir data_path () const;
|
||||
|
||||
QAudioDeviceInfo const& audio_input_device () const;
|
||||
AudioDevice::Channel audio_input_channel () const;
|
||||
QAudioDeviceInfo const& audio_output_device () const;
|
||||
AudioDevice::Channel audio_output_channel () const;
|
||||
|
||||
// These query methods should be used after a call to exec() to
|
||||
// determine if either the audio input or audio output stream
|
||||
// parameters have changed. The respective streams should be
|
||||
// re-opened if they return true.
|
||||
bool restart_audio_input () const;
|
||||
bool restart_audio_output () const;
|
||||
|
||||
QString my_callsign () const;
|
||||
QString my_grid () const;
|
||||
QFont decoded_text_font () const;
|
||||
qint32 id_interval () const;
|
||||
bool id_after_73 () const;
|
||||
bool spot_to_psk_reporter () const;
|
||||
bool monitor_off_at_startup () const;
|
||||
bool log_as_RTTY () const;
|
||||
bool report_in_comments () const;
|
||||
bool prompt_to_log () const;
|
||||
bool insert_blank () const;
|
||||
bool DXCC () const;
|
||||
bool clear_DX () const;
|
||||
bool miles () const;
|
||||
bool quick_call () const;
|
||||
bool disable_TX_on_73 () const;
|
||||
bool watchdog () const;
|
||||
bool TX_messages () const;
|
||||
bool split_mode () const;
|
||||
Bands * bands ();
|
||||
FrequencyList * frequencies ();
|
||||
StationList * stations ();
|
||||
QStringListModel * macros ();
|
||||
QDir save_directory () const;
|
||||
QString rig_name () const;
|
||||
unsigned jt9w_bw_mult () const;
|
||||
float jt9w_min_dt () const;
|
||||
float jt9w_max_dt () const;
|
||||
|
||||
// This method queries if a CAT and PTT connection is operational,
|
||||
//
|
||||
// It also doubles as an initialisation method when the
|
||||
// open_if_closed parameter is passed as true.
|
||||
bool transceiver_online (bool open_if_closed = false);
|
||||
|
||||
// Close down connection to rig.
|
||||
void transceiver_offline ();
|
||||
|
||||
// Set transceiver frequency in Hertz.
|
||||
Q_SLOT void transceiver_frequency (Frequency);
|
||||
|
||||
// Setting a non zero TX frequency means split operation
|
||||
// rationalise_mode means ensure TX uses same mode as RX.
|
||||
Q_SLOT void transceiver_tx_frequency (Frequency = 0u);
|
||||
|
||||
// Set transceiver mode.
|
||||
//
|
||||
// Rationalise means ensure TX uses same mode as RX.
|
||||
Q_SLOT void transceiver_mode (MODE);
|
||||
|
||||
// Set/unset PTT.
|
||||
//
|
||||
// Note that this must be called even if VOX PTT is selected since
|
||||
// the "Emulate Split" mode requires PTT information to coordinate
|
||||
// frequency changes.
|
||||
Q_SLOT void transceiver_ptt (bool = true);
|
||||
|
||||
// Attempt to (re-)synchronise transceiver state.
|
||||
//
|
||||
// Force signal guarantees either a transceiver_update or a
|
||||
// transceiver_failure signal.
|
||||
//
|
||||
// The enforce_mode_and_split parameter ensures that future
|
||||
// transceiver updates have the correct mode and split setting
|
||||
// i.e. the transceiver is ready for use.
|
||||
Q_SLOT void sync_transceiver (bool force_signal = false, bool enforce_mode_and_split = false);
|
||||
|
||||
|
||||
//
|
||||
// This signal indicates that a font has been selected and accepted
|
||||
// for the decoded text.
|
||||
//
|
||||
Q_SIGNAL void decoded_text_font_changed (QFont);
|
||||
|
||||
|
||||
//
|
||||
// These signals are emitted and reflect transceiver state changes
|
||||
//
|
||||
|
||||
// signals a change in one of the TransceiverState members
|
||||
Q_SIGNAL void transceiver_update (Transceiver::TransceiverState) const;
|
||||
|
||||
// Signals a failure of a control rig CAT or PTT connection.
|
||||
//
|
||||
// A failed rig CAT or PTT connection is fatal and the underlying
|
||||
// connections are closed automatically. The connections can be
|
||||
// re-established with a call to transceiver_online(true) assuming
|
||||
// the fault condition has been rectified or is transient.
|
||||
Q_SIGNAL void transceiver_failure (QString reason) const;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE (Configuration::DataMode);
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
ENUM_QDEBUG_OPS_DECL (Configuration, DataMode);
|
||||
#endif
|
||||
|
||||
ENUM_QDATASTREAM_OPS_DECL (Configuration, DataMode);
|
||||
|
||||
ENUM_CONVERSION_OPS_DECL (Configuration, DataMode);
|
||||
|
||||
#endif
|
1812
Configuration.ui
Normal file
1
Copyright.txt
Normal file
@ -0,0 +1 @@
|
||||
Copyright (C) 2001-2014 by Joe Taylor, K1JT
|
377
DXLabSuiteCommanderTransceiver.cpp
Normal file
@ -0,0 +1,377 @@
|
||||
#include "DXLabSuiteCommanderTransceiver.hpp"
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "NetworkServerLookup.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const commander_transceiver_name {"DX Lab Suite Commander"};
|
||||
int socket_wait_time {5000};
|
||||
|
||||
QString map_mode (Transceiver::MODE mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Transceiver::AM: return "AM";
|
||||
case Transceiver::CW: return "CW";
|
||||
case Transceiver::CW_R: return "CW-R";
|
||||
case Transceiver::USB: return "USB";
|
||||
case Transceiver::LSB: return "LSB";
|
||||
case Transceiver::FSK: return "RTTY";
|
||||
case Transceiver::FSK_R: return "RTTY-R";
|
||||
case Transceiver::DIG_L: return "DATA-L";
|
||||
case Transceiver::DIG_U: return "DATA-U";
|
||||
case Transceiver::FM:
|
||||
case Transceiver::DIG_FM:
|
||||
return "FM";
|
||||
default: break;
|
||||
}
|
||||
return "USB";
|
||||
}
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
|
||||
{
|
||||
(*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true};
|
||||
}
|
||||
|
||||
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval)
|
||||
: PollingTransceiver {poll_interval}
|
||||
, wrapped_ {std::move (wrapped)}
|
||||
, use_for_ptt_ {use_for_ptt}
|
||||
, server_ {address}
|
||||
, commander_ {nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_start ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::start";
|
||||
#endif
|
||||
|
||||
wrapped_->start ();
|
||||
|
||||
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol);
|
||||
|
||||
if (!commander_)
|
||||
{
|
||||
commander_ = new QTcpSocket {this}; // QObject takes ownership
|
||||
}
|
||||
|
||||
commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
|
||||
if (!commander_->waitForConnected (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::start failed to connect" << commander_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Failed to connect to DX Lab Suite Commander\n" + commander_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
poll ();
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_stop ()
|
||||
{
|
||||
if (commander_)
|
||||
{
|
||||
commander_->close ();
|
||||
delete commander_, commander_ = nullptr;
|
||||
}
|
||||
|
||||
wrapped_->stop ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::stop";
|
||||
#endif
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::do_ptt:" << on << state ();
|
||||
#endif
|
||||
|
||||
if (use_for_ptt_)
|
||||
{
|
||||
send_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
|
||||
}
|
||||
else
|
||||
{
|
||||
wrapped_->ptt (on);
|
||||
}
|
||||
|
||||
update_PTT (on);
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::do_frequency:" << f << state ();
|
||||
#endif
|
||||
|
||||
// number is localised
|
||||
// avoid floating point translation errors by adding a small number (0.1Hz)
|
||||
send_command ("<command:10>CmdSetFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (f / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ());
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::do_tx_frequency:" << tx << state ();
|
||||
#endif
|
||||
|
||||
if (tx)
|
||||
{
|
||||
send_command ("<command:8>CmdSplit<parameters:7><1:2>on");
|
||||
update_split (true);
|
||||
|
||||
// number is localised
|
||||
// avoid floating point translation errors by adding a small number (0.1Hz)
|
||||
|
||||
// set TX frequency after going split because going split
|
||||
// rationalises TX VFO mode and that can change the frequency on
|
||||
// Yaesu rigs if CW is involved
|
||||
send_command ("<command:12>CmdSetTxFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (tx / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ());
|
||||
}
|
||||
else
|
||||
{
|
||||
send_command ("<command:8>CmdSplit<parameters:8><1:3>off");
|
||||
}
|
||||
update_other_frequency (tx);
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::do_mode (MODE mode, bool /* rationalise */)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::do_mode:" << mode << state ();
|
||||
#endif
|
||||
|
||||
auto mapped = map_mode (mode);
|
||||
send_command ((QString ("<command:10>CmdSetMode<parameters:%1><1:%2>").arg (5 + mapped.size ()).arg (mapped.size ()) + mapped).toLocal8Bit ());
|
||||
|
||||
if (state ().split ())
|
||||
{
|
||||
// this toggle ensures that the TX VFO mode is the same as the RX VFO
|
||||
send_command ("<command:8>CmdSplit<parameters:8><1:3>off");
|
||||
send_command ("<command:8>CmdSplit<parameters:7><1:2>on");
|
||||
}
|
||||
|
||||
// setting TX frequency rationalises the mode on Icoms so get current and set
|
||||
poll ();
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::poll ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
bool quiet {false};
|
||||
#else
|
||||
bool quiet {true};
|
||||
#endif
|
||||
|
||||
send_command ("<command:11>CmdSendFreq<parameters:0>", quiet);
|
||||
auto reply = read_reply (quiet);
|
||||
if (0 == reply.indexOf ("<CmdFreq:"))
|
||||
{
|
||||
// remove thousands separator and DP - relies of n.nnn kHz format so we can do uint conversion
|
||||
reply = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", "");
|
||||
update_rx_frequency (reply.toUInt ());
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get frequency unexpected response";
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander didn't respond correctly polling frequency"};
|
||||
}
|
||||
|
||||
send_command ("<command:13>CmdSendTXFreq<parameters:0>", quiet);
|
||||
reply = read_reply (quiet);
|
||||
if (0 == reply.indexOf ("<CmdTXFreq:"))
|
||||
{
|
||||
// remove thousands separator and DP - relies of n.nnn kHz format so we ca do uint conversion
|
||||
auto text = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", "");
|
||||
if ("000" != text)
|
||||
{
|
||||
update_other_frequency (text.toUInt ());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get tx frequency unexpected response";
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander didn't respond correctly polling TX frequency"};
|
||||
}
|
||||
|
||||
send_command ("<command:12>CmdSendSplit<parameters:0>", quiet);
|
||||
reply = read_reply (quiet);
|
||||
if (0 == reply.indexOf ("<CmdSplit:"))
|
||||
{
|
||||
auto split = reply.mid (reply.indexOf ('>') + 1);
|
||||
if ("ON" == split)
|
||||
{
|
||||
update_split (true);
|
||||
}
|
||||
else if ("OFF" == split)
|
||||
{
|
||||
update_split (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected split state" << split;
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander sent an unrecognised split state: " + split};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: get split mode unexpected response";
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander didn't respond correctly polling split status"};
|
||||
}
|
||||
|
||||
send_command ("<command:11>CmdSendMode<parameters:0>", quiet);
|
||||
reply = read_reply (quiet);
|
||||
if (0 == reply.indexOf ("<CmdMode:"))
|
||||
{
|
||||
auto mode = reply.mid (reply.indexOf ('>') + 1);
|
||||
MODE m {UNK};
|
||||
if ("AM" == mode)
|
||||
{
|
||||
m = AM;
|
||||
}
|
||||
else if ("CW" == mode)
|
||||
{
|
||||
m = CW;
|
||||
}
|
||||
else if ("CW-R" == mode)
|
||||
{
|
||||
m = CW_R;
|
||||
}
|
||||
else if ("FM" == mode || "WBFM" == mode)
|
||||
{
|
||||
m = FM;
|
||||
}
|
||||
else if ("LSB" == mode)
|
||||
{
|
||||
m = LSB;
|
||||
}
|
||||
else if ("USB" == mode)
|
||||
{
|
||||
m = USB;
|
||||
}
|
||||
else if ("RTTY" == mode)
|
||||
{
|
||||
m = FSK;
|
||||
}
|
||||
else if ("RTTY-R" == mode)
|
||||
{
|
||||
m = FSK_R;
|
||||
}
|
||||
else if ("PKT" == mode || "DATA-L" == mode || "Data-L" == mode)
|
||||
{
|
||||
m = DIG_L;
|
||||
}
|
||||
else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode)
|
||||
{
|
||||
m = DIG_U;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected mode name" << mode;
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander sent an unrecognised mode: " + mode};
|
||||
}
|
||||
update_mode (m);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected response";
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander didn't respond correctly polling mode"};
|
||||
}
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::send_command (QByteArray const& cmd, bool no_debug)
|
||||
{
|
||||
Q_ASSERT (commander_);
|
||||
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver:send_command(" << cmd << ')';
|
||||
#endif
|
||||
}
|
||||
|
||||
if (QTcpSocket::ConnectedState != commander_->state ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::send_command failed:" << commander_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander send command failed\n" + commander_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
commander_->write (cmd);
|
||||
if (!commander_->waitForBytesWritten (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::send_command failed:" << commander_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander send command failed\n" + commander_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray DXLabSuiteCommanderTransceiver::read_reply (bool no_debug)
|
||||
{
|
||||
Q_ASSERT (commander_);
|
||||
|
||||
if (QTcpSocket::ConnectedState != commander_->state ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::read_reply failed:" << commander_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander read reply failed\n" + commander_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
if (!commander_->waitForReadyRead (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver::read_reply failed:" << commander_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"DX Lab Suite Commander read reply failed\n" + commander_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
auto result = commander_->readAll ();
|
||||
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "DXLabSuiteCommanderTransceiver:read_reply() ->" << result;
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
50
DXLabSuiteCommanderTransceiver.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef DX_LAB_SUITE_COMMANDER_TRANSCEIVER_HPP__
|
||||
#define DX_LAB_SUITE_COMMANDER_TRANSCEIVER_HPP__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "PollingTransceiver.hpp"
|
||||
|
||||
class QTcpSocket;
|
||||
|
||||
//
|
||||
// DX Lab Suite Commander Interface
|
||||
//
|
||||
// Implemented as a Transceiver decorator because we may want the PTT
|
||||
// services of another Transceiver type such as the HamlibTransceiver
|
||||
// which can be enabled by wrapping a HamlibTransceiver instantiated
|
||||
// as a "Hamlib Dummy" transceiver in the Transceiver factory method.
|
||||
//
|
||||
class DXLabSuiteCommanderTransceiver final
|
||||
: public PollingTransceiver
|
||||
{
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
|
||||
|
||||
// takes ownership of wrapped Transceiver
|
||||
explicit DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval);
|
||||
~DXLabSuiteCommanderTransceiver ();
|
||||
|
||||
private:
|
||||
void do_start () override;
|
||||
void do_stop () override;
|
||||
void do_frequency (Frequency) override;
|
||||
void do_tx_frequency (Frequency, bool rationalise_mode) override;
|
||||
void do_mode (MODE, bool rationalise) override;
|
||||
void do_ptt (bool on) override;
|
||||
|
||||
void poll () override;
|
||||
|
||||
void send_command (QByteArray const&, bool no_debug = false);
|
||||
QByteArray read_reply (bool no_debug = false);
|
||||
|
||||
std::unique_ptr<TransceiverBase> wrapped_;
|
||||
bool use_for_ptt_;
|
||||
QString server_;
|
||||
QTcpSocket * commander_;
|
||||
};
|
||||
|
||||
#endif
|
@ -4,6 +4,8 @@
|
||||
#include <QDebug>
|
||||
#include "commons.h"
|
||||
|
||||
#include "moc_Detector.cpp"
|
||||
|
||||
extern "C" {
|
||||
void fil4_(qint16*, qint32*, qint16*, qint32*);
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ private:
|
||||
Q_SLOT bool reset ();
|
||||
Q_SLOT void close () {AudioDevice::close ();}
|
||||
|
||||
Q_SIGNAL void framesWritten (qint64);
|
||||
Q_SIGNAL void framesWritten (qint64) const;
|
||||
|
||||
void clear (); // discard buffer contents
|
||||
unsigned secondInPeriod () const;
|
||||
|
102
EmulateSplitTransceiver.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "EmulateSplitTransceiver.hpp"
|
||||
|
||||
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped)
|
||||
: wrapped_ {std::move (wrapped)}
|
||||
, frequency_ {0, 0}
|
||||
, tx_ {false}
|
||||
{
|
||||
// Connect update signal of wrapped Transceiver object instance to ours.
|
||||
connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update);
|
||||
|
||||
// Connect failure signal of wrapped Transceiver object to our
|
||||
// parent failure signal.
|
||||
connect (wrapped_.get (), &Transceiver::failure, this, &Transceiver::failure);
|
||||
}
|
||||
|
||||
void EmulateSplitTransceiver::start () noexcept
|
||||
{
|
||||
wrapped_->start ();
|
||||
wrapped_->tx_frequency (0, false);
|
||||
}
|
||||
|
||||
void EmulateSplitTransceiver::frequency (Frequency rx) noexcept
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "EmulateSplitTransceiver::frequency:" << rx;
|
||||
#endif
|
||||
|
||||
// Save frequency parameters.
|
||||
frequency_[0] = rx;
|
||||
|
||||
// Set active frequency.
|
||||
wrapped_->frequency (rx);
|
||||
}
|
||||
|
||||
void EmulateSplitTransceiver::tx_frequency (Frequency tx, bool /* rationalise_mode */) noexcept
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "EmulateSplitTransceiver::tx_frequency:" << tx;
|
||||
#endif
|
||||
|
||||
// Save frequency parameter.
|
||||
frequency_[1] = tx;
|
||||
|
||||
// Set active frequency.
|
||||
wrapped_->frequency (frequency_[(tx_ && frequency_[1]) ? 1 : 0]);
|
||||
}
|
||||
|
||||
void EmulateSplitTransceiver::ptt (bool on) noexcept
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "EmulateSplitTransceiver::ptt:" << on;
|
||||
#endif
|
||||
|
||||
// Save TX state for future frequency change requests.
|
||||
tx_ = on;
|
||||
|
||||
// Switch to other frequency if we have one i.e. client wants split
|
||||
// operation).
|
||||
wrapped_->frequency (frequency_[(on && frequency_[1]) ? 1 : 0]);
|
||||
|
||||
// Change TX state.
|
||||
wrapped_->ptt (on);
|
||||
}
|
||||
|
||||
void EmulateSplitTransceiver::handle_update (TransceiverState state)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state;
|
||||
#endif
|
||||
|
||||
// Change to reflect emulated state, we don't want to report the
|
||||
// shifted frequency when transmitting.
|
||||
if (tx_)
|
||||
{
|
||||
state.frequency (frequency_[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Follow the rig if in RX mode.
|
||||
frequency_[0] = state.frequency ();
|
||||
}
|
||||
|
||||
// Always report the other frequency as the Tx frequency we will use.
|
||||
state.tx_frequency (frequency_[1]);
|
||||
|
||||
if (state.split ())
|
||||
{
|
||||
Q_EMIT failure (tr ("Emulated split mode requires rig to in simplex mode"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Always emit rigs split state so clients can detect abuse.
|
||||
state.split (true);
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state;
|
||||
#endif
|
||||
|
||||
// signal emulated state
|
||||
Q_EMIT update (state);
|
||||
}
|
||||
}
|
52
EmulateSplitTransceiver.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef EMULATE_SPLIT_TRANSCEIVER_HPP__
|
||||
#define EMULATE_SPLIT_TRANSCEIVER_HPP__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Transceiver.hpp"
|
||||
|
||||
//
|
||||
// Emulate Split Transceiver
|
||||
//
|
||||
// Helper decorator class that encapsulates the emulation of split TX
|
||||
// operation.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Delegates all but setting of other (split) frequency to the
|
||||
// wrapped Transceiver instance. Also routes failure signals from the
|
||||
// wrapped Transceiver instance to this instances failure signal.
|
||||
//
|
||||
// Intercepts status updates from the wrapped Transceiver instance
|
||||
// and re-signals it with the emulated status.
|
||||
//
|
||||
// Generates a status update signal if the other (split) frequency is
|
||||
// changed, this is necessary since the wrapped transceiver instance
|
||||
// never receives other frequency changes.
|
||||
//
|
||||
class EmulateSplitTransceiver final
|
||||
: public Transceiver
|
||||
{
|
||||
public:
|
||||
// takes ownership of wrapped Transceiver
|
||||
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped);
|
||||
|
||||
void start () noexcept override;
|
||||
void frequency (Frequency) noexcept override;
|
||||
void tx_frequency (Frequency, bool rationalise_mode) noexcept override;
|
||||
void ptt (bool on) noexcept override;
|
||||
|
||||
// forward everything else to wrapped Transceiver
|
||||
void stop () noexcept override {wrapped_->stop (); Q_EMIT finished ();}
|
||||
void mode (MODE m, bool /* rationalise */) noexcept override {wrapped_->mode (m, false);}
|
||||
void sync (bool force_signal) noexcept override {wrapped_->sync (force_signal);}
|
||||
|
||||
private:
|
||||
void handle_update (TransceiverState);
|
||||
|
||||
std::unique_ptr<Transceiver> wrapped_;
|
||||
Frequency frequency_[2]; // [0] <- RX, [1] <- other
|
||||
bool tx_;
|
||||
};
|
||||
|
||||
#endif
|
77
ForeignKeyDelegate.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "ForeignKeyDelegate.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class CandidateKeyFilter final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
explicit CandidateKeyFilter (QAbstractItemModel const * referencing_model
|
||||
, QAbstractItemModel * referenced_model
|
||||
, int referenced_key_column
|
||||
, int referencing_key_role
|
||||
, int referenced_key_role)
|
||||
: QSortFilterProxyModel {nullptr} // ForeignKeyDelegate owns us
|
||||
, referencing_ {referencing_model}
|
||||
, referencing_key_role_ {referencing_key_role}
|
||||
, referenced_key_column_ {referenced_key_column}
|
||||
, referenced_key_role_ {referenced_key_role}
|
||||
{
|
||||
setSourceModel (referenced_model);
|
||||
}
|
||||
|
||||
void set_active_key (QModelIndex const& index)
|
||||
{
|
||||
active_key_ = index;
|
||||
invalidateFilter ();
|
||||
}
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const override
|
||||
{
|
||||
auto candidate_key = sourceModel ()->index (candidate_row, referenced_key_column_, candidate_parent).data (referenced_key_role_);
|
||||
|
||||
// Include the current key.
|
||||
if (candidate_key == active_key_.data (referencing_key_role_))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Filter out any candidates already in the referencing key rows.
|
||||
return referencing_->match (referencing_->index (0, active_key_.column ()), referencing_key_role_, candidate_key, 1, Qt::MatchExactly).isEmpty ();
|
||||
}
|
||||
|
||||
private:
|
||||
QAbstractItemModel const * referencing_;
|
||||
int referencing_key_role_;
|
||||
int referenced_key_column_;
|
||||
int referenced_key_role_;
|
||||
QModelIndex active_key_;
|
||||
};
|
||||
|
||||
ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
||||
, QAbstractItemModel * referenced_model
|
||||
, int referenced_key_column
|
||||
, QObject * parent
|
||||
, int referencing_key_role
|
||||
, int referenced_key_role)
|
||||
: QStyledItemDelegate {parent}
|
||||
, candidate_key_filter_ {new CandidateKeyFilter {referencing_model, referenced_model, referenced_key_column, referencing_key_role, referenced_key_role}}
|
||||
{
|
||||
}
|
||||
|
||||
ForeignKeyDelegate::~ForeignKeyDelegate ()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget * ForeignKeyDelegate::createEditor (QWidget * parent
|
||||
, QStyleOptionViewItem const& /* option */
|
||||
, QModelIndex const& index) const
|
||||
{
|
||||
auto editor = new QComboBox {parent};
|
||||
editor->setFrame (false);
|
||||
candidate_key_filter_->set_active_key (index);
|
||||
editor->setModel (candidate_key_filter_.data ());
|
||||
return editor;
|
||||
}
|
34
ForeignKeyDelegate.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef FOREIGN_KEY_DELEGATE_HPP_
|
||||
#define FOREIGN_KEY_DELEGATE_HPP_
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class CandidateKeyFilter;
|
||||
|
||||
//
|
||||
// Class ForeignKeyDelegate
|
||||
//
|
||||
// Item delegate for editing a foreign key item in a one or many
|
||||
// to one relationship. A QComboBox is used as an item delegate
|
||||
// for the edit role.
|
||||
//
|
||||
class ForeignKeyDelegate final
|
||||
: public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit ForeignKeyDelegate (QAbstractItemModel const * referencing_model
|
||||
, QAbstractItemModel * referenced_model
|
||||
, int referenced_key_column = 0
|
||||
, QObject * parent = nullptr
|
||||
, int referencing_key_role = Qt::EditRole
|
||||
, int referenced_key_role = Qt::EditRole);
|
||||
~ForeignKeyDelegate ();
|
||||
|
||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
|
||||
private:
|
||||
QScopedPointer<CandidateKeyFilter> candidate_key_filter_;
|
||||
};
|
||||
|
||||
#endif
|
36
FrequencyItemDelegate.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "FrequencyItemDelegate.hpp"
|
||||
|
||||
#include "Radio.hpp"
|
||||
#include "FrequencyLineEdit.hpp"
|
||||
#include "Bands.hpp"
|
||||
|
||||
QString FrequencyItemDelegate::displayText (QVariant const& value, QLocale const& locale) const
|
||||
{
|
||||
auto frequency = value.value<Radio::Frequency> ();
|
||||
auto band_name = bands_->data (bands_->find (frequency));
|
||||
return Radio::pretty_frequency_MHz_string (frequency, locale) + " MHz (" + band_name.toString () + ')';
|
||||
}
|
||||
|
||||
QWidget * FrequencyItemDelegate::createEditor (QWidget * parent
|
||||
, QStyleOptionViewItem const& /* option */
|
||||
, QModelIndex const& /* index */) const
|
||||
{
|
||||
auto editor = new FrequencyLineEdit {parent};
|
||||
editor->setFrame (false);
|
||||
return editor;
|
||||
}
|
||||
|
||||
|
||||
QString FrequencyDeltaItemDelegate::displayText (QVariant const& value, QLocale const& locale) const
|
||||
{
|
||||
return Radio::pretty_frequency_MHz_string (value.value<Radio::FrequencyDelta> (), locale) + " MHz";
|
||||
}
|
||||
|
||||
QWidget * FrequencyDeltaItemDelegate::createEditor (QWidget * parent
|
||||
, QStyleOptionViewItem const& /* option */
|
||||
, QModelIndex const& /* index */) const
|
||||
{
|
||||
auto editor = new FrequencyDeltaLineEdit {parent};
|
||||
editor->setFrame (false);
|
||||
return editor;
|
||||
}
|
56
FrequencyItemDelegate.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef FREQUENCY_ITEM_DELEGATE_HPP_
|
||||
#define FREQUENCY_ITEM_DELEGATE_HPP_
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class QStyleOptionItemView;
|
||||
class QWidget;
|
||||
class QModelIndex;
|
||||
class Bands;
|
||||
|
||||
//
|
||||
// Class FrequencyItemDelegate
|
||||
//
|
||||
// Item delegate for displaying and editing a Frequency item in a
|
||||
// view that uses a FrequencyLineEdit as an item delegate for the
|
||||
// edit role.
|
||||
//
|
||||
class FrequencyItemDelegate final
|
||||
: public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit FrequencyItemDelegate (Bands const * bands, QObject * parent = nullptr)
|
||||
: QStyledItemDelegate {parent}
|
||||
, bands_ {bands}
|
||||
{
|
||||
}
|
||||
|
||||
QString displayText (QVariant const& value, QLocale const&) const override;
|
||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
|
||||
private:
|
||||
Bands const * bands_;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Class FrequencyDeltaItemDelegate
|
||||
//
|
||||
// Item delegate for displaying and editing a FrequencyDelta item
|
||||
// in a view that uses a FrequencyDeltaLineEdit as an item
|
||||
// delegate for the edit role.
|
||||
//
|
||||
class FrequencyDeltaItemDelegate final
|
||||
: public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit FrequencyDeltaItemDelegate (QObject * parent = nullptr)
|
||||
: QStyledItemDelegate {parent}
|
||||
{
|
||||
}
|
||||
|
||||
QString displayText (QVariant const& value, QLocale const&) const override;
|
||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
};
|
||||
|
||||
#endif
|
40
FrequencyLineEdit.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "FrequencyLineEdit.hpp"
|
||||
|
||||
#include <QRegExpValidator>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
|
||||
#include "moc_FrequencyLineEdit.cpp"
|
||||
|
||||
FrequencyLineEdit::FrequencyLineEdit (QWidget * parent)
|
||||
: QLineEdit (parent)
|
||||
{
|
||||
setValidator (new QRegExpValidator {QRegExp {R"(\d{0,6}(\.\d{0,6})?)"}, this});
|
||||
}
|
||||
|
||||
auto FrequencyLineEdit::frequency () const -> Frequency
|
||||
{
|
||||
return Radio::frequency (text (), 6);
|
||||
}
|
||||
|
||||
void FrequencyLineEdit::frequency (Frequency f)
|
||||
{
|
||||
setText (Radio::frequency_MHz_string (f));
|
||||
}
|
||||
|
||||
|
||||
FrequencyDeltaLineEdit::FrequencyDeltaLineEdit (QWidget * parent)
|
||||
: QLineEdit (parent)
|
||||
{
|
||||
setValidator (new QRegExpValidator {QRegExp {R"(-?\d{0,6}(\.\d{0,6})?)"}, this});
|
||||
}
|
||||
|
||||
auto FrequencyDeltaLineEdit::frequency_delta () const -> FrequencyDelta
|
||||
{
|
||||
return Radio::frequency_delta (text (), 6);
|
||||
}
|
||||
|
||||
void FrequencyDeltaLineEdit::frequency_delta (FrequencyDelta d)
|
||||
{
|
||||
setText (Radio::frequency_MHz_string (d));
|
||||
}
|
45
FrequencyLineEdit.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef FREQUENCY_LINE_EDIT_HPP_
|
||||
#define FREQUENCY_LINE_EDIT_HPP_
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
class QWidget;
|
||||
|
||||
//
|
||||
// MHz frequency line edits with validation
|
||||
//
|
||||
class FrequencyLineEdit final
|
||||
: public QLineEdit
|
||||
{
|
||||
Q_OBJECT;
|
||||
Q_PROPERTY (Frequency frequency READ frequency WRITE frequency USER true);
|
||||
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
explicit FrequencyLineEdit (QWidget * parent = nullptr);
|
||||
|
||||
// Property frequency implementation
|
||||
Frequency frequency () const;
|
||||
void frequency (Frequency);
|
||||
};
|
||||
|
||||
class FrequencyDeltaLineEdit final
|
||||
: public QLineEdit
|
||||
{
|
||||
Q_OBJECT;
|
||||
Q_PROPERTY (FrequencyDelta frequency_delta READ frequency_delta WRITE frequency_delta USER true);
|
||||
|
||||
public:
|
||||
using FrequencyDelta = Radio::FrequencyDelta;
|
||||
|
||||
explicit FrequencyDeltaLineEdit (QWidget * parent = nullptr);
|
||||
|
||||
// Property frequency_delta implementation
|
||||
FrequencyDelta frequency_delta () const;
|
||||
void frequency_delta (FrequencyDelta);
|
||||
};
|
||||
|
||||
#endif
|
358
FrequencyList.cpp
Normal file
@ -0,0 +1,358 @@
|
||||
#include "FrequencyList.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QListIterator>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#include <QMimeData>
|
||||
#include <QDataStream>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
class FrequencyList::impl final
|
||||
: public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
impl (Frequencies frequencies, QObject * parent)
|
||||
: QAbstractTableModel {parent}
|
||||
, frequencies_ {frequencies}
|
||||
{
|
||||
}
|
||||
|
||||
Frequencies const& frequencies () const {return frequencies_;}
|
||||
void assign (Frequencies);
|
||||
QModelIndex add (Frequency);
|
||||
|
||||
protected:
|
||||
// Implement the QAbstractTableModel interface
|
||||
int rowCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
int columnCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override;
|
||||
QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override;
|
||||
bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override;
|
||||
QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override;
|
||||
bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
QStringList mimeTypes () const override;
|
||||
QMimeData * mimeData (QModelIndexList const&) const override;
|
||||
|
||||
private:
|
||||
static int constexpr num_cols {2};
|
||||
static auto constexpr mime_type ="application/wsjt.Frequencies";
|
||||
|
||||
Frequencies frequencies_;
|
||||
};
|
||||
|
||||
FrequencyList::FrequencyList (QObject * parent)
|
||||
: FrequencyList {{}, parent}
|
||||
{
|
||||
}
|
||||
|
||||
FrequencyList::FrequencyList (Frequencies frequencies, QObject * parent)
|
||||
: QSortFilterProxyModel {parent}
|
||||
, m_ {frequencies, parent}
|
||||
{
|
||||
// setDynamicSortFilter (true);
|
||||
setSourceModel (&*m_);
|
||||
setSortRole (SortRole);
|
||||
}
|
||||
|
||||
FrequencyList::~FrequencyList ()
|
||||
{
|
||||
}
|
||||
|
||||
FrequencyList& FrequencyList::operator = (Frequencies frequencies)
|
||||
{
|
||||
m_->assign (frequencies);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto FrequencyList::frequencies () const -> Frequencies
|
||||
{
|
||||
return m_->frequencies ();
|
||||
}
|
||||
|
||||
QModelIndex FrequencyList::add (Frequency f)
|
||||
{
|
||||
return mapFromSource (m_->add (f));
|
||||
}
|
||||
|
||||
bool FrequencyList::remove (Frequency f)
|
||||
{
|
||||
auto row = m_->frequencies ().indexOf (f);
|
||||
|
||||
if (0 > row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_->removeRow (row);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return lhs.row () > rhs.row ();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrequencyList::removeDisjointRows (QModelIndexList rows)
|
||||
{
|
||||
bool result {true};
|
||||
|
||||
// We must work with source model indexes because we don't want row
|
||||
// removes to invalidate model indexes we haven't yet processed. We
|
||||
// achieve that by processing them in decending row order.
|
||||
for (int r = 0; r < rows.size (); ++r)
|
||||
{
|
||||
rows[r] = mapToSource (rows[r]);
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
qSort (rows.begin (), rows.end (), row_is_higher);
|
||||
Q_FOREACH (auto index, rows)
|
||||
{
|
||||
if (result && !m_->removeRow (index.row ()))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void FrequencyList::impl::assign (Frequencies frequencies)
|
||||
{
|
||||
beginResetModel ();
|
||||
std::swap (frequencies_, frequencies);
|
||||
endResetModel ();
|
||||
}
|
||||
|
||||
QModelIndex FrequencyList::impl::add (Frequency f)
|
||||
{
|
||||
// Any Frequency that isn't in the list may be added
|
||||
if (!frequencies_.contains (f))
|
||||
{
|
||||
auto row = frequencies_.size ();
|
||||
|
||||
beginInsertRows (QModelIndex {}, row, row);
|
||||
frequencies_.append (f);
|
||||
endInsertRows ();
|
||||
|
||||
return index (row, 0);
|
||||
}
|
||||
|
||||
return QModelIndex {};
|
||||
}
|
||||
|
||||
int FrequencyList::impl::rowCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : frequencies_.size ();
|
||||
}
|
||||
|
||||
int FrequencyList::impl::columnCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : num_cols;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FrequencyList::impl::flags (QModelIndex const& index) const
|
||||
{
|
||||
auto result = QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled;
|
||||
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
|
||||
if (index.isValid ()
|
||||
&& row < frequencies_.size ()
|
||||
&& column < num_cols)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
result |= Qt::ItemIsDragEnabled;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const
|
||||
{
|
||||
QVariant item;
|
||||
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
|
||||
if (index.isValid ()
|
||||
&& row < frequencies_.size ()
|
||||
&& column < num_cols)
|
||||
{
|
||||
auto frequency = frequencies_.at (row);
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
switch (role)
|
||||
{
|
||||
case SortRole:
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = frequency;
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Frequency");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = static_cast<double> (frequency / 1.e6);
|
||||
break;
|
||||
|
||||
case SortRole: // use the underlying Frequency value
|
||||
item = frequency;
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Frequency MHz");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role)
|
||||
{
|
||||
bool changed {false};
|
||||
|
||||
auto row = model_index.row ();
|
||||
if (model_index.isValid ()
|
||||
&& Qt::EditRole == role
|
||||
&& row < frequencies_.size ()
|
||||
&& 0 == model_index.column ()
|
||||
&& value.canConvert<Frequency> ())
|
||||
{
|
||||
auto frequency = value.value<Frequency> ();
|
||||
auto original_frequency = frequencies_.at (row);
|
||||
if (frequency != original_frequency)
|
||||
{
|
||||
frequencies_.replace (row, frequency);
|
||||
Q_EMIT dataChanged (model_index, index (model_index.row (), 1), QVector<int> {} << role);
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
QVariant FrequencyList::impl::headerData (int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant header;
|
||||
|
||||
if (Qt::DisplayRole == role
|
||||
&& Qt::Horizontal == orientation
|
||||
&& section < num_cols)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case 0: header = tr ("Frequency"); break;
|
||||
case 1: header = tr ("Frequency (MHz)"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header = QAbstractTableModel::headerData (section, orientation, role);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::removeRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count && (row + count) <= rowCount (parent))
|
||||
{
|
||||
beginRemoveRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
frequencies_.removeAt (row);
|
||||
}
|
||||
endRemoveRows ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyList::impl::insertRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count)
|
||||
{
|
||||
beginInsertRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
frequencies_.insert (row, Frequency {});
|
||||
}
|
||||
endInsertRows ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList FrequencyList::impl::mimeTypes () const
|
||||
{
|
||||
QStringList types;
|
||||
types << mime_type;
|
||||
return types;
|
||||
}
|
||||
|
||||
QMimeData * FrequencyList::impl::mimeData (QModelIndexList const& items) const
|
||||
{
|
||||
QMimeData * mime_data = new QMimeData {};
|
||||
QByteArray encoded_data;
|
||||
QDataStream stream {&encoded_data, QIODevice::WriteOnly};
|
||||
|
||||
Q_FOREACH (auto const& item, items)
|
||||
{
|
||||
if (item.isValid ())
|
||||
{
|
||||
stream << QString {data (item, Qt::DisplayRole).toString ()};
|
||||
}
|
||||
}
|
||||
|
||||
mime_data->setData (mime_type, encoded_data);
|
||||
return mime_data;
|
||||
}
|
59
FrequencyList.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
#ifndef FREQUENCY_LIST_HPP__
|
||||
#define FREQUENCY_LIST_HPP__
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
//
|
||||
// Class FrequencyList
|
||||
//
|
||||
// Encapsulates a collection of frequencies. The implementation is a
|
||||
// table containing the list of Frequency type elements which is
|
||||
// editable and a second column which is an immutable double
|
||||
// representation of the corresponding Frequency item scaled to
|
||||
// mega-Hertz.
|
||||
//
|
||||
// The list is ordered.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Stores internally a list of unique frequencies. Provides methods
|
||||
// to add and delete list elements.
|
||||
//
|
||||
// Collaborations
|
||||
//
|
||||
// Implements the QSortFilterProxyModel interface for a list of spot
|
||||
// frequencies.
|
||||
//
|
||||
class FrequencyList final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
using Frequencies = Radio::Frequencies;
|
||||
|
||||
explicit FrequencyList (QObject * parent = nullptr);
|
||||
explicit FrequencyList (Frequencies, QObject * parent = nullptr);
|
||||
~FrequencyList ();
|
||||
|
||||
// Load and store contents
|
||||
FrequencyList& operator = (Frequencies);
|
||||
Frequencies frequencies () const;
|
||||
|
||||
// Model API
|
||||
QModelIndex add (Frequency);
|
||||
bool remove (Frequency);
|
||||
bool removeDisjointRows (QModelIndexList);
|
||||
|
||||
// Custom roles.
|
||||
static int constexpr SortRole = Qt::UserRole;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
75
GetUserId.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "GetUserId.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QString>
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QRegExpValidator>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFormLayout>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
//
|
||||
// Dialog to get callsign
|
||||
//
|
||||
class CallsignDialog final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY (CallsignDialog);
|
||||
|
||||
public:
|
||||
explicit CallsignDialog (QWidget * parent = nullptr)
|
||||
: QDialog {parent}
|
||||
{
|
||||
setWindowTitle (QApplication::applicationName () + " - " + tr ("Callsign"));
|
||||
|
||||
callsign_.setValidator (new QRegExpValidator {QRegExp {"[A-Za-z0-9]+"}, this});
|
||||
|
||||
auto form_layout = new QFormLayout ();
|
||||
form_layout->addRow ("&Callsign:", &callsign_);
|
||||
|
||||
auto main_layout = new QVBoxLayout (this);
|
||||
main_layout->addLayout (form_layout);
|
||||
|
||||
auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
|
||||
main_layout->addWidget (button_box);
|
||||
|
||||
connect (button_box, &QDialogButtonBox::accepted, this, &CallsignDialog::accept);
|
||||
connect (button_box, &QDialogButtonBox::rejected, this, &CallsignDialog::reject);
|
||||
}
|
||||
|
||||
QString callsign () const {return callsign_.text ();}
|
||||
|
||||
private:
|
||||
QLineEdit callsign_;
|
||||
};
|
||||
|
||||
#include "GetUserId.moc"
|
||||
|
||||
QString get_user_id ()
|
||||
{
|
||||
// get the users callsign so we can use it to persist the
|
||||
// settings and log file against a unique tag
|
||||
QString id;
|
||||
{
|
||||
CallsignDialog dialog;
|
||||
while (id.isEmpty ())
|
||||
{
|
||||
if (QDialog::Accepted == dialog.exec ())
|
||||
{
|
||||
id = dialog.callsign ().toUpper ();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error ("Callsign required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
8
GetUserId.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef GETUSERID_HPP_
|
||||
#define GETUSERID_HPP_
|
||||
|
||||
#include <QString>
|
||||
|
||||
QString get_user_id ();
|
||||
|
||||
#endif
|
766
HRDTransceiver.cpp
Normal file
@ -0,0 +1,766 @@
|
||||
#include "HRDTransceiver.hpp"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QByteArray>
|
||||
#include <QRegExp>
|
||||
#include <QTcpSocket>
|
||||
#include <QThread>
|
||||
|
||||
#include "NetworkServerLookup.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const HRD_transceiver_name = "Ham Radio Deluxe";
|
||||
int socket_wait_time {5000};
|
||||
}
|
||||
|
||||
void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
|
||||
{
|
||||
(*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, "localhost:7809", true);
|
||||
}
|
||||
|
||||
struct HRDMessage
|
||||
{
|
||||
// placement style new overload for outgoing messages that does the construction too
|
||||
static void * operator new (size_t size, QString const& payload)
|
||||
{
|
||||
size += sizeof (QChar) * (payload.size () + 1); // space for terminator too
|
||||
HRDMessage * storage (reinterpret_cast<HRDMessage *> (new char[size]));
|
||||
storage->size_ = size ;
|
||||
ushort const * pl (payload.utf16 ());
|
||||
qCopy (pl, pl + payload.size () + 1, storage->payload_); // copy terminator too
|
||||
storage->magic_1_ = magic_1_value_;
|
||||
storage->magic_2_ = magic_2_value_;
|
||||
storage->checksum_ = 0;
|
||||
return storage;
|
||||
}
|
||||
|
||||
// placement style new overload for incoming messages that does the construction too
|
||||
//
|
||||
// no memory allocation here
|
||||
static void * operator new (size_t /* size */, QByteArray const& message)
|
||||
{
|
||||
// nasty const_cast here to avoid copying the message buffer
|
||||
return const_cast<HRDMessage *> (reinterpret_cast<HRDMessage const *> (message.data ()));
|
||||
}
|
||||
|
||||
void operator delete (void * p, size_t)
|
||||
{
|
||||
delete [] reinterpret_cast<char *> (p); // mirror allocation in operator new above
|
||||
}
|
||||
|
||||
qint32 size_;
|
||||
qint32 magic_1_;
|
||||
qint32 magic_2_;
|
||||
qint32 checksum_; // apparently not used
|
||||
QChar payload_[0]; // UTF-16 (which is wchar_t on Windows)
|
||||
|
||||
static qint32 const magic_1_value_;
|
||||
static qint32 const magic_2_value_;
|
||||
};
|
||||
|
||||
qint32 const HRDMessage::magic_1_value_ (0x1234ABCD);
|
||||
qint32 const HRDMessage::magic_2_value_ (0xABCD1234);
|
||||
|
||||
HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& server, bool use_for_ptt, int poll_interval)
|
||||
: PollingTransceiver {poll_interval}
|
||||
, wrapped_ {std::move (wrapped)}
|
||||
, use_for_ptt_ {use_for_ptt}
|
||||
, server_ {server}
|
||||
, hrd_ {0}
|
||||
, protocol_ {none}
|
||||
, current_radio_ {0}
|
||||
, vfo_count_ {0}
|
||||
, vfo_A_button_ {-1}
|
||||
, vfo_B_button_ {-1}
|
||||
, vfo_toggle_button_ {-1}
|
||||
, mode_A_dropdown_ {-1}
|
||||
, mode_B_dropdown_ {-1}
|
||||
, split_mode_button_ {-1}
|
||||
, split_mode_dropdown_ {-1}
|
||||
, split_mode_dropdown_write_only_ {false}
|
||||
, split_mode_dropdown_selection_on_ {-1}
|
||||
, split_mode_dropdown_selection_off_ {-1}
|
||||
, split_off_button_ {-1}
|
||||
, tx_A_button_ {-1}
|
||||
, tx_B_button_ {-1}
|
||||
, ptt_button_ {-1}
|
||||
, reversed_ {false}
|
||||
{
|
||||
}
|
||||
|
||||
HRDTransceiver::~HRDTransceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_start ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::start";
|
||||
#endif
|
||||
|
||||
wrapped_->start ();
|
||||
|
||||
auto server_details = network_server_lookup (server_, 7809u);
|
||||
|
||||
if (!hrd_)
|
||||
{
|
||||
hrd_ = new QTcpSocket {this}; // QObject takes ownership
|
||||
}
|
||||
|
||||
hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
|
||||
if (!hrd_->waitForConnected (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::start failed to connect:" << hrd_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Failed to connect to Ham Radio Deluxe\n" + hrd_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
init_radio ();
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_stop ()
|
||||
{
|
||||
if (hrd_)
|
||||
{
|
||||
hrd_->close ();
|
||||
}
|
||||
|
||||
if (wrapped_)
|
||||
{
|
||||
wrapped_->stop ();
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::stop: state:" << state () << "reversed =" << reversed_;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HRDTransceiver::init_radio ()
|
||||
{
|
||||
Q_ASSERT (hrd_);
|
||||
|
||||
if (none == protocol_)
|
||||
{
|
||||
try
|
||||
{
|
||||
protocol_ = v5; // try this first (works for v6 too)
|
||||
send_command ("get context", false, false);
|
||||
}
|
||||
catch (error const&)
|
||||
{
|
||||
protocol_ = none;
|
||||
}
|
||||
}
|
||||
|
||||
if (none == protocol_)
|
||||
{
|
||||
hrd_->close ();
|
||||
|
||||
protocol_ = v4; // try again with older protocol
|
||||
hrd_->connectToHost (QHostAddress::LocalHost, 7809);
|
||||
if (!hrd_->waitForConnected (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::init_radio failed to connect:" << hrd_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Failed to connect to Ham Radio Deluxe\n" + hrd_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
send_command ("get context", false, false);
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << send_command ("get id", false, false);
|
||||
qDebug () << send_command ("get version", false, false);
|
||||
#endif
|
||||
|
||||
auto radios = send_command ("get radios", false, false).trimmed ().split (',', QString::SkipEmptyParts);
|
||||
if (radios.isEmpty ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::init_radio no rig found";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe: no rig found"};
|
||||
}
|
||||
|
||||
Q_FOREACH (auto const& radio, radios)
|
||||
{
|
||||
auto entries = radio.trimmed ().split (':', QString::SkipEmptyParts);
|
||||
radios_.push_back (std::forward_as_tuple (entries[0].toUInt (), entries[1]));
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "radios:";
|
||||
Q_FOREACH (auto const& radio, radios_)
|
||||
{
|
||||
qDebug () << "\t[" << std::get<0> (radio) << "] " << std::get<1> (radio);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (send_command ("get radio", false, false, true).isEmpty ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::init_radio no rig found";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe: no rig found"};
|
||||
}
|
||||
|
||||
vfo_count_ = send_command ("get vfo-count").toUInt ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "vfo count:" << vfo_count_;
|
||||
#endif
|
||||
|
||||
buttons_ = send_command ("get buttons").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~");
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRD Buttons: " << buttons_;
|
||||
#endif
|
||||
|
||||
dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', QString::SkipEmptyParts);
|
||||
Q_FOREACH (auto d, dropdown_names_)
|
||||
{
|
||||
dropdowns_[d] = send_command ("get dropdown-list {" + d + "}").trimmed ().split (',', QString::SkipEmptyParts);
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRD Dropdowns: " << dropdowns_;
|
||||
#endif
|
||||
|
||||
vfo_A_button_ = find_button (QRegExp ("^(VFO~A|Main)$"));
|
||||
vfo_B_button_ = find_button (QRegExp ("^(VFO~B|Sub)$"));
|
||||
vfo_toggle_button_ = find_button (QRegExp ("^(A~/~B)$"));
|
||||
Q_ASSERT (vfo_toggle_button_ >= 0 || (vfo_A_button_ >= 0 && vfo_B_button_ >=0));
|
||||
|
||||
split_mode_button_ = find_button (QRegExp ("^(Spl~On|Spl_On|Split)$"));
|
||||
split_off_button_ = find_button (QRegExp ("^(Spl~Off|Spl_Off)$"));
|
||||
|
||||
if ((split_mode_dropdown_ = find_dropdown (QRegExp ("^(Split)$"))) >= 0)
|
||||
{
|
||||
split_mode_dropdown_selection_on_ = find_dropdown_selection (split_mode_dropdown_, QRegExp ("^(On)$"));
|
||||
split_mode_dropdown_selection_off_ = find_dropdown_selection (split_mode_dropdown_, QRegExp ("^(Off)$"));
|
||||
}
|
||||
|
||||
tx_A_button_ = find_button (QRegExp ("^(TX~main|TX~-~A)$"));
|
||||
tx_B_button_ = find_button (QRegExp ("^(TX~sub|TX~-~B)$"));
|
||||
|
||||
Q_ASSERT (split_mode_button_ >= 0 || split_mode_dropdown_ >= 0 || (tx_A_button_ >= 0 && tx_B_button_ >= 0));
|
||||
|
||||
mode_A_dropdown_ = find_dropdown (QRegExp ("^(Main Mode|Mode)$"));
|
||||
map_modes (mode_A_dropdown_, &mode_A_map_);
|
||||
|
||||
if ((mode_B_dropdown_ = find_dropdown (QRegExp ("^(Sub Mode)$"))) >= 0)
|
||||
{
|
||||
map_modes (mode_B_dropdown_, &mode_B_map_);
|
||||
}
|
||||
|
||||
ptt_button_ = find_button (QRegExp ("^(TX)$"));
|
||||
|
||||
sync_impl ();
|
||||
}
|
||||
|
||||
void HRDTransceiver::sync_impl ()
|
||||
{
|
||||
if (vfo_count_ == 1)
|
||||
{
|
||||
// put the rig into a known state
|
||||
auto f = send_command ("get frequency").toUInt ();
|
||||
auto m = lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_);
|
||||
set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_);
|
||||
auto fo = send_command ("get frequency").toUInt ();
|
||||
update_other_frequency (fo);
|
||||
auto mo = lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_);
|
||||
set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_);
|
||||
if (f != fo || m != mo)
|
||||
{
|
||||
// we must have started with A/MAIN
|
||||
update_rx_frequency (f);
|
||||
update_mode (m);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (send_command ("get frequency").toUInt ());
|
||||
update_mode (lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_));
|
||||
}
|
||||
}
|
||||
|
||||
poll ();
|
||||
}
|
||||
|
||||
int HRDTransceiver::find_button (QRegExp const& re) const
|
||||
{
|
||||
return buttons_.indexOf (re);
|
||||
}
|
||||
|
||||
int HRDTransceiver::find_dropdown (QRegExp const& re) const
|
||||
{
|
||||
return dropdown_names_.indexOf (re);
|
||||
}
|
||||
|
||||
int HRDTransceiver::find_dropdown_selection (int dropdown, QRegExp const& re) const
|
||||
{
|
||||
return dropdowns_.value (dropdown_names_.value (dropdown)).lastIndexOf (re); // backwards because more specialised modes tend to be later in list
|
||||
}
|
||||
|
||||
int HRDTransceiver::lookup_dropdown_selection (int dropdown, QString const& selection) const
|
||||
{
|
||||
int index {dropdowns_.value (dropdown_names_.value (dropdown)).indexOf (selection)};
|
||||
Q_ASSERT (-1 != index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void HRDTransceiver::map_modes (int dropdown, ModeMap *map)
|
||||
{
|
||||
// order matters here (both in the map and in the regexps)
|
||||
map->push_back (std::forward_as_tuple (CW, find_dropdown_selection (dropdown, QRegExp ("^(CW|CW\\(N\\))$"))));
|
||||
map->push_back (std::forward_as_tuple (CW_R, find_dropdown_selection (dropdown, QRegExp ("^(CW-R|CW)$"))));
|
||||
map->push_back (std::forward_as_tuple (LSB, find_dropdown_selection (dropdown, QRegExp ("^(LSB)$"))));
|
||||
map->push_back (std::forward_as_tuple (USB, find_dropdown_selection (dropdown, QRegExp ("^(USB)$"))));
|
||||
map->push_back (std::forward_as_tuple (DIG_U, find_dropdown_selection (dropdown, QRegExp ("^(DIG|PKT-U|USB)$"))));
|
||||
map->push_back (std::forward_as_tuple (DIG_L, find_dropdown_selection (dropdown, QRegExp ("^(DIG|PKT-L|LSB)$"))));
|
||||
map->push_back (std::forward_as_tuple (FSK, find_dropdown_selection (dropdown, QRegExp ("^(DIG|FSK|RTTY)$"))));
|
||||
map->push_back (std::forward_as_tuple (FSK_R, find_dropdown_selection (dropdown, QRegExp ("^(DIG|FSK-R|RTTY-R|RTTY)$"))));
|
||||
map->push_back (std::forward_as_tuple (AM, find_dropdown_selection (dropdown, QRegExp ("^(AM)$"))));
|
||||
map->push_back (std::forward_as_tuple (FM, find_dropdown_selection (dropdown, QRegExp ("^(FM|FM\\(N\\)|WFM)$"))));
|
||||
map->push_back (std::forward_as_tuple (DIG_FM, find_dropdown_selection (dropdown, QRegExp ("^(PKT-FM|PKT|FM)$"))));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::map_modes: for dropdown" << dropdown_names_[dropdown];
|
||||
|
||||
std::for_each (map->begin (), map->end (), [this, dropdown] (ModeMap::value_type const& item)
|
||||
{
|
||||
auto rhs = std::get<1> (item);
|
||||
qDebug () << '\t' << std::get<0> (item) << "<->" << (rhs >= 0 ? dropdowns_[dropdown_names_[dropdown]][rhs] : "None");
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
int HRDTransceiver::lookup_mode (MODE mode, ModeMap const& map) const
|
||||
{
|
||||
auto it = std::find_if (map.begin (), map.end (), [mode] (ModeMap::value_type const& item) {return std::get<0> (item) == mode;});
|
||||
if (map.end () == it)
|
||||
{
|
||||
throw error {"Ham Radio Deluxe: rig doesn't support mode"};
|
||||
}
|
||||
return std::get<1> (*it);
|
||||
}
|
||||
|
||||
auto HRDTransceiver::lookup_mode (int mode, ModeMap const& map) const -> MODE
|
||||
{
|
||||
auto it = std::find_if (map.begin (), map.end (), [mode] (ModeMap::value_type const& item) {return std::get<1> (item) == mode;});
|
||||
if (map.end () == it)
|
||||
{
|
||||
throw error {"Ham Radio Deluxe: sent an unrecognised mode"};
|
||||
}
|
||||
return std::get<0> (*it);
|
||||
}
|
||||
|
||||
int HRDTransceiver::get_dropdown (int dd, bool no_debug)
|
||||
{
|
||||
auto dd_name = dropdown_names_.value (dd);
|
||||
auto reply = send_command ("get dropdown-text {" + dd_name + "}", no_debug);
|
||||
auto colon_index = reply.indexOf (':');
|
||||
|
||||
if (colon_index < 0)
|
||||
{
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::get_dropdown bad response";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe didn't respond as expected"};
|
||||
}
|
||||
|
||||
Q_ASSERT (reply.left (colon_index).trimmed () == dd_name);
|
||||
return lookup_dropdown_selection (dd, reply.mid (colon_index + 1).trimmed ());
|
||||
}
|
||||
|
||||
void HRDTransceiver::set_dropdown (int dd, int value)
|
||||
{
|
||||
auto dd_name = dropdown_names_.value (dd);
|
||||
if (value >= 0)
|
||||
{
|
||||
send_simple_command ("set dropdown " + dd_name.replace (' ', '~') + ' ' + dropdowns_.value (dd_name).value (value) + ' ' + QString::number (value));
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::set_dropdown item" << value << "not found in" << dd_name;
|
||||
#endif
|
||||
|
||||
throw error {("Ham Radio Deluxe: item not found in " + dd_name + " dropdown list").toLocal8Bit ()};
|
||||
}
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_ptt (bool on)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::do_ptt:" << on;
|
||||
#endif
|
||||
|
||||
if (use_for_ptt_)
|
||||
{
|
||||
set_button (ptt_button_, on);
|
||||
}
|
||||
else
|
||||
{
|
||||
wrapped_->ptt (on);
|
||||
}
|
||||
update_PTT (on);
|
||||
}
|
||||
|
||||
void HRDTransceiver::set_button (int button_index, bool checked)
|
||||
{
|
||||
if (button_index >= 0)
|
||||
{
|
||||
send_simple_command ("set button-select " + buttons_.value (button_index) + (checked ? " 1" : " 0"));
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::set_button invalid button";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe: button not available"};
|
||||
}
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_frequency (Frequency f)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::do_frequency:" << f << "reversed:" << reversed_;
|
||||
#endif
|
||||
|
||||
send_simple_command ("set frequency-hz " + QString::number (f));
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::do_tx_frequency:" << tx << "reversed:" << reversed_;
|
||||
#endif
|
||||
|
||||
bool split {tx != 0};
|
||||
|
||||
if (vfo_count_ > 1)
|
||||
{
|
||||
reversed_ = is_button_checked (vfo_B_button_);
|
||||
}
|
||||
|
||||
if (split)
|
||||
{
|
||||
auto fo_string = QString::number (tx);
|
||||
if (reversed_)
|
||||
{
|
||||
Q_ASSERT (vfo_count_ > 1);
|
||||
|
||||
auto frequencies = send_command ("get frequencies").trimmed ().split ('-', QString::SkipEmptyParts);
|
||||
send_simple_command ("set frequencies-hz " + fo_string + ' ' + QString::number (frequencies[1].toUInt ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vfo_count_ > 1)
|
||||
{
|
||||
auto frequencies = send_command ("get frequencies").trimmed ().split ('-', QString::SkipEmptyParts);
|
||||
send_simple_command ("set frequencies-hz " + QString::number (frequencies[0].toUInt ()) + ' ' + fo_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we rationalise the modes and VFOs here as well as the frequencies
|
||||
set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_);
|
||||
send_simple_command ("set frequency-hz " + fo_string);
|
||||
if (rationalise_mode)
|
||||
{
|
||||
set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (state ().mode (), mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_));
|
||||
}
|
||||
set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_);
|
||||
}
|
||||
}
|
||||
if (rationalise_mode)
|
||||
{
|
||||
set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (state ().mode (), mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_));
|
||||
}
|
||||
}
|
||||
update_other_frequency (tx);
|
||||
|
||||
if (split_mode_button_ >= 0)
|
||||
{
|
||||
if (split_off_button_ >= 0 && !split)
|
||||
{
|
||||
set_button (split_off_button_);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_button (split_mode_button_, split);
|
||||
}
|
||||
}
|
||||
else if (split_mode_dropdown_ >= 0)
|
||||
{
|
||||
set_dropdown (split_mode_dropdown_, split ? split_mode_dropdown_selection_on_ : split_mode_dropdown_selection_off_);
|
||||
}
|
||||
else if (vfo_A_button_ >= 0 && vfo_B_button_ >= 0 && tx_A_button_ >= 0 && tx_B_button_ >= 0)
|
||||
{
|
||||
if (split)
|
||||
{
|
||||
if (reversed_ != is_button_checked (tx_A_button_))
|
||||
{
|
||||
set_button (reversed_ ? tx_A_button_ : tx_B_button_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reversed_ != is_button_checked (tx_B_button_))
|
||||
{
|
||||
set_button (reversed_ ? tx_B_button_ : tx_A_button_);
|
||||
}
|
||||
}
|
||||
}
|
||||
update_split (split);
|
||||
}
|
||||
|
||||
void HRDTransceiver::do_mode (MODE mode, bool rationalise)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::do_mode:" << mode;
|
||||
#endif
|
||||
|
||||
set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_));
|
||||
|
||||
if (rationalise && state ().split ()) // rationalise mode if split
|
||||
{
|
||||
if (mode_B_dropdown_ >= 0)
|
||||
{
|
||||
set_dropdown (mode_B_dropdown_, lookup_mode (mode, mode_B_map_));
|
||||
}
|
||||
else if (vfo_count_ < 2)
|
||||
{
|
||||
set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_);
|
||||
set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (mode, mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_));
|
||||
set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_);
|
||||
}
|
||||
}
|
||||
|
||||
update_mode (mode);
|
||||
}
|
||||
|
||||
bool HRDTransceiver::is_button_checked (int button_index, bool no_debug)
|
||||
{
|
||||
auto reply = send_command ("get button-select " + buttons_.value (button_index), no_debug);
|
||||
if ("1" != reply && "0" != reply)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::is_button_checked bad response";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe didn't respond as expected"};
|
||||
}
|
||||
return "1" == reply;
|
||||
}
|
||||
|
||||
void HRDTransceiver::poll ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
bool quiet {false};
|
||||
#else
|
||||
bool quiet {true};
|
||||
#endif
|
||||
|
||||
if (split_off_button_ >= 0)
|
||||
{
|
||||
// we are probably dealing with an Icom and have to guess SPLIT mode :(
|
||||
}
|
||||
else if (split_mode_button_ >= 0)
|
||||
{
|
||||
update_split (is_button_checked (split_mode_button_, quiet));
|
||||
}
|
||||
else if (split_mode_dropdown_ >= 0)
|
||||
{
|
||||
if (!split_mode_dropdown_write_only_)
|
||||
{
|
||||
try
|
||||
{
|
||||
update_split (get_dropdown (split_mode_dropdown_, quiet) == split_mode_dropdown_selection_on_);
|
||||
}
|
||||
catch (error const&)
|
||||
{
|
||||
// leave split alone as we can't query it - it should be
|
||||
// correct so long as rig or HRD haven't been changed
|
||||
split_mode_dropdown_write_only_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (vfo_A_button_ >= 0 && vfo_B_button_ >= 0 && tx_A_button_ >= 0 && tx_B_button_ >= 0)
|
||||
{
|
||||
auto vfo_A = is_button_checked (vfo_A_button_, quiet);
|
||||
auto tx_A = is_button_checked (tx_A_button_, quiet);
|
||||
|
||||
update_split (vfo_A != tx_A);
|
||||
reversed_ = !vfo_A;
|
||||
}
|
||||
|
||||
if (vfo_count_ > 1)
|
||||
{
|
||||
auto frequencies = send_command ("get frequencies", quiet).trimmed ().split ('-', QString::SkipEmptyParts);
|
||||
update_rx_frequency (frequencies[reversed_ ? 1 : 0].toUInt ());
|
||||
update_other_frequency (frequencies[reversed_ ? 0 : 1].toUInt ());
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (send_command ("get frequency", quiet).toUInt ());
|
||||
}
|
||||
|
||||
update_mode (lookup_mode (get_dropdown (mode_A_dropdown_, quiet), mode_A_map_));
|
||||
}
|
||||
|
||||
QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool prepend_context, bool recurse)
|
||||
{
|
||||
Q_ASSERT (hrd_);
|
||||
|
||||
QString result;
|
||||
|
||||
if (current_radio_ && prepend_context && vfo_count_ < 2)
|
||||
{
|
||||
// TODO G4WJS: get rid of this or move to a thread required on some
|
||||
// radios because commands don't get executed correctly (ICOM for
|
||||
// example)
|
||||
QThread::msleep (50);
|
||||
}
|
||||
|
||||
if (QTcpSocket::ConnectedState != hrd_->state ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command connection failed:" << hrd_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe connection failed\n" + hrd_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
if (!recurse && prepend_context)
|
||||
{
|
||||
auto radio_name = send_command ("get radio", no_debug, current_radio_, true);
|
||||
auto radio_iter = std::find_if (radios_.begin (), radios_.end (), [this, &radio_name] (RadioMap::value_type const& radio)
|
||||
{
|
||||
return std::get<1> (radio) == radio_name;
|
||||
});
|
||||
if (radio_iter == radios_.end ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command rig disappeared or changed";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe: rig has disappeared or changed"};
|
||||
}
|
||||
|
||||
if (0u == current_radio_ || std::get<0> (*radio_iter) != current_radio_)
|
||||
{
|
||||
current_radio_ = std::get<0> (*radio_iter);
|
||||
}
|
||||
}
|
||||
|
||||
auto context = '[' + QString::number (current_radio_) + "] ";
|
||||
|
||||
int bytes_to_send;
|
||||
int bytes_sent;
|
||||
if (v4 == protocol_)
|
||||
{
|
||||
auto message = ((prepend_context ? context + cmd : cmd) + "\r").toLocal8Bit ();
|
||||
bytes_to_send = message.size ();
|
||||
bytes_sent = hrd_->write (message.data (), bytes_to_send);
|
||||
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command:" << message;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto string = prepend_context ? context + cmd : cmd;
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command:" << string;
|
||||
#endif
|
||||
}
|
||||
|
||||
QScopedPointer<HRDMessage> message (new (string) HRDMessage);
|
||||
bytes_to_send = message->size_;
|
||||
bytes_sent = hrd_->write (reinterpret_cast<char *> (message.data ()), bytes_to_send);
|
||||
}
|
||||
|
||||
if (bytes_sent < bytes_to_send
|
||||
|| !hrd_->waitForBytesWritten (socket_wait_time)
|
||||
|| QTcpSocket::ConnectedState != hrd_->state ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command failed" << hrd_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe send command failed\n" + hrd_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
|
||||
if (!hrd_->waitForReadyRead (socket_wait_time))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command failed to reply" << hrd_->errorString ();
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe failed to reply to command\n" + hrd_->errorString ().toLocal8Bit ()};
|
||||
}
|
||||
QByteArray buffer (hrd_->readAll ());
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command: reply byte count:" << buffer.size ();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (v4 == protocol_)
|
||||
{
|
||||
result = QString {buffer}.trimmed ();
|
||||
}
|
||||
else
|
||||
{
|
||||
HRDMessage const * reply (new (buffer) HRDMessage);
|
||||
|
||||
if (reply->magic_1_value_ != reply->magic_1_ && reply->magic_2_value_ != reply->magic_2_)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command invalid reply";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe sent an invalid reply to our command"};
|
||||
}
|
||||
|
||||
result = QString {reply->payload_}; // this is not a memory leak (honest!)
|
||||
}
|
||||
|
||||
if (!no_debug)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_command: reply: " << result;
|
||||
#endif
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void HRDTransceiver::send_simple_command (QString const& command, bool no_debug)
|
||||
{
|
||||
if ("OK" != send_command (command, no_debug))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HRDTransceiver::send_simple_command unexpected response";
|
||||
#endif
|
||||
|
||||
throw error {"Ham Radio Deluxe didn't respond as expected"};
|
||||
}
|
||||
}
|
95
HRDTransceiver.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
#ifndef HRD_TRANSCEIVER_HPP__
|
||||
#define HRD_TRANSCEIVER_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "PollingTransceiver.hpp"
|
||||
|
||||
class QRegExp;
|
||||
class QTcpSocket;
|
||||
|
||||
//
|
||||
// Ham Radio Deluxe Transceiver Interface
|
||||
//
|
||||
// Implemented as a Transceiver decorator because we may want the PTT
|
||||
// services of another Transceiver type such as the HamlibTransceiver
|
||||
// which can be enabled by wrapping a HamlibTransceiver instantiated
|
||||
// as a "Hamlib Dummy" transceiver in the Transceiver factory method.
|
||||
//
|
||||
class HRDTransceiver final
|
||||
: public PollingTransceiver
|
||||
{
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
|
||||
|
||||
// takes ownership of wrapped Transceiver
|
||||
explicit HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& server, bool use_for_ptt, int poll_interval);
|
||||
~HRDTransceiver ();
|
||||
|
||||
private:
|
||||
void do_start () override;
|
||||
void do_stop () override;
|
||||
void do_frequency (Frequency) override;
|
||||
void do_tx_frequency (Frequency, bool rationalise_mode) override;
|
||||
void do_mode (MODE, bool rationalise) override;
|
||||
void do_ptt (bool on) override;
|
||||
|
||||
void poll () override;
|
||||
|
||||
void init_radio ();
|
||||
QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false);
|
||||
void send_simple_command (QString const&, bool no_debug = false);
|
||||
void sync_impl ();
|
||||
int find_button (QRegExp const&) const;
|
||||
int find_dropdown (QRegExp const&) const;
|
||||
int find_dropdown_selection (int dropdown, QRegExp const&) const;
|
||||
int lookup_dropdown_selection (int dropdown, QString const&) const;
|
||||
int get_dropdown (int, bool no_debug = false);
|
||||
void set_dropdown (int, int);
|
||||
void set_button (int button_index, bool checked = true);
|
||||
bool is_button_checked (int button_index, bool no_debug = false);
|
||||
|
||||
using ModeMap = std::vector<std::tuple<MODE, int> >;
|
||||
void map_modes (int dropdown, ModeMap *);
|
||||
int lookup_mode (MODE, ModeMap const&) const;
|
||||
MODE lookup_mode (int, ModeMap const&) const;
|
||||
|
||||
std::unique_ptr<TransceiverBase> wrapped_;
|
||||
bool use_for_ptt_;
|
||||
QString server_;
|
||||
QTcpSocket * hrd_;
|
||||
enum {none, v4, v5} protocol_;
|
||||
using RadioMap = std::vector<std::tuple<unsigned, QString> >;
|
||||
RadioMap radios_;
|
||||
unsigned current_radio_;
|
||||
unsigned vfo_count_;
|
||||
QStringList buttons_;
|
||||
QStringList dropdown_names_;
|
||||
QMap<QString, QStringList> dropdowns_;
|
||||
int vfo_A_button_;
|
||||
int vfo_B_button_;
|
||||
int vfo_toggle_button_;
|
||||
int mode_A_dropdown_;
|
||||
ModeMap mode_A_map_;
|
||||
int mode_B_dropdown_;
|
||||
ModeMap mode_B_map_;
|
||||
int split_mode_button_;
|
||||
int split_mode_dropdown_;
|
||||
bool split_mode_dropdown_write_only_;
|
||||
int split_mode_dropdown_selection_on_;
|
||||
int split_mode_dropdown_selection_off_;
|
||||
int split_off_button_;
|
||||
int tx_A_button_;
|
||||
int tx_B_button_;
|
||||
int ptt_button_;
|
||||
bool reversed_;
|
||||
};
|
||||
|
||||
#endif
|
845
HamlibTransceiver.cpp
Normal file
@ -0,0 +1,845 @@
|
||||
#include "HamlibTransceiver.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
namespace
|
||||
{
|
||||
// Unfortunately bandwidth is conflated with mode, this is probably
|
||||
// because Icom do the same. So we have to care about bandwidth if
|
||||
// we want to set mode otherwise we will end up setting unwanted
|
||||
// bandwidths every time we change mode. The best we can do via the
|
||||
// Hamlib API is to request the normal option for the mode and hope
|
||||
// that an appropriate filter is selected. Also ensure that mode is
|
||||
// only set is absolutely necessary. On Icoms (and probably others)
|
||||
// the filter is selected by number without checking the actual BW
|
||||
// so unless the "normal" defaults are set on the rig we won't get
|
||||
// desirable results.
|
||||
//
|
||||
// As an ultimate workaround make sure the user always has the
|
||||
// option to skip mode setting altogether.
|
||||
|
||||
// reroute Hamlib diagnostic messages to Qt
|
||||
int debug_callback (enum rig_debug_level_e level, rig_ptr_t /* arg */, char const * format, va_list ap)
|
||||
{
|
||||
QString message;
|
||||
message = message.vsprintf (format, ap).trimmed ();
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case RIG_DEBUG_BUG:
|
||||
qFatal ("%s", message.toLocal8Bit ().data ());
|
||||
break;
|
||||
|
||||
case RIG_DEBUG_ERR:
|
||||
qCritical ("%s", message.toLocal8Bit ().data ());
|
||||
break;
|
||||
|
||||
case RIG_DEBUG_WARN:
|
||||
qWarning ("%s", message.toLocal8Bit ().data ());
|
||||
break;
|
||||
|
||||
default:
|
||||
qDebug ("%s", message.toLocal8Bit ().data ());
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// callback function that receives transceiver capabilities from the
|
||||
// hamlib libraries
|
||||
int rigCallback (rig_caps const * caps, void * callback_data)
|
||||
{
|
||||
TransceiverFactory::Transceivers * rigs = reinterpret_cast<TransceiverFactory::Transceivers *> (callback_data);
|
||||
|
||||
QString key;
|
||||
if ("Hamlib" == QString::fromLatin1 (caps->mfg_name).trimmed ()
|
||||
&& "Dummy" == QString::fromLatin1 (caps->model_name).trimmed ())
|
||||
{
|
||||
key = TransceiverFactory::basic_transceiver_name_;
|
||||
}
|
||||
else
|
||||
{
|
||||
key = QString::fromLatin1 (caps->mfg_name).trimmed ()
|
||||
+ ' '+ QString::fromLatin1 (caps->model_name).trimmed ()
|
||||
// + ' '+ QString::fromLatin1 (caps->version).trimmed ()
|
||||
// + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')'
|
||||
;
|
||||
}
|
||||
|
||||
auto port_type = TransceiverFactory::Capabilities::none;
|
||||
switch (caps->port_type)
|
||||
{
|
||||
case RIG_PORT_SERIAL:
|
||||
port_type = TransceiverFactory::Capabilities::serial;
|
||||
break;
|
||||
|
||||
case RIG_PORT_NETWORK:
|
||||
port_type = TransceiverFactory::Capabilities::network;
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
(*rigs)[key] = TransceiverFactory::Capabilities (caps->rig_model
|
||||
, port_type
|
||||
, RIG_PTT_RIG == caps->ptt_type || RIG_PTT_RIG_MICDATA == caps->ptt_type
|
||||
, RIG_PTT_RIG_MICDATA == caps->ptt_type);
|
||||
|
||||
return 1; // keep them coming
|
||||
}
|
||||
|
||||
// int frequency_change_callback (RIG * /* rig */, vfo_t vfo, freq_t f, rig_ptr_t arg)
|
||||
// {
|
||||
// (void)vfo; // unused in release build
|
||||
|
||||
// Q_ASSERT (vfo == RIG_VFO_CURR); // G4WJS: at the time of writing only current VFO is signalled by hamlib
|
||||
|
||||
// HamlibTransceiver * transceiver (reinterpret_cast<HamlibTransceiver *> (arg));
|
||||
// Q_EMIT transceiver->frequency_change (f, Transceiver::A);
|
||||
// return RIG_OK;
|
||||
// }
|
||||
|
||||
class hamlib_tx_vfo_fixup final
|
||||
{
|
||||
public:
|
||||
hamlib_tx_vfo_fixup (RIG * rig, vfo_t tx_vfo)
|
||||
: rig_ {rig}
|
||||
{
|
||||
original_vfo_ = rig_->state.tx_vfo;
|
||||
rig_->state.tx_vfo = tx_vfo;
|
||||
}
|
||||
|
||||
~hamlib_tx_vfo_fixup ()
|
||||
{
|
||||
rig_->state.tx_vfo = original_vfo_;
|
||||
}
|
||||
|
||||
private:
|
||||
RIG * rig_;
|
||||
vfo_t original_vfo_;
|
||||
};
|
||||
}
|
||||
|
||||
void HamlibTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry)
|
||||
{
|
||||
rig_set_debug_callback (debug_callback, nullptr);
|
||||
|
||||
#if WSJT_HAMLIB_TRACE
|
||||
rig_set_debug (RIG_DEBUG_TRACE);
|
||||
#elif defined (NDEBUG)
|
||||
rig_set_debug (RIG_DEBUG_ERR);
|
||||
#else
|
||||
rig_set_debug (RIG_DEBUG_VERBOSE);
|
||||
#endif
|
||||
|
||||
rig_load_all_backends ();
|
||||
rig_list_foreach (rigCallback, registry);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig)
|
||||
{
|
||||
if (rig)
|
||||
{
|
||||
// rig->state.obj = 0;
|
||||
rig_cleanup (rig);
|
||||
}
|
||||
}
|
||||
|
||||
HamlibTransceiver::HamlibTransceiver (int model_number
|
||||
, QString const& cat_port
|
||||
, int cat_baud
|
||||
, TransceiverFactory::DataBits cat_data_bits
|
||||
, TransceiverFactory::StopBits cat_stop_bits
|
||||
, TransceiverFactory::Handshake cat_handshake
|
||||
, bool cat_dtr_always_on
|
||||
, bool cat_rts_always_on
|
||||
, TransceiverFactory::PTTMethod ptt_type
|
||||
, TransceiverFactory::TXAudioSource back_ptt_port
|
||||
, QString const& ptt_port
|
||||
, int poll_interval)
|
||||
: PollingTransceiver {poll_interval}
|
||||
, rig_ {rig_init (model_number)}
|
||||
, back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == back_ptt_port}
|
||||
, is_dummy_ {RIG_MODEL_DUMMY == model_number}
|
||||
, reversed_ {false}
|
||||
{
|
||||
if (!rig_)
|
||||
{
|
||||
throw error {"Hamlib initialisation error"};
|
||||
}
|
||||
|
||||
// rig_->state.obj = this;
|
||||
|
||||
if (/*!is_dummy_ &&*/ !cat_port.isEmpty () /*&& cat_port != "None"*/)
|
||||
{
|
||||
// #if defined (WIN32)
|
||||
// set_conf ("rig_pathname", ("\\\\.\\" + cat_port).toLatin1 ().data ());
|
||||
// #else
|
||||
set_conf ("rig_pathname", cat_port.toLatin1 ().data ());
|
||||
// #endif
|
||||
}
|
||||
|
||||
set_conf ("serial_speed", QByteArray::number (cat_baud).data ());
|
||||
set_conf ("data_bits", TransceiverFactory::seven_data_bits == cat_data_bits ? "7" : "8");
|
||||
set_conf ("stop_bits", TransceiverFactory::one_stop_bit == cat_stop_bits ? "1" : "2");
|
||||
|
||||
switch (cat_handshake)
|
||||
{
|
||||
case TransceiverFactory::handshake_none: set_conf ("serial_handshake", "None"); break;
|
||||
case TransceiverFactory::handshake_XonXoff: set_conf ("serial_handshake", "XONXOFF"); break;
|
||||
case TransceiverFactory::handshake_hardware: set_conf ("serial_handshake", "Hardware"); break;
|
||||
}
|
||||
|
||||
if (cat_dtr_always_on)
|
||||
{
|
||||
set_conf ("dtr_state", "ON");
|
||||
}
|
||||
if (TransceiverFactory::handshake_hardware != cat_handshake && cat_rts_always_on)
|
||||
{
|
||||
set_conf ("rts_state", "ON");
|
||||
}
|
||||
|
||||
switch (ptt_type)
|
||||
{
|
||||
case TransceiverFactory::PTT_method_VOX:
|
||||
set_conf ("ptt_type", "None");
|
||||
break;
|
||||
|
||||
case TransceiverFactory::PTT_method_CAT:
|
||||
set_conf ("ptt_type", "RIG");
|
||||
break;
|
||||
|
||||
case TransceiverFactory::PTT_method_DTR:
|
||||
case TransceiverFactory::PTT_method_RTS:
|
||||
if (!ptt_port.isEmpty () && ptt_port != "None" && ptt_port != cat_port)
|
||||
{
|
||||
#if defined (WIN32)
|
||||
set_conf ("ptt_pathname", ("\\\\.\\" + ptt_port).toLatin1 ().data ());
|
||||
#else
|
||||
set_conf ("ptt_pathname", ptt_port.toLatin1 ().data ());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (TransceiverFactory::PTT_method_DTR == ptt_type)
|
||||
{
|
||||
set_conf ("ptt_type", "DTR");
|
||||
}
|
||||
else
|
||||
{
|
||||
set_conf ("ptt_type", "RTS");
|
||||
}
|
||||
}
|
||||
|
||||
// Make Icom CAT split commands less glitchy
|
||||
set_conf ("no_xchg", "1");
|
||||
|
||||
// would be nice to get events but not supported on Windows and also not on a lot of rigs
|
||||
// rig_set_freq_callback (rig_.data (), &frequency_change_callback, this);
|
||||
}
|
||||
|
||||
HamlibTransceiver::~HamlibTransceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_start ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_start rig:" << QString::fromLatin1 (rig_->caps->mfg_name).trimmed () + ' '
|
||||
+ QString::fromLatin1 (rig_->caps->model_name).trimmed ();
|
||||
#endif
|
||||
|
||||
error_check (rig_open (rig_.data ()));
|
||||
|
||||
init_rig ();
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_stop ()
|
||||
{
|
||||
if (rig_)
|
||||
{
|
||||
rig_close (rig_.data ());
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_stop: state:" << state () << "reversed =" << reversed_;
|
||||
#endif
|
||||
}
|
||||
|
||||
void HamlibTransceiver::init_rig ()
|
||||
{
|
||||
if (!is_dummy_)
|
||||
{
|
||||
freq_t f1;
|
||||
freq_t f2;
|
||||
rmode_t m {RIG_MODE_USB};
|
||||
rmode_t mb;
|
||||
pbwidth_t w {rig_passband_wide (rig_.data (), m)};
|
||||
pbwidth_t wb;
|
||||
if (!rig_->caps->get_vfo)
|
||||
{
|
||||
// Icom have deficient CAT protocol with no way of reading which
|
||||
// VFO is selected or if SPLIT is selected so we have to simply
|
||||
// assume it is as when we started by setting at open time right
|
||||
// here. We also gather/set other initial state.
|
||||
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f1;
|
||||
#endif
|
||||
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w;
|
||||
#endif
|
||||
|
||||
if (!rig_->caps->set_vfo)
|
||||
{
|
||||
if (rig_has_vfo_op (rig_.data (), RIG_OP_TOGGLE))
|
||||
{
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_vfo_op TOGGLE";
|
||||
#endif
|
||||
|
||||
error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw error {"Hamlib: unable to initialise rig"};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_set_vfo";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB));
|
||||
}
|
||||
|
||||
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f2));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f2;
|
||||
#endif
|
||||
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &mb, &wb));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << mb << "bw =" << wb;
|
||||
#endif
|
||||
|
||||
update_other_frequency (f2);
|
||||
|
||||
if (!rig_->caps->set_vfo)
|
||||
{
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_vfo_op TOGGLE";
|
||||
#endif
|
||||
|
||||
error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE));
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_set_vfo";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN));
|
||||
}
|
||||
|
||||
if (f1 != f2 || m != mb || w != wb) // we must have started with MAIN/A
|
||||
{
|
||||
update_rx_frequency (f1);
|
||||
}
|
||||
else
|
||||
{
|
||||
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f1;
|
||||
#endif
|
||||
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w;
|
||||
#endif
|
||||
|
||||
update_rx_frequency (f1);
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_set_split_vfo";
|
||||
#endif
|
||||
|
||||
// error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, RIG_SPLIT_OFF, RIG_VFO_CURR));
|
||||
// update_split (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
vfo_t v;
|
||||
error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ().nospace () << "HamlibTransceiver::init_rig rig_get_vfo = 0x" << hex << v;
|
||||
#endif
|
||||
|
||||
reversed_ = RIG_VFO_B == v;
|
||||
|
||||
if (!(rig_->caps->targetable_vfo & (RIG_TARGETABLE_MODE | RIG_TARGETABLE_PURE)))
|
||||
{
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
update_mode (map_mode (m));
|
||||
}
|
||||
|
||||
poll ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::init_rig exit" << state () << "reversed =" << reversed_;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto HamlibTransceiver::get_vfos () const -> std::tuple<vfo_t, vfo_t>
|
||||
{
|
||||
if (rig_->caps->get_vfo)
|
||||
{
|
||||
vfo_t v;
|
||||
error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ().nospace () << "HamlibTransceiver::get_vfos rig_get_vfo = 0x" << hex << v;
|
||||
#endif
|
||||
|
||||
reversed_ = RIG_VFO_B == v;
|
||||
}
|
||||
else if (rig_->caps->set_vfo)
|
||||
{
|
||||
// use VFO A/MAIN for main frequency and B/SUB for Tx
|
||||
// frequency if split since these type of radios can only
|
||||
// support this way around
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::get_vfos rig_set_vfo";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN));
|
||||
}
|
||||
// else only toggle available but both VFOs should be substitutable
|
||||
|
||||
auto rx_vfo = rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN;
|
||||
auto tx_vfo = state ().split () ? (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB) : rx_vfo;
|
||||
if (reversed_)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::get_vfos reversing VFOs";
|
||||
#endif
|
||||
|
||||
std::swap (rx_vfo, tx_vfo);
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ().nospace () << "HamlibTransceiver::get_vfos RX VFO = 0x" << hex << rx_vfo << " TX VFO = 0x" << hex << tx_vfo;
|
||||
#endif
|
||||
|
||||
return std::make_tuple (rx_vfo, tx_vfo);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_frequency (Frequency f)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_frequency:" << f << "reversed:" << reversed_;
|
||||
#endif
|
||||
|
||||
if (!is_dummy_)
|
||||
{
|
||||
error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, f));
|
||||
}
|
||||
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_tx_frequency:" << tx << "rationalise mode:" << rationalise_mode << "reversed:" << reversed_;
|
||||
#endif
|
||||
|
||||
if (!is_dummy_)
|
||||
{
|
||||
auto vfos = get_vfos ();
|
||||
// auto rx_vfo = std::get<0> (vfos);
|
||||
auto tx_vfo = std::get<1> (vfos);
|
||||
|
||||
if (tx)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_freq";
|
||||
#endif
|
||||
|
||||
hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo);
|
||||
error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx));
|
||||
|
||||
if (rationalise_mode)
|
||||
{
|
||||
rmode_t current_mode;
|
||||
pbwidth_t current_width;
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::mode rig_get_split_mode";
|
||||
#endif
|
||||
auto new_mode = map_mode (state ().mode ());
|
||||
error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width));
|
||||
|
||||
if (new_mode != current_mode)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_mode";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// enable split last since some rigs (Kenwood for one) come out
|
||||
// of split when you switch RX VFO (to set split mode above for
|
||||
// example)
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_vfo";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF, tx_vfo));
|
||||
}
|
||||
|
||||
update_split (tx);
|
||||
update_other_frequency (tx);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_mode (MODE mode, bool rationalise)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_mode:" << mode << "rationalise:" << rationalise;
|
||||
#endif
|
||||
|
||||
if (!is_dummy_)
|
||||
{
|
||||
auto vfos = get_vfos ();
|
||||
// auto rx_vfo = std::get<0> (vfos);
|
||||
auto tx_vfo = std::get<1> (vfos);
|
||||
|
||||
rmode_t current_mode;
|
||||
pbwidth_t current_width;
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::mode rig_get_mode";
|
||||
#endif
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width));
|
||||
|
||||
auto new_mode = map_mode (mode);
|
||||
if (new_mode != current_mode)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::mode rig_set_mode";
|
||||
#endif
|
||||
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode)));
|
||||
}
|
||||
|
||||
if (state ().split () && rationalise)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::mode rig_get_split_mode";
|
||||
#endif
|
||||
error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width));
|
||||
|
||||
if (new_mode != current_mode)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::mode rig_set_split_mode";
|
||||
#endif
|
||||
hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo);
|
||||
error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_mode (mode);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::poll ()
|
||||
{
|
||||
if (is_dummy_)
|
||||
{
|
||||
// split with dummy is never reported since there is no rig
|
||||
if (state ().split ())
|
||||
{
|
||||
update_split (false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !WSJT_TRACE_CAT_POLLS
|
||||
#if defined (NDEBUG)
|
||||
rig_set_debug (RIG_DEBUG_ERR);
|
||||
#else
|
||||
rig_set_debug (RIG_DEBUG_VERBOSE);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
freq_t f;
|
||||
rmode_t m;
|
||||
pbwidth_t w;
|
||||
split_t s;
|
||||
|
||||
if (rig_->caps->get_vfo)
|
||||
{
|
||||
vfo_t v;
|
||||
error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug ().nospace () << "HamlibTransceiver::state rig_get_vfo = 0x" << hex << v;
|
||||
#endif
|
||||
|
||||
reversed_ = RIG_VFO_B == v;
|
||||
}
|
||||
|
||||
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f));
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug () << "HamlibTransceiver::state rig_get_freq =" << f;
|
||||
#endif
|
||||
|
||||
update_rx_frequency (f);
|
||||
|
||||
if (rig_->caps->targetable_vfo & (RIG_TARGETABLE_FREQ | RIG_TARGETABLE_PURE))
|
||||
{
|
||||
// we can only probe current VFO unless rig supports reading the other one directly
|
||||
error_check (rig_get_freq (rig_.data ()
|
||||
, reversed_
|
||||
? (rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN)
|
||||
: (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)
|
||||
, &f));
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug () << "HamlibTransceiver::state rig_get_freq other =" << f;
|
||||
#endif
|
||||
|
||||
update_other_frequency (f);
|
||||
}
|
||||
|
||||
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w));
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug () << "HamlibTransceiver::state rig_get_mode =" << m << "bw =" << w;
|
||||
#endif
|
||||
|
||||
update_mode (map_mode (m));
|
||||
|
||||
vfo_t v {RIG_VFO_NONE}; // so we can tell if it doesn't get updated :(
|
||||
auto rc = rig_get_split_vfo (rig_.data (), RIG_VFO_CURR, &s, &v);
|
||||
if (RIG_OK == rc && RIG_SPLIT_ON == s)
|
||||
{
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo split = " << s << " VFO = 0x" << hex << v;
|
||||
#endif
|
||||
|
||||
update_split (true);
|
||||
// if (RIG_VFO_A == v)
|
||||
// {
|
||||
// reversed_ = true; // not sure if this helps us here
|
||||
// }
|
||||
}
|
||||
else if (RIG_OK == rc) // not split
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo split = " << s << " VFO = 0x" << hex << v;
|
||||
#endif
|
||||
|
||||
update_split (false);
|
||||
}
|
||||
else if (-RIG_ENAVAIL == rc) // Some rigs (Icom) don't have a way of reporting SPLIT mode
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo can't do on this rig";
|
||||
#endif
|
||||
|
||||
// just report how we see it based on prior commands
|
||||
}
|
||||
else
|
||||
{
|
||||
error_check (rc);
|
||||
}
|
||||
|
||||
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt && rig_->caps->get_ptt)
|
||||
{
|
||||
ptt_t p;
|
||||
error_check (rig_get_ptt (rig_.data (), RIG_VFO_CURR, &p));
|
||||
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug () << "HamlibTransceiver::state rig_get_ptt =" << p;
|
||||
#endif
|
||||
|
||||
update_PTT (!(RIG_PTT_OFF == p));
|
||||
}
|
||||
|
||||
#if !WSJT_TRACE_CAT_POLLS
|
||||
#if WSJT_HAMLIB_TRACE
|
||||
rig_set_debug (RIG_DEBUG_TRACE);
|
||||
#elif defined (NDEBUG)
|
||||
rig_set_debug (RIG_DEBUG_ERR);
|
||||
#else
|
||||
rig_set_debug (RIG_DEBUG_VERBOSE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibTransceiver::do_ptt (bool on)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::do_ptt:" << on << state () << "reversed =" << reversed_;
|
||||
#endif
|
||||
|
||||
if (on)
|
||||
{
|
||||
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::ptt rig_set_ptt";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, back_ptt_port_ ? RIG_PTT_ON_DATA : RIG_PTT_ON));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "HamlibTransceiver::ptt rig_set_ptt";
|
||||
#endif
|
||||
|
||||
error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, RIG_PTT_OFF));
|
||||
}
|
||||
}
|
||||
|
||||
update_PTT (on);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::error_check (int ret_code) const
|
||||
{
|
||||
if (RIG_OK != ret_code)
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
qDebug () << "HamlibTransceiver::error_check: error:" << rigerror (ret_code);
|
||||
#endif
|
||||
|
||||
throw error {QByteArray ("Hamlib error: ") + rigerror (ret_code)};
|
||||
}
|
||||
}
|
||||
|
||||
void HamlibTransceiver::set_conf (char const * item, char const * value)
|
||||
{
|
||||
token_t token = rig_token_lookup (rig_.data (), item);
|
||||
if (RIG_CONF_END != token) // only set if valid for rig model
|
||||
{
|
||||
error_check (rig_set_conf (rig_.data (), token, value));
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray HamlibTransceiver::get_conf (char const * item)
|
||||
{
|
||||
token_t token = rig_token_lookup (rig_.data (), item);
|
||||
QByteArray value {128, '\0'};
|
||||
if (RIG_CONF_END != token) // only get if valid for rig model
|
||||
{
|
||||
error_check (rig_get_conf (rig_.data (), token, value.data ()));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
auto HamlibTransceiver::map_mode (rmode_t m) const -> MODE
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case RIG_MODE_AM:
|
||||
case RIG_MODE_SAM:
|
||||
case RIG_MODE_AMS:
|
||||
case RIG_MODE_DSB:
|
||||
return AM;
|
||||
|
||||
case RIG_MODE_CW:
|
||||
return CW;
|
||||
|
||||
case RIG_MODE_CWR:
|
||||
return CW_R;
|
||||
|
||||
case RIG_MODE_USB:
|
||||
case RIG_MODE_ECSSUSB:
|
||||
case RIG_MODE_SAH:
|
||||
case RIG_MODE_FAX:
|
||||
return USB;
|
||||
|
||||
case RIG_MODE_LSB:
|
||||
case RIG_MODE_ECSSLSB:
|
||||
case RIG_MODE_SAL:
|
||||
return LSB;
|
||||
|
||||
case RIG_MODE_RTTY:
|
||||
return FSK;
|
||||
|
||||
case RIG_MODE_RTTYR:
|
||||
return FSK_R;
|
||||
|
||||
case RIG_MODE_PKTLSB:
|
||||
return DIG_L;
|
||||
|
||||
case RIG_MODE_PKTUSB:
|
||||
return DIG_U;
|
||||
|
||||
case RIG_MODE_FM:
|
||||
case RIG_MODE_WFM:
|
||||
return FM;
|
||||
|
||||
case RIG_MODE_PKTFM:
|
||||
return DIG_FM;
|
||||
|
||||
default:
|
||||
return UNK;
|
||||
}
|
||||
}
|
||||
|
||||
rmode_t HamlibTransceiver::map_mode (MODE mode) const
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AM: return RIG_MODE_AM;
|
||||
case CW: return RIG_MODE_CW;
|
||||
case CW_R: return RIG_MODE_CWR;
|
||||
case USB: return RIG_MODE_USB;
|
||||
case LSB: return RIG_MODE_LSB;
|
||||
case FSK: return RIG_MODE_RTTY;
|
||||
case FSK_R: return RIG_MODE_RTTYR;
|
||||
case DIG_L: return RIG_MODE_PKTLSB;
|
||||
case DIG_U: return RIG_MODE_PKTUSB;
|
||||
case FM: return RIG_MODE_FM;
|
||||
case DIG_FM: return RIG_MODE_PKTFM;
|
||||
default: break;
|
||||
}
|
||||
return RIG_MODE_USB; // quieten compiler grumble
|
||||
}
|
68
HamlibTransceiver.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef HAMLIB_TRANSCEIVER_HPP_
|
||||
#define HAMLIB_TRANSCEIVER_HPP_
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include <hamlib/rig.h>
|
||||
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "PollingTransceiver.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
typedef struct rig RIG;
|
||||
struct rig_caps;
|
||||
typedef int vfo_t;
|
||||
}
|
||||
|
||||
// hamlib transceiver and PTT mostly delegated directly to hamlib Rig class
|
||||
class HamlibTransceiver final
|
||||
: public PollingTransceiver
|
||||
{
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *);
|
||||
|
||||
explicit HamlibTransceiver (int model_number
|
||||
, QString const& cat_port
|
||||
, int cat_baud
|
||||
, TransceiverFactory::DataBits cat_data_bits
|
||||
, TransceiverFactory::StopBits cat_stop_bits
|
||||
, TransceiverFactory::Handshake cat_handshake
|
||||
, bool cat_dtr_always_on
|
||||
, bool cat_rts_always_on
|
||||
, TransceiverFactory::PTTMethod ptt_type
|
||||
, TransceiverFactory::TXAudioSource back_ptt_port
|
||||
, QString const& ptt_port
|
||||
, int poll_interval = 0);
|
||||
~HamlibTransceiver ();
|
||||
|
||||
private:
|
||||
void do_start () override;
|
||||
void do_stop () override;
|
||||
void do_frequency (Frequency) override;
|
||||
void do_tx_frequency (Frequency, bool rationalise_mode) override;
|
||||
void do_mode (MODE, bool rationalise) override;
|
||||
void do_ptt (bool) override;
|
||||
|
||||
void poll () override;
|
||||
|
||||
void error_check (int ret_code) const;
|
||||
void set_conf (char const * item, char const * value);
|
||||
QByteArray get_conf (char const * item);
|
||||
Transceiver::MODE map_mode (rmode_t) const;
|
||||
rmode_t map_mode (Transceiver::MODE mode) const;
|
||||
void init_rig ();
|
||||
std::tuple<vfo_t, vfo_t> get_vfos () const;
|
||||
|
||||
struct RIGDeleter {static void cleanup (RIG *);};
|
||||
QScopedPointer<RIG, RIGDeleter> rig_;
|
||||
|
||||
bool back_ptt_port_;
|
||||
bool is_dummy_;
|
||||
|
||||
bool mutable reversed_;
|
||||
};
|
||||
|
||||
#endif
|
10
Info.plist.in
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>@WSJTX_BUNDLE_NAME@</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>@WSJTX_BUNDLE_NAME@</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,30 +1,30 @@
|
||||
+ + + This Software is released under the "Simplified BSD License" + + +
|
||||
Copyright 2010 Moe Wheatley. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation
|
||||
are those of the authors and should not be interpreted as representing
|
||||
official policies, either expressed or implied, of Moe Wheatley.
|
||||
+ + + This Software is released under the "Simplified BSD License" + + +
|
||||
Copyright 2010 Moe Wheatley. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation
|
||||
are those of the authors and should not be interpreted as representing
|
||||
official policies, either expressed or implied, of Moe Wheatley.
|
92
LiveFrequencyValidator.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "LiveFrequencyValidator.hpp"
|
||||
|
||||
#include <QLocale>
|
||||
#include <QString>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "Bands.hpp"
|
||||
#include "FrequencyList.hpp"
|
||||
|
||||
#include "moc_LiveFrequencyValidator.cpp"
|
||||
|
||||
LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box
|
||||
, Bands const * bands
|
||||
, FrequencyList const * frequencies
|
||||
, QWidget * parent)
|
||||
: QRegExpValidator {
|
||||
QRegExp { // frequency in MHz or band
|
||||
bands->data (QModelIndex {}).toString () // out of band string
|
||||
+ QString {R"(|((\d{0,6}(\)"} // up to 6 digits
|
||||
+ QLocale {}.decimalPoint () // (followed by decimal separator
|
||||
+ R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive))
|
||||
+ QLocale {}.decimalPoint () // or a decimal separator
|
||||
+ R"(\d{0,6})?))" // followed by up to 6 digits
|
||||
}
|
||||
, parent
|
||||
}
|
||||
, bands_ {bands}
|
||||
, frequencies_ {frequencies}
|
||||
, combo_box_ {combo_box}
|
||||
{
|
||||
}
|
||||
|
||||
auto LiveFrequencyValidator::validate (QString& input, int& pos) const -> State
|
||||
{
|
||||
auto state = QRegExpValidator::validate (input, pos);
|
||||
|
||||
// by never being Acceptable we force fixup calls on ENTER or
|
||||
// losing focus
|
||||
return Acceptable == state ? Intermediate : state;
|
||||
}
|
||||
|
||||
void LiveFrequencyValidator::fixup (QString& input) const
|
||||
{
|
||||
QRegExpValidator::fixup (input);
|
||||
|
||||
auto out_of_band = bands_->data (QModelIndex {}).toString ();
|
||||
|
||||
if (!out_of_band.startsWith (input))
|
||||
{
|
||||
if (input.contains ('m', Qt::CaseInsensitive))
|
||||
{
|
||||
input = input.toLower ();
|
||||
|
||||
QVector<QVariant> frequencies;
|
||||
for (int r = 0; r < frequencies_->rowCount (); ++r)
|
||||
{
|
||||
auto frequency = frequencies_->index (r, 0).data ();
|
||||
auto band_index = bands_->find (frequency);
|
||||
if (band_index.data ().toString () == input)
|
||||
{
|
||||
frequencies << frequency;
|
||||
}
|
||||
}
|
||||
if (!frequencies.isEmpty ())
|
||||
{
|
||||
Q_EMIT valid (frequencies.first ().value<Frequency> ());
|
||||
}
|
||||
else
|
||||
{
|
||||
input = QString {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// frequency input
|
||||
auto f = Radio::frequency (input, 6);
|
||||
input = bands_->data (bands_->find (f)).toString ();
|
||||
Q_EMIT valid (f);
|
||||
}
|
||||
|
||||
if (out_of_band == input)
|
||||
{
|
||||
combo_box_->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}");
|
||||
}
|
||||
else
|
||||
{
|
||||
combo_box_->lineEdit ()->setStyleSheet ({});
|
||||
}
|
||||
combo_box_->setCurrentText (input);
|
||||
}
|
||||
}
|
52
LiveFrequencyValidator.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef LIVE_FREQUENCY_VALIDATOR_HPP__
|
||||
#define LIVE_FREQUENCY_VALIDATOR_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QRegExpValidator>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
class Bands;
|
||||
class FrequencyList;
|
||||
class QComboBox;
|
||||
class QWidget;
|
||||
|
||||
//
|
||||
// Class LiveFrequencyValidator
|
||||
//
|
||||
// QLineEdit validator that controls input to an editable
|
||||
// QComboBox where the user can enter a valid band or a valid
|
||||
// frequency in megahetz.
|
||||
//
|
||||
// Collabrations
|
||||
//
|
||||
// Implements the QRegExpValidator interface. Validates input
|
||||
// from the supplied QComboBox as either a valid frequency in
|
||||
// megahertz or a valid band as defined by the supplied column of
|
||||
// the supplied QAbstractItemModel.
|
||||
//
|
||||
class LiveFrequencyValidator final
|
||||
: public QRegExpValidator
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
LiveFrequencyValidator (QComboBox * combo_box // associated combo box
|
||||
, Bands const * bands // bands model
|
||||
, FrequencyList const * frequencies // working frequencies model
|
||||
, QWidget * parent = nullptr);
|
||||
|
||||
State validate (QString& input, int& pos) const override;
|
||||
void fixup (QString& input) const override;
|
||||
|
||||
Q_SIGNAL void valid (Frequency) const;
|
||||
|
||||
private:
|
||||
Bands const * bands_;
|
||||
FrequencyList const * frequencies_;
|
||||
QComboBox * combo_box_;
|
||||
};
|
||||
|
||||
#endif
|
16
Mac-wsjtx-startup.sh
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
WSJTX_BUNDLE="`echo "$0" | sed -e 's/\/Contents\/MacOS\/.*//'`"
|
||||
WSJTX_RESOURCES="$WSJTX_BUNDLE/Contents/Resources"
|
||||
WSJTX_TEMP="/tmp/wsjtx/$UID"
|
||||
|
||||
echo "running $0"
|
||||
echo "WSJTX_BUNDLE: $WSJTX_BUNDLE"
|
||||
|
||||
# Setup temporary runtime files
|
||||
rm -rf "$WSJTX_TEMP"
|
||||
|
||||
export "DYLD_LIBRARY_PATH=$WSJTX_RESOURCES/lib"
|
||||
export "PATH=$WSJTX_RESOURCES/bin:$PATH"
|
||||
|
||||
#export
|
||||
exec "$WSJTX_RESOURCES/bin/wsjtx"
|
@ -5,7 +5,10 @@
|
||||
#include <QDebug>
|
||||
#include "mainwindow.h"
|
||||
|
||||
extern float gran(); // Noise generator (for tests only)
|
||||
#include "moc_Modulator.cpp"
|
||||
|
||||
extern float gran(); // Noise generator (for tests only)
|
||||
|
||||
#define RAMP_INCREMENT 64 // MUST be an integral factor of 2^16
|
||||
|
||||
#if defined (WSJT_SOFT_KEYING)
|
||||
|
@ -57,7 +57,7 @@ private:
|
||||
Q_SLOT void tune (bool newState = true) {m_tuning = newState;}
|
||||
Q_SLOT void mute (bool newState = true) {m_muted = newState;}
|
||||
Q_SLOT void setFrequency (unsigned newFrequency) {m_frequency = newFrequency;}
|
||||
Q_SIGNAL void stateChanged (ModulatorState);
|
||||
Q_SIGNAL void stateChanged (ModulatorState) const;
|
||||
|
||||
private:
|
||||
qint16 postProcessSample (qint16 sample) const;
|
||||
|
83
NetworkServerLookup.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "NetworkServerLookup.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QHostInfo>
|
||||
#include <QString>
|
||||
|
||||
std::tuple<QHostAddress, quint16>
|
||||
network_server_lookup (QString const& query
|
||||
, quint16 default_service_port
|
||||
, QHostAddress default_host_address
|
||||
, QAbstractSocket::NetworkLayerProtocol required_protocol)
|
||||
{
|
||||
QHostAddress host_address {default_host_address};
|
||||
quint16 service_port {default_service_port};
|
||||
|
||||
QString host_name;
|
||||
if (!query.isEmpty ())
|
||||
{
|
||||
int port_colon_index {-1};
|
||||
|
||||
if ('[' == query[0])
|
||||
{
|
||||
// assume IPv6 combined address/port syntax [<address>]:<port>
|
||||
auto close_bracket_index = query.lastIndexOf (']');
|
||||
host_name = query.mid (1, close_bracket_index - 1);
|
||||
port_colon_index = query.indexOf (':', close_bracket_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
port_colon_index = query.lastIndexOf (':');
|
||||
host_name = query.left (port_colon_index);
|
||||
}
|
||||
|
||||
if (port_colon_index >= 0)
|
||||
{
|
||||
bool ok;
|
||||
service_port = query.mid (port_colon_index + 1).toUShort (&ok);
|
||||
if (!ok)
|
||||
{
|
||||
throw std::runtime_error {"network server lookup error: invalid port"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!host_name.isEmpty ())
|
||||
{
|
||||
auto host_info = QHostInfo::fromName (host_name);
|
||||
if (host_info.addresses ().isEmpty ())
|
||||
{
|
||||
throw std::runtime_error {"network server lookup error: host name lookup failed"};
|
||||
}
|
||||
|
||||
bool found {false};
|
||||
for (int i {0}; i < host_info.addresses ().size () && !found; ++i)
|
||||
{
|
||||
host_address = host_info.addresses ().at (i);
|
||||
switch (required_protocol)
|
||||
{
|
||||
case QAbstractSocket::IPv4Protocol:
|
||||
case QAbstractSocket::IPv6Protocol:
|
||||
if (required_protocol != host_address.protocol ())
|
||||
{
|
||||
break;
|
||||
}
|
||||
// drop through
|
||||
|
||||
case QAbstractSocket::AnyIPProtocol:
|
||||
found = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error {"network server lookup error: invalid required protocol"};
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
throw std::runtime_error {"network server lookup error: no suitable host address found"};
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple (host_address, service_port);
|
||||
}
|
38
NetworkServerLookup.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef NETWORK_SERVER_LOOKUP_HPP__
|
||||
#define NETWORK_SERVER_LOOKUP_HPP__
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QAbstractSocket>
|
||||
|
||||
class QString;
|
||||
|
||||
//
|
||||
// Do a blocking DNS lookup using query as a destination host address
|
||||
// and port.
|
||||
//
|
||||
// query can be one of:
|
||||
//
|
||||
// 1) "" (empty string) - use defaults
|
||||
// 2) ":nnnnn" - override default service port with port nnnnn
|
||||
// 3) "<valid-host-name>" - override default host address with DNS lookup
|
||||
// 4) "nnn.nnn.nnn.nnn" - override default host address with the IPv4 address given by nnn.nnn.nnn.nnn
|
||||
// 5) "[<valid-IPv6-address]" - override default host address with the given IPv6 address
|
||||
// 6) "<valid-host-name>:nnnnn" - use as per (3) & (2)
|
||||
// 7) "nnn.nnn.nnn.nnn:nnnnn" - use as per (4) & (2)
|
||||
// 8) "[<valid-IPv6-address]:nnnnn" - use as per (5) & (2)
|
||||
//
|
||||
// The first host address matching the protocol and the service port
|
||||
// number are returned.
|
||||
//
|
||||
// If no suitable host address is found QHostAddress::Null will be
|
||||
// returned in the first member of the result tuple.
|
||||
//
|
||||
std::tuple<QHostAddress, quint16>
|
||||
network_server_lookup (QString const& query
|
||||
, quint16 default_service_port
|
||||
, QHostAddress default_host_address = QHostAddress::LocalHost
|
||||
, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol);
|
||||
|
||||
#endif
|
741
OmniRigTransceiver.cpp
Normal file
@ -0,0 +1,741 @@
|
||||
#include "OmniRigTransceiver.hpp"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include <objbase.h>
|
||||
|
||||
#include "moc_OmniRigTransceiver.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
auto constexpr OmniRig_transceiver_one_name = "OmniRig Rig 1";
|
||||
auto constexpr OmniRig_transceiver_two_name = "OmniRig Rig 2";
|
||||
}
|
||||
|
||||
auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
|
||||
{
|
||||
if (param & OmniRig::PM_CW_U)
|
||||
{
|
||||
return CW_R;
|
||||
}
|
||||
else if (param & OmniRig::PM_CW_L)
|
||||
{
|
||||
return CW;
|
||||
}
|
||||
else if (param & OmniRig::PM_SSB_U)
|
||||
{
|
||||
return USB;
|
||||
}
|
||||
else if (param & OmniRig::PM_SSB_L)
|
||||
{
|
||||
return LSB;
|
||||
}
|
||||
else if (param & OmniRig::PM_DIG_U)
|
||||
{
|
||||
return DIG_U;
|
||||
}
|
||||
else if (param & OmniRig::PM_DIG_L)
|
||||
{
|
||||
return DIG_L;
|
||||
}
|
||||
else if (param & OmniRig::PM_AM)
|
||||
{
|
||||
return AM;
|
||||
}
|
||||
else if (param & OmniRig::PM_FM)
|
||||
{
|
||||
return FM;
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRig map_mode unrecognized mode";
|
||||
#endif
|
||||
|
||||
throw error {"OmniRig: unrecognized mode"};
|
||||
}
|
||||
|
||||
OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case AM: return OmniRig::PM_AM;
|
||||
case CW: return OmniRig::PM_CW_L;
|
||||
case CW_R: return OmniRig::PM_CW_U;
|
||||
case USB: return OmniRig::PM_SSB_U;
|
||||
case LSB: return OmniRig::PM_SSB_L;
|
||||
case FSK: return OmniRig::PM_DIG_L;
|
||||
case FSK_R: return OmniRig::PM_DIG_U;
|
||||
case DIG_L: return OmniRig::PM_DIG_L;
|
||||
case DIG_U: return OmniRig::PM_DIG_U;
|
||||
case FM: return OmniRig::PM_FM;
|
||||
case DIG_FM: return OmniRig::PM_FM;
|
||||
default: break;
|
||||
}
|
||||
return OmniRig::PM_SSB_U; // quieten compiler grumble
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2)
|
||||
{
|
||||
(*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
|
||||
id1
|
||||
, TransceiverFactory::Capabilities::none // COM isn't serial or network
|
||||
, true // does PTT
|
||||
, false // doesn't select mic/data (use OmniRig config file)
|
||||
, true // can remote control RTS nd DTR
|
||||
, true // asynchronous interface
|
||||
};
|
||||
(*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities {
|
||||
id2
|
||||
, TransceiverFactory::Capabilities::none // COM isn't serial or network
|
||||
, true // does PTT
|
||||
, false // doesn't select mic/data (use OmniRig config file)
|
||||
, true // can remote control RTS nd DTR
|
||||
, true // asynchronous interface
|
||||
};
|
||||
}
|
||||
|
||||
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber n, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port)
|
||||
: wrapped_ {std::move (wrapped)}
|
||||
, use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))}
|
||||
, ptt_type_ {ptt_type}
|
||||
, startup_poll_countdown_ {2}
|
||||
, rig_number_ {n}
|
||||
, readable_params_ {0}
|
||||
, writable_params_ {0}
|
||||
, send_update_signal_ {false}
|
||||
, reversed_ {false}
|
||||
, starting_ {false}
|
||||
{
|
||||
}
|
||||
|
||||
OmniRigTransceiver::~OmniRigTransceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_start ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_start";
|
||||
#endif
|
||||
|
||||
wrapped_->start ();
|
||||
|
||||
CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
|
||||
|
||||
omni_rig_.reset (new OmniRig::OmniRigX {this});
|
||||
if (omni_rig_->isNull ())
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_start: failed to start COM server";
|
||||
#endif
|
||||
|
||||
throw error {"Failed to start OmniRig COM server"};
|
||||
}
|
||||
|
||||
// COM/OLE exceptions get signalled
|
||||
connect (&*omni_rig_, SIGNAL (exception (int, QString, QString, QString)), this, SLOT (handle_COM_exception (int, QString, QString, QString)));
|
||||
|
||||
// IOmniRigXEvent interface signals
|
||||
connect (&*omni_rig_, SIGNAL (VisibleChange ()), this, SLOT (handle_visible_change ()));
|
||||
connect (&*omni_rig_, SIGNAL (RigTypeChange (int)), this, SLOT (handle_rig_type_change (int)));
|
||||
connect (&*omni_rig_, SIGNAL (StatusChange (int)), this, SLOT (handle_status_change (int)));
|
||||
connect (&*omni_rig_, SIGNAL (ParamsChange (int, int)), this, SLOT (handle_params_change (int, int)));
|
||||
connect (&*omni_rig_
|
||||
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
|
||||
, this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&)));
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
|
||||
<< "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ()
|
||||
;
|
||||
#endif
|
||||
|
||||
// fetch the interface of the RigX CoClass and instantiate a proxy object
|
||||
switch (rig_number_)
|
||||
{
|
||||
case One: rig_.reset (new OmniRig::RigX (omni_rig_->Rig1 ())); break;
|
||||
case Two: rig_.reset (new OmniRig::RigX (omni_rig_->Rig2 ())); break;
|
||||
}
|
||||
|
||||
Q_ASSERT (rig_);
|
||||
Q_ASSERT (!rig_->isNull ());
|
||||
|
||||
if (use_for_ptt_ && (TransceiverFactory::PTT_method_DTR == ptt_type_ || TransceiverFactory::PTT_method_RTS == ptt_type_))
|
||||
{
|
||||
// fetch the interface for the serial port if we need it for PTT
|
||||
port_.reset (new OmniRig::PortBits (rig_->PortBits ()));
|
||||
|
||||
Q_ASSERT (port_);
|
||||
Q_ASSERT (!port_->isNull ());
|
||||
|
||||
// if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
|
||||
// {
|
||||
// throw error {("Failed to get exclusive use of " + ptt_type + " from OmniRig").toLocal8Bit ()};
|
||||
// }
|
||||
|
||||
// start off so we don't accidentally key the radio
|
||||
if (TransceiverFactory::PTT_method_DTR == ptt_type_)
|
||||
{
|
||||
port_->SetDtr (false);
|
||||
}
|
||||
else // RTS
|
||||
{
|
||||
port_->SetRts (false);
|
||||
}
|
||||
}
|
||||
|
||||
readable_params_ = rig_->ReadableParams ();
|
||||
writable_params_ = rig_->WriteableParams ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< QString ("OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4")
|
||||
.arg (rig_->RigType ())
|
||||
.arg (readable_params_, 8, 16, QChar ('0'))
|
||||
.arg (writable_params_, 8, 16, QChar ('0'))
|
||||
.arg (rig_number_).toLocal8Bit ()
|
||||
;
|
||||
#endif
|
||||
|
||||
starting_ = true;
|
||||
QTimer::singleShot (5000, this, SLOT (startup_check ()));
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_stop ()
|
||||
{
|
||||
if (port_)
|
||||
{
|
||||
// port_->Unlock (); // release serial port
|
||||
port_->clear ();
|
||||
}
|
||||
|
||||
rig_->clear ();
|
||||
|
||||
omni_rig_->clear ();
|
||||
|
||||
CoUninitialize ();
|
||||
|
||||
wrapped_->stop ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_stop";
|
||||
#endif
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::startup_check ()
|
||||
{
|
||||
if (starting_)
|
||||
{
|
||||
if (--startup_poll_countdown_)
|
||||
{
|
||||
init_rig ();
|
||||
QTimer::singleShot (5000, this, SLOT (startup_check ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
startup_poll_countdown_ = 2;
|
||||
|
||||
// signal that we haven't seen anything from OmniRig
|
||||
offline ("OmniRig initialisation timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::init_rig ()
|
||||
{
|
||||
if (writable_params_ & OmniRig::PM_VFOA)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::init_rig: set VFO A";
|
||||
#endif
|
||||
|
||||
rig_->SetVfo (OmniRig::PM_VFOA);
|
||||
if (writable_params_ & OmniRig::PM_SPLITOFF)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::init_rig: set SPLIT off";
|
||||
#endif
|
||||
|
||||
rig_->SetSplit (OmniRig::PM_SPLITOFF);
|
||||
}
|
||||
}
|
||||
else if (writable_params_ & OmniRig::PM_VFOAA)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::init_rig: set VFO A and SPLIT off";
|
||||
#endif
|
||||
|
||||
rig_->SetVfo (OmniRig::PM_VFOAA);
|
||||
}
|
||||
|
||||
reversed_ = false;
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_sync (bool force_signal)
|
||||
{
|
||||
// nothing much we can do here, we just have to let OmniRig do its
|
||||
// stuff and its first poll should send us and update that will
|
||||
// trigger a update signal from us. Any attempt to query OmniRig
|
||||
// leads to a whole mess of trouble since its internal state is
|
||||
// garbage until it has done its first rig poll.
|
||||
send_update_signal_ = force_signal;
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::handle_COM_exception:" << QString::number (code) + " at " + source + ": " + desc + " (" + help + ')';
|
||||
#endif
|
||||
|
||||
throw error {("OmniRig COM/OLE error: " + QString::number (code) + " at " + source + ": " + desc + " (" + help + ')').toLocal8Bit ()};
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_visible_change ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRig visibility change: visibility =" << omni_rig_->DialogVisible ();
|
||||
#endif
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
|
||||
{
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
readable_params_ = rig_->ReadableParams ();
|
||||
writable_params_ = rig_->WriteableParams ();
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< QString ("OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4")
|
||||
.arg (rig_->RigType ())
|
||||
.arg (readable_params_, 8, 16, QChar ('0'))
|
||||
.arg (writable_params_, 8, 16, QChar ('0'))
|
||||
.arg (rig_number).toLocal8Bit ()
|
||||
;
|
||||
#endif
|
||||
|
||||
offline ("OmniRig rig changed");
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_status_change (int rig_number)
|
||||
{
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< QString ("OmniRig status change: new status for rig %1 =").arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit ();
|
||||
#endif
|
||||
|
||||
if (OmniRig::ST_ONLINE != rig_->Status ())
|
||||
{
|
||||
offline ("OmniRig rig went offline");
|
||||
}
|
||||
else
|
||||
{
|
||||
starting_ = false;
|
||||
|
||||
TransceiverState old_state {state ()};
|
||||
init_rig ();
|
||||
|
||||
if (old_state != state () || send_update_signal_)
|
||||
{
|
||||
update_complete ();
|
||||
send_update_signal_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
||||
{
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< QString ("OmniRig params change: params = 0x%1 for rig %2")
|
||||
.arg (params, 8, 16, QChar ('0'))
|
||||
.arg (rig_number).toLocal8Bit ()
|
||||
<< "state before:" << state ()
|
||||
;
|
||||
#endif
|
||||
|
||||
starting_ = false;
|
||||
|
||||
TransceiverState old_state {state ()};
|
||||
auto need_frequency = false;
|
||||
|
||||
// state_.online = true; // sometimes we don't get an initial
|
||||
// // OmniRig::ST_ONLINE status change
|
||||
// // event
|
||||
|
||||
if (params & OmniRig::PM_VFOAA)
|
||||
{
|
||||
update_split (false);
|
||||
reversed_ = false;
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
}
|
||||
if (params & OmniRig::PM_VFOAB)
|
||||
{
|
||||
update_split (true);
|
||||
reversed_ = false;
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
}
|
||||
if (params & OmniRig::PM_VFOBA)
|
||||
{
|
||||
update_split (true);
|
||||
reversed_ = true;
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
}
|
||||
if (params & OmniRig::PM_VFOBB)
|
||||
{
|
||||
update_split (false);
|
||||
reversed_ = true;
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
}
|
||||
if (params & OmniRig::PM_VFOA)
|
||||
{
|
||||
reversed_ = false;
|
||||
need_frequency = true;
|
||||
}
|
||||
if (params & OmniRig::PM_VFOB)
|
||||
{
|
||||
reversed_ = true;
|
||||
need_frequency = true;
|
||||
}
|
||||
|
||||
if (params & OmniRig::PM_FREQ)
|
||||
{
|
||||
need_frequency = true;
|
||||
}
|
||||
if (params & OmniRig::PM_FREQA)
|
||||
{
|
||||
if (reversed_)
|
||||
{
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
}
|
||||
}
|
||||
if (params & OmniRig::PM_FREQB)
|
||||
{
|
||||
if (reversed_)
|
||||
{
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
}
|
||||
else
|
||||
{
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
}
|
||||
}
|
||||
|
||||
if (need_frequency)
|
||||
{
|
||||
if (readable_params_ & OmniRig::PM_FREQA)
|
||||
{
|
||||
if (reversed_)
|
||||
{
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
}
|
||||
need_frequency = false;
|
||||
}
|
||||
if (readable_params_ & OmniRig::PM_FREQB)
|
||||
{
|
||||
if (reversed_)
|
||||
{
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
}
|
||||
else
|
||||
{
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (need_frequency && (readable_params_ & OmniRig::PM_FREQ))
|
||||
{
|
||||
update_rx_frequency (rig_->Freq ());
|
||||
}
|
||||
|
||||
if (params & OmniRig::PM_PITCH)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_RITOFFSET)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_RIT0)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_VFOEQUAL)
|
||||
{
|
||||
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
|
||||
update_rx_frequency (f);
|
||||
update_other_frequency (f);
|
||||
update_mode (map_mode (rig_->Mode ()));
|
||||
}
|
||||
if (params & OmniRig::PM_VFOSWAP)
|
||||
{
|
||||
auto temp = state ().tx_frequency ();
|
||||
update_other_frequency (state ().frequency ());
|
||||
update_rx_frequency (temp);
|
||||
update_mode (map_mode (rig_->Mode ()));
|
||||
}
|
||||
if (params & OmniRig::PM_SPLITON)
|
||||
{
|
||||
update_split (true);
|
||||
}
|
||||
if (params & OmniRig::PM_SPLITOFF)
|
||||
{
|
||||
update_split (false);
|
||||
}
|
||||
if (params & OmniRig::PM_RITON)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_RITOFF)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_XITON)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_XITOFF)
|
||||
{
|
||||
}
|
||||
if (params & OmniRig::PM_RX)
|
||||
{
|
||||
update_PTT (false);
|
||||
}
|
||||
if (params & OmniRig::PM_TX)
|
||||
{
|
||||
update_PTT ();
|
||||
}
|
||||
if (params & OmniRig::PM_CW_U)
|
||||
{
|
||||
update_mode (CW_R);
|
||||
}
|
||||
if (params & OmniRig::PM_CW_L)
|
||||
{
|
||||
update_mode (CW);
|
||||
}
|
||||
if (params & OmniRig::PM_SSB_U)
|
||||
{
|
||||
update_mode (USB);
|
||||
}
|
||||
if (params & OmniRig::PM_SSB_L)
|
||||
{
|
||||
update_mode (LSB);
|
||||
}
|
||||
if (params & OmniRig::PM_DIG_U)
|
||||
{
|
||||
update_mode (DIG_U);
|
||||
}
|
||||
if (params & OmniRig::PM_DIG_L)
|
||||
{
|
||||
update_mode (DIG_L);
|
||||
}
|
||||
if (params & OmniRig::PM_AM)
|
||||
{
|
||||
update_mode (AM);
|
||||
}
|
||||
if (params & OmniRig::PM_FM)
|
||||
{
|
||||
update_mode (FM);
|
||||
}
|
||||
|
||||
if (old_state != state () || send_update_signal_)
|
||||
{
|
||||
update_complete ();
|
||||
send_update_signal_ = false;
|
||||
}
|
||||
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< "OmniRig params change: state after:" << state ()
|
||||
;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply)
|
||||
{
|
||||
(void)command;
|
||||
(void)reply;
|
||||
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug ()
|
||||
<< "OmniRig custom command" << command.toString ().toLocal8Bit ()
|
||||
<< "with reply" << reply.toString ().toLocal8Bit ()
|
||||
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ()
|
||||
;
|
||||
|
||||
qDebug () << "OmniRig rig number:" << rig_number_ << ':' << state ();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_ptt (bool on)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_ptt:" << on << state ();
|
||||
#endif
|
||||
|
||||
if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_ptt: set PTT";
|
||||
#endif
|
||||
|
||||
rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (port_)
|
||||
{
|
||||
if (TransceiverFactory::PTT_method_RTS == ptt_type_)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_ptt: set RTS";
|
||||
#endif
|
||||
port_->SetRts (on);
|
||||
}
|
||||
else // "DTR"
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_ptt: set DTR";
|
||||
#endif
|
||||
|
||||
port_->SetDtr (on);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_ptt: set PTT using basic transceiver";
|
||||
#endif
|
||||
|
||||
wrapped_->ptt (on);
|
||||
}
|
||||
|
||||
if (state ().ptt () != on)
|
||||
{
|
||||
update_PTT (on);
|
||||
update_complete ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_frequency (Frequency f)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_frequency:" << f << state ();
|
||||
#endif
|
||||
|
||||
if (OmniRig::PM_FREQ & writable_params_)
|
||||
{
|
||||
rig_->SetFreq (f);
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
|
||||
{
|
||||
rig_->SetFreqB (f);
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
|
||||
{
|
||||
rig_->SetFreqA (f);
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw error {"OmniRig: don't know how to set rig frequency"};
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_tx_frequency:" << tx << state ();
|
||||
#endif
|
||||
|
||||
bool split {tx != 0};
|
||||
|
||||
if (split)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_tx_frequency: set SPLIT mode on";
|
||||
#endif
|
||||
|
||||
rig_->SetSplitMode (state ().frequency (), tx);
|
||||
update_other_frequency (tx);
|
||||
update_split (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_tx_frequency: set SPLIT mode off";
|
||||
#endif
|
||||
|
||||
rig_->SetSimplexMode (state ().frequency ());
|
||||
update_split (false);
|
||||
}
|
||||
|
||||
bool notify {false};
|
||||
|
||||
if (readable_params_ & OmniRig::PM_FREQ || !(readable_params_ & (OmniRig::PM_FREQA | OmniRig::PM_FREQB)))
|
||||
{
|
||||
update_other_frequency (tx); // async updates won't return this
|
||||
// so just store it and hope
|
||||
// operator doesn't change the
|
||||
// "back" VFO on rig
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_tx_frequency: setting SPLIT manually";
|
||||
#endif
|
||||
|
||||
update_split (split); // we can't read it so just set and
|
||||
// hope op doesn't change it
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (notify)
|
||||
{
|
||||
update_complete ();
|
||||
}
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_mode (MODE mode, bool /* rationalise */)
|
||||
{
|
||||
#if WSJT_TRACE_CAT
|
||||
qDebug () << "OmniRigTransceiver::do_mode:" << mode << state ();
|
||||
#endif
|
||||
|
||||
// TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
|
||||
|
||||
auto mapped = map_mode (mode);
|
||||
|
||||
if (mapped & writable_params_)
|
||||
{
|
||||
rig_->SetMode (mapped);
|
||||
update_mode (mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
offline ("OmniRig invalid mode");
|
||||
}
|
||||
}
|
73
OmniRigTransceiver.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef OMNI_RIG_TRANSCEIVER_HPP__
|
||||
#define OMNI_RIG_TRANSCEIVER_HPP__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QScopedPointer>
|
||||
#include <QString>
|
||||
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "TransceiverBase.hpp"
|
||||
|
||||
#include "OmniRig.h"
|
||||
|
||||
//
|
||||
// OmniRig Transceiver Interface
|
||||
//
|
||||
// Implemented as a Transceiver decorator because we may want the PTT
|
||||
// services of another Transceiver type such as the HamlibTransceiver
|
||||
// which can be enabled by wrapping a HamlibTransceiver instantiated
|
||||
// as a "Hamlib Dummy" transceiver in the Transceiver factory method.
|
||||
//
|
||||
class OmniRigTransceiver final
|
||||
: public TransceiverBase
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *, int id1, int id2);
|
||||
|
||||
enum RigNumber {One = 1, Two};
|
||||
|
||||
// takes ownership of wrapped Transceiver
|
||||
explicit OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port);
|
||||
~OmniRigTransceiver ();
|
||||
|
||||
void do_start () override;
|
||||
void do_stop () override;
|
||||
void do_frequency (Frequency) override;
|
||||
void do_tx_frequency (Frequency, bool rationalise_mode) override;
|
||||
void do_mode (MODE, bool rationalise) override;
|
||||
void do_ptt (bool on) override;
|
||||
void do_sync (bool force_signal) override;
|
||||
|
||||
private:
|
||||
Q_SLOT void startup_check ();
|
||||
Q_SLOT void handle_COM_exception (int, QString, QString, QString);
|
||||
Q_SLOT void handle_visible_change ();
|
||||
Q_SLOT void handle_rig_type_change (int rig_number);
|
||||
Q_SLOT void handle_status_change (int rig_number);
|
||||
Q_SLOT void handle_params_change (int rig_number, int params);
|
||||
Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply);
|
||||
|
||||
void init_rig ();
|
||||
|
||||
static MODE map_mode (OmniRig::RigParamX param);
|
||||
static OmniRig::RigParamX map_mode (MODE mode);
|
||||
|
||||
std::unique_ptr<TransceiverBase> wrapped_;
|
||||
bool use_for_ptt_;
|
||||
TransceiverFactory::PTTMethod ptt_type_;
|
||||
unsigned startup_poll_countdown_;
|
||||
QScopedPointer<OmniRig::OmniRigX> omni_rig_;
|
||||
RigNumber rig_number_;
|
||||
QScopedPointer<OmniRig::RigX> rig_;
|
||||
QScopedPointer<OmniRig::PortBits> port_;
|
||||
int readable_params_;
|
||||
int writable_params_;
|
||||
bool send_update_signal_;
|
||||
bool reversed_; // some rigs can reverse VFOs
|
||||
bool starting_;
|
||||
};
|
||||
|
||||
#endif
|
209
PollingTransceiver.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
#include "PollingTransceiver.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
unsigned const polls_to_stabilize {3};
|
||||
}
|
||||
|
||||
// Internal implementation of the PollingTransceiver class.
|
||||
class PollingTransceiver::impl final
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY (impl);
|
||||
|
||||
public:
|
||||
impl (PollingTransceiver * self, int poll_interval)
|
||||
: QObject {self}
|
||||
, self_ {self}
|
||||
, interval_ {poll_interval}
|
||||
, poll_timer_ {nullptr}
|
||||
, retries_ {0}
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void start_timer ()
|
||||
{
|
||||
if (interval_)
|
||||
{
|
||||
if (!poll_timer_)
|
||||
{
|
||||
poll_timer_ = new QTimer {this}; // pass ownership to QObject which handles destruction for us
|
||||
|
||||
connect (poll_timer_, &QTimer::timeout, this, &PollingTransceiver::impl::handle_timeout);
|
||||
}
|
||||
poll_timer_->start (interval_);
|
||||
}
|
||||
}
|
||||
|
||||
void stop_timer ()
|
||||
{
|
||||
if (poll_timer_)
|
||||
{
|
||||
poll_timer_->stop ();
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void handle_timeout ();
|
||||
|
||||
PollingTransceiver * self_; // our owner so we can call methods
|
||||
int interval_; // polling interval in milliseconds
|
||||
QTimer * poll_timer_;
|
||||
|
||||
// keep a record of the last state signalled so we can elide
|
||||
// duplicate updates
|
||||
Transceiver::TransceiverState last_signalled_state_;
|
||||
|
||||
// keep a record of expected state so we can compare with actual
|
||||
// updates to determine when state changes have bubbled through
|
||||
Transceiver::TransceiverState next_state_;
|
||||
|
||||
unsigned retries_; // number of incorrect polls left
|
||||
|
||||
friend class PollingTransceiver;
|
||||
};
|
||||
|
||||
#include "PollingTransceiver.moc"
|
||||
|
||||
|
||||
PollingTransceiver::PollingTransceiver (int poll_interval)
|
||||
: m_ {this, poll_interval}
|
||||
{
|
||||
}
|
||||
|
||||
PollingTransceiver::~PollingTransceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_post_start ()
|
||||
{
|
||||
m_->start_timer ();
|
||||
if (!m_->next_state_.online ())
|
||||
{
|
||||
// remember that we are expecting to go online
|
||||
m_->next_state_.online (true);
|
||||
m_->retries_ = polls_to_stabilize;
|
||||
}
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_post_stop ()
|
||||
{
|
||||
// not much point waiting for rig to go offline since we are ceasing
|
||||
// polls
|
||||
m_->stop_timer ();
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_post_frequency (Frequency f)
|
||||
{
|
||||
if (m_->next_state_.frequency () != f)
|
||||
{
|
||||
// update expected state with new frequency and set poll count
|
||||
m_->next_state_.frequency (f);
|
||||
m_->retries_ = polls_to_stabilize;
|
||||
}
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_post_tx_frequency (Frequency f)
|
||||
{
|
||||
if (m_->next_state_.tx_frequency () != f)
|
||||
{
|
||||
// update expected state with new TX frequency and set poll
|
||||
// count
|
||||
m_->next_state_.tx_frequency (f);
|
||||
m_->next_state_.split (f); // setting non-zero TX frequency means split
|
||||
m_->retries_ = polls_to_stabilize;
|
||||
}
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_post_mode (MODE m)
|
||||
{
|
||||
if (m_->next_state_.mode () != m)
|
||||
{
|
||||
// update expected state with new mode and set poll count
|
||||
m_->next_state_.mode (m);
|
||||
m_->retries_ = polls_to_stabilize;
|
||||
}
|
||||
}
|
||||
|
||||
bool PollingTransceiver::do_pre_update ()
|
||||
{
|
||||
// if we are holding off a change then withhold the signal
|
||||
if (m_->retries_ && state () != m_->next_state_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PollingTransceiver::do_sync (bool force_signal)
|
||||
{
|
||||
poll (); // tell sub-classes to update our
|
||||
// state
|
||||
|
||||
// Signal new state if it is directly requested or, what we expected
|
||||
// or, hasn't become what we expected after polls_to_stabilize
|
||||
// polls. Unsolicited changes will be signalled immediately unless
|
||||
// they intervene in a expected sequence where they will be delayed.
|
||||
if (m_->retries_)
|
||||
{
|
||||
--m_->retries_;
|
||||
if (force_signal || state () == m_->next_state_ || !m_->retries_)
|
||||
{
|
||||
// our client wants a signal regardless
|
||||
// or the expected state has arrived
|
||||
// or there are no more retries
|
||||
force_signal = true;
|
||||
}
|
||||
}
|
||||
else if (force_signal || state () != m_->last_signalled_state_)
|
||||
{
|
||||
// here is the normal passive polling path
|
||||
// either our client has requested a state update regardless of change
|
||||
// or sate has changed asynchronously
|
||||
force_signal = true;
|
||||
}
|
||||
|
||||
if (force_signal)
|
||||
{
|
||||
// reset everything, record and signal the current state
|
||||
m_->retries_ = 0;
|
||||
m_->next_state_ = state ();
|
||||
m_->last_signalled_state_ = state ();
|
||||
update_complete ();
|
||||
}
|
||||
}
|
||||
|
||||
void PollingTransceiver::impl::handle_timeout ()
|
||||
{
|
||||
QString message;
|
||||
|
||||
// we must catch all exceptions here since we are called by Qt and
|
||||
// inform our parent of the failure via the offline() message
|
||||
try
|
||||
{
|
||||
self_->do_sync (false);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
self_->offline (message);
|
||||
}
|
||||
}
|
60
PollingTransceiver.hpp
Normal file
@ -0,0 +1,60 @@
|
||||
#ifndef POLLING_TRANSCEIVER_HPP__
|
||||
#define POLLING_TRANSCEIVER_HPP__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "TransceiverBase.hpp"
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
//
|
||||
// Polling Transceiver
|
||||
//
|
||||
// Helper base class that encapsulates the emulation of continuous
|
||||
// update and caching of a transceiver state.
|
||||
//
|
||||
// Collaborations
|
||||
//
|
||||
// Implements the TransceiverBase post action interface and provides
|
||||
// the abstract poll() operation for sub-classes to implement. The
|
||||
// pol operation is invoked every poll_interval milliseconds.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Because some rig interfaces don't immediately update after a state
|
||||
// change request; this class allows a rig a few polls to stabilise
|
||||
// to the requested state before signalling the change. This means
|
||||
// that clients don't see intermediate states that are sometimes
|
||||
// inaccurate, e.g. changing the split TX frequency on Icom rigs
|
||||
// requires a VFO switch and polls while switched will return the
|
||||
// wrong current frequency.
|
||||
//
|
||||
class PollingTransceiver
|
||||
: public TransceiverBase
|
||||
{
|
||||
protected:
|
||||
explicit PollingTransceiver (int poll_interval); // in milliseconds
|
||||
|
||||
public:
|
||||
~PollingTransceiver ();
|
||||
|
||||
protected:
|
||||
void do_sync (bool /* force_signal */) override final;
|
||||
|
||||
// Sub-classes implement this and fetch what they can from the rig
|
||||
// in a non-intrusive manner.
|
||||
virtual void poll () = 0;
|
||||
|
||||
void do_post_start () override final;
|
||||
void do_post_stop () override final;
|
||||
void do_post_frequency (Frequency) override final;
|
||||
void do_post_tx_frequency (Frequency) override final;
|
||||
void do_post_mode (MODE) override final;
|
||||
bool do_pre_update () override final;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
72
Radio.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "Radio.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <QChar>
|
||||
#include <QDebug>
|
||||
#include <QRegExpValidator>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace Radio
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct init
|
||||
{
|
||||
init ()
|
||||
{
|
||||
qRegisterMetaType<Frequency> ("Frequency");
|
||||
|
||||
qRegisterMetaType<Frequencies> ("Frequencies");
|
||||
qRegisterMetaTypeStreamOperators<Frequencies> ("Frequencies");
|
||||
|
||||
qRegisterMetaType<FrequencyDelta> ("FrequencyDelta");
|
||||
}
|
||||
} static_initaializer;
|
||||
|
||||
double constexpr MHz_factor {1.e6};
|
||||
int constexpr frequency_precsion {6};
|
||||
}
|
||||
|
||||
|
||||
Frequency frequency (QVariant const& v, int scale)
|
||||
{
|
||||
return std::llround (v.toDouble () * std::pow (10., scale));
|
||||
}
|
||||
|
||||
FrequencyDelta frequency_delta (QVariant const& v, int scale)
|
||||
{
|
||||
return std::llround (v.toDouble () * std::pow (10., scale));
|
||||
}
|
||||
|
||||
|
||||
QString frequency_MHz_string (Frequency f, QLocale const& locale)
|
||||
{
|
||||
return locale.toString (f / MHz_factor, 'f', frequency_precsion);
|
||||
}
|
||||
|
||||
QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale)
|
||||
{
|
||||
return locale.toString (d / MHz_factor, 'f', frequency_precsion);
|
||||
}
|
||||
|
||||
QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale)
|
||||
{
|
||||
auto f_string = locale.toString (f / MHz_factor, 'f', frequency_precsion);
|
||||
return f_string.insert (f_string.size () - 3, QChar::Nbsp);
|
||||
}
|
||||
|
||||
QString pretty_frequency_MHz_string (double f, int scale, QLocale const& locale)
|
||||
{
|
||||
auto f_string = locale.toString (f / std::pow (10., scale - 6), 'f', frequency_precsion);
|
||||
return f_string.insert (f_string.size () - 3, QChar::Nbsp);
|
||||
}
|
||||
|
||||
QString pretty_frequency_MHz_string (FrequencyDelta d, QLocale const& locale)
|
||||
{
|
||||
auto d_string = locale.toString (d / MHz_factor, 'f', frequency_precsion);
|
||||
return d_string.insert (d_string.size () - 3, QChar::Nbsp);
|
||||
}
|
||||
}
|
47
Radio.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef RADIO_HPP_
|
||||
#define RADIO_HPP_
|
||||
|
||||
#include <QObject>
|
||||
#include <QLocale>
|
||||
#include <QList>
|
||||
|
||||
class QVariant;
|
||||
class QString;
|
||||
|
||||
//
|
||||
// Declarations common to radio software.
|
||||
//
|
||||
|
||||
namespace Radio
|
||||
{
|
||||
//
|
||||
// Frequency types
|
||||
//
|
||||
using Frequency = quint64;
|
||||
using Frequencies = QList<Frequency>;
|
||||
using FrequencyDelta = qint64;
|
||||
|
||||
//
|
||||
// Frequency type conversion.
|
||||
//
|
||||
// QVariant argument is convertible to double and is assumed to
|
||||
// be scaled by (10 ** -scale).
|
||||
//
|
||||
Frequency frequency (QVariant const&, int scale);
|
||||
FrequencyDelta frequency_delta (QVariant const&, int scale);
|
||||
|
||||
//
|
||||
// Frequency type formatting
|
||||
//
|
||||
QString frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (Radio::Frequency);
|
||||
Q_DECLARE_METATYPE (Radio::Frequencies);
|
||||
Q_DECLARE_METATYPE (Radio::FrequencyDelta);
|
||||
|
||||
#endif
|
34
SettingsGroup.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef SETTINGS_GROUP_HPP_
|
||||
#define SETTINGS_GROUP_HPP_
|
||||
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
|
||||
//
|
||||
// Class SettingsGroup
|
||||
//
|
||||
// Simple RAII type class to apply a QSettings group witin a
|
||||
// scope.
|
||||
//
|
||||
class SettingsGroup
|
||||
{
|
||||
public:
|
||||
SettingsGroup (QSettings * settings, QString const& group)
|
||||
: settings_ {settings}
|
||||
{
|
||||
settings_->beginGroup (group);
|
||||
}
|
||||
|
||||
SettingsGroup (SettingsGroup const&) = delete;
|
||||
SettingsGroup& operator = (SettingsGroup const&) = delete;
|
||||
|
||||
~SettingsGroup ()
|
||||
{
|
||||
settings_->endGroup ();
|
||||
}
|
||||
|
||||
private:
|
||||
QSettings * settings_;
|
||||
};
|
||||
|
||||
#endif
|
551
StationList.cpp
Normal file
@ -0,0 +1,551 @@
|
||||
#include "StationList.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QStringList>
|
||||
#include <QMimeData>
|
||||
#include <QDataStream>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "Bands.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct init
|
||||
{
|
||||
init ()
|
||||
{
|
||||
qRegisterMetaType<StationList::Station> ("Station");
|
||||
qRegisterMetaTypeStreamOperators<StationList::Station> ("Station");
|
||||
qRegisterMetaType<StationList::Stations> ("Stations");
|
||||
qRegisterMetaTypeStreamOperators<StationList::Stations> ("Stations");
|
||||
}
|
||||
} static_initializer;
|
||||
}
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
QDebug operator << (QDebug debug, StationList::Station const& station)
|
||||
{
|
||||
debug.nospace () << "Station("
|
||||
<< station.band_name_ << ", "
|
||||
<< station.offset_ << ", "
|
||||
<< station.antenna_description_ << ')';
|
||||
return debug.space ();
|
||||
}
|
||||
#endif
|
||||
|
||||
QDataStream& operator << (QDataStream& os, StationList::Station const& station)
|
||||
{
|
||||
return os << station.band_name_
|
||||
<< station.offset_
|
||||
<< station.antenna_description_;
|
||||
}
|
||||
|
||||
QDataStream& operator >> (QDataStream& is, StationList::Station& station)
|
||||
{
|
||||
return is >> station.band_name_
|
||||
>> station.offset_
|
||||
>> station.antenna_description_;
|
||||
}
|
||||
|
||||
|
||||
class StationList::impl final
|
||||
: public QAbstractTableModel
|
||||
{
|
||||
public:
|
||||
impl (Bands const * bands, Stations stations, QObject * parent)
|
||||
: QAbstractTableModel {parent}
|
||||
, bands_ {bands}
|
||||
, stations_ {stations}
|
||||
{
|
||||
}
|
||||
|
||||
Stations const& stations () const {return stations_;}
|
||||
void assign (Stations);
|
||||
QModelIndex add (Station);
|
||||
FrequencyDelta offset (Frequency) const;
|
||||
|
||||
protected:
|
||||
// Implement the QAbstractTableModel interface.
|
||||
int rowCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
int columnCount (QModelIndex const& parent = QModelIndex {}) const override;
|
||||
Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override;
|
||||
QVariant data (QModelIndex const&, int role) const override;
|
||||
QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override;
|
||||
bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override;
|
||||
bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override;
|
||||
Qt::DropActions supportedDropActions () const override;
|
||||
QStringList mimeTypes () const override;
|
||||
QMimeData * mimeData (QModelIndexList const&) const override;
|
||||
bool dropMimeData (QMimeData const *, Qt::DropAction, int row, int column, QModelIndex const& parent) override;
|
||||
|
||||
private:
|
||||
// Helper method for band validation.
|
||||
QModelIndex first_matching_band (QString const& band_name) const
|
||||
{
|
||||
// find first exact match in bands
|
||||
auto matches = bands_->match (bands_->index (0, 0)
|
||||
, Qt::DisplayRole
|
||||
, band_name
|
||||
, 1
|
||||
, Qt::MatchExactly);
|
||||
return matches.isEmpty () ? QModelIndex {} : matches.first ();
|
||||
}
|
||||
|
||||
static int constexpr num_columns {3};
|
||||
static auto constexpr mime_type = "application/wsjt.antenna-descriptions";
|
||||
|
||||
Bands const * bands_;
|
||||
Stations stations_;
|
||||
};
|
||||
|
||||
StationList::StationList (Bands const * bands, QObject * parent)
|
||||
: StationList {bands, {}, parent}
|
||||
{
|
||||
}
|
||||
|
||||
StationList::StationList (Bands const * bands, Stations stations, QObject * parent)
|
||||
: QSortFilterProxyModel {parent}
|
||||
, m_ {bands, stations, parent}
|
||||
{
|
||||
// setDynamicSortFilter (true);
|
||||
setSourceModel (&*m_);
|
||||
setSortRole (SortRole);
|
||||
}
|
||||
|
||||
StationList::~StationList ()
|
||||
{
|
||||
}
|
||||
|
||||
StationList& StationList::operator = (Stations stations)
|
||||
{
|
||||
m_->assign (stations);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto StationList::stations () const -> Stations
|
||||
{
|
||||
return m_->stations ();
|
||||
}
|
||||
|
||||
QModelIndex StationList::add (Station s)
|
||||
{
|
||||
return mapFromSource (m_->add (s));
|
||||
}
|
||||
|
||||
bool StationList::remove (Station s)
|
||||
{
|
||||
auto row = m_->stations ().indexOf (s);
|
||||
|
||||
if (0 > row)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeRow (row);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return lhs.row () > rhs.row ();
|
||||
}
|
||||
}
|
||||
|
||||
bool StationList::removeDisjointRows (QModelIndexList rows)
|
||||
{
|
||||
bool result {true};
|
||||
|
||||
// We must work with source model indexes because we don't want row
|
||||
// removes to invalidate model indexes we haven't yet processed. We
|
||||
// achieve that by processing them in decending row order.
|
||||
for (int r = 0; r < rows.size (); ++r)
|
||||
{
|
||||
rows[r] = mapToSource (rows[r]);
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
qSort (rows.begin (), rows.end (), row_is_higher);
|
||||
Q_FOREACH (auto index, rows)
|
||||
{
|
||||
if (result && !m_->removeRow (index.row ()))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto StationList::offset (Frequency f) const -> FrequencyDelta
|
||||
{
|
||||
return m_->offset (f);
|
||||
}
|
||||
|
||||
|
||||
void StationList::impl::assign (Stations stations)
|
||||
{
|
||||
beginResetModel ();
|
||||
std::swap (stations_, stations);
|
||||
endResetModel ();
|
||||
}
|
||||
|
||||
QModelIndex StationList::impl::add (Station s)
|
||||
{
|
||||
// Any band that isn't in the list may be added
|
||||
if (!stations_.contains (s))
|
||||
{
|
||||
auto row = stations_.size ();
|
||||
|
||||
beginInsertRows (QModelIndex {}, row, row);
|
||||
stations_.append (s);
|
||||
endInsertRows ();
|
||||
|
||||
return index (row, 0);
|
||||
}
|
||||
|
||||
return QModelIndex {};
|
||||
}
|
||||
|
||||
auto StationList::impl::offset (Frequency f) const -> FrequencyDelta
|
||||
{
|
||||
// Lookup band for frequency
|
||||
auto band_index = bands_->find (f);
|
||||
if (band_index.isValid ())
|
||||
{
|
||||
auto band_name = band_index.data ().toString ();
|
||||
|
||||
// Lookup station for band
|
||||
for (int i = 0; i < stations ().size (); ++i)
|
||||
{
|
||||
if (stations_[i].band_name_ == band_name)
|
||||
{
|
||||
return stations_[i].offset_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // no offset
|
||||
}
|
||||
|
||||
int StationList::impl::rowCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : stations_.size ();
|
||||
}
|
||||
|
||||
int StationList::impl::columnCount (QModelIndex const& parent) const
|
||||
{
|
||||
return parent.isValid () ? 0 : num_columns;
|
||||
}
|
||||
|
||||
Qt::ItemFlags StationList::impl::flags (QModelIndex const& index) const
|
||||
{
|
||||
auto result = QAbstractTableModel::flags (index);
|
||||
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
|
||||
if (index.isValid ()
|
||||
&& row < stations_.size ()
|
||||
&& column < num_columns)
|
||||
{
|
||||
if (2 == column)
|
||||
{
|
||||
result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= Qt::ItemIsEditable | Qt::ItemIsDropEnabled;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result |= Qt::ItemIsDropEnabled;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QVariant StationList::impl::data (QModelIndex const& index, int role) const
|
||||
{
|
||||
QVariant item;
|
||||
|
||||
auto row = index.row ();
|
||||
auto column = index.column ();
|
||||
|
||||
if (index.isValid ()
|
||||
&& row < stations_.size ())
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
case 0: // band name
|
||||
switch (role)
|
||||
{
|
||||
case SortRole:
|
||||
{
|
||||
// Lookup band.
|
||||
auto band_index = first_matching_band (stations_.at (row).band_name_);
|
||||
// Use the sort role value of the band.
|
||||
item = band_index.data (Bands::SortRole);
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = stations_.at (row).band_name_;
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Band name");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignHCenter + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // frequency offset
|
||||
{
|
||||
auto frequency_offset = stations_.at (row).offset_;
|
||||
switch (role)
|
||||
{
|
||||
case Qt::AccessibleTextRole:
|
||||
item = frequency_offset;
|
||||
break;
|
||||
|
||||
case SortRole:
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
item = frequency_offset;
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Frequency offset");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignRight + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // antenna description
|
||||
switch (role)
|
||||
{
|
||||
case SortRole:
|
||||
case Qt::EditRole:
|
||||
case Qt::DisplayRole:
|
||||
case Qt::AccessibleTextRole:
|
||||
item = stations_.at (row).antenna_description_;
|
||||
break;
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
case Qt::AccessibleDescriptionRole:
|
||||
item = tr ("Antenna description");
|
||||
break;
|
||||
|
||||
case Qt::TextAlignmentRole:
|
||||
item = Qt::AlignLeft + Qt::AlignVCenter;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
QVariant StationList::impl::headerData (int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QVariant header;
|
||||
|
||||
if (Qt::DisplayRole == role && Qt::Horizontal == orientation)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case 0: header = tr ("Band"); break;
|
||||
case 1: header = tr ("Offset"); break;
|
||||
case 2: header = tr ("Antenna Description"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
header = QAbstractTableModel::headerData (section, orientation, role);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
bool StationList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role)
|
||||
{
|
||||
bool changed {false};
|
||||
|
||||
auto row = model_index.row ();
|
||||
auto size = stations_.size ();
|
||||
if (model_index.isValid ()
|
||||
&& Qt::EditRole == role
|
||||
&& row < size)
|
||||
{
|
||||
QVector<int> roles;
|
||||
roles << role;
|
||||
|
||||
switch (model_index.column ())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Check if band name is valid.
|
||||
auto band_index = first_matching_band (value.toString ());
|
||||
if (band_index.isValid ())
|
||||
{
|
||||
stations_[row].band_name_ = band_index.data ().toString ();
|
||||
Q_EMIT dataChanged (model_index, model_index, roles);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
stations_[row].offset_ = value.value<FrequencyDelta> ();
|
||||
Q_EMIT dataChanged (model_index, model_index, roles);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
stations_[row].antenna_description_ = value.toString ();
|
||||
Q_EMIT dataChanged (model_index, model_index, roles);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool StationList::impl::removeRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count && (row + count) <= rowCount (parent))
|
||||
{
|
||||
beginRemoveRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
stations_.removeAt (row);
|
||||
}
|
||||
endRemoveRows ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StationList::impl::insertRows (int row, int count, QModelIndex const& parent)
|
||||
{
|
||||
if (0 < count)
|
||||
{
|
||||
beginInsertRows (parent, row, row + count - 1);
|
||||
for (auto r = 0; r < count; ++r)
|
||||
{
|
||||
stations_.insert (row, Station ());
|
||||
}
|
||||
endInsertRows ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::DropActions StationList::impl::supportedDropActions () const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
QStringList StationList::impl::mimeTypes () const
|
||||
{
|
||||
QStringList types;
|
||||
types << mime_type;
|
||||
types << "application/wsjt.Frequencies";
|
||||
return types;
|
||||
}
|
||||
|
||||
QMimeData * StationList::impl::mimeData (QModelIndexList const& items) const
|
||||
{
|
||||
QMimeData * mime_data = new QMimeData {};
|
||||
QByteArray encoded_data;
|
||||
QDataStream stream {&encoded_data, QIODevice::WriteOnly};
|
||||
|
||||
Q_FOREACH (auto const& item, items)
|
||||
{
|
||||
if (item.isValid ())
|
||||
{
|
||||
stream << QString {data (item, Qt::DisplayRole).toString ()};
|
||||
}
|
||||
}
|
||||
|
||||
mime_data->setData (mime_type, encoded_data);
|
||||
return mime_data;
|
||||
}
|
||||
|
||||
bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction action, int /* row */, int /* column */, QModelIndex const& parent)
|
||||
{
|
||||
if (Qt::IgnoreAction == action)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parent.isValid ()
|
||||
&& 2 == parent.column ()
|
||||
&& data->hasFormat (mime_type))
|
||||
{
|
||||
QByteArray encoded_data {data->data (mime_type)};
|
||||
QDataStream stream {&encoded_data, QIODevice::ReadOnly};
|
||||
auto dest_index = parent;
|
||||
while (!stream.atEnd ())
|
||||
{
|
||||
QString text;
|
||||
stream >> text;
|
||||
setData (dest_index, text);
|
||||
dest_index = index (dest_index.row () + 1, dest_index.column (), QModelIndex {});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (data->hasFormat ("application/wsjt.Frequencies"))
|
||||
{
|
||||
QByteArray encoded_data {data->data ("application/wsjt.Frequencies")};
|
||||
QDataStream stream {&encoded_data, QIODevice::ReadOnly};
|
||||
while (!stream.atEnd ())
|
||||
{
|
||||
QString frequency_string;
|
||||
stream >> frequency_string;
|
||||
auto frequency = Radio::frequency (frequency_string, 0);
|
||||
auto band_index = bands_->find (frequency);
|
||||
if (stations_.cend () == std::find_if (stations_.cbegin ()
|
||||
, stations_.cend ()
|
||||
, [&band_index] (Station const& s) {return s.band_name_ == band_index.data ().toString ();}))
|
||||
{
|
||||
add (Station {band_index.data ().toString (), 0, QString {}});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
96
StationList.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef STATION_LIST_HPP__
|
||||
#define STATION_LIST_HPP__
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
class Bands;
|
||||
|
||||
//
|
||||
// Class StationList
|
||||
//
|
||||
// Encapsulates information about a collection of unique operating
|
||||
// stations per band. The implementation is a table model with the
|
||||
// first column being the unique (within the table rows) band name
|
||||
// and, the second the frequency offset for transverter usage and,
|
||||
// the third the antenna description. All are editable.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Stores internally an unordered table of bands.
|
||||
//
|
||||
// If an ordered representaion is required then wrapping with an
|
||||
// appropriate proxy model is sufficient
|
||||
// e.g. QSortFilterProxyModel. A custom SortRole role is provided for
|
||||
// the band name column which returns a numeric value (Bands lower
|
||||
// frequency limit) which gives a strict frequency ordering by band.
|
||||
//
|
||||
// Collaborations
|
||||
//
|
||||
// Implements the QAbstractTableModel interface for a grid of bands
|
||||
// with offset frequencies and antenna descriptions.
|
||||
//
|
||||
// Uses the QAbstractItemModel interface of the bands model to lookup
|
||||
// band information.
|
||||
//
|
||||
class StationList final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
using FrequencyDelta = Radio::FrequencyDelta;
|
||||
|
||||
//
|
||||
// Struct Station
|
||||
//
|
||||
// Aggregation of fields that describe a radio station on a band.
|
||||
//
|
||||
struct Station
|
||||
{
|
||||
QString band_name_;
|
||||
FrequencyDelta offset_;
|
||||
QString antenna_description_;
|
||||
};
|
||||
|
||||
using Stations = QList<Station>;
|
||||
|
||||
explicit StationList (Bands const * bands, QObject * parent = nullptr);
|
||||
explicit StationList (Bands const * bands, Stations, QObject * parent = nullptr);
|
||||
~StationList ();
|
||||
|
||||
// Load and store contents.
|
||||
StationList& operator = (Stations);
|
||||
Stations stations () const;
|
||||
|
||||
//
|
||||
// Model API
|
||||
//
|
||||
QModelIndex add (Station); // Add a new Station
|
||||
bool remove (Station); // Remove a Station
|
||||
bool removeDisjointRows (QModelIndexList); // Remove one or more stations
|
||||
FrequencyDelta offset (Frequency) const; // Return the offset to be used for a Frequency
|
||||
|
||||
// Custom sort role.
|
||||
static int constexpr SortRole = Qt::UserRole;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
// Station equivalence is based on band name alone.
|
||||
inline
|
||||
bool operator == (StationList::Station const& lhs, StationList::Station const& rhs)
|
||||
{
|
||||
return lhs.band_name_ == rhs.band_name_;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (StationList::Station);
|
||||
Q_DECLARE_METATYPE (StationList::Stations);
|
||||
|
||||
#endif
|
470
TestConfiguration.cpp
Normal file
@ -0,0 +1,470 @@
|
||||
#include "TestConfiguration.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSettings>
|
||||
#include <QMainWindow>
|
||||
#include <QLabel>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QLineEdit>
|
||||
#include <QDebug>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "Bands.hpp"
|
||||
#include "FrequencyList.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "LiveFrequencyValidator.hpp"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "ui_TestConfiguration.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const title = "Configuration Test v" WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MAJOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MINOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_PATCH) ", " WSJTX_STRINGIZE (SVNVERSION);
|
||||
|
||||
// these undocumented flag values when stored in (Qt::UserRole - 1)
|
||||
// of a ComboBox item model index allow the item to be enabled or
|
||||
// disabled
|
||||
int const combo_box_item_enabled (32 | 1);
|
||||
int const combo_box_item_disabled (0);
|
||||
|
||||
auto LCD_error = "-- Error --";
|
||||
}
|
||||
|
||||
class status_bar_frequency final
|
||||
: public QLabel
|
||||
{
|
||||
public:
|
||||
status_bar_frequency (QString const& prefix, QWidget * parent = 0)
|
||||
: QLabel {parent}
|
||||
, prefix_ {prefix}
|
||||
{
|
||||
}
|
||||
|
||||
void setText (QString const& text)
|
||||
{
|
||||
QLabel::setText (prefix_ + " Frequency: " + text);
|
||||
}
|
||||
|
||||
private:
|
||||
QString prefix_;
|
||||
};
|
||||
|
||||
class TestConfiguration::impl final
|
||||
: public QMainWindow
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
explicit impl (QString const& instance_key, QSettings *, QWidget * parent = nullptr);
|
||||
|
||||
void closeEvent (QCloseEvent *) override;
|
||||
|
||||
Q_SIGNAL void new_frequency (Frequency) const;
|
||||
Q_SIGNAL void new_tx_frequency (Frequency = 0, bool rationalise_mode = true) const;
|
||||
|
||||
private:
|
||||
Q_SLOT void on_configuration_action_triggered ();
|
||||
Q_SLOT void on_band_combo_box_activated (int);
|
||||
Q_SLOT void on_mode_combo_box_activated (int);
|
||||
Q_SLOT void on_PTT_push_button_clicked (bool);
|
||||
Q_SLOT void on_split_push_button_clicked (bool);
|
||||
Q_SLOT void on_TX_offset_spin_box_valueChanged (int);
|
||||
Q_SLOT void on_sync_push_button_clicked (bool);
|
||||
|
||||
Q_SLOT void handle_transceiver_update (Transceiver::TransceiverState);
|
||||
Q_SLOT void handle_transceiver_failure (QString);
|
||||
|
||||
void information_message_box (QString const& reason, QString const& detail);
|
||||
void read_settings ();
|
||||
void write_settings ();
|
||||
void load_models ();
|
||||
void sync_rig ();
|
||||
void band_changed (Frequency);
|
||||
void frequency_changed (Frequency);
|
||||
|
||||
Ui::test_configuration_main_window * ui_;
|
||||
QSettings * settings_;
|
||||
QString callsign_;
|
||||
Configuration configuration_dialog_;
|
||||
bool updating_models_; // hold off UI reaction while adjusting models
|
||||
bool band_edited_;
|
||||
|
||||
Transceiver::TransceiverState rig_state_;
|
||||
|
||||
Frequency desired_frequency_;
|
||||
|
||||
status_bar_frequency RX_frequency_;
|
||||
status_bar_frequency TX_frequency_;
|
||||
};
|
||||
|
||||
#include "TestConfiguration.moc"
|
||||
|
||||
TestConfiguration::TestConfiguration (QString const& instance_key, QSettings * settings, QWidget * parent)
|
||||
: m_ {instance_key, settings, parent}
|
||||
{
|
||||
}
|
||||
|
||||
TestConfiguration::~TestConfiguration ()
|
||||
{
|
||||
}
|
||||
|
||||
TestConfiguration::impl::impl (QString const& instance_key, QSettings * settings, QWidget * parent)
|
||||
: QMainWindow {parent}
|
||||
, ui_ {new Ui::test_configuration_main_window}
|
||||
, settings_ {settings}
|
||||
, configuration_dialog_ {instance_key, settings, this}
|
||||
, updating_models_ {false}
|
||||
, band_edited_ {false}
|
||||
, desired_frequency_ {0u}
|
||||
, RX_frequency_ {"RX"}
|
||||
, TX_frequency_ {"TX"}
|
||||
{
|
||||
ui_->setupUi (this);
|
||||
|
||||
setWindowTitle (QApplication::applicationName () + " - " + title);
|
||||
|
||||
// mode "Unknown" is display only
|
||||
ui_->mode_combo_box->setItemData (ui_->mode_combo_box->findText ("Unknown"), combo_box_item_disabled, Qt::UserRole - 1);
|
||||
|
||||
// setup status bar widgets
|
||||
statusBar ()->insertPermanentWidget (0, &TX_frequency_);
|
||||
statusBar ()->insertPermanentWidget (0, &RX_frequency_);
|
||||
|
||||
// assign push button ids
|
||||
ui_->TX_button_group->setId (ui_->vfo_0_TX_push_button, 0);
|
||||
ui_->TX_button_group->setId (ui_->vfo_1_TX_push_button, 1);
|
||||
|
||||
// enable live band combo box entry validation and action
|
||||
auto band_validator = new LiveFrequencyValidator {ui_->band_combo_box
|
||||
, configuration_dialog_.bands ()
|
||||
, configuration_dialog_.frequencies ()
|
||||
, this};
|
||||
ui_->band_combo_box->setValidator (band_validator);
|
||||
connect (band_validator, &LiveFrequencyValidator::valid, this, &TestConfiguration::impl::band_changed);
|
||||
connect (ui_->band_combo_box->lineEdit (), &QLineEdit::textEdited, [this] (QString const&) {band_edited_ = true;});
|
||||
|
||||
// hook up band data model
|
||||
ui_->band_combo_box->setModel (configuration_dialog_.frequencies ());
|
||||
|
||||
// combo box drop downs are limited to the drop down selector width,
|
||||
// this almost random increase improves the situation
|
||||
ui_->band_combo_box->view ()->setMinimumWidth (ui_->band_combo_box->view ()->sizeHintForColumn (0) + 10);
|
||||
|
||||
// hook up configuration signals
|
||||
connect (&configuration_dialog_, &Configuration::transceiver_update, this, &TestConfiguration::impl::handle_transceiver_update);
|
||||
connect (&configuration_dialog_, &Configuration::transceiver_failure, this, &TestConfiguration::impl::handle_transceiver_failure);
|
||||
|
||||
// hook up configuration slots
|
||||
connect (this, &TestConfiguration::impl::new_frequency, &configuration_dialog_, &Configuration::transceiver_frequency);
|
||||
connect (this, &TestConfiguration::impl::new_tx_frequency, &configuration_dialog_, &Configuration::transceiver_tx_frequency);
|
||||
|
||||
load_models ();
|
||||
|
||||
read_settings ();
|
||||
|
||||
show ();
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::closeEvent (QCloseEvent * e)
|
||||
{
|
||||
write_settings ();
|
||||
|
||||
QMainWindow::closeEvent (e);
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_configuration_action_triggered ()
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_configuration_action_triggered";
|
||||
if (QDialog::Accepted == configuration_dialog_.exec ())
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_configuration_action_triggered: Configuration changed";
|
||||
qDebug () << "TestConfiguration::on_configuration_action_triggered: rig is" << configuration_dialog_.rig_name ();
|
||||
|
||||
if (configuration_dialog_.restart_audio_input ())
|
||||
{
|
||||
qDebug () << "Audio Device Changes - Configuration changes require an audio input device to be restarted";
|
||||
}
|
||||
if (configuration_dialog_.restart_audio_output ())
|
||||
{
|
||||
qDebug () << "Audio Device Changes - Configuration changes require an audio output device to be restarted";
|
||||
}
|
||||
|
||||
load_models ();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_configuration_action_triggered: Confiugration changes cancelled";
|
||||
}
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_band_combo_box_activated (int index)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_band_combo_box_activated: " << ui_->band_combo_box->currentText ();
|
||||
|
||||
auto model = configuration_dialog_.frequencies ();
|
||||
auto value = model->data (model->index (index, 2), Qt::DisplayRole).toString ();
|
||||
|
||||
if (configuration_dialog_.bands ()->data (QModelIndex {}).toString () == value)
|
||||
{
|
||||
ui_->band_combo_box->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->band_combo_box->lineEdit ()->setStyleSheet ({});
|
||||
}
|
||||
|
||||
ui_->band_combo_box->setCurrentText (value);
|
||||
|
||||
auto f = model->data (model->index (index, 0), Qt::UserRole + 1).value<Frequency> ();
|
||||
|
||||
band_edited_ = true;
|
||||
band_changed (f);
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::band_changed (Frequency f)
|
||||
{
|
||||
if (band_edited_)
|
||||
{
|
||||
band_edited_ = false;
|
||||
frequency_changed (f);
|
||||
sync_rig ();
|
||||
}
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::frequency_changed (Frequency f)
|
||||
{
|
||||
desired_frequency_ = f;
|
||||
|
||||
// lookup band
|
||||
auto bands_model = configuration_dialog_.bands ();
|
||||
ui_->band_combo_box->setCurrentText (bands_model->data (bands_model->find (f)).toString ());
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_sync_push_button_clicked (bool /* checked */)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_sync_push_button_clicked";
|
||||
|
||||
auto model = configuration_dialog_.frequencies ();
|
||||
auto model_index = model->index (ui_->band_combo_box->currentIndex (), 0);
|
||||
desired_frequency_ = model->data (model_index, Qt::UserRole + 1).value<Frequency> ();
|
||||
|
||||
sync_rig ();
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_mode_combo_box_activated (int index)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_vfo_A_mode_combo_box_activated: " << static_cast<Transceiver::MODE> (index);
|
||||
|
||||
// reset combo box back to current mode and let status update do the actual change
|
||||
ui_->mode_combo_box->setCurrentIndex (rig_state_.mode ());
|
||||
|
||||
Q_EMIT configuration_dialog_.transceiver_mode (static_cast<Transceiver::MODE> (index));
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_TX_offset_spin_box_valueChanged (int value)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_TX_offset_spin_box_editingFinished: " << value;
|
||||
|
||||
Q_EMIT new_tx_frequency (rig_state_.frequency () + value);
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_PTT_push_button_clicked (bool checked)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_PTT_push_button_clicked: " << (checked ? "true" : "false");
|
||||
|
||||
// reset button and let status update do the actual checking
|
||||
ui_->PTT_push_button->setChecked (rig_state_.ptt ());
|
||||
|
||||
Q_EMIT configuration_dialog_.transceiver_ptt (checked);
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::on_split_push_button_clicked (bool checked)
|
||||
{
|
||||
qDebug () << "TestConfiguration::on_split_push_button_clicked: " << (checked ? "true" : "false");
|
||||
|
||||
// reset button and let status update do the actual checking
|
||||
ui_->split_push_button->setChecked (rig_state_.split ());
|
||||
|
||||
if (checked)
|
||||
{
|
||||
Q_EMIT new_tx_frequency (rig_state_.frequency () + ui_->TX_offset_spin_box->value ());
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT new_tx_frequency ();
|
||||
}
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::sync_rig ()
|
||||
{
|
||||
if (!updating_models_)
|
||||
{
|
||||
if (configuration_dialog_.transceiver_online (true))
|
||||
{
|
||||
if (configuration_dialog_.split_mode ())
|
||||
{
|
||||
Q_EMIT new_frequency (desired_frequency_);
|
||||
Q_EMIT new_tx_frequency (desired_frequency_ + ui_->TX_offset_spin_box->value ());
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT new_frequency (desired_frequency_);
|
||||
Q_EMIT new_tx_frequency ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::handle_transceiver_update (Transceiver::TransceiverState s)
|
||||
{
|
||||
rig_state_ = s;
|
||||
|
||||
auto model = configuration_dialog_.frequencies ();
|
||||
bool valid {false};
|
||||
for (int row = 0; row < model->rowCount (); ++row)
|
||||
{
|
||||
auto working_frequency = model->data (model->index (row, 0), Qt::UserRole + 1).value<Frequency> ();
|
||||
if (std::abs (static_cast<Radio::FrequencyDelta> (working_frequency - s.frequency ())) < 10000)
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
if (!valid)
|
||||
{
|
||||
ui_->vfo_0_lcd_number->setStyleSheet ("QLCDNumber {background-color: red;}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->vfo_0_lcd_number->setStyleSheet (QString {});
|
||||
}
|
||||
|
||||
ui_->vfo_0_lcd_number->display (Radio::pretty_frequency_MHz_string (s.frequency ()));
|
||||
|
||||
if (s.split ())
|
||||
{
|
||||
ui_->vfo_1_lcd_number->display (Radio::pretty_frequency_MHz_string (s.tx_frequency ()));
|
||||
|
||||
valid = false;
|
||||
for (int row = 0; row < model->rowCount (); ++row)
|
||||
{
|
||||
auto working_frequency = model->data (model->index (row, 0), Qt::UserRole + 1).value<Frequency> ();
|
||||
if (std::abs (static_cast<Radio::FrequencyDelta> (working_frequency - s.tx_frequency ())) < 10000)
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
if (!valid)
|
||||
{
|
||||
ui_->vfo_1_lcd_number->setStyleSheet ("QLCDNumber {background-color: red;}");
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->vfo_1_lcd_number->setStyleSheet (QString {});
|
||||
}
|
||||
ui_->vfo_1_lcd_number->show ();
|
||||
ui_->vfo_1_TX_push_button->show ();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->vfo_1_lcd_number->hide ();
|
||||
ui_->vfo_1_TX_push_button->hide ();
|
||||
}
|
||||
|
||||
frequency_changed (s.frequency ());
|
||||
|
||||
ui_->radio_widget->setEnabled (s.online ());
|
||||
|
||||
ui_->mode_combo_box->setCurrentIndex (s.mode ());
|
||||
|
||||
RX_frequency_.setText (Radio::pretty_frequency_MHz_string (s.frequency ()));
|
||||
TX_frequency_.setText (Radio::pretty_frequency_MHz_string (s.split () ? s.tx_frequency () : s.frequency ()));
|
||||
|
||||
ui_->TX_button_group->button (s.split ())->setChecked (true);
|
||||
|
||||
ui_->PTT_push_button->setChecked (s.ptt ());
|
||||
|
||||
ui_->split_push_button->setChecked (s.split ());
|
||||
|
||||
ui_->radio_widget->setEnabled (s.online ());
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::handle_transceiver_failure (QString reason)
|
||||
{
|
||||
ui_->radio_widget->setEnabled (false);
|
||||
|
||||
ui_->vfo_0_lcd_number->display (LCD_error);
|
||||
ui_->vfo_1_lcd_number->display (LCD_error);
|
||||
information_message_box ("Rig failure", reason);
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::read_settings ()
|
||||
{
|
||||
settings_->beginGroup ("TestConfiguration");
|
||||
resize (settings_->value ("window/size", size ()).toSize ());
|
||||
move (settings_->value ("window/pos", pos ()).toPoint ());
|
||||
restoreState (settings_->value ("window/state", saveState ()).toByteArray ());
|
||||
ui_->band_combo_box->setCurrentText (settings_->value ("Band").toString ());
|
||||
settings_->endGroup ();
|
||||
|
||||
settings_->beginGroup ("Configuration");
|
||||
callsign_ = settings_->value ("MyCall").toString ();
|
||||
settings_->endGroup ();
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::write_settings ()
|
||||
{
|
||||
settings_->beginGroup ("TestConfiguration");
|
||||
settings_->setValue ("window/size", size ());
|
||||
settings_->setValue ("window/pos", pos ());
|
||||
settings_->setValue ("window/state", saveState ());
|
||||
settings_->setValue ("Band", ui_->band_combo_box->currentText ());
|
||||
settings_->endGroup ();
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::load_models ()
|
||||
{
|
||||
updating_models_ = true;
|
||||
|
||||
ui_->frequency_group_box->setTitle (configuration_dialog_.rig_name ());
|
||||
// if (auto rig = configuration_dialog_.rig (false)) // don't open radio
|
||||
if (configuration_dialog_.transceiver_online (false)) // don't open radio
|
||||
{
|
||||
Q_EMIT configuration_dialog_.sync_transceiver (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_->radio_widget->setEnabled (false);
|
||||
ui_->vfo_0_lcd_number->display (Radio::pretty_frequency_MHz_string (static_cast<Frequency> (0)));
|
||||
ui_->vfo_1_lcd_number->display (Radio::pretty_frequency_MHz_string (static_cast<Frequency> (0)));
|
||||
}
|
||||
|
||||
if (!configuration_dialog_.split_mode ())
|
||||
{
|
||||
ui_->vfo_1_lcd_number->hide ();
|
||||
ui_->vfo_1_TX_push_button->hide ();
|
||||
}
|
||||
|
||||
updating_models_ = false;
|
||||
}
|
||||
|
||||
void TestConfiguration::impl::information_message_box (QString const& reason, QString const& detail)
|
||||
{
|
||||
qDebug () << "TestConfiguration::information_message_box: reason =" << reason << "detail =" << detail;
|
||||
QMessageBox mb;
|
||||
mb.setWindowFlags (mb.windowFlags () | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint);
|
||||
mb.setText (reason);
|
||||
if (!detail.isEmpty ())
|
||||
{
|
||||
mb.setDetailedText (detail);
|
||||
}
|
||||
mb.setStandardButtons (QMessageBox::Ok);
|
||||
mb.setDefaultButton (QMessageBox::Ok);
|
||||
mb.setIcon (QMessageBox::Information);
|
||||
mb.exec ();
|
||||
}
|
21
TestConfiguration.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef TEST_CONFIGURATION_HPP_
|
||||
#define TEST_CONFIGURATION_HPP_
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
class QSettings;
|
||||
class QWidget;
|
||||
|
||||
class TestConfiguration final
|
||||
{
|
||||
public:
|
||||
explicit TestConfiguration (QString const& instance_key, QSettings *, QWidget * parent = nullptr);
|
||||
~TestConfiguration ();
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
627
TestConfiguration.ui
Normal file
@ -0,0 +1,627 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>test_configuration_main_window</class>
|
||||
<widget class="QMainWindow" name="test_configuration_main_window">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>550</width>
|
||||
<height>227</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>550</width>
|
||||
<height>227</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Configuration Test</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="radio_widget" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="frequency_group_box">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Frequency</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLCDNumber" name="vfo_0_lcd_number">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="digitCount">
|
||||
<number>12</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLCDNumber" name="vfo_1_lcd_number">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="digitCount">
|
||||
<number>12</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="vfo_0_TX_push_button">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
border: 2px solid #8f8f91;
|
||||
border-radius: 6px;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #f6f7fa, stop: 1 #dadbde);
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #ff2a2e, stop: 1 #ffc6ca);
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none; /* no border for a flat push button */
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border-color: navy; /* make the default button prominent */
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">TX_button_group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="vfo_1_TX_push_button">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
border: 2px solid #8f8f91;
|
||||
border-radius: 6px;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #f6f7fa, stop: 1 #dadbde);
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #ff2a2e, stop: 1 #ffc6ca);
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none; /* no border for a flat push button */
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border-color: navy; /* make the default button prominent */
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">TX_button_group</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="mode_group_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="mode_combo_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Unknown</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CW</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>CW_R</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>USB</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>LSB</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FSK</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FSK_R</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DIG_U</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DIG_L</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>AM</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>FM</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>DIG_FM</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="PTT_push_button">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
border: 2px solid #8f8f91;
|
||||
border-radius: 6px;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #9aff9a, stop: 1 #2aff2e);
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #ff2b2e, stop: 1 #ff979a);
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none; /* no border for a flat push button */
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border-color: navy; /* make the default button prominent */
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PTT</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="split_push_button">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
border: 2px solid #8f8f91;
|
||||
border-radius: 6px;
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #9aff9a, stop: 1 #2aff2e);
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||
stop: 0 #ff2b2e, stop: 1 #ff979a);
|
||||
}
|
||||
|
||||
QPushButton:flat {
|
||||
border: none; /* no border for a flat push button */
|
||||
}
|
||||
|
||||
QPushButton:default {
|
||||
border-color: navy; /* make the default button prominent */
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Split</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="TX_offset_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Split Offset:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>TX_offset_spin_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="TX_offset_spin_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> Hz</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-10000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="band_label">
|
||||
<property name="text">
|
||||
<string>Band:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>band_combo_box</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="band_combo_box">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::NoInsert</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="sync_push_button">
|
||||
<property name="text">
|
||||
<string>Sync</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menu_bar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>550</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="file_menu">
|
||||
<property name="title">
|
||||
<string>&File</string>
|
||||
</property>
|
||||
<addaction name="exit_action"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="options_menu">
|
||||
<property name="title">
|
||||
<string>&Options</string>
|
||||
</property>
|
||||
<addaction name="configuration_action"/>
|
||||
</widget>
|
||||
<addaction name="file_menu"/>
|
||||
<addaction name="options_menu"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="status_bar"/>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
<action name="exit_action">
|
||||
<property name="text">
|
||||
<string>E&xit</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="configuration_action">
|
||||
<property name="text">
|
||||
<string>&Configuration</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>exit_action</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>test_configuration_main_window</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>261</x>
|
||||
<y>214</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="TX_button_group"/>
|
||||
</buttongroups>
|
||||
</ui>
|
118
TraceFile.cpp
Normal file
@ -0,0 +1,118 @@
|
||||
#include "TraceFile.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QMessageLogContext>
|
||||
#include <QDateTime>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
QMutex lock;
|
||||
}
|
||||
|
||||
class TraceFile::impl
|
||||
{
|
||||
public:
|
||||
impl (QString const& trace_file_path);
|
||||
~impl ();
|
||||
|
||||
// no copying
|
||||
impl (impl const&) = delete;
|
||||
impl& operator = (impl const&) = delete;
|
||||
|
||||
private:
|
||||
// write Qt messages to the diagnostic log file
|
||||
static void message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg);
|
||||
|
||||
QFile file_;
|
||||
QTextStream stream_;
|
||||
QTextStream * original_stream_;
|
||||
QtMessageHandler original_handler_;
|
||||
static QTextStream * current_stream_;
|
||||
};
|
||||
|
||||
QTextStream * TraceFile::impl::current_stream_;
|
||||
|
||||
|
||||
// delegate to implementation class
|
||||
TraceFile::TraceFile (QString const& trace_file_path)
|
||||
: m_ {trace_file_path}
|
||||
{
|
||||
}
|
||||
|
||||
TraceFile::~TraceFile ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
TraceFile::impl::impl (QString const& trace_file_path)
|
||||
: file_ {trace_file_path}
|
||||
, original_stream_ {current_stream_}
|
||||
, original_handler_ {nullptr}
|
||||
{
|
||||
// if the log file is writeable; initialise diagnostic logging to it
|
||||
// for append and hook up the Qt global message handler
|
||||
if (file_.open (QFile::WriteOnly | QFile::Append | QFile::Text))
|
||||
{
|
||||
stream_.setDevice (&file_);
|
||||
current_stream_ = &stream_;
|
||||
original_handler_ = qInstallMessageHandler (message_handler);
|
||||
}
|
||||
}
|
||||
|
||||
TraceFile::impl::~impl ()
|
||||
{
|
||||
// unhook our message handler before the stream and file are destroyed
|
||||
if (original_handler_)
|
||||
{
|
||||
qInstallMessageHandler (original_handler_);
|
||||
}
|
||||
current_stream_ = original_stream_; // revert to prior stream
|
||||
}
|
||||
|
||||
// write Qt messages to the diagnostic log file
|
||||
void TraceFile::impl::message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
|
||||
{
|
||||
char const * severity;
|
||||
switch (type)
|
||||
{
|
||||
case QtDebugMsg:
|
||||
severity = "Debug";
|
||||
break;
|
||||
|
||||
case QtWarningMsg:
|
||||
severity = "Warning";
|
||||
break;
|
||||
|
||||
case QtFatalMsg:
|
||||
severity = "Fatal";
|
||||
break;
|
||||
|
||||
default:
|
||||
severity = "Critical";
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
// guard against multiple threads with overlapping messages
|
||||
QMutexLocker guard (&lock);
|
||||
Q_ASSERT_X (current_stream_, "TraceFile:message_handler", "no stream to write to");
|
||||
*current_stream_
|
||||
<< QDateTime::currentDateTimeUtc ().toString ()
|
||||
<< '(' << context.file << ':' << context.line /* << ", " << context.function */ << ')'
|
||||
<< severity << ": " << msg.trimmed () << endl;
|
||||
}
|
||||
|
||||
if (QtFatalMsg == type)
|
||||
{
|
||||
throw std::runtime_error {"Fatal Qt Error"};
|
||||
}
|
||||
}
|
23
TraceFile.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef TRACE_FILE_HPP_
|
||||
#define TRACE_FILE_HPP_
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
|
||||
class TraceFile final
|
||||
{
|
||||
public:
|
||||
explicit TraceFile (QString const& TraceFile_file_path);
|
||||
~TraceFile ();
|
||||
|
||||
// copying not allowed
|
||||
TraceFile (TraceFile const&) = delete;
|
||||
TraceFile& operator = (TraceFile const&) = delete;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
51
Transceiver.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "Transceiver.hpp"
|
||||
|
||||
#include "moc_Transceiver.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct init
|
||||
{
|
||||
init ()
|
||||
{
|
||||
qRegisterMetaType<Transceiver::TransceiverState> ("Transceiver::TransceiverState");
|
||||
qRegisterMetaType<Transceiver::MODE> ("Transceiver::MODE");
|
||||
}
|
||||
} static_initialization;
|
||||
}
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
|
||||
ENUM_QDEBUG_OPS_IMPL (Transceiver, MODE);
|
||||
|
||||
QDebug operator << (QDebug d, Transceiver::TransceiverState const& s)
|
||||
{
|
||||
d.nospace ()
|
||||
<< "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no")
|
||||
<< " Frequency {" << s.frequency_[0] << "Hz, " << s.frequency_[1] << "Hz} " << s.mode_
|
||||
<< "; SPLIT: " << (Transceiver::TransceiverState::on == s.split_ ? "on" : Transceiver::TransceiverState::off == s.split_ ? "off" : "unknown")
|
||||
<< "; PTT: " << (s.ptt_ ? "on" : "off")
|
||||
<< ')';
|
||||
return d.space ();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ENUM_QDATASTREAM_OPS_IMPL (Transceiver, MODE);
|
||||
|
||||
ENUM_CONVERSION_OPS_IMPL (Transceiver, MODE);
|
||||
|
||||
bool operator != (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs)
|
||||
{
|
||||
return lhs.online_ != rhs.online_
|
||||
|| lhs.frequency_[0] != rhs.frequency_[0]
|
||||
|| lhs.frequency_[1] != rhs.frequency_[1]
|
||||
|| lhs.mode_ != rhs.mode_
|
||||
|| lhs.split_ != rhs.split_
|
||||
|| lhs.ptt_ != rhs.ptt_;
|
||||
}
|
||||
|
||||
bool operator == (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs)
|
||||
{
|
||||
return !(lhs != rhs);
|
||||
}
|
169
Transceiver.hpp
Normal file
@ -0,0 +1,169 @@
|
||||
#ifndef TRANSCEIVER_HPP__
|
||||
#define TRANSCEIVER_HPP__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
#include "Radio.hpp"
|
||||
|
||||
class QString;
|
||||
|
||||
//
|
||||
// Abstract Transceiver Interface
|
||||
//
|
||||
// This is the minimal generic interface to a rig as required by
|
||||
// wsjtx.
|
||||
//
|
||||
// Responsibilities
|
||||
//
|
||||
// Provides Qt slots to set the frequency, mode and PTT of some
|
||||
// transceiver. They are Qt slots so that they may be invoked across
|
||||
// a thread boundary.
|
||||
//
|
||||
// Provides a synchronisation Qt slot which should be implemented in
|
||||
// sub-classes in such a way that normal operation of the rig is not
|
||||
// disturbed. This is intended to be use to poll rig state
|
||||
// periodically and changing VFO to read the other VFO frequency or
|
||||
// mode for example should not be done since the operator may be
|
||||
// tuning the VFO at the time and would be surprised by an unprompted
|
||||
// VFO change.
|
||||
//
|
||||
// Provides a control interface using Qt slots to start and stop the
|
||||
// rig control and PTT connections.
|
||||
//
|
||||
// These are Qt slots rather than the constructor and destructor
|
||||
// because it is expected that the concrete Transceiver
|
||||
// implementations will run in a separate thread from where they are
|
||||
// constructed.
|
||||
//
|
||||
// Qt signals are defined to notify clients of asynchronous rig state
|
||||
// changes and failures. These can and are expected to cross thread
|
||||
// boundaries.
|
||||
//
|
||||
// A signal finished() is defined that concrete Transceiver
|
||||
// implementations must emit when they are ripe for destruction. This
|
||||
// is intended to be used by clients that move the Transceiver
|
||||
// instance to a thread and need to use QObject::deleteLater() to
|
||||
// safely dispose of the Transceiver instance. Implementations should
|
||||
// expect Qt slot calls after emitting finished, it is up to the
|
||||
// implementation whether these slot invocations are ignored.
|
||||
//
|
||||
class Transceiver
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
Q_ENUMS (MODE);
|
||||
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
|
||||
protected:
|
||||
Transceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~Transceiver ()
|
||||
{
|
||||
}
|
||||
|
||||
enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM};
|
||||
|
||||
//
|
||||
// Aggregation of all of the rig and PTT state accessible via this
|
||||
// interface.
|
||||
class TransceiverState
|
||||
{
|
||||
public:
|
||||
TransceiverState ()
|
||||
: online_ {false}
|
||||
, frequency_ {0, 0}
|
||||
, mode_ {UNK}
|
||||
, split_ {unknown}
|
||||
, ptt_ {false}
|
||||
{
|
||||
}
|
||||
|
||||
bool online () const {return online_;}
|
||||
Frequency frequency () const {return frequency_[0];}
|
||||
Frequency tx_frequency () const {return frequency_[1];}
|
||||
bool split () const {return on == split_;}
|
||||
MODE mode () const {return mode_;}
|
||||
bool ptt () const {return ptt_;}
|
||||
|
||||
void online (bool state) {online_ = state;}
|
||||
void frequency (Frequency f) {frequency_[0] = f;}
|
||||
void tx_frequency (Frequency f) {frequency_[1] = f;}
|
||||
void split (bool state) {split_ = state ? on : off;}
|
||||
void mode (MODE m) {mode_ = m;}
|
||||
void ptt (bool state) {ptt_ = state;}
|
||||
|
||||
private:
|
||||
bool online_;
|
||||
Frequency frequency_[2]; // [0] -> Rx; [1] -> Other
|
||||
MODE mode_;
|
||||
enum {unknown, off, on} split_;
|
||||
bool ptt_;
|
||||
// Don't forget to update the debug print and != operator if you
|
||||
// add more members here
|
||||
|
||||
friend QDebug operator << (QDebug, TransceiverState const&);
|
||||
friend bool operator != (TransceiverState const&, TransceiverState const&);
|
||||
};
|
||||
|
||||
//
|
||||
// The following slots and signals are expected to all run in the
|
||||
// same thread which is not necessarily the main GUI thread. It is
|
||||
// up to the client of the Transceiver class to organise the
|
||||
// allocation to a thread and the lifetime of the object instances.
|
||||
//
|
||||
|
||||
// Connect and disconnect.
|
||||
Q_SLOT virtual void start () noexcept = 0;
|
||||
Q_SLOT virtual void stop () noexcept = 0;
|
||||
|
||||
// Ready to be destroyed.
|
||||
Q_SIGNAL void finished () const;
|
||||
|
||||
// Set frequency in Hertz.
|
||||
Q_SLOT virtual void frequency (Frequency) noexcept = 0;
|
||||
|
||||
// Setting a non-zero TX frequency means split operation, the value
|
||||
// zero means simplex operation.
|
||||
//
|
||||
// Rationalise_mode means ensure TX uses same mode as RX.
|
||||
Q_SLOT virtual void tx_frequency (Frequency tx = 0, bool rationalise_mode = true) noexcept = 0;
|
||||
|
||||
// Set mode.
|
||||
// Rationalise means ensure TX uses same mode as RX.
|
||||
Q_SLOT virtual void mode (MODE, bool rationalise = true) noexcept = 0;
|
||||
|
||||
// Set/unset PTT.
|
||||
Q_SLOT virtual void ptt (bool = true) noexcept = 0;
|
||||
|
||||
// Attempt to re-synchronise or query state.
|
||||
// Force_signal guarantees a update or failure signal.
|
||||
Q_SLOT virtual void sync (bool force_signal = false) noexcept = 0;
|
||||
|
||||
// asynchronous status updates
|
||||
Q_SIGNAL void update (Transceiver::TransceiverState) const;
|
||||
Q_SIGNAL void failure (QString reason) const;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE (Transceiver::TransceiverState);
|
||||
Q_DECLARE_METATYPE (Transceiver::MODE);
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
ENUM_QDEBUG_OPS_DECL (Transceiver, MODE);
|
||||
|
||||
QDebug operator << (QDebug, Transceiver::TransceiverState const&);
|
||||
#endif
|
||||
|
||||
ENUM_QDATASTREAM_OPS_DECL (Transceiver, MODE);
|
||||
|
||||
ENUM_CONVERSION_OPS_DECL (Transceiver, MODE);
|
||||
|
||||
bool operator != (Transceiver::TransceiverState const&, Transceiver::TransceiverState const&);
|
||||
bool operator == (Transceiver::TransceiverState const&, Transceiver::TransceiverState const&);
|
||||
|
||||
#endif
|
269
TransceiverBase.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
#include "TransceiverBase.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
class TransceiverBase::impl final
|
||||
{
|
||||
public:
|
||||
impl ()
|
||||
{
|
||||
}
|
||||
|
||||
impl (impl const&) = delete;
|
||||
impl& operator = (impl const&) = delete;
|
||||
|
||||
TransceiverState state_;
|
||||
};
|
||||
|
||||
|
||||
TransceiverBase::TransceiverBase ()
|
||||
{
|
||||
}
|
||||
|
||||
TransceiverBase::~TransceiverBase ()
|
||||
{
|
||||
}
|
||||
|
||||
void TransceiverBase::start () noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_stop ();
|
||||
do_post_stop ();
|
||||
m_->state_.online (false);
|
||||
}
|
||||
do_start ();
|
||||
do_post_start ();
|
||||
m_->state_.online (true);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::stop () noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
do_stop ();
|
||||
do_post_stop ();
|
||||
m_->state_.online (false);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_EMIT finished ();
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::frequency (Frequency f) noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_frequency (f);
|
||||
do_post_frequency (f);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::tx_frequency (Frequency tx, bool rationalise_mode) noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_tx_frequency (tx, rationalise_mode);
|
||||
do_post_tx_frequency (tx);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::mode (MODE m, bool rationalise) noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_mode (m, rationalise);
|
||||
do_post_mode (m);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::ptt (bool on) noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_ptt (on);
|
||||
do_post_ptt (on);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::sync (bool force_signal) noexcept
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
do_sync (force_signal);
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
if (!message.isEmpty ())
|
||||
{
|
||||
offline (message);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::update_rx_frequency (Frequency rx)
|
||||
{
|
||||
m_->state_.frequency (rx);
|
||||
}
|
||||
|
||||
void TransceiverBase::update_other_frequency (Frequency tx)
|
||||
{
|
||||
m_->state_.tx_frequency (tx);
|
||||
}
|
||||
|
||||
void TransceiverBase::update_split (bool state)
|
||||
{
|
||||
m_->state_.split (state);
|
||||
}
|
||||
|
||||
void TransceiverBase::update_mode (MODE m)
|
||||
{
|
||||
m_->state_.mode (m);
|
||||
}
|
||||
|
||||
void TransceiverBase::update_PTT (bool state)
|
||||
{
|
||||
m_->state_.ptt (state);
|
||||
}
|
||||
|
||||
void TransceiverBase::update_complete ()
|
||||
{
|
||||
if (do_pre_update ())
|
||||
{
|
||||
Q_EMIT update (m_->state_);
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::offline (QString const& reason)
|
||||
{
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
if (m_->state_.online ())
|
||||
{
|
||||
m_->state_.online (false);
|
||||
do_stop ();
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
message = e.what ();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
message = "Unexpected rig error";
|
||||
}
|
||||
Q_EMIT failure (reason + '\n' + message);
|
||||
}
|
||||
|
||||
auto TransceiverBase::state () const -> TransceiverState const&
|
||||
{
|
||||
return m_->state_;
|
||||
}
|
136
TransceiverBase.hpp
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef TRANSCEIVER_BASE_HPP__
|
||||
#define TRANSCEIVER_BASE_HPP__
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Transceiver.hpp"
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
|
||||
//
|
||||
// Base Transceiver Implementation
|
||||
//
|
||||
// Behaviour common to all Transceiver implementations.
|
||||
//
|
||||
// Collaborations
|
||||
//
|
||||
// Implements the Transceiver abstract interface as template methods
|
||||
// and provides a new abstract interface with similar functionality
|
||||
// (do_XXXXX operations). Provides and calls abstract interface that
|
||||
// gets called post the above operations (do_post_XXXXX) to allow
|
||||
// caching implementation etc.
|
||||
//
|
||||
// A key factor is to catch all exceptions thrown by sub-class
|
||||
// implementations where the template method is a Qt slot which is
|
||||
// therefore likely to be called by Qt which doesn't handle
|
||||
// exceptions. Any exceptions are converted to Transceiver::failure()
|
||||
// signals.
|
||||
//
|
||||
// Sub-classes update the stored state via a protected interface.
|
||||
//
|
||||
// Responsibilities:
|
||||
//
|
||||
// Wrap incoming Transceiver messages catching all exceptions in Qt
|
||||
// slot driven messages and converting them to Qt signals. This is
|
||||
// done because exceptions make concrete Transceiver implementations
|
||||
// simpler to write, but exceptions cannot cross signal/slot
|
||||
// boundaries (especially across threads). This also removes any
|
||||
// requirement for the client code to handle exceptions.
|
||||
//
|
||||
// Maintain the state of the concrete Transceiver instance that is
|
||||
// passed back via the Transceiver::update(TransceiverState) signal,
|
||||
// it is still the responsibility of concrete Transceiver
|
||||
// implementations to emit the state_change signal when they have a
|
||||
// status update.
|
||||
//
|
||||
// Maintain a go/no-go status for concrete Transceiver
|
||||
// implementations ensuring only a valid sequence of messages are
|
||||
// passed. A concrete Transceiver instance must be started before it
|
||||
// can receive messages, any exception thrown takes the Transceiver
|
||||
// offline.
|
||||
//
|
||||
// Implements methods that concrete Transceiver implementations use
|
||||
// to update the Transceiver state. These do not signal state change
|
||||
// to clients as this is the responsibility of the concrete
|
||||
// Transceiver implementation, thus allowing multiple state component
|
||||
// updates to be signalled together if required.
|
||||
//
|
||||
class TransceiverBase
|
||||
: public Transceiver
|
||||
{
|
||||
protected:
|
||||
TransceiverBase ();
|
||||
|
||||
public:
|
||||
~TransceiverBase ();
|
||||
|
||||
//
|
||||
// Implement the Transceiver abstract interface.
|
||||
//
|
||||
void start () noexcept override final;
|
||||
void stop () noexcept override final;
|
||||
void frequency (Frequency rx) noexcept override final;
|
||||
void tx_frequency (Frequency tx, bool rationalise_mode) noexcept override final;
|
||||
void mode (MODE, bool rationalise) noexcept override final;
|
||||
void ptt (bool) noexcept override final;
|
||||
void sync (bool force_signal) noexcept override final;
|
||||
|
||||
protected:
|
||||
//
|
||||
// Error exception which is thrown to signal unexpected errors.
|
||||
//
|
||||
struct error
|
||||
: public std::runtime_error
|
||||
{
|
||||
error (char const * msg) : std::runtime_error (msg) {}
|
||||
};
|
||||
|
||||
// Template methods that sub classes implement to do what they need to do.
|
||||
//
|
||||
// These methods may throw exceptions to signal errors.
|
||||
virtual void do_start () = 0;
|
||||
virtual void do_post_start () {}
|
||||
|
||||
virtual void do_stop () = 0;
|
||||
virtual void do_post_stop () {}
|
||||
|
||||
virtual void do_frequency (Frequency rx) = 0;
|
||||
virtual void do_post_frequency (Frequency) {}
|
||||
|
||||
virtual void do_tx_frequency (Frequency tx = 0, bool rationalise_mode = true) = 0;
|
||||
virtual void do_post_tx_frequency (Frequency) {}
|
||||
|
||||
virtual void do_mode (MODE, bool rationalise = true) = 0;
|
||||
virtual void do_post_mode (MODE) {}
|
||||
|
||||
virtual void do_ptt (bool = true) = 0;
|
||||
virtual void do_post_ptt (bool) {}
|
||||
|
||||
virtual void do_sync (bool force_signal = false) = 0;
|
||||
|
||||
virtual bool do_pre_update () {return true;}
|
||||
|
||||
// sub classes report rig state changes with these methods
|
||||
void update_rx_frequency (Frequency);
|
||||
void update_other_frequency (Frequency = 0);
|
||||
void update_split (bool);
|
||||
void update_mode (MODE);
|
||||
void update_PTT (bool = true);
|
||||
|
||||
// Calling this triggers the Transceiver::update(State) signal.
|
||||
void update_complete ();
|
||||
|
||||
// sub class may asynchronously take the rig offline by calling this
|
||||
void offline (QString const& reason);
|
||||
|
||||
// and query state with this one
|
||||
TransceiverState const& state () const;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
301
TransceiverFactory.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
#include "TransceiverFactory.hpp"
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
#include "HamlibTransceiver.hpp"
|
||||
#include "DXLabSuiteCommanderTransceiver.hpp"
|
||||
#include "HRDTransceiver.hpp"
|
||||
#include "EmulateSplitTransceiver.hpp"
|
||||
|
||||
#if defined (WIN32)
|
||||
#include "OmniRigTransceiver.hpp"
|
||||
#endif
|
||||
|
||||
#include "moc_TransceiverFactory.cpp"
|
||||
|
||||
// we use the hamlib "Hamlib Dummy" transceiver for non-CAT radios,
|
||||
// this allows us to still use the hamlib PTT control features for a
|
||||
// unified PTT control solution
|
||||
|
||||
char const * const TransceiverFactory::basic_transceiver_name_ = "None";
|
||||
|
||||
namespace
|
||||
{
|
||||
struct init
|
||||
{
|
||||
init ()
|
||||
{
|
||||
qRegisterMetaType<TransceiverFactory::DataBits> ("TransceiverFactory::DataBits");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::DataBits> ("TransceiverFactory::DataBits");
|
||||
qRegisterMetaType<TransceiverFactory::StopBits> ("TransceiverFactory::StopBits");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::StopBits> ("TransceiverFactory::StopBits");
|
||||
qRegisterMetaType<TransceiverFactory::Handshake> ("TransceiverFactory::Handshake");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::Handshake> ("TransceiverFactory::Handshake");
|
||||
qRegisterMetaType<TransceiverFactory::PTTMethod> ("TransceiverFactory::PTTMethod");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::PTTMethod> ("TransceiverFactory::PTTMethod");
|
||||
qRegisterMetaType<TransceiverFactory::TXAudioSource> ("TransceiverFactory::TXAudioSource");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::TXAudioSource> ("TransceiverFactory::TXAudioSource");
|
||||
qRegisterMetaType<TransceiverFactory::SplitMode> ("TransceiverFactory::SplitMode");
|
||||
qRegisterMetaTypeStreamOperators<TransceiverFactory::SplitMode> ("TransceiverFactory::SplitMode");
|
||||
}
|
||||
} static_initializer;
|
||||
|
||||
enum // supported non-hamlib radio interfaces
|
||||
{
|
||||
NonHamlibBaseId = 9899
|
||||
, CommanderId
|
||||
, HRDId
|
||||
, OmniRigOneId
|
||||
, OmniRigTwoId
|
||||
};
|
||||
}
|
||||
|
||||
TransceiverFactory::TransceiverFactory ()
|
||||
{
|
||||
HamlibTransceiver::register_transceivers (&transceivers_);
|
||||
DXLabSuiteCommanderTransceiver::register_transceivers (&transceivers_, CommanderId);
|
||||
HRDTransceiver::register_transceivers (&transceivers_, HRDId);
|
||||
|
||||
#if defined (WIN32)
|
||||
// OmniRig is ActiveX/COM server so only on Windows
|
||||
OmniRigTransceiver::register_transceivers (&transceivers_, OmniRigOneId, OmniRigTwoId);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto TransceiverFactory::supported_transceivers () const -> Transceivers const&
|
||||
{
|
||||
return transceivers_;
|
||||
}
|
||||
|
||||
auto TransceiverFactory::CAT_port_type (QString const& name) const -> Capabilities::PortType
|
||||
{
|
||||
return supported_transceivers ()[name].port_type_;
|
||||
}
|
||||
|
||||
bool TransceiverFactory::has_CAT_PTT (QString const& name) const
|
||||
{
|
||||
return
|
||||
supported_transceivers ()[name].has_CAT_PTT_
|
||||
|| supported_transceivers ()[name].model_number_ > NonHamlibBaseId;
|
||||
}
|
||||
|
||||
bool TransceiverFactory::has_CAT_PTT_mic_data (QString const& name) const
|
||||
{
|
||||
return supported_transceivers ()[name].has_CAT_PTT_mic_data_;
|
||||
}
|
||||
|
||||
bool TransceiverFactory::has_CAT_indirect_serial_PTT (QString const& name) const
|
||||
{
|
||||
return supported_transceivers ()[name].has_CAT_indirect_serial_PTT_;
|
||||
}
|
||||
|
||||
bool TransceiverFactory::has_asynchronous_CAT (QString const& name) const
|
||||
{
|
||||
return supported_transceivers ()[name].asynchronous_;
|
||||
}
|
||||
|
||||
std::unique_ptr<Transceiver> TransceiverFactory::create (QString const& name
|
||||
, QString const& cat_port
|
||||
, int cat_baud
|
||||
, DataBits cat_data_bits
|
||||
, StopBits cat_stop_bits
|
||||
, Handshake cat_handshake
|
||||
, bool cat_dtr_always_on
|
||||
, bool cat_rts_always_on
|
||||
, PTTMethod ptt_type
|
||||
, TXAudioSource ptt_use_data_ptt
|
||||
, SplitMode split_mode
|
||||
, QString const& ptt_port
|
||||
, int poll_interval
|
||||
, QThread * target_thread)
|
||||
{
|
||||
std::unique_ptr<Transceiver> result;
|
||||
switch (supported_transceivers ()[name].model_number_)
|
||||
{
|
||||
case CommanderId:
|
||||
{
|
||||
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
|
||||
std::unique_ptr<TransceiverBase> basic_transceiver {
|
||||
new HamlibTransceiver {
|
||||
supported_transceivers ()[basic_transceiver_name_].model_number_
|
||||
, cat_port
|
||||
, cat_baud
|
||||
, cat_data_bits
|
||||
, cat_stop_bits
|
||||
, cat_handshake
|
||||
, cat_dtr_always_on
|
||||
, cat_rts_always_on
|
||||
, ptt_type
|
||||
, ptt_use_data_ptt
|
||||
, "CAT" == ptt_port ? "" : ptt_port
|
||||
}
|
||||
};
|
||||
if (target_thread)
|
||||
{
|
||||
basic_transceiver.get ()->moveToThread (target_thread);
|
||||
}
|
||||
|
||||
// wrap the basic Transceiver object instance with a decorator object that talks to DX Lab Suite Commander
|
||||
result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), cat_port, PTT_method_CAT == ptt_type, poll_interval});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HRDId:
|
||||
{
|
||||
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
|
||||
std::unique_ptr<TransceiverBase> basic_transceiver {
|
||||
new HamlibTransceiver {
|
||||
supported_transceivers ()[basic_transceiver_name_].model_number_
|
||||
, cat_port
|
||||
, cat_baud
|
||||
, cat_data_bits
|
||||
, cat_stop_bits
|
||||
, cat_handshake
|
||||
, cat_dtr_always_on
|
||||
, cat_rts_always_on
|
||||
, ptt_type
|
||||
, ptt_use_data_ptt
|
||||
, "CAT" == ptt_port ? "" : ptt_port
|
||||
}
|
||||
};
|
||||
if (target_thread)
|
||||
{
|
||||
basic_transceiver.get ()->moveToThread (target_thread);
|
||||
}
|
||||
|
||||
// wrap the basic Transceiver object instance with a decorator object that talks to ham Radio Deluxe
|
||||
result.reset (new HRDTransceiver {std::move (basic_transceiver), cat_port, PTT_method_CAT == ptt_type, poll_interval});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
#if defined (WIN32)
|
||||
case OmniRigOneId:
|
||||
{
|
||||
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
|
||||
std::unique_ptr<TransceiverBase> basic_transceiver {
|
||||
new HamlibTransceiver {
|
||||
supported_transceivers ()[basic_transceiver_name_].model_number_
|
||||
, cat_port
|
||||
, cat_baud
|
||||
, cat_data_bits
|
||||
, cat_stop_bits
|
||||
, cat_handshake
|
||||
, cat_dtr_always_on
|
||||
, cat_rts_always_on
|
||||
, ptt_type
|
||||
, ptt_use_data_ptt
|
||||
, "CAT" == ptt_port ? "" : ptt_port
|
||||
}
|
||||
};
|
||||
if (target_thread)
|
||||
{
|
||||
basic_transceiver.get ()->moveToThread (target_thread);
|
||||
}
|
||||
|
||||
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig one
|
||||
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, ptt_type, ptt_port});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OmniRigTwoId:
|
||||
{
|
||||
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
|
||||
std::unique_ptr<TransceiverBase> basic_transceiver {
|
||||
new HamlibTransceiver {
|
||||
supported_transceivers ()[basic_transceiver_name_].model_number_
|
||||
, cat_port
|
||||
, cat_baud
|
||||
, cat_data_bits
|
||||
, cat_stop_bits
|
||||
, cat_handshake
|
||||
, cat_dtr_always_on
|
||||
, cat_rts_always_on
|
||||
, ptt_type
|
||||
, ptt_use_data_ptt
|
||||
, "CAT" == ptt_port ? "" : ptt_port
|
||||
}
|
||||
};
|
||||
if (target_thread)
|
||||
{
|
||||
basic_transceiver.get ()->moveToThread (target_thread);
|
||||
}
|
||||
|
||||
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig two
|
||||
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, ptt_type, ptt_port});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
result.reset (new HamlibTransceiver {
|
||||
supported_transceivers ()[name].model_number_
|
||||
, cat_port
|
||||
, cat_baud
|
||||
, cat_data_bits
|
||||
, cat_stop_bits
|
||||
, cat_handshake
|
||||
, cat_dtr_always_on
|
||||
, cat_rts_always_on
|
||||
, ptt_type
|
||||
, ptt_use_data_ptt
|
||||
, "CAT" == ptt_port ? cat_port : ptt_port
|
||||
, poll_interval
|
||||
});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (split_mode_emulate == split_mode)
|
||||
{
|
||||
// wrap the Transceiver object instance with a decorator that emulates split mode
|
||||
result.reset (new EmulateSplitTransceiver {std::move (result)});
|
||||
if (target_thread)
|
||||
{
|
||||
result.get ()->moveToThread (target_thread);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move (result);
|
||||
}
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, DataBits);
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, StopBits);
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, Handshake);
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, PTTMethod);
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, SplitMode);
|
||||
#endif
|
||||
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, DataBits);
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, StopBits);
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, Handshake);
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, PTTMethod);
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, SplitMode);
|
||||
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, DataBits);
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, StopBits);
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, Handshake);
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, PTTMethod);
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, SplitMode);
|
155
TransceiverFactory.hpp
Normal file
@ -0,0 +1,155 @@
|
||||
#ifndef TRANSCEIVER_FACTORY_HPP__
|
||||
#define TRANSCEIVER_FACTORY_HPP__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
||||
#include "Transceiver.hpp"
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
|
||||
class QString;
|
||||
class QThread;
|
||||
|
||||
//
|
||||
// Transceiver Factory
|
||||
//
|
||||
class TransceiverFactory
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
Q_ENUMS (DataBits StopBits Handshake PTTMethod TXAudioSource SplitMode);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY (TransceiverFactory);
|
||||
|
||||
public:
|
||||
//
|
||||
// Capabilities of a Transceiver that can be determined without
|
||||
// actually instantiating one, these are for use in Configuration
|
||||
// GUI behaviour determination
|
||||
//
|
||||
struct Capabilities
|
||||
{
|
||||
enum PortType {none, serial, network};
|
||||
|
||||
explicit Capabilities (int model_number = 0
|
||||
, PortType port_type = none
|
||||
, bool has_CAT_PTT = false
|
||||
, bool has_CAT_PTT_mic_data = false
|
||||
, bool has_CAT_indirect_serial_PTT = false
|
||||
, bool asynchronous = false)
|
||||
: model_number_ {model_number}
|
||||
, port_type_ {port_type}
|
||||
, has_CAT_PTT_ {has_CAT_PTT}
|
||||
, has_CAT_PTT_mic_data_ {has_CAT_PTT_mic_data}
|
||||
, has_CAT_indirect_serial_PTT_ {has_CAT_indirect_serial_PTT}
|
||||
, asynchronous_ {asynchronous}
|
||||
{
|
||||
}
|
||||
|
||||
int model_number_;
|
||||
PortType port_type_;
|
||||
bool has_CAT_PTT_;
|
||||
bool has_CAT_PTT_mic_data_;
|
||||
bool has_CAT_indirect_serial_PTT_; // OmniRig controls RTS/DTR via COM interface
|
||||
bool asynchronous_;
|
||||
};
|
||||
|
||||
//
|
||||
// Dictionary of Transceiver types Capabilities
|
||||
//
|
||||
typedef QMap<QString, Capabilities> Transceivers;
|
||||
|
||||
//
|
||||
// various Transceiver parameters
|
||||
//
|
||||
enum DataBits {seven_data_bits = 7, eight_data_bits};
|
||||
enum StopBits {one_stop_bit = 1, two_stop_bits};
|
||||
enum Handshake {handshake_none, handshake_XonXoff, handshake_hardware};
|
||||
enum PTTMethod {PTT_method_VOX, PTT_method_CAT, PTT_method_DTR, PTT_method_RTS};
|
||||
enum TXAudioSource {TX_audio_source_front, TX_audio_source_rear};
|
||||
enum SplitMode {split_mode_none, split_mode_rig, split_mode_emulate};
|
||||
|
||||
TransceiverFactory ();
|
||||
|
||||
static char const * const basic_transceiver_name_; // dummy transceiver is basic model
|
||||
|
||||
//
|
||||
// fetch all supported rigs as a list of name and model id
|
||||
//
|
||||
Transceivers const& supported_transceivers () const;
|
||||
|
||||
// supported model queries
|
||||
Capabilities::PortType CAT_port_type (QString const& name) const; // how to talk to CAT
|
||||
bool has_CAT_PTT (QString const& name) const; // can be keyed via CAT
|
||||
bool has_CAT_PTT_mic_data (QString const& name) const; // Tx audio port is switchable via CAT
|
||||
bool has_CAT_indirect_serial_PTT (QString const& name) const; // Can PTT via CAT port use DTR or RTS (OmniRig for example)
|
||||
bool has_asynchronous_CAT (QString const& name) const; // CAT asynchronous rather than polled
|
||||
|
||||
// make a new Transceiver instance
|
||||
//
|
||||
// cat_port, cat_baud, cat_data_bits, cat_stop_bits, cat_handshake,
|
||||
// cat_dtr_alway_on, cat_rts_always_on are only relevant to
|
||||
// interfaces that are served by hamlib
|
||||
//
|
||||
// PTT port and to some extent ptt_type are independent of interface
|
||||
// type
|
||||
//
|
||||
std::unique_ptr<Transceiver> create (QString const& name // from supported_transceivers () key
|
||||
, QString const& cat_port // serial port device name or empty
|
||||
, int cat_baud
|
||||
, DataBits cat_data_bits
|
||||
, StopBits cat_stop_bits
|
||||
, Handshake cat_handshake
|
||||
, bool cat_dtr_always_on // to power interface
|
||||
, bool cat_rts_always_on // to power inteface
|
||||
, PTTMethod ptt_type // "CAT" | "DTR" | "RTS" | "VOX"
|
||||
, TXAudioSource ptt_use_data_ptt // some rigs allow audio routing to Mic/Data connector
|
||||
, SplitMode split_mode // how to support split TX mode
|
||||
, QString const& ptt_port // serial port device name or special value "CAT"
|
||||
, int poll_interval // in milliseconds for interfaces that require polling for parameter changes
|
||||
, QThread * target_thread = nullptr
|
||||
);
|
||||
|
||||
private:
|
||||
Transceivers transceivers_;
|
||||
};
|
||||
|
||||
//
|
||||
// boilerplate routines to make enum types useable and debuggable in
|
||||
// Qt
|
||||
//
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::DataBits);
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::StopBits);
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::Handshake);
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::PTTMethod);
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::TXAudioSource);
|
||||
Q_DECLARE_METATYPE (TransceiverFactory::SplitMode);
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, DataBits);
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, StopBits);
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, Handshake);
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, PTTMethod);
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_QDEBUG_OPS_DECL (TransceiverFactory, SplitMode);
|
||||
#endif
|
||||
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, DataBits);
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, StopBits);
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, Handshake);
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, PTTMethod);
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, SplitMode);
|
||||
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, DataBits);
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, StopBits);
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, Handshake);
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, PTTMethod);
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, TXAudioSource);
|
||||
ENUM_CONVERSION_OPS_DECL (TransceiverFactory, SplitMode);
|
||||
|
||||
#endif
|
10
Versions.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
# Version number components
|
||||
set (WSJTX_VERSION_MAJOR 1)
|
||||
set (WSJTX_VERSION_MINOR 4)
|
||||
set (WSJTX_VERSION_PATCH 0)
|
||||
#set (WSJTX_RC 1)
|
||||
set (WSJTX_VERSION_IS_RELEASE 0)
|
||||
|
||||
set (CONFIG_TEST_VERSION_MAJOR 0)
|
||||
set (CONFIG_TEST_VERSION_MINOR 2)
|
||||
set (CONFIG_TEST_VERSION_PATCH 13)
|
@ -1,6 +1,8 @@
|
||||
#include "about.h"
|
||||
#include "ui_about.h"
|
||||
|
||||
#include "moc_about.cpp"
|
||||
|
||||
CAboutDlg::CAboutDlg(QWidget *parent, QString Revision) :
|
||||
QDialog(parent),
|
||||
m_Revision(Revision),
|
||||
|
74
about.ui
@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>374</width>
|
||||
<height>164</height>
|
||||
<height>144</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -24,17 +24,75 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTxt">
|
||||
<property name="text">
|
||||
<string/>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTxt">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="okButton">
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>okButton</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>CAboutDlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>321</x>
|
||||
<y>120</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>186</x>
|
||||
<y>71</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
143
artwork/DragNDrop Background.svg
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="Layer_1"
|
||||
space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
viewBox="0 0 640 480"
|
||||
version="1.1"
|
||||
sodipodi:docname="tasto_5_architetto_franc_01.svg"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
width="100%"
|
||||
height="100%">
|
||||
<defs
|
||||
id="defs6110" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
pagecolor="#ffffff"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.89375"
|
||||
inkscape:cx="227.06271"
|
||||
inkscape:cy="240"
|
||||
inkscape:window-width="1855"
|
||||
inkscape:window-height="1056"
|
||||
inkscape:window-x="65"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3" />
|
||||
<g
|
||||
id="g3"
|
||||
transform="translate(0,342.62101)">
|
||||
<radialGradient
|
||||
id="XMLID_2_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cy="42.188999"
|
||||
cx="42.785999"
|
||||
r="69.833"
|
||||
gradientTransform="translate(237.98657,-269.79195)">
|
||||
<stop
|
||||
id="stop6"
|
||||
stop-color="#D1F593"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop8"
|
||||
stop-color="#81CE09"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
id="path10"
|
||||
d="m 294.86757,-261.31295 v 11.564 c 0,1.741 1.297,11.914 0,13.039 -1.02,0.886 -6.762,0 -8.05,0 h -18.478 c -7.957,0 -22.38,-2.722 -27.218,5.536 -3.929,6.711 -1.526,19.418 -1.526,26.711 0,8.382 -0.774,17.167 0,25.519 0,13.355 15.573,11.178 24.427,11.178 h 30.258 c 1.235,0 0.587,21.391 0.587,24.12 0,9.468 9.867,13.694 16.696,6.885 5.979,-5.956 11.956,-11.914 17.934,-17.874 12.723,-12.681 27.184,-24.79 38.705,-38.581 8.129,-8.157 -0.109,-15.919 -5.847,-21.664 -7.818,-7.824 -15.637,-15.65 -23.456,-23.475 -7.681,-7.69 -15.364,-15.38 -23.047,-23.068 -4.98,-4.985 -11.292,-11.356 -18.616,-5 -1.493,1.285 -2.366,3.107 -2.366,5.097"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#XMLID_2_)" />
|
||||
<path
|
||||
id="path12"
|
||||
d="m 239.59557,-225.52895 v 25.519 c 0,7.292 -2.404,20 1.526,26.707 4.839,8.26 19.258,5.537 27.214,5.537 h 17.257 c 1.47,0 8.123,-1.016 9.274,0 1.14,1.006 0,9.882 0,11.396 v 14.252 c 0,10.854 12.136,9.904 17.771,4.287 15.045,-14.996 30.09,-29.993 45.136,-44.991 5.456,-5.438 15.865,-12.589 13.176,-21.487 -0.317,0.598 -3.726,2.826 -4.396,3.495 -3.066,3.059 -6.136,6.117 -9.201,9.174 -8.52,8.492 -17.037,16.985 -25.555,25.477 -5.672,5.654 -11.342,11.308 -17.012,16.961 -2.447,2.439 -6.51,4.557 -9.908,2.681 -4.008,-2.21 -2.962,-8.699 -2.962,-12.543 v -15.579 c 0,-2.856 -2.156,-1.731 -4.79,-1.731 h -33.59 c -8.25,0 -16.507,-0.511 -16.507,-10.853 v -32.609 c 0,-4.972 -1.083,-11.582 0.677,-16.393 -4.732,1.342 -8.114,5.758 -8.114,10.688"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#67ba06" />
|
||||
<path
|
||||
id="path14"
|
||||
d="m 247.70757,-168.51195 c -2.771,-7.597 -0.677,-18.748 -0.677,-26.612 0,-8.349 -1.456,-18.099 0.173,-26.347 1.843,-9.349 18.409,-6.667 25.217,-6.667 h 29.23 c 0.768,0 -0.319,-21.606 0.808,-24.748 3.407,-9.491 12.963,0.134 16.668,3.857 15.269,15.341 30.538,30.681 45.805,46.022 1.224,1.231 5.246,3.916 6.019,5.379 2.872,-9.511 -10.045,-18.432 -15.802,-24.213 l -32.322,-32.461 c -5.208,-5.23 -12.42,-16.139 -21.006,-14.488 -11.102,2.133 -6.951,23.787 -6.951,31.819 -9.333,0 -18.663,-0.002 -27.996,-0.002 -9.16,-0.001 -27.276,-2.955 -27.276,11.18 -0.863,9.313 0,19.11 0,28.458 0,8.799 -3.183,25.591 8.112,28.823"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b2ed21" />
|
||||
<path
|
||||
id="path16"
|
||||
d="m 299.44057,-268.80395 c -9.2,3.806 -5.476,22.395 -5.476,30.261 0,1.951 -23.229,0.93 -26.087,0.93 -8.576,0 -23.012,-2.757 -27.835,6.535 -3.627,6.99 -1.35,19.146 -1.35,26.616 0,8.4 -0.709,17.143 0,25.519 0,13.884 15.395,12.081 24.75,12.081 h 30.326 c 0.616,0 -0.196,23.651 0.363,26.81 1.344,7.601 11.873,10.134 17.171,4.83 1.235,-0.97 2.304,-2.297 3.414,-3.404 6.86,-6.837 13.72,-13.676 20.579,-20.514 7.706,-7.681 15.41,-15.36 23.116,-23.042 4.011,-3.997 10.436,-8.579 12.863,-13.906 3.874,-8.509 -2.996,-14.135 -8.281,-19.426 -7.462,-7.47 -14.926,-14.941 -22.389,-22.411 -7.348,-7.353 -14.693,-14.708 -22.041,-22.061 -5.328,-5.353 -10.368,-12.444 -19.115,-8.826 m -58.942,89.86 v -25.519 c 0,-7.083 -2.537,-20.456 1.757,-26.809 4.938,-7.304 19.854,-4.536 27.283,-4.536 h 17.136 c 1.448,0 7.955,0.999 9.096,0 1.124,-0.984 0,-9.795 0,-11.286 v -14.11 c 0,-6.577 10.11,-8.923 14.281,-4.749 1.512,1.514 3.024,3.027 4.536,4.539 l 46.438,46.479 c 5.123,5.13 13.199,11.335 7.847,19.468 -3.387,5.148 -9.155,9.432 -13.47,13.732 -14.601,14.553 -29.201,29.107 -43.802,43.659 -2.575,2.568 -5.353,4.298 -9.308,3.666 -6.041,-0.967 -6.522,-6.041 -6.522,-10.962 0,-3.029 1.173,-23.3 -1.004,-23.3 h -29.582 c -8.374,-0.01 -24.69,2.6 -24.69,-10.282"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#739b07" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff7400;fill-opacity:0.77254902;stroke:none;font-family:Purisa;-inkscape-font-specification:Sans Bold"
|
||||
x="316.39532"
|
||||
y="-58.000538"
|
||||
id="text6112"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6114"
|
||||
x="316.39532"
|
||||
y="-58.000538">Drag the icon</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="316.39532"
|
||||
y="-8.0005379"
|
||||
id="tspan6116">onto the link</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="316.39532"
|
||||
y="41.999462"
|
||||
id="tspan6118">to install WSJT-X</tspan></text>
|
||||
</g>
|
||||
<metadata
|
||||
id="metadata6108">
|
||||
<rdf:RDF>
|
||||
<cc:Work>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
|
||||
<dc:publisher>
|
||||
<cc:Agent
|
||||
rdf:about="http://openclipart.org/">
|
||||
<dc:title>Openclipart</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:title></dc:title>
|
||||
<dc:date>2010-03-28T09:25:56</dc:date>
|
||||
<dc:description>Drawing by Francesco 'Architetto' Rollandin. From OCAL 0.18 release.</dc:description>
|
||||
<dc:source>http://openclipart.org/detail/34711/architetto----tasto-5-by-anonymous</dc:source>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Anonymous</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>arrow</rdf:li>
|
||||
<rdf:li>clip art</rdf:li>
|
||||
<rdf:li>clipart</rdf:li>
|
||||
<rdf:li>green</rdf:li>
|
||||
<rdf:li>icon</rdf:li>
|
||||
<rdf:li>right</rdf:li>
|
||||
<rdf:li>sign</rdf:li>
|
||||
<rdf:li>symbol</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/publicdomain/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
27
artwork/README
Normal file
@ -0,0 +1,27 @@
|
||||
This directory contains original artwork used to generate the graphics
|
||||
used in various parts of the WSJT-X ecosystem. The CMake build scripts
|
||||
do not generate the final image bitmaps becuase of teh extra tools
|
||||
required to complete this step. Instead there is shell script here
|
||||
(make_graphics.sh) that does the generation. If you want to modify the
|
||||
sourec graphics or add new ones then you need an SVG editor (I use
|
||||
inkscape) and a tool to do various conversion steps (I use
|
||||
ImageMagick), the sheel script explicitly uses these tools.
|
||||
|
||||
The files here are:
|
||||
|
||||
installer_logo.svg - A 150x57 pixel image (the size is important with
|
||||
a whte background that is used at the top right of the NSIS
|
||||
Windows installer.
|
||||
|
||||
wsjtx_globe_1024x1024.svg - A 1024x1024 pixel image which is used in
|
||||
various places, mainly for high resolution icons.
|
||||
|
||||
wsjtx_globe_128x128.svg - A 128x128 pixel image which is used for low
|
||||
resolution icons.
|
||||
|
||||
make_graphics.sh - Run this script (on Linux) to generate the
|
||||
intermediate bitmap image files used in the wsjtx build. This
|
||||
script generates all but the final Mac iconset file which is
|
||||
generated by a build on Mac since it requires a Mac developer tool
|
||||
(iconutil). This script requires that inkscape and ImageMagick are
|
||||
installed.
|
1126
artwork/installer_logo.svg
Normal file
After Width: | Height: | Size: 49 KiB |
58
artwork/make_graphics.sh
Normal file
@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Windows
|
||||
#
|
||||
inkscape -z -e /tmp/image-0.png wsjtx_globe_128x128.svg
|
||||
inkscape -z -e /tmp/image-1.png wsjtx_globe_1024x1024.svg
|
||||
convert '/tmp/image-%d.png[0-1]' -background transparent \
|
||||
\( -clone 0 -resize 16 -colors 256 -compress none \) \
|
||||
\( -clone 0 -resize 20 -colors 256 -compress none \) \
|
||||
\( -clone 0 -resize 24 -colors 256 -compress none \) \
|
||||
\( -clone 0 -resize 32 -colors 256 -compress none \) \
|
||||
\( -clone 0 -resize 40 -colors 256 -compress none \) \
|
||||
\( -clone 1 -resize 48 -colors 256 -compress none \) \
|
||||
\( -clone 1 -resize 96 -colors 256 -compress none \) \
|
||||
\( -clone 1 -resize 128 -colors 256 -compress none \) \
|
||||
\( -clone 0 -resize 16 -compress none \) \
|
||||
\( -clone 0 -resize 20 -compress none \) \
|
||||
\( -clone 0 -resize 24 -compress none \) \
|
||||
\( -clone 0 -resize 32 -compress none \) \
|
||||
\( -clone 0 -resize 40 -compress none \) \
|
||||
\( -clone 1 -resize 48 -compress none \) \
|
||||
\( -clone 1 -resize 64 -compress none \) \
|
||||
\( -clone 1 -resize 96 -compress none \) \
|
||||
\( -clone 1 -resize 128 -compress none \) \
|
||||
\( -clone 1 -resize 256 -compress Zip \) \
|
||||
-delete 1 -delete 0 \
|
||||
-alpha remove ../icons/windows-icons/wsjtx.ico
|
||||
rm /tmp/image-0.png /tmp/image-1.png
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/wsjtx.ico
|
||||
#
|
||||
inkscape -z -e /dev/stdout -w 150 -h 57 -b white installer_logo.svg | tail -n +4 | \
|
||||
convert png:- -resize 150x57 +matte BMP3:../icons/windows-icons/installer_logo.bmp
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/installer_logo.bmp
|
||||
|
||||
#
|
||||
# Mac
|
||||
#
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_16x16.png -w 16 -h 16 wsjtx_globe_128x128.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_16x16@2x.png -w 32 -h 32 wsjtx_globe_128x128.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_32x32.png -w 32 -h 32 wsjtx_globe_128x128.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_32x32@2x.png -w 64 -h 64 wsjtx_globe_128x128.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_128x128.png -w 128 -h 128 wsjtx_globe_1024x1024.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_128x128@2x.png -w 256 -h 256 wsjtx_globe_1024x1024.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_256x256.png -w 256 -h 256 wsjtx_globe_1024x1024.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_256x256@2x.png -w 512 -h 512 wsjtx_globe_1024x1024.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_512x512.png -w 512 -h 512 wsjtx_globe_1024x1024.svg
|
||||
inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_512x512@2x.png -w 1024 -h 1024 wsjtx_globe_1024x1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Darwin/wsjtx.iconset/*
|
||||
#
|
||||
inkscape -z -e "../icons/Darwin/DragNDrop Background.png" -w 640 -h 480 -b white "DragNDrop Background.svg"
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' "../icons/Darwin/DragNDrop Background.png"
|
||||
|
||||
#
|
||||
# KDE & Gnome
|
||||
#
|
||||
inkscape -z -e ../icons/Unix/wsjtx_icon.png -w 128 -h 128 wsjtx_globe_1024x1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Unix/wsjtx_icon.png
|
602
artwork/wsjtx_globe_1024x1024.svg
Normal file
@ -0,0 +1,602 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/bill/Dropbox/src/wsjtx-dev/icons/windows-icons/icon_1024x1024.png"
|
||||
version="1.1"
|
||||
width="1024"
|
||||
sodipodi:version="0.32"
|
||||
sodipodi:docname="wsjtx_globe_1024x1024.svg"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
id="svg1432"
|
||||
height="1024">
|
||||
<sodipodi:namedview
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
id="base"
|
||||
inkscape:current-layer="svg1432"
|
||||
inkscape:cx="512"
|
||||
inkscape:cy="512"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-height="858"
|
||||
inkscape:window-width="1114"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="31"
|
||||
inkscape:zoom="0.69433594"
|
||||
pagecolor="#ffffff"
|
||||
showborder="true"
|
||||
showguides="true"
|
||||
showgrid="false"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="false" />
|
||||
<metadata
|
||||
id="metadata3060">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title />
|
||||
<dc:description>Simple globe centered on North America</dc:description>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>earth globe northamerica</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:publisher>
|
||||
<cc:Agent
|
||||
rdf:about="http://www.openclipart.org/">
|
||||
<dc:title>Open Clip Art Library</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Dan Gerhrads</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Dan Gerhrads</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>May 1, 2005</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://web.resource.org/cc/PublicDomain" />
|
||||
<dc:language>en</dc:language>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://web.resource.org/cc/PublicDomain">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1759">
|
||||
<linearGradient
|
||||
id="linearGradient37658">
|
||||
<stop
|
||||
style="stop-color:#2f7aff;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop37660" />
|
||||
<stop
|
||||
style="stop-color:#0000b3;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop37662" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient20137">
|
||||
<stop
|
||||
style="stop-color:#00bf00;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop20139" />
|
||||
<stop
|
||||
style="stop-color:#007500;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop20141" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient20143"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient37658"
|
||||
r="112.3373"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient37664"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.806632,1.239722)"
|
||||
fy="270.08731"
|
||||
fx="221.61394"
|
||||
cy="270.08731"
|
||||
cx="221.61394" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1902"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1904"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1906"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1908"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1910"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1912"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1914"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1916"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1918"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1920"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient37658-427"
|
||||
r="112.3373"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient37664-403"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.806632,1.239722)"
|
||||
fy="270.08731"
|
||||
fx="221.61394"
|
||||
cy="270.08731"
|
||||
cx="221.61394" />
|
||||
<linearGradient
|
||||
id="linearGradient37658-427">
|
||||
<stop
|
||||
style="stop-color:#488afe;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7178" />
|
||||
<stop
|
||||
style="stop-color:#0000cc;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7180" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-396"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1902-893"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-396">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7184" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7186" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-527"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1904-666"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-527">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7190" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7192" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-774"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1906-717"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-774">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7196" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7198" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-253"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1908-922"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-253">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7202" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7204" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-972"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1910-261"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-972">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7208" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7210" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-945"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1912-713"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-945">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7214" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7216" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-600"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1914-146"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-600">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7220" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7222" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-196"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1916-817"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-196">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7226" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7228" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-388"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1918-23"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-388">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7232" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7234" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-202"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient1920-260"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-202">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7238" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7240" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
xlink:href="#linearGradient20137-151"
|
||||
r="188.61865"
|
||||
inkscape:collect="always"
|
||||
id="radialGradient20143-884"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
fy="298.37747"
|
||||
fx="205.17723"
|
||||
cy="297.1124"
|
||||
cx="202.06305" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-151">
|
||||
<stop
|
||||
style="stop-color:#00d800;stop-opacity:1.0000000;"
|
||||
offset="0.0000000"
|
||||
id="stop7244" />
|
||||
<stop
|
||||
style="stop-color:#008e00;stop-opacity:1.0000000;"
|
||||
offset="1.0000000"
|
||||
id="stop7246" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<desc
|
||||
id="desc1434">wmf2svg</desc>
|
||||
<polyline
|
||||
transform="matrix(2.2285926,-3.5745108,2.3232002,1.4484387,-677.00545,692.67468)"
|
||||
style="fill:url(#radialGradient37664-403);fill-opacity:1;stroke:#0c0c0c;stroke-width:0.6875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4"
|
||||
points="103.191467,409.292114 100.730545,403.134064 98.525299,396.927094 96.543777,390.573547 94.785973,384.171082 93.219925,377.622009 91.909569,371.072968 90.790962,364.426147 89.928040,357.779327 89.256882,351.083649 88.777481,344.387970 88.521805,337.692261 88.489838,330.947693 88.681602,324.300873 89.033165,317.605194 89.640404,311.007263 90.407448,304.458221 91.398209,298.006897 92.580727,291.604431 93.986977,285.299744 95.553017,279.092804 97.342781,272.983582 99.324303,267.069916 101.465622,261.253937 103.830666,255.584595 106.387474,250.110733 109.104080,244.783508 112.012444,239.700668 115.112572,234.764420 118.404449,230.072571 121.856140,225.625061 125.499588,221.373062 129.302826,217.414291 133.265884,213.748779 137.260895,210.425354 141.351776,207.444077 145.506577,204.804901 149.725311,202.556717 153.975983,200.601776 158.258652,198.988937 162.573242,197.718231 166.919815,196.789627 171.266373,196.154266 175.644897,195.909912 179.991470,195.909912 184.338043,196.300888 188.652649,196.985123 192.967270,197.962585 197.217941,199.233307 201.468628,200.846130 205.623444,202.801071 209.746277,205.000397 213.805222,207.541809 217.768265,210.376495 221.667389,213.504395 225.470627,216.876678 229.177994,220.591064 232.789490,224.598709 236.273132,228.850708 239.628937,233.444839 242.856903,238.283325 245.957031,243.366180 248.929321,248.791153 251.709839,254.411621 254.362534,260.374207 256.791504,266.483398 258.996704,272.739227 261.010223,279.043915 262.768005,285.495270 264.302094,291.995453 265.644409,298.593384 266.731079,305.191315 267.625946,311.838104 268.297119,318.533813 268.744537,325.278351 269.000244,331.974030 269.032166,338.669739 268.872375,345.365448 268.488861,352.012238 267.913574,358.610168 267.114594,365.159241 266.123840,371.659424 264.941284,378.061890 263.567017,384.366577 261.969025,390.573547 260.211212,396.633850 258.229706,402.596436 256.056396,408.412384 253.723328,414.032837 251.166504,419.555603 248.449905,424.833923 245.509598,429.965668 242.409470,434.853027 239.149536,439.593750 235.697845,444.041260 232.054413,448.244385 228.219193,452.252045 224.288116,455.917542 220.261139,459.240967 216.170258,462.173401 212.015442,464.812561 207.828674,467.109589 203.577988,469.064545 199.263397,470.628510 194.948776,471.899200 190.634186,472.876678 186.255646,473.463196 181.909073,473.756439 177.562500,473.707550 173.215942,473.365448 168.869370,472.681213 164.586731,471.703735 160.304077,470.384155 156.085358,468.771332 151.898590,466.865265 147.807709,464.617065 143.748779,462.124512 139.753769,459.289856 135.854645,456.161926 132.051392,452.740753 128.344025,449.026367 124.764503,445.067596 121.280853,440.766724 117.893089,436.221466 114.665123,431.382996 111.565002,426.251282 108.624680,420.875153 105.812187,415.205811 103.191467,409.292114 "
|
||||
id="polyline1560" />
|
||||
<g
|
||||
transform="matrix(4.1610916,0,0,4.1610916,-268.34786,-716.6628)"
|
||||
style="fill:url(#radialGradient1902-893);fill-opacity:1;fill-rule:evenodd"
|
||||
inkscape:label="Continents"
|
||||
id="Continents">
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1904-666);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="132.818436,384.610931 131.827682,384.953064 128.184235,388.227600 126.074867,388.667450 123.422180,385.441803 120.417938,385.832794 117.126053,389.107300 116.838409,393.896912 116.902328,400.641479 115.655891,409.292114 114.409447,415.352448 115.432167,418.235992 113.578484,420.386444 111.053642,420.924042 109.903076,422.732361 109.871117,423.172241 116.295090,433.728943 123.262383,443.161530 130.836914,451.421204 140.904327,460.071808 151.195465,466.425385 153.528549,464.470428 157.683365,463.248596 162.509323,463.688446 165.321808,463.835083 165.577484,459.387573 165.066132,457.628113 162.381485,454.793457 158.162750,448.244385 155.094604,442.526184 156.884354,433.680054 153.496597,425.664764 151.738800,419.017975 150.811951,410.220703 147.743790,405.479980 144.579727,397.953430 142.438416,395.851868 141.447662,389.058441 138.858887,386.419281 135.886597,384.855316 133.968994,384.219940 133.201950,380.994293 131.667877,376.351288 131.923553,370.926331 132.850403,369.069122 135.407196,368.140533 137.292847,366.625458 137.452652,362.911041 136.557755,358.316925 134.736038,353.967194 135.886597,352.158844 138.123810,351.914490 141.255890,349.812927 141.223938,346.245178 139.050644,343.557098 136.973236,343.654846 135.471115,341.602142 134.863876,337.643402 135.407196,330.947693 138.219681,327.575439 141.191971,325.571625 145.890106,326.158081 148.510818,330.361237 149.469635,333.635773 149.565506,336.421570 150.588226,338.327637 152.314072,339.891571 153.656403,344.045837 154.135788,348.737701 152.665634,353.771698 151.578995,357.730469 152.985229,359.587646 155.094604,356.557495 156.468872,353.234070 159.153519,351.572388 161.870132,351.474609 165.865143,353.967194 168.517807,355.237885 171.458130,352.696472 175.389221,351.425751 178.457397,351.621246 179.416199,352.940857 181.301834,352.403229 184.497833,352.452087 185.968002,353.918335 186.095840,356.704102 187.949539,357.143951 190.602203,359.392151 192.551788,361.004974 193.670380,358.610168 191.912582,355.482269 191.561020,353.576202 193.095093,351.767883 192.583740,349.030945 190.442413,347.906860 188.620697,346.147400 189.259888,345.169952 191.976501,346.049652 195.044662,348.835449 196.259140,351.230255 198.624191,353.771698 200.733551,353.282928 202.363510,350.057312 203.609955,343.996979 204.153275,334.710999 202.906830,330.752228 202.331558,328.406281 203.258408,324.594147 202.459396,316.676605 201.053146,314.819397 198.496353,317.311981 195.300339,320.781982 194.788971,323.567780 192.871368,323.518921 189.739288,325.669373 186.894852,327.477692 187.342285,323.470032 188.524796,319.755646 190.666138,317.311981 190.314575,313.450958 189.132050,308.123718 190.090851,304.898071 193.734299,301.965637 197.601471,302.063385 202.107834,303.773987 205.559525,305.875519 207.796722,306.217651 208.435928,302.698761 206.678116,300.548309 206.038925,296.980560 207.796722,294.585724 209.490601,292.581909 208.627686,290.529205 205.783249,292.386414 203.961517,291.555573 204.057388,289.258514 207.189484,286.130615 208.787491,282.953796 208.627686,279.532684 209.874130,275.427277 208.276123,272.152740 206.198730,271.273010 205.783249,275.231781 204.185242,277.479980 203.929565,281.927460 201.788239,283.393677 202.715073,269.513580 203.546036,261.938171 204.888351,257.050812 206.997711,254.998108 209.426682,251.283707 209.171005,248.595657 207.541046,244.734634 206.262650,243.708313 205.687363,241.362366 204.600723,239.211929 202.715073,239.798416 201.276871,239.896149 200.030426,234.959930 197.697342,233.395966 194.916824,233.884705 192.743546,234.715546 191.976501,231.929764 192.264130,228.068741 190.698090,230.903412 191.145538,235.986282 191.912582,241.362366 192.104340,246.347473 190.154770,250.208496 187.406204,253.238663 182.548279,255.780090 177.338791,257.979401 173.695343,260.911804 171.745789,262.866760 169.956009,265.603668 166.696106,264.919464 162.924805,265.066071 158.290604,266.092438 153.464630,269.220306 146.944778,274.352051 145.091095,277.479980 141.671371,283.442566 138.954773,288.036682 135.918564,293.950378 135.119568,298.593384 135.503082,301.476929 136.334045,297.420410 138.091858,294.048126 140.361008,290.871338 142.310577,287.987793 143.365265,289.502869 141.959015,292.190948 138.283615,298.055786 135.087601,306.071045 132.946274,309.492188 130.900833,310.176422 129.686356,314.330688 130.069870,319.755646 128.919312,329.628113 129.494598,334.515503 130.677109,341.797638 131.603958,350.546021 131.540024,355.482269 130.964752,360.565125 128.280106,366.772064 129.047150,373.125641 129.654404,377.084412 131.476105,380.456665 132.818436,384.610931 "
|
||||
id="polyline1460" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1906-717);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="144.579727,355.237885 146.497345,357.241699 147.583984,362.324585 148.638672,368.384918 149.980988,372.539154 150.364502,377.035553 148.798477,377.279877 146.753021,373.467743 146.976746,368.433777 146.593216,362.959930 144.579727,355.237885 "
|
||||
id="polyline1464" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1908-922);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="150.108826,378.892731 148.830429,379.870209 149.309830,383.584595 150.172745,386.614777 152.633667,390.964508 152.505829,386.565887 153.336792,386.077148 153.017197,383.780090 150.108826,378.892731 "
|
||||
id="polyline1468" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1910-261);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="201.181000,353.820557 200.797470,356.459747 200.413956,361.053864 199.966507,365.990082 199.007706,368.238281 196.866379,363.204285 197.921066,357.681580 199.103592,354.895782 201.181000,353.820557 "
|
||||
id="polyline1472" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1912-713);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="204.632675,320.342133 203.993469,316.774353 202.906830,312.668976 206.230682,314.575043 208.883362,312.277985 209.842163,309.052338 208.915314,303.822876 208.563766,299.961853 209.874130,294.487976 212.430923,295.514343 213.261887,299.570862 213.581497,305.386810 213.837173,310.958405 212.111328,314.917175 211.951538,317.018738 212.335052,321.466217 210.800964,325.913727 208.691605,325.376099 206.454407,326.646820 205.016205,324.203125 204.632675,320.342133 "
|
||||
id="polyline1476" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1914-146);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="213.773254,334.955322 213.709335,340.820160 213.869125,343.263885 216.362015,346.636139 219.526062,350.594910 218.695099,344.632324 219.685867,341.406677 222.722061,339.060730 227.835663,334.662109 230.648163,335.786194 233.332809,333.147003 234.355515,326.011475 234.675125,319.218048 233.556534,312.522369 233.588486,306.071045 231.511078,300.646057 228.890350,297.420410 226.365509,296.980560 225.246902,294.830109 226.365509,293.461639 225.023193,290.235992 222.338547,287.401337 220.964264,289.698364 217.352783,290.675842 216.330063,294.292480 216.362015,299.375336 220.644653,297.469269 222.754028,297.958038 220.708588,301.623535 219.941544,306.852997 220.293106,310.714020 219.845657,317.654083 219.078629,323.909912 214.955765,329.237122 215.083618,333.000397 213.773254,334.955322 "
|
||||
id="polyline1480" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1916-817);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="232.533813,338.767487 230.200729,341.748779 229.401718,344.436798 229.721313,347.662476 233.460648,348.542206 237.040161,346.929382 237.231934,342.872864 235.378250,340.478088 233.620453,340.282562 232.533813,338.767487 "
|
||||
id="polyline1484" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1918-23);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="264.877380,375.813690 262.991730,379.919098 260.371002,382.167236 258.645172,385.148529 258.645172,389.693817 256.887360,391.062256 255.161514,388.911804 254.522308,386.956848 252.732559,385.197418 253.339798,391.697632 251.709839,392.332977 249.792236,387.738861 248.322067,389.840393 246.724060,393.017212 244.327072,395.412018 242.952774,399.664001 241.514587,403.329529 241.067139,407.337158 239.565018,407.190552 238.318573,407.777039 235.665894,409.096649 233.173004,407.679291 233.141052,401.570068 234.707092,399.712891 235.122574,395.705231 237.231934,393.652588 239.692856,394.434540 241.834183,392.039734 240.843414,387.152374 239.980499,385.099670 240.907349,382.265015 244.550781,379.332581 247.043671,372.685760 247.139542,368.384918 249.121078,365.110352 252.125320,361.151611 254.202728,353.967194 256.503845,350.399414 255.992477,346.684998 255.864655,342.335266 255.832672,339.744965 254.586243,339.353973 254.074875,342.139771 251.581985,340.722412 252.189240,343.654846 251.997467,347.760223 251.294357,352.647583 251.486115,357.632721 248.897354,354.993530 246.915833,355.335632 246.020950,351.670135 245.733307,346.489502 247.778748,340.233704 248.002472,336.763672 247.586990,329.774750 247.299347,324.545258 248.705597,319.315765 251.230438,321.319580 254.234680,326.451324 255.608963,324.154266 255.097595,321.417358 253.915070,321.612854 252.253159,317.702942 254.202728,316.774353 253.883118,313.548676 254.714081,310.469666 254.746033,303.969452 255.001724,293.999268 253.947052,293.070679 252.349030,292.777405 250.495346,290.382599 250.303589,287.938934 249.312836,284.077911 247.235428,279.679291 245.381744,278.261963 242.920822,273.912201 241.546539,269.122559 241.035172,265.408173 242.345535,263.502106 240.555771,258.761383 238.734055,257.979401 236.560776,253.336411 234.131805,249.573120 232.501846,251.185959 230.648163,249.768631 228.315079,245.272247 226.653152,244.343658 225.438675,246.494095 223.808701,245.174515 220.964264,239.896149 219.430176,240.775894 217.672379,242.193222 216.170258,241.069122 212.718582,239.945023 210.257645,239.114182 207.509079,237.647964 205.527557,234.422318 206.805954,234.471191 209.362778,234.520065 210.225693,233.004974 208.979248,230.072571 209.810211,226.993515 210.737045,224.109970 213.325806,224.794220 215.722824,225.967178 216.745544,227.873245 219.781738,227.531128 222.114822,224.549835 224.032425,226.553665 226.844925,229.241714 229.785248,226.700287 230.136795,223.425751 229.721313,220.933197 236.592728,229.290573 241.898102,236.768250 246.564270,244.490280 251.837677,254.704865 255.960541,264.332947 260.115326,276.258118 264.302094,291.995453 267.210449,308.514740 268.393005,320.537628 269.000244,333.538025 268.840424,345.169952 268.201202,356.557495 266.794983,367.358551 265.516571,374.933960 264.877380,375.813690 "
|
||||
id="polyline1488" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient1920-260);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="244.774506,357.779327 243.304337,356.313110 242.345535,358.512421 241.131058,362.666687 239.916580,364.817108 238.062897,367.945038 237.615448,372.930145 238.414459,375.324951 239.373245,373.125641 239.852661,368.727020 240.683624,367.602905 241.067139,371.121826 241.290848,375.129456 241.834183,378.452850 243.336304,376.986664 243.528061,376.937775 243.719818,376.888916 243.911591,376.791168 244.071396,376.644531 244.390976,376.351288 244.678635,376.009186 244.934311,375.618195 245.126068,375.324951 245.221939,375.129456 245.285858,375.031708 245.253906,372.490265 244.486862,365.159241 244.199219,361.884705 244.774506,357.779327 "
|
||||
id="polyline1492" />
|
||||
<polyline
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)"
|
||||
style="fill:url(#radialGradient20143-884);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
points="262.192749,389.449432 260.115326,391.160004 258.261658,394.287933 256.663635,394.532288 255.992477,392.968323 255.065643,395.802979 253.851151,400.885864 250.783005,404.209290 250.175766,401.276855 249.344803,399.810638 245.573502,402.889679 242.537292,406.213104 240.747543,406.995056 238.126801,409.878601 236.049423,409.976349 233.748291,411.002716 231.606964,415.010315 228.986237,420.826294 226.333542,423.123352 220.996231,428.059601 216.617691,433.680054 213.485611,439.349396 210.385483,446.582703 209.171005,453.278381 210.353516,458.605591 214.220703,458.654480 216.777496,458.752228 218.535294,460.560547 223.648911,456.504028 228.187241,452.252045 232.693619,447.511292 237.775253,441.402100 243.400223,433.386810 247.906586,425.811401 251.677872,418.529236 255.161514,410.611694 258.069885,403.134064 260.690613,394.923279 262.192749,389.449432 "
|
||||
id="polyline1496" />
|
||||
</g>
|
||||
<text
|
||||
transform="matrix(0.98267844,-0.18531886,0.18531886,0.98267844,0,0)"
|
||||
sodipodi:linespacing="125%"
|
||||
id="text3096"
|
||||
y="456.47293"
|
||||
x="394.15689"
|
||||
style="font-size:187.56237793000002512px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff8119;fill-opacity:0.77168947;stroke:none;font-family:Purisa;-inkscape-font-specification:Purisa Bold"
|
||||
xml:space="preserve"><tspan
|
||||
dy="-109.78781"
|
||||
dx="0 -9.8032694 -11.763925 -17.645885"
|
||||
id="tspan3102"
|
||||
y="456.47293"
|
||||
x="394.15692"
|
||||
sodipodi:role="line">WSJT-X</tspan><tspan
|
||||
dx="0 -17.645885 -23.527849 -13.724579 29.30662 29.30662 -17.645885 -23.527849"
|
||||
id="tspan3874"
|
||||
y="690.92584"
|
||||
x="394.15689"
|
||||
sodipodi:role="line">JT65&JT9</tspan><tspan
|
||||
dy="91.489815"
|
||||
dx="0 0 0 0 -17.645885 -5.8819623 -17.645885"
|
||||
id="tspan3100"
|
||||
y="925.37885"
|
||||
x="394.15689"
|
||||
sodipodi:role="line">by K1JT</tspan></text>
|
||||
</svg>
|
After Width: | Height: | Size: 33 KiB |
839
artwork/wsjtx_globe_128x128.svg
Normal file
@ -0,0 +1,839 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="128"
|
||||
id="svg1432"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="wsjtx_globe_128x128.svg"
|
||||
sodipodi:version="0.32"
|
||||
width="128"
|
||||
version="1.1">
|
||||
<sodipodi:namedview
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
id="base"
|
||||
inkscape:current-layer="svg1432"
|
||||
inkscape:cx="64"
|
||||
inkscape:cy="64"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-width="1846"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="24"
|
||||
inkscape:zoom="6.875"
|
||||
pagecolor="#ffffff"
|
||||
showborder="false"
|
||||
showguides="true"
|
||||
showgrid="false"
|
||||
inkscape:window-maximized="0"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="false"
|
||||
inkscape:snap-from-guide="true" />
|
||||
<metadata
|
||||
id="metadata3060">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title />
|
||||
<dc:description>Simple globe centered on North America</dc:description>
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>earth globe northamerica</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:publisher>
|
||||
<cc:Agent
|
||||
rdf:about="http://www.openclipart.org/">
|
||||
<dc:title>Open Clip Art Library</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Dan Gerhrads</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Dan Gerhrads</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date>May 1, 2005</dc:date>
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://web.resource.org/cc/PublicDomain" />
|
||||
<dc:language>en</dc:language>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://web.resource.org/cc/PublicDomain">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs1759">
|
||||
<linearGradient
|
||||
id="linearGradient37658">
|
||||
<stop
|
||||
id="stop37660"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#619afe;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop37662"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#0000e5;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient20137">
|
||||
<stop
|
||||
id="stop20139"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#00f100;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop20141"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00a700;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient20143"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="221.61394"
|
||||
cy="270.08731"
|
||||
fx="221.61394"
|
||||
fy="270.08731"
|
||||
gradientTransform="scale(0.806632,1.239722)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient37664"
|
||||
inkscape:collect="always"
|
||||
r="112.3373"
|
||||
xlink:href="#linearGradient37658" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1902"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1904"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1906"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1908"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1910"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1912"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1914"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1916"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1918"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1920"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137" />
|
||||
<radialGradient
|
||||
cx="221.61394"
|
||||
cy="270.08731"
|
||||
fx="221.61394"
|
||||
fy="270.08731"
|
||||
gradientTransform="scale(0.806632,1.239722)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient37664-284"
|
||||
inkscape:collect="always"
|
||||
r="112.3373"
|
||||
xlink:href="#linearGradient37658-332" />
|
||||
<linearGradient
|
||||
id="linearGradient37658-332">
|
||||
<stop
|
||||
id="stop8375"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#7aaafe;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8377"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#0000fe;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1902-108"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-970" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-970">
|
||||
<stop
|
||||
id="stop8381"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8383"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1904-972"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-24" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-24">
|
||||
<stop
|
||||
id="stop8387"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8389"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1906-828"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-376" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-376">
|
||||
<stop
|
||||
id="stop8393"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8395"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1908-502"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-289" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-289">
|
||||
<stop
|
||||
id="stop8399"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8401"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1910-187"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-879" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-879">
|
||||
<stop
|
||||
id="stop8405"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8407"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1912-38"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-891" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-891">
|
||||
<stop
|
||||
id="stop8411"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8413"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1914-119"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-529" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-529">
|
||||
<stop
|
||||
id="stop8417"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8419"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1916-825"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-469" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-469">
|
||||
<stop
|
||||
id="stop8423"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8425"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1918-387"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-981" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-981">
|
||||
<stop
|
||||
id="stop8429"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8431"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1920-264"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-421" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-421">
|
||||
<stop
|
||||
id="stop8435"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8437"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient20143-627"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-58" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-58">
|
||||
<stop
|
||||
id="stop8441"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#0bfe0b;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8443"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00c000;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="221.61394"
|
||||
cy="270.08731"
|
||||
fx="221.61394"
|
||||
fy="270.08731"
|
||||
gradientTransform="scale(0.806632,1.239722)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient37664-284-781"
|
||||
inkscape:collect="always"
|
||||
r="112.3373"
|
||||
xlink:href="#linearGradient37658-332-354" />
|
||||
<linearGradient
|
||||
id="linearGradient37658-332-354">
|
||||
<stop
|
||||
id="stop8612"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#93bafe;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8614"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#1818ff;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1902-108-124"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-970-892" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-970-892">
|
||||
<stop
|
||||
id="stop8618"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8620"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1904-972-786"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-24-119" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-24-119">
|
||||
<stop
|
||||
id="stop8624"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8626"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1906-828-861"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-376-597" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-376-597">
|
||||
<stop
|
||||
id="stop8630"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8632"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1908-502-115"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-289-824" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-289-824">
|
||||
<stop
|
||||
id="stop8636"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8638"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1910-187-480"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-879-315" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-879-315">
|
||||
<stop
|
||||
id="stop8642"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8644"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1912-38-980"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-891-460" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-891-460">
|
||||
<stop
|
||||
id="stop8648"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8650"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1914-119-179"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-529-932" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-529-932">
|
||||
<stop
|
||||
id="stop8654"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8656"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1916-825-422"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-469-24" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-469-24">
|
||||
<stop
|
||||
id="stop8660"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8662"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1918-387-755"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-981-521" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-981-521">
|
||||
<stop
|
||||
id="stop8666"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8668"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient1920-264-381"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-421-297" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-421-297">
|
||||
<stop
|
||||
id="stop8672"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8674"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="202.06305"
|
||||
cy="297.1124"
|
||||
fx="205.17723"
|
||||
fy="298.37747"
|
||||
gradientTransform="scale(0.893959,1.11862)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient20143-627-23"
|
||||
inkscape:collect="always"
|
||||
r="188.61865"
|
||||
xlink:href="#linearGradient20137-58-615" />
|
||||
<linearGradient
|
||||
id="linearGradient20137-58-615">
|
||||
<stop
|
||||
id="stop8678"
|
||||
offset="0.0000000"
|
||||
style="stop-color:#24fe24;stop-opacity:1.0000000;" />
|
||||
<stop
|
||||
id="stop8680"
|
||||
offset="1.0000000"
|
||||
style="stop-color:#00d900;stop-opacity:1.0000000;" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<desc
|
||||
id="desc1434">wmf2svg</desc>
|
||||
<polyline
|
||||
id="polyline1560"
|
||||
points="103.191467,409.292114 100.730545,403.134064 98.525299,396.927094 96.543777,390.573547 94.785973,384.171082 93.219925,377.622009 91.909569,371.072968 90.790962,364.426147 89.928040,357.779327 89.256882,351.083649 88.777481,344.387970 88.521805,337.692261 88.489838,330.947693 88.681602,324.300873 89.033165,317.605194 89.640404,311.007263 90.407448,304.458221 91.398209,298.006897 92.580727,291.604431 93.986977,285.299744 95.553017,279.092804 97.342781,272.983582 99.324303,267.069916 101.465622,261.253937 103.830666,255.584595 106.387474,250.110733 109.104080,244.783508 112.012444,239.700668 115.112572,234.764420 118.404449,230.072571 121.856140,225.625061 125.499588,221.373062 129.302826,217.414291 133.265884,213.748779 137.260895,210.425354 141.351776,207.444077 145.506577,204.804901 149.725311,202.556717 153.975983,200.601776 158.258652,198.988937 162.573242,197.718231 166.919815,196.789627 171.266373,196.154266 175.644897,195.909912 179.991470,195.909912 184.338043,196.300888 188.652649,196.985123 192.967270,197.962585 197.217941,199.233307 201.468628,200.846130 205.623444,202.801071 209.746277,205.000397 213.805222,207.541809 217.768265,210.376495 221.667389,213.504395 225.470627,216.876678 229.177994,220.591064 232.789490,224.598709 236.273132,228.850708 239.628937,233.444839 242.856903,238.283325 245.957031,243.366180 248.929321,248.791153 251.709839,254.411621 254.362534,260.374207 256.791504,266.483398 258.996704,272.739227 261.010223,279.043915 262.768005,285.495270 264.302094,291.995453 265.644409,298.593384 266.731079,305.191315 267.625946,311.838104 268.297119,318.533813 268.744537,325.278351 269.000244,331.974030 269.032166,338.669739 268.872375,345.365448 268.488861,352.012238 267.913574,358.610168 267.114594,365.159241 266.123840,371.659424 264.941284,378.061890 263.567017,384.366577 261.969025,390.573547 260.211212,396.633850 258.229706,402.596436 256.056396,408.412384 253.723328,414.032837 251.166504,419.555603 248.449905,424.833923 245.509598,429.965668 242.409470,434.853027 239.149536,439.593750 235.697845,444.041260 232.054413,448.244385 228.219193,452.252045 224.288116,455.917542 220.261139,459.240967 216.170258,462.173401 212.015442,464.812561 207.828674,467.109589 203.577988,469.064545 199.263397,470.628510 194.948776,471.899200 190.634186,472.876678 186.255646,473.463196 181.909073,473.756439 177.562500,473.707550 173.215942,473.365448 168.869370,472.681213 164.586731,471.703735 160.304077,470.384155 156.085358,468.771332 151.898590,466.865265 147.807709,464.617065 143.748779,462.124512 139.753769,459.289856 135.854645,456.161926 132.051392,452.740753 128.344025,449.026367 124.764503,445.067596 121.280853,440.766724 117.893089,436.221466 114.665123,431.382996 111.565002,426.251282 108.624680,420.875153 105.812187,415.205811 103.191467,409.292114 "
|
||||
style="fill:url(#radialGradient37664-284-781);fill-opacity:1;stroke:#181818;stroke-width:0.6875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4"
|
||||
transform="matrix(0.3640721,-0.58678257,0.3795276,0.23777199,-128.13162,89.336986)" />
|
||||
<g
|
||||
id="Continents"
|
||||
inkscape:label="Continents"
|
||||
style="fill:url(#radialGradient1902-108-124);fill-opacity:1;fill-rule:evenodd"
|
||||
transform="matrix(0.67977314,0,0,0.68307417,-61.371653,-142.01634)">
|
||||
<polyline
|
||||
id="polyline1460"
|
||||
points="132.818436,384.610931 131.827682,384.953064 128.184235,388.227600 126.074867,388.667450 123.422180,385.441803 120.417938,385.832794 117.126053,389.107300 116.838409,393.896912 116.902328,400.641479 115.655891,409.292114 114.409447,415.352448 115.432167,418.235992 113.578484,420.386444 111.053642,420.924042 109.903076,422.732361 109.871117,423.172241 116.295090,433.728943 123.262383,443.161530 130.836914,451.421204 140.904327,460.071808 151.195465,466.425385 153.528549,464.470428 157.683365,463.248596 162.509323,463.688446 165.321808,463.835083 165.577484,459.387573 165.066132,457.628113 162.381485,454.793457 158.162750,448.244385 155.094604,442.526184 156.884354,433.680054 153.496597,425.664764 151.738800,419.017975 150.811951,410.220703 147.743790,405.479980 144.579727,397.953430 142.438416,395.851868 141.447662,389.058441 138.858887,386.419281 135.886597,384.855316 133.968994,384.219940 133.201950,380.994293 131.667877,376.351288 131.923553,370.926331 132.850403,369.069122 135.407196,368.140533 137.292847,366.625458 137.452652,362.911041 136.557755,358.316925 134.736038,353.967194 135.886597,352.158844 138.123810,351.914490 141.255890,349.812927 141.223938,346.245178 139.050644,343.557098 136.973236,343.654846 135.471115,341.602142 134.863876,337.643402 135.407196,330.947693 138.219681,327.575439 141.191971,325.571625 145.890106,326.158081 148.510818,330.361237 149.469635,333.635773 149.565506,336.421570 150.588226,338.327637 152.314072,339.891571 153.656403,344.045837 154.135788,348.737701 152.665634,353.771698 151.578995,357.730469 152.985229,359.587646 155.094604,356.557495 156.468872,353.234070 159.153519,351.572388 161.870132,351.474609 165.865143,353.967194 168.517807,355.237885 171.458130,352.696472 175.389221,351.425751 178.457397,351.621246 179.416199,352.940857 181.301834,352.403229 184.497833,352.452087 185.968002,353.918335 186.095840,356.704102 187.949539,357.143951 190.602203,359.392151 192.551788,361.004974 193.670380,358.610168 191.912582,355.482269 191.561020,353.576202 193.095093,351.767883 192.583740,349.030945 190.442413,347.906860 188.620697,346.147400 189.259888,345.169952 191.976501,346.049652 195.044662,348.835449 196.259140,351.230255 198.624191,353.771698 200.733551,353.282928 202.363510,350.057312 203.609955,343.996979 204.153275,334.710999 202.906830,330.752228 202.331558,328.406281 203.258408,324.594147 202.459396,316.676605 201.053146,314.819397 198.496353,317.311981 195.300339,320.781982 194.788971,323.567780 192.871368,323.518921 189.739288,325.669373 186.894852,327.477692 187.342285,323.470032 188.524796,319.755646 190.666138,317.311981 190.314575,313.450958 189.132050,308.123718 190.090851,304.898071 193.734299,301.965637 197.601471,302.063385 202.107834,303.773987 205.559525,305.875519 207.796722,306.217651 208.435928,302.698761 206.678116,300.548309 206.038925,296.980560 207.796722,294.585724 209.490601,292.581909 208.627686,290.529205 205.783249,292.386414 203.961517,291.555573 204.057388,289.258514 207.189484,286.130615 208.787491,282.953796 208.627686,279.532684 209.874130,275.427277 208.276123,272.152740 206.198730,271.273010 205.783249,275.231781 204.185242,277.479980 203.929565,281.927460 201.788239,283.393677 202.715073,269.513580 203.546036,261.938171 204.888351,257.050812 206.997711,254.998108 209.426682,251.283707 209.171005,248.595657 207.541046,244.734634 206.262650,243.708313 205.687363,241.362366 204.600723,239.211929 202.715073,239.798416 201.276871,239.896149 200.030426,234.959930 197.697342,233.395966 194.916824,233.884705 192.743546,234.715546 191.976501,231.929764 192.264130,228.068741 190.698090,230.903412 191.145538,235.986282 191.912582,241.362366 192.104340,246.347473 190.154770,250.208496 187.406204,253.238663 182.548279,255.780090 177.338791,257.979401 173.695343,260.911804 171.745789,262.866760 169.956009,265.603668 166.696106,264.919464 162.924805,265.066071 158.290604,266.092438 153.464630,269.220306 146.944778,274.352051 145.091095,277.479980 141.671371,283.442566 138.954773,288.036682 135.918564,293.950378 135.119568,298.593384 135.503082,301.476929 136.334045,297.420410 138.091858,294.048126 140.361008,290.871338 142.310577,287.987793 143.365265,289.502869 141.959015,292.190948 138.283615,298.055786 135.087601,306.071045 132.946274,309.492188 130.900833,310.176422 129.686356,314.330688 130.069870,319.755646 128.919312,329.628113 129.494598,334.515503 130.677109,341.797638 131.603958,350.546021 131.540024,355.482269 130.964752,360.565125 128.280106,366.772064 129.047150,373.125641 129.654404,377.084412 131.476105,380.456665 132.818436,384.610931 "
|
||||
style="fill:url(#radialGradient1904-972-786);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1464"
|
||||
points="144.579727,355.237885 146.497345,357.241699 147.583984,362.324585 148.638672,368.384918 149.980988,372.539154 150.364502,377.035553 148.798477,377.279877 146.753021,373.467743 146.976746,368.433777 146.593216,362.959930 144.579727,355.237885 "
|
||||
style="fill:url(#radialGradient1906-828-861);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1468"
|
||||
points="150.108826,378.892731 148.830429,379.870209 149.309830,383.584595 150.172745,386.614777 152.633667,390.964508 152.505829,386.565887 153.336792,386.077148 153.017197,383.780090 150.108826,378.892731 "
|
||||
style="fill:url(#radialGradient1908-502-115);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1472"
|
||||
points="201.181000,353.820557 200.797470,356.459747 200.413956,361.053864 199.966507,365.990082 199.007706,368.238281 196.866379,363.204285 197.921066,357.681580 199.103592,354.895782 201.181000,353.820557 "
|
||||
style="fill:url(#radialGradient1910-187-480);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1476"
|
||||
points="204.632675,320.342133 203.993469,316.774353 202.906830,312.668976 206.230682,314.575043 208.883362,312.277985 209.842163,309.052338 208.915314,303.822876 208.563766,299.961853 209.874130,294.487976 212.430923,295.514343 213.261887,299.570862 213.581497,305.386810 213.837173,310.958405 212.111328,314.917175 211.951538,317.018738 212.335052,321.466217 210.800964,325.913727 208.691605,325.376099 206.454407,326.646820 205.016205,324.203125 204.632675,320.342133 "
|
||||
style="fill:url(#radialGradient1912-38-980);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1480"
|
||||
points="213.773254,334.955322 213.709335,340.820160 213.869125,343.263885 216.362015,346.636139 219.526062,350.594910 218.695099,344.632324 219.685867,341.406677 222.722061,339.060730 227.835663,334.662109 230.648163,335.786194 233.332809,333.147003 234.355515,326.011475 234.675125,319.218048 233.556534,312.522369 233.588486,306.071045 231.511078,300.646057 228.890350,297.420410 226.365509,296.980560 225.246902,294.830109 226.365509,293.461639 225.023193,290.235992 222.338547,287.401337 220.964264,289.698364 217.352783,290.675842 216.330063,294.292480 216.362015,299.375336 220.644653,297.469269 222.754028,297.958038 220.708588,301.623535 219.941544,306.852997 220.293106,310.714020 219.845657,317.654083 219.078629,323.909912 214.955765,329.237122 215.083618,333.000397 213.773254,334.955322 "
|
||||
style="fill:url(#radialGradient1914-119-179);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1484"
|
||||
points="232.533813,338.767487 230.200729,341.748779 229.401718,344.436798 229.721313,347.662476 233.460648,348.542206 237.040161,346.929382 237.231934,342.872864 235.378250,340.478088 233.620453,340.282562 232.533813,338.767487 "
|
||||
style="fill:url(#radialGradient1916-825-422);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1488"
|
||||
points="264.877380,375.813690 262.991730,379.919098 260.371002,382.167236 258.645172,385.148529 258.645172,389.693817 256.887360,391.062256 255.161514,388.911804 254.522308,386.956848 252.732559,385.197418 253.339798,391.697632 251.709839,392.332977 249.792236,387.738861 248.322067,389.840393 246.724060,393.017212 244.327072,395.412018 242.952774,399.664001 241.514587,403.329529 241.067139,407.337158 239.565018,407.190552 238.318573,407.777039 235.665894,409.096649 233.173004,407.679291 233.141052,401.570068 234.707092,399.712891 235.122574,395.705231 237.231934,393.652588 239.692856,394.434540 241.834183,392.039734 240.843414,387.152374 239.980499,385.099670 240.907349,382.265015 244.550781,379.332581 247.043671,372.685760 247.139542,368.384918 249.121078,365.110352 252.125320,361.151611 254.202728,353.967194 256.503845,350.399414 255.992477,346.684998 255.864655,342.335266 255.832672,339.744965 254.586243,339.353973 254.074875,342.139771 251.581985,340.722412 252.189240,343.654846 251.997467,347.760223 251.294357,352.647583 251.486115,357.632721 248.897354,354.993530 246.915833,355.335632 246.020950,351.670135 245.733307,346.489502 247.778748,340.233704 248.002472,336.763672 247.586990,329.774750 247.299347,324.545258 248.705597,319.315765 251.230438,321.319580 254.234680,326.451324 255.608963,324.154266 255.097595,321.417358 253.915070,321.612854 252.253159,317.702942 254.202728,316.774353 253.883118,313.548676 254.714081,310.469666 254.746033,303.969452 255.001724,293.999268 253.947052,293.070679 252.349030,292.777405 250.495346,290.382599 250.303589,287.938934 249.312836,284.077911 247.235428,279.679291 245.381744,278.261963 242.920822,273.912201 241.546539,269.122559 241.035172,265.408173 242.345535,263.502106 240.555771,258.761383 238.734055,257.979401 236.560776,253.336411 234.131805,249.573120 232.501846,251.185959 230.648163,249.768631 228.315079,245.272247 226.653152,244.343658 225.438675,246.494095 223.808701,245.174515 220.964264,239.896149 219.430176,240.775894 217.672379,242.193222 216.170258,241.069122 212.718582,239.945023 210.257645,239.114182 207.509079,237.647964 205.527557,234.422318 206.805954,234.471191 209.362778,234.520065 210.225693,233.004974 208.979248,230.072571 209.810211,226.993515 210.737045,224.109970 213.325806,224.794220 215.722824,225.967178 216.745544,227.873245 219.781738,227.531128 222.114822,224.549835 224.032425,226.553665 226.844925,229.241714 229.785248,226.700287 230.136795,223.425751 229.721313,220.933197 236.592728,229.290573 241.898102,236.768250 246.564270,244.490280 251.837677,254.704865 255.960541,264.332947 260.115326,276.258118 264.302094,291.995453 267.210449,308.514740 268.393005,320.537628 269.000244,333.538025 268.840424,345.169952 268.201202,356.557495 266.794983,367.358551 265.516571,374.933960 264.877380,375.813690 "
|
||||
style="fill:url(#radialGradient1918-387-755);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1492"
|
||||
points="244.774506,357.779327 243.304337,356.313110 242.345535,358.512421 241.131058,362.666687 239.916580,364.817108 238.062897,367.945038 237.615448,372.930145 238.414459,375.324951 239.373245,373.125641 239.852661,368.727020 240.683624,367.602905 241.067139,371.121826 241.290848,375.129456 241.834183,378.452850 243.336304,376.986664 243.528061,376.937775 243.719818,376.888916 243.911591,376.791168 244.071396,376.644531 244.390976,376.351288 244.678635,376.009186 244.934311,375.618195 245.126068,375.324951 245.221939,375.129456 245.285858,375.031708 245.253906,372.490265 244.486862,365.159241 244.199219,361.884705 244.774506,357.779327 "
|
||||
style="fill:url(#radialGradient1920-264-381);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
<polyline
|
||||
id="polyline1496"
|
||||
points="262.192749,389.449432 260.115326,391.160004 258.261658,394.287933 256.663635,394.532288 255.992477,392.968323 255.065643,395.802979 253.851151,400.885864 250.783005,404.209290 250.175766,401.276855 249.344803,399.810638 245.573502,402.889679 242.537292,406.213104 240.747543,406.995056 238.126801,409.878601 236.049423,409.976349 233.748291,411.002716 231.606964,415.010315 228.986237,420.826294 226.333542,423.123352 220.996231,428.059601 216.617691,433.680054 213.485611,439.349396 210.385483,446.582703 209.171005,453.278381 210.353516,458.605591 214.220703,458.654480 216.777496,458.752228 218.535294,460.560547 223.648911,456.504028 228.187241,452.252045 232.693619,447.511292 237.775253,441.402100 243.400223,433.386810 247.906586,425.811401 251.677872,418.529236 255.161514,410.611694 258.069885,403.134064 260.690613,394.923279 262.192749,389.449432 "
|
||||
style="fill:url(#radialGradient20143-627-23);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round"
|
||||
transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 40 KiB |
208
astro.cpp
@ -1,37 +1,101 @@
|
||||
#include "astro.h"
|
||||
#include "ui_astro.h"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QMessageBox>
|
||||
#include <QSettings>
|
||||
#include <QDateTime>
|
||||
#include <QFont>
|
||||
#include <QFontDialog>
|
||||
|
||||
#include "commons.h"
|
||||
|
||||
Astro::Astro(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::Astro)
|
||||
#include "ui_astro.h"
|
||||
|
||||
#include "moc_astro.cpp"
|
||||
|
||||
Astro::Astro(QSettings * settings, QDir const& dataPath, QWidget * parent) :
|
||||
QWidget {parent},
|
||||
settings_ {settings},
|
||||
ui_ {new Ui::Astro},
|
||||
data_path_ {dataPath}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->astroTextBrowser->setStyleSheet(
|
||||
"QTextBrowser { background-color : cyan; color : black; }");
|
||||
ui->astroTextBrowser->clear();
|
||||
ui_->setupUi(this);
|
||||
|
||||
setWindowFlags (Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint);
|
||||
|
||||
setWindowTitle(QApplication::applicationName () + " - " + tr ("Astronomical Data"));
|
||||
|
||||
read_settings ();
|
||||
|
||||
ui_->text_label->clear();
|
||||
}
|
||||
|
||||
Astro::~Astro()
|
||||
Astro::~Astro ()
|
||||
{
|
||||
delete ui;
|
||||
if (isVisible ())
|
||||
{
|
||||
write_settings ();
|
||||
}
|
||||
}
|
||||
|
||||
void Astro::closeEvent (QCloseEvent * e)
|
||||
{
|
||||
write_settings ();
|
||||
QWidget::closeEvent (e);
|
||||
}
|
||||
|
||||
void Astro::read_settings ()
|
||||
{
|
||||
settings_->beginGroup ("Astro");
|
||||
move (settings_->value ("window/pos", pos ()).toPoint ());
|
||||
QFont font;
|
||||
if (font.fromString (settings_->value ("font", ui_->text_label->font ().toString ()).toString ()))
|
||||
{
|
||||
ui_->text_label->setFont (font);
|
||||
adjustSize ();
|
||||
}
|
||||
settings_->endGroup ();
|
||||
}
|
||||
|
||||
void Astro::write_settings ()
|
||||
{
|
||||
settings_->beginGroup ("Astro");
|
||||
settings_->setValue ("window/pos", pos ());
|
||||
settings_->setValue ("font", ui_->text_label->font ().toString ());
|
||||
settings_->endGroup ();
|
||||
}
|
||||
|
||||
void Astro::on_font_push_button_clicked (bool /* checked */)
|
||||
{
|
||||
bool changed;
|
||||
ui_->text_label->setFont (QFontDialog::getFont (&changed
|
||||
, ui_->text_label->font ()
|
||||
, this
|
||||
, tr ("WSJT-X Astro Text Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, QFontDialog::MonospacedFonts
|
||||
#endif
|
||||
));
|
||||
if (changed)
|
||||
{
|
||||
adjustSize ();
|
||||
}
|
||||
}
|
||||
|
||||
void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid,
|
||||
int fQSO, int nsetftx, int ntxFreq, QString azelDir)
|
||||
int fQSO, int nsetftx, int ntxFreq)
|
||||
{
|
||||
static int ntxFreq0=-99;
|
||||
static bool astroBusy=false;
|
||||
char cc[300];
|
||||
double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx;
|
||||
double ramoon,decmoon,dgrd,poloffset,xnr,techo;
|
||||
int ntsky,ndop,ndop00;
|
||||
QString date = t.date().toString("yyyy MMM dd");
|
||||
QString utc = t.time().toString();
|
||||
QString date = t.date().toString("yyyy MMM dd").trimmed ();
|
||||
QString utc = t.time().toString().trimmed ();
|
||||
int nyear=t.date().year();
|
||||
int month=t.date().month();
|
||||
int nday=t.date().day();
|
||||
@ -40,7 +104,7 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid,
|
||||
double sec=t.time().second() + 0.001*t.time().msec();
|
||||
int isec=sec;
|
||||
double uth=nhr + nmin/60.0 + sec/3600.0;
|
||||
// int nfreq=(int)datcom_.fcenter;
|
||||
// int nfreq=(int)datcom_.fcenter;
|
||||
int nfreq=10368;
|
||||
if(nfreq<10 or nfreq > 50000) nfreq=144;
|
||||
|
||||
@ -48,60 +112,82 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid,
|
||||
astroBusy=true;
|
||||
|
||||
astrosub_(&nyear, &month, &nday, &uth, &nfreq, mygrid.toLatin1(),
|
||||
hisgrid.toLatin1(), &azsun, &elsun, &azmoon, &elmoon,
|
||||
&azmoondx, &elmoondx, &ntsky, &ndop, &ndop00,&ramoon, &decmoon,
|
||||
&dgrd, &poloffset, &xnr, &techo, 6, 6);
|
||||
hisgrid.toLatin1(), &azsun, &elsun, &azmoon, &elmoon,
|
||||
&azmoondx, &elmoondx, &ntsky, &ndop, &ndop00,&ramoon, &decmoon,
|
||||
&dgrd, &poloffset, &xnr, &techo, 6, 6);
|
||||
astroBusy=false;
|
||||
}
|
||||
|
||||
sprintf(cc,
|
||||
"Az: %6.1f\n"
|
||||
"El: %6.1f\n"
|
||||
"MyDop: %6d\n"
|
||||
"Delay: %6.2f\n"
|
||||
"DxAz: %6.1f\n"
|
||||
"DxEl: %6.1f\n"
|
||||
"DxDop: %6d\n"
|
||||
"Dec: %6.1f\n"
|
||||
"SunAz: %6.1f\n"
|
||||
"SunEl: %6.1f\n"
|
||||
"Freq: %6d\n"
|
||||
"Tsky: %6d\n"
|
||||
"MNR: %6.1f\n"
|
||||
"Dgrd: %6.1f",
|
||||
azmoon,elmoon,ndop00,techo,azmoondx,elmoondx,ndop,decmoon,
|
||||
azsun,elsun,nfreq,ntsky,xnr,dgrd);
|
||||
ui->astroTextBrowser->setText(" "+ date + "\nUTC: " + utc + "\n" + cc);
|
||||
QString message;
|
||||
{
|
||||
QTextStream out {&message};
|
||||
out
|
||||
<< " " << date << "\n"
|
||||
"UTC: " << utc << "\n"
|
||||
<< fixed
|
||||
<< qSetFieldWidth (6)
|
||||
<< qSetRealNumberPrecision (1)
|
||||
<< "Az: " << azmoon << "\n"
|
||||
"El: " << elmoon << "\n"
|
||||
"MyDop: " << ndop00 << "\n"
|
||||
<< qSetRealNumberPrecision (2)
|
||||
<< "Delay: " << techo << "\n"
|
||||
<< qSetRealNumberPrecision (1)
|
||||
<< "DxAz: " << azmoondx << "\n"
|
||||
"DxEl: " << elmoondx << "\n"
|
||||
"DxDop: " << ndop << "\n"
|
||||
"Dec: " << decmoon << "\n"
|
||||
"SunAz: " << azsun << "\n"
|
||||
"SunEl: " << elsun << "\n"
|
||||
"Freq: " << nfreq << "\n"
|
||||
"Tsky: " << ntsky << "\n"
|
||||
"MNR: " << xnr << "\n"
|
||||
"Dgrd: " << dgrd;
|
||||
}
|
||||
ui_->text_label->setText(message);
|
||||
|
||||
QString fname=azelDir+"/azel.dat";
|
||||
QFile f(fname);
|
||||
QString fname {"azel.dat"};
|
||||
QFile f(data_path_.absoluteFilePath (fname));
|
||||
if(!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox mb;
|
||||
mb.setText("Cannot open " + fname);
|
||||
mb.setText("Cannot open \"" + f.fileName () + "\".");
|
||||
mb.exec();
|
||||
return;
|
||||
}
|
||||
int ndiff=0;
|
||||
if(ntxFreq != ntxFreq0) ndiff=1;
|
||||
ntxFreq0=ntxFreq;
|
||||
QTextStream out(&f);
|
||||
sprintf(cc,"%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Moon\n"
|
||||
"%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Sun\n"
|
||||
"%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Source\n"
|
||||
"%4d,%6d,Doppler\n"
|
||||
"%3d,%1d,fQSO\n"
|
||||
"%3d,%1d,fQSO2\n",
|
||||
nhr,nmin,isec,azmoon,elmoon,
|
||||
nhr,nmin,isec,azsun,elsun,
|
||||
nhr,nmin,isec,0.0,0.0,
|
||||
nfreq,ndop,
|
||||
fQSO,nsetftx,
|
||||
ntxFreq,ndiff);
|
||||
out << cc;
|
||||
{
|
||||
QTextStream out {&f};
|
||||
out << fixed
|
||||
<< qSetFieldWidth (2)
|
||||
<< qSetRealNumberPrecision (1)
|
||||
<< qSetPadChar ('0')
|
||||
<< right
|
||||
<< nhr << ':' << nmin << ':' << isec
|
||||
<< qSetFieldWidth (5)
|
||||
<< ',' << azmoon << ',' << elmoon << ",Moon\n"
|
||||
<< qSetFieldWidth (2)
|
||||
<< nhr << ':' << nmin << ':' << isec
|
||||
<< qSetFieldWidth (5)
|
||||
<< ',' << azsun << ',' << elsun << ",Sun\n"
|
||||
<< qSetFieldWidth (2)
|
||||
<< nhr << ':' << nmin << ':' << isec
|
||||
<< qSetFieldWidth (5)
|
||||
<< ',' << 0. << ',' << 0. << ",Sun\n"
|
||||
<< qSetPadChar (' ')
|
||||
<< qSetFieldWidth (4)
|
||||
<< nfreq << ','
|
||||
<< qSetFieldWidth (6)
|
||||
<< ndop << ",Doppler\n"
|
||||
<< qSetFieldWidth (3)
|
||||
<< fQSO << ','
|
||||
<< qSetFieldWidth (1)
|
||||
<< nsetftx << ",fQSO\n"
|
||||
<< qSetFieldWidth (3)
|
||||
<< ntxFreq << ','
|
||||
<< qSetFieldWidth (1)
|
||||
<< ndiff << ",fQSO2";
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
void Astro::setFontSize(int n)
|
||||
{
|
||||
ui->astroTextBrowser->setFontPointSize(n);
|
||||
}
|
||||
|
50
astro.h
@ -1,36 +1,52 @@
|
||||
// -*- Mode: C++ -*-
|
||||
#ifndef ASTRO_H
|
||||
#define ASTRO_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
|
||||
class QSettings;
|
||||
|
||||
namespace Ui {
|
||||
class Astro;
|
||||
}
|
||||
|
||||
class Astro : public QWidget
|
||||
class Astro final
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Astro(QWidget *parent = 0);
|
||||
void astroUpdate(QDateTime t, QString mygrid, QString hisgrid,
|
||||
int fQSO, int nsetftx, int ntxFreq, QString azelDir);
|
||||
void setFontSize(int n);
|
||||
// ~Astro();
|
||||
virtual ~Astro();
|
||||
Q_OBJECT;
|
||||
|
||||
private:
|
||||
Ui::Astro *ui;
|
||||
Q_DISABLE_COPY (Astro);
|
||||
|
||||
public:
|
||||
explicit Astro(QSettings * settings, QDir const& dataPath, QWidget * parent = nullptr);
|
||||
~Astro ();
|
||||
|
||||
void astroUpdate(QDateTime t, QString mygrid, QString hisgrid,
|
||||
int fQSO, int nsetftx, int ntxFreq);
|
||||
|
||||
Q_SLOT void on_font_push_button_clicked (bool);
|
||||
|
||||
protected:
|
||||
void closeEvent (QCloseEvent *) override;
|
||||
|
||||
private:
|
||||
void read_settings ();
|
||||
void write_settings ();
|
||||
|
||||
QSettings * settings_;
|
||||
QScopedPointer<Ui::Astro> ui_;
|
||||
QDir data_path_;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
void astrosub_(int* nyear, int* month, int* nday, double* uth, int* nfreq,
|
||||
const char* mygrid, const char* hisgrid, double* azsun,
|
||||
double* elsun, double* azmoon, double* elmoon, double* azmoondx,
|
||||
double* elmoondx, int* ntsky, int* ndop, int* ndop00,
|
||||
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
|
||||
double* xnr, double* techo, int len1, int len2);
|
||||
const char* mygrid, const char* hisgrid, double* azsun,
|
||||
double* elsun, double* azmoon, double* elmoon, double* azmoondx,
|
||||
double* elmoondx, int* ntsky, int* ndop, int* ndop00,
|
||||
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
|
||||
double* xnr, double* techo, int len1, int len2);
|
||||
}
|
||||
|
||||
#endif // ASTRO_H
|
||||
|
109
astro.ui
@ -6,31 +6,100 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>262</width>
|
||||
<height>483</height>
|
||||
<width>169</width>
|
||||
<height>79</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QTextBrowser" name="astroTextBrowser">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>10</y>
|
||||
<width>256</width>
|
||||
<height>451</height>
|
||||
</rect>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QWidget {
|
||||
background: cyan;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier New</family>
|
||||
<pointsize>20</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="text_label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
<pointsize>18</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Astro Data</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="font_push_button">
|
||||
<property name="text">
|
||||
<string>Font ...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
BIN
contrib/Commander TCPIP Mesages.pdf
Normal file
750
devsetup.cpp
@ -1,750 +0,0 @@
|
||||
#include "devsetup.h"
|
||||
|
||||
#include "ui_devsetup.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <tr1/functional>
|
||||
#include <qmath.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QAudioDeviceInfo>
|
||||
#include <QAudioInput>
|
||||
#include <QMap>
|
||||
|
||||
extern double dFreq[16];
|
||||
qint32 g2_iptt;
|
||||
qint32 g2_COMportOpen;
|
||||
|
||||
//----------------------------------------------------------- DevSetup()
|
||||
DevSetup::DevSetup(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui (new Ui::DevSetup)
|
||||
, m_audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput))
|
||||
, m_audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput))
|
||||
{
|
||||
ui->setupUi(this); //setup the dialog form
|
||||
m_restartSoundIn=false;
|
||||
m_restartSoundOut=false;
|
||||
m_firstCall=true;
|
||||
g2_iptt=0;
|
||||
m_test=0;
|
||||
m_bRigOpen=false;
|
||||
g2_COMportOpen=0;
|
||||
}
|
||||
|
||||
DevSetup::~DevSetup()
|
||||
{
|
||||
}
|
||||
|
||||
void DevSetup::initDlg()
|
||||
{
|
||||
QString m_appDir = QApplication::applicationDirPath();
|
||||
QString inifile = m_appDir + "/wsjtx.ini";
|
||||
QSettings settings(inifile, QSettings::IniFormat);
|
||||
settings.beginGroup("Common");
|
||||
QString catPortDriver = settings.value("CATdriver","None").toString();
|
||||
settings.endGroup();
|
||||
|
||||
//
|
||||
// load combo boxes with audio setup choices
|
||||
//
|
||||
loadAudioDevices (m_audioInputDevices, ui->comboBoxSndIn, m_audioInputDevice, QAudioDeviceInfo::defaultInputDevice ());
|
||||
loadAudioDevices (m_audioOutputDevices, ui->comboBoxSndOut, m_audioOutputDevice, QAudioDeviceInfo::defaultOutputDevice ());
|
||||
|
||||
{
|
||||
using namespace std::tr1;
|
||||
using namespace std::tr1::placeholders;
|
||||
|
||||
function<void (int)> cb (bind (&DevSetup::updateAudioChannels, this, ui->comboBoxSndIn, _1, ui->audioInputChannel, false));
|
||||
connect (ui->comboBoxSndIn, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb);
|
||||
cb = bind (&DevSetup::updateAudioChannels, this, ui->comboBoxSndOut, _1, ui->audioOutputChannel, true);
|
||||
connect (ui->comboBoxSndOut, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb);
|
||||
|
||||
updateAudioChannels (ui->comboBoxSndIn, ui->comboBoxSndIn->currentIndex (), ui->audioInputChannel, false);
|
||||
updateAudioChannels (ui->comboBoxSndOut, ui->comboBoxSndOut->currentIndex (), ui->audioOutputChannel, true);
|
||||
|
||||
ui->audioInputChannel->setCurrentIndex (m_audioInputChannel);
|
||||
ui->audioOutputChannel->setCurrentIndex (m_audioOutputChannel);
|
||||
}
|
||||
|
||||
enumerateRigs ();
|
||||
|
||||
QPalette pal(ui->myCallEntry->palette());
|
||||
if(m_myCall=="") {
|
||||
pal.setColor(QPalette::Base,"#ffccff");
|
||||
} else {
|
||||
pal.setColor(QPalette::Base,Qt::white);
|
||||
}
|
||||
ui->myCallEntry->setPalette(pal);
|
||||
ui->myGridEntry->setPalette(pal);
|
||||
ui->myCallEntry->setText(m_myCall);
|
||||
ui->myGridEntry->setText(m_myGrid);
|
||||
|
||||
ui->idIntSpinBox->setValue(m_idInt);
|
||||
ui->pttMethodComboBox->setCurrentIndex(m_pttMethodIndex);
|
||||
ui->saveDirEntry->setText(m_saveDir);
|
||||
ui->cbID73->setChecked(m_After73);
|
||||
ui->cbDisplayAstroData->setChecked(m_bAstroData);
|
||||
ui->cbPSKReporter->setChecked(m_pskReporter);
|
||||
ui->cbSplit->setChecked(m_bSplit and m_catEnabled);
|
||||
ui->cbXIT->setChecked(m_bXIT);
|
||||
ui->cbXIT->setVisible(false);
|
||||
ui->dtMinSpinBox->setValue(m_DTmin);
|
||||
ui->dtMaxSpinBox->setValue(m_DTmax);
|
||||
|
||||
enableWidgets();
|
||||
|
||||
ui->catPortComboBox->setCurrentIndex(m_catPortIndex);
|
||||
ui->serialRateComboBox->setCurrentIndex(m_serialRateIndex);
|
||||
ui->dataBitsComboBox->setCurrentIndex(m_dataBitsIndex);
|
||||
ui->stopBitsComboBox->setCurrentIndex(m_stopBitsIndex);
|
||||
ui->handshakeComboBox->setCurrentIndex(m_handshakeIndex);
|
||||
ui->rbData->setChecked(m_pttData);
|
||||
ui->pollSpinBox->setValue(m_poll);
|
||||
ui->cbEMEband->setCurrentIndex(m_EMEbandIndex);
|
||||
ui->cbBWmult->setCurrentIndex(m_toneMultIndex);
|
||||
ui->astroFontSpinBox->setValue(m_astroFont);
|
||||
|
||||
// PY2SDR -- Per OS serial port names
|
||||
m_tmp=m_pttPort;
|
||||
ui->pttComboBox->clear();
|
||||
ui->catPortComboBox->clear();
|
||||
ui->pttComboBox->addItem("None");
|
||||
ui->catPortComboBox->addItem("None");
|
||||
#ifdef WIN32
|
||||
for ( int i = 1; i < 100; i++ ) {
|
||||
ui->pttComboBox->addItem("COM" + QString::number(i));
|
||||
ui->catPortComboBox->addItem("COM" + QString::number(i));
|
||||
}
|
||||
ui->pttComboBox->addItem("USB");
|
||||
ui->catPortComboBox->addItem("USB");
|
||||
#else
|
||||
ui->catPortComboBox->addItem("/dev/ttyS0");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS1");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS2");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS3");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS4");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS5");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS6");
|
||||
ui->catPortComboBox->addItem("/dev/ttyS7");
|
||||
ui->catPortComboBox->addItem("/dev/ttyUSB0");
|
||||
ui->catPortComboBox->addItem("/dev/ttyUSB1");
|
||||
ui->catPortComboBox->addItem("/dev/ttyUSB2");
|
||||
ui->catPortComboBox->addItem("/dev/ttyUSB3");
|
||||
ui->catPortComboBox->addItem(catPortDriver);
|
||||
|
||||
ui->pttComboBox->addItem("/dev/ttyS0");
|
||||
ui->pttComboBox->addItem("/dev/ttyS1");
|
||||
ui->pttComboBox->addItem("/dev/ttyS2");
|
||||
ui->pttComboBox->addItem("/dev/ttyS3");
|
||||
ui->pttComboBox->addItem("/dev/ttyS4");
|
||||
ui->pttComboBox->addItem("/dev/ttyS5");
|
||||
ui->pttComboBox->addItem("/dev/ttyS6");
|
||||
ui->pttComboBox->addItem("/dev/ttyS7");
|
||||
ui->pttComboBox->addItem("/dev/ttyUSB0");
|
||||
ui->pttComboBox->addItem("/dev/ttyUSB1");
|
||||
ui->pttComboBox->addItem("/dev/ttyUSB2");
|
||||
ui->pttComboBox->addItem("/dev/ttyUSB3");
|
||||
#endif
|
||||
ui->pttComboBox->setCurrentIndex(m_tmp);
|
||||
ui->catPortComboBox->setCurrentIndex(m_catPortIndex);
|
||||
|
||||
int n=m_macro.length();
|
||||
if(n>=1) ui->macro1->setText(m_macro[0].toUpper());
|
||||
if(n>=2) ui->macro2->setText(m_macro[1].toUpper());
|
||||
if(n>=3) ui->macro3->setText(m_macro[2].toUpper());
|
||||
if(n>=4) ui->macro4->setText(m_macro[3].toUpper());
|
||||
if(n>=5) ui->macro5->setText(m_macro[4].toUpper());
|
||||
if(n>=6) ui->macro6->setText(m_macro[5].toUpper());
|
||||
if(n>=7) ui->macro7->setText(m_macro[6].toUpper());
|
||||
if(n>=8) ui->macro8->setText(m_macro[7].toUpper());
|
||||
if(n>=8) ui->macro9->setText(m_macro[8].toUpper());
|
||||
if(n>=10) ui->macro10->setText(m_macro[9].toUpper());
|
||||
|
||||
ui->f1->setText(m_dFreq[0]);
|
||||
ui->f2->setText(m_dFreq[1]);
|
||||
ui->f3->setText(m_dFreq[2]);
|
||||
ui->f4->setText(m_dFreq[3]);
|
||||
ui->f5->setText(m_dFreq[4]);
|
||||
ui->f6->setText(m_dFreq[5]);
|
||||
ui->f7->setText(m_dFreq[6]);
|
||||
ui->f8->setText(m_dFreq[7]);
|
||||
ui->f9->setText(m_dFreq[8]);
|
||||
ui->f10->setText(m_dFreq[9]);
|
||||
ui->f11->setText(m_dFreq[10]);
|
||||
ui->f12->setText(m_dFreq[11]);
|
||||
ui->f13->setText(m_dFreq[12]);
|
||||
ui->f14->setText(m_dFreq[13]);
|
||||
ui->f15->setText(m_dFreq[14]);
|
||||
ui->f16->setText(m_dFreq[15]);
|
||||
|
||||
ui->AntDescription1->setText(m_antDescription[0]);
|
||||
ui->AntDescription2->setText(m_antDescription[1]);
|
||||
ui->AntDescription3->setText(m_antDescription[2]);
|
||||
ui->AntDescription4->setText(m_antDescription[3]);
|
||||
ui->AntDescription5->setText(m_antDescription[4]);
|
||||
ui->AntDescription6->setText(m_antDescription[5]);
|
||||
ui->AntDescription7->setText(m_antDescription[6]);
|
||||
ui->AntDescription8->setText(m_antDescription[7]);
|
||||
ui->AntDescription9->setText(m_antDescription[8]);
|
||||
ui->AntDescription10->setText(m_antDescription[9]);
|
||||
ui->AntDescription11->setText(m_antDescription[10]);
|
||||
ui->AntDescription12->setText(m_antDescription[11]);
|
||||
ui->AntDescription13->setText(m_antDescription[12]);
|
||||
ui->AntDescription14->setText(m_antDescription[13]);
|
||||
ui->AntDescription15->setText(m_antDescription[14]);
|
||||
ui->AntDescription16->setText(m_antDescription[15]);
|
||||
|
||||
ui->Band1->setText(m_bandDescription[0]);
|
||||
ui->Band2->setText(m_bandDescription[1]);
|
||||
ui->Band3->setText(m_bandDescription[2]);
|
||||
ui->Band4->setText(m_bandDescription[3]);
|
||||
ui->Band5->setText(m_bandDescription[4]);
|
||||
ui->Band6->setText(m_bandDescription[5]);
|
||||
ui->Band7->setText(m_bandDescription[6]);
|
||||
ui->Band8->setText(m_bandDescription[7]);
|
||||
ui->Band9->setText(m_bandDescription[8]);
|
||||
ui->Band10->setText(m_bandDescription[9]);
|
||||
ui->Band11->setText(m_bandDescription[10]);
|
||||
ui->Band12->setText(m_bandDescription[11]);
|
||||
ui->Band13->setText(m_bandDescription[12]);
|
||||
ui->Band14->setText(m_bandDescription[13]);
|
||||
ui->Band15->setText(m_bandDescription[14]);
|
||||
ui->Band16->setText(m_bandDescription[15]);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------- accept()
|
||||
void DevSetup::accept()
|
||||
{
|
||||
// Called when OK button is clicked.
|
||||
// Check to see whether SoundInThread must be restarted,
|
||||
// and save user parameters.
|
||||
|
||||
m_restartSoundIn = false;
|
||||
m_restartSoundOut = false;
|
||||
|
||||
if (m_audioInputDevice != m_audioInputDevices[ui->comboBoxSndIn->currentIndex ()])
|
||||
{
|
||||
m_audioInputDevice = m_audioInputDevices[ui->comboBoxSndIn->currentIndex ()];
|
||||
m_restartSoundIn = true;
|
||||
}
|
||||
|
||||
if (m_audioOutputDevice != m_audioOutputDevices[ui->comboBoxSndOut->currentIndex ()])
|
||||
{
|
||||
m_audioOutputDevice = m_audioOutputDevices[ui->comboBoxSndOut->currentIndex ()];
|
||||
m_restartSoundOut = true;
|
||||
}
|
||||
|
||||
if (m_audioInputChannel != static_cast<AudioDevice::Channel> (ui->audioInputChannel->currentIndex ()))
|
||||
{
|
||||
m_audioInputChannel = static_cast<AudioDevice::Channel> (ui->audioInputChannel->currentIndex ());
|
||||
m_restartSoundIn = true;
|
||||
}
|
||||
Q_ASSERT (m_audioInputChannel <= AudioDevice::Right);
|
||||
|
||||
if (m_audioOutputChannel != static_cast<AudioDevice::Channel> (ui->audioOutputChannel->currentIndex ()))
|
||||
{
|
||||
m_audioOutputChannel = static_cast<AudioDevice::Channel> (ui->audioOutputChannel->currentIndex ());
|
||||
m_restartSoundOut = true;
|
||||
}
|
||||
Q_ASSERT (m_audioOutputChannel <= AudioDevice::Both);
|
||||
|
||||
m_myCall=ui->myCallEntry->text();
|
||||
m_myGrid=ui->myGridEntry->text();
|
||||
m_idInt=ui->idIntSpinBox->value();
|
||||
m_pttMethodIndex=ui->pttMethodComboBox->currentIndex();
|
||||
m_pttPort=ui->pttComboBox->currentIndex();
|
||||
m_saveDir=ui->saveDirEntry->text();
|
||||
|
||||
m_macro.clear();
|
||||
m_macro.append(ui->macro1->text());
|
||||
m_macro.append(ui->macro2->text());
|
||||
m_macro.append(ui->macro3->text());
|
||||
m_macro.append(ui->macro4->text());
|
||||
m_macro.append(ui->macro5->text());
|
||||
m_macro.append(ui->macro6->text());
|
||||
m_macro.append(ui->macro7->text());
|
||||
m_macro.append(ui->macro8->text());
|
||||
m_macro.append(ui->macro9->text());
|
||||
m_macro.append(ui->macro10->text());
|
||||
|
||||
m_dFreq.clear();
|
||||
m_dFreq.append(ui->f1->text());
|
||||
m_dFreq.append(ui->f2->text());
|
||||
m_dFreq.append(ui->f3->text());
|
||||
m_dFreq.append(ui->f4->text());
|
||||
m_dFreq.append(ui->f5->text());
|
||||
m_dFreq.append(ui->f6->text());
|
||||
m_dFreq.append(ui->f7->text());
|
||||
m_dFreq.append(ui->f8->text());
|
||||
m_dFreq.append(ui->f9->text());
|
||||
m_dFreq.append(ui->f10->text());
|
||||
m_dFreq.append(ui->f11->text());
|
||||
m_dFreq.append(ui->f12->text());
|
||||
m_dFreq.append(ui->f13->text());
|
||||
m_dFreq.append(ui->f14->text());
|
||||
m_dFreq.append(ui->f15->text());
|
||||
m_dFreq.append(ui->f16->text());
|
||||
|
||||
m_antDescription.clear();
|
||||
m_antDescription.append(ui->AntDescription1->text());
|
||||
m_antDescription.append(ui->AntDescription2->text());
|
||||
m_antDescription.append(ui->AntDescription3->text());
|
||||
m_antDescription.append(ui->AntDescription4->text());
|
||||
m_antDescription.append(ui->AntDescription5->text());
|
||||
m_antDescription.append(ui->AntDescription6->text());
|
||||
m_antDescription.append(ui->AntDescription7->text());
|
||||
m_antDescription.append(ui->AntDescription8->text());
|
||||
m_antDescription.append(ui->AntDescription9->text());
|
||||
m_antDescription.append(ui->AntDescription10->text());
|
||||
m_antDescription.append(ui->AntDescription11->text());
|
||||
m_antDescription.append(ui->AntDescription12->text());
|
||||
m_antDescription.append(ui->AntDescription13->text());
|
||||
m_antDescription.append(ui->AntDescription14->text());
|
||||
m_antDescription.append(ui->AntDescription15->text());
|
||||
m_antDescription.append(ui->AntDescription16->text());
|
||||
|
||||
m_bandDescription.clear();
|
||||
m_bandDescription.append(ui->Band1->text());
|
||||
m_bandDescription.append(ui->Band2->text());
|
||||
m_bandDescription.append(ui->Band3->text());
|
||||
m_bandDescription.append(ui->Band4->text());
|
||||
m_bandDescription.append(ui->Band5->text());
|
||||
m_bandDescription.append(ui->Band6->text());
|
||||
m_bandDescription.append(ui->Band7->text());
|
||||
m_bandDescription.append(ui->Band8->text());
|
||||
m_bandDescription.append(ui->Band9->text());
|
||||
m_bandDescription.append(ui->Band10->text());
|
||||
m_bandDescription.append(ui->Band11->text());
|
||||
m_bandDescription.append(ui->Band12->text());
|
||||
m_bandDescription.append(ui->Band13->text());
|
||||
m_bandDescription.append(ui->Band14->text());
|
||||
m_bandDescription.append(ui->Band15->text());
|
||||
m_bandDescription.append(ui->Band16->text());
|
||||
|
||||
|
||||
if(m_bRigOpen) {
|
||||
rig->close();
|
||||
if(m_rig<9900) delete rig;
|
||||
m_bRigOpen=false;
|
||||
}
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
//------------------------------------------------------- reject()
|
||||
void DevSetup::reject()
|
||||
{
|
||||
if(m_bRigOpen) rig->close();
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void DevSetup::msgBox(QString t) //msgBox
|
||||
{
|
||||
msgBox0.setText(t);
|
||||
msgBox0.exec();
|
||||
}
|
||||
|
||||
void DevSetup::on_myCallEntry_editingFinished()
|
||||
{
|
||||
QString t=ui->myCallEntry->text();
|
||||
ui->myCallEntry->setText(t.toUpper());
|
||||
}
|
||||
|
||||
void DevSetup::on_myGridEntry_editingFinished()
|
||||
{
|
||||
QString t=ui->myGridEntry->text();
|
||||
t=t.mid(0,4).toUpper()+t.mid(4,2).toLower();
|
||||
ui->myGridEntry->setText(t);
|
||||
}
|
||||
|
||||
void DevSetup::setEnableAntennaDescriptions(bool enable)
|
||||
{
|
||||
ui->AntDescription1->setEnabled(enable);
|
||||
ui->AntDescription2->setEnabled(enable);
|
||||
ui->AntDescription3->setEnabled(enable);
|
||||
ui->AntDescription4->setEnabled(enable);
|
||||
ui->AntDescription5->setEnabled(enable);
|
||||
ui->AntDescription6->setEnabled(enable);
|
||||
ui->AntDescription7->setEnabled(enable);
|
||||
ui->AntDescription8->setEnabled(enable);
|
||||
ui->AntDescription9->setEnabled(enable);
|
||||
ui->AntDescription10->setEnabled(enable);
|
||||
ui->AntDescription11->setEnabled(enable);
|
||||
ui->AntDescription12->setEnabled(enable);
|
||||
ui->AntDescription13->setEnabled(enable);
|
||||
ui->AntDescription14->setEnabled(enable);
|
||||
ui->AntDescription15->setEnabled(enable);
|
||||
ui->AntDescription16->setEnabled(enable);
|
||||
if (enable)
|
||||
ui->AntDescriptionColumnLabel->setText("Antenna description");
|
||||
else
|
||||
ui->AntDescriptionColumnLabel->setText("Antenna description (enable PSK Reporter)");
|
||||
}
|
||||
|
||||
void DevSetup::on_cbPSKReporter_clicked(bool b)
|
||||
{
|
||||
m_pskReporter=b;
|
||||
setEnableAntennaDescriptions(m_pskReporter);
|
||||
}
|
||||
|
||||
void DevSetup::on_pttMethodComboBox_activated(int index)
|
||||
{
|
||||
m_pttMethodIndex=index;
|
||||
enableWidgets();
|
||||
}
|
||||
|
||||
void DevSetup::on_catPortComboBox_activated(int index)
|
||||
{
|
||||
m_catPortIndex=index;
|
||||
m_catPort=ui->catPortComboBox->itemText(index);
|
||||
}
|
||||
|
||||
void DevSetup::on_cbEnableCAT_toggled(bool b)
|
||||
{
|
||||
m_catEnabled=b;
|
||||
enableWidgets();
|
||||
ui->cbSplit->setChecked(m_bSplit and m_catEnabled);
|
||||
}
|
||||
|
||||
void DevSetup::on_serialRateComboBox_activated(int index)
|
||||
{
|
||||
m_serialRateIndex=index;
|
||||
m_serialRate=ui->serialRateComboBox->itemText(index).toInt();
|
||||
}
|
||||
|
||||
void DevSetup::on_handshakeComboBox_activated(int index)
|
||||
{
|
||||
m_handshakeIndex=index;
|
||||
m_handshake=ui->handshakeComboBox->itemText(index);
|
||||
}
|
||||
|
||||
void DevSetup::on_handshakeComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
ui->RTSCheckBox->setEnabled(index != 2);
|
||||
}
|
||||
|
||||
void DevSetup::on_dataBitsComboBox_activated(int index)
|
||||
{
|
||||
m_dataBitsIndex=index;
|
||||
m_dataBits=ui->dataBitsComboBox->itemText(index).toInt();
|
||||
}
|
||||
|
||||
void DevSetup::on_stopBitsComboBox_activated(int index)
|
||||
{
|
||||
m_stopBitsIndex=index;
|
||||
m_stopBits=ui->stopBitsComboBox->itemText(index).toInt();
|
||||
}
|
||||
|
||||
void DevSetup::on_rigComboBox_activated(int index)
|
||||
{
|
||||
m_rig = ui->rigComboBox->itemData (index).toInt ();
|
||||
enableWidgets();
|
||||
}
|
||||
|
||||
void DevSetup::on_cbID73_toggled(bool checked)
|
||||
{
|
||||
m_After73=checked;
|
||||
}
|
||||
|
||||
void DevSetup::on_testCATButton_clicked()
|
||||
{
|
||||
openRig();
|
||||
if(!m_catEnabled) return;
|
||||
QString t;
|
||||
double fMHz=rig->getFreq(RIG_VFO_CURR)/1000000.0;
|
||||
if(fMHz>0.0) {
|
||||
t.sprintf("Rig control appears to be working.\nDial Frequency: %.6f MHz",
|
||||
fMHz);
|
||||
} else {
|
||||
t.sprintf("Rig control error %d\nFailed to read frequency.",
|
||||
int(1000000.0*fMHz));
|
||||
if(m_poll>0) {
|
||||
m_catEnabled=false;
|
||||
ui->cbEnableCAT->setChecked(false);
|
||||
}
|
||||
}
|
||||
msgBox(t);
|
||||
}
|
||||
|
||||
void DevSetup::openRig()
|
||||
{
|
||||
QString t;
|
||||
int ret;
|
||||
|
||||
if(!m_catEnabled) return;
|
||||
if(m_bRigOpen) {
|
||||
rig->close();
|
||||
if(m_rig<9900) delete rig;
|
||||
m_bRigOpen=false;
|
||||
}
|
||||
|
||||
rig = new Rig();
|
||||
|
||||
if(m_rig<9900) {
|
||||
if (!rig->init(m_rig)) {
|
||||
msgBox("Rig init failure");
|
||||
m_catEnabled=false;
|
||||
return;
|
||||
}
|
||||
QString sCATport=m_catPort;
|
||||
#ifdef WIN32
|
||||
sCATport="\\\\.\\" + m_catPort; //Allow COM ports above 9
|
||||
#endif
|
||||
rig->setConf("rig_pathname", sCATport.toLatin1().data());
|
||||
char buf[80];
|
||||
sprintf(buf,"%d",m_serialRate);
|
||||
rig->setConf("serial_speed",buf);
|
||||
sprintf(buf,"%d",m_dataBits);
|
||||
rig->setConf("data_bits",buf);
|
||||
sprintf(buf,"%d",m_stopBits);
|
||||
rig->setConf("stop_bits",buf);
|
||||
rig->setConf("serial_handshake",m_handshake.toLatin1().data());
|
||||
rig->setConf("dtr_state",m_bDTR ? "ON" : "OFF");
|
||||
if(ui->RTSCheckBox->isEnabled()) {
|
||||
rig->setConf("rts_state",m_bRTS ? "ON" : "OFF");
|
||||
}
|
||||
}
|
||||
|
||||
ret=rig->open(m_rig);
|
||||
if(ret==RIG_OK) {
|
||||
m_bRigOpen=true;
|
||||
} else {
|
||||
t="Open rig failed";
|
||||
msgBox(t);
|
||||
m_catEnabled=false;
|
||||
ui->cbEnableCAT->setChecked(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DevSetup::on_testPTTButton_clicked()
|
||||
{
|
||||
m_test=1-m_test;
|
||||
if(m_pttMethodIndex==1 or m_pttMethodIndex==2) {
|
||||
ptt(m_pttPort,m_test,&g2_iptt,&g2_COMportOpen);
|
||||
}
|
||||
if(m_pttMethodIndex==0 and !m_bRigOpen) {
|
||||
// on_testCATButton_clicked();
|
||||
openRig();
|
||||
}
|
||||
if(m_pttMethodIndex==0 and m_bRigOpen) {
|
||||
if(m_test==0) rig->setPTT(RIG_PTT_OFF, RIG_VFO_CURR);
|
||||
if(m_test==1) {
|
||||
if(m_pttData) rig->setPTT(RIG_PTT_ON_DATA, RIG_VFO_CURR);
|
||||
if(!m_pttData) rig->setPTT(RIG_PTT_ON_MIC, RIG_VFO_CURR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevSetup::on_DTRCheckBox_toggled(bool checked)
|
||||
{
|
||||
m_bDTR=checked;
|
||||
}
|
||||
|
||||
void DevSetup::on_RTSCheckBox_toggled(bool checked)
|
||||
{
|
||||
m_bRTS=checked;
|
||||
}
|
||||
|
||||
void DevSetup::on_rbData_toggled(bool checked)
|
||||
{
|
||||
m_pttData=checked;
|
||||
}
|
||||
|
||||
void DevSetup::on_pollSpinBox_valueChanged(int n)
|
||||
{
|
||||
m_poll=n;
|
||||
}
|
||||
|
||||
void DevSetup::on_pttComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
m_pttPort=index;
|
||||
enableWidgets();
|
||||
}
|
||||
|
||||
void DevSetup::on_pttMethodComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
m_pttMethodIndex=index;
|
||||
bool b=m_pttMethodIndex==1 or m_pttMethodIndex==2;
|
||||
ui->pttComboBox->setEnabled(b);
|
||||
}
|
||||
|
||||
void DevSetup::enableWidgets()
|
||||
{
|
||||
ui->cbEnableCAT->setChecked(m_catEnabled);
|
||||
ui->rigComboBox->setEnabled(m_catEnabled);
|
||||
ui->testCATButton->setEnabled(m_catEnabled);
|
||||
ui->label_4->setEnabled(m_catEnabled);
|
||||
ui->label_47->setEnabled(m_catEnabled);
|
||||
ui->cbSplit->setEnabled(m_catEnabled);
|
||||
if(m_rig==9999) { //No Split Tx with HRD
|
||||
ui->cbSplit->setChecked(false);
|
||||
ui->cbSplit->setEnabled(false);
|
||||
}
|
||||
ui->cbXIT->setEnabled(m_catEnabled);
|
||||
|
||||
bool bSerial=m_catEnabled and (m_rig<9900);
|
||||
ui->catPortComboBox->setEnabled(bSerial);
|
||||
ui->serialRateComboBox->setEnabled(bSerial);
|
||||
ui->dataBitsComboBox->setEnabled(bSerial);
|
||||
ui->stopBitsComboBox->setEnabled(bSerial);
|
||||
ui->handshakeComboBox->setEnabled(bSerial);
|
||||
ui->DTRCheckBox->setEnabled(bSerial);
|
||||
ui->DTRCheckBox->setChecked(m_bDTR);
|
||||
ui->RTSCheckBox->setEnabled(bSerial && m_handshakeIndex != 2);
|
||||
ui->RTSCheckBox->setChecked(m_bRTS);
|
||||
ui->rbData->setEnabled(bSerial);
|
||||
ui->rbMic->setEnabled(bSerial);
|
||||
ui->label_21->setEnabled(bSerial);
|
||||
ui->label_22->setEnabled(bSerial);
|
||||
ui->label_23->setEnabled(bSerial);
|
||||
ui->label_24->setEnabled(bSerial);
|
||||
ui->label_25->setEnabled(bSerial);
|
||||
|
||||
ui->pollSpinBox->setEnabled(m_catEnabled);
|
||||
bool b1=(m_pttMethodIndex==1 or m_pttMethodIndex==2);
|
||||
ui->pttComboBox->setEnabled(b1);
|
||||
b1=b1 and (m_pttPort!=0);
|
||||
bool b2 = (m_catEnabled and m_pttMethodIndex==1 and m_rig<9900) or
|
||||
(m_catEnabled and m_pttMethodIndex==2 and m_rig<9900);
|
||||
bool b3 = (m_catEnabled and m_pttMethodIndex==0);
|
||||
ui->testPTTButton->setEnabled(b1 or b2 or b3); //Include PTT via HRD or Commander
|
||||
setEnableAntennaDescriptions(m_pskReporter);
|
||||
}
|
||||
|
||||
void DevSetup::on_cbSplit_toggled(bool checked)
|
||||
{
|
||||
m_bSplit=checked;
|
||||
if(m_bSplit and m_bXIT) ui->cbXIT->setChecked(false);
|
||||
}
|
||||
|
||||
void DevSetup::on_cbXIT_toggled(bool checked)
|
||||
{
|
||||
m_bXIT=checked;
|
||||
if(m_bSplit and m_bXIT) ui->cbSplit->setChecked(false);
|
||||
}
|
||||
|
||||
void DevSetup::loadAudioDevices (AudioDevices const& d, QComboBox * cb, QAudioDeviceInfo const& device, QAudioDeviceInfo const& defaultDevice)
|
||||
{
|
||||
using std::copy;
|
||||
using std::back_inserter;
|
||||
|
||||
int currentIndex = -1;
|
||||
int defaultIndex = 0;
|
||||
for (AudioDevices::const_iterator p = d.cbegin (); p != d.cend (); ++p)
|
||||
{
|
||||
// convert supported channel counts into something we can store in the item model
|
||||
QList<QVariant> channelCounts;
|
||||
QList<int> scc (p->supportedChannelCounts ());
|
||||
copy (scc.cbegin (), scc.cend (), back_inserter (channelCounts));
|
||||
|
||||
cb->addItem (p->deviceName (), channelCounts);
|
||||
if (*p == device)
|
||||
{
|
||||
currentIndex = p - d.cbegin ();
|
||||
}
|
||||
else if (*p == defaultDevice)
|
||||
{
|
||||
defaultIndex = p - d.cbegin ();
|
||||
}
|
||||
}
|
||||
cb->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex);
|
||||
}
|
||||
|
||||
void DevSetup::updateAudioChannels (QComboBox const * srcCb, int index, QComboBox * cb, bool allowBoth)
|
||||
{
|
||||
// disable all items
|
||||
for (int i (0); i < cb->count (); ++i)
|
||||
{
|
||||
cb->setItemData (i, 0, Qt::UserRole - 1); // undocumented model internals allows disable
|
||||
}
|
||||
|
||||
Q_FOREACH (QVariant const& v, srcCb->itemData (index).toList ())
|
||||
{
|
||||
// enable valid options
|
||||
int n (v.toInt ());
|
||||
if (2 == n)
|
||||
{
|
||||
cb->setItemData (AudioDevice::Left, 32 | 1, Qt::UserRole - 1); // undocumented model internals allows enable
|
||||
cb->setItemData (AudioDevice::Right, 32 | 1, Qt::UserRole - 1);
|
||||
if (allowBoth)
|
||||
{
|
||||
cb->setItemData (AudioDevice::Both, 32 | 1, Qt::UserRole - 1);
|
||||
}
|
||||
}
|
||||
else if (1 == n)
|
||||
{
|
||||
cb->setItemData (AudioDevice::Mono, 32 | 1, Qt::UserRole - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef QMap<QString, int> RigList;
|
||||
|
||||
int rigCallback (rig_caps const * caps, void * cbData)
|
||||
{
|
||||
RigList * rigs = reinterpret_cast<RigList *> (cbData);
|
||||
|
||||
QString key (QString::fromLatin1 (caps->mfg_name).trimmed ()
|
||||
+ ' '+ QString::fromLatin1 (caps->model_name).trimmed ()
|
||||
// + ' '+ QString::fromLatin1 (caps->version).trimmed ()
|
||||
// + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')'
|
||||
);
|
||||
|
||||
(*rigs)[key] = caps->rig_model;
|
||||
|
||||
return 1; // keep them coming
|
||||
}
|
||||
|
||||
void DevSetup::enumerateRigs ()
|
||||
{
|
||||
RigList rigs;
|
||||
rig_load_all_backends ();
|
||||
rig_list_foreach (rigCallback, &rigs);
|
||||
|
||||
for (RigList::const_iterator r = rigs.cbegin (); r != rigs.cend (); ++r)
|
||||
{
|
||||
ui->rigComboBox->addItem (r.key (), r.value ());
|
||||
}
|
||||
|
||||
ui->rigComboBox->addItem ("DX Lab Suite Commander", 9998);
|
||||
ui->rigComboBox->addItem ("Ham Radio Deluxe", 9999);
|
||||
ui->rigComboBox->setCurrentIndex (ui->rigComboBox->findData (m_rig));
|
||||
}
|
||||
|
||||
void DevSetup::on_cbEMEband_activated(int index)
|
||||
{
|
||||
m_EMEbandIndex=index;
|
||||
m_EMEband=ui->cbEMEband->itemText(index).toInt();
|
||||
}
|
||||
|
||||
void DevSetup::on_cbBWmult_activated(int index)
|
||||
{
|
||||
m_toneMultIndex=index;
|
||||
m_toneMult=pow(2,index);
|
||||
}
|
||||
|
||||
void DevSetup::on_dtMinSpinBox_valueChanged(double arg1)
|
||||
{
|
||||
m_DTmin=arg1;
|
||||
}
|
||||
|
||||
void DevSetup::on_dtMaxSpinBox_valueChanged(double arg1)
|
||||
{
|
||||
m_DTmax=arg1;
|
||||
}
|
||||
|
||||
void DevSetup::on_astroFontSpinBox_valueChanged(int arg1)
|
||||
{
|
||||
if(arg1==-999) m_astroFont=18; //silence compiler warning
|
||||
m_astroFont=ui->astroFontSpinBox->value();
|
||||
}
|
||||
|
||||
void DevSetup::on_cbDisplayAstroData_toggled(bool checked)
|
||||
{
|
||||
m_bAstroData=checked;
|
||||
}
|
156
devsetup.h
@ -1,156 +0,0 @@
|
||||
#ifndef DEVSETUP_H
|
||||
#define DEVSETUP_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QProcess>
|
||||
#include <QMessageBox>
|
||||
#include <QAudioDeviceInfo>
|
||||
|
||||
#include <hamlib/rig.h>
|
||||
|
||||
#include "rigclass.h"
|
||||
#include "AudioDevice.hpp"
|
||||
|
||||
int rigCallback (rig_caps const *, void *);
|
||||
|
||||
namespace Ui {
|
||||
class DevSetup;
|
||||
}
|
||||
|
||||
class QComboBox;
|
||||
|
||||
class DevSetup : public QDialog
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
private:
|
||||
Ui::DevSetup * ui;
|
||||
|
||||
public:
|
||||
DevSetup(QWidget *parent=0);
|
||||
~DevSetup();
|
||||
|
||||
void initDlg();
|
||||
|
||||
float m_DTmin;
|
||||
float m_DTmax;
|
||||
|
||||
qint32 m_idInt;
|
||||
qint32 m_pttMethodIndex;
|
||||
qint32 m_pttPort;
|
||||
qint32 m_catPortIndex;
|
||||
qint32 m_rig;
|
||||
qint32 m_rigIndex;
|
||||
qint32 m_serialRate;
|
||||
qint32 m_serialRateIndex;
|
||||
qint32 m_dataBits;
|
||||
qint32 m_dataBitsIndex;
|
||||
qint32 m_stopBits;
|
||||
qint32 m_stopBitsIndex;
|
||||
qint32 m_handshakeIndex;
|
||||
qint32 m_test;
|
||||
qint32 m_poll;
|
||||
qint32 m_tmp;
|
||||
qint32 m_EMEband;
|
||||
qint32 m_EMEbandIndex;
|
||||
qint32 m_toneMult;
|
||||
qint32 m_toneMultIndex;
|
||||
qint32 m_astroFont;
|
||||
|
||||
typedef QList<QAudioDeviceInfo> AudioDevices;
|
||||
AudioDevices m_audioInputDevices; // available input devices
|
||||
AudioDevices m_audioOutputDevices; // available output devices
|
||||
QAudioDeviceInfo m_audioInputDevice; // selected input device
|
||||
QAudioDeviceInfo m_audioOutputDevice; // selected output device
|
||||
AudioDevice::Channel m_audioInputChannel;
|
||||
AudioDevice::Channel m_audioOutputChannel;
|
||||
|
||||
bool m_restartSoundIn;
|
||||
bool m_restartSoundOut;
|
||||
bool m_pskReporter;
|
||||
bool m_firstCall;
|
||||
bool m_catEnabled;
|
||||
bool m_After73;
|
||||
bool m_bRigOpen;
|
||||
bool m_bDTR;
|
||||
bool m_bRTS;
|
||||
bool m_pttData;
|
||||
bool m_bSplit;
|
||||
bool m_bXIT;
|
||||
bool m_bAstroData;
|
||||
|
||||
QString m_myCall;
|
||||
QString m_myGrid;
|
||||
QString m_saveDir;
|
||||
QString m_azelDir;
|
||||
QString m_catPort;
|
||||
QString m_handshake;
|
||||
|
||||
QStringList m_macro;
|
||||
QStringList m_dFreq; // per band frequency in MHz as a string
|
||||
QStringList m_antDescription; // per band antenna description
|
||||
QStringList m_bandDescription; // per band description
|
||||
|
||||
QMessageBox msgBox0;
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
private slots:
|
||||
void on_myCallEntry_editingFinished();
|
||||
void on_myGridEntry_editingFinished();
|
||||
void on_cbPSKReporter_clicked(bool checked);
|
||||
void on_pttMethodComboBox_activated(int index);
|
||||
void on_catPortComboBox_activated(int index);
|
||||
void on_cbEnableCAT_toggled(bool checked);
|
||||
void on_serialRateComboBox_activated(int index);
|
||||
void on_handshakeComboBox_activated(int index);
|
||||
void on_handshakeComboBox_currentIndexChanged(int index);
|
||||
void on_dataBitsComboBox_activated(int index);
|
||||
void on_stopBitsComboBox_activated(int index);
|
||||
void on_rigComboBox_activated(int index);
|
||||
void on_cbID73_toggled(bool checked);
|
||||
void on_testCATButton_clicked();
|
||||
void on_testPTTButton_clicked();
|
||||
void on_DTRCheckBox_toggled(bool checked);
|
||||
void on_RTSCheckBox_toggled(bool checked);
|
||||
void on_rbData_toggled(bool checked);
|
||||
void on_pollSpinBox_valueChanged(int n);
|
||||
void on_pttComboBox_currentIndexChanged(int index);
|
||||
void on_pttMethodComboBox_currentIndexChanged(int index);
|
||||
void on_cbSplit_toggled(bool checked);
|
||||
void on_cbXIT_toggled(bool checked);
|
||||
void on_cbEMEband_activated(int index);
|
||||
void on_cbBWmult_activated(int index);
|
||||
void on_dtMinSpinBox_valueChanged(double arg1);
|
||||
void on_dtMaxSpinBox_valueChanged(double arg1);
|
||||
void on_astroFontSpinBox_valueChanged(int arg1);
|
||||
void on_cbDisplayAstroData_toggled(bool checked);
|
||||
|
||||
private:
|
||||
void loadAudioDevices (AudioDevices const&, QComboBox *, QAudioDeviceInfo const&, QAudioDeviceInfo const&);
|
||||
void updateAudioChannels (QComboBox const *, int, QComboBox *, bool);
|
||||
void enumerateRigs ();
|
||||
Rig* rig;
|
||||
void msgBox(QString t);
|
||||
void setEnableAntennaDescriptions(bool enable);
|
||||
void enableWidgets();
|
||||
void openRig();
|
||||
|
||||
friend int rigCallback (rig_caps const *, void *);
|
||||
};
|
||||
|
||||
extern int ptt(int nport, int ntx, int* iptt, int* nopen);
|
||||
|
||||
#ifdef WIN32
|
||||
extern "C" {
|
||||
bool HRDInterfaceConnect(const wchar_t *host, const ushort);
|
||||
void HRDInterfaceDisconnect();
|
||||
bool HRDInterfaceIsConnected();
|
||||
wchar_t* HRDInterfaceSendMessage(const wchar_t *msg);
|
||||
void HRDInterfaceFreeString(const wchar_t *lstring);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // DEVSETUP_H
|
2835
devsetup.ui
@ -3,6 +3,8 @@
|
||||
#include <QMouseEvent>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "moc_displaytext.cpp"
|
||||
|
||||
DisplayText::DisplayText(QWidget *parent) :
|
||||
QTextBrowser(parent)
|
||||
{
|
||||
@ -19,7 +21,7 @@ void DisplayText::mouseDoubleClickEvent(QMouseEvent *e)
|
||||
}
|
||||
|
||||
|
||||
void DisplayText::setFont(QFont font)
|
||||
void DisplayText::setFont(QFont const& font)
|
||||
{
|
||||
QFontMetrics qfm(font);
|
||||
_fontWidth = qfm.averageCharWidth()+1; // the plus one is emperical
|
||||
|
@ -12,7 +12,7 @@ class DisplayText : public QTextBrowser
|
||||
public:
|
||||
explicit DisplayText(QWidget *parent = 0);
|
||||
|
||||
void setFont(QFont font);
|
||||
void setFont(QFont const& font);
|
||||
|
||||
void insertLineSpacer();
|
||||
void displayDecodedText(DecodedText decodedText, QString myCall, bool displayDXCCEntity, LogBook logBook);
|
||||
|
11
getfile.cpp
@ -19,8 +19,10 @@
|
||||
void getfile(QString fname, int ntrperiod)
|
||||
{
|
||||
|
||||
char name[80];
|
||||
strcpy(name,fname.toLatin1());
|
||||
char name[512];
|
||||
strncpy(name,fname.toLatin1(), sizeof (name) - 1);
|
||||
name[sizeof (name) - 1] = '\0';
|
||||
|
||||
FILE* fp=fopen(name,"rb");
|
||||
|
||||
int i0=fname.indexOf(".wav");
|
||||
@ -60,8 +62,9 @@ void savewav(QString fname, int ntrperiod)
|
||||
|
||||
int npts=ntrperiod*12000;
|
||||
// qint16* buf=(qint16*)malloc(2*npts);
|
||||
char name[80];
|
||||
strcpy(name,fname.toLatin1());
|
||||
char name[512];
|
||||
strncpy(name,fname.toLatin1(),sizeof (name) - 1);
|
||||
name[sizeof (name) - 1] = '\0';
|
||||
FILE* fp=fopen(name,"wb");
|
||||
|
||||
if(fp != NULL) {
|
||||
|
BIN
icons/Darwin/DragNDrop Background.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_128x128.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_256x256.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_512x512.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
icons/Darwin/wsjtx.iconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 246 KiB |
3
icons/README
Normal file
@ -0,0 +1,3 @@
|
||||
Files under this directory are generated. Even though they are checked
|
||||
into source control they should not be modified directly. See
|
||||
../artwork/README for details of generating these files.
|