dl-fldigi/src/include/mt63base.h

376 wiersze
13 KiB
C++

/*
* mt63base.h -- MT63 transmitter and receiver in C++ for LINUX
*
* Copyright (c) 2007, 2008 Dave Freese, W1HKJ
*
* base class for use by fldigi
* modified from original
* excluded CW_ID which is a part of the base modem class for fldigi
*
* based on mt63 code by Pawel Jalocha
* Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC
* Copyright (c) 2007-2011 Dave Freese, W1HKJ
*
* This file is part of fldigi.
*
* Fldigi 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 3 of the License, or
* (at your option) any later version.
*
* Fldigi 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 fldigi. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MT63BASE_H
#define MT63BASE_H
// ==========================================================================
// Character encoder and block interleaver for the MT63 modem
/*
How to use this class:
1. Create or declare an object like:
MT63encoder Encoder;
2. Preset the object for the given number of carriers and interleave:
err=Encoder.Preset(<carriers>,<interleave>,<pattern>);
MT63 uses 64 carriers and interleave of 32 or 64
- the corresponding interleave patterns can be found in mt63.dat.
If Encode.Preset() returns non-zero you are in big troubles !
3. For each character to be encode you make the call:
Encoder.Process(<character>);
and you should then take the bits from the Encode.Output
- these are the bits to be sent out on the carriers.
(for MT63 logical 0 means carrier flip, logical 1 is no flip).
4. At any time you can call Encoder.Preset() again to change the parameters
(this will clean away the interleaver pipe).
*/
// MT63 modem specific routines, made to be compatible with the MT63ASC.ASM
// (c) 1999 Pawel Jalocha, SP9VRC, jalocha@hpdel1.ifj.edu.pl
// Date: 05-NOV-1999
class MT63encoder
{
public:
MT63encoder();
~MT63encoder();
void Free();
int Preset(int Carriers, int Intlv, int *Pattern, int RandFill=0);
int Process(char code);
char *Output;
private:
int DataCarriers;
char CodeMask;
int IntlvLen;
int IntlvSize;
int *IntlvPatt;
char *IntlvPipe;
int IntlvPtr;
double *WalshBuff;
} ;
// ==========================================================================
// MT63 deinterleaver and decoder
/*
How to use this class:
1. Create or declare an object:
MT63decoder Decoder;
2. Preset given parameters with Decoder.Preset();
Decoder.Preset();
Number of carriers and interleave are same as for MT63encoder.
"Margin" is the number of extra carriers demodulated on the side
because with the MT63 you cannot say with full confidence which
is really the first carrier: the FEC decoder have to tell you this.
"Integ" is the integration period to find the best FEC match.
"Integ" is measured in MT63 symbols (at 1000 kHz we do 10 symbols/s).
3. For each symbol period feed the demodulated data into the object:
Decoder.Process(<data>);
and then get the demodulated character code from Decoder.Output
You can get as well the measured signal-to-noise ratio from Decoder.SNR
and the index of the first carrier (according to the FEC match)
from Decoder.CarrOfs
4. You can change the parameters at any time with Decoder.Preset()
(this will clean the data pipes).
*/
class MT63decoder
{
public:
MT63decoder();
~MT63decoder();
void Free();
int Preset(int Carriers, int Intlv, int *Pattern, int Margin, int Integ);
int Process(double *Data);
char Output;
double SignalToNoise;
int CarrOfs;
private:
int DataCarriers;
double *IntlvPipe;
int IntlvLen;
int IntlvSize;
int IntlvPtr;
int *IntlvPatt;
double *WalshBuff;
int ScanLen;
int ScanSize;
double *DecodeSnrMid,*DecodeSnrOut;
double W1, W2, W5;
char *DecodePipe;
int DecodeLen;
int DecodeSize;
int DecodePtr;
} ;
// ==========================================================================
// MT63 transmitter
/*
How to use this class:
1. Create or declare an object:
MT63tx Tx;
2. Preset parameters:
Tx.Preset(<bandwidth>,<interleave>);
Allowed values are: bandwidth=500,1000,2000; interleave=0,1;
Non-zero value returned means there was a problem...
3. For each character to be sent:
Tx.SendChar(<char>);
After each call to SendChar() you must read the samples
from the Tx.Comb.Output.Data, the number of samples to read
is in Tx.Comb.Output.Len. They are in double floating point, so you should
convert them to 16-bit integers and output them to your soundcard.
4. If you have nothing to transmit, you must not stop, because
you have to keep the sound going. MT63 transmits NL characters (code=0)
in this case.
5. When you are done with all the characters and you want to stop,
you should still put some NL characters in to flush the interleave
thus please call the Tx.SendChar() Tx.DataInterleave times
(still add few more characters to flush the windowed IFFT buffers).
After that the MT63 transmits a jamming dspSequence for some time
to speed up carrier drop at the receiver: you do this by calling
Tx.SendJam();
6. You can transmit few symbols of silence by:
Tx.SendSilence()
to make a gracefull switch-off.
Remember: each time you call SendChar(), SendJam() or SendSilence()
you must send the contains of Tx.Comb.Output out to your soundcard.
Each Tx.SendXxx() produces the amount of sound corresponding to one
symbol time that is 0.1 second for the 1000 Hz mode.
The soundcard output rate must be 8000 Hz, rather precisely,
that is the error should be below 1 Hz. If it is not you should
use the rate converter: look into mt63tx for an example.
7. Inbetween transmissions you may change the settings by calling
the Tx.Preset() again.
*/
class MT63tx
{
public:
MT63tx();
~MT63tx();
void Free(void);
int Preset(double freq, int BandWidth=1000, int LongInterleave=0);
int SendTune(bool twotones);
int SendChar(char ch);
int SendJam(void);
int SendSilence(void);
private:
int DataCarriers; // the number of data carriers
int FirstDataCarr; // the FFT index of the first data carrier
int WindowLen; // FFT window and symbol shape length
double *TxWindow; // The shape of the FFT window (=symbol shape)
int AliasFilterLen; // anti-alias filter length
//double *AliasShapeI,
// *AliasShapeQ; // and shapes (for fixed lower freq of 500 Hz)
int DecimateRatio; // decimation/interpolation after/before filter
int *InterleavePattern; // how the bits of one block are placed on data carriers
double TxAmpl; // Amplitude applied to generate a carrier (before IFFT)
long CarrMarkCode;
int CarrMarkAmpl;
MT63encoder Encoder; // data encode and interleaver
int *TxVect; // modulator vector (dspPhases)
int *dspPhaseCorr; // dspPhase corrections for each carrier
dspCmpx_buff WindowBuff; // FFT/window buffer
dsp_r2FFT FFT; // FFT engine
dspCmpxMixer txmixer;
dspCmpxOverlapWindow Window; // overlapping window
int ProcessTxVect();
public:
int DataInterleave;
dspQuadrComb Comb; // the output of this module is in Comb.Output
} ;
// ==========================================================================
// MT63 receiver
/*
How to use this class:
1. Declare the object:
MT63rx Rx;
2. Preset paramateres
Rx.Preset(<bandwidth>,<interleave>,<integration>);
For weak signals I recommend integration of 32 or more,
otherwise 16 is enough. By the way, 16 means 1.6 second for 1000 Hz mode
because then we transmit 10 symbols per second.
3. After EACH new batch of samples
you should look into Rx.Output for the decoded characters.
You can egzamin the receiver status at any time by calling:
Rx.SYNC_LockStatus() => logical value 0 or 1
Rx.SYNC_Confidence() => lock confidence: a double between 0.0 and 1.0
Rx.FEC_SNR() => signal-to-noise seen by FEC
Rx.TotalFreqOffset() => measured frequency offset in [Hz]
assuming 8000 Hz sAmpling
*/
class MT63rx
{
public:
MT63rx();
~MT63rx();
void Free(void);
int Preset( double freq,
int BandWidth = 1000,
int LongInterleave = 0,
int Integ = 16,
void (*Display)(double *Spectra, int Len) = NULL);
int Process(double_buff *Input);
char_buff Output; // decoded characters
int SYNC_LockStatus(void); // 1 => locked, 0 => not locked
double SYNC_Confidence(void); // lock confidence <0..1>
double SYNC_FreqOffset(void);
double SYNC_FreqDevdspRMS(void);
double SYNC_TimeOffset(void);
double TotalFreqOffset(); // Total frequency offset in [Hz]
double FEC_SNR(void); // signal-to-noise ratio at the FEC
int FEC_CarrOffset(void);
private:
dspQuadrSplit InpSplit; // input filter, I/Q splitter, decimator
dspCmpxMixer TestOfs; // frequency offset for tests
dspDelayLine<dspCmpx> ProcLine; // processing pipe
int ProcdspDelay; // processing dspDelay for optimal symbol probing
int SyncProcPtr; // sAmpling pointer for the synchronizer
int DataProcPtr; // sAmpling pointer for the data demodulator
dsp_r2FFT FFT; // FFT engine
int WindowLen; // FFT window length = symbol shape length
int WindowLenMask; // WindowLen-1 for pointer wrapping
double *RxWindow; // FFT window shape = symbol shape
void (*SpectraDisplay)(double *Spectra, int Len);
double *SpectradspPower;
int AliasFilterLen; // anti-alias filter length
//double *AliasShapeI,
// *AliasShapeQ; // and shapes
int DecimateRatio; // decimation/interpolation after/before filter
// how the bits of one block are placed on data carriers
int *InterleavePattern;
int DataInterleave; // data interleave depth
int DataCarriers; // number of carriers
int FirstDataCarr; // the FFT index of the first data carrier
// int DataCarrSepar; // freq. separation between carriers [FFT bins]
long CarrMarkCode; // code to mark carriers (not in use here)
// int SymbolSepar; // time separation between symbols [samples]
int ScanMargin; // How many carriers up and down to search
int IntegLen; // Over how many symbols we integrate to synchronize
int SymbolDiv; // =4 we probe the input 4 times per symbol time
int SyncStep; // SymbolSepar/SymbolDiv
int ScanFirst; // first carrier to scan
int ScanLen; // number of carriers to scan
dspCmpx *FFTbuff;
dspCmpx *FFTbuff2;
// here starts the time/frequency synchronizer
void SyncProcess(dspCmpx *Slice);
dspCmpx *SyncPipe[4]; // FFT result buffer for sync.
int SyncPtr; // wrapping pointer for SyncPipe and integrators
int SymbPtr; // points about where the symbol is
dspCmpx *SyncPhCorr; // dspPhase corrections for the sync. processor
dspCmpx *CorrelMid[4],
*CorrelOut[4]; // correlation integrator
double *dspPowerMid,
*dspPowerOut; // carrier dspPower integrator
dspCmpx *CorrelNorm[4]; // normalized correlation
double W1, W2, W5; // correlation integrator weights
double W1p, W2p, W5p; // dspPower integrator weights
dspCmpx *CorrelAver[4]; // sliding sum to fit the carrier pattern
int FitLen;
void DoCorrelSum(dspCmpx *Correl1, dspCmpx *Correl2, dspCmpx *Aver);
dspCmpx *SymbFit; // vectors to match symbol shift and confidence
int SymbFitPos; // "smoothed" peak position
double *FreqPipe; // smoothing pipe for frequency offset
dspCmpx *SymbPipe; // smoothing pipe for symbol shift
int TrackPipeLen; // tracking pipe length
int TrackPipePtr; // pipe pointer
double AverFreq; // dspAveraged frequency
dspCmpx AverSymb; // dspAveraged symbol dspPhase
double SyncLockThres; // lock confidence threshold
double SyncHoldThres; // minimal confidence to hold the lock
int SyncLocked; // locked or not locked
double SyncSymbConf; // current smoothed confidence
double SyncFreqOfs; // current smoothed frequency offset
double SyncFreqDev; // frequency deviation (dspRMS)
double SyncSymbShift; // current smoothed symbol time shift
// here starts the data decoder
void DataProcess( dspCmpx *EvenSlice,
dspCmpx *OddSlice,
double FreqOfs,
int TimeDist);
int DataScanMargin; // +/- data carriers to scan for best FEC match
int DataScanLen; // total number of data carriers being processed
int DataScanFirst;
dspCmpx *RefDataSlice; // reference data slice for differential dspPhase decode
dspCmpx *DataVect; // differentially decoded data vactor
int DataPipeLen; // pipe length
int DataPipePtr; // wrapping pointer
dspCmpx **DataPipe; // decoded vectors pipe
double *DataPwrMid,
*DataPwrOut; // carrier dspPower integrator
dspCmpx *DataSqrMid,
*DataSqrOut; // carrier complex square integrator
double dW1, dW2, dW5; // integrator constants
double *DatadspPhase; // differential decoded dspPhases
double *DatadspPhase2; // rather for debugging, not use otherwise
MT63decoder Decoder;
} ;
#endif // MT63_BASE_H