mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2025-02-03 09:44:24 -05:00
Merge tag 'wsjtx-2.0.0-rc5' into develop
Tagging the WSJT-X v2.0.0 RC5 release
This commit is contained in:
commit
dc173e4847
@ -16,7 +16,7 @@ set (hamlib_LIBRARY_DIRS)
|
||||
|
||||
# pkg-config?
|
||||
find_path (__hamlib_pc_path NAMES hamlib.pc
|
||||
PATH_SUFFIXES lib/pkgconfig
|
||||
PATH_SUFFIXES lib/pkgconfig lib64/pkgconfig
|
||||
)
|
||||
if (__hamlib_pc_path)
|
||||
set (ENV{PKG_CONFIG_PATH} "${__hamlib_pc_path}" "$ENV{PKG_CONFIG_PATH}")
|
||||
|
@ -265,6 +265,7 @@ set (wsjt_qt_CXXSRCS
|
||||
models/DecodeHighlightingModel.cpp
|
||||
widgets/DecodeHighlightingListView.cpp
|
||||
models/FoxLog.cpp
|
||||
widgets/AbstractLogWindow.cpp
|
||||
widgets/FoxLogWindow.cpp
|
||||
widgets/CabrilloLogWindow.cpp
|
||||
item_delegates/CallsignDelegate.cpp
|
||||
@ -382,7 +383,6 @@ set (wsjt_FSRCS
|
||||
lib/badmsg.f90
|
||||
lib/ft8/baseline.f90
|
||||
lib/bpdecode40.f90
|
||||
lib/bpdecode144.f90
|
||||
lib/bpdecode128_90.f90
|
||||
lib/ft8/bpdecode174.f90
|
||||
lib/ft8/bpdecode174_91.f90
|
||||
@ -414,7 +414,6 @@ set (wsjt_FSRCS
|
||||
lib/encode232.f90
|
||||
lib/encode4.f90
|
||||
lib/encode_msk40.f90
|
||||
lib/encode_msk144.f90
|
||||
lib/encode_128_90.f90
|
||||
lib/ft8/encode174.f90
|
||||
lib/ft8/encode174_91.f90
|
||||
@ -422,7 +421,6 @@ set (wsjt_FSRCS
|
||||
lib/ephem.f90
|
||||
lib/extract.f90
|
||||
lib/extract4.f90
|
||||
lib/extractmessage144.f90
|
||||
lib/extractmessage77.f90
|
||||
lib/ft8/extractmessage174.f90
|
||||
lib/ft8/extractmessage174_91.f90
|
||||
@ -497,10 +495,8 @@ set (wsjt_FSRCS
|
||||
lib/moondopjpl.f90
|
||||
lib/morse.f90
|
||||
lib/move.f90
|
||||
lib/msk144d.f90
|
||||
lib/msk40decodeframe.f90
|
||||
lib/msk144decodeframe.f90
|
||||
lib/msk144sd.f90
|
||||
lib/msk40spd.f90
|
||||
lib/msk144spd.f90
|
||||
lib/msk40sync.f90
|
||||
@ -519,6 +515,8 @@ set (wsjt_FSRCS
|
||||
lib/peakdt9.f90
|
||||
lib/peakup.f90
|
||||
lib/plotsave.f90
|
||||
lib/platanh.f90
|
||||
lib/pltanh.f90
|
||||
lib/polyfit.f90
|
||||
lib/prog_args.f90
|
||||
lib/ps4.f90
|
||||
@ -561,7 +559,6 @@ set (wsjt_FSRCS
|
||||
lib/twkfreq.f90
|
||||
lib/ft8/twkfreq1.f90
|
||||
lib/twkfreq65.f90
|
||||
lib/unpackmsg144.f90
|
||||
lib/update_recent_calls.f90
|
||||
lib/update_hasharray.f90
|
||||
lib/ft8/watterson.f90
|
||||
@ -1252,14 +1249,9 @@ target_link_libraries (ft8code wsjt_fort wsjt_cxx)
|
||||
add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc)
|
||||
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144sd lib/msk144sd.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144sd wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144d lib/msk144d.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144d wsjt_fort wsjt_cxx)
|
||||
endif(WSJT_BUILD_UTILS)
|
||||
|
||||
# build the main application
|
||||
@ -1708,11 +1700,11 @@ endif ()
|
||||
|
||||
set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
|
||||
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}")
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.2), libqt5multimedia5-plugins (>=5.2), libqt5widgets5 (>=5.2), libusb-1.0-0, libudev1, libc6 (>=2.19)")
|
||||
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.5), libqt5multimedia5-plugins (>=5.5), libqt5widgets5 (>=5.5), libqt5sql5-sqlite (>=5.5), libusb-1.0-0, libudev1, libc6 (>=2.19)")
|
||||
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.2, qt5-qtmultimedia >= 5.2, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2")
|
||||
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.5, qt5-qtmultimedia >= 5.5, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2")
|
||||
set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1)
|
||||
|
||||
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"
|
||||
|
@ -1091,6 +1091,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
||||
frequencies_.sort (FrequencyList_v2::frequency_column);
|
||||
|
||||
ui_->frequencies_table_view->setModel (&next_frequencies_);
|
||||
ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
|
||||
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
|
||||
|
||||
@ -1131,6 +1133,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
||||
//
|
||||
stations_.sort (StationList::band_column);
|
||||
ui_->stations_table_view->setModel (&next_stations_);
|
||||
ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
|
||||
|
||||
// stations delegates
|
||||
@ -2275,10 +2279,10 @@ void Configuration::impl::delete_selected_macros (QModelIndexList selected_rows)
|
||||
{
|
||||
// sort in reverse row order so that we can delete without changing
|
||||
// indices underneath us
|
||||
qSort (selected_rows.begin (), selected_rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return rhs.row () < lhs.row (); // reverse row ordering
|
||||
});
|
||||
std::sort (selected_rows.begin (), selected_rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return rhs.row () < lhs.row (); // reverse row ordering
|
||||
});
|
||||
|
||||
// now delete them
|
||||
Q_FOREACH (auto index, selected_rows)
|
||||
|
@ -1712,24 +1712,24 @@ QListView::item:hover {
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="report_in_comments_check_box">
|
||||
<property name="toolTip">
|
||||
<string>Some logging programs will not accept JT-65 or JT9 as a recognized mode.</string>
|
||||
<string>Some logging programs will not accept the type of reports
|
||||
saved by this program.
|
||||
Check this option to save the sent and received reports in the
|
||||
comments field.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Con&vert mode to RTTY</string>
|
||||
<string>d&B reports to comments</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="clear_DX_check_box">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The callsign of the operator, if different from the station callsign.</p></body></html></string>
|
||||
<string>Check this option to force the clearing of the DX Call
|
||||
and DX Grid fields when a 73 or free text message is sent.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbAutoLog">
|
||||
<property name="text">
|
||||
<string>Log automatically</string>
|
||||
<string>Clear &DX call and grid after logging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -36,5 +36,7 @@
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -12,6 +12,31 @@
|
||||
|
||||
Copyright 2001 - 2018 by Joe Taylor, K1JT.
|
||||
|
||||
Release: WSJT-X 2.0-rc5
|
||||
November 26, 2018
|
||||
-----------------------
|
||||
|
||||
Release Candidate 5 ("RC5") is stable, works well, and fixes the known
|
||||
problems in RC4. It is likely that the General Availability (GA)
|
||||
release of WSJT-X 2.0 will be nearly identical to RC5.
|
||||
|
||||
Changes from WSJT-X 2.0-rc4 include the following:
|
||||
|
||||
- Make the "Auto Seq" checkbox sticky, again
|
||||
- Remove the 5-minute mouse timer
|
||||
- Correct the "worked before" logic for color highlighting
|
||||
- Add "No own call decodes" checkbox in WSPR mode
|
||||
- Display and log UTC (not local time) in contest operations
|
||||
- Validate contest QSO details before allowing logging
|
||||
- Force Aqua theme on macOS to avoid issues with Mojave dark theme
|
||||
- Move Fox log Reset action to Fox log window context menu
|
||||
- Improve layout of Working Frequencies and Station Information tables
|
||||
- Allow deletes and editing in Fox and Contest log windows
|
||||
- Add Tool Tips for Fox and Contest log windows
|
||||
- Fix a bug causing false AP decodes in JT65 VHF+ operation
|
||||
- Fix a bug that could switch unexpectedly from JT65 to JT9 mode
|
||||
|
||||
|
||||
Release: WSJT-X 2.0-rc4
|
||||
November 12, 2018
|
||||
-----------------------
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Version number components
|
||||
set (WSJTX_VERSION_MAJOR 2)
|
||||
set (WSJTX_VERSION_MINOR 0)
|
||||
set (WSJTX_VERSION_PATCH 1)
|
||||
#set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions
|
||||
set (WSJTX_VERSION_PATCH 0)
|
||||
set (WSJTX_RC 5) # release candidate number, comment out or zero for development versions
|
||||
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
|
||||
static QDateTime to_date_time (QVariant const& value)
|
||||
{
|
||||
return QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull);
|
||||
return QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull, Qt::UTC);
|
||||
}
|
||||
|
||||
QString displayText (QVariant const& value, QLocale const& locale) const override
|
||||
|
@ -1,7 +1,10 @@
|
||||
#include "ForeignKeyDelegate.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QComboBox>
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QSize>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include "CandidateKeyFilter.hpp"
|
||||
|
||||
ForeignKeyDelegate::ForeignKeyDelegate (QAbstractItemModel const * referenced_model
|
||||
@ -40,3 +43,20 @@ QWidget * ForeignKeyDelegate::createEditor (QWidget * parent
|
||||
editor->setSizeAdjustPolicy (QComboBox::AdjustToContents);
|
||||
return editor;
|
||||
}
|
||||
|
||||
QSize ForeignKeyDelegate::sizeHint (QStyleOptionViewItem const& option, QModelIndex const& index) const
|
||||
{
|
||||
auto size_hint = QStyledItemDelegate::sizeHint (option, index);
|
||||
QFontMetrics metrics {option.font};
|
||||
QStyleOptionComboBox combo_box_option;
|
||||
combo_box_option.rect = option.rect;
|
||||
combo_box_option.state = option.state | QStyle::State_Enabled;
|
||||
for (auto row = 0; row < candidate_key_filter_->rowCount (); ++row)
|
||||
{
|
||||
size_hint = size_hint.expandedTo (qApp->style ()->sizeFromContents (QStyle::CT_ComboBox
|
||||
, &combo_box_option
|
||||
, {metrics.width (candidate_key_filter_->data (candidate_key_filter_->index (row, 0)).toString ())
|
||||
, metrics.height ()}));
|
||||
}
|
||||
return size_hint;
|
||||
}
|
||||
|
@ -33,9 +33,10 @@ public:
|
||||
, int referencing_key_role = Qt::EditRole);
|
||||
~ForeignKeyDelegate ();
|
||||
|
||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
|
||||
private:
|
||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
QSize sizeHint (QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||
|
||||
QScopedPointer<CandidateKeyFilter> candidate_key_filter_;
|
||||
};
|
||||
|
||||
|
@ -10,12 +10,12 @@ subtypes.
|
||||
i3.n3 Example message Bits Total Purpose
|
||||
----------------------------------------------------------------------------------
|
||||
0.0 FREE TEXT MSG 71 71 Free text
|
||||
0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -11 28 28 10 5 71 DXpedition Mode
|
||||
0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -12 28 28 10 5 71 DXpedition Mode
|
||||
0.2 PA3XYZ/P R 590003 IO91NP 28 1 1 3 12 25 70 EU VHF contest
|
||||
0.3 WA9XYZ KA1ABC R 16A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
||||
0.4 WA9XYZ KA1ABC R 32A EMA 28 28 1 4 3 7 71 ARRL Field Day
|
||||
0.5 123456789ABCDEF012 71 71 Telemetry (18 hex)
|
||||
0.6 ... tbd
|
||||
0.6 K1ABC RR73; CQ W9XYZ EN37 28 28 15 71 Contesting
|
||||
0.7 ... tbd
|
||||
|
||||
1 WA9XYZ/R KA1ABC/R R FN42 28 1 28 1 1 15 74 Standard msg
|
||||
|
@ -1,75 +0,0 @@
|
||||
|
||||
# Set paths
|
||||
EXE_DIR = ..\\..\\wsjtx_install
|
||||
QT_DIR = C:/wsjt-env/Qt5/5.2.1/mingw48_32
|
||||
FFTW3_DIR = ..
|
||||
|
||||
INCPATH = -I${QT_DIR}/include/QtCore -I${QT_DIR}/include
|
||||
|
||||
# Compilers
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
FC = gfortran
|
||||
AR = ar cr
|
||||
RANLIB = ranlib
|
||||
MKDIR = mkdir -p
|
||||
CP = cp
|
||||
RM = rm -f
|
||||
|
||||
FFLAGS = -O2 -fbounds-check -Wall -Wno-conversion
|
||||
CFLAGS = -O2 -I.
|
||||
|
||||
# Default rules
|
||||
%.o: %.c
|
||||
${CC} ${CFLAGS} -c $<
|
||||
%.o: %.f
|
||||
${FC} ${FFLAGS} -c $<
|
||||
%.o: %.F
|
||||
${FC} ${FFLAGS} -c $<
|
||||
%.o: %.f90
|
||||
${FC} ${FFLAGS} -c $<
|
||||
%.o: %.F90
|
||||
${FC} ${FFLAGS} -c $<
|
||||
|
||||
#all: jt9code JTMSKcode.exe
|
||||
all: jtmsk.exe JTMSKsim.exe JTMSKcode.exe fixwav.exe
|
||||
|
||||
OBJS3 = JTMSKsim.o wavhdr.o gran.o four2a.o db.o
|
||||
JTMSKsim.exe: $(OBJS3)
|
||||
$(FC) -o JTMSKsim.exe $(OBJS3) C:\JTSDK\fftw3f\libfftw3f-3.dll
|
||||
|
||||
OBJS4 = jt9code.o packjt.o fmtmsg.o gen9.o deg2grid.o grid2deg.o \
|
||||
entail.o encode232.o interleave9.o graycode.o igray.o
|
||||
jt9code: $(OBJS4)
|
||||
$(FC) -o jt9code $(OBJS4)
|
||||
|
||||
OBJS5 = JTMSKcode.o packjt.o fmtmsg.o genmsk.o deg2grid.o grid2deg.o \
|
||||
entail.o tab.o vit213.o hashing.o nhash.o
|
||||
JTMSKcode.exe: $(OBJS5)
|
||||
$(FC) -o JTMSKcode.exe $(OBJS5)
|
||||
|
||||
OBJS6 = jtmsk.o analytic.o four2a.o db.o pctile.o \
|
||||
shell.o tweak1.o syncmsk.o genmsk.o packjt.o fmtmsg.o indexx.o \
|
||||
deg2grid.o grid2deg.o entail.o hashing.o nhash.o tab.o vit213.o \
|
||||
mskdt.o rectify_msk.o timer.o jtmsk_decode.o genmsk_short.o \
|
||||
jtmsk_short.o golay24_table.o hash.o
|
||||
|
||||
jtmsk.exe: $(OBJS6)
|
||||
$(FC) -o jtmsk.exe $(OBJS6) C:\JTSDK\fftw3f\libfftw3f-3.dll
|
||||
|
||||
OBJS1 = fixwav.o wavhdr.o
|
||||
fixwav.exe: $(OBJS1)
|
||||
$(FC) -o fixwav.exe $(OBJS1)
|
||||
|
||||
OBJS2 = t2.o four2a.o db.o
|
||||
t2: $(OBJS2)
|
||||
$(FC) -o t2 $(OBJS2) C:\JTSDK\fftw3f\libfftw3f-3.dll
|
||||
|
||||
OBJS6 = t6.o four2a.o db.o
|
||||
t6: $(OBJS6)
|
||||
$(FC) -o t6 $(OBJS6) C:\JTSDK\fftw3f\libfftw3f-3.dll
|
||||
|
||||
.PHONY : clean
|
||||
|
||||
clean:
|
||||
$(RM) *.o JTMSKcode JTMSKcode.exe
|
@ -1,111 +0,0 @@
|
||||
subroutine encode_msk144(message,codeword)
|
||||
! Encode an 80-bit message and return a 128-bit codeword.
|
||||
! The generator matrix has dimensions (48,80).
|
||||
! The code is a (128,80) regular ldpc code with column weight 3.
|
||||
! The code was generated using the PEG algorithm.
|
||||
! After creating the codeword, the columns are re-ordered according to
|
||||
! "colorder" to make the codeword compatible with the parity-check
|
||||
! matrix stored in Radford Neal's "pchk" format.
|
||||
!
|
||||
character*20 g(48)
|
||||
integer*1 codeword(128)
|
||||
integer*1 colorder(128)
|
||||
integer*1 gen144(48,80)
|
||||
integer*1 itmp(128)
|
||||
integer*1 message(80)
|
||||
integer*1 pchecks(48)
|
||||
logical first
|
||||
data first/.true./
|
||||
data g/ & !parity-check generator matrix for (128,80) code
|
||||
"24084000800020008000", &
|
||||
"b39678f7ccdb1baf5f4c", &
|
||||
"10001000400408012000", &
|
||||
"08104000100002010800", &
|
||||
"dc9c18f61ea0e4b7f05c", &
|
||||
"42c040160909ca002c00", &
|
||||
"cc50b52b9a80db0d7f9e", &
|
||||
"dde5ace80780bae74740", &
|
||||
"00800080020000890080", &
|
||||
"01020040010400400040", &
|
||||
"20008010020000100030", &
|
||||
"80400008004000040050", &
|
||||
"a4b397810915126f5604", &
|
||||
"04040100001040200008", &
|
||||
"00800006000888000800", &
|
||||
"00010c00000104040001", &
|
||||
"cc7cd7d953cdc204eba0", &
|
||||
"0094abe7dd146beb16ce", &
|
||||
"5af2aec8c7b051c7544a", &
|
||||
"14040508801840200088", &
|
||||
"7392f5e720f8f5a62c1e", &
|
||||
"503cc2a06bff4e684ec9", &
|
||||
"5a2efd46f1efbb513b80", &
|
||||
"ac06e9513fd411f1de03", &
|
||||
"16a31be3dd3082ca2bd6", &
|
||||
"28542e0daf62fe1d9332", &
|
||||
"00210c002001540c0401", &
|
||||
"0ed90d56f84298706a98", &
|
||||
"939670f7ecdf9baf4f4c", &
|
||||
"cfe41dec47a433e66240", &
|
||||
"16d2179c2d5888222630", &
|
||||
"408000160108ca002800", &
|
||||
"808000830a00018900a0", &
|
||||
"9ae2ed8ef3afbf8c3a52", &
|
||||
"5aaafd86f3efbfc83b02", &
|
||||
"f39658f68cdb0baf1f4c", &
|
||||
"9414bb6495106261366a", &
|
||||
"71ba18670c08411bf682", &
|
||||
"7298f1a7217cf5c62e5e", &
|
||||
"86d7a4864396a981369b", &
|
||||
"a8042c01ae22fe191362", &
|
||||
"9235ae108b2d60d0e306", &
|
||||
"dfe5ade807a03be74640", &
|
||||
"d2451588e6e27ccd9bc4", &
|
||||
"12b51ae39d20e2ea3bde", &
|
||||
"a49387810d95136fd604", &
|
||||
"467e7578e51d5b3b8a0e", &
|
||||
"f6ad1ac7cc3aaa3fe580"/
|
||||
|
||||
data colorder/0,1,2,3,4,5,6,7,8,9, &
|
||||
10,11,12,13,14,15,24,26,29,30, &
|
||||
32,43,44,47,60,77,79,97,101,111, &
|
||||
96,38,64,53,93,34,59,94,74,90, &
|
||||
108,123,85,57,70,25,69,62,48,49, &
|
||||
50,51,52,33,54,55,56,21,58,36, &
|
||||
16,61,23,63,20,65,66,67,68,46, &
|
||||
22,71,72,73,31,75,76,45,78,17, &
|
||||
80,81,82,83,84,42,86,87,88,89, &
|
||||
39,91,92,35,37,95,19,27,98,99, &
|
||||
100,28,102,103,104,105,106,107,40,109, &
|
||||
110,18,112,113,114,115,116,117,118,119, &
|
||||
120,121,122,41,124,125,126,127/
|
||||
|
||||
save first,gen144
|
||||
|
||||
if( first ) then ! fill the generator matrix
|
||||
gen144=0
|
||||
do i=1,48
|
||||
do j=1,5
|
||||
read(g(i)( (j-1)*4+1:(j-1)*4+4 ),"(Z4)") istr
|
||||
do jj=1,16
|
||||
icol=(j-1)*16+jj
|
||||
if( btest(istr,16-jj) ) gen144(i,icol)=1
|
||||
enddo
|
||||
enddo
|
||||
enddo
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
do i=1,48
|
||||
nsum=0
|
||||
do j=1,80
|
||||
nsum=nsum+message(j)*gen144(i,j)
|
||||
enddo
|
||||
pchecks(i)=mod(nsum,2)
|
||||
enddo
|
||||
itmp(1:48)=pchecks
|
||||
itmp(49:128)=message(1:80)
|
||||
codeword(colorder+1)=itmp(1:128)
|
||||
|
||||
return
|
||||
end subroutine encode_msk144
|
@ -1,51 +0,0 @@
|
||||
subroutine extractmessage144(decoded,msgreceived,nhashflag,recent_calls,nrecent)
|
||||
use iso_c_binding, only: c_loc,c_size_t
|
||||
use packjt
|
||||
use hashing
|
||||
|
||||
character*22 msgreceived
|
||||
character*12 call1,call2
|
||||
character*12 recent_calls(nrecent)
|
||||
integer*1 decoded(80)
|
||||
integer*1, target:: i1Dec8BitBytes(10)
|
||||
integer*1 i1hashdec
|
||||
integer*4 i4Dec6BitWords(12)
|
||||
|
||||
! Collapse 80 decoded bits to 10 bytes. Bytes 1-9 are the message, byte 10 is the hash
|
||||
do ibyte=1,10
|
||||
itmp=0
|
||||
do ibit=1,8
|
||||
itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*8+ibit))
|
||||
enddo
|
||||
i1Dec8BitBytes(ibyte)=itmp
|
||||
enddo
|
||||
|
||||
! Calculate the hash using the first 9 bytes.
|
||||
ihashdec=nhash(c_loc(i1Dec8BitBytes),int(9,c_size_t),146)
|
||||
ihashdec=2*iand(ihashdec,255)
|
||||
|
||||
! Compare calculated hash with received byte 10 - if they agree, keep the message.
|
||||
i1hashdec=ihashdec
|
||||
if( i1hashdec .eq. i1Dec8BitBytes(10) ) then
|
||||
! Good hash --- unpack 72-bit message
|
||||
do ibyte=1,12
|
||||
itmp=0
|
||||
do ibit=1,6
|
||||
itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*6+ibit))
|
||||
enddo
|
||||
i4Dec6BitWords(ibyte)=itmp
|
||||
enddo
|
||||
call unpackmsg144(i4Dec6BitWords,msgreceived,call1,call2)
|
||||
nhashflag=1
|
||||
if( call1(1:2) .ne. 'CQ' .and. call1(1:2) .ne. ' ' ) then
|
||||
call update_recent_calls(call1,recent_calls,nrecent)
|
||||
endif
|
||||
if( call2(1:2) .ne. ' ' ) then
|
||||
call update_recent_calls(call2,recent_calls,nrecent)
|
||||
endif
|
||||
else
|
||||
msgreceived=' '
|
||||
nhashflag=-1
|
||||
endif
|
||||
return
|
||||
end subroutine extractmessage144
|
@ -1,146 +0,0 @@
|
||||
subroutine genmsk144(msg0,mygrid,ichk,msgsent,i4tone,itype)
|
||||
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
|
||||
!
|
||||
! Encode an MSK144 message
|
||||
! Input:
|
||||
! - msg0 requested message to be transmitted
|
||||
! - ichk if ichk=1, return only msgsent
|
||||
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
|
||||
! - msgsent message as it will be decoded
|
||||
! - i4tone array of audio tone values, 0 or 1
|
||||
! - itype message type
|
||||
! 1 = standard message "Call_1 Call_2 Grid/Rpt"
|
||||
! 2 = type 1 prefix
|
||||
! 3 = type 1 suffix
|
||||
! 4 = type 2 prefix
|
||||
! 5 = type 2 suffix
|
||||
! 6 = free text (up to 13 characters)
|
||||
! 7 = short message "<Call_1 Call2> Rpt"
|
||||
|
||||
use iso_c_binding, only: c_loc,c_size_t
|
||||
use packjt
|
||||
use hashing
|
||||
character*22 msg0
|
||||
character*22 message !Message to be generated
|
||||
character*22 msgsent !Message as it will be received
|
||||
character*6 mygrid
|
||||
integer*4 i4Msg6BitWords(13) !72-bit message as 6-bit words
|
||||
integer*4 i4tone(144) !
|
||||
integer*1, target:: i1Msg8BitBytes(10) !80 bits represented in 10 bytes
|
||||
integer*1 codeword(128) !Encoded bits before re-ordering
|
||||
integer*1 msgbits(80) !72-bit message + 8-bit hash
|
||||
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
|
||||
integer*1 i1hash(4)
|
||||
integer*1 s8(8)
|
||||
real*8 pp(12)
|
||||
real*8 xi(864),xq(864),pi,twopi
|
||||
data s8/0,1,1,1,0,0,1,0/
|
||||
equivalence (ihash,i1hash)
|
||||
logical first
|
||||
data first/.true./
|
||||
save
|
||||
|
||||
if(first) then
|
||||
first=.false.
|
||||
nsym=128
|
||||
pi=4.0*atan(1.0)
|
||||
twopi=8.*atan(1.0)
|
||||
do i=1,12
|
||||
pp(i)=sin((i-1)*pi/12)
|
||||
enddo
|
||||
endif
|
||||
|
||||
if(msg0(1:1).eq.'@') then !Generate a fixed tone
|
||||
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
|
||||
go to 2
|
||||
1 nfreq=1000
|
||||
2 i4tone(1)=nfreq
|
||||
else
|
||||
message=msg0
|
||||
do i=1,22
|
||||
if(ichar(message(i:i)).eq.0) then
|
||||
message(i:)=' '
|
||||
exit
|
||||
endif
|
||||
enddo
|
||||
|
||||
do i=1,22 !Strip leading blanks
|
||||
if(message(1:1).ne.' ') exit
|
||||
message=message(i+1:)
|
||||
enddo
|
||||
|
||||
if(message(1:1).eq.'<') then
|
||||
call genmsk40(message,msgsent,ichk,i4tone,itype)
|
||||
if(itype.lt.0) go to 999
|
||||
i4tone(41)=-40
|
||||
go to 999
|
||||
endif
|
||||
|
||||
call packmsg(message,i4Msg6BitWords,itype) !Pack into 12 6-bit bytes
|
||||
call unpackmsg(i4Msg6BitWords,msgsent) !Unpack to get msgsent
|
||||
|
||||
if(ichk.eq.1) go to 999
|
||||
i4=0
|
||||
ik=0
|
||||
im=0
|
||||
do i=1,12
|
||||
nn=i4Msg6BitWords(i)
|
||||
do j=1, 6
|
||||
ik=ik+1
|
||||
i4=i4+i4+iand(1,ishft(nn,j-6))
|
||||
i4=iand(i4,255)
|
||||
if(ik.eq.8) then
|
||||
im=im+1
|
||||
i1Msg8BitBytes(im)=i4
|
||||
ik=0
|
||||
endif
|
||||
enddo
|
||||
enddo
|
||||
|
||||
ihash=nhash(c_loc(i1Msg8BitBytes),int(9,c_size_t),146)
|
||||
ihash=2*iand(ihash,32767) !Generate the 8-bit hash
|
||||
i1Msg8BitBytes(10)=i1hash(1) !Hash code to byte 10
|
||||
|
||||
mbit=0
|
||||
do i=1, 10
|
||||
i1=i1Msg8BitBytes(i)
|
||||
do ibit=1,8
|
||||
mbit=mbit+1
|
||||
msgbits(mbit)=iand(1,ishft(i1,ibit-8))
|
||||
enddo
|
||||
enddo
|
||||
|
||||
call encode_msk144(msgbits,codeword)
|
||||
|
||||
!Create 144-bit channel vector:
|
||||
!8-bit sync word + 48 bits + 8-bit sync word + 80 bits
|
||||
bitseq=0
|
||||
bitseq(1:8)=s8
|
||||
bitseq(9:56)=codeword(1:48)
|
||||
bitseq(57:64)=s8
|
||||
bitseq(65:144)=codeword(49:128)
|
||||
bitseq=2*bitseq-1
|
||||
|
||||
xq(1:6)=bitseq(1)*pp(7:12) !first bit is mapped to 1st half-symbol on q
|
||||
do i=1,71
|
||||
is=(i-1)*12+7
|
||||
xq(is:is+11)=bitseq(2*i+1)*pp
|
||||
enddo
|
||||
xq(864-5:864)=bitseq(1)*pp(1:6) !last half symbol
|
||||
do i=1,72
|
||||
is=(i-1)*12+1
|
||||
xi(is:is+11)=bitseq(2*i)*pp
|
||||
enddo
|
||||
! Map I and Q to tones.
|
||||
i4tone=0
|
||||
do i=1,72
|
||||
i4tone(2*i-1)=(bitseq(2*i)*bitseq(2*i-1)+1)/2;
|
||||
i4tone(2*i)=-(bitseq(2*i)*bitseq(mod(2*i,144)+1)-1)/2;
|
||||
enddo
|
||||
endif
|
||||
|
||||
! Flip polarity
|
||||
i4tone=-i4tone+1
|
||||
|
||||
999 return
|
||||
end subroutine genmsk144
|
@ -1,66 +0,0 @@
|
||||
subroutine genmsk_short(msg,msgsent,ichk,itone,itype)
|
||||
|
||||
use hashing
|
||||
character*22 msg,msgsent
|
||||
character*3 crpt,rpt(0:7)
|
||||
logical first
|
||||
integer itone(35)
|
||||
integer ig24(0:4096-1) !Codewords for Golay (24,12) code
|
||||
integer b11(11)
|
||||
data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 code
|
||||
data rpt /'26 ','27 ','28 ','R26','R27','R28','RRR','73 '/
|
||||
data first/.true./
|
||||
save first,ig24
|
||||
|
||||
if(first) then
|
||||
call golay24_table(ig24) !Define the Golay(24,12) codewords
|
||||
first=.false.
|
||||
endif
|
||||
|
||||
itype=-1
|
||||
msgsent='*** bad message ***'
|
||||
itone=0
|
||||
i1=index(msg,'>')
|
||||
if(i1.lt.9) go to 900
|
||||
call fmtmsg(msg,iz)
|
||||
crpt=msg(i1+2:i1+5)
|
||||
do i=0,7
|
||||
if(crpt.eq.rpt(i)) go to 10
|
||||
enddo
|
||||
go to 900
|
||||
|
||||
10 irpt=i !Report index, 0-7
|
||||
if(ichk.lt.10000) then
|
||||
call hash(msg(2:i1-1),i1-2,ihash)
|
||||
ihash=iand(ihash,511) !9-bit hash for the two callsigns
|
||||
ig=8*ihash + irpt !12-bit message information
|
||||
else
|
||||
ig=ichk-10000
|
||||
endif
|
||||
ncodeword=ig24(ig)
|
||||
itone(1:11)=b11 !Insert the Barker-11 code
|
||||
n=2**24
|
||||
do i=12,35 !Insert codeword into itone array
|
||||
n=n/2
|
||||
itone(i)=0
|
||||
if(iand(ncodeword,n).ne.0) itone(i)=1
|
||||
enddo
|
||||
msgsent=msg
|
||||
itype=7
|
||||
|
||||
n=count(itone(1:35).eq.0)
|
||||
if(mod(n,2).ne.0) stop 'Parity error in genmsk_short.'
|
||||
|
||||
900 return
|
||||
end subroutine genmsk_short
|
||||
|
||||
subroutine hash_calls(calls,ih9)
|
||||
|
||||
use hashing
|
||||
character*(*) calls
|
||||
i1=index(calls,'>')
|
||||
call hash(calls(2:i1-1),i1-2,ih9)
|
||||
ih9=iand(ih9,511) !9-bit hash for the two callsigns
|
||||
|
||||
return
|
||||
end subroutine hash_calls
|
@ -1,175 +0,0 @@
|
||||
program ldpcsim
|
||||
|
||||
use, intrinsic :: iso_c_binding
|
||||
use iso_c_binding, only: c_loc,c_size_t
|
||||
use hashing
|
||||
use packjt
|
||||
parameter(NRECENT=10)
|
||||
character*12 recent_calls(NRECENT)
|
||||
character*22 msg,msgsent,msgreceived
|
||||
character*8 arg
|
||||
integer*1, allocatable :: codeword(:), decoded(:), message(:)
|
||||
integer*1, target:: i1Msg8BitBytes(10)
|
||||
integer*1 i1hash(4)
|
||||
integer*1 msgbits(80)
|
||||
integer*4 i4Msg6BitWords(13)
|
||||
integer ihash
|
||||
integer nerrtot(0:128),nerrdec(0:128)
|
||||
real*8, allocatable :: lratio(:), rxdata(:), rxavgd(:)
|
||||
real, allocatable :: yy(:), llr(:)
|
||||
equivalence(ihash,i1hash)
|
||||
|
||||
do i=1,NRECENT
|
||||
recent_calls(i)=' '
|
||||
enddo
|
||||
nerrtot=0
|
||||
nerrdec=0
|
||||
|
||||
nargs=iargc()
|
||||
if(nargs.ne.4) then
|
||||
print*,'Usage: ldpcsim niter navg #trials s '
|
||||
print*,'eg: ldpcsim 10 1 1000 0.75'
|
||||
return
|
||||
endif
|
||||
call getarg(1,arg)
|
||||
read(arg,*) max_iterations
|
||||
call getarg(2,arg)
|
||||
read(arg,*) navg
|
||||
call getarg(3,arg)
|
||||
read(arg,*) ntrials
|
||||
call getarg(4,arg)
|
||||
read(arg,*) s
|
||||
|
||||
! don't count hash bits as data bits
|
||||
K=72
|
||||
N=128
|
||||
rate=real(K)/real(N)
|
||||
|
||||
write(*,*) "rate: ",rate
|
||||
|
||||
write(*,*) "niter= ",max_iterations," navg= ",navg," s= ",s
|
||||
|
||||
allocate ( codeword(N), decoded(K), message(K) )
|
||||
allocate ( lratio(N), rxdata(N), rxavgd(N), yy(N), llr(N) )
|
||||
|
||||
msg="K9AN K1JT EN50"
|
||||
call packmsg(msg,i4Msg6BitWords,itype) !Pack into 12 6-bit bytes
|
||||
call unpackmsg(i4Msg6BitWords,msgsent) !Unpack to get msgsent
|
||||
write(*,*) "message sent ",msgsent
|
||||
|
||||
i4=0
|
||||
ik=0
|
||||
im=0
|
||||
do i=1,12
|
||||
nn=i4Msg6BitWords(i)
|
||||
do j=1, 6
|
||||
ik=ik+1
|
||||
i4=i4+i4+iand(1,ishft(nn,j-6))
|
||||
i4=iand(i4,255)
|
||||
if(ik.eq.8) then
|
||||
im=im+1
|
||||
! if(i4.gt.127) i4=i4-256
|
||||
i1Msg8BitBytes(im)=i4
|
||||
ik=0
|
||||
endif
|
||||
enddo
|
||||
enddo
|
||||
|
||||
ihash=nhash(c_loc(i1Msg8BitBytes),int(9,c_size_t),146)
|
||||
ihash=2*iand(ihash,32767) !Generate the 8-bit hash
|
||||
i1Msg8BitBytes(10)=i1hash(1) !Hash code to byte 10
|
||||
mbit=0
|
||||
do i=1, 10
|
||||
i1=i1Msg8BitBytes(i)
|
||||
do ibit=1,8
|
||||
mbit=mbit+1
|
||||
msgbits(mbit)=iand(1,ishft(i1,ibit-8))
|
||||
enddo
|
||||
enddo
|
||||
call encode_msk144(msgbits,codeword)
|
||||
call init_random_seed()
|
||||
|
||||
write(*,*) "Eb/N0 SNR2500 ngood nundetected nbadhash sigma"
|
||||
do idb = 14,-6,-1
|
||||
db=idb/2.0-1.0
|
||||
sigma=1/sqrt( 2*rate*(10**(db/10.0)) )
|
||||
ngood=0
|
||||
nue=0
|
||||
nbadhash=0
|
||||
|
||||
do itrial=1, ntrials
|
||||
rxavgd=0d0
|
||||
do iav=1,navg
|
||||
call sgran()
|
||||
! Create a realization of a noisy received word
|
||||
do i=1,N
|
||||
rxdata(i) = 2.0*codeword(i)-1.0 + sigma*gran()
|
||||
enddo
|
||||
rxavgd=rxavgd+rxdata
|
||||
enddo
|
||||
rxdata=rxavgd
|
||||
nerr=0
|
||||
do i=1,N
|
||||
if( rxdata(i)*(2*codeword(i)-1.0) .lt. 0 ) nerr=nerr+1
|
||||
enddo
|
||||
nerrtot(nerr)=nerrtot(nerr)+1
|
||||
|
||||
! Correct signal normalization is important for this decoder.
|
||||
rxav=sum(rxdata)/N
|
||||
rx2av=sum(rxdata*rxdata)/N
|
||||
rxsig=sqrt(rx2av-rxav*rxav)
|
||||
rxdata=rxdata/rxsig
|
||||
! To match the metric to the channel, s should be set to the noise standard deviation.
|
||||
! For now, set s to the value that optimizes decode probability near threshold.
|
||||
! The s parameter can be tuned to trade a few tenth's dB of threshold for an order of
|
||||
! magnitude in UER
|
||||
if( s .lt. 0 ) then
|
||||
ss=sigma
|
||||
else
|
||||
ss=s
|
||||
endif
|
||||
|
||||
llr=2.0*rxdata/(ss*ss)
|
||||
lratio=exp(llr)
|
||||
yy=rxdata
|
||||
|
||||
! max_iterations is max number of belief propagation iterations
|
||||
! call ldpc_decode(lratio, decoded, max_iterations, niterations, max_dither, ndither)
|
||||
! call amsdecode(yy, max_iterations, decoded, niterations)
|
||||
! call bitflipmsk144(rxdata, decoded, niterations)
|
||||
call bpdecode144(llr, max_iterations, decoded, niterations)
|
||||
|
||||
! If the decoder finds a valid codeword, niterations will be .ge. 0.
|
||||
if( niterations .ge. 0 ) then
|
||||
call extractmessage144(decoded,msgreceived,nhashflag,recent_calls,nrecent)
|
||||
if( nhashflag .ne. 1 ) then
|
||||
nbadhash=nbadhash+1
|
||||
endif
|
||||
nueflag=0
|
||||
|
||||
! Check the message plus hash against what was sent.
|
||||
do i=1,K
|
||||
if( msgbits(i) .ne. decoded(i) ) then
|
||||
nueflag=1
|
||||
endif
|
||||
enddo
|
||||
if( nhashflag .eq. 1 .and. nueflag .eq. 0 ) then
|
||||
ngood=ngood+1
|
||||
nerrdec(nerr)=nerrdec(nerr)+1
|
||||
else if( nhashflag .eq. 1 .and. nueflag .eq. 1 ) then
|
||||
nue=nue+1;
|
||||
endif
|
||||
endif
|
||||
enddo
|
||||
snr2500=db-3.5
|
||||
write(*,"(f4.1,4x,f5.1,1x,i8,1x,i8,1x,i8,8x,f5.2)") db,snr2500,ngood,nue,nbadhash,ss
|
||||
|
||||
enddo
|
||||
|
||||
open(unit=23,file='nerrhisto.dat',status='unknown')
|
||||
do i=0,128
|
||||
write(23,'(i4,2x,i10,i10,f10.2)') i,nerrdec(i),nerrtot(i),real(nerrdec(i))/real(nerrtot(i)+1e-10)
|
||||
enddo
|
||||
close(23)
|
||||
|
||||
end program ldpcsim
|
136
lib/msk144d.f90
136
lib/msk144d.f90
@ -1,136 +0,0 @@
|
||||
program msk144d
|
||||
|
||||
! Test the msk144 decoder for WSJT-X
|
||||
|
||||
use options
|
||||
use timer_module, only: timer
|
||||
use timer_impl, only: init_timer
|
||||
use readwav
|
||||
|
||||
character c
|
||||
character*80 line
|
||||
character*512 datadir
|
||||
character*500 infile
|
||||
character*12 mycall,hiscall
|
||||
character*6 mygrid
|
||||
character(len=500) optarg
|
||||
|
||||
logical :: display_help=.false.
|
||||
logical*1 bShMsgs
|
||||
logical*1 btrain
|
||||
logical*1 bswl
|
||||
|
||||
type(wav_header) :: wav
|
||||
|
||||
integer*2 id2(30*12000)
|
||||
integer*2 ichunk(7*1024)
|
||||
|
||||
real*8 pcoeffs(5)
|
||||
|
||||
type (option) :: long_options(9) = [ &
|
||||
option ('ndepth',.true.,'c','ndepth',''), &
|
||||
option ('dxcall',.true.,'d','hiscall',''), &
|
||||
option ('evemode',.true.,'e','Must be used with -s.',''), &
|
||||
option ('frequency',.true.,'f','rxfreq',''), &
|
||||
option ('help',.false.,'h','Display this help message',''), &
|
||||
option ('mycall',.true.,'m','mycall',''), &
|
||||
option ('nftol',.true.,'n','nftol',''), &
|
||||
option ('rxequalize',.false.,'r','Rx Equalize',''), &
|
||||
option ('short',.false.,'s','enable Sh','') &
|
||||
]
|
||||
t0=0.0
|
||||
ndepth=3
|
||||
ntol=100
|
||||
nrxfreq=1500
|
||||
mycall=''
|
||||
mygrid='EN50WC'
|
||||
hiscall=''
|
||||
bShMsgs=.false.
|
||||
btrain=.false.
|
||||
bswl=.false.
|
||||
datadir='.'
|
||||
pcoeffs=0.d0
|
||||
|
||||
do
|
||||
call getopt('c:d:ef:hm:n:rs',long_options,c,optarg,narglen,nstat,noffset,nremain,.true.)
|
||||
if( nstat .ne. 0 ) then
|
||||
exit
|
||||
end if
|
||||
select case (c)
|
||||
case ('c')
|
||||
read (optarg(:narglen), *) ndepth
|
||||
case ('d')
|
||||
read (optarg(:narglen), *) hiscall
|
||||
case ('e')
|
||||
bswl=.true.
|
||||
case ('f')
|
||||
read (optarg(:narglen), *) nrxfreq
|
||||
case ('h')
|
||||
display_help = .true.
|
||||
case ('m')
|
||||
read (optarg(:narglen), *) mycall
|
||||
case ('n')
|
||||
read (optarg(:narglen), *) ntol
|
||||
case ('r')
|
||||
btrain=.true.
|
||||
case ('s')
|
||||
bShMsgs=.true.
|
||||
end select
|
||||
end do
|
||||
|
||||
if(display_help .or. nstat.lt.0 .or. nremain.lt.1) then
|
||||
print *, ''
|
||||
print *, 'Usage: msk144d [OPTIONS] file1 [file2 ...]'
|
||||
print *, ''
|
||||
print *, ' msk144 decode pre-recorded .WAV file(s)'
|
||||
print *, ''
|
||||
print *, 'OPTIONS:'
|
||||
do i = 1, size (long_options)
|
||||
call long_options(i) % print (6)
|
||||
end do
|
||||
go to 999
|
||||
endif
|
||||
|
||||
call init_timer ('timer.out')
|
||||
call timer('msk144 ',0)
|
||||
ndecoded=0
|
||||
do ifile=noffset+1,noffset+nremain
|
||||
call get_command_argument(ifile,optarg,narglen)
|
||||
infile=optarg(:narglen)
|
||||
call timer('read ',0)
|
||||
call wav%read (infile)
|
||||
i1=index(infile,'.wav')
|
||||
if( i1 .eq. 0 ) i1=index(infile,'.WAV')
|
||||
read(infile(i1-6:i1-1),*,err=998) nutc
|
||||
inquire(FILE=infile,SIZE=isize)
|
||||
npts=min((isize-216)/2,360000)
|
||||
read(unit=wav%lun) id2(1:npts)
|
||||
close(unit=wav%lun)
|
||||
call timer('read ',1)
|
||||
|
||||
do i=1,npts-7*1024+1,7*512
|
||||
ichunk=id2(i:i+7*1024-1)
|
||||
tsec=(i-1)/12000.0
|
||||
tt=sum(float(abs(id2(i:i+7*512-1))))
|
||||
if( tt .ne. 0.0 ) then
|
||||
call mskrtd(ichunk,nutc,tsec,ntol,nrxfreq,ndepth,mycall,mygrid,hiscall,bShMsgs, &
|
||||
btrain,pcoeffs,bswl,datadir,line)
|
||||
if( index(line,"&") .ne. 0 .or. &
|
||||
index(line,"^") .ne. 0 .or. &
|
||||
index(line,"!") .ne. 0 .or. &
|
||||
index(line,"@") .ne. 0 ) then
|
||||
write(*,*) line
|
||||
endif
|
||||
endif
|
||||
enddo
|
||||
enddo
|
||||
|
||||
call timer('msk144 ',1)
|
||||
call timer('msk144 ',101)
|
||||
go to 999
|
||||
|
||||
998 print*,'Cannot read from file:'
|
||||
print*,infile
|
||||
|
||||
999 continue
|
||||
end program msk144d
|
197
lib/msk144sd.f90
197
lib/msk144sd.f90
@ -1,197 +0,0 @@
|
||||
program msk144sd
|
||||
!
|
||||
! A simple decoder for slow msk144.
|
||||
! Can be used as a (slow) brute-force multi-decoder by looping
|
||||
! over a set of carrier frequencies.
|
||||
!
|
||||
use options
|
||||
use timer_module, only: timer
|
||||
use timer_impl, only: init_timer
|
||||
use readwav
|
||||
|
||||
parameter (NRECENT=10)
|
||||
parameter (NSPM=864)
|
||||
parameter (NPATTERNS=4)
|
||||
|
||||
character ch
|
||||
character*80 line
|
||||
character*500 infile
|
||||
character*12 mycall,hiscall
|
||||
character*6 mygrid
|
||||
character(len=500) optarg
|
||||
character*22 msgreceived
|
||||
character*12 recent_calls(NRECENT)
|
||||
|
||||
complex cdat(30*375)
|
||||
complex c(NSPM)
|
||||
complex ct(NSPM)
|
||||
|
||||
real softbits(144)
|
||||
real xmc(NPATTERNS)
|
||||
|
||||
logical :: display_help=.false.
|
||||
|
||||
type(wav_header) :: wav
|
||||
|
||||
integer iavmask(8)
|
||||
integer iavpatterns(8,NPATTERNS)
|
||||
integer npkloc(10)
|
||||
|
||||
integer*2 id2(30*12000)
|
||||
integer*2 ichunk(7*1024)
|
||||
|
||||
data iavpatterns/ &
|
||||
1,1,1,1,0,0,0,0, &
|
||||
0,1,1,1,1,0,0,0, &
|
||||
0,0,1,1,1,1,0,0, &
|
||||
1,1,1,1,1,1,0,0/
|
||||
data xmc/2.0,4.5,2.5,3.0/
|
||||
|
||||
type (option) :: long_options(2) = [ &
|
||||
option ('frequency',.true.,'f','rxfreq',''), &
|
||||
option ('help',.false.,'h','Display this help message','') &
|
||||
]
|
||||
t0=0.0
|
||||
ntol=100
|
||||
nrxfreq=1500
|
||||
|
||||
do
|
||||
call getopt('f:h',long_options,ch,optarg,narglen,nstat,noffset,nremain,.true.)
|
||||
if( nstat .ne. 0 ) then
|
||||
exit
|
||||
end if
|
||||
select case (ch)
|
||||
case ('f')
|
||||
read (optarg(:narglen), *) nrxfreq
|
||||
case ('h')
|
||||
display_help = .true.
|
||||
end select
|
||||
end do
|
||||
|
||||
if(display_help .or. nstat.lt.0 .or. nremain.lt.1) then
|
||||
print *, ''
|
||||
print *, 'Usage: msk144sd [OPTIONS] file1 [file2 ...]'
|
||||
print *, ''
|
||||
print *, ' decode pre-recorded .WAV file(s)'
|
||||
print *, ''
|
||||
print *, 'OPTIONS:'
|
||||
do i = 1, size (long_options)
|
||||
call long_options(i) % print (6)
|
||||
end do
|
||||
go to 999
|
||||
endif
|
||||
|
||||
call init_timer ('timer.out')
|
||||
call timer('msk144 ',0)
|
||||
ndecoded=0
|
||||
do ifile=noffset+1,noffset+nremain
|
||||
call get_command_argument(ifile,optarg,narglen)
|
||||
infile=optarg(:narglen)
|
||||
call timer('read ',0)
|
||||
call wav%read (infile)
|
||||
i1=index(infile,'.wav')
|
||||
if( i1 .eq. 0 ) i1=index(infile,'.WAV')
|
||||
read(infile(i1-6:i1-1),*,err=998) nutc
|
||||
inquire(FILE=infile,SIZE=isize)
|
||||
npts=min((isize-216)/2,360000)
|
||||
read(unit=wav%lun) id2(1:npts)
|
||||
close(unit=wav%lun)
|
||||
call timer('read ',1)
|
||||
|
||||
! do if=1,89 ! brute force multi-decoder
|
||||
fo=nrxfreq
|
||||
! fo=(if-1)*25.0+300.0
|
||||
call msksddc(id2,npts,fo,cdat)
|
||||
np=npts/32
|
||||
ntol=200 ! actual ntol is ntol/32=6.25 Hz. Detection window is 12.5 Hz wide
|
||||
fc=1500.0
|
||||
call msk144spd(cdat,np,ntol,ndecodesuccess,msgreceived,fc,fest,tdec,navg,ct, &
|
||||
softbits,recent_calls,nrecent)
|
||||
nsnr=0 ! need an snr estimate
|
||||
if( ndecodesuccess .eq. 1 ) then
|
||||
fest=fo+fest-fc ! fudging because spd thinks input signal is at 1500 Hz
|
||||
goto 900
|
||||
endif
|
||||
! If short ping decoder doesn't find a decode
|
||||
npat=NPATTERNS
|
||||
do iavg=1,npat
|
||||
iavmask=iavpatterns(1:8,iavg)
|
||||
navg=sum(iavmask)
|
||||
deltaf=4.0/real(navg) ! search increment for frequency sync
|
||||
npeaks=4
|
||||
ntol=200
|
||||
fc=1500.0
|
||||
call msk144sync(cdat(1:6*NSPM),6,ntol,deltaf,iavmask,npeaks,fc, &
|
||||
fest,npkloc,nsyncsuccess,xmax,c)
|
||||
if( nsyncsuccess .eq. 0 ) cycle
|
||||
|
||||
do ipk=1,npeaks
|
||||
do is=1,3
|
||||
ic0=npkloc(ipk)
|
||||
if(is.eq.2) ic0=max(1,ic0-1)
|
||||
if(is.eq.3) ic0=min(NSPM,ic0+1)
|
||||
ct=cshift(c,ic0-1)
|
||||
call msk144decodeframe(ct,softbits,msgreceived,ndecodesuccess, &
|
||||
recent_calls,nrecent)
|
||||
if(ndecodesuccess .gt. 0) then
|
||||
tdec=tsec+xmc(iavg)*tframe
|
||||
fest=fo+(fest-fc)/32.0
|
||||
goto 900
|
||||
endif
|
||||
enddo !Slicer dither
|
||||
enddo !Peak loop
|
||||
enddo
|
||||
|
||||
! enddo
|
||||
900 continue
|
||||
if( ndecodesuccess .gt. 0 ) then
|
||||
write(*,1020) nutc,nsnr,tdec,nint(fest),' % ',msgreceived,navg
|
||||
1020 format(i6.6,i4,f5.1,i5,a3,a22,i4)
|
||||
endif
|
||||
enddo
|
||||
|
||||
call timer('msk144 ',1)
|
||||
call timer('msk144 ',101)
|
||||
go to 999
|
||||
|
||||
998 print*,'Cannot read from file:'
|
||||
print*,infile
|
||||
|
||||
999 continue
|
||||
end program msk144sd
|
||||
|
||||
subroutine msksddc(id2,npts,fc,cdat)
|
||||
|
||||
! The msk144 detector/demodulator/decoder will decode signals
|
||||
! with carrier frequency, fc, in the range fN/4 +/- 0.03333*fN.
|
||||
!
|
||||
! For slow MSK144 with nslow=32:
|
||||
! fs=12000/32=375 Hz, fN=187.5 Hz
|
||||
!
|
||||
! This routine accepts input samples with fs=12000 Hz. It
|
||||
! downconverts and decimates by 32 to center a signal with input carrier
|
||||
! frequency fc at new carrier frequency 1500/32=46.875 Hz.
|
||||
! The analytic signal is returned.
|
||||
|
||||
parameter (NFFT1=30*12000,NFFT2=30*375)
|
||||
integer*2 id2(npts)
|
||||
complex cx(0:NFFT1)
|
||||
complex cdat(30*375)
|
||||
|
||||
dt=1.0/12000.0
|
||||
df=1.0/(NFFT1*dt)
|
||||
icenter=int(fc/df+0.5)
|
||||
i46p875=int(46.875/df+0.5)
|
||||
ishift=icenter-i46p875
|
||||
cx=cmplx(0.0,0.0)
|
||||
cx(1:npts)=id2
|
||||
call four2a(cx,NFFT1,1,-1,1)
|
||||
cx=cshift(cx,ishift)
|
||||
cx(1)=0.5*cx(1)
|
||||
cx(2*i46p875+1:)=cmplx(0.0,0.0)
|
||||
call four2a(cx,NFFT2,1,1,1)
|
||||
cdat(1:npts/32)=cx(0:npts/32-1)/NFFT1
|
||||
return
|
||||
|
||||
end subroutine msksddc
|
||||
|
@ -13,10 +13,10 @@ program msk144sim
|
||||
integer itone(144) !Message bits
|
||||
|
||||
nargs=iargc()
|
||||
if(nargs.ne.6) then
|
||||
print*,'Usage: msk144sim message freq width nslow snr nfiles'
|
||||
print*,'Example: msk144sim "K1ABC W9XYZ EN37" 1500 0.12 1 2 1'
|
||||
print*,' msk144sim "K1ABC W9XYZ EN37" 1500 2.5 32 15 1'
|
||||
if(nargs.ne.5) then
|
||||
print*,'Usage: msk144sim message freq width snr nfiles'
|
||||
print*,'Example: msk144sim "K1ABC W9XYZ EN37" 1500 0.12 2 1'
|
||||
print*,' msk144sim "K1ABC W9XYZ EN37" 1500 2.5 15 1'
|
||||
go to 999
|
||||
endif
|
||||
call getarg(1,msg)
|
||||
@ -25,10 +25,8 @@ program msk144sim
|
||||
call getarg(3,arg)
|
||||
read(arg,*) width
|
||||
call getarg(4,arg)
|
||||
read(arg,*) nslow
|
||||
call getarg(5,arg)
|
||||
read(arg,*) snrdb
|
||||
call getarg(6,arg)
|
||||
call getarg(5,arg)
|
||||
read(arg,*) nfiles
|
||||
|
||||
!sig is the peak amplitude of the ping.
|
||||
@ -50,9 +48,9 @@ program msk144sim
|
||||
|
||||
twopi=8.d0*atan(1.d0)
|
||||
nsym=144
|
||||
nsps=6*nslow
|
||||
nsps=6
|
||||
if( itone(41) .lt. 0 ) nsym=40
|
||||
baud=2000.d0/nslow
|
||||
baud=2000.d0
|
||||
dphi0=twopi*(freq-0.25d0*baud)/12000.d0
|
||||
dphi1=twopi*(freq+0.25d0*baud)/12000.d0
|
||||
phi=0.0
|
||||
@ -79,7 +77,7 @@ program msk144sim
|
||||
go to 999
|
||||
endif
|
||||
|
||||
if(nslow.eq.1) call makepings(pings,NMAX,width,sig)
|
||||
call makepings(pings,NMAX,width,sig)
|
||||
|
||||
! call sgran()
|
||||
do ifile=1,nfiles !Loop over requested number of files
|
||||
@ -92,8 +90,7 @@ program msk144sim
|
||||
fac=sqrt(6000.0/2500.0)
|
||||
do i=0,NMAX-1
|
||||
xx=gran()
|
||||
if(nslow.eq.1) wave(i)=pings(i)*waveform(i) + fac*xx
|
||||
if(nslow.gt.1) wave(i)=sig*waveform(i) + fac*xx
|
||||
wave(i)=pings(i)*waveform(i) + fac*xx
|
||||
iwave(i)=30.0*wave(i)
|
||||
enddo
|
||||
|
||||
|
24
lib/platanh.f90
Normal file
24
lib/platanh.f90
Normal file
@ -0,0 +1,24 @@
|
||||
subroutine platanh(x,y)
|
||||
isign=+1
|
||||
z=x
|
||||
if( x.lt.0 ) then
|
||||
isign=-1
|
||||
z=abs(x)
|
||||
endif
|
||||
if( z.le. 0.664 ) then
|
||||
y=x/0.83
|
||||
return
|
||||
elseif( z.le. 0.9217 ) then
|
||||
y=isign*(z-0.4064)/0.322
|
||||
return
|
||||
elseif( z.le. 0.9951 ) then
|
||||
y=isign*(z-0.8378)/0.0524
|
||||
return
|
||||
elseif( z.le. 0.9998 ) then
|
||||
y=isign*(z-0.9914)/0.0012
|
||||
return
|
||||
else
|
||||
y=isign*7.0
|
||||
return
|
||||
endif
|
||||
end subroutine platanh
|
24
lib/pltanh.f90
Normal file
24
lib/pltanh.f90
Normal file
@ -0,0 +1,24 @@
|
||||
subroutine pltanh(x,y)
|
||||
isign=+1
|
||||
z=x
|
||||
if( x.lt.0 ) then
|
||||
isign=-1
|
||||
z=abs(x)
|
||||
endif
|
||||
if( z.le. 0.8 ) then
|
||||
y=0.83*x
|
||||
return
|
||||
elseif( z.le. 1.6 ) then
|
||||
y=isign*(0.322*z+0.4064)
|
||||
return
|
||||
elseif( z.le. 3.0 ) then
|
||||
y=isign*(0.0524*z+0.8378)
|
||||
return
|
||||
elseif( z.lt. 7.0 ) then
|
||||
y=isign*(0.0012*z+0.9914)
|
||||
return
|
||||
else
|
||||
y=isign*0.9998
|
||||
return
|
||||
endif
|
||||
end subroutine pltanh
|
@ -57,8 +57,9 @@ subroutine sync65(nfa,nfb,naggressive,ntol,nqsym,ca,ncand,nrobust, &
|
||||
freq=i*df
|
||||
itry=0
|
||||
! if(naggressive.gt.0 .and. ntol.lt.1000 .and. ccfmax.ge.thresh0) then
|
||||
if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then
|
||||
if(i.ne.ipk) cycle
|
||||
! if(naggressive.gt.0 .and. ccfmax.ge.thresh0) then
|
||||
if(bVHF) then
|
||||
if(i.ne.ipk .or. ccfmax.lt.thresh0) cycle
|
||||
itry=1
|
||||
ncand=ncand+1
|
||||
else
|
||||
|
@ -1,117 +0,0 @@
|
||||
subroutine unpackmsg144(dat,msg,c1,c2)
|
||||
! special unpackmsg for MSK144 - returns call1 and call2 to enable
|
||||
! maintenance of a recent-calls-heard list
|
||||
|
||||
use packjt
|
||||
parameter (NBASE=37*36*10*27*27*27)
|
||||
parameter (NGBASE=180*180)
|
||||
integer dat(12)
|
||||
character c1*12,c2*12,grid*4,msg*22,grid6*6,psfx*4,junk2*4
|
||||
logical cqnnn
|
||||
|
||||
cqnnn=.false.
|
||||
nc1=ishft(dat(1),22) + ishft(dat(2),16) + ishft(dat(3),10)+ &
|
||||
ishft(dat(4),4) + iand(ishft(dat(5),-2),15)
|
||||
|
||||
nc2=ishft(iand(dat(5),3),26) + ishft(dat(6),20) + &
|
||||
ishft(dat(7),14) + ishft(dat(8),8) + ishft(dat(9),2) + &
|
||||
iand(ishft(dat(10),-4),3)
|
||||
|
||||
ng=ishft(iand(dat(10),15),12) + ishft(dat(11),6) + dat(12)
|
||||
|
||||
if(ng.ge.32768) then
|
||||
call unpacktext(nc1,nc2,ng,msg)
|
||||
c1(1:12)=' '
|
||||
c2(1:12)=' '
|
||||
go to 100
|
||||
endif
|
||||
|
||||
call unpackcall(nc1,c1,iv2,psfx)
|
||||
if(iv2.eq.0) then
|
||||
! This is an "original JT65" message
|
||||
if(nc1.eq.NBASE+1) c1='CQ '
|
||||
if(nc1.eq.NBASE+2) c1='QRZ '
|
||||
nfreq=nc1-NBASE-3
|
||||
if(nfreq.ge.0 .and. nfreq.le.999) then
|
||||
write(c1,1002) nfreq
|
||||
1002 format('CQ ',i3.3)
|
||||
cqnnn=.true.
|
||||
endif
|
||||
endif
|
||||
|
||||
call unpackcall(nc2,c2,junk1,junk2)
|
||||
call unpackgrid(ng,grid)
|
||||
|
||||
if(iv2.gt.0) then
|
||||
! This is a JT65v2 message
|
||||
do i=1,4
|
||||
if(ichar(psfx(i:i)).eq.0) psfx(i:i)=' '
|
||||
enddo
|
||||
|
||||
n1=len_trim(psfx)
|
||||
n2=len_trim(c2)
|
||||
if(iv2.eq.1) msg='CQ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
|
||||
if(iv2.eq.2) msg='QRZ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
|
||||
if(iv2.eq.3) msg='DE '//psfx(:n1)//'/'//c2(:n2)//' '//grid
|
||||
if(iv2.eq.4) msg='CQ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
|
||||
if(iv2.eq.5) msg='QRZ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
|
||||
if(iv2.eq.6) msg='DE '//c2(:n2)//'/'//psfx(:n1)//' '//grid
|
||||
if(iv2.eq.7) msg='DE '//c2(:n2)//' '//grid
|
||||
if(iv2.eq.8) msg=' '
|
||||
go to 100
|
||||
else
|
||||
|
||||
endif
|
||||
|
||||
grid6=grid//'ma'
|
||||
call grid2k(grid6,k)
|
||||
if(k.ge.1 .and. k.le.450) call getpfx2(k,c1)
|
||||
if(k.ge.451 .and. k.le.900) call getpfx2(k,c2)
|
||||
|
||||
i=index(c1,char(0))
|
||||
if(i.ge.3) c1=c1(1:i-1)//' '
|
||||
i=index(c2,char(0))
|
||||
if(i.ge.3) c2=c2(1:i-1)//' '
|
||||
|
||||
msg=' '
|
||||
j=0
|
||||
if(cqnnn) then
|
||||
msg=c1//' '
|
||||
j=7 !### ??? ###
|
||||
go to 10
|
||||
endif
|
||||
|
||||
do i=1,12
|
||||
j=j+1
|
||||
msg(j:j)=c1(i:i)
|
||||
if(c1(i:i).eq.' ') go to 10
|
||||
enddo
|
||||
j=j+1
|
||||
msg(j:j)=' '
|
||||
|
||||
10 do i=1,12
|
||||
if(j.le.21) j=j+1
|
||||
msg(j:j)=c2(i:i)
|
||||
if(c2(i:i).eq.' ') go to 20
|
||||
enddo
|
||||
if(j.le.21) j=j+1
|
||||
msg(j:j)=' '
|
||||
|
||||
20 if(k.eq.0) then
|
||||
do i=1,4
|
||||
if(j.le.21) j=j+1
|
||||
msg(j:j)=grid(i:i)
|
||||
enddo
|
||||
if(j.le.21) j=j+1
|
||||
msg(j:j)=' '
|
||||
endif
|
||||
|
||||
100 continue
|
||||
if(msg(1:6).eq.'CQ9DX ') msg(3:3)=' '
|
||||
if(msg(1:2).eq.'E9' .and. &
|
||||
msg(3:3).ge.'A' .and. msg(3:3).le.'Z' .and. &
|
||||
msg(4:4).ge.'A' .and. msg(4:4).le.'Z' .and. &
|
||||
msg(5:5).eq.' ') msg='CQ '//msg(3:)
|
||||
|
||||
return
|
||||
end subroutine unpackmsg144
|
@ -1,18 +1,34 @@
|
||||
#include "WorkedBefore.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/key_extractors.hpp>
|
||||
#include <QChar>
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QDebug>
|
||||
#include "qt_helpers.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
using namespace boost::multi_index;
|
||||
|
||||
// hash function for QString members in hashed indexes
|
||||
inline
|
||||
std::size_t hash_value (QString const& s)
|
||||
{
|
||||
return std::hash<QString> {} (s);
|
||||
}
|
||||
|
||||
//
|
||||
// worked before set element
|
||||
//
|
||||
struct worked_entry
|
||||
{
|
||||
explicit worked_entry (QString const& call, QString const& grid, QString const& band
|
||||
@ -39,7 +55,52 @@ struct worked_entry
|
||||
int ITU_zone_;
|
||||
};
|
||||
|
||||
// less then predidate for the Continent enum class
|
||||
bool operator == (worked_entry const& lhs, worked_entry const& rhs)
|
||||
{
|
||||
return
|
||||
lhs.continent_ == rhs.continent_ // check 1st as it is fast
|
||||
&& lhs.CQ_zone_ == rhs.CQ_zone_ // ditto
|
||||
&& lhs.ITU_zone_ == rhs.ITU_zone_ // ditto
|
||||
&& lhs.call_ == rhs.call_ // check the rest in decreasing
|
||||
&& lhs.grid_ == rhs.grid_ // domain size order to shortcut
|
||||
&& lhs.country_ == rhs.country_ // differences as quickly as possible
|
||||
&& lhs.band_ == rhs.band_
|
||||
&& lhs.mode_ == rhs.mode_;
|
||||
}
|
||||
|
||||
std::size_t hash_value (worked_entry const& we)
|
||||
{
|
||||
std::size_t seed {0};
|
||||
boost::hash_combine (seed, we.call_);
|
||||
boost::hash_combine (seed, we.grid_);
|
||||
boost::hash_combine (seed, we.band_);
|
||||
boost::hash_combine (seed, we.mode_);
|
||||
boost::hash_combine (seed, we.country_);
|
||||
boost::hash_combine (seed, we.continent_);
|
||||
boost::hash_combine (seed, we.CQ_zone_);
|
||||
boost::hash_combine (seed, we.ITU_zone_);
|
||||
return seed;
|
||||
}
|
||||
|
||||
#if !defined (QT_NO_DEBUG_STREAM)
|
||||
QDebug operator << (QDebug dbg, worked_entry const& e)
|
||||
{
|
||||
QDebugStateSaver saver {dbg};
|
||||
dbg.nospace () << "worked_entry("
|
||||
<< e.call_ << ", "
|
||||
<< e.grid_ << ", "
|
||||
<< e.band_ << ", "
|
||||
<< e.mode_ << ", "
|
||||
<< e.country_ << ", "
|
||||
<< e.continent_ << ", "
|
||||
<< e.CQ_zone_ << ", "
|
||||
<< e.ITU_zone_ << ')';
|
||||
return dbg;
|
||||
}
|
||||
#endif
|
||||
|
||||
// less then predidate for the Continent enum class, needed for
|
||||
// ordered indexes
|
||||
struct Continent_less
|
||||
{
|
||||
bool operator () (AD1CCty::Continent lhs, AD1CCty::Continent rhs) const
|
||||
@ -48,7 +109,7 @@ struct Continent_less
|
||||
}
|
||||
};
|
||||
|
||||
// tags
|
||||
// index tags
|
||||
struct call_mode_band {};
|
||||
struct call_band {};
|
||||
struct grid_mode_band {};
|
||||
@ -62,85 +123,92 @@ struct CQ_zone_band {};
|
||||
struct ITU_zone_mode_band {};
|
||||
struct ITU_zone_band {};
|
||||
|
||||
// set with multiple ordered unique indexes that allow for efficient
|
||||
// determination of various categories of worked before status
|
||||
// set with multiple ordered unique indexes that allow for optimally
|
||||
// efficient determination of various categories of worked before
|
||||
// status
|
||||
typedef multi_index_container<
|
||||
worked_entry,
|
||||
indexed_by<
|
||||
// basic unordered set constraint - we don't need duplicate worked entries
|
||||
hashed_unique<identity<worked_entry>>,
|
||||
|
||||
//
|
||||
// The following indexes are used to discover worked before stuff.
|
||||
//
|
||||
// They are ordered so as to support partial lookups and
|
||||
// non-unique because container inserts must be valid for all
|
||||
// indexes.
|
||||
//
|
||||
|
||||
// call+mode+band
|
||||
ordered_unique<tag<call_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::call_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<call_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::call_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// call+band
|
||||
ordered_unique<tag<call_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::call_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<call_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::call_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// grid+mode+band
|
||||
ordered_unique<tag<grid_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::grid_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<grid_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::grid_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// grid+band
|
||||
ordered_unique<tag<grid_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::grid_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<grid_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::grid_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// country+mode+band
|
||||
ordered_unique<tag<entity_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::country_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<entity_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::country_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// country+band
|
||||
ordered_unique<tag<entity_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::country_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<entity_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, QString, &worked_entry::country_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// continent+mode+band
|
||||
ordered_unique<tag<continent_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> >,
|
||||
composite_key_compare<
|
||||
Continent_less,
|
||||
std::less<QString>,
|
||||
std::less<QString> > >,
|
||||
ordered_non_unique<tag<continent_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> >,
|
||||
composite_key_compare<Continent_less, std::less<QString>, std::less<QString> > >,
|
||||
// continent+band
|
||||
ordered_unique<tag<continent_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> >,
|
||||
composite_key_compare<
|
||||
Continent_less,
|
||||
std::less<QString> > >,
|
||||
ordered_non_unique<tag<continent_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> >,
|
||||
composite_key_compare<Continent_less, std::less<QString> > >,
|
||||
// CQ-zone+mode+band
|
||||
ordered_unique<tag<CQ_zone_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::CQ_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<CQ_zone_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::CQ_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// CQ-zone+band
|
||||
ordered_unique<tag<CQ_zone_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::CQ_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<CQ_zone_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::CQ_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// ITU-zone+mode+band
|
||||
ordered_unique<tag<ITU_zone_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::ITU_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
ordered_non_unique<tag<ITU_zone_mode_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::ITU_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::mode_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > >,
|
||||
// ITU-zone+band
|
||||
ordered_unique<tag<ITU_zone_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::ITU_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > > >
|
||||
> worked_type;
|
||||
ordered_non_unique<tag<ITU_zone_band>,
|
||||
composite_key<worked_entry,
|
||||
member<worked_entry, int, &worked_entry::ITU_zone_>,
|
||||
member<worked_entry, QString, &worked_entry::band_> > > >
|
||||
> worked_before_database_type;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -188,7 +256,7 @@ public:
|
||||
|
||||
QString path_;
|
||||
AD1CCty prefixes_;
|
||||
worked_type worked_;
|
||||
worked_before_database_type worked_;
|
||||
};
|
||||
|
||||
WorkedBefore::WorkedBefore ()
|
||||
@ -196,7 +264,7 @@ WorkedBefore::WorkedBefore ()
|
||||
QFile inputFile {m_->path_};
|
||||
if (inputFile.open (QFile::ReadOnly))
|
||||
{
|
||||
QTextStream in(&inputFile);
|
||||
QTextStream in {&inputFile};
|
||||
QString buffer;
|
||||
bool pre_read {false};
|
||||
int end_position {-1};
|
||||
|
36
main.cpp
36
main.cpp
@ -52,6 +52,38 @@ namespace
|
||||
qsrand (seed); // this is good for rand() as well
|
||||
}
|
||||
} seeding;
|
||||
|
||||
// We can't use the GUI after QApplication::exit() is called so
|
||||
// uncaught exceptions can get lost on Windows systems where there
|
||||
// is no console terminal, so here we override
|
||||
// QApplication::notify() and wrap the base class call with a try
|
||||
// block to catch and display exceptions in a message box.
|
||||
class ExceptionCatchingApplication final
|
||||
: public QApplication
|
||||
{
|
||||
public:
|
||||
explicit ExceptionCatchingApplication (int& argc, char * * argv)
|
||||
: QApplication {argc, argv}
|
||||
{
|
||||
}
|
||||
bool notify (QObject * receiver, QEvent * e) override
|
||||
{
|
||||
try
|
||||
{
|
||||
return QApplication::notify (receiver, e);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
MessageBox::critical_message (nullptr, translate ("main", "Fatal error"), e.what ());
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
MessageBox::critical_message (nullptr, translate ("main", "Unexpected fatal error"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -68,7 +100,7 @@ int main(int argc, char *argv[])
|
||||
// Multiple instances communicate with jt9 via this
|
||||
QSharedMemory mem_jt9;
|
||||
|
||||
QApplication a(argc, argv);
|
||||
ExceptionCatchingApplication a(argc, argv);
|
||||
try
|
||||
{
|
||||
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
|
||||
@ -339,12 +371,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
MessageBox::critical_message (nullptr, a.translate ("main", "Fatal error"), e.what ());
|
||||
std::cerr << "Error: " << e.what () << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
MessageBox::critical_message (nullptr, a.translate ("main", "Unexpected fatal error"));
|
||||
std::cerr << "Unexpected fatal error\n";
|
||||
throw; // hoping the runtime might tell us more about the exception
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ CabrilloLog::impl::impl (Configuration const * configuration)
|
||||
SQL_error_check (export_query_, &QSqlQuery::prepare,
|
||||
"SELECT frequency, \"when\", exchange_sent, call, exchange_rcvd FROM cabrillo_log ORDER BY \"when\"");
|
||||
|
||||
setEditStrategy (QSqlTableModel::OnManualSubmit);
|
||||
setEditStrategy (QSqlTableModel::OnRowChange);
|
||||
setTable ("cabrillo_log");
|
||||
setHeaderData (fieldIndex ("frequency"), Qt::Horizontal, tr ("Freq(kHz)"));
|
||||
setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)"));
|
||||
@ -73,24 +73,49 @@ CabrilloLog::~CabrilloLog ()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel * CabrilloLog::model ()
|
||||
QSqlTableModel * CabrilloLog::model ()
|
||||
{
|
||||
return &*m_;
|
||||
}
|
||||
|
||||
void CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call
|
||||
namespace
|
||||
{
|
||||
void set_value_maybe_null (QSqlRecord& record, QString const& name, QString const& value)
|
||||
{
|
||||
if (value.size ())
|
||||
{
|
||||
record.setValue (name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.setNull (name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CabrilloLog::add_QSO (Frequency frequency, QDateTime const& when, QString const& call
|
||||
, QString const& exchange_sent, QString const& exchange_received)
|
||||
{
|
||||
ConditionalTransaction transaction {*m_};
|
||||
auto record = m_->record ();
|
||||
record.setValue ("frequency", frequency / 1000ull); // kHz
|
||||
record.setValue ("when", when.toMSecsSinceEpoch () / 1000ull);
|
||||
record.setValue ("call", call);
|
||||
record.setValue ("exchange_sent", exchange_sent);
|
||||
record.setValue ("exchange_rcvd", exchange_received);
|
||||
record.setValue ("band", m_->configuration_->bands ()->find (frequency));
|
||||
SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record);
|
||||
transaction.submit ();
|
||||
if (!when.isNull ())
|
||||
{
|
||||
record.setValue ("when", when.toMSecsSinceEpoch () / 1000ull);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.setNull ("when");
|
||||
}
|
||||
set_value_maybe_null (record, "call", call);
|
||||
set_value_maybe_null (record, "exchange_sent", exchange_sent);
|
||||
set_value_maybe_null (record, "exchange_rcvd", exchange_received);
|
||||
set_value_maybe_null (record, "band", m_->configuration_->bands ()->find (frequency));
|
||||
auto ok = m_->insertRecord (-1, record);
|
||||
if (ok)
|
||||
{
|
||||
m_->select (); // to refresh views
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CabrilloLog::dupe (Frequency frequency, QString const& call) const
|
||||
@ -106,9 +131,12 @@ void CabrilloLog::reset ()
|
||||
{
|
||||
if (m_->rowCount ())
|
||||
{
|
||||
m_->setEditStrategy (QSqlTableModel::OnManualSubmit);
|
||||
ConditionalTransaction transaction {*m_};
|
||||
SQL_error_check (*m_, &QSqlTableModel::removeRows, 0, m_->rowCount (), QModelIndex {});
|
||||
transaction.submit ();
|
||||
m_->select (); // to refresh views
|
||||
m_->setEditStrategy (QSqlTableModel::OnRowChange);
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +156,7 @@ void CabrilloLog::export_qsos (QTextStream& stream) const
|
||||
frequency = frequency > 50000000ull ? frequency / 1000ull : frequency;
|
||||
stream << QString {"QSO: %1 DG %2 %3 %4 %5 %6\n"}
|
||||
.arg (frequency, 5)
|
||||
.arg (QDateTime::fromMSecsSinceEpoch (m_->export_query_.value (when_index).toULongLong () * 1000ull).toString ("yyyy-MM-dd hhmm"))
|
||||
.arg (QDateTime::fromMSecsSinceEpoch (m_->export_query_.value (when_index).toULongLong () * 1000ull, Qt::UTC).toString ("yyyy-MM-dd hhmm"))
|
||||
.arg (my_call, -12)
|
||||
.arg (m_->export_query_.value (sent_index).toString (), -13)
|
||||
.arg (m_->export_query_.value (call_index).toString (), -12)
|
||||
|
@ -8,7 +8,7 @@
|
||||
class Configuration;
|
||||
class QDateTime;
|
||||
class QString;
|
||||
class QAbstractItemModel;
|
||||
class QSqlTableModel;
|
||||
class QTextStream;
|
||||
|
||||
class CabrilloLog final
|
||||
@ -21,11 +21,11 @@ public:
|
||||
~CabrilloLog ();
|
||||
|
||||
// returns false if insert fails
|
||||
void add_QSO (Frequency, QDateTime const&, QString const& call
|
||||
bool add_QSO (Frequency, QDateTime const&, QString const& call
|
||||
, QString const& report_sent, QString const& report_received);
|
||||
bool dupe (Frequency, QString const& call) const;
|
||||
|
||||
QAbstractItemModel * model ();
|
||||
QSqlTableModel * model ();
|
||||
void reset ();
|
||||
void export_qsos (QTextStream&) const;
|
||||
|
||||
|
@ -43,7 +43,7 @@ FoxLog::impl::impl ()
|
||||
SQL_error_check (dupe_query_, &QSqlQuery::prepare,
|
||||
"SELECT COUNT(*) FROM fox_log WHERE call = :call AND band = :band");
|
||||
|
||||
setEditStrategy (QSqlTableModel::OnManualSubmit);
|
||||
setEditStrategy (QSqlTableModel::OnRowChange);
|
||||
setTable ("fox_log");
|
||||
setHeaderData (fieldIndex ("when"), Qt::Horizontal, tr ("Date & Time(UTC)"));
|
||||
setHeaderData (fieldIndex ("call"), Qt::Horizontal, tr ("Call"));
|
||||
@ -62,7 +62,7 @@ FoxLog::~FoxLog ()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractItemModel * FoxLog::model ()
|
||||
QSqlTableModel * FoxLog::model ()
|
||||
{
|
||||
return &*m_;
|
||||
}
|
||||
@ -86,21 +86,26 @@ bool FoxLog::add_QSO (QDateTime const& when, QString const& call, QString const&
|
||||
, QString const& report_sent, QString const& report_received
|
||||
, QString const& band)
|
||||
{
|
||||
ConditionalTransaction transaction {*m_};
|
||||
auto record = m_->record ();
|
||||
record.setValue ("when", when.toMSecsSinceEpoch () / 1000);
|
||||
record.setValue ("call", call);
|
||||
if (!when.isNull ())
|
||||
{
|
||||
record.setValue ("when", when.toMSecsSinceEpoch () / 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.setNull ("when");
|
||||
}
|
||||
set_value_maybe_null (record, "call", call);
|
||||
set_value_maybe_null (record, "grid", grid);
|
||||
set_value_maybe_null (record, "report_sent", report_sent);
|
||||
set_value_maybe_null (record, "report_rcvd", report_received);
|
||||
record.setValue ("band", band);
|
||||
SQL_error_check (*m_, &QSqlTableModel::insertRecord, -1, record);
|
||||
if (!transaction.submit (false))
|
||||
set_value_maybe_null (record, "band", band);
|
||||
auto ok = m_->insertRecord (-1, record);
|
||||
if (ok)
|
||||
{
|
||||
transaction.revert ();
|
||||
return false;
|
||||
m_->select (); // to refresh views
|
||||
}
|
||||
return true;
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool FoxLog::dupe (QString const& call, QString const& band) const
|
||||
@ -116,8 +121,11 @@ void FoxLog::reset ()
|
||||
{
|
||||
if (m_->rowCount ())
|
||||
{
|
||||
m_->setEditStrategy (QSqlTableModel::OnManualSubmit);
|
||||
ConditionalTransaction transaction {*m_};
|
||||
SQL_error_check (*m_, &QSqlTableModel::removeRows, 0, m_->rowCount (), QModelIndex {});
|
||||
transaction.submit ();
|
||||
m_->select (); // to refresh views
|
||||
m_->setEditStrategy (QSqlTableModel::OnRowChange);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
class QDateTime;
|
||||
class QString;
|
||||
class QAbstractItemModel;
|
||||
class QSqlTableModel;
|
||||
|
||||
class FoxLog final
|
||||
: private boost::noncopyable
|
||||
@ -21,7 +21,7 @@ public:
|
||||
, QString const& band);
|
||||
bool dupe (QString const& call, QString const& band) const;
|
||||
|
||||
QAbstractItemModel * model ();
|
||||
QSqlTableModel * model ();
|
||||
void reset ();
|
||||
|
||||
private:
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QMetaType>
|
||||
#include <QAbstractTableModel>
|
||||
@ -350,14 +351,6 @@ bool FrequencyList_v2::remove (Item f)
|
||||
return m_->removeRow (row);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return lhs.row () > rhs.row ();
|
||||
}
|
||||
}
|
||||
|
||||
bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows)
|
||||
{
|
||||
bool result {true};
|
||||
@ -371,7 +364,10 @@ bool FrequencyList_v2::removeDisjointRows (QModelIndexList rows)
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
qSort (rows.begin (), rows.end (), row_is_higher);
|
||||
std::sort (rows.begin (), rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return rhs.row () < lhs.row (); // reverse row ordering
|
||||
});
|
||||
Q_FOREACH (auto index, rows)
|
||||
{
|
||||
if (result && !m_->removeRow (index.row ()))
|
||||
|
@ -139,14 +139,6 @@ bool StationList::remove (Station s)
|
||||
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};
|
||||
@ -160,7 +152,10 @@ bool StationList::removeDisjointRows (QModelIndexList rows)
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
qSort (rows.begin (), rows.end (), row_is_higher);
|
||||
std::sort (rows.begin (), rows.end (), [] (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return rhs.row () < lhs.row (); // reverse row ordering
|
||||
});
|
||||
Q_FOREACH (auto index, rows)
|
||||
{
|
||||
if (result && !m_->removeRow (index.row ()))
|
||||
|
@ -25,37 +25,56 @@ class ConditionalTransaction final
|
||||
{
|
||||
public:
|
||||
explicit ConditionalTransaction (QSqlTableModel& model)
|
||||
: model_ {model}
|
||||
: model_ (model)
|
||||
, submitted_ {false}
|
||||
{
|
||||
model_.database ().transaction ();
|
||||
}
|
||||
|
||||
bool submit (bool throw_on_error = true)
|
||||
{
|
||||
Q_ASSERT (model_.isDirty ());
|
||||
bool ok {true};
|
||||
if (throw_on_error)
|
||||
{
|
||||
SQL_error_check (model_, &QSqlTableModel::submitAll);
|
||||
SQL_error_check (model_
|
||||
, QSqlTableModel::OnManualSubmit == model_.editStrategy ()
|
||||
? &QSqlTableModel::submitAll
|
||||
: &QSqlTableModel::submit);
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = model_.submitAll ();
|
||||
ok = QSqlTableModel::OnManualSubmit == model_.editStrategy ()
|
||||
? model_.submitAll () : model_.submit ();
|
||||
}
|
||||
submitted_ = submitted_ || ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void revert ()
|
||||
{
|
||||
Q_ASSERT (model_.isDirty ());
|
||||
model_.revertAll ();
|
||||
if (QSqlTableModel::OnManualSubmit == model_.editStrategy ())
|
||||
{
|
||||
model_.revertAll ();
|
||||
}
|
||||
else
|
||||
{
|
||||
model_.revert ();
|
||||
}
|
||||
}
|
||||
|
||||
~ConditionalTransaction ()
|
||||
{
|
||||
if (model_.isDirty ())
|
||||
{
|
||||
// abandon un-submitted changes to the model
|
||||
model_.revertAll ();
|
||||
if (QSqlTableModel::OnManualSubmit == model_.editStrategy ())
|
||||
{
|
||||
model_.revertAll ();
|
||||
}
|
||||
else
|
||||
{
|
||||
model_.revert ();
|
||||
}
|
||||
}
|
||||
auto database = model_.database ();
|
||||
if (submitted_)
|
||||
@ -67,6 +86,7 @@ public:
|
||||
database.rollback ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QSqlTableModel& model_;
|
||||
bool submitted_;
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QString>
|
||||
#include <QChar>
|
||||
#include <QMetaObject>
|
||||
#include <QHostAddress>
|
||||
#include <QDataStream>
|
||||
@ -77,6 +78,26 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
// std::hash<> specialization for QString based on the dbj2
|
||||
// algorithm http://www.cse.yorku.ca/~oz/hash.html because qHash()
|
||||
// is poor on 64-bit platforms due to being a 32-bit hash value
|
||||
template<>
|
||||
struct hash<QString>
|
||||
{
|
||||
std::size_t operator () (QString const& s) const noexcept
|
||||
{
|
||||
std::size_t hash {5381};
|
||||
for (int i = 0; i < s.size (); ++i)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + ((s.at (i).row () << 8) | s.at (i).cell ());
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Register some useful Qt types with QMetaType
|
||||
Q_DECLARE_METATYPE (QHostAddress);
|
||||
|
||||
|
133
widgets/AbstractLogWindow.cpp
Normal file
133
widgets/AbstractLogWindow.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "AbstractLogWindow.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <QSettings>
|
||||
#include <QString>
|
||||
#include <QTableView>
|
||||
#include <QHeaderView>
|
||||
#include <QAction>
|
||||
#include <QSqlTableModel>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QItemSelection>
|
||||
#include "Configuration.hpp"
|
||||
#include "SettingsGroup.hpp"
|
||||
#include "MessageBox.hpp"
|
||||
#include "models/FontOverrideModel.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
class AbstractLogWindow::impl final
|
||||
{
|
||||
public:
|
||||
impl (AbstractLogWindow * self, QString const& settings_key, QSettings * settings
|
||||
, Configuration const * configuration)
|
||||
: self_ {self}
|
||||
, settings_key_ {settings_key}
|
||||
, settings_ {settings}
|
||||
, configuration_ {configuration}
|
||||
, log_view_ {nullptr}
|
||||
{
|
||||
}
|
||||
|
||||
void delete_QSOs ();
|
||||
|
||||
AbstractLogWindow * self_;
|
||||
QString settings_key_;
|
||||
QSettings * settings_;
|
||||
Configuration const * configuration_;
|
||||
QTableView * log_view_;
|
||||
FontOverrideModel model_;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
bool row_is_higher (QModelIndex const& lhs, QModelIndex const& rhs)
|
||||
{
|
||||
return lhs.row () > rhs.row ();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractLogWindow::impl::delete_QSOs ()
|
||||
{
|
||||
auto selection_model = log_view_->selectionModel ();
|
||||
selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
auto row_indexes = selection_model->selectedRows ();
|
||||
|
||||
if (row_indexes.size ()
|
||||
&& MessageBox::Yes == MessageBox::query_message (self_
|
||||
, tr ("Confirm Delete")
|
||||
, tr ("Are you sure you want to delete the %n "
|
||||
"selected QSO(s) from the log", ""
|
||||
, row_indexes.size ())))
|
||||
{
|
||||
// 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 (auto& row_index : row_indexes)
|
||||
{
|
||||
row_index = model_.mapToSource (row_index);
|
||||
}
|
||||
|
||||
// reverse sort by row
|
||||
std::sort (row_indexes.begin (), row_indexes.end (), row_is_higher);
|
||||
for (auto index : row_indexes)
|
||||
{
|
||||
auto row = model_.mapFromSource (index).row ();
|
||||
model_.removeRow (row);
|
||||
self_->log_model_changed ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AbstractLogWindow::AbstractLogWindow (QString const& settings_key, QSettings * settings
|
||||
, Configuration const * configuration
|
||||
, QWidget * parent)
|
||||
: QWidget {parent}
|
||||
, m_ {this, settings_key, settings, configuration}
|
||||
{
|
||||
// ensure view scrolls to latest new row
|
||||
connect (&m_->model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) {
|
||||
if (m_->log_view_) m_->log_view_->scrollToBottom ();
|
||||
});
|
||||
}
|
||||
|
||||
AbstractLogWindow::~AbstractLogWindow ()
|
||||
{
|
||||
SettingsGroup g {m_->settings_, m_->settings_key_};
|
||||
m_->settings_->setValue ("window/geometry", saveGeometry ());
|
||||
}
|
||||
|
||||
void AbstractLogWindow::set_log_view (QTableView * log_view)
|
||||
{
|
||||
// do this here because we know the UI must be setup before this
|
||||
SettingsGroup g {m_->settings_, m_->settings_key_};
|
||||
restoreGeometry (m_->settings_->value ("window/geometry").toByteArray ());
|
||||
m_->log_view_ = log_view;
|
||||
m_->log_view_->setContextMenuPolicy (Qt::ActionsContextMenu);
|
||||
m_->log_view_->setAlternatingRowColors (true);
|
||||
m_->log_view_->setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||
m_->log_view_->setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||
m_->model_.setSourceModel (m_->log_view_->model ());
|
||||
m_->log_view_->setModel (&m_->model_);
|
||||
m_->log_view_->setColumnHidden (0, true);
|
||||
auto horizontal_header = log_view->horizontalHeader ();
|
||||
horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
horizontal_header->setSectionsMovable (true);
|
||||
m_->log_view_->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
set_log_view_font (m_->configuration_->decoded_text_font ());
|
||||
m_->log_view_->scrollToBottom ();
|
||||
|
||||
// actions
|
||||
auto delete_action = new QAction {tr ("&Delete ..."), m_->log_view_};
|
||||
m_->log_view_->insertAction (nullptr, delete_action);
|
||||
connect (delete_action, &QAction::triggered, [this] (bool /*checked*/) {
|
||||
m_->delete_QSOs ();
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractLogWindow::set_log_view_font (QFont const& font)
|
||||
{
|
||||
// m_->log_view_->setFont (font);
|
||||
// m_->log_view_->horizontalHeader ()->setFont (font);
|
||||
// m_->log_view_->verticalHeader ()->setFont (font);
|
||||
m_->model_.set_font (font);
|
||||
}
|
41
widgets/AbstractLogWindow.hpp
Normal file
41
widgets/AbstractLogWindow.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef ABSTRACT_LOG_WINDOW_HPP_
|
||||
#define ABSTRACT_LOG_WINDOW_HPP_
|
||||
|
||||
#include <QWidget>
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
class QSettings;
|
||||
class Configuration;
|
||||
class QTableView;
|
||||
class QFont;
|
||||
|
||||
//
|
||||
// AbstractLogWindow - Base class for log view windows
|
||||
//
|
||||
// QWidget that manages the common functionality shared by windows
|
||||
// that include a QSO log view.
|
||||
//
|
||||
class AbstractLogWindow
|
||||
: public QWidget
|
||||
{
|
||||
public:
|
||||
AbstractLogWindow (QString const& settings_key, QSettings * settings
|
||||
, Configuration const * configuration
|
||||
, QWidget * parent = nullptr);
|
||||
virtual ~AbstractLogWindow () = 0;
|
||||
|
||||
// set the QTableView that shows the log records, must have its
|
||||
// model set before calling this
|
||||
void set_log_view (QTableView *);
|
||||
|
||||
void set_log_view_font (QFont const&);
|
||||
|
||||
private:
|
||||
virtual void log_model_changed (int row = -1) = 0;
|
||||
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,77 +1,87 @@
|
||||
#include "CabrilloLogWindow.hpp"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QPainter>
|
||||
|
||||
#include "SettingsGroup.hpp"
|
||||
#include <QIdentityProxyModel>
|
||||
#include <QSqlTableModel>
|
||||
#include "Configuration.hpp"
|
||||
#include "models/Bands.hpp"
|
||||
#include "item_delegates/ForeignKeyDelegate.hpp"
|
||||
#include "item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp"
|
||||
#include "item_delegates/CallsignDelegate.hpp"
|
||||
#include "item_delegates/MaidenheadLocatorDelegate.hpp"
|
||||
#include "widgets/MessageBox.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "ui_CabrilloLogWindow.h"
|
||||
|
||||
CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const * configuration
|
||||
, QAbstractItemModel * cabrillo_log_model, QWidget * parent)
|
||||
: QWidget {parent}
|
||||
, settings_ {settings}
|
||||
, configuration_ {configuration}
|
||||
, ui_ {new Ui::CabrilloLogWindow}
|
||||
namespace
|
||||
{
|
||||
cabrillo_log_model_.setSourceModel (cabrillo_log_model);
|
||||
setWindowTitle (QApplication::applicationName () + " - Cabrillo Log");
|
||||
ui_->setupUi (this);
|
||||
read_settings ();
|
||||
change_font (configuration_->decoded_text_font ());
|
||||
ui_->log_table_view->setModel (&cabrillo_log_model_);
|
||||
ui_->log_table_view->setColumnHidden (0, true);
|
||||
ui_->log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this});
|
||||
ui_->log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this});
|
||||
ui_->log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration_->bands (), &cabrillo_log_model_, 0, 6, this});
|
||||
ui_->log_table_view->setSelectionMode (QTableView::SingleSelection);
|
||||
auto horizontal_header = ui_->log_table_view->horizontalHeader ();
|
||||
horizontal_header->setStretchLastSection (true);
|
||||
horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
horizontal_header->setSectionsMovable (true);
|
||||
horizontal_header->moveSection (6, 1); // band to first column
|
||||
ui_->log_table_view->scrollToBottom ();
|
||||
class FormatProxyModel final
|
||||
: public QIdentityProxyModel
|
||||
{
|
||||
public:
|
||||
explicit FormatProxyModel (QObject * parent = nullptr)
|
||||
: QIdentityProxyModel {parent}
|
||||
{
|
||||
}
|
||||
|
||||
// ensure view scrolls to latest new row
|
||||
connect (&cabrillo_log_model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) {
|
||||
ui_->log_table_view->scrollToBottom ();
|
||||
});
|
||||
QVariant data (QModelIndex const& index, int role) const override
|
||||
{
|
||||
if (Qt::TextAlignmentRole == role && index.isValid ())
|
||||
{
|
||||
switch (index.column ())
|
||||
{
|
||||
case 1:
|
||||
case 6:
|
||||
return Qt::AlignRight + Qt::AlignVCenter;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QIdentityProxyModel::data (index, role);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class CabrilloLogWindow::impl final
|
||||
{
|
||||
public:
|
||||
explicit impl (QSqlTableModel * log_model)
|
||||
: log_model_ {log_model}
|
||||
{
|
||||
}
|
||||
|
||||
QSqlTableModel * log_model_;
|
||||
FormatProxyModel format_model_;
|
||||
Ui::CabrilloLogWindow ui_;
|
||||
};
|
||||
|
||||
CabrilloLogWindow::CabrilloLogWindow (QSettings * settings, Configuration const * configuration
|
||||
, QSqlTableModel * cabrillo_log_model, QWidget * parent)
|
||||
: AbstractLogWindow {"Cabrillo Log Window", settings, configuration, parent}
|
||||
, m_{cabrillo_log_model}
|
||||
{
|
||||
setWindowTitle (QApplication::applicationName () + " - Cabrillo Log");
|
||||
m_->ui_.setupUi (this);
|
||||
m_->format_model_.setSourceModel (m_->log_model_);
|
||||
m_->ui_.log_table_view->setModel (&m_->format_model_);
|
||||
set_log_view (m_->ui_.log_table_view);
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (2, new DateTimeAsSecsSinceEpochDelegate {this});
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (3, new CallsignDelegate {this});
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), m_->log_model_, 0, 6, this});
|
||||
m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // band to first column
|
||||
}
|
||||
|
||||
CabrilloLogWindow::~CabrilloLogWindow ()
|
||||
{
|
||||
write_settings ();
|
||||
}
|
||||
|
||||
void CabrilloLogWindow::read_settings ()
|
||||
void CabrilloLogWindow::log_model_changed (int row)
|
||||
{
|
||||
SettingsGroup g {settings_, "Cabrillo Log Window"};
|
||||
restoreGeometry (settings_->value ("window/geometery").toByteArray ());
|
||||
}
|
||||
|
||||
void CabrilloLogWindow::write_settings () const
|
||||
{
|
||||
SettingsGroup g {settings_, "Cabrillo Log Window"};
|
||||
settings_->setValue ("window/geometery", saveGeometry ());
|
||||
}
|
||||
|
||||
void CabrilloLogWindow::change_font (QFont const& font)
|
||||
{
|
||||
// ui_->log_table_view->setFont (font);
|
||||
// ui_->log_table_view->horizontalHeader ()->setFont (font);
|
||||
// ui_->log_table_view->verticalHeader ()->setFont (font);
|
||||
cabrillo_log_model_.set_font (font);
|
||||
if (row >= 0)
|
||||
{
|
||||
m_->log_model_->selectRow (row);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_->log_model_->select ();
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,27 @@
|
||||
#ifndef CABRILLO_LOG_WINDOW_HPP_
|
||||
#define CABRILLO_LOG_WINDOW_HPP_
|
||||
|
||||
#include <QWidget>
|
||||
#include <QScopedPointer>
|
||||
#include <QIdentityProxyModel>
|
||||
#include "models/FontOverrideModel.hpp"
|
||||
#include "AbstractLogWindow.hpp"
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QSettings;
|
||||
class Configuration;
|
||||
class QFont;
|
||||
class QDateTime;
|
||||
class QAbstractItemModel;
|
||||
namespace Ui
|
||||
{
|
||||
class CabrilloLogWindow;
|
||||
}
|
||||
class QSqlTableModel;
|
||||
|
||||
class CabrilloLogWindow final
|
||||
: public QWidget
|
||||
: public AbstractLogWindow
|
||||
{
|
||||
public:
|
||||
explicit CabrilloLogWindow (QSettings *, Configuration const *, QAbstractItemModel * cabrillo_log_model
|
||||
, QWidget * parent = nullptr);
|
||||
explicit CabrilloLogWindow (QSettings *, Configuration const *, QSqlTableModel * cabrillo_log_model
|
||||
, QWidget * parent = nullptr);
|
||||
~CabrilloLogWindow ();
|
||||
|
||||
void change_font (QFont const&);
|
||||
|
||||
private:
|
||||
void read_settings ();
|
||||
void write_settings () const;
|
||||
void log_model_changed (int row) override;
|
||||
|
||||
QSettings * settings_;
|
||||
Configuration const * configuration_;
|
||||
FontOverrideModel cabrillo_log_model_;
|
||||
QScopedPointer<Ui::CabrilloLogWindow> ui_;
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -2,12 +2,27 @@
|
||||
<ui version="4.0">
|
||||
<class>CabrilloLogWindow</class>
|
||||
<widget class="QWidget" name="CabrilloLogWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>493</width>
|
||||
<height>210</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Contest Log</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="log_table_view"/>
|
||||
<widget class="QTableView" name="log_table_view">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Right-click here for available actions.</p></body></html></string>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -76,7 +76,7 @@ void ExportCabrillo::save_log ()
|
||||
auto fname = QFileDialog::getSaveFileName (this
|
||||
, tr ("Save Log File")
|
||||
, configuration_->writeable_data_dir ().absolutePath ()
|
||||
, tr ("Cabrillo Log (*.log)"));
|
||||
, tr ("Cabrillo Log (*.cbr)"));
|
||||
if (fname.size ())
|
||||
{
|
||||
QFile f {fname};
|
||||
|
@ -1,96 +1,97 @@
|
||||
#include "FoxLogWindow.hpp"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QDateTime>
|
||||
#include <QSqlTableModel>
|
||||
#include <QAction>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QPainter>
|
||||
|
||||
#include "SettingsGroup.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "MessageBox.hpp"
|
||||
#include "models/Bands.hpp"
|
||||
#include "item_delegates/ForeignKeyDelegate.hpp"
|
||||
#include "item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp"
|
||||
#include "item_delegates/CallsignDelegate.hpp"
|
||||
#include "item_delegates/MaidenheadLocatorDelegate.hpp"
|
||||
#include "widgets/MessageBox.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "ui_FoxLogWindow.h"
|
||||
#include "moc_FoxLogWindow.cpp"
|
||||
|
||||
class FoxLogWindow::impl final
|
||||
{
|
||||
public:
|
||||
explicit impl (QSqlTableModel * log_model)
|
||||
: log_model_ {log_model}
|
||||
{
|
||||
}
|
||||
|
||||
QSqlTableModel * log_model_;
|
||||
Ui::FoxLogWindow ui_;
|
||||
};
|
||||
|
||||
FoxLogWindow::FoxLogWindow (QSettings * settings, Configuration const * configuration
|
||||
, QAbstractItemModel * fox_log_model, QWidget * parent)
|
||||
: QWidget {parent}
|
||||
, settings_ {settings}
|
||||
, configuration_ {configuration}
|
||||
, ui_ {new Ui::FoxLogWindow}
|
||||
, QSqlTableModel * fox_log_model, QWidget * parent)
|
||||
: AbstractLogWindow {"Fox Log Window", settings, configuration, parent}
|
||||
, m_ {fox_log_model}
|
||||
{
|
||||
fox_log_model_.setSourceModel (fox_log_model);
|
||||
setWindowTitle (QApplication::applicationName () + " - Fox Log");
|
||||
ui_->setupUi (this);
|
||||
read_settings ();
|
||||
change_font (configuration_->decoded_text_font ());
|
||||
ui_->log_table_view->setModel (&fox_log_model_);
|
||||
ui_->log_table_view->setColumnHidden (0, true);
|
||||
ui_->log_table_view->setItemDelegateForColumn (1, new DateTimeAsSecsSinceEpochDelegate {this});
|
||||
ui_->log_table_view->setItemDelegateForColumn (2, new CallsignDelegate {this});
|
||||
ui_->log_table_view->setItemDelegateForColumn (3, new MaidenheadLocatorDelegate {this});
|
||||
ui_->log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration_->bands (), &fox_log_model_, 0, 6, this});
|
||||
ui_->log_table_view->setSelectionMode (QTableView::SingleSelection);
|
||||
auto horizontal_header = ui_->log_table_view->horizontalHeader ();
|
||||
horizontal_header->setStretchLastSection (true);
|
||||
horizontal_header->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
horizontal_header->setSectionsMovable (true);
|
||||
horizontal_header->moveSection (6, 1); // move band to first column
|
||||
ui_->rate_label->setNum (0);
|
||||
ui_->queued_label->setNum (0);
|
||||
ui_->callers_label->setNum (0);
|
||||
ui_->log_table_view->scrollToBottom ();
|
||||
m_->ui_.setupUi (this);
|
||||
m_->ui_.log_table_view->setModel (m_->log_model_);
|
||||
set_log_view (m_->ui_.log_table_view);
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (1, new DateTimeAsSecsSinceEpochDelegate {this});
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (2, new CallsignDelegate {this});
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (3, new MaidenheadLocatorDelegate {this});
|
||||
m_->ui_.log_table_view->setItemDelegateForColumn (6, new ForeignKeyDelegate {configuration->bands (), m_->log_model_, 0, 6, this});
|
||||
m_->ui_.log_table_view->horizontalHeader ()->moveSection (6, 1); // move band to first column
|
||||
m_->ui_.rate_label->setNum (0);
|
||||
m_->ui_.queued_label->setNum (0);
|
||||
m_->ui_.callers_label->setNum (0);
|
||||
|
||||
// ensure view scrolls to latest new row
|
||||
connect (&fox_log_model_, &QAbstractItemModel::rowsInserted, [this] (QModelIndex const& /*parent*/, int /*first*/, int /*last*/) {
|
||||
ui_->log_table_view->scrollToBottom ();
|
||||
// actions
|
||||
auto reset_action = new QAction {tr ("&Reset ..."), m_->ui_.log_table_view};
|
||||
m_->ui_.log_table_view->insertAction (nullptr, reset_action);
|
||||
connect (reset_action, &QAction::triggered, [this, configuration] (bool /*checked*/) {
|
||||
if (MessageBox::Yes == MessageBox::query_message( this
|
||||
, tr ("Confirm Reset")
|
||||
, tr ("Are you sure you want to erase file FoxQSO.txt "
|
||||
"and start a new Fox log?")))
|
||||
{
|
||||
QFile f{configuration->writeable_data_dir ().absoluteFilePath ("FoxQSO.txt")};
|
||||
f.remove ();
|
||||
Q_EMIT reset_log_model ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
FoxLogWindow::~FoxLogWindow ()
|
||||
{
|
||||
write_settings ();
|
||||
}
|
||||
|
||||
void FoxLogWindow::read_settings ()
|
||||
{
|
||||
SettingsGroup g {settings_, "Fox Log Window"};
|
||||
restoreGeometry (settings_->value ("window/geometery").toByteArray ());
|
||||
}
|
||||
|
||||
void FoxLogWindow::write_settings () const
|
||||
{
|
||||
SettingsGroup g {settings_, "Fox Log Window"};
|
||||
settings_->setValue ("window/geometery", saveGeometry ());
|
||||
}
|
||||
|
||||
void FoxLogWindow::change_font (QFont const& font)
|
||||
{
|
||||
// ui_->log_table_view->setFont (font);
|
||||
// ui_->log_table_view->horizontalHeader ()->setFont (font);
|
||||
// ui_->log_table_view->verticalHeader ()->setFont (font);
|
||||
fox_log_model_.set_font (font);
|
||||
}
|
||||
|
||||
void FoxLogWindow::callers (int n)
|
||||
{
|
||||
ui_->callers_label->setNum (n);
|
||||
m_->ui_.callers_label->setNum (n);
|
||||
}
|
||||
|
||||
void FoxLogWindow::queued (int n)
|
||||
{
|
||||
ui_->queued_label->setNum (n);
|
||||
m_->ui_.queued_label->setNum (n);
|
||||
}
|
||||
|
||||
void FoxLogWindow::rate (int n)
|
||||
{
|
||||
ui_->rate_label->setNum (n);
|
||||
m_->ui_.rate_label->setNum (n);
|
||||
}
|
||||
|
||||
void FoxLogWindow::log_model_changed (int row)
|
||||
{
|
||||
if (row >= 0)
|
||||
{
|
||||
m_->log_model_->selectRow (row);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_->log_model_->select ();
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +1,35 @@
|
||||
#ifndef FOX_LOG_WINDOW_HPP_
|
||||
#define FOX_LOG_WINDOW_HPP_
|
||||
|
||||
#include <QWidget>
|
||||
#include <QScopedPointer>
|
||||
#include <QIdentityProxyModel>
|
||||
#include "models/FontOverrideModel.hpp"
|
||||
#include "AbstractLogWindow.hpp"
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QSettings;
|
||||
class Configuration;
|
||||
class QFont;
|
||||
class QDateTime;
|
||||
class QAbstractItemModel;
|
||||
namespace Ui
|
||||
{
|
||||
class FoxLogWindow;
|
||||
}
|
||||
class QSqlTableModel;
|
||||
|
||||
class FoxLogWindow final
|
||||
: public QWidget
|
||||
: public AbstractLogWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FoxLogWindow (QSettings *, Configuration const *, QAbstractItemModel * fox_log_model
|
||||
explicit FoxLogWindow (QSettings *, Configuration const *, QSqlTableModel * fox_log_model
|
||||
, QWidget * parent = nullptr);
|
||||
~FoxLogWindow ();
|
||||
|
||||
void change_font (QFont const&);
|
||||
void callers (int);
|
||||
void queued (int);
|
||||
void rate (int);
|
||||
|
||||
private:
|
||||
void read_settings ();
|
||||
void write_settings () const;
|
||||
Q_SIGNAL void reset_log_model () const;
|
||||
|
||||
QSettings * settings_;
|
||||
Configuration const * configuration_;
|
||||
FontOverrideModel fox_log_model_;
|
||||
QScopedPointer<Ui::FoxLogWindow> ui_;
|
||||
private:
|
||||
void log_model_changed (int row) override;
|
||||
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -2,12 +2,33 @@
|
||||
<ui version="4.0">
|
||||
<class>FoxLogWindow</class>
|
||||
<widget class="QWidget" name="FoxLogWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>453</width>
|
||||
<height>238</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::DefaultContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Fox Log</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="log_table_view"/>
|
||||
<widget class="QTableView" name="log_table_view">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Right-click here for available actions.</p></body></html></string>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
|
@ -213,7 +213,7 @@ auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const
|
||||
// we do the next period if we calculate just before it starts
|
||||
auto sec_since_epoch = t.toMSecsSinceEpoch () / 1000 + 2;
|
||||
auto target_sec = sec_since_epoch - sec_since_epoch % TR_period + TR_period / 2;
|
||||
auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000, QTimeZone::utc ());
|
||||
auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000, Qt::UTC);
|
||||
QString date {target_date_time.date().toString("yyyy MMM dd").trimmed ()};
|
||||
QString utc {target_date_time.time().toString().trimmed ()};
|
||||
int nyear {target_date_time.date().year()};
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "MessageBox.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "models/Bands.hpp"
|
||||
#include "models/CabrilloLog.hpp"
|
||||
#include "validators/MaidenheadLocatorValidator.hpp"
|
||||
|
||||
#include "ui_logqso.h"
|
||||
@ -57,11 +58,10 @@ void LogQSO::storeSettings () const
|
||||
void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode,
|
||||
QString const& rptSent, QString const& rptRcvd,
|
||||
QDateTime const& dateTimeOn, QDateTime const& dateTimeOff,
|
||||
Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd)
|
||||
Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd,
|
||||
CabrilloLog * cabrillo_log)
|
||||
{
|
||||
if(!isHidden()) return;
|
||||
m_xSent=xSent;
|
||||
m_xRcvd=xRcvd;
|
||||
ui->call->setText(hisCall);
|
||||
ui->grid->setText(hisGrid);
|
||||
ui->name->setText("");
|
||||
@ -87,17 +87,23 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString
|
||||
m_myGrid=m_config->my_grid();
|
||||
ui->band->setText (m_config->bands ()->find (dialFreq));
|
||||
ui->loggedOperator->setText(m_config->opCall());
|
||||
ui->exchSent->setText(m_xSent);
|
||||
ui->exchRcvd->setText(m_xRcvd);
|
||||
ui->exchSent->setText (xSent);
|
||||
ui->exchRcvd->setText (xRcvd);
|
||||
m_cabrilloLog = cabrillo_log;
|
||||
|
||||
using SpOp = Configuration::SpecialOperatingActivity;
|
||||
if( SpOp::FOX == m_config->special_op_id() or
|
||||
(m_config->autoLog() and SpOp::NONE < m_config->special_op_id() and
|
||||
m_xSent!="" and m_xRcvd!="")) {
|
||||
accept();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
auto special_op = m_config->special_op_id ();
|
||||
if (SpOp::FOX == special_op
|
||||
|| (m_config->autoLog ()
|
||||
&& SpOp::NONE < special_op && special_op < SpOp::FOX))
|
||||
{
|
||||
// allow auto logging in Fox mode and contests
|
||||
accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void LogQSO::accept()
|
||||
@ -120,6 +126,29 @@ void LogQSO::accept()
|
||||
QString strDialFreq(QString::number(m_dialFreq / 1.e6,'f',6));
|
||||
operator_call = ui->loggedOperator->text();
|
||||
|
||||
// validate
|
||||
using SpOp = Configuration::SpecialOperatingActivity;
|
||||
auto special_op = m_config->special_op_id ();
|
||||
if (SpOp::NONE < special_op && special_op < SpOp::FOX)
|
||||
{
|
||||
if (ui->exchSent->text ().isEmpty () || ui->exchRcvd->text ().isEmpty ())
|
||||
{
|
||||
show ();
|
||||
MessageBox::warning_message (this, tr ("Invalid QSO Data"),
|
||||
tr ("Check exchange sent and received"));
|
||||
return; // without accepting
|
||||
}
|
||||
|
||||
if (!m_cabrilloLog->add_QSO (m_dialFreq, QDateTime::currentDateTimeUtc (), hisCall,
|
||||
ui->exchSent->text (), ui->exchRcvd->text ()))
|
||||
{
|
||||
show ();
|
||||
MessageBox::warning_message (this, tr ("Invalid QSO Data"),
|
||||
tr ("Check all fields"));
|
||||
return; // without accepting
|
||||
}
|
||||
}
|
||||
|
||||
//Log this QSO to file "wsjtx.log"
|
||||
static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")};
|
||||
if(!f.open(QIODevice::Text | QIODevice::Append)) {
|
||||
@ -169,8 +198,8 @@ void LogQSO::accept()
|
||||
, m_myGrid
|
||||
, m_txPower
|
||||
, operator_call
|
||||
, m_xSent
|
||||
, m_xRcvd));
|
||||
, ui->exchSent->text ()
|
||||
, ui->exchRcvd->text ()));
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace Ui {
|
||||
class QSettings;
|
||||
class Configuration;
|
||||
class QByteArray;
|
||||
class CabrilloLog;
|
||||
|
||||
class LogQSO : public QDialog
|
||||
{
|
||||
@ -28,7 +29,7 @@ public:
|
||||
void initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode,
|
||||
QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn,
|
||||
QDateTime const& dateTimeOff, Radio::Frequency dialFreq,
|
||||
bool noSuffix, QString xSent, QString xRcvd);
|
||||
bool noSuffix, QString xSent, QString xRcvd, CabrilloLog *);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
@ -56,10 +57,9 @@ private:
|
||||
Radio::Frequency m_dialFreq;
|
||||
QString m_myCall;
|
||||
QString m_myGrid;
|
||||
QString m_xSent;
|
||||
QString m_xRcvd;
|
||||
QDateTime m_dateTimeOn;
|
||||
QDateTime m_dateTimeOff;
|
||||
CabrilloLog * m_cabrilloLog;
|
||||
};
|
||||
|
||||
#endif // LogQSO_H
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <fftw3.h>
|
||||
#include <QLineEdit>
|
||||
#include <QRegExpValidator>
|
||||
@ -735,6 +736,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
ui->labDXped->setStyleSheet("QLabel {background-color: red; color: white;}");
|
||||
ui->labNextCall->setText("");
|
||||
ui->labNextCall->setVisible(false);
|
||||
ui->labNextCall->setToolTip(""); //### Possibly temporary ? ###
|
||||
|
||||
for(int i=0; i<28; i++) { //Initialize dBm values
|
||||
float dbm=(10.0*i)/3.0 - 30.0;
|
||||
@ -931,13 +933,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
ui->cbMenus->setChecked(true);
|
||||
ui->cbMenus->setChecked(false);
|
||||
}
|
||||
|
||||
mouseLastPos=QCursor::pos();
|
||||
m_mouseIdleSeconds=0;
|
||||
connect(&mouseTimer, &QTimer::timeout, this, &MainWindow::mouseTimerTick);
|
||||
mouseTimer.start(1000);
|
||||
|
||||
// this must be the last statement of constructor
|
||||
// this must be the last statement of constructor
|
||||
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
|
||||
}
|
||||
|
||||
@ -948,10 +944,16 @@ void MainWindow::not_GA_warning_message ()
|
||||
|
||||
MessageBox::critical_message (this,
|
||||
"This version of WSJT-X is a beta-level Release Candidate.\n\n"
|
||||
"In FT8 and MSK144 modes it uses ONLY the new 77-bit\n"
|
||||
"message formats. It will not decode 75-bit or 72-bit\n"
|
||||
"messages.\n\n"
|
||||
"On December 10, 2018, 77-bit messages will become the\n"
|
||||
"standard. Everyone should upgrade to WSJT-X 2.0 by\n"
|
||||
"January 1, 2019.\n\n"
|
||||
"On-the-air use carries an obligation to report problems\n"
|
||||
"to the WSJT Development group and to upgrade to a GA\n"
|
||||
"(General Availability) release when it becomes available.\n\n"
|
||||
"This version cannot be used after December 31, 2018\n\n");
|
||||
"This version cannot be used after December 31, 2018.\n\n");
|
||||
|
||||
if(now.daysTo(timeout) < 0) Q_EMIT finished();
|
||||
}
|
||||
@ -962,22 +964,6 @@ void MainWindow::initialize_fonts ()
|
||||
setDecodedTextFont (m_config.decoded_text_font ());
|
||||
}
|
||||
|
||||
void MainWindow::mouseTimerTick()
|
||||
{
|
||||
QPoint point = QCursor::pos();
|
||||
if(point != mouseLastPos)
|
||||
m_mouseIdleSeconds = 0;
|
||||
else
|
||||
m_mouseIdleSeconds++;
|
||||
mouseLastPos = point;
|
||||
//Here we should do what's necessary when mouseIdleSeconds gets too big.
|
||||
// qDebug() << m_mouseIdleSeconds;
|
||||
if(ui->cbAutoSeq->isChecked() and m_mouseIdleSeconds>300) {
|
||||
auto_tx_mode (false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::splash_done ()
|
||||
{
|
||||
m_splash && m_splash->close ();
|
||||
@ -1077,6 +1063,7 @@ void MainWindow::writeSettings()
|
||||
m_settings->setValue("RR73",m_send_RR73);
|
||||
m_settings->setValue ("WSPRPreferType1", ui->WSPR_prefer_type_1_check_box->isChecked ());
|
||||
m_settings->setValue("UploadSpots",m_uploadSpots);
|
||||
m_settings->setValue("NoOwnCall",ui->cbNoOwnCall->isChecked());
|
||||
m_settings->setValue ("BandHopping", ui->band_hopping_group_box->isChecked ());
|
||||
m_settings->setValue ("TRPeriod", ui->sbTR->value ());
|
||||
m_settings->setValue("FastMode",m_bFastMode);
|
||||
@ -1168,6 +1155,7 @@ void MainWindow::readSettings()
|
||||
ui->WSPR_prefer_type_1_check_box->setChecked (m_settings->value ("WSPRPreferType1", true).toBool ());
|
||||
m_uploadSpots=m_settings->value("UploadSpots",false).toBool();
|
||||
if(!m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}");
|
||||
ui->cbNoOwnCall->setChecked(m_settings->value("NoOwnCall",false).toBool());
|
||||
ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool());
|
||||
// setup initial value of tx attenuator
|
||||
m_block_pwr_tooltip = true;
|
||||
@ -1248,10 +1236,10 @@ void MainWindow::setDecodedTextFont (QFont const& font)
|
||||
m_msgAvgWidget->changeFont (font);
|
||||
}
|
||||
if (m_foxLogWindow) {
|
||||
m_foxLogWindow->change_font (font);
|
||||
m_foxLogWindow->set_log_view_font (font);
|
||||
}
|
||||
if (m_contestLogWindow) {
|
||||
m_contestLogWindow->change_font (font);
|
||||
m_contestLogWindow->set_log_view_font (font);
|
||||
}
|
||||
updateGeometry ();
|
||||
}
|
||||
@ -1880,7 +1868,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
|
||||
if((e->modifiers() & Qt::ControlModifier) and (e->modifiers() & Qt::ShiftModifier)) {
|
||||
m_bandEdited = true;
|
||||
band_changed(m_freqNominal-2000);
|
||||
// qDebug() << "Down" << m_freqNominal;
|
||||
} else {
|
||||
n=11;
|
||||
if(e->modifiers() & Qt::ControlModifier) n+=100;
|
||||
@ -1895,7 +1882,6 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
|
||||
if((e->modifiers() & Qt::ControlModifier) and (e->modifiers() & Qt::ShiftModifier)) {
|
||||
m_bandEdited = true;
|
||||
band_changed(m_freqNominal+2000);
|
||||
// qDebug() << "Up " << m_freqNominal;
|
||||
} else {
|
||||
n=12;
|
||||
if(e->modifiers() & Qt::ControlModifier) n+=100;
|
||||
@ -2440,6 +2426,10 @@ void MainWindow::on_fox_log_action_triggered()
|
||||
|
||||
// Connect signals from fox log window
|
||||
connect (this, &MainWindow::finished, m_foxLogWindow.data (), &FoxLogWindow::close);
|
||||
connect (m_foxLogWindow.data (), &FoxLogWindow::reset_log_model, [this] () {
|
||||
if (!m_foxLog) m_foxLog.reset (new FoxLog);
|
||||
m_foxLog->reset ();
|
||||
});
|
||||
}
|
||||
m_foxLogWindow->showNormal ();
|
||||
m_foxLogWindow->raise ();
|
||||
@ -5353,9 +5343,15 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button
|
||||
default: break;
|
||||
}
|
||||
|
||||
auto special_op = m_config.special_op_id ();
|
||||
if (SpecOp::NONE < special_op && special_op < SpecOp::FOX)
|
||||
{
|
||||
if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config});
|
||||
}
|
||||
m_logDlg->initLogQSO (m_hisCall, grid, m_modeTx, m_rptSent, m_rptRcvd,
|
||||
m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal +
|
||||
ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd);
|
||||
ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd,
|
||||
m_cabrilloLog.data ());
|
||||
}
|
||||
|
||||
void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid
|
||||
@ -5372,15 +5368,6 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
|
||||
tr ("Cannot open \"%1\"").arg (m_logBook.path ()));
|
||||
}
|
||||
|
||||
|
||||
if (SpecOp::NONE < m_config.special_op_id() && SpecOp::FOX > m_config.special_op_id()) {
|
||||
if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config});
|
||||
m_cabrilloLog->add_QSO (m_freqNominal, QDateTime::currentDateTimeUtc (), m_hisCall, m_xSent, m_xRcvd);
|
||||
m_xSent="";
|
||||
m_xRcvd="";
|
||||
ui->sbSerialNumber->setValue (ui->sbSerialNumber->value () + 1);
|
||||
}
|
||||
|
||||
m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received
|
||||
, tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid);
|
||||
m_messageClient->logged_ADIF (ADIF);
|
||||
@ -5400,6 +5387,14 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
|
||||
|
||||
if (m_config.clear_DX () and SpecOp::HOUND != m_config.special_op_id()) clearDX ();
|
||||
m_dateTimeQSOOn = QDateTime {};
|
||||
auto special_op = m_config.special_op_id ();
|
||||
if (SpecOp::NONE < special_op && special_op < SpecOp::FOX)
|
||||
{
|
||||
ui->sbSerialNumber->setValue (ui->sbSerialNumber->value () + 1);
|
||||
}
|
||||
|
||||
m_xSent.clear ();
|
||||
m_xRcvd.clear ();
|
||||
}
|
||||
|
||||
qint64 MainWindow::nWidgets(QString t)
|
||||
@ -5464,7 +5459,6 @@ void MainWindow::displayWidgets(qint64 n)
|
||||
if(i==32) ui->cbCQonly->setVisible(b);
|
||||
j=j>>1;
|
||||
}
|
||||
if(!ui->cbAutoSeq->isVisible()) ui->cbAutoSeq->setChecked(false);
|
||||
b=SpecOp::EU_VHF==m_config.special_op_id() or (SpecOp::RTTY==m_config.special_op_id() and
|
||||
(m_config.RTTY_Exchange()=="#" or m_config.RTTY_Exchange()=="DX"));
|
||||
ui->sbSerialNumber->setVisible(b);
|
||||
@ -6134,25 +6128,14 @@ void MainWindow::on_actionErase_ALL_TXT_triggered() //Erase ALL.TXT
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_reset_fox_log_action_triggered ()
|
||||
{
|
||||
int ret = MessageBox::query_message(this, tr("Confirm Reset"),
|
||||
tr("Are you sure you want to erase file FoxQSO.txt and start a new Fox log?"));
|
||||
if(ret==MessageBox::Yes) {
|
||||
QFile f{m_config.writeable_data_dir().absoluteFilePath("FoxQSO.txt")};
|
||||
f.remove();
|
||||
if (!m_foxLog) m_foxLog.reset (new FoxLog);
|
||||
m_foxLog->reset ();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_reset_cabrillo_log_action_triggered ()
|
||||
{
|
||||
if (MessageBox::Yes == MessageBox::query_message(this, tr("Confirm Erase"),
|
||||
tr("Are you sure you want to erase file cabrillo.log"
|
||||
" and start a new Cabrillo log?")))
|
||||
if (MessageBox::Yes == MessageBox::query_message (this, tr ("Confirm Reset"),
|
||||
tr ("Are you sure you want to erase your contest log?"),
|
||||
tr ("Doing this will remove all QSO records for the current contest. "
|
||||
"They will be kept in the ADIF log file but will not be available "
|
||||
"for export in your Cabrillo log.")))
|
||||
{
|
||||
QFile {m_config.writeable_data_dir ().absoluteFilePath ("cabrillo.log")}.remove ();
|
||||
ui->sbSerialNumber->setValue (1);
|
||||
if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config});
|
||||
m_cabrilloLog->reset ();
|
||||
@ -6505,15 +6488,17 @@ void MainWindow::on_readFreq_clicked()
|
||||
|
||||
void MainWindow::on_pbTxMode_clicked()
|
||||
{
|
||||
if(m_modeTx=="JT9") {
|
||||
m_modeTx="JT65";
|
||||
ui->pbTxMode->setText("Tx JT65 #");
|
||||
} else {
|
||||
m_modeTx="JT9";
|
||||
ui->pbTxMode->setText("Tx JT9 @");
|
||||
if(m_mode=="JT9+JT65") {
|
||||
if(m_modeTx=="JT9") {
|
||||
m_modeTx="JT65";
|
||||
ui->pbTxMode->setText("Tx JT65 #");
|
||||
} else {
|
||||
m_modeTx="JT9";
|
||||
ui->pbTxMode->setText("Tx JT9 @");
|
||||
}
|
||||
m_wideGraph->setModeTx(m_modeTx);
|
||||
statusChanged();
|
||||
}
|
||||
m_wideGraph->setModeTx(m_modeTx);
|
||||
statusChanged();
|
||||
}
|
||||
|
||||
void MainWindow::setXIT(int n, Frequency base)
|
||||
@ -7266,6 +7251,10 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout
|
||||
QString t1;
|
||||
while(p1.canReadLine()) {
|
||||
QString t(p1.readLine());
|
||||
if(ui->cbNoOwnCall->isChecked()) {
|
||||
if(t.contains(" " + m_config.my_callsign() + " ")) continue;
|
||||
if(t.contains(" <" + m_config.my_callsign() + "> ")) continue;
|
||||
}
|
||||
if(t.indexOf("<DecodeFinished>") >= 0) {
|
||||
m_bDecoded = m_nWSPRdecodes > 0;
|
||||
if(!m_diskData) {
|
||||
@ -7391,7 +7380,6 @@ void MainWindow::WSPR_history(Frequency dialFreq, int ndecodes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MainWindow::uploadSpots()
|
||||
{
|
||||
// do not spot replays or if rig control not working
|
||||
@ -7933,9 +7921,9 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
|
||||
|
||||
if(isort>1) {
|
||||
if(bReverse) {
|
||||
qSort(list.begin(),list.end(),qGreater<int>());
|
||||
std::sort (list.begin (), list.end (), std::greater<int> ());
|
||||
} else {
|
||||
qSort(list.begin(),list.end());
|
||||
std::sort (list.begin (), list.end ());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,6 @@ private slots:
|
||||
void on_actionDeepestDecode_toggled (bool);
|
||||
void bumpFqso(int n);
|
||||
void on_actionErase_ALL_TXT_triggered();
|
||||
void on_reset_fox_log_action_triggered ();
|
||||
void on_reset_cabrillo_log_action_triggered ();
|
||||
void on_actionErase_wsjtx_log_adi_triggered();
|
||||
void on_actionExport_Cabrillo_log_triggered();
|
||||
@ -446,7 +445,6 @@ private:
|
||||
qint32 m_Nslots=5;
|
||||
qint32 m_nFoxMsgTimes[5]={0,0,0,0,0};
|
||||
qint32 m_tAutoOn;
|
||||
qint32 m_mouseIdleSeconds;
|
||||
qint32 m_tFoxTx=0;
|
||||
qint32 m_tFoxTx0=0;
|
||||
qint32 m_maxStrikes=3; //Max # of repeats: 3 strikes and you're out
|
||||
@ -570,9 +568,6 @@ private:
|
||||
QTimer minuteTimer;
|
||||
QTimer splashTimer;
|
||||
QTimer p1Timer;
|
||||
QTimer mouseTimer;
|
||||
|
||||
QPoint mouseLastPos;
|
||||
|
||||
QString m_path;
|
||||
QString m_baseCall;
|
||||
@ -701,7 +696,6 @@ private:
|
||||
void CQTxFreq();
|
||||
void useNextCall();
|
||||
void abortQSO();
|
||||
void mouseTimerTick();
|
||||
bool isWorked(int itype, QString key, float fMHz=0, QString="");
|
||||
|
||||
QString save_wave_file (QString const& name
|
||||
|
@ -573,6 +573,430 @@ QLabel[oob="true"] {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="SignalMeter" name="signal_meter_widget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html></string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QWidget" name="DX_controls_widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DX Call</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DX Grid</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="dxCallEntry">
|
||||
<property name="toolTip">
|
||||
<string>Callsign of station to be worked</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="lookupButton">
|
||||
<property name="toolTip">
|
||||
<string>Search for callsign in database</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Lookup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="dxGridEntry">
|
||||
<property name="toolTip">
|
||||
<string>Locator of station to be worked</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="labAz">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Az: 251 16553 km</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="toolTip">
|
||||
<string>Add callsign and locator to database</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string> Pwr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
|
||||
<widget class="QPushButton" name="readFreq">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html></string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
font-family: helvetica;
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-style: solid;
|
||||
border-width:1px;
|
||||
border-radius:10px;
|
||||
border-color: gray;
|
||||
max-width:20px;
|
||||
max-height:20px;
|
||||
min-width:20px;
|
||||
min-height:20px;
|
||||
}
|
||||
QPushButton[state="error"] {
|
||||
background-color: red;
|
||||
}
|
||||
QPushButton[state="warning"] {
|
||||
background-color: orange;
|
||||
}
|
||||
QPushButton[state="ok"] {
|
||||
background-color: #00ff00;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4" rowspan="2">
|
||||
<widget class="QSlider" name="outAttenuation">
|
||||
<property name="toolTip">
|
||||
<string>Adjust Tx audio level</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>450</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="bandComboBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html></string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::NoInsert</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="labUTC">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel {
|
||||
font-family: MS Shell Dlg 2;
|
||||
font-size: 16pt;
|
||||
background-color : black;
|
||||
color : yellow;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3" rowspan="3">
|
||||
<widget class="QStackedWidget" name="controls_stack_widget">
|
||||
<property name="currentIndex">
|
||||
@ -845,7 +1269,7 @@ QLabel[oob="true"] {
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Check this to call CQ on the &quot;Tx CQ&quot; frequency. Rx will be on the current frequency and the CQ message wiill include the current Rx frequency so callers know which frequency to reply on.</p><p>Not available to type 2 compound callsign holders.</p></body></html></string>
|
||||
<string><html><head/><body><p>Check this to call CQ on the &quot;Tx CQ&quot; frequency. Rx will be on the current frequency and the CQ message wiill include the current Rx frequency so callers know which frequency to reply on.</p><p>Not available to nonstandard callsign holders.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
@ -2105,6 +2529,13 @@ list. The list can be maintained in Settings (F2).</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbNoOwnCall">
|
||||
<property name="text">
|
||||
<string>No own call decodes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<item>
|
||||
@ -2199,430 +2630,6 @@ list. The list can be maintained in Settings (F2).</string>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="SignalMeter" name="signal_meter_widget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>30dB recommended when only noise present<br/>Green when good<br/>Red when clipping may occur<br/>Yellow when too low</p></body></html></string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QWidget" name="DX_controls_widget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DX Call</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>252</red>
|
||||
<green>252</green>
|
||||
<blue>252</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>159</red>
|
||||
<green>175</green>
|
||||
<blue>213</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DX Grid</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="dxCallEntry">
|
||||
<property name="toolTip">
|
||||
<string>Callsign of station to be worked</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="dxGridEntry">
|
||||
<property name="toolTip">
|
||||
<string>Locator of station to be worked</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QPushButton" name="lookupButton">
|
||||
<property name="toolTip">
|
||||
<string>Search for callsign in database</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Lookup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="toolTip">
|
||||
<string>Add callsign and locator to database</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="labAz">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Az: 251 16553 km</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string> Pwr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
|
||||
<widget class="QPushButton" name="readFreq">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</p></body></html></string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
font-family: helvetica;
|
||||
font-size: 9pt;
|
||||
font-weight: bold;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border-style: solid;
|
||||
border-width:1px;
|
||||
border-radius:10px;
|
||||
border-color: gray;
|
||||
max-width:20px;
|
||||
max-height:20px;
|
||||
min-width:20px;
|
||||
min-height:20px;
|
||||
}
|
||||
QPushButton[state="error"] {
|
||||
background-color: red;
|
||||
}
|
||||
QPushButton[state="warning"] {
|
||||
background-color: orange;
|
||||
}
|
||||
QPushButton[state="ok"] {
|
||||
background-color: #00ff00;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4" rowspan="2">
|
||||
<widget class="QSlider" name="outAttenuation">
|
||||
<property name="toolTip">
|
||||
<string>Adjust Tx audio level</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>450</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="invertedControls">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QComboBox" name="bandComboBox">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</p></body></html></string>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::NoInsert</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="labUTC">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QLabel {
|
||||
font-family: MS Shell Dlg 2;
|
||||
font-size: 16pt;
|
||||
background-color : black;
|
||||
color : yellow;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="midLineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p align="center"> 2015 Jun 17 </p><p align="center"> 01:23:45 </p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -2646,7 +2653,6 @@ QPushButton[state="ok"] {
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionDelete_all_wav_files_in_SaveDir"/>
|
||||
<addaction name="actionErase_ALL_TXT"/>
|
||||
<addaction name="reset_fox_log_action"/>
|
||||
<addaction name="actionErase_wsjtx_log_adi"/>
|
||||
<addaction name="reset_cabrillo_log_action"/>
|
||||
<addaction name="actionExport_Cabrillo_log"/>
|
||||
|
@ -7,7 +7,8 @@ SOURCES += \
|
||||
widgets/echoplot.cpp widgets/echograph.cpp widgets/fastgraph.cpp \
|
||||
widgets/fastplot.cpp widgets/MessageBox.cpp \
|
||||
widgets/colorhighlighting.cpp widgets/ExportCabrillo.cpp \
|
||||
widgets/CabrilloLogWindow.cpp
|
||||
widgets/AbstractLogWindow.cpp \
|
||||
widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
widgets/mainwindow.h widgets/plotter.h \
|
||||
@ -17,8 +18,8 @@ HEADERS += \
|
||||
widgets/meterwidget.h widgets/messageaveraging.h \
|
||||
widgets/echoplot.h widgets/echograph.h widgets/fastgraph.h \
|
||||
widgets/fastplot.h widgets/MessageBox.hpp widgets/colorhighlighting.h \
|
||||
widgets/ExportCabrillo.h \
|
||||
widgets/CabrilloLogWindow.cpp
|
||||
widgets/ExportCabrillo.h widgets/AbstractLogWindow.hpp \
|
||||
widgets/FoxLogWindow.cpp widgets/CabrilloLogWindow.cpp
|
||||
|
||||
FORMS += \
|
||||
widgets/mainwindow.ui widgets/about.ui \
|
||||
@ -26,5 +27,4 @@ FORMS += \
|
||||
widgets/logqso.ui widgets/messageaveraging.ui \
|
||||
widgets/echograph.ui widgets/fastgraph.ui \
|
||||
widgets/colorhighlighting.ui widgets/ExportCabrillo.ui \
|
||||
widgets/FoxLogWindow.ui \
|
||||
widgets/CabrilloLogWindow.ui
|
||||
widgets/FoxLogWindow.ui widgets/CabrilloLogWindow.ui
|
||||
|
Loading…
Reference in New Issue
Block a user