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