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
This commit is contained in:
Bill Somerville 2014-03-26 13:21:00 +00:00
parent 8dd4e8cfa3
commit 9247e10115
145 changed files with 18714 additions and 6544 deletions

23
AudioDevice.cpp Normal file
View 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);
}

View File

@ -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
View 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
View 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

View File

View 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})

View 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)

View 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)

View 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)

View 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 ()

View 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
View 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)

View 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}")

View File

@ -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
View 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

File diff suppressed because it is too large Load Diff

188
Configuration.hpp Normal file
View 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

File diff suppressed because it is too large Load Diff

1
Copyright.txt Normal file
View File

@ -0,0 +1 @@
Copyright (C) 2001-2014 by Joe Taylor, K1JT

View 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;
}

View 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

View File

@ -4,6 +4,8 @@
#include <QDebug>
#include "commons.h"
#include "moc_Detector.cpp"
extern "C" {
void fil4_(qint16*, qint32*, qint16*, qint32*);
}

View File

@ -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
View 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);
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,8 @@
#ifndef GETUSERID_HPP_
#define GETUSERID_HPP_
#include <QString>
QString get_user_id ();
#endif

766
HRDTransceiver.cpp Normal file
View 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
View 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
View 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, &current_mode, &current_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, &current_mode, &current_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, &current_mode, &current_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
View 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
View 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>

View File

@ -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.

View 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);
}
}

View 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
View 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"

View File

@ -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)

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>&amp;File</string>
</property>
<addaction name="exit_action"/>
</widget>
<widget class="QMenu" name="options_menu">
<property name="title">
<string>&amp;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&amp;xit</string>
</property>
</action>
<action name="configuration_action">
<property name="text">
<string>&amp;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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View File

@ -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),

View File

@ -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>

View 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
View 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

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 49 KiB

58
artwork/make_graphics.sh Normal file
View 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

View 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&amp;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

View 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
View File

@ -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
View File

@ -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
View File

@ -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/>

Binary file not shown.

Binary file not shown.

View File

@ -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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);

View File

@ -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) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

3
icons/README Normal file
View 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.

Some files were not shown because too many files have changed in this diff Show More