kopia lustrzana https://github.com/jamescoxon/dl-fldigi
376 wiersze
13 KiB
C++
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
|