Added support for use of "Standard" locations for writable files.
This allows  writable files  to be  located in  the "correct"
         location for  each platform rather  than in the  directory of
         the  executable  which, in  general,  is  not recommended  or
         allowed in some cases.
         A preprocessor macro  WSJT_STANDARD_FILE_LOCATIONS is used to
         switch be tween old and new functionality, currently it is on
         by default.  It  can be turned off by defining  it to a false
         value (0)  or more simply  with cmake-gui setting  the option
         with  the same  name.  JTAlert  can  only work  with the  old
         non-standard file  locations until  Laurie VK3AMA  chooses to
         support the new file locations.
         Even  if the  above is  not  enabled; the  QSettings file  is
         written to a  user specific location so it will  be shared by
         all instances  of the  program (i.e.  across  upgrades).  See
         below for multiple concurrent instance support changes.
Added a command line parser module for Fortran.
         Added 'lib/options.f90'  to facilitate more  complex argument
         passing to jt9 to cover explicit file locations.
Changed the way multiple concurrent instances are handled.
         This is  to allow the program  to be run multiple  times from
         the same installation directory.
         A new wsjtx command line  optional argument is available "-r"
         or  "--rig"   which  enables  multiple   concurrent  instance
         support.  The  parameter of the  new option is a  unique name
         signifying  a rig  or equivalent.   The name  is used  as the
         shared memory segment key and  in window titles.  The name is
         also used to  access unique settings files  and writable data
         files like ALL.TXT  and log files.  No attempt  has been made
         to share these files between concurrent instances.
         If  "-r" or  "--rig" is  used  without a  parameter it  still
         enables  multiple   concurrent  instance  support   for  that
         instance.  All instances must use  a unique parameter, one of
         which may be empty.
         The        rig       name        is       appended        the
         QCoreApplication::applicationName() for convenient usage like
         window titles.
Set non Qt locale to "C".
         This ensures that C library functions give consistent results
         whatever the  system locale is set  to.  QApplication follows
         the system locale as before.  Thus using QApplication and its
         descendants  like widgets  and QString  for all  user visible
         formating will give correct l10n and using C/C++ library will
         give consistent formatting across locales.
Added top level C++ exception handling to main.cpp.
         Because  the   new  transceiver  framework   uses  exceptions
         internally, the main function now handles any exceptions that
         aren't caught.
Retired devsetup, replaced with Configuration.
         Configuration  is  a  class  that encapsulates  most  of  the
         configuration  behavior.   Because  rig configuration  is  so
         closely coupled with rig operation, Configuration serves as a
         proxy  for   access  to  the  rig   control  functions.   See
         Configuration.hpp  for  more  details  of  the  Configuration
         interface.
Menu changes.
         Various checkable  menu actions moved  from main menu  to the
         Configuration  dialog.   The  whole settings  menu  has  been
         retired with  the single  "Settings..."  action moved  to the
         file  menu  for  consistency  on  Mac  where  it  appears  as
         "Preferences" in line with Mac guidelines.
New data models for data used by the application.
         ADIF amateur band parameters,  free text message macros, spot
         working   frequencies  and,   station  information   (station
         descriptions and transverter offsets per band) each implement
         the  QAbstractItemModel interface  allowing them  to be  used
         directly with  Qt view widgets  (Bands.hpp, FrequencyList.hpp
         and, StationList.hpp).  Configuration  manages maintenance of
         an instance of  all but the former of the  above models.  The
         ADIF band  model is  owned by  Configuration but  requires no
         user maintenance as it is immutable.
Band combo box gets more functionality.
         This  widget is  now an  editable QComboBox  with some  extra
         input capabilities.
         The popup list is still the list of spot working frequencies,
         now  showing the  actual  frequency decorated  with the  band
         name.  This  allows multiple  spot frequencies  on a  band if
         required.
         The  line edit  allows direct  frequency entry  in mega-Hertz
         with  a completer  built  in to  suggest  the available  spot
         working frequencies.   It also  allows band name  entry where
         the  first  available  spot working  frequency  is  selected.
         Recognized band names are those  that are defined by the ADIF
         specification and  can be found  in in the  implementation of
         the ADIF bands model (Bands.cpp).
         If an out of band frequency  is chosen, the line edit shows a
         warning red  background and the  text "OOB".  Out of  band is
         only defined  by the  ADIF band limits  which in  general are
         wider than any entities regulations.
Qt 5.2 now supports default audio i/p and o/p devices.
         These devices are placeholders  for whatever the user defines
         as the  default device.   Because of  this they  need special
         treatment as  the actual device  used is chosen at  open time
         behind the scenes.
Close-down behavior is simplified.
         The close-down  semantics were broken such  that some objects
         were not being shut down cleanly, this required amendments to
         facilitate correct close down of threads.
User font selection added to Configuration UI.
         Buttons to set the application font and the font for the band
         and  Rx frequency  activity widgets  have been  added to  the
         Configuration UI to replace the file based font size control.
Free text macros now selected directly.
         The free text line edit  widgets are now editable combo boxes
         that have  the current free  text macro definitions  as their
         popup  list.   The old  context  menu  to  do this  has  been
         retired.
Astronomical data window dynamically formatted and has font a chooser.
         This window is now autonomous,  has its own font chooser and,
         dynamically resizes to cover the contents.
Double click to Tx enabled now has its own widget in the status bar.
QDir used for portable path and file name handling throughout.
The  "Monitor", "Decode",  "Enable  Tx" and,  "Tune"  buttons are  now
checkable.
         Being checkable allows these  buttons control their own state
         and rendering.
Calls to PSK Reporter interface simplified.
         In   mainwindow.cpp  the   calls   to   this  interface   are
         rationalized to just 3 locations.
Manipulation of ALL.TXT simplified.
         Moved, where possible, to common functions.
Elevated frequency types to be Qt types.
         Frequency  and FrequencyDelta  defined as  Qt types  in their
         meta-type system  (Radio.hpp).  They  are integral  types for
         maximum accuracy.
Re-factored rig control calls in mainwindow.cpp.
         The new  Configuration proxy  access to rig  control required
         many changes  (mostly simplifications) to the  MainWindow rig
         control code.  Some  common code has been  gathered in member
         functions   like   qsy(),   monitor(),   band_changed()   and
         auto_tx_mode().
Rig control enhancements.
         The  rig control  for  clients interface  is  declared as  an
         abstract    interface   (See    Transceiver.hpp).    Concrete
         implementations of this interface are provided for the Hamlib
         rig  control library,  DX Lab  Suite Commander  via a  TCP/IP
         command channel, Ham  Radio Deluxe also via  a TCP/IP command
         channel and, OmniRig via its Windows COM server interface.
         Concrete Transceiver implementations are expected to be moved
         to a separate thread after construction since many operations
         are blocking  and not suitable  for running in a  GUI thread.
         To facilitate this all  instantiation of concrete Transceiver
         instances are handled by  Configuration using a factory class
         (TransceiverFactory)   for   configuration  parameter   based
         instantiation.
         Various   common  functionality   shared  by   different  rig
         interface implementations  are factored out into  helper base
         classes that  implement or delegate parts  of the Transceiver
         interface.  They  are TransceiverBase  which caches  state to
         minimize expensive rig commands, it also maps the Transceiver
         interface  into a  more  convenient  form for  implementation
         (template methods).  PollingTransceiver that provides a state
         polling   mechanism  that   only   reports  actual   changes.
         EmulateSplitTransceiver  that  provides  split  operation  by
         QSYing on PTT state changes.
         EmulateSplitTransceiver can  be used with  any implementation
         as  it follows  the GoF  Decorator pattern  and can  wrap any
         Transceiver implementation.
         OmniRigTransceiver is  derived directly  from TransceiverBase
         since  it doesn't  require  polling due  to its  asynchronous
         nature.  OmniRigTransceiver is only built on Windows as it is
         a COM server client.  To build  it you must first install the
         OmniRig     client     on     the     development     machine
         (http://www.dxatlas.com/omnirig/).
         DXLabSuiteCommanderTransceiver          derives          from
         PollingTransceiver since  it is a  synchronous communications
         channel.   No  third  party  library  is  required  for  this
         interface.
         HRDTransceiver also derives from PollingTransceiver.  The HRD
         interface  library has  been  reverse  engineered to  provide
         functionality with  all available versions of  HRD.  No third
         party libraries are required.
         HamlibTransceiver  likewise  derives from  PollingTransceiver
         since  the Hamlib  asynchronous interface  is non-functional.
         Although this  class will interface with  the release version
         of Hamlib (1.2.15.3);  for correct operation on  most rigs it
         needs to  run with the  latest master branch code  of Hamlib.
         During development many changes to Hamlib have been submitted
         and accepted,  hence this requirement.  Hamlib  source can be
         obtained from git://git.code.sf.net/p/hamlib/code  and at the
         time of writing he master branch was at SHA 6e4432.
         The Hamlib interface directly calls the "C" interface and the
         modified rigclass.{h,cpp} files have been retired.
         There is a rig type selection of "None" which may be used for
         non-CAT  rigs, this  is actually  a connection  to the  dummy
         Hamlib device.
         PollingTransvceiver   derives    from   TransceiverBase   and
         TransceiverBase derives from the Transceiver interface.
         Each interface implementation offers  some possibility of PTT
         control via  a different serial  port than the CAT  port.  We
         also support PTT  control directly via a  second serial port.
         This is done  by delegating to a dummy  Hamlib instance which
         is   only   used   for   PTT  control.    This   means   that
         DXLabSuiteCommanderTransceiver,       HRDTransceiver      and
         OmniRigTransceiver  always  wrap  a  dummy  HamlibTransceiver
         instance.  The  factory class TransceiverFactory  manages all
         these constructional complexities.
         Serial port  selection combo  boxes are  now editable  with a
         manually  entered value  being  saved to  the settings  file.
         This allows  a non-standard  port device  to be  used without
         having to edit the settings file manually.
         For TCP/IP  network CAT  interfaces; the network  address and
         port  may  be specified  allowing  the  target device  to  be
         located on a different machine  from the one running wsjtx if
         required.  The  default used when  the address field  is left
         blank is the correct one for normal usage on the local host.
         Selecting a polling  interval of zero is  no longer possible,
         this  is because  the rig  control capability  can no  longer
         support one way connection.  This  is in line with most other
         CAT control software.
         In the Configuration dialog there are options to select split
         mode  control  by  the  software  and  mode  control  by  the
         software.  For  the former  "None", "Rig"  and "Fake  it" are
         available,  for  the latter  "None",  "USB"  and, "Data"  are
         available.  Because  tone generation is implicitly  linked to
         split mode  operation; it is  no longer possible to  have the
         software in split  mode and the rig not or  vice versa.  This
         may mean some rigs cannot be used in split mode and therefore
         not in dual JT65+JT9 until  issues with CAT control with that
         rig are  resolved.  Single  mode with VOX  keying and  no CAT
         control are still possible so even the most basic transceiver
         setup is supported as before.
         Configuration now  supports a  frequency offset  suitable for
         transverter   operation.     The   station    details   model
         (StationList.hpp) includes  a column  to store an  offset for
         each band if required.
CMake build script improvements.
         The CMakeLists.txt from the  'lib' directory has been retired
         with its  contents merged into the  top level CMakeLists.txt.
         Install  target support  has been  greatly improved  with the
         Release build  configuration now building a  fully standalone
         installation  on Mac  and Windows.   The Debug  configuration
         still   builds   an   installation   that   has   environment
         dependencies for  external libraries, which is  desirable for
         testing and debugging.
         Package target  support is largely complete  for Mac, Windows
         and, Linux, it should be possible to build release installers
         directly from CMake/CPack.
         Cmake FindXXXX.cmake  modules have been added  to improve the
         location of fftw-3 and Hamlib packages.
         Version numbers are now stored in Versions.cmake and work in
         concert with automatic svn revision lookup during build.  The
         version string becomes 'rlocal'± if there are any uncommitted
         changes in the build source tree.
Moved resource like files to Qt resources.
      	 Because location of resource files  (when they cannot go into
      	 the  installation directory  because of  packaging rules)  is
      	 hard to standardize.  I have used the Qt  resource system for
      	 all ancillary data files. Some  like kvasd.dat are dumped out
      	 to the temp (working directory)  because they are accessed by
      	 an external program, others like the audio samples are copied
      	 out so  they appear in  the data directory under  the default
      	 save directory.
git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3929 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
			
			
							
								
								
									
										23
									
								
								AudioDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | ||||
| #include "AudioDevice.hpp" | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   struct init | ||||
|   { | ||||
|     init () | ||||
|     { | ||||
|       qRegisterMetaType<AudioDevice::Channel> ("AudioDevice::Channel"); | ||||
|     } | ||||
|   } static_initializer; | ||||
| } | ||||
| 
 | ||||
| bool AudioDevice::open (OpenMode mode, Channel channel) | ||||
| { | ||||
|   m_channel = channel; | ||||
| 
 | ||||
|   // ensure we are unbuffered
 | ||||
|   return QIODevice::open (mode | QIODevice::Unbuffered); | ||||
| } | ||||
| 
 | ||||
| @ -24,13 +24,7 @@ public: | ||||
|     return "both" == s ? Both : "right" == s ? Right : "left" == s ? Left : Mono; | ||||
|   } | ||||
| 
 | ||||
|   bool open (OpenMode mode, Channel channel) | ||||
|   { | ||||
|     m_channel = channel; | ||||
| 
 | ||||
|     // ensure we are unbuffered
 | ||||
|     return QIODevice::open (mode | QIODevice::Unbuffered); | ||||
|   } | ||||
|   bool open (OpenMode mode, Channel channel); | ||||
| 
 | ||||
|   bool isSequential () const {return true;} | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										196
									
								
								Bands.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,196 @@ | ||||
| #include "Bands.hpp" | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include <QString> | ||||
| #include <QVariant> | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   // Local structure to hold a single ADIF band definition.
 | ||||
|   struct ADIF_band | ||||
|   { | ||||
|     char const * const name_; | ||||
|     Radio::Frequency lower_bound_; | ||||
|     Radio::Frequency upper_bound_; | ||||
|   }; | ||||
| 
 | ||||
|   // Table of ADIF band definitions as defined in the ADIF
 | ||||
|   // specification.
 | ||||
|   ADIF_band constexpr ADIF_bands[] = { | ||||
|     {"2190m", 	136000u, 	137000u}, | ||||
|     {"630m", 	472000u, 	479000u}, | ||||
|     {"560m", 	501000u, 	504000u}, | ||||
|     {"160m", 	1800000u, 	2000000u}, | ||||
|     {"80m", 	3500000u, 	4000000u}, | ||||
|     {"60m", 	5102000u, 	5406500u}, | ||||
|     {"40m", 	7000000u, 	7300000u}, | ||||
|     {"30m", 	10000000u, 	10150000u}, | ||||
|     {"20m", 	14000000u, 	14350000u}, | ||||
|     {"17m", 	18068000u, 	18168000u}, | ||||
|     {"15m", 	21000000u, 	21450000u}, | ||||
|     {"12m", 	24890000u, 	24990000u}, | ||||
|     {"10m", 	28000000u, 	29700000u}, | ||||
|     {"6m", 	50000000u, 	54000000u}, | ||||
|     {"4m", 	70000000u, 	71000000u}, | ||||
|     {"2m", 	144000000u, 	148000000u}, | ||||
|     {"1.25m", 	222000000u, 	225000000u}, | ||||
|     {"70cm", 	420000000u, 	450000000u}, | ||||
|     {"33cm", 	902000000u, 	928000000u}, | ||||
|     {"23cm", 	1240000000u, 	1300000000u}, | ||||
|     {"13cm", 	2300000000u, 	2450000000u}, | ||||
|     {"9cm", 	3300000000u, 	3500000000u}, | ||||
|     {"6cm", 	5650000000u, 	5925000000u}, | ||||
|     {"3cm", 	10000000000u,	10500000000u}, | ||||
|     {"1.25cm", 	24000000000u,	24250000000u}, | ||||
|     {"6mm", 	47000000000u,	47200000000u}, | ||||
|     {"4mm", 	75500000000u,	81000000000u}, | ||||
|     {"2.5mm", 	119980000000u,	120020000000u}, | ||||
|     {"2mm", 	142000000000u,	149000000000u}, | ||||
|     {"1mm", 	241000000000u,	250000000000u}, | ||||
|   }; | ||||
| 
 | ||||
|   auto constexpr out_of_band = "OOB"; | ||||
| 
 | ||||
|   int constexpr table_rows () | ||||
|   { | ||||
|     return sizeof (ADIF_bands) / sizeof (ADIF_bands[0]); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| Bands::Bands (QObject * parent) | ||||
|   : QAbstractTableModel {parent} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| QModelIndex Bands::find (QVariant const& v) const | ||||
| { | ||||
|   auto f = v.value<Radio::Frequency> (); | ||||
|   auto end_iter = ADIF_bands + table_rows (); | ||||
|   auto row_iter = std::find_if (ADIF_bands, end_iter, [f] (ADIF_band const& band) { | ||||
|       return band.lower_bound_ <= f && f <= band.upper_bound_; | ||||
|     }); | ||||
|   if (row_iter != end_iter) | ||||
|     { | ||||
|       return index (row_iter - ADIF_bands, 0); // return the band row index
 | ||||
|     } | ||||
| 
 | ||||
|   return QModelIndex {}; | ||||
| } | ||||
| 
 | ||||
| int Bands::rowCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : table_rows (); | ||||
| } | ||||
| 
 | ||||
| int Bands::columnCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : 3; | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags Bands::flags (QModelIndex const& index) const | ||||
| { | ||||
|   return QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled; | ||||
| } | ||||
| 
 | ||||
| QVariant Bands::data (QModelIndex const& index, int role) const | ||||
| { | ||||
|   QVariant item; | ||||
| 
 | ||||
|   if (!index.isValid ()) | ||||
|     { | ||||
|       // Hijack root for OOB string.
 | ||||
|       if (Qt::DisplayRole == role) | ||||
|         { | ||||
|           item = out_of_band; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       auto row = index.row (); | ||||
|       auto column = index.column (); | ||||
| 
 | ||||
|       if (row < table_rows ()) | ||||
|         { | ||||
|           switch (role) | ||||
|             { | ||||
|             case Qt::ToolTipRole: | ||||
|             case Qt::AccessibleDescriptionRole: | ||||
|               switch (column) | ||||
|                 { | ||||
|                 case 0: item = tr ("Band name"); break; | ||||
|                 case 1: item = tr ("Lower frequency limit"); break; | ||||
|                 case 2: item = tr ("Upper frequency limit"); break; | ||||
|                 } | ||||
|               break; | ||||
| 
 | ||||
|             case SortRole: | ||||
|             case Qt::DisplayRole: | ||||
|             case Qt::EditRole: | ||||
|               switch (column) | ||||
|                 { | ||||
|                 case 0: | ||||
|                   if (SortRole == role) | ||||
|                     { | ||||
|                       // band name sorts by lower bound
 | ||||
|                       item = ADIF_bands[row].lower_bound_; | ||||
|                     } | ||||
|                   else | ||||
|                     { | ||||
|                       item = ADIF_bands[row].name_; | ||||
|                     } | ||||
|                   break; | ||||
| 
 | ||||
|                 case 1: item = ADIF_bands[row].lower_bound_; break; | ||||
|                 case 2: item = ADIF_bands[row].upper_bound_; break; | ||||
|                 } | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::AccessibleTextRole: | ||||
|               switch (column) | ||||
|                 { | ||||
|                 case 0: item = ADIF_bands[row].name_; break; | ||||
|                 case 1: item = ADIF_bands[row].lower_bound_; break; | ||||
|                 case 2: item = ADIF_bands[row].upper_bound_; break; | ||||
|                 } | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::TextAlignmentRole: | ||||
|               switch (column) | ||||
|                 { | ||||
|                 case 0: | ||||
|                   item = Qt::AlignHCenter + Qt::AlignVCenter; | ||||
|                   break; | ||||
| 
 | ||||
|                 case 1: | ||||
|                 case 2: | ||||
|                   item = Qt::AlignRight + Qt::AlignVCenter; | ||||
|                   break; | ||||
|                 } | ||||
|               break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   return item; | ||||
| } | ||||
| 
 | ||||
| QVariant Bands::headerData (int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|   QVariant result; | ||||
| 
 | ||||
|   if (Qt::DisplayRole == role && Qt::Horizontal == orientation) | ||||
|     { | ||||
|       switch (section) | ||||
|         { | ||||
|         case 0: result = tr ("Band"); break; | ||||
|         case 1: result = tr ("Lower Limit"); break; | ||||
|         case 2: result = tr ("Upper Limit"); break; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       result = QAbstractTableModel::headerData (section, orientation, role); | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
							
								
								
									
										58
									
								
								Bands.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,58 @@ | ||||
| #ifndef BANDS_HPP__ | ||||
| #define BANDS_HPP__ | ||||
| 
 | ||||
| #include <QAbstractTableModel> | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| //
 | ||||
| // Class Bands
 | ||||
| //
 | ||||
| //  Encapsulates information  about amateur radio bands  as defined by
 | ||||
| //  the  ADIF specification.  The model  is immutable.   The rows  are
 | ||||
| //  stored in asscending order of frequency.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Provides  a  well  known  band  name mapped  to  lower  and  upper
 | ||||
| //  frequency  limits.  Also  provides   a  convenience  operation  to
 | ||||
| //  determine the band details for  any given frequency, the result of
 | ||||
| //  which may be  invalid if the given frequency doesn't  lie within a
 | ||||
| //  recognised band.
 | ||||
| //
 | ||||
| // Collaborations
 | ||||
| //
 | ||||
| //  Implements the QAbstractTableModel interface as an immutable table
 | ||||
| //  where rows  are bands and  columns are band name,  lower frequency
 | ||||
| //  limit and, upper ferquency limit respectively.
 | ||||
| //
 | ||||
| class Bands final | ||||
|   : public QAbstractTableModel | ||||
| { | ||||
| public: | ||||
|   explicit Bands (QObject * parent = nullptr); | ||||
| 
 | ||||
|   //
 | ||||
|   // Model API
 | ||||
|   //
 | ||||
|   QModelIndex find (QVariant const&) const; // find band Frequency is in
 | ||||
| 
 | ||||
|   // Custom role for sorting.
 | ||||
|   static int constexpr SortRole = Qt::UserRole; | ||||
| 
 | ||||
|   // Implement the QAbstractTableModel interface
 | ||||
|   int rowCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   int columnCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override; | ||||
|   QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; | ||||
| 
 | ||||
|   // The value return for the Qt::DisplayRole role for the root of the
 | ||||
|   // model (invalid index) is a special string representing out of
 | ||||
|   // band.
 | ||||
|   //
 | ||||
|   // All columns return a number for the custom role SortRole, this
 | ||||
|   // number defines a strict frequency order for the rows.
 | ||||
|   QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										93
									
								
								CMake/Modules/FindFFTW3.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,93 @@ | ||||
| # - Try to find FFTW3. | ||||
| # Usage: find_package(FFTW3 [COMPONENTS [single double long-double threads]]) | ||||
| # | ||||
| # Variables used by this module: | ||||
| #  FFTW3_ROOT_DIR             - FFTW3 root directory | ||||
| # Variables defined by this module: | ||||
| #  FFTW3_FOUND                - system has FFTW3 | ||||
| #  FFTW3_INCLUDE_DIR          - the FFTW3 include directory (cached) | ||||
| #  FFTW3_INCLUDE_DIRS         - the FFTW3 include directories | ||||
| #                               (identical to FFTW3_INCLUDE_DIR) | ||||
| #  FFTW3[FL]?_LIBRARY         - the FFTW3 library - double, single(F),  | ||||
| #                               long-double(L) precision (cached) | ||||
| #  FFTW3[FL]?_THREADS_LIBRARY - the threaded FFTW3 library - double, single(F),  | ||||
| #                               long-double(L) precision (cached) | ||||
| #  FFTW3_LIBRARIES            - list of all FFTW3 libraries found | ||||
| 
 | ||||
| # Copyright (C) 2009-2010 | ||||
| # ASTRON (Netherlands Institute for Radio Astronomy) | ||||
| # P.O.Box 2, 7990 AA Dwingeloo, The Netherlands | ||||
| # | ||||
| # This file is part of the LOFAR software suite. | ||||
| # The LOFAR software suite is free software: you can redistribute it and/or | ||||
| # modify it under the terms of the GNU General Public License as published | ||||
| # by the Free Software Foundation, either version 3 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # The LOFAR software suite is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # | ||||
| # You should have received a copy of the GNU General Public License along | ||||
| # with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| # $Id: FindFFTW3.cmake 15918 2010-06-25 11:12:42Z loose $ | ||||
| 
 | ||||
| # Use double precision by default. | ||||
| if (FFTW3_FIND_COMPONENTS MATCHES "^$") | ||||
|   set (_components double) | ||||
| else () | ||||
|   set (_components ${FFTW3_FIND_COMPONENTS}) | ||||
| endif () | ||||
| 
 | ||||
| # Loop over each component. | ||||
| set (_libraries) | ||||
| foreach (_comp ${_components}) | ||||
|   if (_comp STREQUAL "single") | ||||
|     list (APPEND _libraries fftw3f) | ||||
|   elseif (_comp STREQUAL "double") | ||||
|     list (APPEND _libraries fftw3) | ||||
|   elseif (_comp STREQUAL "long-double") | ||||
|     list (APPEND _libraries fftw3l) | ||||
|   elseif (_comp STREQUAL "threads") | ||||
|     set (_use_threads ON) | ||||
|   else (_comp STREQUAL "single") | ||||
|     message (FATAL_ERROR "FindFFTW3: unknown component `${_comp}' specified. " | ||||
|       "Valid components are `single', `double', `long-double', and `threads'.") | ||||
|   endif (_comp STREQUAL "single") | ||||
| endforeach (_comp ${_components}) | ||||
| 
 | ||||
| # If using threads, we need to link against threaded libraries as well. | ||||
| if (_use_threads) | ||||
|   set (_thread_libs) | ||||
|   foreach (_lib ${_libraries}) | ||||
|     list (APPEND _thread_libs ${_lib}_threads) | ||||
|   endforeach (_lib ${_libraries}) | ||||
|   set (_libraries ${_thread_libs} ${_libraries}) | ||||
| endif (_use_threads) | ||||
| 
 | ||||
| # Keep a list of variable names that we need to pass on to | ||||
| # find_package_handle_standard_args(). | ||||
| set (_check_list) | ||||
| 
 | ||||
| # Search for all requested libraries. | ||||
| foreach (_lib ${_libraries}) | ||||
|   string (TOUPPER ${_lib} _LIB) | ||||
|   find_library (${_LIB}_LIBRARY NAMES ${_lib} ${_lib}-3 | ||||
|     HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES lib) | ||||
|   mark_as_advanced (${_LIB}_LIBRARY) | ||||
|   list (APPEND FFTW3_LIBRARIES ${${_LIB}_LIBRARY}) | ||||
|   list (APPEND _check_list ${_LIB}_LIBRARY) | ||||
| endforeach (_lib ${_libraries}) | ||||
| 
 | ||||
| # Search for the header file. | ||||
| find_path (FFTW3_INCLUDE_DIR fftw3.h  | ||||
|   HINTS ${FFTW3_ROOT_DIR} PATH_SUFFIXES include) | ||||
| mark_as_advanced (FFTW3_INCLUDE_DIR) | ||||
| list(APPEND _check_list FFTW3_INCLUDE_DIR) | ||||
| 
 | ||||
| # Handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if | ||||
| # all listed variables are TRUE | ||||
| include (FindPackageHandleStandardArgs) | ||||
| find_package_handle_standard_args (FFTW3 DEFAULT_MSG ${_check_list}) | ||||
							
								
								
									
										28
									
								
								CMake/Modules/Findhamlib.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | ||||
| # - Try to find hamlib | ||||
| # Once done, this will define: | ||||
| # | ||||
| #  hamlib_FOUND - system has Hamlib-2 | ||||
| #  hamlib_INCLUDE_DIRS - the Hamlib-2 include directories | ||||
| #  hamlib_LIBRARIES - link these to use Hamlib-2 | ||||
| 
 | ||||
| include (LibFindMacros) | ||||
| 
 | ||||
| # Use pkg-config to get hints about paths | ||||
| libfind_pkg_check_modules (hamlib_PKGCONF hamlib) | ||||
| 
 | ||||
| # Include dirs | ||||
| find_path (hamlib_INCLUDE_DIR | ||||
|   NAMES hamlib/rig.h | ||||
|   PATHS ${hamlib_PKGCONF_INCLUDE_DIRS} | ||||
| ) | ||||
| 
 | ||||
| # The library | ||||
| find_library (hamlib_LIBRARY | ||||
|   NAMES hamlib hamlib-2 | ||||
|   PATHS ${hamlib_PKGCONF_LIBRARY_DIRS} | ||||
| ) | ||||
| 
 | ||||
| # Set the include dir variables and the libraries and let libfind_process do the rest | ||||
| set (hamlib_PROCESS_INCLUDES hamlib_INCLUDE_DIR) | ||||
| set (hamlib_PROCESS_LIBS hamlib_LIBRARY) | ||||
| libfind_process (hamlib) | ||||
							
								
								
									
										112
									
								
								CMake/Modules/LibFindMacros.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,112 @@ | ||||
| # Version 1.0 (2013-04-12) | ||||
| # Public Domain, originally written by Lasse Kärkkäinen <tronic@zi.fi> | ||||
| # Published at http://www.cmake.org/Wiki/CMake:How_To_Find_Libraries | ||||
| 
 | ||||
| # If you improve the script, please modify the forementioned wiki page because | ||||
| # I no longer maintain my scripts (hosted as static files at zi.fi). Feel free | ||||
| # to remove this entire header if you use real version control instead. | ||||
| 
 | ||||
| # Changelog: | ||||
| # 2013-04-12  Added version number (1.0) and this header, no other changes | ||||
| # 2009-10-08  Originally published | ||||
| 
 | ||||
| 
 | ||||
| # Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments | ||||
| # used for the current package. For this to work, the first parameter must be the | ||||
| # prefix of the current package, then the prefix of the new package etc, which are | ||||
| # passed to find_package. | ||||
| macro (libfind_package PREFIX) | ||||
|   set (LIBFIND_PACKAGE_ARGS ${ARGN}) | ||||
|   if (${PREFIX}_FIND_QUIETLY) | ||||
|     set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) | ||||
|   endif (${PREFIX}_FIND_QUIETLY) | ||||
|   if (${PREFIX}_FIND_REQUIRED) | ||||
|     set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) | ||||
|   endif (${PREFIX}_FIND_REQUIRED) | ||||
|   find_package(${LIBFIND_PACKAGE_ARGS}) | ||||
| endmacro (libfind_package) | ||||
| 
 | ||||
| # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) | ||||
| # where they added pkg_check_modules. Consequently I need to support both in my scripts | ||||
| # to avoid those deprecated warnings. Here's a helper that does just that. | ||||
| # Works identically to pkg_check_modules, except that no checks are needed prior to use. | ||||
| macro (libfind_pkg_check_modules PREFIX PKGNAME) | ||||
|   if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) | ||||
|     include(UsePkgConfig) | ||||
|     pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) | ||||
|   else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) | ||||
|     find_package(PkgConfig) | ||||
|     if (PKG_CONFIG_FOUND) | ||||
|       pkg_check_modules(${PREFIX} ${PKGNAME}) | ||||
|     endif (PKG_CONFIG_FOUND) | ||||
|   endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) | ||||
| endmacro (libfind_pkg_check_modules) | ||||
| 
 | ||||
| # Do the final processing once the paths have been detected. | ||||
| # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain | ||||
| # all the variables, each of which contain one include directory. | ||||
| # Ditto for ${PREFIX}_PROCESS_LIBS and library files. | ||||
| # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. | ||||
| # Also handles errors in case library detection was required, etc. | ||||
| macro (libfind_process PREFIX) | ||||
|   # Skip processing if already processed during this run | ||||
|   if (NOT ${PREFIX}_FOUND) | ||||
|     # Start with the assumption that the library was found | ||||
|     set (${PREFIX}_FOUND TRUE) | ||||
| 
 | ||||
|     # Process all includes and set _FOUND to false if any are missing | ||||
|     foreach (i ${${PREFIX}_PROCESS_INCLUDES}) | ||||
|       if (${i}) | ||||
|         set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) | ||||
|         mark_as_advanced(${i}) | ||||
|       else (${i}) | ||||
|         set (${PREFIX}_FOUND FALSE) | ||||
|       endif (${i}) | ||||
|     endforeach (i) | ||||
| 
 | ||||
|     # Process all libraries and set _FOUND to false if any are missing | ||||
|     foreach (i ${${PREFIX}_PROCESS_LIBS}) | ||||
|       if (${i}) | ||||
|         set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) | ||||
|         mark_as_advanced(${i}) | ||||
|       else (${i}) | ||||
|         set (${PREFIX}_FOUND FALSE) | ||||
|       endif (${i}) | ||||
|     endforeach (i) | ||||
| 
 | ||||
|     # Print message and/or exit on fatal error | ||||
|     if (${PREFIX}_FOUND) | ||||
|       if (NOT ${PREFIX}_FIND_QUIETLY) | ||||
|         message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") | ||||
|       endif (NOT ${PREFIX}_FIND_QUIETLY) | ||||
|     else (${PREFIX}_FOUND) | ||||
|       if (${PREFIX}_FIND_REQUIRED) | ||||
|         foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) | ||||
|           message("${i}=${${i}}") | ||||
|         endforeach (i) | ||||
|         message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") | ||||
|       endif (${PREFIX}_FIND_REQUIRED) | ||||
|     endif (${PREFIX}_FOUND) | ||||
|   endif (NOT ${PREFIX}_FOUND) | ||||
| endmacro (libfind_process) | ||||
| 
 | ||||
| macro(libfind_library PREFIX basename) | ||||
|   set(TMP "") | ||||
|   if(MSVC80) | ||||
|     set(TMP -vc80) | ||||
|   endif(MSVC80) | ||||
|   if(MSVC90) | ||||
|     set(TMP -vc90) | ||||
|   endif(MSVC90) | ||||
|   set(${PREFIX}_LIBNAMES ${basename}${TMP}) | ||||
|   if(${ARGC} GREATER 2) | ||||
|     set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) | ||||
|     string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) | ||||
|     set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) | ||||
|   endif(${ARGC} GREATER 2) | ||||
|   find_library(${PREFIX}_LIBRARY | ||||
|     NAMES ${${PREFIX}_LIBNAMES} | ||||
|     PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} | ||||
|   ) | ||||
| endmacro(libfind_library) | ||||
| 
 | ||||
							
								
								
									
										36
									
								
								CMake/Modules/QtAxMacros.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,36 @@ | ||||
| # | ||||
| # Macros for processing ActiveX and COM controls with ActiveQt | ||||
| # | ||||
| 
 | ||||
| if (WIN32) | ||||
|   include (CMakeParseArguments) | ||||
| 
 | ||||
|   find_program (DUMPCPP_Executable dumpcpp.exe) | ||||
| 
 | ||||
|   # wrap_ax_server (outfiles inputfile ...) | ||||
| 
 | ||||
|   function (WRAP_AX_SERVER outfiles) | ||||
|     set (options) | ||||
|     set (oneValueArgs) | ||||
|     set (multiValueArgs OPTIONS) | ||||
|      | ||||
|     cmake_parse_arguments (_WRAP_AX_SERVER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||||
|      | ||||
|     set (ax_server_files ${_WRAP_AX_SERVER_UNPARSED_ARGUMENTS}) | ||||
|     set (ax_server_options ${_WRAP_AX_SERVER_OPTIONS}) | ||||
| 
 | ||||
|     foreach (it ${ax_server_files}) | ||||
|       get_filename_component (outfile ${it} NAME_WE) | ||||
|       get_filename_component (infile ${it} ABSOLUTE) | ||||
|       set (outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfile}) | ||||
|       add_custom_command ( | ||||
| 	OUTPUT ${outfile}.h ${outfile}.cpp | ||||
| 	COMMAND ${DUMPCPP_Executable} | ||||
| 	ARGS ${AX_SERVER_options} -o "${outfile}" "${infile}" | ||||
| 	MAIN_DEPENDENCY ${infile} VERBATIM) | ||||
|       list (APPEND ${outfiles} ${outfile}.cpp) | ||||
|     endforeach() | ||||
|     set(${outfiles} ${${outfiles}} PARENT_SCOPE) | ||||
|   endfunction () | ||||
| 
 | ||||
| endif (WIN32) | ||||
							
								
								
									
										8
									
								
								CMake/VersionCompute.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,8 @@ | ||||
| # Load version number components. | ||||
| include (${wsjtx_SOURCE_DIR}/Versions.cmake) | ||||
| 
 | ||||
| # Compute the full version string. | ||||
| set (wsjtx_VERSION ${WSJTX_VERSION_MAJOR}.${WSJTX_VERSION_MINOR}.${WSJTX_VERSION_PATCH}) | ||||
| if (WSJTX_RC) | ||||
|   set (wsjtx_VERSION ${wsjtx_VERSION}-rc${WSJTX_RC}) | ||||
| endif () | ||||
							
								
								
									
										22
									
								
								CMake/cmake_uninstall.cmake.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | ||||
| if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") | ||||
|   message (FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") | ||||
| endif () | ||||
| 
 | ||||
| file (READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) | ||||
| string (REGEX REPLACE "\n" ";" files "${files}") | ||||
| foreach (file ${files}) | ||||
|   message (STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") | ||||
|   if (EXISTS "$ENV{DESTDIR}${file}") | ||||
|     exec_program ( | ||||
|       "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" | ||||
|       OUTPUT_VARIABLE rm_out | ||||
|       RETURN_VALUE rm_retval | ||||
|       ) | ||||
|     if ("${rm_retval}" STREQUAL 0) | ||||
|     else () | ||||
|       message (FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") | ||||
|     endif () | ||||
|   else () | ||||
|     message (STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") | ||||
|   endif () | ||||
| endforeach () | ||||
							
								
								
									
										15
									
								
								CMake/getsvn.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,15 @@ | ||||
| find_package (Subversion) | ||||
| if (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn) | ||||
|   # the FindSubversion.cmake module is part of the standard distribution | ||||
|   include (FindSubversion) | ||||
|   # extract working copy information for SOURCE_DIR into MY_XXX variables | ||||
|   Subversion_WC_INFO (${SOURCE_DIR} MY) | ||||
|   # write a file with the SVNVERSION define | ||||
|   file (WRITE svnversion.h.txt "#define SVNVERSION r${MY_WC_REVISION}\n") | ||||
| else (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn) | ||||
|   file (WRITE svnversion.h.txt "#define SVNVERSION local\n") | ||||
| endif (Subversion_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.svn) | ||||
| 
 | ||||
| # copy the file to the final header only if the version changes | ||||
| # reduces needless rebuilds | ||||
| execute_process (COMMAND ${CMAKE_COMMAND} -E copy_if_different svnversion.h.txt svnversion.h) | ||||
							
								
								
									
										81
									
								
								CMakeCPackOptions.cmake.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,81 @@ | ||||
| # This file is configured at cmake time, and loaded at cpack time. | ||||
| # To pass variables to cpack from cmake, they must be configured | ||||
| # in this file. | ||||
| 
 | ||||
| set (CPACK_PACKAGE_VENDOR "Joe Taylor, K1JT") | ||||
| set (CPACK_PACKAGE_CONTACT "k1jt@arrl.net") | ||||
| set (CPACK_PACKAGE_DESCRIPTION_FILE "@PROJECT_SOURCE_DIR@/Copyright.txt") | ||||
| set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "WSJT-X - JT9 and JT65 Modes for LF, MF and HF Amateur Radio") | ||||
| set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/LICENSE_WHEATLEY.txt") | ||||
| set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME}) | ||||
| set (CPACK_PACKAGE_EXECUTABLES wsjtx "WSJT-X") | ||||
| set (CPACK_CREATE_DESKTOP_LINKS wsjtx) | ||||
| set (CPACK_STRIP_FILES TRUE) | ||||
| 
 | ||||
| # | ||||
| # components | ||||
| # | ||||
| #set (CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "WSJT-X Application") | ||||
| #set (CPACK_COMPONENT_APPLICATIONS_DESCRIPTION "@WSJTX_DESCRIPTION_SUMMARY@") | ||||
| 
 | ||||
| if (CPACK_GENERATOR MATCHES "NSIS") | ||||
|   set (CPACK_STRIP_FILES FALSE)	# breaks Qt packaging on Windows | ||||
| 
 | ||||
|   set (CPACK_NSIS_INSTALL_ROOT "C:\\WSJT") | ||||
| 
 | ||||
|   # set the install/unistall icon used for the installer itself | ||||
|   # There is a bug in NSI that does not handle full unix paths properly. | ||||
|   set (CPACK_NSIS_MUI_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\wsjtx.ico") | ||||
|   set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\wsjtx.ico") | ||||
|   # set the package header icon for MUI | ||||
|   set (CPACK_PACKAGE_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\installer_logo.bmp") | ||||
|   # tell cpack to create links to the doc files | ||||
|   set (CPACK_NSIS_MENU_LINKS | ||||
|     "@PROJECT_MANUAL@" "WSJT-X Documentation" | ||||
|     "@PROJECT_HOMEPAGE@" "WSJT-X Web Site" | ||||
|     ) | ||||
|   # Use the icon from wsjtx for add-remove programs | ||||
|   set (CPACK_NSIS_INSTALLED_ICON_NAME "@PROJECT_BINARY_DIR@\\wsjtx.exe") | ||||
| 
 | ||||
|   set (CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") | ||||
|   set (CPACK_NSIS_HELP_LINK "@PROJECT_MANUAL@") | ||||
|   set (CPACK_NSIS_URL_INFO_ABOUT "@PROJECT_HOMEPAGE@") | ||||
|   set (CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}") | ||||
|   set (CPACK_NSIS_MUI_FINISHPAGE_RUN "wsjtx.exe") | ||||
|   set (CPACK_NSIS_MODIFY_PATH ON) | ||||
| endif () | ||||
| 
 | ||||
| if ("${CPACK_GENERATOR}" STREQUAL "PackageMaker") | ||||
|   set (CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-pkg") | ||||
|   set (CPACK_PACKAGE_DEFAULT_LOCATION "/Applications") | ||||
|   set (CPACK_PACKAGING_INSTALL_PREFIX "/") | ||||
| endif () | ||||
| 
 | ||||
| if ("${CPACK_GENERATOR}" STREQUAL "DragNDrop") | ||||
|   set (CPACK_DMG_BACKGROUND_IMAGE "@PROJECT_SOURCE_DIR@/icons/Darwin/DragNDrop Background.png") | ||||
|   set (CPACK_DMG_DS_STORE "@PROJECT_SOURCE_DIR@/wsjtx_DMG.DS_Store") | ||||
|   set (CPACK_BUNDLE_NAME "@WSJTX_BUNDLE_NAME@") | ||||
|   set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/wsjtx.icns") | ||||
|   set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/wsjtx.icns") | ||||
|   set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-wsjtx-startup.sh") | ||||
| endif () | ||||
| 
 | ||||
| if ("${CPACK_GENERATOR}" STREQUAL "WIX") | ||||
|   # Reset CPACK_PACKAGE_VERSION to deal with WiX restriction. | ||||
|   # But the file names still use the full CMake_VERSION value: | ||||
|   set (CPACK_PACKAGE_FILE_NAME | ||||
|     "${CPACK_PACKAGE_NAME}-@wsjtx_VERSION@-${CPACK_SYSTEM_NAME}") | ||||
|   set (CPACK_SOURCE_PACKAGE_FILE_NAME | ||||
|     "${CPACK_PACKAGE_NAME}-@wsjtx_VERSION@-Source") | ||||
| 
 | ||||
|   if (NOT CPACK_WIX_SIZEOF_VOID_P) | ||||
|     set (CPACK_WIX_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@") | ||||
|   endif () | ||||
| endif () | ||||
| 
 | ||||
| if ("${CPACK_GENERATOR}" STREQUAL "DEB") | ||||
|   set (CPACK_PACKAGE_FILE_NAME "@DEBIAN_PACKAGE_FILE_NAME@") | ||||
|   set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "@PROJECT_HOMEPAGE@") | ||||
| endif ("${CPACK_GENERATOR}" STREQUAL "DEB") | ||||
| 
 | ||||
| message (STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") | ||||
							
								
								
									
										852
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						| @ -1,9 +1,314 @@ | ||||
| cmake_minimum_required (VERSION 2.8.9) | ||||
| cmake_minimum_required (VERSION 2.8.9 FATAL_ERROR) | ||||
| 
 | ||||
| project (wsjtx C CXX Fortran) | ||||
| 
 | ||||
| set (WSJTX_VERSION_MAJOR 1) | ||||
| set (WSJTX_VERSION_MINOR 3) | ||||
| set (WSJTX_COPYRIGHT "Copyright (C) 2001-2014 by Joe Taylor, K1JT") | ||||
| 
 | ||||
| set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMake/Modules) | ||||
| 
 | ||||
| include (${PROJECT_SOURCE_DIR}/CMake/VersionCompute.cmake) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # Options & features | ||||
| # | ||||
| #	Some of these directly effect compilation by being defined in | ||||
| #	wsjtx_config.h.in which makes them available to the C/C++ | ||||
| #	pre-processor. | ||||
| # | ||||
| option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts | ||||
| files (WARNING: make clean will delete the source .ts files! Danger!)") | ||||
| option (WSJT_SHARED_RUNTIME "Debugging option that allows running from a shared Cloud directory.") | ||||
| option (WSJT_QDEBUG_IN_RELEASE "Leave qDebug statements in Release configuration.") | ||||
| option (WSJT_TRACE_CAT "Debugging option that turns on CAT diagnostics.") | ||||
| option (WSJT_TRACE_CAT_POLLS "Debugging option that turns on CAT diagnostics during polling.") | ||||
| option (WSJT_HAMLIB_TRACE "Debugging option that turns on full Hamlib internal diagnostics.") | ||||
| option (WSJT_STANDARD_FILE_LOCATIONS "All non-installation files located in \"Standard\" platfom specific locations." ON) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # install locations | ||||
| # | ||||
| set (WSJT_BIN_DESTINATION bin) | ||||
| set (WSJT_LIB_DESTINATION lib) | ||||
| set (WSJT_SHARE_DESTINATION share/wsjtx) | ||||
| set (WSJT_DOC_DESTINATION share/wsjtx/doc) | ||||
| set (WSJT_PLUGIN_DESTINATION lib/plugins) | ||||
| set (WSJT_QT_CONF_DESTINATION bin) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # project information | ||||
| # | ||||
| set (PROJECT_HOMEPAGE "http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html") | ||||
| set (PROJECT_MANUAL "http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjtx-main-toc2.html") | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # Project sources | ||||
| # | ||||
| set (wsjt_qt_CXXSRCS | ||||
|   NetworkServerLookup.cpp | ||||
|   Radio.cpp | ||||
|   Bands.cpp | ||||
|   FrequencyList.cpp | ||||
|   StationList.cpp | ||||
|   FrequencyLineEdit.cpp | ||||
|   FrequencyItemDelegate.cpp | ||||
|   ForeignKeyDelegate.cpp | ||||
|   LiveFrequencyValidator.cpp | ||||
|   GetUserId.cpp | ||||
|   TraceFile.cpp | ||||
|   AudioDevice.cpp | ||||
|   Transceiver.cpp | ||||
|   TransceiverBase.cpp | ||||
|   EmulateSplitTransceiver.cpp | ||||
|   TransceiverFactory.cpp | ||||
|   PollingTransceiver.cpp | ||||
|   HamlibTransceiver.cpp | ||||
|   HRDTransceiver.cpp | ||||
|   DXLabSuiteCommanderTransceiver.cpp | ||||
|   ) | ||||
| 
 | ||||
| set (ConfigTest_CXXSRCS | ||||
|   Configuration.cpp | ||||
|   TestConfiguration.cpp | ||||
|   ConfigTest.cpp | ||||
|   ) | ||||
| 
 | ||||
| set (jt9_CXXSRCS | ||||
|   lib/ipcomm.cpp | ||||
|   ) | ||||
| 
 | ||||
| set (wsjtx_CXXSRCS | ||||
|   logbook/adif.cpp | ||||
|   logbook/countrydat.cpp | ||||
|   logbook/countriesworked.cpp | ||||
|   logbook/logbook.cpp | ||||
|   psk_reporter.cpp | ||||
|   Modulator.cpp | ||||
|   Detector.cpp | ||||
|   logqso.cpp | ||||
|   displaytext.cpp | ||||
|   decodedtext.cpp | ||||
|   getfile.cpp | ||||
|   soundout.cpp | ||||
|   soundin.cpp | ||||
|   meterwidget.cpp | ||||
|   signalmeter.cpp | ||||
|   plotter.cpp | ||||
|   widegraph.cpp | ||||
|   about.cpp | ||||
|   astro.cpp | ||||
|   mainwindow.cpp | ||||
|   Configuration.cpp | ||||
|   main.cpp | ||||
|   ) | ||||
| 
 | ||||
| if (WIN32) | ||||
|   set (ConfigTest_CXXSRCS | ||||
|     ${ConfigTest_CXXSRCS} | ||||
|     OmniRigTransceiver.cpp | ||||
|     ) | ||||
| 
 | ||||
|   set (wsjt_CXXSRCS | ||||
|     ${wsjt_CXXSRCS} | ||||
|     killbyname.cpp | ||||
|     ) | ||||
| 
 | ||||
|   set (wsjt_qt_CXXSRCS | ||||
|     ${wsjt_qt_CXXSRCS} | ||||
|     OmniRigTransceiver.cpp | ||||
|     ) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| set (wsjt_FSRCS | ||||
|   lib/afc65b.f90 | ||||
|   lib/afc9.f90 | ||||
|   lib/analytic.f90 | ||||
|   lib/astro.f90 | ||||
|   lib/astrosub.f90 | ||||
|   lib/astro0.f90 | ||||
|   lib/azdist.f90 | ||||
|   lib/baddata.f90 | ||||
|   lib/ccf2.f90 | ||||
|   lib/ccf65.f90 | ||||
|   lib/chkhist.f90 | ||||
|   lib/chkss2.f90 | ||||
|   lib/coord.f90 | ||||
|   lib/db.f90 | ||||
|   lib/dcoord.f90 | ||||
|   lib/decode65a.f90 | ||||
|   lib/decode65b.f90 | ||||
|   lib/decode9.f90 | ||||
|   lib/decoder.f90 | ||||
|   lib/deg2grid.f90 | ||||
|   lib/demod64a.f90 | ||||
|   lib/determ.f90 | ||||
|   lib/dot.f90 | ||||
|   lib/downsam9.f90 | ||||
|   lib/encode232.f90 | ||||
|   lib/entail.f90 | ||||
|   lib/extract.F90 | ||||
|   lib/geocentric.f90 | ||||
|   lib/f77_wisdom.f90 | ||||
|   lib/fano232.f90 | ||||
|   lib/fchisq.f90 | ||||
|   lib/fchisq65.f90 | ||||
|   lib/fil3.f90 | ||||
|   lib/fil4.f90 | ||||
|   lib/fil6521.f90 | ||||
|   lib/filbig.f90 | ||||
|   lib/fillcom.f90 | ||||
|   lib/flat1.f90 | ||||
|   lib/flat2.f90 | ||||
|   lib/flat3.f90 | ||||
|   lib/flat65.f90 | ||||
|   lib/four2a.f90 | ||||
|   lib/gen65.f90 | ||||
|   lib/genjt9.f90 | ||||
|   lib/geodist.f90 | ||||
|   lib/getlags.f90 | ||||
|   lib/getpfx1.f90 | ||||
|   lib/getpfx2.f90 | ||||
|   lib/graycode.f90 | ||||
|   lib/graycode65.f90 | ||||
|   lib/grid2deg.f90 | ||||
|   lib/grid2k.f90 | ||||
|   lib/grid2n.f90 | ||||
|   lib/indexx.f90 | ||||
|   lib/interleave63.f90 | ||||
|   lib/interleave9.f90 | ||||
|   lib/jt65a.f90 | ||||
|   lib/k2grid.f90 | ||||
|   lib/moon2.f90 | ||||
|   lib/moondop.f90 | ||||
|   lib/morse.f90 | ||||
|   lib/move.f90 | ||||
|   lib/n2grid.f90 | ||||
|   lib/nchar.f90 | ||||
|   lib/options.f90 | ||||
|   lib/packbits.f90 | ||||
|   lib/packcall.f90 | ||||
|   lib/packgrid.f90 | ||||
|   lib/packmsg.f90 | ||||
|   lib/packtext.f90 | ||||
|   lib/pctile.f90 | ||||
|   lib/peakdt9.f90 | ||||
|   lib/pfxdump.f90 | ||||
|   lib/polfit.f90 | ||||
|   lib/prog_args.f90 | ||||
|   lib/sec_midn.f90 | ||||
|   lib/setup65.f90 | ||||
|   lib/sleep_msec.f90 | ||||
|   lib/smo.f90 | ||||
|   lib/smo121.f90 | ||||
|   lib/softsym.f90 | ||||
|   lib/sort.f90 | ||||
|   lib/ssort.f90 | ||||
|   lib/stdmsg.f90 | ||||
|   lib/sun.f90 | ||||
|   lib/symspec.f90 | ||||
|   lib/symspec2.f90 | ||||
|   lib/symspec65.f90 | ||||
|   lib/sync9.f90 | ||||
|   lib/timer.f90 | ||||
|   lib/tm2.f90 | ||||
|   lib/toxyz.f90 | ||||
|   lib/twkfreq.f90 | ||||
|   lib/twkfreq65.f90 | ||||
|   lib/unpackbits.f90 | ||||
|   lib/unpackcall.f90 | ||||
|   lib/unpackgrid.f90 | ||||
|   lib/unpackmsg.f90 | ||||
|   lib/unpacktext.f90 | ||||
|   lib/zplot9.f90 | ||||
|   ) | ||||
| 
 | ||||
| set (wsjt_CSRCS | ||||
|   lib/decode_rs.c | ||||
|   lib/encode_rs.c | ||||
|   lib/gran.c | ||||
|   lib/igray.c | ||||
|   lib/init_rs.c | ||||
|   lib/tmoonsub.c | ||||
|   lib/usleep.c | ||||
|   lib/wrapkarn.c | ||||
|   ) | ||||
| 
 | ||||
| set (ConfigTest_UISRCS | ||||
|   TestConfiguration.ui | ||||
|   Configuration.ui | ||||
|   ) | ||||
| 
 | ||||
| set (wsjtx_UISRCS | ||||
|   mainwindow.ui | ||||
|   about.ui | ||||
|   astro.ui | ||||
|   widegraph.ui | ||||
|   logqso.ui | ||||
|   Configuration.ui | ||||
|   ) | ||||
| 
 | ||||
| set (all_C_and_CXXSRCS | ||||
|   ${wsjt_CSRCS} | ||||
|   ${wsjt_CXXSRCS} | ||||
|   ${wsjt_qt_CXXSRCS} | ||||
|   ${jt9_CXXSRCS} | ||||
|   ${ConfigTest_CXXSRCS} | ||||
|   ${wsjtx_CXXSRCS} | ||||
|   ) | ||||
| 
 | ||||
| if (APPLE) | ||||
|   set (WSJTX_ICON_FILE ${CMAKE_PROJECT_NAME}.icns) | ||||
|   set (ICONSRCS | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_16x16.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_16x16@2x.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_32x32.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_32x32@2x.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_128x128.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_128x128@2x.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_256x256.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_256x256@2x.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_512x512.png | ||||
|     icons/Darwin/${CMAKE_PROJECT_NAME}.iconset/icon_512x512@2x.png | ||||
|     ) | ||||
|   add_custom_command ( | ||||
|     OUTPUT ${WSJTX_ICON_FILE} | ||||
|     COMMAND iconutil -c icns --output "${CMAKE_BINARY_DIR}/${WSJTX_ICON_FILE}" "${CMAKE_SOURCE_DIR}/icons/Darwin/${CMAKE_PROJECT_NAME}.iconset" | ||||
|     DEPENDS ${ICONSRCS} | ||||
|     COMMENT "Building Icons" | ||||
|     ) | ||||
| endif (APPLE) | ||||
| 
 | ||||
| set_source_files_properties (${WSJTX_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) | ||||
| 
 | ||||
| if (NOT WSJT_QDEBUG_IN_RELEASE) | ||||
|   set_directory_properties (PROPERTIES COMPILE_DEFINITIONS_RELEASE "QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT") | ||||
|   set_directory_properties (PROPERTIES COMPILE_DEFINITIONS_MINSIZEREL "QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT") | ||||
| endif (NOT WSJT_QDEBUG_IN_RELEASE) | ||||
| 
 | ||||
| set_property (SOURCE ${all_C_and_CXXSRCS} APPEND PROPERTY COMPILE_FLAGS "-include wsjtx_config.h") | ||||
| set_property (SOURCE ${all_C_and_CXXSRCS} APPEND PROPERTY OBJECT_DEPENDS wsjtx_config.h) | ||||
| 
 | ||||
| if (WIN32) | ||||
|   # generate the OmniRig COM interface source | ||||
|   find_program (DUMPCPP dumpcpp) | ||||
|   if (DUMPCPP-NOTFOUND) | ||||
|     message (FATAL_ERROR "dumpcpp tool not found") | ||||
|   endif (DUMPCPP-NOTFOUND) | ||||
|   execute_process ( | ||||
|     COMMAND ${DUMPCPP} -getfile {4FE359C5-A58F-459D-BE95-CA559FB4F270} | ||||
|     OUTPUT_VARIABLE AXSERVER | ||||
|     OUTPUT_STRIP_TRAILING_WHITESPACE | ||||
|     ) | ||||
|   string (STRIP "${AXSERVER}" AXSERVER) | ||||
|   if (NOT AXSERVER) | ||||
|     message (FATAL_ERROR "You need to install OmniRig on this computer") | ||||
|   endif (NOT AXSERVER) | ||||
|   string (REPLACE "\"" "" AXSERVER ${AXSERVER}) | ||||
|   file (TO_CMAKE_PATH ${AXSERVER} AXSERVERSRCS) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| if (POLICY CMP0020) | ||||
|   cmake_policy (SET CMP0020 NEW) # link to Qt winmain on Windows | ||||
| @ -18,79 +323,142 @@ endif (NOT CMAKE_BUILD_TYPE) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # C++ setup | ||||
| # decide on platform specifc packing and fixing up | ||||
| # | ||||
| if (APPLE) | ||||
|   set (WSJTX_BUNDLE_VERSION ${wsjtx_VERSION}) | ||||
| 
 | ||||
| set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") | ||||
|   # make sure CMAKE_INSTALL_PREFIX ends in / | ||||
|   string (LENGTH "${CMAKE_INSTALL_PREFIX}" LEN) | ||||
|   math (EXPR LEN "${LEN} -1" ) | ||||
|   string (SUBSTRING "${CMAKE_INSTALL_PREFIX}" ${LEN} 1 ENDCH) | ||||
|   if (NOT "${ENDCH}" STREQUAL "/") | ||||
|     set (CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/") | ||||
|   endif () | ||||
|   # install inside bundle | ||||
|   # set (CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}wsjtx.app") | ||||
| 
 | ||||
|   set (WSJT_PLUGIN_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/PlugIns) | ||||
|   set (WSJT_QT_CONF_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/Resources) | ||||
|   set (WSJT_SHARE_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/Resources) | ||||
|   set (WSJT_BIN_DESTINATION ${CMAKE_PROJECT_NAME}.app/Contents/MacOS) | ||||
| endif (APPLE) | ||||
| 
 | ||||
| #set (QT_NEED_RPATH FALSE) | ||||
| #if (NOT "${QT_LIBRARY_DIR}" STREQUAL "/lib" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/usr/lib" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/lib64" AND NOT "${QT_LIBRARY_DIR}" STREQUAL "/usr/lib64") | ||||
| #  set (QT_NEED_RPATH TRUE) | ||||
| #endif () | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # C & C++ setup | ||||
| # | ||||
| set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") | ||||
| set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++11 -fexceptions -frtti") | ||||
| if (WIN32) | ||||
|   set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-keep-inline-dllexport") | ||||
| endif (WIN32) | ||||
| if (APPLE) | ||||
|   set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") | ||||
| else (APPLE) | ||||
|   set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") | ||||
|   set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") | ||||
| endif (APPLE) | ||||
| 
 | ||||
| 
 | ||||
| set (CXXSRCS | ||||
|   logbook/adif.cpp | ||||
|   logbook/countrydat.cpp | ||||
|   logbook/countriesworked.cpp | ||||
|   logbook/logbook.cpp | ||||
|   rigclass.cpp | ||||
|   psk_reporter.cpp | ||||
|   Modulator.cpp | ||||
|   Detector.cpp | ||||
|   logqso.cpp | ||||
|   displaytext.cpp | ||||
|   decodedtext.cpp | ||||
|   getfile.cpp | ||||
|   soundout.cpp | ||||
|   soundin.cpp | ||||
|   meterwidget.cpp | ||||
|   signalmeter.cpp | ||||
|   plotter.cpp | ||||
|   widegraph.cpp | ||||
|   devsetup.cpp | ||||
|   about.cpp | ||||
|   astro.cpp | ||||
|   mainwindow.cpp | ||||
|   main.cpp | ||||
|   ) | ||||
| # | ||||
| # Fortran setup | ||||
| # | ||||
| set (General_FFLAGS "-fbounds-check -Wall -Wno-conversion -fno-second-underscore") | ||||
| 
 | ||||
| if (WIN32) | ||||
|   set (CXXSRCS ${CXXSRCS} killbyname.cpp) | ||||
| endif (WIN32) | ||||
| # FFLAGS depend on the compiler | ||||
| get_filename_component (Fortran_COMPILER_NAME ${CMAKE_Fortran_COMPILER} NAME) | ||||
| 
 | ||||
| set_property (SOURCE ${CXXSRCS} APPEND PROPERTY COMPILE_FLAGS "-include wsjtx_config.h") | ||||
| if (Fortran_COMPILER_NAME MATCHES "gfortran.*") | ||||
|   # gfortran | ||||
|   set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3 ${General_FFLAGS}") | ||||
|   set (CMAKE_Fortran_FLAGS_DEBUG   "-fno-f2c -O0 -g ${General_FFLAGS}") | ||||
| elseif (Fortran_COMPILER_NAME MATCHES "ifort.*") | ||||
|   # ifort (untested) | ||||
|   set (CMAKE_Fortran_FLAGS_RELEASE "-f77rtl -O3 ${General_FFLAGS}") | ||||
|   set (CMAKE_Fortran_FLAGS_DEBUG   "-f77rtl -O0 -g ${General_FFLAGS}") | ||||
| elseif (Fortran_COMPILER_NAME MATCHES "g77") | ||||
|   # g77 | ||||
|   set (CMAKE_Fortran_FLAGS_RELEASE "-funroll-all-loops -fno-f2c -O3 -m32 ${General_FFLAGS}") | ||||
|   set (CMAKE_Fortran_FLAGS_DEBUG   "-fno-f2c -O0 -g -m32 ${General_FFLAGS}") | ||||
| else (Fortran_COMPILER_NAME MATCHES "gfortran.*") | ||||
|   message ("CMAKE_Fortran_COMPILER full path: " ${CMAKE_Fortran_COMPILER}) | ||||
|   message ("Fortran compiler: " ${Fortran_COMPILER_NAME}) | ||||
|   message ("No optimized Fortran compiler flags are known, we just try -O2...") | ||||
|   set (CMAKE_Fortran_FLAGS_RELEASE "-O2 ${General_FFLAGS}") | ||||
|   set (CMAKE_Fortran_FLAGS_DEBUG   "-O0 -g ${General_FFLAGS}") | ||||
| endif (Fortran_COMPILER_NAME MATCHES "gfortran.*") | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # setup and test Fortran C/C++ interaction | ||||
| # | ||||
| 
 | ||||
| include (FortranCInterface) | ||||
| FortranCInterface_VERIFY (CXX QUIET) | ||||
| FortranCInterface_HEADER (FC.h MACRO_NAMESPACE "FC_" SYMBOL_NAMESPACE "FC_" | ||||
|   SYMBOLS ) | ||||
| 
 | ||||
| set (UISRCS | ||||
|   mainwindow.ui | ||||
|   about.ui | ||||
|   astro.ui | ||||
|   devsetup.ui | ||||
|   widegraph.ui | ||||
|   logqso.ui | ||||
| ) | ||||
| 
 | ||||
| # | ||||
| # sort out pre-requisites | ||||
| # | ||||
| 
 | ||||
| # | ||||
| # libfftw3 setup | ||||
| # Setup RPATH so that built executable targets will run in both the | ||||
| # build tree and the install location without having to set a | ||||
| # (DYLD|LD)_LIBRARY_PATH override. | ||||
| # | ||||
| find_path (fftw3f_INCLUDES fftw3.f) | ||||
| find_library (fftw3f_LIBRARIES NAMES fftw3f fftw3f-3) | ||||
| include_directories (${fftw3f_INCLUDES}) | ||||
| 
 | ||||
| # use the full RPATH of the build tree | ||||
| set (CMAKE_SKIP_BUILD_RPATH FALSE) | ||||
| 
 | ||||
| # when building, don't use the install RPATH, it will still be used | ||||
| # later on in the install phase | ||||
| set (CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) | ||||
| 
 | ||||
| set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}") | ||||
| 
 | ||||
| # add teh automaticaly determined parts of the RPATH which point to | ||||
| # directories outside of the build tree to the install RPATH | ||||
| set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) | ||||
| 
 | ||||
| # the RPATH to be used when installing, but only if it's not a system | ||||
| # directory | ||||
| list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}" isSystemDir) | ||||
| if ("${isSystemDir}" STREQUAL "-1") | ||||
|   set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${WSJT_LIB_DESTINATION}") | ||||
| endif ("${isSystemDir}" STREQUAL "-1") | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # fftw3 single precsion library | ||||
| # | ||||
| find_package (FFTW3 COMPONENTS single REQUIRED) | ||||
| if (NOT FFTW3_FOUND) | ||||
|   message (FATAL_ERROR "fftw3 single precsion library not found") | ||||
| endif (NOT FFTW3_FOUND) | ||||
| include_directories (${FFTW3_INCLUDE_DIRS}) | ||||
| 
 | ||||
| # | ||||
| # libhamlib setup | ||||
| # | ||||
| find_path (hamlib_INCLUDES hamlib/rig.h) | ||||
| find_library (hamlib_LIBRARIES NAMES hamlib hamlib-2 PATH_SUFFIXES msvc gcc) | ||||
| include_directories (${hamlib_INCLUDES}) | ||||
| find_package (hamlib REQUIRED) | ||||
| if (NOT hamlib_FOUND) | ||||
|   message (FATAL_ERROR "hamlib library not found") | ||||
| endif (NOT hamlib_FOUND) | ||||
| include_directories (${hamlib_INCLUDE_DIRS}) | ||||
| 
 | ||||
| if (WIN32) | ||||
|    find_library (hamlib_RUNTIME NAMES hamlib hamlib-2 PATH_SUFFIXES bin) | ||||
|    get_filename_component (_hamlib_runtime_path "${hamlib_RUNTIME}" PATH) | ||||
|    file (GLOB hamlib_BACKENDS ${_hamlib_runtime_path}/hamlib*.dll) | ||||
|    find_library (usb_RUNTIME NAMES usb0 PATH_SUFFIXES bin) | ||||
|   find_library (hamlib_RUNTIME NAMES hamlib hamlib-2 PATH_SUFFIXES bin) | ||||
|   get_filename_component (_hamlib_runtime_path "${hamlib_RUNTIME}" PATH) | ||||
|   file (GLOB hamlib_BACKENDS ${_hamlib_runtime_path}/hamlib*.dll) | ||||
|   find_library (usb_RUNTIME NAMES usb0 PATH_SUFFIXES bin) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| 
 | ||||
| @ -102,117 +470,373 @@ endif (WIN32) | ||||
| find_package (Qt5Widgets REQUIRED) | ||||
| find_package (Qt5Multimedia REQUIRED) | ||||
| 
 | ||||
| if (WIN32) | ||||
|   add_definitions (-DQT_NEEDS_QTMAIN) | ||||
|   find_package (Qt5AxContainer REQUIRED) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| # | ||||
| # stuff only qmake can tell us | ||||
| # | ||||
| get_target_property (QMAKE_EXECUTABLE Qt5::qmake LOCATION) | ||||
| function (QUERY_QMAKE VAR RESULT) | ||||
|   exec_program (${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output) | ||||
|   if (NOT return_code) | ||||
|     file (TO_CMAKE_PATH "${output}" output) | ||||
|     set (${RESULT} ${output} PARENT_SCOPE) | ||||
|   endif (NOT return_code) | ||||
| endfunction (QUERY_QMAKE) | ||||
| 
 | ||||
| query_qmake (QT_INSTALL_PLUGINS QT_PLUGINS_DIR) | ||||
| query_qmake (QT_INSTALL_IMPORTS QT_IMPORTS_DIR) | ||||
| query_qmake (QT_HOST_DATA QT_DATA_DIR) | ||||
| set (QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs) | ||||
| 
 | ||||
| # Tell CMake to run moc when necessary | ||||
| set (CMAKE_AUTOMOC ON) | ||||
| 
 | ||||
| # don't use Qt "keywords" signal, slot, emit in generated files to | ||||
| # avoid compatability issue with other libraries | ||||
| #ADD_DEFINITIONS (-DQT_NO_KEYWORDS) | ||||
| # ADD_DEFINITIONS (-DQT_NO_KEYWORDS) | ||||
| # ADD_DEFINITIONS (-DUNICODE)	#as per qmake | ||||
| 
 | ||||
| # As moc files are generated in the binary dir, tell CMake to always | ||||
| # look for includes there: | ||||
| set (CMAKE_INCLUDE_CURRENT_DIR ON) | ||||
| 
 | ||||
| # project definitions | ||||
| add_definitions (-DQT5) | ||||
| add_definitions (-DQT5 -DCMAKE_BUILD -DBIGSYM=1) | ||||
| if (CMAKE_HOST_UNIX) | ||||
|   add_definitions (-DUNIX) | ||||
| elseif (CMAKE_HOST_WIN32) | ||||
|   add_definitions (-DWIN32) | ||||
| endif () | ||||
| 
 | ||||
| # add_definitions (-DWSJT_SOFT_KEYING) | ||||
| set_property (DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_RELEASE QT_NO_DEBUG_OUTPUT QT_NO_WARNING_OUTPUT) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # build the subdirectories | ||||
| # | ||||
| add_subdirectory (lib) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # fetch kvasd | ||||
| # | ||||
| if (APPLE) | ||||
|   set (kvasd_NAME http://svn.berlios.de/wsvn/wsjt/trunk/KVASD_gfortran_Mac${CMAKE_EXECUTABLE_SUFFIX}) | ||||
| else () | ||||
|   set (kvasd_NAME http://www.physics.princeton.edu/pulsar/K1JT/kvasd${CMAKE_EXECUTABLE_SUFFIX}) | ||||
| endif () | ||||
| set (kvasd_NAME https://svn.code.sf.net/p/wsjt/wsjt/trunk/kvasd-binary/${CMAKE_SYSTEM_NAME}/kvasd${CMAKE_EXECUTABLE_SUFFIX}) | ||||
| file ( | ||||
|   DOWNLOAD ${kvasd_NAME} contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX} | ||||
|   TIMEOUT 10 | ||||
|   STATUS kvasd_STATUS | ||||
|   LOG kvasd_LOG | ||||
|   SHOW_PROGRESS | ||||
|   ) | ||||
| message (${kvasd_LOG}) | ||||
| list (GET kvasd_STATUS 0 kvasd_RC) | ||||
| if (!kvasd_RC) | ||||
|   message (FATAL_ERROR "${kvasd_STATUS}") | ||||
| endif (!kvasd_RC) | ||||
| if (kvasd_RC) | ||||
|   message (WARNING "${kvasd_STATUS}") | ||||
| endif (kvasd_RC) | ||||
| add_custom_target (kvasd DEPENDS contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX}) | ||||
| 
 | ||||
| 
 | ||||
| # UI generation | ||||
| qt5_wrap_ui (GENUISRCS ${UISRCS}) | ||||
| qt5_wrap_ui (GENUISRCS ${ConfigTest_UISRCS} ${wsjtx_UISRCS}) | ||||
| 
 | ||||
| add_executable (wsjtx ${CXXSRCS} ${GENUISRCS} wsjtx.rc) | ||||
| target_link_libraries (wsjtx jt9impl ${hamlib_LIBRARIES} ${fftw3f_LIBRARIES}) | ||||
| # Resource generation | ||||
| qt5_add_resources (wsjtx_RESOURCES_RCC wsjtx.qrc) | ||||
| 
 | ||||
| # AX COM servers | ||||
| if (WIN32) | ||||
|   target_link_libraries (wsjtx ${CMAKE_CURRENT_SOURCE_DIR}/libHRDInterface001.a) | ||||
|   set_target_properties (wsjtx PROPERTIES LINK_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} -mwindows") | ||||
|   include (QtAxMacros) | ||||
|   wrap_ax_server (GENAXSRCS ${AXSERVERSRCS}) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # targets | ||||
| # | ||||
| 
 | ||||
| # build a library of package functionality | ||||
| add_library (wsjt STATIC ${wsjt_CSRCS} ${wsjt_CXXSRCS} ${wsjt_FSRCS}) | ||||
| 
 | ||||
| # build a library of package Qt functionality | ||||
| add_library (wsjt_qt STATIC ${wsjt_qt_CXXSRCS} ${GENAXSRCS}) | ||||
| qt5_use_modules (wsjt_qt Widgets OpenGL Network) | ||||
| if (WIN32) | ||||
|   qt5_use_modules (wsjt_qt AxContainer AxBase) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| add_executable (jt9sim lib/jt9sim.f90 wsjtx.rc) | ||||
| target_link_libraries (jt9sim wsjt) | ||||
| 
 | ||||
| add_executable (jt9code lib/jt9code.f90 wsjtx.rc) | ||||
| target_link_libraries (jt9code wsjt) | ||||
| 
 | ||||
| add_executable (jt9 lib/jt9.f90 lib/jt9a.f90 lib/jt9b.f90 lib/jt9c.f90 ${jt9_CXXSRCS} wsjtx.rc) | ||||
| target_link_libraries (jt9 wsjt ${FFTW3F_LIBRARY}) | ||||
| qt5_use_modules (jt9 Core) | ||||
| 
 | ||||
| # build configuration dialog and transceiver interface test application | ||||
| #add_executable (ConfigTest ${ConfigTest_CXXSRCS} ${ConfigTest_UISRCS} wsjtx.rc) | ||||
| #target_link_libraries (ConfigTest wsjt wsjt_qt ${hamlib_LIBRARIES}) | ||||
| #qt5_use_modules (ConfigTest Widgets OpenGL Network Multimedia) | ||||
| 
 | ||||
| # build the main application | ||||
| add_executable (wsjtx WIN32 MACOSX_BUNDLE ${wsjtx_CXXSRCS} ${wsjtx_UISRCS} wsjtx.rc ${WSJTX_ICON_FILE} ${wsjtx_RESOURCES_RCC}) | ||||
| qt5_use_modules (wsjtx Widgets OpenGL Network Multimedia) | ||||
| 
 | ||||
| set_target_properties (wsjtx PROPERTIES | ||||
|   MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}" | ||||
|   MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}" | ||||
|   MACOSX_BUNDLE_BUNDLE_VERSION ${wsjtx_VERSION} | ||||
|   MACOSX_BUNDLE_SHORT_VERSION_STRING "v${wsjtx_VERSION}" | ||||
|   MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${wsjtx_VERSION}" | ||||
|   MACOSX_BUNDLE_BUNDLE_NAME "WSJT-X" | ||||
|   MACOSX_BUNDLE_COPYRIGHT "${WSJTX_COPYRIGHT}" | ||||
|   ) | ||||
| 
 | ||||
| target_link_libraries (wsjtx wsjt wsjt_qt ${hamlib_LIBRARIES} ${FFTW3F_LIBRARY}) | ||||
| add_dependencies (wsjtx kvasd) | ||||
| qt5_use_modules (wsjtx Widgets Multimedia OpenGL) | ||||
| 
 | ||||
| 
 | ||||
| install ( | ||||
|   TARGETS wsjtx | ||||
|   RUNTIME DESTINATION bin | ||||
|   LIBRARY DESTINATION lib | ||||
|   ) | ||||
| 
 | ||||
| install (DIRECTORY Palettes DESTINATION bin PATTERN *.pal) | ||||
| install (DIRECTORY samples DESTINATION bin/save) | ||||
| install (FILES | ||||
|   shortcuts.txt | ||||
|   mouse_commands.txt | ||||
|   prefixes.txt | ||||
|   cty.dat | ||||
|   kvasd.dat | ||||
|   DESTINATION bin | ||||
|   ) | ||||
| 
 | ||||
| install ( | ||||
|   PROGRAMS ${CMAKE_BINARY_DIR}/contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX} | ||||
|   DESTINATION bin | ||||
| ) | ||||
| 
 | ||||
| if (WIN32) | ||||
|   install ( | ||||
|     FILES ${hamlib_RUNTIME} ${hamlib_BACKENDS} ${fftw3f_LIBRARIES} ${usb_RUNTIME} contrib/HRDInterface001.dll | ||||
|     DESTINATION bin COMPONENT Runtime | ||||
|   set_target_properties ( | ||||
|     wsjtx | ||||
|     # ConfigTest | ||||
|     PROPERTIES LINK_FLAGS_RELEASE "${LINKER_FLAGS_RELEASE} -mwindows" | ||||
|     ) | ||||
| endif (WIN32) | ||||
| 
 | ||||
| 
 | ||||
| # a custom target that is always built | ||||
| ADD_CUSTOM_TARGET (revisiontag ALL) | ||||
| # | ||||
| # installation | ||||
| # | ||||
| install (TARGETS wsjtx | ||||
|   RUNTIME DESTINATION ${WSJT_BIN_DESTINATION} | ||||
|   LIBRARY DESTINATION ${WSJT_LIB_DESTINATION} | ||||
|   BUNDLE DESTINATION . | ||||
|   # COMPONENT Runtime | ||||
|   ) | ||||
| 
 | ||||
| # creates svnversion.h using cmake script | ||||
| ADD_CUSTOM_COMMAND (TARGET revisiontag COMMAND ${CMAKE_COMMAND} | ||||
|   -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}  | ||||
|   -P ${CMAKE_CURRENT_SOURCE_DIR}/getsvn.cmake) | ||||
| install (TARGETS jt9 | ||||
|   RUNTIME DESTINATION ${WSJT_BIN_DESTINATION} | ||||
|   LIBRARY DESTINATION ${WSJT_LIB_DESTINATION} | ||||
|   BUNDLE DESTINATION ${WSJT_BIN_DESTINATION} | ||||
|   #  COMPONENT Runtime | ||||
|   ) | ||||
| 
 | ||||
| install (PROGRAMS | ||||
|   ${CMAKE_BINARY_DIR}/contrib/kvasd${CMAKE_EXECUTABLE_SUFFIX} | ||||
|   DESTINATION ${WSJT_BIN_DESTINATION} | ||||
|   #  COMPONENT Runtime | ||||
|   ) | ||||
| 
 | ||||
| # explicitly say that the executable depends on custom target | ||||
| add_dependencies(wsjtx revisiontag) | ||||
| 
 | ||||
| # | ||||
| # versioning | ||||
| # uninstall support | ||||
| # | ||||
| configure_file ( | ||||
|   "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in" | ||||
|   "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" | ||||
|   @ONLY) | ||||
| add_custom_target (uninstall | ||||
|   "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") | ||||
| 
 | ||||
| 
 | ||||
| # creates svnversion.h using cmake script | ||||
| add_custom_target (revisiontag | ||||
|   COMMAND ${CMAKE_COMMAND} -D SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/getsvn.cmake | ||||
|   COMMENT "Generating Subversion revision information" | ||||
|   VERBATIM | ||||
|   ) | ||||
| # explicitly say that the executable depends on custom target, this is | ||||
| # done indirectly so that the revisiontag target gets built exactly | ||||
| # once per build | ||||
| add_dependencies(wsjt revisiontag) | ||||
| add_dependencies(jt9 revisiontag) | ||||
| add_dependencies(wsjtx revisiontag) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # versioning and configuration | ||||
| # | ||||
| configure_file ( | ||||
|   "${PROJECT_SOURCE_DIR}/wsjtx_config.h.in" | ||||
|   "${PROJECT_BINARY_DIR}/wsjtx_config.h" | ||||
|   ) | ||||
| 
 | ||||
| include_directories ("${PROJECT_BINARY_DIR}") | ||||
| 
 | ||||
| 
 | ||||
| if (NOT WIN32 AND NOT APPLE) | ||||
|   # install a desktop file so wsjtx appears in the application start | ||||
|   # menu with an icon | ||||
|   install (FILES wsjtx.desktop DESTINATION share/applications) | ||||
|   install (FILES icons/Unix/wsjtx_icon.png DESTINATION share/pixmaps) | ||||
| endif (NOT WIN32 AND NOT APPLE) | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # bundle fixup only done in Release or MinSizeRel configurations | ||||
| # | ||||
| if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") | ||||
| 
 | ||||
|   # get_target_property (QtCore_location Qt5::Core LOCATION) | ||||
|   # get_filename_component (QtCore_location ${QtCore_location} PATH) | ||||
|   # list (APPEND fixup_library_dirs ${QtCore_location}) | ||||
| 
 | ||||
|   if (APPLE) | ||||
|     set (CMAKE_POSTFLIGHT_SCRIPT | ||||
|       "${wsjtx_BINARY_DIR}/postflight.sh") | ||||
|     set (CMAKE_POSTUPGRADE_SCRIPT | ||||
|       "${wsjtx_BINARY_DIR}/postupgrade.sh") | ||||
|     configure_file ("${wsjtx_SOURCE_DIR}/postflight.sh.in" | ||||
|       "${wsjtx_BINARY_DIR}/postflight.sh") | ||||
|     configure_file ("${wsjtx_SOURCE_DIR}/postupgrade.sh.in" | ||||
|       "${wsjtx_BINARY_DIR}/postupgrade.sh") | ||||
|   endif () | ||||
| 
 | ||||
|   if (APPLE OR WIN32) | ||||
|     # install rules for including 3rd party libs such as Qt | ||||
| 
 | ||||
|     # install a qt.conf file | ||||
|     install (CODE " | ||||
|   file (WRITE \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\" | ||||
| \"[Paths] | ||||
| \") | ||||
| " #COMPONENT Runtime | ||||
|       ) | ||||
| 
 | ||||
|     # if a system Qt is used (e.g. installed in /usr/lib/), it will not be included in the installation | ||||
|     set (fixup_exe "\${CMAKE_INSTALL_PREFIX}/${WSJT_BIN_DESTINATION}/${CMAKE_PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}") | ||||
|     get_filename_component (hamlib_lib_dir ${hamlib_LIBRARIES} PATH) | ||||
| 
 | ||||
|     if (APPLE) | ||||
|       # install required Qt plugins | ||||
|       install ( | ||||
| 	DIRECTORY | ||||
| 	${QT_PLUGINS_DIR}/platforms | ||||
| 	${QT_PLUGINS_DIR}/audio | ||||
| 	DESTINATION ${WSJT_PLUGIN_DESTINATION} | ||||
| 	CONFIGURATIONS Release MinSizeRel | ||||
| 	#      COMPONENT Runtime | ||||
| 	FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}" | ||||
| 	PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	PATTERN "*_debug${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	) | ||||
|       # install ( | ||||
|       # 	DIRECTORY | ||||
|       # 	${QT_PLUGINS_DIR}/platforms | ||||
|       # 	${QT_PLUGINS_DIR}/audio | ||||
|       # 	DESTINATION ${WSJT_PLUGIN_DESTINATION} | ||||
|       # 	CONFIGURATIONS Debug | ||||
|       # 	#      COMPONENT Runtime | ||||
|       # 	FILES_MATCHING PATTERN "*_debug${CMAKE_SHARED_LIBRARY_SUFFIX}" | ||||
|       # 	PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
|       # 	PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
|       # 	) | ||||
| 
 | ||||
|       # add plugins path for Mac Bundle | ||||
|       install (CODE " | ||||
| file (APPEND \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\" | ||||
| \"Plugins = Plugins | ||||
| \") | ||||
| " #COMPONENT Runtime | ||||
| 	) | ||||
|     endif (APPLE) | ||||
| 
 | ||||
|     if (WIN32) | ||||
|       # DLL directory | ||||
|       set (hamlib_lib_dir ${hamlib_lib_dir}/../bin) | ||||
| 
 | ||||
|       get_filename_component (fftw_lib_dir ${FFTW3F_LIBRARY} PATH) | ||||
|       list (APPEND fixup_library_dirs ${fftw_lib_dir}) | ||||
| 
 | ||||
|       # install required Qt plugins | ||||
|       install ( | ||||
| 	DIRECTORY | ||||
| 	${QT_PLUGINS_DIR}/platforms | ||||
| 	DESTINATION ${WSJT_PLUGIN_DESTINATION} | ||||
| 	CONFIGURATIONS Release MinSizeRel | ||||
| 	#      COMPONENT Runtime | ||||
| 	FILES_MATCHING PATTERN "*${CMAKE_SHARED_LIBRARY_SUFFIX}" | ||||
| 	PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	PATTERN "*d${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
| 	) | ||||
|       # install ( | ||||
|       # 	DIRECTORY | ||||
|       # 	${QT_PLUGINS_DIR}/platforms | ||||
|       # 	DESTINATION ${WSJT_PLUGIN_DESTINATION} | ||||
|       # 	CONFIGURATIONS Debug | ||||
|       # 	#      COMPONENT Runtime | ||||
|       # 	FILES_MATCHING PATTERN "*d${CMAKE_SHARED_LIBRARY_SUFFIX}" | ||||
|       # 	PATTERN "*minimal*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
|       # 	PATTERN "*offscreen*${CMAKE_SHARED_LIBRARY_SUFFIX}" EXCLUDE | ||||
|       # 	) | ||||
| 
 | ||||
|       # add plugins path for WIN32 | ||||
|       file (RELATIVE_PATH _plugins_path "${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}" "${CMAKE_INSTALL_PREFIX}/${WSJT_PLUGIN_DESTINATION}") | ||||
|       install (CODE " | ||||
| file (APPEND \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_QT_CONF_DESTINATION}/qt.conf\" | ||||
| \"Plugins = ${_plugins_path} | ||||
| \") | ||||
| " #COMPONENT Runtime | ||||
| 	) | ||||
| 
 | ||||
|       # set (gp_tool "objdump")	# we want MinGW tool - not MSVC (See GetPrerequisites.cmake) | ||||
|     endif (WIN32) | ||||
| 
 | ||||
|     list (APPEND fixup_library_dirs ${hamlib_lib_dir}) | ||||
| 
 | ||||
|     install (CODE " | ||||
|      file (GLOB_RECURSE QTPLUGINS | ||||
|        \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${WSJT_PLUGIN_DESTINATION}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") | ||||
|      include (BundleUtilities) | ||||
|      set (BU_CHMOD_BUNDLE_ITEMS ON) | ||||
|      set (gp_tool ${gp_tool}) | ||||
|      message (STATUS \"fixup_exe: ${fixup_exe}\") | ||||
|      fixup_bundle (\"${fixup_exe}\" \"\${QTPLUGINS}\" \"${fixup_library_dirs}\") | ||||
|    " | ||||
|       #COMPONENT Runtime | ||||
|       ) | ||||
|   endif (APPLE OR WIN32) | ||||
| 
 | ||||
| endif (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") | ||||
| 
 | ||||
| 
 | ||||
| # | ||||
| # packaging | ||||
| # | ||||
| set (CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}") | ||||
| set (CPACK_PACKAGE_VERSION_MAJOR ${WSJTX_VERSION_MAJOR}) | ||||
| set (CPACK_PACKAGE_VERSION_MINOR ${WSJTX_VERSION_MINOR}) | ||||
| set (CPACK_PACKAGE_VERSION_PATCH ${WSJTX_VERSION_PATCH}) | ||||
| 
 | ||||
| if (WIN32) | ||||
|   set (CPACK_GENERATOR "NSIS") | ||||
| elseif (APPLE) | ||||
|   set (CPACK_GENERATOR "DragNDrop" "PackageMaker") | ||||
| else () | ||||
|   # | ||||
|   # Derive the correct filename for a Debian package because the DEB | ||||
|   # generator doesn't do this correctly at present. | ||||
|   # | ||||
|   string (TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) | ||||
|   find_program (DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems") | ||||
|   if (DPKG_PROGRAM) | ||||
|     execute_process ( | ||||
|       COMMAND ${DPKG_PROGRAM} --print-architecture | ||||
|       OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE | ||||
|       OUTPUT_STRIP_TRAILING_WHITESPACE | ||||
|       ) | ||||
|     set (DEBIAN_PACKAGE_FILE_NAME | ||||
|       "${CPACK_PACKAGE_NAME_LOWERCASE}_${wsjtx_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")    | ||||
| 
 | ||||
|   else (DPKG_PROGRAM) | ||||
|     set (DEBIAN_PACKAGE_FILE_NAME | ||||
|       "${CPACK_PACKAGE_NAME_LOWERCASE}_${wsjtx_VERSION}_${CMAKE_SYSTEM_NAME}") | ||||
|   endif (DPKG_PROGRAM) | ||||
| 
 | ||||
|   set (CPACK_DEBIAN_PACKAGE_PACKAGE_SHLIBDEPS ON) | ||||
| 
 | ||||
|   set (CPACK_GENERATOR "DEB" "RPM" "TGZ") | ||||
| endif () | ||||
| 
 | ||||
| configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in" | ||||
|   "${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake" @ONLY) | ||||
| set (CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CMakeCPackOptions.cmake") | ||||
| 
 | ||||
| include (CPack) | ||||
|  | ||||
							
								
								
									
										63
									
								
								ConfigTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,63 @@ | ||||
| #include <iostream> | ||||
| #include <exception> | ||||
| 
 | ||||
| #include <locale.h> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QSettings> | ||||
| #include <QDir> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include "GetUserId.hpp" | ||||
| #include "TraceFile.hpp" | ||||
| #include "TestConfiguration.hpp" | ||||
| #include "AudioDevice.hpp" | ||||
| #include "TransceiverFactory.hpp" | ||||
| #include "Configuration.hpp" | ||||
| 
 | ||||
| int main (int argc, char *argv[]) | ||||
| { | ||||
|   try | ||||
|     { | ||||
|       QApplication application {argc, argv}; | ||||
|       setlocale (LC_NUMERIC, "C"); // ensure number forms are in
 | ||||
| 				   // consistent format, do this after
 | ||||
| 				   // instantiating QApplication so
 | ||||
| 				   // that GUI has correct l18n
 | ||||
| 
 | ||||
|       // get a unique id from the user
 | ||||
|       auto id = get_user_id (); | ||||
| 
 | ||||
|       // open a user specific trace file
 | ||||
|       TraceFile trace_file {QDir {QApplication::applicationDirPath () + "/logs"}.absoluteFilePath (id + "_config_test.log")}; | ||||
| 
 | ||||
|       // announce to log file
 | ||||
|       qDebug () << "Configuration Test v" WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MAJOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MINOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_PATCH) ", " WSJTX_STRINGIZE (SVNVERSION) " - Program startup"; | ||||
| 
 | ||||
|       // open user specific settings
 | ||||
|       QSettings settings {QDir {QApplication::applicationDirPath () + "/settings"}.absoluteFilePath (id + "_config_test.ini"), QSettings::IniFormat}; | ||||
| 
 | ||||
|       // the test GUI
 | ||||
|       TestConfiguration main_window ("ConfigTest", &settings); | ||||
| 
 | ||||
|       // hook up close down mechanism
 | ||||
|       QObject::connect (&application, SIGNAL (lastWindowClosed ()), &application, SLOT (quit ())); | ||||
| 
 | ||||
|       // start event loop
 | ||||
|       auto status = application.exec(); | ||||
|       qDebug () << "Normal exit with status: " << status; | ||||
|       return status; | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       qDebug () << "Error exit: " << e.what () << '\n'; | ||||
|       std::cerr << "Error: " << e.what () << '\n'; | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       qDebug () << "Unknown error exit\n"; | ||||
|       std::cerr << "Unexpected error\n"; | ||||
|       throw; // hoping the runtime might tell us more about the exception
 | ||||
|     } | ||||
|   return -1; | ||||
| } | ||||
							
								
								
									
										2128
									
								
								Configuration.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										188
									
								
								Configuration.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,188 @@ | ||||
| #ifndef CONFIGURATION_HPP_ | ||||
| #define CONFIGURATION_HPP_ | ||||
| 
 | ||||
| #include <QObject> | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| #include "AudioDevice.hpp" | ||||
| #include "Transceiver.hpp" | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| class QSettings; | ||||
| class QWidget; | ||||
| class QAudioDeviceInfo; | ||||
| class QString; | ||||
| class QDir; | ||||
| class QFont; | ||||
| class Bands; | ||||
| class FrequencyList; | ||||
| class StationList; | ||||
| class QStringListModel; | ||||
| 
 | ||||
| //
 | ||||
| // Class Configuration
 | ||||
| //
 | ||||
| //  Encapsulates the control, access  and, persistence of user defined
 | ||||
| //  settings for the wsjtx GUI.  Setting values are accessed through a
 | ||||
| //  QDialog window containing concept orientated tab windows.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Provides management of  the CAT and PTT  rig interfaces, providing
 | ||||
| //  control access  via a minimal generic  set of Qt slots  and status
 | ||||
| //  updates via Qt signals.  Internally  the rig control capability is
 | ||||
| //  farmed out  to a  separate thread  since many  of the  rig control
 | ||||
| //  functions are blocking.
 | ||||
| //
 | ||||
| //  All user  settings required by  the wsjtx GUI are  exposed through
 | ||||
| //  query methods.
 | ||||
| //
 | ||||
| //  The QSettings instance  passed to the constructor is  used to read
 | ||||
| //  and write user settings.
 | ||||
| //
 | ||||
| //  Pointers to three QAbstractItemModel  objects are provided to give
 | ||||
| //  access to amateur band  information, user working frequencies and,
 | ||||
| //  user operating  band information.  These porovide  consistent data
 | ||||
| //  models that can  be used in GUI lists or  tables or simply queried
 | ||||
| //  for user defined bands, default operating frequencies and, station
 | ||||
| //  descriptions.
 | ||||
| //
 | ||||
| class Configuration final | ||||
|   : public QObject | ||||
| { | ||||
|   Q_OBJECT; | ||||
|   Q_ENUMS (DataMode); | ||||
| 
 | ||||
| public: | ||||
|   using MODE = Transceiver::MODE; | ||||
|   using TransceiverState = Transceiver::TransceiverState; | ||||
|   using Frequency = Radio::Frequency; | ||||
| 
 | ||||
|   enum DataMode {data_mode_none, data_mode_USB, data_mode_data}; | ||||
| 
 | ||||
|   explicit Configuration (QString const& instance_key, QSettings * settings, QWidget * parent = nullptr); | ||||
|   ~Configuration (); | ||||
| 
 | ||||
|   int exec (); | ||||
| 
 | ||||
|   QDir data_path () const; | ||||
| 
 | ||||
|   QAudioDeviceInfo const& audio_input_device () const; | ||||
|   AudioDevice::Channel audio_input_channel () const; | ||||
|   QAudioDeviceInfo const& audio_output_device () const; | ||||
|   AudioDevice::Channel audio_output_channel () const; | ||||
| 
 | ||||
|   // These query methods should be used after a call to exec() to
 | ||||
|   // determine if either the audio input or audio output stream
 | ||||
|   // parameters have changed. The respective streams should be
 | ||||
|   // re-opened if they return true.
 | ||||
|   bool restart_audio_input () const; | ||||
|   bool restart_audio_output () const; | ||||
| 
 | ||||
|   QString my_callsign () const; | ||||
|   QString my_grid () const; | ||||
|   QFont decoded_text_font () const; | ||||
|   qint32 id_interval () const; | ||||
|   bool id_after_73 () const; | ||||
|   bool spot_to_psk_reporter () const; | ||||
|   bool monitor_off_at_startup () const; | ||||
|   bool log_as_RTTY () const; | ||||
|   bool report_in_comments () const; | ||||
|   bool prompt_to_log () const; | ||||
|   bool insert_blank () const; | ||||
|   bool DXCC () const; | ||||
|   bool clear_DX () const; | ||||
|   bool miles () const; | ||||
|   bool quick_call () const; | ||||
|   bool disable_TX_on_73 () const; | ||||
|   bool watchdog () const; | ||||
|   bool TX_messages () const; | ||||
|   bool split_mode () const; | ||||
|   Bands * bands (); | ||||
|   FrequencyList * frequencies (); | ||||
|   StationList * stations (); | ||||
|   QStringListModel * macros (); | ||||
|   QDir save_directory () const; | ||||
|   QString rig_name () const; | ||||
|   unsigned jt9w_bw_mult () const; | ||||
|   float jt9w_min_dt () const; | ||||
|   float jt9w_max_dt () const; | ||||
| 
 | ||||
|   // This method queries if a CAT and PTT connection is operational,
 | ||||
|   //
 | ||||
|   // It also doubles as an initialisation method when the
 | ||||
|   // open_if_closed parameter is passed as true.
 | ||||
|   bool transceiver_online (bool open_if_closed = false); | ||||
| 
 | ||||
|   // Close down connection to rig.
 | ||||
|   void transceiver_offline (); | ||||
| 
 | ||||
|   // Set transceiver frequency in Hertz.
 | ||||
|   Q_SLOT void transceiver_frequency (Frequency); | ||||
| 
 | ||||
|   // Setting a non zero TX frequency means split operation
 | ||||
|   // rationalise_mode means ensure TX uses same mode as RX.
 | ||||
|   Q_SLOT void transceiver_tx_frequency (Frequency = 0u); | ||||
| 
 | ||||
|   // Set transceiver mode.
 | ||||
|   //
 | ||||
|   // Rationalise means ensure TX uses same mode as RX.
 | ||||
|   Q_SLOT void transceiver_mode (MODE); | ||||
| 
 | ||||
|   // Set/unset PTT.
 | ||||
|   //
 | ||||
|   // Note that this must be called even if VOX PTT is selected since
 | ||||
|   // the "Emulate Split" mode requires PTT information to coordinate
 | ||||
|   // frequency changes.
 | ||||
|   Q_SLOT void transceiver_ptt (bool = true); | ||||
| 
 | ||||
|   // Attempt to (re-)synchronise transceiver state.
 | ||||
|   //
 | ||||
|   // Force signal guarantees either a transceiver_update or a
 | ||||
|   // transceiver_failure signal.
 | ||||
|   //
 | ||||
|   // The enforce_mode_and_split parameter ensures that future
 | ||||
|   // transceiver updates have the correct mode and split setting
 | ||||
|   // i.e. the transceiver is ready for use.
 | ||||
|   Q_SLOT void sync_transceiver (bool force_signal = false, bool enforce_mode_and_split = false); | ||||
| 
 | ||||
| 
 | ||||
|   //
 | ||||
|   // This signal indicates that a font has been selected and accepted
 | ||||
|   // for the decoded text.
 | ||||
|   //
 | ||||
|   Q_SIGNAL void decoded_text_font_changed (QFont); | ||||
| 
 | ||||
| 
 | ||||
|   //
 | ||||
|   // These signals are emitted and reflect transceiver state changes
 | ||||
|   //
 | ||||
| 
 | ||||
|   // signals a change in one of the TransceiverState members
 | ||||
|   Q_SIGNAL void transceiver_update (Transceiver::TransceiverState) const; | ||||
| 
 | ||||
|   // Signals a failure of a control rig CAT or PTT connection.
 | ||||
|   //
 | ||||
|   // A failed rig CAT or PTT connection is fatal and the underlying
 | ||||
|   // connections are closed automatically. The connections can be
 | ||||
|   // re-established with a call to transceiver_online(true) assuming
 | ||||
|   // the fault condition has been rectified or is transient.
 | ||||
|   Q_SIGNAL void transceiver_failure (QString reason) const; | ||||
| 
 | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| Q_DECLARE_METATYPE (Configuration::DataMode); | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| ENUM_QDEBUG_OPS_DECL (Configuration, DataMode); | ||||
| #endif | ||||
| 
 | ||||
| ENUM_QDATASTREAM_OPS_DECL (Configuration, DataMode); | ||||
| 
 | ||||
| ENUM_CONVERSION_OPS_DECL (Configuration, DataMode); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										1812
									
								
								Configuration.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								Copyright.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1 @@ | ||||
| Copyright (C) 2001-2014 by Joe Taylor, K1JT | ||||
							
								
								
									
										377
									
								
								DXLabSuiteCommanderTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,377 @@ | ||||
| #include "DXLabSuiteCommanderTransceiver.hpp" | ||||
| 
 | ||||
| #include <QTcpSocket> | ||||
| #include <QRegularExpression> | ||||
| 
 | ||||
| #include "NetworkServerLookup.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   char const * const commander_transceiver_name {"DX Lab Suite Commander"}; | ||||
|   int socket_wait_time {5000}; | ||||
| 
 | ||||
|   QString map_mode (Transceiver::MODE mode) | ||||
|   { | ||||
|     switch (mode) | ||||
|       { | ||||
|       case Transceiver::AM: return "AM"; | ||||
|       case Transceiver::CW: return "CW"; | ||||
|       case Transceiver::CW_R: return "CW-R"; | ||||
|       case Transceiver::USB: return "USB"; | ||||
|       case Transceiver::LSB: return "LSB"; | ||||
|       case Transceiver::FSK: return "RTTY"; | ||||
|       case Transceiver::FSK_R: return "RTTY-R"; | ||||
|       case Transceiver::DIG_L: return "DATA-L"; | ||||
|       case Transceiver::DIG_U: return "DATA-U"; | ||||
|       case Transceiver::FM: | ||||
|       case Transceiver::DIG_FM: | ||||
| 	return "FM"; | ||||
|       default: break; | ||||
|       } | ||||
|     return "USB"; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id) | ||||
| { | ||||
|   (*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true}; | ||||
| } | ||||
| 
 | ||||
| DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval) | ||||
|   : PollingTransceiver {poll_interval} | ||||
|   , wrapped_ {std::move (wrapped)} | ||||
|   , use_for_ptt_ {use_for_ptt} | ||||
|   , server_ {address} | ||||
|   , commander_ {nullptr} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| DXLabSuiteCommanderTransceiver::~DXLabSuiteCommanderTransceiver () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_start () | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::start"; | ||||
| #endif | ||||
| 
 | ||||
|   wrapped_->start (); | ||||
| 
 | ||||
|   auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol); | ||||
| 
 | ||||
|   if (!commander_) | ||||
|     { | ||||
|       commander_ = new QTcpSocket {this}; // QObject takes ownership
 | ||||
|     } | ||||
| 
 | ||||
|   commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); | ||||
|   if (!commander_->waitForConnected (socket_wait_time)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::start failed to connect" << commander_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Failed to connect to DX Lab Suite Commander\n" + commander_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
|        | ||||
|   poll (); | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_stop () | ||||
| { | ||||
|   if (commander_) | ||||
|     { | ||||
|       commander_->close (); | ||||
|       delete commander_, commander_ = nullptr; | ||||
|     } | ||||
| 
 | ||||
|   wrapped_->stop (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::stop"; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_ptt (bool on) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::do_ptt:" << on << state (); | ||||
| #endif | ||||
| 
 | ||||
|   if (use_for_ptt_) | ||||
|     { | ||||
|       send_command (on  ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>"); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       wrapped_->ptt (on); | ||||
|     } | ||||
| 
 | ||||
|   update_PTT (on); | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::do_frequency:" << f << state (); | ||||
| #endif | ||||
| 
 | ||||
|   // number is localised
 | ||||
|   // avoid floating point translation errors by adding a small number (0.1Hz)
 | ||||
|   send_command ("<command:10>CmdSetFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (f / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ()); | ||||
|   update_rx_frequency (f); | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::do_tx_frequency:" << tx << state (); | ||||
| #endif | ||||
| 
 | ||||
|   if (tx) | ||||
|     { | ||||
|       send_command ("<command:8>CmdSplit<parameters:7><1:2>on"); | ||||
|       update_split (true); | ||||
| 
 | ||||
|       // number is localised
 | ||||
|       // avoid floating point translation errors by adding a small number (0.1Hz)
 | ||||
| 
 | ||||
|       // set TX frequency after going split because going split
 | ||||
|       // rationalises TX VFO mode and that can change the frequency on
 | ||||
|       // Yaesu rigs if CW is involved
 | ||||
|       send_command ("<command:12>CmdSetTxFreq<parameters:23><xcvrfreq:10>" + QString ("%L1").arg (tx / 1e3 + 1e-4, 10, 'f', 3).toLocal8Bit ()); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       send_command ("<command:8>CmdSplit<parameters:8><1:3>off"); | ||||
|     } | ||||
|   update_other_frequency (tx); | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::do_mode (MODE mode, bool /* rationalise */) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "DXLabSuiteCommanderTransceiver::do_mode:" << mode << state (); | ||||
| #endif | ||||
| 
 | ||||
|   auto mapped = map_mode (mode); | ||||
|   send_command ((QString ("<command:10>CmdSetMode<parameters:%1><1:%2>").arg (5 + mapped.size ()).arg (mapped.size ()) + mapped).toLocal8Bit ()); | ||||
| 
 | ||||
|   if (state ().split ()) | ||||
|     { | ||||
|       // this toggle ensures that the TX VFO mode is the same as the RX VFO
 | ||||
|       send_command ("<command:8>CmdSplit<parameters:8><1:3>off"); | ||||
|       send_command ("<command:8>CmdSplit<parameters:7><1:2>on"); | ||||
|     } | ||||
| 
 | ||||
|   // setting TX frequency rationalises the mode on Icoms so get current and set
 | ||||
|   poll (); | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::poll () | ||||
| { | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|   bool quiet {false}; | ||||
| #else | ||||
|   bool quiet {true}; | ||||
| #endif | ||||
| 
 | ||||
|   send_command ("<command:11>CmdSendFreq<parameters:0>", quiet); | ||||
|   auto reply = read_reply (quiet); | ||||
|   if (0 == reply.indexOf ("<CmdFreq:")) | ||||
|     { | ||||
|       // remove thousands separator and DP - relies of n.nnn kHz format so we can do uint conversion
 | ||||
|       reply = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", ""); | ||||
|       update_rx_frequency (reply.toUInt ()); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::poll: get frequency unexpected response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander didn't respond correctly polling frequency"}; | ||||
|     } | ||||
| 
 | ||||
|   send_command ("<command:13>CmdSendTXFreq<parameters:0>", quiet); | ||||
|   reply = read_reply (quiet); | ||||
|   if (0 == reply.indexOf ("<CmdTXFreq:")) | ||||
|     { | ||||
|       // remove thousands separator and DP - relies of n.nnn kHz format so we ca do uint conversion
 | ||||
|       auto text = reply.mid (reply.indexOf ('>') + 1).replace (",", "").replace (".", ""); | ||||
|       if ("000" != text) | ||||
| 	{ | ||||
| 	  update_other_frequency (text.toUInt ()); | ||||
| 	} | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::poll: get tx frequency unexpected response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander didn't respond correctly polling TX frequency"}; | ||||
|     } | ||||
| 
 | ||||
|   send_command ("<command:12>CmdSendSplit<parameters:0>", quiet); | ||||
|   reply = read_reply (quiet); | ||||
|   if (0 == reply.indexOf ("<CmdSplit:")) | ||||
|     { | ||||
|       auto split = reply.mid (reply.indexOf ('>') + 1); | ||||
|       if ("ON" == split) | ||||
| 	{ | ||||
| 	  update_split (true); | ||||
| 	} | ||||
|       else if ("OFF" == split) | ||||
| 	{ | ||||
| 	  update_split (false); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected split state" << split; | ||||
| #endif | ||||
| 
 | ||||
| 	  throw error {"DX Lab Suite Commander sent an unrecognised split state: " + split}; | ||||
| 	} | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::poll: get split mode unexpected response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander didn't respond correctly polling split status"}; | ||||
|     } | ||||
| 
 | ||||
|   send_command ("<command:11>CmdSendMode<parameters:0>", quiet); | ||||
|   reply = read_reply (quiet); | ||||
|   if (0 == reply.indexOf ("<CmdMode:")) | ||||
|     { | ||||
|       auto mode = reply.mid (reply.indexOf ('>') + 1); | ||||
|       MODE m {UNK}; | ||||
|       if ("AM" == mode) | ||||
| 	{ | ||||
| 	  m = AM; | ||||
| 	} | ||||
|       else if ("CW" == mode) | ||||
| 	{ | ||||
| 	  m = CW; | ||||
| 	} | ||||
|       else if ("CW-R" == mode) | ||||
| 	{ | ||||
| 	  m = CW_R; | ||||
| 	} | ||||
|       else if ("FM" == mode || "WBFM" == mode) | ||||
| 	{ | ||||
| 	  m = FM; | ||||
| 	} | ||||
|       else if ("LSB" == mode) | ||||
| 	{ | ||||
| 	  m = LSB; | ||||
| 	} | ||||
|       else if ("USB" == mode) | ||||
| 	{ | ||||
| 	  m = USB; | ||||
| 	} | ||||
|       else if ("RTTY" == mode) | ||||
| 	{ | ||||
| 	  m = FSK; | ||||
| 	} | ||||
|       else if ("RTTY-R" == mode) | ||||
| 	{ | ||||
| 	  m = FSK_R; | ||||
| 	} | ||||
|       else if ("PKT" == mode || "DATA-L" == mode || "Data-L" == mode) | ||||
| 	{ | ||||
| 	  m = DIG_L; | ||||
| 	} | ||||
|       else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode) | ||||
| 	{ | ||||
| 	  m = DIG_U; | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected mode name" << mode; | ||||
| #endif | ||||
| 
 | ||||
| 	  throw error {"DX Lab Suite Commander sent an unrecognised mode: " + mode}; | ||||
| 	} | ||||
|       update_mode (m); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::poll: unexpected response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander didn't respond correctly polling mode"}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void DXLabSuiteCommanderTransceiver::send_command (QByteArray const& cmd, bool no_debug) | ||||
| { | ||||
|   Q_ASSERT (commander_); | ||||
| 
 | ||||
|   if (!no_debug) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver:send_command(" << cmd << ')'; | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|   if (QTcpSocket::ConnectedState != commander_->state ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::send_command failed:" << commander_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander send command failed\n" + commander_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   commander_->write (cmd); | ||||
|   if (!commander_->waitForBytesWritten (socket_wait_time)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::send_command failed:" << commander_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander send command failed\n" + commander_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| QByteArray DXLabSuiteCommanderTransceiver::read_reply (bool no_debug) | ||||
| { | ||||
|   Q_ASSERT (commander_); | ||||
| 
 | ||||
|   if (QTcpSocket::ConnectedState != commander_->state ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::read_reply failed:" << commander_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander read reply failed\n" + commander_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   if (!commander_->waitForReadyRead (socket_wait_time)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver::read_reply failed:" << commander_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"DX Lab Suite Commander read reply failed\n" + commander_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   auto result = commander_->readAll (); | ||||
| 
 | ||||
|   if (!no_debug) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "DXLabSuiteCommanderTransceiver:read_reply() ->" << result; | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
							
								
								
									
										50
									
								
								DXLabSuiteCommanderTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,50 @@ | ||||
| #ifndef DX_LAB_SUITE_COMMANDER_TRANSCEIVER_HPP__ | ||||
| #define DX_LAB_SUITE_COMMANDER_TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QByteArray> | ||||
| 
 | ||||
| #include "TransceiverFactory.hpp" | ||||
| #include "PollingTransceiver.hpp" | ||||
| 
 | ||||
| class QTcpSocket; | ||||
| 
 | ||||
| //
 | ||||
| // DX Lab Suite Commander Interface
 | ||||
| //
 | ||||
| // Implemented as a Transceiver decorator  because we may want the PTT
 | ||||
| // services of another Transceiver  type such as the HamlibTransceiver
 | ||||
| // which can  be enabled by wrapping  a HamlibTransceiver instantiated
 | ||||
| // as a "Hamlib Dummy" transceiver in the Transceiver factory method.
 | ||||
| //
 | ||||
| class DXLabSuiteCommanderTransceiver final | ||||
|   : public PollingTransceiver | ||||
| { | ||||
| public: | ||||
|   static void register_transceivers (TransceiverFactory::Transceivers *, int id); | ||||
| 
 | ||||
|   // takes ownership of wrapped Transceiver
 | ||||
|   explicit DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& address, bool use_for_ptt, int poll_interval); | ||||
|   ~DXLabSuiteCommanderTransceiver (); | ||||
| 
 | ||||
| private: | ||||
|   void do_start () override; | ||||
|   void do_stop () override; | ||||
|   void do_frequency (Frequency) override; | ||||
|   void do_tx_frequency (Frequency, bool rationalise_mode) override; | ||||
|   void do_mode (MODE, bool rationalise) override; | ||||
|   void do_ptt (bool on) override; | ||||
| 
 | ||||
|   void poll () override; | ||||
| 
 | ||||
|   void send_command (QByteArray const&, bool no_debug = false); | ||||
|   QByteArray read_reply (bool no_debug = false); | ||||
| 
 | ||||
|   std::unique_ptr<TransceiverBase> wrapped_; | ||||
|   bool use_for_ptt_; | ||||
|   QString server_; | ||||
|   QTcpSocket * commander_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -4,6 +4,8 @@ | ||||
| #include <QDebug> | ||||
| #include "commons.h" | ||||
| 
 | ||||
| #include "moc_Detector.cpp" | ||||
| 
 | ||||
| extern "C" { | ||||
| void   fil4_(qint16*, qint32*, qint16*, qint32*); | ||||
| } | ||||
|  | ||||
| @ -45,7 +45,7 @@ private: | ||||
|   Q_SLOT bool reset (); | ||||
|   Q_SLOT void close () {AudioDevice::close ();} | ||||
| 
 | ||||
|   Q_SIGNAL void framesWritten (qint64); | ||||
|   Q_SIGNAL void framesWritten (qint64) const; | ||||
| 
 | ||||
|   void clear ();		// discard buffer contents
 | ||||
|   unsigned secondInPeriod () const; | ||||
|  | ||||
							
								
								
									
										102
									
								
								EmulateSplitTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,102 @@ | ||||
| #include "EmulateSplitTransceiver.hpp" | ||||
| 
 | ||||
| EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped) | ||||
|   : wrapped_ {std::move (wrapped)} | ||||
|   , frequency_ {0, 0} | ||||
|   , tx_ {false} | ||||
| { | ||||
|   // Connect update signal of wrapped Transceiver object instance to ours.
 | ||||
|   connect (wrapped_.get (), &Transceiver::update, this, &EmulateSplitTransceiver::handle_update); | ||||
| 
 | ||||
|   // Connect failure signal of wrapped Transceiver object to our
 | ||||
|   // parent failure signal.
 | ||||
|   connect (wrapped_.get (), &Transceiver::failure, this, &Transceiver::failure); | ||||
| } | ||||
| 
 | ||||
| void EmulateSplitTransceiver::start () noexcept | ||||
| { | ||||
|   wrapped_->start (); | ||||
|   wrapped_->tx_frequency (0, false); | ||||
| } | ||||
| 
 | ||||
| void EmulateSplitTransceiver::frequency (Frequency rx) noexcept | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "EmulateSplitTransceiver::frequency:" << rx; | ||||
| #endif | ||||
| 
 | ||||
|   // Save frequency parameters.
 | ||||
|   frequency_[0] = rx; | ||||
| 
 | ||||
|   // Set active frequency.
 | ||||
|   wrapped_->frequency (rx); | ||||
| } | ||||
| 
 | ||||
| void EmulateSplitTransceiver::tx_frequency (Frequency tx, bool /* rationalise_mode */) noexcept | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "EmulateSplitTransceiver::tx_frequency:" << tx; | ||||
| #endif | ||||
| 
 | ||||
|   // Save frequency parameter.
 | ||||
|   frequency_[1] = tx; | ||||
| 
 | ||||
|   // Set active frequency.
 | ||||
|   wrapped_->frequency (frequency_[(tx_ && frequency_[1]) ? 1 : 0]); | ||||
| } | ||||
| 
 | ||||
| void EmulateSplitTransceiver::ptt (bool on) noexcept | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "EmulateSplitTransceiver::ptt:" << on; | ||||
| #endif | ||||
| 
 | ||||
|   // Save TX state for future frequency change requests.
 | ||||
|   tx_ = on; | ||||
| 
 | ||||
|   // Switch to other frequency if we have one i.e. client wants split
 | ||||
|   // operation).
 | ||||
|   wrapped_->frequency (frequency_[(on && frequency_[1]) ? 1 : 0]); | ||||
| 
 | ||||
|   // Change TX state.
 | ||||
|   wrapped_->ptt (on); | ||||
| } | ||||
| 
 | ||||
| void EmulateSplitTransceiver::handle_update (TransceiverState state) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "EmulateSplitTransceiver::handle_update: from wrapped:" << state; | ||||
| #endif | ||||
| 
 | ||||
|   // Change to reflect emulated state, we don't want to report the
 | ||||
|   // shifted frequency when transmitting.
 | ||||
|   if (tx_) | ||||
|     { | ||||
|       state.frequency (frequency_[0]); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       // Follow the rig if in RX mode.
 | ||||
|       frequency_[0] = state.frequency (); | ||||
|     } | ||||
| 
 | ||||
|   // Always report the other frequency as the Tx frequency we will use.
 | ||||
|   state.tx_frequency (frequency_[1]); | ||||
| 
 | ||||
|   if (state.split ()) | ||||
|     { | ||||
|       Q_EMIT failure (tr ("Emulated split mode requires rig to in simplex mode")); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       // Always emit rigs split state so clients can detect abuse.
 | ||||
|       state.split (true); | ||||
|    | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "EmulateSplitTransceiver::handle_update: signalling:" << state; | ||||
| #endif | ||||
| 
 | ||||
|       // signal emulated state
 | ||||
|       Q_EMIT update (state); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										52
									
								
								EmulateSplitTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,52 @@ | ||||
| #ifndef EMULATE_SPLIT_TRANSCEIVER_HPP__ | ||||
| #define EMULATE_SPLIT_TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "Transceiver.hpp" | ||||
| 
 | ||||
| //
 | ||||
| // Emulate Split Transceiver
 | ||||
| //
 | ||||
| // Helper decorator class that encapsulates  the emulation of split TX
 | ||||
| // operation.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Delegates  all  but setting  of  other  (split) frequency  to  the
 | ||||
| //  wrapped Transceiver instance. Also routes failure signals from the
 | ||||
| //  wrapped Transceiver instance to this instances failure signal.
 | ||||
| //
 | ||||
| //  Intercepts status  updates from  the wrapped  Transceiver instance
 | ||||
| //  and re-signals it with the emulated status.
 | ||||
| //
 | ||||
| //  Generates a status update signal if the other (split) frequency is
 | ||||
| //  changed, this is necessary  since the wrapped transceiver instance
 | ||||
| //  never receives other frequency changes.
 | ||||
| //
 | ||||
| class EmulateSplitTransceiver final | ||||
|   : public Transceiver | ||||
| { | ||||
| public: | ||||
|   // takes ownership of wrapped Transceiver
 | ||||
|   explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped); | ||||
| 
 | ||||
|   void start () noexcept override; | ||||
|   void frequency (Frequency) noexcept override; | ||||
|   void tx_frequency (Frequency, bool rationalise_mode) noexcept override; | ||||
|   void ptt (bool on) noexcept override; | ||||
| 
 | ||||
|   // forward everything else to wrapped Transceiver
 | ||||
|   void stop () noexcept override {wrapped_->stop (); Q_EMIT finished ();} | ||||
|   void mode (MODE m, bool /* rationalise */) noexcept override {wrapped_->mode (m, false);} | ||||
|   void sync (bool force_signal) noexcept override {wrapped_->sync (force_signal);} | ||||
| 
 | ||||
| private: | ||||
|   void handle_update (TransceiverState); | ||||
| 
 | ||||
|   std::unique_ptr<Transceiver> wrapped_; | ||||
|   Frequency frequency_[2];  // [0] <- RX, [1] <- other
 | ||||
|   bool tx_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										77
									
								
								ForeignKeyDelegate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,77 @@ | ||||
| #include "ForeignKeyDelegate.hpp" | ||||
| 
 | ||||
| #include <QComboBox> | ||||
| #include <QSortFilterProxyModel> | ||||
| 
 | ||||
| class CandidateKeyFilter final | ||||
|   : public QSortFilterProxyModel | ||||
| { | ||||
| public: | ||||
|   explicit CandidateKeyFilter (QAbstractItemModel const * referencing_model | ||||
|                                , QAbstractItemModel * referenced_model | ||||
|                                , int referenced_key_column | ||||
|                                , int referencing_key_role | ||||
|                                , int referenced_key_role) | ||||
|     : QSortFilterProxyModel {nullptr} // ForeignKeyDelegate owns us
 | ||||
|     , referencing_ {referencing_model} | ||||
|     , referencing_key_role_ {referencing_key_role} | ||||
|     , referenced_key_column_ {referenced_key_column} | ||||
|     , referenced_key_role_ {referenced_key_role} | ||||
|   { | ||||
|     setSourceModel (referenced_model); | ||||
|   } | ||||
| 
 | ||||
|   void set_active_key (QModelIndex const& index) | ||||
|   { | ||||
|     active_key_ = index; | ||||
|     invalidateFilter (); | ||||
|   } | ||||
| 
 | ||||
| protected: | ||||
|   bool filterAcceptsRow (int candidate_row, QModelIndex const& candidate_parent) const override | ||||
|   { | ||||
|     auto candidate_key = sourceModel ()->index (candidate_row, referenced_key_column_, candidate_parent).data (referenced_key_role_); | ||||
| 
 | ||||
|     // Include the current key.
 | ||||
|     if (candidate_key == active_key_.data (referencing_key_role_)) | ||||
|       { | ||||
|         return true; | ||||
|       } | ||||
| 
 | ||||
|     // Filter out any candidates already in the referencing key rows.
 | ||||
|     return referencing_->match (referencing_->index (0, active_key_.column ()), referencing_key_role_, candidate_key, 1, Qt::MatchExactly).isEmpty (); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   QAbstractItemModel const * referencing_; | ||||
|   int referencing_key_role_; | ||||
|   int referenced_key_column_; | ||||
|   int referenced_key_role_; | ||||
|   QModelIndex active_key_; | ||||
| }; | ||||
| 
 | ||||
| ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referencing_model | ||||
|                                         , QAbstractItemModel * referenced_model | ||||
|                                         , int referenced_key_column | ||||
|                                         , QObject * parent | ||||
|                                         , int referencing_key_role | ||||
|                                         , int referenced_key_role) | ||||
|   : QStyledItemDelegate {parent} | ||||
|   , candidate_key_filter_ {new CandidateKeyFilter {referencing_model, referenced_model, referenced_key_column, referencing_key_role, referenced_key_role}} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| ForeignKeyDelegate::~ForeignKeyDelegate () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| QWidget * ForeignKeyDelegate::createEditor (QWidget * parent | ||||
|                                             , QStyleOptionViewItem const& /* option */ | ||||
|                                             , QModelIndex const& index) const | ||||
| { | ||||
|   auto editor = new QComboBox {parent}; | ||||
|   editor->setFrame (false); | ||||
|   candidate_key_filter_->set_active_key (index); | ||||
|   editor->setModel (candidate_key_filter_.data ()); | ||||
|   return editor; | ||||
| } | ||||
							
								
								
									
										34
									
								
								ForeignKeyDelegate.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,34 @@ | ||||
| #ifndef FOREIGN_KEY_DELEGATE_HPP_ | ||||
| #define FOREIGN_KEY_DELEGATE_HPP_ | ||||
| 
 | ||||
| #include <QStyledItemDelegate> | ||||
| #include <QScopedPointer> | ||||
| 
 | ||||
| class CandidateKeyFilter; | ||||
| 
 | ||||
| //
 | ||||
| // Class ForeignKeyDelegate
 | ||||
| //
 | ||||
| //	Item delegate for editing a foreign key item in a one or many
 | ||||
| //	to one relationship. A QComboBox is used as an item delegate
 | ||||
| //	for the edit role.
 | ||||
| //
 | ||||
| class ForeignKeyDelegate final | ||||
|   : public QStyledItemDelegate | ||||
| { | ||||
| public: | ||||
|   explicit ForeignKeyDelegate (QAbstractItemModel const * referencing_model | ||||
|                                , QAbstractItemModel * referenced_model | ||||
|                                , int referenced_key_column = 0 | ||||
|                                , QObject * parent = nullptr | ||||
|                                , int referencing_key_role = Qt::EditRole | ||||
|                                , int referenced_key_role = Qt::EditRole); | ||||
|   ~ForeignKeyDelegate (); | ||||
| 
 | ||||
|   QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; | ||||
| 
 | ||||
| private: | ||||
|   QScopedPointer<CandidateKeyFilter> candidate_key_filter_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										36
									
								
								FrequencyItemDelegate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,36 @@ | ||||
| #include "FrequencyItemDelegate.hpp" | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| #include "FrequencyLineEdit.hpp" | ||||
| #include "Bands.hpp" | ||||
| 
 | ||||
| QString FrequencyItemDelegate::displayText (QVariant const& value, QLocale const& locale) const | ||||
| { | ||||
|   auto frequency = value.value<Radio::Frequency> (); | ||||
|   auto band_name = bands_->data (bands_->find (frequency)); | ||||
|   return Radio::pretty_frequency_MHz_string (frequency, locale) + " MHz (" + band_name.toString () + ')'; | ||||
| } | ||||
| 
 | ||||
| QWidget * FrequencyItemDelegate::createEditor (QWidget * parent | ||||
| 					       , QStyleOptionViewItem const& /* option */ | ||||
| 					       , QModelIndex const& /* index */) const | ||||
| { | ||||
|   auto editor = new FrequencyLineEdit {parent}; | ||||
|   editor->setFrame (false); | ||||
|   return editor; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| QString FrequencyDeltaItemDelegate::displayText (QVariant const& value, QLocale const& locale) const | ||||
| { | ||||
|   return Radio::pretty_frequency_MHz_string (value.value<Radio::FrequencyDelta> (), locale) + " MHz"; | ||||
| } | ||||
| 
 | ||||
| QWidget * FrequencyDeltaItemDelegate::createEditor (QWidget * parent | ||||
| 					       , QStyleOptionViewItem const& /* option */ | ||||
| 					       , QModelIndex const& /* index */) const | ||||
| { | ||||
|   auto editor = new FrequencyDeltaLineEdit {parent}; | ||||
|   editor->setFrame (false); | ||||
|   return editor; | ||||
| } | ||||
							
								
								
									
										56
									
								
								FrequencyItemDelegate.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,56 @@ | ||||
| #ifndef FREQUENCY_ITEM_DELEGATE_HPP_ | ||||
| #define FREQUENCY_ITEM_DELEGATE_HPP_ | ||||
| 
 | ||||
| #include <QStyledItemDelegate> | ||||
| 
 | ||||
| class QStyleOptionItemView; | ||||
| class QWidget; | ||||
| class QModelIndex; | ||||
| class Bands; | ||||
| 
 | ||||
| //
 | ||||
| // Class FrequencyItemDelegate
 | ||||
| //
 | ||||
| //	Item delegate for displaying and editing a Frequency item in a
 | ||||
| //	view that uses a FrequencyLineEdit as an item delegate for the
 | ||||
| //	edit role.
 | ||||
| //
 | ||||
| class FrequencyItemDelegate final | ||||
|   : public QStyledItemDelegate | ||||
| { | ||||
| public: | ||||
|   explicit FrequencyItemDelegate (Bands const * bands, QObject * parent = nullptr) | ||||
|     : QStyledItemDelegate {parent} | ||||
|     , bands_ {bands} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   QString displayText (QVariant const& value, QLocale const&) const override; | ||||
|   QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; | ||||
| 
 | ||||
| private: | ||||
|   Bands const * bands_; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| //
 | ||||
| // Class FrequencyDeltaItemDelegate
 | ||||
| //
 | ||||
| //	Item delegate for displaying and editing a FrequencyDelta item
 | ||||
| //	in a view that uses a FrequencyDeltaLineEdit as an item
 | ||||
| //	delegate for the edit role.
 | ||||
| //
 | ||||
| class FrequencyDeltaItemDelegate final | ||||
|   : public QStyledItemDelegate | ||||
| { | ||||
| public: | ||||
|   explicit FrequencyDeltaItemDelegate (QObject * parent = nullptr) | ||||
|     : QStyledItemDelegate {parent} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   QString displayText (QVariant const& value, QLocale const&) const override; | ||||
|   QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										40
									
								
								FrequencyLineEdit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,40 @@ | ||||
| #include "FrequencyLineEdit.hpp" | ||||
| 
 | ||||
| #include <QRegExpValidator> | ||||
| #include <QRegExp> | ||||
| #include <QString> | ||||
| 
 | ||||
| #include "moc_FrequencyLineEdit.cpp" | ||||
| 
 | ||||
| FrequencyLineEdit::FrequencyLineEdit (QWidget * parent) | ||||
|   : QLineEdit (parent) | ||||
| { | ||||
|   setValidator (new QRegExpValidator {QRegExp {R"(\d{0,6}(\.\d{0,6})?)"}, this}); | ||||
| } | ||||
| 
 | ||||
| auto FrequencyLineEdit::frequency () const -> Frequency | ||||
| { | ||||
|   return Radio::frequency (text (), 6); | ||||
| } | ||||
| 
 | ||||
| void FrequencyLineEdit::frequency (Frequency f) | ||||
| { | ||||
|   setText (Radio::frequency_MHz_string (f)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| FrequencyDeltaLineEdit::FrequencyDeltaLineEdit (QWidget * parent) | ||||
|   : QLineEdit (parent) | ||||
| { | ||||
|   setValidator (new QRegExpValidator {QRegExp {R"(-?\d{0,6}(\.\d{0,6})?)"}, this}); | ||||
| } | ||||
| 
 | ||||
| auto FrequencyDeltaLineEdit::frequency_delta () const -> FrequencyDelta | ||||
| { | ||||
|   return Radio::frequency_delta (text (), 6); | ||||
| } | ||||
| 
 | ||||
| void FrequencyDeltaLineEdit::frequency_delta (FrequencyDelta d) | ||||
| { | ||||
|   setText (Radio::frequency_MHz_string (d)); | ||||
| } | ||||
							
								
								
									
										45
									
								
								FrequencyLineEdit.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,45 @@ | ||||
| #ifndef FREQUENCY_LINE_EDIT_HPP_ | ||||
| #define FREQUENCY_LINE_EDIT_HPP_ | ||||
| 
 | ||||
| #include <QLineEdit> | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| class QWidget; | ||||
| 
 | ||||
| //
 | ||||
| // MHz frequency line edits with validation
 | ||||
| //
 | ||||
| class FrequencyLineEdit final | ||||
|   : public QLineEdit | ||||
| { | ||||
|   Q_OBJECT; | ||||
|   Q_PROPERTY (Frequency frequency READ frequency WRITE frequency USER true); | ||||
| 
 | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
| 
 | ||||
|   explicit FrequencyLineEdit (QWidget * parent = nullptr); | ||||
| 
 | ||||
|   // Property frequency implementation
 | ||||
|   Frequency frequency () const; | ||||
|   void frequency (Frequency); | ||||
| }; | ||||
| 
 | ||||
| class FrequencyDeltaLineEdit final | ||||
|   : public QLineEdit | ||||
| { | ||||
|   Q_OBJECT; | ||||
|   Q_PROPERTY (FrequencyDelta frequency_delta READ frequency_delta WRITE frequency_delta USER true); | ||||
| 
 | ||||
| public: | ||||
|   using FrequencyDelta = Radio::FrequencyDelta; | ||||
| 
 | ||||
|   explicit FrequencyDeltaLineEdit (QWidget * parent = nullptr); | ||||
| 
 | ||||
|   // Property frequency_delta implementation
 | ||||
|   FrequencyDelta frequency_delta () const; | ||||
|   void frequency_delta (FrequencyDelta); | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										358
									
								
								FrequencyList.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,358 @@ | ||||
| #include "FrequencyList.hpp" | ||||
| 
 | ||||
| #include <utility> | ||||
| 
 | ||||
| #include <QAbstractTableModel> | ||||
| #include <QString> | ||||
| #include <QList> | ||||
| #include <QListIterator> | ||||
| #include <QVector> | ||||
| #include <QStringList> | ||||
| #include <QMimeData> | ||||
| #include <QDataStream> | ||||
| #include <QByteArray> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| class FrequencyList::impl final | ||||
|   : public QAbstractTableModel | ||||
| { | ||||
| public: | ||||
|   impl (Frequencies frequencies, QObject * parent) | ||||
|     : QAbstractTableModel {parent} | ||||
|     , frequencies_ {frequencies} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   Frequencies const& frequencies () const {return frequencies_;} | ||||
|   void assign (Frequencies); | ||||
|   QModelIndex add (Frequency); | ||||
| 
 | ||||
| protected: | ||||
|   // Implement the QAbstractTableModel interface
 | ||||
|   int rowCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   int columnCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override; | ||||
|   QVariant data (QModelIndex const&, int role = Qt::DisplayRole) const override; | ||||
|   bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override; | ||||
|   QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; | ||||
|   bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override; | ||||
|   bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override; | ||||
|   QStringList mimeTypes () const override; | ||||
|   QMimeData * mimeData (QModelIndexList const&) const override; | ||||
| 
 | ||||
| private: | ||||
|   static int constexpr num_cols {2}; | ||||
|   static auto constexpr mime_type ="application/wsjt.Frequencies"; | ||||
| 
 | ||||
|   Frequencies frequencies_; | ||||
| }; | ||||
| 
 | ||||
| FrequencyList::FrequencyList (QObject * parent) | ||||
|   : FrequencyList {{}, parent} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| FrequencyList::FrequencyList (Frequencies frequencies, QObject * parent) | ||||
|   : QSortFilterProxyModel {parent} | ||||
|   , m_ {frequencies, parent} | ||||
| { | ||||
|   // setDynamicSortFilter (true);
 | ||||
|   setSourceModel (&*m_); | ||||
|   setSortRole (SortRole); | ||||
| } | ||||
| 
 | ||||
| FrequencyList::~FrequencyList () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| FrequencyList& FrequencyList::operator = (Frequencies frequencies) | ||||
| { | ||||
|   m_->assign (frequencies); | ||||
|   return *this; | ||||
| } | ||||
| 
 | ||||
| auto FrequencyList::frequencies () const -> Frequencies | ||||
| { | ||||
|   return m_->frequencies (); | ||||
| } | ||||
| 
 | ||||
| QModelIndex FrequencyList::add (Frequency f) | ||||
| { | ||||
|   return mapFromSource (m_->add (f)); | ||||
| } | ||||
| 
 | ||||
| bool FrequencyList::remove (Frequency f) | ||||
| { | ||||
|   auto row = m_->frequencies ().indexOf (f); | ||||
| 
 | ||||
|   if (0 > row) | ||||
|     { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|   return m_->removeRow (row); | ||||
| } | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs) | ||||
|   { | ||||
|     return lhs.row () > rhs.row (); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool FrequencyList::removeDisjointRows (QModelIndexList rows) | ||||
| { | ||||
|   bool result {true}; | ||||
| 
 | ||||
|   // We must work with source model indexes because we don't want row
 | ||||
|   // removes to invalidate model indexes we haven't yet processed. We
 | ||||
|   // achieve that by processing them in decending row order.
 | ||||
|   for (int r = 0; r < rows.size (); ++r) | ||||
|     { | ||||
|       rows[r] = mapToSource (rows[r]); | ||||
|     } | ||||
| 
 | ||||
|   // reverse sort by row
 | ||||
|   qSort (rows.begin (), rows.end (), row_is_higher); | ||||
|   Q_FOREACH (auto index, rows) | ||||
|     { | ||||
|       if (result && !m_->removeRow (index.row ())) | ||||
|         { | ||||
|           result = false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void FrequencyList::impl::assign (Frequencies frequencies) | ||||
| { | ||||
|   beginResetModel (); | ||||
|   std::swap (frequencies_, frequencies); | ||||
|   endResetModel (); | ||||
| } | ||||
| 
 | ||||
| QModelIndex FrequencyList::impl::add (Frequency f) | ||||
| { | ||||
|   // Any Frequency that isn't in the list may be added
 | ||||
|   if (!frequencies_.contains (f)) | ||||
|     { | ||||
|       auto row = frequencies_.size (); | ||||
| 
 | ||||
|       beginInsertRows (QModelIndex {}, row, row); | ||||
|       frequencies_.append (f); | ||||
|       endInsertRows (); | ||||
| 
 | ||||
|       return index (row, 0); | ||||
|     } | ||||
| 
 | ||||
|   return QModelIndex {}; | ||||
| } | ||||
| 
 | ||||
| int FrequencyList::impl::rowCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : frequencies_.size (); | ||||
| } | ||||
| 
 | ||||
| int FrequencyList::impl::columnCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : num_cols; | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags FrequencyList::impl::flags (QModelIndex const& index) const | ||||
| { | ||||
|   auto result = QAbstractTableModel::flags (index) | Qt::ItemIsDropEnabled; | ||||
| 
 | ||||
|   auto row = index.row (); | ||||
|   auto column = index.column (); | ||||
| 
 | ||||
|   if (index.isValid () | ||||
|       && row < frequencies_.size () | ||||
|       && column < num_cols) | ||||
|     { | ||||
|       switch (column) | ||||
|         { | ||||
|         case 0: | ||||
|           result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; | ||||
|           break; | ||||
| 
 | ||||
|         case 1: | ||||
|           result |= Qt::ItemIsDragEnabled; | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| QVariant FrequencyList::impl::data (QModelIndex const& index, int role) const | ||||
| { | ||||
|   QVariant item; | ||||
| 
 | ||||
|   auto row = index.row (); | ||||
|   auto column = index.column (); | ||||
| 
 | ||||
|   if (index.isValid () | ||||
|       && row < frequencies_.size () | ||||
|       && column < num_cols) | ||||
|     { | ||||
|       auto frequency = frequencies_.at (row); | ||||
| 
 | ||||
|       switch (column) | ||||
|         { | ||||
|         case 0: | ||||
|           switch (role) | ||||
|             { | ||||
|             case SortRole: | ||||
|             case Qt::DisplayRole: | ||||
|             case Qt::EditRole: | ||||
|             case Qt::AccessibleTextRole: | ||||
|               item = frequency; | ||||
|               break; | ||||
| 	       | ||||
|             case Qt::ToolTipRole: | ||||
|             case Qt::AccessibleDescriptionRole: | ||||
|               item = tr ("Frequency"); | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::TextAlignmentRole: | ||||
|               item = Qt::AlignRight + Qt::AlignVCenter; | ||||
|               break; | ||||
|             } | ||||
|           break; | ||||
| 
 | ||||
|         case 1: | ||||
|           switch (role) | ||||
|             { | ||||
|             case Qt::DisplayRole: | ||||
|             case Qt::EditRole: | ||||
|             case Qt::AccessibleTextRole: | ||||
|               item = static_cast<double> (frequency / 1.e6); | ||||
|               break; | ||||
| 
 | ||||
|             case SortRole:	// use the underlying Frequency value
 | ||||
|               item = frequency; | ||||
|               break; | ||||
| 	       | ||||
|             case Qt::ToolTipRole: | ||||
|             case Qt::AccessibleDescriptionRole: | ||||
|               item = tr ("Frequency MHz"); | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::TextAlignmentRole: | ||||
|               item = Qt::AlignRight + Qt::AlignVCenter; | ||||
|               break; | ||||
|             } | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return item; | ||||
| } | ||||
| 
 | ||||
| bool FrequencyList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role) | ||||
| { | ||||
|   bool changed {false}; | ||||
| 
 | ||||
|   auto row = model_index.row (); | ||||
|   if (model_index.isValid () | ||||
|       && Qt::EditRole == role | ||||
|       && row < frequencies_.size () | ||||
|       && 0 == model_index.column () | ||||
|       && value.canConvert<Frequency> ()) | ||||
|     { | ||||
|       auto frequency = value.value<Frequency> (); | ||||
|       auto original_frequency = frequencies_.at (row); | ||||
|       if (frequency != original_frequency) | ||||
|         { | ||||
|           frequencies_.replace (row, frequency); | ||||
|           Q_EMIT dataChanged (model_index, index (model_index.row (), 1), QVector<int> {} << role); | ||||
|         } | ||||
|       changed = true; | ||||
|     } | ||||
| 
 | ||||
|   return changed; | ||||
| } | ||||
| 
 | ||||
| QVariant FrequencyList::impl::headerData (int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|   QVariant header; | ||||
| 
 | ||||
|   if (Qt::DisplayRole == role | ||||
|       && Qt::Horizontal == orientation | ||||
|       && section < num_cols) | ||||
|     { | ||||
|       switch (section) | ||||
|         { | ||||
|         case 0: header = tr ("Frequency"); break; | ||||
|         case 1: header = tr ("Frequency (MHz)"); break; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       header = QAbstractTableModel::headerData (section, orientation, role); | ||||
|     } | ||||
| 
 | ||||
|   return header; | ||||
| } | ||||
| 
 | ||||
| bool FrequencyList::impl::removeRows (int row, int count, QModelIndex const& parent) | ||||
| { | ||||
|   if (0 < count && (row + count) <= rowCount (parent)) | ||||
|     { | ||||
|       beginRemoveRows (parent, row, row + count - 1); | ||||
|       for (auto r = 0; r < count; ++r) | ||||
|         { | ||||
|           frequencies_.removeAt (row); | ||||
|         } | ||||
|       endRemoveRows (); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| bool FrequencyList::impl::insertRows (int row, int count, QModelIndex const& parent) | ||||
| { | ||||
|   if (0 < count) | ||||
|     { | ||||
|       beginInsertRows (parent, row, row + count - 1); | ||||
|       for (auto r = 0; r < count; ++r) | ||||
|         { | ||||
|           frequencies_.insert (row, Frequency {}); | ||||
|         } | ||||
|       endInsertRows (); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| QStringList FrequencyList::impl::mimeTypes () const | ||||
| { | ||||
|   QStringList types; | ||||
|   types << mime_type; | ||||
|   return types; | ||||
| } | ||||
| 
 | ||||
| QMimeData * FrequencyList::impl::mimeData (QModelIndexList const& items) const | ||||
| { | ||||
|   QMimeData * mime_data = new QMimeData {}; | ||||
|   QByteArray encoded_data; | ||||
|   QDataStream stream {&encoded_data, QIODevice::WriteOnly}; | ||||
| 
 | ||||
|   Q_FOREACH (auto const& item, items) | ||||
|     { | ||||
|       if (item.isValid ()) | ||||
|         { | ||||
|           stream << QString {data (item, Qt::DisplayRole).toString ()}; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   mime_data->setData (mime_type, encoded_data); | ||||
|   return mime_data; | ||||
| } | ||||
							
								
								
									
										59
									
								
								FrequencyList.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,59 @@ | ||||
| #ifndef FREQUENCY_LIST_HPP__ | ||||
| #define FREQUENCY_LIST_HPP__ | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| #include <QSortFilterProxyModel> | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| //
 | ||||
| // Class FrequencyList
 | ||||
| //
 | ||||
| //  Encapsulates a collection of frequencies.  The implementation is a
 | ||||
| //  table  containing the  list of  Frequency type  elements which  is
 | ||||
| //  editable  and  a  second  column  which  is  an  immutable  double
 | ||||
| //  representation  of  the  corresponding Frequency  item  scaled  to
 | ||||
| //  mega-Hertz.
 | ||||
| //
 | ||||
| //  The list is ordered.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Stores internally a list  of unique frequencies.  Provides methods
 | ||||
| //  to add and delete list elements.
 | ||||
| //
 | ||||
| // Collaborations
 | ||||
| //
 | ||||
| //  Implements the QSortFilterProxyModel interface  for a list of spot
 | ||||
| //  frequencies.
 | ||||
| //
 | ||||
| class FrequencyList final | ||||
|   : public QSortFilterProxyModel | ||||
| { | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
|   using Frequencies = Radio::Frequencies; | ||||
| 
 | ||||
|   explicit FrequencyList (QObject * parent = nullptr); | ||||
|   explicit FrequencyList (Frequencies, QObject * parent = nullptr); | ||||
|   ~FrequencyList (); | ||||
| 
 | ||||
|   // Load and store contents
 | ||||
|   FrequencyList& operator = (Frequencies); | ||||
|   Frequencies frequencies () const; | ||||
| 
 | ||||
|   // Model API
 | ||||
|   QModelIndex add (Frequency); | ||||
|   bool remove (Frequency); | ||||
|   bool removeDisjointRows (QModelIndexList); | ||||
| 
 | ||||
|   // Custom roles.
 | ||||
|   static int constexpr SortRole = Qt::UserRole; | ||||
| 
 | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										75
									
								
								GetUserId.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,75 @@ | ||||
| #include "GetUserId.hpp" | ||||
| 
 | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QString> | ||||
| #include <QDialog> | ||||
| #include <QLineEdit> | ||||
| #include <QRegExpValidator> | ||||
| #include <QDialogButtonBox> | ||||
| #include <QFormLayout> | ||||
| #include <QVBoxLayout> | ||||
| 
 | ||||
| //
 | ||||
| // Dialog to get callsign
 | ||||
| //
 | ||||
| class CallsignDialog final | ||||
|   : public QDialog | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| private: | ||||
|   Q_DISABLE_COPY (CallsignDialog); | ||||
| 
 | ||||
| public: | ||||
|   explicit CallsignDialog (QWidget * parent = nullptr) | ||||
|     : QDialog {parent} | ||||
|   { | ||||
|     setWindowTitle (QApplication::applicationName () + " - " + tr ("Callsign")); | ||||
|      | ||||
|     callsign_.setValidator (new QRegExpValidator {QRegExp {"[A-Za-z0-9]+"}, this}); | ||||
|      | ||||
|     auto form_layout = new QFormLayout (); | ||||
|     form_layout->addRow ("&Callsign:", &callsign_); | ||||
|      | ||||
|     auto main_layout = new QVBoxLayout (this); | ||||
|     main_layout->addLayout (form_layout); | ||||
|      | ||||
|     auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel}; | ||||
|     main_layout->addWidget (button_box); | ||||
| 
 | ||||
|     connect (button_box, &QDialogButtonBox::accepted, this, &CallsignDialog::accept); | ||||
|     connect (button_box, &QDialogButtonBox::rejected, this, &CallsignDialog::reject); | ||||
|   } | ||||
|    | ||||
|   QString callsign () const {return callsign_.text ();} | ||||
|    | ||||
| private: | ||||
|   QLineEdit callsign_; | ||||
| }; | ||||
| 
 | ||||
| #include "GetUserId.moc" | ||||
| 
 | ||||
| QString get_user_id () | ||||
| { | ||||
|   // get the users callsign so we can use it to persist the
 | ||||
|   // settings and log file against a unique tag
 | ||||
|   QString id; | ||||
|   { | ||||
|     CallsignDialog dialog; | ||||
|     while (id.isEmpty ()) | ||||
|       { | ||||
|         if (QDialog::Accepted == dialog.exec ()) | ||||
|           { | ||||
|             id = dialog.callsign ().toUpper (); | ||||
|           } | ||||
|         else | ||||
|           { | ||||
|             throw std::runtime_error ("Callsign required"); | ||||
|           } | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   return id; | ||||
| } | ||||
							
								
								
									
										8
									
								
								GetUserId.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,8 @@ | ||||
| #ifndef GETUSERID_HPP_ | ||||
| #define GETUSERID_HPP_ | ||||
| 
 | ||||
| #include <QString> | ||||
| 
 | ||||
| QString get_user_id (); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										766
									
								
								HRDTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,766 @@ | ||||
| #include "HRDTransceiver.hpp" | ||||
| 
 | ||||
| #include <QHostAddress> | ||||
| #include <QByteArray> | ||||
| #include <QRegExp> | ||||
| #include <QTcpSocket> | ||||
| #include <QThread> | ||||
| 
 | ||||
| #include "NetworkServerLookup.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   char const * const HRD_transceiver_name = "Ham Radio Deluxe"; | ||||
|   int socket_wait_time {5000}; | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id) | ||||
| { | ||||
|   (*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, "localhost:7809", true); | ||||
| } | ||||
| 
 | ||||
| struct HRDMessage | ||||
| { | ||||
|   // placement style new overload for outgoing messages that does the construction too
 | ||||
|   static void * operator new (size_t size, QString const& payload) | ||||
|   { | ||||
|     size += sizeof (QChar) * (payload.size () + 1); // space for terminator too
 | ||||
|     HRDMessage * storage (reinterpret_cast<HRDMessage *> (new char[size])); | ||||
|     storage->size_ = size ; | ||||
|     ushort const * pl (payload.utf16 ()); | ||||
|     qCopy (pl, pl + payload.size () + 1, storage->payload_); // copy terminator too
 | ||||
|     storage->magic_1_ = magic_1_value_; | ||||
|     storage->magic_2_ = magic_2_value_; | ||||
|     storage->checksum_ = 0; | ||||
|     return storage; | ||||
|   } | ||||
| 
 | ||||
|   // placement style new overload for incoming messages that does the construction too
 | ||||
|   //
 | ||||
|   // no memory allocation here
 | ||||
|   static void * operator new (size_t /* size */, QByteArray const& message) | ||||
|   { | ||||
|     // nasty const_cast here to avoid copying the message buffer
 | ||||
|     return const_cast<HRDMessage *> (reinterpret_cast<HRDMessage const *> (message.data ())); | ||||
|   } | ||||
| 
 | ||||
|   void operator delete (void * p, size_t) | ||||
|   { | ||||
|     delete [] reinterpret_cast<char *> (p); // mirror allocation in operator new above
 | ||||
|   } | ||||
| 
 | ||||
|   qint32 size_; | ||||
|   qint32 magic_1_; | ||||
|   qint32 magic_2_; | ||||
|   qint32 checksum_;	       // apparently not used
 | ||||
|   QChar payload_[0];	       // UTF-16 (which is wchar_t on Windows)
 | ||||
| 
 | ||||
|   static qint32 const magic_1_value_; | ||||
|   static qint32 const magic_2_value_; | ||||
| }; | ||||
| 
 | ||||
| qint32 const HRDMessage::magic_1_value_ (0x1234ABCD); | ||||
| qint32 const HRDMessage::magic_2_value_ (0xABCD1234); | ||||
| 
 | ||||
| HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& server, bool use_for_ptt, int poll_interval) | ||||
|   : PollingTransceiver {poll_interval} | ||||
|   , wrapped_ {std::move (wrapped)} | ||||
|   , use_for_ptt_ {use_for_ptt} | ||||
|   , server_ {server} | ||||
|   , hrd_ {0} | ||||
|   , protocol_ {none} | ||||
|   , current_radio_ {0} | ||||
|   , vfo_count_ {0} | ||||
|   , vfo_A_button_ {-1} | ||||
|   , vfo_B_button_ {-1} | ||||
|   , vfo_toggle_button_ {-1} | ||||
|   , mode_A_dropdown_ {-1} | ||||
|   , mode_B_dropdown_ {-1} | ||||
|   , split_mode_button_ {-1} | ||||
|   , split_mode_dropdown_ {-1} | ||||
|   , split_mode_dropdown_write_only_ {false} | ||||
|   , split_mode_dropdown_selection_on_ {-1} | ||||
|   , split_mode_dropdown_selection_off_ {-1} | ||||
|   , split_off_button_ {-1} | ||||
|   , tx_A_button_ {-1} | ||||
|   , tx_B_button_ {-1} | ||||
|   , ptt_button_ {-1} | ||||
|   , reversed_ {false} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| HRDTransceiver::~HRDTransceiver () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_start () | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::start"; | ||||
| #endif | ||||
| 
 | ||||
|   wrapped_->start (); | ||||
| 
 | ||||
|   auto server_details = network_server_lookup (server_, 7809u); | ||||
| 
 | ||||
|   if (!hrd_) | ||||
|     { | ||||
|       hrd_ = new QTcpSocket {this}; // QObject takes ownership
 | ||||
|     } | ||||
| 
 | ||||
|   hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details)); | ||||
|   if (!hrd_->waitForConnected (socket_wait_time)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::start failed to connect:" <<  hrd_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Failed to connect to Ham Radio Deluxe\n" + hrd_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   init_radio (); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_stop () | ||||
| { | ||||
|   if (hrd_) | ||||
|     { | ||||
|       hrd_->close (); | ||||
|     } | ||||
| 
 | ||||
|   if (wrapped_) | ||||
|     { | ||||
|       wrapped_->stop (); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::stop: state:" << state () << "reversed =" << reversed_; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::init_radio () | ||||
| { | ||||
|   Q_ASSERT (hrd_); | ||||
| 
 | ||||
|   if (none == protocol_) | ||||
|     { | ||||
|       try | ||||
| 	{ | ||||
| 	  protocol_ = v5;	// try this first (works for v6 too)
 | ||||
| 	  send_command ("get context", false, false); | ||||
| 	} | ||||
|       catch (error const&) | ||||
| 	{ | ||||
| 	  protocol_ = none; | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   if (none == protocol_) | ||||
|     { | ||||
|       hrd_->close (); | ||||
| 
 | ||||
|       protocol_ = v4;		// try again with older protocol
 | ||||
|       hrd_->connectToHost (QHostAddress::LocalHost, 7809); | ||||
|       if (!hrd_->waitForConnected (socket_wait_time)) | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "HRDTransceiver::init_radio failed to connect:" <<  hrd_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
| 	  throw error {"Failed to connect to Ham Radio Deluxe\n" + hrd_->errorString ().toLocal8Bit ()}; | ||||
| 	} | ||||
| 
 | ||||
|       send_command ("get context", false, false); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << send_command ("get id", false, false); | ||||
|   qDebug () << send_command ("get version", false, false); | ||||
| #endif | ||||
| 
 | ||||
|   auto radios = send_command ("get radios", false, false).trimmed ().split (',', QString::SkipEmptyParts); | ||||
|   if (radios.isEmpty ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::init_radio no rig found"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe: no rig found"}; | ||||
|     } | ||||
| 
 | ||||
|   Q_FOREACH (auto const& radio, radios) | ||||
|     { | ||||
|       auto entries = radio.trimmed ().split (':', QString::SkipEmptyParts); | ||||
|       radios_.push_back (std::forward_as_tuple (entries[0].toUInt (), entries[1])); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "radios:"; | ||||
|   Q_FOREACH (auto const& radio, radios_) | ||||
|     { | ||||
|       qDebug () << "\t[" << std::get<0> (radio) << "] " << std::get<1> (radio); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|   if (send_command ("get radio", false, false, true).isEmpty ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::init_radio no rig found"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe: no rig found"}; | ||||
|     } | ||||
| 
 | ||||
|   vfo_count_ = send_command ("get vfo-count").toUInt (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "vfo count:" << vfo_count_; | ||||
| #endif | ||||
| 
 | ||||
|   buttons_ = send_command ("get buttons").trimmed ().split (',', QString::SkipEmptyParts).replaceInStrings (" ", "~"); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRD Buttons: " << buttons_; | ||||
| #endif | ||||
| 
 | ||||
|   dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', QString::SkipEmptyParts); | ||||
|   Q_FOREACH (auto d, dropdown_names_) | ||||
|     { | ||||
|       dropdowns_[d] = send_command ("get dropdown-list {" + d + "}").trimmed ().split (',', QString::SkipEmptyParts); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRD Dropdowns: " << dropdowns_; | ||||
| #endif | ||||
| 
 | ||||
|   vfo_A_button_ = find_button (QRegExp ("^(VFO~A|Main)$")); | ||||
|   vfo_B_button_ = find_button (QRegExp ("^(VFO~B|Sub)$")); | ||||
|   vfo_toggle_button_ = find_button (QRegExp ("^(A~/~B)$")); | ||||
|   Q_ASSERT (vfo_toggle_button_ >= 0 || (vfo_A_button_ >= 0 && vfo_B_button_ >=0)); | ||||
|   | ||||
|   split_mode_button_ = find_button (QRegExp ("^(Spl~On|Spl_On|Split)$")); | ||||
|   split_off_button_ = find_button (QRegExp ("^(Spl~Off|Spl_Off)$")); | ||||
| 
 | ||||
|   if ((split_mode_dropdown_ = find_dropdown (QRegExp ("^(Split)$"))) >= 0) | ||||
|     { | ||||
|       split_mode_dropdown_selection_on_ = find_dropdown_selection (split_mode_dropdown_, QRegExp ("^(On)$")); | ||||
|       split_mode_dropdown_selection_off_ = find_dropdown_selection (split_mode_dropdown_, QRegExp ("^(Off)$")); | ||||
|     } | ||||
| 
 | ||||
|   tx_A_button_ = find_button (QRegExp ("^(TX~main|TX~-~A)$")); | ||||
|   tx_B_button_ = find_button (QRegExp ("^(TX~sub|TX~-~B)$")); | ||||
| 
 | ||||
|   Q_ASSERT (split_mode_button_ >= 0 || split_mode_dropdown_ >= 0 || (tx_A_button_ >= 0 && tx_B_button_ >= 0)); | ||||
| 
 | ||||
|   mode_A_dropdown_ = find_dropdown (QRegExp ("^(Main Mode|Mode)$")); | ||||
|   map_modes (mode_A_dropdown_, &mode_A_map_); | ||||
| 
 | ||||
|   if ((mode_B_dropdown_ = find_dropdown (QRegExp ("^(Sub Mode)$"))) >= 0) | ||||
|     { | ||||
|       map_modes (mode_B_dropdown_, &mode_B_map_); | ||||
|     } | ||||
| 
 | ||||
|   ptt_button_ = find_button (QRegExp ("^(TX)$")); | ||||
| 
 | ||||
|   sync_impl (); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::sync_impl () | ||||
| { | ||||
|   if (vfo_count_ == 1) | ||||
|     { | ||||
|       // put the rig into a known state
 | ||||
|       auto f = send_command ("get frequency").toUInt (); | ||||
|       auto m = lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_); | ||||
|       set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); | ||||
|       auto fo = send_command ("get frequency").toUInt (); | ||||
|       update_other_frequency (fo); | ||||
|       auto mo = lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_); | ||||
|       set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); | ||||
|       if (f != fo || m != mo) | ||||
| 	{ | ||||
| 	  // we must have started with A/MAIN
 | ||||
| 	  update_rx_frequency (f); | ||||
| 	  update_mode (m); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  update_rx_frequency (send_command ("get frequency").toUInt ()); | ||||
| 	  update_mode (lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_)); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   poll (); | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::find_button (QRegExp const& re) const | ||||
| { | ||||
|   return buttons_.indexOf (re); | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::find_dropdown (QRegExp const& re) const | ||||
| { | ||||
|   return dropdown_names_.indexOf (re); | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::find_dropdown_selection (int dropdown, QRegExp const& re) const | ||||
| { | ||||
|   return dropdowns_.value (dropdown_names_.value (dropdown)).lastIndexOf (re); // backwards because more specialised modes tend to be later in list
 | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::lookup_dropdown_selection (int dropdown, QString const& selection) const | ||||
| { | ||||
|   int index {dropdowns_.value (dropdown_names_.value (dropdown)).indexOf (selection)}; | ||||
|   Q_ASSERT (-1 != index); | ||||
|   return index; | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::map_modes (int dropdown, ModeMap *map) | ||||
| { | ||||
|   // order matters here (both in the map and in the regexps)
 | ||||
|   map->push_back (std::forward_as_tuple (CW, find_dropdown_selection (dropdown, QRegExp ("^(CW|CW\\(N\\))$")))); | ||||
|   map->push_back (std::forward_as_tuple (CW_R, find_dropdown_selection (dropdown, QRegExp ("^(CW-R|CW)$")))); | ||||
|   map->push_back (std::forward_as_tuple (LSB, find_dropdown_selection (dropdown, QRegExp ("^(LSB)$")))); | ||||
|   map->push_back (std::forward_as_tuple (USB, find_dropdown_selection (dropdown, QRegExp ("^(USB)$")))); | ||||
|   map->push_back (std::forward_as_tuple (DIG_U, find_dropdown_selection (dropdown, QRegExp ("^(DIG|PKT-U|USB)$")))); | ||||
|   map->push_back (std::forward_as_tuple (DIG_L, find_dropdown_selection (dropdown, QRegExp ("^(DIG|PKT-L|LSB)$")))); | ||||
|   map->push_back (std::forward_as_tuple (FSK, find_dropdown_selection (dropdown, QRegExp ("^(DIG|FSK|RTTY)$")))); | ||||
|   map->push_back (std::forward_as_tuple (FSK_R, find_dropdown_selection (dropdown, QRegExp ("^(DIG|FSK-R|RTTY-R|RTTY)$")))); | ||||
|   map->push_back (std::forward_as_tuple (AM, find_dropdown_selection (dropdown, QRegExp ("^(AM)$")))); | ||||
|   map->push_back (std::forward_as_tuple (FM, find_dropdown_selection (dropdown, QRegExp ("^(FM|FM\\(N\\)|WFM)$")))); | ||||
|   map->push_back (std::forward_as_tuple (DIG_FM, find_dropdown_selection (dropdown, QRegExp ("^(PKT-FM|PKT|FM)$")))); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::map_modes: for dropdown" << dropdown_names_[dropdown]; | ||||
| 
 | ||||
|   std::for_each (map->begin (), map->end (), [this, dropdown] (ModeMap::value_type const& item) | ||||
| 		{ | ||||
| 		  auto rhs = std::get<1> (item); | ||||
| 		  qDebug () << '\t' << std::get<0> (item) << "<->" << (rhs >= 0 ? dropdowns_[dropdown_names_[dropdown]][rhs] : "None"); | ||||
| 		}); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::lookup_mode (MODE mode, ModeMap const& map) const | ||||
| { | ||||
|   auto it = std::find_if (map.begin (), map.end (), [mode] (ModeMap::value_type const& item) {return std::get<0> (item) == mode;}); | ||||
|   if (map.end () == it) | ||||
|     { | ||||
|       throw error {"Ham Radio Deluxe: rig doesn't support mode"}; | ||||
|     } | ||||
|   return std::get<1> (*it); | ||||
| } | ||||
| 
 | ||||
| auto HRDTransceiver::lookup_mode (int mode, ModeMap const& map) const -> MODE | ||||
| { | ||||
|   auto it = std::find_if (map.begin (), map.end (), [mode] (ModeMap::value_type const& item) {return std::get<1> (item) == mode;}); | ||||
|   if (map.end () == it) | ||||
|     { | ||||
|       throw error {"Ham Radio Deluxe: sent an unrecognised mode"}; | ||||
|     } | ||||
|   return std::get<0> (*it); | ||||
| } | ||||
| 
 | ||||
| int HRDTransceiver::get_dropdown (int dd, bool no_debug) | ||||
| { | ||||
|   auto dd_name = dropdown_names_.value (dd); | ||||
|   auto reply = send_command ("get dropdown-text {" + dd_name + "}", no_debug); | ||||
|   auto colon_index = reply.indexOf (':'); | ||||
| 
 | ||||
|   if (colon_index < 0) | ||||
|     { | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::get_dropdown bad response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe didn't respond as expected"}; | ||||
|     } | ||||
| 
 | ||||
|   Q_ASSERT (reply.left (colon_index).trimmed () == dd_name); | ||||
|   return lookup_dropdown_selection (dd, reply.mid (colon_index + 1).trimmed ()); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::set_dropdown (int dd, int value) | ||||
| { | ||||
|   auto dd_name = dropdown_names_.value (dd); | ||||
|   if (value >= 0) | ||||
|     { | ||||
|       send_simple_command ("set dropdown " + dd_name.replace (' ', '~') + ' ' + dropdowns_.value (dd_name).value (value) + ' ' + QString::number (value)); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::set_dropdown item" << value << "not found in" << dd_name; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {("Ham Radio Deluxe: item not found in " + dd_name + " dropdown list").toLocal8Bit ()}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_ptt (bool on) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::do_ptt:" << on; | ||||
| #endif | ||||
| 
 | ||||
|   if (use_for_ptt_) | ||||
|     { | ||||
|       set_button (ptt_button_, on); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       wrapped_->ptt (on); | ||||
|     } | ||||
|   update_PTT (on); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::set_button (int button_index, bool checked) | ||||
| { | ||||
|   if (button_index >= 0) | ||||
|     { | ||||
|       send_simple_command ("set button-select " + buttons_.value (button_index) + (checked ? " 1" : " 0")); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::set_button invalid button"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe: button not available"}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_frequency (Frequency f) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::do_frequency:" << f << "reversed:" << reversed_; | ||||
| #endif | ||||
| 
 | ||||
|   send_simple_command ("set frequency-hz " + QString::number (f)); | ||||
|   update_rx_frequency (f); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::do_tx_frequency:" << tx << "reversed:" << reversed_; | ||||
| #endif | ||||
| 
 | ||||
|   bool split {tx != 0}; | ||||
| 
 | ||||
|   if (vfo_count_ > 1) | ||||
|     { | ||||
|       reversed_ = is_button_checked (vfo_B_button_); | ||||
|     } | ||||
| 
 | ||||
|   if (split) | ||||
|     { | ||||
|       auto fo_string = QString::number (tx); | ||||
|       if (reversed_) | ||||
| 	{ | ||||
| 	  Q_ASSERT (vfo_count_ > 1); | ||||
| 
 | ||||
| 	  auto frequencies = send_command ("get frequencies").trimmed ().split ('-', QString::SkipEmptyParts); | ||||
| 	  send_simple_command ("set frequencies-hz " + fo_string + ' ' + QString::number (frequencies[1].toUInt ())); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  if (vfo_count_ > 1) | ||||
| 	    { | ||||
| 	      auto frequencies = send_command ("get frequencies").trimmed ().split ('-', QString::SkipEmptyParts); | ||||
| 	      send_simple_command ("set frequencies-hz " + QString::number (frequencies[0].toUInt ()) + ' ' + fo_string); | ||||
| 	    } | ||||
| 	  else | ||||
| 	    { | ||||
| 	      // we rationalise the modes and VFOs here as well as the frequencies
 | ||||
| 	      set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); | ||||
| 	      send_simple_command ("set frequency-hz " + fo_string); | ||||
| 	      if (rationalise_mode) | ||||
| 		{ | ||||
| 		  set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (state ().mode (), mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_)); | ||||
| 		} | ||||
| 	      set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); | ||||
| 	    } | ||||
| 	} | ||||
|       if (rationalise_mode) | ||||
| 	{ | ||||
| 	  set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (state ().mode (), mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_)); | ||||
| 	} | ||||
|     } | ||||
|   update_other_frequency (tx); | ||||
| 
 | ||||
|   if (split_mode_button_ >= 0) | ||||
|     { | ||||
|       if (split_off_button_ >= 0 && !split) | ||||
| 	{ | ||||
| 	  set_button (split_off_button_); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  set_button (split_mode_button_, split); | ||||
| 	} | ||||
|     } | ||||
|   else if (split_mode_dropdown_ >= 0) | ||||
|     { | ||||
|       set_dropdown (split_mode_dropdown_, split ? split_mode_dropdown_selection_on_ : split_mode_dropdown_selection_off_); | ||||
|     } | ||||
|   else if (vfo_A_button_ >= 0 && vfo_B_button_ >= 0 && tx_A_button_ >= 0 && tx_B_button_ >= 0) | ||||
|     { | ||||
|       if (split) | ||||
| 	{ | ||||
| 	  if (reversed_ != is_button_checked (tx_A_button_)) | ||||
| 	    { | ||||
| 	      set_button (reversed_ ? tx_A_button_ : tx_B_button_); | ||||
| 	    } | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  if (reversed_ != is_button_checked (tx_B_button_)) | ||||
| 	    { | ||||
| 	      set_button (reversed_ ? tx_B_button_ : tx_A_button_); | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
|   update_split (split); | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::do_mode (MODE mode, bool rationalise) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HRDTransceiver::do_mode:" << mode; | ||||
| #endif | ||||
| 
 | ||||
|   set_dropdown (mode_A_dropdown_, lookup_mode (mode, mode_A_map_)); | ||||
| 
 | ||||
|   if (rationalise && state ().split ()) // rationalise mode if split
 | ||||
|     { | ||||
|       if (mode_B_dropdown_ >= 0) | ||||
| 	{ | ||||
| 	  set_dropdown (mode_B_dropdown_, lookup_mode (mode, mode_B_map_)); | ||||
| 	} | ||||
|       else if (vfo_count_ < 2) | ||||
| 	{ | ||||
| 	  set_button (vfo_B_button_ >= 0 ? vfo_B_button_ : vfo_toggle_button_); | ||||
| 	  set_dropdown (mode_B_dropdown_ >= 0 ? mode_B_dropdown_ : mode_A_dropdown_, lookup_mode (mode, mode_B_dropdown_ >= 0 ? mode_B_map_ : mode_A_map_)); | ||||
| 	  set_button (vfo_A_button_ >= 0 ? vfo_A_button_ : vfo_toggle_button_); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   update_mode (mode); | ||||
| } | ||||
| 
 | ||||
| bool HRDTransceiver::is_button_checked (int button_index, bool no_debug) | ||||
| { | ||||
|   auto reply = send_command ("get button-select " + buttons_.value (button_index), no_debug); | ||||
|   if ("1" != reply && "0" != reply) | ||||
|   { | ||||
| #if WSJT_TRACE_CAT | ||||
|     qDebug () << "HRDTransceiver::is_button_checked bad response"; | ||||
| #endif | ||||
| 
 | ||||
|     throw error {"Ham Radio Deluxe didn't respond as expected"}; | ||||
|   } | ||||
|   return "1" == reply; | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::poll () | ||||
| { | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|   bool quiet {false}; | ||||
| #else | ||||
|   bool quiet {true}; | ||||
| #endif | ||||
| 
 | ||||
|   if (split_off_button_ >= 0) | ||||
|     { | ||||
|       // we are probably dealing with an Icom and have to guess SPLIT mode :(
 | ||||
|     } | ||||
|   else if (split_mode_button_ >= 0) | ||||
|     { | ||||
|       update_split (is_button_checked (split_mode_button_, quiet)); | ||||
|     } | ||||
|   else if (split_mode_dropdown_ >= 0) | ||||
|     { | ||||
|       if (!split_mode_dropdown_write_only_) | ||||
| 	{ | ||||
| 	  try | ||||
| 	    { | ||||
| 	      update_split (get_dropdown (split_mode_dropdown_, quiet) == split_mode_dropdown_selection_on_); | ||||
| 	    } | ||||
| 	  catch (error const&) | ||||
| 	    { | ||||
| 	      // leave split alone as we can't query it - it should be
 | ||||
| 	      // correct so long as rig or HRD haven't been changed
 | ||||
| 	      split_mode_dropdown_write_only_ = true; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
|   else if (vfo_A_button_ >= 0 && vfo_B_button_ >= 0 && tx_A_button_ >= 0 && tx_B_button_ >= 0) | ||||
|     { | ||||
|       auto vfo_A = is_button_checked (vfo_A_button_, quiet); | ||||
|       auto tx_A = is_button_checked (tx_A_button_, quiet); | ||||
| 
 | ||||
|       update_split (vfo_A != tx_A); | ||||
|       reversed_ = !vfo_A; | ||||
|     } | ||||
| 
 | ||||
|   if (vfo_count_ > 1) | ||||
|     { | ||||
|       auto frequencies = send_command ("get frequencies", quiet).trimmed ().split ('-', QString::SkipEmptyParts); | ||||
|       update_rx_frequency (frequencies[reversed_ ? 1 : 0].toUInt ()); | ||||
|       update_other_frequency (frequencies[reversed_ ? 0 : 1].toUInt ()); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       update_rx_frequency (send_command ("get frequency", quiet).toUInt ()); | ||||
|     } | ||||
| 
 | ||||
|   update_mode (lookup_mode (get_dropdown (mode_A_dropdown_, quiet), mode_A_map_)); | ||||
| } | ||||
| 
 | ||||
| QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool prepend_context, bool recurse) | ||||
| { | ||||
|   Q_ASSERT (hrd_); | ||||
| 
 | ||||
|   QString result; | ||||
| 
 | ||||
|   if (current_radio_ && prepend_context && vfo_count_ < 2) | ||||
|     { | ||||
|       // TODO G4WJS: get rid of this or move to a thread required on some
 | ||||
|       // radios because commands don't get executed correctly (ICOM for
 | ||||
|       // example)
 | ||||
|       QThread::msleep (50); | ||||
|     } | ||||
| 
 | ||||
|   if (QTcpSocket::ConnectedState != hrd_->state ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_command connection failed:" << hrd_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe connection failed\n" + hrd_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   if (!recurse && prepend_context) | ||||
|     { | ||||
|       auto radio_name = send_command ("get radio", no_debug, current_radio_, true); | ||||
|       auto radio_iter = std::find_if (radios_.begin (), radios_.end (), [this, &radio_name] (RadioMap::value_type const& radio) | ||||
| 				      { | ||||
| 					return std::get<1> (radio) == radio_name; | ||||
| 				      }); | ||||
|       if (radio_iter == radios_.end ()) | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "HRDTransceiver::send_command rig disappeared or changed"; | ||||
| #endif | ||||
| 
 | ||||
| 	  throw error {"Ham Radio Deluxe: rig has disappeared or changed"}; | ||||
| 	} | ||||
| 
 | ||||
|       if (0u == current_radio_ || std::get<0> (*radio_iter) != current_radio_) | ||||
| 	{ | ||||
| 	  current_radio_ = std::get<0> (*radio_iter); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   auto context = '[' + QString::number (current_radio_) + "] "; | ||||
| 
 | ||||
|   int bytes_to_send; | ||||
|   int bytes_sent; | ||||
|   if (v4 == protocol_) | ||||
|     { | ||||
|       auto message = ((prepend_context ? context + cmd : cmd) + "\r").toLocal8Bit (); | ||||
|       bytes_to_send = message.size (); | ||||
|       bytes_sent = hrd_->write (message.data (), bytes_to_send); | ||||
| 
 | ||||
|       if (!no_debug) | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "HRDTransceiver::send_command:" << message; | ||||
| #endif | ||||
| 	} | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       auto string = prepend_context ? context + cmd : cmd; | ||||
|       if (!no_debug) | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "HRDTransceiver::send_command:" << string; | ||||
| #endif | ||||
| 	} | ||||
| 
 | ||||
|       QScopedPointer<HRDMessage> message (new (string) HRDMessage); | ||||
|       bytes_to_send = message->size_; | ||||
|       bytes_sent = hrd_->write (reinterpret_cast<char *> (message.data ()), bytes_to_send); | ||||
|     } | ||||
| 
 | ||||
|   if (bytes_sent < bytes_to_send  | ||||
|       || !hrd_->waitForBytesWritten (socket_wait_time) | ||||
|       || QTcpSocket::ConnectedState != hrd_->state ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_command failed" << hrd_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe send command failed\n" + hrd_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
| 
 | ||||
|   if (!hrd_->waitForReadyRead (socket_wait_time)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_command failed to reply" << hrd_->errorString (); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe failed to reply to command\n" + hrd_->errorString ().toLocal8Bit ()}; | ||||
|     } | ||||
|   QByteArray buffer (hrd_->readAll ()); | ||||
|   if (!no_debug) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_command: reply byte count:" << buffer.size (); | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|   if (v4 == protocol_) | ||||
|     { | ||||
|       result = QString {buffer}.trimmed (); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       HRDMessage const * reply (new (buffer) HRDMessage); | ||||
| 
 | ||||
|       if (reply->magic_1_value_ != reply->magic_1_ && reply->magic_2_value_ != reply->magic_2_) | ||||
| 	{ | ||||
| #if WSJT_TRACE_CAT | ||||
| 	  qDebug () << "HRDTransceiver::send_command invalid reply"; | ||||
| #endif | ||||
| 
 | ||||
| 	  throw error {"Ham Radio Deluxe sent an invalid reply to our command"}; | ||||
| 	} | ||||
| 
 | ||||
|       result = QString {reply->payload_}; // this is not a memory leak (honest!)
 | ||||
|     } | ||||
| 
 | ||||
|   if (!no_debug) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_command: reply: " << result; | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| void HRDTransceiver::send_simple_command (QString const& command, bool no_debug) | ||||
| { | ||||
|   if ("OK" != send_command (command, no_debug)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HRDTransceiver::send_simple_command unexpected response"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Ham Radio Deluxe didn't respond as expected"}; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										95
									
								
								HRDTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,95 @@ | ||||
| #ifndef HRD_TRANSCEIVER_HPP__ | ||||
| #define HRD_TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <tuple> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QScopedPointer> | ||||
| #include <QString> | ||||
| #include <QStringList> | ||||
| 
 | ||||
| #include "TransceiverFactory.hpp" | ||||
| #include "PollingTransceiver.hpp" | ||||
| 
 | ||||
| class QRegExp; | ||||
| class QTcpSocket; | ||||
| 
 | ||||
| //
 | ||||
| // Ham Radio Deluxe Transceiver Interface
 | ||||
| //
 | ||||
| // Implemented as a Transceiver decorator  because we may want the PTT
 | ||||
| // services of another Transceiver  type such as the HamlibTransceiver
 | ||||
| // which can  be enabled by wrapping  a HamlibTransceiver instantiated
 | ||||
| // as a "Hamlib Dummy" transceiver in the Transceiver factory method.
 | ||||
| //
 | ||||
| class HRDTransceiver final | ||||
|   : public PollingTransceiver | ||||
| { | ||||
| public: | ||||
|   static void register_transceivers (TransceiverFactory::Transceivers *, int id); | ||||
| 
 | ||||
|   // takes ownership of wrapped Transceiver
 | ||||
|   explicit HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped, QString const& server, bool use_for_ptt, int poll_interval); | ||||
|   ~HRDTransceiver (); | ||||
| 
 | ||||
| private: | ||||
|   void do_start () override; | ||||
|   void do_stop () override; | ||||
|   void do_frequency (Frequency) override; | ||||
|   void do_tx_frequency (Frequency, bool rationalise_mode) override; | ||||
|   void do_mode (MODE, bool rationalise) override; | ||||
|   void do_ptt (bool on) override; | ||||
| 
 | ||||
|   void poll () override; | ||||
| 
 | ||||
|   void init_radio (); | ||||
|   QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false); | ||||
|   void send_simple_command (QString const&, bool no_debug = false); | ||||
|   void sync_impl (); | ||||
|   int find_button (QRegExp const&) const; | ||||
|   int find_dropdown (QRegExp const&) const; | ||||
|   int find_dropdown_selection (int dropdown, QRegExp const&) const; | ||||
|   int lookup_dropdown_selection (int dropdown, QString const&) const; | ||||
|   int get_dropdown (int, bool no_debug = false); | ||||
|   void set_dropdown (int, int); | ||||
|   void set_button (int button_index, bool checked = true); | ||||
|   bool is_button_checked (int button_index, bool no_debug = false); | ||||
| 
 | ||||
|   using ModeMap = std::vector<std::tuple<MODE, int> >; | ||||
|   void map_modes (int dropdown, ModeMap *); | ||||
|   int lookup_mode (MODE, ModeMap const&) const; | ||||
|   MODE lookup_mode (int, ModeMap const&) const; | ||||
| 
 | ||||
|   std::unique_ptr<TransceiverBase> wrapped_; | ||||
|   bool use_for_ptt_; | ||||
|   QString server_; | ||||
|   QTcpSocket * hrd_; | ||||
|   enum {none, v4, v5} protocol_; | ||||
|   using RadioMap = std::vector<std::tuple<unsigned, QString> >; | ||||
|   RadioMap radios_; | ||||
|   unsigned current_radio_; | ||||
|   unsigned vfo_count_; | ||||
|   QStringList buttons_; | ||||
|   QStringList dropdown_names_; | ||||
|   QMap<QString, QStringList> dropdowns_; | ||||
|   int vfo_A_button_; | ||||
|   int vfo_B_button_; | ||||
|   int vfo_toggle_button_; | ||||
|   int mode_A_dropdown_; | ||||
|   ModeMap mode_A_map_; | ||||
|   int mode_B_dropdown_; | ||||
|   ModeMap mode_B_map_; | ||||
|   int split_mode_button_; | ||||
|   int split_mode_dropdown_; | ||||
|   bool split_mode_dropdown_write_only_; | ||||
|   int split_mode_dropdown_selection_on_; | ||||
|   int split_mode_dropdown_selection_off_; | ||||
|   int split_off_button_; | ||||
|   int tx_A_button_; | ||||
|   int tx_B_button_; | ||||
|   int ptt_button_; | ||||
|   bool reversed_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										845
									
								
								HamlibTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,845 @@ | ||||
| #include "HamlibTransceiver.hpp" | ||||
| 
 | ||||
| #include <cstring> | ||||
| 
 | ||||
| #include <QByteArray> | ||||
| #include <QString> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   // Unfortunately bandwidth is conflated  with mode, this is probably
 | ||||
|   // because Icom do  the same. So we have to  care about bandwidth if
 | ||||
|   // we want  to set  mode otherwise we  will end up  setting unwanted
 | ||||
|   // bandwidths every time we change mode.  The best we can do via the
 | ||||
|   // Hamlib API is to request the  normal option for the mode and hope
 | ||||
|   // that an appropriate filter is selected.  Also ensure that mode is
 | ||||
|   // only set is absolutely necessary.  On Icoms (and probably others)
 | ||||
|   // the filter is  selected by number without checking  the actual BW
 | ||||
|   // so unless the  "normal" defaults are set on the  rig we won't get
 | ||||
|   // desirable results.
 | ||||
|   //
 | ||||
|   // As  an ultimate  workaround make  sure  the user  always has  the
 | ||||
|   // option to skip mode setting altogether.
 | ||||
| 
 | ||||
|   // reroute Hamlib diagnostic messages to Qt
 | ||||
|   int debug_callback (enum rig_debug_level_e level, rig_ptr_t /* arg */, char const * format, va_list ap) | ||||
|   { | ||||
|     QString message; | ||||
|     message = message.vsprintf (format, ap).trimmed (); | ||||
| 
 | ||||
|     switch (level) | ||||
|       { | ||||
|       case RIG_DEBUG_BUG: | ||||
|         qFatal ("%s", message.toLocal8Bit ().data ()); | ||||
|         break; | ||||
| 
 | ||||
|       case RIG_DEBUG_ERR: | ||||
|         qCritical ("%s", message.toLocal8Bit ().data ()); | ||||
|         break; | ||||
| 
 | ||||
|       case RIG_DEBUG_WARN: | ||||
|         qWarning ("%s", message.toLocal8Bit ().data ()); | ||||
|         break; | ||||
| 
 | ||||
|       default: | ||||
|         qDebug ("%s", message.toLocal8Bit ().data ()); | ||||
|         break; | ||||
|       } | ||||
| 
 | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
|   // callback function that receives transceiver capabilities from the
 | ||||
|   // hamlib libraries
 | ||||
|   int rigCallback (rig_caps const * caps, void * callback_data) | ||||
|   { | ||||
|     TransceiverFactory::Transceivers * rigs = reinterpret_cast<TransceiverFactory::Transceivers *> (callback_data); | ||||
| 
 | ||||
|     QString key; | ||||
|     if ("Hamlib" == QString::fromLatin1 (caps->mfg_name).trimmed () | ||||
|         && "Dummy" == QString::fromLatin1 (caps->model_name).trimmed ()) | ||||
|       { | ||||
|         key = TransceiverFactory::basic_transceiver_name_; | ||||
|       } | ||||
|     else | ||||
|       { | ||||
|         key = QString::fromLatin1 (caps->mfg_name).trimmed () | ||||
|           + ' '+ QString::fromLatin1 (caps->model_name).trimmed () | ||||
|           // + ' '+ QString::fromLatin1 (caps->version).trimmed ()
 | ||||
|           // + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')'
 | ||||
|           ; | ||||
|       } | ||||
| 
 | ||||
|     auto port_type = TransceiverFactory::Capabilities::none; | ||||
|     switch (caps->port_type) | ||||
|       { | ||||
|       case RIG_PORT_SERIAL: | ||||
|         port_type = TransceiverFactory::Capabilities::serial; | ||||
|         break; | ||||
| 
 | ||||
|       case RIG_PORT_NETWORK: | ||||
|         port_type = TransceiverFactory::Capabilities::network; | ||||
|         break; | ||||
| 
 | ||||
|       default: break; | ||||
|       } | ||||
|     (*rigs)[key] = TransceiverFactory::Capabilities (caps->rig_model | ||||
|                                                      , port_type | ||||
|                                                      , RIG_PTT_RIG == caps->ptt_type || RIG_PTT_RIG_MICDATA == caps->ptt_type | ||||
|                                                      , RIG_PTT_RIG_MICDATA == caps->ptt_type); | ||||
| 
 | ||||
|     return 1;			// keep them coming
 | ||||
|   } | ||||
| 
 | ||||
|   // int frequency_change_callback (RIG * /* rig */, vfo_t vfo, freq_t f, rig_ptr_t arg)
 | ||||
|   // {
 | ||||
|   //   (void)vfo;			// unused in release build
 | ||||
| 
 | ||||
|   //   Q_ASSERT (vfo == RIG_VFO_CURR); // G4WJS: at the time of writing only current VFO is signalled by hamlib
 | ||||
| 
 | ||||
|   //   HamlibTransceiver * transceiver (reinterpret_cast<HamlibTransceiver *> (arg));
 | ||||
|   //   Q_EMIT transceiver->frequency_change (f, Transceiver::A);
 | ||||
|   //   return RIG_OK;
 | ||||
|   // }
 | ||||
| 
 | ||||
|   class hamlib_tx_vfo_fixup final | ||||
|   { | ||||
|   public: | ||||
|     hamlib_tx_vfo_fixup (RIG * rig, vfo_t tx_vfo) | ||||
|       : rig_ {rig} | ||||
|     { | ||||
|       original_vfo_ = rig_->state.tx_vfo; | ||||
|       rig_->state.tx_vfo = tx_vfo; | ||||
|     } | ||||
| 
 | ||||
|     ~hamlib_tx_vfo_fixup () | ||||
|     { | ||||
|       rig_->state.tx_vfo = original_vfo_; | ||||
|     } | ||||
| 
 | ||||
|   private: | ||||
|     RIG * rig_; | ||||
|     vfo_t original_vfo_; | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry) | ||||
| { | ||||
|   rig_set_debug_callback (debug_callback, nullptr); | ||||
| 
 | ||||
| #if WSJT_HAMLIB_TRACE | ||||
|   rig_set_debug (RIG_DEBUG_TRACE); | ||||
| #elif defined (NDEBUG) | ||||
|   rig_set_debug (RIG_DEBUG_ERR); | ||||
| #else | ||||
|   rig_set_debug (RIG_DEBUG_VERBOSE); | ||||
| #endif | ||||
| 
 | ||||
|   rig_load_all_backends (); | ||||
|   rig_list_foreach (rigCallback, registry); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig) | ||||
| { | ||||
|   if (rig) | ||||
|     { | ||||
|       // rig->state.obj = 0;
 | ||||
|       rig_cleanup (rig); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| HamlibTransceiver::HamlibTransceiver (int model_number | ||||
|                                       , QString const& cat_port | ||||
|                                       , int cat_baud | ||||
|                                       , TransceiverFactory::DataBits cat_data_bits | ||||
|                                       , TransceiverFactory::StopBits cat_stop_bits | ||||
|                                       , TransceiverFactory::Handshake cat_handshake | ||||
|                                       , bool cat_dtr_always_on | ||||
|                                       , bool cat_rts_always_on | ||||
|                                       , TransceiverFactory::PTTMethod ptt_type | ||||
|                                       , TransceiverFactory::TXAudioSource back_ptt_port | ||||
|                                       , QString const& ptt_port | ||||
|                                       , int poll_interval) | ||||
| : PollingTransceiver {poll_interval} | ||||
| , rig_ {rig_init (model_number)} | ||||
| , back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == back_ptt_port} | ||||
| , is_dummy_ {RIG_MODEL_DUMMY == model_number} | ||||
| , reversed_ {false} | ||||
| { | ||||
|   if (!rig_) | ||||
|     { | ||||
|       throw error {"Hamlib initialisation error"}; | ||||
|     } | ||||
| 
 | ||||
|   // rig_->state.obj = this;
 | ||||
| 
 | ||||
|   if (/*!is_dummy_ &&*/ !cat_port.isEmpty () /*&& cat_port != "None"*/) | ||||
|     { | ||||
| // #if defined (WIN32)
 | ||||
| //       set_conf ("rig_pathname", ("\\\\.\\" + cat_port).toLatin1 ().data ());
 | ||||
| // #else
 | ||||
|       set_conf ("rig_pathname", cat_port.toLatin1 ().data ()); | ||||
| // #endif
 | ||||
|     } | ||||
| 
 | ||||
|   set_conf ("serial_speed", QByteArray::number (cat_baud).data ()); | ||||
|   set_conf ("data_bits", TransceiverFactory::seven_data_bits == cat_data_bits ? "7" : "8"); | ||||
|   set_conf ("stop_bits", TransceiverFactory::one_stop_bit == cat_stop_bits ? "1" : "2"); | ||||
| 
 | ||||
|   switch (cat_handshake) | ||||
|     { | ||||
|     case TransceiverFactory::handshake_none: set_conf ("serial_handshake", "None"); break; | ||||
|     case TransceiverFactory::handshake_XonXoff: set_conf ("serial_handshake", "XONXOFF"); break; | ||||
|     case TransceiverFactory::handshake_hardware: set_conf ("serial_handshake", "Hardware"); break; | ||||
|     } | ||||
| 
 | ||||
|   if (cat_dtr_always_on) | ||||
|     { | ||||
|       set_conf ("dtr_state", "ON"); | ||||
|     } | ||||
|   if (TransceiverFactory::handshake_hardware != cat_handshake && cat_rts_always_on) | ||||
|     { | ||||
|       set_conf ("rts_state", "ON"); | ||||
|     } | ||||
| 
 | ||||
|   switch (ptt_type) | ||||
|     { | ||||
|     case TransceiverFactory::PTT_method_VOX: | ||||
|       set_conf ("ptt_type", "None"); | ||||
|       break; | ||||
| 
 | ||||
|     case TransceiverFactory::PTT_method_CAT: | ||||
|       set_conf ("ptt_type", "RIG"); | ||||
|       break; | ||||
| 
 | ||||
|     case TransceiverFactory::PTT_method_DTR: | ||||
|     case TransceiverFactory::PTT_method_RTS: | ||||
|       if (!ptt_port.isEmpty () && ptt_port != "None" && ptt_port != cat_port) | ||||
|         { | ||||
| #if defined (WIN32) | ||||
|           set_conf ("ptt_pathname", ("\\\\.\\" + ptt_port).toLatin1 ().data ()); | ||||
| #else | ||||
|           set_conf ("ptt_pathname", ptt_port.toLatin1 ().data ()); | ||||
| #endif | ||||
|         } | ||||
| 
 | ||||
|       if (TransceiverFactory::PTT_method_DTR == ptt_type) | ||||
|         { | ||||
|           set_conf ("ptt_type", "DTR"); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           set_conf ("ptt_type", "RTS"); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   // Make Icom CAT split commands less glitchy
 | ||||
|   set_conf ("no_xchg", "1"); | ||||
| 
 | ||||
|   // would be nice to get events but not supported on Windows and also not on a lot of rigs
 | ||||
|   // rig_set_freq_callback (rig_.data (), &frequency_change_callback, this);
 | ||||
| } | ||||
| 
 | ||||
| HamlibTransceiver::~HamlibTransceiver () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_start () | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_start rig:" << QString::fromLatin1 (rig_->caps->mfg_name).trimmed () + ' ' | ||||
|     + QString::fromLatin1 (rig_->caps->model_name).trimmed (); | ||||
| #endif | ||||
| 
 | ||||
|   error_check (rig_open (rig_.data ())); | ||||
| 
 | ||||
|   init_rig (); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_stop () | ||||
| { | ||||
|   if (rig_) | ||||
|     { | ||||
|       rig_close (rig_.data ()); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_stop: state:" << state () << "reversed =" << reversed_; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::init_rig () | ||||
| { | ||||
|   if (!is_dummy_) | ||||
|     { | ||||
|       freq_t f1; | ||||
|       freq_t f2; | ||||
|       rmode_t m {RIG_MODE_USB}; | ||||
|       rmode_t mb; | ||||
|       pbwidth_t w {rig_passband_wide (rig_.data (), m)}; | ||||
|       pbwidth_t wb; | ||||
|       if (!rig_->caps->get_vfo) | ||||
|         { | ||||
|           // Icom have deficient CAT protocol with no way of reading which
 | ||||
|           // VFO is selected or if SPLIT is selected so we have to simply
 | ||||
|           // assume it is as when we started by setting at open time right
 | ||||
|           // here. We also gather/set other initial state.
 | ||||
|           error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f1; | ||||
| #endif | ||||
| 
 | ||||
|           error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w; | ||||
| #endif | ||||
| 
 | ||||
|           if (!rig_->caps->set_vfo) | ||||
|             { | ||||
|               if (rig_has_vfo_op (rig_.data (), RIG_OP_TOGGLE)) | ||||
|                 { | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|                   qDebug () << "HamlibTransceiver::init_rig rig_vfo_op TOGGLE"; | ||||
| #endif | ||||
| 
 | ||||
|                   error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE)); | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   throw error {"Hamlib: unable to initialise rig"}; | ||||
|                 } | ||||
|             } | ||||
|           else | ||||
|             { | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_set_vfo"; | ||||
| #endif | ||||
| 
 | ||||
|               error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)); | ||||
|             } | ||||
| 
 | ||||
|           error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f2)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f2; | ||||
| #endif | ||||
| 
 | ||||
|           error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &mb, &wb)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << mb << "bw =" << wb; | ||||
| #endif | ||||
| 
 | ||||
|           update_other_frequency (f2); | ||||
| 
 | ||||
|           if (!rig_->caps->set_vfo) | ||||
|             { | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_vfo_op TOGGLE"; | ||||
| #endif | ||||
| 
 | ||||
|               error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE)); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_set_vfo"; | ||||
| #endif | ||||
| 
 | ||||
|               error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN)); | ||||
|             } | ||||
| 
 | ||||
|           if (f1 != f2 || m != mb || w != wb)	// we must have started with MAIN/A
 | ||||
|             { | ||||
|               update_rx_frequency (f1); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_get_freq =" << f1; | ||||
| #endif | ||||
| 
 | ||||
|               error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w; | ||||
| #endif | ||||
| 
 | ||||
|               update_rx_frequency (f1); | ||||
|             } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::init_rig rig_set_split_vfo"; | ||||
| #endif | ||||
| 
 | ||||
|           // error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, RIG_SPLIT_OFF, RIG_VFO_CURR));
 | ||||
|           // update_split (false);
 | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           vfo_t v; | ||||
|           error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
 | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug ().nospace () << "HamlibTransceiver::init_rig rig_get_vfo = 0x" << hex << v; | ||||
| #endif | ||||
| 
 | ||||
|           reversed_ = RIG_VFO_B == v; | ||||
| 
 | ||||
|           if (!(rig_->caps->targetable_vfo & (RIG_TARGETABLE_MODE | RIG_TARGETABLE_PURE))) | ||||
|             { | ||||
|               error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::init_rig rig_get_mode =" << m << "bw =" << w; | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|       update_mode (map_mode (m)); | ||||
|     } | ||||
| 
 | ||||
|   poll (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::init_rig exit" << state () << "reversed =" << reversed_; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| auto HamlibTransceiver::get_vfos () const -> std::tuple<vfo_t, vfo_t> | ||||
| { | ||||
|   if (rig_->caps->get_vfo) | ||||
|     { | ||||
|       vfo_t v; | ||||
|       error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
 | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug ().nospace () << "HamlibTransceiver::get_vfos rig_get_vfo = 0x" << hex << v; | ||||
| #endif | ||||
| 
 | ||||
|       reversed_ = RIG_VFO_B == v; | ||||
|     } | ||||
|   else if (rig_->caps->set_vfo) | ||||
|     { | ||||
|       // use VFO A/MAIN for main frequency and B/SUB for Tx
 | ||||
|       // frequency if split since these type of radios can only
 | ||||
|       // support this way around
 | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HamlibTransceiver::get_vfos rig_set_vfo"; | ||||
| #endif | ||||
| 
 | ||||
|       error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN)); | ||||
|     } | ||||
|   // else only toggle available but both VFOs should be substitutable 
 | ||||
| 
 | ||||
|   auto rx_vfo = rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN; | ||||
|   auto tx_vfo = state ().split () ? (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB) : rx_vfo; | ||||
|   if (reversed_) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HamlibTransceiver::get_vfos reversing VFOs"; | ||||
| #endif | ||||
| 
 | ||||
|       std::swap (rx_vfo, tx_vfo); | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug ().nospace () << "HamlibTransceiver::get_vfos RX VFO = 0x" << hex << rx_vfo << " TX VFO = 0x" << hex << tx_vfo; | ||||
| #endif | ||||
| 
 | ||||
|   return std::make_tuple (rx_vfo, tx_vfo); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_frequency (Frequency f) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_frequency:" << f << "reversed:" << reversed_; | ||||
| #endif | ||||
| 
 | ||||
|   if (!is_dummy_) | ||||
|     { | ||||
|       error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, f)); | ||||
|     } | ||||
| 
 | ||||
|   update_rx_frequency (f); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_tx_frequency (Frequency tx, bool rationalise_mode) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_tx_frequency:" << tx << "rationalise mode:" << rationalise_mode << "reversed:" << reversed_; | ||||
| #endif | ||||
| 
 | ||||
|   if (!is_dummy_) | ||||
|     { | ||||
|       auto vfos = get_vfos (); | ||||
|       // auto rx_vfo = std::get<0> (vfos);
 | ||||
|       auto tx_vfo = std::get<1> (vfos); | ||||
| 
 | ||||
|       if (tx) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_freq"; | ||||
| #endif | ||||
| 
 | ||||
|           hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo); | ||||
|           error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx)); | ||||
| 
 | ||||
|           if (rationalise_mode) | ||||
|             { | ||||
|               rmode_t current_mode; | ||||
|               pbwidth_t current_width; | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::mode rig_get_split_mode"; | ||||
| #endif | ||||
|               auto new_mode = map_mode (state ().mode ()); | ||||
|               error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width)); | ||||
| 
 | ||||
|               if (new_mode != current_mode) | ||||
|                 { | ||||
| #if WSJT_TRACE_CAT | ||||
|                   qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_mode"; | ||||
| #endif | ||||
| 
 | ||||
|                   error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode))); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|       // enable split last since some rigs (Kenwood for one) come out
 | ||||
|       // of split when you switch RX VFO (to set split mode above for
 | ||||
|       // example)
 | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HamlibTransceiver::do_tx_frequency rig_set_split_vfo"; | ||||
| #endif | ||||
| 
 | ||||
|       error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, tx ? RIG_SPLIT_ON : RIG_SPLIT_OFF, tx_vfo)); | ||||
|     } | ||||
| 
 | ||||
|   update_split (tx); | ||||
|   update_other_frequency (tx); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_mode (MODE mode, bool rationalise) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_mode:" << mode << "rationalise:" << rationalise; | ||||
| #endif | ||||
| 
 | ||||
|   if (!is_dummy_) | ||||
|     { | ||||
|       auto vfos = get_vfos (); | ||||
|       // auto rx_vfo = std::get<0> (vfos);
 | ||||
|       auto tx_vfo = std::get<1> (vfos); | ||||
| 
 | ||||
|       rmode_t current_mode; | ||||
|       pbwidth_t current_width; | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "HamlibTransceiver::mode rig_get_mode"; | ||||
| #endif | ||||
|       error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width)); | ||||
| 
 | ||||
|       auto new_mode = map_mode (mode); | ||||
|       if (new_mode != current_mode) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::mode rig_set_mode"; | ||||
| #endif | ||||
|           error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode))); | ||||
|         } | ||||
|        | ||||
|       if (state ().split () && rationalise) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::mode rig_get_split_mode"; | ||||
| #endif | ||||
|           error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, ¤t_mode, ¤t_width)); | ||||
| 
 | ||||
|           if (new_mode != current_mode) | ||||
|             { | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "HamlibTransceiver::mode rig_set_split_mode"; | ||||
| #endif | ||||
|               hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo); | ||||
|               error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, rig_passband_wide (rig_.data (), new_mode))); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   update_mode (mode); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::poll () | ||||
| { | ||||
|   if (is_dummy_) | ||||
|     { | ||||
|       // split with dummy is never reported since there is no rig
 | ||||
|       if (state ().split ()) | ||||
|         { | ||||
|           update_split (false); | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if !WSJT_TRACE_CAT_POLLS | ||||
| #if defined (NDEBUG) | ||||
|       rig_set_debug (RIG_DEBUG_ERR); | ||||
| #else | ||||
|       rig_set_debug (RIG_DEBUG_VERBOSE); | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
|       freq_t f; | ||||
|       rmode_t m; | ||||
|       pbwidth_t w; | ||||
|       split_t s; | ||||
| 
 | ||||
|       if (rig_->caps->get_vfo) | ||||
|         { | ||||
|           vfo_t v; | ||||
|           error_check (rig_get_vfo (rig_.data (), &v)); // has side effect of establishing current VFO inside hamlib
 | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug ().nospace () << "HamlibTransceiver::state rig_get_vfo = 0x" << hex << v; | ||||
| #endif | ||||
| 
 | ||||
|           reversed_ = RIG_VFO_B == v; | ||||
|         } | ||||
| 
 | ||||
|       error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|       qDebug () << "HamlibTransceiver::state rig_get_freq =" << f; | ||||
| #endif | ||||
| 
 | ||||
|       update_rx_frequency (f); | ||||
| 
 | ||||
|       if (rig_->caps->targetable_vfo & (RIG_TARGETABLE_FREQ | RIG_TARGETABLE_PURE)) | ||||
|         { | ||||
|           // we can only probe current VFO unless rig supports reading the other one directly
 | ||||
|           error_check (rig_get_freq (rig_.data () | ||||
|                                      , reversed_ | ||||
|                                      ? (rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN) | ||||
|                                      : (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB) | ||||
|                                      , &f)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug () << "HamlibTransceiver::state rig_get_freq other =" << f; | ||||
| #endif | ||||
| 
 | ||||
|           update_other_frequency (f); | ||||
|         } | ||||
| 
 | ||||
|       error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|       qDebug () << "HamlibTransceiver::state rig_get_mode =" << m << "bw =" << w; | ||||
| #endif | ||||
| 
 | ||||
|       update_mode (map_mode (m)); | ||||
| 
 | ||||
|       vfo_t v {RIG_VFO_NONE};		// so we can tell if it doesn't get updated :(
 | ||||
|       auto rc = rig_get_split_vfo (rig_.data (), RIG_VFO_CURR, &s, &v); | ||||
|       if (RIG_OK == rc && RIG_SPLIT_ON == s) | ||||
|         { | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo split = " << s << " VFO = 0x" << hex << v; | ||||
| #endif | ||||
| 
 | ||||
|           update_split (true); | ||||
|           // if (RIG_VFO_A == v)
 | ||||
|           // 	{
 | ||||
|           // 	  reversed_ = true;	// not sure if this helps us here
 | ||||
|           // 	}
 | ||||
|         } | ||||
|       else if (RIG_OK == rc)	// not split
 | ||||
|         { | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo split = " << s << " VFO = 0x" << hex << v; | ||||
| #endif | ||||
| 
 | ||||
|           update_split (false); | ||||
|         } | ||||
|       else if (-RIG_ENAVAIL == rc) // Some rigs (Icom) don't have a way of reporting SPLIT mode
 | ||||
|         { | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug ().nospace () << "HamlibTransceiver::state rig_get_split_vfo can't do on this rig"; | ||||
| #endif | ||||
| 
 | ||||
|           // just report how we see it based on prior commands
 | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           error_check (rc); | ||||
|         } | ||||
| 
 | ||||
|       if (RIG_PTT_NONE != rig_->state.pttport.type.ptt && rig_->caps->get_ptt) | ||||
|         { | ||||
|           ptt_t p; | ||||
|           error_check (rig_get_ptt (rig_.data (), RIG_VFO_CURR, &p)); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|           qDebug () << "HamlibTransceiver::state rig_get_ptt =" << p; | ||||
| #endif | ||||
| 
 | ||||
|           update_PTT (!(RIG_PTT_OFF == p)); | ||||
|         } | ||||
| 
 | ||||
| #if !WSJT_TRACE_CAT_POLLS | ||||
| #if WSJT_HAMLIB_TRACE | ||||
|       rig_set_debug (RIG_DEBUG_TRACE); | ||||
| #elif defined (NDEBUG) | ||||
|       rig_set_debug (RIG_DEBUG_ERR); | ||||
| #else | ||||
|       rig_set_debug (RIG_DEBUG_VERBOSE); | ||||
| #endif | ||||
| #endif | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::do_ptt (bool on) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "HamlibTransceiver::do_ptt:" << on << state () << "reversed =" << reversed_; | ||||
| #endif | ||||
| 
 | ||||
|   if (on) | ||||
|     { | ||||
|       if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::ptt rig_set_ptt"; | ||||
| #endif | ||||
| 
 | ||||
|           error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, back_ptt_port_ ? RIG_PTT_ON_DATA : RIG_PTT_ON)); | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       if (RIG_PTT_NONE != rig_->state.pttport.type.ptt) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "HamlibTransceiver::ptt rig_set_ptt"; | ||||
| #endif | ||||
| 
 | ||||
|           error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, RIG_PTT_OFF)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   update_PTT (on); | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::error_check (int ret_code) const | ||||
| { | ||||
|   if (RIG_OK != ret_code) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS | ||||
|       qDebug () << "HamlibTransceiver::error_check: error:" << rigerror (ret_code); | ||||
| #endif | ||||
| 
 | ||||
|       throw error {QByteArray ("Hamlib error: ") + rigerror (ret_code)}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void HamlibTransceiver::set_conf (char const * item, char const * value) | ||||
| { | ||||
|   token_t token = rig_token_lookup (rig_.data (), item); | ||||
|   if (RIG_CONF_END != token)	// only set if valid for rig model
 | ||||
|     { | ||||
|       error_check (rig_set_conf (rig_.data (), token, value)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| QByteArray HamlibTransceiver::get_conf (char const * item) | ||||
| { | ||||
|   token_t token = rig_token_lookup (rig_.data (), item); | ||||
|   QByteArray value {128, '\0'}; | ||||
|   if (RIG_CONF_END != token)	// only get if valid for rig model
 | ||||
|     { | ||||
|       error_check (rig_get_conf (rig_.data (), token, value.data ())); | ||||
|     } | ||||
|   return value; | ||||
| } | ||||
| 
 | ||||
| auto HamlibTransceiver::map_mode (rmode_t m) const -> MODE | ||||
| { | ||||
|   switch (m) | ||||
|     { | ||||
|     case RIG_MODE_AM: | ||||
|     case RIG_MODE_SAM: | ||||
|     case RIG_MODE_AMS: | ||||
|     case RIG_MODE_DSB: | ||||
|       return AM; | ||||
| 
 | ||||
|     case RIG_MODE_CW: | ||||
|       return CW; | ||||
| 
 | ||||
|     case RIG_MODE_CWR: | ||||
|       return CW_R; | ||||
| 
 | ||||
|     case RIG_MODE_USB: | ||||
|     case RIG_MODE_ECSSUSB: | ||||
|     case RIG_MODE_SAH: | ||||
|     case RIG_MODE_FAX: | ||||
|       return USB; | ||||
| 
 | ||||
|     case RIG_MODE_LSB: | ||||
|     case RIG_MODE_ECSSLSB: | ||||
|     case RIG_MODE_SAL: | ||||
|       return LSB; | ||||
| 
 | ||||
|     case RIG_MODE_RTTY: | ||||
|       return FSK; | ||||
| 
 | ||||
|     case RIG_MODE_RTTYR: | ||||
|       return FSK_R; | ||||
| 
 | ||||
|     case RIG_MODE_PKTLSB: | ||||
|       return DIG_L; | ||||
| 
 | ||||
|     case RIG_MODE_PKTUSB: | ||||
|       return DIG_U; | ||||
| 
 | ||||
|     case RIG_MODE_FM: | ||||
|     case RIG_MODE_WFM: | ||||
|       return FM; | ||||
| 
 | ||||
|     case RIG_MODE_PKTFM: | ||||
|       return DIG_FM; | ||||
| 
 | ||||
|     default: | ||||
|       return UNK; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| rmode_t HamlibTransceiver::map_mode (MODE mode) const | ||||
| { | ||||
|   switch (mode) | ||||
|     { | ||||
|     case AM: return RIG_MODE_AM; | ||||
|     case CW: return RIG_MODE_CW; | ||||
|     case CW_R: return RIG_MODE_CWR; | ||||
|     case USB: return RIG_MODE_USB; | ||||
|     case LSB: return RIG_MODE_LSB; | ||||
|     case FSK: return RIG_MODE_RTTY; | ||||
|     case FSK_R: return RIG_MODE_RTTYR; | ||||
|     case DIG_L: return RIG_MODE_PKTLSB; | ||||
|     case DIG_U: return RIG_MODE_PKTUSB; | ||||
|     case FM: return RIG_MODE_FM; | ||||
|     case DIG_FM: return RIG_MODE_PKTFM; | ||||
|     default: break; | ||||
|     } | ||||
|   return RIG_MODE_USB;	// quieten compiler grumble
 | ||||
| } | ||||
							
								
								
									
										68
									
								
								HamlibTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,68 @@ | ||||
| #ifndef HAMLIB_TRANSCEIVER_HPP_ | ||||
| #define HAMLIB_TRANSCEIVER_HPP_ | ||||
| 
 | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include <QString> | ||||
| 
 | ||||
| #include <hamlib/rig.h> | ||||
| 
 | ||||
| #include "TransceiverFactory.hpp" | ||||
| #include "PollingTransceiver.hpp" | ||||
| 
 | ||||
| extern "C" | ||||
| { | ||||
|   typedef struct rig RIG; | ||||
|   struct rig_caps; | ||||
|   typedef int vfo_t; | ||||
| } | ||||
| 
 | ||||
| // hamlib transceiver and PTT mostly delegated directly to hamlib Rig class
 | ||||
| class HamlibTransceiver final | ||||
|   : public PollingTransceiver | ||||
| { | ||||
|  public: | ||||
|   static void register_transceivers (TransceiverFactory::Transceivers *); | ||||
| 
 | ||||
|   explicit HamlibTransceiver (int model_number | ||||
|                               , QString const& cat_port | ||||
|                               , int cat_baud | ||||
|                               , TransceiverFactory::DataBits cat_data_bits | ||||
|                               , TransceiverFactory::StopBits cat_stop_bits | ||||
|                               , TransceiverFactory::Handshake cat_handshake | ||||
|                               , bool cat_dtr_always_on | ||||
|                               , bool cat_rts_always_on | ||||
|                               , TransceiverFactory::PTTMethod ptt_type | ||||
|                               , TransceiverFactory::TXAudioSource back_ptt_port | ||||
|                               , QString const& ptt_port | ||||
|                               , int poll_interval = 0); | ||||
|   ~HamlibTransceiver (); | ||||
| 
 | ||||
|  private: | ||||
|   void do_start () override; | ||||
|   void do_stop () override; | ||||
|   void do_frequency (Frequency) override; | ||||
|   void do_tx_frequency (Frequency, bool rationalise_mode) override; | ||||
|   void do_mode (MODE, bool rationalise) override; | ||||
|   void do_ptt (bool) override; | ||||
| 
 | ||||
|   void poll () override; | ||||
| 
 | ||||
|   void error_check (int ret_code) const; | ||||
|   void set_conf (char const * item, char const * value); | ||||
|   QByteArray get_conf (char const * item); | ||||
|   Transceiver::MODE map_mode (rmode_t) const; | ||||
|   rmode_t map_mode (Transceiver::MODE mode) const; | ||||
|   void init_rig (); | ||||
|   std::tuple<vfo_t, vfo_t> get_vfos () const; | ||||
| 
 | ||||
|   struct RIGDeleter {static void cleanup (RIG *);}; | ||||
|   QScopedPointer<RIG, RIGDeleter> rig_; | ||||
| 
 | ||||
|   bool back_ptt_port_; | ||||
|   bool is_dummy_; | ||||
| 
 | ||||
|   bool mutable reversed_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										10
									
								
								Info.plist.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>CFBundleExecutable</key> | ||||
| 	<string>@WSJTX_BUNDLE_NAME@</string> | ||||
| 	<key>CFBundleDisplayName</key> | ||||
| 	<string>@WSJTX_BUNDLE_NAME@</string> | ||||
| </dict> | ||||
| </plist> | ||||
| @ -1,30 +1,30 @@ | ||||
| + + + This Software is released under the "Simplified BSD License"  + + + | ||||
| Copyright 2010 Moe Wheatley. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without  | ||||
| modification, are permitted provided that the following conditions are  | ||||
| met: | ||||
| 
 | ||||
|   1. Redistributions of source code must retain the above copyright  | ||||
|      notice, this list of conditions and the following disclaimer. | ||||
| 
 | ||||
|   2. Redistributions in binary form must reproduce the above copyright  | ||||
|      notice, this list of conditions and the following disclaimer in  | ||||
|      the documentation and/or other materials provided with the  | ||||
|      distribution. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR  | ||||
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  | ||||
| DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE  | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  | ||||
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  | ||||
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  | ||||
| BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  | ||||
| WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  | ||||
| OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  | ||||
| EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| 
 | ||||
| The views and conclusions contained in the software and documentation  | ||||
| are those of the authors and should not be interpreted as representing  | ||||
| official policies, either expressed or implied, of Moe Wheatley. | ||||
| + + + This Software is released under the "Simplified BSD License"  + + + | ||||
| Copyright 2010 Moe Wheatley. All rights reserved. | ||||
| 
 | ||||
| Redistribution and use in source and binary forms, with or without  | ||||
| modification, are permitted provided that the following conditions are  | ||||
| met: | ||||
| 
 | ||||
|   1. Redistributions of source code must retain the above copyright  | ||||
|      notice, this list of conditions and the following disclaimer. | ||||
| 
 | ||||
|   2. Redistributions in binary form must reproduce the above copyright  | ||||
|      notice, this list of conditions and the following disclaimer in  | ||||
|      the documentation and/or other materials provided with the  | ||||
|      distribution. | ||||
| 
 | ||||
| THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR  | ||||
| IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  | ||||
| DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR CONTRIBUTORS BE LIABLE  | ||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  | ||||
| CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF  | ||||
| SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  | ||||
| BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  | ||||
| WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  | ||||
| OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  | ||||
| EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| 
 | ||||
| The views and conclusions contained in the software and documentation  | ||||
| are those of the authors and should not be interpreted as representing  | ||||
| official policies, either expressed or implied, of Moe Wheatley. | ||||
							
								
								
									
										92
									
								
								LiveFrequencyValidator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,92 @@ | ||||
| #include "LiveFrequencyValidator.hpp" | ||||
| 
 | ||||
| #include <QLocale> | ||||
| #include <QString> | ||||
| #include <QComboBox> | ||||
| #include <QLineEdit> | ||||
| 
 | ||||
| #include "Bands.hpp" | ||||
| #include "FrequencyList.hpp" | ||||
| 
 | ||||
| #include "moc_LiveFrequencyValidator.cpp" | ||||
| 
 | ||||
| LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box | ||||
|                                                 , Bands const * bands | ||||
|                                                 , FrequencyList const * frequencies | ||||
|                                                 , QWidget * parent) | ||||
|   : QRegExpValidator { | ||||
|   QRegExp {				// frequency in MHz or band
 | ||||
|     bands->data (QModelIndex {}).toString () // out of band string
 | ||||
|       + QString {R"(|((\d{0,6}(\)"}		 // up to 6 digits
 | ||||
|     + QLocale {}.decimalPoint () // (followed by decimal separator
 | ||||
|                    + R"(\d{0,2})?)([Mm]{1,2}|([Cc][Mm])))|(\d{0,4}(\)" // followed by up to 2 digits and either 'm' or 'cm' or 'mm' (case insensitive))
 | ||||
|                    + QLocale {}.decimalPoint () // or a decimal separator
 | ||||
|                                   + R"(\d{0,6})?))"	       //  followed by up to 6 digits
 | ||||
|                                   } | ||||
|   , parent | ||||
|       } | ||||
|   , bands_ {bands} | ||||
|   , frequencies_ {frequencies} | ||||
|   , combo_box_ {combo_box} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| auto LiveFrequencyValidator::validate (QString& input, int& pos) const -> State | ||||
| { | ||||
|   auto state = QRegExpValidator::validate (input, pos); | ||||
| 
 | ||||
|   // by never being Acceptable we force fixup calls on ENTER or
 | ||||
|   // losing focus
 | ||||
|   return Acceptable == state ? Intermediate : state; | ||||
| } | ||||
| 
 | ||||
| void LiveFrequencyValidator::fixup (QString& input) const | ||||
| { | ||||
|   QRegExpValidator::fixup (input); | ||||
| 
 | ||||
|   auto out_of_band = bands_->data (QModelIndex {}).toString (); | ||||
| 
 | ||||
|   if (!out_of_band.startsWith (input)) | ||||
|     { | ||||
|       if (input.contains ('m', Qt::CaseInsensitive)) | ||||
| 	{ | ||||
| 	  input = input.toLower (); | ||||
| 
 | ||||
| 	  QVector<QVariant> frequencies; | ||||
| 	  for (int r = 0; r < frequencies_->rowCount (); ++r) | ||||
| 	    { | ||||
| 	      auto frequency = frequencies_->index (r, 0).data (); | ||||
| 	      auto band_index = bands_->find (frequency); | ||||
| 	      if (band_index.data ().toString () == input) | ||||
| 		{ | ||||
| 		  frequencies << frequency; | ||||
| 		} | ||||
| 	    } | ||||
| 	  if (!frequencies.isEmpty ()) | ||||
| 	    { | ||||
| 	      Q_EMIT valid (frequencies.first ().value<Frequency> ()); | ||||
| 	    } | ||||
| 	  else | ||||
| 	    { | ||||
| 	      input = QString {}; | ||||
| 	    } | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  // frequency input
 | ||||
| 	  auto f = Radio::frequency (input, 6); | ||||
| 	  input = bands_->data (bands_->find (f)).toString (); | ||||
| 	  Q_EMIT valid (f); | ||||
| 	} | ||||
| 
 | ||||
|       if (out_of_band == input) | ||||
| 	{ | ||||
| 	  combo_box_->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  combo_box_->lineEdit ()->setStyleSheet ({}); | ||||
| 	} | ||||
|       combo_box_->setCurrentText (input); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										52
									
								
								LiveFrequencyValidator.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,52 @@ | ||||
| #ifndef LIVE_FREQUENCY_VALIDATOR_HPP__ | ||||
| #define LIVE_FREQUENCY_VALIDATOR_HPP__ | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QRegExpValidator> | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| class Bands; | ||||
| class FrequencyList; | ||||
| class QComboBox; | ||||
| class QWidget; | ||||
| 
 | ||||
| //
 | ||||
| // Class LiveFrequencyValidator
 | ||||
| //
 | ||||
| //	QLineEdit validator that controls input to an editable
 | ||||
| //	QComboBox where the user can enter a valid band or a valid
 | ||||
| //	frequency in megahetz.
 | ||||
| //
 | ||||
| // Collabrations
 | ||||
| //
 | ||||
| //	Implements the QRegExpValidator interface. Validates input
 | ||||
| //	from the supplied QComboBox as either a valid frequency in
 | ||||
| //	megahertz or a valid band as defined by the supplied column of
 | ||||
| //	the supplied QAbstractItemModel.
 | ||||
| //
 | ||||
| class LiveFrequencyValidator final | ||||
|   : public QRegExpValidator | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
| 
 | ||||
|   LiveFrequencyValidator (QComboBox * combo_box // associated combo box
 | ||||
|                           , Bands const * bands // bands model
 | ||||
|                           , FrequencyList const * frequencies // working frequencies model
 | ||||
|                           , QWidget * parent = nullptr); | ||||
| 
 | ||||
|   State validate (QString& input, int& pos) const override; | ||||
|   void fixup (QString& input) const override; | ||||
| 
 | ||||
|   Q_SIGNAL void valid (Frequency) const; | ||||
| 
 | ||||
| private: | ||||
|   Bands const * bands_; | ||||
|   FrequencyList const * frequencies_; | ||||
|   QComboBox * combo_box_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										16
									
								
								Mac-wsjtx-startup.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,16 @@ | ||||
| #!/bin/sh | ||||
| WSJTX_BUNDLE="`echo "$0" | sed -e 's/\/Contents\/MacOS\/.*//'`" | ||||
| WSJTX_RESOURCES="$WSJTX_BUNDLE/Contents/Resources" | ||||
| WSJTX_TEMP="/tmp/wsjtx/$UID" | ||||
| 
 | ||||
| echo "running $0" | ||||
| echo "WSJTX_BUNDLE: $WSJTX_BUNDLE" | ||||
| 
 | ||||
| # Setup temporary runtime files | ||||
| rm -rf "$WSJTX_TEMP" | ||||
| 
 | ||||
| export "DYLD_LIBRARY_PATH=$WSJTX_RESOURCES/lib" | ||||
| export "PATH=$WSJTX_RESOURCES/bin:$PATH" | ||||
| 
 | ||||
| #export | ||||
| exec "$WSJTX_RESOURCES/bin/wsjtx" | ||||
| @ -5,7 +5,10 @@ | ||||
| #include <QDebug> | ||||
| #include "mainwindow.h" | ||||
| 
 | ||||
| extern float gran();		   // Noise generator (for tests only)
 | ||||
| #include "moc_Modulator.cpp" | ||||
| 
 | ||||
| extern float gran();		// Noise generator (for tests only)
 | ||||
| 
 | ||||
| #define RAMP_INCREMENT 64  // MUST be an integral factor of 2^16
 | ||||
| 
 | ||||
| #if defined (WSJT_SOFT_KEYING) | ||||
|  | ||||
| @ -57,7 +57,7 @@ private: | ||||
|   Q_SLOT void tune (bool newState = true) {m_tuning = newState;} | ||||
|   Q_SLOT void mute (bool newState = true) {m_muted = newState;} | ||||
|   Q_SLOT void setFrequency (unsigned newFrequency) {m_frequency = newFrequency;} | ||||
|   Q_SIGNAL void stateChanged (ModulatorState); | ||||
|   Q_SIGNAL void stateChanged (ModulatorState) const; | ||||
| 
 | ||||
| private: | ||||
|   qint16 postProcessSample (qint16 sample) const; | ||||
|  | ||||
							
								
								
									
										83
									
								
								NetworkServerLookup.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,83 @@ | ||||
| #include "NetworkServerLookup.hpp" | ||||
| 
 | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| #include <QHostInfo> | ||||
| #include <QString> | ||||
| 
 | ||||
| std::tuple<QHostAddress, quint16> | ||||
| network_server_lookup (QString const& query | ||||
| 		       , quint16 default_service_port | ||||
| 		       , QHostAddress default_host_address | ||||
| 		       , QAbstractSocket::NetworkLayerProtocol required_protocol) | ||||
| { | ||||
|   QHostAddress host_address {default_host_address}; | ||||
|   quint16 service_port {default_service_port}; | ||||
| 
 | ||||
|   QString host_name; | ||||
|   if (!query.isEmpty ()) | ||||
|     { | ||||
|       int port_colon_index {-1}; | ||||
| 
 | ||||
|       if ('[' == query[0]) | ||||
| 	{ | ||||
| 	  // assume IPv6 combined address/port syntax [<address>]:<port>
 | ||||
| 	  auto close_bracket_index = query.lastIndexOf (']'); | ||||
| 	  host_name = query.mid (1, close_bracket_index - 1); | ||||
| 	  port_colon_index = query.indexOf (':', close_bracket_index); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
| 	  port_colon_index = query.lastIndexOf (':'); | ||||
| 	  host_name = query.left (port_colon_index); | ||||
| 	} | ||||
| 
 | ||||
|       if (port_colon_index >= 0) | ||||
| 	{ | ||||
| 	  bool ok; | ||||
| 	  service_port = query.mid (port_colon_index + 1).toUShort (&ok); | ||||
| 	  if (!ok) | ||||
| 	    { | ||||
| 	      throw std::runtime_error {"network server lookup error: invalid port"}; | ||||
| 	    } | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   if (!host_name.isEmpty ()) | ||||
|     { | ||||
|       auto host_info = QHostInfo::fromName (host_name); | ||||
|       if (host_info.addresses ().isEmpty ()) | ||||
| 	{ | ||||
| 	  throw std::runtime_error {"network server lookup error: host name lookup failed"}; | ||||
| 	} | ||||
| 
 | ||||
|       bool found {false}; | ||||
|       for (int i {0}; i < host_info.addresses ().size () && !found; ++i) | ||||
| 	{ | ||||
| 	  host_address = host_info.addresses ().at (i); | ||||
| 	  switch (required_protocol) | ||||
| 	    { | ||||
| 	    case QAbstractSocket::IPv4Protocol: | ||||
| 	    case QAbstractSocket::IPv6Protocol: | ||||
| 	      if (required_protocol != host_address.protocol ()) | ||||
| 		{ | ||||
| 		  break; | ||||
| 		} | ||||
| 	      // drop through
 | ||||
| 
 | ||||
| 	    case QAbstractSocket::AnyIPProtocol: | ||||
| 	      found = true; | ||||
| 	      break; | ||||
| 
 | ||||
| 	    default: | ||||
| 	      throw std::runtime_error {"network server lookup error: invalid required protocol"}; | ||||
| 	    } | ||||
| 	} | ||||
|       if (!found) | ||||
| 	{ | ||||
| 	  throw std::runtime_error {"network server lookup error: no suitable host address found"}; | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   return std::make_tuple (host_address, service_port); | ||||
| } | ||||
							
								
								
									
										38
									
								
								NetworkServerLookup.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,38 @@ | ||||
| #ifndef NETWORK_SERVER_LOOKUP_HPP__ | ||||
| #define NETWORK_SERVER_LOOKUP_HPP__ | ||||
| 
 | ||||
| #include <tuple> | ||||
| 
 | ||||
| #include <QHostAddress> | ||||
| #include <QAbstractSocket> | ||||
| 
 | ||||
| class QString; | ||||
| 
 | ||||
| //
 | ||||
| // Do a blocking DNS lookup using query as a destination host address
 | ||||
| // and port.
 | ||||
| //
 | ||||
| // query can be one of:
 | ||||
| //
 | ||||
| // 1) "" (empty string) - use defaults
 | ||||
| // 2) ":nnnnn" - override default service port with port nnnnn
 | ||||
| // 3) "<valid-host-name>" - override default host address with DNS lookup
 | ||||
| // 4) "nnn.nnn.nnn.nnn" - override default host address with the IPv4 address given by nnn.nnn.nnn.nnn
 | ||||
| // 5) "[<valid-IPv6-address]" - override default host address with the given IPv6 address
 | ||||
| // 6) "<valid-host-name>:nnnnn" - use as per (3) & (2)
 | ||||
| // 7) "nnn.nnn.nnn.nnn:nnnnn" - use as per (4) & (2)
 | ||||
| // 8) "[<valid-IPv6-address]:nnnnn" - use as per (5) & (2)
 | ||||
| //
 | ||||
| // The first host address matching the protocol and the service port
 | ||||
| // number are returned.
 | ||||
| //
 | ||||
| // If no suitable host address is found QHostAddress::Null will be
 | ||||
| // returned in the first member of the result tuple.
 | ||||
| //
 | ||||
| std::tuple<QHostAddress, quint16> | ||||
| network_server_lookup (QString const& query | ||||
| 		       , quint16 default_service_port | ||||
| 		       , QHostAddress default_host_address = QHostAddress::LocalHost | ||||
| 		       , QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										741
									
								
								OmniRigTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,741 @@ | ||||
| #include "OmniRigTransceiver.hpp" | ||||
| 
 | ||||
| #include <QTimer> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include <objbase.h> | ||||
| 
 | ||||
| #include "moc_OmniRigTransceiver.cpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   auto constexpr OmniRig_transceiver_one_name = "OmniRig Rig 1"; | ||||
|   auto constexpr OmniRig_transceiver_two_name = "OmniRig Rig 2"; | ||||
| } | ||||
| 
 | ||||
| auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE | ||||
| { | ||||
|   if (param & OmniRig::PM_CW_U) | ||||
|     { | ||||
|       return CW_R; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_CW_L) | ||||
|     { | ||||
|       return CW; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_SSB_U) | ||||
|     { | ||||
|       return USB; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_SSB_L) | ||||
|     { | ||||
|       return LSB; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_DIG_U) | ||||
|     { | ||||
|       return DIG_U; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_DIG_L) | ||||
|     { | ||||
|       return DIG_L; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_AM) | ||||
|     { | ||||
|       return AM; | ||||
|     } | ||||
|   else if (param & OmniRig::PM_FM) | ||||
|     { | ||||
|       return FM; | ||||
|     } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRig map_mode unrecognized mode"; | ||||
| #endif | ||||
| 
 | ||||
|   throw error {"OmniRig: unrecognized mode"}; | ||||
| } | ||||
| 
 | ||||
| OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode) | ||||
| { | ||||
|   switch (mode) | ||||
|     { | ||||
|     case AM: return OmniRig::PM_AM; | ||||
|     case CW: return OmniRig::PM_CW_L; | ||||
|     case CW_R: return OmniRig::PM_CW_U; | ||||
|     case USB: return OmniRig::PM_SSB_U; | ||||
|     case LSB: return OmniRig::PM_SSB_L; | ||||
|     case FSK: return OmniRig::PM_DIG_L; | ||||
|     case FSK_R: return OmniRig::PM_DIG_U; | ||||
|     case DIG_L: return OmniRig::PM_DIG_L; | ||||
|     case DIG_U: return OmniRig::PM_DIG_U; | ||||
|     case FM: return OmniRig::PM_FM; | ||||
|     case DIG_FM: return OmniRig::PM_FM; | ||||
|     default: break; | ||||
|     } | ||||
|   return OmniRig::PM_SSB_U;	// quieten compiler grumble
 | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2) | ||||
| { | ||||
|   (*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities { | ||||
|     id1 | ||||
|     , TransceiverFactory::Capabilities::none // COM isn't serial or network
 | ||||
|     , true				     // does PTT
 | ||||
|     , false				     // doesn't select mic/data (use OmniRig config file)
 | ||||
|     , true				     // can remote control RTS nd DTR
 | ||||
|     , true				     // asynchronous interface
 | ||||
|   }; | ||||
|   (*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities { | ||||
|     id2 | ||||
|     , TransceiverFactory::Capabilities::none // COM isn't serial or network
 | ||||
|     , true				     // does PTT
 | ||||
|     , false				     // doesn't select mic/data (use OmniRig config file)
 | ||||
|     , true				     // can remote control RTS nd DTR
 | ||||
|     , true				     // asynchronous interface
 | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber n, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port) | ||||
|   : wrapped_ {std::move (wrapped)} | ||||
|   , use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))} | ||||
|   , ptt_type_ {ptt_type} | ||||
|   , startup_poll_countdown_ {2} | ||||
|   , rig_number_ {n} | ||||
|   , readable_params_ {0} | ||||
|   , writable_params_ {0} | ||||
|   , send_update_signal_ {false} | ||||
|   , reversed_ {false} | ||||
|   , starting_ {false} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| OmniRigTransceiver::~OmniRigTransceiver () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_start () | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_start"; | ||||
| #endif | ||||
| 
 | ||||
|   wrapped_->start (); | ||||
| 
 | ||||
|   CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
 | ||||
| 
 | ||||
|   omni_rig_.reset (new OmniRig::OmniRigX {this}); | ||||
|   if (omni_rig_->isNull ()) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::do_start: failed to start COM server"; | ||||
| #endif | ||||
| 
 | ||||
|       throw error {"Failed to start OmniRig COM server"}; | ||||
|     } | ||||
| 
 | ||||
|   // COM/OLE exceptions get signalled
 | ||||
|   connect (&*omni_rig_, SIGNAL (exception (int, QString, QString, QString)), this, SLOT (handle_COM_exception (int, QString, QString, QString))); | ||||
| 
 | ||||
|   // IOmniRigXEvent interface signals
 | ||||
|   connect (&*omni_rig_, SIGNAL (VisibleChange ()), this, SLOT (handle_visible_change ())); | ||||
|   connect (&*omni_rig_, SIGNAL (RigTypeChange (int)), this, SLOT (handle_rig_type_change (int))); | ||||
|   connect (&*omni_rig_, SIGNAL (StatusChange (int)), this, SLOT (handle_status_change (int))); | ||||
|   connect (&*omni_rig_, SIGNAL (ParamsChange (int, int)), this, SLOT (handle_params_change (int, int))); | ||||
|   connect (&*omni_rig_ | ||||
|            , SIGNAL (CustomReply (int, QVariant const&, QVariant const&)) | ||||
|            , this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&))); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () | ||||
|     << "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit () | ||||
|     << "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit () | ||||
|     ; | ||||
| #endif | ||||
| 
 | ||||
|   // fetch the interface of the RigX CoClass and instantiate a proxy object
 | ||||
|   switch (rig_number_) | ||||
|     { | ||||
|     case One: rig_.reset (new OmniRig::RigX (omni_rig_->Rig1 ())); break; | ||||
|     case Two: rig_.reset (new OmniRig::RigX (omni_rig_->Rig2 ())); break; | ||||
|     } | ||||
| 
 | ||||
|   Q_ASSERT (rig_); | ||||
|   Q_ASSERT (!rig_->isNull ()); | ||||
| 
 | ||||
|   if (use_for_ptt_ && (TransceiverFactory::PTT_method_DTR == ptt_type_ || TransceiverFactory::PTT_method_RTS == ptt_type_)) | ||||
|     { | ||||
|       // fetch the interface for the serial port if we need it for PTT
 | ||||
|       port_.reset (new OmniRig::PortBits (rig_->PortBits ())); | ||||
| 
 | ||||
|       Q_ASSERT (port_); | ||||
|       Q_ASSERT (!port_->isNull ()); | ||||
| 
 | ||||
|       // if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
 | ||||
|       // 	{
 | ||||
|       // 	  throw error {("Failed to get exclusive use of " + ptt_type + " from OmniRig").toLocal8Bit ()};
 | ||||
|       // 	}
 | ||||
| 
 | ||||
|       // start off so we don't accidentally key the radio
 | ||||
|       if (TransceiverFactory::PTT_method_DTR == ptt_type_) | ||||
|         { | ||||
|           port_->SetDtr (false); | ||||
|         } | ||||
|       else			// RTS
 | ||||
|         { | ||||
|           port_->SetRts (false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   readable_params_ = rig_->ReadableParams (); | ||||
|   writable_params_ = rig_->WriteableParams (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () | ||||
|     << QString ("OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4") | ||||
|     .arg (rig_->RigType ()) | ||||
|     .arg (readable_params_, 8, 16, QChar ('0')) | ||||
|     .arg (writable_params_, 8, 16, QChar ('0')) | ||||
|     .arg (rig_number_).toLocal8Bit () | ||||
|     ; | ||||
| #endif | ||||
| 
 | ||||
|   starting_ = true; | ||||
|   QTimer::singleShot (5000, this, SLOT (startup_check ())); | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_stop () | ||||
| { | ||||
|   if (port_) | ||||
|     { | ||||
|       // port_->Unlock ();		// release serial port
 | ||||
|       port_->clear (); | ||||
|     } | ||||
| 
 | ||||
|   rig_->clear (); | ||||
| 
 | ||||
|   omni_rig_->clear (); | ||||
| 
 | ||||
|   CoUninitialize (); | ||||
| 
 | ||||
|   wrapped_->stop (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_stop"; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::startup_check () | ||||
| { | ||||
|   if (starting_) | ||||
|     { | ||||
|       if (--startup_poll_countdown_) | ||||
|         { | ||||
|           init_rig (); | ||||
|           QTimer::singleShot (5000, this, SLOT (startup_check ())); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           startup_poll_countdown_ = 2; | ||||
| 
 | ||||
|           // signal that we haven't seen anything from OmniRig
 | ||||
|           offline ("OmniRig initialisation timeout"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::init_rig () | ||||
| { | ||||
|   if (writable_params_ & OmniRig::PM_VFOA) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::init_rig: set VFO A"; | ||||
| #endif | ||||
| 
 | ||||
|       rig_->SetVfo (OmniRig::PM_VFOA); | ||||
|       if (writable_params_ & OmniRig::PM_SPLITOFF) | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "OmniRigTransceiver::init_rig: set SPLIT off"; | ||||
| #endif | ||||
| 
 | ||||
|           rig_->SetSplit (OmniRig::PM_SPLITOFF); | ||||
|         } | ||||
|     } | ||||
|   else if (writable_params_ & OmniRig::PM_VFOAA) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::init_rig: set VFO A and SPLIT off"; | ||||
| #endif | ||||
| 
 | ||||
|       rig_->SetVfo (OmniRig::PM_VFOAA); | ||||
|     } | ||||
| 
 | ||||
|   reversed_ = false; | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_sync (bool force_signal) | ||||
| { | ||||
|   // nothing much we can do here, we just have to let OmniRig do its
 | ||||
|   // stuff and its first poll should send us and update that will
 | ||||
|   // trigger a update signal from us. Any attempt to query OmniRig
 | ||||
|   // leads to a whole mess of trouble since its internal state is
 | ||||
|   // garbage until it has done its first rig poll.
 | ||||
|   send_update_signal_ = force_signal; | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::handle_COM_exception:" << QString::number (code) + " at " + source + ": " + desc + " (" + help + ')'; | ||||
| #endif | ||||
| 
 | ||||
|   throw error {("OmniRig COM/OLE error: " + QString::number (code) + " at " + source + ": " + desc + " (" + help + ')').toLocal8Bit ()}; | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_visible_change () | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRig visibility change: visibility =" << omni_rig_->DialogVisible (); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_rig_type_change (int rig_number) | ||||
| { | ||||
|   if (rig_number_ == rig_number) | ||||
|     { | ||||
|       readable_params_ = rig_->ReadableParams (); | ||||
|       writable_params_ = rig_->WriteableParams (); | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () | ||||
|         << QString ("OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4") | ||||
|         .arg (rig_->RigType ()) | ||||
|         .arg (readable_params_, 8, 16, QChar ('0')) | ||||
|         .arg (writable_params_, 8, 16, QChar ('0')) | ||||
|         .arg (rig_number).toLocal8Bit () | ||||
|         ; | ||||
| #endif | ||||
| 
 | ||||
|       offline ("OmniRig rig changed"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_status_change (int rig_number) | ||||
| { | ||||
|   if (rig_number_ == rig_number) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () | ||||
|         << QString ("OmniRig status change: new status for rig %1 =").arg (rig_number).toLocal8Bit () << rig_->StatusStr ().toLocal8Bit (); | ||||
| #endif | ||||
| 
 | ||||
|       if (OmniRig::ST_ONLINE != rig_->Status ()) | ||||
|         { | ||||
|           offline ("OmniRig rig went offline"); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           starting_ = false; | ||||
| 
 | ||||
|           TransceiverState old_state {state ()}; | ||||
|           init_rig (); | ||||
| 
 | ||||
|           if (old_state != state () || send_update_signal_) | ||||
|             { | ||||
|               update_complete (); | ||||
|               send_update_signal_ = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_params_change (int rig_number, int params) | ||||
| { | ||||
|   if (rig_number_ == rig_number) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () | ||||
|         << QString ("OmniRig params change: params = 0x%1 for rig %2") | ||||
|         .arg (params, 8, 16, QChar ('0')) | ||||
|         .arg (rig_number).toLocal8Bit () | ||||
|         << "state before:" << state () | ||||
|         ; | ||||
| #endif | ||||
| 
 | ||||
|       starting_ = false; | ||||
| 
 | ||||
|       TransceiverState old_state {state ()}; | ||||
|       auto need_frequency = false; | ||||
| 
 | ||||
|       // state_.online = true;	// sometimes we don't get an initial
 | ||||
|       // 				// OmniRig::ST_ONLINE status change
 | ||||
|       // 				// event
 | ||||
| 
 | ||||
|       if (params & OmniRig::PM_VFOAA) | ||||
|         { | ||||
|           update_split (false); | ||||
|           reversed_ = false; | ||||
|           update_rx_frequency (rig_->FreqA ()); | ||||
|           update_other_frequency (rig_->FreqB ()); | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOAB) | ||||
|         { | ||||
|           update_split (true); | ||||
|           reversed_ = false; | ||||
|           update_rx_frequency (rig_->FreqA ()); | ||||
|           update_other_frequency (rig_->FreqB ()); | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOBA) | ||||
|         { | ||||
|           update_split (true); | ||||
|           reversed_ = true; | ||||
|           update_other_frequency (rig_->FreqA ()); | ||||
|           update_rx_frequency (rig_->FreqB ()); | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOBB) | ||||
|         { | ||||
|           update_split (false); | ||||
|           reversed_ = true; | ||||
|           update_other_frequency (rig_->FreqA ()); | ||||
|           update_rx_frequency (rig_->FreqB ()); | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOA) | ||||
|         { | ||||
|           reversed_ = false; | ||||
|           need_frequency = true; | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOB) | ||||
|         { | ||||
|           reversed_ = true; | ||||
|           need_frequency = true; | ||||
|         } | ||||
| 
 | ||||
|       if (params & OmniRig::PM_FREQ) | ||||
|         { | ||||
|           need_frequency = true; | ||||
|         } | ||||
|       if (params & OmniRig::PM_FREQA) | ||||
|         { | ||||
|           if (reversed_) | ||||
|             { | ||||
|               update_other_frequency (rig_->FreqA ()); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               update_rx_frequency (rig_->FreqA ()); | ||||
|             } | ||||
|         } | ||||
|       if (params & OmniRig::PM_FREQB) | ||||
|         { | ||||
|           if (reversed_) | ||||
|             { | ||||
|               update_rx_frequency (rig_->FreqB ()); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               update_other_frequency (rig_->FreqB ()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|       if (need_frequency) | ||||
|         { | ||||
|           if (readable_params_ & OmniRig::PM_FREQA) | ||||
|             { | ||||
|               if (reversed_) | ||||
|                 { | ||||
|                   update_other_frequency (rig_->FreqA ()); | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   update_rx_frequency (rig_->FreqA ()); | ||||
|                 } | ||||
|               need_frequency = false; | ||||
|             } | ||||
|           if (readable_params_ & OmniRig::PM_FREQB) | ||||
|             { | ||||
|               if (reversed_) | ||||
|                 { | ||||
|                   update_rx_frequency (rig_->FreqB ()); | ||||
|                 } | ||||
|               else | ||||
|                 { | ||||
|                   update_other_frequency (rig_->FreqB ()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|       if (need_frequency && (readable_params_ & OmniRig::PM_FREQ)) | ||||
|         { | ||||
|           update_rx_frequency (rig_->Freq ()); | ||||
|         } | ||||
| 
 | ||||
|       if (params & OmniRig::PM_PITCH) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_RITOFFSET) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_RIT0) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOEQUAL) | ||||
|         { | ||||
|           auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq (); | ||||
|           update_rx_frequency (f); | ||||
|           update_other_frequency (f); | ||||
|           update_mode (map_mode (rig_->Mode ())); | ||||
|         } | ||||
|       if (params & OmniRig::PM_VFOSWAP) | ||||
|         { | ||||
|           auto temp = state ().tx_frequency (); | ||||
|           update_other_frequency (state ().frequency ()); | ||||
|           update_rx_frequency (temp); | ||||
|           update_mode (map_mode (rig_->Mode ())); | ||||
|         } | ||||
|       if (params & OmniRig::PM_SPLITON) | ||||
|         { | ||||
|           update_split (true); | ||||
|         } | ||||
|       if (params & OmniRig::PM_SPLITOFF) | ||||
|         { | ||||
|           update_split (false); | ||||
|         } | ||||
|       if (params & OmniRig::PM_RITON) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_RITOFF) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_XITON) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_XITOFF) | ||||
|         { | ||||
|         } | ||||
|       if (params & OmniRig::PM_RX) | ||||
|         { | ||||
|           update_PTT (false); | ||||
|         } | ||||
|       if (params & OmniRig::PM_TX) | ||||
|         { | ||||
|           update_PTT (); | ||||
|         } | ||||
|       if (params & OmniRig::PM_CW_U) | ||||
|         { | ||||
|           update_mode (CW_R); | ||||
|         } | ||||
|       if (params & OmniRig::PM_CW_L) | ||||
|         { | ||||
|           update_mode (CW); | ||||
|         } | ||||
|       if (params & OmniRig::PM_SSB_U) | ||||
|         { | ||||
|           update_mode (USB); | ||||
|         } | ||||
|       if (params & OmniRig::PM_SSB_L) | ||||
|         { | ||||
|           update_mode (LSB); | ||||
|         } | ||||
|       if (params & OmniRig::PM_DIG_U) | ||||
|         { | ||||
|           update_mode (DIG_U); | ||||
|         } | ||||
|       if (params & OmniRig::PM_DIG_L) | ||||
|         { | ||||
|           update_mode (DIG_L); | ||||
|         } | ||||
|       if (params & OmniRig::PM_AM) | ||||
|         { | ||||
|           update_mode (AM); | ||||
|         } | ||||
|       if (params & OmniRig::PM_FM) | ||||
|         { | ||||
|           update_mode (FM); | ||||
|         } | ||||
| 
 | ||||
|       if (old_state != state () || send_update_signal_) | ||||
|         { | ||||
|           update_complete (); | ||||
|           send_update_signal_ = false; | ||||
|         } | ||||
| 
 | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () | ||||
|         << "OmniRig params change: state after:" << state () | ||||
|         ; | ||||
| #endif | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply) | ||||
| { | ||||
|   (void)command; | ||||
|   (void)reply; | ||||
| 
 | ||||
|   if (rig_number_ == rig_number) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () | ||||
|         << "OmniRig custom command" << command.toString ().toLocal8Bit () | ||||
|         << "with reply" << reply.toString ().toLocal8Bit () | ||||
|         << QString ("for rig %1").arg (rig_number).toLocal8Bit () | ||||
|         ; | ||||
| 
 | ||||
|       qDebug () << "OmniRig rig number:" << rig_number_ << ':' << state (); | ||||
| #endif | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_ptt (bool on) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_ptt:" << on << state (); | ||||
| #endif | ||||
| 
 | ||||
|   if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::do_ptt: set PTT"; | ||||
| #endif | ||||
| 
 | ||||
|       rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       if (port_) | ||||
|         { | ||||
|           if (TransceiverFactory::PTT_method_RTS == ptt_type_) | ||||
|             { | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "OmniRigTransceiver::do_ptt: set RTS"; | ||||
| #endif | ||||
|               port_->SetRts (on); | ||||
|             } | ||||
|           else			// "DTR"
 | ||||
|             { | ||||
| #if WSJT_TRACE_CAT | ||||
|               qDebug () << "OmniRigTransceiver::do_ptt: set DTR"; | ||||
| #endif | ||||
| 
 | ||||
|               port_->SetDtr (on); | ||||
|             } | ||||
|         } | ||||
|       else | ||||
|         { | ||||
| #if WSJT_TRACE_CAT | ||||
|           qDebug () << "OmniRigTransceiver::do_ptt: set PTT using basic transceiver"; | ||||
| #endif | ||||
| 
 | ||||
|           wrapped_->ptt (on); | ||||
|         } | ||||
| 
 | ||||
|       if (state ().ptt () != on) | ||||
|         { | ||||
|           update_PTT (on); | ||||
|           update_complete (); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_frequency (Frequency f) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_frequency:" << f << state (); | ||||
| #endif | ||||
| 
 | ||||
|   if (OmniRig::PM_FREQ & writable_params_) | ||||
|     { | ||||
|       rig_->SetFreq (f); | ||||
|       update_rx_frequency (f); | ||||
|     } | ||||
|   else if (reversed_ && (OmniRig::PM_FREQB & writable_params_)) | ||||
|     { | ||||
|       rig_->SetFreqB (f); | ||||
|       update_rx_frequency (f); | ||||
|     } | ||||
|   else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_)) | ||||
|     { | ||||
|       rig_->SetFreqA (f); | ||||
|       update_rx_frequency (f); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       throw error {"OmniRig: don't know how to set rig frequency"}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_tx_frequency (Frequency tx, bool /* rationalise_mode */) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_tx_frequency:" << tx << state (); | ||||
| #endif | ||||
| 
 | ||||
|   bool split {tx != 0}; | ||||
| 
 | ||||
|   if (split) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::do_tx_frequency: set SPLIT mode on"; | ||||
| #endif | ||||
| 
 | ||||
|       rig_->SetSplitMode (state ().frequency (), tx); | ||||
|       update_other_frequency (tx); | ||||
|       update_split (true); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::do_tx_frequency: set SPLIT mode off"; | ||||
| #endif | ||||
| 
 | ||||
|       rig_->SetSimplexMode (state ().frequency ()); | ||||
|       update_split (false); | ||||
|     } | ||||
| 
 | ||||
|   bool notify {false}; | ||||
| 
 | ||||
|   if (readable_params_ & OmniRig::PM_FREQ || !(readable_params_ & (OmniRig::PM_FREQA | OmniRig::PM_FREQB))) | ||||
|     { | ||||
|       update_other_frequency (tx); // async updates won't return this
 | ||||
|       // so just store it and hope
 | ||||
|       // operator doesn't change the
 | ||||
|       // "back" VFO on rig
 | ||||
|       notify = true; | ||||
|     } | ||||
| 
 | ||||
|   if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_)) | ||||
|     { | ||||
| #if WSJT_TRACE_CAT | ||||
|       qDebug () << "OmniRigTransceiver::do_tx_frequency: setting SPLIT manually"; | ||||
| #endif | ||||
| 
 | ||||
|       update_split (split);	// we can't read it so just set and
 | ||||
|       // hope op doesn't change it
 | ||||
|       notify = true; | ||||
|     } | ||||
| 
 | ||||
|   if (notify) | ||||
|     { | ||||
|       update_complete (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OmniRigTransceiver::do_mode (MODE mode, bool /* rationalise */) | ||||
| { | ||||
| #if WSJT_TRACE_CAT | ||||
|   qDebug () << "OmniRigTransceiver::do_mode:" << mode << state (); | ||||
| #endif | ||||
| 
 | ||||
|   // TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
 | ||||
| 
 | ||||
|   auto mapped = map_mode (mode); | ||||
| 
 | ||||
|   if (mapped & writable_params_) | ||||
|     { | ||||
|       rig_->SetMode (mapped); | ||||
|       update_mode (mode); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       offline ("OmniRig invalid mode"); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										73
									
								
								OmniRigTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,73 @@ | ||||
| #ifndef OMNI_RIG_TRANSCEIVER_HPP__ | ||||
| #define OMNI_RIG_TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QScopedPointer> | ||||
| #include <QString> | ||||
| 
 | ||||
| #include "TransceiverFactory.hpp" | ||||
| #include "TransceiverBase.hpp" | ||||
| 
 | ||||
| #include "OmniRig.h" | ||||
| 
 | ||||
| //
 | ||||
| // OmniRig Transceiver Interface
 | ||||
| //
 | ||||
| // Implemented as a Transceiver decorator  because we may want the PTT
 | ||||
| // services of another Transceiver  type such as the HamlibTransceiver
 | ||||
| // which can  be enabled by wrapping  a HamlibTransceiver instantiated
 | ||||
| // as a "Hamlib Dummy" transceiver in the Transceiver factory method.
 | ||||
| //
 | ||||
| class OmniRigTransceiver final | ||||
|   : public TransceiverBase | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| public: | ||||
|   static void register_transceivers (TransceiverFactory::Transceivers *, int id1, int id2); | ||||
| 
 | ||||
|   enum RigNumber {One = 1, Two}; | ||||
| 
 | ||||
|   // takes ownership of wrapped Transceiver
 | ||||
|   explicit OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port); | ||||
|   ~OmniRigTransceiver (); | ||||
| 
 | ||||
|   void do_start () override; | ||||
|   void do_stop () override; | ||||
|   void do_frequency (Frequency) override; | ||||
|   void do_tx_frequency (Frequency, bool rationalise_mode) override; | ||||
|   void do_mode (MODE, bool rationalise) override; | ||||
|   void do_ptt (bool on) override; | ||||
|   void do_sync (bool force_signal) override; | ||||
| 
 | ||||
| private: | ||||
|   Q_SLOT void startup_check (); | ||||
|   Q_SLOT void handle_COM_exception (int,  QString, QString, QString); | ||||
|   Q_SLOT void handle_visible_change (); | ||||
|   Q_SLOT void handle_rig_type_change (int rig_number); | ||||
|   Q_SLOT void handle_status_change (int rig_number); | ||||
|   Q_SLOT void handle_params_change (int rig_number, int params); | ||||
|   Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply); | ||||
| 
 | ||||
|   void init_rig (); | ||||
| 
 | ||||
|   static MODE map_mode (OmniRig::RigParamX param); | ||||
|   static OmniRig::RigParamX map_mode (MODE mode); | ||||
| 
 | ||||
|   std::unique_ptr<TransceiverBase> wrapped_; | ||||
|   bool use_for_ptt_; | ||||
|   TransceiverFactory::PTTMethod ptt_type_; | ||||
|   unsigned startup_poll_countdown_; | ||||
|   QScopedPointer<OmniRig::OmniRigX> omni_rig_; | ||||
|   RigNumber rig_number_; | ||||
|   QScopedPointer<OmniRig::RigX> rig_; | ||||
|   QScopedPointer<OmniRig::PortBits> port_; | ||||
|   int readable_params_; | ||||
|   int writable_params_; | ||||
|   bool send_update_signal_; | ||||
|   bool reversed_;   // some rigs can reverse VFOs
 | ||||
|   bool starting_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										209
									
								
								PollingTransceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,209 @@ | ||||
| #include "PollingTransceiver.hpp" | ||||
| 
 | ||||
| #include <exception> | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QString> | ||||
| #include <QTimer> | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   unsigned const polls_to_stabilize {3}; | ||||
| } | ||||
| 
 | ||||
| // Internal implementation of the PollingTransceiver class.
 | ||||
| class PollingTransceiver::impl final | ||||
|   : public QObject | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| private: | ||||
|   Q_DISABLE_COPY (impl); | ||||
| 
 | ||||
| public: | ||||
|   impl (PollingTransceiver * self, int poll_interval) | ||||
|     : QObject {self} | ||||
|     , self_ {self} | ||||
|     , interval_ {poll_interval} | ||||
|     , poll_timer_ {nullptr} | ||||
|     , retries_ {0} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   void start_timer () | ||||
|   { | ||||
|     if (interval_) | ||||
|       { | ||||
|         if (!poll_timer_) | ||||
|           { | ||||
|             poll_timer_ = new QTimer {this}; // pass ownership to QObject which handles destruction for us
 | ||||
| 
 | ||||
|             connect (poll_timer_, &QTimer::timeout, this, &PollingTransceiver::impl::handle_timeout); | ||||
|           } | ||||
|         poll_timer_->start (interval_); | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   void stop_timer () | ||||
|   { | ||||
|     if (poll_timer_) | ||||
|       { | ||||
|         poll_timer_->stop (); | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   Q_SLOT void handle_timeout (); | ||||
| 
 | ||||
|   PollingTransceiver * self_; // our owner so we can call methods
 | ||||
|   int interval_;    // polling interval in milliseconds
 | ||||
|   QTimer * poll_timer_; | ||||
| 
 | ||||
|   // keep a record of the last state signalled so we can elide
 | ||||
|   // duplicate updates
 | ||||
|   Transceiver::TransceiverState last_signalled_state_; | ||||
| 
 | ||||
|   // keep a record of expected state so we can compare with actual
 | ||||
|   // updates to determine when state changes have bubbled through
 | ||||
|   Transceiver::TransceiverState next_state_; | ||||
| 
 | ||||
|   unsigned retries_;            // number of incorrect polls left
 | ||||
| 
 | ||||
|   friend class PollingTransceiver; | ||||
| }; | ||||
| 
 | ||||
| #include "PollingTransceiver.moc" | ||||
| 
 | ||||
| 
 | ||||
| PollingTransceiver::PollingTransceiver (int poll_interval) | ||||
|   : m_ {this, poll_interval} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| PollingTransceiver::~PollingTransceiver () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_post_start () | ||||
| { | ||||
|   m_->start_timer (); | ||||
|   if (!m_->next_state_.online ()) | ||||
|     { | ||||
|       // remember that we are expecting to go online
 | ||||
|       m_->next_state_.online (true); | ||||
|       m_->retries_ = polls_to_stabilize; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_post_stop () | ||||
| { | ||||
|   // not much point waiting for rig to go offline since we are ceasing
 | ||||
|   // polls
 | ||||
|   m_->stop_timer (); | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_post_frequency (Frequency f) | ||||
| { | ||||
|   if (m_->next_state_.frequency () != f) | ||||
|     { | ||||
|       // update expected state with new frequency and set poll count
 | ||||
|       m_->next_state_.frequency (f); | ||||
|       m_->retries_ = polls_to_stabilize; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_post_tx_frequency (Frequency f) | ||||
| { | ||||
|   if (m_->next_state_.tx_frequency () != f) | ||||
|     { | ||||
|       // update expected state with new TX frequency and set poll
 | ||||
|       // count
 | ||||
|       m_->next_state_.tx_frequency (f); | ||||
|       m_->next_state_.split (f); // setting non-zero TX frequency means split
 | ||||
|       m_->retries_ = polls_to_stabilize; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_post_mode (MODE m) | ||||
| { | ||||
|   if (m_->next_state_.mode () != m) | ||||
|     { | ||||
|       // update expected state with new mode and set poll count
 | ||||
|       m_->next_state_.mode (m); | ||||
|       m_->retries_ = polls_to_stabilize; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool PollingTransceiver::do_pre_update () | ||||
| { | ||||
|   // if we are holding off a change then withhold the signal
 | ||||
|   if (m_->retries_ && state () != m_->next_state_) | ||||
|     { | ||||
|       return false; | ||||
|     } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::do_sync (bool force_signal) | ||||
| { | ||||
|   poll ();                      // tell sub-classes to update our
 | ||||
|                                 // state
 | ||||
| 
 | ||||
|   // Signal new state if it is directly requested or, what we expected
 | ||||
|   // or, hasn't become what we expected after polls_to_stabilize
 | ||||
|   // polls. Unsolicited changes will be signalled immediately unless
 | ||||
|   // they intervene in a expected sequence where they will be delayed.
 | ||||
|   if (m_->retries_) | ||||
|     { | ||||
|       --m_->retries_; | ||||
|       if (force_signal || state () == m_->next_state_ || !m_->retries_) | ||||
|         { | ||||
|           // our client wants a signal regardless
 | ||||
|           // or the expected state has arrived
 | ||||
|           // or there are no more retries
 | ||||
|           force_signal = true; | ||||
|         } | ||||
|     } | ||||
|   else if (force_signal || state () != m_->last_signalled_state_) | ||||
|     { | ||||
|       // here is the normal passive polling path
 | ||||
|       // either our client has requested a state update regardless of change
 | ||||
|       // or sate has changed asynchronously
 | ||||
|       force_signal = true; | ||||
|     } | ||||
| 
 | ||||
|   if (force_signal) | ||||
|     { | ||||
|       // reset everything, record and signal the current state
 | ||||
|       m_->retries_ = 0; | ||||
|       m_->next_state_ = state (); | ||||
|       m_->last_signalled_state_ = state (); | ||||
|       update_complete (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void PollingTransceiver::impl::handle_timeout () | ||||
| { | ||||
|   QString message; | ||||
| 
 | ||||
|   // we must catch all exceptions here since we are called by Qt and
 | ||||
|   // inform our parent of the failure via the offline() message
 | ||||
|   try | ||||
|     { | ||||
|       self_->do_sync (false); | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       self_->offline (message); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										60
									
								
								PollingTransceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,60 @@ | ||||
| #ifndef POLLING_TRANSCEIVER_HPP__ | ||||
| #define POLLING_TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <QObject> | ||||
| 
 | ||||
| #include "TransceiverBase.hpp" | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| //
 | ||||
| // Polling Transceiver
 | ||||
| //
 | ||||
| //  Helper base  class that  encapsulates the emulation  of continuous
 | ||||
| //  update and caching of a transceiver state.
 | ||||
| //
 | ||||
| // Collaborations
 | ||||
| //
 | ||||
| //  Implements the TransceiverBase post  action interface and provides
 | ||||
| //  the abstract  poll() operation  for sub-classes to  implement. The
 | ||||
| //  pol operation is invoked every poll_interval milliseconds.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Because some rig interfaces don't immediately update after a state
 | ||||
| //  change request; this  class allows a rig a few  polls to stabilise
 | ||||
| //  to the  requested state before  signalling the change.  This means
 | ||||
| //  that  clients don't  see  intermediate states  that are  sometimes
 | ||||
| //  inaccurate,  e.g. changing  the split  TX frequency  on Icom  rigs
 | ||||
| //  requires a  VFO switch  and polls while  switched will  return the
 | ||||
| //  wrong current frequency.
 | ||||
| //
 | ||||
| class PollingTransceiver | ||||
|   : public TransceiverBase | ||||
| { | ||||
| protected: | ||||
|   explicit PollingTransceiver (int poll_interval); // in milliseconds
 | ||||
| 
 | ||||
| public: | ||||
|   ~PollingTransceiver (); | ||||
| 
 | ||||
| protected: | ||||
|   void do_sync (bool /* force_signal */) override final; | ||||
| 
 | ||||
|   // Sub-classes implement this and fetch what they can from the rig
 | ||||
|   // in a non-intrusive manner.
 | ||||
|   virtual void poll () = 0; | ||||
| 
 | ||||
|   void do_post_start () override final; | ||||
|   void do_post_stop () override final; | ||||
|   void do_post_frequency (Frequency) override final; | ||||
|   void do_post_tx_frequency (Frequency) override final; | ||||
|   void do_post_mode (MODE) override final; | ||||
|   bool do_pre_update () override final; | ||||
| 
 | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										72
									
								
								Radio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,72 @@ | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| #include <cmath> | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| #include <QString> | ||||
| #include <QChar> | ||||
| #include <QDebug> | ||||
| #include <QRegExpValidator> | ||||
| #include <QDataStream> | ||||
| 
 | ||||
| namespace Radio | ||||
| { | ||||
|   namespace | ||||
|   { | ||||
|     struct init | ||||
|     { | ||||
|       init () | ||||
|       { | ||||
|         qRegisterMetaType<Frequency> ("Frequency"); | ||||
| 
 | ||||
|         qRegisterMetaType<Frequencies> ("Frequencies"); | ||||
|         qRegisterMetaTypeStreamOperators<Frequencies> ("Frequencies"); | ||||
| 
 | ||||
|         qRegisterMetaType<FrequencyDelta> ("FrequencyDelta"); | ||||
|       } | ||||
|     } static_initaializer; | ||||
| 
 | ||||
|     double constexpr MHz_factor {1.e6}; | ||||
|     int constexpr frequency_precsion {6}; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   Frequency frequency (QVariant const& v, int scale) | ||||
|   { | ||||
|     return std::llround (v.toDouble () * std::pow (10., scale)); | ||||
|   } | ||||
| 
 | ||||
|   FrequencyDelta frequency_delta (QVariant const& v, int scale) | ||||
|   { | ||||
|     return std::llround (v.toDouble () * std::pow (10., scale)); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   QString frequency_MHz_string (Frequency f, QLocale const& locale) | ||||
|   { | ||||
|     return locale.toString (f / MHz_factor, 'f', frequency_precsion); | ||||
|   } | ||||
| 
 | ||||
|   QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale) | ||||
|   { | ||||
|     return locale.toString (d / MHz_factor, 'f', frequency_precsion); | ||||
|   } | ||||
| 
 | ||||
|   QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale) | ||||
|   { | ||||
|     auto f_string = locale.toString (f / MHz_factor, 'f', frequency_precsion); | ||||
|     return f_string.insert (f_string.size () - 3, QChar::Nbsp); | ||||
|   } | ||||
| 
 | ||||
|   QString pretty_frequency_MHz_string (double f, int scale, QLocale const& locale) | ||||
|   { | ||||
|     auto f_string = locale.toString (f / std::pow (10., scale - 6), 'f', frequency_precsion); | ||||
|     return f_string.insert (f_string.size () - 3, QChar::Nbsp); | ||||
|   } | ||||
| 
 | ||||
|   QString pretty_frequency_MHz_string (FrequencyDelta d, QLocale const& locale) | ||||
|   { | ||||
|     auto d_string = locale.toString (d / MHz_factor, 'f', frequency_precsion); | ||||
|     return d_string.insert (d_string.size () - 3, QChar::Nbsp); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										47
									
								
								Radio.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,47 @@ | ||||
| #ifndef RADIO_HPP_ | ||||
| #define RADIO_HPP_ | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QLocale> | ||||
| #include <QList> | ||||
| 
 | ||||
| class QVariant; | ||||
| class QString; | ||||
| 
 | ||||
| //
 | ||||
| // Declarations common to radio software.
 | ||||
| //
 | ||||
| 
 | ||||
| namespace Radio | ||||
| { | ||||
|   //
 | ||||
|   // Frequency types
 | ||||
|   //
 | ||||
|   using Frequency = quint64; | ||||
|   using Frequencies = QList<Frequency>; | ||||
|   using FrequencyDelta = qint64; | ||||
| 
 | ||||
|   //
 | ||||
|   // Frequency type conversion.
 | ||||
|   //
 | ||||
|   //	QVariant argument is convertible to double and is assumed to
 | ||||
|   //	be scaled by (10 ** -scale).
 | ||||
|   //
 | ||||
|   Frequency frequency (QVariant const&, int scale); | ||||
|   FrequencyDelta frequency_delta (QVariant const&, int scale); | ||||
| 
 | ||||
|   //
 | ||||
|   // Frequency type formatting
 | ||||
|   //
 | ||||
|   QString frequency_MHz_string (Frequency, QLocale const& = QLocale ()); | ||||
|   QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); | ||||
|   QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ()); | ||||
|   QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ()); | ||||
|   QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ()); | ||||
| } | ||||
| 
 | ||||
| Q_DECLARE_METATYPE (Radio::Frequency); | ||||
| Q_DECLARE_METATYPE (Radio::Frequencies); | ||||
| Q_DECLARE_METATYPE (Radio::FrequencyDelta); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										34
									
								
								SettingsGroup.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,34 @@ | ||||
| #ifndef SETTINGS_GROUP_HPP_ | ||||
| #define SETTINGS_GROUP_HPP_ | ||||
| 
 | ||||
| #include <QSettings> | ||||
| #include <QString> | ||||
| 
 | ||||
| //
 | ||||
| // Class SettingsGroup
 | ||||
| //
 | ||||
| //	Simple RAII type class to apply a QSettings group witin a
 | ||||
| //	scope.
 | ||||
| //
 | ||||
| class SettingsGroup | ||||
| { | ||||
| public: | ||||
|   SettingsGroup (QSettings * settings, QString const& group) | ||||
|     : settings_ {settings} | ||||
|   { | ||||
|     settings_->beginGroup (group); | ||||
|   } | ||||
| 
 | ||||
|   SettingsGroup (SettingsGroup const&) = delete; | ||||
|   SettingsGroup& operator = (SettingsGroup const&) = delete; | ||||
| 
 | ||||
|   ~SettingsGroup () | ||||
|   { | ||||
|     settings_->endGroup (); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   QSettings * settings_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										551
									
								
								StationList.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,551 @@ | ||||
| #include "StationList.hpp" | ||||
| 
 | ||||
| #include <utility> | ||||
| #include <algorithm> | ||||
| #include <cmath> | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| #include <QAbstractTableModel> | ||||
| #include <QObject> | ||||
| #include <QString> | ||||
| #include <QVector> | ||||
| #include <QStringList> | ||||
| #include <QMimeData> | ||||
| #include <QDataStream> | ||||
| #include <QByteArray> | ||||
| #include <QDebug> | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| #include "Bands.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   struct init | ||||
|   { | ||||
|     init () | ||||
|     { | ||||
|       qRegisterMetaType<StationList::Station> ("Station"); | ||||
|       qRegisterMetaTypeStreamOperators<StationList::Station> ("Station"); | ||||
|       qRegisterMetaType<StationList::Stations> ("Stations"); | ||||
|       qRegisterMetaTypeStreamOperators<StationList::Stations> ("Stations"); | ||||
|     } | ||||
|   } static_initializer; | ||||
| } | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| QDebug operator << (QDebug debug, StationList::Station const& station) | ||||
| { | ||||
|   debug.nospace () << "Station(" | ||||
|                    << station.band_name_ << ", " | ||||
|                    << station.offset_ << ", " | ||||
|                    << station.antenna_description_ << ')'; | ||||
|   return debug.space (); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| QDataStream& operator << (QDataStream& os, StationList::Station const& station) | ||||
| { | ||||
|   return os << station.band_name_ | ||||
|             << station.offset_ | ||||
|             << station.antenna_description_; | ||||
| } | ||||
| 
 | ||||
| QDataStream& operator >> (QDataStream& is, StationList::Station& station) | ||||
| { | ||||
|   return is >> station.band_name_ | ||||
|             >> station.offset_ | ||||
|             >> station.antenna_description_; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class StationList::impl final | ||||
|   : public QAbstractTableModel | ||||
| { | ||||
| public: | ||||
|   impl (Bands const * bands, Stations stations, QObject * parent) | ||||
|     : QAbstractTableModel {parent} | ||||
|     , bands_ {bands} | ||||
|     , stations_ {stations} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   Stations const& stations () const {return stations_;} | ||||
|   void assign (Stations); | ||||
|   QModelIndex add (Station); | ||||
|   FrequencyDelta offset (Frequency) const; | ||||
| 
 | ||||
| protected: | ||||
|   // Implement the QAbstractTableModel interface.
 | ||||
|   int rowCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   int columnCount (QModelIndex const& parent = QModelIndex {}) const override; | ||||
|   Qt::ItemFlags flags (QModelIndex const& = QModelIndex {}) const override; | ||||
|   QVariant data (QModelIndex const&, int role) const override; | ||||
|   QVariant headerData (int section, Qt::Orientation, int = Qt::DisplayRole) const override; | ||||
|   bool setData (QModelIndex const&, QVariant const& value, int role = Qt::EditRole) override; | ||||
|   bool removeRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override; | ||||
|   bool insertRows (int row, int count, QModelIndex const& parent = QModelIndex {}) override; | ||||
|   Qt::DropActions supportedDropActions () const override; | ||||
|   QStringList mimeTypes () const override; | ||||
|   QMimeData * mimeData (QModelIndexList const&) const override; | ||||
|   bool dropMimeData (QMimeData const *, Qt::DropAction, int row, int column, QModelIndex const& parent) override; | ||||
| 
 | ||||
| private: | ||||
|   // Helper method for band validation.
 | ||||
|   QModelIndex first_matching_band (QString const& band_name) const | ||||
|   { | ||||
|     // find first exact match in bands
 | ||||
|     auto matches = bands_->match (bands_->index (0, 0) | ||||
|                                   , Qt::DisplayRole | ||||
|                                   , band_name | ||||
|                                   , 1 | ||||
|                                   , Qt::MatchExactly); | ||||
|     return matches.isEmpty () ? QModelIndex {} : matches.first (); | ||||
|   } | ||||
| 
 | ||||
|   static int constexpr num_columns {3}; | ||||
|   static auto constexpr mime_type = "application/wsjt.antenna-descriptions"; | ||||
| 
 | ||||
|   Bands const * bands_; | ||||
|   Stations stations_; | ||||
| }; | ||||
| 
 | ||||
| StationList::StationList (Bands const * bands, QObject * parent) | ||||
|   : StationList {bands, {}, parent} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| StationList::StationList (Bands const * bands, Stations stations, QObject * parent) | ||||
|   : QSortFilterProxyModel {parent} | ||||
|   , m_ {bands, stations, parent} | ||||
| { | ||||
|   // setDynamicSortFilter (true);
 | ||||
|   setSourceModel (&*m_); | ||||
|   setSortRole (SortRole); | ||||
| } | ||||
| 
 | ||||
| StationList::~StationList () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| StationList& StationList::operator = (Stations stations) | ||||
| { | ||||
|   m_->assign (stations); | ||||
|   return *this; | ||||
| } | ||||
| 
 | ||||
| auto StationList::stations () const -> Stations | ||||
| { | ||||
|   return m_->stations (); | ||||
| } | ||||
| 
 | ||||
| QModelIndex StationList::add (Station s) | ||||
| { | ||||
|   return mapFromSource (m_->add (s)); | ||||
| } | ||||
| 
 | ||||
| bool StationList::remove (Station s) | ||||
| { | ||||
|   auto row = m_->stations ().indexOf (s); | ||||
| 
 | ||||
|   if (0 > row) | ||||
|     { | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
|   return removeRow (row); | ||||
| } | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs) | ||||
|   { | ||||
|     return lhs.row () > rhs.row (); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool StationList::removeDisjointRows (QModelIndexList rows) | ||||
| { | ||||
|   bool result {true}; | ||||
| 
 | ||||
|   // We must work with source model indexes because we don't want row
 | ||||
|   // removes to invalidate model indexes we haven't yet processed. We
 | ||||
|   // achieve that by processing them in decending row order.
 | ||||
|   for (int r = 0; r < rows.size (); ++r) | ||||
|     { | ||||
|       rows[r] = mapToSource (rows[r]); | ||||
|     } | ||||
| 
 | ||||
|   // reverse sort by row
 | ||||
|   qSort (rows.begin (), rows.end (), row_is_higher); | ||||
|   Q_FOREACH (auto index, rows) | ||||
|     { | ||||
|       if (result && !m_->removeRow (index.row ())) | ||||
|         { | ||||
|           result = false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| auto StationList::offset (Frequency f) const -> FrequencyDelta | ||||
| { | ||||
|   return m_->offset (f); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void StationList::impl::assign (Stations stations) | ||||
| { | ||||
|   beginResetModel (); | ||||
|   std::swap (stations_, stations); | ||||
|   endResetModel (); | ||||
| } | ||||
| 
 | ||||
| QModelIndex StationList::impl::add (Station s) | ||||
| { | ||||
|   // Any band that isn't in the list may be added
 | ||||
|   if (!stations_.contains (s)) | ||||
|     { | ||||
|       auto row = stations_.size (); | ||||
| 
 | ||||
|       beginInsertRows (QModelIndex {}, row, row); | ||||
|       stations_.append (s); | ||||
|       endInsertRows (); | ||||
| 
 | ||||
|       return index (row, 0); | ||||
|     } | ||||
| 
 | ||||
|   return QModelIndex {}; | ||||
| } | ||||
| 
 | ||||
| auto StationList::impl::offset (Frequency f) const -> FrequencyDelta | ||||
| { | ||||
|   // Lookup band for frequency
 | ||||
|   auto band_index = bands_->find (f); | ||||
|   if (band_index.isValid ()) | ||||
|     { | ||||
|       auto band_name = band_index.data ().toString (); | ||||
| 
 | ||||
|       // Lookup station for band
 | ||||
|       for (int i = 0; i < stations ().size (); ++i) | ||||
|         { | ||||
|           if (stations_[i].band_name_ == band_name) | ||||
|             { | ||||
|               return stations_[i].offset_; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return 0;			// no offset
 | ||||
| } | ||||
| 
 | ||||
| int StationList::impl::rowCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : stations_.size (); | ||||
| } | ||||
| 
 | ||||
| int StationList::impl::columnCount (QModelIndex const& parent) const | ||||
| { | ||||
|   return parent.isValid () ? 0 : num_columns; | ||||
| } | ||||
| 
 | ||||
| Qt::ItemFlags StationList::impl::flags (QModelIndex const& index) const | ||||
| { | ||||
|   auto result = QAbstractTableModel::flags (index); | ||||
| 
 | ||||
|   auto row = index.row (); | ||||
|   auto column = index.column (); | ||||
| 
 | ||||
|   if (index.isValid () | ||||
|       && row < stations_.size () | ||||
|       && column < num_columns) | ||||
|     { | ||||
|       if (2 == column) | ||||
|         { | ||||
|           result |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           result |= Qt::ItemIsEditable | Qt::ItemIsDropEnabled; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       result |= Qt::ItemIsDropEnabled; | ||||
|     } | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| QVariant StationList::impl::data (QModelIndex const& index, int role) const | ||||
| { | ||||
|   QVariant item; | ||||
| 
 | ||||
|   auto row = index.row (); | ||||
|   auto column = index.column (); | ||||
| 
 | ||||
|   if (index.isValid () | ||||
|       && row < stations_.size ()) | ||||
|     { | ||||
|       switch (column) | ||||
|         { | ||||
|         case 0:			// band name
 | ||||
|           switch (role) | ||||
|             { | ||||
|             case SortRole: | ||||
|               { | ||||
|                 // Lookup band.
 | ||||
|                 auto band_index = first_matching_band (stations_.at (row).band_name_); | ||||
|                 // Use the sort role value of the band.
 | ||||
|                 item = band_index.data (Bands::SortRole); | ||||
|               } | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::DisplayRole: | ||||
|             case Qt::EditRole: | ||||
|             case Qt::AccessibleTextRole: | ||||
|               item = stations_.at (row).band_name_; | ||||
|               break; | ||||
| 	       | ||||
|             case Qt::ToolTipRole: | ||||
|             case Qt::AccessibleDescriptionRole: | ||||
|               item = tr ("Band name"); | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::TextAlignmentRole: | ||||
|               item = Qt::AlignHCenter + Qt::AlignVCenter; | ||||
|               break; | ||||
|             } | ||||
|           break; | ||||
| 
 | ||||
|         case 1:			// frequency offset
 | ||||
|           { | ||||
|             auto frequency_offset = stations_.at (row).offset_; | ||||
|             switch (role) | ||||
|               { | ||||
|               case Qt::AccessibleTextRole: | ||||
|                 item = frequency_offset; | ||||
|                 break; | ||||
| 
 | ||||
|               case SortRole: | ||||
|               case Qt::DisplayRole: | ||||
|               case Qt::EditRole: | ||||
|                 item = frequency_offset; | ||||
|                 break; | ||||
| 
 | ||||
|               case Qt::ToolTipRole: | ||||
|               case Qt::AccessibleDescriptionRole: | ||||
|                 item = tr ("Frequency offset"); | ||||
|                 break; | ||||
| 
 | ||||
|               case Qt::TextAlignmentRole: | ||||
|                 item = Qt::AlignRight + Qt::AlignVCenter; | ||||
|                 break; | ||||
|               } | ||||
|           } | ||||
|           break; | ||||
| 
 | ||||
|         case 2:			// antenna description
 | ||||
|           switch (role) | ||||
|             { | ||||
|             case SortRole: | ||||
|             case Qt::EditRole: | ||||
|             case Qt::DisplayRole: | ||||
|             case Qt::AccessibleTextRole: | ||||
|               item = stations_.at (row).antenna_description_; | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::ToolTipRole: | ||||
|             case Qt::AccessibleDescriptionRole: | ||||
|               item = tr ("Antenna description"); | ||||
|               break; | ||||
| 
 | ||||
|             case Qt::TextAlignmentRole: | ||||
|               item = Qt::AlignLeft + Qt::AlignVCenter; | ||||
|               break; | ||||
|             } | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return item; | ||||
| } | ||||
| 
 | ||||
| QVariant StationList::impl::headerData (int section, Qt::Orientation orientation, int role) const | ||||
| { | ||||
|   QVariant header; | ||||
| 
 | ||||
|   if (Qt::DisplayRole == role && Qt::Horizontal == orientation) | ||||
|     { | ||||
|       switch (section) | ||||
|         { | ||||
|         case 0: header = tr ("Band"); break; | ||||
|         case 1: header = tr ("Offset"); break; | ||||
|         case 2: header = tr ("Antenna Description"); break; | ||||
|         } | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       header = QAbstractTableModel::headerData (section, orientation, role); | ||||
|     } | ||||
| 
 | ||||
|   return header; | ||||
| } | ||||
| 
 | ||||
| bool StationList::impl::setData (QModelIndex const& model_index, QVariant const& value, int role) | ||||
| { | ||||
|   bool changed {false}; | ||||
| 
 | ||||
|   auto row = model_index.row (); | ||||
|   auto size = stations_.size (); | ||||
|   if (model_index.isValid () | ||||
|       && Qt::EditRole == role | ||||
|       && row < size) | ||||
|     { | ||||
|       QVector<int> roles; | ||||
|       roles << role; | ||||
| 
 | ||||
|       switch (model_index.column ()) | ||||
|         { | ||||
|         case 0: | ||||
|           { | ||||
|             // Check if band name is valid.
 | ||||
|             auto band_index = first_matching_band (value.toString ()); | ||||
|             if (band_index.isValid ()) | ||||
|               { | ||||
|                 stations_[row].band_name_ = band_index.data ().toString (); | ||||
|                 Q_EMIT dataChanged (model_index, model_index, roles); | ||||
|                 changed = true; | ||||
|               } | ||||
|           } | ||||
|           break; | ||||
| 
 | ||||
|         case 1: | ||||
|           { | ||||
|             stations_[row].offset_ = value.value<FrequencyDelta> (); | ||||
|             Q_EMIT dataChanged (model_index, model_index, roles); | ||||
|             changed = true; | ||||
|           } | ||||
|           break; | ||||
| 
 | ||||
|         case 2: | ||||
|           stations_[row].antenna_description_ = value.toString (); | ||||
|           Q_EMIT dataChanged (model_index, model_index, roles); | ||||
|           changed = true; | ||||
|           break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   return changed; | ||||
| } | ||||
| 
 | ||||
| bool StationList::impl::removeRows (int row, int count, QModelIndex const& parent) | ||||
| { | ||||
|   if (0 < count && (row + count) <= rowCount (parent)) | ||||
|     { | ||||
|       beginRemoveRows (parent, row, row + count - 1); | ||||
|       for (auto r = 0; r < count; ++r) | ||||
|         { | ||||
|           stations_.removeAt (row); | ||||
|         } | ||||
|       endRemoveRows (); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| bool StationList::impl::insertRows (int row, int count, QModelIndex const& parent) | ||||
| { | ||||
|   if (0 < count) | ||||
|     { | ||||
|       beginInsertRows (parent, row, row + count - 1); | ||||
|       for (auto r = 0; r < count; ++r) | ||||
|         { | ||||
|           stations_.insert (row, Station ()); | ||||
|         } | ||||
|       endInsertRows (); | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| Qt::DropActions StationList::impl::supportedDropActions () const | ||||
| { | ||||
|   return Qt::CopyAction | Qt::MoveAction; | ||||
| } | ||||
| 
 | ||||
| QStringList StationList::impl::mimeTypes () const | ||||
| { | ||||
|   QStringList types; | ||||
|   types << mime_type; | ||||
|   types << "application/wsjt.Frequencies"; | ||||
|   return types; | ||||
| } | ||||
| 
 | ||||
| QMimeData * StationList::impl::mimeData (QModelIndexList const& items) const | ||||
| { | ||||
|   QMimeData * mime_data = new QMimeData {}; | ||||
|   QByteArray encoded_data; | ||||
|   QDataStream stream {&encoded_data, QIODevice::WriteOnly}; | ||||
| 
 | ||||
|   Q_FOREACH (auto const& item, items) | ||||
|     { | ||||
|       if (item.isValid ()) | ||||
|         { | ||||
|           stream << QString {data (item, Qt::DisplayRole).toString ()}; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|   mime_data->setData (mime_type, encoded_data); | ||||
|   return mime_data; | ||||
| } | ||||
| 
 | ||||
| bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction action, int /* row */, int /* column */, QModelIndex const& parent) | ||||
| { | ||||
|   if (Qt::IgnoreAction == action) | ||||
|     { | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   if (parent.isValid () | ||||
|       && 2 == parent.column () | ||||
|       && data->hasFormat (mime_type)) | ||||
|     { | ||||
|       QByteArray encoded_data {data->data (mime_type)}; | ||||
|       QDataStream stream {&encoded_data, QIODevice::ReadOnly}; | ||||
|       auto dest_index = parent; | ||||
|       while (!stream.atEnd ()) | ||||
|         { | ||||
|           QString text; | ||||
|           stream >> text; | ||||
|           setData (dest_index, text); | ||||
|           dest_index = index (dest_index.row () + 1, dest_index.column (), QModelIndex {}); | ||||
|         } | ||||
|       return true; | ||||
|     } | ||||
|   else if (data->hasFormat ("application/wsjt.Frequencies")) | ||||
|     { | ||||
|       QByteArray encoded_data {data->data ("application/wsjt.Frequencies")}; | ||||
|       QDataStream stream {&encoded_data, QIODevice::ReadOnly}; | ||||
|       while (!stream.atEnd ()) | ||||
|         { | ||||
|           QString frequency_string; | ||||
|           stream >> frequency_string; | ||||
|           auto frequency = Radio::frequency (frequency_string, 0); | ||||
|           auto band_index = bands_->find (frequency); | ||||
|           if (stations_.cend () == std::find_if (stations_.cbegin () | ||||
|                                                  , stations_.cend () | ||||
|                                                  , [&band_index] (Station const& s) {return s.band_name_ == band_index.data ().toString ();})) | ||||
|             { | ||||
|               add (Station {band_index.data ().toString (), 0, QString {}}); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
							
								
								
									
										96
									
								
								StationList.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,96 @@ | ||||
| #ifndef STATION_LIST_HPP__ | ||||
| #define STATION_LIST_HPP__ | ||||
| 
 | ||||
| #include <QSortFilterProxyModel> | ||||
| #include <QString> | ||||
| #include <QList> | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| class Bands; | ||||
| 
 | ||||
| //
 | ||||
| // Class StationList
 | ||||
| //
 | ||||
| //  Encapsulates information  about a  collection of  unique operating
 | ||||
| //  stations per  band. The implementation  is a table model  with the
 | ||||
| //  first column  being the unique  (within the table rows)  band name
 | ||||
| //  and, the  second the frequency  offset for transverter  usage and,
 | ||||
| //  the third the antenna description. All are editable.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Stores internally an unordered table of bands.
 | ||||
| //
 | ||||
| //  If  an ordered  representaion is  required then  wrapping with  an
 | ||||
| //  appropriate        proxy         model        is        sufficient
 | ||||
| //  e.g. QSortFilterProxyModel. A custom SortRole role is provided for
 | ||||
| //  the band  name column which  returns a numeric value  (Bands lower
 | ||||
| //  frequency limit) which gives a strict frequency ordering by band.
 | ||||
| //
 | ||||
| // Collaborations
 | ||||
| //
 | ||||
| //  Implements the  QAbstractTableModel interface for a  grid of bands
 | ||||
| //  with offset frequencies and antenna descriptions.
 | ||||
| //
 | ||||
| //  Uses the QAbstractItemModel interface of the bands model to lookup
 | ||||
| //  band information.
 | ||||
| //
 | ||||
| class StationList final | ||||
|   : public QSortFilterProxyModel | ||||
| { | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
|   using FrequencyDelta = Radio::FrequencyDelta; | ||||
|    | ||||
|   //
 | ||||
|   // Struct Station
 | ||||
|   //
 | ||||
|   //  Aggregation of fields that describe a radio station on a band.
 | ||||
|   //
 | ||||
|   struct Station | ||||
|   { | ||||
|     QString band_name_; | ||||
|     FrequencyDelta offset_; | ||||
|     QString antenna_description_; | ||||
|   }; | ||||
|    | ||||
|   using Stations = QList<Station>; | ||||
| 
 | ||||
|   explicit StationList (Bands const * bands, QObject * parent = nullptr); | ||||
|   explicit StationList (Bands const * bands, Stations, QObject * parent = nullptr); | ||||
|   ~StationList (); | ||||
| 
 | ||||
|   // Load and store contents.
 | ||||
|   StationList& operator = (Stations); | ||||
|   Stations stations () const; | ||||
| 
 | ||||
|   //
 | ||||
|   // Model API
 | ||||
|   //
 | ||||
|   QModelIndex add (Station);                 // Add a new Station
 | ||||
|   bool remove (Station);                     // Remove a Station
 | ||||
|   bool removeDisjointRows (QModelIndexList); // Remove one or more stations
 | ||||
|   FrequencyDelta offset (Frequency) const;   // Return the offset to be used for a Frequency
 | ||||
| 
 | ||||
|   // Custom sort role.
 | ||||
|   static int constexpr SortRole = Qt::UserRole; | ||||
|    | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| // Station equivalence is based on band name alone.
 | ||||
| inline | ||||
| bool operator == (StationList::Station const& lhs, StationList::Station const& rhs) | ||||
| { | ||||
|   return lhs.band_name_ == rhs.band_name_; | ||||
| } | ||||
| 
 | ||||
| Q_DECLARE_METATYPE (StationList::Station); | ||||
| Q_DECLARE_METATYPE (StationList::Stations); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										470
									
								
								TestConfiguration.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,470 @@ | ||||
| #include "TestConfiguration.hpp" | ||||
| 
 | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QSettings> | ||||
| #include <QMainWindow> | ||||
| #include <QLabel> | ||||
| #include <QDir> | ||||
| #include <QMessageBox> | ||||
| #include <QLineEdit> | ||||
| #include <QDebug> | ||||
| #include <QSortFilterProxyModel> | ||||
| 
 | ||||
| #include "Bands.hpp" | ||||
| #include "FrequencyList.hpp" | ||||
| #include "Configuration.hpp" | ||||
| #include "LiveFrequencyValidator.hpp" | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| #include "ui_TestConfiguration.h" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   char const * const title = "Configuration Test v" WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MAJOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_MINOR) "." WSJTX_STRINGIZE (CONFIG_TEST_VERSION_PATCH) ", " WSJTX_STRINGIZE (SVNVERSION); | ||||
| 
 | ||||
|   // these undocumented flag values when stored in (Qt::UserRole - 1)
 | ||||
|   // of a ComboBox item model index allow the item to be enabled or
 | ||||
|   // disabled
 | ||||
|   int const combo_box_item_enabled (32 | 1); | ||||
|   int const combo_box_item_disabled (0); | ||||
| 
 | ||||
|   auto LCD_error = "-- Error --"; | ||||
| } | ||||
| 
 | ||||
| class status_bar_frequency final | ||||
|   : public QLabel | ||||
| { | ||||
| public: | ||||
|   status_bar_frequency (QString const& prefix, QWidget * parent = 0) | ||||
|     : QLabel {parent} | ||||
|     , prefix_ {prefix} | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   void setText (QString const& text) | ||||
|   { | ||||
|     QLabel::setText (prefix_ + " Frequency: " + text); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   QString prefix_; | ||||
| }; | ||||
| 
 | ||||
| class TestConfiguration::impl final | ||||
|   : public QMainWindow | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
| 
 | ||||
|   explicit impl (QString const& instance_key, QSettings *, QWidget * parent = nullptr); | ||||
| 
 | ||||
|   void closeEvent (QCloseEvent *) override; | ||||
| 
 | ||||
|   Q_SIGNAL void new_frequency (Frequency) const; | ||||
|   Q_SIGNAL void new_tx_frequency (Frequency = 0, bool rationalise_mode = true) const; | ||||
| 
 | ||||
| private: | ||||
|   Q_SLOT void on_configuration_action_triggered (); | ||||
|   Q_SLOT void on_band_combo_box_activated (int); | ||||
|   Q_SLOT void on_mode_combo_box_activated (int); | ||||
|   Q_SLOT void on_PTT_push_button_clicked (bool); | ||||
|   Q_SLOT void on_split_push_button_clicked (bool); | ||||
|   Q_SLOT void on_TX_offset_spin_box_valueChanged (int); | ||||
|   Q_SLOT void on_sync_push_button_clicked (bool); | ||||
| 
 | ||||
|   Q_SLOT void handle_transceiver_update (Transceiver::TransceiverState); | ||||
|   Q_SLOT void handle_transceiver_failure (QString); | ||||
| 
 | ||||
|   void information_message_box (QString const& reason, QString const& detail); | ||||
|   void read_settings (); | ||||
|   void write_settings (); | ||||
|   void load_models (); | ||||
|   void sync_rig (); | ||||
|   void band_changed (Frequency); | ||||
|   void frequency_changed (Frequency); | ||||
| 
 | ||||
|   Ui::test_configuration_main_window * ui_; | ||||
|   QSettings * settings_; | ||||
|   QString callsign_; | ||||
|   Configuration configuration_dialog_; | ||||
|   bool updating_models_;	// hold off UI reaction while adjusting models
 | ||||
|   bool band_edited_; | ||||
| 
 | ||||
|   Transceiver::TransceiverState rig_state_; | ||||
| 
 | ||||
|   Frequency desired_frequency_; | ||||
| 
 | ||||
|   status_bar_frequency RX_frequency_; | ||||
|   status_bar_frequency TX_frequency_; | ||||
| }; | ||||
| 
 | ||||
| #include "TestConfiguration.moc" | ||||
| 
 | ||||
| TestConfiguration::TestConfiguration (QString const& instance_key, QSettings * settings, QWidget * parent) | ||||
|   : m_ {instance_key, settings, parent} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| TestConfiguration::~TestConfiguration () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| TestConfiguration::impl::impl (QString const& instance_key, QSettings * settings, QWidget * parent) | ||||
|   : QMainWindow {parent} | ||||
|   , ui_ {new Ui::test_configuration_main_window} | ||||
|   , settings_ {settings} | ||||
|   , configuration_dialog_ {instance_key, settings, this} | ||||
|   , updating_models_ {false} | ||||
|   , band_edited_ {false} | ||||
|   , desired_frequency_ {0u} | ||||
|   , RX_frequency_ {"RX"} | ||||
|   , TX_frequency_ {"TX"} | ||||
| { | ||||
|   ui_->setupUi (this); | ||||
| 
 | ||||
|   setWindowTitle (QApplication::applicationName () + " - " + title); | ||||
| 
 | ||||
|   // mode "Unknown" is display only
 | ||||
|   ui_->mode_combo_box->setItemData (ui_->mode_combo_box->findText ("Unknown"), combo_box_item_disabled, Qt::UserRole - 1); | ||||
| 
 | ||||
|   // setup status bar widgets
 | ||||
|   statusBar ()->insertPermanentWidget (0, &TX_frequency_); | ||||
|   statusBar ()->insertPermanentWidget (0, &RX_frequency_); | ||||
| 
 | ||||
|   // assign push button ids
 | ||||
|   ui_->TX_button_group->setId (ui_->vfo_0_TX_push_button, 0); | ||||
|   ui_->TX_button_group->setId (ui_->vfo_1_TX_push_button, 1); | ||||
| 
 | ||||
|   // enable live band combo box entry validation and action
 | ||||
|   auto band_validator = new LiveFrequencyValidator {ui_->band_combo_box | ||||
|                                                     , configuration_dialog_.bands () | ||||
|                                                     , configuration_dialog_.frequencies () | ||||
|                                                     , this}; | ||||
|   ui_->band_combo_box->setValidator (band_validator); | ||||
|   connect (band_validator, &LiveFrequencyValidator::valid, this, &TestConfiguration::impl::band_changed); | ||||
|   connect (ui_->band_combo_box->lineEdit (), &QLineEdit::textEdited, [this] (QString const&) {band_edited_ = true;}); | ||||
| 
 | ||||
|   // hook up band data model
 | ||||
|   ui_->band_combo_box->setModel (configuration_dialog_.frequencies ()); | ||||
| 
 | ||||
|   // combo box drop downs are limited to the drop down selector width,
 | ||||
|   // this almost random increase improves the situation
 | ||||
|   ui_->band_combo_box->view ()->setMinimumWidth (ui_->band_combo_box->view ()->sizeHintForColumn (0) + 10); | ||||
| 
 | ||||
|   // hook up configuration signals
 | ||||
|   connect (&configuration_dialog_, &Configuration::transceiver_update, this, &TestConfiguration::impl::handle_transceiver_update); | ||||
|   connect (&configuration_dialog_, &Configuration::transceiver_failure, this, &TestConfiguration::impl::handle_transceiver_failure); | ||||
| 
 | ||||
|   // hook up configuration slots
 | ||||
|   connect (this, &TestConfiguration::impl::new_frequency, &configuration_dialog_, &Configuration::transceiver_frequency); | ||||
|   connect (this, &TestConfiguration::impl::new_tx_frequency, &configuration_dialog_, &Configuration::transceiver_tx_frequency); | ||||
| 
 | ||||
|   load_models (); | ||||
| 
 | ||||
|   read_settings (); | ||||
| 
 | ||||
|   show (); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::closeEvent (QCloseEvent * e) | ||||
| { | ||||
|   write_settings (); | ||||
| 
 | ||||
|   QMainWindow::closeEvent (e); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_configuration_action_triggered () | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_configuration_action_triggered"; | ||||
|   if (QDialog::Accepted == configuration_dialog_.exec ()) | ||||
|     { | ||||
|       qDebug () << "TestConfiguration::on_configuration_action_triggered: Configuration changed"; | ||||
|       qDebug () << "TestConfiguration::on_configuration_action_triggered: rig is" << configuration_dialog_.rig_name (); | ||||
| 
 | ||||
|       if (configuration_dialog_.restart_audio_input ()) | ||||
|         { | ||||
|           qDebug () << "Audio Device Changes - Configuration changes require an audio input device to be restarted"; | ||||
|         } | ||||
|       if (configuration_dialog_.restart_audio_output ()) | ||||
|         { | ||||
|           qDebug () << "Audio Device Changes - Configuration changes require an audio output device to be restarted"; | ||||
|         } | ||||
| 
 | ||||
|       load_models (); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       qDebug () << "TestConfiguration::on_configuration_action_triggered: Confiugration changes cancelled"; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_band_combo_box_activated (int index) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_band_combo_box_activated: " << ui_->band_combo_box->currentText (); | ||||
| 
 | ||||
|   auto model = configuration_dialog_.frequencies (); | ||||
|   auto value = model->data (model->index (index, 2), Qt::DisplayRole).toString (); | ||||
| 
 | ||||
|   if (configuration_dialog_.bands ()->data (QModelIndex {}).toString () == value) | ||||
|     { | ||||
|       ui_->band_combo_box->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}"); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       ui_->band_combo_box->lineEdit ()->setStyleSheet ({}); | ||||
|     } | ||||
| 
 | ||||
|   ui_->band_combo_box->setCurrentText (value); | ||||
| 
 | ||||
|   auto f = model->data (model->index (index, 0), Qt::UserRole + 1).value<Frequency> (); | ||||
| 
 | ||||
|   band_edited_ = true; | ||||
|   band_changed (f); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::band_changed (Frequency f) | ||||
| { | ||||
|   if (band_edited_) | ||||
|     { | ||||
|       band_edited_ = false; | ||||
|       frequency_changed (f); | ||||
|       sync_rig (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::frequency_changed (Frequency f) | ||||
| { | ||||
|   desired_frequency_ = f; | ||||
| 
 | ||||
|   // lookup band
 | ||||
|   auto bands_model = configuration_dialog_.bands (); | ||||
|   ui_->band_combo_box->setCurrentText (bands_model->data (bands_model->find (f)).toString ()); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_sync_push_button_clicked (bool /* checked */) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_sync_push_button_clicked"; | ||||
| 
 | ||||
|   auto model = configuration_dialog_.frequencies (); | ||||
|   auto model_index = model->index (ui_->band_combo_box->currentIndex (), 0); | ||||
|   desired_frequency_ = model->data (model_index, Qt::UserRole + 1).value<Frequency> (); | ||||
| 
 | ||||
|   sync_rig (); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_mode_combo_box_activated (int index) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_vfo_A_mode_combo_box_activated: " << static_cast<Transceiver::MODE> (index); | ||||
| 
 | ||||
|   // reset combo box back to current mode and let status update do the actual change
 | ||||
|   ui_->mode_combo_box->setCurrentIndex (rig_state_.mode ()); | ||||
| 
 | ||||
|   Q_EMIT configuration_dialog_.transceiver_mode (static_cast<Transceiver::MODE> (index)); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_TX_offset_spin_box_valueChanged (int value) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_TX_offset_spin_box_editingFinished: " << value; | ||||
| 
 | ||||
|   Q_EMIT new_tx_frequency (rig_state_.frequency () + value); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_PTT_push_button_clicked (bool checked) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_PTT_push_button_clicked: " << (checked ? "true" : "false"); | ||||
| 
 | ||||
|   // reset button and let status update do the actual checking
 | ||||
|   ui_->PTT_push_button->setChecked (rig_state_.ptt ()); | ||||
| 
 | ||||
|   Q_EMIT configuration_dialog_.transceiver_ptt (checked); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::on_split_push_button_clicked (bool checked) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::on_split_push_button_clicked: " << (checked ? "true" : "false"); | ||||
| 
 | ||||
|   // reset button and let status update do the actual checking
 | ||||
|   ui_->split_push_button->setChecked (rig_state_.split ()); | ||||
| 
 | ||||
|   if (checked) | ||||
|     { | ||||
|       Q_EMIT new_tx_frequency (rig_state_.frequency () + ui_->TX_offset_spin_box->value ()); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       Q_EMIT new_tx_frequency (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::sync_rig () | ||||
| { | ||||
|   if (!updating_models_) | ||||
|     { | ||||
|       if (configuration_dialog_.transceiver_online (true)) | ||||
|         { | ||||
|           if (configuration_dialog_.split_mode ()) | ||||
|             { | ||||
|               Q_EMIT new_frequency (desired_frequency_); | ||||
|               Q_EMIT new_tx_frequency (desired_frequency_ + ui_->TX_offset_spin_box->value ()); | ||||
|             } | ||||
|           else | ||||
|             { | ||||
|               Q_EMIT new_frequency (desired_frequency_); | ||||
|               Q_EMIT new_tx_frequency (); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::handle_transceiver_update (Transceiver::TransceiverState s) | ||||
| { | ||||
|   rig_state_ = s; | ||||
| 
 | ||||
|   auto model = configuration_dialog_.frequencies (); | ||||
|   bool valid {false}; | ||||
|   for (int row = 0; row < model->rowCount (); ++row) | ||||
|     { | ||||
|       auto working_frequency = model->data (model->index (row, 0), Qt::UserRole + 1).value<Frequency> (); | ||||
|       if (std::abs (static_cast<Radio::FrequencyDelta> (working_frequency - s.frequency ())) < 10000) | ||||
|         { | ||||
|           valid = true; | ||||
|         } | ||||
|     } | ||||
|   if (!valid) | ||||
|     { | ||||
|       ui_->vfo_0_lcd_number->setStyleSheet ("QLCDNumber {background-color: red;}"); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       ui_->vfo_0_lcd_number->setStyleSheet (QString {}); | ||||
|     } | ||||
| 
 | ||||
|   ui_->vfo_0_lcd_number->display (Radio::pretty_frequency_MHz_string (s.frequency ())); | ||||
| 
 | ||||
|   if (s.split ()) | ||||
|     { | ||||
|       ui_->vfo_1_lcd_number->display (Radio::pretty_frequency_MHz_string (s.tx_frequency ())); | ||||
| 
 | ||||
|       valid = false; | ||||
|       for (int row = 0; row < model->rowCount (); ++row) | ||||
|         { | ||||
|           auto working_frequency = model->data (model->index (row, 0), Qt::UserRole + 1).value<Frequency> (); | ||||
|           if (std::abs (static_cast<Radio::FrequencyDelta> (working_frequency - s.tx_frequency ())) < 10000) | ||||
|             { | ||||
|               valid = true; | ||||
|             } | ||||
|         } | ||||
|       if (!valid) | ||||
|         { | ||||
|           ui_->vfo_1_lcd_number->setStyleSheet ("QLCDNumber {background-color: red;}"); | ||||
|         } | ||||
|       else | ||||
|         { | ||||
|           ui_->vfo_1_lcd_number->setStyleSheet (QString {}); | ||||
|         } | ||||
|       ui_->vfo_1_lcd_number->show (); | ||||
|       ui_->vfo_1_TX_push_button->show (); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       ui_->vfo_1_lcd_number->hide (); | ||||
|       ui_->vfo_1_TX_push_button->hide (); | ||||
|     } | ||||
| 
 | ||||
|   frequency_changed (s.frequency ()); | ||||
| 
 | ||||
|   ui_->radio_widget->setEnabled (s.online ()); | ||||
| 
 | ||||
|   ui_->mode_combo_box->setCurrentIndex (s.mode ()); | ||||
| 
 | ||||
|   RX_frequency_.setText (Radio::pretty_frequency_MHz_string (s.frequency ())); | ||||
|   TX_frequency_.setText (Radio::pretty_frequency_MHz_string (s.split () ? s.tx_frequency () : s.frequency ())); | ||||
| 
 | ||||
|   ui_->TX_button_group->button (s.split ())->setChecked (true); | ||||
| 
 | ||||
|   ui_->PTT_push_button->setChecked (s.ptt ()); | ||||
| 
 | ||||
|   ui_->split_push_button->setChecked (s.split ()); | ||||
| 
 | ||||
|   ui_->radio_widget->setEnabled (s.online ()); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::handle_transceiver_failure (QString reason) | ||||
| { | ||||
|   ui_->radio_widget->setEnabled (false); | ||||
| 
 | ||||
|   ui_->vfo_0_lcd_number->display (LCD_error); | ||||
|   ui_->vfo_1_lcd_number->display (LCD_error); | ||||
|   information_message_box ("Rig failure", reason); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::read_settings () | ||||
| { | ||||
|   settings_->beginGroup ("TestConfiguration"); | ||||
|   resize (settings_->value ("window/size", size ()).toSize ()); | ||||
|   move (settings_->value ("window/pos", pos ()).toPoint ()); | ||||
|   restoreState (settings_->value ("window/state", saveState ()).toByteArray ()); | ||||
|   ui_->band_combo_box->setCurrentText (settings_->value ("Band").toString ()); | ||||
|   settings_->endGroup (); | ||||
| 
 | ||||
|   settings_->beginGroup ("Configuration"); | ||||
|   callsign_ = settings_->value ("MyCall").toString (); | ||||
|   settings_->endGroup (); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::write_settings () | ||||
| { | ||||
|   settings_->beginGroup ("TestConfiguration"); | ||||
|   settings_->setValue ("window/size", size ()); | ||||
|   settings_->setValue ("window/pos", pos ()); | ||||
|   settings_->setValue ("window/state", saveState ()); | ||||
|   settings_->setValue ("Band", ui_->band_combo_box->currentText ()); | ||||
|   settings_->endGroup (); | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::load_models () | ||||
| { | ||||
|   updating_models_ = true; | ||||
| 
 | ||||
|   ui_->frequency_group_box->setTitle (configuration_dialog_.rig_name ()); | ||||
|   // if (auto rig = configuration_dialog_.rig (false)) // don't open radio
 | ||||
|   if (configuration_dialog_.transceiver_online (false)) // don't open radio
 | ||||
|     { | ||||
|       Q_EMIT configuration_dialog_.sync_transceiver (true); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       ui_->radio_widget->setEnabled (false); | ||||
|       ui_->vfo_0_lcd_number->display (Radio::pretty_frequency_MHz_string (static_cast<Frequency> (0))); | ||||
|       ui_->vfo_1_lcd_number->display (Radio::pretty_frequency_MHz_string (static_cast<Frequency> (0))); | ||||
|     } | ||||
| 
 | ||||
|   if (!configuration_dialog_.split_mode ()) | ||||
|     { | ||||
|       ui_->vfo_1_lcd_number->hide (); | ||||
|       ui_->vfo_1_TX_push_button->hide (); | ||||
|     } | ||||
| 
 | ||||
|   updating_models_ = false; | ||||
| } | ||||
| 
 | ||||
| void TestConfiguration::impl::information_message_box (QString const& reason, QString const& detail) | ||||
| { | ||||
|   qDebug () << "TestConfiguration::information_message_box: reason =" << reason << "detail =" << detail; | ||||
|   QMessageBox mb; | ||||
|   mb.setWindowFlags (mb.windowFlags () | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint); | ||||
|   mb.setText (reason); | ||||
|   if (!detail.isEmpty ()) | ||||
|     { | ||||
|       mb.setDetailedText (detail); | ||||
|     } | ||||
|   mb.setStandardButtons (QMessageBox::Ok); | ||||
|   mb.setDefaultButton (QMessageBox::Ok); | ||||
|   mb.setIcon (QMessageBox::Information); | ||||
|   mb.exec (); | ||||
| } | ||||
							
								
								
									
										21
									
								
								TestConfiguration.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,21 @@ | ||||
| #ifndef TEST_CONFIGURATION_HPP_ | ||||
| #define TEST_CONFIGURATION_HPP_ | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| class QString; | ||||
| class QSettings; | ||||
| class QWidget; | ||||
| 
 | ||||
| class TestConfiguration final | ||||
| { | ||||
|  public: | ||||
|   explicit TestConfiguration (QString const& instance_key, QSettings *, QWidget * parent = nullptr); | ||||
|   ~TestConfiguration (); | ||||
| 
 | ||||
|  private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										627
									
								
								TestConfiguration.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,627 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <ui version="4.0"> | ||||
|  <class>test_configuration_main_window</class> | ||||
|  <widget class="QMainWindow" name="test_configuration_main_window"> | ||||
|   <property name="geometry"> | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>550</width> | ||||
|     <height>227</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
|    <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> | ||||
|     <horstretch>0</horstretch> | ||||
|     <verstretch>0</verstretch> | ||||
|    </sizepolicy> | ||||
|   </property> | ||||
|   <property name="minimumSize"> | ||||
|    <size> | ||||
|     <width>550</width> | ||||
|     <height>227</height> | ||||
|    </size> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Configuration Test</string> | ||||
|   </property> | ||||
|   <widget class="QWidget" name="centralwidget"> | ||||
|    <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|     <item> | ||||
|      <widget class="QWidget" name="radio_widget" native="true"> | ||||
|       <property name="enabled"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|       <property name="sizePolicy"> | ||||
|        <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> | ||||
|         <horstretch>1</horstretch> | ||||
|         <verstretch>0</verstretch> | ||||
|        </sizepolicy> | ||||
|       </property> | ||||
|       <layout class="QGridLayout" name="gridLayout"> | ||||
|        <item row="0" column="0"> | ||||
|         <widget class="QGroupBox" name="frequency_group_box"> | ||||
|          <property name="enabled"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Preferred" vsizetype="Expanding"> | ||||
|            <horstretch>1</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="title"> | ||||
|           <string>Frequency</string> | ||||
|          </property> | ||||
|          <layout class="QGridLayout" name="gridLayout_2"> | ||||
|           <item row="0" column="0"> | ||||
|            <widget class="QLCDNumber" name="vfo_0_lcd_number"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> | ||||
|               <horstretch>1</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>11</pointsize> | ||||
|              </font> | ||||
|             </property> | ||||
|             <property name="digitCount"> | ||||
|              <number>12</number> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="1" column="0"> | ||||
|            <widget class="QLCDNumber" name="vfo_1_lcd_number"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding"> | ||||
|               <horstretch>1</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="font"> | ||||
|              <font> | ||||
|               <pointsize>11</pointsize> | ||||
|              </font> | ||||
|             </property> | ||||
|             <property name="digitCount"> | ||||
|              <number>12</number> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="0" column="1"> | ||||
|            <widget class="QPushButton" name="vfo_0_TX_push_button"> | ||||
|             <property name="enabled"> | ||||
|              <bool>false</bool> | ||||
|             </property> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="minimumSize"> | ||||
|              <size> | ||||
|               <width>20</width> | ||||
|               <height>20</height> | ||||
|              </size> | ||||
|             </property> | ||||
|             <property name="maximumSize"> | ||||
|              <size> | ||||
|               <width>20</width> | ||||
|               <height>20</height> | ||||
|              </size> | ||||
|             </property> | ||||
|             <property name="styleSheet"> | ||||
|              <string notr="true">QPushButton { | ||||
|     border: 2px solid #8f8f91; | ||||
|     border-radius: 6px; | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #f6f7fa, stop: 1 #dadbde); | ||||
|     min-width: 16px; | ||||
| } | ||||
| 
 | ||||
| QPushButton:checked { | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #ff2a2e, stop: 1 #ffc6ca); | ||||
| } | ||||
| 
 | ||||
| QPushButton:flat { | ||||
|     border: none; /* no border for a flat push button */ | ||||
| } | ||||
| 
 | ||||
| QPushButton:default { | ||||
|     border-color: navy; /* make the default button prominent */ | ||||
| }</string> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string/> | ||||
|             </property> | ||||
|             <property name="checkable"> | ||||
|              <bool>true</bool> | ||||
|             </property> | ||||
|             <property name="checked"> | ||||
|              <bool>true</bool> | ||||
|             </property> | ||||
|             <property name="autoExclusive"> | ||||
|              <bool>true</bool> | ||||
|             </property> | ||||
|             <attribute name="buttonGroup"> | ||||
|              <string notr="true">TX_button_group</string> | ||||
|             </attribute> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="1" column="1"> | ||||
|            <widget class="QPushButton" name="vfo_1_TX_push_button"> | ||||
|             <property name="enabled"> | ||||
|              <bool>false</bool> | ||||
|             </property> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="minimumSize"> | ||||
|              <size> | ||||
|               <width>20</width> | ||||
|               <height>20</height> | ||||
|              </size> | ||||
|             </property> | ||||
|             <property name="maximumSize"> | ||||
|              <size> | ||||
|               <width>20</width> | ||||
|               <height>20</height> | ||||
|              </size> | ||||
|             </property> | ||||
|             <property name="styleSheet"> | ||||
|              <string notr="true">QPushButton { | ||||
|     border: 2px solid #8f8f91; | ||||
|     border-radius: 6px; | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #f6f7fa, stop: 1 #dadbde); | ||||
|     min-width: 16px; | ||||
| } | ||||
| 
 | ||||
| QPushButton:checked { | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #ff2a2e, stop: 1 #ffc6ca); | ||||
| } | ||||
| 
 | ||||
| QPushButton:flat { | ||||
|     border: none; /* no border for a flat push button */ | ||||
| } | ||||
| 
 | ||||
| QPushButton:default { | ||||
|     border-color: navy; /* make the default button prominent */ | ||||
| }</string> | ||||
|             </property> | ||||
|             <property name="text"> | ||||
|              <string/> | ||||
|             </property> | ||||
|             <property name="checkable"> | ||||
|              <bool>true</bool> | ||||
|             </property> | ||||
|             <property name="checked"> | ||||
|              <bool>false</bool> | ||||
|             </property> | ||||
|             <property name="autoExclusive"> | ||||
|              <bool>true</bool> | ||||
|             </property> | ||||
|             <attribute name="buttonGroup"> | ||||
|              <string notr="true">TX_button_group</string> | ||||
|             </attribute> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="0" column="1"> | ||||
|         <widget class="QGroupBox" name="mode_group_box"> | ||||
|          <property name="sizePolicy"> | ||||
|           <sizepolicy hsizetype="Minimum" vsizetype="Expanding"> | ||||
|            <horstretch>0</horstretch> | ||||
|            <verstretch>0</verstretch> | ||||
|           </sizepolicy> | ||||
|          </property> | ||||
|          <property name="title"> | ||||
|           <string>Mode</string> | ||||
|          </property> | ||||
|          <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|           <item> | ||||
|            <widget class="QComboBox" name="mode_combo_box"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
|             </property> | ||||
|             <property name="currentIndex"> | ||||
|              <number>0</number> | ||||
|             </property> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>Unknown</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>CW</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>CW_R</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>USB</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>LSB</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>FSK</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>FSK_R</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>DIG_U</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>DIG_L</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>AM</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>FM</string> | ||||
|              </property> | ||||
|             </item> | ||||
|             <item> | ||||
|              <property name="text"> | ||||
|               <string>DIG_FM</string> | ||||
|              </property> | ||||
|             </item> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item row="0" column="2"> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||
|          <item> | ||||
|           <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||||
|            <item> | ||||
|             <spacer name="horizontalSpacer"> | ||||
|              <property name="orientation"> | ||||
|               <enum>Qt::Horizontal</enum> | ||||
|              </property> | ||||
|              <property name="sizeHint" stdset="0"> | ||||
|               <size> | ||||
|                <width>40</width> | ||||
|                <height>20</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </spacer> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="PTT_push_button"> | ||||
|              <property name="styleSheet"> | ||||
|               <string notr="true">QPushButton { | ||||
|     border: 2px solid #8f8f91; | ||||
|     border-radius: 6px; | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #9aff9a, stop: 1 #2aff2e); | ||||
|     min-width: 80px; | ||||
| } | ||||
| 
 | ||||
| QPushButton:checked { | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #ff2b2e, stop: 1 #ff979a); | ||||
| } | ||||
| 
 | ||||
| QPushButton:flat { | ||||
|     border: none; /* no border for a flat push button */ | ||||
| } | ||||
| 
 | ||||
| QPushButton:default { | ||||
|     border-color: navy; /* make the default button prominent */ | ||||
| }</string> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>PTT</string> | ||||
|              </property> | ||||
|              <property name="checkable"> | ||||
|               <bool>true</bool> | ||||
|              </property> | ||||
|              <property name="checked"> | ||||
|               <bool>false</bool> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <spacer name="horizontalSpacer_2"> | ||||
|              <property name="orientation"> | ||||
|               <enum>Qt::Horizontal</enum> | ||||
|              </property> | ||||
|              <property name="sizeHint" stdset="0"> | ||||
|               <size> | ||||
|                <width>40</width> | ||||
|                <height>20</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </spacer> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|          <item> | ||||
|           <spacer name="verticalSpacer"> | ||||
|            <property name="orientation"> | ||||
|             <enum>Qt::Vertical</enum> | ||||
|            </property> | ||||
|            <property name="sizeType"> | ||||
|             <enum>QSizePolicy::Expanding</enum> | ||||
|            </property> | ||||
|            <property name="sizeHint" stdset="0"> | ||||
|             <size> | ||||
|              <width>0</width> | ||||
|              <height>0</height> | ||||
|             </size> | ||||
|            </property> | ||||
|           </spacer> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|            <item> | ||||
|             <spacer name="horizontalSpacer_3"> | ||||
|              <property name="orientation"> | ||||
|               <enum>Qt::Horizontal</enum> | ||||
|              </property> | ||||
|              <property name="sizeHint" stdset="0"> | ||||
|               <size> | ||||
|                <width>40</width> | ||||
|                <height>20</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </spacer> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QPushButton" name="split_push_button"> | ||||
|              <property name="styleSheet"> | ||||
|               <string notr="true">QPushButton { | ||||
|     border: 2px solid #8f8f91; | ||||
|     border-radius: 6px; | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #9aff9a, stop: 1 #2aff2e); | ||||
|     min-width: 80px; | ||||
| } | ||||
| 
 | ||||
| QPushButton:checked { | ||||
|     background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, | ||||
|                                       stop: 0 #ff2b2e, stop: 1 #ff979a); | ||||
| } | ||||
| 
 | ||||
| QPushButton:flat { | ||||
|     border: none; /* no border for a flat push button */ | ||||
| } | ||||
| 
 | ||||
| QPushButton:default { | ||||
|     border-color: navy; /* make the default button prominent */ | ||||
| }</string> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Split</string> | ||||
|              </property> | ||||
|              <property name="checkable"> | ||||
|               <bool>true</bool> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <spacer name="horizontalSpacer_4"> | ||||
|              <property name="orientation"> | ||||
|               <enum>Qt::Horizontal</enum> | ||||
|              </property> | ||||
|              <property name="sizeHint" stdset="0"> | ||||
|               <size> | ||||
|                <width>40</width> | ||||
|                <height>20</height> | ||||
|               </size> | ||||
|              </property> | ||||
|             </spacer> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|          <item> | ||||
|           <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="TX_offset_label"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="text"> | ||||
|               <string>Split Offset:</string> | ||||
|              </property> | ||||
|              <property name="buddy"> | ||||
|               <cstring>TX_offset_spin_box</cstring> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QSpinBox" name="TX_offset_spin_box"> | ||||
|              <property name="sizePolicy"> | ||||
|               <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||
|                <horstretch>0</horstretch> | ||||
|                <verstretch>0</verstretch> | ||||
|               </sizepolicy> | ||||
|              </property> | ||||
|              <property name="suffix"> | ||||
|               <string> Hz</string> | ||||
|              </property> | ||||
|              <property name="minimum"> | ||||
|               <number>-10000</number> | ||||
|              </property> | ||||
|              <property name="maximum"> | ||||
|               <number>10000</number> | ||||
|              </property> | ||||
|              <property name="singleStep"> | ||||
|               <number>100</number> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </item> | ||||
|       </layout> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item> | ||||
|      <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||||
|       <item> | ||||
|        <widget class="QLabel" name="band_label"> | ||||
|         <property name="text"> | ||||
|          <string>Band:</string> | ||||
|         </property> | ||||
|         <property name="buddy"> | ||||
|          <cstring>band_combo_box</cstring> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QComboBox" name="band_combo_box"> | ||||
|         <property name="sizePolicy"> | ||||
|          <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | ||||
|           <horstretch>0</horstretch> | ||||
|           <verstretch>0</verstretch> | ||||
|          </sizepolicy> | ||||
|         </property> | ||||
|         <property name="minimumSize"> | ||||
|          <size> | ||||
|           <width>100</width> | ||||
|           <height>0</height> | ||||
|          </size> | ||||
|         </property> | ||||
|         <property name="editable"> | ||||
|          <bool>true</bool> | ||||
|         </property> | ||||
|         <property name="insertPolicy"> | ||||
|          <enum>QComboBox::NoInsert</enum> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|       <item> | ||||
|        <spacer name="horizontalSpacer_5"> | ||||
|         <property name="orientation"> | ||||
|          <enum>Qt::Horizontal</enum> | ||||
|         </property> | ||||
|         <property name="sizeHint" stdset="0"> | ||||
|          <size> | ||||
|           <width>40</width> | ||||
|           <height>20</height> | ||||
|          </size> | ||||
|         </property> | ||||
|        </spacer> | ||||
|       </item> | ||||
|       <item> | ||||
|        <widget class="QPushButton" name="sync_push_button"> | ||||
|         <property name="text"> | ||||
|          <string>Sync</string> | ||||
|         </property> | ||||
|        </widget> | ||||
|       </item> | ||||
|      </layout> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|   <widget class="QMenuBar" name="menu_bar"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>0</x> | ||||
|      <y>0</y> | ||||
|      <width>550</width> | ||||
|      <height>20</height> | ||||
|     </rect> | ||||
|    </property> | ||||
|    <widget class="QMenu" name="file_menu"> | ||||
|     <property name="title"> | ||||
|      <string>&File</string> | ||||
|     </property> | ||||
|     <addaction name="exit_action"/> | ||||
|    </widget> | ||||
|    <widget class="QMenu" name="options_menu"> | ||||
|     <property name="title"> | ||||
|      <string>&Options</string> | ||||
|     </property> | ||||
|     <addaction name="configuration_action"/> | ||||
|    </widget> | ||||
|    <addaction name="file_menu"/> | ||||
|    <addaction name="options_menu"/> | ||||
|   </widget> | ||||
|   <widget class="QStatusBar" name="status_bar"/> | ||||
|   <widget class="QToolBar" name="toolBar"> | ||||
|    <property name="windowTitle"> | ||||
|     <string>toolBar</string> | ||||
|    </property> | ||||
|    <attribute name="toolBarArea"> | ||||
|     <enum>TopToolBarArea</enum> | ||||
|    </attribute> | ||||
|    <attribute name="toolBarBreak"> | ||||
|     <bool>false</bool> | ||||
|    </attribute> | ||||
|   </widget> | ||||
|   <action name="exit_action"> | ||||
|    <property name="text"> | ||||
|     <string>E&xit</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="configuration_action"> | ||||
|    <property name="text"> | ||||
|     <string>&Configuration</string> | ||||
|    </property> | ||||
|   </action> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>exit_action</sender> | ||||
|    <signal>triggered()</signal> | ||||
|    <receiver>test_configuration_main_window</receiver> | ||||
|    <slot>close()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>-1</x> | ||||
|      <y>-1</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>261</x> | ||||
|      <y>214</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
|  <buttongroups> | ||||
|   <buttongroup name="TX_button_group"/> | ||||
|  </buttongroups> | ||||
| </ui> | ||||
							
								
								
									
										118
									
								
								TraceFile.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,118 @@ | ||||
| #include "TraceFile.hpp" | ||||
| 
 | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| #include <QDebug> | ||||
| #include <QString> | ||||
| #include <QFile> | ||||
| #include <QTextStream> | ||||
| #include <QMessageLogContext> | ||||
| #include <QDateTime> | ||||
| #include <QMutex> | ||||
| #include <QMutexLocker> | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   QMutex lock; | ||||
| } | ||||
| 
 | ||||
| class TraceFile::impl | ||||
| { | ||||
| public: | ||||
|   impl (QString const& trace_file_path); | ||||
|   ~impl (); | ||||
| 
 | ||||
|   // no copying
 | ||||
|   impl (impl const&) = delete; | ||||
|   impl& operator = (impl const&) = delete; | ||||
| 
 | ||||
| private: | ||||
|   // write Qt messages to the diagnostic log file
 | ||||
|   static void message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg); | ||||
| 
 | ||||
|   QFile file_; | ||||
|   QTextStream stream_; | ||||
|   QTextStream * original_stream_; | ||||
|   QtMessageHandler original_handler_; | ||||
|   static QTextStream * current_stream_; | ||||
| }; | ||||
| 
 | ||||
| QTextStream * TraceFile::impl::current_stream_; | ||||
| 
 | ||||
| 
 | ||||
| // delegate to implementation class
 | ||||
| TraceFile::TraceFile (QString const& trace_file_path) | ||||
|   : m_ {trace_file_path} | ||||
| { | ||||
| } | ||||
| 
 | ||||
| TraceFile::~TraceFile () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| TraceFile::impl::impl (QString const& trace_file_path) | ||||
|   : file_ {trace_file_path} | ||||
|   , original_stream_ {current_stream_} | ||||
|   , original_handler_ {nullptr} | ||||
| { | ||||
|   // if the log file is writeable; initialise diagnostic logging to it
 | ||||
|   // for append and hook up the Qt global message handler
 | ||||
|   if (file_.open (QFile::WriteOnly | QFile::Append | QFile::Text)) | ||||
|     { | ||||
|       stream_.setDevice (&file_); | ||||
|       current_stream_ = &stream_; | ||||
|       original_handler_ = qInstallMessageHandler (message_handler); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TraceFile::impl::~impl () | ||||
| { | ||||
|   // unhook our message handler before the stream and file are destroyed
 | ||||
|   if (original_handler_) | ||||
|     { | ||||
|       qInstallMessageHandler (original_handler_); | ||||
|     } | ||||
|   current_stream_ = original_stream_; // revert to prior stream
 | ||||
| } | ||||
| 
 | ||||
| // write Qt messages to the diagnostic log file
 | ||||
| void TraceFile::impl::message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg) | ||||
| { | ||||
|   char const * severity; | ||||
|   switch (type) | ||||
|     { | ||||
|     case QtDebugMsg: | ||||
|       severity = "Debug"; | ||||
|       break; | ||||
| 
 | ||||
|     case QtWarningMsg: | ||||
|       severity = "Warning"; | ||||
|       break; | ||||
| 
 | ||||
|     case QtFatalMsg: | ||||
|       severity = "Fatal"; | ||||
|       break; | ||||
| 
 | ||||
|     default: | ||||
|       severity = "Critical"; | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|   { | ||||
|     // guard against multiple threads with overlapping messages
 | ||||
|     QMutexLocker guard (&lock); | ||||
|     Q_ASSERT_X (current_stream_, "TraceFile:message_handler", "no stream to write to"); | ||||
|     *current_stream_ | ||||
|       << QDateTime::currentDateTimeUtc ().toString () | ||||
|       << '(' << context.file << ':' << context.line /* << ", " << context.function */ << ')' | ||||
|       << severity << ": " << msg.trimmed () << endl; | ||||
|   } | ||||
| 
 | ||||
|   if (QtFatalMsg == type) | ||||
|     { | ||||
|       throw std::runtime_error {"Fatal Qt Error"}; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								TraceFile.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | ||||
| #ifndef TRACE_FILE_HPP_ | ||||
| #define TRACE_FILE_HPP_ | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| class QString; | ||||
| 
 | ||||
| class TraceFile final | ||||
| { | ||||
| public: | ||||
|   explicit TraceFile (QString const& TraceFile_file_path); | ||||
|   ~TraceFile (); | ||||
| 
 | ||||
|   // copying not allowed
 | ||||
|   TraceFile (TraceFile const&) = delete; | ||||
|   TraceFile& operator = (TraceFile const&) = delete; | ||||
| 
 | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										51
									
								
								Transceiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,51 @@ | ||||
| #include "Transceiver.hpp" | ||||
| 
 | ||||
| #include "moc_Transceiver.cpp" | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   struct init | ||||
|   { | ||||
|     init () | ||||
|     { | ||||
|       qRegisterMetaType<Transceiver::TransceiverState> ("Transceiver::TransceiverState"); | ||||
|       qRegisterMetaType<Transceiver::MODE> ("Transceiver::MODE"); | ||||
|     } | ||||
|   } static_initialization; | ||||
| } | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| 
 | ||||
| ENUM_QDEBUG_OPS_IMPL (Transceiver, MODE); | ||||
| 
 | ||||
| QDebug operator << (QDebug d, Transceiver::TransceiverState const& s) | ||||
| { | ||||
|   d.nospace () | ||||
|     << "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no") | ||||
|     << " Frequency {" << s.frequency_[0] << "Hz, " << s.frequency_[1] << "Hz} " << s.mode_ | ||||
|     << "; SPLIT: " << (Transceiver::TransceiverState::on == s.split_ ? "on" : Transceiver::TransceiverState::off == s.split_ ? "off" : "unknown") | ||||
|     << "; PTT: " << (s.ptt_ ? "on" : "off") | ||||
|     << ')'; | ||||
|   return d.space ();  | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| ENUM_QDATASTREAM_OPS_IMPL (Transceiver, MODE); | ||||
| 
 | ||||
| ENUM_CONVERSION_OPS_IMPL (Transceiver, MODE); | ||||
| 
 | ||||
| bool operator != (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs) | ||||
| { | ||||
|   return lhs.online_ != rhs.online_ | ||||
|     || lhs.frequency_[0] != rhs.frequency_[0] | ||||
|     || lhs.frequency_[1] != rhs.frequency_[1] | ||||
|     || lhs.mode_ != rhs.mode_ | ||||
|     || lhs.split_ != rhs.split_ | ||||
|     || lhs.ptt_ != rhs.ptt_; | ||||
| } | ||||
| 
 | ||||
| bool operator == (Transceiver::TransceiverState const& lhs, Transceiver::TransceiverState const& rhs) | ||||
| { | ||||
|   return !(lhs != rhs); | ||||
| } | ||||
							
								
								
									
										169
									
								
								Transceiver.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,169 @@ | ||||
| #ifndef TRANSCEIVER_HPP__ | ||||
| #define TRANSCEIVER_HPP__ | ||||
| 
 | ||||
| #include <QObject> | ||||
| 
 | ||||
| #include "qt_helpers.hpp" | ||||
| #include "Radio.hpp" | ||||
| 
 | ||||
| class QString; | ||||
| 
 | ||||
| //
 | ||||
| // Abstract Transceiver Interface
 | ||||
| //
 | ||||
| //  This is  the minimal  generic interface  to a  rig as  required by
 | ||||
| //  wsjtx.
 | ||||
| //
 | ||||
| // Responsibilities
 | ||||
| //
 | ||||
| //  Provides  Qt slots  to set  the frequency,  mode and  PTT of  some
 | ||||
| //  transceiver. They are Qt slots so  that they may be invoked across
 | ||||
| //  a thread boundary.
 | ||||
| //
 | ||||
| //  Provides a synchronisation Qt slot  which should be implemented in
 | ||||
| //  sub-classes in such a way that  normal operation of the rig is not
 | ||||
| //  disturbed.  This  is  intended  to   be  use  to  poll  rig  state
 | ||||
| //  periodically and changing  VFO to read the other  VFO frequency or
 | ||||
| //  mode for  example should  not be  done since  the operator  may be
 | ||||
| //  tuning the VFO at the time and would be surprised by an unprompted
 | ||||
| //  VFO change.
 | ||||
| //
 | ||||
| //  Provides a control interface using Qt  slots to start and stop the
 | ||||
| //  rig control and PTT connections.
 | ||||
| //
 | ||||
| //  These  are Qt  slots rather  than the  constructor and  destructor
 | ||||
| //  because   it   is   expected   that   the   concrete   Transceiver
 | ||||
| //  implementations will run in a  separate thread from where they are
 | ||||
| //  constructed.
 | ||||
| //
 | ||||
| //  Qt signals are defined to notify clients of asynchronous rig state
 | ||||
| //  changes and failures.  These can and are expected  to cross thread
 | ||||
| //  boundaries.
 | ||||
| //
 | ||||
| //  A  signal   finished()  is   defined  that   concrete  Transceiver
 | ||||
| //  implementations must emit when they are ripe for destruction. This
 | ||||
| //  is  intended to  be  used  by clients  that  move the  Transceiver
 | ||||
| //  instance to  a thread  and need  to use  QObject::deleteLater() to
 | ||||
| //  safely dispose of the Transceiver instance. Implementations should
 | ||||
| //  expect Qt  slot calls  after emitting  finished, it  is up  to the
 | ||||
| //  implementation whether these slot invocations are ignored.
 | ||||
| //
 | ||||
| class Transceiver | ||||
|   : public QObject | ||||
| { | ||||
|   Q_OBJECT; | ||||
|   Q_ENUMS (MODE); | ||||
| 
 | ||||
| public: | ||||
|   using Frequency = Radio::Frequency; | ||||
| 
 | ||||
| protected: | ||||
|   Transceiver () | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   virtual ~Transceiver () | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   enum MODE {UNK, CW, CW_R, USB, LSB, FSK, FSK_R, DIG_U, DIG_L, AM, FM, DIG_FM}; | ||||
| 
 | ||||
|   //
 | ||||
|   // Aggregation of all of the rig and PTT state accessible via this
 | ||||
|   // interface.
 | ||||
|   class TransceiverState | ||||
|   { | ||||
|   public: | ||||
|     TransceiverState () | ||||
|       : online_ {false} | ||||
|       , frequency_ {0, 0} | ||||
|       , mode_ {UNK} | ||||
|       , split_ {unknown} | ||||
|       , ptt_ {false} | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     bool online () const {return online_;} | ||||
|     Frequency frequency () const {return frequency_[0];} | ||||
|     Frequency tx_frequency () const {return frequency_[1];} | ||||
|     bool split () const {return on == split_;} | ||||
|     MODE mode () const {return mode_;} | ||||
|     bool ptt () const {return ptt_;} | ||||
| 
 | ||||
|     void online (bool state) {online_ = state;} | ||||
|     void frequency (Frequency f) {frequency_[0] = f;} | ||||
|     void tx_frequency (Frequency f) {frequency_[1] = f;} | ||||
|     void split (bool state) {split_ = state ? on : off;} | ||||
|     void mode (MODE m) {mode_ = m;} | ||||
|     void ptt (bool state) {ptt_ = state;} | ||||
| 
 | ||||
|   private: | ||||
|     bool online_; | ||||
|     Frequency frequency_[2];  // [0] -> Rx; [1] -> Other
 | ||||
|     MODE mode_; | ||||
|     enum {unknown, off, on} split_; | ||||
|     bool ptt_; | ||||
|     // Don't forget to update the debug print and != operator if you
 | ||||
|     // add more members here
 | ||||
| 
 | ||||
|     friend QDebug operator << (QDebug, TransceiverState const&); | ||||
|     friend bool operator != (TransceiverState const&, TransceiverState const&); | ||||
|   }; | ||||
| 
 | ||||
|   //
 | ||||
|   // The following slots and signals are expected to all run in the
 | ||||
|   // same thread which is not necessarily the main GUI thread. It is
 | ||||
|   // up to the client of the Transceiver class to organise the
 | ||||
|   // allocation to a thread and the lifetime of the object instances.
 | ||||
|   //
 | ||||
| 
 | ||||
|   // Connect and disconnect.
 | ||||
|   Q_SLOT virtual void start () noexcept = 0; | ||||
|   Q_SLOT virtual void stop () noexcept = 0; | ||||
| 
 | ||||
|   // Ready to be destroyed.
 | ||||
|   Q_SIGNAL void finished () const; | ||||
| 
 | ||||
|   // Set frequency in Hertz.
 | ||||
|   Q_SLOT virtual void frequency (Frequency) noexcept = 0; | ||||
| 
 | ||||
|   // Setting a non-zero TX frequency means split operation, the value
 | ||||
|   // zero means simplex operation.
 | ||||
|   //
 | ||||
|   // Rationalise_mode means ensure TX uses same mode as RX.
 | ||||
|   Q_SLOT virtual void tx_frequency (Frequency tx = 0, bool rationalise_mode = true) noexcept = 0; | ||||
| 
 | ||||
|   // Set mode.
 | ||||
|   // Rationalise means ensure TX uses same mode as RX.
 | ||||
|   Q_SLOT virtual void mode (MODE, bool rationalise = true) noexcept = 0; | ||||
| 
 | ||||
|   // Set/unset PTT.
 | ||||
|   Q_SLOT virtual void ptt (bool = true) noexcept = 0; | ||||
| 
 | ||||
|   // Attempt to re-synchronise or query state.
 | ||||
|   // Force_signal guarantees a update or failure signal.
 | ||||
|   Q_SLOT virtual void sync (bool force_signal = false) noexcept = 0; | ||||
| 
 | ||||
|   // asynchronous status updates
 | ||||
|   Q_SIGNAL void update (Transceiver::TransceiverState) const; | ||||
|   Q_SIGNAL void failure (QString reason) const; | ||||
| }; | ||||
| 
 | ||||
| Q_DECLARE_METATYPE (Transceiver::TransceiverState); | ||||
| Q_DECLARE_METATYPE (Transceiver::MODE); | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| ENUM_QDEBUG_OPS_DECL (Transceiver, MODE); | ||||
| 
 | ||||
| QDebug operator << (QDebug, Transceiver::TransceiverState const&); | ||||
| #endif | ||||
| 
 | ||||
| ENUM_QDATASTREAM_OPS_DECL (Transceiver, MODE); | ||||
| 
 | ||||
| ENUM_CONVERSION_OPS_DECL (Transceiver, MODE); | ||||
| 
 | ||||
| bool operator != (Transceiver::TransceiverState const&, Transceiver::TransceiverState const&); | ||||
| bool operator == (Transceiver::TransceiverState const&, Transceiver::TransceiverState const&); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										269
									
								
								TransceiverBase.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,269 @@ | ||||
| #include "TransceiverBase.hpp" | ||||
| 
 | ||||
| #include <exception> | ||||
| 
 | ||||
| #include <QString> | ||||
| 
 | ||||
| #include "pimpl_impl.hpp" | ||||
| 
 | ||||
| class TransceiverBase::impl final | ||||
| { | ||||
| public: | ||||
|   impl () | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   impl (impl const&) = delete; | ||||
|   impl& operator = (impl const&) = delete; | ||||
| 
 | ||||
|   TransceiverState state_; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| TransceiverBase::TransceiverBase () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| TransceiverBase::~TransceiverBase () | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::start () noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_stop (); | ||||
|           do_post_stop (); | ||||
|           m_->state_.online (false); | ||||
|         } | ||||
|       do_start (); | ||||
|       do_post_start (); | ||||
|       m_->state_.online (true); | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::stop () noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       do_stop (); | ||||
|       do_post_stop (); | ||||
|       m_->state_.online (false); | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
|   else | ||||
|     { | ||||
|       Q_EMIT finished (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::frequency (Frequency f) noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_frequency (f); | ||||
|           do_post_frequency (f); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::tx_frequency (Frequency tx, bool rationalise_mode) noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_tx_frequency (tx, rationalise_mode); | ||||
|           do_post_tx_frequency (tx); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::mode (MODE m, bool rationalise) noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_mode (m, rationalise); | ||||
|           do_post_mode (m); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::ptt (bool on) noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_ptt (on); | ||||
|           do_post_ptt (on); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::sync (bool force_signal) noexcept | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           do_sync (force_signal); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   if (!message.isEmpty ()) | ||||
|     { | ||||
|       offline (message); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_rx_frequency (Frequency rx) | ||||
| { | ||||
|   m_->state_.frequency (rx); | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_other_frequency (Frequency tx) | ||||
| { | ||||
|   m_->state_.tx_frequency (tx); | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_split (bool state) | ||||
| { | ||||
|   m_->state_.split (state); | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_mode (MODE m) | ||||
| { | ||||
|   m_->state_.mode (m); | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_PTT (bool state) | ||||
| { | ||||
|   m_->state_.ptt (state); | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::update_complete () | ||||
| { | ||||
|   if (do_pre_update ()) | ||||
|     { | ||||
|       Q_EMIT update (m_->state_); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TransceiverBase::offline (QString const& reason) | ||||
| { | ||||
|   QString message; | ||||
|   try | ||||
|     { | ||||
|       if (m_->state_.online ()) | ||||
|         { | ||||
|           m_->state_.online (false); | ||||
|           do_stop (); | ||||
|         } | ||||
|     } | ||||
|   catch (std::exception const& e) | ||||
|     { | ||||
|       message = e.what (); | ||||
|     } | ||||
|   catch (...) | ||||
|     { | ||||
|       message = "Unexpected rig error"; | ||||
|     } | ||||
|   Q_EMIT failure (reason + '\n' + message); | ||||
| } | ||||
| 
 | ||||
| auto TransceiverBase::state () const -> TransceiverState const& | ||||
| { | ||||
|   return m_->state_; | ||||
| } | ||||
							
								
								
									
										136
									
								
								TransceiverBase.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,136 @@ | ||||
| #ifndef TRANSCEIVER_BASE_HPP__ | ||||
| #define TRANSCEIVER_BASE_HPP__ | ||||
| 
 | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| #include "Transceiver.hpp" | ||||
| 
 | ||||
| #include "pimpl_h.hpp" | ||||
| 
 | ||||
| class QString; | ||||
| 
 | ||||
| //
 | ||||
| // Base Transceiver Implementation
 | ||||
| //
 | ||||
| //  Behaviour common to all Transceiver implementations.
 | ||||
| //
 | ||||
| // Collaborations
 | ||||
| //
 | ||||
| //  Implements the Transceiver abstract  interface as template methods
 | ||||
| //  and provides  a new abstract interface  with similar functionality
 | ||||
| //  (do_XXXXX operations). Provides and  calls abstract interface that
 | ||||
| //  gets  called post  the above  operations (do_post_XXXXX)  to allow
 | ||||
| //  caching implementation etc.
 | ||||
| //
 | ||||
| //  A  key factor  is  to  catch all  exceptions  thrown by  sub-class
 | ||||
| //  implementations where  the template method  is a Qt slot  which is
 | ||||
| //  therefore  likely  to  be  called   by  Qt  which  doesn't  handle
 | ||||
| //  exceptions. Any exceptions are converted to Transceiver::failure()
 | ||||
| //  signals.
 | ||||
| //
 | ||||
| //  Sub-classes update the stored state via a protected interface.
 | ||||
| //
 | ||||
| // Responsibilities:
 | ||||
| //
 | ||||
| //  Wrap incoming  Transceiver messages catching all  exceptions in Qt
 | ||||
| //  slot driven  messages and converting  them to Qt signals.  This is
 | ||||
| //  done because exceptions  make concrete Transceiver implementations
 | ||||
| //  simpler  to   write,  but  exceptions  cannot   cross  signal/slot
 | ||||
| //  boundaries  (especially across  threads).  This  also removes  any
 | ||||
| //  requirement for the client code to handle exceptions.
 | ||||
| //
 | ||||
| //  Maintain the  state of the  concrete Transceiver instance  that is
 | ||||
| //  passed back via  the Transceiver::update(TransceiverState) signal,
 | ||||
| //  it   is  still   the   responsibility   of  concrete   Transceiver
 | ||||
| //  implementations to emit  the state_change signal when  they have a
 | ||||
| //  status update.
 | ||||
| //
 | ||||
| //  Maintain    a   go/no-go    status   for    concrete   Transceiver
 | ||||
| //  implementations  ensuring only  a valid  sequence of  messages are
 | ||||
| //  passed. A concrete Transceiver instance  must be started before it
 | ||||
| //  can receive  messages, any exception thrown  takes the Transceiver
 | ||||
| //  offline.
 | ||||
| //
 | ||||
| //  Implements methods  that concrete Transceiver  implementations use
 | ||||
| //  to update the Transceiver state.  These do not signal state change
 | ||||
| //  to  clients  as  this  is   the  responsibility  of  the  concrete
 | ||||
| //  Transceiver implementation, thus allowing multiple state component
 | ||||
| //  updates to be signalled together if required.
 | ||||
| //
 | ||||
| class TransceiverBase | ||||
|   : public Transceiver | ||||
| { | ||||
| protected: | ||||
|   TransceiverBase (); | ||||
| 
 | ||||
| public: | ||||
|   ~TransceiverBase (); | ||||
| 
 | ||||
|   //
 | ||||
|   // Implement the Transceiver abstract interface.
 | ||||
|   //
 | ||||
|   void start () noexcept override final; | ||||
|   void stop () noexcept override final; | ||||
|   void frequency (Frequency rx) noexcept override final; | ||||
|   void tx_frequency (Frequency tx, bool rationalise_mode) noexcept override final; | ||||
|   void mode (MODE, bool rationalise) noexcept override final; | ||||
|   void ptt (bool) noexcept override final; | ||||
|   void sync (bool force_signal) noexcept override final; | ||||
| 
 | ||||
| protected: | ||||
|   //
 | ||||
|   // Error exception which is thrown to signal unexpected errors.
 | ||||
|   //
 | ||||
|   struct error | ||||
|     : public std::runtime_error | ||||
|   { | ||||
|     error (char const * msg) : std::runtime_error (msg) {} | ||||
|   }; | ||||
| 
 | ||||
|   // Template methods that sub classes implement to do what they need to do.
 | ||||
|   //
 | ||||
|   // These methods may throw exceptions to signal errors.
 | ||||
|   virtual void do_start () = 0; | ||||
|   virtual void do_post_start () {} | ||||
| 
 | ||||
|   virtual void do_stop () = 0; | ||||
|   virtual void do_post_stop () {} | ||||
| 
 | ||||
|   virtual void do_frequency (Frequency rx) = 0; | ||||
|   virtual void do_post_frequency (Frequency) {} | ||||
| 
 | ||||
|   virtual void do_tx_frequency (Frequency tx = 0, bool rationalise_mode = true) = 0; | ||||
|   virtual void do_post_tx_frequency (Frequency) {} | ||||
| 
 | ||||
|   virtual void do_mode (MODE, bool rationalise = true) = 0; | ||||
|   virtual void do_post_mode (MODE) {} | ||||
| 
 | ||||
|   virtual void do_ptt (bool = true) = 0; | ||||
|   virtual void do_post_ptt (bool) {} | ||||
| 
 | ||||
|   virtual void do_sync (bool force_signal = false) = 0; | ||||
| 
 | ||||
|   virtual bool do_pre_update () {return true;} | ||||
| 
 | ||||
|   // sub classes report rig state changes with these methods
 | ||||
|   void update_rx_frequency (Frequency); | ||||
|   void update_other_frequency (Frequency = 0); | ||||
|   void update_split (bool); | ||||
|   void update_mode (MODE); | ||||
|   void update_PTT (bool = true); | ||||
| 
 | ||||
|   // Calling this triggers the Transceiver::update(State) signal.
 | ||||
|   void update_complete (); | ||||
| 
 | ||||
|   // sub class may asynchronously take the rig offline by calling this
 | ||||
|   void offline (QString const& reason); | ||||
| 
 | ||||
|   // and query state with this one
 | ||||
|   TransceiverState const& state () const; | ||||
| 
 | ||||
| private: | ||||
|   class impl; | ||||
|   pimpl<impl> m_; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										301
									
								
								TransceiverFactory.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,301 @@ | ||||
| #include "TransceiverFactory.hpp" | ||||
| 
 | ||||
| #include <QMetaType> | ||||
| 
 | ||||
| #include "HamlibTransceiver.hpp" | ||||
| #include "DXLabSuiteCommanderTransceiver.hpp" | ||||
| #include "HRDTransceiver.hpp" | ||||
| #include "EmulateSplitTransceiver.hpp" | ||||
| 
 | ||||
| #if defined (WIN32) | ||||
| #include "OmniRigTransceiver.hpp" | ||||
| #endif | ||||
| 
 | ||||
| #include "moc_TransceiverFactory.cpp" | ||||
| 
 | ||||
| // we use the hamlib "Hamlib Dummy" transceiver for non-CAT radios,
 | ||||
| // this allows us to still use the hamlib PTT control features for a
 | ||||
| // unified PTT control solution
 | ||||
| 
 | ||||
| char const * const TransceiverFactory::basic_transceiver_name_ = "None"; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
|   struct init | ||||
|   { | ||||
|     init () | ||||
|     { | ||||
|       qRegisterMetaType<TransceiverFactory::DataBits> ("TransceiverFactory::DataBits"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::DataBits> ("TransceiverFactory::DataBits"); | ||||
|       qRegisterMetaType<TransceiverFactory::StopBits> ("TransceiverFactory::StopBits"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::StopBits> ("TransceiverFactory::StopBits"); | ||||
|       qRegisterMetaType<TransceiverFactory::Handshake> ("TransceiverFactory::Handshake"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::Handshake> ("TransceiverFactory::Handshake"); | ||||
|       qRegisterMetaType<TransceiverFactory::PTTMethod> ("TransceiverFactory::PTTMethod"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::PTTMethod> ("TransceiverFactory::PTTMethod"); | ||||
|       qRegisterMetaType<TransceiverFactory::TXAudioSource> ("TransceiverFactory::TXAudioSource"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::TXAudioSource> ("TransceiverFactory::TXAudioSource"); | ||||
|       qRegisterMetaType<TransceiverFactory::SplitMode> ("TransceiverFactory::SplitMode"); | ||||
|       qRegisterMetaTypeStreamOperators<TransceiverFactory::SplitMode> ("TransceiverFactory::SplitMode"); | ||||
|     } | ||||
|   } static_initializer; | ||||
| 
 | ||||
|   enum				// supported non-hamlib radio interfaces | ||||
|     { | ||||
|       NonHamlibBaseId = 9899 | ||||
|       , CommanderId | ||||
|       , HRDId | ||||
|       , OmniRigOneId | ||||
|       , OmniRigTwoId | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| TransceiverFactory::TransceiverFactory () | ||||
| { | ||||
|   HamlibTransceiver::register_transceivers (&transceivers_); | ||||
|   DXLabSuiteCommanderTransceiver::register_transceivers (&transceivers_, CommanderId); | ||||
|   HRDTransceiver::register_transceivers (&transceivers_, HRDId); | ||||
|    | ||||
| #if defined (WIN32) | ||||
|   // OmniRig is ActiveX/COM server so only on Windows
 | ||||
|   OmniRigTransceiver::register_transceivers (&transceivers_, OmniRigOneId, OmniRigTwoId); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| auto TransceiverFactory::supported_transceivers () const -> Transceivers const& | ||||
| { | ||||
|   return transceivers_; | ||||
| } | ||||
| 
 | ||||
| auto TransceiverFactory::CAT_port_type (QString const& name) const -> Capabilities::PortType | ||||
| { | ||||
|   return supported_transceivers ()[name].port_type_; | ||||
| } | ||||
| 
 | ||||
| bool TransceiverFactory::has_CAT_PTT (QString const& name) const | ||||
| { | ||||
|   return | ||||
|     supported_transceivers ()[name].has_CAT_PTT_ | ||||
|     || supported_transceivers ()[name].model_number_ > NonHamlibBaseId; | ||||
| } | ||||
| 
 | ||||
| bool TransceiverFactory::has_CAT_PTT_mic_data (QString const& name) const | ||||
| { | ||||
|   return supported_transceivers ()[name].has_CAT_PTT_mic_data_; | ||||
| } | ||||
| 
 | ||||
| bool TransceiverFactory::has_CAT_indirect_serial_PTT (QString const& name) const | ||||
| { | ||||
|   return supported_transceivers ()[name].has_CAT_indirect_serial_PTT_; | ||||
| } | ||||
| 
 | ||||
| bool TransceiverFactory::has_asynchronous_CAT (QString const& name) const | ||||
| { | ||||
|   return supported_transceivers ()[name].asynchronous_; | ||||
| } | ||||
| 
 | ||||
| std::unique_ptr<Transceiver> TransceiverFactory::create (QString const& name | ||||
| 							 , QString const& cat_port | ||||
| 							 , int cat_baud | ||||
| 							 , DataBits cat_data_bits | ||||
| 							 , StopBits cat_stop_bits | ||||
| 							 , Handshake cat_handshake | ||||
| 							 , bool cat_dtr_always_on | ||||
| 							 , bool cat_rts_always_on | ||||
| 							 , PTTMethod ptt_type | ||||
| 							 , TXAudioSource ptt_use_data_ptt | ||||
| 							 , SplitMode split_mode | ||||
| 							 , QString const& ptt_port | ||||
| 							 , int poll_interval | ||||
| 							 , QThread * target_thread) | ||||
| { | ||||
|   std::unique_ptr<Transceiver> result; | ||||
|   switch (supported_transceivers ()[name].model_number_) | ||||
|     { | ||||
|     case CommanderId: | ||||
|       { | ||||
| 	// we start with a dummy HamlibTransceiver object instance that can support direct PTT
 | ||||
| 	std::unique_ptr<TransceiverBase> basic_transceiver { | ||||
| 	  new HamlibTransceiver { | ||||
| 	    supported_transceivers ()[basic_transceiver_name_].model_number_ | ||||
| 	      , cat_port | ||||
| 	      , cat_baud | ||||
| 	      , cat_data_bits | ||||
| 	      , cat_stop_bits | ||||
| 	      , cat_handshake | ||||
| 	      , cat_dtr_always_on | ||||
| 	      , cat_rts_always_on | ||||
| 	      , ptt_type | ||||
| 	      , ptt_use_data_ptt | ||||
| 	      , "CAT" == ptt_port ? "" : ptt_port | ||||
| 	      } | ||||
| 	}; | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    basic_transceiver.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
| 
 | ||||
| 	// wrap the basic Transceiver object instance with a decorator object that talks to DX Lab Suite Commander
 | ||||
| 	result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), cat_port, PTT_method_CAT == ptt_type, poll_interval}); | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    result.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case HRDId: | ||||
|       { | ||||
| 	// we start with a dummy HamlibTransceiver object instance that can support direct PTT
 | ||||
| 	std::unique_ptr<TransceiverBase> basic_transceiver { | ||||
| 	  new HamlibTransceiver { | ||||
| 	    supported_transceivers ()[basic_transceiver_name_].model_number_ | ||||
| 	      , cat_port | ||||
| 	      , cat_baud | ||||
| 	      , cat_data_bits | ||||
| 	      , cat_stop_bits | ||||
| 	      , cat_handshake | ||||
| 	      , cat_dtr_always_on | ||||
| 	      , cat_rts_always_on | ||||
| 	      , ptt_type | ||||
| 	      , ptt_use_data_ptt | ||||
| 	      , "CAT" == ptt_port ? "" : ptt_port | ||||
| 	      } | ||||
| 	}; | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    basic_transceiver.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
| 
 | ||||
| 	// wrap the basic Transceiver object instance with a decorator object that talks to ham Radio Deluxe
 | ||||
| 	result.reset (new HRDTransceiver {std::move (basic_transceiver), cat_port, PTT_method_CAT == ptt_type, poll_interval}); | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    result.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
| #if defined (WIN32) | ||||
|     case OmniRigOneId: | ||||
|       { | ||||
| 	// we start with a dummy HamlibTransceiver object instance that can support direct PTT
 | ||||
| 	std::unique_ptr<TransceiverBase> basic_transceiver { | ||||
| 	  new HamlibTransceiver { | ||||
| 	    supported_transceivers ()[basic_transceiver_name_].model_number_ | ||||
| 	      , cat_port | ||||
| 	      , cat_baud | ||||
| 	      , cat_data_bits | ||||
| 	      , cat_stop_bits | ||||
| 	      , cat_handshake | ||||
| 	      , cat_dtr_always_on | ||||
| 	      , cat_rts_always_on | ||||
| 	      , ptt_type | ||||
| 	      , ptt_use_data_ptt | ||||
| 	      , "CAT" == ptt_port ? "" : ptt_port | ||||
| 	      } | ||||
| 	}; | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    basic_transceiver.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
| 
 | ||||
| 	// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig one
 | ||||
| 	result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, ptt_type, ptt_port}); | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    result.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
|       } | ||||
|       break; | ||||
| 
 | ||||
|     case OmniRigTwoId: | ||||
|       { | ||||
| 	// we start with a dummy HamlibTransceiver object instance that can support direct PTT
 | ||||
| 	std::unique_ptr<TransceiverBase> basic_transceiver { | ||||
| 	  new HamlibTransceiver { | ||||
| 	    supported_transceivers ()[basic_transceiver_name_].model_number_ | ||||
| 	      , cat_port | ||||
| 	      , cat_baud | ||||
| 	      , cat_data_bits | ||||
| 	      , cat_stop_bits | ||||
| 	      , cat_handshake | ||||
| 	      , cat_dtr_always_on | ||||
| 	      , cat_rts_always_on | ||||
| 	      , ptt_type | ||||
| 	      , ptt_use_data_ptt | ||||
| 	      , "CAT" == ptt_port ? "" : ptt_port | ||||
| 	      } | ||||
| 	}; | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    basic_transceiver.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
| 
 | ||||
| 	// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig two
 | ||||
| 	result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, ptt_type, ptt_port}); | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    result.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
|       } | ||||
|       break; | ||||
| #endif | ||||
| 
 | ||||
|     default: | ||||
|       result.reset (new HamlibTransceiver { | ||||
| 	  supported_transceivers ()[name].model_number_ | ||||
| 	    , cat_port | ||||
| 	    , cat_baud | ||||
| 	    , cat_data_bits | ||||
| 	    , cat_stop_bits | ||||
| 	    , cat_handshake | ||||
| 	    , cat_dtr_always_on | ||||
| 	    , cat_rts_always_on | ||||
| 	    , ptt_type | ||||
| 	    , ptt_use_data_ptt | ||||
| 	    , "CAT" == ptt_port ? cat_port : ptt_port | ||||
| 	    , poll_interval | ||||
| 	    }); | ||||
| 	if (target_thread) | ||||
| 	  { | ||||
| 	    result.get ()->moveToThread (target_thread); | ||||
| 	  } | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|   if (split_mode_emulate == split_mode) | ||||
|     { | ||||
|       // wrap the Transceiver object instance with a decorator that emulates split mode
 | ||||
|       result.reset (new EmulateSplitTransceiver {std::move (result)}); | ||||
|       if (target_thread) | ||||
| 	{ | ||||
| 	  result.get ()->moveToThread (target_thread); | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   return std::move (result); | ||||
| } | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, DataBits); | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, StopBits); | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, Handshake); | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, PTTMethod); | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_QDEBUG_OPS_IMPL (TransceiverFactory, SplitMode); | ||||
| #endif | ||||
| 
 | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, DataBits); | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, StopBits); | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, Handshake); | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, PTTMethod); | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_QDATASTREAM_OPS_IMPL (TransceiverFactory, SplitMode); | ||||
| 
 | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, DataBits); | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, StopBits); | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, Handshake); | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, PTTMethod); | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_CONVERSION_OPS_IMPL (TransceiverFactory, SplitMode); | ||||
							
								
								
									
										155
									
								
								TransceiverFactory.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,155 @@ | ||||
| #ifndef TRANSCEIVER_FACTORY_HPP__ | ||||
| #define TRANSCEIVER_FACTORY_HPP__ | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QMap> | ||||
| 
 | ||||
| #include "Transceiver.hpp" | ||||
| 
 | ||||
| #include "qt_helpers.hpp" | ||||
| 
 | ||||
| class QString; | ||||
| class QThread; | ||||
| 
 | ||||
| //
 | ||||
| // Transceiver Factory
 | ||||
| //
 | ||||
| class TransceiverFactory | ||||
|   : public QObject | ||||
| { | ||||
|   Q_OBJECT; | ||||
|   Q_ENUMS (DataBits StopBits Handshake PTTMethod TXAudioSource SplitMode); | ||||
| 
 | ||||
| private: | ||||
|   Q_DISABLE_COPY (TransceiverFactory); | ||||
| 
 | ||||
| public: | ||||
|   //
 | ||||
|   // Capabilities of a Transceiver that can be determined without
 | ||||
|   // actually instantiating one, these are for use in Configuration
 | ||||
|   // GUI behaviour determination
 | ||||
|   //
 | ||||
|   struct Capabilities | ||||
|   { | ||||
|     enum PortType {none, serial, network}; | ||||
| 
 | ||||
|     explicit Capabilities (int model_number = 0 | ||||
| 			   , PortType port_type = none | ||||
| 			   , bool has_CAT_PTT = false | ||||
| 			   , bool has_CAT_PTT_mic_data = false | ||||
| 			   , bool has_CAT_indirect_serial_PTT = false | ||||
| 			   , bool asynchronous = false) | ||||
|       : model_number_ {model_number} | ||||
|       , port_type_ {port_type} | ||||
|       , has_CAT_PTT_ {has_CAT_PTT} | ||||
|       , has_CAT_PTT_mic_data_ {has_CAT_PTT_mic_data} | ||||
|       , has_CAT_indirect_serial_PTT_ {has_CAT_indirect_serial_PTT} | ||||
|       , asynchronous_ {asynchronous} | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     int model_number_; | ||||
|     PortType port_type_; | ||||
|     bool has_CAT_PTT_; | ||||
|     bool has_CAT_PTT_mic_data_; | ||||
|     bool has_CAT_indirect_serial_PTT_; // OmniRig controls RTS/DTR via COM interface
 | ||||
|     bool asynchronous_; | ||||
|   }; | ||||
| 
 | ||||
|   //
 | ||||
|   // Dictionary of Transceiver types Capabilities
 | ||||
|   //
 | ||||
|   typedef QMap<QString, Capabilities> Transceivers; | ||||
| 
 | ||||
|   //
 | ||||
|   // various Transceiver parameters
 | ||||
|   //
 | ||||
|   enum DataBits {seven_data_bits = 7, eight_data_bits}; | ||||
|   enum StopBits {one_stop_bit = 1, two_stop_bits}; | ||||
|   enum Handshake {handshake_none, handshake_XonXoff, handshake_hardware}; | ||||
|   enum PTTMethod {PTT_method_VOX, PTT_method_CAT, PTT_method_DTR, PTT_method_RTS}; | ||||
|   enum TXAudioSource {TX_audio_source_front, TX_audio_source_rear}; | ||||
|   enum SplitMode {split_mode_none, split_mode_rig, split_mode_emulate}; | ||||
| 
 | ||||
|   TransceiverFactory (); | ||||
| 
 | ||||
|   static char const * const basic_transceiver_name_; // dummy transceiver is basic model
 | ||||
| 
 | ||||
|   //
 | ||||
|   // fetch all supported rigs as a list of name and model id
 | ||||
|   //
 | ||||
|   Transceivers const& supported_transceivers () const; | ||||
| 
 | ||||
|   // supported model queries
 | ||||
|   Capabilities::PortType CAT_port_type (QString const& name) const; // how to talk to CAT
 | ||||
|   bool has_CAT_PTT (QString const& name) const;	// can be keyed via CAT
 | ||||
|   bool has_CAT_PTT_mic_data (QString const& name) const; // Tx audio port is switchable via CAT
 | ||||
|   bool has_CAT_indirect_serial_PTT (QString const& name) const; // Can PTT via CAT port use DTR or RTS (OmniRig for example)
 | ||||
|   bool has_asynchronous_CAT (QString const& name) const; // CAT asynchronous rather than polled
 | ||||
| 
 | ||||
|   // make a new Transceiver instance
 | ||||
|   //
 | ||||
|   // cat_port, cat_baud, cat_data_bits, cat_stop_bits, cat_handshake,
 | ||||
|   // cat_dtr_alway_on, cat_rts_always_on are only relevant to
 | ||||
|   // interfaces that are served by hamlib
 | ||||
|   //
 | ||||
|   // PTT port and to some extent ptt_type are independent of interface
 | ||||
|   // type
 | ||||
|   //
 | ||||
|   std::unique_ptr<Transceiver> create (QString const& name // from supported_transceivers () key
 | ||||
| 				       , QString const& cat_port // serial port device name or empty
 | ||||
| 				       , int cat_baud | ||||
| 				       , DataBits cat_data_bits | ||||
| 				       , StopBits cat_stop_bits | ||||
| 				       , Handshake cat_handshake | ||||
| 				       , bool cat_dtr_always_on	// to power interface
 | ||||
| 				       , bool cat_rts_always_on	// to power inteface
 | ||||
| 				       , PTTMethod ptt_type // "CAT" | "DTR" | "RTS" | "VOX"
 | ||||
| 				       , TXAudioSource ptt_use_data_ptt	// some rigs allow audio routing to Mic/Data connector
 | ||||
| 				       , SplitMode split_mode // how to support split TX mode
 | ||||
| 				       , QString const& ptt_port // serial port device name or special value "CAT"
 | ||||
| 				       , int poll_interval // in milliseconds for interfaces that require polling for parameter changes
 | ||||
| 				       , QThread * target_thread = nullptr | ||||
| 				       ); | ||||
| 
 | ||||
| private: | ||||
|     Transceivers transceivers_; | ||||
| }; | ||||
| 
 | ||||
| //
 | ||||
| // boilerplate routines to make enum types useable and debuggable in
 | ||||
| // Qt
 | ||||
| //
 | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::DataBits); | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::StopBits); | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::Handshake); | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::PTTMethod); | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::TXAudioSource); | ||||
| Q_DECLARE_METATYPE (TransceiverFactory::SplitMode); | ||||
| 
 | ||||
| #if !defined (QT_NO_DEBUG_STREAM) | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, DataBits); | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, StopBits); | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, Handshake); | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, PTTMethod); | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_QDEBUG_OPS_DECL (TransceiverFactory, SplitMode); | ||||
| #endif | ||||
| 
 | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, DataBits); | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, StopBits); | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, Handshake); | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, PTTMethod); | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_QDATASTREAM_OPS_DECL (TransceiverFactory, SplitMode); | ||||
| 
 | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, DataBits); | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, StopBits); | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, Handshake); | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, PTTMethod); | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, TXAudioSource); | ||||
| ENUM_CONVERSION_OPS_DECL (TransceiverFactory, SplitMode); | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										10
									
								
								Versions.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | ||||
| # Version number components | ||||
| set (WSJTX_VERSION_MAJOR 1) | ||||
| set (WSJTX_VERSION_MINOR 4) | ||||
| set (WSJTX_VERSION_PATCH 0) | ||||
| #set (WSJTX_RC 1) | ||||
| set (WSJTX_VERSION_IS_RELEASE 0) | ||||
| 
 | ||||
| set (CONFIG_TEST_VERSION_MAJOR 0) | ||||
| set (CONFIG_TEST_VERSION_MINOR 2) | ||||
| set (CONFIG_TEST_VERSION_PATCH 13) | ||||
| @ -1,6 +1,8 @@ | ||||
| #include "about.h" | ||||
| #include "ui_about.h" | ||||
| 
 | ||||
| #include "moc_about.cpp" | ||||
| 
 | ||||
| CAboutDlg::CAboutDlg(QWidget *parent, QString Revision) : | ||||
|   QDialog(parent), | ||||
|   m_Revision(Revision), | ||||
|  | ||||
							
								
								
									
										74
									
								
								about.ui
									
									
									
									
									
								
							
							
						
						| @ -10,7 +10,7 @@ | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>374</width> | ||||
|     <height>164</height> | ||||
|     <height>144</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="sizePolicy"> | ||||
| @ -24,17 +24,75 @@ | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="labelTxt"> | ||||
|      <property name="text"> | ||||
|       <string/> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|      <item> | ||||
|       <widget class="QLabel" name="labelTxt"> | ||||
|        <property name="text"> | ||||
|         <string/> | ||||
|        </property> | ||||
|        <property name="alignment"> | ||||
|         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item> | ||||
|     <spacer name="verticalSpacer"> | ||||
|      <property name="orientation"> | ||||
|       <enum>Qt::Vertical</enum> | ||||
|      </property> | ||||
|      <property name="alignment"> | ||||
|       <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||||
|      <property name="sizeHint" stdset="0"> | ||||
|       <size> | ||||
|        <width>20</width> | ||||
|        <height>40</height> | ||||
|       </size> | ||||
|      </property> | ||||
|     </widget> | ||||
|     </spacer> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <item> | ||||
|       <spacer name="horizontalSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Horizontal</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>20</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="okButton"> | ||||
|        <property name="text"> | ||||
|         <string>OK</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
|  <connections> | ||||
|   <connection> | ||||
|    <sender>okButton</sender> | ||||
|    <signal>clicked()</signal> | ||||
|    <receiver>CAboutDlg</receiver> | ||||
|    <slot>accept()</slot> | ||||
|    <hints> | ||||
|     <hint type="sourcelabel"> | ||||
|      <x>321</x> | ||||
|      <y>120</y> | ||||
|     </hint> | ||||
|     <hint type="destinationlabel"> | ||||
|      <x>186</x> | ||||
|      <y>71</y> | ||||
|     </hint> | ||||
|    </hints> | ||||
|   </connection> | ||||
|  </connections> | ||||
| </ui> | ||||
|  | ||||
							
								
								
									
										143
									
								
								artwork/DragNDrop Background.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,143 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    id="Layer_1" | ||||
|    space="preserve" | ||||
|    sodipodi:version="0.32" | ||||
|    viewBox="0 0 640 480" | ||||
|    version="1.1" | ||||
|    sodipodi:docname="tasto_5_architetto_franc_01.svg" | ||||
|    inkscape:version="0.48.4 r9939" | ||||
|    width="100%" | ||||
|    height="100%"> | ||||
|   <defs | ||||
|      id="defs6110" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      pagecolor="#ffffff" | ||||
|      showgrid="false" | ||||
|      inkscape:zoom="1.89375" | ||||
|      inkscape:cx="227.06271" | ||||
|      inkscape:cy="240" | ||||
|      inkscape:window-width="1855" | ||||
|      inkscape:window-height="1056" | ||||
|      inkscape:window-x="65" | ||||
|      inkscape:window-y="24" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:current-layer="g3" /> | ||||
|   <g | ||||
|      id="g3" | ||||
|      transform="translate(0,342.62101)"> | ||||
|     <radialGradient | ||||
|        id="XMLID_2_" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        cy="42.188999" | ||||
|        cx="42.785999" | ||||
|        r="69.833" | ||||
|        gradientTransform="translate(237.98657,-269.79195)"> | ||||
|       <stop | ||||
|          id="stop6" | ||||
|          stop-color="#D1F593" | ||||
|          offset="0" /> | ||||
|       <stop | ||||
|          id="stop8" | ||||
|          stop-color="#81CE09" | ||||
|          offset="1" /> | ||||
|     </radialGradient> | ||||
|     <path | ||||
|        id="path10" | ||||
|        d="m 294.86757,-261.31295 v 11.564 c 0,1.741 1.297,11.914 0,13.039 -1.02,0.886 -6.762,0 -8.05,0 h -18.478 c -7.957,0 -22.38,-2.722 -27.218,5.536 -3.929,6.711 -1.526,19.418 -1.526,26.711 0,8.382 -0.774,17.167 0,25.519 0,13.355 15.573,11.178 24.427,11.178 h 30.258 c 1.235,0 0.587,21.391 0.587,24.12 0,9.468 9.867,13.694 16.696,6.885 5.979,-5.956 11.956,-11.914 17.934,-17.874 12.723,-12.681 27.184,-24.79 38.705,-38.581 8.129,-8.157 -0.109,-15.919 -5.847,-21.664 -7.818,-7.824 -15.637,-15.65 -23.456,-23.475 -7.681,-7.69 -15.364,-15.38 -23.047,-23.068 -4.98,-4.985 -11.292,-11.356 -18.616,-5 -1.493,1.285 -2.366,3.107 -2.366,5.097" | ||||
|        inkscape:connector-curvature="0" | ||||
|        style="fill:url(#XMLID_2_)" /> | ||||
|     <path | ||||
|        id="path12" | ||||
|        d="m 239.59557,-225.52895 v 25.519 c 0,7.292 -2.404,20 1.526,26.707 4.839,8.26 19.258,5.537 27.214,5.537 h 17.257 c 1.47,0 8.123,-1.016 9.274,0 1.14,1.006 0,9.882 0,11.396 v 14.252 c 0,10.854 12.136,9.904 17.771,4.287 15.045,-14.996 30.09,-29.993 45.136,-44.991 5.456,-5.438 15.865,-12.589 13.176,-21.487 -0.317,0.598 -3.726,2.826 -4.396,3.495 -3.066,3.059 -6.136,6.117 -9.201,9.174 -8.52,8.492 -17.037,16.985 -25.555,25.477 -5.672,5.654 -11.342,11.308 -17.012,16.961 -2.447,2.439 -6.51,4.557 -9.908,2.681 -4.008,-2.21 -2.962,-8.699 -2.962,-12.543 v -15.579 c 0,-2.856 -2.156,-1.731 -4.79,-1.731 h -33.59 c -8.25,0 -16.507,-0.511 -16.507,-10.853 v -32.609 c 0,-4.972 -1.083,-11.582 0.677,-16.393 -4.732,1.342 -8.114,5.758 -8.114,10.688" | ||||
|        inkscape:connector-curvature="0" | ||||
|        style="fill:#67ba06" /> | ||||
|     <path | ||||
|        id="path14" | ||||
|        d="m 247.70757,-168.51195 c -2.771,-7.597 -0.677,-18.748 -0.677,-26.612 0,-8.349 -1.456,-18.099 0.173,-26.347 1.843,-9.349 18.409,-6.667 25.217,-6.667 h 29.23 c 0.768,0 -0.319,-21.606 0.808,-24.748 3.407,-9.491 12.963,0.134 16.668,3.857 15.269,15.341 30.538,30.681 45.805,46.022 1.224,1.231 5.246,3.916 6.019,5.379 2.872,-9.511 -10.045,-18.432 -15.802,-24.213 l -32.322,-32.461 c -5.208,-5.23 -12.42,-16.139 -21.006,-14.488 -11.102,2.133 -6.951,23.787 -6.951,31.819 -9.333,0 -18.663,-0.002 -27.996,-0.002 -9.16,-0.001 -27.276,-2.955 -27.276,11.18 -0.863,9.313 0,19.11 0,28.458 0,8.799 -3.183,25.591 8.112,28.823" | ||||
|        inkscape:connector-curvature="0" | ||||
|        style="fill:#b2ed21" /> | ||||
|     <path | ||||
|        id="path16" | ||||
|        d="m 299.44057,-268.80395 c -9.2,3.806 -5.476,22.395 -5.476,30.261 0,1.951 -23.229,0.93 -26.087,0.93 -8.576,0 -23.012,-2.757 -27.835,6.535 -3.627,6.99 -1.35,19.146 -1.35,26.616 0,8.4 -0.709,17.143 0,25.519 0,13.884 15.395,12.081 24.75,12.081 h 30.326 c 0.616,0 -0.196,23.651 0.363,26.81 1.344,7.601 11.873,10.134 17.171,4.83 1.235,-0.97 2.304,-2.297 3.414,-3.404 6.86,-6.837 13.72,-13.676 20.579,-20.514 7.706,-7.681 15.41,-15.36 23.116,-23.042 4.011,-3.997 10.436,-8.579 12.863,-13.906 3.874,-8.509 -2.996,-14.135 -8.281,-19.426 -7.462,-7.47 -14.926,-14.941 -22.389,-22.411 -7.348,-7.353 -14.693,-14.708 -22.041,-22.061 -5.328,-5.353 -10.368,-12.444 -19.115,-8.826 m -58.942,89.86 v -25.519 c 0,-7.083 -2.537,-20.456 1.757,-26.809 4.938,-7.304 19.854,-4.536 27.283,-4.536 h 17.136 c 1.448,0 7.955,0.999 9.096,0 1.124,-0.984 0,-9.795 0,-11.286 v -14.11 c 0,-6.577 10.11,-8.923 14.281,-4.749 1.512,1.514 3.024,3.027 4.536,4.539 l 46.438,46.479 c 5.123,5.13 13.199,11.335 7.847,19.468 -3.387,5.148 -9.155,9.432 -13.47,13.732 -14.601,14.553 -29.201,29.107 -43.802,43.659 -2.575,2.568 -5.353,4.298 -9.308,3.666 -6.041,-0.967 -6.522,-6.041 -6.522,-10.962 0,-3.029 1.173,-23.3 -1.004,-23.3 h -29.582 c -8.374,-0.01 -24.69,2.6 -24.69,-10.282" | ||||
|        inkscape:connector-curvature="0" | ||||
|        style="fill:#739b07" /> | ||||
|     <text | ||||
|        xml:space="preserve" | ||||
|        style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff7400;fill-opacity:0.77254902;stroke:none;font-family:Purisa;-inkscape-font-specification:Sans Bold" | ||||
|        x="316.39532" | ||||
|        y="-58.000538" | ||||
|        id="text6112" | ||||
|        sodipodi:linespacing="125%"><tspan | ||||
|          sodipodi:role="line" | ||||
|          id="tspan6114" | ||||
|          x="316.39532" | ||||
|          y="-58.000538">Drag the icon</tspan><tspan | ||||
|          sodipodi:role="line" | ||||
|          x="316.39532" | ||||
|          y="-8.0005379" | ||||
|          id="tspan6116">onto the link</tspan><tspan | ||||
|          sodipodi:role="line" | ||||
|          x="316.39532" | ||||
|          y="41.999462" | ||||
|          id="tspan6118">to install WSJT-X</tspan></text> | ||||
|   </g> | ||||
|   <metadata | ||||
|      id="metadata6108"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <cc:license | ||||
|            rdf:resource="http://creativecommons.org/licenses/publicdomain/" /> | ||||
|         <dc:publisher> | ||||
|           <cc:Agent | ||||
|              rdf:about="http://openclipart.org/"> | ||||
|             <dc:title>Openclipart</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:publisher> | ||||
|         <dc:title></dc:title> | ||||
|         <dc:date>2010-03-28T09:25:56</dc:date> | ||||
|         <dc:description>Drawing by Francesco 'Architetto' Rollandin. From OCAL 0.18 release.</dc:description> | ||||
|         <dc:source>http://openclipart.org/detail/34711/architetto----tasto-5-by-anonymous</dc:source> | ||||
|         <dc:creator> | ||||
|           <cc:Agent> | ||||
|             <dc:title>Anonymous</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:creator> | ||||
|         <dc:subject> | ||||
|           <rdf:Bag> | ||||
|             <rdf:li>arrow</rdf:li> | ||||
|             <rdf:li>clip art</rdf:li> | ||||
|             <rdf:li>clipart</rdf:li> | ||||
|             <rdf:li>green</rdf:li> | ||||
|             <rdf:li>icon</rdf:li> | ||||
|             <rdf:li>right</rdf:li> | ||||
|             <rdf:li>sign</rdf:li> | ||||
|             <rdf:li>symbol</rdf:li> | ||||
|           </rdf:Bag> | ||||
|         </dc:subject> | ||||
|       </cc:Work> | ||||
|       <cc:License | ||||
|          rdf:about="http://creativecommons.org/licenses/publicdomain/"> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://creativecommons.org/ns#Reproduction" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://creativecommons.org/ns#Distribution" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> | ||||
|       </cc:License> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 7.6 KiB | 
							
								
								
									
										27
									
								
								artwork/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | ||||
| This directory contains original artwork used to generate the graphics | ||||
| used in various parts of the WSJT-X ecosystem. The CMake build scripts | ||||
| do not  generate the  final image bitmaps  becuase of teh  extra tools | ||||
| required to  complete this  step. Instead there  is shell  script here | ||||
| (make_graphics.sh) that does the generation. If you want to modify the | ||||
| sourec graphics  or add new  ones then you  need an SVG editor  (I use | ||||
| inkscape)  and  a   tool  to  do  various  conversion   steps  (I  use | ||||
| ImageMagick), the sheel script explicitly uses these tools. | ||||
| 
 | ||||
| The files here are: | ||||
| 
 | ||||
| installer_logo.svg - A 150x57 pixel  image (the size is important with | ||||
|     a  whte background  that is  used  at the  top right  of the  NSIS | ||||
|     Windows installer. | ||||
| 
 | ||||
| wsjtx_globe_1024x1024.svg -  A 1024x1024 pixel image which  is used in | ||||
|     various places, mainly for high resolution icons. | ||||
| 
 | ||||
| wsjtx_globe_128x128.svg - A 128x128 pixel  image which is used for low | ||||
|     resolution icons. | ||||
| 
 | ||||
| make_graphics.sh  -  Run  this  script  (on  Linux)  to  generate  the | ||||
|     intermediate  bitmap image  files used  in the  wsjtx  build. This | ||||
|     script  generates all  but the  final  Mac iconset  file which  is | ||||
|     generated by a build on Mac since it requires a Mac developer tool | ||||
|     (iconutil). This script requires that inkscape and ImageMagick are | ||||
|     installed. | ||||
							
								
								
									
										1126
									
								
								artwork/installer_logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 49 KiB | 
							
								
								
									
										58
									
								
								artwork/make_graphics.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,58 @@ | ||||
| #!/bin/sh | ||||
| 
 | ||||
| # | ||||
| # Windows | ||||
| # | ||||
| inkscape -z -e /tmp/image-0.png wsjtx_globe_128x128.svg | ||||
| inkscape -z -e /tmp/image-1.png wsjtx_globe_1024x1024.svg | ||||
| convert '/tmp/image-%d.png[0-1]' -background transparent	\ | ||||
|     \( -clone 0 -resize 16 -colors 256 -compress none \)	\ | ||||
|     \( -clone 0 -resize 20 -colors 256 -compress none \)	\ | ||||
|     \( -clone 0 -resize 24 -colors 256 -compress none \)	\ | ||||
|     \( -clone 0 -resize 32 -colors 256 -compress none \)	\ | ||||
|     \( -clone 0 -resize 40 -colors 256 -compress none \)	\ | ||||
|     \( -clone 1 -resize 48 -colors 256 -compress none \)	\ | ||||
|     \( -clone 1 -resize 96 -colors 256 -compress none \)	\ | ||||
|     \( -clone 1 -resize 128 -colors 256 -compress none \)	\ | ||||
|     \( -clone 0 -resize 16 -compress none \)	\ | ||||
|     \( -clone 0 -resize 20 -compress none \)	\ | ||||
|     \( -clone 0 -resize 24 -compress none \)	\ | ||||
|     \( -clone 0 -resize 32 -compress none \)	\ | ||||
|     \( -clone 0 -resize 40 -compress none \)	\ | ||||
|     \( -clone 1 -resize 48 -compress none \)	\ | ||||
|     \( -clone 1 -resize 64 -compress none \)	\ | ||||
|     \( -clone 1 -resize 96 -compress none \)	\ | ||||
|     \( -clone 1 -resize 128 -compress none \)	\ | ||||
|     \( -clone 1 -resize 256 -compress Zip \)	\ | ||||
|     -delete 1 -delete 0	\ | ||||
|     -alpha remove ../icons/windows-icons/wsjtx.ico | ||||
| rm /tmp/image-0.png /tmp/image-1.png | ||||
| identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/wsjtx.ico | ||||
| # | ||||
| inkscape -z -e /dev/stdout -w 150 -h 57 -b white installer_logo.svg | tail -n +4 |	\ | ||||
|     convert png:- -resize 150x57 +matte BMP3:../icons/windows-icons/installer_logo.bmp | ||||
| identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/installer_logo.bmp | ||||
| 
 | ||||
| # | ||||
| # Mac | ||||
| # | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_16x16.png -w 16 -h 16 wsjtx_globe_128x128.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_16x16@2x.png -w 32 -h 32 wsjtx_globe_128x128.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_32x32.png -w 32 -h 32 wsjtx_globe_128x128.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_32x32@2x.png -w 64 -h 64 wsjtx_globe_128x128.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_128x128.png -w 128 -h 128 wsjtx_globe_1024x1024.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_128x128@2x.png -w 256 -h 256 wsjtx_globe_1024x1024.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_256x256.png -w 256 -h 256 wsjtx_globe_1024x1024.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_256x256@2x.png -w 512 -h 512 wsjtx_globe_1024x1024.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_512x512.png -w 512 -h 512 wsjtx_globe_1024x1024.svg | ||||
| inkscape -z -e ../icons/Darwin/wsjtx.iconset/icon_512x512@2x.png -w 1024 -h 1024 wsjtx_globe_1024x1024.svg | ||||
| identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Darwin/wsjtx.iconset/* | ||||
| # | ||||
| inkscape -z -e "../icons/Darwin/DragNDrop Background.png" -w 640 -h 480 -b white "DragNDrop Background.svg" | ||||
| identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' "../icons/Darwin/DragNDrop Background.png" | ||||
| 
 | ||||
| # | ||||
| # KDE & Gnome | ||||
| # | ||||
| inkscape -z -e ../icons/Unix/wsjtx_icon.png -w 128 -h 128 wsjtx_globe_1024x1024.svg | ||||
| identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Unix/wsjtx_icon.png | ||||
							
								
								
									
										602
									
								
								artwork/wsjtx_globe_1024x1024.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,602 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    inkscape:export-ydpi="90" | ||||
|    inkscape:export-xdpi="90" | ||||
|    inkscape:export-filename="/home/bill/Dropbox/src/wsjtx-dev/icons/windows-icons/icon_1024x1024.png" | ||||
|    version="1.1" | ||||
|    width="1024" | ||||
|    sodipodi:version="0.32" | ||||
|    sodipodi:docname="wsjtx_globe_1024x1024.svg" | ||||
|    inkscape:version="0.48.4 r9939" | ||||
|    id="svg1432" | ||||
|    height="1024"> | ||||
|   <sodipodi:namedview | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      id="base" | ||||
|      inkscape:current-layer="svg1432" | ||||
|      inkscape:cx="512" | ||||
|      inkscape:cy="512" | ||||
|      inkscape:guide-bbox="true" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-height="858" | ||||
|      inkscape:window-width="1114" | ||||
|      inkscape:window-x="72" | ||||
|      inkscape:window-y="31" | ||||
|      inkscape:zoom="0.69433594" | ||||
|      pagecolor="#ffffff" | ||||
|      showborder="true" | ||||
|      showguides="true" | ||||
|      showgrid="false" | ||||
|      inkscape:window-maximized="0" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:showpageshadow="false" /> | ||||
|   <metadata | ||||
|      id="metadata3060"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:title /> | ||||
|         <dc:description>Simple globe centered on North America</dc:description> | ||||
|         <dc:subject> | ||||
|           <rdf:Bag> | ||||
|             <rdf:li>earth globe northamerica</rdf:li> | ||||
|           </rdf:Bag> | ||||
|         </dc:subject> | ||||
|         <dc:publisher> | ||||
|           <cc:Agent | ||||
|              rdf:about="http://www.openclipart.org/"> | ||||
|             <dc:title>Open Clip Art Library</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:publisher> | ||||
|         <dc:creator> | ||||
|           <cc:Agent> | ||||
|             <dc:title>Dan Gerhrads</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:creator> | ||||
|         <dc:rights> | ||||
|           <cc:Agent> | ||||
|             <dc:title>Dan Gerhrads</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:rights> | ||||
|         <dc:date>May 1, 2005</dc:date> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <cc:license | ||||
|            rdf:resource="http://web.resource.org/cc/PublicDomain" /> | ||||
|         <dc:language>en</dc:language> | ||||
|       </cc:Work> | ||||
|       <cc:License | ||||
|          rdf:about="http://web.resource.org/cc/PublicDomain"> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/Reproduction" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/Distribution" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> | ||||
|       </cc:License> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <defs | ||||
|      id="defs1759"> | ||||
|     <linearGradient | ||||
|        id="linearGradient37658"> | ||||
|       <stop | ||||
|          style="stop-color:#2f7aff;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop37660" /> | ||||
|       <stop | ||||
|          style="stop-color:#0000b3;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop37662" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137"> | ||||
|       <stop | ||||
|          style="stop-color:#00bf00;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop20139" /> | ||||
|       <stop | ||||
|          style="stop-color:#007500;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop20141" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient20143" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient37658" | ||||
|        r="112.3373" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient37664" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.806632,1.239722)" | ||||
|        fy="270.08731" | ||||
|        fx="221.61394" | ||||
|        cy="270.08731" | ||||
|        cx="221.61394" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1902" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1904" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1906" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1908" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1910" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1912" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1914" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1916" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1918" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1920" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient37658-427" | ||||
|        r="112.3373" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient37664-403" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.806632,1.239722)" | ||||
|        fy="270.08731" | ||||
|        fx="221.61394" | ||||
|        cy="270.08731" | ||||
|        cx="221.61394" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient37658-427"> | ||||
|       <stop | ||||
|          style="stop-color:#488afe;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7178" /> | ||||
|       <stop | ||||
|          style="stop-color:#0000cc;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7180" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-396" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1902-893" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-396"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7184" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7186" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-527" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1904-666" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-527"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7190" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7192" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-774" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1906-717" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-774"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7196" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7198" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-253" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1908-922" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-253"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7202" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7204" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-972" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1910-261" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-972"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7208" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7210" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-945" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1912-713" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-945"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7214" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7216" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-600" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1914-146" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-600"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7220" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7222" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-196" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1916-817" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-196"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7226" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7228" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-388" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1918-23" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-388"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7232" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7234" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-202" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient1920-260" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-202"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7238" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7240" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        xlink:href="#linearGradient20137-151" | ||||
|        r="188.61865" | ||||
|        inkscape:collect="always" | ||||
|        id="radialGradient20143-884" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        fy="298.37747" | ||||
|        fx="205.17723" | ||||
|        cy="297.1124" | ||||
|        cx="202.06305" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-151"> | ||||
|       <stop | ||||
|          style="stop-color:#00d800;stop-opacity:1.0000000;" | ||||
|          offset="0.0000000" | ||||
|          id="stop7244" /> | ||||
|       <stop | ||||
|          style="stop-color:#008e00;stop-opacity:1.0000000;" | ||||
|          offset="1.0000000" | ||||
|          id="stop7246" /> | ||||
|     </linearGradient> | ||||
|   </defs> | ||||
|   <desc | ||||
|      id="desc1434">wmf2svg</desc> | ||||
|   <polyline | ||||
|      transform="matrix(2.2285926,-3.5745108,2.3232002,1.4484387,-677.00545,692.67468)" | ||||
|      style="fill:url(#radialGradient37664-403);fill-opacity:1;stroke:#0c0c0c;stroke-width:0.6875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4" | ||||
|      points="103.191467,409.292114   100.730545,403.134064 98.525299,396.927094 96.543777,390.573547 94.785973,384.171082   93.219925,377.622009 91.909569,371.072968 90.790962,364.426147 89.928040,357.779327   89.256882,351.083649 88.777481,344.387970 88.521805,337.692261 88.489838,330.947693   88.681602,324.300873 89.033165,317.605194 89.640404,311.007263 90.407448,304.458221   91.398209,298.006897 92.580727,291.604431 93.986977,285.299744 95.553017,279.092804   97.342781,272.983582 99.324303,267.069916 101.465622,261.253937 103.830666,255.584595   106.387474,250.110733 109.104080,244.783508 112.012444,239.700668 115.112572,234.764420   118.404449,230.072571 121.856140,225.625061 125.499588,221.373062 129.302826,217.414291   133.265884,213.748779 137.260895,210.425354 141.351776,207.444077 145.506577,204.804901   149.725311,202.556717 153.975983,200.601776 158.258652,198.988937 162.573242,197.718231   166.919815,196.789627 171.266373,196.154266 175.644897,195.909912 179.991470,195.909912   184.338043,196.300888 188.652649,196.985123 192.967270,197.962585 197.217941,199.233307   201.468628,200.846130 205.623444,202.801071 209.746277,205.000397 213.805222,207.541809   217.768265,210.376495 221.667389,213.504395 225.470627,216.876678 229.177994,220.591064   232.789490,224.598709 236.273132,228.850708 239.628937,233.444839 242.856903,238.283325   245.957031,243.366180 248.929321,248.791153 251.709839,254.411621 254.362534,260.374207   256.791504,266.483398 258.996704,272.739227 261.010223,279.043915 262.768005,285.495270   264.302094,291.995453 265.644409,298.593384 266.731079,305.191315 267.625946,311.838104   268.297119,318.533813 268.744537,325.278351 269.000244,331.974030 269.032166,338.669739   268.872375,345.365448 268.488861,352.012238 267.913574,358.610168 267.114594,365.159241   266.123840,371.659424 264.941284,378.061890 263.567017,384.366577 261.969025,390.573547   260.211212,396.633850 258.229706,402.596436 256.056396,408.412384 253.723328,414.032837   251.166504,419.555603 248.449905,424.833923 245.509598,429.965668 242.409470,434.853027   239.149536,439.593750 235.697845,444.041260 232.054413,448.244385 228.219193,452.252045   224.288116,455.917542 220.261139,459.240967 216.170258,462.173401 212.015442,464.812561   207.828674,467.109589 203.577988,469.064545 199.263397,470.628510 194.948776,471.899200   190.634186,472.876678 186.255646,473.463196 181.909073,473.756439 177.562500,473.707550   173.215942,473.365448 168.869370,472.681213 164.586731,471.703735 160.304077,470.384155   156.085358,468.771332 151.898590,466.865265 147.807709,464.617065 143.748779,462.124512   139.753769,459.289856 135.854645,456.161926 132.051392,452.740753 128.344025,449.026367   124.764503,445.067596 121.280853,440.766724 117.893089,436.221466 114.665123,431.382996   111.565002,426.251282 108.624680,420.875153 105.812187,415.205811 103.191467,409.292114 " | ||||
|      id="polyline1560" /> | ||||
|   <g | ||||
|      transform="matrix(4.1610916,0,0,4.1610916,-268.34786,-716.6628)" | ||||
|      style="fill:url(#radialGradient1902-893);fill-opacity:1;fill-rule:evenodd" | ||||
|      inkscape:label="Continents" | ||||
|      id="Continents"> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1904-666);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="132.818436,384.610931   131.827682,384.953064 128.184235,388.227600 126.074867,388.667450 123.422180,385.441803   120.417938,385.832794 117.126053,389.107300 116.838409,393.896912 116.902328,400.641479   115.655891,409.292114 114.409447,415.352448 115.432167,418.235992 113.578484,420.386444   111.053642,420.924042 109.903076,422.732361 109.871117,423.172241 116.295090,433.728943   123.262383,443.161530 130.836914,451.421204 140.904327,460.071808 151.195465,466.425385   153.528549,464.470428 157.683365,463.248596 162.509323,463.688446 165.321808,463.835083   165.577484,459.387573 165.066132,457.628113 162.381485,454.793457 158.162750,448.244385   155.094604,442.526184 156.884354,433.680054 153.496597,425.664764 151.738800,419.017975   150.811951,410.220703 147.743790,405.479980 144.579727,397.953430 142.438416,395.851868   141.447662,389.058441 138.858887,386.419281 135.886597,384.855316 133.968994,384.219940   133.201950,380.994293 131.667877,376.351288 131.923553,370.926331 132.850403,369.069122   135.407196,368.140533 137.292847,366.625458 137.452652,362.911041 136.557755,358.316925   134.736038,353.967194 135.886597,352.158844 138.123810,351.914490 141.255890,349.812927   141.223938,346.245178 139.050644,343.557098 136.973236,343.654846 135.471115,341.602142   134.863876,337.643402 135.407196,330.947693 138.219681,327.575439 141.191971,325.571625   145.890106,326.158081 148.510818,330.361237 149.469635,333.635773 149.565506,336.421570   150.588226,338.327637 152.314072,339.891571 153.656403,344.045837 154.135788,348.737701   152.665634,353.771698 151.578995,357.730469 152.985229,359.587646 155.094604,356.557495   156.468872,353.234070 159.153519,351.572388 161.870132,351.474609 165.865143,353.967194   168.517807,355.237885 171.458130,352.696472 175.389221,351.425751 178.457397,351.621246   179.416199,352.940857 181.301834,352.403229 184.497833,352.452087 185.968002,353.918335   186.095840,356.704102 187.949539,357.143951 190.602203,359.392151 192.551788,361.004974   193.670380,358.610168 191.912582,355.482269 191.561020,353.576202 193.095093,351.767883   192.583740,349.030945 190.442413,347.906860 188.620697,346.147400 189.259888,345.169952   191.976501,346.049652 195.044662,348.835449 196.259140,351.230255 198.624191,353.771698   200.733551,353.282928 202.363510,350.057312 203.609955,343.996979 204.153275,334.710999   202.906830,330.752228 202.331558,328.406281 203.258408,324.594147 202.459396,316.676605   201.053146,314.819397 198.496353,317.311981 195.300339,320.781982 194.788971,323.567780   192.871368,323.518921 189.739288,325.669373 186.894852,327.477692 187.342285,323.470032   188.524796,319.755646 190.666138,317.311981 190.314575,313.450958 189.132050,308.123718   190.090851,304.898071 193.734299,301.965637 197.601471,302.063385 202.107834,303.773987   205.559525,305.875519 207.796722,306.217651 208.435928,302.698761 206.678116,300.548309   206.038925,296.980560 207.796722,294.585724 209.490601,292.581909 208.627686,290.529205   205.783249,292.386414 203.961517,291.555573 204.057388,289.258514 207.189484,286.130615   208.787491,282.953796 208.627686,279.532684 209.874130,275.427277 208.276123,272.152740   206.198730,271.273010 205.783249,275.231781 204.185242,277.479980 203.929565,281.927460   201.788239,283.393677 202.715073,269.513580 203.546036,261.938171 204.888351,257.050812   206.997711,254.998108 209.426682,251.283707 209.171005,248.595657 207.541046,244.734634   206.262650,243.708313 205.687363,241.362366 204.600723,239.211929 202.715073,239.798416   201.276871,239.896149 200.030426,234.959930 197.697342,233.395966 194.916824,233.884705   192.743546,234.715546 191.976501,231.929764 192.264130,228.068741 190.698090,230.903412   191.145538,235.986282 191.912582,241.362366 192.104340,246.347473 190.154770,250.208496   187.406204,253.238663 182.548279,255.780090 177.338791,257.979401 173.695343,260.911804   171.745789,262.866760 169.956009,265.603668 166.696106,264.919464 162.924805,265.066071   158.290604,266.092438 153.464630,269.220306 146.944778,274.352051 145.091095,277.479980   141.671371,283.442566 138.954773,288.036682 135.918564,293.950378 135.119568,298.593384   135.503082,301.476929 136.334045,297.420410 138.091858,294.048126 140.361008,290.871338   142.310577,287.987793 143.365265,289.502869 141.959015,292.190948 138.283615,298.055786   135.087601,306.071045 132.946274,309.492188 130.900833,310.176422 129.686356,314.330688   130.069870,319.755646 128.919312,329.628113 129.494598,334.515503 130.677109,341.797638   131.603958,350.546021 131.540024,355.482269 130.964752,360.565125 128.280106,366.772064   129.047150,373.125641 129.654404,377.084412 131.476105,380.456665 132.818436,384.610931 " | ||||
|        id="polyline1460" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1906-717);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="144.579727,355.237885   146.497345,357.241699 147.583984,362.324585 148.638672,368.384918 149.980988,372.539154   150.364502,377.035553 148.798477,377.279877 146.753021,373.467743 146.976746,368.433777   146.593216,362.959930 144.579727,355.237885 " | ||||
|        id="polyline1464" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1908-922);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="150.108826,378.892731   148.830429,379.870209 149.309830,383.584595 150.172745,386.614777 152.633667,390.964508   152.505829,386.565887 153.336792,386.077148 153.017197,383.780090 150.108826,378.892731 " | ||||
|        id="polyline1468" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1910-261);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="201.181000,353.820557   200.797470,356.459747 200.413956,361.053864 199.966507,365.990082 199.007706,368.238281   196.866379,363.204285 197.921066,357.681580 199.103592,354.895782 201.181000,353.820557 " | ||||
|        id="polyline1472" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1912-713);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="204.632675,320.342133   203.993469,316.774353 202.906830,312.668976 206.230682,314.575043 208.883362,312.277985   209.842163,309.052338 208.915314,303.822876 208.563766,299.961853 209.874130,294.487976   212.430923,295.514343 213.261887,299.570862 213.581497,305.386810 213.837173,310.958405   212.111328,314.917175 211.951538,317.018738 212.335052,321.466217 210.800964,325.913727   208.691605,325.376099 206.454407,326.646820 205.016205,324.203125 204.632675,320.342133 " | ||||
|        id="polyline1476" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1914-146);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="213.773254,334.955322   213.709335,340.820160 213.869125,343.263885 216.362015,346.636139 219.526062,350.594910   218.695099,344.632324 219.685867,341.406677 222.722061,339.060730 227.835663,334.662109   230.648163,335.786194 233.332809,333.147003 234.355515,326.011475 234.675125,319.218048   233.556534,312.522369 233.588486,306.071045 231.511078,300.646057 228.890350,297.420410   226.365509,296.980560 225.246902,294.830109 226.365509,293.461639 225.023193,290.235992   222.338547,287.401337 220.964264,289.698364 217.352783,290.675842 216.330063,294.292480   216.362015,299.375336 220.644653,297.469269 222.754028,297.958038 220.708588,301.623535   219.941544,306.852997 220.293106,310.714020 219.845657,317.654083 219.078629,323.909912   214.955765,329.237122 215.083618,333.000397 213.773254,334.955322 " | ||||
|        id="polyline1480" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1916-817);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="232.533813,338.767487   230.200729,341.748779 229.401718,344.436798 229.721313,347.662476 233.460648,348.542206   237.040161,346.929382 237.231934,342.872864 235.378250,340.478088 233.620453,340.282562   232.533813,338.767487 " | ||||
|        id="polyline1484" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1918-23);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="264.877380,375.813690   262.991730,379.919098 260.371002,382.167236 258.645172,385.148529 258.645172,389.693817   256.887360,391.062256 255.161514,388.911804 254.522308,386.956848 252.732559,385.197418   253.339798,391.697632 251.709839,392.332977 249.792236,387.738861 248.322067,389.840393   246.724060,393.017212 244.327072,395.412018 242.952774,399.664001 241.514587,403.329529   241.067139,407.337158 239.565018,407.190552 238.318573,407.777039 235.665894,409.096649   233.173004,407.679291 233.141052,401.570068 234.707092,399.712891 235.122574,395.705231   237.231934,393.652588 239.692856,394.434540 241.834183,392.039734 240.843414,387.152374   239.980499,385.099670 240.907349,382.265015 244.550781,379.332581 247.043671,372.685760   247.139542,368.384918 249.121078,365.110352 252.125320,361.151611 254.202728,353.967194   256.503845,350.399414 255.992477,346.684998 255.864655,342.335266 255.832672,339.744965   254.586243,339.353973 254.074875,342.139771 251.581985,340.722412 252.189240,343.654846   251.997467,347.760223 251.294357,352.647583 251.486115,357.632721 248.897354,354.993530   246.915833,355.335632 246.020950,351.670135 245.733307,346.489502 247.778748,340.233704   248.002472,336.763672 247.586990,329.774750 247.299347,324.545258 248.705597,319.315765   251.230438,321.319580 254.234680,326.451324 255.608963,324.154266 255.097595,321.417358   253.915070,321.612854 252.253159,317.702942 254.202728,316.774353 253.883118,313.548676   254.714081,310.469666 254.746033,303.969452 255.001724,293.999268 253.947052,293.070679   252.349030,292.777405 250.495346,290.382599 250.303589,287.938934 249.312836,284.077911   247.235428,279.679291 245.381744,278.261963 242.920822,273.912201 241.546539,269.122559   241.035172,265.408173 242.345535,263.502106 240.555771,258.761383 238.734055,257.979401   236.560776,253.336411 234.131805,249.573120 232.501846,251.185959 230.648163,249.768631   228.315079,245.272247 226.653152,244.343658 225.438675,246.494095 223.808701,245.174515   220.964264,239.896149 219.430176,240.775894 217.672379,242.193222 216.170258,241.069122   212.718582,239.945023 210.257645,239.114182 207.509079,237.647964 205.527557,234.422318   206.805954,234.471191 209.362778,234.520065 210.225693,233.004974 208.979248,230.072571   209.810211,226.993515 210.737045,224.109970 213.325806,224.794220 215.722824,225.967178   216.745544,227.873245 219.781738,227.531128 222.114822,224.549835 224.032425,226.553665   226.844925,229.241714 229.785248,226.700287 230.136795,223.425751 229.721313,220.933197   236.592728,229.290573 241.898102,236.768250 246.564270,244.490280 251.837677,254.704865   255.960541,264.332947 260.115326,276.258118 264.302094,291.995453 267.210449,308.514740   268.393005,320.537628 269.000244,333.538025 268.840424,345.169952 268.201202,356.557495   266.794983,367.358551 265.516571,374.933960 264.877380,375.813690 " | ||||
|        id="polyline1488" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient1920-260);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="244.774506,357.779327   243.304337,356.313110 242.345535,358.512421 241.131058,362.666687 239.916580,364.817108   238.062897,367.945038 237.615448,372.930145 238.414459,375.324951 239.373245,373.125641   239.852661,368.727020 240.683624,367.602905 241.067139,371.121826 241.290848,375.129456   241.834183,378.452850 243.336304,376.986664 243.528061,376.937775 243.719818,376.888916   243.911591,376.791168 244.071396,376.644531 244.390976,376.351288 244.678635,376.009186   244.934311,375.618195 245.126068,375.324951 245.221939,375.129456 245.285858,375.031708   245.253906,372.490265 244.486862,365.159241 244.199219,361.884705 244.774506,357.779327 " | ||||
|        id="polyline1492" /> | ||||
|     <polyline | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" | ||||
|        style="fill:url(#radialGradient20143-884);stroke:#0c0c0c;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        points="262.192749,389.449432   260.115326,391.160004 258.261658,394.287933 256.663635,394.532288 255.992477,392.968323   255.065643,395.802979 253.851151,400.885864 250.783005,404.209290 250.175766,401.276855   249.344803,399.810638 245.573502,402.889679 242.537292,406.213104 240.747543,406.995056   238.126801,409.878601 236.049423,409.976349 233.748291,411.002716 231.606964,415.010315   228.986237,420.826294 226.333542,423.123352 220.996231,428.059601 216.617691,433.680054   213.485611,439.349396 210.385483,446.582703 209.171005,453.278381 210.353516,458.605591   214.220703,458.654480 216.777496,458.752228 218.535294,460.560547 223.648911,456.504028   228.187241,452.252045 232.693619,447.511292 237.775253,441.402100 243.400223,433.386810   247.906586,425.811401 251.677872,418.529236 255.161514,410.611694 258.069885,403.134064   260.690613,394.923279 262.192749,389.449432 " | ||||
|        id="polyline1496" /> | ||||
|   </g> | ||||
|   <text | ||||
|      transform="matrix(0.98267844,-0.18531886,0.18531886,0.98267844,0,0)" | ||||
|      sodipodi:linespacing="125%" | ||||
|      id="text3096" | ||||
|      y="456.47293" | ||||
|      x="394.15689" | ||||
|      style="font-size:187.56237793000002512px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff8119;fill-opacity:0.77168947;stroke:none;font-family:Purisa;-inkscape-font-specification:Purisa Bold" | ||||
|      xml:space="preserve"><tspan | ||||
|        dy="-109.78781" | ||||
|        dx="0 -9.8032694 -11.763925 -17.645885" | ||||
|        id="tspan3102" | ||||
|        y="456.47293" | ||||
|        x="394.15692" | ||||
|        sodipodi:role="line">WSJT-X</tspan><tspan | ||||
|        dx="0 -17.645885 -23.527849 -13.724579 29.30662 29.30662 -17.645885 -23.527849" | ||||
|        id="tspan3874" | ||||
|        y="690.92584" | ||||
|        x="394.15689" | ||||
|        sodipodi:role="line">JT65&JT9</tspan><tspan | ||||
|        dy="91.489815" | ||||
|        dx="0 0 0 0 -17.645885 -5.8819623 -17.645885" | ||||
|        id="tspan3100" | ||||
|        y="925.37885" | ||||
|        x="394.15689" | ||||
|        sodipodi:role="line">by K1JT</tspan></text> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 33 KiB | 
							
								
								
									
										839
									
								
								artwork/wsjtx_globe_128x128.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,839 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    height="128" | ||||
|    id="svg1432" | ||||
|    inkscape:version="0.48.4 r9939" | ||||
|    sodipodi:docname="wsjtx_globe_128x128.svg" | ||||
|    sodipodi:version="0.32" | ||||
|    width="128" | ||||
|    version="1.1"> | ||||
|   <sodipodi:namedview | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      id="base" | ||||
|      inkscape:current-layer="svg1432" | ||||
|      inkscape:cx="64" | ||||
|      inkscape:cy="64" | ||||
|      inkscape:guide-bbox="true" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:window-height="1027" | ||||
|      inkscape:window-width="1846" | ||||
|      inkscape:window-x="72" | ||||
|      inkscape:window-y="24" | ||||
|      inkscape:zoom="6.875" | ||||
|      pagecolor="#ffffff" | ||||
|      showborder="false" | ||||
|      showguides="true" | ||||
|      showgrid="false" | ||||
|      inkscape:window-maximized="0" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-from-guide="true" /> | ||||
|   <metadata | ||||
|      id="metadata3060"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:title /> | ||||
|         <dc:description>Simple globe centered on North America</dc:description> | ||||
|         <dc:subject> | ||||
|           <rdf:Bag> | ||||
|             <rdf:li>earth globe northamerica</rdf:li> | ||||
|           </rdf:Bag> | ||||
|         </dc:subject> | ||||
|         <dc:publisher> | ||||
|           <cc:Agent | ||||
|              rdf:about="http://www.openclipart.org/"> | ||||
|             <dc:title>Open Clip Art Library</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:publisher> | ||||
|         <dc:creator> | ||||
|           <cc:Agent> | ||||
|             <dc:title>Dan Gerhrads</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:creator> | ||||
|         <dc:rights> | ||||
|           <cc:Agent> | ||||
|             <dc:title>Dan Gerhrads</dc:title> | ||||
|           </cc:Agent> | ||||
|         </dc:rights> | ||||
|         <dc:date>May 1, 2005</dc:date> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <cc:license | ||||
|            rdf:resource="http://web.resource.org/cc/PublicDomain" /> | ||||
|         <dc:language>en</dc:language> | ||||
|       </cc:Work> | ||||
|       <cc:License | ||||
|          rdf:about="http://web.resource.org/cc/PublicDomain"> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/Reproduction" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/Distribution" /> | ||||
|         <cc:permits | ||||
|            rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> | ||||
|       </cc:License> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <defs | ||||
|      id="defs1759"> | ||||
|     <linearGradient | ||||
|        id="linearGradient37658"> | ||||
|       <stop | ||||
|          id="stop37660" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#619afe;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop37662" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#0000e5;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137"> | ||||
|       <stop | ||||
|          id="stop20139" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#00f100;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop20141" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00a700;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient20143" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="221.61394" | ||||
|        cy="270.08731" | ||||
|        fx="221.61394" | ||||
|        fy="270.08731" | ||||
|        gradientTransform="scale(0.806632,1.239722)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient37664" | ||||
|        inkscape:collect="always" | ||||
|        r="112.3373" | ||||
|        xlink:href="#linearGradient37658" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1902" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1904" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1906" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1908" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1910" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1912" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1914" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1916" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1918" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1920" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137" /> | ||||
|     <radialGradient | ||||
|        cx="221.61394" | ||||
|        cy="270.08731" | ||||
|        fx="221.61394" | ||||
|        fy="270.08731" | ||||
|        gradientTransform="scale(0.806632,1.239722)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient37664-284" | ||||
|        inkscape:collect="always" | ||||
|        r="112.3373" | ||||
|        xlink:href="#linearGradient37658-332" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient37658-332"> | ||||
|       <stop | ||||
|          id="stop8375" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#7aaafe;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8377" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#0000fe;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1902-108" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-970" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-970"> | ||||
|       <stop | ||||
|          id="stop8381" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8383" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1904-972" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-24" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-24"> | ||||
|       <stop | ||||
|          id="stop8387" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8389" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1906-828" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-376" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-376"> | ||||
|       <stop | ||||
|          id="stop8393" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8395" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1908-502" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-289" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-289"> | ||||
|       <stop | ||||
|          id="stop8399" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8401" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1910-187" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-879" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-879"> | ||||
|       <stop | ||||
|          id="stop8405" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8407" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1912-38" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-891" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-891"> | ||||
|       <stop | ||||
|          id="stop8411" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8413" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1914-119" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-529" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-529"> | ||||
|       <stop | ||||
|          id="stop8417" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8419" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1916-825" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-469" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-469"> | ||||
|       <stop | ||||
|          id="stop8423" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8425" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1918-387" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-981" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-981"> | ||||
|       <stop | ||||
|          id="stop8429" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8431" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1920-264" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-421" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-421"> | ||||
|       <stop | ||||
|          id="stop8435" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8437" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient20143-627" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-58" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-58"> | ||||
|       <stop | ||||
|          id="stop8441" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#0bfe0b;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8443" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00c000;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="221.61394" | ||||
|        cy="270.08731" | ||||
|        fx="221.61394" | ||||
|        fy="270.08731" | ||||
|        gradientTransform="scale(0.806632,1.239722)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient37664-284-781" | ||||
|        inkscape:collect="always" | ||||
|        r="112.3373" | ||||
|        xlink:href="#linearGradient37658-332-354" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient37658-332-354"> | ||||
|       <stop | ||||
|          id="stop8612" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#93bafe;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8614" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#1818ff;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1902-108-124" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-970-892" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-970-892"> | ||||
|       <stop | ||||
|          id="stop8618" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8620" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1904-972-786" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-24-119" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-24-119"> | ||||
|       <stop | ||||
|          id="stop8624" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8626" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1906-828-861" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-376-597" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-376-597"> | ||||
|       <stop | ||||
|          id="stop8630" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8632" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1908-502-115" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-289-824" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-289-824"> | ||||
|       <stop | ||||
|          id="stop8636" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8638" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1910-187-480" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-879-315" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-879-315"> | ||||
|       <stop | ||||
|          id="stop8642" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8644" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1912-38-980" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-891-460" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-891-460"> | ||||
|       <stop | ||||
|          id="stop8648" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8650" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1914-119-179" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-529-932" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-529-932"> | ||||
|       <stop | ||||
|          id="stop8654" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8656" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1916-825-422" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-469-24" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-469-24"> | ||||
|       <stop | ||||
|          id="stop8660" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8662" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1918-387-755" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-981-521" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-981-521"> | ||||
|       <stop | ||||
|          id="stop8666" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8668" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient1920-264-381" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-421-297" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-421-297"> | ||||
|       <stop | ||||
|          id="stop8672" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8674" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|     <radialGradient | ||||
|        cx="202.06305" | ||||
|        cy="297.1124" | ||||
|        fx="205.17723" | ||||
|        fy="298.37747" | ||||
|        gradientTransform="scale(0.893959,1.11862)" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        id="radialGradient20143-627-23" | ||||
|        inkscape:collect="always" | ||||
|        r="188.61865" | ||||
|        xlink:href="#linearGradient20137-58-615" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient20137-58-615"> | ||||
|       <stop | ||||
|          id="stop8678" | ||||
|          offset="0.0000000" | ||||
|          style="stop-color:#24fe24;stop-opacity:1.0000000;" /> | ||||
|       <stop | ||||
|          id="stop8680" | ||||
|          offset="1.0000000" | ||||
|          style="stop-color:#00d900;stop-opacity:1.0000000;" /> | ||||
|     </linearGradient> | ||||
|   </defs> | ||||
|   <desc | ||||
|      id="desc1434">wmf2svg</desc> | ||||
|   <polyline | ||||
|      id="polyline1560" | ||||
|      points="103.191467,409.292114   100.730545,403.134064 98.525299,396.927094 96.543777,390.573547 94.785973,384.171082   93.219925,377.622009 91.909569,371.072968 90.790962,364.426147 89.928040,357.779327   89.256882,351.083649 88.777481,344.387970 88.521805,337.692261 88.489838,330.947693   88.681602,324.300873 89.033165,317.605194 89.640404,311.007263 90.407448,304.458221   91.398209,298.006897 92.580727,291.604431 93.986977,285.299744 95.553017,279.092804   97.342781,272.983582 99.324303,267.069916 101.465622,261.253937 103.830666,255.584595   106.387474,250.110733 109.104080,244.783508 112.012444,239.700668 115.112572,234.764420   118.404449,230.072571 121.856140,225.625061 125.499588,221.373062 129.302826,217.414291   133.265884,213.748779 137.260895,210.425354 141.351776,207.444077 145.506577,204.804901   149.725311,202.556717 153.975983,200.601776 158.258652,198.988937 162.573242,197.718231   166.919815,196.789627 171.266373,196.154266 175.644897,195.909912 179.991470,195.909912   184.338043,196.300888 188.652649,196.985123 192.967270,197.962585 197.217941,199.233307   201.468628,200.846130 205.623444,202.801071 209.746277,205.000397 213.805222,207.541809   217.768265,210.376495 221.667389,213.504395 225.470627,216.876678 229.177994,220.591064   232.789490,224.598709 236.273132,228.850708 239.628937,233.444839 242.856903,238.283325   245.957031,243.366180 248.929321,248.791153 251.709839,254.411621 254.362534,260.374207   256.791504,266.483398 258.996704,272.739227 261.010223,279.043915 262.768005,285.495270   264.302094,291.995453 265.644409,298.593384 266.731079,305.191315 267.625946,311.838104   268.297119,318.533813 268.744537,325.278351 269.000244,331.974030 269.032166,338.669739   268.872375,345.365448 268.488861,352.012238 267.913574,358.610168 267.114594,365.159241   266.123840,371.659424 264.941284,378.061890 263.567017,384.366577 261.969025,390.573547   260.211212,396.633850 258.229706,402.596436 256.056396,408.412384 253.723328,414.032837   251.166504,419.555603 248.449905,424.833923 245.509598,429.965668 242.409470,434.853027   239.149536,439.593750 235.697845,444.041260 232.054413,448.244385 228.219193,452.252045   224.288116,455.917542 220.261139,459.240967 216.170258,462.173401 212.015442,464.812561   207.828674,467.109589 203.577988,469.064545 199.263397,470.628510 194.948776,471.899200   190.634186,472.876678 186.255646,473.463196 181.909073,473.756439 177.562500,473.707550   173.215942,473.365448 168.869370,472.681213 164.586731,471.703735 160.304077,470.384155   156.085358,468.771332 151.898590,466.865265 147.807709,464.617065 143.748779,462.124512   139.753769,459.289856 135.854645,456.161926 132.051392,452.740753 128.344025,449.026367   124.764503,445.067596 121.280853,440.766724 117.893089,436.221466 114.665123,431.382996   111.565002,426.251282 108.624680,420.875153 105.812187,415.205811 103.191467,409.292114 " | ||||
|      style="fill:url(#radialGradient37664-284-781);fill-opacity:1;stroke:#181818;stroke-width:0.6875;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4" | ||||
|      transform="matrix(0.3640721,-0.58678257,0.3795276,0.23777199,-128.13162,89.336986)" /> | ||||
|   <g | ||||
|      id="Continents" | ||||
|      inkscape:label="Continents" | ||||
|      style="fill:url(#radialGradient1902-108-124);fill-opacity:1;fill-rule:evenodd" | ||||
|      transform="matrix(0.67977314,0,0,0.68307417,-61.371653,-142.01634)"> | ||||
|     <polyline | ||||
|        id="polyline1460" | ||||
|        points="132.818436,384.610931   131.827682,384.953064 128.184235,388.227600 126.074867,388.667450 123.422180,385.441803   120.417938,385.832794 117.126053,389.107300 116.838409,393.896912 116.902328,400.641479   115.655891,409.292114 114.409447,415.352448 115.432167,418.235992 113.578484,420.386444   111.053642,420.924042 109.903076,422.732361 109.871117,423.172241 116.295090,433.728943   123.262383,443.161530 130.836914,451.421204 140.904327,460.071808 151.195465,466.425385   153.528549,464.470428 157.683365,463.248596 162.509323,463.688446 165.321808,463.835083   165.577484,459.387573 165.066132,457.628113 162.381485,454.793457 158.162750,448.244385   155.094604,442.526184 156.884354,433.680054 153.496597,425.664764 151.738800,419.017975   150.811951,410.220703 147.743790,405.479980 144.579727,397.953430 142.438416,395.851868   141.447662,389.058441 138.858887,386.419281 135.886597,384.855316 133.968994,384.219940   133.201950,380.994293 131.667877,376.351288 131.923553,370.926331 132.850403,369.069122   135.407196,368.140533 137.292847,366.625458 137.452652,362.911041 136.557755,358.316925   134.736038,353.967194 135.886597,352.158844 138.123810,351.914490 141.255890,349.812927   141.223938,346.245178 139.050644,343.557098 136.973236,343.654846 135.471115,341.602142   134.863876,337.643402 135.407196,330.947693 138.219681,327.575439 141.191971,325.571625   145.890106,326.158081 148.510818,330.361237 149.469635,333.635773 149.565506,336.421570   150.588226,338.327637 152.314072,339.891571 153.656403,344.045837 154.135788,348.737701   152.665634,353.771698 151.578995,357.730469 152.985229,359.587646 155.094604,356.557495   156.468872,353.234070 159.153519,351.572388 161.870132,351.474609 165.865143,353.967194   168.517807,355.237885 171.458130,352.696472 175.389221,351.425751 178.457397,351.621246   179.416199,352.940857 181.301834,352.403229 184.497833,352.452087 185.968002,353.918335   186.095840,356.704102 187.949539,357.143951 190.602203,359.392151 192.551788,361.004974   193.670380,358.610168 191.912582,355.482269 191.561020,353.576202 193.095093,351.767883   192.583740,349.030945 190.442413,347.906860 188.620697,346.147400 189.259888,345.169952   191.976501,346.049652 195.044662,348.835449 196.259140,351.230255 198.624191,353.771698   200.733551,353.282928 202.363510,350.057312 203.609955,343.996979 204.153275,334.710999   202.906830,330.752228 202.331558,328.406281 203.258408,324.594147 202.459396,316.676605   201.053146,314.819397 198.496353,317.311981 195.300339,320.781982 194.788971,323.567780   192.871368,323.518921 189.739288,325.669373 186.894852,327.477692 187.342285,323.470032   188.524796,319.755646 190.666138,317.311981 190.314575,313.450958 189.132050,308.123718   190.090851,304.898071 193.734299,301.965637 197.601471,302.063385 202.107834,303.773987   205.559525,305.875519 207.796722,306.217651 208.435928,302.698761 206.678116,300.548309   206.038925,296.980560 207.796722,294.585724 209.490601,292.581909 208.627686,290.529205   205.783249,292.386414 203.961517,291.555573 204.057388,289.258514 207.189484,286.130615   208.787491,282.953796 208.627686,279.532684 209.874130,275.427277 208.276123,272.152740   206.198730,271.273010 205.783249,275.231781 204.185242,277.479980 203.929565,281.927460   201.788239,283.393677 202.715073,269.513580 203.546036,261.938171 204.888351,257.050812   206.997711,254.998108 209.426682,251.283707 209.171005,248.595657 207.541046,244.734634   206.262650,243.708313 205.687363,241.362366 204.600723,239.211929 202.715073,239.798416   201.276871,239.896149 200.030426,234.959930 197.697342,233.395966 194.916824,233.884705   192.743546,234.715546 191.976501,231.929764 192.264130,228.068741 190.698090,230.903412   191.145538,235.986282 191.912582,241.362366 192.104340,246.347473 190.154770,250.208496   187.406204,253.238663 182.548279,255.780090 177.338791,257.979401 173.695343,260.911804   171.745789,262.866760 169.956009,265.603668 166.696106,264.919464 162.924805,265.066071   158.290604,266.092438 153.464630,269.220306 146.944778,274.352051 145.091095,277.479980   141.671371,283.442566 138.954773,288.036682 135.918564,293.950378 135.119568,298.593384   135.503082,301.476929 136.334045,297.420410 138.091858,294.048126 140.361008,290.871338   142.310577,287.987793 143.365265,289.502869 141.959015,292.190948 138.283615,298.055786   135.087601,306.071045 132.946274,309.492188 130.900833,310.176422 129.686356,314.330688   130.069870,319.755646 128.919312,329.628113 129.494598,334.515503 130.677109,341.797638   131.603958,350.546021 131.540024,355.482269 130.964752,360.565125 128.280106,366.772064   129.047150,373.125641 129.654404,377.084412 131.476105,380.456665 132.818436,384.610931 " | ||||
|        style="fill:url(#radialGradient1904-972-786);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1464" | ||||
|        points="144.579727,355.237885   146.497345,357.241699 147.583984,362.324585 148.638672,368.384918 149.980988,372.539154   150.364502,377.035553 148.798477,377.279877 146.753021,373.467743 146.976746,368.433777   146.593216,362.959930 144.579727,355.237885 " | ||||
|        style="fill:url(#radialGradient1906-828-861);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1468" | ||||
|        points="150.108826,378.892731   148.830429,379.870209 149.309830,383.584595 150.172745,386.614777 152.633667,390.964508   152.505829,386.565887 153.336792,386.077148 153.017197,383.780090 150.108826,378.892731 " | ||||
|        style="fill:url(#radialGradient1908-502-115);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1472" | ||||
|        points="201.181000,353.820557   200.797470,356.459747 200.413956,361.053864 199.966507,365.990082 199.007706,368.238281   196.866379,363.204285 197.921066,357.681580 199.103592,354.895782 201.181000,353.820557 " | ||||
|        style="fill:url(#radialGradient1910-187-480);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1476" | ||||
|        points="204.632675,320.342133   203.993469,316.774353 202.906830,312.668976 206.230682,314.575043 208.883362,312.277985   209.842163,309.052338 208.915314,303.822876 208.563766,299.961853 209.874130,294.487976   212.430923,295.514343 213.261887,299.570862 213.581497,305.386810 213.837173,310.958405   212.111328,314.917175 211.951538,317.018738 212.335052,321.466217 210.800964,325.913727   208.691605,325.376099 206.454407,326.646820 205.016205,324.203125 204.632675,320.342133 " | ||||
|        style="fill:url(#radialGradient1912-38-980);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1480" | ||||
|        points="213.773254,334.955322   213.709335,340.820160 213.869125,343.263885 216.362015,346.636139 219.526062,350.594910   218.695099,344.632324 219.685867,341.406677 222.722061,339.060730 227.835663,334.662109   230.648163,335.786194 233.332809,333.147003 234.355515,326.011475 234.675125,319.218048   233.556534,312.522369 233.588486,306.071045 231.511078,300.646057 228.890350,297.420410   226.365509,296.980560 225.246902,294.830109 226.365509,293.461639 225.023193,290.235992   222.338547,287.401337 220.964264,289.698364 217.352783,290.675842 216.330063,294.292480   216.362015,299.375336 220.644653,297.469269 222.754028,297.958038 220.708588,301.623535   219.941544,306.852997 220.293106,310.714020 219.845657,317.654083 219.078629,323.909912   214.955765,329.237122 215.083618,333.000397 213.773254,334.955322 " | ||||
|        style="fill:url(#radialGradient1914-119-179);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1484" | ||||
|        points="232.533813,338.767487   230.200729,341.748779 229.401718,344.436798 229.721313,347.662476 233.460648,348.542206   237.040161,346.929382 237.231934,342.872864 235.378250,340.478088 233.620453,340.282562   232.533813,338.767487 " | ||||
|        style="fill:url(#radialGradient1916-825-422);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1488" | ||||
|        points="264.877380,375.813690   262.991730,379.919098 260.371002,382.167236 258.645172,385.148529 258.645172,389.693817   256.887360,391.062256 255.161514,388.911804 254.522308,386.956848 252.732559,385.197418   253.339798,391.697632 251.709839,392.332977 249.792236,387.738861 248.322067,389.840393   246.724060,393.017212 244.327072,395.412018 242.952774,399.664001 241.514587,403.329529   241.067139,407.337158 239.565018,407.190552 238.318573,407.777039 235.665894,409.096649   233.173004,407.679291 233.141052,401.570068 234.707092,399.712891 235.122574,395.705231   237.231934,393.652588 239.692856,394.434540 241.834183,392.039734 240.843414,387.152374   239.980499,385.099670 240.907349,382.265015 244.550781,379.332581 247.043671,372.685760   247.139542,368.384918 249.121078,365.110352 252.125320,361.151611 254.202728,353.967194   256.503845,350.399414 255.992477,346.684998 255.864655,342.335266 255.832672,339.744965   254.586243,339.353973 254.074875,342.139771 251.581985,340.722412 252.189240,343.654846   251.997467,347.760223 251.294357,352.647583 251.486115,357.632721 248.897354,354.993530   246.915833,355.335632 246.020950,351.670135 245.733307,346.489502 247.778748,340.233704   248.002472,336.763672 247.586990,329.774750 247.299347,324.545258 248.705597,319.315765   251.230438,321.319580 254.234680,326.451324 255.608963,324.154266 255.097595,321.417358   253.915070,321.612854 252.253159,317.702942 254.202728,316.774353 253.883118,313.548676   254.714081,310.469666 254.746033,303.969452 255.001724,293.999268 253.947052,293.070679   252.349030,292.777405 250.495346,290.382599 250.303589,287.938934 249.312836,284.077911   247.235428,279.679291 245.381744,278.261963 242.920822,273.912201 241.546539,269.122559   241.035172,265.408173 242.345535,263.502106 240.555771,258.761383 238.734055,257.979401   236.560776,253.336411 234.131805,249.573120 232.501846,251.185959 230.648163,249.768631   228.315079,245.272247 226.653152,244.343658 225.438675,246.494095 223.808701,245.174515   220.964264,239.896149 219.430176,240.775894 217.672379,242.193222 216.170258,241.069122   212.718582,239.945023 210.257645,239.114182 207.509079,237.647964 205.527557,234.422318   206.805954,234.471191 209.362778,234.520065 210.225693,233.004974 208.979248,230.072571   209.810211,226.993515 210.737045,224.109970 213.325806,224.794220 215.722824,225.967178   216.745544,227.873245 219.781738,227.531128 222.114822,224.549835 224.032425,226.553665   226.844925,229.241714 229.785248,226.700287 230.136795,223.425751 229.721313,220.933197   236.592728,229.290573 241.898102,236.768250 246.564270,244.490280 251.837677,254.704865   255.960541,264.332947 260.115326,276.258118 264.302094,291.995453 267.210449,308.514740   268.393005,320.537628 269.000244,333.538025 268.840424,345.169952 268.201202,356.557495   266.794983,367.358551 265.516571,374.933960 264.877380,375.813690 " | ||||
|        style="fill:url(#radialGradient1918-387-755);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1492" | ||||
|        points="244.774506,357.779327   243.304337,356.313110 242.345535,358.512421 241.131058,362.666687 239.916580,364.817108   238.062897,367.945038 237.615448,372.930145 238.414459,375.324951 239.373245,373.125641   239.852661,368.727020 240.683624,367.602905 241.067139,371.121826 241.290848,375.129456   241.834183,378.452850 243.336304,376.986664 243.528061,376.937775 243.719818,376.888916   243.911591,376.791168 244.071396,376.644531 244.390976,376.351288 244.678635,376.009186   244.934311,375.618195 245.126068,375.324951 245.221939,375.129456 245.285858,375.031708   245.253906,372.490265 244.486862,365.159241 244.199219,361.884705 244.774506,357.779327 " | ||||
|        style="fill:url(#radialGradient1920-264-381);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|     <polyline | ||||
|        id="polyline1496" | ||||
|        points="262.192749,389.449432   260.115326,391.160004 258.261658,394.287933 256.663635,394.532288 255.992477,392.968323   255.065643,395.802979 253.851151,400.885864 250.783005,404.209290 250.175766,401.276855   249.344803,399.810638 245.573502,402.889679 242.537292,406.213104 240.747543,406.995056   238.126801,409.878601 236.049423,409.976349 233.748291,411.002716 231.606964,415.010315   228.986237,420.826294 226.333542,423.123352 220.996231,428.059601 216.617691,433.680054   213.485611,439.349396 210.385483,446.582703 209.171005,453.278381 210.353516,458.605591   214.220703,458.654480 216.777496,458.752228 218.535294,460.560547 223.648911,456.504028   228.187241,452.252045 232.693619,447.511292 237.775253,441.402100 243.400223,433.386810   247.906586,425.811401 251.677872,418.529236 255.161514,410.611694 258.069885,403.134064   260.690613,394.923279 262.192749,389.449432 " | ||||
|        style="fill:url(#radialGradient20143-627-23);stroke:#181818;stroke-width:0.444585;stroke-linecap:round;stroke-linejoin:round" | ||||
|        transform="matrix(0.535579,-0.859032,0.558315,0.348091,-98.20917,338.6942)" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 40 KiB | 
							
								
								
									
										208
									
								
								astro.cpp
									
									
									
									
									
								
							
							
						
						| @ -1,37 +1,101 @@ | ||||
| #include "astro.h" | ||||
| #include "ui_astro.h" | ||||
| #include <QDebug> | ||||
| #include <QFile> | ||||
| #include <QMessageBox> | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <QApplication> | ||||
| #include <QFile> | ||||
| #include <QTextStream> | ||||
| #include <QMessageBox> | ||||
| #include <QSettings> | ||||
| #include <QDateTime> | ||||
| #include <QFont> | ||||
| #include <QFontDialog> | ||||
| 
 | ||||
| #include "commons.h" | ||||
| 
 | ||||
| Astro::Astro(QWidget *parent) : | ||||
|   QWidget(parent), | ||||
|   ui(new Ui::Astro) | ||||
| #include "ui_astro.h" | ||||
| 
 | ||||
| #include "moc_astro.cpp" | ||||
| 
 | ||||
| Astro::Astro(QSettings * settings, QDir const& dataPath, QWidget * parent) : | ||||
|   QWidget {parent}, | ||||
|   settings_ {settings}, | ||||
|   ui_ {new Ui::Astro}, | ||||
|   data_path_ {dataPath} | ||||
| { | ||||
|   ui->setupUi(this); | ||||
|   ui->astroTextBrowser->setStyleSheet( | ||||
|         "QTextBrowser { background-color : cyan; color : black; }"); | ||||
|   ui->astroTextBrowser->clear(); | ||||
|   ui_->setupUi(this); | ||||
| 
 | ||||
|   setWindowFlags (Qt::Dialog | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint); | ||||
| 
 | ||||
|   setWindowTitle(QApplication::applicationName () + " - " + tr ("Astronomical Data")); | ||||
| 
 | ||||
|   read_settings (); | ||||
| 
 | ||||
|   ui_->text_label->clear(); | ||||
| } | ||||
| 
 | ||||
| Astro::~Astro() | ||||
| Astro::~Astro () | ||||
| { | ||||
|     delete ui; | ||||
|   if (isVisible ()) | ||||
|     { | ||||
|       write_settings (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Astro::closeEvent (QCloseEvent * e) | ||||
| { | ||||
|   write_settings (); | ||||
|   QWidget::closeEvent (e); | ||||
| } | ||||
| 
 | ||||
| void Astro::read_settings () | ||||
| { | ||||
|   settings_->beginGroup ("Astro"); | ||||
|   move (settings_->value ("window/pos", pos ()).toPoint ()); | ||||
|   QFont font; | ||||
|   if (font.fromString (settings_->value ("font", ui_->text_label->font ().toString ()).toString ())) | ||||
|     { | ||||
|       ui_->text_label->setFont (font); | ||||
|       adjustSize (); | ||||
|     } | ||||
|   settings_->endGroup (); | ||||
| } | ||||
| 
 | ||||
| void Astro::write_settings () | ||||
| { | ||||
|   settings_->beginGroup ("Astro"); | ||||
|   settings_->setValue ("window/pos", pos ()); | ||||
|   settings_->setValue ("font", ui_->text_label->font ().toString ()); | ||||
|   settings_->endGroup (); | ||||
| } | ||||
| 
 | ||||
| void Astro::on_font_push_button_clicked (bool /* checked */) | ||||
| { | ||||
|   bool changed; | ||||
|   ui_->text_label->setFont (QFontDialog::getFont (&changed | ||||
|                                                   , ui_->text_label->font () | ||||
|                                                   , this | ||||
|                                                   , tr ("WSJT-X Astro Text Font Chooser") | ||||
| #if QT_VERSION >= 0x050201 | ||||
|                                                   , QFontDialog::MonospacedFonts | ||||
| #endif | ||||
|                                                   )); | ||||
|   if (changed) | ||||
|     { | ||||
|       adjustSize (); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, | ||||
|                         int fQSO, int nsetftx, int ntxFreq, QString azelDir) | ||||
|                         int fQSO, int nsetftx, int ntxFreq) | ||||
| { | ||||
|   static int ntxFreq0=-99; | ||||
|   static bool astroBusy=false; | ||||
|   char cc[300]; | ||||
|   double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx; | ||||
|   double ramoon,decmoon,dgrd,poloffset,xnr,techo; | ||||
|   int ntsky,ndop,ndop00; | ||||
|   QString date = t.date().toString("yyyy MMM dd"); | ||||
|   QString utc = t.time().toString(); | ||||
|   QString date = t.date().toString("yyyy MMM dd").trimmed (); | ||||
|   QString utc = t.time().toString().trimmed (); | ||||
|   int nyear=t.date().year(); | ||||
|   int month=t.date().month(); | ||||
|   int nday=t.date().day(); | ||||
| @ -40,7 +104,7 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, | ||||
|   double sec=t.time().second() + 0.001*t.time().msec(); | ||||
|   int isec=sec; | ||||
|   double uth=nhr + nmin/60.0 + sec/3600.0; | ||||
| //  int nfreq=(int)datcom_.fcenter;
 | ||||
|   //  int nfreq=(int)datcom_.fcenter;
 | ||||
|   int nfreq=10368; | ||||
|   if(nfreq<10 or nfreq > 50000) nfreq=144; | ||||
| 
 | ||||
| @ -48,60 +112,82 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, | ||||
|     astroBusy=true; | ||||
| 
 | ||||
|     astrosub_(&nyear, &month, &nday, &uth, &nfreq, mygrid.toLatin1(), | ||||
|             hisgrid.toLatin1(), &azsun, &elsun, &azmoon, &elmoon, | ||||
|             &azmoondx, &elmoondx, &ntsky, &ndop, &ndop00,&ramoon, &decmoon, | ||||
|             &dgrd, &poloffset, &xnr, &techo, 6, 6); | ||||
|               hisgrid.toLatin1(), &azsun, &elsun, &azmoon, &elmoon, | ||||
|               &azmoondx, &elmoondx, &ntsky, &ndop, &ndop00,&ramoon, &decmoon, | ||||
|               &dgrd, &poloffset, &xnr, &techo, 6, 6); | ||||
|     astroBusy=false; | ||||
|   } | ||||
| 
 | ||||
|   sprintf(cc, | ||||
|           "Az:    %6.1f\n" | ||||
|           "El:    %6.1f\n" | ||||
|           "MyDop: %6d\n" | ||||
|           "Delay: %6.2f\n" | ||||
|           "DxAz:  %6.1f\n" | ||||
|           "DxEl:  %6.1f\n" | ||||
|           "DxDop: %6d\n" | ||||
|           "Dec:   %6.1f\n" | ||||
|           "SunAz: %6.1f\n" | ||||
|           "SunEl: %6.1f\n" | ||||
|           "Freq:  %6d\n" | ||||
|           "Tsky:  %6d\n" | ||||
|           "MNR:   %6.1f\n" | ||||
|           "Dgrd:  %6.1f", | ||||
|           azmoon,elmoon,ndop00,techo,azmoondx,elmoondx,ndop,decmoon, | ||||
|           azsun,elsun,nfreq,ntsky,xnr,dgrd); | ||||
|   ui->astroTextBrowser->setText(" "+ date + "\nUTC: " + utc + "\n" + cc); | ||||
|   QString message; | ||||
|   { | ||||
|     QTextStream out {&message}; | ||||
|     out | ||||
|       << " " << date << "\n" | ||||
|       "UTC: " << utc << "\n" | ||||
|       << fixed | ||||
|       << qSetFieldWidth (6) | ||||
|       << qSetRealNumberPrecision (1) | ||||
|       << "Az:    " << azmoon << "\n" | ||||
|       "El:    " << elmoon << "\n" | ||||
|       "MyDop: " << ndop00 << "\n" | ||||
|       << qSetRealNumberPrecision (2) | ||||
|       << "Delay: " << techo << "\n" | ||||
|       << qSetRealNumberPrecision (1) | ||||
|       << "DxAz:  " << azmoondx << "\n" | ||||
|       "DxEl:  " << elmoondx << "\n" | ||||
|       "DxDop: " << ndop << "\n" | ||||
|       "Dec:   " << decmoon << "\n" | ||||
|       "SunAz: " << azsun << "\n" | ||||
|       "SunEl: " << elsun << "\n" | ||||
|       "Freq:  " << nfreq << "\n" | ||||
|       "Tsky:  " << ntsky << "\n" | ||||
|       "MNR:   " << xnr << "\n" | ||||
|       "Dgrd:  " << dgrd; | ||||
|   } | ||||
|   ui_->text_label->setText(message); | ||||
| 
 | ||||
|   QString fname=azelDir+"/azel.dat"; | ||||
|   QFile f(fname); | ||||
|   QString fname {"azel.dat"}; | ||||
|   QFile f(data_path_.absoluteFilePath (fname)); | ||||
|   if(!f.open(QIODevice::WriteOnly | QIODevice::Text)) { | ||||
|     QMessageBox mb; | ||||
|     mb.setText("Cannot open " + fname); | ||||
|     mb.setText("Cannot open \"" + f.fileName () + "\"."); | ||||
|     mb.exec(); | ||||
|     return; | ||||
|   } | ||||
|   int ndiff=0; | ||||
|   if(ntxFreq != ntxFreq0) ndiff=1; | ||||
|   ntxFreq0=ntxFreq; | ||||
|   QTextStream out(&f); | ||||
|   sprintf(cc,"%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Moon\n" | ||||
|           "%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Sun\n" | ||||
|           "%2.2d:%2.2d:%2.2d,%5.1f,%5.1f,Source\n" | ||||
|           "%4d,%6d,Doppler\n" | ||||
|           "%3d,%1d,fQSO\n" | ||||
|           "%3d,%1d,fQSO2\n", | ||||
|           nhr,nmin,isec,azmoon,elmoon, | ||||
|           nhr,nmin,isec,azsun,elsun, | ||||
|           nhr,nmin,isec,0.0,0.0, | ||||
|           nfreq,ndop, | ||||
|           fQSO,nsetftx, | ||||
|           ntxFreq,ndiff); | ||||
|   out << cc; | ||||
|   { | ||||
|     QTextStream out {&f}; | ||||
|     out << fixed | ||||
|         << qSetFieldWidth (2) | ||||
|         << qSetRealNumberPrecision (1) | ||||
|         << qSetPadChar ('0') | ||||
|         << right | ||||
|         << nhr << ':' << nmin << ':' << isec | ||||
|         << qSetFieldWidth (5) | ||||
|         << ',' << azmoon << ',' << elmoon << ",Moon\n" | ||||
|         << qSetFieldWidth (2) | ||||
|         << nhr << ':' << nmin << ':' << isec | ||||
|         << qSetFieldWidth (5) | ||||
|         << ',' << azsun << ',' << elsun << ",Sun\n" | ||||
|         << qSetFieldWidth (2) | ||||
|         << nhr << ':' << nmin << ':' << isec | ||||
|         << qSetFieldWidth (5) | ||||
|         << ',' << 0. << ',' << 0. << ",Sun\n" | ||||
|         << qSetPadChar (' ') | ||||
|         << qSetFieldWidth (4) | ||||
|         << nfreq << ',' | ||||
|         << qSetFieldWidth (6) | ||||
|         << ndop << ",Doppler\n" | ||||
|         << qSetFieldWidth (3) | ||||
|         << fQSO << ',' | ||||
|         << qSetFieldWidth (1) | ||||
|         << nsetftx << ",fQSO\n" | ||||
|         << qSetFieldWidth (3) | ||||
|         << ntxFreq << ',' | ||||
|         << qSetFieldWidth (1) | ||||
|         << ndiff << ",fQSO2"; | ||||
|   } | ||||
|   f.close(); | ||||
| } | ||||
| 
 | ||||
| void Astro::setFontSize(int n) | ||||
| { | ||||
|   ui->astroTextBrowser->setFontPointSize(n); | ||||
| } | ||||
|  | ||||
							
								
								
									
										50
									
								
								astro.h
									
									
									
									
									
								
							
							
						
						| @ -1,36 +1,52 @@ | ||||
| // -*- Mode: C++ -*-
 | ||||
| #ifndef ASTRO_H | ||||
| #define ASTRO_H | ||||
| 
 | ||||
| #include <QWidget> | ||||
| #include <QDateTime> | ||||
| #include <QDir> | ||||
| 
 | ||||
| class QSettings; | ||||
| 
 | ||||
| namespace Ui { | ||||
|   class Astro; | ||||
| } | ||||
| 
 | ||||
| class Astro : public QWidget | ||||
| class Astro final | ||||
|   : public QWidget | ||||
| { | ||||
|   Q_OBJECT | ||||
| 
 | ||||
| public: | ||||
|   explicit Astro(QWidget *parent = 0); | ||||
|   void astroUpdate(QDateTime t, QString mygrid, QString hisgrid, | ||||
|                    int fQSO, int nsetftx, int ntxFreq, QString azelDir); | ||||
|   void setFontSize(int n); | ||||
| //  ~Astro();
 | ||||
|   virtual ~Astro(); | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
| private: | ||||
|     Ui::Astro *ui; | ||||
|   Q_DISABLE_COPY (Astro); | ||||
| 
 | ||||
| public: | ||||
|   explicit Astro(QSettings * settings, QDir const& dataPath, QWidget * parent = nullptr); | ||||
|   ~Astro (); | ||||
| 
 | ||||
|   void astroUpdate(QDateTime t, QString mygrid, QString hisgrid, | ||||
|                    int fQSO, int nsetftx, int ntxFreq); | ||||
| 
 | ||||
|   Q_SLOT void on_font_push_button_clicked (bool); | ||||
| 
 | ||||
| protected: | ||||
|   void closeEvent (QCloseEvent *) override; | ||||
| 
 | ||||
| private: | ||||
|   void read_settings (); | ||||
|   void write_settings (); | ||||
| 
 | ||||
|   QSettings * settings_; | ||||
|   QScopedPointer<Ui::Astro> ui_; | ||||
|   QDir data_path_; | ||||
| }; | ||||
| 
 | ||||
| extern "C" { | ||||
|   void astrosub_(int* nyear, int* month, int* nday, double* uth, int* nfreq, | ||||
|      const char* mygrid, const char* hisgrid, double* azsun, | ||||
|      double* elsun, double* azmoon, double* elmoon, double* azmoondx, | ||||
|      double* elmoondx, int* ntsky, int* ndop, int* ndop00, | ||||
|      double* ramoon, double* decmoon, double* dgrd, double* poloffset, | ||||
|      double* xnr, double* techo, int len1, int len2); | ||||
|                  const char* mygrid, const char* hisgrid, double* azsun, | ||||
|                  double* elsun, double* azmoon, double* elmoon, double* azmoondx, | ||||
|                  double* elmoondx, int* ntsky, int* ndop, int* ndop00, | ||||
|                  double* ramoon, double* decmoon, double* dgrd, double* poloffset, | ||||
|                  double* xnr, double* techo, int len1, int len2); | ||||
| } | ||||
| 
 | ||||
| #endif // ASTRO_H
 | ||||
|  | ||||
							
								
								
									
										109
									
								
								astro.ui
									
									
									
									
									
								
							
							
						
						| @ -6,31 +6,100 @@ | ||||
|    <rect> | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>262</width> | ||||
|     <height>483</height> | ||||
|     <width>169</width> | ||||
|     <height>79</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|    <string>Form</string> | ||||
|   <property name="sizePolicy"> | ||||
|    <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> | ||||
|     <horstretch>0</horstretch> | ||||
|     <verstretch>0</verstretch> | ||||
|    </sizepolicy> | ||||
|   </property> | ||||
|   <widget class="QTextBrowser" name="astroTextBrowser"> | ||||
|    <property name="geometry"> | ||||
|     <rect> | ||||
|      <x>0</x> | ||||
|      <y>10</y> | ||||
|      <width>256</width> | ||||
|      <height>451</height> | ||||
|     </rect> | ||||
|   <property name="styleSheet"> | ||||
|    <string notr="true">QWidget { | ||||
| 	background: cyan; | ||||
| }</string> | ||||
|   </property> | ||||
|   <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|    <property name="leftMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="font"> | ||||
|     <font> | ||||
|      <family>Courier New</family> | ||||
|      <pointsize>20</pointsize> | ||||
|      <weight>75</weight> | ||||
|      <bold>true</bold> | ||||
|     </font> | ||||
|    <property name="topMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|   </widget> | ||||
|    <property name="rightMargin"> | ||||
|     <number>0</number> | ||||
|    </property> | ||||
|    <property name="bottomMargin"> | ||||
|     <number>9</number> | ||||
|    </property> | ||||
|    <item> | ||||
|     <widget class="QLabel" name="text_label"> | ||||
|      <property name="sizePolicy"> | ||||
|       <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> | ||||
|        <horstretch>0</horstretch> | ||||
|        <verstretch>0</verstretch> | ||||
|       </sizepolicy> | ||||
|      </property> | ||||
|      <property name="font"> | ||||
|       <font> | ||||
|        <family>Courier</family> | ||||
|        <pointsize>18</pointsize> | ||||
|       </font> | ||||
|      </property> | ||||
|      <property name="frameShadow"> | ||||
|       <enum>QFrame::Sunken</enum> | ||||
|      </property> | ||||
|      <property name="text"> | ||||
|       <string>Astro Data</string> | ||||
|      </property> | ||||
|      <property name="alignment"> | ||||
|       <set>Qt::AlignCenter</set> | ||||
|      </property> | ||||
|      <property name="margin"> | ||||
|       <number>6</number> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item> | ||||
|     <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|      <item> | ||||
|       <spacer name="horizontalSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Horizontal</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>20</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QPushButton" name="font_push_button"> | ||||
|        <property name="text"> | ||||
|         <string>Font ...</string> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="horizontalSpacer_2"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Horizontal</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>40</width> | ||||
|          <height>20</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								contrib/Commander TCPIP Mesages.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										750
									
								
								devsetup.cpp
									
									
									
									
									
								
							
							
						
						| @ -1,750 +0,0 @@ | ||||
| #include "devsetup.h" | ||||
| 
 | ||||
| #include "ui_devsetup.h" | ||||
| 
 | ||||
| #include <iterator> | ||||
| #include <algorithm> | ||||
| #include <tr1/functional> | ||||
| #include <qmath.h> | ||||
| 
 | ||||
| #include <QDebug> | ||||
| #include <QSettings> | ||||
| #include <QAudioDeviceInfo> | ||||
| #include <QAudioInput> | ||||
| #include <QMap> | ||||
| 
 | ||||
| extern double dFreq[16]; | ||||
| qint32  g2_iptt; | ||||
| qint32  g2_COMportOpen; | ||||
| 
 | ||||
| //----------------------------------------------------------- DevSetup()
 | ||||
| DevSetup::DevSetup(QWidget *parent) | ||||
|   : QDialog(parent) | ||||
|   , ui (new Ui::DevSetup) | ||||
|   , m_audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput)) | ||||
|   , m_audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) | ||||
| { | ||||
|   ui->setupUi(this);	                              //setup the dialog form
 | ||||
|   m_restartSoundIn=false; | ||||
|   m_restartSoundOut=false; | ||||
|   m_firstCall=true; | ||||
|   g2_iptt=0; | ||||
|   m_test=0; | ||||
|   m_bRigOpen=false; | ||||
|   g2_COMportOpen=0; | ||||
| } | ||||
| 
 | ||||
| DevSetup::~DevSetup() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void DevSetup::initDlg() | ||||
| { | ||||
|   QString m_appDir = QApplication::applicationDirPath(); | ||||
|   QString inifile = m_appDir + "/wsjtx.ini"; | ||||
|   QSettings settings(inifile, QSettings::IniFormat); | ||||
|   settings.beginGroup("Common"); | ||||
|   QString catPortDriver = settings.value("CATdriver","None").toString(); | ||||
|   settings.endGroup(); | ||||
| 
 | ||||
|   //
 | ||||
|   // load combo boxes with audio setup choices
 | ||||
|   //
 | ||||
|   loadAudioDevices (m_audioInputDevices, ui->comboBoxSndIn, m_audioInputDevice, QAudioDeviceInfo::defaultInputDevice ()); | ||||
|   loadAudioDevices (m_audioOutputDevices, ui->comboBoxSndOut, m_audioOutputDevice, QAudioDeviceInfo::defaultOutputDevice ()); | ||||
| 
 | ||||
|   { | ||||
|     using namespace std::tr1; | ||||
|     using namespace std::tr1::placeholders; | ||||
| 
 | ||||
|     function<void (int)> cb (bind (&DevSetup::updateAudioChannels, this, ui->comboBoxSndIn, _1, ui->audioInputChannel, false)); | ||||
|     connect (ui->comboBoxSndIn, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb); | ||||
|     cb = bind (&DevSetup::updateAudioChannels, this, ui->comboBoxSndOut, _1, ui->audioOutputChannel, true); | ||||
|     connect (ui->comboBoxSndOut, static_cast<void (QComboBox::*)(int)> (&QComboBox::currentIndexChanged), cb); | ||||
| 
 | ||||
|     updateAudioChannels (ui->comboBoxSndIn, ui->comboBoxSndIn->currentIndex (), ui->audioInputChannel, false); | ||||
|     updateAudioChannels (ui->comboBoxSndOut, ui->comboBoxSndOut->currentIndex (), ui->audioOutputChannel, true); | ||||
| 
 | ||||
|     ui->audioInputChannel->setCurrentIndex (m_audioInputChannel); | ||||
|     ui->audioOutputChannel->setCurrentIndex (m_audioOutputChannel); | ||||
|   } | ||||
| 
 | ||||
|   enumerateRigs (); | ||||
| 
 | ||||
|   QPalette pal(ui->myCallEntry->palette()); | ||||
|   if(m_myCall=="") { | ||||
|     pal.setColor(QPalette::Base,"#ffccff"); | ||||
|   } else { | ||||
|     pal.setColor(QPalette::Base,Qt::white); | ||||
|   } | ||||
|   ui->myCallEntry->setPalette(pal); | ||||
|   ui->myGridEntry->setPalette(pal); | ||||
|   ui->myCallEntry->setText(m_myCall); | ||||
|   ui->myGridEntry->setText(m_myGrid); | ||||
| 
 | ||||
|   ui->idIntSpinBox->setValue(m_idInt); | ||||
|   ui->pttMethodComboBox->setCurrentIndex(m_pttMethodIndex); | ||||
|   ui->saveDirEntry->setText(m_saveDir); | ||||
|   ui->cbID73->setChecked(m_After73); | ||||
|   ui->cbDisplayAstroData->setChecked(m_bAstroData); | ||||
|   ui->cbPSKReporter->setChecked(m_pskReporter); | ||||
|   ui->cbSplit->setChecked(m_bSplit and m_catEnabled); | ||||
|   ui->cbXIT->setChecked(m_bXIT); | ||||
|   ui->cbXIT->setVisible(false); | ||||
|   ui->dtMinSpinBox->setValue(m_DTmin); | ||||
|   ui->dtMaxSpinBox->setValue(m_DTmax); | ||||
| 
 | ||||
|   enableWidgets(); | ||||
| 
 | ||||
|   ui->catPortComboBox->setCurrentIndex(m_catPortIndex); | ||||
|   ui->serialRateComboBox->setCurrentIndex(m_serialRateIndex); | ||||
|   ui->dataBitsComboBox->setCurrentIndex(m_dataBitsIndex); | ||||
|   ui->stopBitsComboBox->setCurrentIndex(m_stopBitsIndex); | ||||
|   ui->handshakeComboBox->setCurrentIndex(m_handshakeIndex); | ||||
|   ui->rbData->setChecked(m_pttData); | ||||
|   ui->pollSpinBox->setValue(m_poll); | ||||
|   ui->cbEMEband->setCurrentIndex(m_EMEbandIndex); | ||||
|   ui->cbBWmult->setCurrentIndex(m_toneMultIndex); | ||||
|   ui->astroFontSpinBox->setValue(m_astroFont); | ||||
| 
 | ||||
|   // PY2SDR -- Per OS serial port names
 | ||||
|   m_tmp=m_pttPort; | ||||
|   ui->pttComboBox->clear(); | ||||
|   ui->catPortComboBox->clear(); | ||||
|   ui->pttComboBox->addItem("None"); | ||||
|   ui->catPortComboBox->addItem("None"); | ||||
| #ifdef WIN32 | ||||
|   for ( int i = 1; i < 100; i++ ) { | ||||
|     ui->pttComboBox->addItem("COM" + QString::number(i)); | ||||
|     ui->catPortComboBox->addItem("COM" + QString::number(i)); | ||||
|   } | ||||
|   ui->pttComboBox->addItem("USB"); | ||||
|   ui->catPortComboBox->addItem("USB"); | ||||
| #else | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS0"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS1"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS2"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS3"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS4"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS5"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS6"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyS7"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyUSB0"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyUSB1"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyUSB2"); | ||||
|   ui->catPortComboBox->addItem("/dev/ttyUSB3"); | ||||
|   ui->catPortComboBox->addItem(catPortDriver); | ||||
| 
 | ||||
|   ui->pttComboBox->addItem("/dev/ttyS0"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS1"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS2"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS3"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS4"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS5"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS6"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyS7"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyUSB0"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyUSB1"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyUSB2"); | ||||
|   ui->pttComboBox->addItem("/dev/ttyUSB3"); | ||||
| #endif | ||||
|   ui->pttComboBox->setCurrentIndex(m_tmp); | ||||
|   ui->catPortComboBox->setCurrentIndex(m_catPortIndex); | ||||
| 
 | ||||
|   int n=m_macro.length(); | ||||
|   if(n>=1) ui->macro1->setText(m_macro[0].toUpper()); | ||||
|   if(n>=2) ui->macro2->setText(m_macro[1].toUpper()); | ||||
|   if(n>=3) ui->macro3->setText(m_macro[2].toUpper()); | ||||
|   if(n>=4) ui->macro4->setText(m_macro[3].toUpper()); | ||||
|   if(n>=5) ui->macro5->setText(m_macro[4].toUpper()); | ||||
|   if(n>=6) ui->macro6->setText(m_macro[5].toUpper()); | ||||
|   if(n>=7) ui->macro7->setText(m_macro[6].toUpper()); | ||||
|   if(n>=8) ui->macro8->setText(m_macro[7].toUpper()); | ||||
|   if(n>=8) ui->macro9->setText(m_macro[8].toUpper()); | ||||
|   if(n>=10) ui->macro10->setText(m_macro[9].toUpper()); | ||||
| 
 | ||||
|   ui->f1->setText(m_dFreq[0]); | ||||
|   ui->f2->setText(m_dFreq[1]); | ||||
|   ui->f3->setText(m_dFreq[2]); | ||||
|   ui->f4->setText(m_dFreq[3]); | ||||
|   ui->f5->setText(m_dFreq[4]); | ||||
|   ui->f6->setText(m_dFreq[5]); | ||||
|   ui->f7->setText(m_dFreq[6]); | ||||
|   ui->f8->setText(m_dFreq[7]); | ||||
|   ui->f9->setText(m_dFreq[8]); | ||||
|   ui->f10->setText(m_dFreq[9]); | ||||
|   ui->f11->setText(m_dFreq[10]); | ||||
|   ui->f12->setText(m_dFreq[11]); | ||||
|   ui->f13->setText(m_dFreq[12]); | ||||
|   ui->f14->setText(m_dFreq[13]); | ||||
|   ui->f15->setText(m_dFreq[14]); | ||||
|   ui->f16->setText(m_dFreq[15]); | ||||
| 
 | ||||
|   ui->AntDescription1->setText(m_antDescription[0]); | ||||
|   ui->AntDescription2->setText(m_antDescription[1]); | ||||
|   ui->AntDescription3->setText(m_antDescription[2]); | ||||
|   ui->AntDescription4->setText(m_antDescription[3]); | ||||
|   ui->AntDescription5->setText(m_antDescription[4]); | ||||
|   ui->AntDescription6->setText(m_antDescription[5]); | ||||
|   ui->AntDescription7->setText(m_antDescription[6]); | ||||
|   ui->AntDescription8->setText(m_antDescription[7]); | ||||
|   ui->AntDescription9->setText(m_antDescription[8]); | ||||
|   ui->AntDescription10->setText(m_antDescription[9]); | ||||
|   ui->AntDescription11->setText(m_antDescription[10]); | ||||
|   ui->AntDescription12->setText(m_antDescription[11]); | ||||
|   ui->AntDescription13->setText(m_antDescription[12]); | ||||
|   ui->AntDescription14->setText(m_antDescription[13]); | ||||
|   ui->AntDescription15->setText(m_antDescription[14]); | ||||
|   ui->AntDescription16->setText(m_antDescription[15]); | ||||
| 
 | ||||
|   ui->Band1->setText(m_bandDescription[0]); | ||||
|   ui->Band2->setText(m_bandDescription[1]); | ||||
|   ui->Band3->setText(m_bandDescription[2]); | ||||
|   ui->Band4->setText(m_bandDescription[3]); | ||||
|   ui->Band5->setText(m_bandDescription[4]); | ||||
|   ui->Band6->setText(m_bandDescription[5]); | ||||
|   ui->Band7->setText(m_bandDescription[6]); | ||||
|   ui->Band8->setText(m_bandDescription[7]); | ||||
|   ui->Band9->setText(m_bandDescription[8]); | ||||
|   ui->Band10->setText(m_bandDescription[9]); | ||||
|   ui->Band11->setText(m_bandDescription[10]); | ||||
|   ui->Band12->setText(m_bandDescription[11]); | ||||
|   ui->Band13->setText(m_bandDescription[12]); | ||||
|   ui->Band14->setText(m_bandDescription[13]); | ||||
|   ui->Band15->setText(m_bandDescription[14]); | ||||
|   ui->Band16->setText(m_bandDescription[15]); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------- accept()
 | ||||
| void DevSetup::accept() | ||||
| { | ||||
|   // Called when OK button is clicked.
 | ||||
|   // Check to see whether SoundInThread must be restarted,
 | ||||
|   // and save user parameters.
 | ||||
| 
 | ||||
|   m_restartSoundIn = false; | ||||
|   m_restartSoundOut = false; | ||||
| 
 | ||||
|   if (m_audioInputDevice != m_audioInputDevices[ui->comboBoxSndIn->currentIndex ()]) | ||||
|     { | ||||
|       m_audioInputDevice = m_audioInputDevices[ui->comboBoxSndIn->currentIndex ()]; | ||||
|       m_restartSoundIn = true; | ||||
|     } | ||||
| 
 | ||||
|   if (m_audioOutputDevice != m_audioOutputDevices[ui->comboBoxSndOut->currentIndex ()]) | ||||
|     { | ||||
|       m_audioOutputDevice = m_audioOutputDevices[ui->comboBoxSndOut->currentIndex ()]; | ||||
|       m_restartSoundOut = true; | ||||
|     } | ||||
| 
 | ||||
|   if (m_audioInputChannel != static_cast<AudioDevice::Channel> (ui->audioInputChannel->currentIndex ())) | ||||
|     { | ||||
|       m_audioInputChannel = static_cast<AudioDevice::Channel> (ui->audioInputChannel->currentIndex ()); | ||||
|       m_restartSoundIn = true; | ||||
|     } | ||||
|   Q_ASSERT (m_audioInputChannel <= AudioDevice::Right); | ||||
| 
 | ||||
|   if (m_audioOutputChannel != static_cast<AudioDevice::Channel> (ui->audioOutputChannel->currentIndex ())) | ||||
|     { | ||||
|       m_audioOutputChannel = static_cast<AudioDevice::Channel> (ui->audioOutputChannel->currentIndex ()); | ||||
|       m_restartSoundOut = true; | ||||
|     } | ||||
|   Q_ASSERT (m_audioOutputChannel <= AudioDevice::Both); | ||||
| 
 | ||||
|   m_myCall=ui->myCallEntry->text(); | ||||
|   m_myGrid=ui->myGridEntry->text(); | ||||
|   m_idInt=ui->idIntSpinBox->value(); | ||||
|   m_pttMethodIndex=ui->pttMethodComboBox->currentIndex(); | ||||
|   m_pttPort=ui->pttComboBox->currentIndex(); | ||||
|   m_saveDir=ui->saveDirEntry->text(); | ||||
| 
 | ||||
|   m_macro.clear(); | ||||
|   m_macro.append(ui->macro1->text()); | ||||
|   m_macro.append(ui->macro2->text()); | ||||
|   m_macro.append(ui->macro3->text()); | ||||
|   m_macro.append(ui->macro4->text()); | ||||
|   m_macro.append(ui->macro5->text()); | ||||
|   m_macro.append(ui->macro6->text()); | ||||
|   m_macro.append(ui->macro7->text()); | ||||
|   m_macro.append(ui->macro8->text()); | ||||
|   m_macro.append(ui->macro9->text()); | ||||
|   m_macro.append(ui->macro10->text()); | ||||
| 
 | ||||
|   m_dFreq.clear(); | ||||
|   m_dFreq.append(ui->f1->text()); | ||||
|   m_dFreq.append(ui->f2->text()); | ||||
|   m_dFreq.append(ui->f3->text()); | ||||
|   m_dFreq.append(ui->f4->text()); | ||||
|   m_dFreq.append(ui->f5->text()); | ||||
|   m_dFreq.append(ui->f6->text()); | ||||
|   m_dFreq.append(ui->f7->text()); | ||||
|   m_dFreq.append(ui->f8->text()); | ||||
|   m_dFreq.append(ui->f9->text()); | ||||
|   m_dFreq.append(ui->f10->text()); | ||||
|   m_dFreq.append(ui->f11->text()); | ||||
|   m_dFreq.append(ui->f12->text()); | ||||
|   m_dFreq.append(ui->f13->text()); | ||||
|   m_dFreq.append(ui->f14->text()); | ||||
|   m_dFreq.append(ui->f15->text()); | ||||
|   m_dFreq.append(ui->f16->text()); | ||||
| 
 | ||||
|   m_antDescription.clear(); | ||||
|   m_antDescription.append(ui->AntDescription1->text()); | ||||
|   m_antDescription.append(ui->AntDescription2->text()); | ||||
|   m_antDescription.append(ui->AntDescription3->text()); | ||||
|   m_antDescription.append(ui->AntDescription4->text()); | ||||
|   m_antDescription.append(ui->AntDescription5->text()); | ||||
|   m_antDescription.append(ui->AntDescription6->text()); | ||||
|   m_antDescription.append(ui->AntDescription7->text()); | ||||
|   m_antDescription.append(ui->AntDescription8->text()); | ||||
|   m_antDescription.append(ui->AntDescription9->text()); | ||||
|   m_antDescription.append(ui->AntDescription10->text()); | ||||
|   m_antDescription.append(ui->AntDescription11->text()); | ||||
|   m_antDescription.append(ui->AntDescription12->text()); | ||||
|   m_antDescription.append(ui->AntDescription13->text()); | ||||
|   m_antDescription.append(ui->AntDescription14->text()); | ||||
|   m_antDescription.append(ui->AntDescription15->text()); | ||||
|   m_antDescription.append(ui->AntDescription16->text()); | ||||
| 
 | ||||
|   m_bandDescription.clear(); | ||||
|   m_bandDescription.append(ui->Band1->text()); | ||||
|   m_bandDescription.append(ui->Band2->text()); | ||||
|   m_bandDescription.append(ui->Band3->text()); | ||||
|   m_bandDescription.append(ui->Band4->text()); | ||||
|   m_bandDescription.append(ui->Band5->text()); | ||||
|   m_bandDescription.append(ui->Band6->text()); | ||||
|   m_bandDescription.append(ui->Band7->text()); | ||||
|   m_bandDescription.append(ui->Band8->text()); | ||||
|   m_bandDescription.append(ui->Band9->text()); | ||||
|   m_bandDescription.append(ui->Band10->text()); | ||||
|   m_bandDescription.append(ui->Band11->text()); | ||||
|   m_bandDescription.append(ui->Band12->text()); | ||||
|   m_bandDescription.append(ui->Band13->text()); | ||||
|   m_bandDescription.append(ui->Band14->text()); | ||||
|   m_bandDescription.append(ui->Band15->text()); | ||||
|   m_bandDescription.append(ui->Band16->text()); | ||||
| 
 | ||||
| 
 | ||||
|   if(m_bRigOpen) { | ||||
|     rig->close(); | ||||
|     if(m_rig<9900) delete rig; | ||||
|     m_bRigOpen=false; | ||||
|   } | ||||
| 
 | ||||
|   QDialog::accept(); | ||||
| } | ||||
| 
 | ||||
| //------------------------------------------------------- reject()
 | ||||
| void DevSetup::reject() | ||||
| { | ||||
|   if(m_bRigOpen) rig->close(); | ||||
|   QDialog::reject(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::msgBox(QString t)                             //msgBox
 | ||||
| { | ||||
|   msgBox0.setText(t); | ||||
|   msgBox0.exec(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_myCallEntry_editingFinished() | ||||
| { | ||||
|   QString t=ui->myCallEntry->text(); | ||||
|   ui->myCallEntry->setText(t.toUpper()); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_myGridEntry_editingFinished() | ||||
| { | ||||
|   QString t=ui->myGridEntry->text(); | ||||
|   t=t.mid(0,4).toUpper()+t.mid(4,2).toLower(); | ||||
|   ui->myGridEntry->setText(t); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::setEnableAntennaDescriptions(bool enable) | ||||
| { | ||||
|     ui->AntDescription1->setEnabled(enable); | ||||
|     ui->AntDescription2->setEnabled(enable); | ||||
|     ui->AntDescription3->setEnabled(enable); | ||||
|     ui->AntDescription4->setEnabled(enable); | ||||
|     ui->AntDescription5->setEnabled(enable); | ||||
|     ui->AntDescription6->setEnabled(enable); | ||||
|     ui->AntDescription7->setEnabled(enable); | ||||
|     ui->AntDescription8->setEnabled(enable); | ||||
|     ui->AntDescription9->setEnabled(enable); | ||||
|     ui->AntDescription10->setEnabled(enable); | ||||
|     ui->AntDescription11->setEnabled(enable); | ||||
|     ui->AntDescription12->setEnabled(enable); | ||||
|     ui->AntDescription13->setEnabled(enable); | ||||
|     ui->AntDescription14->setEnabled(enable); | ||||
|     ui->AntDescription15->setEnabled(enable); | ||||
|     ui->AntDescription16->setEnabled(enable); | ||||
|     if (enable) | ||||
|         ui->AntDescriptionColumnLabel->setText("Antenna description"); | ||||
|     else | ||||
|         ui->AntDescriptionColumnLabel->setText("Antenna description (enable PSK Reporter)"); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbPSKReporter_clicked(bool b) | ||||
| { | ||||
|   m_pskReporter=b; | ||||
|   setEnableAntennaDescriptions(m_pskReporter); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_pttMethodComboBox_activated(int index) | ||||
| { | ||||
|   m_pttMethodIndex=index; | ||||
|   enableWidgets(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_catPortComboBox_activated(int index) | ||||
| { | ||||
|   m_catPortIndex=index; | ||||
|   m_catPort=ui->catPortComboBox->itemText(index); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbEnableCAT_toggled(bool b) | ||||
| { | ||||
|   m_catEnabled=b; | ||||
|   enableWidgets(); | ||||
|   ui->cbSplit->setChecked(m_bSplit and m_catEnabled); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_serialRateComboBox_activated(int index) | ||||
| { | ||||
|   m_serialRateIndex=index; | ||||
|   m_serialRate=ui->serialRateComboBox->itemText(index).toInt(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_handshakeComboBox_activated(int index) | ||||
| { | ||||
|   m_handshakeIndex=index; | ||||
|   m_handshake=ui->handshakeComboBox->itemText(index); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_handshakeComboBox_currentIndexChanged(int index) | ||||
| { | ||||
|   ui->RTSCheckBox->setEnabled(index != 2); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_dataBitsComboBox_activated(int index) | ||||
| { | ||||
|   m_dataBitsIndex=index; | ||||
|   m_dataBits=ui->dataBitsComboBox->itemText(index).toInt(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_stopBitsComboBox_activated(int index) | ||||
| { | ||||
|   m_stopBitsIndex=index; | ||||
|   m_stopBits=ui->stopBitsComboBox->itemText(index).toInt(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_rigComboBox_activated(int index) | ||||
| { | ||||
|   m_rig = ui->rigComboBox->itemData (index).toInt (); | ||||
|   enableWidgets(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbID73_toggled(bool checked) | ||||
| { | ||||
|   m_After73=checked; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_testCATButton_clicked() | ||||
| { | ||||
|   openRig(); | ||||
|   if(!m_catEnabled) return; | ||||
|   QString t; | ||||
|   double fMHz=rig->getFreq(RIG_VFO_CURR)/1000000.0; | ||||
|   if(fMHz>0.0) { | ||||
|     t.sprintf("Rig control appears to be working.\nDial Frequency:  %.6f MHz", | ||||
|               fMHz); | ||||
|   } else { | ||||
|     t.sprintf("Rig control error %d\nFailed to read frequency.", | ||||
|               int(1000000.0*fMHz)); | ||||
|     if(m_poll>0) { | ||||
|       m_catEnabled=false; | ||||
|       ui->cbEnableCAT->setChecked(false); | ||||
|     } | ||||
|   } | ||||
|   msgBox(t); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::openRig() | ||||
| { | ||||
|   QString t; | ||||
|   int ret; | ||||
| 
 | ||||
|   if(!m_catEnabled) return; | ||||
|   if(m_bRigOpen) { | ||||
|     rig->close(); | ||||
|     if(m_rig<9900) delete rig; | ||||
|     m_bRigOpen=false; | ||||
|   } | ||||
| 
 | ||||
|   rig = new Rig(); | ||||
| 
 | ||||
|   if(m_rig<9900) { | ||||
|     if (!rig->init(m_rig)) { | ||||
|       msgBox("Rig init failure"); | ||||
|       m_catEnabled=false; | ||||
|       return; | ||||
|     } | ||||
|     QString sCATport=m_catPort; | ||||
| #ifdef WIN32 | ||||
|     sCATport="\\\\.\\" + m_catPort;    //Allow COM ports above 9
 | ||||
| #endif | ||||
|     rig->setConf("rig_pathname", sCATport.toLatin1().data()); | ||||
|     char buf[80]; | ||||
|     sprintf(buf,"%d",m_serialRate); | ||||
|     rig->setConf("serial_speed",buf); | ||||
|     sprintf(buf,"%d",m_dataBits); | ||||
|     rig->setConf("data_bits",buf); | ||||
|     sprintf(buf,"%d",m_stopBits); | ||||
|     rig->setConf("stop_bits",buf); | ||||
|     rig->setConf("serial_handshake",m_handshake.toLatin1().data()); | ||||
|     rig->setConf("dtr_state",m_bDTR ? "ON" : "OFF"); | ||||
|     if(ui->RTSCheckBox->isEnabled()) { | ||||
|       rig->setConf("rts_state",m_bRTS ? "ON" : "OFF"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ret=rig->open(m_rig); | ||||
|   if(ret==RIG_OK) { | ||||
|     m_bRigOpen=true; | ||||
|   } else { | ||||
|     t="Open rig failed"; | ||||
|     msgBox(t); | ||||
|     m_catEnabled=false; | ||||
|     ui->cbEnableCAT->setChecked(false); | ||||
|     return; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_testPTTButton_clicked() | ||||
| { | ||||
|   m_test=1-m_test; | ||||
|   if(m_pttMethodIndex==1 or m_pttMethodIndex==2) { | ||||
|     ptt(m_pttPort,m_test,&g2_iptt,&g2_COMportOpen); | ||||
|   } | ||||
|   if(m_pttMethodIndex==0 and !m_bRigOpen) { | ||||
| //    on_testCATButton_clicked();
 | ||||
|     openRig(); | ||||
|   } | ||||
|   if(m_pttMethodIndex==0 and m_bRigOpen) { | ||||
|     if(m_test==0) rig->setPTT(RIG_PTT_OFF, RIG_VFO_CURR); | ||||
|     if(m_test==1) { | ||||
|       if(m_pttData) rig->setPTT(RIG_PTT_ON_DATA, RIG_VFO_CURR); | ||||
|       if(!m_pttData) rig->setPTT(RIG_PTT_ON_MIC, RIG_VFO_CURR); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_DTRCheckBox_toggled(bool checked) | ||||
| { | ||||
|   m_bDTR=checked; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_RTSCheckBox_toggled(bool checked) | ||||
| { | ||||
|   m_bRTS=checked; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_rbData_toggled(bool checked) | ||||
| { | ||||
|   m_pttData=checked; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_pollSpinBox_valueChanged(int n) | ||||
| { | ||||
|   m_poll=n; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_pttComboBox_currentIndexChanged(int index) | ||||
| { | ||||
|   m_pttPort=index; | ||||
|   enableWidgets(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_pttMethodComboBox_currentIndexChanged(int index) | ||||
| { | ||||
|   m_pttMethodIndex=index; | ||||
|   bool b=m_pttMethodIndex==1 or m_pttMethodIndex==2; | ||||
|   ui->pttComboBox->setEnabled(b); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::enableWidgets() | ||||
| { | ||||
|   ui->cbEnableCAT->setChecked(m_catEnabled); | ||||
|   ui->rigComboBox->setEnabled(m_catEnabled); | ||||
|   ui->testCATButton->setEnabled(m_catEnabled); | ||||
|   ui->label_4->setEnabled(m_catEnabled); | ||||
|   ui->label_47->setEnabled(m_catEnabled); | ||||
|   ui->cbSplit->setEnabled(m_catEnabled); | ||||
|   if(m_rig==9999) {                    //No Split Tx with HRD
 | ||||
|     ui->cbSplit->setChecked(false); | ||||
|     ui->cbSplit->setEnabled(false); | ||||
|   } | ||||
|   ui->cbXIT->setEnabled(m_catEnabled); | ||||
| 
 | ||||
|   bool bSerial=m_catEnabled and (m_rig<9900); | ||||
|   ui->catPortComboBox->setEnabled(bSerial); | ||||
|   ui->serialRateComboBox->setEnabled(bSerial); | ||||
|   ui->dataBitsComboBox->setEnabled(bSerial); | ||||
|   ui->stopBitsComboBox->setEnabled(bSerial); | ||||
|   ui->handshakeComboBox->setEnabled(bSerial); | ||||
|   ui->DTRCheckBox->setEnabled(bSerial); | ||||
|   ui->DTRCheckBox->setChecked(m_bDTR); | ||||
|   ui->RTSCheckBox->setEnabled(bSerial && m_handshakeIndex != 2); | ||||
|   ui->RTSCheckBox->setChecked(m_bRTS); | ||||
|   ui->rbData->setEnabled(bSerial); | ||||
|   ui->rbMic->setEnabled(bSerial); | ||||
|   ui->label_21->setEnabled(bSerial); | ||||
|   ui->label_22->setEnabled(bSerial); | ||||
|   ui->label_23->setEnabled(bSerial); | ||||
|   ui->label_24->setEnabled(bSerial); | ||||
|   ui->label_25->setEnabled(bSerial); | ||||
| 
 | ||||
|   ui->pollSpinBox->setEnabled(m_catEnabled); | ||||
|   bool b1=(m_pttMethodIndex==1 or m_pttMethodIndex==2); | ||||
|   ui->pttComboBox->setEnabled(b1); | ||||
|   b1=b1 and (m_pttPort!=0); | ||||
|   bool b2 = (m_catEnabled and m_pttMethodIndex==1 and m_rig<9900) or | ||||
|             (m_catEnabled and m_pttMethodIndex==2 and m_rig<9900); | ||||
|   bool b3 = (m_catEnabled and m_pttMethodIndex==0); | ||||
|   ui->testPTTButton->setEnabled(b1 or b2 or b3);  //Include PTT via HRD or Commander
 | ||||
|   setEnableAntennaDescriptions(m_pskReporter); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbSplit_toggled(bool checked) | ||||
| { | ||||
|   m_bSplit=checked; | ||||
|   if(m_bSplit and m_bXIT) ui->cbXIT->setChecked(false); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbXIT_toggled(bool checked) | ||||
| { | ||||
|   m_bXIT=checked; | ||||
|   if(m_bSplit and m_bXIT) ui->cbSplit->setChecked(false); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::loadAudioDevices (AudioDevices const& d, QComboBox * cb, QAudioDeviceInfo const& device, QAudioDeviceInfo const& defaultDevice) | ||||
| { | ||||
|   using std::copy; | ||||
|   using std::back_inserter; | ||||
| 
 | ||||
|   int currentIndex = -1; | ||||
|   int defaultIndex = 0; | ||||
|   for (AudioDevices::const_iterator p = d.cbegin (); p != d.cend (); ++p) | ||||
|     { | ||||
|       // convert supported channel counts into something we can store in the item model
 | ||||
|       QList<QVariant> channelCounts; | ||||
|       QList<int> scc (p->supportedChannelCounts ()); | ||||
|       copy (scc.cbegin (), scc.cend (), back_inserter (channelCounts)); | ||||
| 
 | ||||
|       cb->addItem (p->deviceName (), channelCounts); | ||||
|       if (*p == device) | ||||
| 	{ | ||||
| 	  currentIndex = p - d.cbegin (); | ||||
| 	} | ||||
|       else if (*p == defaultDevice) | ||||
| 	{ | ||||
| 	  defaultIndex = p - d.cbegin (); | ||||
| 	} | ||||
|     } | ||||
|   cb->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::updateAudioChannels (QComboBox const * srcCb, int index, QComboBox * cb, bool allowBoth) | ||||
| { | ||||
|   // disable all items
 | ||||
|   for (int i (0); i < cb->count (); ++i) | ||||
|     { | ||||
|       cb->setItemData (i, 0, Qt::UserRole - 1); // undocumented model internals allows disable
 | ||||
|     } | ||||
| 
 | ||||
|   Q_FOREACH (QVariant const& v, srcCb->itemData (index).toList ()) | ||||
|     { | ||||
|       // enable valid options
 | ||||
|       int n (v.toInt ()); | ||||
|       if (2 == n) | ||||
| 	{ | ||||
| 	  cb->setItemData (AudioDevice::Left, 32 | 1, Qt::UserRole - 1); // undocumented model internals allows enable
 | ||||
| 	  cb->setItemData (AudioDevice::Right, 32 | 1, Qt::UserRole - 1); | ||||
| 	  if (allowBoth) | ||||
| 	    { | ||||
| 	      cb->setItemData (AudioDevice::Both, 32 | 1, Qt::UserRole - 1); | ||||
| 	    } | ||||
| 	} | ||||
|       else if (1 == n) | ||||
| 	{ | ||||
| 	  cb->setItemData (AudioDevice::Mono, 32 | 1, Qt::UserRole - 1); | ||||
| 	} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| typedef QMap<QString, int> RigList; | ||||
| 
 | ||||
| int rigCallback (rig_caps const * caps, void * cbData) | ||||
| { | ||||
|   RigList * rigs = reinterpret_cast<RigList *> (cbData); | ||||
| 
 | ||||
|   QString key (QString::fromLatin1 (caps->mfg_name).trimmed () | ||||
| 	       + ' '+ QString::fromLatin1 (caps->model_name).trimmed () | ||||
| 	       // + ' '+ QString::fromLatin1 (caps->version).trimmed ()
 | ||||
| 	       // + " (" + QString::fromLatin1 (rig_strstatus (caps->status)).trimmed () + ')'
 | ||||
| 	       ); | ||||
| 
 | ||||
|   (*rigs)[key] = caps->rig_model; | ||||
| 
 | ||||
|   return 1;			// keep them coming
 | ||||
| } | ||||
| 
 | ||||
| void DevSetup::enumerateRigs () | ||||
| { | ||||
|   RigList rigs; | ||||
|   rig_load_all_backends (); | ||||
|   rig_list_foreach (rigCallback, &rigs); | ||||
| 
 | ||||
|   for (RigList::const_iterator r = rigs.cbegin (); r != rigs.cend (); ++r) | ||||
|     { | ||||
|       ui->rigComboBox->addItem (r.key (), r.value ()); | ||||
|     } | ||||
| 
 | ||||
|   ui->rigComboBox->addItem ("DX Lab Suite Commander", 9998); | ||||
|   ui->rigComboBox->addItem ("Ham Radio Deluxe", 9999); | ||||
|   ui->rigComboBox->setCurrentIndex (ui->rigComboBox->findData (m_rig)); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbEMEband_activated(int index) | ||||
| { | ||||
|   m_EMEbandIndex=index; | ||||
|   m_EMEband=ui->cbEMEband->itemText(index).toInt(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbBWmult_activated(int index) | ||||
| { | ||||
|   m_toneMultIndex=index; | ||||
|   m_toneMult=pow(2,index); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_dtMinSpinBox_valueChanged(double arg1) | ||||
| { | ||||
|   m_DTmin=arg1; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_dtMaxSpinBox_valueChanged(double arg1) | ||||
| { | ||||
|   m_DTmax=arg1; | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_astroFontSpinBox_valueChanged(int arg1) | ||||
| { | ||||
|   if(arg1==-999) m_astroFont=18;   //silence compiler warning
 | ||||
|   m_astroFont=ui->astroFontSpinBox->value(); | ||||
| } | ||||
| 
 | ||||
| void DevSetup::on_cbDisplayAstroData_toggled(bool checked) | ||||
| { | ||||
|   m_bAstroData=checked; | ||||
| } | ||||
							
								
								
									
										156
									
								
								devsetup.h
									
									
									
									
									
								
							
							
						
						| @ -1,156 +0,0 @@ | ||||
| #ifndef DEVSETUP_H | ||||
| #define DEVSETUP_H | ||||
| 
 | ||||
| #include <QDialog> | ||||
| #include <QProcess> | ||||
| #include <QMessageBox> | ||||
| #include <QAudioDeviceInfo> | ||||
| 
 | ||||
| #include <hamlib/rig.h> | ||||
| 
 | ||||
| #include "rigclass.h" | ||||
| #include "AudioDevice.hpp" | ||||
| 
 | ||||
| int rigCallback (rig_caps const *, void *); | ||||
| 
 | ||||
| namespace Ui { | ||||
|     class DevSetup; | ||||
| } | ||||
| 
 | ||||
| class QComboBox; | ||||
| 
 | ||||
| class DevSetup : public QDialog | ||||
| { | ||||
|   Q_OBJECT; | ||||
| 
 | ||||
|  private: | ||||
|   Ui::DevSetup * ui; | ||||
| 
 | ||||
| public: | ||||
|   DevSetup(QWidget *parent=0); | ||||
|   ~DevSetup(); | ||||
| 
 | ||||
|   void initDlg(); | ||||
| 
 | ||||
|   float   m_DTmin; | ||||
|   float   m_DTmax; | ||||
| 
 | ||||
|   qint32  m_idInt; | ||||
|   qint32  m_pttMethodIndex; | ||||
|   qint32  m_pttPort; | ||||
|   qint32  m_catPortIndex; | ||||
|   qint32  m_rig; | ||||
|   qint32  m_rigIndex; | ||||
|   qint32  m_serialRate; | ||||
|   qint32  m_serialRateIndex; | ||||
|   qint32  m_dataBits; | ||||
|   qint32  m_dataBitsIndex; | ||||
|   qint32  m_stopBits; | ||||
|   qint32  m_stopBitsIndex; | ||||
|   qint32  m_handshakeIndex; | ||||
|   qint32  m_test; | ||||
|   qint32  m_poll; | ||||
|   qint32  m_tmp; | ||||
|   qint32  m_EMEband; | ||||
|   qint32  m_EMEbandIndex; | ||||
|   qint32  m_toneMult; | ||||
|   qint32  m_toneMultIndex; | ||||
|   qint32  m_astroFont; | ||||
| 
 | ||||
|   typedef QList<QAudioDeviceInfo> AudioDevices; | ||||
|   AudioDevices m_audioInputDevices;         // available input devices
 | ||||
|   AudioDevices m_audioOutputDevices;        // available output devices
 | ||||
|   QAudioDeviceInfo m_audioInputDevice;      // selected input device
 | ||||
|   QAudioDeviceInfo m_audioOutputDevice;     // selected output device
 | ||||
|   AudioDevice::Channel m_audioInputChannel; | ||||
|   AudioDevice::Channel m_audioOutputChannel; | ||||
| 
 | ||||
|   bool    m_restartSoundIn; | ||||
|   bool    m_restartSoundOut; | ||||
|   bool    m_pskReporter; | ||||
|   bool    m_firstCall; | ||||
|   bool    m_catEnabled; | ||||
|   bool    m_After73; | ||||
|   bool    m_bRigOpen; | ||||
|   bool    m_bDTR; | ||||
|   bool    m_bRTS; | ||||
|   bool    m_pttData; | ||||
|   bool    m_bSplit; | ||||
|   bool    m_bXIT; | ||||
|   bool    m_bAstroData; | ||||
| 
 | ||||
|   QString m_myCall; | ||||
|   QString m_myGrid; | ||||
|   QString m_saveDir; | ||||
|   QString m_azelDir; | ||||
|   QString m_catPort; | ||||
|   QString m_handshake; | ||||
| 
 | ||||
|   QStringList m_macro; | ||||
|   QStringList m_dFreq;           // per band frequency in MHz as a string
 | ||||
|   QStringList m_antDescription;  // per band antenna description
 | ||||
|   QStringList m_bandDescription; // per band description
 | ||||
| 
 | ||||
|   QMessageBox msgBox0; | ||||
| 
 | ||||
| public slots: | ||||
|   void accept(); | ||||
|   void reject(); | ||||
| 
 | ||||
| private slots: | ||||
|   void on_myCallEntry_editingFinished(); | ||||
|   void on_myGridEntry_editingFinished(); | ||||
|   void on_cbPSKReporter_clicked(bool checked); | ||||
|   void on_pttMethodComboBox_activated(int index); | ||||
|   void on_catPortComboBox_activated(int index); | ||||
|   void on_cbEnableCAT_toggled(bool checked); | ||||
|   void on_serialRateComboBox_activated(int index); | ||||
|   void on_handshakeComboBox_activated(int index); | ||||
|   void on_handshakeComboBox_currentIndexChanged(int index); | ||||
|   void on_dataBitsComboBox_activated(int index); | ||||
|   void on_stopBitsComboBox_activated(int index); | ||||
|   void on_rigComboBox_activated(int index); | ||||
|   void on_cbID73_toggled(bool checked); | ||||
|   void on_testCATButton_clicked(); | ||||
|   void on_testPTTButton_clicked(); | ||||
|   void on_DTRCheckBox_toggled(bool checked); | ||||
|   void on_RTSCheckBox_toggled(bool checked); | ||||
|   void on_rbData_toggled(bool checked); | ||||
|   void on_pollSpinBox_valueChanged(int n); | ||||
|   void on_pttComboBox_currentIndexChanged(int index); | ||||
|   void on_pttMethodComboBox_currentIndexChanged(int index); | ||||
|   void on_cbSplit_toggled(bool checked); | ||||
|   void on_cbXIT_toggled(bool checked); | ||||
|   void on_cbEMEband_activated(int index); | ||||
|   void on_cbBWmult_activated(int index); | ||||
|   void on_dtMinSpinBox_valueChanged(double arg1); | ||||
|   void on_dtMaxSpinBox_valueChanged(double arg1); | ||||
|   void on_astroFontSpinBox_valueChanged(int arg1); | ||||
|   void on_cbDisplayAstroData_toggled(bool checked); | ||||
| 
 | ||||
| private: | ||||
|   void loadAudioDevices (AudioDevices const&, QComboBox *, QAudioDeviceInfo const&, QAudioDeviceInfo const&); | ||||
|   void updateAudioChannels (QComboBox const *, int, QComboBox *, bool); | ||||
|   void enumerateRigs (); | ||||
|   Rig* rig; | ||||
|   void msgBox(QString t); | ||||
|   void setEnableAntennaDescriptions(bool enable); | ||||
|   void enableWidgets(); | ||||
|   void openRig(); | ||||
| 
 | ||||
|   friend int rigCallback (rig_caps const *, void *); | ||||
| }; | ||||
| 
 | ||||
| extern int ptt(int nport, int ntx, int* iptt, int* nopen); | ||||
| 
 | ||||
| #ifdef WIN32 | ||||
| extern "C" { | ||||
|   bool HRDInterfaceConnect(const wchar_t *host, const ushort); | ||||
|   void HRDInterfaceDisconnect(); | ||||
|   bool HRDInterfaceIsConnected(); | ||||
|   wchar_t* HRDInterfaceSendMessage(const wchar_t *msg); | ||||
|   void HRDInterfaceFreeString(const wchar_t *lstring); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif // DEVSETUP_H
 | ||||
							
								
								
									
										2835
									
								
								devsetup.ui
									
									
									
									
									
								
							
							
						
						| @ -3,6 +3,8 @@ | ||||
| #include <QMouseEvent> | ||||
| #include <QDateTime> | ||||
| 
 | ||||
| #include "moc_displaytext.cpp" | ||||
| 
 | ||||
| DisplayText::DisplayText(QWidget *parent) : | ||||
|     QTextBrowser(parent) | ||||
| { | ||||
| @ -19,7 +21,7 @@ void DisplayText::mouseDoubleClickEvent(QMouseEvent *e) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void DisplayText::setFont(QFont font) | ||||
| void DisplayText::setFont(QFont const& font) | ||||
| { | ||||
|   QFontMetrics qfm(font); | ||||
|   _fontWidth = qfm.averageCharWidth()+1;  // the plus one is emperical
 | ||||
|  | ||||
| @ -12,7 +12,7 @@ class DisplayText : public QTextBrowser | ||||
| public: | ||||
|     explicit DisplayText(QWidget *parent = 0); | ||||
| 
 | ||||
|     void setFont(QFont font); | ||||
|     void setFont(QFont const& font); | ||||
| 
 | ||||
|     void insertLineSpacer(); | ||||
|     void displayDecodedText(DecodedText decodedText, QString myCall, bool displayDXCCEntity, LogBook logBook); | ||||
|  | ||||
							
								
								
									
										11
									
								
								getfile.cpp
									
									
									
									
									
								
							
							
						
						| @ -19,8 +19,10 @@ | ||||
| void getfile(QString fname, int ntrperiod) | ||||
| { | ||||
| 
 | ||||
|   char name[80]; | ||||
|   strcpy(name,fname.toLatin1()); | ||||
|   char name[512]; | ||||
|   strncpy(name,fname.toLatin1(), sizeof (name) - 1); | ||||
|   name[sizeof (name) - 1] = '\0'; | ||||
| 
 | ||||
|   FILE* fp=fopen(name,"rb"); | ||||
| 
 | ||||
|   int i0=fname.indexOf(".wav"); | ||||
| @ -60,8 +62,9 @@ void savewav(QString fname, int ntrperiod) | ||||
| 
 | ||||
|   int npts=ntrperiod*12000; | ||||
| //  qint16* buf=(qint16*)malloc(2*npts);
 | ||||
|   char name[80]; | ||||
|   strcpy(name,fname.toLatin1()); | ||||
|   char name[512]; | ||||
|   strncpy(name,fname.toLatin1(),sizeof (name) - 1); | ||||
|   name[sizeof (name) - 1] = '\0'; | ||||
|   FILE* fp=fopen(name,"wb"); | ||||
| 
 | ||||
|   if(fp != NULL) { | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/DragNDrop Background.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 32 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_128x128.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_128x128@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 42 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_16x16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_16x16@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_256x256.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 42 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_256x256@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 102 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_32x32.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_32x32@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_512x512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 102 KiB | 
							
								
								
									
										
											BIN
										
									
								
								icons/Darwin/wsjtx.iconset/icon_512x512@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 246 KiB | 
							
								
								
									
										3
									
								
								icons/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,3 @@ | ||||
| Files under this directory are generated. Even though they are checked | ||||
| into  source  control  they  should  not be  modified  directly.   See | ||||
| ../artwork/README for details of generating these files. | ||||