1 // ----------------------------------------------------------------------------
2 // olivia.cxx -- OLIVIA 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
30 #include <sstream>
31
32 #include <FL/Fl.H>
33
34 #include "olivia.h"
35 #include "modem.h"
36 #include "fl_digi.h"
37
38 #include "misc.h"
39 #include "confdialog.h"
40 #include "status.h"
41 #include "debug.h"
42 #include "qrunner.h"
43
44 //------------------------------------------------------------------------------
45 #include "threads.h"
46
47 static pthread_mutex_t olivia_mutex = PTHREAD_MUTEX_INITIALIZER;
48 //------------------------------------------------------------------------------
49
50 LOG_FILE_SOURCE(debug::LOG_MODEM);
51
52 using namespace std;
53
nco(double freq)54 double olivia::nco(double freq)
55 {
56 preamblephase += 2.0 * M_PI * freq / samplerate;
57
58 if (preamblephase > M_PI)
59 preamblephase -= 2.0 * M_PI;
60
61 return cos(preamblephase);
62 }
63
tx_init()64 void olivia::tx_init()
65 {
66 phaseacc = 0;
67 prevsymbol = cmplx (1.0, 0.0);
68 preamble = 32;
69 shreg = 0;
70
71 preamblesent = 0;
72 postamblesent = 0;
73 txbasefreq = get_txfreq_woffset();
74
75 rx_flush();
76
77 { // critical section
78 guard_lock dsp_lock(&olivia_mutex);
79
80 double fc_offset = Tx->Bandwidth*(1.0 - 0.5/Tx->Tones)/2.0;
81 if (reverse) {
82 Tx->FirstCarrierMultiplier = (txbasefreq + fc_offset)/500.0;
83 Tx->Reverse = 1;
84 } else {
85 Tx->FirstCarrierMultiplier = (txbasefreq - fc_offset)/500.0;
86 Tx->Reverse = 0;
87 }
88 Tx->Preset();
89 Tx->Start();
90 } // end critical section
91
92 videoText();
93
94 escape = 0;
95 }
96
rx_flush()97 void olivia::rx_flush()
98 {
99 guard_lock dsp_lock(&olivia_mutex);
100
101 unsigned char c;
102 Rx->Flush();
103 while (Rx->GetChar(c) > 0)
104 put_rx_char(c);
105 }
106
send_tones()107 void olivia::send_tones()
108 {
109 if (!progdefaults.olivia_start_tones) return;
110
111 double freqa, freqb;
112 tone_bw = bandwidth;
113 tone_midfreq = txbasefreq;
114
115 if (reverse) {
116 freqa = tone_midfreq + (tone_bw / 2.0);
117 freqb = tone_midfreq - (tone_bw / 2.0);
118 } else {
119 freqa = tone_midfreq - (tone_bw / 2.0);
120 freqb = tone_midfreq + (tone_bw / 2.0);
121 }
122
123 preamblephase = 0;
124 for (int i = 0; i < SR4; i++)
125 tonebuff[2*SR4 + i] = tonebuff[i] = nco(freqa) * ampshape[i];
126
127 preamblephase = 0;
128 for (int i = 0; i < SR4; i++)
129 tonebuff[3*SR4 + i] = tonebuff[SR4 + i] = nco(freqb) * ampshape[i];
130
131 for (int j = 0; j < TONE_DURATION; j += SCBLOCKSIZE)
132 ModulateXmtr(&tonebuff[j], SCBLOCKSIZE);
133
134 }
135
rx_init()136 void olivia::rx_init()
137 {
138 guard_lock dsp_lock(&olivia_mutex);
139
140 Rx->Reset();
141 escape = 0;
142 }
143
unescape(int c)144 int olivia::unescape(int c)
145 {
146 if (progdefaults.olivia8bit == 0)
147 return c;
148
149 if (escape) {
150 escape = 0;
151 return c + 128;
152 }
153
154 if (c == 127) {
155 escape = 1;
156 return -1;
157 }
158
159 return c;
160 }
161
tx_process()162 int olivia::tx_process()
163 {
164 modem::tx_process();
165
166 int c = 0, len = 0;
167
168 if ((mode == MODE_OLIVIA &&
169 (tones != progdefaults.oliviatones ||
170 bw != progdefaults.oliviabw)) ||
171 smargin != progdefaults.oliviasmargin ||
172 sinteg != progdefaults.oliviasinteg )
173 restart();
174
175 { // critical section
176 guard_lock dsp_lock(&olivia_mutex);
177
178 if (preamblesent != 1) {
179 send_tones();
180 preamblesent = 1;
181 // Olivia Transmitter class requires at least character
182 Tx->PutChar(0);
183 }
184
185 // The encoder works with BitsPerSymbol length blocks. If the
186 // modem already has that many characters buffered, don't try
187 // to read any more. If stopflag is set, we will always read
188 // whatever there is.
189 if (stopflag || (Tx->GetReadReady() < Tx->BitsPerSymbol)) {
190 if (!stopflag && (c = get_tx_char()) == GET_TX_CHAR_ETX)
191 stopflag = true;
192 if (stopflag)
193 Tx->Stop();
194 else {
195 if (c == GET_TX_CHAR_NODATA)
196 c = 0;
197 if (c > 127) {
198 if (progdefaults.olivia8bit && c <= 255) {
199 Tx->PutChar(127);
200 Tx->PutChar(c & 127);
201 }
202 else {
203 c = '.';
204 Tx->PutChar(c);
205 }
206 }
207 else
208 Tx->PutChar(c);
209 }
210 }
211
212 if (c > 0)
213 put_echo_char(c);
214
215 if ((len = Tx->Output(txfbuffer)) > 0)
216 ModulateXmtr(txfbuffer, len);
217
218 if (stopflag && Tx->DoPostambleYet() == 1 && postamblesent != 1) {
219 postamblesent = 1;
220 send_tones();
221 memset(tonebuff, 0, sizeof(*tonebuff) * SCBLOCKSIZE);
222 ModulateXmtr(tonebuff, SCBLOCKSIZE);
223 }
224
225 if (!Tx->Running()) {
226 stopflag = false;
227 return -1;
228 }
229 } // end critical section
230 return 0;
231 }
232
233
rx_process(const double * buf,int len)234 int olivia::rx_process(const double *buf, int len)
235 {
236 int c;
237 unsigned char ch = 0;
238 static double snr = 1e-3;
239 static char msg1[20];
240 static char msg2[20];
241 double rxf_offset = 0;
242 double rx_bw = 0;
243 double rx_tones = 0;
244 double rx_snr = 0;
245 int fc_offset = 0;
246 bool gotchar = false;
247
248 if ((mode == MODE_OLIVIA &&
249 (tones != progdefaults.oliviatones ||
250 bw != progdefaults.oliviabw)) ||
251 smargin != progdefaults.oliviasmargin ||
252 sinteg != progdefaults.oliviasinteg )
253 restart();
254
255 { // critical section
256 guard_lock dsp_lock(&olivia_mutex);
257
258 fc_offset = Tx->Bandwidth*(1.0 - 0.5/Tx->Tones)/2.0;
259
260 if ((lastfreq != frequency || Rx->Reverse) && !reverse) {
261 Rx->FirstCarrierMultiplier = (frequency - fc_offset)/500.0;
262 Rx->Reverse = 0;
263 lastfreq = frequency;
264 Rx->Preset();
265 }
266 else if ((lastfreq != frequency || !Rx->Reverse) && reverse) {
267 Rx->FirstCarrierMultiplier = (frequency + fc_offset)/500.0;
268 Rx->Reverse = 1;
269 lastfreq = frequency;
270 Rx->Preset();
271 }
272
273 Rx->SyncThreshold = progStatus.sqlonoff ?
274 clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 0, 90.0) : 0.0;
275
276 Rx->Process(buf, len);
277
278 while (Rx->GetChar(ch) > 0) {
279 if ((c = unescape(ch)) != -1 && c > 7) {
280 put_rx_char(c);
281 gotchar = true;
282 }
283 }
284
285 rxf_offset = Rx->FrequencyOffset();
286 rx_bw = Rx->Bandwidth;
287 rx_tones = Rx->Tones;
288 rx_snr = Rx->SignalToNoiseRatio();
289 } // end critical section
290
291 sp = 0;
292 for (int i = frequency - fc_offset; i < frequency + fc_offset; i++)
293 if (wf->Pwr(i) > sp)
294 sp = wf->Pwr(i);
295
296 np = wf->Pwr(static_cast<int>(frequency + rx_bw/2 + 2*rx_bw/rx_tones));
297
298 if (np == 0) np = sp + 1e-8;
299
300 sigpwr = decayavg( sigpwr, sp, 10);
301 noisepwr = decayavg( noisepwr, np, 50);
302 snr = CLAMP(sigpwr / noisepwr, 0.001, 100000);
303
304 metric = clamp( 5.0 * (rx_snr - 3.0), 0, 100);
305 display_metric(metric);
306
307 if (gotchar) {
308 snprintf(msg1, sizeof(msg1), "s/n: %4.1f dB", 10*log10(snr) - 20);
309 put_Status1(msg1, 5, STATUS_CLEAR);
310 snprintf(msg2, sizeof(msg2), "f/o %+4.1f Hz", rxf_offset);
311 put_Status2(msg2, 5, STATUS_CLEAR);
312 }
313
314 return 0;
315 }
316
restart()317 void olivia::restart()
318 {
319 if (mode == MODE_OLIVIA) {
320 tones = progdefaults.oliviatones;
321 bw = progdefaults.oliviabw;
322 }
323 smargin = progdefaults.oliviasmargin;
324 sinteg = progdefaults.oliviasinteg;
325
326 samplerate = 8000;
327 bandwidth = 125 * (1 << bw);
328
329 Tx->Tones = 2 * (1 << tones);
330 Tx->Bandwidth = bandwidth;
331 Tx->SampleRate = samplerate;
332 Tx->OutputSampleRate = samplerate;
333 txbasefreq = get_txfreq_woffset();
334
335 { // critical section
336 guard_lock dsp_lock(&olivia_mutex);
337
338 int fc_offset = Tx->Bandwidth * (1.0 - 0.5/Tx->Tones) / 2.0;
339 if (reverse) {
340 Tx->FirstCarrierMultiplier = (txbasefreq + fc_offset)/500.0;
341 Tx->Reverse = 1;
342 } else {
343 Tx->FirstCarrierMultiplier = (txbasefreq - fc_offset)/500.0;
344 Tx->Reverse = 0;
345 }
346
347 if (Tx->Preset() < 0) {
348 LOG_ERROR("olivia: transmitter preset failed!");
349 return;
350 }
351
352 txbufferlen = Tx->MaxOutputLen;
353
354 if (txfbuffer) delete [] txfbuffer;
355 txfbuffer = new double[txbufferlen];
356
357 Rx->Tones = Tx->Tones;
358 Rx->Bandwidth = bandwidth;
359 Rx->SyncMargin = smargin;
360 Rx->SyncIntegLen = sinteg;
361 Rx->SyncThreshold = progStatus.sqlonoff ?
362 clamp(progStatus.sldrSquelchValue / 5.0 + 3.0, 0, 90.0) : 0.0;
363
364 Rx->SampleRate = samplerate;
365 Rx->InputSampleRate = samplerate;
366
367 fc_offset = Rx->Bandwidth * (1.0 - 0.5/Rx->Tones) / 2.0;
368 if (reverse) {
369 Rx->FirstCarrierMultiplier = (frequency + fc_offset)/500.0;
370 Rx->Reverse = 1;
371 } else {
372 Rx->FirstCarrierMultiplier = (frequency - fc_offset)/500.0;
373 Rx->Reverse = 0;
374 }
375
376 if (Rx->Preset() < 0) {
377 LOG_ERROR("olivia: receiver preset failed!");
378 return;
379 }
380 fragmentsize = 1024;
381 set_bandwidth(Tx->Bandwidth - Tx->Bandwidth / Tx->Tones);
382
383 stringstream info;
384 info << mode_info[mode].sname;
385 put_MODEstatus("%s", info.str().c_str());
386
387 metric = 0;
388
389 sigpwr = 1e-10; noisepwr = 1e-8;
390 LOG_DEBUG("\nOlivia Rx parameters:\n%s", Rx->PrintParameters());
391 } // end critical section
392 }
393
init()394 void olivia::init()
395 {
396 restart();
397 modem::init();
398 set_scope_mode(Digiscope::BLANK);
399
400 if (progdefaults.StartAtSweetSpot)
401 set_freq(progdefaults.PSKsweetspot);
402 else if (progStatus.carrier != 0) {
403 set_freq(progStatus.carrier);
404 #if !BENCHMARK_MODE
405 progStatus.carrier = 0;
406 #endif
407 } else
408 set_freq(wf->Carrier());
409
410 }
411
olivia(trx_mode omode)412 olivia::olivia(trx_mode omode)
413 {
414 mode = omode;
415 cap |= CAP_REV;
416
417 txfbuffer = 0;
418 samplerate = 8000;
419
420 switch (mode) {
421 case MODE_OLIVIA_4_125:
422 progdefaults.oliviatones = tones = 1;
423 progdefaults.oliviabw = bw = 0;
424 REQ(set_olivia_tab_widgets);
425 break;
426 case MODE_OLIVIA_4_250:
427 progdefaults.oliviatones = tones = 1;
428 progdefaults.oliviabw = bw = 1;
429 REQ(set_olivia_tab_widgets);
430 break;
431 case MODE_OLIVIA_4_500:
432 progdefaults.oliviatones = tones = 1;
433 progdefaults.oliviabw = bw = 2;
434 REQ(set_olivia_tab_widgets);
435 break;
436 case MODE_OLIVIA_4_1000:
437 progdefaults.oliviatones = tones = 1;
438 progdefaults.oliviabw = bw = 3;
439 REQ(set_olivia_tab_widgets);
440 break;
441 case MODE_OLIVIA_4_2000:
442 progdefaults.oliviatones = tones = 1;
443 progdefaults.oliviabw = bw = 4;
444 REQ(set_olivia_tab_widgets);
445 break;
446 case MODE_OLIVIA_8_125:
447 progdefaults.oliviatones = tones = 2;
448 progdefaults.oliviabw = bw = 0;
449 REQ(set_olivia_tab_widgets);
450 break;
451 case MODE_OLIVIA_8_250:
452 progdefaults.oliviatones = tones = 2;
453 progdefaults.oliviabw = bw = 1;
454 REQ(set_olivia_tab_widgets);
455 break;
456 case MODE_OLIVIA_8_500:
457 progdefaults.oliviatones = tones = 2;
458 progdefaults.oliviabw = bw = 2;
459 REQ(set_olivia_tab_widgets);
460 break;
461 case MODE_OLIVIA_8_1000:
462 progdefaults.oliviatones = tones = 2;
463 progdefaults.oliviabw = bw = 3;
464 REQ(set_olivia_tab_widgets);
465 break;
466 case MODE_OLIVIA_8_2000:
467 progdefaults.oliviatones = tones = 2;
468 progdefaults.oliviabw = bw = 4;
469 REQ(set_olivia_tab_widgets);
470 break;
471 case MODE_OLIVIA_16_500:
472 progdefaults.oliviatones = tones = 3;
473 progdefaults.oliviabw = bw = 2;
474 REQ(set_olivia_tab_widgets);
475 break;
476 case MODE_OLIVIA_16_1000:
477 progdefaults.oliviatones = tones = 3;
478 progdefaults.oliviabw = bw = 3;
479 REQ(set_olivia_tab_widgets);
480 break;
481 case MODE_OLIVIA_16_2000:
482 progdefaults.oliviatones = tones = 3;
483 progdefaults.oliviabw = bw = 4;
484 REQ(set_olivia_tab_widgets);
485 break;
486 case MODE_OLIVIA_32_1000:
487 progdefaults.oliviatones = tones = 4;
488 progdefaults.oliviabw = bw = 3;
489 REQ(set_olivia_tab_widgets);
490 break;
491 case MODE_OLIVIA_32_2000:
492 progdefaults.oliviatones = tones = 4;
493 progdefaults.oliviabw = bw = 4;
494 REQ(set_olivia_tab_widgets);
495 break;
496 case MODE_OLIVIA_64_500:
497 progdefaults.oliviatones = tones = 5;
498 progdefaults.oliviabw = bw = 2;
499 REQ(set_olivia_tab_widgets);
500 break;
501 case MODE_OLIVIA_64_1000:
502 progdefaults.oliviatones = tones = 5;
503 progdefaults.oliviabw = bw = 3;
504 REQ(set_olivia_tab_widgets);
505 break;
506 case MODE_OLIVIA_64_2000:
507 progdefaults.oliviatones = tones = 5;
508 progdefaults.oliviabw = bw = 4;
509 REQ(set_olivia_tab_widgets);
510 break;
511 case MODE_OLIVIA:
512 default:
513 tones = progdefaults.oliviatones;
514 bw = progdefaults.oliviabw;
515 REQ(set_olivia_tab_widgets);
516 break;
517 }
518
519 Tx = new MFSK_Transmitter< double >;
520 Rx = new MFSK_Receiver< double >;
521
522 lastfreq = 0;
523
524 for (int i = 0; i < SR4; i++) ampshape[i] = 1.0;
525 for (int i = 0; i < SR4 / 8; i++)
526 ampshape[i] = ampshape[SR4 - 1 - i] = 0.5 * (1.0 - cos(M_PI * i / (SR4/8)));
527
528 for (int i = 0; i < TONE_DURATION; i++) tonebuff[i] = 0;
529
530 tone_bw = -1;
531 tone_midfreq = -1;
532 }
533
~olivia()534 olivia::~olivia()
535 {
536 guard_lock dsp_lock(&olivia_mutex);
537
538 if (Tx) delete Tx;
539 if (Rx) delete Rx;
540 if (txfbuffer) delete [] txfbuffer;
541 }
542
543