SVN r8748

This commit is contained in:
Jordan Sherer
2018-06-14 21:27:34 -04:00
parent 419c039d08
commit 4f1fe4fc94
581 changed files with 69338 additions and 39836 deletions
@@ -0,0 +1,56 @@
subroutine genft8(msg,mygrid,bcontest,i3bit,msgsent,msgbits,itone)
! Encode an FT8 message, producing array itone().
use crc
use packjt
include 'ft8_params.f90'
character*22 msg,msgsent
character*6 mygrid
character*87 cbits
logical bcontest,checksumok
integer*4 i4Msg6BitWords(12) !72-bit message as 6-bit words
integer*1 msgbits(KK),codeword(3*ND)
integer*1, target:: i1Msg8BitBytes(11)
integer itone(NN)
integer icos7(0:6)
data icos7/2,5,6,0,4,1,3/ !Costas 7x7 tone pattern
call packmsg(msg,i4Msg6BitWords,itype,bcontest) !Pack into 12 6-bit bytes
call unpackmsg(i4Msg6BitWords,msgsent,bcontest,mygrid) !Unpack to get msgsent
write(cbits,1000) i4Msg6BitWords,32*i3bit
1000 format(12b6.6,b8.8)
read(cbits,1001) i1Msg8BitBytes(1:10)
1001 format(10b8)
i1Msg8BitBytes(10)=iand(i1Msg8BitBytes(10),128+64+32)
i1Msg8BitBytes(11)=0
icrc12=crc12(c_loc(i1Msg8BitBytes),11)
! For reference, here's how to check the CRC
! i1Msg8BitBytes(10)=icrc12/256
! i1Msg8BitBytes(11)=iand (icrc12,255)
! checksumok = crc12_check(c_loc (i1Msg8BitBytes), 11)
! if( checksumok ) write(*,*) 'Good checksum'
write(cbits,1003) i4Msg6BitWords,i3bit,icrc12
1003 format(12b6.6,b3.3,b12.12)
read(cbits,1004) msgbits
1004 format(87i1)
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
return
end subroutine genft8
@@ -1,45 +0,0 @@
#!/bin/sh
# Example of how an LDPC code can be encoded using using sparse,
# dense, and mixed representations of the generator matrix. The dense
# and mixed representations are based on the same set of message bits
# as the sparse method with minprod heuristic. This allows the correctness
# of these methods to be checked by verifying that they all produce the same
# result when encoding random messages. The results are also checked by
# 'verify'.
#
# A (400,200) LDPC code with 3 checks per bit is used for the test.
set -e # Stop if an error occurs
set -v # Echo commands as they are read
make-ldpc ex-ldpc-encode.pchk 200 400 1 evenboth 3
make-gen ex-ldpc-encode.pchk ex-ldpc-encode.genf sparse first
make-gen ex-ldpc-encode.pchk ex-ldpc-encode.genc sparse mincol
make-gen ex-ldpc-encode.pchk ex-ldpc-encode.genp sparse minprod
make-gen ex-ldpc-encode.pchk ex-ldpc-encode.gend dense ex-ldpc-encode.genp
make-gen ex-ldpc-encode.pchk ex-ldpc-encode.genm mixed ex-ldpc-encode.genp
rand-src ex-ldpc-encode.src 1 200x10
encode ex-ldpc-encode.pchk ex-ldpc-encode.genf ex-ldpc-encode.src \
ex-ldpc-encode.encf
encode ex-ldpc-encode.pchk ex-ldpc-encode.genc ex-ldpc-encode.src \
ex-ldpc-encode.encc
encode ex-ldpc-encode.pchk ex-ldpc-encode.genp ex-ldpc-encode.src \
ex-ldpc-encode.encp
encode ex-ldpc-encode.pchk ex-ldpc-encode.gend ex-ldpc-encode.src \
ex-ldpc-encode.encd
encode ex-ldpc-encode.pchk ex-ldpc-encode.genm ex-ldpc-encode.src \
ex-ldpc-encode.encm
cmp ex-ldpc-encode.encp ex-ldpc-encode.encd
cmp ex-ldpc-encode.encp ex-ldpc-encode.encm
verify ex-ldpc-encode.pchk ex-ldpc-encode.encf ex-ldpc-encode.genf \
ex-ldpc-encode.src
verify ex-ldpc-encode.pchk ex-ldpc-encode.encc ex-ldpc-encode.genc \
ex-ldpc-encode.src
verify ex-ldpc-encode.pchk ex-ldpc-encode.encp ex-ldpc-encode.genp \
ex-ldpc-encode.src
@@ -0,0 +1,405 @@
#include "displaytext.h"
#include "mainwindow.h"
#include <QMouseEvent>
#include <QDateTime>
#include <QTextCharFormat>
#include <QTextCursor>
#include <QTextBlock>
#include <QMenu>
#include <QAction>
#include "qt_helpers.hpp"
#include "moc_displaytext.cpp"
DisplayText::DisplayText(QWidget *parent)
: QTextEdit(parent)
, erase_action_ {new QAction {tr ("&Erase"), this}}
{
setReadOnly (true);
setUndoRedoEnabled (false);
viewport ()->setCursor (Qt::ArrowCursor);
setWordWrapMode (QTextOption::NoWrap);
// max lines to limit heap usage
document ()->setMaximumBlockCount (5000);
// context menu erase action
setContextMenuPolicy (Qt::CustomContextMenu);
connect (this, &DisplayText::customContextMenuRequested, [this] (QPoint const& position) {
auto * menu = createStandardContextMenu (position);
menu->addAction (erase_action_);
menu->exec (mapToGlobal (position));
delete menu;
});
connect (erase_action_, &QAction::triggered, this, &DisplayText::erase);
}
void DisplayText::erase ()
{
clear ();
Q_EMIT erased ();
}
void DisplayText::setContentFont(QFont const& font)
{
char_font_ = font;
selectAll ();
auto cursor = textCursor ();
cursor.beginEditBlock ();
auto char_format = cursor.charFormat ();
char_format.setFont (char_font_);
cursor.mergeCharFormat (char_format);
cursor.clearSelection ();
cursor.movePosition (QTextCursor::End);
// position so viewport scrolled to left
cursor.movePosition (QTextCursor::Up);
cursor.movePosition (QTextCursor::StartOfLine);
cursor.endEditBlock ();
setTextCursor (cursor);
ensureCursorVisible ();
}
void DisplayText::mouseDoubleClickEvent(QMouseEvent *e)
{
Q_EMIT selectCallsign(e->modifiers ());
QTextEdit::mouseDoubleClickEvent(e);
}
void DisplayText::insertLineSpacer(QString const& line)
{
appendText (line, "#d3d3d3");
}
void DisplayText::appendText(QString const& text, QColor bg, QString const& call1, QString const& call2)
{
auto cursor = textCursor ();
cursor.movePosition (QTextCursor::End);
auto block_format = cursor.blockFormat ();
block_format.setBackground (bg);
if (0 == cursor.position ())
{
cursor.setBlockFormat (block_format);
auto char_format = cursor.charFormat ();
char_format.setFont (char_font_);
cursor.setCharFormat (char_format);
}
else
{
cursor.insertBlock (block_format);
}
QTextCharFormat format = cursor.charFormat();
format.clearBackground();
int text_index {0};
if (call1.size ())
{
auto call_index = text.indexOf (call1);
if (call_index != -1) // sanity check
{
auto pos = highlighted_calls_.find (call1);
if (pos != highlighted_calls_.end ())
{
cursor.insertText(text.left (call_index), format);
if (pos.value ().first.isValid ())
{
format.setBackground (pos.value ().first);
}
if (pos.value ().second.isValid ())
{
format.setForeground (pos.value ().second);
}
cursor.insertText(text.mid (call_index, call1.size ()), format);
text_index = call_index + call1.size ();
}
}
}
if (call2.size ())
{
auto call_index = text.indexOf (call2, text_index);
if (call_index != -1) // sanity check
{
auto pos = highlighted_calls_.find (call2);
if (pos != highlighted_calls_.end ())
{
format.setBackground (bg);
format.clearForeground ();
cursor.insertText(text.mid (text_index, call_index - text_index), format);
if (pos.value ().second.isValid ())
{
format.setBackground (pos.value ().first);
}
if (pos.value ().second.isValid ())
{
format.setForeground (pos.value ().second);
}
cursor.insertText(text.mid (call_index, call2.size ()), format);
text_index = call_index + call2.size ();
}
}
}
format.setBackground (bg);
format.clearForeground ();
cursor.insertText(text.mid (text_index), format);
// position so viewport scrolled to left
cursor.movePosition (QTextCursor::StartOfLine);
setTextCursor (cursor);
ensureCursorVisible ();
document ()->setMaximumBlockCount (document ()->maximumBlockCount ());
}
QString DisplayText::appendDXCCWorkedB4(QString message, QString const& callsign, QColor * bg,
LogBook const& logBook, QColor color_CQ,
QColor color_DXCC,
QColor color_NewCall)
{
// allow for seconds
int padding {message.indexOf (" ") > 4 ? 2 : 0};
QString call = callsign;
QString countryName;
bool callWorkedBefore;
bool countryWorkedBefore;
if(call.length()==2) {
int i0=message.indexOf("CQ "+call);
call=message.mid(i0+6,-1);
i0=call.indexOf(" ");
call=call.mid(0,i0);
}
if(call.length()<3) return message;
if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message;
logBook.match(/*in*/call,/*out*/countryName,callWorkedBefore,countryWorkedBefore);
message = message.trimmed ();
QString appendage;
if (!countryWorkedBefore) // therefore not worked call either
{
appendage += "!";
*bg = color_DXCC;
}
else
{
if (!callWorkedBefore) // but have worked the country
{
appendage += "~";
*bg = color_NewCall;
}
else
{
appendage += " "; // have worked this call before
*bg = color_CQ;
}
}
int i1=countryName.indexOf(";");
if(m_bPrincipalPrefix) {
int i2=countryName.lastIndexOf(";");
if(i1>0) countryName=countryName.mid(i1+2,i2-i1-2);
} else {
if(i1>0) countryName=countryName.mid(0,i1);
// do some obvious abbreviations
countryName.replace ("Islands", "Is.");
countryName.replace ("Island", "Is.");
countryName.replace ("North ", "N. ");
countryName.replace ("Northern ", "N. ");
countryName.replace ("South ", "S. ");
countryName.replace ("East ", "E. ");
countryName.replace ("Eastern ", "E. ");
countryName.replace ("West ", "W. ");
countryName.replace ("Western ", "W. ");
countryName.replace ("Central ", "C. ");
countryName.replace (" and ", " & ");
countryName.replace ("Republic", "Rep.");
countryName.replace ("United States", "U.S.A.");
countryName.replace ("Fed. Rep. of ", "");
countryName.replace ("French ", "Fr.");
countryName.replace ("Asiatic", "AS");
countryName.replace ("European", "EU");
countryName.replace ("African", "AF");
}
appendage += countryName;
// use a nbsp to save the start of appended text so we can find
// it again later, align appended data at a fixed column if
// there is space otherwise let it float to the right
int space_count {40 + padding - message.size ()};
if (space_count > 0)
{
message += QString {space_count, QChar {' '}};
}
message += QChar::Nbsp + appendage;
return message;
}
void DisplayText::displayDecodedText(DecodedText const& decodedText, QString const& myCall,
bool displayDXCCEntity, LogBook const& logBook,
QColor color_CQ, QColor color_MyCall,
QColor color_DXCC, QColor color_NewCall, bool ppfx,
bool bCQonly)
{
m_bPrincipalPrefix=ppfx;
QColor bg {Qt::transparent};
bool CQcall = false;
if (decodedText.string ().contains (" CQ ")
|| decodedText.string ().contains (" CQDX ")
|| decodedText.string ().contains (" QRZ "))
{
CQcall = true;
bg = color_CQ;
}
if(bCQonly and !CQcall) return;
if (myCall != "" and (
decodedText.indexOf (" " + myCall + " ") >= 0
or decodedText.indexOf (" " + myCall + "/") >= 0
or decodedText.indexOf ("/" + myCall + " ") >= 0
or decodedText.indexOf ("<" + myCall + " ") >= 0
or decodedText.indexOf (" " + myCall + ">") >= 0)) {
bg = color_MyCall;
}
auto message = decodedText.string ();
QString dxCall;
QString dxGrid;
decodedText.deCallAndGrid (dxCall, dxGrid);
message = message.left (message.indexOf (QChar::Nbsp)); // strip appended info
if (displayDXCCEntity && CQcall)
// if enabled add the DXCC entity and B4 status to the end of the
// preformated text line t1
message = appendDXCCWorkedB4 (message, decodedText.CQersCall (), &bg, logBook, color_CQ,
color_DXCC, color_NewCall);
appendText (message.trimmed (), bg, decodedText.call (), dxCall);
}
void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 txFreq,
QColor color_TxMsg, bool bFastMode)
{
QString t1=" @ ";
if(modeTx=="FT8") t1=" ~ ";
if(modeTx=="JT4") t1=" $ ";
if(modeTx=="JT65") t1=" # ";
if(modeTx=="MSK144") t1=" & ";
QString t2;
t2.sprintf("%4d",txFreq);
QString t;
if(bFastMode or modeTx=="FT8") {
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
" Tx " + t2 + t1 + text;
} else if(modeTx.mid(0,6)=="FT8fox") {
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
" Tx" + modeTx.mid(7) + " " + text;
} else {
t = QDateTime::currentDateTimeUtc().toString("hhmm") + \
" Tx " + t2 + t1 + text;
}
appendText (t, color_TxMsg);
}
void DisplayText::displayQSY(QString text)
{
QString t = QDateTime::currentDateTimeUtc().toString("hhmmss") + " " + text;
appendText (t, "hotpink");
}
void DisplayText::displayFoxToBeCalled(QString t, QColor bg)
{
appendText(t,bg);
}
namespace
{
void update_selection (QTextCursor& cursor, QColor const& bg, QColor const& fg)
{
if (!cursor.isNull ())
{
QTextCharFormat format {cursor.charFormat ()};
if (bg.isValid ())
{
format.setBackground (bg);
}
else
{
format.clearBackground ();
}
if (fg.isValid ())
{
format.setForeground (fg);
}
else
{
format.clearForeground ();
}
cursor.mergeCharFormat (format);
}
}
void reset_selection (QTextCursor& cursor)
{
if (!cursor.isNull ())
{
// restore previous text format, we rely on the text
// char format at he start of the selection being the
// old one which should be the case
auto c2 = cursor;
c2.setPosition (c2.selectionStart ());
cursor.setCharFormat (c2.charFormat ());
}
}
}
void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only)
{
QTextCharFormat old_format {currentCharFormat ()};
QTextCursor cursor {document ()};
if (last_only)
{
cursor.movePosition (QTextCursor::End);
cursor = document ()->find (callsign, cursor
, QTextDocument::FindBackward | QTextDocument::FindWholeWords);
if (bg.isValid () || fg.isValid ())
{
update_selection (cursor, bg, fg);
}
else
{
reset_selection (cursor);
}
}
else
{
auto pos = highlighted_calls_.find (callsign);
if (bg.isValid () || fg.isValid ())
{
auto colours = qMakePair (bg, fg);
if (pos == highlighted_calls_.end ())
{
pos = highlighted_calls_.insert (callsign.toUpper (), colours);
}
else
{
pos.value () = colours; // update colours
}
while (!cursor.isNull ())
{
cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords);
update_selection (cursor, bg, fg);
}
}
else if (pos != highlighted_calls_.end ())
{
highlighted_calls_.erase (pos);
QTextCursor cursor {document ()};
while (!cursor.isNull ())
{
cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords);
reset_selection (cursor);
}
}
}
setCurrentCharFormat (old_format);
}
@@ -0,0 +1,117 @@
#ifndef MESSAGE_CLIENT_HPP__
#define MESSAGE_CLIENT_HPP__
#include <QObject>
#include <QTime>
#include <QDateTime>
#include <QString>
#include "Radio.hpp"
#include "pimpl_h.hpp"
class QByteArray;
class QHostAddress;
class QColor;
//
// MessageClient - Manage messages sent and replies received from a
// matching server (MessageServer) at the other end of
// the wire
//
//
// Each outgoing message type is a Qt slot
//
class MessageClient
: public QObject
{
Q_OBJECT;
public:
using Frequency = Radio::Frequency;
using port_type = quint16;
// instantiate and initiate a host lookup on the server
//
// messages will be silently dropped until a server host lookup is complete
MessageClient (QString const& id, QString const& version, QString const& revision,
QString const& server, port_type server_port, QObject * parent = nullptr);
// query server details
QHostAddress server_address () const;
port_type server_port () const;
// initiate a new server host lookup or is the server name is empty
// the sending of messages is disabled
Q_SLOT void set_server (QString const& server = QString {});
// change the server port messages are sent to
Q_SLOT void set_server_port (port_type server_port = 0u);
// outgoing messages
Q_SLOT void status_update (Frequency, QString const& mode, QString const& dx_call, QString const& report
, QString const& tx_mode, bool tx_enabled, bool transmitting, bool decoding
, qint32 rx_df, qint32 tx_df, QString const& de_call, QString const& de_grid
, QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode
, bool fast_mode);
Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
, QString const& mode, QString const& message, bool low_confidence
, bool off_air);
Q_SLOT void WSPR_decode (bool is_new, QTime time, qint32 snr, float delta_time, Frequency
, qint32 drift, QString const& callsign, QString const& grid, qint32 power
, bool off_air);
Q_SLOT void clear_decodes ();
Q_SLOT void qso_logged (QDateTime time_off, QString const& dx_call, QString const& dx_grid
, Frequency dial_frequency, QString const& mode, QString const& report_sent
, QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid);
// ADIF_record argument should be valid ADIF excluding any <EOR> end
// of record marker
Q_SLOT void logged_ADIF (QByteArray const& ADIF_record);
// this slot may be used to send arbitrary UDP datagrams to and
// destination allowing the underlying socket to be used for general
// UDP messaging if desired
Q_SLOT void send_raw_datagram (QByteArray const&, QHostAddress const& dest_address, port_type dest_port);
// disallowed message destination (does not block datagrams sent
// with send_raw_datagram() above)
Q_SLOT void add_blocked_destination (QHostAddress const&);
// this signal is emitted if the server sends us a reply, the only
// reply supported is reply to a prior CQ or QRZ message
Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode
, QString const& message_text, bool low_confidence, quint8 modifiers);
// this signal is emitted if the server has requested a replay of
// all decodes
Q_SIGNAL void replay ();
// this signal is emitted if the server has requested immediate (or
// auto Tx if auto_only is true) transmission to halt
Q_SIGNAL void halt_tx (bool auto_only);
// this signal is emitted if the server has requested a new free
// message text
Q_SIGNAL void free_text (QString const&, bool send);
// this signal is emitted if the server has sent a highlight
// callsign request for the specified call
Q_SIGNAL void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only);
// this signal is emitted when network errors occur or if a host
// lookup fails
Q_SIGNAL void error (QString const&) const;
// this signal is emitted if the message obtains a location from a
// server. (It doesn't have to be new, could be a periodic location
// update)
Q_SIGNAL void location (QString const&);
private:
class impl;
pimpl<impl> m_;
};
#endif