1 //
2 // feld.cxx -- FELDHELL modem
3 //
4 // Copyright (C) 2006-2010
5 // Dave Freese, W1HKJ
6 //
7 // This modem code owes much to the efforts of Joe Veldhuis, N8FQ
8 //
9 // Adapted from code contained in gmfsk source code distribution.
10 // gmfsk Copyright (C) 2001, 2002, 2003
11 // Tomi Manninen (oh2bns@sral.fi)
12 // Copyright (C) 2004
13 // Lawrence Glaister (ve7it@shaw.ca)
14 // ----------------------------------------------------------------------------
15 // Copyright (C) 2014
16 // David Freese, W1HKJ
17 //
18 // This file is part of fldigi
19 //
20 // fldigi is free software; you can redistribute it and/or modify
21 // it under the terms of the GNU General Public License as published by
22 // the Free Software Foundation; either version 3 of the License, or
23 // (at your option) any later version.
24 //
25 // fldigi is distributed in the hope that it will be useful,
26 // but WITHOUT ANY WARRANTY; without even the implied warranty of
27 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 // GNU General Public License for more details.
29 //
30 // You should have received a copy of the GNU General Public License
31 // along with this program. If not, see <http://www.gnu.org/licenses/>.
32 // ----------------------------------------------------------------------------
33
34 #include <config.h>
35
36 #include <stdlib.h>
37 #include <stdio.h>
38
39 #include <iostream>
40
41 using namespace std;
42
43 #include "feld.h"
44 #include "fl_digi.h"
45 #include "fontdef.h"
46 #include "confdialog.h"
47 #include "qrunner.h"
48 #include "status.h"
49 #include "debug.h"
50 #include "threads.h"
51
52 #include <FL/Fl.H>
53 #include <FL/Fl_Value_Slider.H>
54
55 LOG_FILE_SOURCE(debug::LOG_MODEM);
56
57 pthread_mutex_t feld_mutex = PTHREAD_MUTEX_INITIALIZER;
58
59 char feldmsg[80];
60
tx_init()61 void feld::tx_init()
62 {
63 txcounter = 0.0;
64 tx_state = PREAMBLE;
65 preamble = 3;
66 prevsymb = false;
67 videoText();
68 return;
69 }
70
rx_init()71 void feld::rx_init()
72 {
73 rxcounter = 0.0;
74 peakhold = 0.0;
75 for (int i = 0; i < 2 * MAX_RX_COLUMN_LEN; i++ )
76 col_data[i] = 0;
77 col_pointer = 0;
78 peakhold = 0.0;
79 agc = 0.0;
80
81 RxColumnLen = progdefaults.HellRcvHeight;
82 switch (mode) {
83 // Amplitude modulation modes
84 case MODE_FELDHELL:
85 rxpixrate = (RxColumnLen * feldcolumnrate);
86 downsampleinc = (double)(rxpixrate/samplerate);
87 break;
88 case MODE_SLOWHELL:
89 rxpixrate = (RxColumnLen * feldcolumnrate);
90 downsampleinc = (double)(rxpixrate/samplerate);
91 break;
92 case MODE_HELLX5:
93 rxpixrate = (RxColumnLen * feldcolumnrate);
94 downsampleinc = (double)(rxpixrate/samplerate);
95 break;
96 case MODE_HELLX9:
97 rxpixrate = (RxColumnLen * feldcolumnrate);
98 downsampleinc = (double)(rxpixrate/samplerate);
99 break;
100 // Frequency modulation modes
101 case MODE_FSKH245:
102 rxpixrate = (RxColumnLen * feldcolumnrate);
103 downsampleinc = (double)(rxpixrate/samplerate);
104 break;
105 case MODE_FSKH105:
106 rxpixrate = (RxColumnLen * feldcolumnrate);
107 downsampleinc = (double)(rxpixrate/samplerate);
108 break;
109 case MODE_HELL80:
110 rxpixrate = (RxColumnLen * feldcolumnrate);
111 downsampleinc = (double)(rxpixrate/samplerate);
112 break;
113 default :
114 rxpixrate = (RxColumnLen * feldcolumnrate);
115 downsampleinc = (double)(rxpixrate/samplerate);
116 break;
117 }
118
119 return;
120 }
121
init()122 void feld::init()
123 {
124 modem::init();
125 initKeyWaveform();
126 set_scope_mode(Digiscope::BLANK);
127 put_MODEstatus(mode);
128
129 if (progdefaults.StartAtSweetSpot)
130 set_freq(progdefaults.PSKsweetspot);
131 else if (progStatus.carrier != 0) {
132 set_freq(progStatus.carrier);
133 #if !BENCHMARK_MODE
134 progStatus.carrier = 0;
135 #endif
136 } else
137 set_freq(wf->Carrier());
138
139 rx_init();
140
141 }
142
set_HellBW(double val)143 static void set_HellBW(double val)
144 {
145 sldrHellBW->value(val);
146 }
147
restart()148 void feld::restart()
149 {
150 RxColumnLen = progdefaults.HellRcvHeight;
151 TxColumnLen = FELD_COLUMN_LEN;
152
153 switch (mode) {
154 // Amplitude modulation modes
155 case MODE_FELDHELL:
156 feldcolumnrate = 17.5;
157 rxpixrate = (RxColumnLen * feldcolumnrate);
158 txpixrate = (TxColumnLen * feldcolumnrate);
159 downsampleinc = (double)(rxpixrate/samplerate);
160 upsampleinc = (double)(txpixrate/samplerate);
161 hell_bandwidth = txpixrate;
162 filter_bandwidth = 5 * round(1.2 * hell_bandwidth / 5.0);
163 progdefaults.HELL_BW_FH = filter_bandwidth;
164 break;
165 case MODE_SLOWHELL:
166 feldcolumnrate = 2.1875;
167 rxpixrate = (RxColumnLen * feldcolumnrate);
168 txpixrate = (TxColumnLen * feldcolumnrate);
169 downsampleinc = (double)(rxpixrate/samplerate);
170 upsampleinc = (double)(txpixrate/samplerate);
171 hell_bandwidth = txpixrate;
172 filter_bandwidth = 5 * round(1.2 * hell_bandwidth / 5.0);
173 progdefaults.HELL_BW_FH = filter_bandwidth;
174 break;
175 case MODE_HELLX5:
176 feldcolumnrate = 87.5;
177 rxpixrate = (RxColumnLen * feldcolumnrate);
178 txpixrate = (TxColumnLen * feldcolumnrate);
179 downsampleinc = (double)(rxpixrate/samplerate);
180 upsampleinc = (double)(txpixrate/samplerate);
181 hell_bandwidth = txpixrate;
182 filter_bandwidth = 5 * round(1.2 * hell_bandwidth / 5.0);
183 progdefaults.HELL_BW_X5 = filter_bandwidth;
184 break;
185 case MODE_HELLX9:
186 feldcolumnrate = 157.5;
187 rxpixrate = (RxColumnLen * feldcolumnrate);
188 txpixrate = (TxColumnLen * feldcolumnrate);
189 downsampleinc = (double)(rxpixrate/samplerate);
190 upsampleinc = (double)(txpixrate/samplerate);
191 hell_bandwidth = txpixrate;
192 filter_bandwidth = 5 * round(1.2 * hell_bandwidth / 5.0);
193 progdefaults.HELL_BW_X9 = filter_bandwidth;
194 break;
195 // Frequency modulation modes
196 case MODE_FSKH245:
197 feldcolumnrate = 17.5;
198 rxpixrate = (RxColumnLen * feldcolumnrate);
199 txpixrate = (TxColumnLen * feldcolumnrate);
200 downsampleinc = (double)(rxpixrate/samplerate);
201 upsampleinc = (double)(txpixrate/samplerate);
202 hell_bandwidth = 122.5;
203 phi2freq = samplerate / M_PI / (hell_bandwidth / 2.0);
204 filter_bandwidth = 5 * round(4.0 * hell_bandwidth / 5.0);
205 progdefaults.HELL_BW_FSKH245 = filter_bandwidth;
206 cap |= CAP_REV;
207 break;
208 case MODE_FSKH105:
209 feldcolumnrate = 17.5;
210 rxpixrate = (RxColumnLen * feldcolumnrate);
211 txpixrate = (TxColumnLen * feldcolumnrate);
212 downsampleinc = (double)(rxpixrate/samplerate);
213 upsampleinc = (double)(txpixrate/samplerate);
214 hell_bandwidth = 55;
215 phi2freq = samplerate / M_PI / (hell_bandwidth / 2.0);
216 filter_bandwidth = 5 * round(4.0 * hell_bandwidth / 5.0);
217 progdefaults.HELL_BW_FSKH105 = filter_bandwidth;
218 cap |= CAP_REV;
219 break;
220 case MODE_HELL80:
221 feldcolumnrate = 35;
222 rxpixrate = (RxColumnLen * feldcolumnrate);
223 txpixrate = (TxColumnLen * feldcolumnrate);
224 downsampleinc = (double)(rxpixrate/samplerate);
225 upsampleinc = (double)(txpixrate/samplerate);
226 hell_bandwidth = 300;
227 phi2freq = samplerate / M_PI / (hell_bandwidth / 2.0);
228 filter_bandwidth = 5 * round(4.0 * hell_bandwidth / 5.0);
229 progdefaults.HELL_BW_HELL80 = filter_bandwidth;
230 cap |= CAP_REV;
231 break;
232 default :
233 feldcolumnrate = 17.5;
234 break;
235 }
236 set_bandwidth(hell_bandwidth);
237 lpfilt->create_filter(0, 1.0 * filter_bandwidth / samplerate);//0.5 * filter_bandwidth / samplerate);
238 average->setLength( 500 * samplerate / rxpixrate);
239
240 rx_init();
241
242 wf->redraw_marker();
243
244 REQ(set_HellBW, filter_bandwidth);
245
246 /*
247 std::cout <<
248 "HellRcvHeight: " << progdefaults.HellRcvHeight << "\n" <<
249 "rx column length: " << RxColumnLen << "\n" <<
250 "tx column length: " << TxColumnLen << "\n" <<
251 "feldcolumrate: " << feldcolumnrate << "\n" <<
252 "rxpixrate: " << rxpixrate << "\n" <<
253 "txpixrate: " << txpixrate << "\n" <<
254 "downsampleinc: " << downsampleinc << "\n" <<
255 "upsampleinc: " << upsampleinc << "\n" <<
256 "hell_bandwidth: " << hell_bandwidth << "\n" <<
257 "filter bandwidth: " << filter_bandwidth << "\n" <<
258 "baud: " << 0.5 * txpixrate << "\n";
259 */
260
261 }
262
~feld()263 feld::~feld()
264 {
265 if (hilbert) delete hilbert;
266 if (lpfilt) delete lpfilt;
267 if (average) delete average;
268 }
269
feld(trx_mode m)270 feld::feld(trx_mode m)
271 {
272 mode = m;
273 samplerate = FeldSampleRate;
274
275 cap |= CAP_BW;
276
277 lpfilt = new fftfilt (0, 0.5, 1024);
278 average = new Cmovavg (200);
279 bbfilt = new Cmovavg(8);
280 hilbert = new C_FIR_filter();
281 hilbert->init_hilbert (37, 1);
282
283 rxphacc = 0.0;
284 txphacc = 0.0;
285
286 wf->redraw_marker();
287
288 REQ(&Raster::set_marquee, FHdisp, progdefaults.HellMarquee);
289
290 col_data.alloc(2 * MAX_RX_COLUMN_LEN);
291
292 restart();
293
294 }
295
296 // rx section
297
mixer(cmplx in)298 cmplx feld::mixer(cmplx in)
299 {
300
301 cmplx z;
302
303 z = cmplx( cos(rxphacc), sin(rxphacc) );
304
305 z = z * in;
306
307 rxphacc -= 2.0 * M_PI * frequency / samplerate;
308
309 if (rxphacc < 0) rxphacc += TWOPI;
310
311 return z;
312 }
313
FSKH_rx(cmplx z)314 void feld::FSKH_rx(cmplx z)
315 {
316 guard_lock raster_lock(&feld_mutex);
317
318 double f;
319 double vid;
320 double avg;
321
322 f = arg(conj(prev) * z) * phi2freq;
323 prev = z;
324 f = bbfilt->run(f);
325
326 avg = average->run(abs(z));
327
328 rxcounter += downsampleinc;
329 if (rxcounter < 1.0)
330 return;
331 rxcounter -= 1.0;
332
333 if (avg > agc)
334 agc = avg;
335 else
336 switch (progdefaults.hellagc) {
337 case 3:
338 agc *= (1.0 - 0.2 / RxColumnLen);
339 break;
340 case 2:
341 agc *= (1.0 - 0.075 / RxColumnLen);
342 break;
343 case 1:
344 default:
345 agc *= (1.0 - 0.01 / RxColumnLen);
346 }
347
348 metric = CLAMP(1000*agc, 0.0, 100.0);
349 display_metric(metric);
350
351 vid = CLAMP(0.5*(f + 1), 0.0, 1.0);
352
353 if (reverse)
354 vid = 1.0 - vid;
355 if (progdefaults.HellBlackboard)
356 vid = 1.0 - vid;
357
358 col_data[col_pointer + RxColumnLen] = (int)(vid * 255.0);
359 col_pointer++;
360 if (col_pointer == RxColumnLen) {
361 if (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false) {
362 switch (progdefaults.HellRcvWidth) {
363 case 4:
364 REQ(put_rx_data, col_data, 2 * RxColumnLen);
365 case 3:
366 REQ(put_rx_data, col_data, 2 * RxColumnLen);
367 case 2:
368 REQ(put_rx_data, col_data, 2 * RxColumnLen);
369 case 1:
370 default:
371 REQ(put_rx_data, col_data, 2 * RxColumnLen);
372 }
373 }
374 col_pointer = 0;
375 for (int i = 0; i < RxColumnLen; i++)
376 col_data[i] = col_data[i + RxColumnLen];
377 }
378 }
379
rx(cmplx z)380 void feld::rx(cmplx z)
381 {
382 guard_lock raster_lock(&feld_mutex);
383
384 double x, avg;
385 int ix;
386
387 x = abs(z);
388 if (x > peakval) peakval = x;
389 avg = average->run(x);
390
391 rxcounter += downsampleinc;
392 if (rxcounter < 1.0)
393 return;
394
395 rxcounter -= 1.0;
396
397 x = peakval;
398 peakval = 0;
399 if (x > peakhold)
400 peakhold = x;
401 else
402 peakhold *= (1.0 - 0.02 / RxColumnLen);
403 ix = CLAMP(255 * x / peakhold, 0, 255);
404
405 if (avg > agc)
406 agc = avg;
407 else
408 switch (progdefaults.hellagc) {
409 case 3:
410 agc *= (1.0 - 0.2 / RxColumnLen);
411 break;
412 case 2:
413 agc *= (1.0 - 0.075 / RxColumnLen);
414 break;
415 case 1:
416 default:
417 agc *= (1.0 - 0.01 / RxColumnLen);
418 }
419
420 metric = CLAMP(1000*agc, 0.0, 100.0);
421 display_metric(metric);
422
423 if (!progdefaults.HellBlackboard)
424 ix = 255 - ix;
425
426 col_data[col_pointer + RxColumnLen] = ix;
427 col_pointer++;
428 if (col_pointer >= RxColumnLen) {
429 if (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false) {
430 switch (progdefaults.HellRcvWidth) {
431 case 4:
432 REQ(put_rx_data, col_data, 2 * RxColumnLen);
433 case 3:
434 REQ(put_rx_data, col_data, 2 * RxColumnLen);
435 case 2:
436 REQ(put_rx_data, col_data, 2 * RxColumnLen);
437 case 1:
438 default:
439 REQ(put_rx_data, col_data, 2 * RxColumnLen);
440 }
441 }
442 col_pointer = 0;
443 for (int i = 0; i < RxColumnLen; i++)
444 col_data[i] = col_data[i + RxColumnLen];
445 }
446
447 }
448
rx_process(const double * buf,int len)449 int feld::rx_process(const double *buf, int len)
450 {
451
452 cmplx z, *zp;
453 int i, n;
454
455 if (progdefaults.HELL_BW != filter_bandwidth) {
456 filter_bandwidth = progdefaults.HELL_BW;
457 switch (mode) {
458 case MODE_FELDHELL:
459 progdefaults.HELL_BW_FH = filter_bandwidth;
460 break;
461 case MODE_SLOWHELL:
462 progdefaults.HELL_BW_SH = filter_bandwidth;
463 break;
464 case MODE_HELLX5:
465 progdefaults.HELL_BW_X5 = filter_bandwidth;
466 break;
467 case MODE_HELLX9:
468 progdefaults.HELL_BW_X9 = filter_bandwidth;
469 break;
470 case MODE_FSKH245:
471 progdefaults.HELL_BW_FSKH245 = filter_bandwidth;
472 break;
473 case MODE_FSKH105:
474 progdefaults.HELL_BW_FSKH105 = filter_bandwidth;
475 break;
476 case MODE_HELL80:
477 progdefaults.HELL_BW_HELL80 = filter_bandwidth;
478 }
479
480 lpfilt->create_filter(0, 0.5 * filter_bandwidth / samplerate);
481 wf->redraw_marker();
482 }
483
484 while (len-- > 0) {
485 /* create analytic signal... */
486 z = cmplx( *buf, *buf );
487 buf++;
488
489 /// hilbert transform need for complex frequency shift
490
491 hilbert->run(z, z);
492 z = mixer(z);
493
494 n = lpfilt->run(z, &zp);
495
496 switch (mode) {
497 case MODE_FSKH245:
498 case MODE_FSKH105:
499 case MODE_HELL80:
500 for (i = 0; i < n; i++) {
501 FSKH_rx(zp[i]);
502 }
503 break;
504 default:
505 for (i = 0; i < n; i++)
506 rx(zp[i]);
507 break;
508 }
509 }
510
511 return 0;
512 }
513
514 //=====================================================================
515 // tx section
516
517 // returns value = column bits with b0 ... b13 the transmit rows respecfully
518 // 1 = on, 0 = off
519 // if all bits are 0
520 // and no lesser bits are set then character is complete
521 // then return -1;
522
get_font_data(unsigned char c,int col)523 int feld::get_font_data(unsigned char c, int col)
524 {
525 int bits = 0;
526 int mask;
527 int bin;
528 int ordbits = 0;
529 fntchr *font = 0;
530
531 if (col > 15 || c < ' ' || c > '~')
532 return -1;
533 mask = 1 << (15 - col);
534 switch (progdefaults.feldfontnbr) {
535 case 0: font = feld7x7_14; break;
536 case 1: font = feld7x7n_14; break;
537 case 2: font = feldDx_14; break;
538 case 3: font = feldfat_14; break;
539 case 4: font = feldhell_12; break;
540 case 5: font = feldlittle_12; break;
541 case 6: font = feldlo8_14; break;
542 case 7: font = feldlow_14; break;
543 case 8: font = feldmodern_14; break;
544 case 9: font = feldmodern8_14; break;
545 case 10: font = feldnarr_14; break;
546 case 11: font = feldreal_14; break;
547 case 12: font = feldstyl_14; break;
548 case 13: font = feldvert_14; break;
549 case 14: font = feldwide_14; break;
550 default: font = feld7x7_14;
551 }
552 for (int i = 0; i < 14; i++) ordbits |= font[c-' '].byte[i];
553
554 for (int row = 0; row < 14; row ++) {
555 bin = font[c - ' '].byte[13 - row] & mask;
556 if ( bin != 0)
557 bits |= 1 << row;
558 }
559 int testval = (1 << (15 - col)) - 1;
560 if ( (bits == 0) && ((ordbits & testval) == 0) )
561 return -1;
562 return bits;
563 }
564
nco(double freq)565 double feld::nco(double freq)
566 {
567 double x = sin(txphacc);
568
569 txphacc += 2.0 * M_PI * freq / samplerate;
570
571 if (txphacc > TWOPI) txphacc -= TWOPI;
572
573 return x;
574 }
575
send_symbol(int currsymb,int nextsymb)576 void feld::send_symbol(int currsymb, int nextsymb)
577 {
578 int outlen = 0;
579
580 double Amp = 1.0;
581 double tone = get_txfreq_woffset();
582
583 if (hardkeying != progdefaults.HellPulseFast) {
584 hardkeying = progdefaults.HellPulseFast;
585 initKeyWaveform();
586 }
587
588 if (mode == MODE_FSKH245 || mode == MODE_FSKH105 || mode == MODE_HELL80)
589 tone += (reverse ? -1 : 1) * (currsymb ? -1 : 1) * bandwidth / 2.0;
590 for (;;) {
591 switch (mode) {
592 case MODE_FSKH245 : case MODE_FSKH105 : case MODE_HELL80 :
593 break;
594 case MODE_HELLX5 : case MODE_HELLX9 :
595 Amp = currsymb;
596 break;
597 case MODE_FELDHELL : case MODE_SLOWHELL :
598 default :
599 if (prevsymb == 0 && currsymb == 1) {
600 Amp = OnShape[outlen];
601 } else if (currsymb == 1 && nextsymb == 0) {
602 Amp = OffShape[outlen];
603 } else
604 Amp = currsymb;
605 break;
606 }
607 outbuf[outlen++] = Amp * nco(tone);
608
609 if (outlen >= OUTBUFSIZE) {
610 LOG_DEBUG("feld reset");
611 outlen = 0;
612 break;
613 }
614 txcounter += upsampleinc;
615 if (txcounter < 1.0)
616 continue;
617 txcounter -= 1.0;
618 break;
619 }
620 prevsymb = currsymb;
621
622 // write to soundcard & display
623 ModulateXmtr(outbuf, outlen);
624
625 rx_process(outbuf, outlen);
626
627 }
628
send_null_column()629 void feld::send_null_column()
630 {
631 for (int i = 0; i < 14; i++)
632 send_symbol(0, 0);
633 }
634
tx_char(char c)635 void feld::tx_char(char c)
636 {
637 int column = 0;
638 int bits, colbits;
639 int currbit, nextbit;
640 send_null_column();
641 if (c == ' ') {
642 send_null_column();
643 send_null_column();
644 send_null_column();
645 } else {
646 while ((bits = get_font_data(c, column)) != -1) {
647 for (int col = 0; col < progdefaults.HellXmtWidth; col++) {
648 colbits = bits;
649 for (int i = 0; i < 14; i++) {
650 currbit = colbits & 1;
651 colbits = colbits >> 1;
652 nextbit = colbits & 1;
653 send_symbol(currbit, nextbit);
654 }
655 }
656 column++;
657 }
658 }
659 send_null_column();
660 return;
661 }
662
tx_process()663 int feld::tx_process()
664 {
665 modem::tx_process();
666
667 int c;
668
669 if (hardkeying != progdefaults.HellPulseFast) {
670 hardkeying = progdefaults.HellPulseFast;
671 initKeyWaveform();
672 }
673
674 if (tx_state == PREAMBLE) {
675 if (preamble-- > 0) {
676 tx_char('.');
677 return 0;
678 }
679 tx_state = DATA;
680 }
681
682 if (tx_state == POSTAMBLE) {
683 if (postamble-- > 0) {
684 tx_char('.');
685 return 0;
686 }
687 tx_char(' ');
688 tx_state = PREAMBLE;
689 return -1;
690 }
691
692 c = get_tx_char();
693
694 if (c == GET_TX_CHAR_ETX || stopflag) {
695 tx_state = POSTAMBLE;
696 postamble = 3;
697 return 0;
698 }
699
700 // if TX buffer empty
701 // send idle character
702 if (c == GET_TX_CHAR_NODATA) {
703 if (progdefaults.HellXmtIdle == true)
704 c = '.';
705 else {
706 send_null_column();
707 send_null_column();
708 return 0;
709 }
710 }
711 if (c == '\r' || c == '\n')
712 c = ' ';
713
714 tx_char(c);
715
716 return 0;
717 }
718
initKeyWaveform()719 void feld::initKeyWaveform()
720 {
721 for (int i = 0; i < MAXLEN; i++) {
722 OnShape[i] = 1.0;
723 OffShape[i] = 0.0;
724 }
725 for (int i = 0; i < 32; i++) {
726 switch (hardkeying) {
727 case 0:
728 OnShape[i] = 0.5*(1.0 - cos(M_PI * i / 33)); // raised cosine with 4 msec rise
729 break;
730 case 1:
731 if (i < 16)
732 OnShape[i] = 0.5*(1.0 - cos(M_PI * i / 16)); // raised cosine with 2 msec rise
733 break;
734 case 2:
735 if (i < 8)
736 OnShape[i] = 0.5*(1.0 - cos(M_PI * i / 8)); // raised cosine with 1 msec rise
737 case 3:
738 default: // square shaped pulse
739 break;
740 }
741 OffShape[31 - i] = OnShape[i];
742 }
743 }
744