Merge branch 'decoder-experiments' into ft8call-develop
This commit is contained in:
commit
bb548c3672
71
Detector.cpp
71
Detector.cpp
@ -43,18 +43,9 @@ bool Detector::reset ()
|
|||||||
|
|
||||||
void Detector::clear ()
|
void Detector::clear ()
|
||||||
{
|
{
|
||||||
QMutexLocker mutex(&m_lock);
|
|
||||||
|
|
||||||
#if JS8_RING_BUFFER
|
#if JS8_RING_BUFFER
|
||||||
// set index to roughly where we are in time (1ms resolution)
|
resetBufferPosition();
|
||||||
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
resetBufferContent();
|
||||||
unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
|
|
||||||
int prevKin = dec_data.params.kin;
|
|
||||||
dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
|
|
||||||
m_bufferPos = 0;
|
|
||||||
m_ns=secondInPeriod();
|
|
||||||
memset(dec_data.d2, 0, sizeof(dec_data.d2));
|
|
||||||
qDebug() << "advancing detector buffer from" << prevKin << "to" << dec_data.params.kin << "delta" << dec_data.params.kin - prevKin;
|
|
||||||
#else
|
#else
|
||||||
dec_data.params.kin = 0;
|
dec_data.params.kin = 0;
|
||||||
m_bufferPos = 0;
|
m_bufferPos = 0;
|
||||||
@ -64,6 +55,63 @@ void Detector::clear ()
|
|||||||
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
|
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shift the elements of an array left by n items using a temporary array for efficient moving
|
||||||
|
*
|
||||||
|
* this function moves the leading n elements to the end of the array
|
||||||
|
*
|
||||||
|
* the temporary array needs to be allocated to accept at least n items
|
||||||
|
*/
|
||||||
|
template<typename T> void rotate_array_left(T array[], qint64 size, T temp[], qint64 n) {
|
||||||
|
memcpy(temp, array, n * sizeof(T)); // temporarily save leading n elements
|
||||||
|
memmove(array, array + n, (size - n) * sizeof(T)); // shift array to the left
|
||||||
|
memmove(array + size - n, temp, n * sizeof(T)); // append saved
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shift the elements of an array right by n items using a temporary array for efficient moving
|
||||||
|
*
|
||||||
|
* this function moves the trailing n elements to the start of the array
|
||||||
|
*
|
||||||
|
* the temporary array needs to be allocated to accept at least n items
|
||||||
|
*/
|
||||||
|
template<typename T> void rotate_array_right(T array[], qint64 size, T temp[], qint64 n) {
|
||||||
|
memcpy(temp, array + size - n, n * sizeof(T)); // temporarily save trailing n elements
|
||||||
|
memmove(array + n, array, (size - n) * sizeof(T)); // shift array to the right
|
||||||
|
memcpy(array, temp, n * sizeof(T)); // prepend saved
|
||||||
|
}
|
||||||
|
|
||||||
|
void Detector::resetBufferPosition(){
|
||||||
|
// temporary buffer for efficient copies
|
||||||
|
static short int d0[NTMAX*RX_SAMPLE_RATE];
|
||||||
|
|
||||||
|
QMutexLocker mutex(&m_lock);
|
||||||
|
|
||||||
|
// set index to roughly where we are in time (1ms resolution)
|
||||||
|
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
||||||
|
unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
|
||||||
|
int prevKin = dec_data.params.kin;
|
||||||
|
dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
|
||||||
|
m_bufferPos = 0;
|
||||||
|
m_ns=secondInPeriod();
|
||||||
|
int delta = dec_data.params.kin - prevKin;
|
||||||
|
qDebug() << "advancing detector buffer from" << prevKin << "to" << dec_data.params.kin << "delta" << delta;
|
||||||
|
|
||||||
|
// rotate buffer moving the contents that were at prevKin to the new kin position
|
||||||
|
if(delta < 0){
|
||||||
|
rotate_array_left<short int>(dec_data.d2, NTMAX*RX_SAMPLE_RATE, d0, -delta);
|
||||||
|
} else {
|
||||||
|
rotate_array_right<short int>(dec_data.d2, NTMAX*RX_SAMPLE_RATE, d0, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Detector::resetBufferContent(){
|
||||||
|
QMutexLocker mutex(&m_lock);
|
||||||
|
|
||||||
|
memset(dec_data.d2, 0, sizeof(dec_data.d2));
|
||||||
|
qDebug() << "clearing detector buffer content";
|
||||||
|
}
|
||||||
|
|
||||||
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
||||||
{
|
{
|
||||||
QMutexLocker mutex(&m_lock);
|
QMutexLocker mutex(&m_lock);
|
||||||
@ -138,7 +186,6 @@ unsigned Detector::secondInPeriod () const
|
|||||||
// we take the time of the data as the following assuming no latency
|
// we take the time of the data as the following assuming no latency
|
||||||
// delivering it to us (not true but close enough for us)
|
// delivering it to us (not true but close enough for us)
|
||||||
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
||||||
|
|
||||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
unsigned secondInToday ((now % 86400000LL) / 1000);
|
||||||
return secondInToday % m_period;
|
return secondInToday % m_period;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,9 @@ public:
|
|||||||
Q_SLOT void setBlockSize (unsigned);
|
Q_SLOT void setBlockSize (unsigned);
|
||||||
|
|
||||||
void clear (); // discard buffer contents
|
void clear (); // discard buffer contents
|
||||||
|
void resetBufferPosition();
|
||||||
|
void resetBufferContent();
|
||||||
|
|
||||||
unsigned secondInPeriod () const;
|
unsigned secondInPeriod () const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#define JS8_DECODE_THREAD 1 // use a separate thread for decode process handling
|
#define JS8_DECODE_THREAD 1 // use a separate thread for decode process handling
|
||||||
#define JS8_ALLOW_EXTENDED 1 // allow extended latin-1 capital charset
|
#define JS8_ALLOW_EXTENDED 1 // allow extended latin-1 capital charset
|
||||||
#define JS8_SAVE_AUDIO 0 // enable the save menu
|
#define JS8_SAVE_AUDIO 0 // enable the save menu
|
||||||
|
#define JS8_AUTO_SYNC 1 // enable the experimental auto sync feature
|
||||||
|
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
#define JS8_DEBUG_DECODE 0 // emit debug statements for the decode pipeline
|
#define JS8_DEBUG_DECODE 0 // emit debug statements for the decode pipeline
|
||||||
@ -97,6 +98,7 @@ extern struct dec_data {
|
|||||||
int nfSplit; // JT65 | JT9 split frequency
|
int nfSplit; // JT65 | JT9 split frequency
|
||||||
int nfb; // High decode limit (Hz) (filter max)
|
int nfb; // High decode limit (Hz) (filter max)
|
||||||
int ntol; // +/- decoding range around fQSO (Hz)
|
int ntol; // +/- decoding range around fQSO (Hz)
|
||||||
|
bool syncStats; // only compute sync candidates
|
||||||
int kin; // number of frames written to d2
|
int kin; // number of frames written to d2
|
||||||
int kposA; // starting position of decode for submode A
|
int kposA; // starting position of decode for submode A
|
||||||
int kposB; // starting position of decode for submode B
|
int kposB; // starting position of decode for submode B
|
||||||
|
@ -26,6 +26,7 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
|
|||||||
, isAlt_(false)
|
, isAlt_(false)
|
||||||
, bits_{0}
|
, bits_{0}
|
||||||
, submode_{ string_.mid(column_mode + padding_, 3).trimmed().at(0).cell() - 'A' }
|
, submode_{ string_.mid(column_mode + padding_, 3).trimmed().at(0).cell() - 'A' }
|
||||||
|
, frame_ { string_.mid (column_qsoText + padding_, 12).trimmed () }
|
||||||
{
|
{
|
||||||
if(message_.length() >= 1) {
|
if(message_.length() >= 1) {
|
||||||
message_ = message_.left (21).remove (QRegularExpression {"[<>]"});
|
message_ = message_.left (21).remove (QRegularExpression {"[<>]"});
|
||||||
@ -71,7 +72,8 @@ DecodedText::DecodedText (QString const& js8callmessage, int bits, int submode):
|
|||||||
isHeartbeat_(false),
|
isHeartbeat_(false),
|
||||||
isAlt_(false),
|
isAlt_(false),
|
||||||
bits_(bits),
|
bits_(bits),
|
||||||
submode_(submode)
|
submode_(submode),
|
||||||
|
frame_(js8callmessage)
|
||||||
{
|
{
|
||||||
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
|
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ public:
|
|||||||
bool tryUnpackFastData();
|
bool tryUnpackFastData();
|
||||||
|
|
||||||
quint8 frameType() const { return frameType_; }
|
quint8 frameType() const { return frameType_; }
|
||||||
|
QString frame() const { return frame_; }
|
||||||
|
|
||||||
QString extra() const { return extra_; }
|
QString extra() const { return extra_; }
|
||||||
QString compoundCall() const { return compound_; }
|
QString compoundCall() const { return compound_; }
|
||||||
@ -113,6 +114,7 @@ private:
|
|||||||
bool is_standard_;
|
bool is_standard_;
|
||||||
int bits_;
|
int bits_;
|
||||||
int submode_;
|
int submode_;
|
||||||
|
QString frame_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DECODEDTEXT_H
|
#endif // DECODEDTEXT_H
|
||||||
|
101
lib/decoder.f90
101
lib/decoder.f90
@ -88,13 +88,28 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
pos = max(0,params%kposI)
|
pos = max(0,params%kposI)
|
||||||
sz = max(0,params%kszI)
|
sz = max(0,params%kszI)
|
||||||
id0=0
|
id0=0
|
||||||
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
imax=int(NTMAX*12000)
|
||||||
|
|
||||||
|
if(params%syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncMeta> sync start', pos, sz
|
||||||
|
endif
|
||||||
|
|
||||||
|
if((imax-pos).lt.sz) then
|
||||||
|
! this means that the first part of the id0 is at the end of the buffer
|
||||||
|
! and the second half is at the beginning of the buffer
|
||||||
|
firstsize=int(imax-pos)-1
|
||||||
|
secondsize=int(sz-firstsize)+1
|
||||||
|
id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1)
|
||||||
|
id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1)
|
||||||
|
else
|
||||||
|
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
||||||
|
endif
|
||||||
|
|
||||||
call my_js8i%decode(js8i_decoded,id0,params%nQSOProgress,params%nfqso, &
|
call my_js8i%decode(js8i_decoded,id0,params%nQSOProgress,params%nfqso, &
|
||||||
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
||||||
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
||||||
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
||||||
mycall,mygrid,hiscall,hisgrid)
|
mycall,mygrid,hiscall,hisgrid,logical(params%syncStats))
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> mode I decode finished'
|
write(*,*) '<DecodeDebug> mode I decode finished'
|
||||||
|
|
||||||
@ -111,13 +126,28 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
pos = max(0,params%kposE)
|
pos = max(0,params%kposE)
|
||||||
sz = max(0,params%kszE)
|
sz = max(0,params%kszE)
|
||||||
id0=0
|
id0=0
|
||||||
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
imax=int(NTMAX*12000)
|
||||||
|
|
||||||
|
if(params%syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncMeta> sync start', pos, sz
|
||||||
|
endif
|
||||||
|
|
||||||
|
if((imax-pos).lt.sz) then
|
||||||
|
! this means that the first part of the id0 is at the end of the buffer
|
||||||
|
! and the second half is at the beginning of the buffer
|
||||||
|
firstsize=int(imax-pos)-1
|
||||||
|
secondsize=int(sz-firstsize)+1
|
||||||
|
id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1)
|
||||||
|
id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1)
|
||||||
|
else
|
||||||
|
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
||||||
|
endif
|
||||||
|
|
||||||
call my_js8e%decode(js8e_decoded,id0,params%nQSOProgress,params%nfqso, &
|
call my_js8e%decode(js8e_decoded,id0,params%nQSOProgress,params%nfqso, &
|
||||||
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
||||||
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
||||||
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
||||||
mycall,mygrid,hiscall,hisgrid)
|
mycall,mygrid,hiscall,hisgrid,logical(params%syncStats))
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> mode E decode finished'
|
write(*,*) '<DecodeDebug> mode E decode finished'
|
||||||
|
|
||||||
@ -134,13 +164,28 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
pos = max(0,params%kposC)
|
pos = max(0,params%kposC)
|
||||||
sz = max(0,params%kszC)
|
sz = max(0,params%kszC)
|
||||||
id0=0
|
id0=0
|
||||||
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
imax=int(NTMAX*12000)
|
||||||
|
|
||||||
|
if(params%syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncMeta> sync start', pos, sz
|
||||||
|
endif
|
||||||
|
|
||||||
|
if((imax-pos).lt.sz) then
|
||||||
|
! this means that the first part of the id0 is at the end of the buffer
|
||||||
|
! and the second half is at the beginning of the buffer
|
||||||
|
firstsize=int(imax-pos)-1
|
||||||
|
secondsize=int(sz-firstsize)+1
|
||||||
|
id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1)
|
||||||
|
id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1)
|
||||||
|
else
|
||||||
|
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
||||||
|
endif
|
||||||
|
|
||||||
call my_js8c%decode(js8c_decoded,id0,params%nQSOProgress,params%nfqso, &
|
call my_js8c%decode(js8c_decoded,id0,params%nQSOProgress,params%nfqso, &
|
||||||
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
||||||
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
||||||
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
||||||
mycall,mygrid,hiscall,hisgrid)
|
mycall,mygrid,hiscall,hisgrid,logical(params%syncStats))
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> mode C decode finished'
|
write(*,*) '<DecodeDebug> mode C decode finished'
|
||||||
|
|
||||||
@ -157,13 +202,28 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
pos = max(0,params%kposB)
|
pos = max(0,params%kposB)
|
||||||
sz = max(0,params%kszB)
|
sz = max(0,params%kszB)
|
||||||
id0=0
|
id0=0
|
||||||
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
imax=int(NTMAX*12000)
|
||||||
|
|
||||||
|
if(params%syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncMeta> sync start', pos, sz
|
||||||
|
endif
|
||||||
|
|
||||||
|
if((imax-pos).lt.sz) then
|
||||||
|
! this means that the first part of the id0 is at the end of the buffer
|
||||||
|
! and the second half is at the beginning of the buffer
|
||||||
|
firstsize=int(imax-pos)-1
|
||||||
|
secondsize=int(sz-firstsize)+1
|
||||||
|
id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1)
|
||||||
|
id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1)
|
||||||
|
else
|
||||||
|
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
||||||
|
endif
|
||||||
|
|
||||||
call my_js8b%decode(js8b_decoded,id0,params%nQSOProgress,params%nfqso, &
|
call my_js8b%decode(js8b_decoded,id0,params%nQSOProgress,params%nfqso, &
|
||||||
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
||||||
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
||||||
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
||||||
mycall,mygrid,hiscall,hisgrid)
|
mycall,mygrid,hiscall,hisgrid,logical(params%syncStats))
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> mode B decode finished'
|
write(*,*) '<DecodeDebug> mode B decode finished'
|
||||||
|
|
||||||
@ -177,16 +237,31 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
write(*,*) '<DecodeDebug> mode A decode started'
|
write(*,*) '<DecodeDebug> mode A decode started'
|
||||||
|
|
||||||
! copy the relevant frames for decoding
|
! copy the relevant frames for decoding
|
||||||
pos = max(0,params%kposA)
|
pos = int(max(0,params%kposA))
|
||||||
sz = max(0,params%kszA)
|
sz = int(max(0,params%kszA))
|
||||||
id0=0
|
id0=0
|
||||||
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
imax=int(NTMAX*12000)
|
||||||
|
|
||||||
|
if(params%syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncMeta> sync start', pos, sz
|
||||||
|
endif
|
||||||
|
|
||||||
|
if((imax-pos).lt.sz) then
|
||||||
|
! this means that the first part of the id0 is at the end of the buffer
|
||||||
|
! and the second half is at the beginning of the buffer
|
||||||
|
firstsize=int(imax-pos)-1
|
||||||
|
secondsize=int(sz-firstsize)+1
|
||||||
|
id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1)
|
||||||
|
id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1)
|
||||||
|
else
|
||||||
|
id0(1:sz+1)=id2(pos+1:pos+sz+1)
|
||||||
|
endif
|
||||||
|
|
||||||
call my_js8a%decode(js8a_decoded,id0,params%nQSOProgress,params%nfqso, &
|
call my_js8a%decode(js8a_decoded,id0,params%nQSOProgress,params%nfqso, &
|
||||||
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
|
||||||
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
params%nexp_decode,params%ndepth,logical(params%nagain), &
|
||||||
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
|
||||||
mycall,mygrid,hiscall,hisgrid)
|
mycall,mygrid,hiscall,hisgrid,logical(params%syncStats))
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> mode A decode finished'
|
write(*,*) '<DecodeDebug> mode A decode finished'
|
||||||
|
|
||||||
@ -196,7 +271,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
write(*,*) '<DecodeDebug> finished'
|
write(*,*) '<DecodeDebug> finished'
|
||||||
call flush(6)
|
call flush(6)
|
||||||
|
|
||||||
ndecoded = my_js8a%decoded + my_js8b%decoded + my_js8c%decoded + my_js8e%decoded
|
ndecoded = my_js8a%decoded + my_js8b%decoded + my_js8c%decoded + my_js8e%decoded + my_js8i%decoded
|
||||||
!call sleep_msec(3000)
|
!call sleep_msec(3000)
|
||||||
write(*,1010) ndecoded
|
write(*,1010) ndecoded
|
||||||
1010 format('<DecodeFinished>',i4)
|
1010 format('<DecodeFinished>',i4)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
! When modifying this file, please ensure the modifications are made in ft8_params.f90 too.
|
! When modifying this file, please ensure the modifications are made in ft8_params.f90 too.
|
||||||
|
|
||||||
|
parameter (NSUBMODE=0)
|
||||||
parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
||||||
|
|
||||||
parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s
|
parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
parameter (NSUBMODE=1)
|
||||||
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
||||||
|
|
||||||
parameter (NSPS=1200, NTXDUR=10, NDOWNSPS=20, NDD=100, JZ=144) ! 80 Hz 10 baud 24 wpm -23.0dB (1.0Eb/N0) 7.90s
|
parameter (NSPS=1200, NTXDUR=10, NDOWNSPS=20, NDD=100, JZ=144) ! 80 Hz 10 baud 24 wpm -23.0dB (1.0Eb/N0) 7.90s
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
parameter (NSUBMODE=2)
|
||||||
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
||||||
|
|
||||||
parameter (NSPS=600, NTXDUR=6, NDOWNSPS=12, NDD=120, JZ=172) ! 160 Hz 20 baud 40 wpm -20.0dB (1.0Eb/N0) 3.95s
|
parameter (NSPS=600, NTXDUR=6, NDOWNSPS=12, NDD=120, JZ=172) ! 160 Hz 20 baud 40 wpm -20.0dB (1.0Eb/N0) 3.95s
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
|
subroutine js8dec(dd0,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
|
||||||
napwid,lsubtract,nagain,iaptype,mycall12,mygrid6,hiscall12,bcontest, &
|
napwid,lsubtract,nagain,iaptype,mycall12,mygrid6,hiscall12,bcontest, &
|
||||||
sync0,f1,xdt,xbase,apsym,nharderrors,dmin,nbadcrc,ipass,iera,msg37,xsnr)
|
sync0,f1,xdt,xbase,apsym,nharderrors,dmin,nbadcrc,ipass,iera,msg37,xsnr)
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly
|
|||||||
character*12 mycall12,hiscall12
|
character*12 mycall12,hiscall12
|
||||||
character*6 mycall6,mygrid6,hiscall6,c1,c2
|
character*6 mycall6,mygrid6,hiscall6,c1,c2
|
||||||
character*87 cbits
|
character*87 cbits
|
||||||
logical bcontest
|
logical bcontest,syncStats
|
||||||
real a(5)
|
real a(5)
|
||||||
real s1(0:7,ND),s2(0:7,NN),s1sort(8*ND)
|
real s1(0:7,ND),s2(0:7,NN),s1sort(8*ND)
|
||||||
real ps(0:7),psl(0:7)
|
real ps(0:7),psl(0:7)
|
||||||
@ -224,6 +224,11 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncStat> candidate ', NSUBMODE, 'f1', f1, 'sync', nsync, 'xdt', xdt
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
j=0
|
j=0
|
||||||
do k=1,NN
|
do k=1,NN
|
||||||
if(k.le.7) cycle
|
if(k.le.7) cycle
|
||||||
@ -417,6 +422,11 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly
|
|||||||
i3bit=4*decoded(73) + 2*decoded(74) + decoded(75)
|
i3bit=4*decoded(73) + 2*decoded(74) + decoded(75)
|
||||||
|
|
||||||
if(nbadcrc.eq.0) then
|
if(nbadcrc.eq.0) then
|
||||||
|
if(syncStats) then
|
||||||
|
write(*,*) '<DecodeSyncStat> decode ', NSUBMODE, 'f1', f1, 'sync', (sync*10), 'xdt', xdt2
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
decoded0=decoded
|
decoded0=decoded
|
||||||
call extractmessage174(decoded,origmsg,ncrcflag)
|
call extractmessage174(decoded,origmsg,ncrcflag)
|
||||||
decoded=decoded0
|
decoded=decoded0
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
parameter (NSUBMODE=4)
|
||||||
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
||||||
|
|
||||||
parameter (NSPS=3840, NTXDUR=28, NDOWNSPS=32, NDD=90, JZ=32) ! 25 Hz 3.125 baud 8 wpm -28.0dB (1.0Eb/N0) 25.28s
|
parameter (NSPS=3840, NTXDUR=28, NDOWNSPS=32, NDD=90, JZ=32) ! 25 Hz 3.125 baud 8 wpm -28.0dB (1.0Eb/N0) 25.28s
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
parameter (NSUBMODE=8)
|
||||||
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas)
|
||||||
|
|
||||||
parameter (NSPS=384, NTXDUR=4, NDOWNSPS=12, NDD=125, JZ=250) ! 250 Hz 31.25 baud 60 wpm -18.0dB (1.0Eb/N0) 2.52s
|
parameter (NSPS=384, NTXDUR=4, NDOWNSPS=12, NDD=125, JZ=250) ! 250 Hz 31.25 baud 60 wpm -18.0dB (1.0Eb/N0) 2.52s
|
||||||
|
@ -25,7 +25,7 @@ contains
|
|||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
||||||
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
||||||
mycall12,mygrid6,hiscall12,hisgrid6)
|
mycall12,mygrid6,hiscall12,hisgrid6,syncStats)
|
||||||
! use wavhdr
|
! use wavhdr
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
! type(hdr) h
|
! type(hdr) h
|
||||||
@ -38,7 +38,7 @@ contains
|
|||||||
real candidate(3,NMAXCAND)
|
real candidate(3,NMAXCAND)
|
||||||
real dd(NMAX)
|
real dd(NMAX)
|
||||||
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
||||||
logical newdat,lsubtract,ldupe,bcontest
|
logical newdat,lsubtract,ldupe,bcontest,syncStats
|
||||||
character*12 mycall12, hiscall12
|
character*12 mycall12, hiscall12
|
||||||
character*6 mygrid6,hisgrid6
|
character*6 mygrid6,hisgrid6
|
||||||
integer*2 iwave(NMAX)
|
integer*2 iwave(NMAX)
|
||||||
@ -93,25 +93,15 @@ contains
|
|||||||
lsubtract=.false.
|
lsubtract=.false.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(NWRITELOG.eq.1) then
|
||||||
|
write(*,*) '<DecodeDebug> pass', ipass, 'of', npass, 'subtract', lsubtract
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
call timer('syncjs8 ',0)
|
call timer('syncjs8 ',0)
|
||||||
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
||||||
call timer('syncjs8 ',1)
|
call timer('syncjs8 ',1)
|
||||||
|
|
||||||
if(NWRITELOG.eq.1) then
|
|
||||||
write(*,*) '<DecodeDebug>', ncand, "candidates"
|
|
||||||
flush(6)
|
|
||||||
|
|
||||||
do icand=1,ncand
|
|
||||||
sync=candidate(3,icand)
|
|
||||||
f1=candidate(1,icand)
|
|
||||||
xdt=candidate(2,icand)
|
|
||||||
xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz
|
|
||||||
|
|
||||||
write(*,*) '<DecodeDebug> candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase
|
|
||||||
flush(6)
|
|
||||||
enddo
|
|
||||||
endif
|
|
||||||
|
|
||||||
do icand=1,ncand
|
do icand=1,ncand
|
||||||
sync=candidate(3,icand)
|
sync=candidate(3,icand)
|
||||||
f1=candidate(1,icand)
|
f1=candidate(1,icand)
|
||||||
@ -124,7 +114,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',0)
|
call timer('js8dec ',0)
|
||||||
call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
||||||
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
||||||
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
||||||
nbadcrc,iappass,iera,msg37,xsnr)
|
nbadcrc,iappass,iera,msg37,xsnr)
|
||||||
@ -137,7 +127,7 @@ contains
|
|||||||
write(*,*) '<DecodeDebug> candidate', icand, 'hard', hd, 'nbadcrc', nbadcrc
|
write(*,*) '<DecodeDebug> candidate', icand, 'hard', hd, 'nbadcrc', nbadcrc
|
||||||
flush(6)
|
flush(6)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',1)
|
call timer('js8dec ',1)
|
||||||
if(nbadcrc.eq.0) then
|
if(nbadcrc.eq.0) then
|
||||||
ldupe=.false.
|
ldupe=.false.
|
||||||
|
@ -25,7 +25,7 @@ contains
|
|||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
||||||
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
||||||
mycall12,mygrid6,hiscall12,hisgrid6)
|
mycall12,mygrid6,hiscall12,hisgrid6,syncStats)
|
||||||
! use wavhdr
|
! use wavhdr
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
! type(hdr) h
|
! type(hdr) h
|
||||||
@ -38,7 +38,7 @@ contains
|
|||||||
real candidate(3,NMAXCAND)
|
real candidate(3,NMAXCAND)
|
||||||
real dd(NMAX)
|
real dd(NMAX)
|
||||||
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
||||||
logical newdat,lsubtract,ldupe,bcontest
|
logical newdat,lsubtract,ldupe,bcontest,syncStats
|
||||||
character*12 mycall12, hiscall12
|
character*12 mycall12, hiscall12
|
||||||
character*6 mygrid6,hisgrid6
|
character*6 mygrid6,hisgrid6
|
||||||
integer*2 iwave(NMAX)
|
integer*2 iwave(NMAX)
|
||||||
@ -93,6 +93,11 @@ contains
|
|||||||
lsubtract=.false.
|
lsubtract=.false.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(NWRITELOG.eq.1) then
|
||||||
|
write(*,*) '<DecodeDebug> pass', ipass, 'of', npass, 'subtract', lsubtract
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
call timer('syncjs8 ',0)
|
call timer('syncjs8 ',0)
|
||||||
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
||||||
call timer('syncjs8 ',1)
|
call timer('syncjs8 ',1)
|
||||||
@ -114,7 +119,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',0)
|
call timer('js8dec ',0)
|
||||||
call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
||||||
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
||||||
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
||||||
nbadcrc,iappass,iera,msg37,xsnr)
|
nbadcrc,iappass,iera,msg37,xsnr)
|
||||||
|
@ -25,7 +25,7 @@ contains
|
|||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
||||||
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
||||||
mycall12,mygrid6,hiscall12,hisgrid6)
|
mycall12,mygrid6,hiscall12,hisgrid6,syncStats)
|
||||||
! use wavhdr
|
! use wavhdr
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
! type(hdr) h
|
! type(hdr) h
|
||||||
@ -38,7 +38,7 @@ contains
|
|||||||
real candidate(3,NMAXCAND)
|
real candidate(3,NMAXCAND)
|
||||||
real dd(NMAX)
|
real dd(NMAX)
|
||||||
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
||||||
logical newdat,lsubtract,ldupe,bcontest
|
logical newdat,lsubtract,ldupe,bcontest,syncStats
|
||||||
character*12 mycall12, hiscall12
|
character*12 mycall12, hiscall12
|
||||||
character*6 mygrid6,hisgrid6
|
character*6 mygrid6,hisgrid6
|
||||||
integer*2 iwave(NMAX)
|
integer*2 iwave(NMAX)
|
||||||
@ -93,6 +93,11 @@ contains
|
|||||||
lsubtract=.false.
|
lsubtract=.false.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(NWRITELOG.eq.1) then
|
||||||
|
write(*,*) '<DecodeDebug> pass', ipass, 'of', npass, 'subtract', lsubtract
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
call timer('syncjs8 ',0)
|
call timer('syncjs8 ',0)
|
||||||
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
||||||
call timer('syncjs8 ',1)
|
call timer('syncjs8 ',1)
|
||||||
@ -114,7 +119,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',0)
|
call timer('js8dec ',0)
|
||||||
call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
||||||
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
||||||
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
||||||
nbadcrc,iappass,iera,msg37,xsnr)
|
nbadcrc,iappass,iera,msg37,xsnr)
|
||||||
|
@ -25,7 +25,7 @@ contains
|
|||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
||||||
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
||||||
mycall12,mygrid6,hiscall12,hisgrid6)
|
mycall12,mygrid6,hiscall12,hisgrid6,syncStats)
|
||||||
! use wavhdr
|
! use wavhdr
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
! type(hdr) h
|
! type(hdr) h
|
||||||
@ -38,7 +38,7 @@ contains
|
|||||||
real candidate(3,NMAXCAND)
|
real candidate(3,NMAXCAND)
|
||||||
real dd(NMAX)
|
real dd(NMAX)
|
||||||
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
||||||
logical newdat,lsubtract,ldupe,bcontest
|
logical newdat,lsubtract,ldupe,bcontest,syncStats
|
||||||
character*12 mycall12, hiscall12
|
character*12 mycall12, hiscall12
|
||||||
character*6 mygrid6,hisgrid6
|
character*6 mygrid6,hisgrid6
|
||||||
integer*2 iwave(NMAX)
|
integer*2 iwave(NMAX)
|
||||||
@ -93,6 +93,11 @@ contains
|
|||||||
lsubtract=.false.
|
lsubtract=.false.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(NWRITELOG.eq.1) then
|
||||||
|
write(*,*) '<DecodeDebug> pass', ipass, 'of', npass, 'subtract', lsubtract
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
call timer('syncjs8 ',0)
|
call timer('syncjs8 ',0)
|
||||||
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
||||||
call timer('syncjs8 ',1)
|
call timer('syncjs8 ',1)
|
||||||
@ -114,7 +119,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',0)
|
call timer('js8dec ',0)
|
||||||
call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
||||||
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
||||||
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
||||||
nbadcrc,iappass,iera,msg37,xsnr)
|
nbadcrc,iappass,iera,msg37,xsnr)
|
||||||
|
@ -25,7 +25,7 @@ contains
|
|||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, &
|
||||||
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, &
|
||||||
mycall12,mygrid6,hiscall12,hisgrid6)
|
mycall12,mygrid6,hiscall12,hisgrid6,syncStats)
|
||||||
! use wavhdr
|
! use wavhdr
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
! type(hdr) h
|
! type(hdr) h
|
||||||
@ -38,7 +38,7 @@ contains
|
|||||||
real candidate(3,NMAXCAND)
|
real candidate(3,NMAXCAND)
|
||||||
real dd(NMAX)
|
real dd(NMAX)
|
||||||
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
logical, intent(in) :: lft8apon,lapcqonly,nagain
|
||||||
logical newdat,lsubtract,ldupe,bcontest
|
logical newdat,lsubtract,ldupe,bcontest,syncStats
|
||||||
character*12 mycall12, hiscall12
|
character*12 mycall12, hiscall12
|
||||||
character*6 mygrid6,hisgrid6
|
character*6 mygrid6,hisgrid6
|
||||||
integer*2 iwave(NMAX)
|
integer*2 iwave(NMAX)
|
||||||
@ -93,6 +93,11 @@ contains
|
|||||||
lsubtract=.false.
|
lsubtract=.false.
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(NWRITELOG.eq.1) then
|
||||||
|
write(*,*) '<DecodeDebug> pass', ipass, 'of', npass, 'subtract', lsubtract
|
||||||
|
flush(6)
|
||||||
|
endif
|
||||||
|
|
||||||
call timer('syncjs8 ',0)
|
call timer('syncjs8 ',0)
|
||||||
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase)
|
||||||
call timer('syncjs8 ',1)
|
call timer('syncjs8 ',1)
|
||||||
@ -114,7 +119,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
call timer('js8dec ',0)
|
call timer('js8dec ',0)
|
||||||
call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, &
|
||||||
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, &
|
||||||
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, &
|
||||||
nbadcrc,iappass,iera,msg37,xsnr)
|
nbadcrc,iappass,iera,msg37,xsnr)
|
||||||
|
10
lib/jt9.f90
10
lib/jt9.f90
@ -21,8 +21,8 @@ program jt9
|
|||||||
!### ndepth was defined as 60001. Why???
|
!### ndepth was defined as 60001. Why???
|
||||||
integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, &
|
integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, &
|
||||||
fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0
|
fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0
|
||||||
logical :: read_files = .true., tx9 = .false., display_help = .false.
|
logical :: read_files = .true., tx9 = .false., display_help = .false., syncStats = .false.
|
||||||
type (option) :: long_options(21) = [ &
|
type (option) :: long_options(22) = [ &
|
||||||
option ('help', .false., 'h', 'Display this help message', ''), &
|
option ('help', .false., 'h', 'Display this help message', ''), &
|
||||||
option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), &
|
option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), &
|
||||||
option ('tr-period', .true., 'p', 'Tx/Rx period, default MINUTES=1', &
|
option ('tr-period', .true., 'p', 'Tx/Rx period, default MINUTES=1', &
|
||||||
@ -50,6 +50,7 @@ program jt9
|
|||||||
!option ('jt65', .false., '6', 'JT65 mode', ''), &
|
!option ('jt65', .false., '6', 'JT65 mode', ''), &
|
||||||
!option ('jt9', .false., '9', 'JT9 mode', ''), &
|
!option ('jt9', .false., '9', 'JT9 mode', ''), &
|
||||||
option ('js8', .false., '8', 'JS8 mode', ''), &
|
option ('js8', .false., '8', 'JS8 mode', ''), &
|
||||||
|
option ('syncStats', .false., 'y', 'Sync only', ''), &
|
||||||
!option ('jt4', .false., '4', 'JT4 mode', ''), &
|
!option ('jt4', .false., '4', 'JT4 mode', ''), &
|
||||||
!option ('qra64', .false., 'q', 'QRA64 mode', ''), &
|
!option ('qra64', .false., 'q', 'QRA64 mode', ''), &
|
||||||
option ('sub-mode', .true., 'b', 'Sub mode, default SUBMODE=A', 'A'), &
|
option ('sub-mode', .true., 'b', 'Sub mode, default SUBMODE=A', 'A'), &
|
||||||
@ -118,6 +119,8 @@ program jt9
|
|||||||
! if (mode.lt.9.or.mode.eq.65) mode = mode + 9
|
! if (mode.lt.9.or.mode.eq.65) mode = mode + 9
|
||||||
case ('8')
|
case ('8')
|
||||||
mode = 8
|
mode = 8
|
||||||
|
case ('y')
|
||||||
|
syncStats = .true.
|
||||||
case ('T')
|
case ('T')
|
||||||
tx9 = .true.
|
tx9 = .true.
|
||||||
case ('w')
|
case ('w')
|
||||||
@ -254,6 +257,7 @@ program jt9
|
|||||||
shared_data%params%ljt65apon=.true.
|
shared_data%params%ljt65apon=.true.
|
||||||
shared_data%params%napwid=75
|
shared_data%params%napwid=75
|
||||||
shared_data%params%dttol=3.
|
shared_data%params%dttol=3.
|
||||||
|
shared_data%params%syncStats=syncStats
|
||||||
|
|
||||||
! shared_data%params%minsync=0 !### TEST ONLY
|
! shared_data%params%minsync=0 !### TEST ONLY
|
||||||
! shared_data%params%nfqso=1500 !### TEST ONLY
|
! shared_data%params%nfqso=1500 !### TEST ONLY
|
||||||
@ -290,10 +294,12 @@ program jt9
|
|||||||
shared_data%params%kposB=0
|
shared_data%params%kposB=0
|
||||||
shared_data%params%kposC=0
|
shared_data%params%kposC=0
|
||||||
shared_data%params%kposE=0
|
shared_data%params%kposE=0
|
||||||
|
shared_data%params%kposI=0
|
||||||
shared_data%params%kszA=NMAX-1
|
shared_data%params%kszA=NMAX-1
|
||||||
shared_data%params%kszB=NMAX-1
|
shared_data%params%kszB=NMAX-1
|
||||||
shared_data%params%kszC=NMAX-1
|
shared_data%params%kszC=NMAX-1
|
||||||
shared_data%params%kszE=NMAX-1
|
shared_data%params%kszE=NMAX-1
|
||||||
|
shared_data%params%kszI=NMAX-1
|
||||||
call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample)
|
call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample)
|
||||||
enddo
|
enddo
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
integer(c_int) :: nfsplit
|
integer(c_int) :: nfsplit
|
||||||
integer(c_int) :: nfb
|
integer(c_int) :: nfb
|
||||||
integer(c_int) :: ntol
|
integer(c_int) :: ntol
|
||||||
|
logical(c_bool) :: syncStats
|
||||||
integer(c_int) :: kin
|
integer(c_int) :: kin
|
||||||
integer(c_int) :: kposA
|
integer(c_int) :: kposA
|
||||||
integer(c_int) :: kposB
|
integer(c_int) :: kposB
|
||||||
|
719
mainwindow.cpp
719
mainwindow.cpp
@ -810,6 +810,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
|
|
||||||
connect(m_wideGraph.data(), &WideGraph::qsy, this, &MainWindow::qsy);
|
connect(m_wideGraph.data(), &WideGraph::qsy, this, &MainWindow::qsy);
|
||||||
|
|
||||||
|
connect(m_wideGraph.data(), &WideGraph::drifted, this, &MainWindow::drifted);
|
||||||
|
|
||||||
decodeBusy(false);
|
decodeBusy(false);
|
||||||
QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW",
|
QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW",
|
||||||
"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW",
|
"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW",
|
||||||
@ -2636,36 +2638,73 @@ int MainWindow::computeStop(int submode, int period){
|
|||||||
return stop;
|
return stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
// int MainWindow::computeCurrentCycle(int period){
|
/**
|
||||||
// return m_detector->secondInPeriod() / period;
|
* @brief MainWindow::computeCycleForDecode
|
||||||
// }
|
*
|
||||||
//
|
* compute which cycle we are currently in based on a submode frames per cycle and our current k position
|
||||||
// int MainWindow::computeCycleStartForDecode(int cycle, int period){
|
*
|
||||||
// qint32 samplesPerCycle = period * RX_SAMPLE_RATE;
|
* @param submode
|
||||||
// return cycle * samplesPerCycle;
|
* @param k
|
||||||
// }
|
* @return
|
||||||
|
*/
|
||||||
int MainWindow::computeCycleForDecode(int submode, int k){
|
int MainWindow::computeCycleForDecode(int submode, int k){
|
||||||
qint32 maxFrames = m_detector->period() * RX_SAMPLE_RATE;
|
qint32 maxFrames = NTMAX * RX_SAMPLE_RATE;
|
||||||
qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
|
qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
|
||||||
qint32 currentCycle = (k / cycleFrames) % (maxFrames / cycleFrames); // we mod here so we loop back to zero correctly
|
qint32 currentCycle = (k / cycleFrames) % (maxFrames / cycleFrames); // we mod here so we loop back to zero correctly
|
||||||
return currentCycle;
|
return currentCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MainWindow::computeAltCycleForDecode
|
||||||
|
*
|
||||||
|
* compute an alternate cycle offset by a specific number of frames
|
||||||
|
*
|
||||||
|
* e.g., if we want the 0 cycle to start at second 5, we'd provide an offset of 5*RX_SAMPLE_RATE
|
||||||
|
*
|
||||||
|
* @param submode
|
||||||
|
* @param k
|
||||||
|
* @param offsetFrames
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
int MainWindow::computeAltCycleForDecode(int submode, int k, int offsetFrames){
|
||||||
|
int altK = k - offsetFrames;
|
||||||
|
if(altK < 0){
|
||||||
|
altK += NTMAX * RX_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
return computeCycleForDecode(submode, altK);
|
||||||
|
}
|
||||||
|
|
||||||
int MainWindow::computeFramesPerCycleForDecode(int submode){
|
int MainWindow::computeFramesPerCycleForDecode(int submode){
|
||||||
return computePeriodForSubmode(submode) * RX_SAMPLE_RATE;
|
return computePeriodForSubmode(submode) * RX_SAMPLE_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MainWindow::computeFramesNeededForDecode(int submode){
|
int MainWindow::computePeriodStartDelayForDecode(int submode){
|
||||||
int symbolSamples = 0;
|
int delay = 0;
|
||||||
float threshold = 0.0;
|
|
||||||
switch(submode){
|
switch(submode){
|
||||||
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; threshold = JS8A_START_DELAY_MS/1000.0 + 0.5; break;
|
case Varicode::JS8CallNormal: delay = JS8A_START_DELAY_MS; break;
|
||||||
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; threshold = JS8B_START_DELAY_MS/1000.0 + 0.5; break;
|
case Varicode::JS8CallFast: delay = JS8B_START_DELAY_MS; break;
|
||||||
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; threshold = JS8C_START_DELAY_MS/1000.0 + 0.5; break;
|
case Varicode::JS8CallTurbo: delay = JS8C_START_DELAY_MS; break;
|
||||||
case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; threshold = JS8E_START_DELAY_MS/1000.0 + 0.5; break;
|
case Varicode::JS8CallSlow: delay = JS8E_START_DELAY_MS; break;
|
||||||
case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; threshold = JS8I_START_DELAY_MS/1000.0 + 0.5; break;
|
case Varicode::JS8CallUltra: delay = JS8I_START_DELAY_MS; break;
|
||||||
}
|
}
|
||||||
|
return delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::computeFramesPerSymbolForDecode(int submode){
|
||||||
|
int symbolSamples = 0;
|
||||||
|
switch(submode){
|
||||||
|
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; break;
|
||||||
|
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; break;
|
||||||
|
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; break;
|
||||||
|
case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; break;
|
||||||
|
case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; break;
|
||||||
|
}
|
||||||
|
return symbolSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::computeFramesNeededForDecode(int submode){
|
||||||
|
float threshold = 0.5 + computePeriodStartDelayForDecode(submode)/1000.0;
|
||||||
|
int symbolSamples = computeFramesPerSymbolForDecode(submode);
|
||||||
return int(qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)));
|
return int(qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2686,6 +2725,8 @@ void MainWindow::dataSink(qint64 frames)
|
|||||||
k0 = k;
|
k0 = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//qDebug() << "k" << k << "k0" << k0 << "delta" << k-k0;
|
||||||
|
|
||||||
#if JS8_USE_REFSPEC
|
#if JS8_USE_REFSPEC
|
||||||
QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))};
|
QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))};
|
||||||
QByteArray bafname = fname.toLatin1();
|
QByteArray bafname = fname.toLatin1();
|
||||||
@ -4214,10 +4255,19 @@ bool MainWindow::decode(qint32 k){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ready = decodeEnqueueReady(k, kZero);
|
bool ready = false;
|
||||||
|
|
||||||
|
#if JS8_USE_EXPERIMENTAL_DECODE_TIMING
|
||||||
|
ready = decodeEnqueueReady(k, kZero);
|
||||||
if(ready || !m_decoderQueue.isEmpty()){
|
if(ready || !m_decoderQueue.isEmpty()){
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder is ready to be run with" << m_decoderQueue.count() << "decode periods";
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder is ready to be run with" << m_decoderQueue.count() << "decode periods";
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
ready = decodeEnqueueReadyExperiment(k, kZero);
|
||||||
|
if(ready || !m_decoderQueue.isEmpty()){
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder is ready to be run with" << m_decoderQueue.count() << "decode periods";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// TODO: what follows can likely be pulled out to an async process
|
// TODO: what follows can likely be pulled out to an async process
|
||||||
@ -4232,6 +4282,11 @@ bool MainWindow::decode(qint32 k){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_decoderBusyStartTime.isValid() && m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()) < 1000){
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder paused for 1000 ms after last decode start";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int threshold = m_nSubMode == Varicode::JS8CallSlow ? 4000 : 2000; // two seconds
|
int threshold = m_nSubMode == Varicode::JS8CallSlow ? 4000 : 2000; // two seconds
|
||||||
if(isInDecodeDelayThreshold(threshold)){
|
if(isInDecodeDelayThreshold(threshold)){
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder paused for" << threshold << "ms after transmit stop";
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder paused for" << threshold << "ms after transmit stop";
|
||||||
@ -4265,14 +4320,41 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
// compute the next decode for each submode
|
// compute the next decode for each submode
|
||||||
// enqueue those decodes that are "ready"
|
// enqueue those decodes that are "ready"
|
||||||
// on an interval, issue a decode
|
// on an interval, issue a decode
|
||||||
|
int decodes = 0;
|
||||||
|
|
||||||
static qint32 currentDecodeStartA = -1;
|
bool couldDecodeA = false;
|
||||||
static qint32 nextDecodeStartA = -1;
|
|
||||||
qint32 startA = -1;
|
qint32 startA = -1;
|
||||||
qint32 szA = -1;
|
qint32 szA = -1;
|
||||||
qint32 cycleA = -1;
|
qint32 cycleA = -1;
|
||||||
|
|
||||||
|
bool couldDecodeB = false;
|
||||||
|
qint32 startB = -1;
|
||||||
|
qint32 szB = -1;
|
||||||
|
qint32 cycleB = -1;
|
||||||
|
|
||||||
|
bool couldDecodeC = false;
|
||||||
|
qint32 startC = -1;
|
||||||
|
qint32 szC = -1;
|
||||||
|
qint32 cycleC = -1;
|
||||||
|
|
||||||
|
#if JS8_ENABLE_JS8E
|
||||||
|
bool couldDecodeE = false;
|
||||||
|
qint32 startE = -1;
|
||||||
|
qint32 szE = -1;
|
||||||
|
qint32 cycleE = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JS8_ENABLE_JS8I
|
||||||
|
bool couldDecodeI = false;
|
||||||
|
qint32 startI = -1;
|
||||||
|
qint32 szI = -1;
|
||||||
|
qint32 cycleI = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static qint32 currentDecodeStartA = -1;
|
||||||
|
static qint32 nextDecodeStartA = -1;
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "? NORMAL " << currentDecodeStartA << nextDecodeStartA;
|
if(JS8_DEBUG_DECODE) qDebug() << "? NORMAL " << currentDecodeStartA << nextDecodeStartA;
|
||||||
bool couldDecodeA = isDecodeReady(Varicode::JS8CallNormal, k, k0, ¤tDecodeStartA, &nextDecodeStartA, &startA, &szA, &cycleA);
|
couldDecodeA = isDecodeReady(Varicode::JS8CallNormal, k, k0, ¤tDecodeStartA, &nextDecodeStartA, &startA, &szA, &cycleA);
|
||||||
if(m_diskData){
|
if(m_diskData){
|
||||||
startA = 0;
|
startA = 0;
|
||||||
szA = NTMAX*RX_SAMPLE_RATE-1;
|
szA = NTMAX*RX_SAMPLE_RATE-1;
|
||||||
@ -4281,11 +4363,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
|
|
||||||
static qint32 currentDecodeStartB = -1;
|
static qint32 currentDecodeStartB = -1;
|
||||||
static qint32 nextDecodeStartB = -1;
|
static qint32 nextDecodeStartB = -1;
|
||||||
qint32 startB = -1;
|
|
||||||
qint32 szB = -1;
|
|
||||||
qint32 cycleB = -1;
|
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "? FAST " << currentDecodeStartB << nextDecodeStartB;
|
if(JS8_DEBUG_DECODE) qDebug() << "? FAST " << currentDecodeStartB << nextDecodeStartB;
|
||||||
bool couldDecodeB = isDecodeReady(Varicode::JS8CallFast, k, k0, ¤tDecodeStartB, &nextDecodeStartB, &startB, &szB, &cycleB);
|
couldDecodeB = isDecodeReady(Varicode::JS8CallFast, k, k0, ¤tDecodeStartB, &nextDecodeStartB, &startB, &szB, &cycleB);
|
||||||
if(m_diskData){
|
if(m_diskData){
|
||||||
startB = 0;
|
startB = 0;
|
||||||
szB = NTMAX*RX_SAMPLE_RATE-1;
|
szB = NTMAX*RX_SAMPLE_RATE-1;
|
||||||
@ -4294,11 +4373,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
|
|
||||||
static qint32 currentDecodeStartC = -1;
|
static qint32 currentDecodeStartC = -1;
|
||||||
static qint32 nextDecodeStartC = -1;
|
static qint32 nextDecodeStartC = -1;
|
||||||
qint32 startC = -1;
|
|
||||||
qint32 szC = -1;
|
|
||||||
qint32 cycleC = -1;
|
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "? TURBO " << currentDecodeStartC << nextDecodeStartC;
|
if(JS8_DEBUG_DECODE) qDebug() << "? TURBO " << currentDecodeStartC << nextDecodeStartC;
|
||||||
bool couldDecodeC = isDecodeReady(Varicode::JS8CallTurbo, k, k0, ¤tDecodeStartC, &nextDecodeStartC, &startC, &szC, &cycleC);
|
couldDecodeC = isDecodeReady(Varicode::JS8CallTurbo, k, k0, ¤tDecodeStartC, &nextDecodeStartC, &startC, &szC, &cycleC);
|
||||||
if(m_diskData){
|
if(m_diskData){
|
||||||
startC = 0;
|
startC = 0;
|
||||||
szC = NTMAX*RX_SAMPLE_RATE-1;
|
szC = NTMAX*RX_SAMPLE_RATE-1;
|
||||||
@ -4308,11 +4384,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
#if JS8_ENABLE_JS8E
|
#if JS8_ENABLE_JS8E
|
||||||
static qint32 currentDecodeStartE = -1;
|
static qint32 currentDecodeStartE = -1;
|
||||||
static qint32 nextDecodeStartE = -1;
|
static qint32 nextDecodeStartE = -1;
|
||||||
qint32 startE = -1;
|
|
||||||
qint32 szE = -1;
|
|
||||||
qint32 cycleE = -1;
|
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "? SLOW " << currentDecodeStartE << nextDecodeStartE;
|
if(JS8_DEBUG_DECODE) qDebug() << "? SLOW " << currentDecodeStartE << nextDecodeStartE;
|
||||||
bool couldDecodeE = isDecodeReady(Varicode::JS8CallSlow, k, k0, ¤tDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE);
|
couldDecodeE = isDecodeReady(Varicode::JS8CallSlow, k, k0, ¤tDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE);
|
||||||
if(m_diskData){
|
if(m_diskData){
|
||||||
startE = 0;
|
startE = 0;
|
||||||
szE = NTMAX*RX_SAMPLE_RATE-1;
|
szE = NTMAX*RX_SAMPLE_RATE-1;
|
||||||
@ -4323,11 +4396,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
#if JS8_ENABLE_JS8I
|
#if JS8_ENABLE_JS8I
|
||||||
static qint32 currentDecodeStartI = -1;
|
static qint32 currentDecodeStartI = -1;
|
||||||
static qint32 nextDecodeStartI = -1;
|
static qint32 nextDecodeStartI = -1;
|
||||||
qint32 startI = -1;
|
|
||||||
qint32 szI = -1;
|
|
||||||
qint32 cycleI = -1;
|
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "? ULTRA " << currentDecodeStartI << nextDecodeStartI;
|
if(JS8_DEBUG_DECODE) qDebug() << "? ULTRA " << currentDecodeStartI << nextDecodeStartI;
|
||||||
bool couldDecodeI = isDecodeReady(Varicode::JS8CallUltra, k, k0, ¤tDecodeStartI, &nextDecodeStartI, &startI, &szI, &cycleI);
|
couldDecodeI = isDecodeReady(Varicode::JS8CallUltra, k, k0, ¤tDecodeStartI, &nextDecodeStartI, &startI, &szI, &cycleI);
|
||||||
if(m_diskData){
|
if(m_diskData){
|
||||||
startI = 0;
|
startI = 0;
|
||||||
szI = NTMAX*RX_SAMPLE_RATE-1;
|
szI = NTMAX*RX_SAMPLE_RATE-1;
|
||||||
@ -4335,12 +4405,9 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int decodes = 0;
|
|
||||||
|
|
||||||
if(couldDecodeA){
|
if(couldDecodeA){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
d.submode = Varicode::JS8CallNormal;
|
d.submode = Varicode::JS8CallNormal;
|
||||||
d.cycle = cycleA;
|
|
||||||
d.start = startA;
|
d.start = startA;
|
||||||
d.sz = szA;
|
d.sz = szA;
|
||||||
m_decoderQueue.append(d);
|
m_decoderQueue.append(d);
|
||||||
@ -4350,7 +4417,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
if(couldDecodeB){
|
if(couldDecodeB){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
d.submode = Varicode::JS8CallFast;
|
d.submode = Varicode::JS8CallFast;
|
||||||
d.cycle = cycleB;
|
|
||||||
d.start = startB;
|
d.start = startB;
|
||||||
d.sz = szB;
|
d.sz = szB;
|
||||||
m_decoderQueue.append(d);
|
m_decoderQueue.append(d);
|
||||||
@ -4360,7 +4426,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
if(couldDecodeC){
|
if(couldDecodeC){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
d.submode = Varicode::JS8CallTurbo;
|
d.submode = Varicode::JS8CallTurbo;
|
||||||
d.cycle = cycleC;
|
|
||||||
d.start = startC;
|
d.start = startC;
|
||||||
d.sz = szC;
|
d.sz = szC;
|
||||||
m_decoderQueue.append(d);
|
m_decoderQueue.append(d);
|
||||||
@ -4371,7 +4436,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
if(couldDecodeE){
|
if(couldDecodeE){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
d.submode = Varicode::JS8CallSlow;
|
d.submode = Varicode::JS8CallSlow;
|
||||||
d.cycle = cycleE;
|
|
||||||
d.start = startE;
|
d.start = startE;
|
||||||
d.sz = szE;
|
d.sz = szE;
|
||||||
m_decoderQueue.append(d);
|
m_decoderQueue.append(d);
|
||||||
@ -4383,7 +4447,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
if(couldDecodeI){
|
if(couldDecodeI){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
d.submode = Varicode::JS8CallUltra;
|
d.submode = Varicode::JS8CallUltra;
|
||||||
d.cycle = cycleI;
|
|
||||||
d.start = startI;
|
d.start = startI;
|
||||||
d.sz = szI;
|
d.sz = szI;
|
||||||
m_decoderQueue.append(d);
|
m_decoderQueue.append(d);
|
||||||
@ -4394,6 +4457,311 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
|
|||||||
return decodes > 0;
|
return decodes > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MainWindow::decodeEnqueueReadyExperiment
|
||||||
|
* compute the available decoder ranges that can be processed and
|
||||||
|
* place them in the decode queue
|
||||||
|
*
|
||||||
|
* experiment with decoding on a much shorter interval than usual
|
||||||
|
*
|
||||||
|
* @param k - the current frame count
|
||||||
|
* @param k0 - the previous frame count
|
||||||
|
* @return true if decoder ranges were queued, false otherwise
|
||||||
|
*/
|
||||||
|
bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
|
||||||
|
// TODO: make this non-static field of MainWindow?
|
||||||
|
// map of last decode positions for each submode
|
||||||
|
// static QMap<qint32, qint32> m_lastDecodeStartMap;
|
||||||
|
|
||||||
|
// TODO: make this non-static field of MainWindow?
|
||||||
|
// map of submodes to decode + optional alternate decode positions
|
||||||
|
static QMap<qint32, QList<qint32>> submodes = {
|
||||||
|
{Varicode::JS8CallSlow, {0}},
|
||||||
|
{Varicode::JS8CallNormal, {0}},
|
||||||
|
{Varicode::JS8CallFast, {0, 5}},
|
||||||
|
{Varicode::JS8CallTurbo, {0, 3}},
|
||||||
|
};
|
||||||
|
|
||||||
|
static qint32 maxSamples = NTMAX*RX_SAMPLE_RATE;
|
||||||
|
static qint32 oneSecondSamples = RX_SAMPLE_RATE;
|
||||||
|
|
||||||
|
int decodes = 0;
|
||||||
|
|
||||||
|
// do we have a better way to check this?
|
||||||
|
bool multi = ui->actionModeMultiDecoder->isChecked();
|
||||||
|
|
||||||
|
// do we have a better way to check this?
|
||||||
|
bool everySecond = m_wideGraph->shouldAutoSync();
|
||||||
|
|
||||||
|
// do we need to process alternate positions?
|
||||||
|
bool skipAlt = false;
|
||||||
|
|
||||||
|
foreach(auto submode, submodes.keys()){
|
||||||
|
// skip if multi is disabled and this mode is not the current submode
|
||||||
|
if(!multi && submode != m_nSubMode){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check all alternate decode positions
|
||||||
|
foreach(auto alt, submodes.value(submode)){
|
||||||
|
// skip alts if we are decoding every second
|
||||||
|
if(everySecond && alt != 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip alt decode positions if needed
|
||||||
|
if(skipAlt && alt != 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples);
|
||||||
|
qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
|
||||||
|
qint32 cycleFramesNeeded = computeFramesPerSymbolForDecode(submode)*JS8_NUM_SYMBOLS; //computeFramesNeededForDecode(submode) - oneSecondSamples;
|
||||||
|
qint32 cycleFramesReady = k - (cycle * cycleFrames);
|
||||||
|
|
||||||
|
if(!m_lastDecodeStartMap.contains(submode)){
|
||||||
|
m_lastDecodeStartMap[submode] = cycle * cycleFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 lastDecodeStart = m_lastDecodeStartMap[submode];
|
||||||
|
qint32 incrementedBy = k - lastDecodeStart;
|
||||||
|
if(k < lastDecodeStart){
|
||||||
|
incrementedBy = maxSamples - lastDecodeStart + k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << submodeName(submode) << "alt" << alt << "cycle" << cycle << "cycle frames" << cycleFrames << "cycle start" << cycle*cycleFrames << "cycle end" << (cycle+1)*cycleFrames << "k" << k << "frames ready" << cycleFramesReady << "incremeted by" << incrementedBy;
|
||||||
|
|
||||||
|
if(everySecond && incrementedBy >= oneSecondSamples){
|
||||||
|
DecodeParams d;
|
||||||
|
d.submode = submode;
|
||||||
|
d.sz = cycleFrames;
|
||||||
|
d.start = k - d.sz;
|
||||||
|
if(d.start < 0){
|
||||||
|
d.start += maxSamples;
|
||||||
|
}
|
||||||
|
m_decoderQueue.append(d);
|
||||||
|
decodes++;
|
||||||
|
|
||||||
|
// keep track of last decode position
|
||||||
|
m_lastDecodeStartMap[submode] = k;
|
||||||
|
}
|
||||||
|
else if(
|
||||||
|
(incrementedBy >= 2*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded ) ||
|
||||||
|
(incrementedBy >= oneSecondSamples && cycleFramesReady < 1.25*oneSecondSamples)
|
||||||
|
){
|
||||||
|
DecodeParams d;
|
||||||
|
d.submode = submode;
|
||||||
|
d.start = cycle*cycleFrames;
|
||||||
|
d.sz = cycleFramesReady;
|
||||||
|
m_decoderQueue.append(d);
|
||||||
|
decodes++;
|
||||||
|
|
||||||
|
// keep track of last decode position
|
||||||
|
m_lastDecodeStartMap[submode] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static qint32 lastDecodeStartK = -1;
|
||||||
|
|
||||||
|
//if(lastDecodeStartK == -1){
|
||||||
|
// qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal);
|
||||||
|
// qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE;
|
||||||
|
// lastDecodeStartK = secondStartK;
|
||||||
|
//}
|
||||||
|
|
||||||
|
static qint32 lastDecodeStartSec = -1;
|
||||||
|
|
||||||
|
int decodes = 0;
|
||||||
|
qint32 startA = 0;
|
||||||
|
qint32 szA = 0;
|
||||||
|
qint32 maxSamples = NTMAX*RX_SAMPLE_RATE;
|
||||||
|
qint32 oneSecondSamples = RX_SAMPLE_RATE;
|
||||||
|
|
||||||
|
// compute how much we've incremented since the last decode ready event
|
||||||
|
qint32 incrementedBy = k - lastDecodeStartK;
|
||||||
|
if(k < lastDecodeStartK){
|
||||||
|
incrementedBy = maxSamples - lastDecodeStartK + k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we've advanced in time enough since the last decode
|
||||||
|
int thisSec = DriftingDateTime::currentDateTimeUtc().time().second();
|
||||||
|
if(incrementedBy >= oneSecondSamples){ // && lastDecodeStartSec != thisSec){
|
||||||
|
qDebug() << "ready to detect decode" << incrementedBy;
|
||||||
|
|
||||||
|
QList<int> submodes = {
|
||||||
|
//Varicode::JS8CallSlow,
|
||||||
|
Varicode::JS8CallNormal,
|
||||||
|
//Varicode::JS8CallFast,
|
||||||
|
//Varicode::JS8CallTurbo
|
||||||
|
};
|
||||||
|
foreach(auto submode, submodes){
|
||||||
|
|
||||||
|
// start at now and subtract the frames in one cycle...
|
||||||
|
// for normal mode this allows us to look through the last 15 seconds of data
|
||||||
|
// + the amount that we've just incremented (say if we were caught in a decode)
|
||||||
|
// to search for decodable signals... and we do this _every_ second!
|
||||||
|
// szA = computeFramesPerCycleForDecode(submode) + incrementedBy;
|
||||||
|
szA = computeFramesNeededForDecode(submode);
|
||||||
|
startA = k - szA;
|
||||||
|
|
||||||
|
// when the start position is negative, we need to start at the end of the
|
||||||
|
// buffer and wrap around. the decoder knows how to do the wrap around, so
|
||||||
|
// all we need to do is
|
||||||
|
if(startA < 0){
|
||||||
|
startA += maxSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the decode params and queue it
|
||||||
|
DecodeParams d;
|
||||||
|
d.submode = submode;
|
||||||
|
d.start = startA;
|
||||||
|
d.sz = szA;
|
||||||
|
m_decoderQueue.append(d);
|
||||||
|
decodes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the decoder is going to look +/- multiple seconds... so this may partial decode
|
||||||
|
// up to a few seconds in the future...meaning if we're doing this every second
|
||||||
|
// we may actually decode this same signal 2-3 more times... but we have a
|
||||||
|
// message decode dedupe that should prevent any issues with dupes out of the
|
||||||
|
// decoder when this happens.
|
||||||
|
lastDecodeStartK = k;
|
||||||
|
lastDecodeStartSec = thisSec;
|
||||||
|
|
||||||
|
// TODO: remove this after testing
|
||||||
|
m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodes > 0;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// when no other mode is being decoded, do a sync stats decode for normal mode
|
||||||
|
bool experiment = true;
|
||||||
|
|
||||||
|
if(experiment){
|
||||||
|
static qint32 lastDecodeStartA = 0;
|
||||||
|
|
||||||
|
qint32 maxSamples = m_detector->period()*RX_SAMPLE_RATE;
|
||||||
|
qint32 oneSecondSamples = RX_SAMPLE_RATE;
|
||||||
|
|
||||||
|
// when we've incremented at least one second into the future
|
||||||
|
qint32 incrementedBy = k - lastDecodeStartA;
|
||||||
|
if(k < lastDecodeStartA){
|
||||||
|
incrementedBy = maxSamples - lastDecodeStartA + k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(incrementedBy >= oneSecondSamples){
|
||||||
|
// we've incremented at least one second, so look backwards
|
||||||
|
|
||||||
|
|
||||||
|
//startA = lastDecodeStartA + oneSecondSamples;
|
||||||
|
//szA = computeFramesNeededForDecode(Varicode::JS8CallNormal) + oneSecondSamples;
|
||||||
|
//lastDecodeStartA +=
|
||||||
|
// startA = k - incrementedBy - computeFramesNeededForDecode(Varicode::JS8CallNormal);
|
||||||
|
// if(startA < 0){
|
||||||
|
// startA += maxSamples;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// szA = incrementedBy + computeFramesNeededForDecode(Varicode::JS8CallNormal);
|
||||||
|
//
|
||||||
|
// qDebug() << "A: start:" << startA << "sz:" << szA << "stop:" << startA + szA;
|
||||||
|
//
|
||||||
|
// lastDecodeStartA = k;
|
||||||
|
// couldDecodeA = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal);
|
||||||
|
//if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){
|
||||||
|
// startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal);
|
||||||
|
//
|
||||||
|
// if(startA < 0){
|
||||||
|
// // decoder wraps around ranges
|
||||||
|
// startA += m_detector->period() * RX_SAMPLE_RATE;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// szA = computeFramesNeededForDecode(Varicode::JS8CallNormal);
|
||||||
|
// lastDecodeStartA = k;
|
||||||
|
// couldDecodeA = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
couldDecodeB = couldDecodeC = couldDecodeE = false;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static qint32 lastDecodeStartB = -1;
|
||||||
|
qint32 oneSecondFramesB = computeFramesPerCycleForDecode(Varicode::JS8CallFast)/computePeriodForSubmode(Varicode::JS8CallFast);
|
||||||
|
if(lastDecodeStartB == -1 || k < k0 || k - lastDecodeStartB > oneSecondFramesB){
|
||||||
|
startB = k-computeFramesNeededForDecode(Varicode::JS8CallFast);
|
||||||
|
|
||||||
|
if(startB < 0){
|
||||||
|
// decoder wraps around ranges
|
||||||
|
startB += m_detector->period() * RX_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
szB = computeFramesNeededForDecode(Varicode::JS8CallFast);
|
||||||
|
lastDecodeStartB = k;
|
||||||
|
couldDecodeB = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static qint32 lastDecodeStartC = -1;
|
||||||
|
qint32 oneSecondFramesC = computeFramesPerCycleForDecode(Varicode::JS8CallTurbo)/computePeriodForSubmode(Varicode::JS8CallTurbo);
|
||||||
|
if(lastDecodeStartC == -1 || k < k0 || k - lastDecodeStartC > oneSecondFramesC){
|
||||||
|
startC = k-computeFramesNeededForDecode(Varicode::JS8CallTurbo);
|
||||||
|
|
||||||
|
if(startC < 0){
|
||||||
|
// decoder wraps around ranges
|
||||||
|
startC += m_detector->period() * RX_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
szC = computeFramesNeededForDecode(Varicode::JS8CallTurbo);
|
||||||
|
lastDecodeStartC = k;
|
||||||
|
couldDecodeC = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JS8_ENABLE_JS8E
|
||||||
|
static qint32 lastDecodeStartE = -1;
|
||||||
|
qint32 oneSecondFramesE = computeFramesPerCycleForDecode(Varicode::JS8CallSlow)/computePeriodForSubmode(Varicode::JS8CallSlow);
|
||||||
|
if(lastDecodeStartE == -1 || k < k0 || k - lastDecodeStartE > oneSecondFramesE){
|
||||||
|
startE = k-computeFramesNeededForDecode(Varicode::JS8CallSlow);
|
||||||
|
|
||||||
|
if(startE < 0){
|
||||||
|
// decoder wraps around ranges
|
||||||
|
startE += m_detector->period() * RX_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
szE = computeFramesNeededForDecode(Varicode::JS8CallSlow);
|
||||||
|
lastDecodeStartE = k;
|
||||||
|
couldDecodeE = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JS8_ENABLE_JS8I
|
||||||
|
static qint32 lastDecodeStartI = -1;
|
||||||
|
qint32 oneSecondFramesI = computeFramesPerCycleForDecode(Varicode::JS8CallUltra)/computePeriodForSubmode(Varicode::JS8CallUltra);
|
||||||
|
if(lastDecodeStartI == -1 || k < k0 || k - lastDecodeStartI > oneSecondFramesI){
|
||||||
|
startI = k-computeFramesNeededForDecode(Varicode::JS8CallUltra);
|
||||||
|
|
||||||
|
if(startI < 0){
|
||||||
|
// decoder wraps around ranges
|
||||||
|
startI += m_detector->period() * RX_SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
szI = computeFramesNeededForDecode(Varicode::JS8CallUltra);
|
||||||
|
lastDecodeStartI = k;
|
||||||
|
couldDecodeI = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MainWindow::decodeProcessQueue
|
* @brief MainWindow::decodeProcessQueue
|
||||||
* process the decode queue by merging available decode ranges
|
* process the decode queue by merging available decode ranges
|
||||||
@ -4406,7 +4774,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
|
|||||||
QMutexLocker mutex(m_detector->getMutex());
|
QMutexLocker mutex(m_detector->getMutex());
|
||||||
|
|
||||||
if(m_decoderBusy){
|
if(m_decoderBusy){
|
||||||
int seconds = m_decoderBusyStartTime.secsTo(DriftingDateTime::currentDateTimeUtc());
|
int seconds = m_decoderBusyStartTime.secsTo(QDateTime::currentDateTimeUtc());
|
||||||
if(seconds > 60){
|
if(seconds > 60){
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder should be killed!" << QString("(%1 seconds)").arg(seconds);
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder should be killed!" << QString("(%1 seconds)").arg(seconds);
|
||||||
} else if(seconds > 30){
|
} else if(seconds > 30){
|
||||||
@ -4436,6 +4804,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
|
|||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder skipping at least 1 decode cycle" << "count" << count << "max" << maxDecodes;
|
if(JS8_DEBUG_DECODE) qDebug() << "--> decoder skipping at least 1 decode cycle" << "count" << count << "max" << maxDecodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default to no submodes being decoded, then bitwise OR the modes together to decode them all at once
|
||||||
dec_data.params.nsubmodes = 0;
|
dec_data.params.nsubmodes = 0;
|
||||||
|
|
||||||
while(!m_decoderQueue.isEmpty()){
|
while(!m_decoderQueue.isEmpty()){
|
||||||
@ -4494,6 +4863,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
|
|||||||
|
|
||||||
int period = computePeriodForSubmode(submode);
|
int period = computePeriodForSubmode(submode);
|
||||||
|
|
||||||
|
dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || m_wideGraph->shouldAutoSync());
|
||||||
dec_data.params.npts8=(m_ihsym*m_nsps)/16;
|
dec_data.params.npts8=(m_ihsym*m_nsps)/16;
|
||||||
dec_data.params.newdat=1;
|
dec_data.params.newdat=1;
|
||||||
dec_data.params.nagain=0;
|
dec_data.params.nagain=0;
|
||||||
@ -4673,7 +5043,7 @@ void MainWindow::decodeBusy(bool b) //decodeBusy()
|
|||||||
m_decoderBusy=b;
|
m_decoderBusy=b;
|
||||||
if(m_decoderBusy){
|
if(m_decoderBusy){
|
||||||
tx_status_label.setText("Decoding");
|
tx_status_label.setText("Decoding");
|
||||||
m_decoderBusyStartTime = DriftingDateTime::currentDateTimeUtc();
|
m_decoderBusyStartTime = QDateTime::currentDateTimeUtc(); //DriftingDateTime::currentDateTimeUtc();
|
||||||
m_decoderBusyFreq = dialFrequency();
|
m_decoderBusyFreq = dialFrequency();
|
||||||
m_decoderBusyBand = m_config.bands()->find (m_decoderBusyFreq);
|
m_decoderBusyBand = m_config.bands()->find (m_decoderBusyFreq);
|
||||||
}
|
}
|
||||||
@ -4699,7 +5069,16 @@ void MainWindow::decodeDone ()
|
|||||||
m_nclearave=0;
|
m_nclearave=0;
|
||||||
m_RxLog=0;
|
m_RxLog=0;
|
||||||
m_blankLine=true;
|
m_blankLine=true;
|
||||||
m_messageDupeCache.clear();
|
|
||||||
|
// cleanup old cached messages (messages > submode period old)
|
||||||
|
for (auto it = m_messageDupeCache.begin(); it != m_messageDupeCache.end();){
|
||||||
|
auto cached = it.value();
|
||||||
|
if (cached.date.secsTo(QDateTime::currentDateTimeUtc()) > computePeriodForSubmode(cached.submode)){
|
||||||
|
it = m_messageDupeCache.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
decodeBusy(false);
|
decodeBusy(false);
|
||||||
}
|
}
|
||||||
@ -4750,7 +5129,7 @@ void MainWindow::decodeCheckHangingDecoder(){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!m_decoderBusyStartTime.isValid() || m_decoderBusyStartTime.secsTo(DriftingDateTime::currentDateTimeUtc()) < 60){
|
if(!m_decoderBusyStartTime.isValid() || m_decoderBusyStartTime.secsTo(QDateTime::currentDateTimeUtc()) < 60){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4836,22 +5215,186 @@ void MainWindow::readFromStdout(QProcess * proc) //r
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::processDecodedLine(QByteArray t){
|
void MainWindow::processDecodedLine(QByteArray t){
|
||||||
qDebug() << "JS8: " << QString(t);
|
if(JS8_DEBUG_DECODE) qDebug() << "JS8: " << QString(t);
|
||||||
|
|
||||||
bool bAvgMsg=false;
|
bool bAvgMsg=false;
|
||||||
int navg=0;
|
int navg=0;
|
||||||
if(t.indexOf("<DecodeDebug>") >= 0) {
|
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists());
|
static QList<int> driftQueue;
|
||||||
return;
|
|
||||||
|
static qint32 syncStart = -1;
|
||||||
|
if(t.indexOf("<DecodeSyncMeta> sync start") >= 0){
|
||||||
|
auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts);
|
||||||
|
if(segs.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spos = segs.at(3);
|
||||||
|
syncStart = spos.toInt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t.indexOf("<DecodeSyncStat>") >= 0) {
|
||||||
|
auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts);
|
||||||
|
if(segs.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only continue if we should either display decode attempts or if we should try to auto sync
|
||||||
|
if(!m_wideGraph->shouldDisplayDecodeAttempts() && !m_wideGraph->shouldAutoSync()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto m1 = QString(segs.at(2));
|
||||||
|
auto m = int(m1.toInt());
|
||||||
|
|
||||||
|
auto f1 = QString(segs.at(4));
|
||||||
|
auto f = int(f1.toFloat());
|
||||||
|
|
||||||
|
auto s1 = QString(segs.at(6));
|
||||||
|
auto s = int(s1.toFloat());
|
||||||
|
|
||||||
|
auto xdt1 = QString(segs.at(8));
|
||||||
|
auto xdt = xdt1.toFloat();
|
||||||
|
auto xdtMs = int(xdt*1000);
|
||||||
|
|
||||||
|
// draw candidates
|
||||||
|
if(abs(xdtMs) <= 2000){
|
||||||
|
if(s < 10){
|
||||||
|
m_wideGraph->drawDecodeLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m));
|
||||||
|
} else if (s <= 15){
|
||||||
|
m_wideGraph->drawDecodeLine(QColor(Qt::cyan), f, f + computeBandwidthForSubmode(m));
|
||||||
|
} else if (s <= 21){
|
||||||
|
m_wideGraph->drawDecodeLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!t.contains("decode")){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw decodes
|
||||||
|
m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m));
|
||||||
|
|
||||||
|
// compute time drift if needed
|
||||||
|
if(!m_wideGraph->shouldAutoSync()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: can we do this for FAST & TURBO
|
||||||
|
// if fast/turbo is observed and we're in a period post 15 seconds (i.e., second 18 turbo decode)
|
||||||
|
// then make the drift relative to the first cycle instead
|
||||||
|
if(m != Varicode::JS8CallNormal && m != Varicode::JS8CallSlow){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're here at this point, we _should_ be operating a decode every second
|
||||||
|
//
|
||||||
|
// so we need to figure out where:
|
||||||
|
//
|
||||||
|
// 1) this current decode started
|
||||||
|
// 2) when that cycle _should_ have started
|
||||||
|
// 3) compute the delta
|
||||||
|
// 4) apply the drift
|
||||||
|
|
||||||
|
int periodMs = 1000 * computePeriodForSubmode(m);
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("Decode at %1 (kin: %2, lastDecoded: %3)").arg(syncStart).arg(dec_data.params.kin).arg(m_lastDecodeStartMap.value(m)));
|
||||||
|
|
||||||
|
float expectedStartDelay = computePeriodStartDelayForDecode(m)/1000.0;
|
||||||
|
|
||||||
|
float decodedSignalTime = (float)syncStart/(float)RX_SAMPLE_RATE;
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime));
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> we add a time delta of %1 seconds into the start of the cycle").arg(xdt));
|
||||||
|
|
||||||
|
// adjust for expected start delay
|
||||||
|
decodedSignalTime -= expectedStartDelay;
|
||||||
|
|
||||||
|
// adjust for time delta
|
||||||
|
decodedSignalTime += xdt;
|
||||||
|
|
||||||
|
// ensure that we are within a 60 second minute
|
||||||
|
if(decodedSignalTime < 0){
|
||||||
|
decodedSignalTime += 60.0;
|
||||||
|
} else if(decodedSignalTime > 60){
|
||||||
|
decodedSignalTime -= 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> so signal adjusted started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime));
|
||||||
|
|
||||||
|
int decodedSignalTimeMs = 1000 * decodedSignalTime;
|
||||||
|
int cycleStartTimeMs = (decodedSignalTimeMs / periodMs) * periodMs;
|
||||||
|
int driftMs = cycleStartTimeMs - decodedSignalTimeMs;
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> which is a drift adjustment of %1 milliseconds").arg(driftMs));
|
||||||
|
|
||||||
|
// if we have a large negative offset (say -14000), use the positive inverse of +1000
|
||||||
|
if(driftMs + periodMs < qAbs(driftMs)){
|
||||||
|
driftMs += periodMs;
|
||||||
|
}
|
||||||
|
// if we have a large positive offset (say 14000, use the negative inverse of -1000)
|
||||||
|
else if(qAbs(driftMs - periodMs) < driftMs){
|
||||||
|
driftMs -= periodMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> which is a corrected drift adjustment of %1 milliseconds").arg(driftMs));
|
||||||
|
|
||||||
|
int newDrift = DriftingDateTime::drift() + driftMs;
|
||||||
|
if(newDrift < 0){
|
||||||
|
newDrift %= -periodMs;
|
||||||
|
} else {
|
||||||
|
newDrift %= periodMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift));
|
||||||
|
|
||||||
|
driftQueue.append(newDrift);
|
||||||
|
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists());
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(t.indexOf("<DecodeStarted>") >= 0) {
|
if(t.indexOf("<DecodeStarted>") >= 0) {
|
||||||
if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists());
|
if(m_wideGraph->shouldDisplayDecodeAttempts()){
|
||||||
return;
|
m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(t.indexOf("<DecodeDebug>") >= 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(t.indexOf("<DecodeFinished>") >= 0) {
|
if(t.indexOf("<DecodeFinished>") >= 0) {
|
||||||
if(m_mode=="QRA64") m_wideGraph->drawRed(0,0);
|
int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc());
|
||||||
|
if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms";
|
||||||
|
|
||||||
|
if(!driftQueue.isEmpty()){
|
||||||
|
static int driftN = 1;
|
||||||
|
static int driftAvg = DriftingDateTime::drift();
|
||||||
|
|
||||||
|
// let the widegraph know for timing control
|
||||||
|
m_wideGraph->notifyDriftedSignalsDecoded(driftQueue.count());
|
||||||
|
|
||||||
|
while(!driftQueue.isEmpty()){
|
||||||
|
int newDrift = driftQueue.first();
|
||||||
|
driftQueue.removeFirst();
|
||||||
|
|
||||||
|
driftAvg = ((driftN-1)*driftAvg + newDrift)/driftN;
|
||||||
|
if(driftN < 60) driftN++; // cap it to 60 observations
|
||||||
|
}
|
||||||
|
|
||||||
|
setDrift(driftAvg);
|
||||||
|
|
||||||
|
|
||||||
|
//writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Automatic Drift: %1").arg(driftAvg));
|
||||||
|
}
|
||||||
|
|
||||||
m_bDecoded = t.mid(16).trimmed().toInt() > 0;
|
m_bDecoded = t.mid(16).trimmed().toInt() > 0;
|
||||||
int mswait=3*1000*m_TRperiod/4;
|
int mswait=3*1000*m_TRperiod/4;
|
||||||
if(!m_diskData) killFileTimer.start(mswait); //Kill in 3/4 period
|
if(!m_diskData) killFileTimer.start(mswait); //Kill in 3/4 period
|
||||||
@ -4879,26 +5422,38 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto rawText = QString::fromUtf8 (t.constData ()).remove (QRegularExpression {"\r|\n"});
|
auto rawText = QString::fromUtf8 (t.constData ()).remove (QRegularExpression {"\r|\n"});
|
||||||
|
|
||||||
DecodedText decodedtext {rawText, "FT8" == m_mode &&
|
DecodedText decodedtext {rawText, "FT8" == m_mode &&
|
||||||
ui->cbVHFcontest->isChecked(), m_config.my_grid ()};
|
ui->cbVHFcontest->isChecked(), m_config.my_grid ()};
|
||||||
|
|
||||||
bool bValidFrame = decodedtext.snr() >= rxSnrThreshold(decodedtext.submode());
|
|
||||||
|
|
||||||
// dupe check
|
// frames are also valid if they pass our dupe check (haven't seen the same frame in the past 1/2 decode period)
|
||||||
auto frame = decodedtext.message();
|
|
||||||
auto frameOffset = decodedtext.frequencyOffset();
|
auto frameOffset = decodedtext.frequencyOffset();
|
||||||
if(m_messageDupeCache.contains(frame)){
|
auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(decodedtext.frame());
|
||||||
// check to see if the frequency is near our previous frame
|
if(m_messageDupeCache.contains(frameDedupeKey)){
|
||||||
auto cachedFreq = m_messageDupeCache.value(frame, 0);
|
auto cached = m_messageDupeCache.value(frameDedupeKey);
|
||||||
if(qAbs(cachedFreq - frameOffset) <= rxThreshold(decodedtext.submode())){
|
|
||||||
qDebug() << "duplicate frame from" << cachedFreq << "and" << frameOffset;
|
// check to see if the time since last seen is > 1/2 decode period
|
||||||
bValidFrame = false;
|
auto cachedDate = cached.date;
|
||||||
|
if(cachedDate.secsTo(QDateTime::currentDateTimeUtc()) < 0.5*computePeriodForSubmode(decodedtext.submode())){
|
||||||
|
qDebug() << "duplicate frame at" << cachedDate << "using key" << frameDedupeKey;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// cache for this decode cycle
|
// check to see if the frequency is near our previous frame
|
||||||
m_messageDupeCache[frame] = frameOffset;
|
auto cachedFreq = cached.freq;
|
||||||
|
if(qAbs(cachedFreq - frameOffset) <= rxThreshold(decodedtext.submode())){
|
||||||
|
qDebug() << "duplicate frame from" << cachedFreq << "and" << frameOffset << "using key" << frameDedupeKey;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// huzzah!
|
||||||
|
// if we make it here, the cache is invalid and will be bumped when we cache the new frame below
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// frames are valid if they meet our minimum rx threshold for the submode
|
||||||
|
bool bValidFrame = decodedtext.snr() >= rxSnrThreshold(decodedtext.submode());
|
||||||
|
|
||||||
qDebug() << "valid" << bValidFrame << submodeName(decodedtext.submode()) << "decoded text" << decodedtext.message();
|
qDebug() << "valid" << bValidFrame << submodeName(decodedtext.submode()) << "decoded text" << decodedtext.message();
|
||||||
|
|
||||||
// skip if invalid
|
// skip if invalid
|
||||||
@ -4906,6 +5461,9 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the frame is valid, cache it!
|
||||||
|
m_messageDupeCache[frameDedupeKey] = {QDateTime::currentDateTimeUtc(), decodedtext.submode(), frameOffset};
|
||||||
|
|
||||||
// log valid frames to ALL.txt (and correct their timestamp format)
|
// log valid frames to ALL.txt (and correct their timestamp format)
|
||||||
auto freq = dialFrequency();
|
auto freq = dialFrequency();
|
||||||
|
|
||||||
@ -4954,7 +5512,7 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
d.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
d.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
d.snr = decodedtext.snr();
|
d.snr = decodedtext.snr();
|
||||||
d.isBuffered = false;
|
d.isBuffered = false;
|
||||||
d.tdrift = decodedtext.dt();
|
d.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt();
|
||||||
d.submode = decodedtext.submode();
|
d.submode = decodedtext.submode();
|
||||||
|
|
||||||
// if we have any "first" frame, and a buffer is already established, clear it...
|
// if we have any "first" frame, and a buffer is already established, clear it...
|
||||||
@ -4992,7 +5550,7 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
cd.offset = decodedtext.frequencyOffset();
|
cd.offset = decodedtext.frequencyOffset();
|
||||||
cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
cd.bits = decodedtext.bits();
|
cd.bits = decodedtext.bits();
|
||||||
cd.tdrift = decodedtext.dt();
|
cd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt();
|
||||||
cd.submode = decodedtext.submode();
|
cd.submode = decodedtext.submode();
|
||||||
|
|
||||||
// Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings
|
// Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings
|
||||||
@ -5071,7 +5629,7 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
cmd.bits = decodedtext.bits();
|
cmd.bits = decodedtext.bits();
|
||||||
cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : "";
|
cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : "";
|
||||||
cmd.tdrift = decodedtext.dt();
|
cmd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt();
|
||||||
cmd.submode = decodedtext.submode();
|
cmd.submode = decodedtext.submode();
|
||||||
|
|
||||||
// if the command is a buffered command and its not the last frame OR we have from or to in a separate message (compound call)
|
// if the command is a buffered command and its not the last frame OR we have from or to in a separate message (compound call)
|
||||||
@ -5900,7 +6458,7 @@ void MainWindow::guiUpdate()
|
|||||||
auto drift = DriftingDateTime::drift();
|
auto drift = DriftingDateTime::drift();
|
||||||
QDateTime t = DriftingDateTime::currentDateTimeUtc();
|
QDateTime t = DriftingDateTime::currentDateTimeUtc();
|
||||||
QStringList parts;
|
QStringList parts;
|
||||||
parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms)").arg(drift > 0 ? "+" : "").arg(drift)));
|
parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms%3)").arg(drift > 0 ? "+" : "").arg(drift).arg(m_wideGraph->shouldAutoSync() ? " auto" : "")));
|
||||||
parts << t.date().toString("yyyy MMM dd");
|
parts << t.date().toString("yyyy MMM dd");
|
||||||
ui->labUTC->setText(parts.join("\n"));
|
ui->labUTC->setText(parts.join("\n"));
|
||||||
|
|
||||||
@ -7803,19 +8361,11 @@ void MainWindow::on_actionJS8_triggered()
|
|||||||
if(m_isWideGraphMDI) m_wideGraph->show();
|
if(m_isWideGraphMDI) m_wideGraph->show();
|
||||||
ui->decodedTextLabel2->setText(" UTC dB DT Freq Message");
|
ui->decodedTextLabel2->setText(" UTC dB DT Freq Message");
|
||||||
m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe
|
m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe
|
||||||
#if JS8_RING_BUFFER
|
|
||||||
Q_ASSERT(NTMAX == 60);
|
Q_ASSERT(NTMAX == 60);
|
||||||
m_wideGraph->setPeriod(m_TRperiod, m_nsps);
|
m_wideGraph->setPeriod(m_TRperiod, m_nsps);
|
||||||
#if JS8_ENABLE_JS8E && !JS8E_IS_ULTRA
|
|
||||||
m_detector->setTRPeriod(NTMAX); // TODO - not thread safe
|
m_detector->setTRPeriod(NTMAX); // TODO - not thread safe
|
||||||
#else
|
|
||||||
m_detector->setTRPeriod(NTMAX / 2); // TODO - not thread safe
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else
|
|
||||||
m_wideGraph->setPeriod(m_TRperiod, m_nsps);
|
|
||||||
m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe
|
|
||||||
#endif
|
|
||||||
ui->label_7->setText("Rx Frequency");
|
ui->label_7->setText("Rx Frequency");
|
||||||
if(m_config.bFox()) {
|
if(m_config.bFox()) {
|
||||||
ui->label_6->setText("Stations calling DXpedition " + m_config.my_callsign());
|
ui->label_6->setText("Stations calling DXpedition " + m_config.my_callsign());
|
||||||
@ -9623,6 +10173,12 @@ void MainWindow::qsy(int hzDelta){
|
|||||||
displayActivity(true);
|
displayActivity(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::drifted(int /*prev*/, int /*cur*/){
|
||||||
|
// here we reset the buffer position without clearing the buffer
|
||||||
|
// this makes the detected emit the correct k when drifting time
|
||||||
|
m_detector->resetBufferPosition();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::setFreqOffsetForRestore(int freq, bool shouldRestore){
|
void MainWindow::setFreqOffsetForRestore(int freq, bool shouldRestore){
|
||||||
setFreq4(freq, freq);
|
setFreq4(freq, freq);
|
||||||
if(shouldRestore){
|
if(shouldRestore){
|
||||||
@ -10137,6 +10693,7 @@ void MainWindow::updateModeButtonText(){
|
|||||||
modeText += QString("+HB");
|
modeText += QString("+HB");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->modeButton->setText(modeText);
|
ui->modeButton->setText(modeText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
mainwindow.h
15
mainwindow.h
@ -132,6 +132,7 @@ public slots:
|
|||||||
void readFromStdout(QProcess * proc);
|
void readFromStdout(QProcess * proc);
|
||||||
void setXIT(int n, Frequency base = 0u);
|
void setXIT(int n, Frequency base = 0u);
|
||||||
void qsy(int hzDelta);
|
void qsy(int hzDelta);
|
||||||
|
void drifted(int prev, int cur);
|
||||||
void setFreqOffsetForRestore(int freq, bool shouldRestore);
|
void setFreqOffsetForRestore(int freq, bool shouldRestore);
|
||||||
bool tryRestoreFreqOffset();
|
bool tryRestoreFreqOffset();
|
||||||
void setFreq4(int rxFreq, int txFreq);
|
void setFreq4(int rxFreq, int txFreq);
|
||||||
@ -244,6 +245,7 @@ private slots:
|
|||||||
bool decode(qint32 k);
|
bool decode(qint32 k);
|
||||||
bool isDecodeReady(int submode, qint32 k, qint32 k0, qint32 *pCurrentDecodeStart, qint32 *pNextDecodeStart, qint32 *pStart, qint32 *pSz, qint32 *pCycle);
|
bool isDecodeReady(int submode, qint32 k, qint32 k0, qint32 *pCurrentDecodeStart, qint32 *pNextDecodeStart, qint32 *pStart, qint32 *pSz, qint32 *pCycle);
|
||||||
bool decodeEnqueueReady(qint32 k, qint32 k0);
|
bool decodeEnqueueReady(qint32 k, qint32 k0);
|
||||||
|
bool decodeEnqueueReadyExperiment(qint32 k, qint32 k0);
|
||||||
bool decodeProcessQueue(qint32 *pSubmode);
|
bool decodeProcessQueue(qint32 *pSubmode);
|
||||||
void decodeStart();
|
void decodeStart();
|
||||||
void decodePrepareSaveAudio(int submode);
|
void decodePrepareSaveAudio(int submode);
|
||||||
@ -614,6 +616,7 @@ private:
|
|||||||
bool m_loopall;
|
bool m_loopall;
|
||||||
bool m_decoderBusy;
|
bool m_decoderBusy;
|
||||||
QString m_decoderBusyBand;
|
QString m_decoderBusyBand;
|
||||||
|
QMap<qint32, qint32> m_lastDecodeStartMap; // submode, decode k start position
|
||||||
Radio::Frequency m_decoderBusyFreq;
|
Radio::Frequency m_decoderBusyFreq;
|
||||||
QDateTime m_decoderBusyStartTime;
|
QDateTime m_decoderBusyStartTime;
|
||||||
bool m_auto;
|
bool m_auto;
|
||||||
@ -853,13 +856,18 @@ private:
|
|||||||
|
|
||||||
struct DecodeParams {
|
struct DecodeParams {
|
||||||
int submode;
|
int submode;
|
||||||
int cycle;
|
|
||||||
int start;
|
int start;
|
||||||
int sz;
|
int sz;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CachedFrame {
|
||||||
|
QDateTime date;
|
||||||
|
int submode;
|
||||||
|
int freq;
|
||||||
|
};
|
||||||
|
|
||||||
QQueue<DecodeParams> m_decoderQueue;
|
QQueue<DecodeParams> m_decoderQueue;
|
||||||
QMap<QString, int> m_messageDupeCache; // message frame -> freq offset seen
|
QMap<QString, CachedFrame> m_messageDupeCache; // message frame -> date seen, submode seen, freq offset seen
|
||||||
QMap<QString, QVariant> m_showColumnsCache; // table column:key -> show boolean
|
QMap<QString, QVariant> m_showColumnsCache; // table column:key -> show boolean
|
||||||
QMap<QString, QVariant> m_sortCache; // table key -> sort by
|
QMap<QString, QVariant> m_sortCache; // table key -> sort by
|
||||||
QPriorityQueue<PrioritizedMessage> m_txMessageQueue; // messages to be sent
|
QPriorityQueue<PrioritizedMessage> m_txMessageQueue; // messages to be sent
|
||||||
@ -983,7 +991,10 @@ private:
|
|||||||
//int computeCurrentCycle(int period);
|
//int computeCurrentCycle(int period);
|
||||||
//int computeCycleStartForDecode(int cycle, int period);
|
//int computeCycleStartForDecode(int cycle, int period);
|
||||||
int computeCycleForDecode(int submode, int k);
|
int computeCycleForDecode(int submode, int k);
|
||||||
|
int computeAltCycleForDecode(int submode, int k, int offsetFrames);
|
||||||
int computeFramesPerCycleForDecode(int submode);
|
int computeFramesPerCycleForDecode(int submode);
|
||||||
|
int computePeriodStartDelayForDecode(int submode);
|
||||||
|
int computeFramesPerSymbolForDecode(int submode);
|
||||||
int computeFramesNeededForDecode(int submode);
|
int computeFramesNeededForDecode(int submode);
|
||||||
bool shortList(QString callsign);
|
bool shortList(QString callsign);
|
||||||
void transmit (double snr = 99.);
|
void transmit (double snr = 99.);
|
||||||
|
@ -4753,6 +4753,7 @@ list. The list can be maintained in Settings (F2).</string>
|
|||||||
<addaction name="actionModeAutoreply"/>
|
<addaction name="actionModeAutoreply"/>
|
||||||
<addaction name="actionModeJS8HB"/>
|
<addaction name="actionModeJS8HB"/>
|
||||||
<addaction name="actionHeartbeatAcknowledgements"/>
|
<addaction name="actionHeartbeatAcknowledgements"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
<addaction name="menuConfig"/>
|
<addaction name="menuConfig"/>
|
||||||
@ -5762,6 +5763,14 @@ list. The list can be maintained in Settings (F2).</string>
|
|||||||
<string>Enable Tuning Tone (T&UNE)</string>
|
<string>Enable Tuning Tone (T&UNE)</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionModeAutoSync">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable Automatic Timing Synchronization (S&YNC)</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
55
plotter.cpp
55
plotter.cpp
@ -277,42 +277,31 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed)
|
|||||||
painter2D.drawText(x1-4,y,"73");
|
painter2D.drawText(x1-4,y,"73");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bRed) {
|
|
||||||
std::ifstream f;
|
|
||||||
f.open(m_redFile.toLatin1());
|
|
||||||
if(f) {
|
|
||||||
int x,y;
|
|
||||||
float freq,sync;
|
|
||||||
float slimit=6.0;
|
|
||||||
QPen pen0(Qt::red,1);
|
|
||||||
painter1.setPen(pen0);
|
|
||||||
for(int i=0; i<99999; i++) {
|
|
||||||
f >> freq >> sync;
|
|
||||||
if(f.eof()) break;
|
|
||||||
x=XfromFreq(freq);
|
|
||||||
y=(sync-slimit)*3.0;
|
|
||||||
if(y>0) {
|
|
||||||
if(y>15.0) y=15.0;
|
|
||||||
if(x>=0 and x<=m_w) {
|
|
||||||
painter1.setPen(pen0);
|
|
||||||
painter1.drawLine(x,0,x,y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
// m_bDecodeFinished=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
update(); //trigger a new paintEvent
|
update(); //trigger a new paintEvent
|
||||||
m_bScaleOK=true;
|
m_bScaleOK=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlotter::drawRed(int ia, int ib, float swide[])
|
void CPlotter::drawDecodeLine(const QColor &color, int ia, int ib)
|
||||||
{
|
{
|
||||||
m_ia=ia;
|
int x1=XfromFreq(ia);
|
||||||
m_ib=ib;
|
int x2=XfromFreq(ib);
|
||||||
draw(swide,false,true);
|
|
||||||
|
QPen pen0(color, 1);
|
||||||
|
|
||||||
|
QPainter painter1(&m_WaterfallPixmap);
|
||||||
|
painter1.setPen(pen0);
|
||||||
|
painter1.drawLine(qMin(x1, x2),4,qMax(x1, x2),4);
|
||||||
|
painter1.drawLine(qMin(x1, x2),0,qMin(x1, x2),9);
|
||||||
|
painter1.drawLine(qMax(x1, x2),0,qMax(x1, x2),9);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlotter::drawHorizontalLine(const QColor &color, int x, int width)
|
||||||
|
{
|
||||||
|
QPen pen0(color, 1);
|
||||||
|
|
||||||
|
QPainter painter1(&m_WaterfallPixmap);
|
||||||
|
painter1.setPen(pen0);
|
||||||
|
painter1.drawLine(x,0,width <= 0 ? m_w : x+width,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlotter::replot()
|
void CPlotter::replot()
|
||||||
@ -890,6 +879,10 @@ void CPlotter::setTol(int n) //setTol()
|
|||||||
DrawOverlay();
|
DrawOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QColor> const& CPlotter::colors(){
|
||||||
|
return g_ColorTbl;
|
||||||
|
}
|
||||||
|
|
||||||
void CPlotter::setColours(QVector<QColor> const& cl)
|
void CPlotter::setColours(QVector<QColor> const& cl)
|
||||||
{
|
{
|
||||||
g_ColorTbl = cl;
|
g_ColorTbl = cl;
|
||||||
|
@ -77,6 +77,7 @@ public:
|
|||||||
void setBreadth(qint32 w) {m_w = w;}
|
void setBreadth(qint32 w) {m_w = w;}
|
||||||
qint32 breadth() const {return m_w;}
|
qint32 breadth() const {return m_w;}
|
||||||
float fSpan() const {return m_fSpan;}
|
float fSpan() const {return m_fSpan;}
|
||||||
|
QVector<QColor> const& colors();
|
||||||
void setColours(QVector<QColor> const& cl);
|
void setColours(QVector<QColor> const& cl);
|
||||||
void setFlatten(bool b1, bool b2);
|
void setFlatten(bool b1, bool b2);
|
||||||
void setTol(int n);
|
void setTol(int n);
|
||||||
@ -90,7 +91,8 @@ public:
|
|||||||
void setReference(bool b) {m_bReference = b;}
|
void setReference(bool b) {m_bReference = b;}
|
||||||
bool Reference() const {return m_bReference;}
|
bool Reference() const {return m_bReference;}
|
||||||
#endif
|
#endif
|
||||||
void drawRed(int ia, int ib, float swide[]);
|
void drawDecodeLine(const QColor &color, int ia, int ib);
|
||||||
|
void drawHorizontalLine(const QColor &color, int x, int width);
|
||||||
void setVHF(bool bVHF);
|
void setVHF(bool bVHF);
|
||||||
void setRedFile(QString fRed);
|
void setRedFile(QString fRed);
|
||||||
bool scaleOK () const {return m_bScaleOK;}
|
bool scaleOK () const {return m_bScaleOK;}
|
||||||
|
@ -167,6 +167,8 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) :
|
|||||||
ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool());
|
ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool());
|
||||||
ui->cbControls->setChecked(!m_settings->value("HideControls", false).toBool());
|
ui->cbControls->setChecked(!m_settings->value("HideControls", false).toBool());
|
||||||
ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt());
|
ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt());
|
||||||
|
ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool());
|
||||||
|
ui->autoDriftAutoStopCheckBox->setChecked(m_settings->value ("StopAutoSyncOnDecode", true).toBool());
|
||||||
|
|
||||||
auto splitState = m_settings->value("SplitState").toByteArray();
|
auto splitState = m_settings->value("SplitState").toByteArray();
|
||||||
if(!splitState.isEmpty()){
|
if(!splitState.isEmpty()){
|
||||||
@ -242,11 +244,80 @@ void WideGraph::saveSettings() //saveS
|
|||||||
m_settings->setValue ("FilterOpacityPercent", ui->filterOpacitySpinBox->value());
|
m_settings->setValue ("FilterOpacityPercent", ui->filterOpacitySpinBox->value());
|
||||||
m_settings->setValue ("SplitState", ui->splitter->saveState());
|
m_settings->setValue ("SplitState", ui->splitter->saveState());
|
||||||
m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value());
|
m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value());
|
||||||
|
m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked());
|
||||||
|
m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WideGraph::drawRed(int ia, int ib)
|
bool WideGraph::shouldDisplayDecodeAttempts(){
|
||||||
|
return ui->decodeAttemptCheckBox->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WideGraph::shouldAutoSync(){
|
||||||
|
return ui->autoDriftButton->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WideGraph::notifyDriftedSignalsDecoded(int /*signalsDecoded*/){
|
||||||
|
if(ui->autoDriftAutoStopCheckBox->isChecked()){
|
||||||
|
ui->autoDriftButton->setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WideGraph::on_autoDriftButton_toggled(bool checked){
|
||||||
|
static bool connected = false;
|
||||||
|
if(!connected){
|
||||||
|
connect(&m_autoSyncTimer, &QTimer::timeout, this, [this](){
|
||||||
|
// if auto drift isn't checked, don't worry about this...
|
||||||
|
if(!ui->autoDriftButton->isChecked()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// uncheck after timeout
|
||||||
|
if(m_autoSyncTimeLeft == 0){
|
||||||
|
ui->autoDriftButton->setChecked(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set new text and decrement timeleft
|
||||||
|
auto text = ui->autoDriftButton->text();
|
||||||
|
auto newText = QString("%1 (%2)").arg(text.left(text.indexOf("(")).trimmed()).arg(m_autoSyncTimeLeft--);
|
||||||
|
ui->autoDriftButton->setText(newText);
|
||||||
|
});
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if in the future we want to auto sync timeout after a time period
|
||||||
|
bool autoSyncTimeout = false;
|
||||||
|
|
||||||
|
auto text = ui->autoDriftButton->text();
|
||||||
|
|
||||||
|
if(autoSyncTimeout){
|
||||||
|
if(checked){
|
||||||
|
m_autoSyncTimeLeft = 120;
|
||||||
|
m_autoSyncTimer.setInterval(1000);
|
||||||
|
m_autoSyncTimer.start();
|
||||||
|
ui->autoDriftButton->setText(QString("%1 (%2)").arg(text.replace("Start", "Stop")).arg(m_autoSyncTimeLeft--));
|
||||||
|
} else {
|
||||||
|
m_autoSyncTimeLeft = 0;
|
||||||
|
m_autoSyncTimer.stop();
|
||||||
|
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if(checked){
|
||||||
|
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop"));
|
||||||
|
} else {
|
||||||
|
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WideGraph::drawDecodeLine(const QColor &color, int ia, int ib)
|
||||||
{
|
{
|
||||||
ui->widePlot->drawRed(ia,ib,swide);
|
ui->widePlot->drawDecodeLine(color, ia, ib);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WideGraph::drawHorizontalLine(const QColor &color, int x, int width){
|
||||||
|
ui->widePlot->drawHorizontalLine(color, x, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2
|
void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2
|
||||||
@ -356,6 +427,8 @@ void WideGraph::drawSwide(){
|
|||||||
swideLocal[i] = flagValue;
|
swideLocal[i] = flagValue;
|
||||||
}
|
}
|
||||||
ui->widePlot->draw(swideLocal,true,false);
|
ui->widePlot->draw(swideLocal,true,false);
|
||||||
|
} else if(lastSecondInPeriod != secondInPeriod) {
|
||||||
|
//ui->widePlot->drawHorizontalLine(Qt::white, 0, 5);
|
||||||
}
|
}
|
||||||
lastSecondInPeriod=secondInPeriod;
|
lastSecondInPeriod=secondInPeriod;
|
||||||
|
|
||||||
@ -678,6 +751,10 @@ void WideGraph::readPalette () //readPalette
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVector<QColor> const& WideGraph::colors(){
|
||||||
|
return ui->widePlot->colors();
|
||||||
|
}
|
||||||
|
|
||||||
void WideGraph::on_paletteComboBox_activated (QString const& palette) //palette selector
|
void WideGraph::on_paletteComboBox_activated (QString const& palette) //palette selector
|
||||||
{
|
{
|
||||||
m_waterfallPalette = palette;
|
m_waterfallPalette = palette;
|
||||||
@ -905,6 +982,8 @@ void WideGraph::on_driftSyncResetButton_clicked(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WideGraph::setDrift(int n){
|
void WideGraph::setDrift(int n){
|
||||||
|
int prev = drift();
|
||||||
|
|
||||||
DriftingDateTime::setDrift(n);
|
DriftingDateTime::setDrift(n);
|
||||||
|
|
||||||
qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n;
|
qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n;
|
||||||
@ -914,6 +993,12 @@ void WideGraph::setDrift(int n){
|
|||||||
if(ui->driftSpinBox->value() != n){
|
if(ui->driftSpinBox->value() != n){
|
||||||
ui->driftSpinBox->setValue(n);
|
ui->driftSpinBox->setValue(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit drifted(prev, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WideGraph::drift(){
|
||||||
|
return DriftingDateTime::drift();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WideGraph::setQSYEnabled(bool enabled){
|
void WideGraph::setQSYEnabled(bool enabled){
|
||||||
|
13
widegraph.h
13
widegraph.h
@ -63,10 +63,14 @@ public:
|
|||||||
int smoothYellow();
|
int smoothYellow();
|
||||||
void setRxBand (QString const& band);
|
void setRxBand (QString const& band);
|
||||||
void setWSPRtransmitted();
|
void setWSPRtransmitted();
|
||||||
void drawRed(int ia, int ib);
|
void drawDecodeLine(const QColor &color, int ia, int ib);
|
||||||
|
void drawHorizontalLine(const QColor &color, int x, int width);
|
||||||
void setVHF(bool bVHF);
|
void setVHF(bool bVHF);
|
||||||
void setRedFile(QString fRed);
|
void setRedFile(QString fRed);
|
||||||
void setTurbo(bool turbo);
|
void setTurbo(bool turbo);
|
||||||
|
bool shouldDisplayDecodeAttempts();
|
||||||
|
bool shouldAutoSync();
|
||||||
|
QVector<QColor> const& colors();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void freezeDecode2(int n);
|
void freezeDecode2(int n);
|
||||||
@ -74,6 +78,7 @@ signals:
|
|||||||
void setXIT2(int n);
|
void setXIT2(int n);
|
||||||
void setFreq3(int rxFreq, int txFreq);
|
void setFreq3(int rxFreq, int txFreq);
|
||||||
void qsy(int hzDelta);
|
void qsy(int hzDelta);
|
||||||
|
void drifted(int prev, int cur);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void wideFreezeDecode(int n);
|
void wideFreezeDecode(int n);
|
||||||
@ -82,8 +87,10 @@ public slots:
|
|||||||
void setControlsVisible(bool visible);
|
void setControlsVisible(bool visible);
|
||||||
bool controlsVisible();
|
bool controlsVisible();
|
||||||
void setDrift(int n);
|
void setDrift(int n);
|
||||||
|
int drift();
|
||||||
void setQSYEnabled(bool enabled);
|
void setQSYEnabled(bool enabled);
|
||||||
void setPaused(bool paused){ m_paused = paused; }
|
void setPaused(bool paused){ m_paused = paused; }
|
||||||
|
void notifyDriftedSignalsDecoded(int signalsDecoded);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent (QKeyEvent *e) override;
|
void keyPressEvent (QKeyEvent *e) override;
|
||||||
@ -117,6 +124,7 @@ private slots:
|
|||||||
void on_filterCheckBox_toggled(bool b);
|
void on_filterCheckBox_toggled(bool b);
|
||||||
void on_filterOpacitySpinBox_valueChanged(int n);
|
void on_filterOpacitySpinBox_valueChanged(int n);
|
||||||
|
|
||||||
|
void on_autoDriftButton_toggled(bool checked);
|
||||||
void on_driftSpinBox_valueChanged(int n);
|
void on_driftSpinBox_valueChanged(int n);
|
||||||
void on_driftSyncButton_clicked();
|
void on_driftSyncButton_clicked();
|
||||||
void on_driftSyncEndButton_clicked();
|
void on_driftSyncEndButton_clicked();
|
||||||
@ -158,6 +166,9 @@ private:
|
|||||||
bool m_bRef;
|
bool m_bRef;
|
||||||
bool m_bHaveTransmitted; //Set true at end of a WSPR transmission
|
bool m_bHaveTransmitted; //Set true at end of a WSPR transmission
|
||||||
|
|
||||||
|
QTimer m_autoSyncTimer;
|
||||||
|
int m_autoSyncTimeLeft;
|
||||||
|
|
||||||
QTimer m_drawTimer;
|
QTimer m_drawTimer;
|
||||||
QMutex m_drawLock;
|
QMutex m_drawLock;
|
||||||
|
|
||||||
|
355
widegraph.ui
355
widegraph.ui
@ -179,7 +179,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>270</width>
|
<width>323</width>
|
||||||
<height>372</height>
|
<height>372</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -427,9 +427,9 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>-193</y>
|
<y>0</y>
|
||||||
<width>267</width>
|
<width>323</width>
|
||||||
<height>723</height>
|
<height>742</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
@ -656,92 +656,106 @@
|
|||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Waterfall</string>
|
<string>Waterfall</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item row="1" column="0">
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Gain:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QSlider" name="gainSlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>100</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Waterfall gain</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-50</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TicksAbove</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Zero:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSlider" name="zeroSlider">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>100</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Waterfall zero</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>-50</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickPosition">
|
||||||
|
<enum>QSlider::TicksAbove</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="decodeAttemptCheckBox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Gain:</string>
|
<string>Display Decode Attempts</string>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QSlider" name="gainSlider">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>100</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Waterfall gain</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>-50</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>50</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="tickPosition">
|
|
||||||
<enum>QSlider::TicksAbove</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QSlider" name="zeroSlider">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>100</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>16777215</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Waterfall zero</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>-50</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>50</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="tickPosition">
|
|
||||||
<enum>QSlider::TicksAbove</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Zero:</string>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -961,8 +975,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>281</width>
|
<width>337</width>
|
||||||
<height>198</height>
|
<height>351</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -1007,67 +1021,114 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="driftSyncMinuteButton">
|
<widget class="QGroupBox" name="groupBox_10">
|
||||||
<property name="minimumSize">
|
<property name="title">
|
||||||
<size>
|
<string>Automatic</string>
|
||||||
<width>0</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a minute.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Sync Time Drift to Now (Minute Start)</string>
|
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="autoDriftButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Automatically synchronize time drift every second to decodes of NORMAL and SLOW signals observed.</p><p>This process is CPU intensive and may cause abnormal decoder behavior if run for extended periods of time. Default operation should be paired with stopping automatic time drift after signals have been decoded. </p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Start Automatic Time Drift</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="autoDriftAutoStopCheckBox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Stop Automatic Drift After First Decode</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="driftSyncButton">
|
<widget class="QGroupBox" name="groupBox_11">
|
||||||
<property name="minimumSize">
|
<property name="title">
|
||||||
<size>
|
<string>Manual</string>
|
||||||
<width>0</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle in the current transmission speed.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Sync Time Drift to Now (TX Start)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="driftSyncEndButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle in the current transmission speed.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Sync Time Drift to Now (TX End)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="driftSyncResetButton">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>30</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Reset your time drift to zero.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Reset Time Drift</string>
|
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="driftSyncMinuteButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a minute.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Set Time Drift to Now (Minute Start)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="driftSyncEndButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle in the current transmission speed.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Set Time Drift to Now (TX End)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="driftSyncButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle in the current transmission speed.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Set Time Drift to Now (TX Start)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="driftSyncResetButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>30</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Reset your time drift to zero.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset Time Drift</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
Loading…
Reference in New Issue
Block a user