/* * dsp.cc -- various DSP algorithms * * 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 * */ // Please note, that you should not rely on the correctness // of these routines. They generally work well, but you may find // differences in respect to the mathematical formulas: signs flipped, // orders swapped, etc. #include // only when we do some control printf's #include #include #include "dsp.h" // ---------------------------------------------------------------------------- double Power(float *X, int Len) { double Sum; for(Sum=0.0; Len; Len--,X++) Sum+=(*X)*(*X); return Sum; } double Power(double *X, int Len) { double Sum; for(Sum=0.0; Len; Len--,X++) Sum+=(*X)*(*X); return Sum; } double Power(float *I, float *Q, int Len) { double Sum; for(Sum=0.0; Len; Len--,I++,Q++) Sum+=(*I)*(*I)+(*Q)*(*Q); return Sum; } double Power(double *I, double *Q, int Len) { double Sum; for(Sum=0.0; Len; Len--,I++,Q++) Sum+=(*I)*(*I)+(*Q)*(*Q); return Sum; } double Power(fcmpx *X, int Len) { double Sum; for(Sum=0.0; Len; Len--,X++) Sum+=(X->re)*(X->re)+(X->im)*(X->im); return Sum; } double Power(dcmpx *X, int Len) { double Sum; for(Sum=0.0; Len; Len--,X++) Sum+=(X->re)*(X->re)+(X->im)*(X->im); return Sum; } // ---------------------------------------------------------------------------- // average, extremes, fitting double Average(float *Data, int Len) { double Sum; int i; for(Sum=0.0,i=0; iMax) Max=Pwr; } return Max; } double FindMaxPower(fcmpx *Data, int Len, int &MaxPos) { double Max,Pwr; int i,pos; Max=Power(Data[0]); pos=0; for(i=1; iMax) { Max=Pwr; pos=i; } } MaxPos=pos; return Max; } double FitPoly1(float *Data, int Len, double &A, double &B) { double Sum; int i; A=(Data[Len-1]-Data[0])/(Len-1); for(Sum=0.0,i=0; iEnsureSpace(Len); if(err) return -1; ConvS16toFloat(S16,Float->Data,Len,Gain); Float->Len=Len; return 0; } void ConvFloatToS16(float *Float, s16 *S16, int Len, float Gain) { float out; for(; Len; Len--) { out=(*Float++)*Gain; if(out>32767.0) out=32767.0; else if(out<(-32767.0)) out=(-32767.0); (*S16++)=(short int)floor(out+0.5); } } // we could count the over/underflows ? /* transformed into "inline" - moved to dsp.h int ConvFloatToS16(float_buff *Float, s16_buff *S16, float Gain) { int err=S16->EnsureSpace(Float->Len); if(err) return -1; ConvFloatToS16(Float->Data,S16->Data,Float->Len,Gain); S16->Len=Float->Len; return 0; } */ void ConvU8toFloat(unsigned char *U8, float *Float, int Len, float Gain) { for(; Len; Len--) (*Float++)=((int)(*U8++)-128)*Gain; } int ConvU8toFloat(unsigned char *U8, float_buff *Float, int Len, float Gain) { int err=Float->EnsureSpace(Len); if(err) return -1; ConvU8toFloat(U8,Float->Data,Len,Gain); Float->Len=Len; return 0; } // ---------------------------------------------------------------------------- // other converts void ConvCmpxToPower(fcmpx *Inp, int Len, float *Out) { for(; Len; Len--) (*Out++)=Power(*Inp++); } int ConvCmpxToPower(fcmpx_buff *Input, float_buff *Output) { int err=Output->EnsureSpace(Input->Len); if(err) return err; ConvCmpxToPower(Input->Data,Input->Len,Output->Data); Output->Len=Input->Len; return 0; } void ConvCmpxToAmpl(fcmpx *Inp, int Len, float *Out) { for(; Len; Len--) (*Out++)=sqrt(Power(*Inp++)); } int ConvCmpxToAmpl(fcmpx_buff *Input, float_buff *Output) { int err=Output->EnsureSpace(Input->Len); if(err) return err; ConvCmpxToAmpl(Input->Data,Input->Len,Output->Data); Output->Len=Input->Len; return 0; } void ConvCmpxToPhase(fcmpx *Inp, int Len, float *Out) { for(; Len; Len--) (*Out++)=Phase(*Inp++); } int ConvCmpxToPhase(fcmpx_buff *Input, float_buff *Output) { int err=Output->EnsureSpace(Input->Len); if(err) return err; ConvCmpxToPhase(Input->Data,Input->Len,Output->Data); Output->Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // Pulse noise limiter PulseLimiter::PulseLimiter() { Tap=NULL; } PulseLimiter::~PulseLimiter() { free(Tap); } void PulseLimiter::Free(void) { free(Tap); Tap=NULL; } int PulseLimiter::Preset(int TapLen, float LimitThres) { Len=TapLen; Thres=LimitThres*LimitThres; if(ReallocArray(&Tap,Len)) return -1; ClearArray(Tap,Len); Ptr=0; PwrSum=0.0; return 0; } int PulseLimiter::Process(float *Inp, int InpLen, float *Out) { int i,o; double Lim; for(i=0; i=Len) Ptr-=Len; o=Ptr+(Len/2); if(o>=Len) o-=Len; Lim=Thres*PwrSum/Len; if(Tap[o]*Tap[o]<=Lim) Out[i]=Tap[o]; else { if(Tap[o]>0.0) Out[i]=sqrt(Lim); else Out[i]=(-sqrt(Lim)); } } for(PwrSum=0.0, i=0; iLen); if(err) return -1; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // Signal level monitor LevelMonitor::LevelMonitor() { } LevelMonitor::~LevelMonitor() { } int LevelMonitor::Preset(float Integ, float Range) { LowPass2Coeff(Integ,W1,W2,W5); MaxSqr=Range*Range; PwrMid=0.0; PwrOut=0.0; RMS=0.0; OutOfRangeMid=0.0; OutOfRange=0.0; return 0; } int LevelMonitor::Process(float *Inp, int Len) { int i,Out; double Sqr,Sum; if(Len<=0) return 0; for(Sum=0.0,Out=0,i=0; iMaxSqr); } LowPass2(Sum/Len,PwrMid,PwrOut,W1,W2,W5); LowPass2((float)Out/Len,OutOfRangeMid,OutOfRange,W1,W2,W5); if(OutOfRange<0.0) OutOfRange=0.0; if(PwrOut<=0.0) RMS=0.0; else RMS=sqrt(PwrOut); return 0; } int LevelMonitor::Process(float_buff *Input) { return Process(Input->Data,Input->Len); } // ---------------------------------------------------------------------------- // Automatic Gain/Level Control for the Mixer MixerAutoLevel::MixerAutoLevel() { MinMS=0.01; MaxMS=0.05; IntegLen=8000; PeakHold=4000; MinHold=800; MinLevel=0; MaxLevel=100; AdjStep=1; Level=75; Hold=(-IntegLen); AverMS=0.0; } int MixerAutoLevel::Process(float *Inp, int InpLen) { double MS=Power(Inp,InpLen)/IntegLen; double W=1.0-((double)InpLen)/IntegLen; AverMS = AverMS*W + MS; Hold+=InpLen; if(HoldMaxMS) { Level-=AdjStep; if(LevelMaxLevel) Level=MaxLevel; Hold=0; return 1; } return 0; } // ---------------------------------------------------------------------------- void LowPass2(fcmpx *Inp, dcmpx *Mid, dcmpx *Out, float W1, float W2, float W5) { double Sum, Diff; // printf("\n[LowPass2] %6.3f %6.3f %6.3f",Inp->re,Mid->re,Out->re); Sum=Mid->re+Out->re; Diff=Mid->re-Out->re; Mid->re+=W2*Inp->re-W1*Sum; Out->re+=W5*Diff; // printf(" => %6.3f %6.3f\n",Mid->re,Out->re); Sum=Mid->im+Out->im; Diff=Mid->im-Out->im; Mid->im+=W2*Inp->im-W1*Sum; Out->im+=W5*Diff; } void LowPass2(dcmpx *Inp, dcmpx *Mid, dcmpx *Out, float W1, float W2, float W5) { double Sum, Diff; // printf("[LowPass2] %6.3f %6.3f %6.3f",Inp->re,Mid->re,Out->re); Sum=Mid->re+Out->re; Diff=Mid->re-Out->re; Mid->re+=W2*Inp->re-W1*Sum; Out->re+=W5*Diff; // printf(" => %6.3f %6.3f\n",Mid->re,Out->re); Sum=Mid->im+Out->im; Diff=Mid->im-Out->im; Mid->im+=W2*Inp->im-W1*Sum; Out->im+=W5*Diff; } // ---------------------------------------------------------------------------- // periodic low pass PeriodLowPass2::PeriodLowPass2() { TapMid=NULL; TapOut=NULL; } PeriodLowPass2::~PeriodLowPass2() { free(TapMid); free(TapOut); Output.Free(); } void PeriodLowPass2::Free(void) { free(TapMid); TapMid=NULL; free(TapOut); TapOut=NULL; } int PeriodLowPass2::Preset(int Period, float IntegLen) { int i; Len=Period; if(ReallocArray(&TapMid,Len)) goto Error; if(ReallocArray(&TapOut,Len)) goto Error; for(i=0; i=Len) TapPtr=0; return 0; } int PeriodLowPass2::Process(float *Inp, int InpLen, float *Out) { int i,batch; for(i=0; i=Len) TapPtr=0; } return 0; } int PeriodLowPass2::Process(float_buff *Input) { int err=Output.EnsureSpace(Input->Len); if(err) return -1; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // Low pass "moving box" FIR filter // very unpure spectral response but complexity very low // and independent on the integration time BoxFilter::BoxFilter() { Tap=NULL; } BoxFilter::~BoxFilter() { free(Tap); } void BoxFilter::Free(void) { free(Tap); Tap=NULL; Output.Free(); } int BoxFilter::Preset(int BoxLen) { int i; if(ReallocArray(&Tap,BoxLen)) return -1; for(i=0; i=Len) TapPtr=0; } for(Sum=0, i=0; iLen); if(err) return err; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } CmpxBoxFilter::CmpxBoxFilter() { Tap=NULL; } CmpxBoxFilter::~CmpxBoxFilter() { free(Tap); } void CmpxBoxFilter::Free(void) { free(Tap); Tap=NULL; Output.Free(); } int CmpxBoxFilter::Preset(int BoxLen) { int i; if(ReallocArray(&Tap,BoxLen)) return -1; for(i=0; i=Len) TapPtr=0; } for(Sum.re=Sum.im=0.0, i=0; iLen); if(err) return err; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // FIR filter with a given shape FirFilter::FirFilter() { Tap=NULL; ExternShape=1; } FirFilter::~FirFilter() { free(Tap); if(!ExternShape) free(Shape); } void FirFilter::Free(void) { free(Tap); Tap=NULL; if(!ExternShape) free(Shape); Shape=NULL; Output.Free(); } int FirFilter::Preset(int FilterLen, float *FilterShape) { int i; if(ReallocArray(&Tap,FilterLen)) return -1; for(i=0; i=Len) TapPtr=0; for(Sum=0,s=0,t=TapPtr; tLen); if(err) return err; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // a pair of FIR filters. for quadrature split & decimate // the decimation rate must be an integer QuadrSplit::QuadrSplit() { ExternShape=1; } QuadrSplit::~QuadrSplit() { if(!ExternShape) { free(ShapeI); free(ShapeQ); } } void QuadrSplit::Free(void) { Tap.Free(); if(!ExternShape) { free(ShapeI); free(ShapeQ); } ShapeI=NULL; ShapeQ=NULL; Output.Free(); } int QuadrSplit::Preset(int FilterLen, float *FilterShape_I, float *FilterShape_Q, int DecimateRate) { Len=FilterLen; if(!ExternShape) { free(ShapeI); free(ShapeQ); } ShapeI=FilterShape_I; ShapeQ=FilterShape_Q; ExternShape=1; Tap.EnsureSpace(Len); Tap.Len=Len; ClearArray(Tap.Data,Tap.Len); Rate=DecimateRate; return 0; } int QuadrSplit::ComputeShape(float LowOmega,float UppOmega, double (*Window)(double)) { if(ExternShape) { ShapeI=NULL; ShapeQ=NULL; ExternShape=0; } if(ReallocArray(&ShapeI,Len)) return -1; if(ReallocArray(&ShapeQ,Len)) return -1; WinFirI(LowOmega,UppOmega,ShapeI,Len,Window); WinFirQ(LowOmega,UppOmega,ShapeQ,Len,Window); return 0; } int QuadrSplit::Process(float_buff *Input) { int err,i,s,t,o,l; double SumI,SumQ; float *Inp; fcmpx *Out; int InpLen; InpLen=Input->Len; err=Tap.EnsureSpace(Tap.Len+InpLen); if(err) return err; CopyArray(Tap.Data+Tap.Len,Input->Data,InpLen); // printf("QuadrSplit: InpLen=%d, Tap.Len=%d",InpLen,Tap.Len); Tap.Len+=InpLen; Inp=Tap.Data; // printf(" -> %d",Tap.Len); err=Output.EnsureSpace(InpLen/Rate+2); if(err) return err; Out=Output.Data; for(l=Tap.Len-Len,o=0,i=0; i Tap.Len=%d\n",Tap.Len); return 0; /* if(InpLenLen; err=Output.EnsureSpace(InpLen*Rate); if(err) return err; Inp=Input->Data; Out=Output.Data; Output.Len=InpLen*Rate; for(o=0,i=0; i=2*M_PI) Phase-=2*M_PI; } return InpLen; } int CmpxMixer::ProcessFast(float *InpI, float *InpQ, int InpLen, float *OutI, float *OutQ) { int i; double dI,dQ,I,Q,nI,nQ, N; dI=cos(Omega); dQ=sin(Omega); I=cos(Phase); Q=sin(Phase); for(i=0; i=2*M_PI) Phase-=2*M_PI; } return InpLen; } int CmpxMixer::ProcessFast(fcmpx *Inp, int InpLen, fcmpx *Out) { int i; double dI,dQ,I,Q,nI,nQ, N; dI=cos(Omega); dQ=sin(Omega); I=cos(Phase); Q=sin(Phase); for(i=0; iLen); if(err) return err; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } int CmpxMixer::ProcessFast(fcmpx_buff *Input) { int err=Output.EnsureSpace(Input->Len); if(err) return err; ProcessFast(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // FM demodulator (phase rotation speed meter) FMdemod::FMdemod() { PrevPhase=0.0; RefOmega=0.0; } int FMdemod::Preset(double CenterOmega) { RefOmega=CenterOmega; return 0; } int FMdemod::Process(float *InpI, float *InpQ, int InpLen, float *Out) { int i; float Phase,PhaseDiff; for(i=0; i=M_PI) PhaseDiff-=2*M_PI; else if(PhaseDiff<(-M_PI)) PhaseDiff+=2*M_PI; Out[i]=PhaseDiff; PrevPhase=Phase; } return InpLen; } int FMdemod::Process(fcmpx *Inp, int InpLen, float *Out) { int i; float Phase,PhaseDiff; for(i=0; i=M_PI) PhaseDiff-=2*M_PI; else if(PhaseDiff<(-M_PI)) PhaseDiff+=2*M_PI; Out[i]=PhaseDiff; PrevPhase=Phase; } return InpLen; } int FMdemod::Process(fcmpx_buff *Input) { int err=Output.EnsureSpace(Input->Len); if(err) return err; Process(Input->Data,Input->Len,Output.Data); Output.Len=Input->Len; return 0; } // ---------------------------------------------------------------------------- // Rate converter - real input/output, linear interpolation // expect large error when high frequency components are present // thus the best place to convert rates is after a low pass filter // of a demodulator. RateConvLin::RateConvLin() { PrevSample=0; OutPhase=0; OutStep=1.0; } void RateConvLin::SetOutVsInp(float OutVsInp) { OutStep=1.0/OutVsInp; } void RateConvLin::SetInpVsOut(float InpVsOut) { OutStep=InpVsOut; } int RateConvLin::Process(float_buff *Input) { int err,i,o; float *Inp,*Out; int InpLen; Inp=Input->Data; InpLen=Input->Len; err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; Out=Output.Data; for(o=0; OutPhase<0; ) { Out[o++]=Inp[0]*(1.0+OutPhase)-OutPhase*PrevSample; OutPhase+=OutStep; } for(i=0; i<(InpLen-1); ) { if(OutPhase>=1.0) { OutPhase-=1.0; i++; } else { Out[o++]=Inp[i]*(1.0-OutPhase)+Inp[i+1]*OutPhase; OutPhase+=OutStep; } } Output.Len=o; PrevSample=Inp[i]; OutPhase-=1.0; return 0; } // ---------------------------------------------------------------------------- // Rate converter - real input/output, quadratic interpolation // similar limits like for RateConv1 RateConvQuadr::RateConvQuadr() { int i; for(i=0; i<4; i++) Tap[i]=0; OutStep=1.0; OutPhase=0; TapPtr=0; } void RateConvQuadr::SetOutVsInp(float OutVsInp) { OutStep=1.0/OutVsInp; } void RateConvQuadr::SetInpVsOut(float InpVsOut) { OutStep=InpVsOut; } int RateConvQuadr::Process(float *Inp, int InpLen, float *Out, int MaxOutLen, int *OutLen) { int i,o,t; float Ref0,Ref1,Diff0,Diff1; for(o=i=0; (i=1.0) { Tap[TapPtr]=(*Inp++); i++; TapPtr=(TapPtr+1)&3; OutPhase-=1.0; } else { t=TapPtr; Diff0=(Tap[t^2]-Tap[t])/2; Ref1=Tap[t^2]; t=(t+1)&3; Diff1=(Tap[t^2]-Tap[t])/2; Ref0=Tap[t]; (*Out++)=Ref0*(1.0-OutPhase)+Ref1*OutPhase // linear piece -(Diff1-Diff0)*OutPhase*(1.0-OutPhase)/2; // quadr. piece o++; OutPhase+=OutStep; } } (*OutLen)=o; return i; } int RateConvQuadr::Process(float_buff *Input) { int err,i,o,t; float Ref0,Ref1,Diff0,Diff1; float *Inp,*Out; int InpLen; Inp=Input->Data; InpLen=Input->Len; err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; Out=Output.Data; for(o=i=0; i=1.0) { Tap[TapPtr]=(*Inp++); i++; TapPtr=(TapPtr+1)&3; OutPhase-=1.0; } else { t=TapPtr; Diff0=(Tap[t^2]-Tap[t])/2; Ref1=Tap[t^2]; t=(t+1)&3; Diff1=(Tap[t^2]-Tap[t])/2; Ref0=Tap[t]; (*Out++)=Ref0*(1.0-OutPhase)+Ref1*OutPhase // linear piece -(Diff1-Diff0)*OutPhase*(1.0-OutPhase)/2; // quadr. piece o++; OutPhase+=OutStep; } } Output.Len=o; return 0; } // ---------------------------------------------------------------------------- // Rate converter, real input/output, // bandwidth-limited interpolation, several shifted FIR filters RateConvBL::RateConvBL() { Tap=NULL; Shape=NULL; ExternShape=1; } RateConvBL::~RateConvBL() { Free(); } void RateConvBL::Free(void) { int s; free(Tap); Tap=NULL; if(ExternShape) return; if(Shape) { for(s=0; sData; InpLen=Input->Len; err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; Out=Output.Data; if((Len+InpLen)>TapSize) { Tap=(float*)realloc(Tap,(Len+InpLen)*sizeof(float)); if(Tap==NULL) return -1; TapSize=Len+InpLen; } memcpy(Tap+Len,Inp,InpLen*sizeof(float)); for(o=i=0; i=1.0) { OutPhase-=1.0; i++; } else { idx=(int)floor(OutPhase*ShapeNum); shape=Shape[idx]; for(Sum=0.0,t=0; tData; InpLen=Input->Len; err=Output.EnsureSpace((int)ceil(InpLen/OutStep)+2); if(err) return err; Out=Output.Data; if((Len+InpLen)>TapSize) { Tap=(float*)realloc(Tap,(Len+InpLen)*sizeof(float)); if(Tap==NULL) return -1; TapSize=Len+InpLen; } memcpy(Tap+Len,Inp,InpLen*sizeof(float)); for(o=i=0; i=1.0) { OutPhase-=1.0; i++; } else { idx=(int)floor(OutPhase*ShapeNum); d=OutPhase*ShapeNum-idx; shape=Shape[idx]; for(Sum0=0.0,t=0; t=ShapeNum) { idx=0; i++; } shape=Shape[idx]; for(Sum1=0.0,t=0; tWindowLen) return -1; Len=WindowLen; Dist=SlideDist; if(ReallocArray(&Buff,Len)) return -1; for(i=0; iData; int InpLen=Input->Len; int i,len,err; Output.Len=0; while(InpLen>0) { len=imin(Len-Ptr,InpLen); memcpy(Buff+Ptr,Inp,len*sizeof(fcmpx)); Ptr+=len; Inp+=len; InpLen-=len; if(Ptr>=Len) { len=Output.Len; err=Output.EnsureSpace(len+Len); if(err) return err; if(Window==NULL) memcpy(Output.Data,Buff,Len*sizeof(fcmpx)); else for(i=0; iWindowLen) return -1; Len=WindowLen; Dist=SlideDist; if(ReallocArray(&Buff,Len)) return -1; for(i=0; iLen; i+=Len) { err=Output.EnsureSpace(Output.Len+Dist); if(err) return err; Process(Input->Data+i,Output.Data+Output.Len); Output.Len+=Dist; } return 0; } int CmpxOverlapWindow::Process(fcmpx *Input) { int err; err=Output.EnsureSpace(Dist); if(err) return err; Process(Input,Output.Data); Output.Len=Dist; return 0; } void CmpxOverlapWindow::Process(fcmpx *Inp, fcmpx *Out) { int i; if(Window==NULL) { for(i=0; i=InpTapLen) InpTapPtr=0; diff+=(InpTap[InpTapPtr]=(*Inp++)); DiffTapPtr=(DiffTapPtr+1)&3; LowPass2(diff*diff,DiffInteg0[DiffTapPtr],DiffInteg[DiffTapPtr],W1,W2,W5); if(DiffTapPtr==BitPtr) { for(Sum=0,t=0; t<4; t++) Sum+=DiffInteg[t]; t=DiffTapPtr; SumI = DiffInteg[t]-DiffInteg[t^2]; t=(t+1)&3; SumQ = DiffInteg[t]-DiffInteg[t^2]; if((Sum==0.0)||((SyncConfid=(SumI*SumI+SumQ*SumQ)/(Sum*Sum))==0.0)) { (*BitOut++)=0; (*IbitOut++)=0; o++; continue; } phase=atan2(-SumQ,-SumI)*(4/(2*M_PI)); LowPass2(phase-SyncPhase,SyncDrift0,SyncDrift,W1,W2,W5); SyncPhase=phase; if(phase>0.52) { step=1; SyncPhase-=1.0; } else if(phase<(-0.52)) { step=(-1); SyncPhase+=1.0; } else step=0; float Samp[5],bit,ibit,dx; int p; p=InpTapPtr-4*IntegLen-2; if(p<0) p+=InpTapLen; for(t=0; t<5; t++) { Samp[t]=InpTap[p++]; if(p>=InpTapLen) p=0; } dx=phase-0.5; // bit=Samp[2]+dx*(Samp[2]-Samp[1]); // linear interpolation bit=Samp[2]*(1.0+dx)-Samp[1]*dx // or quadratic +((Samp[3]-Samp[1])-(Samp[2]-Samp[0]))/2*dx*(1.0+dx)/2; ibit=Samp[4]+dx*(Samp[4]-Samp[3]); //linear interpolation is enough (*BitOut++)=bit; (*IbitOut++)=ibit; o++; } else if(DiffTapPtr==(BitPtr^2)) { BitPtr=(BitPtr+step)&3; step=0; } } (*OutLen)=o; return i; } float DiffBitSync4::GetSyncConfid() { return 4*SyncConfid; } float DiffBitSync4::GetSyncDriftRate() { return SyncDrift/4; } // ---------------------------------------------------------------------------- // bit slicer, SNR/Tune meter BitSlicer::BitSlicer(int IntegBits) { int i; TapLen=IntegLen=IntegBits; Tap=(float *)malloc(TapLen*sizeof(float)); for(i=0; i0; LowPass2(Bit,Sum0[l],Sum[l],W1,W2,W5); LowPass2(Bit*Bit,SumSq0[l],SumSq[l],W1,W2,W5); Noise[l]=sqrt(SumSq[l]-Sum[l]*Sum[l]); if(Noise[0]+Noise[1]<=0) OptimThres=0; else OptimThres=(Sum[0]*Noise[1]+Sum[1]*Noise[0])/(Noise[0]+Noise[1]); soft=Tap[TapPtr]-OptimThres; // we could do a better soft-decision if(Bit*PrevBit<0) { LowPass2(PrevIBit,AmplAsym0,AmplAsym,W1,W2,W5); if(Bit>0) PrevIBit=(-PrevIBit); LowPass2(PrevIBit,TimeAsym0,TimeAsym,W1,W2,W5); } (*OutBits++)=soft; PrevBit=Bit; PrevIBit=IBits[i]; Tap[TapPtr]=Bit; TapPtr++; if(TapPtr>=TapLen) TapPtr=0; } return InpLen; } float BitSlicer::GetSigToNoise() { return Noise[1]>0 ? (Sum[1]-OptimThres)/Noise[1] : 0.0; } float BitSlicer::GetAmplAsym() { float Sweep=Sum[1]-Sum[0]; return Sweep>0 ? 2*AmplAsym/Sweep : 0.0; } float BitSlicer::GetTimeAsym() { float Sweep=Sum[1]-Sum[0]; return Sweep>0 ? 2*TimeAsym/Sweep : 0.0; } // ---------------------------------------------------------------------------- // The decoder for the HDLC frames, // makes no AX.25 CRC check, only the length in bytes against MinLen and MaxLen // however it does not pass frames with non-complete bytes. HDLCdecoder::HDLCdecoder(int minlen, int maxlen, int diff, int invert, int chan, int (*handler)(int, char *, int)) { MinLen=minlen; MaxLen=maxlen; RxDiff=diff; RxInvert=invert; ChanId=chan; FrameHandler=handler; Buff=(char *)malloc(MaxLen); Len=(-1); PrevLev=0; ShiftReg=0; BitCount=0; Count1s=0; AllFrameCount=0; BadFrameCount=0; } HDLCdecoder::~HDLCdecoder() { free(Buff); } int HDLCdecoder::Process(float *Inp, int InpLen) { int i,lev,bit,Flag; for(i=0; i0; bit=(lev^(PrevLev&RxDiff))^RxInvert; PrevLev=lev; ShiftReg=(ShiftReg>>1)|(bit<<7); BitCount+=1; Flag=0; if(bit) Count1s+=1; else { if(Count1s>=7) Len=(-1); else if(Count1s==6) Flag=1; else if(Count1s==5) { ShiftReg<<=1; BitCount-=1; } Count1s=0; } if(Flag) { if((Len>=MinLen)&&(BitCount==8)) (*FrameHandler)(ChanId,Buff,Len); Len=0; BitCount=0; } else if(Len>=0) { if(BitCount==8) { if(Len>8)^AX25CRCtable[idx]; } CRC^=0xFFFF; return CRC; } // ---------------------------------------------------------------------------- // radix-2 FFT // constructor r2FFT::r2FFT() { BitRevIdx=NULL; Twiddle=NULL; /* Window=NULL; */ } // destructor: free twiddles, bit-reverse lookup and window tables r2FFT::~r2FFT() { free(BitRevIdx); free(Twiddle); /* free(Window); */ } void r2FFT::Free(void) { free(BitRevIdx); BitRevIdx=NULL; free(Twiddle); Twiddle=NULL; } // .......................................................................... // a radix-2 FFT bufferfly inline void r2FFT::FFTbf(fcmpx &x0, fcmpx &x1, dcmpx &W) { fcmpx x1W; x1W.re=x1.re*W.re+x1.im*W.im; // x1W.re=x1.re*W.re-x1.im*W.im; x1W.im=(-x1.re*W.im)+x1.im*W.re; // x1W.im=x1.re*W.im+x1.im*W.re; x1.re=x0.re-x1W.re; x1.im=x0.im-x1W.im; x0.re=x0.re+x1W.re; x0.im=x0.im+x1W.im; } inline void r2FFT::FFTbf(dcmpx &x0, dcmpx &x1, dcmpx &W) { dcmpx x1W; x1W.re=x1.re*W.re+x1.im*W.im; // x1W.re=x1.re*W.re-x1.im*W.im; x1W.im=(-x1.re*W.im)+x1.im*W.re; // x1W.im=x1.re*W.im+x1.im*W.re; x1.re=x0.re-x1W.re; x1.im=x0.im-x1W.im; x0.re=x0.re+x1W.re; x0.im=x0.im+x1W.im; } // 2-point FFT inline void r2FFT::FFT2(dcmpx &x0, dcmpx &x1) { dcmpx x1W; x1W.re=x1.re; x1W.im=x1.im; x1.re=x0.re-x1.re; x1.im=x0.im-x1.im; x0.re+=x1W.re; x0.im+=x1W.im; } inline void r2FFT::FFT2(fcmpx &x0, fcmpx &x1) { fcmpx x1W; x1W.re=x1.re; x1W.im=x1.im; x1.re=x0.re-x1.re; x1.im=x0.im-x1.im; x0.re+=x1W.re; x0.im+=x1W.im; } // 4-point FFT // beware: these depend on the convention for the twiddle factors ! inline void r2FFT::FFT4(fcmpx &x0, fcmpx &x1, fcmpx &x2, fcmpx &x3) { dcmpx x1W; x1W.re=x2.re; x1W.im=x2.im; x2.re=x0.re-x1W.re; x2.im=x0.im-x1W.im; x0.re=x0.re+x1W.re; x0.im=x0.im+x1W.im; x1W.re=x3.im; x1W.im=(-x3.re); x3.re=x1.re-x1W.re; x3.im=x1.im-x1W.im; x1.re=x1.re+x1W.re; x1.im=x1.im+x1W.im; } inline void r2FFT::FFT4(dcmpx &x0, dcmpx &x1, dcmpx &x2, dcmpx &x3) { dcmpx x1W; x1W.re=x2.re; x1W.im=x2.im; x2.re=x0.re-x1W.re; x2.im=x0.im-x1W.im; x0.re=x0.re+x1W.re; x0.im=x0.im+x1W.im; x1W.re=x3.im; x1W.im=(-x3.re); x3.re=x1.re-x1W.re; x3.im=x1.im-x1W.im; x1.re=x1.re+x1W.re; x1.im=x1.im+x1W.im; } // .......................................................................... // bit reverse (in place) the sequence (before the actuall FTT) void r2FFT::Scramble(fcmpx x[]) { int idx,ridx; fcmpx tmp; for(idx=0; idxidx) { tmp=x[idx]; x[idx]=x[ridx]; x[ridx]=tmp; /* printf("%d <=> %d\n",idx,ridx); */ } } // bit reverse the sequence - double precision void r2FFT::Scramble(dcmpx x[]) { int idx,ridx; dcmpx tmp; for(idx=0; idxidx) { tmp=x[idx]; x[idx]=x[ridx]; x[ridx]=tmp; /* printf("%d <=> %d\n",idx,ridx); */ } } // Preset for given processing size int r2FFT::Preset(int size) { int err,idx,ridx,mask,rmask; double phase; if(!PowerOf2(size)) goto Error; Size=size; err=ReallocArray(&BitRevIdx,Size); if(err) goto Error; err=ReallocArray(&Twiddle,Size); if(err) goto Error; for(idx=0; idx %6.4f => %6.4f %6.4f\n", idx,phase,Twiddle[idx].re,Twiddle[idx].im); */ } for(ridx=0,idx=0; idx>=1,rmask<<=1) { if(idx&mask) ridx|=rmask; } BitRevIdx[idx]=ridx; /* printf("%04x %04x\n",idx,ridx); */ } // free(Window); Window=NULL; WinInpScale=1.0/Size; WinOutScale=0.5; return 0; Error: Free(); return -1; } // compute the window, set input/output scaling /* int r2FFT::SetWindow(double (*NewWindow)(double phase), double InpScale, double OutScale) { int idx; if(NewWindow==NULL) { free(Window); Window=NULL; } else { Window=(double *)realloc(Window,Size*sizeof(double)); if(Window==NULL) return -1; for(idx=0; idx>=1, GroupHalfSize<<=1) for(Group=0,Bf=0; Group>=1, GroupHalfSize<<=1) for(Group=0,Bf=0; Groupoutput delay longer. SlideWinFFT::SlideWinFFT() { SlideBuff=NULL; FFTbuff=NULL; Window=NULL; ExternWindow=1; } SlideWinFFT::~SlideWinFFT() { free(SlideBuff); free(FFTbuff); if(!ExternWindow) free(Window); } void SlideWinFFT::Free(void) { free(SlideBuff); SlideBuff=NULL; free(FFTbuff); FFTbuff=NULL; if(!ExternWindow) free(Window); Window=NULL; ExternWindow=1; Output.Free(); } int SlideWinFFT::Preset(int size, int step, float *window) { int err,i; Size=size; SizeMask=Size-1; err=FFT.Preset(Size); if(err) goto Error; if(!ExternWindow) { free(Window); ExternWindow=1; } Window=window; err=ReallocArray(&FFTbuff,Size); if(err) goto Error; err=ReallocArray(&SlideBuff,Size); if(err) goto Error; for(i=0; iData; InpLen=Input->Len; Output.Len=0; while(InpLen) { for(i=len=imin(InpLen,Left); i; i--) { SlideBuff[SlidePtr++]=(*Inp++); SlidePtr&=SizeMask; } InpLen-=len; Left-=len; if(Left==0) { Slide^=1; Left=Dist; if(Slide) { for(t=0,i=SlidePtr; iData; InpLen=Input->Len; Output.Len=0; while(InpLen) { for(i=len=imin(InpLen,Left); i; i--) { SlideBuff[SlidePtr++]=(*Inp++); SlidePtr&=SizeMask; } InpLen-=len; Left-=len; if(Left==0) { Slide^=1; Left=Dist; if(Slide) { for(t=0,i=SlidePtr; i