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