1 // ----------------------------------------------------------------------------
2 // contestia.cxx  --  CONTESTIA modem
3 //
4 // Copyright (C) 2006-2010
5 //		Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.  Adapted from code contained in gmfsk source code
8 // distribution.
9 //	Copyright (C) 2005
10 //	Tomi Manninen (oh2bns@sral.fi)
11 //
12 // This file is part of fldigi.
13 //
14 // Fldigi is free software: you can redistribute it and/or modify
15 // it under the terms of the GNU General Public License as published by
16 // the Free Software Foundation, either version 3 of the License, or
17 // (at your option) any later version.
18 //
19 // Fldigi is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
26 // ----------------------------------------------------------------------------
27 
28 #include <config.h>
29 #include <sstream>
30 
31 #include <FL/Fl.H>
32 
33 #include "contestia.h"
34 #include "modem.h"
35 #include "fl_digi.h"
36 
37 #include "misc.h"
38 #include "confdialog.h"
39 #include "status.h"
40 #include "debug.h"
41 
42 LOG_FILE_SOURCE(debug::LOG_MODEM);
43 
44 using namespace std;
45 
nco(double freq)46 double contestia::nco(double freq)
47 {
48     preamblephase += 2.0 * M_PI * freq / samplerate;
49 
50 	if (preamblephase > TWOPI)
51 		preamblephase -= TWOPI;
52 
53 	return cos(preamblephase);
54 }
55 
tx_init()56 void contestia::tx_init()
57 {
58 	phaseacc = 0;
59 	prevsymbol = cmplx (1.0, 0.0);
60 	preamble = 32;
61 	shreg = 0;
62 
63 	preamblesent = 0;
64 	postamblesent = 0;
65 	txbasefreq = get_txfreq_woffset();
66 
67 	rx_flush();
68 
69 	if (reverse) {
70 		Tx->FirstCarrierMultiplier = (txbasefreq + (Tx->Bandwidth / 2)) / 500;
71 		Tx->Reverse = 1;
72 	} else {
73 		Tx->FirstCarrierMultiplier = (txbasefreq - (Tx->Bandwidth / 2)) / 500;
74 		Tx->Reverse = 0;
75 	}
76 
77 	videoText();
78 
79 	Tx->Preset();
80 	Tx->Start();
81 	escape = 0;
82 }
83 
rx_flush()84 void contestia::rx_flush()
85 {
86 	unsigned char c;
87 	Rx->Flush();
88 	while (Rx->GetChar(c) > 0)
89 		put_rx_char(c);
90 }
91 
send_tones()92 void contestia::send_tones()
93 {
94 	if (!progdefaults.contestia_start_tones) return;
95 
96 	double freqa, freqb;
97 	tone_bw = bandwidth;
98 	tone_midfreq = txbasefreq;
99 
100 	if (reverse) {
101 		freqa = tone_midfreq + (tone_bw / 2.0);
102 		freqb = tone_midfreq - (tone_bw / 2.0);
103 	} else {
104 		freqa = tone_midfreq - (tone_bw / 2.0);
105 		freqb = tone_midfreq + (tone_bw / 2.0);
106 	}
107 
108 	preamblephase = 0;
109 	for (int i = 0; i < SR4; i++)
110 		tonebuff[2*SR4 + i] = tonebuff[i] = nco(freqa) * ampshape[i];
111 
112 	preamblephase = 0;
113 	for (int i = 0; i < SR4; i++)
114 		tonebuff[3*SR4 + i] = tonebuff[SR4 + i] = nco(freqb) * ampshape[i];
115 
116 	for (int j = 0; j < TONE_DURATION; j += SCBLOCKSIZE)
117 		ModulateXmtr(&tonebuff[j], SCBLOCKSIZE);
118 
119 }
120 
rx_init()121 void contestia::rx_init()
122 {
123 	Rx->Reset();
124 	escape = 0;
125 }
126 
unescape(int c)127 int contestia::unescape(int c)
128 {
129 	if (progdefaults.contestia8bit == 0)
130 		return c;
131 
132 	if (escape) {
133 		escape = 0;
134 		return c + 128;
135 	}
136 
137 	if (c == 127) {
138 		escape = 1;
139 		return -1;
140 	}
141 
142 	return c;
143 }
144 
tx_process()145 int contestia::tx_process()
146 {
147 	int c = 0, len = 0;
148 	unsigned char ch;
149 
150 	if (tones	!= progdefaults.contestiatones ||
151 		bw 		!= progdefaults.contestiabw ||
152 		smargin != progdefaults.contestiasmargin ||
153 		sinteg	!= progdefaults.contestiasinteg )
154 			restart();
155 
156 	if (preamblesent != 1) {
157 		send_tones();
158 		preamblesent = 1;
159 		// Olivia Transmitter class requires at least character
160 		Tx->PutChar(0);
161 	}
162 
163 // The encoder works with BitsPerSymbol length blocks. If the
164 // modem already has that many characters buffered, don't try
165 // to read any more. If stopflag is set, we will always read
166 // whatever there is.
167 	if (stopflag || (Tx->GetReadReady() < Tx->BitsPerSymbol)) {
168 		if (!stopflag && (c = get_tx_char()) == GET_TX_CHAR_ETX)
169 			stopflag = true;
170 		if (stopflag)
171 			Tx->Stop();
172 		else {
173 			if (c == GET_TX_CHAR_NODATA)
174 		                c = 0;
175 			/* Replace un-representable characters with a dot */
176 			if (c > (progdefaults.contestia8bit ? 255 : 127))
177 				c = '.';
178 			if (c > 127) {
179 				c &= 127;
180 				Tx->PutChar(127);
181 			}
182 			Tx->PutChar(c);
183 		}
184 	}
185 
186 	if (Tx->GetChar(ch) > 0)
187 		if ((c = unescape(ch)) != -1)
188 			put_echo_char(progdefaults.rx_lowercase ? tolower(c) : toupper(c));
189 
190     if ((len = Tx->Output(txfbuffer)) > 0)
191 		ModulateXmtr(txfbuffer, len);
192 
193 	if (stopflag && Tx->DoPostambleYet() == 1 && postamblesent != 1) {
194 		postamblesent = 1;
195 		send_tones();
196 	}
197 
198 	if (!Tx->Running()) {
199 		stopflag = false;
200 		return -1;
201 	}
202 
203 	return 0;
204 }
205 
206 
rx_process(const double * buf,int len)207 int contestia::rx_process(const double *buf, int len)
208 {
209 	int c;
210 	unsigned char ch = 0;
211 	static double snr = 1e-3;
212 	static char msg1[20];
213 	static char msg2[20];
214 
215 	if (tones	!= progdefaults.contestiatones ||
216 		bw 		!= progdefaults.contestiabw ||
217 		smargin != progdefaults.contestiasmargin ||
218 		sinteg	!= progdefaults.contestiasinteg )
219 			restart();
220 
221 	if ((lastfreq != frequency || Rx->Reverse) && !reverse) {
222 		Rx->FirstCarrierMultiplier = (frequency - (Rx->Bandwidth / 2)) / 500;
223 		Rx->Reverse = 0;
224 		lastfreq = frequency;
225 		Rx->Preset();
226 	}
227 	else if ((lastfreq != frequency || !Rx->Reverse) && reverse) {
228 		Rx->FirstCarrierMultiplier = (frequency + (Rx->Bandwidth / 2)) / 500;
229 		Rx->Reverse = 1;
230 		lastfreq = frequency;
231 		Rx->Preset();
232 	}
233 
234 	Rx->SyncThreshold = progStatus.sqlonoff ?
235 		clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 3.0, 90.0) : 3.0;
236 
237     Rx->Process(buf, len);
238 	sp = 0;
239 	for (int i = frequency - Rx->Bandwidth/2; i < frequency - 1 + Rx->Bandwidth/2; i++)
240 		if (wf->Pwr(i) > sp)
241 			sp = wf->Pwr(i);
242 	np = wf->Pwr(frequency + Rx->Bandwidth/2 + 2*Rx->Bandwidth/Rx->Tones);
243 	if (np == 0) np = sp + 1e-8;
244 	sigpwr = decayavg( sigpwr, sp, 10);
245 	noisepwr = decayavg( noisepwr, np, 50);
246 	snr = CLAMP(sigpwr / noisepwr, 0.001, 100000);
247 
248 	metric = clamp( 5.0 * (Rx->SignalToNoiseRatio() - 3.0), 0, 100);
249 	display_metric(metric);
250 
251 	bool gotchar = false;
252 	while (Rx->GetChar(ch) > 0) {
253 		if ((c = unescape(ch)) != -1 && c > 7) {
254 			put_rx_char(progdefaults.rx_lowercase ? tolower(c) : c);
255 			gotchar = true;
256 		}
257     }
258 	if (gotchar) {
259 		snprintf(msg1, sizeof(msg1), "s/n: %4.1f dB", 10*log10(snr) - 20);
260 		put_Status1(msg1, 5, STATUS_CLEAR);
261 		snprintf(msg2, sizeof(msg2), "f/o %+4.1f Hz", Rx->FrequencyOffset());
262 		put_Status2(msg2, 5, STATUS_CLEAR);
263 	}
264 
265 	return 0;
266 }
267 
restart()268 void contestia::restart()
269 {
270 	tones	= progdefaults.contestiatones;
271 	bw 		= progdefaults.contestiabw;
272 	smargin = progdefaults.contestiasmargin;
273 	sinteg	= progdefaults.contestiasinteg;
274 
275 	samplerate = 8000;
276 	bandwidth = 125 * (1 << bw);
277 
278 	Tx->Tones = 2 * (1 << tones);
279 	Tx->Bandwidth = bandwidth;
280 	Tx->SampleRate = samplerate;
281 	Tx->OutputSampleRate = samplerate;
282     txbasefreq = get_txfreq_woffset();
283 	Tx->bContestia = true;
284 
285 	if (reverse) {
286 		Tx->FirstCarrierMultiplier = (txbasefreq + (Tx->Bandwidth / 2)) / 500;
287 		Tx->Reverse = 1;
288 	} else {
289 		Tx->FirstCarrierMultiplier = (txbasefreq - (Tx->Bandwidth / 2)) / 500;
290 		Tx->Reverse = 0;
291 	}
292 
293 	if (Tx->Preset() < 0) {
294 		LOG_ERROR("contestia: transmitter preset failed!");
295 		return;
296 	}
297 
298 	txbufferlen = Tx->MaxOutputLen;
299 
300 	if (txfbuffer) delete [] txfbuffer;
301 	txfbuffer = new double[txbufferlen];
302 
303 	Rx->Tones = Tx->Tones;
304 	Rx->Bandwidth = bandwidth;
305 	Rx->SyncMargin = smargin;
306 	Rx->SyncIntegLen = sinteg;
307 	Rx->SyncThreshold = progStatus.sqlonoff ?
308 		clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 0, 90.0) : 0.0;
309 
310 	Rx->SampleRate = samplerate;
311 	Rx->InputSampleRate = samplerate;
312 	Rx->bContestia = true;
313 
314 	if (reverse) {
315 		Rx->FirstCarrierMultiplier = (frequency + (Rx->Bandwidth / 2)) / 500;
316 		Rx->Reverse = 1;
317 	} else {
318 		Rx->FirstCarrierMultiplier = (frequency - (Rx->Bandwidth /2)) / 500;
319 		Rx->Reverse = 0;
320 	}
321 
322 	if (Rx->Preset() < 0) {
323 		LOG_ERROR("contestia: receiver preset failed!");
324 		return;
325 	}
326 	fragmentsize = 1024;
327 	set_bandwidth(Tx->Bandwidth - Tx->Bandwidth / Tx->Tones);
328 
329 	stringstream info;
330 	info << mode_info[mode].sname;
331 	put_MODEstatus("%s", info.str().c_str());
332 
333 	metric = 0;
334 
335 	sigpwr = 1e-10; noisepwr = 1e-8;
336 	LOG_DEBUG("\nContestia Rx parameters:\n%s", Rx->PrintParameters());
337 }
338 
init()339 void contestia::init()
340 {
341 	restart();
342 	modem::init();
343 	set_scope_mode(Digiscope::BLANK);
344 
345 	if (progdefaults.StartAtSweetSpot)
346 		set_freq(progdefaults.PSKsweetspot);
347 	else if (progStatus.carrier != 0) {
348 		set_freq(progStatus.carrier);
349 #if !BENCHMARK_MODE
350 		progStatus.carrier = 0;
351 #endif
352 	} else
353 		set_freq(wf->Carrier());
354 
355 }
356 
contestia(trx_mode omode)357 contestia::contestia(trx_mode omode)
358 {
359 	mode = omode;
360 	cap |= CAP_REV;
361 
362 	txfbuffer = 0;
363 	samplerate = 8000;
364 
365 	switch (mode) {
366 		case MODE_CONTESTIA_4_125:
367 			progdefaults.contestiatones = tones = 1;
368 			progdefaults.contestiabw = bw = 0;
369 			REQ(set_contestia_tab_widgets);
370 			break;
371 		case MODE_CONTESTIA_4_250:
372 			progdefaults.contestiatones = tones = 1;
373 			progdefaults.contestiabw = bw = 1;
374 			REQ(set_contestia_tab_widgets);
375 			break;
376 		case MODE_CONTESTIA_4_500:
377 			progdefaults.contestiatones = tones = 1;
378 			progdefaults.contestiabw = bw = 2;
379 			REQ(set_contestia_tab_widgets);
380 			break;
381 		case MODE_CONTESTIA_4_1000:
382 			progdefaults.contestiatones = tones = 1;
383 			progdefaults.contestiabw = bw = 3;
384 			REQ(set_contestia_tab_widgets);
385 			break;
386 		case MODE_CONTESTIA_4_2000:
387 			progdefaults.contestiatones = tones = 1;
388 			progdefaults.contestiabw = bw = 4;
389 			REQ(set_contestia_tab_widgets);
390 			break;
391 		case MODE_CONTESTIA_8_125:
392 			progdefaults.contestiatones = tones = 2;
393 			progdefaults.contestiabw = bw = 0;
394 			REQ(set_contestia_tab_widgets);
395 			break;
396 		case MODE_CONTESTIA_8_250:
397 			progdefaults.contestiatones = tones = 2;
398 			progdefaults.contestiabw = bw = 1;
399 			REQ(set_contestia_tab_widgets);
400 			break;
401 		case MODE_CONTESTIA_8_500:
402 			progdefaults.contestiatones = tones = 2;
403 			progdefaults.contestiabw = bw = 2;
404 			REQ(set_contestia_tab_widgets);
405 			break;
406 		case MODE_CONTESTIA_8_1000:
407 			progdefaults.contestiatones = tones = 2;
408 			progdefaults.contestiabw = bw = 3;
409 			REQ(set_contestia_tab_widgets);
410 			break;
411 		case MODE_CONTESTIA_8_2000:
412 			progdefaults.contestiatones = tones = 2;
413 			progdefaults.contestiabw = bw = 4;
414 			REQ(set_contestia_tab_widgets);
415 			break;
416 		case MODE_CONTESTIA_16_250:
417 			progdefaults.contestiatones = tones = 3;
418 			progdefaults.contestiabw = bw = 1;
419 			REQ(set_contestia_tab_widgets);
420 			break;
421 		case MODE_CONTESTIA_16_500:
422 			progdefaults.contestiatones = tones = 3;
423 			progdefaults.contestiabw = bw = 2;
424 			REQ(set_contestia_tab_widgets);
425 			break;
426 		case MODE_CONTESTIA_16_1000:
427 			progdefaults.contestiatones = tones = 3;
428 			progdefaults.contestiabw = bw = 3;
429 			REQ(set_contestia_tab_widgets);
430 			break;
431 		case MODE_CONTESTIA_16_2000:
432 			progdefaults.contestiatones = tones = 3;
433 			progdefaults.contestiabw = bw = 4;
434 			REQ(set_contestia_tab_widgets);
435 			break;
436 		case MODE_CONTESTIA_32_1000:
437 			progdefaults.contestiatones = tones = 4;
438 			progdefaults.contestiabw = bw = 3;
439 			REQ(set_contestia_tab_widgets);
440 			break;
441 		case MODE_CONTESTIA_32_2000:
442 			progdefaults.contestiatones = tones = 4;
443 			progdefaults.contestiabw = bw = 4;
444 			REQ(set_contestia_tab_widgets);
445 			break;
446 		case MODE_CONTESTIA_64_500:
447 			progdefaults.contestiatones = tones = 5;
448 			progdefaults.contestiabw = bw = 2;
449 			REQ(set_contestia_tab_widgets);
450 			break;
451 		case MODE_CONTESTIA_64_1000:
452 			progdefaults.contestiatones = tones = 5;
453 			progdefaults.contestiabw = bw = 3;
454 			REQ(set_contestia_tab_widgets);
455 			break;
456 		case MODE_CONTESTIA_64_2000:
457 			progdefaults.contestiatones = tones = 5;
458 			progdefaults.contestiabw = bw = 4;
459 			REQ(set_contestia_tab_widgets);
460 			break;
461 		case MODE_CONTESTIA:
462 		default:
463 			tones = progdefaults.contestiatones;
464 			bw    = progdefaults.contestiabw;
465 			REQ(set_contestia_tab_widgets);
466 			break;
467 	}
468 
469 	Tx = new MFSK_Transmitter< double >;
470 	Rx = new MFSK_Receiver< double >;
471 
472 	Tx->bContestia = true;
473 	Rx->bContestia = true;
474 
475 	lastfreq = 0;
476 
477 	for (int i = 0; i < SR4; i++) ampshape[i] = 1.0;
478 	for (int i = 0; i < SR4 / 8; i++)
479 		ampshape[i] = ampshape[SR4 - 1 - i] = 0.5 * (1.0 - cos(M_PI * i / (SR4/8)));
480 
481 	for (int i = 0; i < TONE_DURATION; i++) tonebuff[i] = 0;
482 
483 	tone_bw = -1;
484 	tone_midfreq = -1;
485 }
486 
~contestia()487 contestia::~contestia()
488 {
489 	if (Tx) delete Tx;
490 	if (Rx) delete Rx;
491 	if (txfbuffer) delete [] txfbuffer;
492 }
493 
494