Merged master 8748
This commit is contained in:
@@ -1,188 +0,0 @@
|
||||
#include "decodedtext.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QRegularExpression>
|
||||
|
||||
QString DecodedText::CQersCall()
|
||||
{
|
||||
// extract the CQer's call TODO: does this work with all call formats?
|
||||
int s1 {0};
|
||||
int position;
|
||||
QString t=_string;
|
||||
if ((position = _string.indexOf (" CQ DX ")) >= 0)
|
||||
{
|
||||
s1 = 7 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" CQDX ")) >= 0)
|
||||
{
|
||||
s1 = 6 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" CQ ")) >= 0)
|
||||
{
|
||||
s1 = 4 + position;
|
||||
if(_string.mid(s1,3).toInt() > 0 and _string.mid(s1,3).toInt() <= 999) s1 += 4;
|
||||
}
|
||||
else if ((position = _string.indexOf (" DE ")) >= 0)
|
||||
{
|
||||
s1 = 4 + position;
|
||||
}
|
||||
else if ((position = _string.indexOf (" QRZ ")) >= 0)
|
||||
{
|
||||
s1 = 5 + position;
|
||||
}
|
||||
auto s2 = _string.indexOf (" ", s1);
|
||||
return _string.mid (s1, s2 - s1);
|
||||
}
|
||||
|
||||
|
||||
bool DecodedText::isJT65()
|
||||
{
|
||||
return _string.indexOf("#") == column_mode;
|
||||
}
|
||||
|
||||
bool DecodedText::isJT9()
|
||||
{
|
||||
return _string.indexOf("@") == column_mode;
|
||||
}
|
||||
|
||||
bool DecodedText::isTX()
|
||||
{
|
||||
int i = _string.indexOf("Tx");
|
||||
return (i >= 0 && i < 15); // TODO guessing those numbers. Does Tx ever move?
|
||||
}
|
||||
|
||||
int DecodedText::frequencyOffset()
|
||||
{
|
||||
return _string.mid(column_freq,4).toInt();
|
||||
}
|
||||
|
||||
int DecodedText::snr()
|
||||
{
|
||||
int i1=_string.indexOf(" ")+1;
|
||||
return _string.mid(i1,3).toInt();
|
||||
}
|
||||
|
||||
float DecodedText::dt()
|
||||
{
|
||||
return _string.mid(column_dt,5).toFloat();
|
||||
}
|
||||
|
||||
/*
|
||||
2343 -11 0.8 1259 # YV6BFE F6GUU R-08
|
||||
2343 -19 0.3 718 # VE6WQ SQ2NIJ -14
|
||||
2343 -7 0.3 815 # KK4DSD W7VP -16
|
||||
2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02
|
||||
|
||||
0605 Tx 1259 # CQ VK3ACF QF22
|
||||
*/
|
||||
|
||||
// find and extract any report. Returns true if this is a standard message
|
||||
bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report)
|
||||
{
|
||||
QString msg=_string.mid(column_qsoText).trimmed();
|
||||
if(msg.length() < 1) return false;
|
||||
msg = msg.remove (QRegularExpression {"[<>]"});
|
||||
int i1=msg.indexOf('\r');
|
||||
if (i1>0)
|
||||
msg=msg.left (i1-1);
|
||||
bool b = stdmsg_ ((msg + " ").toLatin1().constData(),22); // stdmsg is a fortran routine that packs the text, unpacks it and compares the result
|
||||
|
||||
QStringList w=msg.split(" ",QString::SkipEmptyParts);
|
||||
if(w.size ()
|
||||
&& b && (w[0] == myBaseCall
|
||||
|| w[0].endsWith ("/" + myBaseCall)
|
||||
|| w[0].startsWith (myBaseCall + "/")
|
||||
|| (w.size () > 1 && !dxBaseCall.isEmpty ()
|
||||
&& (w[1] == dxBaseCall
|
||||
|| w[1].endsWith ("/" + dxBaseCall)
|
||||
|| w[1].startsWith (dxBaseCall + "/")))))
|
||||
{
|
||||
QString tt="";
|
||||
if(w.size() > 2) tt=w[2];
|
||||
bool ok;
|
||||
i1=tt.toInt(&ok);
|
||||
if (ok and i1>=-50 and i1<50)
|
||||
{
|
||||
report = tt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tt.mid(0,1)=="R")
|
||||
{
|
||||
i1=tt.mid(1).toInt(&ok);
|
||||
if(ok and i1>=-50 and i1<50)
|
||||
{
|
||||
report = tt.mid(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// get the first text word, usually the call
|
||||
QString DecodedText::call()
|
||||
{
|
||||
auto call = _string;
|
||||
call = call.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText);
|
||||
int i = call.indexOf(" ");
|
||||
return call.mid(0,i);
|
||||
}
|
||||
|
||||
// get the second word, most likely the de call and the third word, most likely grid
|
||||
void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid)
|
||||
{
|
||||
auto msg = _string;
|
||||
if(msg.mid(4,1)!=" ") msg=msg.mid(0,4)+msg.mid(6,-1); //Remove seconds from UTC
|
||||
msg = msg.replace (QRegularExpression {" CQ ([A-Z]{2,2}|[0-9]{3,3}) "}, " CQ_\\1 ").mid (column_qsoText);
|
||||
int i1 = msg.indexOf (" ");
|
||||
call = msg.mid (i1 + 1);
|
||||
int i2 = call.indexOf (" ");
|
||||
if (" R " == call.mid (i2, 3)) // MSK144 contest mode report
|
||||
{
|
||||
grid = call.mid (i2 + 3, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
grid = call.mid (i2 + 1, 4);
|
||||
}
|
||||
call = call.left (i2).replace (">", "");
|
||||
}
|
||||
|
||||
int DecodedText::timeInSeconds()
|
||||
{
|
||||
return 60*_string.mid(column_time,2).toInt() + _string.mid(2,2).toInt();
|
||||
}
|
||||
|
||||
/*
|
||||
2343 -11 0.8 1259 # YV6BFE F6GUU R-08
|
||||
2343 -19 0.3 718 # VE6WQ SQ2NIJ -14
|
||||
2343 -7 0.3 815 # KK4DSD W7VP -16
|
||||
2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02
|
||||
|
||||
0605 Tx 1259 # CQ VK3ACF QF22
|
||||
*/
|
||||
|
||||
QString DecodedText::report() // returns a string of the SNR field with a leading + or - followed by two digits
|
||||
{
|
||||
int sr = snr();
|
||||
if (sr<-50)
|
||||
sr = -50;
|
||||
else
|
||||
if (sr > 49)
|
||||
sr = 49;
|
||||
|
||||
QString rpt;
|
||||
rpt.sprintf("%d",abs(sr));
|
||||
if (sr > 9)
|
||||
rpt = "+" + rpt;
|
||||
else
|
||||
if (sr >= 0)
|
||||
rpt = "+0" + rpt;
|
||||
else
|
||||
if (sr >= -9)
|
||||
rpt = "-0" + rpt;
|
||||
else
|
||||
rpt = "-" + rpt;
|
||||
return rpt;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,133 @@
|
||||
// KISS Interface for posting spots to PSK Reporter web site
|
||||
// Implemented by Edson Pereira PY2SDR
|
||||
//
|
||||
// Reports will be sent in batch mode every 5 minutes.
|
||||
|
||||
#include "psk_reporter.h"
|
||||
|
||||
#include <QHostInfo>
|
||||
#include <QTimer>
|
||||
|
||||
#include "MessageClient.hpp"
|
||||
|
||||
#include "moc_psk_reporter.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
int constexpr MAX_PAYLOAD_LENGTH {1400};
|
||||
}
|
||||
|
||||
PSK_Reporter::PSK_Reporter(MessageClient * message_client, QObject *parent) :
|
||||
QObject {parent},
|
||||
m_messageClient {message_client},
|
||||
reportTimer {new QTimer {this}},
|
||||
m_sequenceNumber {0}
|
||||
{
|
||||
m_header_h = "000Allllttttttttssssssssiiiiiiii";
|
||||
|
||||
// We use 50E2 and 50E3 for link Id
|
||||
m_rxInfoDescriptor_h = "0003002C50E200040000"
|
||||
"8002FFFF0000768F" // 2. Rx Call
|
||||
"8004FFFF0000768F" // 4. Rx Grid
|
||||
"8008FFFF0000768F" // 8. Rx Soft
|
||||
"8009FFFF0000768F" // 9. Rx Antenna
|
||||
"0000";
|
||||
|
||||
m_txInfoDescriptor_h = "0002003C50E30007"
|
||||
"8001FFFF0000768F" // 1. Tx Call
|
||||
"800500040000768F" // 5. Tx Freq
|
||||
"800600010000768F" // 6. Tx snr
|
||||
"800AFFFF0000768F" // 10. Tx Mode
|
||||
"8003FFFF0000768F" // 3. Tx Grid
|
||||
"800B00010000768F" // 11. Tx info src
|
||||
"00960004"; // Report time
|
||||
|
||||
|
||||
m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0'));
|
||||
|
||||
QHostInfo::lookupHost("report.pskreporter.info", this, SLOT(dnsLookupResult(QHostInfo)));
|
||||
|
||||
connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport()));
|
||||
reportTimer->start(5*60*1000); // 5 minutes;
|
||||
}
|
||||
|
||||
void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo)
|
||||
{
|
||||
m_rxCall = call;
|
||||
m_rxGrid = gridSquare;
|
||||
m_rxAnt = antenna;
|
||||
m_progId = programInfo;
|
||||
}
|
||||
|
||||
void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time )
|
||||
{
|
||||
QHash<QString,QString> spot;
|
||||
spot["call"] = call;
|
||||
spot["grid"] = grid;
|
||||
spot["snr"] = snr;
|
||||
spot["freq"] = freq;
|
||||
spot["mode"] = mode;
|
||||
spot["time"] = time;
|
||||
m_spotQueue.enqueue(spot);
|
||||
}
|
||||
|
||||
void PSK_Reporter::sendReport()
|
||||
{
|
||||
while (!m_spotQueue.isEmpty()) {
|
||||
QString report_h;
|
||||
|
||||
// Header
|
||||
QString header_h = m_header_h;
|
||||
header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0')));
|
||||
header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0')));
|
||||
header_h.replace("iiiiiiii", m_randomId_h);
|
||||
|
||||
// Receiver information
|
||||
QString rxInfoData_h = "50E2llll";
|
||||
rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex();
|
||||
rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex();
|
||||
rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex();
|
||||
rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex();
|
||||
rxInfoData_h += "0000";
|
||||
rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0')));
|
||||
|
||||
// Sender information
|
||||
QString txInfoData_h = "50E3llll";
|
||||
while (!m_spotQueue.isEmpty()
|
||||
&& (header_h.size () + m_rxInfoDescriptor_h.size () + m_txInfoDescriptor_h.size () + rxInfoData_h.size () + txInfoData_h.size ()) / 2 < MAX_PAYLOAD_LENGTH) {
|
||||
QHash<QString,QString> spot = m_spotQueue.dequeue();
|
||||
txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex();
|
||||
txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0'));
|
||||
txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2);
|
||||
txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex();
|
||||
txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex();
|
||||
txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC
|
||||
txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0'));
|
||||
}
|
||||
txInfoData_h += "0000";
|
||||
txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0')));
|
||||
report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h;
|
||||
//qDebug() << "Sending Report TX: ";
|
||||
|
||||
report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0')));
|
||||
QByteArray report = QByteArray::fromHex(report_h.toUtf8());
|
||||
|
||||
// Send data to PSK Reporter site
|
||||
if (!m_pskReporterAddress.isNull()) {
|
||||
m_messageClient->send_raw_datagram (report, m_pskReporterAddress, 4739);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PSK_Reporter::dnsLookupResult(QHostInfo info)
|
||||
{
|
||||
if (!info.addresses().isEmpty()) {
|
||||
m_pskReporterAddress = info.addresses().at(0);
|
||||
// qDebug() << "PSK Reporter IP: " << m_pskReporterAddress;
|
||||
|
||||
// deal with miss-configured settings that attempt to set a
|
||||
// Pskreporter Internet address for the WSJT-X UDP protocol
|
||||
// server address
|
||||
m_messageClient->add_blocked_destination (m_pskReporterAddress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
program ldpcsim204
|
||||
|
||||
! End-to-end test of the (300,60)/crc10 encoder and decoders.
|
||||
|
||||
use crc
|
||||
use packjt
|
||||
|
||||
parameter(NRECENT=10)
|
||||
character*12 recent_calls(NRECENT)
|
||||
character*8 arg
|
||||
integer*1, allocatable :: codeword(:), decoded(:), message(:)
|
||||
integer*1, target:: i1Msg8BitBytes(9)
|
||||
integer*1, target:: i1Dec8BitBytes(9)
|
||||
integer*1 msgbits(68)
|
||||
integer*1 apmask(204)
|
||||
integer*1 cw(204)
|
||||
integer*2 checksum
|
||||
integer colorder(204)
|
||||
integer nerrtot(204),nerrdec(204),nmpcbad(68)
|
||||
logical checksumok,fsk,bpsk
|
||||
real*8, allocatable :: rxdata(:)
|
||||
real, allocatable :: llr(:)
|
||||
real dllr(204),llrd(204)
|
||||
|
||||
data colorder/ &
|
||||
0, 1, 2, 3, 4, 5, 47, 6, 7, 8, 9, 10, 11, 12, 58, 55, 13, &
|
||||
14, 15, 46, 17, 18, 60, 19, 20, 21, 22, 23, 24, 25, 57, 26, 27, 49, &
|
||||
28, 52, 65, 16, 50, 73, 59, 68, 63, 29, 30, 31, 32, 51, 62, 56, 66, &
|
||||
45, 33, 34, 53, 67, 35, 36, 37, 61, 69, 54, 38, 71, 82, 39, 77, 80, &
|
||||
83, 78, 84, 48, 41, 85, 40, 64, 75, 96, 74, 72, 76, 86, 87, 89, 90, &
|
||||
79, 70, 92, 99, 93,101, 95,100, 97, 94, 42, 98,103,105,102, 43,104, &
|
||||
88, 44,106, 81,107,110,108,111,112,109,113,114,117,118,116,121,115, &
|
||||
119,122,120,125,129,124,127,126,128, 91,123,133,131,130,134,135,137, &
|
||||
136,132,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152, &
|
||||
153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169, &
|
||||
170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186, &
|
||||
187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203/
|
||||
|
||||
do i=1,NRECENT
|
||||
recent_calls(i)=' '
|
||||
enddo
|
||||
nerrtot=0
|
||||
nerrdec=0
|
||||
nmpcbad=0 ! Used to collect the number of errors in the message+crc part of the codeword
|
||||
|
||||
nargs=iargc()
|
||||
if(nargs.ne.4) then
|
||||
print*,'Usage: ldpcsim niter ndeep #trials s '
|
||||
print*,'eg: ldpcsim 100 4 1000 0.84'
|
||||
print*,'If s is negative, then value is ignored and sigma is calculated from SNR.'
|
||||
return
|
||||
endif
|
||||
call getarg(1,arg)
|
||||
read(arg,*) max_iterations
|
||||
call getarg(2,arg)
|
||||
read(arg,*) ndeep
|
||||
call getarg(3,arg)
|
||||
read(arg,*) ntrials
|
||||
call getarg(4,arg)
|
||||
read(arg,*) s
|
||||
|
||||
fsk=.false.
|
||||
bpsk=.true.
|
||||
|
||||
N=204
|
||||
K=68
|
||||
rate=real(K)/real(N)
|
||||
|
||||
write(*,*) "rate: ",rate
|
||||
write(*,*) "niter= ",max_iterations," s= ",s
|
||||
|
||||
allocate ( codeword(N), decoded(K), message(K) )
|
||||
allocate ( rxdata(N), llr(N) )
|
||||
|
||||
! The message should be packed into the first 7 bytes
|
||||
i1Msg8BitBytes(1:6)=85
|
||||
i1Msg8BitBytes(7)=64
|
||||
! The CRC will be put into the last 2 bytes
|
||||
i1Msg8BitBytes(8:9)=0
|
||||
checksum = crc10 (c_loc (i1Msg8BitBytes), 9)
|
||||
! For reference, the next 3 lines show how to check the CRC
|
||||
i1Msg8BitBytes(8)=checksum/256
|
||||
i1Msg8BitBytes(9)=iand (checksum,255)
|
||||
checksumok = crc10_check(c_loc (i1Msg8BitBytes), 9)
|
||||
if( checksumok ) write(*,*) 'Good checksum'
|
||||
write(*,*) i1Msg8BitBytes(1:9)
|
||||
|
||||
mbit=0
|
||||
do i=1, 7
|
||||
i1=i1Msg8BitBytes(i)
|
||||
do ibit=1,8
|
||||
mbit=mbit+1
|
||||
msgbits(mbit)=iand(1,ishft(i1,ibit-8))
|
||||
enddo
|
||||
enddo
|
||||
i1=i1Msg8BitBytes(8) ! First 2 bits of crc10 are LSB of this byte
|
||||
do ibit=1,2
|
||||
msgbits(50+ibit)=iand(1,ishft(i1,ibit-2))
|
||||
enddo
|
||||
i1=i1Msg8BitBytes(9) ! Now shift in last 8 bits of the CRC
|
||||
do ibit=1,8
|
||||
msgbits(52+ibit)=iand(1,ishft(i1,ibit-8))
|
||||
enddo
|
||||
|
||||
write(*,*) 'message'
|
||||
write(*,'(9(8i1,1x))') msgbits
|
||||
|
||||
call encode204(msgbits,codeword)
|
||||
call init_random_seed()
|
||||
call sgran()
|
||||
|
||||
write(*,*) 'codeword'
|
||||
write(*,'(204i1)') codeword
|
||||
|
||||
write(*,*) "Es/N0 SNR2500 ngood nundetected nbadcrc sigma"
|
||||
do idb = 20,-18,-1
|
||||
!do idb = -16, -16, -1
|
||||
db=idb/2.0-1.0
|
||||
! sigma=1/sqrt( 2*rate*(10**(db/10.0)) ) ! to make db represent Eb/No
|
||||
sigma=1/sqrt( 2*(10**(db/10.0)) ) ! db represents Es/No
|
||||
ngood=0
|
||||
nue=0
|
||||
nbadcrc=0
|
||||
nberr=0
|
||||
do itrial=1, ntrials
|
||||
! Create a realization of a noisy received word
|
||||
do i=1,N
|
||||
if( bpsk ) then
|
||||
rxdata(i) = 2.0*codeword(i)-1.0 + sigma*gran()
|
||||
elseif( fsk ) then
|
||||
if( codeword(i) .eq. 1 ) then
|
||||
r1=(1.0 + sigma*gran())**2 + (sigma*gran())**2
|
||||
r2=(sigma*gran())**2 + (sigma*gran())**2
|
||||
elseif( codeword(i) .eq. 0 ) then
|
||||
r2=(1.0 + sigma*gran())**2 + (sigma*gran())**2
|
||||
r1=(sigma*gran())**2 + (sigma*gran())**2
|
||||
endif
|
||||
rxdata(i)=0.35*(sqrt(r1)-sqrt(r2))
|
||||
! rxdata(i)=0.35*(exp(r1)-exp(r2))
|
||||
! rxdata(i)=0.12*(log(r1)-log(r2))
|
||||
endif
|
||||
enddo
|
||||
nerr=0
|
||||
do i=1,N
|
||||
if( rxdata(i)*(2*codeword(i)-1.0) .lt. 0 ) nerr=nerr+1
|
||||
enddo
|
||||
if(nerr.ge.1) nerrtot(nerr)=nerrtot(nerr)+1
|
||||
nberr=nberr+nerr
|
||||
|
||||
! Correct signal normalization is important for this decoder.
|
||||
rxav=sum(rxdata)/N
|
||||
rx2av=sum(rxdata*rxdata)/N
|
||||
rxsig=sqrt(rx2av-rxav*rxav)
|
||||
rxdata=rxdata/rxsig
|
||||
! To match the metric to the channel, s should be set to the noise standard deviation.
|
||||
! For now, set s to the value that optimizes decode probability near threshold.
|
||||
! The s parameter can be tuned to trade a few tenth's dB of threshold for an order of
|
||||
! magnitude in UER
|
||||
if( s .lt. 0 ) then
|
||||
ss=sigma
|
||||
else
|
||||
ss=s
|
||||
endif
|
||||
|
||||
llr=2.0*rxdata/(ss*ss)
|
||||
apmask=0
|
||||
! max_iterations is max number of belief propagation iterations
|
||||
call bpdecode204(llr,apmask,max_iterations,decoded,cw,nharderror,niterations)
|
||||
if( (nharderror .lt. 0) .and. (ndeep .ge. 0) ) then
|
||||
call osd204(llr, apmask, ndeep, decoded, cw, nhardmin, dmin)
|
||||
niterations=nhardmin
|
||||
endif
|
||||
n2err=0
|
||||
do i=1,N
|
||||
if( cw(i)*(2*codeword(i)-1.0) .lt. 0 ) n2err=n2err+1
|
||||
enddo
|
||||
!write(*,*) nerr,niterations,n2err
|
||||
damp=0.75
|
||||
ndither=0
|
||||
if( niterations .lt. 0 ) then
|
||||
do i=1, ndither
|
||||
do in=1,N
|
||||
dllr(in)=damp*gran()
|
||||
enddo
|
||||
llrd=llr+dllr
|
||||
call bpdecode300(llrd, apmask, max_iterations, decoded, cw, nharderror, niterations)
|
||||
if( niterations .ge. 0 ) exit
|
||||
enddo
|
||||
endif
|
||||
|
||||
! If the decoder finds a valid codeword, niterations will be .ge. 0.
|
||||
if( niterations .ge. 0 ) then
|
||||
! Check the CRC
|
||||
do ibyte=1,6
|
||||
itmp=0
|
||||
do ibit=1,8
|
||||
itmp=ishft(itmp,1)+iand(1,decoded((ibyte-1)*8+ibit))
|
||||
enddo
|
||||
i1Dec8BitBytes(ibyte)=itmp
|
||||
enddo
|
||||
i1Dec8BitBytes(7)=decoded(49)*128+decoded(50)*64
|
||||
! Need to pack the received crc into bytes 8 and 9 for crc10_check
|
||||
i1Dec8BitBytes(8)=decoded(51)*2+decoded(52)
|
||||
i1Dec8BitBytes(9)=decoded(53)*128+decoded(54)*64+decoded(55)*32+decoded(56)*16
|
||||
i1Dec8BitBytes(9)=i1Dec8BitBytes(9)+decoded(57)*8+decoded(58)*4+decoded(59)*2+decoded(60)*1
|
||||
ncrcflag=0
|
||||
if( crc10_check( c_loc( i1Dec8BitBytes ), 9 ) ) ncrcflag=1
|
||||
|
||||
if( ncrcflag .ne. 1 ) then
|
||||
nbadcrc=nbadcrc+1
|
||||
endif
|
||||
nueflag=0
|
||||
|
||||
nerrmpc=0
|
||||
do i=1,K ! find number of errors in message+crc part of codeword
|
||||
if( msgbits(i) .ne. decoded(i) ) then
|
||||
nueflag=1
|
||||
nerrmpc=nerrmpc+1
|
||||
endif
|
||||
enddo
|
||||
if(nerrmpc.ge.1) nmpcbad(nerrmpc)=nmpcbad(nerrmpc)+1 ! This histogram should inform our selection of CRC poly
|
||||
if( ncrcflag .eq. 1 .and. nueflag .eq. 0 ) then
|
||||
ngood=ngood+1
|
||||
if(nerr.ge.1) nerrdec(nerr)=nerrdec(nerr)+1
|
||||
else if( ncrcflag .eq. 1 .and. nueflag .eq. 1 ) then
|
||||
nue=nue+1;
|
||||
endif
|
||||
endif
|
||||
enddo
|
||||
snr2500=db+10*log10(200.0/116.0/2500.0)
|
||||
pberr=real(nberr)/(real(ntrials*N))
|
||||
write(*,"(f4.1,4x,f5.1,1x,i8,1x,i8,1x,i8,8x,f5.2,8x,e10.3)") db,snr2500,ngood,nue,nbadcrc,ss,pberr
|
||||
|
||||
enddo
|
||||
|
||||
open(unit=23,file='nerrhisto.dat',status='unknown')
|
||||
do i=1,120
|
||||
write(23,'(i4,2x,i10,i10,f10.2)') i,nerrdec(i),nerrtot(i),real(nerrdec(i))/real(nerrtot(i)+1e-10)
|
||||
enddo
|
||||
close(23)
|
||||
open(unit=25,file='nmpcbad.dat',status='unknown')
|
||||
do i=1,68
|
||||
write(25,'(i4,2x,i10)') i,nmpcbad(i)
|
||||
enddo
|
||||
close(25)
|
||||
|
||||
|
||||
|
||||
end program ldpcsim204
|
||||
@@ -1,217 +0,0 @@
|
||||
program wspr5d
|
||||
|
||||
! Decode WSPR-LF data read from *.c5 or *.wav files.
|
||||
|
||||
! WSPR-LF is a potential WSPR-like mode intended for use at LF and MF.
|
||||
! It uses an LDPC (300,60) code, OQPSK modulation, and 5 minute T/R sequences.
|
||||
|
||||
! Reception and Demodulation algorithm:
|
||||
! 1. Compute coarse spectrum; find fc1 = approx carrier freq
|
||||
! 2. Mix from fc1 to 0; LPF at +/- 0.75*R
|
||||
! 3. Square, FFT; find peaks near -R/2 and +R/2 to get fc2
|
||||
! 4. Mix from fc2 to 0
|
||||
! 5. Fit cb13 (central part of csync) to c -> lag, phase
|
||||
! 6. Fit complex ploynomial for channel equalization
|
||||
! 7. Get soft bits from equalized data
|
||||
|
||||
! Still to do: find and decode more than one signal in the specified passband.
|
||||
|
||||
include 'wsprlf_params.f90'
|
||||
parameter (NMAX=300*12000)
|
||||
character arg*8,message*22,cbits*50,infile*80,fname*16,datetime*11
|
||||
character*120 data_dir
|
||||
complex csync(0:NZ-1) !Sync symbols only, from cbb
|
||||
complex c(0:NZ-1) !Complex waveform
|
||||
complex c1(0:NZ-1) !Complex waveform
|
||||
complex zz(NS+ND) !Complex symbol values (intermediate)
|
||||
complex z
|
||||
real*8 fMHz
|
||||
real rxdata(ND),llr(ND) !Soft symbols
|
||||
real pp(2*NSPS) !Shaped pulse for OQPSK
|
||||
real a(5) !For twkfreq1
|
||||
real aa(20),bb(20) !Fitted polyco's
|
||||
real fpks(20)
|
||||
integer id(NS+ND) !NRZ values (+/-1) for Sync and Data
|
||||
integer ierror(NS+ND)
|
||||
integer isync(48) !Long sync vector
|
||||
integer ib13(13) !Barker 13 code
|
||||
integer ihdr(11)
|
||||
integer*8 n8
|
||||
integer*2 iwave(NMAX) !Generated full-length waveform
|
||||
integer*1 idat(7)
|
||||
integer*1 decoded(KK),apmask(ND),cw(ND)
|
||||
data ib13/1,1,1,1,1,-1,-1,1,1,-1,1,-1,1/
|
||||
|
||||
nargs=iargc()
|
||||
if(nargs.lt.2) then
|
||||
print*,'Usage: wspr5d [-a <data_dir>] [-f fMHz] file1 [file2 ...]'
|
||||
go to 999
|
||||
endif
|
||||
iarg=1
|
||||
data_dir="."
|
||||
call getarg(iarg,arg)
|
||||
if(arg(1:2).eq.'-a') then
|
||||
call getarg(iarg+1,data_dir)
|
||||
iarg=iarg+2
|
||||
endif
|
||||
call getarg(iarg,arg)
|
||||
if(arg(1:2).eq.'-f') then
|
||||
call getarg(iarg+1,arg)
|
||||
read(arg,*) fMHz
|
||||
iarg=iarg+2
|
||||
endif
|
||||
|
||||
open(13,file=trim(data_dir)//'/ALL_WSPR.TXT',status='unknown', &
|
||||
position='append')
|
||||
maxn=8 !Default value
|
||||
twopi=8.0*atan(1.0)
|
||||
fs=NSPS*12000.0/NSPS0 !Sample rate
|
||||
dt=1.0/fs !Sample interval (s)
|
||||
tt=NSPS*dt !Duration of "itone" symbols (s)
|
||||
ts=2*NSPS*dt !Duration of OQPSK symbols (s)
|
||||
baud=1.0/tt !Keying rate for "itone" symbols (baud)
|
||||
txt=NZ*dt !Transmission length (s)
|
||||
|
||||
do i=1,N2 !Half-sine pulse shape
|
||||
pp(i)=sin(0.5*(i-1)*twopi/(2*NSPS))
|
||||
enddo
|
||||
n8=z'cbf089223a51'
|
||||
do i=1,48
|
||||
isync(i)=-1
|
||||
if(iand(n8,1).eq.1) isync(i)=1
|
||||
n8=n8/2
|
||||
enddo
|
||||
|
||||
! Define array id() for sync symbols
|
||||
id=0
|
||||
do j=1,48 !First group of 48
|
||||
id(2*j-1)=2*isync(j)
|
||||
enddo
|
||||
do j=1,13 !Barker 13 code
|
||||
id(j+96)=2*ib13(j)
|
||||
enddo
|
||||
do j=1,48 !Second group of 48
|
||||
id(2*j+109)=2*isync(j)
|
||||
enddo
|
||||
|
||||
csync=0.
|
||||
do j=1,205
|
||||
if(abs(id(j)).eq.2) then
|
||||
ia=nint((j-0.5)*N2)
|
||||
ib=ia+N2-1
|
||||
csync(ia:ib)=pp*id(j)/abs(id(j))
|
||||
endif
|
||||
enddo
|
||||
|
||||
do ifile=iarg,nargs
|
||||
call getarg(ifile,infile)
|
||||
open(10,file=infile,status='old',access='stream')
|
||||
j1=index(infile,'.c5')
|
||||
j2=index(infile,'.wav')
|
||||
if(j1.gt.0) then
|
||||
read(10,end=999) fname,ntrmin,fMHz,c
|
||||
read(fname(8:11),*) nutc
|
||||
write(datetime,'(i11)') nutc
|
||||
else if(j2.gt.0) then
|
||||
read(10,end=999) ihdr,iwave
|
||||
read(infile(j2-4:j2-1),*) nutc
|
||||
datetime=infile(j2-11:j2-1)
|
||||
call wspr5_downsample(iwave,c)
|
||||
else
|
||||
print*,'Wrong file format?'
|
||||
go to 999
|
||||
endif
|
||||
close(10)
|
||||
fa=100.0
|
||||
fb=150.0
|
||||
call getfc1w(c,fs,fa,fb,fc1,xsnr) !First approx for freq
|
||||
npeaks=20
|
||||
call getfc2w(c,csync,npeaks,fs,fc1,fpks) !Refined freq
|
||||
|
||||
a(1)=-fc1
|
||||
a(2:5)=0.
|
||||
call twkfreq1(c,NZ,fs,a,c) !Mix c down by fc1+fc2
|
||||
|
||||
! Find time offset xdt
|
||||
amax=0.
|
||||
jpk=0
|
||||
iaa=0
|
||||
ibb=NZ-1
|
||||
jmax=1260
|
||||
do j=-jmax,jmax,NSPS/8
|
||||
ia=j
|
||||
ib=NZ-1+j
|
||||
if(ia.lt.0) then
|
||||
ia=0
|
||||
iaa=-j
|
||||
else
|
||||
iaa=0
|
||||
endif
|
||||
if(ib.gt.NZ-1) then
|
||||
ib=NZ-1
|
||||
ibb=NZ-1-j
|
||||
endif
|
||||
z=sum(c(ia:ib)*conjg(csync(iaa:ibb)))
|
||||
write(51,*) j/fs,real(z),imag(z)
|
||||
if(abs(z).gt.amax) then
|
||||
amax=abs(z)
|
||||
jpk=j
|
||||
endif
|
||||
enddo
|
||||
xdt=jpk/fs
|
||||
xdt=1.0
|
||||
jpk=fs*xdt
|
||||
do i=0,NZ-1
|
||||
j=i+jpk
|
||||
if(j.ge.0 .and. j.lt.NZ) c1(i)=c(j)
|
||||
enddo
|
||||
|
||||
nterms=maxn
|
||||
do itry=1,npeaks
|
||||
nhard0=0
|
||||
nhardsync0=0
|
||||
ifer=1
|
||||
a(1)=-fpks(itry)
|
||||
a(2:5)=0.
|
||||
call twkfreq1(c1,NZ,fs,a,c) !Mix c1 into c
|
||||
call cpolyfitw(c,pp,id,maxn,aa,bb,zz,nhs)
|
||||
call msksoftsymw(zz,aa,bb,id,nterms,ierror,rxdata,nhard0,nhardsync0)
|
||||
if(nhardsync0.gt.35) cycle
|
||||
rxav=sum(rxdata)/ND
|
||||
rx2av=sum(rxdata*rxdata)/ND
|
||||
rxsig=sqrt(rx2av-rxav*rxav)
|
||||
rxdata=rxdata/rxsig
|
||||
ss=0.84
|
||||
llr=2.0*rxdata/(ss*ss)
|
||||
apmask=0
|
||||
max_iterations=40
|
||||
ifer=0
|
||||
call bpdecode300(llr,apmask,max_iterations,decoded,niterations,cw)
|
||||
if(niterations.lt.0) call osd300(llr,4,decoded,niterations,cw)
|
||||
nbadcrc=0
|
||||
if(niterations.ge.0) call chkcrc10(decoded,nbadcrc)
|
||||
if(niterations.lt.0 .or. nbadcrc.ne.0) ifer=1
|
||||
if(ifer.eq.0) exit
|
||||
enddo !Freq dither loop
|
||||
message=' '
|
||||
if(ifer.eq.0) then
|
||||
write(cbits,1100) decoded(1:50)
|
||||
1100 format(50i1)
|
||||
read(cbits,1102) idat
|
||||
1102 format(6b8,b2)
|
||||
idat(7)=ishft(idat(7),6)
|
||||
call wqdecode(idat,message,itype)
|
||||
nsnr=nint(xsnr)
|
||||
! freq=fMHz + 1.d-6*(fc1+fc2)
|
||||
freq=fMHz + 1.d-6*(fc1+fpks(itry))
|
||||
nfdot=0
|
||||
write(13,1110) datetime,0,nsnr,xdt,freq,message,nfdot
|
||||
1110 format(a11,2i4,f6.2,f12.7,2x,a22,i3)
|
||||
write(*,1112) datetime(8:11),nsnr,xdt,freq,nfdot,message,itry
|
||||
1112 format(a4,i4,f5.1,f11.6,i3,2x,a22,i4)
|
||||
endif
|
||||
enddo ! ifile loop
|
||||
write(*,1120)
|
||||
1120 format("<DecodeFinished>")
|
||||
|
||||
999 end program wspr5d
|
||||
@@ -1,27 +0,0 @@
|
||||
subroutine fix_contest_msg(mycall,mygrid,hiscall,msg)
|
||||
|
||||
! If msg is "mycall hiscall grid1" and distance from mygrid to grid1 is more
|
||||
! thsn 10000 km, change "grid1" to "R grid2" where grid2 is the antipodes
|
||||
! of grid1.
|
||||
|
||||
character*6 mycall,mygrid,hiscall
|
||||
character*22 msg
|
||||
character*6 g1,g2
|
||||
logical isgrid
|
||||
|
||||
n=len(trim(msg))
|
||||
g1=msg(n-3:n)//' '
|
||||
if(isgrid(g1)) then
|
||||
call azdist(mygrid,g1,0.d0,nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter)
|
||||
if(ndkm.gt.10000) then
|
||||
call grid2deg(g1,dlong,dlat)
|
||||
dlong=dlong+180.0
|
||||
if(dlong.gt.180.0) dlong=dlong-360.0
|
||||
dlat=-dlat
|
||||
call deg2grid(dlong,dlat,g2)
|
||||
msg=msg(1:n-4)//'R '//g2(1:4)
|
||||
endif
|
||||
endif
|
||||
|
||||
return
|
||||
end subroutine fix_contest_msg
|
||||
@@ -1,61 +0,0 @@
|
||||
// -*- Mode: C++ -*-
|
||||
#ifndef LogQSO_H
|
||||
#define LogQSO_H
|
||||
|
||||
#ifdef QT5
|
||||
#include <QtWidgets>
|
||||
#else
|
||||
#include <QtGui>
|
||||
#endif
|
||||
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include "Radio.hpp"
|
||||
|
||||
namespace Ui {
|
||||
class LogQSO;
|
||||
}
|
||||
|
||||
class QSettings;
|
||||
|
||||
class LogQSO : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LogQSO(QString const& programTitle, QSettings *, QWidget *parent = 0);
|
||||
~LogQSO();
|
||||
void initLogQSO(QString hisCall, QString hisGrid, QString mode,
|
||||
QString rptSent, QString rptRcvd, QDateTime dateTimeOn,QDateTime dateTimeOff,
|
||||
Radio::Frequency dialFreq, QString myCall, QString myGrid,
|
||||
bool noSuffix, bool toRTTY, bool dBtoComments);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
signals:
|
||||
void acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid
|
||||
, Radio::Frequency dial_freq, QString const& mode
|
||||
, QString const& rpt_sent, QString const& rpt_received
|
||||
, QString const& tx_power, QString const& comments
|
||||
, QString const& name, QDateTime const& QSO_date_on);
|
||||
|
||||
protected:
|
||||
void hideEvent (QHideEvent *);
|
||||
|
||||
private:
|
||||
void loadSettings ();
|
||||
void storeSettings () const;
|
||||
|
||||
QScopedPointer<Ui::LogQSO> ui;
|
||||
QSettings * m_settings;
|
||||
QString m_txPower;
|
||||
QString m_comments;
|
||||
Radio::Frequency m_dialFreq;
|
||||
QString m_myCall;
|
||||
QString m_myGrid;
|
||||
QDateTime m_dateTimeOn;
|
||||
QDateTime m_dateTimeOff;
|
||||
};
|
||||
|
||||
#endif // LogQSO_H
|
||||
Reference in New Issue
Block a user