Compare commits

..

40 Commits

Author SHA1 Message Date
Jordan Sherer 17416144a8 Added relay message storage and display in the call activity list 2018-12-18 21:28:31 -05:00
Jordan Sherer b8ff10fde9 Updated about messaging 2018-12-17 01:46:57 -05:00
Jordan Sherer 6c741e21b5 Removed hash messages '#' as they are now duplicated by relay '>' 2018-12-17 01:33:22 -05:00
Jordan Sherer 47eec2dc0b Added automatic ACK for relay messages 2018-12-17 01:27:41 -05:00
Jordan Sherer 6504a1f220 Bump eol and versions to 0.11.0 2018-12-17 01:04:43 -05:00
Jordan Sherer 91a2a801f9 Fixed heartbeat sub-channel configuration 2018-12-17 01:03:07 -05:00
Jordan Sherer 8c74499700 Removed unused commands 2018-12-17 00:54:54 -05:00
Jordan Sherer 51c9dd2761 Removed QRZ short message 2018-12-17 00:45:44 -05:00
Jordan Sherer dc70d53f5c Added HEARING query back into the app 2018-12-17 00:38:34 -05:00
Jordan Sherer 3c2a5f98ec Fixed #16: do not symlink js8call in usr if not using the opt install prefix 2018-12-14 21:23:06 -05:00
Jordan Sherer 5c1f890095 Fixed #13: configuration validator should require cq or callsign in cq message 2018-12-14 20:16:33 -05:00
Jordan Sherer 29f759dafe Fixed #23: idle watchdog should prevent all types of automated transmissions 2018-12-14 20:02:46 -05:00
Jordan Sherer 90858f8964 Fixed #43: space prefix should not break directed call 2018-12-13 22:22:10 -05:00
Jordan Sherer e6464e952d Removed comments 2018-12-13 22:21:37 -05:00
Jordan Sherer 186fed04e3 Fixed #41: remember pane sizes when showing/hiding 2018-12-13 09:50:02 -05:00
Jordan Sherer 6716eb771e Changed send button color while sending and label when ready 2018-12-12 22:14:45 -05:00
Jordan Sherer b8b2cf33c3 Fixed defines and % in the send button 2018-12-11 23:52:01 -05:00
Jordan Sherer 4025edfc0f Fixed rebase issues 2018-12-11 22:23:07 -05:00
Jordan Sherer c5930a0aa3 Fixed rebase issues 2018-12-11 22:14:26 -05:00
Jordan Sherer 9079d45587 Rename JS8CallExtended to JS8CallFlag 2018-12-10 23:37:22 -05:00
Jordan Sherer 223a4a2183 Updated comments to be more clear 2018-12-10 23:37:22 -05:00
Jordan Sherer 1584cf0404 Turn off turbo for now...still experimental 2018-12-10 23:37:22 -05:00
Jordan Sherer 5619824bd8 Hide turbo button for now 2018-12-10 23:37:22 -05:00
Jordan Sherer e1e2ae50e8 Simplify conditional 2018-12-10 23:35:58 -05:00
Jordan Sherer cc96daa26f Fixed button alignment 2018-12-10 23:35:58 -05:00
Jordan Sherer 5ba31d6df7 Changed frame packing so we can use one of the ibits as a turbo flag 2018-12-10 23:33:50 -05:00
Jordan Sherer 73346240e5 Fixed frame counting during turbo transmission 2018-12-10 23:30:36 -05:00
Jordan Sherer 886e678531 Fixed send button counts while in turbo 2018-12-10 23:27:21 -05:00
Jordan Sherer 526b72022e Added turbo button to the UI 2018-12-10 23:27:21 -05:00
Jordan Sherer a1864fa3f0 Configurable slots 2018-12-10 23:23:31 -05:00
Jordan Sherer b0c57029e8 Experiment with compression 2018-12-10 23:18:19 -05:00
Jordan Sherer 2d5b41d4b7 Added a waterfall indicator for turbo'd signals 2018-12-10 23:18:19 -05:00
Jordan Sherer 21004a7b28 Two-frame turbo for any message 2018-12-10 23:18:19 -05:00
Jordan Sherer 94f2f510fc Initial proof of concept of turbo button 2018-12-10 23:18:19 -05:00
Jordan Sherer b6745c9e2e Fixed #40: Force uppercase without resetting text 2018-12-10 10:18:45 -05:00
Jordan Sherer 8c81b3b83a Fixed #38: Moved Show Heartbeats menu item to the View menu 2018-12-10 09:44:16 -05:00
Jordan Sherer 9ddfec5e72 Fixed #28: band activity clear should not deselect callsign selected in call activity 2018-12-04 22:20:26 -05:00
Jordan Sherer a5f0937cbb Fixed #23: Log window should not halt transmission 2018-12-04 22:10:52 -05:00
Jordan Sherer 26f21cd70a Fixed issue with repeat messages 2018-12-03 22:42:19 -05:00
Jordan Sherer bb7e2544b5 Updated README with new links and history line items 2018-12-02 23:03:42 -05:00
17 changed files with 591 additions and 270 deletions
+7 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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"/>
+7 -1
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
{
+10
View File
@@ -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
View File
@@ -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=&quot;true&quot;] {
<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=&quot;true&quot;]{
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
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+4
View File
@@ -566,3 +566,7 @@ void WideGraph::setRedFile(QString fRed)
{
ui->widePlot->setRedFile(fRed);
}
void WideGraph::setTurbo(bool turbo){
ui->widePlot->setTurbo(turbo);
}
+1
View File
@@ -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);