kopia lustrzana https://github.com/xdsopl/qsstv
864 wiersze
25 KiB
C++
864 wiersze
25 KiB
C++
/**************************************************************************
|
|
* Copyright (C) 2000-2019 by Johan Maes *
|
|
* on4qz@telenet.be *
|
|
* http://users.telenet.be/on4qz *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "sourcedecoder.h"
|
|
#include "drm.h"
|
|
#include "appglobal.h"
|
|
#include "drmproto.h"
|
|
#include "configparams.h"
|
|
#include "dispatch/dispatcher.h"
|
|
#include "reedsolomoncoder.h"
|
|
#include "demodulator.h"
|
|
#include "ftpfunctions.h"
|
|
#include "configparams.h"
|
|
#include "logbook/logbook.h"
|
|
#include "drmstatusframe.h"
|
|
|
|
|
|
#include <QThread>
|
|
#include <QApplication>
|
|
#include <QFileInfo>
|
|
#include <QFile>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
sourceDecoder::sourceDecoder(QObject *parent) : QObject(parent)
|
|
{
|
|
transportBlockPtrList.clear();
|
|
ff=new ftpFunctions;
|
|
connect(ff,SIGNAL(downloadDone(bool,QString)),SLOT(slotDownloadDone(bool,QString)));
|
|
|
|
}
|
|
|
|
void sourceDecoder::init()
|
|
{
|
|
lastTransportBlockPtr=NULL;
|
|
bodyTotalSegments=0;
|
|
checkIt=false;
|
|
erasureList.clear();
|
|
lastContinuityIndex=-1;
|
|
alreadyDisplayed=false;
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief decode of valid data block
|
|
|
|
• header 8 bits.
|
|
• data field n bytes.
|
|
• CRC 16 bits.
|
|
|
|
|
|
\return bool return true if successful
|
|
*/
|
|
bool sourceDecoder::decode()
|
|
{
|
|
double checksum;
|
|
int N_partB;
|
|
// if(!demodulatorPtr->isTimeSync())
|
|
|
|
if (channel_decoded_data_buffer_data_valid != 1) return false;
|
|
if (audio_data_flag == 0)
|
|
{
|
|
addToLog("audio decoding not implemented in qsstv !\n",LOGDRMSRC); return false;
|
|
}
|
|
addToLog("Datapacket received",LOGPERFORM);
|
|
N_partB = (int) (length_decoded_data/ 8);
|
|
addToLog(QString("N-partB lenght=%1").arg(N_partB),LOGDRMSRC);
|
|
if(N_partB>PACKETBUFFERLEN)
|
|
{
|
|
addToLog(QString("packet buffer length exceeded: lenght=%1").arg(N_partB),LOGDRMMOT);
|
|
}
|
|
bits2bytes (channel_decoded_data_buffer, N_partB * 8, packetBuffer);
|
|
crc16_bytewise(&checksum, packetBuffer,N_partB);
|
|
if(fabs (checksum) <= DBL_EPSILON)
|
|
{
|
|
if(!setupDataBlock(packetBuffer,true,N_partB))
|
|
{
|
|
msc_valid=INVALID;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
msc_valid=INVALID;
|
|
return false;
|
|
}
|
|
// at this point we have a dataPacket we now check header / data and buils a transport stream
|
|
switch(currentDataPacket.dataGroupType)
|
|
{
|
|
case MOTDATA: addToLog("Datasegment",LOGDRMSRC); addDataSegment(); break;
|
|
case MOTHEAD: addToLog("Headersegment",LOGDRMSRC); addHeaderSegment(); break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool sourceDecoder::setupDataBlock(unsigned char *buffer,bool crcIsOK,int len)
|
|
{
|
|
currentDataBlock.length=len;
|
|
unsigned char header=buffer[0];
|
|
unsigned int availableBytes;
|
|
const char *bufPtr;
|
|
currentDataBlock.ba=QByteArray((char *)buffer,len);
|
|
currentDataBlock.firstFlag=currentDataBlock.lastFlag=false;
|
|
if(header & 0x80) currentDataBlock.firstFlag = true;
|
|
if(header & 0x40) currentDataBlock.lastFlag = true;
|
|
currentDataBlock.packetID = (header & 0x30) >> 4;
|
|
currentDataBlock.PPI = (header & 0x8) >> 3;
|
|
currentDataBlock.continuityIndex = (header & 0x7);
|
|
currentDataBlock.log();
|
|
if ((currentDataBlock.PPI != 0) && (crcIsOK))
|
|
{
|
|
availableBytes=buffer[1];
|
|
bufPtr=(const char *)&buffer[2];
|
|
}
|
|
else
|
|
{
|
|
availableBytes=len-3;
|
|
bufPtr=(const char *)&buffer[1];
|
|
}
|
|
if(currentDataBlock.firstFlag)
|
|
{
|
|
holdingBuffer.clear();
|
|
lastContinuityIndex=currentDataBlock.continuityIndex;
|
|
}
|
|
else
|
|
{
|
|
if(lastContinuityIndex<0)
|
|
{
|
|
return false;
|
|
}
|
|
lastContinuityIndex=(lastContinuityIndex+1)%8;
|
|
if(currentDataBlock.continuityIndex!=lastContinuityIndex)
|
|
{
|
|
lastContinuityIndex=-1;
|
|
return false;
|
|
}
|
|
|
|
}
|
|
holdingBuffer.append(bufPtr,availableBytes);
|
|
if(currentDataBlock.lastFlag)
|
|
{
|
|
return setupDataPacket(holdingBuffer);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void dataBlock::log()
|
|
{
|
|
addToLog(QString("FFlag %1,LFlag %2, PacketID %3,PPI %4,ContIdx %5, CRC %6 len %7")
|
|
.arg(firstFlag).arg(lastFlag).arg(packetID).arg(PPI).arg(continuityIndex).arg(crcOK).arg(length),LOGDRMMOT);
|
|
}
|
|
|
|
//MSC Header Description 2 bytes or 4 bytes if Extension field
|
|
// First Byte
|
|
// B7 Extension Flag Header has 2 more bytes in the Extension field
|
|
// B6 CRC Flag MOT has CRC
|
|
// B5 Session Flag MOT last flag and segment number present.
|
|
// B4 UserAccess Flag Mot user access field present.
|
|
// B3-B0 Data Group Type
|
|
// 0 0 0 0 (0) General data;
|
|
// 0 0 0 1 (1) CA messages (for example ECMs or EMMs: see subclause 9.3.2.1);
|
|
// 0 0 1 0 (2) General data and CA parameters (for example, DGCA);
|
|
// 0 0 1 1 (3) MOT header information;
|
|
// 0 1 0 0 (4) MOT data
|
|
// 0 1 0 1 (5) MOT data and CA parameters.
|
|
|
|
//Second Byte
|
|
// B7-B4 Continuity Index
|
|
// B3-B0 Repetition Index
|
|
|
|
// 2 more extension bytes if flag is set
|
|
// B15-B0 Extension Field - no further specifications
|
|
|
|
// Session Header (presence indicated by Session Flag
|
|
|
|
// if last flag and segment number present
|
|
// B15 last segment
|
|
// B14-B0 segment number
|
|
|
|
// user access fields
|
|
// B7-B5 rfa (not used)
|
|
// B4 TransportID present flag
|
|
// B3-B0 length of user access fields
|
|
|
|
// if set
|
|
// B15-B0 TransportID
|
|
// other bytes filled with End user address field
|
|
|
|
// This is followed with a 2 bytes Segmentation header
|
|
// B15-B13 Repetition Count (not used)
|
|
// B12-B0 Length of the data that follows
|
|
|
|
|
|
bool sourceDecoder::setupDataPacket(QByteArray ba)
|
|
{
|
|
double checksum;
|
|
unsigned char lengthIndicator;
|
|
unsigned char header;
|
|
|
|
currentDataPacket.ba=ba; // drop crc
|
|
header=ba.at(0);
|
|
currentDataPacket.transportID =0xFFFF;
|
|
currentDataPacket.extFlag=false;
|
|
currentDataPacket.crcFlag=false;
|
|
currentDataPacket.sessionFlag=false;
|
|
currentDataPacket.userFlag=false;
|
|
currentDataPacket.lastSegment=false;
|
|
currentDataPacket.crcOK=currentDataBlock.crcOK;
|
|
|
|
if(header&0x10) currentDataPacket.userFlag=true;
|
|
currentDataPacket.dataGroupType=(edataGroupType)(header&0x07);
|
|
if(header&0x80) currentDataPacket.extFlag=true;
|
|
if(header&0x20) currentDataPacket.sessionFlag=true;
|
|
|
|
if(header&0x40)
|
|
{
|
|
currentDataPacket.crcFlag=true;
|
|
crc16_bytewise (&checksum,(unsigned char *)currentDataPacket.ba.data(),currentDataPacket.ba.count());
|
|
if (fabs (checksum) <= DBL_EPSILON)
|
|
{
|
|
currentDataPacket.crcOK=true;
|
|
}
|
|
else
|
|
{
|
|
currentDataPacket.crcOK=false;
|
|
msc_valid=INVALID;
|
|
return false;
|
|
}
|
|
currentDataPacket.chop(2); // drop crc
|
|
}
|
|
currentDataPacket.advance(2); //skip header and continuity bits
|
|
if(currentDataPacket.extFlag) currentDataPacket.advance(2); // just skip the extension bytes
|
|
|
|
if(currentDataPacket.sessionFlag)
|
|
{
|
|
currentDataPacket.segmentNumber = (((unsigned char)(currentDataPacket.ba.at(0)) & 0x7F))*256+ ((unsigned char)currentDataPacket.ba.at(1)) ;
|
|
if(currentDataPacket.ba.at(0)&0x80)
|
|
{
|
|
currentDataPacket.lastSegment=true;
|
|
}
|
|
currentSegmentNumber=currentDataPacket.segmentNumber;
|
|
currentDataPacket.advance(2);
|
|
}
|
|
|
|
if (currentDataPacket.userFlag)
|
|
{
|
|
currentDataPacket.userAccessField = (unsigned char)(currentDataPacket.ba.at(0));
|
|
currentDataPacket.advance(1);
|
|
lengthIndicator = (currentDataPacket.userAccessField& 0xF);
|
|
|
|
if((currentDataPacket.userAccessField & 0x10) && (lengthIndicator>=2)) currentDataPacket.transportID = (((unsigned char)(currentDataPacket.ba.at(0))))*256+ ((unsigned char)currentDataPacket.ba.at(1)) ;
|
|
currentDataPacket.advance(lengthIndicator);
|
|
}
|
|
currentDataPacket.segmentSize=(((unsigned char)(currentDataPacket.ba.at(0)) & 0x1F))*256+ ((unsigned char)currentDataPacket.ba.at(1));
|
|
currentDataPacket.advance(2);
|
|
currentDataPacket.lenght=currentDataPacket.ba.count();
|
|
currentDataPacket.log();
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void dataPacket::log()
|
|
{
|
|
addToLog(QString("extF %1,dType %2, sessionF %3, lastSegment %4, segment# %5, userF %6, transportId %7, len %8")
|
|
.arg(extFlag).arg(dataGroupType).arg(sessionFlag).arg(lastSegment).arg(segmentNumber).arg(userFlag).arg(transportID).arg(lenght),LOGDRMMOT);
|
|
}
|
|
|
|
|
|
|
|
bool sourceDecoder::addHeaderSegment()
|
|
{
|
|
loadRXImageEvent *stce;
|
|
displayMBoxEvent *stmb;
|
|
transportBlock *tbPtr;
|
|
addToLog(QString("Header segsize: %1").arg(currentDataPacket.segmentSize),LOGDRMSRC);
|
|
tbPtr=getTransporPtr(currentDataPacket.transportID,true);
|
|
if(!tbPtr->alreadyReceived) msc_valid=VALID;
|
|
else
|
|
{
|
|
msc_valid=ALREADYRECEIVED;
|
|
if(!alreadyDisplayed)
|
|
{
|
|
alreadyDisplayed=true;
|
|
// redisplay
|
|
stce= new loadRXImageEvent(QString("%1").arg(tbPtr->newFileName));
|
|
QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
|
|
stmb= new displayMBoxEvent("DRM Receive",QString("File %1 already received").arg(tbPtr->newFileName));
|
|
QApplication::postEvent( dispatcherPtr, stmb );
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
tbPtr->headerReceived=true;
|
|
unsigned char *dataPtr=(unsigned char *)currentDataPacket.ba.data();
|
|
unsigned char PLI;
|
|
unsigned char paramID;
|
|
unsigned char extBit;
|
|
unsigned short dataFieldLength;
|
|
tbPtr->bodySize =
|
|
(((unsigned int)dataPtr[0]) << 20) +
|
|
(((unsigned int)dataPtr[1]) << 12) +
|
|
(((unsigned int)dataPtr[2]) << 4) +
|
|
((((unsigned int)dataPtr[3]) & 0xF0) >> 4);
|
|
|
|
tbPtr->headerSize =
|
|
(((unsigned int)dataPtr[3] & 0x0F) << 9) +
|
|
(((unsigned int)dataPtr[4]) << 1) +
|
|
((((unsigned int)dataPtr[5]) & 0x80) >> 7);
|
|
|
|
tbPtr->contentType = (((unsigned int)dataPtr[5] & 0x7E) >> 1);
|
|
tbPtr->contentSubtype = ((((unsigned int)dataPtr[5]) & 0x1) <<8) +((unsigned int)dataPtr[6]);
|
|
currentDataPacket.advance(7); // size of header core
|
|
// The header core is followed by a number of parameter blocks
|
|
// the first byte of every parameter block contains a 2-bits PLI (B7 and B6) indicating the type of parameter block.
|
|
|
|
while(currentDataPacket.ba.count())
|
|
{
|
|
PLI=dataPtr[0]>>6;
|
|
paramID=dataPtr[0]&0x3F;
|
|
switch (PLI)
|
|
{
|
|
case 0:
|
|
currentDataPacket.advance(1);
|
|
break;
|
|
case 1:
|
|
loadParams(tbPtr,paramID,1);
|
|
currentDataPacket.advance(2);
|
|
break;
|
|
case 2:
|
|
loadParams(tbPtr,paramID,4);
|
|
currentDataPacket.advance(5);
|
|
break;
|
|
case 3:
|
|
extBit=dataPtr[0]&0x80;
|
|
if(extBit)
|
|
{
|
|
dataFieldLength=256*(dataPtr[0]&0x7F)+dataPtr[1];
|
|
currentDataPacket.advance(2);
|
|
}
|
|
else
|
|
{
|
|
dataFieldLength=dataPtr[0]&0x7F;
|
|
currentDataPacket.advance(1);
|
|
}
|
|
loadParams(tbPtr,paramID,dataFieldLength);
|
|
currentDataPacket.advance(dataFieldLength);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
|
|
}
|
|
|
|
void sourceDecoder::loadParams(transportBlock *tbPtr,unsigned char paramID,int len)
|
|
{
|
|
// QByteArray testBA;
|
|
// char *data;
|
|
// unsigned int ct;
|
|
rxDRMStatusEvent *stce;
|
|
QString tmp,t;
|
|
switch(paramID)
|
|
{
|
|
case 5: // expiration
|
|
break;
|
|
case 6:
|
|
break;
|
|
case 12:
|
|
tbPtr->fileName=QString::fromLatin1(currentDataPacket.ba.data()+1).left(len-1);
|
|
stce= new rxDRMStatusEvent(QString("%1").arg(tbPtr->fileName));
|
|
QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void sourceDecoder::addDataSegment()
|
|
{
|
|
int i;
|
|
transportBlock *tbPtr;
|
|
tbPtr=getTransporPtr(currentDataPacket.transportID,true);
|
|
rxTransportID=currentDataPacket.transportID;
|
|
if(callsignValid) tbPtr->callsign=drmCallsign;
|
|
addToLog(QString("Data segsize: %1 segment# %2").arg(currentDataPacket.segmentSize).arg(currentDataPacket.segmentNumber),LOGDRMSRC);
|
|
|
|
|
|
if(!tbPtr->alreadyReceived) msc_valid=VALID;
|
|
else
|
|
{
|
|
msc_valid=ALREADYRECEIVED;
|
|
// return;
|
|
}
|
|
if(currentDataPacket.lastSegment)
|
|
{
|
|
tbPtr->totalSegments=currentDataPacket.segmentNumber+1;
|
|
tbPtr->lastSegmentReceived=true;
|
|
}
|
|
else
|
|
{
|
|
tbPtr->defaultSegmentSize=currentDataPacket.segmentSize;
|
|
}
|
|
for(i=tbPtr->dataSegmentPtrList.count();i<=currentDataPacket.segmentNumber;i++)
|
|
{
|
|
tbPtr->dataSegmentPtrList.append(new dataSegment(tbPtr->defaultSegmentSize));
|
|
}
|
|
|
|
if(!tbPtr->dataSegmentPtrList.at(currentDataPacket.segmentNumber)->hasData())
|
|
{
|
|
checkIt=true;
|
|
}
|
|
else
|
|
{
|
|
msc_valid=ALREADYRECEIVED;
|
|
}
|
|
if(tbPtr->alreadyReceived)
|
|
{
|
|
msc_valid=ALREADYRECEIVED;
|
|
checkIt=false;
|
|
}
|
|
if(tbPtr->totalSegments<currentDataPacket.segmentNumber+1) tbPtr->totalSegments=currentDataPacket.segmentNumber+1;
|
|
bodyTotalSegments=tbPtr->totalSegments;
|
|
rxSegments=tbPtr->segmentsReceived;
|
|
// bytesReceived=rxSegments*tbPtr->defaultSegmentSize;
|
|
|
|
tbPtr->dataSegmentPtrList.at(currentDataPacket.segmentNumber)->setData(currentDataPacket.ba,currentDataPacket.segmentNumber,true);
|
|
|
|
writeData(tbPtr);
|
|
|
|
}
|
|
|
|
void sourceDecoder::writeData(transportBlock *tbPtr)
|
|
{
|
|
int i;
|
|
|
|
QByteArray ba;
|
|
|
|
int length=0;
|
|
erasureList.clear();
|
|
erasureList.append(tbPtr->totalSegments);
|
|
erasureList.append(tbPtr->defaultSegmentSize);
|
|
addToLog("Write data",LOGDRMSRC);
|
|
for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
|
|
{
|
|
if(!tbPtr->dataSegmentPtrList.at(i)->hasData())
|
|
{
|
|
erasureList.append(i);
|
|
ba.append(QByteArray(tbPtr->defaultSegmentSize,0x00));
|
|
}
|
|
else
|
|
{
|
|
ba.append(tbPtr->dataSegmentPtrList.at(i)->data);
|
|
}
|
|
length+=tbPtr->dataSegmentPtrList.at(i)->data.size();
|
|
}
|
|
tbPtr->segmentsReceived=0;
|
|
drmBlockList.clear();
|
|
for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
|
|
{
|
|
if(tbPtr->dataSegmentPtrList.at(i)->hasData())
|
|
{
|
|
drmBlockList.append(i);
|
|
tbPtr->segmentsReceived++;
|
|
}
|
|
}
|
|
if(tbPtr->isAlmostComplete()<63)
|
|
{
|
|
return ;
|
|
}
|
|
addToLog("Image already received",LOGDRMSRC);
|
|
|
|
if(!tbPtr->lastSegmentReceived)
|
|
{
|
|
addToLog("Not lastSegmentReceived",LOGDRMSRC);
|
|
|
|
return;
|
|
}
|
|
checkSaveImage(ba,tbPtr);
|
|
}
|
|
|
|
|
|
void sourceDecoder::saveImage(transportBlock *tbPtr)
|
|
{
|
|
int i;
|
|
QByteArray hybridBa;
|
|
displayMBoxEvent *stmb=0;
|
|
QString downloadF;
|
|
modeCodeTmp=tbPtr->modeCode;
|
|
callsignTmp=tbPtr->callsign;
|
|
|
|
alreadyDisplayed=true;
|
|
if(tbPtr->alreadyReceived)
|
|
{
|
|
addToLog("Image already received",LOGDRMSRC);
|
|
return ;
|
|
}
|
|
if(tbPtr->fileName.isEmpty()) return ;
|
|
if(tbPtr->retrieveTries==0) lastAvgSNR=avgSNR;
|
|
isHybrid=false;
|
|
if((tbPtr->fileName.left(3)==".de") || (tbPtr->fileName.left(3)=="de_"))
|
|
{
|
|
isHybrid=true;
|
|
//ftpInterface ftpIntf("Save Image FTP");
|
|
if((enableHybridRx) && (soundRoutingInput!=soundBase::SNDINFROMFILE))
|
|
{
|
|
addToLog(QString("Hybrid filename %1, attempt %2").arg(tbPtr->fileName).arg(tbPtr->retrieveTries+1),LOGALL);
|
|
downloadF=rxDRMImagesPath+"/"+tbPtr->fileName;
|
|
for(i=0;i<tbPtr->dataSegmentPtrList.count();i++)
|
|
{
|
|
hybridBa+=tbPtr->dataSegmentPtrList.at(i)->data;
|
|
}
|
|
if(hc.deCrypt(&hybridBa))
|
|
{
|
|
if ((tbPtr->retrieveTries==0))
|
|
{
|
|
ff->setupFtp("GetHybridImage",hc.host(),hc.port(),hc.user(),hc.passwd(),hc.dir()+"/"+hybridFtpHybridFilesDirectory);
|
|
|
|
ff->downloadFile(tbPtr->fileName.toLatin1(),downloadF,false,false);
|
|
}
|
|
tbPtr->retrieveTries++;
|
|
|
|
}
|
|
else
|
|
{
|
|
if (tbPtr->retrieveTries==0)
|
|
{
|
|
stmb= new displayMBoxEvent("Hybrid Error","No file downloaded");
|
|
QApplication::postEvent( dispatcherPtr, stmb ); // Qt will delete it when done
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
downloadF.clear();
|
|
}
|
|
tbPtr->newFileName=downloadF;
|
|
} // end of hybrid.
|
|
else
|
|
{
|
|
displayReceivedImage(false,tbPtr->newFileName);
|
|
}
|
|
tbPtr->setAlreadyReceived(true);
|
|
}
|
|
|
|
|
|
void sourceDecoder::slotDownloadDone(bool err,QString filename)
|
|
{
|
|
if(err) return;
|
|
displayReceivedImage(true,filename);
|
|
// send notification
|
|
|
|
QFileInfo fi(filename);
|
|
QString fn=fi.fileName();
|
|
|
|
QString notificationFn = "Dummy"+fn+"+++."+myCallsign+QString(" %1dB SNR").arg(lastAvgSNR,0,'f',0);
|
|
if (enableHybridNotify && !hybridNotifyDir.isEmpty())
|
|
{
|
|
ff->changeThreadName("Notify RX");
|
|
ff->changePath("/"+hc.dir()+"/"+hybridNotifyDir,false);
|
|
ff->mremove("*+++."+myCallsign+" *",false,false);
|
|
ff->uploadData("Dummy\r\n",notificationFn,false,true);
|
|
}
|
|
else
|
|
{
|
|
ff->disconnect();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void sourceDecoder::displayReceivedImage(bool isHybrid,QString filename)
|
|
{
|
|
bool saveOK=false;
|
|
bool done=false;
|
|
bool textMode=false;
|
|
QString t;
|
|
displayTextEvent *stce;
|
|
|
|
QImage test;
|
|
if(filename.isEmpty()) return ;
|
|
if(!test.load(filename))
|
|
{
|
|
// maybe text
|
|
QFileInfo finfo(filename);
|
|
if((finfo.suffix()=="txt") || (finfo.suffix()=="chat") )
|
|
{
|
|
QFile fi(filename);
|
|
if(!fi.open(QIODevice::ReadOnly)) return;
|
|
t=fi.readAll();
|
|
stce= new displayTextEvent(t);
|
|
QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
|
|
textMode=true;
|
|
}
|
|
saveOK=true;
|
|
}
|
|
else
|
|
{
|
|
saveOK=true;
|
|
}
|
|
if(saveOK)
|
|
{
|
|
QFileInfo tfi(filename);
|
|
QString modestr(tfi.fileName());
|
|
modestr+=QString(" %1dB ").arg(lastAvgSNR,0,'f',0);
|
|
if(isHybrid) modestr+="Hybrid ";
|
|
modestr+=compactModeToString(modeCodeTmp);
|
|
logBookPtr->logQSO(callsignTmp,"DSSTV",modestr);
|
|
}
|
|
|
|
if(!textMode)
|
|
{
|
|
QString info="";
|
|
if (isHybrid) info+="Hybrid";
|
|
else info+=compactModeToString(modeCodeTmp);
|
|
|
|
info+=QString(" %2dB de %3").arg(lastAvgSNR,0,'f',0).arg(callsignTmp);
|
|
// slotRXNotification("*** "+info);
|
|
saveDRMImageEvent *ce = new saveDRMImageEvent(filename,info);
|
|
ce->waitFor(&done);
|
|
QApplication::postEvent(dispatcherPtr, ce);
|
|
while(!done)
|
|
{
|
|
qApp->processEvents();
|
|
usleep(10);
|
|
}
|
|
checkIt=false;
|
|
}
|
|
}
|
|
|
|
|
|
bool sourceDecoder::checkSaveImage(QByteArray ba,transportBlock *tbPtr)
|
|
{
|
|
prepareFixEvent *pe;
|
|
QFile outFile;
|
|
reedSolomonCoder rsd;
|
|
QString fileName;
|
|
QString extension;
|
|
fileName=rxDRMImagesPath+"/"+tbPtr->fileName;
|
|
tbPtr->newFileName=fileName;
|
|
QByteArray baFile;
|
|
QByteArray *baFilePtr;
|
|
|
|
if(!checkIt) return false;
|
|
QFileInfo qfinf(fileName);
|
|
extension=qfinf.suffix().toLower();
|
|
if((extension=="rs1") || (extension=="rs2") ||(extension=="rs3")||(extension=="rs4"))
|
|
{
|
|
// try to decode
|
|
addToLog("Try to decode",LOGDRMSRC);
|
|
if(tbPtr->alreadyReceived) return false;
|
|
if(!rsd.decode(ba,fileName,tbPtr->newFileName,baFile,extension,erasureList)) return false;
|
|
baFilePtr=&baFile;
|
|
}
|
|
else
|
|
{
|
|
addToLog("Check complete",LOGDRMSRC);
|
|
if(!tbPtr->isComplete()) return false;
|
|
tbPtr->newFileName=fileName;
|
|
if((tbPtr->fileName=="bsr.bin")&&(!tbPtr->alreadyReceived))
|
|
{
|
|
tbPtr->setAlreadyReceived(true);
|
|
pe = new prepareFixEvent(ba);
|
|
QApplication::postEvent(dispatcherPtr, pe);
|
|
return false;
|
|
}
|
|
baFilePtr=&ba;
|
|
}
|
|
if(!tbPtr->alreadyReceived)
|
|
{
|
|
outFile.setFileName(tbPtr->newFileName);
|
|
if(outFile.open(QIODevice::WriteOnly)<=0)
|
|
{
|
|
outFile.close();
|
|
return false;
|
|
}
|
|
outFile.write(*baFilePtr);
|
|
outFile.close();
|
|
erasureList.clear();
|
|
saveImage(tbPtr);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QList <bsrBlock> *sourceDecoder::getBSR()
|
|
{
|
|
int i;
|
|
transportBlock *tbPtr;
|
|
bsrList.clear();
|
|
|
|
for(i=0;i<transportBlockPtrList.count();i++)
|
|
{
|
|
tbPtr=transportBlockPtrList.at(i);
|
|
if(tbPtr->alreadyReceived) continue;
|
|
if(tbPtr->fileName=="bsr.bin") continue;
|
|
bsrList.append(bsrBlock(tbPtr));
|
|
}
|
|
return &bsrList;
|
|
}
|
|
|
|
bool sourceDecoder::storeBSR(transportBlock *tb, bool compat)
|
|
{
|
|
int i;
|
|
int needsFiller;
|
|
int prevErasure=0;
|
|
// QByteArray ba;
|
|
erasureList.clear();
|
|
erasureList.append(tb->totalSegments);
|
|
erasureList.append(tb->defaultSegmentSize);
|
|
for(i=0;i<tb->dataSegmentPtrList.count();i++)
|
|
{
|
|
if(!tb->dataSegmentPtrList.at(i)->hasData())
|
|
{
|
|
erasureList.append(i);
|
|
}
|
|
}
|
|
tb->baBSR.clear();
|
|
if(erasureList.count()<3) return false; //erasurelist has already totalSegments and defaultSegmentSize
|
|
tb->baBSR.append(QString::number(tb->transportID).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
tb->baBSR.append("H_OK\n");
|
|
tb->baBSR.append(QString::number(erasureList.at(1)).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
tb->baBSR.append(QString::number(erasureList.at(2)).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
|
|
prevErasure=erasureList.at(2);
|
|
needsFiller=false;
|
|
for(i=3;i<erasureList.count();i++) //skip
|
|
{
|
|
if(((prevErasure+1)==erasureList.at(i))&&(compat))
|
|
{
|
|
needsFiller=true;
|
|
}
|
|
else
|
|
{
|
|
if(needsFiller)
|
|
{
|
|
tb->baBSR.append(QString::number(-1).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
needsFiller=false;
|
|
}
|
|
tb->baBSR.append(QString::number(erasureList.at(i)).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
}
|
|
prevErasure=erasureList.at(i);
|
|
}
|
|
if(needsFiller)
|
|
{
|
|
tb->baBSR.append(QString::number(-1).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
needsFiller=false;
|
|
tb->baBSR.append(QString::number(erasureList.at(erasureList.count()-1)).toLatin1().data());
|
|
tb->baBSR.append("\n");
|
|
}
|
|
tb->baBSR.append("-99\n");
|
|
if(!compat)
|
|
{
|
|
QString temp;
|
|
tb->baBSR.append(tb->fileName+"\n");
|
|
temp=QString::number(tb->modeCode);
|
|
while(temp.length()<5) temp.prepend("0");
|
|
tb->baBSR.append(temp);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
transportBlock *sourceDecoder::getTransporPtr(unsigned short tId,bool create)
|
|
{
|
|
int i;
|
|
rxDRMStatusEvent *stce;
|
|
bool found=false;
|
|
for(i=0;i<transportBlockPtrList.count();i++)
|
|
{
|
|
if(transportBlockPtrList.at(i)->transportID==tId)
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
if(found) lastTransportBlockPtr=transportBlockPtrList.at(i);
|
|
else if (create)
|
|
{
|
|
callsignValid=false;
|
|
bodyTotalSegments=0;
|
|
drmBlockList.clear();
|
|
stce= new rxDRMStatusEvent("");
|
|
QApplication::postEvent( dispatcherPtr, stce ); // Qt will delete it when done
|
|
if(transportBlockPtrList.count()>=MAXTRANSPORTLISTS)
|
|
{
|
|
//delete the oldest
|
|
transportBlockPtrList.removeFirst();
|
|
}
|
|
for(i=0;i<transportBlockPtrList.count();)
|
|
{
|
|
if(transportBlockPtrList.at(i)->fileName=="bsr.bin")
|
|
{
|
|
transportBlockPtrList.takeAt(i);
|
|
}
|
|
else i++;
|
|
}
|
|
transportBlockPtrList.append(new transportBlock(tId));
|
|
lastTransportBlockPtr=transportBlockPtrList.last();
|
|
lastTransportBlockPtr->robMode=robustness_mode;
|
|
lastTransportBlockPtr->interLeaver=interleaver_depth_new;
|
|
lastTransportBlockPtr->mscMode=msc_mode_new; // qam
|
|
lastTransportBlockPtr->mpx=multiplex_description.PL_PartB;
|
|
lastTransportBlockPtr->spectrum=spectrum_occupancy_new;
|
|
// remap msc_new to modeCode
|
|
int mCode=1; //default QAM16
|
|
if(msc_mode_new==3) mCode=0;
|
|
if(msc_mode_new==0) mCode=2;
|
|
int protection=0;
|
|
if(multiplex_description.PL_PartB==1) protection=1;
|
|
lastTransportBlockPtr->modeCode=robustness_mode*10000+spectrum_occupancy_new*1000+protection*100+mCode*10+interleaver_depth_new;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
return lastTransportBlockPtr;
|
|
}
|
|
|
|
void sourceDecoder::removeTransporPtr(transportBlock * ptr)
|
|
{
|
|
int i;
|
|
for(i=0;i<transportBlockPtrList.count();i++)
|
|
{
|
|
if(transportBlockPtrList.at(i)==ptr)
|
|
{
|
|
transportBlockPtrList.takeAt(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|