Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17416144a8 | |||
| b8ff10fde9 | |||
| 6c741e21b5 | |||
| 47eec2dc0b | |||
| 6504a1f220 | |||
| 91a2a801f9 | |||
| 8c74499700 | |||
| 51c9dd2761 | |||
| dc70d53f5c | |||
| 3c2a5f98ec | |||
| 5c1f890095 | |||
| 29f759dafe | |||
| 90858f8964 | |||
| e6464e952d | |||
| 186fed04e3 | |||
| 6716eb771e | |||
| b8b2cf33c3 | |||
| 4025edfc0f | |||
| c5930a0aa3 | |||
| 9079d45587 | |||
| 223a4a2183 | |||
| 1584cf0404 | |||
| 5619824bd8 | |||
| e1e2ae50e8 | |||
| cc96daa26f | |||
| 5ba31d6df7 | |||
| 73346240e5 | |||
| 886e678531 | |||
| 526b72022e | |||
| a1864fa3f0 | |||
| b0c57029e8 | |||
| 2d5b41d4b7 | |||
| 21004a7b28 | |||
| 94f2f510fc | |||
| b6745c9e2e | |||
| 8c81b3b83a | |||
| 9ddfec5e72 | |||
| a5f0937cbb | |||
| 26f21cd70a | |||
| bb7e2544b5 |
+7
-5
@@ -1272,12 +1272,14 @@ if (NOT WIN32 AND NOT APPLE)
|
||||
#COMPONENT runtime
|
||||
)
|
||||
|
||||
execute_process(COMMAND ln -s /opt/js8call/bin/js8call ljs8call)
|
||||
IF("${CMAKE_INSTALL_PREFIX}" STREQUAL "/opt/js8call")
|
||||
execute_process(COMMAND ln -s /opt/js8call/bin/js8call ljs8call)
|
||||
|
||||
install(FILES
|
||||
${CMAKE_BINARY_DIR}/ljs8call DESTINATION /usr/bin/ RENAME js8call
|
||||
#COMPONENT runtime
|
||||
)
|
||||
install(FILES
|
||||
${CMAKE_BINARY_DIR}/ljs8call DESTINATION /usr/bin/ RENAME js8call
|
||||
#COMPONENT runtime
|
||||
)
|
||||
endif()
|
||||
endif (NOT WIN32 AND NOT APPLE)
|
||||
|
||||
|
||||
|
||||
+26
-26
@@ -1028,6 +1028,12 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void setUppercase(T* t){
|
||||
auto f = t->font();
|
||||
f.setCapitalization(QFont::AllUppercase);
|
||||
t->setFont(f);
|
||||
}
|
||||
|
||||
Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
|
||||
QSettings * settings, QWidget * parent)
|
||||
: QDialog {parent}
|
||||
@@ -1137,6 +1143,16 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
|
||||
ui_->qth_message_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
ui_->reply_message_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
ui_->cq_message_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
ui_->groups_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
|
||||
setUppercase(ui_->callsign_line_edit);
|
||||
setUppercase(ui_->grid_line_edit);
|
||||
setUppercase(ui_->add_macro_line_edit);
|
||||
setUppercase(ui_->station_message_line_edit);
|
||||
setUppercase(ui_->qth_message_line_edit);
|
||||
setUppercase(ui_->reply_message_line_edit);
|
||||
setUppercase(ui_->cq_message_line_edit);
|
||||
setUppercase(ui_->groups_line_edit);
|
||||
|
||||
ui_->udp_server_port_spin_box->setMinimum (1);
|
||||
ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
|
||||
@@ -2006,7 +2022,7 @@ QStringList splitGroups(QString groupsString, bool filter){
|
||||
|
||||
bool Configuration::impl::validate ()
|
||||
{
|
||||
auto callsign = ui_->callsign_line_edit->text().trimmed();
|
||||
auto callsign = ui_->callsign_line_edit->text().toUpper().trimmed();
|
||||
if(!Varicode::isValidCallsign(callsign, nullptr) || callsign.startsWith("@")){
|
||||
MessageBox::critical_message (this, tr ("The callsign format you provided is not supported"));
|
||||
return false;
|
||||
@@ -2019,6 +2035,12 @@ bool Configuration::impl::validate ()
|
||||
}
|
||||
}
|
||||
|
||||
auto cq = ui_->cq_message_line_edit->text().toUpper().trimmed();
|
||||
if(!cq.isEmpty() && !(cq.startsWith("CQ") || cq.contains(callsign))){
|
||||
MessageBox::critical_message (this, QString("The CQ message format is invalid. It must either start with \"CQ\" or contain your callsign."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui_->sound_input_combo_box->currentIndex () < 0
|
||||
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioInput).empty ())
|
||||
{
|
||||
@@ -2267,12 +2289,10 @@ void Configuration::impl::accept ()
|
||||
Q_ASSERT (audio_output_channel_ <= AudioDevice::Both);
|
||||
|
||||
auto_switch_bands_ = ui_->auto_switch_bands_check_box->isChecked();
|
||||
my_callsign_ = ui_->callsign_line_edit->text ();
|
||||
my_grid_ = ui_->grid_line_edit->text ();
|
||||
my_callsign_ = ui_->callsign_line_edit->text ().toUpper();
|
||||
my_grid_ = ui_->grid_line_edit->text ().toUpper();
|
||||
my_station_ = ui_->station_message_line_edit->text().toUpper();
|
||||
|
||||
my_groups_ = splitGroups(ui_->groups_line_edit->text().toUpper().trimmed(), true);
|
||||
|
||||
cq_ = ui_->cq_message_line_edit->text().toUpper();
|
||||
reply_ = ui_->reply_message_line_edit->text().toUpper();
|
||||
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
||||
@@ -2698,43 +2718,23 @@ void Configuration::impl::on_sound_output_combo_box_currentTextChanged (QString
|
||||
|
||||
void Configuration::impl::on_station_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->station_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Configuration::impl::on_groups_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->groups_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_qth_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->qth_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_cq_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->cq_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_reply_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->reply_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_add_macro_line_edit_editingFinished ()
|
||||
@@ -2793,7 +2793,7 @@ void Configuration::impl::on_add_macro_push_button_clicked (bool /* checked */)
|
||||
{
|
||||
auto index = next_macros_.index (next_macros_.rowCount () - 1);
|
||||
ui_->macros_list_view->setCurrentIndex (index);
|
||||
next_macros_.setData (index, ui_->add_macro_line_edit->text ());
|
||||
next_macros_.setData (index, ui_->add_macro_line_edit->text ().toUpper());
|
||||
ui_->add_macro_line_edit->clear ();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -23,7 +23,7 @@
|
||||
<string>Select tab to change configuration parameters.</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="general_tab">
|
||||
<attribute name="title">
|
||||
@@ -279,7 +279,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>654</width>
|
||||
<width>724</width>
|
||||
<height>489</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -642,7 +642,7 @@ text message.</string>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Disable automatic transmissions:</string>
|
||||
<string>Disable automatic transmissions after:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>tx_watchdog_spin_box</cstring>
|
||||
@@ -661,7 +661,7 @@ text message.</string>
|
||||
<string> minutes of inactivity</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>after </string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
@@ -2084,7 +2084,7 @@ both here.</string>
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>true</bool>
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Check this option to force deselect callsign after logging.</string>
|
||||
@@ -4069,8 +4069,8 @@ soundcard changes</string>
|
||||
<buttongroups>
|
||||
<buttongroup name="PTT_method_button_group"/>
|
||||
<buttongroup name="CAT_data_bits_button_group"/>
|
||||
<buttongroup name="TX_audio_source_button_group"/>
|
||||
<buttongroup name="split_mode_button_group"/>
|
||||
<buttongroup name="TX_audio_source_button_group"/>
|
||||
<buttongroup name="CAT_handshake_button_group"/>
|
||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
||||
<buttongroup name="TX_mode_button_group"/>
|
||||
|
||||
@@ -10,10 +10,12 @@ JS8Call is an experiment to test the feasibility of a digital mode with the robu
|
||||
|
||||
* For release announcements and discussion, join the JS8Call mailing list here: https://groups.io/g/js8call
|
||||
|
||||
* Documentation is available here: https://docs.google.com/document/d/159S4wqMUVdMA7qBgaSWmU-iDI4C9wd4CuWnetN68O9U/edit?pli=1#heading=h.kfnyge37yfr
|
||||
|
||||
|
||||
# Notice
|
||||
|
||||
JS8Call is a derivative of the WSJT-X application, restructured and redesigned for message passing using FT8 modulation. It is not supported by nor endorsed by the WSJT-X development group. While the WSJT-X group maintains copyright over the original work and code, JS8Call is a derivative work licensed under and in accordance with the terms of the GPLv3 license. The source code modifications are public and can be found in this repository: https://bitbucket.org/widefido/wsjtx/
|
||||
JS8Call is a derivative of the WSJT-X application, restructured and redesigned for message passing using FT8 modulation. It is not supported by nor endorsed by the WSJT-X development group. While the WSJT-X group maintains copyright over the original work and code, JS8Call is a derivative work licensed under and in accordance with the terms of the GPLv3 license. The source code modifications are public and can be found in this repository: https://bitbucket.org/widefido/js8call/
|
||||
|
||||
|
||||
# History
|
||||
@@ -33,3 +35,7 @@ JS8Call is a derivative of the WSJT-X application, restructured and redesigned f
|
||||
* August 12, 2018 - Version 0.4 released - (“leaked” on QRZ) - 500 testers
|
||||
* September 2, 2018 - Version 0.5 released - 3000 testers
|
||||
* September 14, 2018 - Version 0.6 released - 5000 testers
|
||||
* October 8, 2018 - Version 0.7 released - 6000 testers, name changed to JS8 & JS8Call
|
||||
* October 31, 2018 - Version 0.8 released - ~7000 testers
|
||||
* November 15, 2018 - Version 0.9 released - ~7500 testers
|
||||
* November 30, 2018 - Version 0.10 released - ~7800 testers
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
# Version number components
|
||||
set (WSJTX_VERSION_MAJOR 0)
|
||||
set (WSJTX_VERSION_MINOR 10)
|
||||
set (WSJTX_VERSION_PATCH 1)
|
||||
set (WSJTX_VERSION_MINOR 11)
|
||||
set (WSJTX_VERSION_PATCH 0)
|
||||
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions
|
||||
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
|
||||
|
||||
@@ -31,7 +31,7 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
||||
"community.<br /><br />"
|
||||
"JS8Call stands on the shoulder of giants...the takeoff angle "
|
||||
"is better up there.<br /><br />"
|
||||
"A special thanks goes out to the JS8Call development team:<br/><br/><strong>"
|
||||
"A special thanks goes out to:<br/><br/><strong>"
|
||||
"KC9QNE, "
|
||||
"KI6SSI, "
|
||||
"K0OG, "
|
||||
@@ -41,8 +41,8 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
||||
"OH8STN, "
|
||||
"VA3OSO, "
|
||||
"VK1MIC, "
|
||||
"W0FW,</strong><br/><br/>and the many other amateur radio operators who have given "
|
||||
"JS8Call a chance.");
|
||||
"W0FW,</strong><br/><br/>and the many other amateur radio operators who have helped<br/>"
|
||||
"bring JS8Call into the world.");
|
||||
}
|
||||
|
||||
CAboutDlg::~CAboutDlg()
|
||||
|
||||
+2
-7
@@ -221,19 +221,14 @@ bool DecodedText::tryUnpackData(){
|
||||
return false;
|
||||
}
|
||||
|
||||
if((bits_ & Varicode::JS8CallData) != Varicode::JS8CallData){
|
||||
return false;
|
||||
}
|
||||
|
||||
quint8 type = Varicode::FrameUnknown;
|
||||
QString data = Varicode::unpackDataMessage(m, &type);
|
||||
QString data = Varicode::unpackDataMessage(m);
|
||||
|
||||
if(data.isEmpty()){
|
||||
return false;
|
||||
}
|
||||
|
||||
message_ = data;
|
||||
frameType_ = type;
|
||||
frameType_ = Varicode::FrameData;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+49
-49
@@ -48,50 +48,50 @@ subroutine foxgen()
|
||||
|
||||
do n=1,nslots
|
||||
i3b=i3bit(n)
|
||||
if(i3b.eq.0) then
|
||||
msg=cmsg(n)(1:22) !Standard FT8 message
|
||||
else
|
||||
i1=index(cmsg(n),' ') !Special Fox message
|
||||
i2=index(cmsg(n),';')
|
||||
i3=index(cmsg(n),'<')
|
||||
i4=index(cmsg(n),'>')
|
||||
msg=cmsg(n)(1:i1)//cmsg(n)(i2+1:i3-2)//' '
|
||||
read(cmsg(n)(i4+2:i4+4),*) irpt
|
||||
endif
|
||||
call genft8(msg,mygrid,bcontest,0,msgsent,msgbits,itone)
|
||||
!if(i3b.eq.0) then
|
||||
msg=cmsg(n)(1:12) !Standard FT8 message
|
||||
!else
|
||||
! i1=index(cmsg(n),' ') !Special Fox message
|
||||
! i2=index(cmsg(n),';')
|
||||
! i3=index(cmsg(n),'<')
|
||||
! i4=index(cmsg(n),'>')
|
||||
! msg=cmsg(n)(1:i1)//cmsg(n)(i2+1:i3-2)//' '
|
||||
! read(cmsg(n)(i4+2:i4+4),*) irpt
|
||||
!endif
|
||||
call genft8(msg,mygrid,bcontest,i3b,msgsent,msgbits,itone)
|
||||
! print*,'Foxgen:',n,cmsg(n),msgsent
|
||||
|
||||
if(i3b.eq.1) then
|
||||
icrc10=crc10(c_loc(mycall),12)
|
||||
nrpt=irpt+30
|
||||
write(cbits,1001) msgbits(1:56),icrc10,nrpt,i3b,0
|
||||
1001 format(56b1.1,b10.10,b6.6,b3.3,b12.12)
|
||||
read(cbits,1002) msgbits
|
||||
1002 format(87i1)
|
||||
|
||||
cb88=cbits//'0'
|
||||
read(cb88,1003) i1Msg8BitBytes(1:11)
|
||||
1003 format(11b8)
|
||||
icrc12=crc12(c_loc(i1Msg8BitBytes),11)
|
||||
! icrc12=xor(icrc12, 41) ! TODO: jsherer - could change the crc here
|
||||
|
||||
write(cbits,1001) msgbits(1:56),icrc10,nrpt,i3b,icrc12
|
||||
read(cbits,1002) msgbits
|
||||
|
||||
call encode174(msgbits,codeword) !Encode the test message
|
||||
|
||||
! Message structure: S7 D29 S7 D29 S7
|
||||
itone(1:7)=icos7
|
||||
itone(36+1:36+7)=icos7
|
||||
itone(NN-6:NN)=icos7
|
||||
k=7
|
||||
do j=1,ND
|
||||
i=3*j -2
|
||||
k=k+1
|
||||
if(j.eq.30) k=k+7
|
||||
itone(k)=codeword(i)*4 + codeword(i+1)*2 + codeword(i+2)
|
||||
enddo
|
||||
endif
|
||||
!! if(i3b.eq.1) then
|
||||
!! icrc10=crc10(c_loc(mycall),12)
|
||||
!! nrpt=irpt+30
|
||||
!! write(cbits,1001) msgbits(1:56),icrc10,nrpt,i3b,0
|
||||
!! 1001 format(56b1.1,b10.10,b6.6,b3.3,b12.12)
|
||||
!! read(cbits,1002) msgbits
|
||||
!! 1002 format(87i1)
|
||||
!!
|
||||
!! cb88=cbits//'0'
|
||||
!! read(cb88,1003) i1Msg8BitBytes(1:11)
|
||||
!! 1003 format(11b8)
|
||||
!! icrc12=crc12(c_loc(i1Msg8BitBytes),11)
|
||||
!! ! icrc12=xor(icrc12, 41) ! TODO: jsherer - could change the crc here
|
||||
!!
|
||||
!! write(cbits,1001) msgbits(1:56),icrc10,nrpt,i3b,icrc12
|
||||
!! read(cbits,1002) msgbits
|
||||
!!
|
||||
!! call encode174(msgbits,codeword) !Encode the test message
|
||||
!!
|
||||
!! ! Message structure: S7 D29 S7 D29 S7
|
||||
!! itone(1:7)=icos7
|
||||
!! itone(36+1:36+7)=icos7
|
||||
!! itone(NN-6:NN)=icos7
|
||||
!! k=7
|
||||
!! do j=1,ND
|
||||
!! i=3*j -2
|
||||
!! k=k+1
|
||||
!! if(j.eq.30) k=k+7
|
||||
!! itone(k)=codeword(i)*4 + codeword(i+1)*2 + codeword(i+2)
|
||||
!! enddo
|
||||
!! endif
|
||||
|
||||
! Make copies of itone() and msgbits() for ft8sim
|
||||
itone2=itone
|
||||
@@ -117,13 +117,13 @@ subroutine foxgen()
|
||||
! call plotspec(1,wave) !Plot the spectrum
|
||||
|
||||
! Apply compression
|
||||
! rms=sqrt(dot_product(wave,wave)/kz)
|
||||
! wave=wave/rms
|
||||
! do i=1,NWAVE
|
||||
! wave(i)=h1(wave(i))
|
||||
! enddo
|
||||
! peak2=maxval(abs(wave))
|
||||
! wave=wave/peak2
|
||||
rms=sqrt(dot_product(wave,wave)/kz)
|
||||
wave=wave/rms
|
||||
do i=1,NWAVE
|
||||
wave(i)=h1(wave(i))
|
||||
enddo
|
||||
peak2=maxval(abs(wave))
|
||||
wave=wave/peak2
|
||||
|
||||
! call plotspec(2,wave) !Plot the spectrum
|
||||
|
||||
|
||||
+303
-116
@@ -151,6 +151,18 @@ extern "C" {
|
||||
void plotsave_(float swide[], int* m_w , int* m_h1, int* irow);
|
||||
}
|
||||
|
||||
#ifndef TEST_FOX_WAVE_GEN
|
||||
#define TEST_FOX_WAVE_GEN 0
|
||||
#endif
|
||||
|
||||
#ifndef TEST_FOX_WAVE_GEN_SLOTS
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
#define TEST_FOX_WAVE_GEN_SLOTS 2
|
||||
#else
|
||||
#define TEST_FOX_WAVE_GEN_SLOTS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
const int NEAR_THRESHOLD_RX = 10;
|
||||
|
||||
int volatile itone[NUM_ISCAT_SYMBOLS]; //Audio tones for all Tx symbols
|
||||
@@ -1206,6 +1218,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
menu->addAction(clearActionAll);
|
||||
|
||||
menu->popup(ui->extFreeTextMsgEdit->mapToGlobal(point));
|
||||
|
||||
displayActivity(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -1290,6 +1304,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
menu->addAction(clearActionAll);
|
||||
|
||||
menu->popup(ui->tableWidgetRXAll->mapToGlobal(point));
|
||||
|
||||
displayActivity(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -1480,7 +1496,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
|
||||
}
|
||||
|
||||
QDate eol(2018, 12, 18);
|
||||
QDate eol(2019, 1, 2);
|
||||
|
||||
void MainWindow::checkExpiryWarningMessage()
|
||||
{
|
||||
@@ -2449,6 +2465,9 @@ void MainWindow::on_menuWindow_aboutToShow(){
|
||||
#if __APPLE__
|
||||
rebuildMacQAction(ui->menuWindow, ui->actionShow_Call_Activity_Columns);
|
||||
#endif
|
||||
|
||||
ui->actionShow_Band_Heartbeats_and_ACKs->setChecked(!m_hbHidden);
|
||||
ui->actionShow_Band_Heartbeats_and_ACKs->setEnabled(ui->actionShow_Band_Activity->isChecked());
|
||||
}
|
||||
|
||||
void MainWindow::on_actionShow_Fullscreen_triggered(bool checked){
|
||||
@@ -2470,26 +2489,89 @@ void MainWindow::on_actionShow_Frequency_Clock_triggered(bool checked){
|
||||
|
||||
void MainWindow::on_actionShow_Band_Activity_triggered(bool checked){
|
||||
auto hsizes = ui->textHorizontalSplitter->sizes();
|
||||
hsizes[0] = checked ? ui->textHorizontalSplitter->width()/4 : 0;
|
||||
|
||||
if(m_bandActivityWidth == 0){
|
||||
m_bandActivityWidth = ui->textHorizontalSplitter->width()/4;
|
||||
}
|
||||
|
||||
if(m_callActivityWidth == 0){
|
||||
m_callActivityWidth = ui->textHorizontalSplitter->width()/4;
|
||||
}
|
||||
|
||||
if(m_textActivityWidth == 0){
|
||||
m_textActivityWidth = ui->textHorizontalSplitter->width()/2;
|
||||
}
|
||||
|
||||
if(checked){
|
||||
hsizes[0] = m_bandActivityWidth;
|
||||
hsizes[1] = m_textActivityWidth;
|
||||
if(hsizes[2]) hsizes[2] = m_callActivityWidth;
|
||||
|
||||
} else {
|
||||
if(hsizes[0]) m_bandActivityWidth = hsizes[0];
|
||||
if(hsizes[1]) m_textActivityWidth = hsizes[1];
|
||||
if(hsizes[2]) m_callActivityWidth = hsizes[2];
|
||||
hsizes[0] = 0;
|
||||
}
|
||||
|
||||
ui->textHorizontalSplitter->setSizes(hsizes);
|
||||
ui->tableWidgetRXAll->setVisible(checked);
|
||||
m_bandActivityWasVisible = checked;
|
||||
|
||||
}
|
||||
|
||||
void MainWindow::on_actionShow_Band_Heartbeats_and_ACKs_triggered(bool checked){
|
||||
m_hbHidden = !checked;
|
||||
displayBandActivity();
|
||||
}
|
||||
|
||||
void MainWindow::on_actionShow_Call_Activity_triggered(bool checked){
|
||||
auto hsizes = ui->textHorizontalSplitter->sizes();
|
||||
hsizes[2] = checked ? ui->textHorizontalSplitter->width()/4 : 0;
|
||||
|
||||
if(m_bandActivityWidth == 0){
|
||||
m_bandActivityWidth = ui->textHorizontalSplitter->width()/4;
|
||||
}
|
||||
|
||||
if(m_callActivityWidth == 0){
|
||||
m_callActivityWidth = ui->textHorizontalSplitter->width()/4;
|
||||
}
|
||||
|
||||
if(m_textActivityWidth == 0){
|
||||
m_textActivityWidth = ui->textHorizontalSplitter->width()/2;
|
||||
}
|
||||
|
||||
if(checked){
|
||||
if(hsizes[0]) hsizes[0] = m_bandActivityWidth;
|
||||
hsizes[1] = m_textActivityWidth;
|
||||
hsizes[2] = m_callActivityWidth;
|
||||
|
||||
} else {
|
||||
if(hsizes[0]) m_bandActivityWidth = hsizes[0];
|
||||
if(hsizes[1]) m_textActivityWidth = hsizes[1];
|
||||
if(hsizes[2]) m_callActivityWidth = hsizes[2];
|
||||
hsizes[2] = 0;
|
||||
}
|
||||
|
||||
ui->textHorizontalSplitter->setSizes(hsizes);
|
||||
ui->tableWidgetCalls->setVisible(checked);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionShow_Waterfall_triggered(bool checked){
|
||||
auto vsizes = ui->mainSplitter->sizes();
|
||||
vsizes[0] = qMin(vsizes[0], ui->logHorizontalWidget->minimumHeight());
|
||||
int oldHeight = vsizes[vsizes.length()-1];
|
||||
int newHeight = checked ? ui->mainSplitter->height()/4 : 0;
|
||||
vsizes[1] += oldHeight - newHeight;
|
||||
vsizes[vsizes.length()-1] = newHeight;
|
||||
|
||||
if(m_waterfallHeight == 0){
|
||||
m_waterfallHeight = ui->mainSplitter->height()/4;
|
||||
}
|
||||
|
||||
if(checked){
|
||||
vsizes[vsizes.length() - 1] = m_waterfallHeight;
|
||||
|
||||
} else {
|
||||
m_waterfallHeight = vsizes[vsizes.length() - 1];
|
||||
vsizes[1] += m_waterfallHeight;
|
||||
vsizes[vsizes.length() - 1] = 0;
|
||||
}
|
||||
|
||||
ui->mainSplitter->setSizes(vsizes);
|
||||
ui->bandHorizontalWidget->setVisible(checked);
|
||||
}
|
||||
@@ -3864,6 +3946,8 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
|
||||
if(!m_bandActivity.contains(offset)){
|
||||
QList<int> offsets = {
|
||||
// offset - 60, offset - 61, offset - 62, offset - 63, offset - 64, offset - 65, offset - 66, offset - 67, offset - 68, offset - 69,
|
||||
// offset + 60, offset + 61, offset + 62, offset + 63, offset + 64, offset + 65, offset + 66, offset + 67, offset + 68, offset + 69,
|
||||
offset - 1, offset - 2, offset - 3, offset - 4, offset - 5, offset - 6, offset - 7, offset - 8, offset - 9, offset - 10,
|
||||
offset + 1, offset + 2, offset + 3, offset + 4, offset + 5, offset + 6, offset + 7, offset + 8, offset + 9, offset + 10
|
||||
};
|
||||
@@ -4175,6 +4259,8 @@ bool MainWindow::hasExistingMessageBuffer(int offset, bool drift, int *pPrevOffs
|
||||
}
|
||||
|
||||
QList<int> offsets = {
|
||||
//offset - 60, offset - 61, offset - 62, offset - 63, offset - 64, offset - 65, offset - 66, offset - 67, offset - 68, offset - 69,
|
||||
//offset + 60, offset + 61, offset + 62, offset + 63, offset + 64, offset + 65, offset + 66, offset + 67, offset + 68, offset + 69,
|
||||
offset - 1, offset - 2, offset - 3, offset - 4, offset - 5, offset - 6, offset - 7, offset - 8, offset - 9, offset - 10,
|
||||
offset + 1, offset + 2, offset + 3, offset + 4, offset + 5, offset + 6, offset + 7, offset + 8, offset + 9, offset + 10
|
||||
};
|
||||
@@ -4479,10 +4565,41 @@ void MainWindow::guiUpdate()
|
||||
const_cast<int *> (itone), 22, 6, 22);
|
||||
msgibits = m_i3bit;
|
||||
msgsent[22]=0;
|
||||
|
||||
m_currentMessage = QString::fromLatin1(msgsent);
|
||||
m_currentMessageBits = msgibits;
|
||||
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
if(ui->turboButton->isChecked()) {
|
||||
|
||||
foxcom_.nslots=1;
|
||||
|
||||
foxcom_.nfreq=ui->TxFreqSpinBox->value();
|
||||
if(m_config.split_mode()) foxcom_.nfreq = foxcom_.nfreq - m_XIT; //Fox Tx freq
|
||||
strncpy(&foxcom_.cmsg[0][0], QString::fromStdString(message).toLatin1(), 12);
|
||||
foxcom_.i3bit[0] = m_i3bit | Varicode::JS8CallExtended;
|
||||
int i = 1;
|
||||
while(!m_txFrameQueue.isEmpty() && foxcom_.nslots < TEST_FOX_WAVE_GEN_SLOTS){
|
||||
auto pair = m_txFrameQueue.dequeue();
|
||||
strncpy(&foxcom_.cmsg[i][0], pair.first.toLatin1(), 12);
|
||||
foxcom_.i3bit[i] = pair.second | Varicode::JS8CallExtended;
|
||||
foxcom_.nslots += 1;
|
||||
|
||||
//m_currentMessage.append(pair.first);
|
||||
//m_currentMessageBits |= pair.second;
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if(i > 1){
|
||||
updateTxButtonDisplay();
|
||||
}
|
||||
|
||||
foxgen_();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
m_currentMessage = QString::fromLatin1(msgsent);
|
||||
m_currentMessageBits = msgibits;
|
||||
m_bCallingCQ = CALLING == m_QSOProgress
|
||||
|| m_currentMessage.contains (QRegularExpression {"^(CQ|QRZ) "});
|
||||
if(m_mode=="FT8") {
|
||||
@@ -4635,11 +4752,11 @@ void MainWindow::guiUpdate()
|
||||
m_nsendingsh=0;
|
||||
if(s[4]==64) m_nsendingsh=1;
|
||||
if(m_nsendingsh==1 or m_currentMessageType==7) {
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #66ffff}");
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #ff0000}");
|
||||
} else if(m_nsendingsh==-1 or m_currentMessageType==6) {
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #ffccff}");
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #ff0000}");
|
||||
} else {
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #ffff33}");
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #ff0000}");
|
||||
}
|
||||
if(m_tune) {
|
||||
tx_status_label.setText("Tx: TUNE");
|
||||
@@ -4655,7 +4772,7 @@ void MainWindow::guiUpdate()
|
||||
|
||||
} else if(m_monitoring) {
|
||||
if (m_tx_watchdog) {
|
||||
tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}");
|
||||
tx_status_label.setStyleSheet ("QLabel{background-color: #000000; color:#ffffff}");
|
||||
tx_status_label.setText ("Inactive watchdog");
|
||||
} else {
|
||||
tx_status_label.setStyleSheet("QLabel{background-color: #00ff00}");
|
||||
@@ -4791,6 +4908,9 @@ void MainWindow::startTx()
|
||||
// disallow editing of the text while transmitting
|
||||
ui->extFreeTextMsgEdit->setReadOnly(true);
|
||||
update_dynamic_property(ui->extFreeTextMsgEdit, "transmitting", true);
|
||||
|
||||
// update the tx button display
|
||||
updateTxButtonDisplay();
|
||||
}
|
||||
|
||||
void MainWindow::startTx2()
|
||||
@@ -5048,6 +5168,7 @@ void MainWindow::clearDX ()
|
||||
m_rptRcvd.clear ();
|
||||
m_qsoStart.clear ();
|
||||
m_qsoStop.clear ();
|
||||
|
||||
if (ui->tabWidget->currentIndex() == 1) {
|
||||
ui->genMsg->setText(ui->tx6->text());
|
||||
m_ntx=7;
|
||||
@@ -5318,6 +5439,8 @@ void MainWindow::clearActivity(){
|
||||
ui->extFreeTextMsgEdit->clear();
|
||||
ui->extFreeTextMsgEdit->setReadOnly(false);
|
||||
update_dynamic_property(ui->extFreeTextMsgEdit, "transmitting", false);
|
||||
|
||||
displayActivity(true);
|
||||
}
|
||||
|
||||
void MainWindow::createAllcallTableRows(QTableWidget *table, QString const &selectedCall){
|
||||
@@ -5325,6 +5448,8 @@ void MainWindow::createAllcallTableRows(QTableWidget *table, QString const &sele
|
||||
auto now = DriftingDateTime::currentDateTimeUtc();
|
||||
int callsignAging = m_config.callsign_aging();
|
||||
|
||||
int startCol = 1;
|
||||
|
||||
if(!ui->selcalButton->isChecked()){
|
||||
table->insertRow(table->rowCount());
|
||||
|
||||
@@ -5337,12 +5462,19 @@ void MainWindow::createAllcallTableRows(QTableWidget *table, QString const &sele
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
auto emptyItem = new QTableWidgetItem("");
|
||||
emptyItem->setData(Qt::UserRole, QVariant("@ALLCALL"));
|
||||
table->setItem(table->rowCount() - 1, 0, emptyItem);
|
||||
|
||||
auto item = new QTableWidgetItem(count == 0 ? QString("@ALLCALL") : QString("@ALLCALL (%1)").arg(count));
|
||||
item->setData(Qt::UserRole, QVariant("@ALLCALL"));
|
||||
table->setItem(table->rowCount() - 1, 0, item);
|
||||
table->setSpan(table->rowCount() - 1, 0, 1, table->columnCount());
|
||||
|
||||
table->setItem(table->rowCount() - 1, startCol, item);
|
||||
table->setSpan(table->rowCount() - 1, startCol, 1, table->columnCount());
|
||||
if(selectedCall == "@ALLCALL"){
|
||||
table->item(table->rowCount()-1, 0)->setSelected(true);
|
||||
table->item(table->rowCount()-1, startCol)->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5351,13 +5483,18 @@ void MainWindow::createAllcallTableRows(QTableWidget *table, QString const &sele
|
||||
foreach(auto group, groups){
|
||||
table->insertRow(table->rowCount());
|
||||
|
||||
auto emptyItem = new QTableWidgetItem("");
|
||||
emptyItem->setData(Qt::UserRole, QVariant(group));
|
||||
table->setItem(table->rowCount() - 1, 0, emptyItem);
|
||||
|
||||
auto item = new QTableWidgetItem(group);
|
||||
item->setData(Qt::UserRole, QVariant(group));
|
||||
table->setItem(table->rowCount() - 1, 0, item);
|
||||
table->setSpan(table->rowCount() - 1, 0, 1, table->columnCount());
|
||||
table->setItem(table->rowCount() - 1, startCol, item);
|
||||
table->setSpan(table->rowCount() - 1, startCol, 1, table->columnCount());
|
||||
|
||||
if(selectedCall == group){
|
||||
table->item(table->rowCount()-1, 0)->setSelected(true);
|
||||
table->item(table->rowCount()-1, startCol)->setSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5584,16 +5721,26 @@ bool MainWindow::ensureSelcalCallsignSelected(bool alert){
|
||||
|
||||
bool MainWindow::ensureKeyNotStuck(QString const& text){
|
||||
// be annoying and drop messages with all the same character to reduce spam...
|
||||
if(text.length() > 10 && QString(text).replace(text.at(0), "").isEmpty()){
|
||||
|
||||
MessageBox::warning_message(this, tr ("Please enter a message before trying to transmit"));
|
||||
|
||||
if(text.length() > 5 && QString(text).replace(text.at(0), "").trimmed().isEmpty()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainWindow::ensureNotIdle(){
|
||||
if (!m_config.watchdog()){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_idleMinutes < m_config.watchdog ()){
|
||||
return true;
|
||||
}
|
||||
|
||||
tx_watchdog (true); // disable transmit and auto replies
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::createMessage(QString const& text){
|
||||
if(!ensureCallsignSet()){
|
||||
on_stopTxButton_clicked();
|
||||
@@ -5605,8 +5752,24 @@ void MainWindow::createMessage(QString const& text){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ensureNotIdle()){
|
||||
on_stopTxButton_clicked();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ensureKeyNotStuck(text)){
|
||||
on_stopTxButton_clicked();
|
||||
|
||||
ui->monitorButton->setChecked(false);
|
||||
on_monitorButton_clicked(false);
|
||||
|
||||
foreach(auto obj, this->children()){
|
||||
if(obj->isWidgetType()){
|
||||
auto wid = qobject_cast<QWidget*>(obj);
|
||||
wid->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6066,7 +6229,6 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
|
||||
m_logBook.init();
|
||||
|
||||
if (m_config.clear_callsign ()){
|
||||
clearDX ();
|
||||
clearCallsignSelected();
|
||||
}
|
||||
|
||||
@@ -6572,6 +6734,7 @@ void MainWindow::on_clearAction_triggered(QObject * sender){
|
||||
m_bandActivity.clear();
|
||||
clearTableWidget(ui->tableWidgetRXAll);
|
||||
resetTimeDeltaAverage();
|
||||
displayBandActivity();
|
||||
}
|
||||
|
||||
// TODO: jsherer - abstract this into a tableWidgetCallsReset function
|
||||
@@ -6580,6 +6743,7 @@ void MainWindow::on_clearAction_triggered(QObject * sender){
|
||||
clearTableWidget((ui->tableWidgetCalls));
|
||||
createAllcallTableRows(ui->tableWidgetCalls, "");
|
||||
resetTimeDeltaAverage();
|
||||
displayCallActivity();
|
||||
}
|
||||
|
||||
if(sender == ui->extFreeTextMsgEdit){
|
||||
@@ -6596,16 +6760,6 @@ void MainWindow::on_clearAction_triggered(QObject * sender){
|
||||
}
|
||||
|
||||
void MainWindow::buildHeartbeatMenu(QMenu *menu){
|
||||
auto hide = menu->addAction("Show Heartbeats and ACKs");
|
||||
hide->setCheckable(true);
|
||||
hide->setChecked(!m_hbHidden);
|
||||
connect(hide, &QAction::triggered, this, [this](bool checked){
|
||||
m_hbHidden = !checked;
|
||||
displayBandActivity();
|
||||
});
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
buildRepeatMenu(menu, ui->hbMacroButton, &m_hbInterval);
|
||||
|
||||
menu->addSeparator();
|
||||
@@ -7079,8 +7233,7 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
||||
if(m_config.transmit_directed()) toggleTx(true);
|
||||
});
|
||||
|
||||
#if ALLOW_STATIONS_HEARD
|
||||
auto heardQueryAction = menu->addAction(QString("%1$ - What are the stations are you hearing? (Top 2 ranked by most recently heard)").arg(call).trimmed());
|
||||
auto heardQueryAction = menu->addAction(QString("%1 HEARING? - What are the stations are you hearing? (Top 4 ranked by most recently heard)").arg(call).trimmed());
|
||||
heardQueryAction->setDisabled(isAllCall);
|
||||
connect(heardQueryAction, &QAction::triggered, this, [this](){
|
||||
|
||||
@@ -7089,23 +7242,10 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
||||
return;
|
||||
}
|
||||
|
||||
addMessageText(QString("%1$").arg(selectedCall), true);
|
||||
addMessageText(QString("%1 HEARING?").arg(selectedCall), true);
|
||||
|
||||
if(m_config.transmit_directed()) toggleTx(true);
|
||||
});
|
||||
#endif
|
||||
|
||||
auto hashAction = menu->addAction(QString("%1#[MESSAGE] - Please ACK if you receive this message in its entirety").arg(call).trimmed());
|
||||
hashAction->setDisabled(isAllCall);
|
||||
connect(hashAction, &QAction::triggered, this, [this](){
|
||||
|
||||
QString selectedCall = callsignSelected();
|
||||
if(selectedCall.isEmpty()){
|
||||
return;
|
||||
}
|
||||
|
||||
addMessageText(QString("%1#[MESSAGE]").arg(selectedCall), true, true);
|
||||
});
|
||||
|
||||
#if 0
|
||||
auto retransmitAction = menu->addAction(QString("%1|[MESSAGE] - Please ACK and retransmit the following message").arg(call).trimmed());
|
||||
@@ -7121,7 +7261,7 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
||||
});
|
||||
#endif
|
||||
|
||||
auto alertAction = menu->addAction(QString("%1>[MESSAGE] - Please (optionally) relay and display this message in an reply dialog").arg(call).trimmed());
|
||||
auto alertAction = menu->addAction(QString("%1>[MESSAGE] - Please ACK, optionally relay, and display this message in an alert").arg(call).trimmed());
|
||||
alertAction->setDisabled(isAllCall);
|
||||
connect(alertAction, &QAction::triggered, this, [this](){
|
||||
|
||||
@@ -7263,19 +7403,6 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
|
||||
if(m_config.transmit_directed()) toggleTx(true);
|
||||
});
|
||||
|
||||
auto qrzAction = menu->addAction(QString("%1 QRZ? - Who is calling me?").arg(call).trimmed());
|
||||
connect(qrzAction, &QAction::triggered, this, [this](){
|
||||
|
||||
QString selectedCall = callsignSelected();
|
||||
if(selectedCall.isEmpty()){
|
||||
return;
|
||||
}
|
||||
|
||||
addMessageText(QString("%1 QRZ?").arg(selectedCall), true);
|
||||
|
||||
if(m_config.transmit_directed()) toggleTx(true);
|
||||
});
|
||||
|
||||
auto sevenThreeAction = menu->addAction(QString("%1 73 - I send my best regards").arg(call).trimmed());
|
||||
connect(sevenThreeAction, &QAction::triggered, this, [this](){
|
||||
|
||||
@@ -7522,7 +7649,15 @@ void MainWindow::on_tableWidgetCalls_cellDoubleClicked(int row, int col){
|
||||
|
||||
auto call = callsignSelected();
|
||||
|
||||
addMessageText(call);
|
||||
if(m_rxCallsignCommandQueue.contains(call) && !m_rxCallsignCommandQueue[call].isEmpty()){
|
||||
CommandDetail d = m_rxCallsignCommandQueue[call].first();
|
||||
m_rxCallsignCommandQueue[call].removeFirst();
|
||||
|
||||
processAlertReplyForCommand(d, d.relayPath, d.cmd);
|
||||
|
||||
} else {
|
||||
addMessageText(call);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_tableWidgetCalls_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected){
|
||||
@@ -7743,6 +7878,12 @@ void MainWindow::on_pbT2R_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_turboButton_clicked(){
|
||||
m_wideGraph->setTurbo(ui->turboButton->isChecked());
|
||||
m_txTextDirty = true;
|
||||
updateTextDisplay();
|
||||
}
|
||||
|
||||
void MainWindow::on_readFreq_clicked()
|
||||
{
|
||||
if (m_transmitting) return;
|
||||
@@ -8029,6 +8170,8 @@ void MainWindow::transmit (double snr)
|
||||
if(m_config.x2ToneSpacing()) toneSpacing=2*12000.0/1920.0;
|
||||
if(m_config.x4ToneSpacing()) toneSpacing=4*12000.0/1920.0;
|
||||
if(m_config.bFox() and !m_tune) toneSpacing=-1;
|
||||
if(TEST_FOX_WAVE_GEN && ui->turboButton->isChecked() && !m_tune) toneSpacing=-1;
|
||||
|
||||
Q_EMIT sendMessage (NUM_FT8_SYMBOLS,
|
||||
1920.0, ui->TxFreqSpinBox->value () - m_XIT,
|
||||
toneSpacing, m_soundOutput, m_config.audio_output_channel (),
|
||||
@@ -8252,11 +8395,7 @@ void MainWindow::transmitDisplay (bool transmitting)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: jsherer - encapsulate this in a function?
|
||||
/*
|
||||
ui->monitorButton->setVisible(!transmitting);
|
||||
ui->monitorTxButton->setVisible(transmitting);
|
||||
*/
|
||||
updateTxButtonDisplay();
|
||||
}
|
||||
|
||||
void MainWindow::on_sbFtol_valueChanged(int value)
|
||||
@@ -8604,10 +8743,14 @@ void MainWindow::refreshTextDisplay(){
|
||||
// ugh...i hate these globals
|
||||
m_txTextDirtyLastSelectedCall = callsignSelected(true);
|
||||
m_txTextDirtyLastText = text;
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
m_txFrameCountEstimate = ui->turboButton->isChecked() ? (int)ceil(float(frames)/TEST_FOX_WAVE_GEN_SLOTS) : frames;
|
||||
#else
|
||||
m_txFrameCountEstimate = frames;
|
||||
#endif
|
||||
m_txTextDirty = false;
|
||||
|
||||
updateTextStatsDisplay(transmitText, frames);
|
||||
updateTextStatsDisplay(transmitText, m_txFrameCountEstimate);
|
||||
updateTxButtonDisplay();
|
||||
|
||||
});
|
||||
@@ -8632,8 +8775,23 @@ void MainWindow::updateTxButtonDisplay(){
|
||||
// update transmit button
|
||||
if(m_tune || m_transmitting || m_txFrameCount > 0){
|
||||
int count = m_txFrameCount;
|
||||
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
if(ui->turboButton->isChecked()){
|
||||
count = qMax(1, (int)ceil(float(count)/TEST_FOX_WAVE_GEN_SLOTS));
|
||||
}
|
||||
|
||||
int left = m_txFrameQueue.count();
|
||||
if(ui->turboButton->isChecked()){
|
||||
left = (int)ceil(float(left)/TEST_FOX_WAVE_GEN_SLOTS);
|
||||
}
|
||||
int sent = qMax(1, count - left);
|
||||
ui->startTxButton->setText(m_tune ? "Tuning" : QString("%1 (%2/%3)").arg(ui->turboButton->isChecked() ? "Turbo" : "Send").arg(sent).arg(count));
|
||||
#else
|
||||
int sent = count - m_txFrameQueue.count();
|
||||
ui->startTxButton->setText(m_tune ? "Tuning" : QString("Sending (%1/%2)").arg(sent).arg(count));
|
||||
ui->startTxButton->setText(
|
||||
m_tune ? "Tuning" : QString("%1 (%2/%3)").arg(m_transmitting ? "Sending" : "Ready").arg(sent).arg(count));
|
||||
#endif
|
||||
ui->startTxButton->setEnabled(false);
|
||||
} else {
|
||||
ui->startTxButton->setText(m_txFrameCountEstimate <= 0 ? QString("Send") : QString("Send (%1)").arg(m_txFrameCountEstimate));
|
||||
@@ -8933,7 +9091,7 @@ void MainWindow::processCompoundActivity() {
|
||||
bits == Varicode::JS8Call ||
|
||||
((bits & Varicode::JS8CallFirst) == Varicode::JS8CallFirst) ||
|
||||
((bits & Varicode::JS8CallLast) == Varicode::JS8CallLast) ||
|
||||
((bits & Varicode::JS8CallData) == Varicode::JS8CallData)
|
||||
((bits & Varicode::JS8CallFlag) == Varicode::JS8CallFlag)
|
||||
);
|
||||
if (!validBits) {
|
||||
qDebug() << "-> buffer.cmd bits is invalid...skip";
|
||||
@@ -9277,11 +9435,10 @@ void MainWindow::processCommandActivity() {
|
||||
reply = QString("%1 QTC %2").arg(d.from).arg(replaceMacros(qtc, buildMacroValues(), true));
|
||||
}
|
||||
|
||||
#if ALLOW_STATIONS_HEARD
|
||||
// QUERIED STATIONS HEARD
|
||||
else if (d.cmd == "$" && !isAllCall) {
|
||||
else if (d.cmd == " HEARING?" && !isAllCall) {
|
||||
int i = 0;
|
||||
int maxStations = 2;
|
||||
int maxStations = 4;
|
||||
auto calls = m_callActivity.keys();
|
||||
qStableSort(calls.begin(), calls.end(), [this](QString
|
||||
const & a, QString
|
||||
@@ -9309,7 +9466,7 @@ void MainWindow::processCommandActivity() {
|
||||
continue;
|
||||
}
|
||||
|
||||
lines.append(QString("%1 %2 (%3)").arg(cd.call).arg(Varicode::formatSNR(cd.snr)).arg(since(cd.utcTimestamp)));
|
||||
lines.append(cd.call);
|
||||
|
||||
i++;
|
||||
}
|
||||
@@ -9317,16 +9474,6 @@ void MainWindow::processCommandActivity() {
|
||||
lines.prepend(QString("%1 HEARING").arg(d.from));
|
||||
reply = lines.join(' ');
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// PROCESS RETRANSMIT
|
||||
else if (d.cmd == "|" && !isAllCall) {
|
||||
// TODO: jsherer - perhaps parse d.text and ensure it is a valid message as well as prefix it with our call...
|
||||
|
||||
reply = QString("%1 ACK\n%2 DE %1").arg(d.from).arg(d.text);
|
||||
}
|
||||
#endif
|
||||
|
||||
// PROCESS RELAY
|
||||
else if (d.cmd == ">" && !isAllCall && !isGroupCall) {
|
||||
@@ -9349,7 +9496,7 @@ void MainWindow::processCommandActivity() {
|
||||
reply = QString("%1 DE %2").arg(text).arg(d.from);
|
||||
|
||||
// otherwise, as long as we're not an ACK...alert the user and either send an ACK or Message
|
||||
} else if(!d.text.startsWith("ACK DE")) {
|
||||
} else if(!d.text.startsWith("ACK")) {
|
||||
QStringList calls;
|
||||
QString callDePattern = {R"(\sDE\s(?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
|
||||
QRegularExpression re(callDePattern);
|
||||
@@ -9373,13 +9520,14 @@ void MainWindow::processCommandActivity() {
|
||||
|
||||
calls.prepend(d.from);
|
||||
|
||||
processAlertReplyForCommand(d, calls.join('>'), ">");
|
||||
}
|
||||
}
|
||||
auto relayPath = calls.join('>');
|
||||
|
||||
// PROCESS BUFFERED MESSAGE
|
||||
else if (d.cmd == "#" && !isAllCall) {
|
||||
reply = QString("%1 ACK").arg(d.from);
|
||||
reply = QString("%1 ACK").arg(relayPath);
|
||||
|
||||
// put message in inbox instead...
|
||||
d.relayPath = relayPath;
|
||||
m_rxCallsignCommandQueue[d.from].append(d);
|
||||
}
|
||||
}
|
||||
|
||||
// PROCESS AGN
|
||||
@@ -9555,19 +9703,15 @@ void MainWindow::processAlertReplyForCommand(CommandDetail d, QString from, QStr
|
||||
msgBox->setText(header);
|
||||
msgBox->setInformativeText(text);
|
||||
|
||||
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
|
||||
auto rb = msgBox->addButton("Reply", QMessageBox::AcceptRole);
|
||||
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
|
||||
|
||||
connect(msgBox, &QMessageBox::buttonClicked, this, [this, cmd, from, fromLabel, d, db, rb, ab](QAbstractButton * btn) {
|
||||
connect(msgBox, &QMessageBox::buttonClicked, this, [this, cmd, from, fromLabel, d, db, rb](QAbstractButton * btn) {
|
||||
if (btn == db) {
|
||||
displayCallActivity();
|
||||
return;
|
||||
}
|
||||
|
||||
if (btn == ab){
|
||||
enqueueMessage(PriorityHigh, QString("%1%2ACK").arg(from).arg(cmd), -1, nullptr);
|
||||
}
|
||||
|
||||
if(btn == rb){
|
||||
#if USE_RELAY_REPLY_DIALOG
|
||||
auto diag = new MessageReplyDialog(this);
|
||||
@@ -10038,6 +10182,16 @@ void MainWindow::displayCallActivity() {
|
||||
keys = listCopyReverse(keys);
|
||||
}
|
||||
|
||||
// pin messages to the top
|
||||
qStableSort(keys.begin(), keys.end(), [this](const QString left, QString right){
|
||||
int leftHas = (int)!(m_rxCallsignCommandQueue.contains(left) && !m_rxCallsignCommandQueue[left].isEmpty());
|
||||
int rightHas = (int)!(m_rxCallsignCommandQueue.contains(right) && !m_rxCallsignCommandQueue[right].isEmpty());
|
||||
|
||||
return leftHas < rightHas;
|
||||
});
|
||||
|
||||
bool showIconColumn = false;
|
||||
|
||||
int callsignAging = m_config.callsign_aging();
|
||||
foreach(QString call, keys) {
|
||||
if(call.trimmed().isEmpty()){
|
||||
@@ -10063,7 +10217,7 @@ void MainWindow::displayCallActivity() {
|
||||
QString displayCall = d.through.isEmpty() ? d.call : QString("%1>%2").arg(d.through).arg(d.call);
|
||||
#else
|
||||
// unicode star
|
||||
QString displayCall = d.ackTimestamp.isValid() ? QString("\u2605 %1").arg(d.call) : d.call;
|
||||
QString displayCall = d.call;
|
||||
#endif
|
||||
|
||||
QString flag;
|
||||
@@ -10072,6 +10226,17 @@ void MainWindow::displayCallActivity() {
|
||||
flag = "\u2713";
|
||||
}
|
||||
|
||||
// icon column (flag -> star -> empty)
|
||||
bool hasMessage = m_rxCallsignCommandQueue.contains(d.call) && !m_rxCallsignCommandQueue[d.call].isEmpty();
|
||||
bool hasAck = d.ackTimestamp.isValid();
|
||||
auto iconItem = new QTableWidgetItem(hasMessage ? "\u2691" : hasAck ? "\u2605" : "");
|
||||
iconItem->setData(Qt::UserRole, QVariant((d.call)));
|
||||
iconItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
ui->tableWidgetCalls->setItem(row, col++, iconItem);
|
||||
if(hasMessage || hasAck){
|
||||
showIconColumn = true;
|
||||
}
|
||||
|
||||
auto displayItem = new QTableWidgetItem(displayCall);
|
||||
displayItem->setData(Qt::UserRole, QVariant((d.call)));
|
||||
ui->tableWidgetCalls->setItem(row, col++, displayItem);
|
||||
@@ -10094,12 +10259,14 @@ void MainWindow::displayCallActivity() {
|
||||
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, col++, distanceItem);
|
||||
|
||||
} else {
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // age
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // snr
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // freq
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // tdrift
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // grid
|
||||
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // distance
|
||||
|
||||
//ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(""));
|
||||
}
|
||||
|
||||
if (isCallSelected) {
|
||||
@@ -10118,10 +10285,13 @@ void MainWindow::displayCallActivity() {
|
||||
|
||||
// Set item fonts
|
||||
for(int row = 0; row < ui->tableWidgetCalls->rowCount(); row++){
|
||||
auto bold = ui->tableWidgetCalls->item(row, 0)->text() == "\u2691";
|
||||
for(int col = 0; col < ui->tableWidgetCalls->columnCount(); col++){
|
||||
auto item = ui->tableWidgetCalls->item(row, col);
|
||||
if(item){
|
||||
item->setFont(m_config.table_font());
|
||||
auto f = m_config.table_font();
|
||||
f.setBold(bold);
|
||||
item->setFont(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10130,14 +10300,15 @@ void MainWindow::displayCallActivity() {
|
||||
ui->tableWidgetCalls->horizontalHeader()->setVisible(showColumn("call", "labels"));
|
||||
|
||||
// Hide columns
|
||||
ui->tableWidgetCalls->setColumnHidden(0, !showColumn("call", "callsign"));
|
||||
ui->tableWidgetCalls->setColumnHidden(1, !showColumn("call", "flag"));
|
||||
ui->tableWidgetCalls->setColumnHidden(2, !showColumn("call", "timestamp"));
|
||||
ui->tableWidgetCalls->setColumnHidden(3, !showColumn("call", "snr"));
|
||||
ui->tableWidgetCalls->setColumnHidden(4, !showColumn("call", "offset"));
|
||||
ui->tableWidgetCalls->setColumnHidden(5, !showColumn("call", "tdrift", false));
|
||||
ui->tableWidgetCalls->setColumnHidden(6, !showColumn("call", "grid", false));
|
||||
ui->tableWidgetCalls->setColumnHidden(7, !showColumn("call", "distance", false));
|
||||
ui->tableWidgetCalls->setColumnHidden(0, !showIconColumn);
|
||||
ui->tableWidgetCalls->setColumnHidden(1, !showColumn("call", "callsign"));
|
||||
ui->tableWidgetCalls->setColumnHidden(2, !showColumn("call", "flag"));
|
||||
ui->tableWidgetCalls->setColumnHidden(3, !showColumn("call", "timestamp"));
|
||||
ui->tableWidgetCalls->setColumnHidden(4, !showColumn("call", "snr"));
|
||||
ui->tableWidgetCalls->setColumnHidden(5, !showColumn("call", "offset"));
|
||||
ui->tableWidgetCalls->setColumnHidden(6, !showColumn("call", "tdrift", false));
|
||||
ui->tableWidgetCalls->setColumnHidden(7, !showColumn("call", "grid", false));
|
||||
ui->tableWidgetCalls->setColumnHidden(8, !showColumn("call", "distance", false));
|
||||
|
||||
// Resize the table columns
|
||||
ui->tableWidgetCalls->resizeColumnToContents(0);
|
||||
@@ -10147,6 +10318,7 @@ void MainWindow::displayCallActivity() {
|
||||
ui->tableWidgetCalls->resizeColumnToContents(4);
|
||||
ui->tableWidgetCalls->resizeColumnToContents(5);
|
||||
ui->tableWidgetCalls->resizeColumnToContents(6);
|
||||
ui->tableWidgetCalls->resizeColumnToContents(7);
|
||||
|
||||
// Reset the scroll position
|
||||
ui->tableWidgetCalls->verticalScrollBar()->setValue(currentScrollPos);
|
||||
@@ -10854,13 +11026,28 @@ void MainWindow::tx_watchdog (bool triggered)
|
||||
if (m_tune) stop_tuning ();
|
||||
if (m_auto) auto_tx_mode (false);
|
||||
stopTx();
|
||||
tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}");
|
||||
tx_status_label.setStyleSheet ("QLabel{background-color: #000000; color:#ffffff; }");
|
||||
tx_status_label.setText ("Inactive watchdog");
|
||||
|
||||
// if the watchdog is triggered...we're no longer active
|
||||
bool wasAuto = ui->autoReplyButton->isChecked();
|
||||
bool wasActive = ui->activeButton->isChecked();
|
||||
bool wasHB = ui->hbMacroButton->isChecked();
|
||||
bool wasCQ = ui->cqMacroButton->isChecked();
|
||||
|
||||
// save the button states
|
||||
ui->autoReplyButton->setChecked(false);
|
||||
ui->activeButton->setChecked(false);
|
||||
ui->hbMacroButton->setChecked(false);
|
||||
ui->cqMacroButton->setChecked(false);
|
||||
|
||||
MessageBox::warning_message(this, QString("Attempting to transmit, but you have been inactive for more than %1 minutes.").arg(m_config.watchdog()));
|
||||
|
||||
// restore the button states
|
||||
ui->autoReplyButton->setChecked(wasAuto);
|
||||
ui->activeButton->setChecked(wasActive);
|
||||
ui->hbMacroButton->setChecked(wasHB);
|
||||
ui->cqMacroButton->setChecked(wasCQ);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -153,6 +153,7 @@ public slots:
|
||||
bool ensureCallsignSet(bool alert=true);
|
||||
bool ensureSelcalCallsignSelected(bool alert=true);
|
||||
bool ensureKeyNotStuck(QString const& text);
|
||||
bool ensureNotIdle();
|
||||
void createMessage(QString const& text);
|
||||
void createMessageTransmitQueue(QString const& text);
|
||||
void resetMessageTransmitQueue();
|
||||
@@ -180,6 +181,7 @@ private slots:
|
||||
void on_actionShow_Fullscreen_triggered(bool checked);
|
||||
void on_actionShow_Frequency_Clock_triggered(bool checked);
|
||||
void on_actionShow_Band_Activity_triggered(bool checked);
|
||||
void on_actionShow_Band_Heartbeats_and_ACKs_triggered(bool checked);
|
||||
void on_actionShow_Call_Activity_triggered(bool checked);
|
||||
void on_actionShow_Waterfall_triggered(bool checked);
|
||||
void on_actionShow_Waterfall_Controls_triggered(bool checked);
|
||||
@@ -331,6 +333,7 @@ private slots:
|
||||
void on_tuneButton_clicked (bool);
|
||||
void on_pbR2T_clicked();
|
||||
void on_pbT2R_clicked();
|
||||
void on_turboButton_clicked();
|
||||
void acceptQSO (QDateTime const&, QString const& call, QString const& grid
|
||||
, Frequency dial_freq, QString const& mode, QString const& submode
|
||||
, QString const& rpt_sent, QString const& rpt_received
|
||||
@@ -726,6 +729,7 @@ private:
|
||||
QString text;
|
||||
QString extra;
|
||||
float tdrift;
|
||||
QString relayPath;
|
||||
};
|
||||
|
||||
struct ActivityDetail
|
||||
@@ -750,6 +754,10 @@ private:
|
||||
QList<ActivityDetail> msgs;
|
||||
};
|
||||
|
||||
int m_bandActivityWidth;
|
||||
int m_callActivityWidth;
|
||||
int m_textActivityWidth;
|
||||
int m_waterfallHeight;
|
||||
bool m_bandActivityWasVisible;
|
||||
bool m_rxDirty;
|
||||
bool m_rxDisplayDirty;
|
||||
@@ -809,6 +817,8 @@ private:
|
||||
QQueue<QString> m_txHeartbeatQueue; // ping frames to be sent
|
||||
QMap<QString, QDateTime> m_aprsCallCache;
|
||||
|
||||
QMap<QString, QList<CommandDetail>> m_rxCallsignCommandQueue; // call -> [cmd, ...]
|
||||
|
||||
QMap<QString, QMap<QString, CallDetail>> m_callActivityCache; // band -> call activity
|
||||
QMap<QString, QMap<int, QList<ActivityDetail>>> m_bandActivityCache; // band -> band activity
|
||||
QMap<QString, QString> m_rxTextCache; // band -> rx text
|
||||
|
||||
+65
-2
@@ -476,7 +476,7 @@ QPushButton:checked {
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8" columnstretch="1,1,1,1" columnminimumwidth="75,75,75,75">
|
||||
<layout class="QGridLayout" name="gridLayout_8" columnstretch="1,1,1,1,0" columnminimumwidth="75,75,75,75,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
@@ -970,6 +970,52 @@ QPushButton:checked {
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="4">
|
||||
<widget class="QPushButton" name="turboButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>30</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QPushButton {
|
||||
background-color:lightgray;
|
||||
padding:0.25em 0.25em; font-weight:normal;
|
||||
border-style:solid;
|
||||
border-width:0px;
|
||||
border-radius:2px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
background-color:#6699ff;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TURBO</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1224,6 +1270,14 @@ QTextEdit[transmitting="true"] {
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>★</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Callsign</string>
|
||||
@@ -1321,7 +1375,7 @@ background-color: #00ff00;
|
||||
color:#555;
|
||||
}
|
||||
QPushButton[transmitting="true"]{
|
||||
background:yellow;
|
||||
background:#ff0000;
|
||||
color:#555;
|
||||
}</string>
|
||||
</property>
|
||||
@@ -4700,6 +4754,7 @@ list. The list can be maintained in Settings (F2).</string>
|
||||
<addaction name="actionShow_Frequency_Clock"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionShow_Band_Activity"/>
|
||||
<addaction name="actionShow_Band_Heartbeats_and_ACKs"/>
|
||||
<addaction name="actionShow_Band_Activity_Columns"/>
|
||||
<addaction name="actionSort_Band_Activity"/>
|
||||
<addaction name="separator"/>
|
||||
@@ -5600,6 +5655,14 @@ list. The list can be maintained in Settings (F2).</string>
|
||||
<string>CQ...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionShow_Band_Heartbeats_and_ACKs">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Band Heartbeats and ACKs</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
||||
+31
@@ -27,6 +27,7 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor
|
||||
m_plot2dGain {0},
|
||||
m_plot2dZero {0},
|
||||
m_nSubMode {0},
|
||||
m_turbo {false},
|
||||
m_Running {false},
|
||||
m_paintEventBusy {false},
|
||||
m_fftBinWidth {1500.0/2048.0},
|
||||
@@ -587,6 +588,9 @@ void CPlotter::DrawOverlay() //DrawOverlay()
|
||||
|
||||
if(m_mode=="FT8"){
|
||||
int fwidth=XfromFreq(m_rxFreq+bw)-XfromFreq(m_rxFreq);
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
int offset=XfromFreq(m_rxFreq+bw+10)-XfromFreq(m_rxFreq+bw);
|
||||
#endif
|
||||
QPainter overPainter(&m_DialOverlayPixmap);
|
||||
overPainter.initFrom(this);
|
||||
overPainter.setCompositionMode(QPainter::CompositionMode_Source);
|
||||
@@ -595,10 +599,22 @@ void CPlotter::DrawOverlay() //DrawOverlay()
|
||||
overPainter.setPen(thinRed);
|
||||
overPainter.drawLine(0, 30, 0, m_h);
|
||||
overPainter.drawLine(fwidth+1, 30, fwidth+1, m_h);
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
if(m_turbo){
|
||||
overPainter.drawLine(offset+fwidth+1, 30, offset+fwidth+1, m_h);
|
||||
overPainter.drawLine(offset+2*fwidth+1, 30, offset+2*fwidth+1, m_h);
|
||||
}
|
||||
#endif
|
||||
|
||||
overPainter.setPen(penRed);
|
||||
overPainter.drawLine(0, 26, fwidth, 26);
|
||||
overPainter.drawLine(0, 28, fwidth, 28);
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
if(m_turbo){
|
||||
overPainter.drawLine(offset+fwidth, 26, offset+2*fwidth, 26);
|
||||
overPainter.drawLine(offset+fwidth, 28, offset+2*fwidth, 28);
|
||||
}
|
||||
#endif
|
||||
|
||||
QPainter hoverPainter(&m_HoverOverlayPixmap);
|
||||
hoverPainter.initFrom(this);
|
||||
@@ -607,6 +623,12 @@ void CPlotter::DrawOverlay() //DrawOverlay()
|
||||
hoverPainter.setPen(QPen(Qt::white));
|
||||
hoverPainter.drawLine(0, 30, 0, m_h);
|
||||
hoverPainter.drawLine(fwidth+1, 30, fwidth+1, m_h);
|
||||
#if TEST_FOX_WAVE_GEN
|
||||
if(m_turbo){
|
||||
hoverPainter.drawLine(offset+fwidth+1, 30, offset+fwidth+1, m_h);
|
||||
hoverPainter.drawLine(offset+2*fwidth+1, 30, offset+2*fwidth+1, m_h);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DRAW_FREQ_OVERLAY
|
||||
int f = FreqfromX(m_lastMouseX);
|
||||
@@ -865,6 +887,15 @@ void CPlotter::setDialFreq(double d)
|
||||
void CPlotter::setRxBand(QString band)
|
||||
{
|
||||
m_rxBand=band;
|
||||
DrawOverlay();
|
||||
update();
|
||||
}
|
||||
|
||||
void CPlotter::setTurbo(bool turbo)
|
||||
{
|
||||
m_turbo=turbo;
|
||||
DrawOverlay();
|
||||
update();
|
||||
}
|
||||
|
||||
void CPlotter::setFlatten(bool b1, bool b2)
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
void setFlatten(bool b1, bool b2);
|
||||
void setTol(int n);
|
||||
void setRxBand(QString band);
|
||||
void setTurbo(bool turbo);
|
||||
void setReference(bool b) {m_bReference = b;}
|
||||
bool Reference() const {return m_bReference;}
|
||||
void drawRed(int ia, int ib, float swide[]);
|
||||
@@ -147,6 +148,7 @@ private:
|
||||
QString m_rxBand;
|
||||
QString m_redFile;
|
||||
|
||||
bool m_turbo;
|
||||
bool m_Running;
|
||||
bool m_paintEventBusy;
|
||||
bool m_dataFromDisk;
|
||||
|
||||
+58
-43
@@ -55,7 +55,8 @@ QMap<QString, int> directed_cmds = {
|
||||
{" QTC?", 2 }, // query station message
|
||||
{"&", 2 }, // compat
|
||||
|
||||
//{"$", 3 }, // unused
|
||||
{" HEARING?", 3 }, // query station calls heard
|
||||
{"$", 3 }, // compat
|
||||
|
||||
{" GRID?", 4 }, // query grid
|
||||
{"^", 4 }, // compat
|
||||
@@ -66,8 +67,7 @@ QMap<QString, int> directed_cmds = {
|
||||
{"*", 6 }, // compat
|
||||
|
||||
//{"!", 7 }, // unused
|
||||
|
||||
{"#", 8 }, // all or nothing message
|
||||
//{"#", 8 }, // unused
|
||||
|
||||
{" TU", 9 }, // thank you
|
||||
|
||||
@@ -75,7 +75,8 @@ QMap<QString, int> directed_cmds = {
|
||||
{" IDLE", 11 }, // i am idle
|
||||
|
||||
{" HB", -1 }, // this is my heartbeat (unused except for faux processing of HBs as directed commands)
|
||||
{" HB ACK", 12 }, // (unused, but a compatibility display)
|
||||
|
||||
// {" ", 12 }, // unused
|
||||
|
||||
{" QUERY", 13 }, // can you transmit a ping to callsign?
|
||||
|
||||
@@ -91,32 +92,32 @@ QMap<QString, int> directed_cmds = {
|
||||
{" RR", 21 }, // roger roger
|
||||
{" QSL?", 22 }, // do you copy?
|
||||
{" QSL", 23 }, // i copy
|
||||
{" QRZ?", 24 }, // who is calling me
|
||||
// {" ", 24 }, // unused
|
||||
{" SNR", 25 }, // seen a station at the provided snr
|
||||
{" NO", 26 }, // negative confirm
|
||||
{" YES", 27 }, // confirm
|
||||
{" 73", 28 }, // best regards, end of contact
|
||||
{" ACK", 29 }, // acknowledge
|
||||
{" AGN?", 30 }, // repeat message
|
||||
{" ", 31 }, // send freetext
|
||||
{" ", 31 }, // send freetext (weird artifact)
|
||||
{" ", 31 }, // send freetext
|
||||
};
|
||||
|
||||
QSet<int> allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, 6, /*7,*/ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
||||
QSet<int> allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, 6, /*7,*/ /*8,*/ 9, 10, 11, /*12,*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
|
||||
|
||||
QSet<int> buffered_cmds = {3, 5, /*6,*/ /*7,*/ 8, 13, 14, 15};
|
||||
QSet<int> buffered_cmds = {3, 5, /*6,*/ /*7,*/ 13, 14, 15};
|
||||
|
||||
QSet<int> snr_cmds = {25, 29};
|
||||
|
||||
QMap<int, int> checksum_cmds = {
|
||||
{ 5, 16 },
|
||||
{ 8, 16 },
|
||||
{ 13, 16 },
|
||||
{ 14, 16 },
|
||||
{ 15, 0 }
|
||||
};
|
||||
|
||||
QString callsign_pattern = QString("(?<callsign>[@]?[A-Z0-9/]+)");
|
||||
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|QRZ[?]|SNR[?]|QTC[?]|QTH[?]|GRID[?]|STATUS[?]|(?:(?:QUERY|ACK|73|YES|NO|SNR|QSL|RR|SK|FB|QTH|QTC|GRID|ACTIVE|IDLE|TU)(?=[ ]|$))|[?*^&@#> ]))?");
|
||||
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|SNR[?]|QTC[?]|QTH[?]|GRID[?]|STATUS[?]|HEARING[?]|(?:(?:QUERY|ACK|73|YES|NO|SNR|QSL|RR|SK|FB|QTH|QTC|GRID|ACTIVE|IDLE|TU)(?=[ ]|$))|[?*^&@$> ]))?");
|
||||
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
|
||||
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
|
||||
QString optional_num_pattern = QString("(?<num>(?<=SNR|ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
|
||||
@@ -1183,7 +1184,7 @@ QString Varicode::packHeartbeatMessage(QString const &text, const QString &calls
|
||||
cqNumber = cqs.key(type, 0);
|
||||
}
|
||||
|
||||
frame = packCompoundFrame(callsign, FrameHeartbeat, packed_extra, cqNumber);
|
||||
frame = packCompoundFrame(callsign, Varicode::FrameHeartbeat, packed_extra, cqNumber);
|
||||
if(frame.isEmpty()){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
@@ -1194,12 +1195,12 @@ QString Varicode::packHeartbeatMessage(QString const &text, const QString &calls
|
||||
}
|
||||
|
||||
QStringList Varicode::unpackHeartbeatMessage(const QString &text, quint8 *pType, bool * isAlt, quint8 * pBits3){
|
||||
quint8 type = FrameHeartbeat;
|
||||
quint8 type = Varicode::FrameHeartbeat;
|
||||
quint16 num = nmaxgrid;
|
||||
quint8 bits3 = 0;
|
||||
|
||||
QStringList unpacked = unpackCompoundFrame(text, &type, &num, &bits3);
|
||||
if(unpacked.isEmpty() || type != FrameHeartbeat){
|
||||
if(unpacked.isEmpty() || type != Varicode::FrameHeartbeat){
|
||||
return QStringList{};
|
||||
}
|
||||
|
||||
@@ -1212,7 +1213,6 @@ QStringList Varicode::unpackHeartbeatMessage(const QString &text, quint8 *pType,
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
|
||||
// KN4CRD/XXXX EM73
|
||||
// XXXX/KN4CRD EM73
|
||||
// KN4CRD/P
|
||||
@@ -1239,7 +1239,7 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
|
||||
return frame;
|
||||
}
|
||||
|
||||
quint8 type = FrameCompound;
|
||||
quint8 type = Varicode::FrameCompound;
|
||||
quint16 extra = nmaxgrid;
|
||||
|
||||
qDebug() << "try pack cmd" << cmd << directed_cmds.contains(cmd) << Varicode::isCommandAllowed(cmd);
|
||||
@@ -1249,7 +1249,7 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
|
||||
qint8 inum = Varicode::packNum(num, nullptr);
|
||||
extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum, &packedNum);
|
||||
|
||||
type = FrameCompoundDirected;
|
||||
type = Varicode::FrameCompoundDirected;
|
||||
} else if(!grid.isEmpty()){
|
||||
extra = Varicode::packGrid(grid);
|
||||
}
|
||||
@@ -1261,12 +1261,12 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
|
||||
}
|
||||
|
||||
QStringList Varicode::unpackCompoundMessage(const QString &text, quint8 *pType, quint8 *pBits3){
|
||||
quint8 type = FrameCompound;
|
||||
quint8 type = Varicode::FrameCompound;
|
||||
quint16 extra = nmaxgrid;
|
||||
quint8 bits3 = 0;
|
||||
|
||||
QStringList unpacked = unpackCompoundFrame(text, &type, &extra, &bits3);
|
||||
if(unpacked.isEmpty() || (type != FrameCompound && type != FrameCompoundDirected)){
|
||||
if(unpacked.isEmpty() || (type != Varicode::FrameCompound && type != Varicode::FrameCompoundDirected)){
|
||||
return QStringList {};
|
||||
}
|
||||
|
||||
@@ -1294,7 +1294,7 @@ QString Varicode::packCompoundFrame(const QString &callsign, quint8 type, quint1
|
||||
QString frame;
|
||||
|
||||
// needs to be a compound type...
|
||||
if(type == FrameDataCompressed || type == FrameDataUncompressed || type == FrameDirected){
|
||||
if(type == Varicode::FrameData || type == Varicode::FrameDirected){
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -1338,7 +1338,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu
|
||||
quint8 packed_flag = Varicode::bitsToInt(bits.mid(0, 3));
|
||||
|
||||
// needs to be a ping type...
|
||||
if(packed_flag == FrameDataCompressed || packed_flag == FrameDataUncompressed || packed_flag == FrameDirected){
|
||||
if(packed_flag == Varicode::FrameData || packed_flag == Varicode::FrameDirected){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
@@ -1439,7 +1439,7 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &mycall
|
||||
cmdOut = cmd.trimmed();
|
||||
packed_cmd = directed_cmds[cmdOut];
|
||||
}
|
||||
quint8 packed_flag = FrameDirected;
|
||||
quint8 packed_flag = Varicode::FrameDirected;
|
||||
quint8 packed_extra = inum;
|
||||
|
||||
// [3][28][28][5],[2][6] = 72
|
||||
@@ -1467,7 +1467,7 @@ QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){
|
||||
auto bits = Varicode::intToBits(Varicode::unpack72bits(text, &extra), 64);
|
||||
|
||||
quint8 packed_flag = Varicode::bitsToInt(bits.mid(0, 3));
|
||||
if(packed_flag != FrameDirected){
|
||||
if(packed_flag != Varicode::FrameDirected){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
@@ -1500,8 +1500,12 @@ QString packHuffMessage(const QString &input, int *n){
|
||||
|
||||
QString frame;
|
||||
|
||||
// [1][71] = 72
|
||||
QVector<bool> frameBits = {false};
|
||||
// [1][1][70] = 72
|
||||
// The first bit is a flag that indicates this is a data frame, technically encoded as [100]
|
||||
// but, since none of the other frame types start with a 0, we can drop the two zeros and use
|
||||
// them for encoding the first two bits of the actuall data sent. boom!
|
||||
// The second bit is a flag that indicates this is not compressed frame (huffman coding)
|
||||
QVector<bool> frameBits = {true, false};
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -1554,8 +1558,12 @@ QString packCompressedMessage(const QString &input, int *n){
|
||||
|
||||
QString frame;
|
||||
|
||||
// [1][71] = 72
|
||||
QVector<bool> frameBits = {true};
|
||||
// [1][1][70] = 72
|
||||
// The first bit is a flag that indicates this is a data frame, technically encoded as [100]
|
||||
// but, since none of the other frame types start with a 1, we can drop the two zeros and use
|
||||
// them for encoding the first two bits of the actuall data sent. boom!
|
||||
// The second bit is a flag that indicates this is a compressed frame (dense coding)
|
||||
QVector<bool> frameBits = {true, true};
|
||||
|
||||
int i = 0;
|
||||
foreach(auto pair, JSC::compress(input)){
|
||||
@@ -1593,24 +1601,25 @@ QString packCompressedMessage(const QString &input, int *n){
|
||||
}
|
||||
|
||||
QString Varicode::packDataMessage(const QString &input, int *n){
|
||||
QString huffFrame;
|
||||
int huffChars = 0;
|
||||
huffFrame = packHuffMessage(input, &huffChars);
|
||||
QString huffFrame;
|
||||
int huffChars = 0;
|
||||
huffFrame = packHuffMessage(input, &huffChars);
|
||||
|
||||
QString compressedFrame;
|
||||
int compressedChars = 0;
|
||||
compressedFrame = packCompressedMessage(input, &compressedChars);
|
||||
QString compressedFrame;
|
||||
int compressedChars = 0;
|
||||
compressedFrame = packCompressedMessage(input, &compressedChars);
|
||||
|
||||
if(huffChars > compressedChars){
|
||||
if(n) *n = huffChars;
|
||||
return huffFrame;
|
||||
} else {
|
||||
if(n) *n = compressedChars;
|
||||
return compressedFrame;
|
||||
}
|
||||
if(huffChars > compressedChars){
|
||||
if(n) *n = huffChars;
|
||||
return huffFrame;
|
||||
} else {
|
||||
if(n) *n = compressedChars;
|
||||
return compressedFrame;
|
||||
}
|
||||
}
|
||||
|
||||
QString Varicode::unpackDataMessage(const QString &text, quint8 *pType){
|
||||
|
||||
QString Varicode::unpackDataMessage(const QString &text){
|
||||
QString unpacked;
|
||||
|
||||
if(text.length() < 12 || text.contains(" ")){
|
||||
@@ -1621,18 +1630,24 @@ QString Varicode::unpackDataMessage(const QString &text, quint8 *pType){
|
||||
quint64 value = Varicode::unpack72bits(text, &rem);
|
||||
auto bits = Varicode::intToBits(value, 64) + Varicode::intToBits(rem, 8);
|
||||
|
||||
bool isData = bits.at(0);
|
||||
if(!isData){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
bits = bits.mid(1);
|
||||
|
||||
bool compressed = bits.at(0);
|
||||
|
||||
int n = bits.lastIndexOf(0);
|
||||
bits = bits.mid(1, n-1);
|
||||
|
||||
if(compressed){
|
||||
// partial word (s,c)-dense coding with code tables
|
||||
unpacked = JSC::decompress(bits);
|
||||
if(pType) *pType = Varicode::FrameDataCompressed;
|
||||
} else {
|
||||
// huff decode the bits (without escapes)
|
||||
unpacked = Varicode::huffDecode(Varicode::defaultHuffTable(), bits);
|
||||
if(pType) *pType = Varicode::FrameDataUncompressed;
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
@@ -1852,7 +1867,7 @@ QList<QPair<QString, int>> Varicode::buildMessageFrames(
|
||||
}
|
||||
|
||||
if(useDat){
|
||||
frames.append({ frame, Varicode::JS8CallData });
|
||||
frames.append({ frame, Varicode::JS8Call });
|
||||
line = line.mid(m);
|
||||
}
|
||||
}
|
||||
|
||||
+15
-10
@@ -21,22 +21,28 @@ public:
|
||||
JS8Call = 0, // [000] <- any other frame of the message
|
||||
JS8CallFirst = 1, // [001] <- the first frame of a message
|
||||
JS8CallLast = 2, // [010] <- the last frame of a message
|
||||
JS8CallData = 4, // [100] <- raw data frame (no frame type header)
|
||||
JS8CallFlag = 4, // [100] <- flagged frame (no frame type header)
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
000 = heartbeat
|
||||
001 = compound
|
||||
010 = compound directed
|
||||
011 = directed
|
||||
1XX = data, with the X lsb bits dropped
|
||||
*/
|
||||
enum FrameType {
|
||||
FrameUnknown = 255, // [11111111] <- only used as a sentinel
|
||||
FrameHeartbeat = 0, // [000]
|
||||
FrameCompound = 1, // [001]
|
||||
FrameCompoundDirected = 2, // [010]
|
||||
FrameDirected = 3, // [011]
|
||||
FrameReservedA = 4, // [100] <- Reserved for future use, likely an extension of one of these formats.
|
||||
FrameDataUncompressed = 5, // [101]
|
||||
FrameDataCompressed = 6, // [110]
|
||||
FrameReservedB = 7, // [111] <- Reserved for future use, likely binary data / other formats.
|
||||
FrameData = 4, // [10X] // but this only encodes the first 2 msb bits and drops the lsb
|
||||
FrameDataCompressed = 6, // [11X] // but this only encodes the first 2 msb bits and drops the lsb
|
||||
};
|
||||
|
||||
static const quint8 FrameTypeMax = 7;
|
||||
static const quint8 FrameTypeMax = 6;
|
||||
|
||||
static QString frameTypeString(quint8 type) {
|
||||
const char* FrameTypeStrings[] = {
|
||||
@@ -44,10 +50,9 @@ public:
|
||||
"FrameCompound",
|
||||
"FrameCompoundDirected",
|
||||
"FrameDirected",
|
||||
"FrameReservedA",
|
||||
"FrameDataUncompressed",
|
||||
"FrameData",
|
||||
"FrameUnknown", // 5
|
||||
"FrameDataCompressed",
|
||||
"FrameReservedB"
|
||||
};
|
||||
|
||||
if(type > FrameTypeMax){
|
||||
@@ -148,7 +153,7 @@ public:
|
||||
static QStringList unpackDirectedMessage(QString const& text, quint8 *pType);
|
||||
|
||||
static QString packDataMessage(QString const& text, int *n);
|
||||
static QString unpackDataMessage(QString const& text, quint8 *pType);
|
||||
static QString unpackDataMessage(QString const& text);
|
||||
|
||||
static QList<QPair<QString, int>> buildMessageFrames(
|
||||
QString const& mycall,
|
||||
|
||||
@@ -566,3 +566,7 @@ void WideGraph::setRedFile(QString fRed)
|
||||
{
|
||||
ui->widePlot->setRedFile(fRed);
|
||||
}
|
||||
|
||||
void WideGraph::setTurbo(bool turbo){
|
||||
ui->widePlot->setTurbo(turbo);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
void drawRed(int ia, int ib);
|
||||
void setVHF(bool bVHF);
|
||||
void setRedFile(QString fRed);
|
||||
void setTurbo(bool turbo);
|
||||
|
||||
signals:
|
||||
void freezeDecode2(int n);
|
||||
|
||||
Reference in New Issue
Block a user