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