1 // ----------------------------------------------------------------------------
2 // ifkp.cxx -- ifkp modem
3 //
4 // Copyright (C) 2015
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 <stdlib.h>
26 #include <iostream>
27 #include <fstream>
28 #include <string>
29 #include <sstream>
30 #include <iomanip>
31 #include <cmath>
32 #include <libgen.h>
33
34 #include <FL/filename.H>
35 #include "progress.h"
36 #include "ifkp.h"
37 #include "complex.h"
38 #include "fl_digi.h"
39 #include "ascii.h"
40 #include "misc.h"
41 #include "fileselect.h"
42 #include "threads.h"
43 #include "debug.h"
44
45 #include "configuration.h"
46 #include "qrunner.h"
47 #include "fl_digi.h"
48 #include "status.h"
49 #include "main.h"
50 #include "icons.h"
51
52 #include "confdialog.h"
53
54 #include "test_signal.h"
55
56 using namespace std;
57
58 #include "ifkp_varicode.cxx"
59
60 #define IFKP_SR 16000
61
62 #include "ifkp-pic.cxx"
63
64 static fre_t call("([[:alnum:]]?[[:alpha:]/]+[[:digit:]]+[[:alnum:]/]+)", REG_EXTENDED);
65 static string teststr = "";
66 static string allowed = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/";
67 static char sz[21];
68
69 static bool enable_audit_log = false;
70 static bool enable_heard_log = false;
71
72 int ifkp::IMAGEspp = IMAGESPP;
73
valid_callsign(char ch)74 static string valid_callsign(char ch)
75 {
76 if (allowed.find(ch) == std::string::npos) ch = ' ';
77 teststr += tolower(ch);
78 if (teststr.length() > 20) teststr.erase(0,1);
79 // wait for ' de '
80 size_t p1;
81 if ((p1 = teststr.find(" de ")) != std::string::npos) { // test for callsign
82 p1 += 4;
83 if (p1 >= teststr.length()) return "";
84 while (p1 < teststr.length() && teststr[p1] == ' ') p1++;
85 if (p1 == teststr.length()) return "";
86 size_t p2 = teststr.rfind(' ');
87 if ((p2 > p1) && (p2 - p1 < 21)) { // found a word, test for callsign
88 memset(sz, 0, 21);
89 strcpy(sz, teststr.substr(p1, p2-p1).c_str());
90 teststr.erase(0, p2);
91 if (call.match(sz)) {
92 return sz;
93 }
94 return "";
95 }
96 }
97 return "";
98 }
99
100 // nibbles table used for fast conversion from tone difference to symbol
101
init_nibbles()102 void ifkp::init_nibbles()
103 {
104 int nibble = 0;
105 for (int i = 0; i < 199; i++) {
106 nibble = floor(0.5 + (i - 99.0)/IFKP_SPACING);
107 // allow for wrap-around (33 tones for 32 tone differences)
108 if (nibble < 0) nibble += 33;
109 if (nibble > 32) nibble -= 33;
110 // adjust for +1 symbol at the transmitter
111 nibble--;
112 nibbles[i] = nibble;
113 }
114 }
115
ifkp(trx_mode md)116 ifkp::ifkp(trx_mode md) : modem()
117 {
118 samplerate = IFKP_SR;
119 symlen = IFKP_SYMLEN;
120
121 cap |= CAP_IMG;
122
123 if (progdefaults.StartAtSweetSpot) {
124 frequency = progdefaults.PSKsweetspot;
125 } else
126 frequency = wf->Carrier();
127 REQ(put_freq, frequency);
128
129 mode = md;
130 fft = new g_fft<double>(IFKP_FFTSIZE);
131 snfilt = new Cmovavg(64);
132 movavg_size = 4;
133 for (int i = 0; i < IFKP_NUMBINS; i++) binfilt[i] = new Cmovavg(movavg_size);
134 txphase = 0;
135 basetone = 197;
136
137 float lo = frequency - 0.75 * bandwidth;
138 float hi = frequency + 0.75 * bandwidth;
139
140 rxfilter = new C_FIR_filter();
141 rxfilter->init_bandpass(129, 1, lo/samplerate, hi/samplerate);
142
143 picfilter = new C_FIR_filter();
144 picfilter->init_lowpass(129, 1, 2.0 * bandwidth / samplerate);
145
146 phase = 0;
147 phidiff = 2.0 * M_PI * frequency / samplerate;
148
149 IMAGEspp = IMAGESPP;
150 pixfilter = new Cmovavg(IMAGEspp / 2);
151 ampfilter = new Cmovavg(IMAGEspp);
152 syncfilter = new Cmovavg(IMAGEspp / 2);
153
154 bkptr = 0;
155 peak_counter = 0;
156 peak = last_peak = 0;
157 max = 0;
158 curr_nibble = prev_nibble = 0;
159 s2n = 0;
160
161 memset(rx_stream, 0, sizeof(rx_stream));
162 rx_text.clear();
163
164 for (int i = 0; i < IFKP_BLOCK_SIZE; i++)
165 a_blackman[i] = blackman(1.0 * i / IFKP_BLOCK_SIZE);
166
167 state = TEXT;
168
169 init_nibbles();
170
171 TX_IMAGE = TX_AVATAR = false;
172
173 restart();
174
175 toggle_logs();
176
177 activate_ifkp_image_item(true);
178
179 }
180
~ifkp()181 ifkp::~ifkp()
182 {
183 delete fft;
184 delete snfilt;
185 delete rxfilter;
186 delete picfilter;
187 for (int i = 0; i < IFKP_NUMBINS; i++)
188 delete binfilt[i];
189 ifkp_deleteTxViewer();
190 ifkp_deleteRxViewer();
191 heard_log.close();
192 audit_log.close();
193
194 activate_ifkp_image_item(false);
195
196 }
197
tx_init()198 void ifkp::tx_init()
199 {
200 tone = prevtone = 0;
201 txphase = 0;
202 send_bot = true;
203 mycall = progdefaults.myCall;
204 if (progdefaults.ifkp_lowercase)
205 for (size_t n = 0; n < mycall.length(); n++) mycall[n] = tolower(mycall[n]);
206 videoText();
207 }
208
rx_init()209 void ifkp::rx_init()
210 {
211 bkptr = 0;
212 peak_counter = 0;
213 peak = last_peak = 0;
214 max = 0;
215 curr_nibble = prev_nibble = 0;
216 s2n = 0;
217
218 memset(rx_stream, 0, sizeof(rx_stream));
219
220 prevz = cmplx(0,0);
221 image_counter = 0;
222 state = TEXT;
223
224 rx_text.clear();
225 for (int i = 0; i < IFKP_NUMBINS; i++) {
226 tones[i] = 0.0;
227 binfilt[i]->reset();
228 }
229 pixel = 0;
230 pic_str = " ";
231 }
232
rx_reset()233 void ifkp::rx_reset()
234 {
235 prevz = cmplx(0,0);
236 image_counter = 0;
237 pixel = 0;
238 pic_str = " ";
239 snfilt->reset();
240 state = TEXT;
241 }
242
init()243 void ifkp::init()
244 {
245 peak_hits = 4;
246
247 mycall = progdefaults.myCall;
248 if (progdefaults.ifkp_lowercase)
249 for (size_t n = 0; n < mycall.length(); n++) mycall[n] = tolower(mycall[n]);
250
251 movavg_size = 3;
252
253 for (int i = 0; i < IFKP_NUMBINS; i++) binfilt[i]->setLength(movavg_size);
254
255 rx_init();
256 }
257
set_freq(double f)258 void ifkp::set_freq(double f)
259 {
260 if (progdefaults.ifkp_freqlock)
261 frequency = 1500;
262 else
263 frequency = f;
264
265 if (frequency < 100 + 0.5 * bandwidth) frequency = 100 + 0.5 * bandwidth;
266 if (frequency > 3900 - 0.5 * bandwidth) frequency = 3900 - 0.5 * bandwidth;
267
268 tx_frequency = frequency;
269
270 REQ(put_freq, frequency);
271
272 set_bandwidth(33 * IFKP_SPACING * samplerate / symlen);
273
274 basetone = ceil((frequency - bandwidth / 2.0) * symlen / samplerate);
275
276 float lo = frequency - 0.75 * bandwidth;
277 float hi = frequency + 0.75 * bandwidth;
278
279 rxfilter->init_bandpass(129, 1, lo/samplerate, hi/samplerate);
280 picfilter->init_lowpass(129, 1, 2.0 * bandwidth / samplerate);
281
282 phase = 0;
283 phidiff = 2.0 * M_PI * frequency / samplerate;
284
285 std::ostringstream it;
286 it << "\nSamplerate..... " << samplerate;
287 it << "\nBandwidth...... " << bandwidth;
288 it << "\nlower cutoff... " << lo;
289 it << "\nupper cutoff... " << hi;
290 it << "\ncenter ........ " << frequency;
291 it << "\nSymbol length.. " << symlen << "\nBlock size..... " << IFKP_SHIFT_SIZE;
292 it << "\nMinimum Hits... " << peak_hits << "\nBasetone....... " << basetone << "\n";
293
294 LOG_VERBOSE("%s", it.str().c_str());
295 }
296
show_mode()297 void ifkp::show_mode()
298 {
299 if (progdefaults.ifkp_baud == 0)
300 put_MODEstatus("IFKP 0.5");
301 else if (progdefaults.ifkp_baud == 1)
302 put_MODEstatus("IFKP 1.0");
303 else
304 put_MODEstatus("IFKP 2.0");
305 return;
306 }
307
toggle_logs()308 void ifkp::toggle_logs()
309 {
310 if (enable_audit_log != progdefaults.ifkp_enable_audit_log){
311 enable_audit_log = progdefaults.ifkp_enable_audit_log;
312 if (audit_log.is_open()) audit_log.close();
313 }
314 if (enable_heard_log != progdefaults.ifkp_enable_heard_log) {
315 enable_heard_log = progdefaults.ifkp_enable_heard_log;
316 if (heard_log.is_open()) heard_log.close();
317 }
318
319 if (enable_heard_log) {
320 heard_log_fname = progdefaults.ifkp_heard_log;
321 std::string sheard = TempDir;
322 sheard.append(heard_log_fname);
323 heard_log.open(sheard.c_str(), ios::app);
324
325 heard_log << "==================================================\n";
326 heard_log << "Heard log: " << zdate() << ", " << ztime() << "\n";
327 heard_log << "==================================================\n";
328 heard_log.flush();
329 }
330
331 if (enable_audit_log) {
332 audit_log_fname = progdefaults.ifkp_audit_log;
333 std::string saudit = TempDir;
334 saudit.append(audit_log_fname);
335 audit_log.close();
336 audit_log.open(saudit.c_str(), ios::app);
337
338 audit_log << "==================================================\n";
339 audit_log << "Audit log: " << zdate() << ", " << ztime() << "\n";
340 audit_log << "==================================================\n";
341 audit_log.flush();
342 }
343
344 }
345
restart()346 void ifkp::restart()
347 {
348 set_freq(wf->Carrier());
349
350 peak_hits = 4;
351
352 mycall = progdefaults.myCall;
353 if (progdefaults.ifkp_lowercase)
354 for (size_t n = 0; n < mycall.length(); n++) mycall[n] = tolower(mycall[n]);
355
356 movavg_size = progdefaults.ifkp_baud == 2 ? 3 : 4;
357
358 for (int i = 0; i < IFKP_NUMBINS; i++) binfilt[i]->setLength(movavg_size);
359
360 show_mode();
361
362 }
363
364 // valid printable character
365
valid_char(int ch)366 bool ifkp::valid_char(int ch)
367 {
368 if ( ! (ch == 10 || ch == 163 || ch == 176 ||
369 ch == 177 || ch == 215 || ch == 247 ||
370 (ch > 31 && ch < 128)))
371 return false;
372 return true;
373 }
374
375 //=====================================================================
376 // receive processing
377 //=====================================================================
378
parse_pic(int ch)379 void ifkp::parse_pic(int ch)
380 {
381 b_ava = false;
382 image_mode = 0;
383
384 pic_str.erase(0,1);
385 pic_str += ch;
386
387 if (pic_str.find("pic%") == 0) {
388 switch (pic_str[4]) {
389 case 'A': picW = 59; picH = 74; b_ava = true; break;
390 case 'T': picW = 59; picH = 74; break;
391 case 't': picW = 59; picH = 74; image_mode = 1; break;
392 case 'S': picW = 160; picH = 120; break;
393 case 's': picW = 160; picH = 120; image_mode = 1; break;
394 case 'L': picW = 320; picH = 240; break;
395 case 'l': picW = 320; picH = 240; image_mode = 1; break;
396 case 'V': picW = 640; picH = 480; break;
397 case 'v': picW = 640; picH = 480; image_mode = 1; break;
398 case 'F': picW = 640; picH = 480; image_mode = 1; break;
399 case 'P': picW = 240; picH = 300; break;
400 case 'p': picW = 240; picH = 300; image_mode = 1; break;
401 case 'M': picW = 120; picH = 150; break;
402 case 'm': picW = 120; picH = 150; image_mode = 1; break;
403 default:
404 syncfilter->reset();
405 pixfilter->reset();
406 ampfilter->reset();
407 return;
408 }
409 } else
410 return;
411
412 if (!b_ava)
413 REQ( ifkp_showRxViewer, pic_str[4]);
414 else
415 REQ( ifkp_clear_avatar );
416
417 image_counter = -symlen / 2;
418 col = row = rgb = 0;
419 syncfilter->reset();
420 pixfilter->reset();
421 // ampfilter->reset();
422 state = IMAGE_START;
423 }
424
process_symbol(int sym)425 void ifkp::process_symbol(int sym)
426 {
427 int nibble = 0;
428 int curr_ch = -1 ;
429
430 symbol = sym;
431
432 nibble = symbol - prev_symbol;
433 if (nibble < -99 || nibble > 99) {
434 prev_symbol = symbol;
435 return;
436 }
437 nibble = nibbles[nibble + 99];
438
439 if (nibble >= 0) { // process nibble
440 curr_nibble = nibble;
441
442 // single-nibble characters
443 if ((prev_nibble < 29) & (curr_nibble < 29)) {
444 curr_ch = ifkp_varidecode[prev_nibble];
445
446 // double-nibble characters
447 } else if ( (prev_nibble < 29) &&
448 (curr_nibble > 28) &&
449 (curr_nibble < 32)) {
450 curr_ch = ifkp_varidecode[prev_nibble * 32 + curr_nibble];
451 }
452 if (curr_ch > 0) {
453 if (ch_sqlch_open || metric >= progStatus.sldrSquelchValue) {
454 // if (metric >= progStatus.sldrSquelchValue) {
455 put_rx_char(curr_ch, FTextBase::RECV);
456 if (enable_audit_log) {
457 audit_log << ifkp_ascii[curr_ch];
458 if (curr_ch == '\n') audit_log << '\n';
459 }
460 parse_pic(curr_ch);
461 if (state != IMAGE_START) {
462 station_calling = valid_callsign(curr_ch);
463 if (!station_calling.empty()) {
464 snprintf(szestimate, sizeof(szestimate), "%.0f db", s2n );
465 REQ(add_to_heard_list, station_calling.c_str(), szestimate);
466 if (enable_heard_log) {
467 std::string sheard = zdate();
468 sheard.append(":").append(ztime());
469 sheard.append(",").append(station_calling);
470 sheard.append(",").append(szestimate).append("\n");
471 heard_log << sheard;
472 heard_log.flush();
473 }
474 }
475 }
476 }
477 }
478 prev_nibble = curr_nibble;
479 }
480
481 prev_symbol = symbol;
482 }
483
process_tones()484 void ifkp::process_tones()
485 {
486 max = 0;
487 peak = 0;
488
489 max = 0;
490 peak = IFKP_NUMBINS / 2;
491
492 int firstbin = frequency * IFKP_SYMLEN / samplerate - IFKP_NUMBINS / 2;
493
494 double sigval = 0;
495
496 double mins[3];
497 double min = 3.0e8;
498 double temp;
499
500 mins[0] = mins[1] = mins[2] = 1.0e8;
501 for (int i = 0; i < IFKP_NUMBINS; ++i) {
502 val = norm(fft_data[i + firstbin]);
503 // looking for maximum signal
504 tones[i] = binfilt[i]->run(val);
505 if (tones[i] > max) {
506 max = tones[i];
507 peak = i;
508 }
509 // looking for minimum signal in a 3 bin sequence
510 mins[2] = tones[i];
511 temp = mins[0] + mins[1] + mins[2];
512 mins[0] = mins[1];
513 mins[1] = mins[2];
514 if (temp < min) min = temp;
515 }
516
517 sigval = tones[peak-1] + tones[peak] + tones[peak+1];
518 if (min == 0) min = 1e-10;
519
520 s2n = 10 * log10( snfilt->run(sigval/min)) - 36.0;
521
522 //scale to -25 to +45 db range
523 // -25 -> 0 linear
524 // 0 - > 45 compressed by 2
525
526 if (s2n <= 0) metric = 2 * (25 + s2n);
527 if (s2n > 0) metric = 50 * ( 1 + s2n / 45);
528 metric = clamp(metric, 0, 100);
529
530 display_metric(metric);
531
532 if (peak == prev_peak) {
533 peak_counter++;
534 } else {
535 peak_counter = 0;
536 }
537
538 if ((peak_counter >= peak_hits) &&
539 (peak != last_peak) &&
540 (metric >= progStatus.sldrSquelchValue ||
541 progStatus.sqlonoff == false)) {
542 process_symbol(peak);
543 peak_counter = 0;
544 last_peak = peak;
545 }
546
547 prev_peak = peak;
548 }
549
recvpic(double smpl)550 void ifkp::recvpic(double smpl)
551 {
552 phidiff = 2.0 * M_PI * frequency / samplerate;
553 phase -= phidiff;
554 if (phase < 0) phase += 2.0 * M_PI;
555
556 cmplx z = smpl * cmplx( cos(phase), sin(phase ) );
557 picfilter->run( z, currz);
558 pixel = (samplerate / TWOPI) * pixfilter->run(arg(conj(prevz) * currz));
559 sync = (samplerate / TWOPI) * syncfilter->run(arg(conj(prevz) * currz));
560
561 prevz = currz;
562
563 image_counter++;
564
565 if (image_counter < 0) return;
566
567 if (state == IMAGE_START) {
568 if (sync < -0.59 * bandwidth) {
569 state = IMAGE_SYNC;
570 }
571 return;
572 }
573 if (state == IMAGE_SYNC) {
574 if (sync > -0.51 * bandwidth) {
575 state = IMAGE;
576 }
577 return;
578 }
579
580 if ((image_counter % IMAGEspp) == 0) {
581 byte = pixel * 256.0 / bandwidth + 128;
582 byte = (int)CLAMP( byte, 0.0, 255.0);
583
584 if (image_mode == 1) { // bw transmission
585 pixelnbr = 3 * (col + row * picW);
586 if (b_ava) {
587 REQ(ifkp_update_avatar, byte, pixelnbr);
588 REQ(ifkp_update_avatar, byte, pixelnbr + 1);
589 REQ(ifkp_update_avatar, byte, pixelnbr + 2);
590 } else {
591 REQ(ifkp_updateRxPic, byte, pixelnbr);
592 REQ(ifkp_updateRxPic, byte, pixelnbr + 1);
593 REQ(ifkp_updateRxPic, byte, pixelnbr + 2);
594 }
595 if (++ col == picW) {
596 col = 0;
597 row++;
598 if (row >= picH) {
599 rx_reset();
600 REQ(ifkp_enableshift);
601 }
602 }
603 } else { // color transmission
604 pixelnbr = rgb + 3 * (col + row * picW);
605 if (b_ava)
606 REQ(ifkp_update_avatar, byte, pixelnbr);
607 else
608 REQ(ifkp_updateRxPic, byte, pixelnbr);
609 if (++col == picW) {
610 col = 0;
611 if (++rgb == 3) {
612 rgb = 0;
613 ++row;
614 }
615 }
616 if (row >= picH) {
617 rx_reset();
618 REQ(ifkp_enableshift);
619 }
620 }
621 }
622 }
623
rx_process(const double * buf,int len)624 int ifkp::rx_process(const double *buf, int len)
625 {
626 double val;
627 cmplx zin, z;
628
629 if (enable_audit_log != progdefaults.ifkp_enable_audit_log ||
630 enable_heard_log != progdefaults.ifkp_enable_heard_log)
631 toggle_logs();
632
633 if (bkptr < 0) bkptr = 0;
634 if (bkptr >= IFKP_SHIFT_SIZE) bkptr = 0;
635
636 if (progStatus.ifkp_rx_abort) {
637 state = TEXT;
638 progStatus.ifkp_rx_abort = false;
639 REQ(ifkp_clear_rximage);
640 }
641
642 while (len) {
643 if (state == TEXT) {
644 rxfilter->Irun(*buf, val);
645 rx_stream[IFKP_BLOCK_SIZE + bkptr] = val;
646 bkptr++;
647
648 if (bkptr == IFKP_SHIFT_SIZE) {
649 bkptr = 0;
650 memmove(rx_stream, // to
651 &rx_stream[IFKP_SHIFT_SIZE], // from
652 IFKP_BLOCK_SIZE*sizeof(*rx_stream)); // # bytes
653
654 for (int n = 0; n < 2*IFKP_FFTSIZE; n++)
655 fft_data[n] = cmplx(0,0);
656
657 for (int i = 0; i < IFKP_BLOCK_SIZE; i++) {
658 double d = rx_stream[i] * a_blackman[i];
659 fft_data[i] = cmplx(d,d);
660 }
661 fft->ComplexFFT(fft_data);
662 process_tones();
663 }
664 } else
665 recvpic(*buf);
666
667 len--;
668 buf++;
669 }
670 return 0;
671 }
672
673 //=====================================================================
674 // transmit processing
675 //=====================================================================
676
transmit(double * buf,int len)677 void ifkp::transmit(double *buf, int len)
678 {
679 // if (xmtfilt && progdefaults.ifkp_xmtfilter)
680 // for (int i = 0; i < len; i++) xmtfilt->Irun(buf[i], buf[i]);
681 ModulateXmtr(buf, len);
682 }
683
send_tone(int tone)684 void ifkp::send_tone(int tone)
685 {
686 double phaseincr;
687 double frequency;
688
689 frequency = (basetone + tone * IFKP_SPACING) * samplerate / symlen;
690 if (test_signal_window && test_signal_window->visible() && btnOffsetOn->value())
691 frequency += ctrl_freq_offset->value();
692
693 phaseincr = 2.0 * M_PI * frequency / samplerate;
694 prevtone = tone;
695
696 int send_symlen = symlen * (
697 progdefaults.ifkp_baud == 2 ? 0.5 :
698 progdefaults.ifkp_baud == 0 ? 2.0 : 1.0);
699
700 for (int i = 0; i < send_symlen; i++) {
701 outbuf[i] = cos(txphase);
702 txphase -= phaseincr;
703 if (txphase < 0) txphase += TWOPI;
704 }
705 transmit(outbuf, send_symlen);
706 }
707
send_symbol(int sym)708 void ifkp::send_symbol(int sym)
709 {
710 tone = (prevtone + sym + IFKP_OFFSET) % 33;
711 send_tone(tone);
712 }
713
send_idle()714 void ifkp::send_idle()
715 {
716 send_symbol(0);
717 }
718
send_char(int ch)719 void ifkp::send_char(int ch)
720 {
721 if (ch <= 0) return send_idle();
722
723 int sym1 = ifkp_varicode[ch][0];
724 int sym2 = ifkp_varicode[ch][1];
725
726 send_symbol(sym1);
727 if (sym2 > 28)
728 send_symbol(sym2);
729 put_echo_char(ch);
730 }
731
send_avatar()732 void ifkp::send_avatar()
733 {
734 int W = 59, H = 74; // grey scale transfer (FAX)
735 float freq, phaseincr;
736 float radians = 2.0 * M_PI / samplerate;
737
738 freq = frequency - 0.6 * bandwidth;
739 #define PHASE_CORR (3 * symlen / 2)
740 phaseincr = radians * freq;
741 for (int n = 0; n < PHASE_CORR; n++) {
742 outbuf[n] = cos(txphase);
743 txphase -= phaseincr;
744 if (txphase < 0) txphase += TWOPI;
745 }
746 transmit(outbuf, PHASE_CORR);
747
748 for (int row = 0; row < H; row++) {
749 for (int color = 0; color < 3; color++) {
750 memset(outbuf, 0, IMAGEspp * sizeof(*outbuf));
751 for (int col = 0; col < W; col++) {
752 if (stopflag) return;
753 tx_pixelnbr = col + row * W;
754 tx_pixel = ifkp_get_avatar_pixel(tx_pixelnbr, color);
755 freq = frequency + (tx_pixel - 128) * bandwidth / 256.0;
756 phaseincr = radians * freq;
757 for (int n = 0; n < IMAGEspp; n++) {
758 outbuf[n] = cos(txphase);
759 txphase -= phaseincr;
760 if (txphase < 0) txphase += TWOPI;
761 }
762 transmit(outbuf, IMAGEspp);
763 }
764 Fl::awake();
765 }
766 }
767 }
768
769 static bool send_color = true;
770
send_image()771 void ifkp::send_image()
772 {
773 int W = 640, H = 480; // grey scale transfer (FAX)
774 float freq, phaseincr;
775 float radians = 2.0 * M_PI / samplerate;
776
777 if (!ifkppicTxWin || !ifkppicTxWin->visible()) {
778 return;
779 }
780
781 switch (selifkppicSize->value()) {
782 case 0 : W = 59; H = 74; break;
783 case 1 : W = 120; H = 150; break;
784 case 2 : W = 240; H = 300; break;
785 case 3 : W = 160; H = 120; break;
786 case 4 : W = 320; H = 240; break;
787 case 5 : W = 640; H = 480; break;
788 }
789
790 REQ(ifkp_clear_tximage);
791
792 stop_deadman();
793
794 for (size_t n = 0; n < imageheader.length(); n++)
795 send_char(imageheader[n]);
796 send_char(0); // needed to flush the header at the Rx decoder
797
798 freq = frequency - 0.6 * bandwidth; // black-black
799 phaseincr = radians * freq;
800
801 for (int j = 0; j < 7; j++) {
802 for (int i = 0; i < symlen/2; i++) {
803 outbuf[i] = cos(txphase);
804 txphase -= phaseincr;
805 if (txphase < 0) txphase += TWOPI;
806 }
807 transmit(outbuf, symlen/2);
808 }
809
810 if (send_color == false) { // grey scale image
811 for (int row = 0; row < H; row++) {
812 memset(outbuf, 0, IMAGEspp * sizeof(*outbuf));
813 for (int col = 0; col < W; col++) {
814 if (stopflag)
815 goto end_image;
816 tx_pixelnbr = col + row * W;
817 tx_pixel = 0.3 * ifkppic_TxGetPixel(tx_pixelnbr, 0) + // red
818 0.6 * ifkppic_TxGetPixel(tx_pixelnbr, 1) + // green
819 0.1 * ifkppic_TxGetPixel(tx_pixelnbr, 2); // blue
820 REQ(ifkp_updateTxPic, tx_pixel, tx_pixelnbr*3 + 0);
821 REQ(ifkp_updateTxPic, tx_pixel, tx_pixelnbr*3 + 1);
822 REQ(ifkp_updateTxPic, tx_pixel, tx_pixelnbr*3 + 2);
823 freq = frequency + (tx_pixel - 128) * bandwidth / 256.0;
824 phaseincr = radians * freq;
825 for (int n = 0; n < IMAGEspp; n++) {
826 outbuf[n] = cos(txphase);
827 txphase -= phaseincr;
828 if (txphase < 0) txphase += TWOPI;
829 }
830 transmit(outbuf, IMAGEspp);
831 Fl::awake();
832 }
833 }
834 } else {
835 for (int row = 0; row < H; row++) {
836 for (int color = 0; color < 3; color++) {
837 memset(outbuf, 0, IMAGEspp * sizeof(*outbuf));
838 for (int col = 0; col < W; col++) {
839 if (stopflag)
840 goto end_image;
841 tx_pixelnbr = col + row * W;
842 tx_pixel = ifkppic_TxGetPixel(tx_pixelnbr, color);
843 REQ(ifkp_updateTxPic, tx_pixel, tx_pixelnbr*3 + color);
844 freq = frequency + (tx_pixel - 128) * bandwidth / 256.0;
845 phaseincr = radians * freq;
846 for (int n = 0; n < IMAGEspp; n++) {
847 outbuf[n] = cos(txphase);
848 txphase -= phaseincr;
849 if (txphase < 0) txphase += TWOPI;
850 }
851 transmit(outbuf, IMAGEspp);
852 }
853 Fl::awake();
854 if (stopflag) goto end_image;
855 }
856 }
857 }
858 end_image:
859 start_deadman();
860 }
861
862 std::string img_str;
863
ifkp_send_image(std::string image_str,bool gray)864 void ifkp::ifkp_send_image(std::string image_str, bool gray) {
865 send_color = !gray;
866 imageheader = image_str;
867 TX_IMAGE = true;
868 start_tx();
869 }
870
ifkp_send_avatar()871 void ifkp::ifkp_send_avatar() {
872 img_str = "\npic%A";
873 TX_AVATAR = true;
874 start_tx();
875 }
876
m_ifkp_send_avatar()877 void ifkp::m_ifkp_send_avatar()
878 {
879 std::string mycall = progdefaults.myCall;
880 for (size_t n = 0; n < mycall.length(); n++)
881 mycall[n] = tolower(mycall[n]);
882 std::string fname = AvatarDir;
883 fname.append(mycall).append(".png");
884
885 my_avatar_img = Fl_Shared_Image::get(fname.c_str(), 59, 74);
886 if (!my_avatar_img) return;
887 unsigned char *img_data = (unsigned char *)my_avatar_img->data()[0];
888 memset(avatar, 0, sizeof(avatar));
889 int D = my_avatar_img->d();
890
891 if (D == 3)
892 memcpy(avatar, img_data, 59*74*3);
893 else if (D == 4) {
894 int i, j, k;
895 for (i = 0; i < 59*74; i++) {
896 j = i*3; k = i*4;
897 avatar[j] = img_data[k];
898 avatar[j+1] = img_data[k+1];
899 avatar[j+2] = img_data[k+2];
900 }
901 } else if (D == 1) {
902 int i, j;
903 for (i = 0; i < 59*74; i++) {
904 j = i * 3;
905 avatar[j] = avatar[j+1] = avatar[j+2] = img_data[i];
906 }
907 } else
908 return;
909
910 ifkp_send_avatar();
911 }
912
tx_process()913 int ifkp::tx_process()
914 {
915 modem::tx_process();
916
917 if (send_bot) {
918 send_bot = false;
919 send_char(0);
920 send_char(0);
921 }
922
923 int c = get_tx_char();
924
925 // if (c == GET_TX_CHAR_ETX || enable_image) {
926 if (TX_IMAGE || TX_AVATAR) {
927 if (img_str.length()) {
928 for (size_t n = 0; n < img_str.length(); n++)
929 send_char(img_str[n]);
930 }
931 img_str.clear();
932
933 if (TX_IMAGE) {
934 send_image();
935 ifkppicTxWin->hide();
936 }
937 if (TX_AVATAR)
938 send_avatar();
939
940 send_char(0);
941
942 TX_IMAGE = false;
943 TX_AVATAR = false;
944 ifkppicTxWin->hide();
945
946 return 0;
947
948 }
949 if ( stopflag || c == GET_TX_CHAR_ETX) { // aborts transmission
950 send_char(0);
951 // TX_IMAGE = false;
952 // TX_AVATAR = false;
953 stopflag = false;
954 return -1;
955 }
956 send_char(c);
957 return 0;
958 }
959
960