// HamFax -- an application for sending and receiving amateur radio facsimiles // Copyright (C) 2001,2002 // Christof Schmitt, DH1CS // // 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 "Config.hpp" #include "FaxReceiver.hpp" #include FaxReceiver::FaxReceiver(QObject* parent) : QObject(parent), rawData(0) { timer=new QTimer(this); connect(timer,SIGNAL(timeout()),this,SLOT(adjustNext())); } void FaxReceiver::init(int sampleRate) { Config& config=Config::instance(); aptStartFreq=config.readNumEntry("/hamfax/APT/startFrequency"); aptStopFreq=config.readNumEntry("/hamfax/APT/stopFrequency"); txLPM=config.readNumEntry("/hamfax/fax/LPM"); phaseInvers=config.readBoolEntry("/hamfax/phasing/invert"); color=config.readBoolEntry("/hamfax/fax/color"); this->sampleRate=sampleRate; state=APTSTART; aptCount=aptTrans=0; aptStop=aptHigh=false; imageSample=0; rawData.resize(1024*1024*8); emit startReception(); } void FaxReceiver::decode(int* buf, int n) { if(n==0) endReception(); for(int i=0; i0) { if(static_cast(rawData.size())<=imageSample) { rawData.resize(rawData.size()+1024*1024); } decodeImage(buf[i]); } } } // The number of transistions between black and white is counted. After 1/2 // second, the frequency is calculated. If it matches the APT start frequency, // the state skips to the detection of phasing lines, if it matches the apt // stop frequency two times, the reception is ended. void FaxReceiver::decodeApt(const int& x) { if(x>229 && !aptHigh) { aptHigh=true; aptTrans++; } else if(x<25 && aptHigh) { aptHigh=false; } if(++aptCount >= sampleRate/2) { int f=sampleRate*aptTrans/aptCount; aptCount=aptTrans=0; emit aptFound(f); if(state==APTSTART) { if(f==aptStartFreq) { skip(); } } else { if(f==aptStopFreq) { if(aptStop) { endReception(); } else { aptStop=true; } } } } } // Phasing lines consist of 2.5% white at the beginning, 95% black and again // 2.5% white at the end (or inverted). In normal phasing lines we try to // count the length between the white-black transitions. If the line has // a reasonable amount of black (4.8%--5.2%) and the length fits in the // range of 60--360lpm (plus some tolerance) it is considered a valid // phasing line. Then the start of a line and the lpm is calculated. void FaxReceiver::decodePhasing(const int& x) { currPhaseLength++; if(x>128) { currPhaseHigh++; } if((!phaseInvers && x>229 && !phaseHigh) || ( phaseInvers && x<25 && phaseHigh)) { phaseHigh=phaseInvers?false:true; } else if((!phaseInvers && x<25 && phaseHigh) || ( phaseInvers && x>229 && !phaseHigh)) { phaseHigh=phaseInvers?true:false; if(currPhaseHigh>=(phaseInvers?0.948:0.048)*currPhaseLength && currPhaseHigh<=(phaseInvers?0.952:0.052)*currPhaseLength && static_cast(currPhaseLength)<=1.1*sampleRate && static_cast(currPhaseLength)>=0.15*sampleRate) { double l=60.0*sampleRate/currPhaseLength; emit phasingLine(l); lpmSum+=l; ++phaseLines; lpm=lpmSum/phaseLines; imageSample=static_cast(1.025*60./lpm*sampleRate); noPhaseLines=0; } else if(phaseLines>0 && ++noPhaseLines>=5) { state=IMAGE; double pos=std::fmod(imageSample,sampleRate*60/lpm); pos/=sampleRate*60.0/lpm; lastCol=static_cast(pos*width); pixel=pixelSamples=0; lastRow=99; // just !=0 which is the first row emit imageStarts(); } else if(currPhaseLength>5*sampleRate) { currPhaseLength=0; } currPhaseLength=currPhaseHigh=0; } } void FaxReceiver::decodeImage(const int& x) { int col=static_cast(width*std::fmod(imageSample,sampleRate*60/lpm) /sampleRate/60.0*lpm); int currRow=static_cast(imageSample*lpm/60.0/sampleRate); rawData[imageSample]=x; if(col==lastCol) { pixel+=x; pixelSamples++; } else { if(pixelSamples>0) { pixel/=pixelSamples; emit setPixel(lastCol, color?currRow/3:currRow, pixel,color?currRow%3:3); if(lastRow!=currRow && state!=PHASING) { emit row((lastRow=currRow)/(color?3:1)); } } lastCol=col; pixel=x; pixelSamples=1; } imageSample++; } void FaxReceiver::correctLPM(double d) { // the setting could have changed color=Config::instance().readBoolEntry("/hamfax/fax/color"); pixel=pixelSamples=imageSample=0; lastCol=99; lpm*= 1.0 + (color ? d/3.0 : d); rawIt=rawData.begin(); timer->start(0); emit redrawStarts(); } void FaxReceiver::correctWidth(int w) { pixel=pixelSamples=imageSample=0; lastCol=99; width=w; if(rawData.isNull()) { emit imageWidth(w); } else { rawIt=rawData.begin(); timer->start(0); emit newSize(0,0,w,0); emit redrawStarts(); } } void FaxReceiver::adjustNext(void) { for(int i=0; i<512; i++) { if(rawIt++>=rawData.end()) { timer->stop(); endReception(); break; } decodeImage(*rawIt); } } void FaxReceiver::skip(void) { if(state==APTSTART) { lpm=lpmSum=0; state=PHASING; phaseHigh = currentValue>=128 ? true : false; currPhaseLength=currPhaseHigh=0; phaseLines=noPhaseLines=0; emit startingPhasing(); } else if(state==PHASING) { lpm=txLPM; state=IMAGE; emit imageStarts(); double pos=std::fmod(imageSample,sampleRate*60/lpm); pos/=sampleRate*60.0/lpm; lastCol=static_cast(pos*width); pixel=pixelSamples=imageSample=0; lastRow=99; // just !=0 which is the first row } } // Here we want to remove the last detected phasing line and the following // non phasing line from the beginning of the image and one second of apt stop // from the end void FaxReceiver::endReception(void) { int h=lastRow-static_cast(lpm/60.0)-1; rawData.resize(imageSample); if(h>0) { emit newSize(0,2,0,color ? h/3 : h); emit bufferNotEmpty(true); } state=DONE; emit end(); } void FaxReceiver::releaseBuffer(void) { rawData.resize(0); emit bufferNotEmpty(false); } void FaxReceiver::setColor(bool b) { color=b; } void FaxReceiver::setAptStartFreq(int f) { aptStartFreq=f; } void FaxReceiver::setAptStopFreq(int f) { aptStopFreq=f; } void FaxReceiver::setWidth(int width) { this->width=width; } void FaxReceiver::setPhasePol(bool pol) { phaseInvers=pol; }