1 /* 2 * mt63base.h -- MT63 transmitter and receiver in C++ for LINUX 3 * 4 * Copyright (c) 2007, 2008 Dave Freese, W1HKJ 5 * 6 * base class for use by fldigi 7 * modified from original 8 * excluded CW_ID which is a part of the base modem class for fldigi 9 * 10 * based on mt63 code by Pawel Jalocha 11 * Copyright (C) 1999-2004 Pawel Jalocha, SP9VRC 12 * Copyright (c) 2007-2011 Dave Freese, W1HKJ 13 * 14 * This file is part of fldigi. 15 * 16 * Fldigi is free software: you can redistribute it and/or modify 17 * it under the terms of the GNU General Public License as published by 18 * the Free Software Foundation, either version 3 of the License, or 19 * (at your option) any later version. 20 * 21 * Fldigi is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU General Public License for more details. 25 * 26 * You should have received a copy of the GNU General Public License 27 * along with fldigi. If not, see <http://www.gnu.org/licenses/>. 28 * 29 */ 30 31 #ifndef MT63BASE_H 32 #define MT63BASE_H 33 34 // ========================================================================== 35 // Character encoder and block interleaver for the MT63 modem 36 /* 37 How to use this class: 38 1. Create or declare an object like: 39 MT63encoder Encoder; 40 2. Preset the object for the given number of carriers and interleave: 41 err=Encoder.Preset(<carriers>,<interleave>,<pattern>); 42 MT63 uses 64 carriers and interleave of 32 or 64 43 - the corresponding interleave patterns can be found in mt63.dat. 44 If Encode.Preset() returns non-zero you are in big troubles ! 45 3. For each character to be encode you make the call: 46 Encoder.Process(<character>); 47 and you should then take the bits from the Encode.Output 48 - these are the bits to be sent out on the carriers. 49 (for MT63 logical 0 means carrier flip, logical 1 is no flip). 50 4. At any time you can call Encoder.Preset() again to change the parameters 51 (this will clean away the interleaver pipe). 52 */ 53 54 // MT63 modem specific routines, made to be compatible with the MT63ASC.ASM 55 // (c) 1999 Pawel Jalocha, SP9VRC, jalocha@hpdel1.ifj.edu.pl 56 // Date: 05-NOV-1999 57 58 class MT63encoder 59 { 60 public: 61 MT63encoder(); 62 ~MT63encoder(); 63 void Free(); 64 int Preset(int Carriers, int Intlv, int *Pattern, int RandFill=0); 65 int Process(char code); 66 char *Output; 67 private: 68 int DataCarriers; 69 char CodeMask; 70 int IntlvLen; 71 int IntlvSize; 72 int *IntlvPatt; 73 char *IntlvPipe; 74 int IntlvPtr; 75 double *WalshBuff; 76 } ; 77 78 // ========================================================================== 79 // MT63 deinterleaver and decoder 80 /* 81 How to use this class: 82 1. Create or declare an object: 83 MT63decoder Decoder; 84 2. Preset given parameters with Decoder.Preset(); 85 Decoder.Preset(); 86 Number of carriers and interleave are same as for MT63encoder. 87 "Margin" is the number of extra carriers demodulated on the side 88 because with the MT63 you cannot say with full confidence which 89 is really the first carrier: the FEC decoder have to tell you this. 90 "Integ" is the integration period to find the best FEC match. 91 "Integ" is measured in MT63 symbols (at 1000 kHz we do 10 symbols/s). 92 3. For each symbol period feed the demodulated data into the object: 93 Decoder.Process(<data>); 94 and then get the demodulated character code from Decoder.Output 95 You can get as well the measured signal-to-noise ratio from Decoder.SNR 96 and the index of the first carrier (according to the FEC match) 97 from Decoder.CarrOfs 98 4. You can change the parameters at any time with Decoder.Preset() 99 (this will clean the data pipes). 100 */ 101 102 class MT63decoder 103 { 104 public: 105 MT63decoder(); 106 ~MT63decoder(); 107 void Free(); 108 int Preset(int Carriers, int Intlv, int *Pattern, int Margin, int Integ); 109 int Process(double *Data); 110 char Output; 111 double SignalToNoise; 112 int CarrOfs; 113 114 private: 115 int DataCarriers; 116 double *IntlvPipe; 117 int IntlvLen; 118 int IntlvSize; 119 int IntlvPtr; 120 int *IntlvPatt; 121 122 double *WalshBuff; 123 124 int ScanLen; 125 int ScanSize; 126 double *DecodeSnrMid,*DecodeSnrOut; 127 double W1, W2, W5; 128 char *DecodePipe; 129 int DecodeLen; 130 int DecodeSize; 131 int DecodePtr; 132 133 } ; 134 135 // ========================================================================== 136 // MT63 transmitter 137 /* 138 How to use this class: 139 1. Create or declare an object: 140 MT63tx Tx; 141 2. Preset parameters: 142 Tx.Preset(<bandwidth>,<interleave>); 143 Allowed values are: bandwidth=500,1000,2000; interleave=0,1; 144 Non-zero value returned means there was a problem... 145 3. For each character to be sent: 146 Tx.SendChar(<char>); 147 After each call to SendChar() you must read the samples 148 from the Tx.Comb.Output.Data, the number of samples to read 149 is in Tx.Comb.Output.Len. They are in double floating point, so you should 150 convert them to 16-bit integers and output them to your soundcard. 151 4. If you have nothing to transmit, you must not stop, because 152 you have to keep the sound going. MT63 transmits NL characters (code=0) 153 in this case. 154 5. When you are done with all the characters and you want to stop, 155 you should still put some NL characters in to flush the interleave 156 thus please call the Tx.SendChar() Tx.DataInterleave times 157 (still add few more characters to flush the windowed IFFT buffers). 158 After that the MT63 transmits a jamming dspSequence for some time 159 to speed up carrier drop at the receiver: you do this by calling 160 Tx.SendJam(); 161 6. You can transmit few symbols of silence by: 162 Tx.SendSilence() 163 to make a gracefull switch-off. 164 Remember: each time you call SendChar(), SendJam() or SendSilence() 165 you must send the contains of Tx.Comb.Output out to your soundcard. 166 Each Tx.SendXxx() produces the amount of sound corresponding to one 167 symbol time that is 0.1 second for the 1000 Hz mode. 168 The soundcard output rate must be 8000 Hz, rather precisely, 169 that is the error should be below 1 Hz. If it is not you should 170 use the rate converter: look into mt63tx for an example. 171 7. Inbetween transmissions you may change the settings by calling 172 the Tx.Preset() again. 173 */ 174 175 class MT63tx 176 { 177 public: 178 MT63tx(); 179 ~MT63tx(); 180 void Free(void); 181 int Preset(double freq, int BandWidth=1000, int LongInterleave=0); 182 int SendTune(bool twotones); 183 int SendChar(char ch); 184 int SendJam(void); 185 int SendSilence(void); 186 187 private: 188 int DataCarriers; // the number of data carriers 189 int FirstDataCarr; // the FFT index of the first data carrier 190 int WindowLen; // FFT window and symbol shape length 191 double *TxWindow; // The shape of the FFT window (=symbol shape) 192 193 int AliasFilterLen; // anti-alias filter length 194 //double *AliasShapeI, 195 // *AliasShapeQ; // and shapes (for fixed lower freq of 500 Hz) 196 int DecimateRatio; // decimation/interpolation after/before filter 197 int *InterleavePattern; // how the bits of one block are placed on data carriers 198 double TxAmpl; // Amplitude applied to generate a carrier (before IFFT) 199 long CarrMarkCode; 200 int CarrMarkAmpl; 201 202 MT63encoder Encoder; // data encode and interleaver 203 int *TxVect; // modulator vector (dspPhases) 204 int *dspPhaseCorr; // dspPhase corrections for each carrier 205 dspCmpx_buff WindowBuff; // FFT/window buffer 206 dsp_r2FFT FFT; // FFT engine 207 208 dspCmpxMixer txmixer; 209 210 dspCmpxOverlapWindow Window; // overlapping window 211 212 int ProcessTxVect(); 213 214 public: 215 int DataInterleave; 216 dspQuadrComb Comb; // the output of this module is in Comb.Output 217 } ; 218 219 // ========================================================================== 220 // MT63 receiver 221 /* 222 How to use this class: 223 1. Declare the object: 224 MT63rx Rx; 225 2. Preset paramateres 226 Rx.Preset(<bandwidth>,<interleave>,<integration>); 227 For weak signals I recommend integration of 32 or more, 228 otherwise 16 is enough. By the way, 16 means 1.6 second for 1000 Hz mode 229 because then we transmit 10 symbols per second. 230 3. After EACH new batch of samples 231 you should look into Rx.Output for the decoded characters. 232 You can egzamin the receiver status at any time by calling: 233 Rx.SYNC_LockStatus() => logical value 0 or 1 234 Rx.SYNC_Confidence() => lock confidence: a double between 0.0 and 1.0 235 Rx.FEC_SNR() => signal-to-noise seen by FEC 236 Rx.TotalFreqOffset() => measured frequency offset in [Hz] 237 assuming 8000 Hz sAmpling 238 */ 239 240 class MT63rx 241 { 242 public: 243 MT63rx(); 244 ~MT63rx(); 245 void Free(void); 246 int Preset( double freq, 247 int BandWidth = 1000, 248 int LongInterleave = 0, 249 int Integ = 16, 250 void (*Display)(double *Spectra, int Len) = NULL); 251 int Process(double_buff *Input); 252 char_buff Output; // decoded characters 253 254 int SYNC_LockStatus(void); // 1 => locked, 0 => not locked 255 double SYNC_Confidence(void); // lock confidence <0..1> 256 double SYNC_FreqOffset(void); 257 double SYNC_FreqDevdspRMS(void); 258 double SYNC_TimeOffset(void); 259 double TotalFreqOffset(); // Total frequency offset in [Hz] 260 double FEC_SNR(void); // signal-to-noise ratio at the FEC 261 int FEC_CarrOffset(void); 262 263 private: 264 dspQuadrSplit InpSplit; // input filter, I/Q splitter, decimator 265 dspCmpxMixer TestOfs; // frequency offset for tests 266 267 dspDelayLine<dspCmpx> ProcLine; // processing pipe 268 int ProcdspDelay; // processing dspDelay for optimal symbol probing 269 int SyncProcPtr; // sAmpling pointer for the synchronizer 270 int DataProcPtr; // sAmpling pointer for the data demodulator 271 272 dsp_r2FFT FFT; // FFT engine 273 int WindowLen; // FFT window length = symbol shape length 274 int WindowLenMask; // WindowLen-1 for pointer wrapping 275 double *RxWindow; // FFT window shape = symbol shape 276 277 void (*SpectraDisplay)(double *Spectra, int Len); 278 double *SpectradspPower; 279 280 int AliasFilterLen; // anti-alias filter length 281 //double *AliasShapeI, 282 // *AliasShapeQ; // and shapes 283 int DecimateRatio; // decimation/interpolation after/before filter 284 285 // how the bits of one block are placed on data carriers 286 int *InterleavePattern; 287 int DataInterleave; // data interleave depth 288 289 int DataCarriers; // number of carriers 290 int FirstDataCarr; // the FFT index of the first data carrier 291 // int DataCarrSepar; // freq. separation between carriers [FFT bins] 292 long CarrMarkCode; // code to mark carriers (not in use here) 293 // int SymbolSepar; // time separation between symbols [samples] 294 int ScanMargin; // How many carriers up and down to search 295 int IntegLen; // Over how many symbols we integrate to synchronize 296 297 int SymbolDiv; // =4 we probe the input 4 times per symbol time 298 int SyncStep; // SymbolSepar/SymbolDiv 299 int ScanFirst; // first carrier to scan 300 int ScanLen; // number of carriers to scan 301 302 dspCmpx *FFTbuff; 303 dspCmpx *FFTbuff2; 304 305 // here starts the time/frequency synchronizer 306 void SyncProcess(dspCmpx *Slice); 307 308 dspCmpx *SyncPipe[4]; // FFT result buffer for sync. 309 int SyncPtr; // wrapping pointer for SyncPipe and integrators 310 int SymbPtr; // points about where the symbol is 311 312 dspCmpx *SyncPhCorr; // dspPhase corrections for the sync. processor 313 314 dspCmpx *CorrelMid[4], 315 *CorrelOut[4]; // correlation integrator 316 double *dspPowerMid, 317 *dspPowerOut; // carrier dspPower integrator 318 dspCmpx *CorrelNorm[4]; // normalized correlation 319 double W1, W2, W5; // correlation integrator weights 320 double W1p, W2p, W5p; // dspPower integrator weights 321 322 dspCmpx *CorrelAver[4]; // sliding sum to fit the carrier pattern 323 int FitLen; 324 325 void DoCorrelSum(dspCmpx *Correl1, dspCmpx *Correl2, dspCmpx *Aver); 326 327 dspCmpx *SymbFit; // vectors to match symbol shift and confidence 328 int SymbFitPos; // "smoothed" peak position 329 330 double *FreqPipe; // smoothing pipe for frequency offset 331 dspCmpx *SymbPipe; // smoothing pipe for symbol shift 332 int TrackPipeLen; // tracking pipe length 333 int TrackPipePtr; // pipe pointer 334 double AverFreq; // dspAveraged frequency 335 dspCmpx AverSymb; // dspAveraged symbol dspPhase 336 337 double SyncLockThres; // lock confidence threshold 338 double SyncHoldThres; // minimal confidence to hold the lock 339 340 int SyncLocked; // locked or not locked 341 double SyncSymbConf; // current smoothed confidence 342 double SyncFreqOfs; // current smoothed frequency offset 343 double SyncFreqDev; // frequency deviation (dspRMS) 344 double SyncSymbShift; // current smoothed symbol time shift 345 346 // here starts the data decoder 347 void DataProcess( dspCmpx *EvenSlice, 348 dspCmpx *OddSlice, 349 double FreqOfs, 350 int TimeDist); 351 352 int DataScanMargin; // +/- data carriers to scan for best FEC match 353 int DataScanLen; // total number of data carriers being processed 354 int DataScanFirst; 355 356 dspCmpx *RefDataSlice; // reference data slice for differential dspPhase decode 357 dspCmpx *DataVect; // differentially decoded data vactor 358 359 int DataPipeLen; // pipe length 360 int DataPipePtr; // wrapping pointer 361 dspCmpx **DataPipe; // decoded vectors pipe 362 double *DataPwrMid, 363 *DataPwrOut; // carrier dspPower integrator 364 dspCmpx *DataSqrMid, 365 *DataSqrOut; // carrier complex square integrator 366 double dW1, dW2, dW5; // integrator constants 367 368 double *DatadspPhase; // differential decoded dspPhases 369 double *DatadspPhase2; // rather for debugging, not use otherwise 370 371 MT63decoder Decoder; 372 373 } ; 374 375 #endif // MT63_BASE_H 376