1 // ----------------------------------------------------------------------------
2 // modem.cxx - modem class - base for all modems
3 //
4 // Copyright (C) 2006-2010
5 //		Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22 
23 #include <config.h>
24 
25 #include <string>
26 
27 #include "misc.h"
28 #include "filters.h"
29 
30 #include "confdialog.h"
31 #include "modem.h"
32 #include "trx.h"
33 #include "fl_digi.h"
34 #include "main.h"
35 #include "arq_io.h"
36 #include "configuration.h"
37 #include "waterfall.h"
38 #include "qrunner.h"
39 #include "macros.h"
40 
41 #include "testsigs.h"
42 #include "test_signal.h"
43 
44 #include "status.h"
45 #include "debug.h"
46 #include "audio_alert.h"
47 
48 using namespace std;
49 
50 modem *null_modem = 0;
51 modem *cw_modem = 0;
52 
53 modem *mfsk8_modem = 0;
54 modem *mfsk16_modem = 0;
55 modem *mfsk32_modem = 0;
56 modem *mfsk4_modem = 0;
57 modem *mfsk11_modem = 0;
58 modem *mfsk22_modem = 0;
59 modem *mfsk31_modem = 0;
60 modem *mfsk64_modem = 0;
61 modem *mfsk128_modem = 0;
62 modem *mfsk64l_modem = 0;
63 modem *mfsk128l_modem = 0;
64 
65 modem *wefax576_modem = 0;
66 modem *wefax288_modem = 0;
67 
68 modem *navtex_modem = 0;
69 modem *sitorb_modem = 0;
70 
71 modem *mt63_500S_modem = 0;
72 modem *mt63_500L_modem = 0;
73 modem *mt63_1000S_modem = 0;
74 modem *mt63_1000L_modem = 0;
75 modem *mt63_2000S_modem = 0;
76 modem *mt63_2000L_modem = 0;
77 
78 modem *feld_modem = 0;
79 modem *feld_slowmodem = 0;
80 modem *feld_x5modem = 0;
81 modem *feld_x9modem = 0;
82 modem *feld_FMmodem = 0;
83 modem *feld_FM105modem = 0;
84 modem *feld_80modem = 0;
85 modem *feld_CMTmodem = 0;
86 
87 modem *psk31_modem = 0;
88 modem *psk63_modem = 0;
89 modem *psk63f_modem = 0;
90 modem *psk125_modem = 0;
91 modem *psk250_modem = 0;
92 modem *psk500_modem = 0;
93 modem *psk1000_modem = 0;
94 
95 modem *qpsk31_modem = 0;
96 modem *qpsk63_modem = 0;
97 modem *qpsk125_modem = 0;
98 modem *qpsk250_modem = 0;
99 modem *qpsk500_modem = 0;
100 
101 modem *_8psk125_modem = 0;
102 modem *_8psk250_modem = 0;
103 modem *_8psk500_modem = 0;
104 modem *_8psk1000_modem = 0;
105 modem *_8psk1200_modem = 0;
106 
107 modem *_8psk125fl_modem = 0;
108 modem *_8psk125f_modem = 0;
109 modem *_8psk250fl_modem = 0;
110 modem *_8psk250f_modem = 0;
111 modem *_8psk500f_modem = 0;
112 modem *_8psk1000f_modem = 0;
113 modem *_8psk1200f_modem = 0;
114 
115 modem *ofdm_500f_modem = 0;
116 modem *ofdm_750f_modem = 0;
117 modem *ofdm_2000f_modem = 0;
118 modem *ofdm_2000_modem = 0;
119 modem *ofdm_3500_modem = 0;
120 
121 modem *psk125r_modem = 0;
122 modem *psk250r_modem = 0;
123 modem *psk500r_modem = 0;
124 modem *psk1000r_modem = 0;
125 
126 modem *psk800_c2_modem = 0;
127 modem *psk800r_c2_modem = 0;
128 modem *psk1000_c2_modem = 0;
129 modem *psk1000r_c2_modem = 0;
130 
131 modem *psk63r_c4_modem = 0;
132 modem *psk63r_c5_modem = 0;
133 modem *psk63r_c10_modem = 0;
134 modem *psk63r_c20_modem = 0;
135 modem *psk63r_c32_modem = 0;
136 
137 modem *psk125r_c4_modem = 0;
138 modem *psk125r_c5_modem = 0;
139 modem *psk125r_c10_modem = 0;
140 modem *psk125_c12_modem = 0;
141 modem *psk125r_c12_modem = 0;
142 modem *psk125r_c16_modem = 0;
143 
144 modem *psk250r_c2_modem = 0;
145 modem *psk250r_c3_modem = 0;
146 modem *psk250r_c5_modem = 0;
147 modem *psk250_c6_modem = 0;
148 modem *psk250r_c6_modem = 0;
149 modem *psk250r_c7_modem = 0;
150 
151 modem *psk500_c2_modem = 0;
152 modem *psk500_c4_modem = 0;
153 
154 modem *psk500r_c2_modem = 0;
155 modem *psk500r_c3_modem = 0;
156 modem *psk500r_c4_modem = 0;
157 
158 modem *olivia_modem = 0;
159 
160 modem *olivia_4_125_modem = 0;
161 modem *olivia_4_250_modem = 0;
162 modem *olivia_4_500_modem = 0;
163 modem *olivia_4_1000_modem = 0;
164 modem *olivia_4_2000_modem = 0;
165 
166 modem *olivia_8_125_modem = 0;
167 modem *olivia_8_250_modem = 0;
168 modem *olivia_8_500_modem = 0;
169 modem *olivia_8_1000_modem = 0;
170 modem *olivia_8_2000_modem = 0;
171 
172 modem *olivia_16_500_modem = 0;
173 modem *olivia_16_1000_modem = 0;
174 modem *olivia_16_2000_modem = 0;
175 
176 modem *olivia_32_1000_modem = 0;
177 modem *olivia_32_2000_modem = 0;
178 
179 modem *olivia_64_500_modem = 0;
180 modem *olivia_64_1000_modem = 0;
181 modem *olivia_64_2000_modem = 0;
182 
183 modem *contestia_modem = 0;
184 
185 modem *contestia_4_125_modem = 0;
186 modem *contestia_4_250_modem = 0;
187 modem *contestia_4_500_modem = 0;
188 modem *contestia_4_1000_modem = 0;
189 modem *contestia_4_2000_modem = 0;
190 
191 modem *contestia_8_125_modem = 0;
192 modem *contestia_8_250_modem = 0;
193 modem *contestia_8_500_modem = 0;
194 modem *contestia_8_1000_modem = 0;
195 modem *contestia_8_2000_modem = 0;
196 
197 modem *contestia_16_250_modem = 0;
198 modem *contestia_16_500_modem = 0;
199 modem *contestia_16_1000_modem = 0;
200 modem *contestia_16_2000_modem = 0;
201 
202 modem *contestia_32_1000_modem = 0;
203 modem *contestia_32_2000_modem = 0;
204 
205 modem *contestia_64_500_modem = 0;
206 modem *contestia_64_1000_modem = 0;
207 modem *contestia_64_2000_modem = 0;
208 
209 modem *rtty_modem = 0;
210 modem *pkt_modem = 0;
211 
212 modem *thormicro_modem = 0;
213 modem *thor4_modem = 0;
214 modem *thor5_modem = 0;
215 modem *thor8_modem = 0;
216 modem *thor11_modem = 0;
217 modem *thor16_modem = 0;
218 modem *thor22_modem = 0;
219 modem *thor25x4_modem = 0;
220 modem *thor50x1_modem = 0;
221 modem *thor50x2_modem = 0;
222 modem *thor100_modem = 0;
223 
224 modem *fsq_modem = 0;
225 
226 modem *ifkp_modem = 0;
227 
228 modem *dominoexmicro_modem = 0;
229 modem *dominoex4_modem = 0;
230 modem *dominoex5_modem = 0;
231 modem *dominoex8_modem = 0;
232 modem *dominoex11_modem = 0;
233 modem *dominoex16_modem = 0;
234 modem *dominoex22_modem = 0;
235 modem *dominoex44_modem = 0;
236 modem *dominoex88_modem = 0;
237 
238 modem *throb1_modem = 0;
239 modem *throb2_modem = 0;
240 modem *throb4_modem = 0;
241 modem *throbx1_modem = 0;
242 modem *throbx2_modem = 0;
243 modem *throbx4_modem = 0;
244 
245 modem *wwv_modem = 0;
246 modem *anal_modem = 0;
247 modem *fmt_modem = 0;
248 modem *ssb_modem = 0;
249 
250 double modem::frequency = 1000;
251 double modem::tx_frequency = 1000;
252 bool   modem::freqlock = false;
253 
254 // For xml socket command
255 unsigned long modem::tx_sample_count = 0;
256 unsigned int  modem::tx_sample_rate  = 0;
257 bool modem::XMLRPC_CPS_TEST = false;
258 
modem()259 modem::modem()
260 {
261 	scptr = 0;
262 
263 	if (wf)
264 		frequency = tx_frequency = wf->Carrier();
265 	else
266 		frequency = tx_frequency = 1000;
267 
268 	sigsearch = 0;
269 	if (wf) {
270 		bool wfrev = wf->Reverse();
271 		bool wfsb = wf->USB();
272 		reverse = wfrev ^ !wfsb;
273 	} else
274 		reverse = false;
275 	historyON = false;
276 	cap = CAP_RX | CAP_TX;
277 	PTTphaseacc = 0.0;
278 	s2n_ncount = s2n_sum = s2n_sum2 = s2n_metric = 0.0;
279 	s2n_valid = false;
280 
281 	bandwidth = 0.0;
282 
283 	for (int i=0; i<QUALITYDEPTH; i++)
284 		quality[i] = 0;
285 
286 	CW_EOT = false;
287 	sig_start = sig_stop = false;
288 }
289 
290 // modem types CW and RTTY do not use the base init()
init()291 void modem::init()
292 {
293 	stopflag = false;
294 	if (!wf) return;
295 
296 	bool wfrev = wf->Reverse();
297 	bool wfsb = wf->USB();
298 	reverse = wfrev ^ !wfsb;
299 
300 }
301 
set_freq(double freq)302 void modem::set_freq(double freq)
303 {
304 	frequency = CLAMP(
305 		freq,
306 		progdefaults.LowFreqCutoff + bandwidth / 2,
307 		progdefaults.HighFreqCutoff - bandwidth / 2);
308 
309 	if (freqlock == false)
310 		tx_frequency = frequency;
311 
312 	if (progdefaults.RxFilt_track_wf)
313 		center_rxfilt_at_track();
314 
315 	REQ(put_freq, frequency);
316 }
317 
set_freqlock(bool on)318 void modem::set_freqlock(bool on)
319 {
320 	freqlock = on;
321 	set_freq(frequency);
322 }
323 
324 
get_txfreq(void) const325 double modem::get_txfreq(void) const
326 {
327 	if (unlikely(!(cap & CAP_TX)))
328 		return 0;
329 	else if (mailserver && progdefaults.PSKmailSweetSpot)
330 		return progdefaults.PSKsweetspot;
331 	if (get_mode() == MODE_FSQ) return 1500;
332 	return tx_frequency;
333 }
334 
get_txfreq_woffset(void) const335 double modem::get_txfreq_woffset(void) const
336 {
337 	if (mailserver && progdefaults.PSKmailSweetSpot)
338 		return (progdefaults.PSKsweetspot - progdefaults.TxOffset);
339 	if (get_mode() == MODE_FSQ) return (1500 - progdefaults.TxOffset);
340 
341 	if (test_signal_window && test_signal_window->visible() && btnOffsetOn->value())
342 		return tx_frequency + ctrl_freq_offset->value();
343 
344 	return (tx_frequency - progdefaults.TxOffset);
345 }
346 
set_bandwidth(double bw)347 void modem::set_bandwidth(double bw)
348 {
349 	bandwidth = bw;
350 	put_Bandwidth((int)bandwidth);
351 }
352 
set_reverse(bool on)353 void modem::set_reverse(bool on)
354 {
355 	if (likely(wf))
356 		reverse = on ^ (!wf->USB());
357 	else
358 		reverse = false;
359 }
360 
set_metric(double m)361 void modem::set_metric(double m)
362 {
363 	metric = m;
364 }
365 
366 extern void callback_set_metric(double metric);
367 
display_metric(double m)368 void modem::display_metric(double m)
369 {
370 	set_metric(m);
371 	if (!progStatus.kpsql_enabled)
372 		REQ(callback_set_metric, m);
373 }
374 
get_cwTrack()375 bool modem::get_cwTrack()
376 {
377 	return cwTrack;
378 }
379 
set_cwTrack(bool b)380 void modem::set_cwTrack(bool b)
381 {
382 	cwTrack = b;
383 }
384 
get_cwLock()385 bool modem::get_cwLock()
386 {
387 	return cwLock;
388 }
389 
set_cwLock(bool b)390 void modem::set_cwLock(bool b)
391 {
392 	cwLock = b;
393 }
394 
get_cwRcvWPM()395 double modem::get_cwRcvWPM()
396 {
397 	return cwRcvWPM;
398 }
399 
get_cwXmtWPM()400 double modem::get_cwXmtWPM()
401 {
402 	return cwXmtWPM;
403 }
404 
set_cwXmtWPM(double wpm)405 void modem::set_cwXmtWPM(double wpm)
406 {
407 	cwXmtWPM = wpm;
408 }
409 
set_samplerate(int smprate)410 void modem::set_samplerate(int smprate)
411 {
412 	samplerate = smprate;
413 }
414 
PTTnco()415 double modem::PTTnco()
416 {
417 	double amp;
418 // sine wave PTT signal
419 	amp = 0.9 * sin(PTTphaseacc);
420 
421 // square wave PTT signal
422 //	if (PTTphaseacc > M_PI)
423 //		amp = - 0.5;
424 //	else
425 //		amp = 0.5;
426 
427 	PTTphaseacc += TWOPI * progdefaults.QSKfrequency / samplerate;
428 	if (PTTphaseacc > TWOPI) PTTphaseacc -= TWOPI;
429 
430 	return amp;
431 }
432 
sigmaN(double es_ovr_n0)433 double modem::sigmaN (double es_ovr_n0)
434 {
435 	double sn_ratio, sigma;
436 	double mode_factor = 0.707;
437 	switch (mode) {
438 	case MODE_CW:
439 		mode_factor /= 0.44;
440 		break;
441 	case MODE_FELDHELL: case MODE_SLOWHELL:
442 	case MODE_HELLX5: case MODE_HELLX9:
443 		mode_factor /= 0.22;
444 		break;
445 	case MODE_MT63_500S: case MODE_MT63_1000S: case MODE_MT63_2000S :
446 	case MODE_MT63_500L: case MODE_MT63_1000L: case MODE_MT63_2000L :
447 		mode_factor *= 3.0;
448 		break;
449 	case MODE_PSK31: case MODE_PSK63: case MODE_PSK63F:
450 	case MODE_PSK125: case MODE_PSK250: case MODE_PSK500:
451 	case MODE_QPSK31: case MODE_QPSK63: case MODE_QPSK125: case MODE_QPSK250:
452 	case MODE_PSK125R: case MODE_PSK250R: case MODE_PSK500R:
453 		mode_factor = 400;
454 		break;
455 	case MODE_THROB1: case MODE_THROB2: case MODE_THROB4:
456 	case MODE_THROBX1: case MODE_THROBX2: case MODE_THROBX4:
457 		mode_factor *= 6.0;
458 		break;
459 //	case MODE_RTTY:
460 //	case MODE_OLIVIA:
461 //	case MODE_DOMINOEX4: case MODE_DOMINOEX5: case MODE_DOMINOEX8:
462 //	case MODE_DOMINOEX11: case MODE_DOMINOEX16: case MODE_DOMINOEX22:
463 //	case MODE_MFSK4: case MODE_MFSK11: case MODE_MFSK22: case MODE_MFSK31:
464 //	case MODE_MFSK64: case MODE_MFSK8: case MODE_MFSK16: case MODE_MFSK32:
465 //	case MODE_THOR4: case MODE_THOR5: case MODE_THOR8:
466 //	case MODE_THOR11:case MODE_THOR16: case MODE_THOR22:
467 //	case MODE_FSKH245: case MODE_FSKH105: case MODE_HELL80:
468 	default: break;
469 	}
470 	if (trx_state == STATE_TUNE) mode_factor = 0.707;
471 
472 	sn_ratio = pow(10, ( es_ovr_n0 / 10) );
473 	sigma =  sqrt ( mode_factor / sn_ratio );
474 	return sigma;
475 }
476 
477 // A Rayleigh-distributed random variable R, with the probability
478 // distribution
479 //	F(R) = 0 where R < 0 and
480 //	F(R) = 1 - exp(-R^2/2*sigma^2) where R >= 0,
481 // is related to a pair of Gaussian variables C and D
482 // through the transformation
483 //	C = R * cos(theta) and
484 //	D = R * sin(theta),
485 // where theta is a uniformly distributed variable in the interval
486 // 0 to 2 * Pi.
487 
gauss(double sigma)488 double modem::gauss(double sigma) {
489 	double u, r;
490 	u = 1.0 * rand() / RAND_MAX;
491 	r = sigma * sqrt( 2.0 * log( 1.0 / (1.0 - u) ) );
492 	u = 1.0 * rand() / RAND_MAX;
493 	return r * cos(2 * M_PI * u);
494 }
495 
496 // given the desired Es/No, calculate the standard deviation of the
497 // additive white gaussian noise (AWGN). The standard deviation of
498 // the AWGN will be used to generate Gaussian random variables
499 // simulating the noise that is added to the signal.
500 // return signal + noise, limiting value to +/- 1.0
501 
add_noise(double * buffer,int len)502 void modem::add_noise(double *buffer, int len)
503 {
504 	double sigma = sigmaN(noiseDB->value());
505 	double sn = 0;
506 
507 	for (int n = 0; n < len; n++) {
508 		if (btnNoiseOn->value()) {
509 			sn = (buffer[n] + gauss(sigma)) / (1.0 + 3.0 * sigma);
510 			buffer[n] = clamp(sn, -1.0, 1.0);
511 		}
512 	}
513 }
514 
s2nreport(void)515 void modem::s2nreport(void)
516 {
517 	double s2n_avg = s2n_sum / s2n_ncount;
518 	double s2n_stddev = sqrt((s2n_sum2 / s2n_ncount) - (s2n_avg * s2n_avg));
519 
520 	pskmail_notify_s2n(s2n_ncount, s2n_avg, s2n_stddev);
521 }
522 
523 // Averages a given [0-100] integer value over the last QUALITYDEPTH calls.
524 // By default, returns ONLY values of:  100, 90, 75, 50, 25, 10, & 0
525 // Prevents erratic / jumpy / unreadable display in GUI
get_quality(int mode)526 int modem::get_quality(int mode)
527 {
528 	/*  Quality value meanings:
529 	 * 100% - zero errors, perfect FEC metrics
530 	 *  90% - zero errors, near-perfect FEC metrics
531 	 *  75% - zero errors, medium FEC metrics
532 	 *  50% - very-rare errors, weak FEC metrics
533 	 *  25% - some errors
534 	 *  10% - half errors
535 	 *   0% - mostly errors
536 	 */
537 
538 	// Average the quality[] array.
539 	int average = 0;
540 	for (int i=0; i<QUALITYDEPTH; i++) {
541 		average += quality[i];
542 	}
543 	average /= QUALITYDEPTH;
544 
545 	// By default this function returns only quantized values
546 	// If the second (optional) parameter to this function is anything other than 0, return the actual value.
547 	if (mode != 0)
548 		return average;
549 
550 	// implied else: Scale phase-quality indicatior to quantized values
551 	if (average > 90) average = 100;
552 	else if (average > 80) average = 90;
553 	else if (average > 60) average = 75;
554 	else if (average > 40) average = 50;
555 	else if (average > 20) average = 25;
556 	else if (average > 9) average = 10;
557 	else average = 0; // Else just round to 0.
558 
559 	return average;
560 }
561 
562 
update_quality(int value,int mode)563 int modem::update_quality(int value, int mode)
564 {
565 	static int index=0;
566 
567 	// Check the passed value, clamp if out of bounds
568 	if (value > 100)
569 		value = 100;
570 	else if (value < 0)
571 		value = 0;
572 
573 	// Save the latest passed value to buffer for averaging
574 	quality[index++] = value;
575 	if ( QUALITYDEPTH == index) index = 0;
576 
577 	return get_quality(mode);
578 }
579 
580 
581 bool disable_modem = false;
582 #define SIGLIMIT 0.95
583 
ModulateXmtr(double * buffer,int len)584 void modem::ModulateXmtr(double *buffer, int len)
585 {
586 	if (unlikely(!TXscard)) return;
587 	if (disable_modem) return;
588 
589 	tx_sample_count += len;
590 
591 	if (sig_start) {
592 		int num = len / 2;
593 		for (int i = 0; i < num; i++)
594 			buffer[i] *= (0.5 * (1.0 - cos (M_PI * i / num)));
595 		sig_start = false;
596 	}
597 	if (sig_stop) {
598 		int num = len / 2;
599 		for (int i = 0; i < num; i++)
600 			buffer[len - i - 1] *= (0.5 * (1.0 - cos (M_PI * i / num)));
601 		sig_stop = false;
602 	}
603 
604 	if (progdefaults.PTTrightchannel) {
605 		for (int i = 0; i < len; i++)
606 			PTTchannel[i] = PTTnco();
607 		ModulateStereo( buffer, PTTchannel, len, false);
608 		return;
609 	}
610 
611 	if (test_signal_window && test_signal_window->visible()) add_noise(buffer, len);
612 
613 	if (progdefaults.viewXmtSignal &&
614 		!(PERFORM_CPS_TEST || active_modem->XMLRPC_CPS_TEST))
615 		trx_xmit_wfall_queue(samplerate, buffer, (size_t)len);
616 
617 	double mult = pow(10, progdefaults.txlevel / 20.0);
618 	if (mult > SIGLIMIT) mult = SIGLIMIT;
619 	for (int i = 0; i < len; i++) {
620 		buffer[i] *= mult;
621 		if (buffer[i] < -SIGLIMIT) buffer[i] = -SIGLIMIT;
622 		if (buffer[i] >  SIGLIMIT) buffer[i] = SIGLIMIT;
623 	}
624 
625 	try {
626 		unsigned n = 4;
627 		while (TXscard->Write(buffer, len) == 0 && --n);
628 		if (n == 0)
629 			throw SndException(-99, "Sound write failed");
630 	}
631 	catch (const SndException& e) {
632 		if(e.error() < 0) {
633  			LOG_ERROR("%s", e.what());
634  			throw;
635 		}
636 		return;
637 	}
638 }
639 
640 #include <iostream>
641 using namespace std;
642 
ModulateStereo(double * left,double * right,int len,bool sample_flag)643 void modem::ModulateStereo(double *left, double *right, int len, bool sample_flag)
644 {
645 	if (unlikely(!TXscard)) return;
646 	if (disable_modem) return;
647 
648 	if(sample_flag)
649 		tx_sample_count += len;
650 
651 	if (test_signal_window && test_signal_window->visible() && progdefaults.noise) add_noise(left, len);
652 
653 	if (progdefaults.viewXmtSignal &&
654 		!(PERFORM_CPS_TEST || active_modem->XMLRPC_CPS_TEST))
655 		trx_xmit_wfall_queue(samplerate, left, (size_t)len);
656 
657 	double mult = pow(10, progdefaults.txlevel / 20.0);
658 	if (mult > SIGLIMIT) mult = SIGLIMIT;
659 
660 	for (int i = 0; i < len; i++) {
661 		if (right[i] < -SIGLIMIT) right[i] = -SIGLIMIT;
662 		if (right[i] >  SIGLIMIT) right[i] = SIGLIMIT;
663 		left[i] *= mult;
664 		if (left[i] < -SIGLIMIT) left[i] = -SIGLIMIT;
665 		if (left[i] >  SIGLIMIT) left[i] = SIGLIMIT;
666 	}
667 	try {
668 		unsigned n = 4;
669 		while (TXscard->Write_stereo(left, right, len) == 0 && --n);
670 		if (n == 0)
671 			throw SndException(-99, "Sound write failed");
672 	}
673 	catch (const SndException& e) {
674 		if(e.error() < 0) {
675  			LOG_ERROR("%s", e.what());
676  			throw;
677 		}
678 		return;
679 	}
680 
681 }
682 
683 //------------------------------------------------------------------------------
684 // tx process
685 //------------------------------------------------------------------------------
tx_process()686 int  modem::tx_process ()
687 {
688 	if (!macro_video_text.empty()) {
689 		wfid_text(macro_video_text);
690 		macro_video_text.clear();
691 	}
692 	if (play_audio) {
693 		disable_modem = true;
694 		TXscard->Audio(audio_filename);
695 		play_audio = false;
696 		disable_modem = false;
697 	}
698 	return 0;
699 }
700 
701 //------------------------------------------------------------------------------
702 // modulate video signal
703 //------------------------------------------------------------------------------
ModulateVideoStereo(double * left,double * right,int len,bool sample_flag)704 void modem::ModulateVideoStereo(double *left, double *right, int len, bool sample_flag)
705 {
706 	if (unlikely(!TXscard)) return;
707 
708 	if(sample_flag)
709 		tx_sample_count += len;
710 
711 	if (test_signal_window && test_signal_window->visible() && progdefaults.noise) add_noise(left, len);
712 
713 	if (progdefaults.viewXmtSignal &&
714 		!(PERFORM_CPS_TEST || active_modem->XMLRPC_CPS_TEST))
715 		trx_xmit_wfall_queue(samplerate, left, (size_t)len);
716 
717 	double mult = SIGLIMIT * pow(10, progdefaults.txlevel / 20.0);
718 
719 	for (int i = 0; i < len; i++) {
720 		if (right[i] < -SIGLIMIT) right[i] = -SIGLIMIT;
721 		if (right[i] >  SIGLIMIT) right[i] = SIGLIMIT;
722 		left[i] *= mult;
723 		if (left[i] < -SIGLIMIT) left[i] = -SIGLIMIT;
724 		if (left[i] >  SIGLIMIT) left[i] = SIGLIMIT;
725 	}
726 	try {
727 		unsigned n = 4;
728 		while (TXscard->Write_stereo(left, right, len) == 0 && --n);
729 		if (n == 0)
730 			throw SndException(-99, "Sound write failed");
731 	}
732 	catch (const SndException& e) {
733 		if(e.error() < 0) {
734  			LOG_ERROR("%s", e.what());
735  			throw;
736 		}
737 		return;
738 	}
739 }
740 
ModulateVideo(double * buffer,int len)741 void modem::ModulateVideo(double *buffer, int len)
742 {
743 	if (unlikely(!TXscard)) return;
744 
745 	tx_sample_count += len;
746 
747 	if (progdefaults.PTTrightchannel) {
748 		for (int i = 0; i < len; i++)
749 			PTTchannel[i] = PTTnco();
750 		ModulateVideoStereo( buffer, PTTchannel, len, false);
751 		return;
752 	}
753 
754 	if (test_signal_window && test_signal_window->visible() && progdefaults.noise) add_noise(buffer, len);
755 
756 	if (progdefaults.viewXmtSignal &&
757 		!(PERFORM_CPS_TEST || active_modem->XMLRPC_CPS_TEST))
758 		trx_xmit_wfall_queue(samplerate, buffer, (size_t)len);
759 
760 	double mult = SIGLIMIT * pow(10, progdefaults.txlevel / 20.0);
761 	for (int i = 0; i < len; i++) {
762 		buffer[i] *= mult;
763 		if (buffer[i] < -SIGLIMIT) buffer[i] = -SIGLIMIT;
764 		if (buffer[i] >  SIGLIMIT) buffer[i] = SIGLIMIT;
765 	}
766 
767 	try {
768 		unsigned n = 4;
769 		while (TXscard->Write(buffer, len) == 0 && --n);
770 		if (n == 0)
771 			throw SndException(-99, "Sound write failed");
772 	}
773 	catch (const SndException& e) {
774 		if(e.error() < 0) {
775  			LOG_ERROR("%s", e.what());
776  			throw;
777 		}
778 		return;
779 	}
780 }
781 
782 //------------------------------------------------------------------------------
videoText()783 void modem::videoText()
784 {
785 	if (trx_state == STATE_TUNE)
786 		return;
787 
788 	if (progdefaults.pretone > 0.2)
789 		pretone();
790 
791 	if (progdefaults.sendtextid == true) {
792 		wfid_text(progdefaults.strTextid);
793 	} else if (progdefaults.macrotextid == true) {
794 		wfid_text(progdefaults.strTextid);
795 		progdefaults.macrotextid = false;
796 	}
797 
798 	if (progdefaults.videoid_modes.test(mode) &&
799 		(progdefaults.sendid || progdefaults.macroid)) {
800 		#define TLEN 20
801 		char idtxt[TLEN] = "";
802 		switch(mode_info[mode].mode) {
803 		case MODE_CONTESTIA:
804 			snprintf(idtxt, TLEN, "%s%d/%d", mode_info[mode].vid_name,
805 				2*(1<<progdefaults.contestiatones),
806 				125*(1<<progdefaults.contestiabw));
807 			break;
808 		case MODE_OLIVIA:
809 			snprintf(idtxt, TLEN, "%s%d/%d", mode_info[mode].vid_name,
810 				2*(1<<progdefaults.oliviatones),
811 				125*(1<<progdefaults.oliviabw));
812 			break;
813 		case MODE_RTTY:
814 			snprintf(idtxt, TLEN, "%s%d/%d", mode_info[mode].vid_name,
815 				static_cast<int>(rtty::BAUD[progdefaults.rtty_baud]),
816 				rtty::BITS[progdefaults.rtty_bits]);
817 			break;
818 		case MODE_DOMINOEX4: case MODE_DOMINOEX5: case MODE_DOMINOEX8:
819 		case MODE_DOMINOEX11: case MODE_DOMINOEX16: case MODE_DOMINOEX22:
820 			if (progdefaults.DOMINOEX_FEC)
821 				snprintf(idtxt, TLEN, "%s/FEC", mode_info[mode].vid_name);
822 			else
823 				strcpy(idtxt, mode_info[mode].vid_name);
824 			break;
825 		default:
826 			strcpy(idtxt, mode_info[mode].vid_name);
827 			break;
828 		}
829 		wfid_text(idtxt);
830 		progdefaults.macroid = false;
831 	}
832 }
833 
834 // CW ID transmit routines
835 
836 //===========================================================================
837 // cw transmit routines to send a post amble message
838 // Define the amplitude envelop for key down events
839 // this is 1/2 cycle of a raised cosine
840 //===========================================================================
841 
cwid_makeshape()842 void modem::cwid_makeshape()
843 {
844 	for (int i = 0; i < 128; i++) cwid_keyshape[i] = 1.0;
845 	for (int i = 0; i < RT; i++)
846 		cwid_keyshape[i] = 0.5 * (1.0 - cos (M_PI * i / RT));
847 }
848 
cwid_nco(double freq)849 double modem::cwid_nco(double freq)
850 {
851 	cwid_phaseacc += 2.0 * M_PI * freq / samplerate;
852 
853 	if (cwid_phaseacc > TWOPI) cwid_phaseacc -= TWOPI;
854 
855 	return sin(cwid_phaseacc);
856 }
857 
858 //=====================================================================
859 // cwid_send_symbol()
860 // Sends a part of a morse character (one dot duration) of either
861 // sound at the correct freq or silence. Rise and fall time is controlled
862 // with a raised cosine shape.
863 //
864 // Left channel contains the shaped A2 CW waveform
865 //=======================================================================
866 
cwid_send_symbol(int bits)867 void modem::cwid_send_symbol(int bits)
868 {
869 	double freq;
870 	int i,
871 		keydown,
872 		keyup,
873 		sample = 0,
874 		currsym = bits & 1;
875 
876 	freq = get_txfreq_woffset() - progdefaults.TxOffset;
877 
878 	if ((currsym == 1) && (cwid_lastsym == 0))
879 		cwid_phaseacc = 0.0;
880 
881 	keydown = cwid_symbollen - RT;
882 	keyup = cwid_symbollen - RT;
883 
884 	if (currsym == 1) {
885 		for (i = 0; i < RT; i++, sample++) {
886 			if (cwid_lastsym == 0)
887 				outbuf[sample] = cwid_nco(freq) * cwid_keyshape[i];
888 			else
889 				outbuf[sample] = cwid_nco(freq);
890 		}
891 		for (i = 0; i < keydown; i++, sample++) {
892 			outbuf[sample] = cwid_nco(freq);
893 		}
894 	}
895 	else {
896 		for (i = RT - 1; i >= 0; i--, sample++) {
897 			if (cwid_lastsym == 1) {
898 				outbuf[sample] = cwid_nco(freq) * cwid_keyshape[i];
899 			} else {
900 				outbuf[sample] = 0.0;
901 			}
902 		}
903 		for (i = 0; i < keyup; i++, sample++) {
904 			outbuf[sample] = 0.0;
905 		}
906 	}
907 
908 	ModulateXmtr(outbuf, cwid_symbollen);
909 
910 	cwid_lastsym = currsym;
911 }
912 
913 //=====================================================================
914 // send_ch()
915 // sends a morse character and the space afterwards
916 //=======================================================================
917 
cwid_send_ch(int ch)918 void modem::cwid_send_ch(int ch)
919 {
920 	std::string code;
921 
922 // handle word space separately (7 dots spacing)
923 // last char already had 2 elements of inter-character spacing
924 
925 	if ((ch == ' ') || (ch == '\n')) {
926 		cwid_send_symbol(0);
927 		cwid_send_symbol(0);
928 		cwid_send_symbol(0);
929 		cwid_send_symbol(0);
930 		cwid_send_symbol(0);
931 		put_echo_char(ch);
932 		return;
933 	}
934 
935 // convert character code to a morse representation
936 	code = morse.tx_lookup(ch); //cw_tx_lookup(ch);
937 	if (!code.length())
938 		return;
939 // loop sending out binary bits of cw character
940 	for (size_t n = 0; n < code.length(); n++) {
941 		cwid_send_symbol(0);
942 		cwid_send_symbol(1);
943 		if (code[n] == '-') {
944 			cwid_send_symbol(1);
945 			cwid_send_symbol(1);
946 		}
947 	}
948 	cwid_send_symbol(0);
949 	cwid_send_symbol(0);
950 
951 }
952 
cwid_sendtext(const string & s)953 void modem::cwid_sendtext (const string& s)
954 {
955 	cwid_symbollen = (int)(1.2 * samplerate / progdefaults.CWIDwpm);
956 	RT = (int) (samplerate * 6 / 1000.0); // 6 msec risetime for CW pulse
957 	cwid_makeshape();
958 	cwid_lastsym = 0;
959 	for (unsigned int i = 0; i < s.length(); i++) {
960 		cwid_send_ch(s[i]);
961 	}
962 }
963 
cwid()964 void modem::cwid()
965 {
966 	if ((CW_EOT && progdefaults.cwid_modes.test(mode) &&
967 		progdefaults.CWid == true) || progdefaults.macroCWid == true) {
968 		string tosend = " DE ";
969 		tosend += progdefaults.myCall;
970 		cwid_sendtext(tosend);
971 		progdefaults.macroCWid = false;
972 		CW_EOT = false;
973 	}
974 }
975 
976 //=====================================================================
977 // transmit processing of waterfall video id
978 //=====================================================================
979 
980 static int NUMROWS;
981 static int NUMCOLS;
982 static int TONESPACING;
983 static int IDSYMLEN;
984 static int CHARSPACE;
985 
986 static bool useIDSMALL = true;
987 
988 #define MAXROWS 14
989 #define MAXIDSYMLEN 16384
990 #define MAXTONES 128
991 #define MAXCHARS 10
992 
993 struct mfntchr  { char c; int byte[MAXROWS]; };
994 extern mfntchr  idch1[]; // original id font definition
995 extern mfntchr  idch2[]; // extended id font definition
996 
997 static int id_symbols[MAXCHARS];
998 
999 static C_FIR_filter vidfilt;
1000 
wfid_make_tones(int numchars)1001 void modem::wfid_make_tones(int numchars)
1002 {
1003 	double f, flo, fhi;
1004 	int vwidth = (numchars*NUMCOLS + (numchars-1)*CHARSPACE - 1);
1005 	f = get_txfreq_woffset() + TONESPACING * vwidth/2.0;
1006 	fhi = f + TONESPACING;
1007 	flo = fhi - (vwidth + 2) * TONESPACING;
1008 	for (int i = 1; i <= NUMCOLS * numchars; i++) {
1009 		wfid_w[i-1] = f * 2.0 * M_PI / samplerate;
1010 		f -= TONESPACING;
1011 		if ( (i > 0) && (i % NUMCOLS == 0) )
1012 			f -= TONESPACING * CHARSPACE;
1013 	}
1014 	vidfilt.init_bandpass( 1024, 1, flo/samplerate, fhi/samplerate) ;
1015 }
1016 
wfid_send(int numchars)1017 void modem::wfid_send(int numchars)
1018 {
1019 	int i, j, k;
1020 	int sym;
1021 	double val;
1022 
1023 	for (i = 0; i < IDSYMLEN; i++) {
1024 		val = 0.0;
1025 		for (k = 0; k < numchars; k++) {
1026 			sym = id_symbols[numchars - k - 1];
1027 			for (j = 0; j < NUMCOLS; j++) {
1028 				if (sym & 1)
1029 					val += sin(wfid_w[j + k * NUMCOLS] * i);
1030 				sym = sym >> 1;
1031 			}
1032 		}
1033 // soft limit the signal - heuristic formulation
1034 		val = (1.0 - exp(-fabs(val)/3.0)) * (val >= 0.0 ? 1 : -1);
1035 // band pass filter the soft limited signal
1036 		vidfilt.Irun( val, val );
1037 		wfid_outbuf[i] = val;
1038 	}
1039 	ModulateVideo(wfid_outbuf, IDSYMLEN);
1040 }
1041 
wfid_sendchars(string s)1042 void modem::wfid_sendchars(string s)
1043 {
1044 	int len = s.length();
1045 	int  n[len];
1046 	int  c;
1047 	wfid_make_tones(s.length());
1048 	for (int i = 0; i < len; i++) {
1049 		if (useIDSMALL) {
1050 			c = toupper(s[i]);
1051 			if (c > 'Z' || c < ' ') c = ' ';
1052 		} else {
1053 			c = s[i];
1054 			if (c > '~' || c < ' ') c = ' ';
1055 		}
1056 		n[i] = c - ' ';
1057 	}
1058 // send rows from bottom to top so they appear to scroll down the waterfall correctly
1059 	for (int row = 0; row < NUMROWS; row++) {
1060 		for (int i = 0; i < len; i++) {
1061 			if (useIDSMALL)
1062 				id_symbols[i] = idch1[n[i]].byte[NUMROWS - 1 - row];
1063 			else
1064 				id_symbols[i] = idch2[n[i]].byte[NUMROWS - 1 - row];
1065 		}
1066 		wfid_send(len);
1067 		if (stopflag)
1068 			return;
1069 	}
1070 }
1071 
pretone()1072 void modem::pretone()
1073 {
1074 	int sr = get_samplerate();
1075 	int symlen = sr / 10;
1076 	double phaseincr = 2.0 * M_PI * get_txfreq_woffset() / sr;
1077 	double phase = 0.0;
1078 	double outbuf[symlen];
1079 
1080 	for (int j = 0; j < symlen; j++) {
1081 		outbuf[j] = (0.5 * (1.0 - cos (M_PI * j / symlen)))*sin(phase);
1082 		phase += phaseincr;
1083 		if (phase > TWOPI) phase -= TWOPI;
1084 	}
1085 	ModulateXmtr(outbuf, symlen);
1086 
1087 	for (int i = 0; i < progdefaults.pretone * 10 - 2; i++) {
1088 		for (int j = 0; j < symlen; j++) {
1089 			outbuf[j] = sin(phase);
1090 			phase += phaseincr;
1091 			if (phase > TWOPI) phase -= TWOPI;
1092 		}
1093 		ModulateXmtr(outbuf, symlen);
1094 	}
1095 
1096 	for (int j = 0; j < symlen; j++) {
1097 		outbuf[j] = (0.5 * (1.0 - cos (M_PI * (symlen - j) / symlen)))*sin(phase);
1098 		phase += phaseincr;
1099 		if (phase > TWOPI) phase -= TWOPI;
1100 	}
1101 	ModulateXmtr(outbuf, symlen);
1102 
1103 	memset(outbuf, 0, symlen * sizeof(*outbuf));
1104 	ModulateXmtr(outbuf, symlen);
1105 
1106 }
1107 
wfid_text(const string & s)1108 void modem::wfid_text(const string& s)
1109 {
1110 	int len = s.length();
1111 	string video = "Video text: ";
1112 	video += s;
1113 
1114 	if (progdefaults.ID_SMALL) {
1115 		NUMROWS = 7;
1116 		NUMCOLS = 5;
1117 		CHARSPACE = 2;
1118 		vidwidth = progdefaults.videowidth;
1119 		TONESPACING = 6;
1120 		IDSYMLEN = 3072;
1121 		useIDSMALL = true;
1122 	} else {
1123 		NUMROWS = 14;
1124 		NUMCOLS = 8;
1125 		CHARSPACE = 2;
1126 		vidwidth = progdefaults.videowidth;
1127 		TONESPACING = 8;
1128 		IDSYMLEN = 2560;
1129 		useIDSMALL = false;
1130 	}
1131 		if (progdefaults.vidlimit) {
1132 			if ((vidwidth * TONESPACING * (NUMCOLS + CHARSPACE)) > 500)
1133 				vidwidth = 500 / (TONESPACING * (NUMCOLS + CHARSPACE));
1134 		}
1135 		if (progdefaults.vidmodelimit) {
1136 			if ((vidwidth * TONESPACING * (NUMCOLS + CHARSPACE)) > get_bandwidth())
1137 				vidwidth = (int)ceil(get_bandwidth() / (TONESPACING * (NUMCOLS + CHARSPACE)));
1138 		}
1139 
1140 	put_status(video.c_str());
1141 
1142 	disable_modem = true;
1143 
1144 	int numlines = 0;
1145 	string tosend;
1146 	while (numlines < len) numlines += vidwidth;
1147 	numlines -= vidwidth;
1148 	while (numlines >= 0) {
1149 		tosend = s.substr(numlines, vidwidth);
1150 		wfid_sendchars(tosend);
1151 		numlines -= vidwidth;
1152 		if (stopflag)
1153 			break;
1154 	}
1155 // blank lines
1156 	for (int i = 0; i < vidwidth; i++) id_symbols[i] = 0;
1157 	wfid_send(vidwidth);
1158 	wfid_send(vidwidth);
1159 
1160 	put_status("");
1161 	disable_modem = false;
1162 }
1163 
1164 double	modem::wfid_outbuf[MAXIDSYMLEN];
1165 double  modem::wfid_w[MAXTONES];
1166 
1167 mfntchr idch1[] = {
1168 { ' ', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, },
1169 { '!', { 0x00, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, }, },
1170 { '"', { 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, }, },
1171 { '#', { 0x00, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x00, }, },
1172 { '$', { 0x00, 0x0F, 0x14, 0x0E, 0x05, 0x1E, 0x00, }, },
1173 { '%', { 0x00, 0x19, 0x02, 0x04, 0x08, 0x13, 0x00, }, },
1174 { '&', { 0x00, 0x08, 0x1C, 0x0D, 0x12, 0x0F, 0x00, }, },
1175 { '\'', { 0x00, 0x18, 0x08, 0x10, 0x00, 0x00, 0x00, }, },
1176 { '(', { 0x00, 0x0C, 0x10, 0x10, 0x10, 0x0C, 0x00, }, },
1177 { ')', { 0x00, 0x18, 0x04, 0x04, 0x04, 0x18, 0x00, }, },
1178 { '*', { 0x00, 0x15, 0x0E, 0x1F, 0x0E, 0x15, 0x00, }, },
1179 { '+', { 0x00, 0x00, 0x04, 0x1F, 0x04, 0x00, 0x00, }, },
1180 { ',', { 0x00, 0x00, 0x00, 0x00, 0x18, 0x08, 0x00, }, },
1181 { '-', { 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, }, },
1182 { '.', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, }, },
1183 { '/', { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, }, },
1184 { '0', { 0x00, 0x0E, 0x13, 0x15, 0x19, 0x0E, 0x00, }, },
1185 { '1', { 0x00, 0x0C, 0x14, 0x04, 0x04, 0x04, 0x00, }, },
1186 { '2', { 0x00, 0x1C, 0x02, 0x04, 0x08, 0x1F, 0x00, }, },
1187 { '3', { 0x00, 0x1E, 0x01, 0x06, 0x01, 0x1E, 0x00, }, },
1188 { '4', { 0x00, 0x12, 0x12, 0x1F, 0x02, 0x02, 0x00, }, },
1189 { '5', { 0x00, 0x1F, 0x10, 0x1E, 0x01, 0x1E, 0x00, }, },
1190 { '6', { 0x00, 0x0E, 0x10, 0x1E, 0x11, 0x0E, 0x00, }, },
1191 { '7', { 0x00, 0x1F, 0x01, 0x02, 0x04, 0x08, 0x00, }, },
1192 { '8', { 0x00, 0x0E, 0x11, 0x0E, 0x11, 0x0E, 0x00, }, },
1193 { '9', { 0x00, 0x0E, 0x11, 0x0F, 0x01, 0x0E, 0x00, }, },
1194 { ':', { 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, }, },
1195 { ';', { 0x00, 0x18, 0x00, 0x18, 0x08, 0x10, 0x00, }, },
1196 { '<', { 0x00, 0x01, 0x06, 0x18, 0x06, 0x01, 0x00, }, },
1197 { '=', { 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, }, },
1198 { '>', { 0x00, 0x10, 0x0C, 0x03, 0x0C, 0x10, 0x00, }, },
1199 { '?', { 0x00, 0x1C, 0x02, 0x04, 0x00, 0x04, 0x00, }, },
1200 { '@', { 0x00, 0x0E, 0x11, 0x16, 0x10, 0x0F, 0x00, }, },
1201 { 'A', { 0x00, 0x0E, 0x11, 0x1F, 0x11, 0x11, 0x00, }, },
1202 { 'B', { 0x00, 0x1E, 0x09, 0x0E, 0x09, 0x1E, 0x00, }, },
1203 { 'C', { 0x00, 0x0F, 0x10, 0x10, 0x10, 0x0F, 0x00, }, },
1204 { 'D', { 0x00, 0x1E, 0x11, 0x11, 0x11, 0x1E, 0x00, }, },
1205 { 'E', { 0x00, 0x1F, 0x10, 0x1C, 0x10, 0x1F, 0x00, }, },
1206 { 'F', { 0x00, 0x1F, 0x10, 0x1C, 0x10, 0x10, 0x00, }, },
1207 { 'G', { 0x00, 0x0F, 0x10, 0x13, 0x11, 0x0F, 0x00, }, },
1208 { 'H', { 0x00, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x00, }, },
1209 { 'I', { 0x00, 0x1C, 0x08, 0x08, 0x08, 0x1C, 0x00, }, },
1210 { 'J', { 0x00, 0x01, 0x01, 0x01, 0x11, 0x0E, 0x00, }, },
1211 { 'K', { 0x00, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x00, }, },
1212 { 'L', { 0x00, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x00, }, },
1213 { 'M', { 0x00, 0x11, 0x1B, 0x15, 0x11, 0x11, 0x00, }, },
1214 { 'N', { 0x00, 0x11, 0x19, 0x15, 0x13, 0x11, 0x00, }, },
1215 { 'O', { 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E, 0x00, }, },
1216 { 'P', { 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10, 0x00, }, },
1217 { 'Q', { 0x00, 0x0E, 0x11, 0x11, 0x15, 0x0E, 0x01, }, },
1218 { 'R', { 0x00, 0x1E, 0x11, 0x1E, 0x12, 0x11, 0x00, }, },
1219 { 'S', { 0x00, 0x0F, 0x10, 0x0E, 0x01, 0x1E, 0x00, }, },
1220 { 'T', { 0x00, 0x1F, 0x04, 0x04, 0x04, 0x04, 0x00, }, },
1221 { 'U', { 0x00, 0x11, 0x11, 0x11, 0x11, 0x0E, 0x00, }, },
1222 { 'V', { 0x00, 0x11, 0x12, 0x14, 0x18, 0x10, 0x00, }, },
1223 { 'W', { 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A, 0x00, }, },
1224 { 'X', { 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00, }, },
1225 { 'Y', { 0x00, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x00, }, },
1226 { 'Z', { 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F, 0x00, }, },
1227 };
1228 
1229 mfntchr idch2[] = {
1230 {' ', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1231 {'!', { 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00 }, },
1232 {'"', { 0x00, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1233 {'#', { 0x00, 0x50, 0x50, 0xF8, 0xF8, 0x50, 0x50, 0xF8, 0xF8, 0x50, 0x50, 0x00, 0x00, 0x00 }, },
1234 {'$', { 0x00, 0x20, 0x20, 0x78, 0xF8, 0xA0, 0xF0, 0x78, 0x28, 0xF8, 0xF0, 0x20, 0x20, 0x00 }, },
1235 {'%', { 0x00, 0x40, 0xE4, 0xE4, 0x4C, 0x18, 0x30, 0x60, 0xC8, 0x9C, 0x9C, 0x88, 0x00, 0x00 }, },
1236 {'&', { 0x00, 0x30, 0x78, 0x48, 0x48, 0x70, 0xF4, 0x8C, 0x88, 0xFC, 0x74, 0x00, 0x00, 0x00 }, },
1237 {'\'', { 0x00, 0x40, 0x40, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1238 {'(', { 0x00, 0x00, 0x20, 0x60, 0xC0, 0x80, 0x80, 0x80, 0x80, 0xC0, 0x60, 0x20, 0x00, 0x00 }, },
1239 {')', { 0x00, 0x00, 0x80, 0xC0, 0x60, 0x20, 0x20, 0x20, 0x20, 0x60, 0xC0, 0x80, 0x00, 0x00 }, },
1240 {'*', { 0x00, 0x00, 0x00, 0x10, 0x10, 0xFE, 0x7C, 0x38, 0x6C, 0x44, 0x00, 0x00, 0x00, 0x00 }, },
1241 {'+', { 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00 }, },
1242 {',', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x40, 0xC0, 0x80 }, },
1243 {'-', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1244 {'.', { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00 }, },
1245 {'/', { 0x00, 0x08, 0x08, 0x18, 0x10, 0x30, 0x20, 0x60, 0x40, 0xC0, 0x80, 0x80, 0x00, 0x00 }, },
1246 {'0', { 0x00, 0x00, 0x78, 0xFC, 0x8C, 0x9C, 0xB4, 0xE4, 0xC4, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1247 {'1', { 0x00, 0x00, 0x10, 0x30, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, },
1248 {'2', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0xFC, 0x00, 0x00 }, },
1249 {'3', { 0x00, 0x00, 0xFC, 0xFC, 0x04, 0x0C, 0x18, 0x1C, 0x04, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1250 {'4', { 0x00, 0x00, 0x38, 0x78, 0x48, 0xC8, 0x88, 0xFC, 0xFC, 0x08, 0x08, 0x08, 0x00, 0x00 }, },
1251 {'5', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF8, 0xFC, 0x04, 0x04, 0xFC, 0xF8, 0x00, 0x00 }, },
1252 {'6', { 0x00, 0x00, 0x78, 0xF8, 0x80, 0x80, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1253 {'7', { 0x00, 0x00, 0xFC, 0xFC, 0x04, 0x04, 0x0C, 0x18, 0x30, 0x20, 0x20, 0x20, 0x00, 0x00 }, },
1254 {'8', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x78, 0xFC, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1255 {'9', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0xFC, 0x7C, 0x04, 0x04, 0x7C, 0x78, 0x00, 0x00 }, },
1256 {':', { 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00 }, },
1257 {';', { 0x00, 0x60, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x20, 0x20, 0xE0, 0xC0, 0x00, 0x00 }, },
1258 {'<', { 0x00, 0x00, 0x08, 0x18, 0x30, 0x60, 0xC0, 0xC0, 0x60, 0x30, 0x18, 0x08, 0x00, 0x00 }, },
1259 {'=', { 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00 }, },
1260 {'>', { 0x00, 0x00, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00 }, },
1261 {'?', { 0x00, 0x00, 0x70, 0xF8, 0x88, 0x08, 0x18, 0x30, 0x20, 0x00, 0x20, 0x20, 0x00, 0x00 }, },
1262 {'@', { 0x00, 0x00, 0x7C, 0xFE, 0x82, 0x82, 0xB2, 0xBE, 0xBC, 0x80, 0xFC, 0x7C, 0x00, 0x00 }, },
1263 {'A', { 0x00, 0x00, 0x30, 0x78, 0xCC, 0x84, 0x84, 0xFC, 0xFC, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
1264 {'B', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xF8, 0xF8, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x00 }, },
1265 {'C', { 0x00, 0x00, 0x38, 0x7C, 0xC4, 0x80, 0x80, 0x80, 0x80, 0xC4, 0x7C, 0x38, 0x00, 0x00 }, },
1266 {'D', { 0x00, 0x00, 0xF0, 0xF8, 0x8C, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xF8, 0xF0, 0x00, 0x00 }, },
1267 {'E', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF0, 0xF0, 0x80, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
1268 {'F', { 0x00, 0x00, 0xFC, 0xFC, 0x80, 0x80, 0xF0, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
1269 {'G', { 0x00, 0x00, 0x3C, 0x7C, 0xC0, 0x80, 0x8C, 0x8C, 0x84, 0xC4, 0x7C, 0x38, 0x00, 0x00 }, },
1270 {'H', { 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0xFC, 0xFC, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
1271 {'I', { 0x00, 0x00, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
1272 {'J', { 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1273 {'K', { 0x00, 0x00, 0x84, 0x84, 0x8C, 0x98, 0xF0, 0xF0, 0x98, 0x8C, 0x84, 0x84, 0x00, 0x00 }, },
1274 {'L', { 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
1275 {'M', { 0x00, 0x00, 0x82, 0xC6, 0xEE, 0xBA, 0x92, 0x82, 0x82, 0x82, 0x82, 0x82, 0x00, 0x00 }, },
1276 {'N', { 0x00, 0x00, 0x84, 0xC4, 0xE4, 0xB4, 0x9C, 0x8C, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
1277 {'O', { 0x00, 0x00, 0x30, 0x78, 0xCC, 0x84, 0x84, 0x84, 0x84, 0xCC, 0x78, 0x30, 0x00, 0x00 }, },
1278 {'P', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0xF8, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
1279 {'Q', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x84, 0x84, 0x94, 0x94, 0xFC, 0x78, 0x08, 0x08 }, },
1280 {'R', { 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xFC, 0xF8, 0x88, 0x8C, 0x84, 0x84, 0x00, 0x00 }, },
1281 {'S', { 0x00, 0x00, 0x78, 0xFC, 0x84, 0x80, 0xF8, 0x7C, 0x04, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1282 {'T', { 0x00, 0x00, 0xF8, 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00 }, },
1283 {'U', { 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1284 {'V', { 0x00, 0x00, 0x82, 0x82, 0x82, 0xC6, 0x44, 0x6C, 0x28, 0x38, 0x10, 0x10, 0x00, 0x00 }, },
1285 {'W', { 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0x82, 0x92, 0x92, 0x92, 0xFE, 0x6C, 0x00, 0x00 }, },
1286 {'X', { 0x00, 0x00, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x82, 0x82, 0x00, 0x00 }, },
1287 {'Y', { 0x00, 0x00, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00 }, },
1288 {'Z', { 0x00, 0x00, 0xFC, 0xFC, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0xFC, 0xFC, 0x00, 0x00 }, },
1289 {'[', { 0x00, 0x00, 0xE0, 0xE0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xE0, 0xE0, 0x00, 0x00 }, },
1290 {'\\', { 0x00, 0x80, 0x80, 0xC0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x08, 0x00, 0x00 }, },
1291 {']', { 0x00, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE0, 0x00, 0x00 }, },
1292 {'^', { 0x00, 0x20, 0x20, 0x70, 0x50, 0xD8, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1293 {'_', { 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00 }, },
1294 {'`', { 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, },
1295 {'a', { 0x00, 0x00, 0x00, 0x00, 0x78, 0x7C, 0x04, 0x7C, 0xFC, 0x84, 0xFC, 0x7C, 0x00, 0x00 }, },
1296 {'b', { 0x00, 0x00, 0x80, 0x80, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0xFC, 0xF8, 0x00, 0x00 }, },
1297 {'c', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xF8, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x78, 0x00, 0x00 }, },
1298 {'d', { 0x00, 0x00, 0x04, 0x04, 0x74, 0xFC, 0x8C, 0x84, 0x84, 0x84, 0xFC, 0x7C, 0x00, 0x00 }, },
1299 {'e', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xFC, 0x84, 0xFC, 0xFC, 0x80, 0xF8, 0x78, 0x00, 0x00 }, },
1300 {'f', { 0x00, 0x00, 0x3C, 0x7C, 0x40, 0x40, 0xF8, 0xF8, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, },
1301 {'g', { 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x7C, 0x78, 0x00 }, },
1302 {'h', { 0x00, 0x00, 0x80, 0x80, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
1303 {'i', { 0x00, 0x20, 0x20, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
1304 {'j', { 0x00, 0x08, 0x08, 0x00, 0x38, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x88, 0xF8, 0x70 }, },
1305 {'k', { 0x00, 0x00, 0x80, 0x88, 0x98, 0xB0, 0xE0, 0xE0, 0xB0, 0x98, 0x88, 0x88, 0x00, 0x00 }, },
1306 {'l', { 0x00, 0x00, 0xE0, 0xE0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF8, 0xF8, 0x00, 0x00 }, },
1307 {'m', { 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0x92, 0x92, 0x82, 0x82, 0x82, 0x82, 0x00, 0x00 }, },
1308 {'n', { 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFC, 0xC4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00 }, },
1309 {'o', { 0x00, 0x00, 0x00, 0x00, 0x78, 0xFC, 0x84, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00 }, },
1310 {'p', { 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFC, 0x84, 0x84, 0xC4, 0xFC, 0xB8, 0x80, 0x80, 0x80 }, },
1311 {'q', { 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x04, 0x04 }, },
1312 {'r', { 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFC, 0xC4, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }, },
1313 {'s', { 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFC, 0x80, 0xF8, 0x7C, 0x04, 0xFC, 0xF8, 0x00, 0x00 }, },
1314 {'t', { 0x00, 0x00, 0x40, 0x40, 0xF0, 0xF0, 0x40, 0x40, 0x40, 0x40, 0x78, 0x38, 0x00, 0x00 }, },
1315 {'u', { 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x00, 0x00 }, },
1316 {'v', { 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x82, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00 }, },
1317 {'w', { 0x00, 0x00, 0x00, 0x00, 0x82, 0x82, 0x82, 0x92, 0x92, 0x92, 0xFE, 0x6C, 0x00, 0x00 }, },
1318 {'x', { 0x00, 0x00, 0x00, 0x00, 0x82, 0xC6, 0x6C, 0x38, 0x38, 0x6C, 0xC6, 0x82, 0x00, 0x00 }, },
1319 {'y', { 0x00, 0x00, 0x00, 0x00, 0x84, 0x84, 0x84, 0x84, 0x8C, 0xFC, 0x74, 0x04, 0x7C, 0x78 }, },
1320 {'z', { 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC, 0x18, 0x30, 0x60, 0xC0, 0xFC, 0xFC, 0x00, 0x00 }, },
1321 {'{', { 0x00, 0x20, 0x60, 0x40, 0x40, 0x40, 0xC0, 0xC0, 0x40, 0x40, 0x40, 0x60, 0x20, 0x00 }, },
1322 {'|', { 0x00, 0x80, 0x80, 0xC0, 0x40, 0x60, 0x20, 0x30, 0x10, 0x18, 0x08, 0x08, 0x00, 0x00 }, },
1323 {'}', { 0x00, 0x80, 0xC0, 0x40, 0x40, 0x40, 0x60, 0x60, 0x40, 0x40, 0x40, 0xC0, 0x80, 0x00 }, },
1324 {'~', { 0x00, 0x98, 0xFC, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
1325 };
1326