1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2018
3 //              David Freese, W1HKJ
4 //
5 // This file is part of flrig.
6 //
7 // flrig is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // flrig is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // aunsigned long int with this program.  If not, see <http://www.gnu.org/licenses/>.
19 // ----------------------------------------------------------------------------
20 
21 #include "IC7851.h"
22 
23 bool IC7851_DEBUG = true;
24 
25 //=============================================================================
26 // IC-7851
27 
28 const char IC7851name_[] = "IC-7851";
29 
30 enum {
31 	LSB7851, USB7851, AM7851, CW7851, RTTY7851,
32 	FM7851,  CWR7851, RTTYR7851, PSK7851, PSKR7851,
33 	LSBD17851, LSBD27851, LSBD37851,
34 	USBD17851, USBD27851, USBD37851 };
35 
36 const char *IC7851modes_[] = {
37 	"LSB", "USB", "AM", "CW", "RTTY",
38 	"FM", "CW-R", "RTTY-R", "PSK", "PSK-R",
39 	"LSB-D1", "LSB-D2", "LSB-D3",
40 	"USB-D1", "USB-D2", "USB-D3", NULL};
41 
42 const char IC7851_mode_type[] = {
43 	'L', 'U', 'U', 'U', 'L', 'U', 'L', 'U', 'U', 'L',
44 	'L', 'L', 'L',
45 	'U', 'U', 'U' };
46 
47 const char IC7851_mode_nbr[] = {
48 	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x12, 0x13,
49 	0x00, 0x00, 0x00,
50 	0x01, 0x01, 0x01 };
51 
52 const char *IC7851_ssb_bws[] = {
53 "50",    "100",  "150",  "200",  "250",  "300",  "350",  "400",  "450",  "500",
54 "600",   "700",  "800",  "900", "1000", "1100", "1200", "1300", "1400", "1500",
55 "1600", "1700", "1800", "1900", "2000", "2100", "2200", "2300", "2400", "2500",
56 "2600", "2700", "2800", "2900", "3000", "3100", "3200", "3300", "3400", "3500",
57 "3600", NULL };
58 
59 static int IC7851_bw_vals_SSB[] = {
60  1, 2, 3, 4, 5, 6, 7, 8, 9,10,
61 11,12,13,14,15,16,17,18,19,20,
62 21,22,23,24,25,26,27,28,29,30,
63 31,32,33,34,35,36,37,38,39,40,
64 41, WVALS_LIMIT};
65 
66 const char *IC7851_am_bws[] = {
67 "200",   "400",  "600",  "800", "1000", "1200", "1400", "1600", "1800", "2000",
68 "2200", "2400", "2600", "2800", "3000", "3200", "3400", "3600", "3800", "4000",
69 "4200", "4400", "4600", "4800", "5000", "5200", "5400", "5600", "5800", "6000",
70 "6200", "6400", "6600", "6800", "7000", "7851", "7400", "7851", "7851", "8000",
71 "8200", "8400", "8600", "8800", "9000", "9200", "9400", "9600", "9800", "10000", NULL };
72 
73 static int IC7851_bw_vals_AM[] = {
74  1, 2, 3, 4, 5, 6, 7, 8, 9,10,
75 11,12,13,14,15,16,17,18,19,20,
76 21,22,23,24,25,26,27,28,29,30,
77 31,32,33,34,35,36,37,38,39,40,
78 41,42,43,44,45,46,47,48,49,50,
79 WVALS_LIMIT};
80 
81 const char *IC7851_fm_bws[] = { "FIXED", NULL };
82 static int IC7851_bw_vals_FM[] = { 1, WVALS_LIMIT};
83 
84 static GUI IC7851_widgets[]= {
85 	{ (Fl_Widget *)btnVol,        2, 125,  50 },	//0
86 	{ (Fl_Widget *)sldrVOLUME,   54, 125, 156 },	//1
87 	{ (Fl_Widget *)btnAGC,        2, 145,  50 },	//2
88 	{ (Fl_Widget *)sldrRFGAIN,   54, 145, 156 },	//3
89 	{ (Fl_Widget *)sldrSQUELCH,  54, 165, 156 },	//4
90 	{ (Fl_Widget *)btnNR,         2, 185,  50 },	//5
91 	{ (Fl_Widget *)sldrNR,       54, 185, 156 },	//6
92 	{ (Fl_Widget *)btnLOCK,     214, 105,  50 },	//7
93 	{ (Fl_Widget *)sldrINNER,   266, 105, 156 },	//8
94 	{ (Fl_Widget *)btnCLRPBT,   214, 125,  50 },	//9
95 	{ (Fl_Widget *)sldrOUTER,   266, 125, 156 },	//10
96 	{ (Fl_Widget *)btnNotch,    214, 145,  50 },	//11
97 	{ (Fl_Widget *)sldrNOTCH,   266, 145, 156 },	//12
98 	{ (Fl_Widget *)sldrMICGAIN, 266, 165, 156 },	//13
99 	{ (Fl_Widget *)sldrPOWER,   266, 185, 156 },	//14
100 	{ (Fl_Widget *)NULL, 0, 0, 0 }
101 };
102 
initialize()103 void RIG_IC7851::initialize()
104 {
105 	IC7851_widgets[0].W = btnVol;
106 	IC7851_widgets[1].W = sldrVOLUME;
107 	IC7851_widgets[2].W = btnAGC;
108 	IC7851_widgets[3].W = sldrRFGAIN;
109 	IC7851_widgets[4].W = sldrSQUELCH;
110 	IC7851_widgets[5].W = btnNR;
111 	IC7851_widgets[6].W = sldrNR;
112 	IC7851_widgets[7].W = btnLOCK;
113 	IC7851_widgets[8].W = sldrINNER;
114 	IC7851_widgets[9].W = btnCLRPBT;
115 	IC7851_widgets[10].W = sldrOUTER;
116 	IC7851_widgets[11].W = btnNotch;
117 	IC7851_widgets[12].W = sldrNOTCH;
118 	IC7851_widgets[13].W = sldrMICGAIN;
119 	IC7851_widgets[14].W = sldrPOWER;
120 
121 	btn_icom_select_11->deactivate();
122 	btn_icom_select_12->deactivate();
123 	btn_icom_select_13->deactivate();
124 
125 	choice_rTONE->activate();
126 	choice_tTONE->activate();
127 }
128 
RIG_IC7851()129 RIG_IC7851::RIG_IC7851() {
130 	defaultCIV = 0x8E;
131 	name_ = IC7851name_;
132 	modes_ = IC7851modes_;
133 	bandwidths_ = IC7851_ssb_bws;
134 	bw_vals_ = IC7851_bw_vals_SSB;
135 
136 	_mode_type = IC7851_mode_type;
137 	adjustCIV(defaultCIV);
138 
139 	comm_baudrate = BR19200;
140 	stopbits = 1;
141 	comm_retries = 2;
142 	comm_wait = 5;
143 	comm_timeout = 50;
144 	comm_echo = true;
145 	comm_rtscts = false;
146 	comm_rtsplus = true;
147 	comm_dtrplus = true;
148 	comm_catptt = true;
149 	comm_rtsptt = false;
150 	comm_dtrptt = false;
151 
152 	widgets = IC7851_widgets;
153 
154 	has_extras =
155 
156 	has_cw_wpm =
157 	has_cw_spot_tone =
158 	has_cw_qsk =
159 
160 	has_vox_onoff =
161 	has_vox_gain =
162 	has_vox_anti =
163 	has_vox_hang =
164 
165 	has_compON =
166 	has_compression =
167 
168 	has_micgain_control =
169 	has_bandwidth_control =
170 	has_smeter =
171 	has_power_control =
172 	has_volume_control =
173 	has_mode_control =
174 	has_notch_control =
175 	has_attenuator_control =
176 	has_preamp_control =
177 	has_ifshift_control =
178 	has_pbt_controls =
179 	has_ptt_control =
180 	has_tune_control =
181 	has_noise_control =
182 	has_noise_reduction =
183 	has_noise_reduction_control =
184 	has_rf_control =
185 	has_sql_control =
186 	has_split_AB =
187 	has_split = true;
188 
189 	ICOMmainsub = true;
190 
191 	has_band_selection = true;
192 
193 	has_xcvr_auto_on_off = true;
194 
195 	precision = 1;
196 	ndigits = 8;
197 
198 
199 };
200 
201 //======================================================================
202 // IC7851 unique commands
203 //======================================================================
204 
selectA()205 void RIG_IC7851::selectA()
206 {
207 	cmd = pre_to;
208 	cmd += '\x07'; cmd += '\xD2'; cmd += '\x00';
209 	cmd.append(post);
210 	sendICcommand(cmd, 6);
211 	waitFB("Select A");
212 }
213 
selectB()214 void RIG_IC7851::selectB()
215 {
216 	cmd = pre_to;
217 	cmd += '\x07'; cmd += '\xD2'; cmd += '\x01';
218 	cmd.append(post);
219 	sendICcommand(cmd, 6);
220 	waitFB("Select B");
221 }
222 
set_xcvr_auto_on()223 void RIG_IC7851::set_xcvr_auto_on()
224 {
225 	cmd = pre_to;
226 	cmd += '\x19'; cmd += '\x00';
227 	cmd.append(post);
228 	if (waitFOR(8, "get ID", 100) == false) {
229 		cmd.clear();
230 		int fes[] = { 2, 2, 2, 3, 7, 13, 25, 50, 75, 150, 150, 150 };
231 		if (progStatus.comm_baudrate >= 0 && progStatus.comm_baudrate <= 11) {
232 			cmd.append( fes[progStatus.comm_baudrate], '\xFE');
233 		}
234 		cmd.append(pre_to);
235 		cmd += '\x18'; cmd += '\x01';
236 		cmd.append(post);
237 		waitFB("Power ON", 200);
238 		for (int i = 0; i < 5000; i += 100) {
239 			MilliSleep(100);
240 			update_progress(100 * i / 5000);
241 			Fl::awake();
242 		}
243 	}
244 }
245 
set_xcvr_auto_off()246 void RIG_IC7851::set_xcvr_auto_off()
247 {
248 	cmd.clear();
249 	cmd.append(pre_to);
250 	cmd += '\x18'; cmd += '\x00';
251 	cmd.append(post);
252 	waitFB("Power OFF", 200);
253 }
254 
check()255 bool RIG_IC7851::check ()
256 {
257 	string resp = pre_fm;
258 	resp += '\x03';
259 	cmd = pre_to;
260 	cmd += '\x03';
261 	cmd.append( post );
262 	bool ok = waitFOR(11, "check vfo");
263 	rig_trace(2, "check()", str2hex(replystr.c_str(), replystr.length()));
264 	return ok;
265 }
266 
get_vfoA()267 unsigned long int RIG_IC7851::get_vfoA ()
268 {
269 	if (useB) return A.freq;
270 	string resp = pre_fm;
271 	resp += '\x03';
272 	cmd = pre_to;
273 	cmd += '\x03';
274 	cmd.append( post );
275 	if (waitFOR(11, "get vfo A")) {
276 		size_t p = replystr.rfind(resp);
277 		if (p != string::npos) {
278 			if (replystr[p+5] == -1)
279 				A.freq = 0;
280 			else
281 				A.freq = fm_bcd_be(replystr.substr(p+5), 10);
282 		}
283 	}
284 	return A.freq;
285 }
286 
set_vfoA(unsigned long int freq)287 void RIG_IC7851::set_vfoA (unsigned long int freq)
288 {
289 	A.freq = freq;
290 	cmd = pre_to;
291 	cmd += '\x05';
292 	cmd.append( to_bcd_be( freq, 10 ) );
293 	cmd.append( post );
294 	waitFB("set vfo A");
295 }
296 
get_vfoB()297 unsigned long int RIG_IC7851::get_vfoB ()
298 {
299 	if (!useB) return B.freq;
300 	string resp = pre_fm;
301 	resp += '\x03';
302 	cmd = pre_to;
303 	cmd += '\x03';
304 	cmd.append( post );
305 	if (waitFOR(11, "get vfo B")) {
306 		size_t p = replystr.rfind(resp);
307 		if (p != string::npos) {
308 			if (replystr[p+5] == -1)
309 				A.freq = 0;
310 			else
311 				B.freq = fm_bcd_be(replystr.substr(p+5), 10);
312 		}
313 	}
314 	return B.freq;
315 }
316 
set_vfoB(unsigned long int freq)317 void RIG_IC7851::set_vfoB (unsigned long int freq)
318 {
319 	B.freq = freq;
320 	cmd = pre_to;
321 	cmd += '\x05';
322 	cmd.append( to_bcd_be( freq, 10 ) );
323 	cmd.append( post );
324 	waitFB("set vfo B");
325 }
326 
can_split()327 bool RIG_IC7851::can_split()
328 {
329 	return true;
330 }
331 
set_split(bool val)332 void RIG_IC7851::set_split(bool val)
333 {
334 	split = val;
335 	cmd = pre_to;
336 	cmd += 0x0F;
337 	cmd += val ? 0x01 : 0x00;
338 	cmd.append(post);
339 	waitFB(val ? "set split ON" : "set split OFF");
340 }
341 
get_split()342 int RIG_IC7851::get_split()
343 {
344 	int read_split = 0;
345 	cmd.assign(pre_to);
346 	cmd.append("\x0F");
347 	cmd.append( post );
348 	if (waitFOR(7, "get split")) {
349 		string resp = pre_fm;
350 		resp.append("\x0F");
351 		size_t p = replystr.find(resp);
352 		if (p != string::npos)
353 			read_split = replystr[p+5];
354 		if (read_split != 0xFA) // fail byte
355 			split = read_split;
356 	}
357 	return split;
358 }
359 
set_modeA(int val)360 void RIG_IC7851::set_modeA(int val)
361 {
362 	A.imode = val;
363 	cmd = pre_to;
364 	cmd += '\x06';
365 	cmd += IC7851_mode_nbr[val];
366 	cmd.append( post );
367 	waitFB("set modeA");
368 
369 // digital set / clear
370 	if (val >= 10) {
371 		cmd = pre_to;
372 		cmd.append("\x1A\x06");
373 		switch (val) {
374 			case 10 : case 13 : cmd.append("\x01\x01"); break;
375 			case 11 : case 14 : cmd.append("\x02\x01"); break;
376 			case 12 : case 15 : cmd.append("\x03\x01"); break;
377 		}
378 		cmd.append( post);
379 		waitFB("set digital mode ON/OFF");
380 	}
381 }
382 
get_modeA()383 int RIG_IC7851::get_modeA()
384 {
385 	int md = 0;
386 	string resp;
387 	size_t p;
388 	cmd.assign(pre_to).append("\x04").append(post);
389 	if (waitFOR(8, "get mode A")) {
390 		resp.assign(pre_fm).append("\x04");
391 		p = replystr.rfind(resp);
392 
393 		if (p == string::npos) return A.imode;
394 
395 		if (replystr[p+5] == -1) { A.imode = 0; return A.imode; }
396 
397 		for (md = 0; md < 10; md++) {
398 			if (replystr[p+5] == IC7851_mode_nbr[md]) {
399 				A.imode = md;
400 			}
401 		}
402 		if (A.imode < 2) {
403 			cmd.assign(pre_to).append("\x1A\x06").append(post);
404 			if (waitFOR(9, "data mode?")) {
405 				resp.assign(pre_fm).append("\x1A\x06");
406 				p = replystr.rfind(resp);
407 				if (p == string::npos) return A.imode;
408 				int dmode = replystr[p+6];
409 				if(dmode != 0) {
410 					if (A.imode == 0) A.imode = 9 + dmode;
411 					else if (A.imode == 1) A.imode = 12 + dmode;
412 				}
413 			}
414 		}
415 	}
416 	if (A.imode > 15) A.imode = 0;
417 	return A.imode;
418 }
419 
set_modeB(int val)420 void RIG_IC7851::set_modeB(int val)
421 {
422 	B.imode = val;
423 	cmd.assign(pre_to).append("\x06");
424 	cmd += IC7851_mode_nbr[val];
425 	cmd.append( post );
426 	waitFB("set modeB");
427 
428 // digital set / clear
429 	if (val >= 10) {
430 		cmd = pre_to;
431 		cmd.append("\x1A\x06");
432 		switch (val) {
433 			case 10 : case 13 : cmd.append("\x01\x01"); break;
434 			case 11 : case 14 : cmd.append("\x02\x01"); break;
435 			case 12 : case 15 : cmd.append("\x03\x01"); break;
436 		}
437 		cmd.append( post);
438 		waitFB("set digital mode ON/OFF");
439 	}
440 }
441 
get_modeB()442 int RIG_IC7851::get_modeB()
443 {
444 	int md = 0;
445 	string resp;
446 	size_t p;
447 	cmd.assign(pre_to).append("\x04").append(post);
448 
449 	if (waitFOR(8, "get mode B")) {
450 		resp.assign(pre_fm).append("\x04");
451 		p = replystr.rfind(resp);
452 
453 		if (p == string::npos) return B.imode;
454 
455 		if (replystr[p+5] == -1) { B.imode = 0; return B.imode; }
456 
457 		for (md = 0; md < 10; md++)
458 			if (replystr[p+5] == IC7851_mode_nbr[md])
459 				break;
460 
461 		if (md == 10) md = 0;
462 		B.imode = md;
463 		if (B.imode < 2) {
464 			cmd.assign(pre_to).append("\x1A\x06").append(post);
465 			if (waitFOR(9, "data mode?")) {
466 				resp.assign(pre_fm).append("\x1A\x06");
467 				p = replystr.rfind(resp);
468 				if (p == string::npos) return B.imode;
469 				int dmode = replystr[p+6];
470 				if(dmode != 0) {
471 					if (B.imode == 0) B.imode = 9 + dmode;
472 					else if (B.imode == 1) B.imode = 12 + dmode;
473 				}
474 			}
475 		}
476 	}
477 	if (B.imode > 15) B.imode = 0;
478 	return B.imode;
479 }
480 
get_bwA()481 int RIG_IC7851::get_bwA()
482 {
483 	if (A.imode == 5) return 0;
484 	cmd = pre_to;
485 	cmd.append("\x1a\x03");
486 	cmd.append(post);
487 	if (waitFOR(8, "get bwA")) {
488 		A.iBW = fm_bcd(replystr.substr(6), 2);
489 	}
490 	return A.iBW;
491 }
492 
set_bwA(int val)493 void RIG_IC7851::set_bwA(int val)
494 {
495 	A.iBW = val;
496 	if (A.imode == 5) return;
497 	cmd = pre_to;
498 	cmd.append("\x1a\x03");
499 	cmd.append(to_bcd(A.iBW, 2));
500 	cmd.append(post);
501 	if (IC7851_DEBUG)
502 		LOG_INFO("%s", str2hex(cmd.data(), cmd.length()));
503 	waitFB("set bwA");
504 }
505 
get_bwB()506 int RIG_IC7851::get_bwB()
507 {
508 	if (B.imode == 5) return 0;
509 	cmd = pre_to;
510 	cmd.append("\x1a\x03");
511 	cmd.append(post);
512 	if (waitFOR(8, "get bwB")) {
513 		B.iBW = fm_bcd(replystr.substr(6), 2);
514 	}
515 	return B.iBW;
516 }
517 
set_bwB(int val)518 void RIG_IC7851::set_bwB(int val)
519 {
520 	B.iBW = val;
521 	if (B.imode == 5) return;
522 	cmd = pre_to;
523 	cmd.append("\x1a\x03");
524 	cmd.append(to_bcd(A.iBW, 2));
525 	cmd.append(post);
526 	if (IC7851_DEBUG)
527 		LOG_INFO("%s", str2hex(cmd.data(), cmd.length()));
528 	waitFB("set bwB");
529 }
530 
adjust_bandwidth(int m)531 int RIG_IC7851::adjust_bandwidth(int m)
532 {
533 	int bw = 0;
534 	switch (m) {
535 		case 2: // AM
536 			bandwidths_ = IC7851_am_bws;
537 			bw_vals_ = IC7851_bw_vals_AM;
538 			bw = 19;
539 			break;
540 		case 5: // FM
541 			bandwidths_ = IC7851_fm_bws;
542 			bw_vals_ = IC7851_bw_vals_FM;
543 			bw = 0;
544 			break;
545 		case 3: case 7: // CW
546 			bandwidths_ = IC7851_ssb_bws;
547 			bw_vals_ = IC7851_bw_vals_SSB;
548 			bw = 12;
549 			break;
550 		case 4: case 8: // RTTY
551 			bandwidths_ = IC7851_ssb_bws;
552 			bw_vals_ = IC7851_bw_vals_SSB;
553 			bw = 12;
554 			break;
555 		case 0: case 1: // SSB
556 		case 12: case 13: // PKT
557 		default:
558 			bandwidths_ = IC7851_ssb_bws;
559 			bw_vals_ = IC7851_bw_vals_SSB;
560 			bw = 34;
561 			break;
562 	}
563 	return bw;
564 }
565 
bwtable(int m)566 const char ** RIG_IC7851::bwtable(int m)
567 {
568 	const char ** table;
569 	switch (m) {
570 		case 2: // AM
571 			table = IC7851_am_bws;
572 			break;
573 		case 5: // FM
574 			table = IC7851_fm_bws;
575 			break;
576 		case 3: case 7: // CW
577 		case 4: case 8: // RTTY
578 		case 0: case 1: // SSB
579 		case 12: case 13: // PKT
580 		default:
581 			table = IC7851_ssb_bws;
582 			break;
583 	}
584 	return table;
585 }
586 
def_bandwidth(int m)587 int RIG_IC7851::def_bandwidth(int m)
588 {
589 	int bw = 0;
590 	switch (m) {
591 		case 2: // AM
592 			bw = 19;
593 			break;
594 		case 5: // FM
595 			bw = 0;
596 			break;
597 		case 4: case 8: // RTTY
598 			bw = 12;
599 			break;
600 		case 3: case 7: // CW
601 			bw = 12;
602 			break;
603 		case 0: case 1: // SSB
604 		case 12: case 13: // PKT
605 		default:
606 			bw = 34;
607 	}
608 	return bw;
609 }
610 
set_mic_gain(int v)611 void RIG_IC7851::set_mic_gain(int v)
612 {
613 	if (progStatus.USBaudio) return;
614 	int ICvol = (int)(v * 255 / 100);
615 
616 	cmd = pre_to;
617 	cmd.append("\x14\x0B");
618 	cmd.append(to_bcd(ICvol, 3));
619 	cmd.append( post );
620 	waitFB("set mic gain");
621 }
622 
623 static const char *atten_labels[] = {
624 "ATT", "3 dB", "6 dB", "9 dB", "12 dB", "15 dB", "18 dB", "21 dB"};
625 
next_attenuator()626 int  RIG_IC7851::next_attenuator()
627 {
628 	if (atten_level >= 7) return 0;
629 	else return (atten_level + 1);
630 }
631 
set_attenuator(int val)632 void RIG_IC7851::set_attenuator(int val)
633 {
634 	atten_level = val;
635 
636 	int cmdval = atten_level;
637 	atten_label(atten_labels[atten_level], true);
638 	cmd = pre_to;
639 	cmd += '\x11';
640 	cmd += cmdval;
641 	cmd.append( post );
642 	waitFB("set_attenuator");
643 }
644 
get_attenuator()645 int RIG_IC7851::get_attenuator()
646 {
647 	cmd = pre_to;
648 	cmd += '\x11';
649 	cmd.append( post );
650 	if (waitFOR(7, "get attenuator")) {
651 		if (replystr[4] == 0x06) {
652 			atten_level = replystr[5];
653 			if (atten_level >= 0 && atten_level <= 7)
654 				atten_label(atten_labels[atten_level], true);
655 		}
656 	}
657 	return atten_level;
658 }
659 
set_compression(int on,int val)660 void RIG_IC7851::set_compression(int on, int val)
661 {
662 	if (on) {
663 		cmd.assign(pre_to).append("\x14\x0E");
664 		cmd.append(to_bcd(val * 255 / 100, 3));
665 		cmd.append( post );
666 		waitFB("set comp");
667 
668 		cmd = pre_to;
669 		cmd.append("\x16\x44");
670 		cmd += '\x01';
671 		cmd.append(post);
672 		waitFB("set Comp ON");
673 
674 	} else{
675 		cmd.assign(pre_to).append("\x16\x44");
676 		cmd += '\x00';
677 		cmd.append(post);
678 		waitFB("set Comp OFF");
679 	}
680 }
681 
set_vox_onoff()682 void RIG_IC7851::set_vox_onoff()
683 {
684 	if (progStatus.vox_onoff) {
685 		cmd.assign(pre_to).append("\x16\x46\x01");
686 		cmd.append( post );
687 		waitFB("set vox ON");
688 	} else {
689 		cmd.assign(pre_to).append("\x16\x46");
690 		cmd += '\x00';		// ALH
691 		cmd.append( post );
692 		waitFB("set vox OFF");
693 	}
694 }
695 
set_vox_gain()696 void RIG_IC7851::set_vox_gain()
697 {
698 	cmd.assign(pre_to).append("\x14\x16");
699 	cmd.append(to_bcd((int)(progStatus.vox_gain * 2.55), 3));
700 	cmd.append( post );
701 	waitFB("SET vox gain");
702 }
703 
set_vox_anti()704 void RIG_IC7851::set_vox_anti()
705 {
706 	cmd.assign(pre_to).append("\x14\x17");
707 	cmd.append(to_bcd((int)(progStatus.vox_anti * 2.55), 3));
708 	cmd.append( post );
709 	waitFB("SET anti-vox");
710 }
711 
set_vox_hang()712 void RIG_IC7851::set_vox_hang()
713 {
714 	cmd.assign(pre_to).append("\x1A\x05\0x01\0x83");
715 	cmd.append(to_bcd((int)(progStatus.vox_hang / 10 ), 2));
716 	cmd.append( post );
717 	waitFB("SET vox hang");
718 }
719 
get_vox_hang_min_max_step(int & min,int & max,int & step)720 void RIG_IC7851::get_vox_hang_min_max_step(int &min, int &max, int &step)
721 {
722 	min = 0; max = 200; step = 10;
723 }
724 
725 // CW controls
726 
set_cw_wpm()727 void RIG_IC7851::set_cw_wpm()
728 {
729 	cmd.assign(pre_to).append("\x14\x0C");
730 	cmd.append(to_bcd(round((progStatus.cw_wpm - 6) * 255 / (48 - 6)), 3));
731 	cmd.append( post );
732 	waitFB("SET cw wpm");
733 }
734 
735 // not implemented -- work in progress
736 //int RIG_IC7851::get_cw_wpm()
737 //{
738 //	int wpm = progStatus.cw_wpm;
739 //	cmd = pre_to;
740 //	cmd.append("\x14\x0C");
741 //	cmd.append(post);
742 //	resp = pre_fm;
743 //	cmd.append("\x14\x0C");
744 //	if (waitFOR(9, "get WPM")) {
745 //		size_t p = replystr.rfind(resp);
746 //		if (p != string::npos) {
747 //			wpm = replystr[p + 6];
748 //
749 //	}
750 //}
751 
get_cw_wpm_min_max(int & min,int & max)752 void RIG_IC7851::get_cw_wpm_min_max(int &min, int &max)
753 {
754 	min = 6; max = 48;
755 }
756 
set_cw_qsk()757 void RIG_IC7851::set_cw_qsk()
758 {
759 	int n = round(progStatus.cw_qsk * 10);
760 	cmd.assign(pre_to).append("\x14\x0F");
761 	cmd.append(to_bcd(n, 3));
762 	cmd.append(post);
763 	waitFB("Set cw qsk delay");
764 }
765 
set_cw_spot_tone()766 void RIG_IC7851::set_cw_spot_tone()
767 {
768 	cmd.assign(pre_to).append("\x14\x09"); // values 0=300Hz 255=900Hz
769 	int n = round((progStatus.cw_spot_tone - 300) * 255.0 / 600.0);
770 	if (n > 255) n = 255;
771 	if (n < 0) n = 0;
772 	cmd.append(to_bcd(n, 3));
773 	cmd.append( post );
774 	waitFB("SET cw spot tone");
775 }
776 
set_cw_vol()777 void RIG_IC7851::set_cw_vol()
778 {
779 	cmd.assign(pre_to);
780 	cmd.append("\x14\x15");
781 	cmd.append(to_bcd((int)(progStatus.cw_vol * 2.55), 3));
782 	cmd.append( post );
783 	waitFB("SET cw sidetone volume");
784 }
785 
get_cw_qsk_min_max_step(double & min,double & max,double & step)786 void RIG_IC7851::get_cw_qsk_min_max_step(double &min, double &max, double &step)
787 {
788 	min = 2.0; max = 13.0; step = 0.1;
789 }
790 
get_cw_spot_tone_min_max_step(int & min,int & max,int & step)791 void RIG_IC7851::get_cw_spot_tone_min_max_step(int &min, int &max, int &step)
792 {
793 	min = 300; max = 900; step = 5;
794 }
795 
796 // Tranceiver PTT on/off
set_PTT_control(int val)797 void RIG_IC7851::set_PTT_control(int val)
798 {
799 	cmd = pre_to;
800 	cmd += '\x1c';
801 	cmd += '\x00';
802 	cmd += (unsigned char) val;
803 	cmd.append( post );
804 	waitFB("set ptt");
805 	ptt_ = val;
806 }
807 
get_PTT()808 int RIG_IC7851::get_PTT()
809 {
810 	cmd = pre_to;
811 	cmd += '\x1c'; cmd += '\x00';
812 	string resp = pre_fm;
813 	resp += '\x1c'; resp += '\x00';
814 	cmd.append(post);
815 	if (waitFOR(8, "get PTT")) {
816 		size_t p = replystr.rfind(resp);
817 		if (p != string::npos)
818 			ptt_ = replystr[p + 6];
819 	}
820 	return ptt_;
821 }
822 
get_pc_min_max_step(double & min,double & max,double & step)823 void RIG_IC7851::get_pc_min_max_step(double &min, double &max, double &step)
824 {
825 	min = 5; pmax = max = 200; step = 1;
826 }
827 
828 // Volume control val 0 ... 100
set_volume_control(int val)829 void RIG_IC7851::set_volume_control(int val)
830 {
831 	cmd = pre_to;
832 	cmd.append("\x14\x01");
833 	cmd.append(bcd255(val));
834 	cmd.append( post );
835 	waitFB("set vol");
836 }
837 
get_volume_control()838 int RIG_IC7851::get_volume_control()
839 {
840 	int val = progStatus.volume;
841 	string cstr = "\x14\x01";
842 	string resp = pre_fm;
843 	resp.append(cstr);
844 	cmd = pre_to;
845 	cmd.append(cstr);
846 	cmd.append( post );
847 	if (waitFOR(9, "get vol")) {
848 		size_t p = replystr.rfind(resp);
849 		if (p != string::npos)
850 			val = num100(replystr.substr(p+6));
851 	}
852 	return val;
853 }
854 
get_vol_min_max_step(int & min,int & max,int & step)855 void RIG_IC7851::get_vol_min_max_step(int &min, int &max, int &step)
856 {
857 	min = 0; max = 100; step = 1;
858 }
859 
get_smeter()860 int RIG_IC7851::get_smeter()
861 {
862 	string cstr = "\x15\x02";
863 	string resp = pre_fm;
864 	resp.append(cstr);
865 	cmd = pre_to;
866 	cmd.append(cstr);
867 	cmd.append( post );
868 	int mtr= -1;
869 	if (waitFOR(9, "get smeter")) {
870 		size_t p = replystr.rfind(resp);
871 		if (p != string::npos) {
872 			mtr = fm_bcd(replystr.substr(p+6), 3);
873 			mtr = (int)ceil(mtr /2.55);
874 			if (mtr > 100) mtr = 100;
875 		}
876 	}
877 	return mtr;
878 }
879 
880 struct pwrpair {int mtr; float pwr;};
881 
882 static pwrpair pwrtbl[] = {
883 {0, 0.0},
884 {40, 20.0},
885 {76, 40.0},
886 {92, 50.0},
887 {103, 60.0},
888 {124, 80.0},
889 {143, 100.0},
890 {183, 150.0},
891 {213, 200.0},
892 {255, 300.0} };
893 
get_power_out(void)894 int RIG_IC7851::get_power_out(void)
895 {
896 	string cstr = "\x15\x11";
897 	string resp = pre_fm;
898 	resp.append(cstr);
899 	cmd = pre_to;
900 	cmd.append(cstr);
901 	cmd.append( post );
902 	int mtr= 0;
903 	if (waitFOR(9, "get power out")) {
904 		size_t p = replystr.rfind(resp);
905 		if (p != string::npos) {
906 			mtr = fm_bcd(replystr.substr(p+6), 3);
907 			size_t i = 0;
908 			for (i = 0; i < sizeof(pwrtbl) / sizeof(pwrpair) - 1; i++)
909 				if (mtr >= pwrtbl[i].mtr && mtr < pwrtbl[i+1].mtr)
910 					break;
911 			if (mtr < 0) mtr = 0;
912 			if (mtr > 213) mtr = 213;
913 			mtr = (int)ceil(pwrtbl[i].pwr +
914 				(pwrtbl[i+1].pwr - pwrtbl[i].pwr)*(mtr - pwrtbl[i].mtr)/(pwrtbl[i+1].mtr - pwrtbl[i].mtr));
915 
916 			if (mtr > 100) mtr = 100;
917 		}
918 	}
919 	return mtr;
920 }
921 
get_swr(void)922 int RIG_IC7851::get_swr(void)
923 {
924 	string cstr = "\x15\x12";
925 	string resp = pre_fm;
926 	resp.append(cstr);
927 	cmd = pre_to;
928 	cmd.append(cstr);
929 	cmd.append( post );
930 	int mtr= -1;
931 	if (waitFOR(9, "get swr")) {
932 		size_t p = replystr.rfind(resp);
933 		if (p != string::npos) {
934 			mtr = fm_bcd(replystr.substr(p+6), 3);
935 			mtr = (int)ceil(mtr /2.55);
936 			if (mtr > 100) mtr = 100;
937 		}
938 	}
939 	return mtr;
940 }
941 
get_alc(void)942 int RIG_IC7851::get_alc(void)
943 {
944 	string cstr = "\x15\x13";
945 	string resp = pre_fm;
946 	resp.append(cstr);
947 	cmd = pre_to;
948 	cmd.append(cstr);
949 	cmd.append( post );
950 	int mtr= -1;
951 	if (waitFOR(9, "get alc")) {
952 		size_t p = replystr.rfind(resp);
953 		if (p != string::npos) {
954 			mtr = fm_bcd(replystr.substr(p+6), 3);
955 			mtr = (int)ceil(mtr /2.55);
956 			if (mtr > 100) mtr = 100;
957 		}
958 	}
959 	return mtr;
960 }
961 
set_pbt_inner(int val)962 void RIG_IC7851::set_pbt_inner(int val)
963 {
964 	int shift = 128 + val * 128 / 50;
965 	if (shift < 0) shift = 0;
966 	if (shift > 255) shift = 255;
967 
968 	cmd = pre_to;
969 	cmd.append("\x14\x07");
970 	cmd.append(to_bcd(shift, 3));
971 	cmd.append(post);
972 	waitFB("set PBT inner");
973 }
974 
set_pbt_outer(int val)975 void RIG_IC7851::set_pbt_outer(int val)
976 {
977 	int shift = 128 + val * 128 / 50;
978 	if (shift < 0) shift = 0;
979 	if (shift > 255) shift = 255;
980 
981 	cmd = pre_to;
982 	cmd.append("\x14\x08");
983 	cmd.append(to_bcd(shift, 3));
984 	cmd.append(post);
985 	waitFB("set PBT outer");
986 }
987 
get_pbt_inner()988 int RIG_IC7851::get_pbt_inner()
989 {
990 	int val = 0;
991 	string cstr = "\x14\x07";
992 	string resp = pre_fm;
993 	resp.append(cstr);
994 	cmd = pre_to;
995 	cmd.append(cstr);
996 	cmd.append( post );
997 	if (waitFOR(9, "get pbt inner")) {
998 		size_t p = replystr.rfind(resp);
999 		if (p != string::npos) {
1000 			val = num100(replystr.substr(p+6));
1001 			val -= 50;
1002 		}
1003 	}
1004 	rig_trace(2, "get_pbt_inner()", str2hex(replystr.c_str(), replystr.length()));
1005 	return val;
1006 }
1007 
get_pbt_outer()1008 int RIG_IC7851::get_pbt_outer()
1009 {
1010 	int val = 0;
1011 	string cstr = "\x14\x08";
1012 	string resp = pre_fm;
1013 	resp.append(cstr);
1014 	cmd = pre_to;
1015 	cmd.append(cstr);
1016 	cmd.append( post );
1017 	if (waitFOR(9, "get pbt outer")) {
1018 		size_t p = replystr.rfind(resp);
1019 		if (p != string::npos) {
1020 			val = num100(replystr.substr(p+6));
1021 			val -= 50;
1022 		}
1023 	}
1024 	rig_trace(2, "get_pbt_outer()", str2hex(replystr.c_str(), replystr.length()));
1025 	return val;
1026 }
1027 
1028 // Read/Write band stack registers
1029 //
1030 // Read 23 bytes
1031 //
1032 //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22
1033 // FE FE nn E0 1A 01 bd rn f5 f4 f3 f2 f1 mo fi fg t1 t2 t3 r1 r2 r3 FD
1034 // Write 23 bytes
1035 //
1036 // FE FE E0 nn 1A 01 bd rn f5 f4 f3 f2 f1 mo fi fg t1 t2 t3 r1 r2 r3 FD
1037 //
1038 // nn - CI-V address
1039 // bd - band selection 1/2/3
1040 // rn - register number 1/2/3
1041 // f5..f1 - frequency BCD reverse
1042 // mo - mode
1043 // fi - filter #
1044 // fg flags: x01 use Tx tone, x02 use Rx tone, x10 data mode
1045 // t1..t3 - tx tone BCD fwd
1046 // r1..r3 - rx tone BCD fwd
1047 //
1048 // FE FE E0 94 1A 01 06 01 70 99 08 18 00 01 03 10 00 08 85 00 08 85 FD
1049 //
1050 // band 6; freq 0018,089,970; USB; data mode; t 88.5; r 88.5
1051 
get_band_selection(int v)1052 void RIG_IC7851::get_band_selection(int v)
1053 {
1054 	cmd.assign(pre_to);
1055 	cmd.append("\x1A\x01");
1056 	cmd += to_bcd_be( v, 2 );
1057 	cmd += '\x01';
1058 	cmd.append( post );
1059 
1060 	if (waitFOR(23, "get band stack")) {
1061 		set_trace(2, "get band stack", str2hex(replystr.c_str(), replystr.length()));
1062 		size_t p = replystr.rfind(pre_fm);
1063 		if (p != string::npos) {
1064 			unsigned long int bandfreq = fm_bcd_be(replystr.substr(p+8, 5), 10);
1065 			int bandmode = replystr[p+13];
1066 			int bandfilter = replystr[p+14];
1067 			int banddata = replystr[p+15] & 0x10;
1068 			int tone = fm_bcd(replystr.substr(p+16, 3), 6);
1069 			size_t index = 0;
1070 			for (index = 0; index < sizeof(PL_tones) / sizeof(*PL_tones); index++)
1071 				if (tone == PL_tones[index]) break;
1072 			tTONE = index;
1073 			tone = fm_bcd(replystr.substr(p+19, 3), 6);
1074 			for (index = 0; index < sizeof(PL_tones) / sizeof(*PL_tones); index++)
1075 				if (tone == PL_tones[index]) break;
1076 			rTONE = index;
1077 			if ((bandmode == 0) && banddata)
1078 				bandmode = ((banddata == 0x10) ? 10 :
1079 							(banddata == 0x20) ? 11 :
1080 							(banddata == 0x30) ? 12 : 0);
1081 			if ((bandmode == 1) && banddata)
1082 				bandmode = ((banddata == 0x10) ? 13 :
1083 							(banddata == 0x20) ? 14 :
1084 							(banddata == 0x30) ? 15 : 1);
1085 			if (useB) {
1086 				set_vfoB(bandfreq);
1087 				set_modeB(bandmode);
1088 				set_FILT(bandfilter);
1089 			} else {
1090 				set_vfoA(bandfreq);
1091 				set_modeA(bandmode);
1092 				set_FILT(bandfilter);
1093 			}
1094 		}
1095 	} else
1096 		set_trace(2, "get band stack", str2hex(replystr.c_str(), replystr.length()));
1097 }
1098 
set_band_selection(int v)1099 void RIG_IC7851::set_band_selection(int v)
1100 {
1101 	unsigned long int freq = (useB ? B.freq : A.freq);
1102 	int mode = (useB ? B.imode : A.imode);
1103 
1104 	cmd.assign(pre_to);
1105 	cmd.append("\x1A\x01");
1106 	cmd += to_bcd_be( v, 2 );
1107 	cmd += '\x01';
1108 	cmd.append( to_bcd_be( freq, 10 ) );
1109 	cmd += mode;
1110 	cmd += '\x01';
1111 	cmd += '\x00';
1112 	cmd.append(to_bcd(PL_tones[tTONE], 6));
1113 	cmd.append(to_bcd(PL_tones[rTONE], 6));
1114 	cmd.append(post);
1115 	waitFB("set_band_selection");
1116 	set_trace(2, "set_band_selection()", str2hex(replystr.c_str(), replystr.length()));
1117 
1118 	cmd.assign(pre_to);
1119 	cmd.append("\x1A\x01");
1120 	cmd += to_bcd_be( v, 2 );
1121 	cmd += '\x01';
1122 	cmd.append( post );
1123 
1124 	waitFOR(23, "get band stack");
1125 }
1126 
set_notch(bool on,int freq)1127 void RIG_IC7851::set_notch(bool on, int freq)
1128 {
1129 	int hexval;
1130 	switch (vfo->imode) {
1131 		default:
1132 		case USB7851: case USBD17851: case USBD27851: case USBD37851: case RTTYR7851:
1133 			hexval = freq - 1500;
1134 			break;
1135 		case LSB7851: case LSBD17851: case LSBD27851: case LSBD37851: case RTTY7851:
1136 			hexval = 1500 - freq;
1137 			break;
1138 		case CW7851:
1139 			hexval = progStatus.cw_spot_tone - freq;
1140 			break;
1141 		case CWR7851:
1142 			hexval = freq - progStatus.cw_spot_tone;
1143 			break;
1144 	}
1145 
1146 	hexval /= 20;
1147 	hexval += 128;
1148 	if (hexval < 0) hexval = 0;
1149 	if (hexval > 255) hexval = 255;
1150 
1151 	cmd = pre_to;
1152 	cmd.append("\x16\x48");
1153 	cmd += on ? '\x01' : '\x00';
1154 	cmd.append(post);
1155 	waitFB("set notch");
1156 
1157 	cmd = pre_to;
1158 	cmd.append("\x14\x0D");
1159 	cmd.append(to_bcd(hexval,3));
1160 	cmd.append(post);
1161 	waitFB("set notch val");
1162 }
1163 
get_notch(int & val)1164 bool RIG_IC7851::get_notch(int &val)
1165 {
1166 	bool on = false;
1167 	val = 1500;
1168 
1169 	string cstr = "\x16\x48";
1170 	string resp = pre_fm;
1171 	resp.append(cstr);
1172 	cmd = pre_to;
1173 	cmd.append(cstr);
1174 	cmd.append( post );
1175 	if (waitFOR(8, "get notch")) {
1176 		size_t p = replystr.rfind(resp);
1177 		if (p != string::npos)
1178 			on = replystr[p + 6];
1179 		cmd = pre_to;
1180 		resp = pre_fm;
1181 		cstr = "\x14\x0D";
1182 		cmd.append(cstr);
1183 		resp.append(cstr);
1184 		cmd.append(post);
1185 		if (waitFOR(9, "notch val")) {
1186 			size_t p = replystr.rfind(resp);
1187 			if (p != string::npos) {
1188 				val = (int)ceil(fm_bcd(replystr.substr(p+6),3));
1189 				val -= 128;
1190 				val *= 20;
1191 				switch (vfo->imode) {
1192 					default:
1193 					case USB7851: case USBD17851: case USBD27851: case USBD37851: case RTTYR7851:
1194 						val = 1500 + val;
1195 						break;
1196 					case LSB: case LSBD17851: case LSBD27851: case LSBD37851: case RTTY7851:
1197 						val = 1500 - val;
1198 						break;
1199 					case CW7851:
1200 						val = progStatus.cw_spot_tone - val;
1201 						break;
1202 					case CWR7851:
1203 						val = progStatus.cw_spot_tone + val;
1204 						break;
1205 				}
1206 			}
1207 		}
1208 	}
1209 	return on;
1210 }
1211 
get_notch_min_max_step(int & min,int & max,int & step)1212 void RIG_IC7851::get_notch_min_max_step(int &min, int &max, int &step)
1213 {
1214 	switch (vfo->imode) {
1215 		default:
1216 		case USB7851: case USBD17851: case USBD27851: case USBD37851: case RTTYR7851:
1217 		case LSB7851: case LSBD17851: case LSBD27851: case LSBD37851: case RTTY7851:
1218 			min = 0; max = 3000; step = 20; break;
1219 		case CW7851: case CWR7851:
1220 			min = progStatus.cw_spot_tone - 500;
1221 			max = progStatus.cw_spot_tone + 500;
1222 			step = 20;
1223 			break;
1224 	}
1225 }
1226 
1227