/* * mt63.cc -- MT63 transmitter and receiver in C++ for LINUX * * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC * * This file is part of MT63. * * MT63 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. * * MT63 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 MT63; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include // only for control printf's // #include #include "dsp.h" #include "mt63.h" #include "morse.dat" // Morse Code table #include "symbol.dat" // symbol shape #include "mt63intl.dat" // interleave patterns #include "alias_k5.dat" // anti-alias filter shapes #include "alias_1k.dat" // for 500, 1000 and 2000 Hz modes #include "alias_2k.dat" // ========================================================================== // Morse Encoder MorseEncoder::MorseEncoder() { TxMsg=NULL; } MorseEncoder::~MorseEncoder() { free(TxMsg); } void MorseEncoder::Free(void) { free(TxMsg); TxMsg=NULL; } int MorseEncoder::SetTxMsg(char *Msg) { int len=strlen(Msg)+1; if(ReallocArray(&TxMsg,len)) return -1; CopyArray(TxMsg,Msg,len); TxPtr=0; Code=0L; return 0; } int MorseEncoder::NextKey(void) { int key,ch; if(TxMsg==NULL) return -1; if(Code<=1) { ch=TxMsg[TxPtr]; if(ch==0) return -1; TxPtr++; if(ch>=1; return key; } // ========================================================================== // MT63 transmitter code MT63tx::MT63tx() { TxVect=NULL; PhaseCorr=NULL; CW_ID=NULL; } MT63tx::~MT63tx() { free(TxVect); free(PhaseCorr); free(CW_ID); } void MT63tx::Free(void) { free(TxVect); TxVect=NULL; free(PhaseCorr); PhaseCorr=NULL; Encoder.Free(); FFT.Free(); Window.Free(); Comb.Free(); WindowBuff.Free(); free(CW_ID); CW_ID=NULL; CW_Coder.Free(); } int MT63tx::Preset(int BandWidth, int LongInterleave, char *ID) { int err,len; int i,p,step,incr,mask; long MarkCode; switch(BandWidth) { case 500: FirstDataCarr=256; AliasShapeI=Alias_k5_I; AliasShapeQ=Alias_k5_Q; AliasFilterLen=Alias_k5_Len; DecimateRatio=8; break; case 1000: FirstDataCarr=128; AliasShapeI=Alias_1k_I; AliasShapeQ=Alias_1k_Q; AliasFilterLen=Alias_1k_Len; DecimateRatio=4; break; case 2000: FirstDataCarr=64; AliasShapeI=Alias_2k_I; AliasShapeQ=Alias_2k_Q; AliasFilterLen=Alias_2k_Len; DecimateRatio=2; break; default: return -1; } DataCarriers=64; // DataCarrSepar=4; // SymbolSepar=200; WindowLen=SymbolLen; TxWindow=SymbolShape; TxAmpl=4.0/DataCarriers; // for maximum output level we can set TxAmpl=4.0/DataCarriers CarrMarkCode=0x16918BBEL; CarrMarkAmpl=0; // WindowLen/32; if(LongInterleave) { DataInterleave=64; InterleavePattern=LongIntlvPatt; } else { DataInterleave=32; InterleavePattern=ShortIntlvPatt; } if(ReallocArray(&TxVect, DataCarriers)) goto Error; if(ReallocArray(&PhaseCorr, DataCarriers)) goto Error; err=WindowBuff.EnsureSpace(2*WindowLen); if(err) goto Error; WindowBuff.Len=2*WindowLen; err=Encoder.Preset(DataCarriers,DataInterleave,InterleavePattern,1); if(err) goto Error; err=FFT.Preset(WindowLen); if(err) goto Error; err=Window.Preset(WindowLen,SymbolSepar/2,TxWindow); if(err) goto Error; err=Comb.Preset(AliasFilterLen,AliasShapeI,AliasShapeQ,DecimateRatio); if(err) goto Error; mask=FFT.Size-1; // Preset the initial phase for each data carrier. // Here we only compute indexes to the FFT twiddle factors // so the actuall vector is FFT.Twiddle[TxVect[i]] for(step=0,incr=1,p=0,i=0; i>=1; PhaseCorr[i]&=mask; PhaseCorr[i+1]&=mask; } } */ if(ID!=NULL) { len=strlen(ID)+1; if(ReallocArray(&CW_ID,len)) goto Error; CopyArray(CW_ID,ID,len); } else { CW_Coder.Free(); free(CW_ID); CW_ID=NULL; } CW_Carr=(FirstDataCarr-2*DataCarrSepar)&mask; CW_Ampl=4*TxAmpl; CW_Phase=0; CW_PhaseCorr=((SymbolSepar/2)*CW_Carr)&mask; return 0; Error: Free(); return -1; } int MT63tx::SendTune(void) { int i,c,r,mask; float Ampl; mask=FFT.Size-1; Ampl=TxAmpl*sqrt(DataCarriers/2); for(i=0; i ", ch, ch>=' ' ? ch : '.'); for(i=0; i only phase correction { TxVect[i]=(TxVect[i]+PhaseCorr[i])&mask; } else // data bit = 0 => phase flip + phase correction { TxVect[i]=(TxVect[i]+PhaseCorr[i]+flip)&mask; } } ProcessTxVect(); return 0; } int MT63tx::SendJam(void) { int i,mask,left,right; mask=FFT.Size-1; left=FFT.Size/4; right=3*(FFT.Size/4); for(i=0; i=IntlvLen) p-=IntlvLen; } return 0; Error: Free(); return -1; } int MT63encoder::Process(char code) // encode an ASCII character "code" { int i,k; code&=CodeMask; for(i=0; i=IntlvSize) k-=IntlvSize; Output[i]=IntlvPipe[k+i]; } IntlvPtr+=DataCarriers; if(IntlvPtr>=IntlvSize) IntlvPtr-=IntlvSize; } else { for(i=0; i0.0) { Sum/=StepsPerSymb; Sum*=ScanLen; // printf("%3d: %8.5f\n",c,Sum); for(p=c,i=0; i=2*DataCarrSepar) n=0; } } IntegPtr+=ScanLen; if(IntegPtr>=ScanSize) IntegPtr=0; if(IntegPtr==0) { printf("NormPwr:\n"); for(i=0; i=IntlvLen) p-=IntlvLen; } // printf("\n"); IntlvSize=(IntlvLen+1)*ScanSize; if(ReallocArray(&IntlvPipe,IntlvSize)) goto Error; ClearArray(IntlvPipe,IntlvSize); IntlvPtr=0; if(ReallocArray(&WalshBuff,DataCarriers)) goto Error; if(ReallocArray(&DecodeSnrMid,ScanLen)) goto Error; if(ReallocArray(&DecodeSnrOut,ScanLen)) goto Error; ClearArray(DecodeSnrMid,ScanLen); ClearArray(DecodeSnrOut,ScanLen); SignalToNoise=0.0; CarrOfs=0; return 0; Error: Free(); return -1; } int MT63decoder::Process(float *data) { int s,i,k; float Min,Max,Sig,Noise,SNR; int MinPos,MaxPos,code; CopyArray(IntlvPipe+IntlvPtr,data,ScanSize); // printf("Decoder [%d/%d/%d]: \n",IntlvPtr,IntlvSize,ScanSize); for(s=0; s=IntlvSize) k-=IntlvSize; } WalshBuff[i]=IntlvPipe[k+s+i]; // printf(" %4d",k/ScanSize); } // printf("\n"); WalshTrans(WalshBuff,DataCarriers); Min=FindMin(WalshBuff,DataCarriers,MinPos); Max=FindMax(WalshBuff,DataCarriers,MaxPos); if(fabs(Max)>fabs(Min)) { code=MaxPos+DataCarriers; Sig=fabs(Max); WalshBuff[MaxPos]=0.0; } else { code=MinPos; Sig=fabs(Min); WalshBuff[MinPos]=0.0; } Noise=RMS(WalshBuff,DataCarriers); if(Noise>0.0) SNR=Sig/Noise; else SNR=0.0; LowPass2(SNR,DecodeSnrMid[s],DecodeSnrOut[s],W1,W2,W5); // printf("%2d: %02x => %c, %5.2f/%5.2f=>%5.2f <%5.2f>\n", // s,code,code<' ' ? '.' : (char)code, // Sig,Noise,SNR,DecodeSnrOut[s]); DecodePipe[DecodePtr+s]=code; } IntlvPtr+=ScanSize; if(IntlvPtr>=IntlvSize) IntlvPtr=0; DecodePtr+=ScanLen; if(DecodePtr>=DecodeSize) DecodePtr=0; Max=FindMax(DecodeSnrOut,ScanLen,MaxPos); Output=DecodePipe[DecodePtr+MaxPos]; SignalToNoise=Max; CarrOfs=MaxPos-(ScanLen-1)/2; /* code=Output; if((code>=' ')||(code=='\n')||(code=='\r')) printf("%c",code); else if(code!='\0') printf("<%02X>",code); */ return 0; } // ========================================================================== // MT63 receiver code MT63rx::MT63rx() { int s; FFTbuff=NULL; FFTbuff2=NULL; for(s=0; s<4; s++) SyncPipe[s]=NULL; SyncPhCorr=NULL; for(s=0; s<4; s++) { CorrelMid[s]=NULL; CorrelOut[s]=NULL; } PowerMid=NULL; PowerOut=NULL; for(s=0; s<4; s++) CorrelNorm[s]=NULL; for(s=0; s<4; s++) CorrelAver[s]=NULL; SymbFit=NULL; SymbPipe=NULL; FreqPipe=NULL; RefDataSlice=NULL; DataPipeLen=0; DataPipe=NULL; DataPwrMid=NULL; DataPwrOut=NULL; DataSqrMid=NULL; DataSqrOut=NULL; DataVect=NULL; DataPhase=NULL; DataPhase2=NULL; SpectraPower=NULL; } MT63rx::~MT63rx() { int s; free(FFTbuff); free(FFTbuff2); for(s=0; s<4; s++) free(SyncPipe[s]); free(SyncPhCorr); for(s=0; s<4; s++) { free(CorrelMid[s]); free(CorrelOut[s]); } free(PowerMid); free(PowerOut); for(s=0; s<4; s++) free(CorrelNorm[s]); for(s=0; s<4; s++) free(CorrelAver[s]); free(SymbFit); free(SymbPipe); free(FreqPipe); free(RefDataSlice); FreeArray2D(DataPipe,DataPipeLen); // for(s=0; sLen,ProcLine.InpLen); while((SyncProcPtr+WindowLen)", // SyncSymbConf,SyncLocked,SyncProcPtr,SyncPtr,SymbPtr,SyncSymbShift,SyncFreqOfs); if(SyncPtr==SymbPtr) { s1=SyncProcPtr-ProcDelay+((int)SyncSymbShift-SymbPtr*SyncStep); s2=s1+SymbolSepar/2; // printf(" Sample at %d,%d (SyncProcPtr-%d), time diff.=%d\n",s1,s2,SyncProcPtr-s1,s1-DataProcPtr); DataProcess(ProcLine.InpPtr+s1,ProcLine.InpPtr+s2,SyncFreqOfs,s1-DataProcPtr); DataProcPtr=s1; } // printf("\n"); SyncProcPtr+=SyncStep; } SyncProcPtr-=ProcLine.InpLen; DataProcPtr-=ProcLine.InpLen; return 0; } void MT63rx::DoCorrelSum(fcmpx *Correl1, fcmpx *Correl2, fcmpx *Aver) { dcmpx sx; int i,s,d; s=2*DataCarrSepar; d=DataCarriers*DataCarrSepar; sx.re=sx.im=0.0; for(i=0; i0.0) { dI=(I*I-Q*Q)/A; dQ=(2*I*Q)/A; } else { dI=dQ=0.0; } LowPass2(P,PowerMid[i],PowerOut[i],W1p,W2p,W5p); pI=PrevSlice[i].re*SyncPhCorr[i].re-PrevSlice[i].im*SyncPhCorr[i].im; pQ=PrevSlice[i].re*SyncPhCorr[i].im+PrevSlice[i].im*SyncPhCorr[i].re; Correl.re=dQ*pQ+dI*pI; Correl.im=dQ*pI-dI*pQ; LowPass2(&Correl,CorrelMid[SyncPtr]+i,CorrelOut[SyncPtr]+i,W1,W2,W5); PrevSlice[i].re=dI; PrevSlice[i].im=dQ; } if(SyncPtr==(SymbPtr^2)) { for(s=0; s0.0) { CorrelNorm[s][i].re=CorrelOut[s][i].re/PowerOut[i]; CorrelNorm[s][i].im=CorrelOut[s][i].im/PowerOut[i]; } else CorrelNorm[s][i].im=CorrelNorm[s][i].re=0.0; } } /* // another way to normalize - a better one ? for(i=0; i0.0) { for(s=0; s1) j-=(k-1)*DataCarrSepar; else if(k<(-1)) j-=(k+1)*DataCarrSepar; SymbFitPos=j; // printf(" => %2d",j); if(P>0.0) { SymbConf=Ampl(SymbFit[j]) + 0.5*(Ampl(SymbFit[j+1])+Ampl(SymbFit[j-1])); SymbConf*=0.5; I=SymbFit[j].re + 0.5*(SymbFit[j-1].re+SymbFit[j+1].re); Q=SymbFit[j].im + 0.5*(SymbFit[j-1].im+SymbFit[j+1].im); SymbTime.re=I; SymbTime.im=Q; SymbShift=(Phase(SymbTime)/(2*M_PI))*SymbolDiv; if(SymbShift<0) SymbShift+=SymbolDiv; // for(i=j-1; i<=j+1; i++) printf(" [%+5.2f,%+5.2f]",SymbFit[i].re,SymbFit[i].im); // make first estimation of FreqOfs // printf(" -> [%+5.2f,%+5.2f] =>",I,Q); // for(i=j-2; i<=j+2; i++) printf(" %+6.3f",I*SymbFit[i].re+Q*SymbFit[i].im); pI = ScalProd(I,Q,SymbFit[j]) + 0.7*ScalProd(I,Q,SymbFit[j-1]) + 0.7*ScalProd(I,Q,SymbFit[j+1]); pQ = 0.7*ScalProd(I,Q,SymbFit[j+1]) - 0.7*ScalProd(I,Q,SymbFit[j-1]) + 0.5*ScalProd(I,Q,SymbFit[j+2]) - 0.5*ScalProd(I,Q,SymbFit[j-2]); FreqOfs=j+Phase(pI,pQ)/(2.0*M_PI/8); /* SYNC TEST */ // refine the FreqOfs i=(int)floor(FreqOfs+0.5); s=(int)floor(SymbShift); s2=(s+1)&(SymbolDiv-1); // printf(" [%5.2f,%2d,%d,%d] ",FreqOfs,i,s,s2); w0=(s+1-SymbShift); w1=(SymbShift-s); // printf(" [%4.2f,%4.2f] ",w0,w1); A=(0.5*WindowLen)/SymbolSepar; I=w0*CorrelAver[s][i].re+w1*CorrelAver[s2][i].re; Q=w0*CorrelAver[s][i].im+w1*CorrelAver[s2][i].im; // printf(" [%5.2f,%2d] -> [%+5.2f,%+5.2f]",FreqOfs,i,I,Q); // FreqOfs=i+Phase(I,Q)/(2.0*M_PI)*0.5*A; // printf(" => %5.2f",FreqOfs); F0=i+Phase(I,Q)/(2.0*M_PI)*A-FreqOfs; Fl=F0-A; Fu=F0+A; if(fabs(Fl) (%5.2f,%5.2f,%5.2f) => %5.2f",Fl,F0,Fu,FreqOfs); } else { SymbTime.re=SymbTime.im=0.0; SymbConf=0.0; SymbShift=0.0; FreqOfs=0.0; } // here we have FreqOfs and SymbTime.re/im // printf("FreqOfs=%5.2f",FreqOfs); if(SyncLocked) { // flip the SymbTime if it doesn't agree with the average if(ScalProd(SymbTime,AverSymb)<0.0) { SymbTime.re=(-SymbTime.re); SymbTime.im=(-SymbTime.im); FreqOfs-=DataCarrSepar; } // reduce the freq. offset towards the average offset A=2*DataCarrSepar; k=(int)floor((FreqOfs-AverFreq)/A+0.5); FreqOfs-=k*A; /* SYNC TEST */ A=(0.5*WindowLen)/SymbolSepar; F0=FreqOfs-AverFreq; // correct freq. auto-correlator wrap Fl=F0-A; Fu=F0+A; if(fabs(Fl) (%5.2f,%5.2f,%5.2f) => %5.2f",Fl,F0,Fu,FreqOfs); } else // of if(SyncLocked) { // flip SymbTime if it doesn't agree with the previous if(ScalProd(SymbTime,SymbPipe[TrackPipePtr])<0.0) { SymbTime.re=(-SymbTime.re); SymbTime.im=(-SymbTime.im); FreqOfs-=DataCarrSepar; } // reduce the FreqOfs towards zero A=2*DataCarrSepar; k=(int)floor(FreqOfs/A+0.5); FreqOfs-=k*A; /* SYNC TEST */ F0=FreqOfs-FreqPipe[TrackPipePtr]; Fl=F0-A; Fu=F0+A; if(fabs(Fl) [%+5.2f,%+5.2f], %5.2f",SymbTime.re,SymbTime.im,FreqOfs); TrackPipePtr+=1; if(TrackPipePtr>=TrackPipeLen) TrackPipePtr-=TrackPipeLen; SymbPipe[TrackPipePtr]=SymbTime; // put SymbTime and FreqOfs into pipes FreqPipe[TrackPipePtr]=FreqOfs; // for averaging // find average symbol time Loops=SelFitAver(SymbPipe,TrackPipeLen,(float)3.0,4,AverSymb,Rms,Incl); // printf(" AverSymb=[%+5.2f,%+5.2f], RMS=%5.3f/%2d", // AverSymb.re,AverSymb.im,Rms,Incl); // find average freq. offset Loops=SelFitAver(FreqPipe,TrackPipeLen,(float)2.5,4,AverFreq,Rms,Incl); SyncFreqDev=Rms; // printf(" AverFreq=%+5.2f, RMS=%5.3f/%2d",AverFreq,Rms,Incl); SymbConf=Ampl(AverSymb); SyncSymbConf=SymbConf; SyncFreqOfs=AverFreq; if(SymbConf>0.0) { SymbShift=Phase(AverSymb)/(2*M_PI)*SymbolSepar; if(SymbShift<0.0) SymbShift+=SymbolSepar; SymbPtr=(int)floor((Phase(AverSymb)/(2*M_PI))*SymbolDiv); if(SymbPtr<0) SymbPtr+=SymbolDiv; SyncSymbShift=SymbShift; } if(SyncLocked) { if((SyncSymbConf0.250)) SyncLocked=0; } else { if((SyncSymbConf>SyncLockThres)&&(SyncFreqDev<0.125)) SyncLocked=1; } SyncSymbConf*=0.5; // printf(" => SyncLocked=%d, SyncSymbShift=%5.1f, SymbPtr=%d", // SyncLocked,SyncSymbShift,SymbPtr); // printf("\n"); } // enf of if(SyncPtr==(SymbPtr^2)) } void MT63rx::DataProcess(fcmpx *EvenSlice, fcmpx *OddSlice, float FreqOfs, int TimeDist) { int i,c,r; dcmpx Freq,Phas; int incr,p; double I,Q,P; dcmpx Dtmp; fcmpx Ftmp; double Aver,Rms; int Loops,Incl; // Here we pickup a symbol in the data history. The time/freq. synchronizer // told us where it is in time and at which frequency offset (FreqOfs) // TimeDist is the distance in samples from the symbol we analyzed // in the previous call to this routine // FreqOfs=0.0; // for DEBUG only ! // printf("DataProcess: FreqOfs=%5.3f, TimeDist=%d, Locked=%d\n", // FreqOfs,TimeDist,SyncLocked); P=(-2*M_PI*FreqOfs)/WindowLen; // make ready for frequency correction Freq.re=cos(P); Freq.im=sin(P); Phas.re=1.0; Phas.im=0.0; for(i=0; i=DataPipeLen) DataPipePtr=0; for(i=0; i0.0) { P=DataVect[i].re/DataPwrOut[i]; if(P>1.0) P=1.0; else if(P<(-1.0)) P=(-1.0); DataPhase[i]=P; } else DataPhase[i]=0.0; } Decoder.Process(DataPhase); Output.EnsureSpace(Output.Len+1); Output.Data[Output.Len]=Decoder.Output; Output.Len+=1; /* printf("Demodulator output vectors:\n"); for(i=0; i %8.5f\n", i,DataVect[i].re,DataVect[i].im,DataPwrOut[i], DataPhase[i]); } */ /* for(i=0; i0.0) P=Phase(DataVect[i]); else P=0.0; DataPhase[i]=P; P*=2; if(P>M_PI) P-=2*M_PI; else if(P<(-M_PI)) P+=2*M_PI; DataPhase2[i]=P; printf("%2d: %6.3f [%6.3f,%6.3f] [%8.5f,%8.5f], %5.2f, %5.2f", i, DataPwrOut[i], DataSqrOut[i].re,DataSqrOut[i].im, DataVect[i].re,DataVect[i].im, DataPhase[i],DataPhase2[i]); if(DataPwrOut[i]>0.0) printf(" %6.3f",Ampl(DataSqrOut[i])/DataPwrOut[i]); printf("\n"); } Loops=SelFitAver(DataPhase2,DataScanLen,(float)2.5,4,Aver,Rms,Incl); printf("Aver=%5.2f, Rms=%5.2f, Incl=%d\n",Aver,Rms,Incl); */ } int MT63rx::SYNC_LockStatus(void) { return SyncLocked; } float MT63rx::SYNC_Confidence(void) { return SyncSymbConf<=1.0 ? SyncSymbConf : 1.0; } float MT63rx::SYNC_FreqOffset(void) { return SyncFreqOfs/DataCarrSepar; } float MT63rx::SYNC_FreqDevRMS(void) { return SyncFreqDev/DataCarrSepar; } float MT63rx::SYNC_TimeOffset(void) { return SyncSymbShift/SymbolSepar; } float MT63rx::FEC_SNR(void) { return Decoder.SignalToNoise; } int MT63rx::FEC_CarrOffset(void) { return Decoder.CarrOfs; } float MT63rx::TotalFreqOffset(void) { return (SyncFreqOfs+DataCarrSepar*Decoder.CarrOfs)*(8000.0/DecimateRatio)/WindowLen; }