1
|
# Copyright 2011 Dan Smith <dsmith@danplanet.com>
|
2
|
#
|
3
|
# This program is free software: you can redistribute it and/or modify
|
4
|
# it under the terms of the GNU General Public License as published by
|
5
|
# the Free Software Foundation, either version 3 of the License, or
|
6
|
# (at your option) any later version.
|
7
|
#
|
8
|
# This program is distributed in the hope that it will be useful,
|
9
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
# GNU General Public License for more details.
|
12
|
#
|
13
|
# You should have received a copy of the GNU General Public License
|
14
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
import time
|
16
|
import logging
|
17
|
import re
|
18
|
|
19
|
from chirp.drivers import yaesu_clone
|
20
|
from chirp import chirp_common, directory, errors, bitwise, util
|
21
|
from chirp.settings import RadioSetting, RadioSettingGroup, \
|
22
|
RadioSettingValueInteger, RadioSettingValueList, \
|
23
|
RadioSettingValueBoolean, RadioSettingValueString, \
|
24
|
RadioSettings
|
25
|
|
26
|
LOG = logging.getLogger(__name__)
|
27
|
|
28
|
MEM_FORMAT = """
|
29
|
struct flag_struct {
|
30
|
u8 unknown1f:5,
|
31
|
skip:1,
|
32
|
mask:1,
|
33
|
used:1;
|
34
|
};
|
35
|
|
36
|
struct mem_struct {
|
37
|
u8 showname:1,
|
38
|
unknown1:3,
|
39
|
unknown2:2,
|
40
|
unknown3:2;
|
41
|
u8 ishighpower:1,
|
42
|
power:2,
|
43
|
unknown4:1,
|
44
|
tuning_step:4;
|
45
|
u8 codememno:4,
|
46
|
codeorpage:2,
|
47
|
duplex:2;
|
48
|
u8 tmode:2,
|
49
|
tone:6;
|
50
|
u8 unknown5:1,
|
51
|
dtcs:7;
|
52
|
u8 unknown6:6,
|
53
|
mode:2;
|
54
|
bbcd freq[3];
|
55
|
bbcd offset[3];
|
56
|
u8 name[4];
|
57
|
};
|
58
|
|
59
|
#seekto 0x000C;
|
60
|
struct {
|
61
|
u8 extendedrx_flg; // Seems to be set to 03 when extended rx is enabled
|
62
|
u8 extendedrx; // Seems to be set to 01 when extended rx is enabled
|
63
|
} extendedrx_struct; // UNFINISHED!!
|
64
|
|
65
|
#seekto 0x001A;
|
66
|
struct flag_struct flag[100];
|
67
|
|
68
|
#seekto 0x079C;
|
69
|
struct flag_struct flag_repeat[100];
|
70
|
|
71
|
#seekto 0x00AA;
|
72
|
struct mem_struct memory[100];
|
73
|
struct mem_struct special[11];
|
74
|
|
75
|
#seekto 0x08C7;
|
76
|
struct {
|
77
|
u8 sub_display;
|
78
|
u8 unknown1s;
|
79
|
u8 apo;
|
80
|
u8 timeout;
|
81
|
u8 lock;
|
82
|
u8 rxsave;
|
83
|
u8 lamp;
|
84
|
u8 bell;
|
85
|
u8 cwid[16];
|
86
|
u8 unknown2s;
|
87
|
u8 artsmode;
|
88
|
u8 artsbeep;
|
89
|
u8 unknown3s;
|
90
|
u8 unknown4s;
|
91
|
struct {
|
92
|
u8 header[3];
|
93
|
u8 mem_num;
|
94
|
u8 digits[16];
|
95
|
} autodial[8];
|
96
|
struct {
|
97
|
u8 header[3];
|
98
|
u8 mem_num;
|
99
|
u8 digits[32];
|
100
|
} autodial9_ro;
|
101
|
bbcd pagingcodec_ro[2];
|
102
|
bbcd pagingcodep[2];
|
103
|
struct {
|
104
|
bbcd digits[2];
|
105
|
} pagingcode[6];
|
106
|
u8 code_dec_c_en:1,
|
107
|
code_dec_p_en:1,
|
108
|
code_dec_1_en:1,
|
109
|
code_dec_2_en:1,
|
110
|
code_dec_3_en:1,
|
111
|
code_dec_4_en:1,
|
112
|
code_dec_5_en:1,
|
113
|
code_dec_6_en:1;
|
114
|
u8 pagingspeed;
|
115
|
u8 pagingdelay;
|
116
|
u8 pagingbell;
|
117
|
u8 paginganswer;
|
118
|
|
119
|
#seekto 0x0E30;
|
120
|
u8 squelch; // squelch
|
121
|
u8 unknown0c;
|
122
|
u8 rptl:1, // repeater input tracking
|
123
|
amod:1, // auto mode
|
124
|
scnl:1, // scan lamp
|
125
|
resm:1, // scan resume mode 0=5sec, 1=carr
|
126
|
ars:1, // automatic repeater shift
|
127
|
keybeep:1, // keypad beep
|
128
|
lck:1, // lock
|
129
|
unknown1c:1;
|
130
|
u8 lgt:1,
|
131
|
pageamsg:1,
|
132
|
unknown2c:1,
|
133
|
bclo:1, // Busy channel lock out
|
134
|
unknown3c:2,
|
135
|
cwid_en:1, // CWID off/on
|
136
|
tsav:1; // TX save
|
137
|
u8 unknown4c:4,
|
138
|
artssped:1, // ARTS/SPED: 0=15s, 1=25s
|
139
|
unknown5c:1,
|
140
|
rvhm:1, // RVHM: 0=home, 1=rev
|
141
|
mon:1; // MON: 0=mon, 1=tcal
|
142
|
} settings;
|
143
|
|
144
|
#seekto 0x080E;
|
145
|
struct mem_struct vfo_mem[10];
|
146
|
|
147
|
|
148
|
"""
|
149
|
|
150
|
# 10 VFO memories: A145, A220, A380, A430, A800,
|
151
|
# B145, B220, B380, B430, B800
|
152
|
|
153
|
DUPLEX = ["", "-", "+"]
|
154
|
MODES = ["FM", "AM", "WFM"]
|
155
|
SKIP_VALUES = ["", "S"]
|
156
|
TMODES = ["", "Tone", "TSQL", "DTCS"]
|
157
|
TUNING_STEPS = [5.0, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0]
|
158
|
TONES = list(chirp_common.OLD_TONES)
|
159
|
|
160
|
# CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ ()+-=*/???|0123456789"
|
161
|
# the = displays as an underscored dash on radio
|
162
|
# the first ? is an uppercase delta - \xA7
|
163
|
# the second ? is an uppercase gamma - \xD1
|
164
|
# the third ? is an uppercase sigma - \xCF
|
165
|
NUMERIC_CHARSET = list("0123456789")
|
166
|
CHARSET = [str(x) for x in range(0, 10)] + \
|
167
|
[chr(x) for x in range(ord("A"), ord("Z")+1)] + \
|
168
|
list(" ()+-=*/" + ("\x00" * 3) + "|") + NUMERIC_CHARSET
|
169
|
DTMFCHARSET = NUMERIC_CHARSET + list("ABCD*#")
|
170
|
|
171
|
POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.0),
|
172
|
chirp_common.PowerLevel("L3", watts=2.5),
|
173
|
chirp_common.PowerLevel("L2", watts=1.0),
|
174
|
chirp_common.PowerLevel("L1", watts=0.1)]
|
175
|
SPECIALS = ["L1", "U1", "L2", "U2", "L3", "U3", "L4", "U4", "L5", "U5", "UNK"]
|
176
|
|
177
|
|
178
|
@directory.register
|
179
|
class FT50Radio(yaesu_clone.YaesuCloneModeRadio):
|
180
|
"""Yaesu FT-50"""
|
181
|
BAUD_RATE = 9600
|
182
|
VENDOR = "Yaesu"
|
183
|
MODEL = "FT-50"
|
184
|
|
185
|
_model = ""
|
186
|
_memsize = 3723
|
187
|
_block_lengths = [10, 16, 112, 16, 16, 1776, 1776, 1]
|
188
|
# _block_delay = 0.15
|
189
|
_block_size = 8
|
190
|
|
191
|
@classmethod
|
192
|
def get_prompts(cls):
|
193
|
rp = chirp_common.RadioPrompts()
|
194
|
rp.pre_download = _(
|
195
|
"1. Turn radio off.\n"
|
196
|
"2. Connect cable to MIC/SP jack.\n"
|
197
|
"3. Press and hold [PTT] & Knob while turning the\n"
|
198
|
" radio on.\n"
|
199
|
"4. <b>After clicking OK</b>, press the [PTT] switch to send"
|
200
|
" image.\n")
|
201
|
rp.pre_upload = _(
|
202
|
"1. Turn radio off.\n"
|
203
|
"2. Connect cable to MIC/SP jack.\n"
|
204
|
"3. Press and hold [PTT] & Knob while turning the\n"
|
205
|
" radio on.\n"
|
206
|
"4. Press the [MONI] switch (\"WAIT\" will appear on the LCD).\n"
|
207
|
"5. Press OK.\n")
|
208
|
return rp
|
209
|
|
210
|
def get_features(self):
|
211
|
rf = chirp_common.RadioFeatures()
|
212
|
rf.memory_bounds = (1, 100)
|
213
|
rf.valid_duplexes = DUPLEX
|
214
|
rf.valid_tmodes = TMODES
|
215
|
rf.valid_power_levels = POWER_LEVELS
|
216
|
rf.valid_tuning_steps = TUNING_STEPS
|
217
|
rf.valid_power_levels = POWER_LEVELS
|
218
|
rf.valid_characters = "".join(CHARSET)
|
219
|
rf.valid_name_length = 4
|
220
|
rf.valid_modes = MODES
|
221
|
# Specials not yet implemented
|
222
|
# rf.valid_special_chans = SPECIALS
|
223
|
rf.valid_bands = [(76000000, 200000000),
|
224
|
(300000000, 540000000),
|
225
|
(590000000, 999000000)]
|
226
|
# rf.can_odd_split = True
|
227
|
rf.has_ctone = False
|
228
|
rf.has_bank = False
|
229
|
rf.has_settings = True
|
230
|
rf.has_dtcs_polarity = False
|
231
|
|
232
|
return rf
|
233
|
|
234
|
def _checksums(self):
|
235
|
return [yaesu_clone.YaesuChecksum(0x0000, 0xE89)]
|
236
|
|
237
|
def process_mmap(self):
|
238
|
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
|
239
|
|
240
|
def get_raw_memory(self, number):
|
241
|
return repr(self._memobj.memory[number - 1])
|
242
|
|
243
|
def get_memory(self, number):
|
244
|
mem = chirp_common.Memory()
|
245
|
_mem = self._memobj.memory[number-1]
|
246
|
_flg = self._memobj.flag[number-1]
|
247
|
mem.number = number
|
248
|
|
249
|
# if not _flg.visible:
|
250
|
# mem.empty = True
|
251
|
if not _flg.used:
|
252
|
mem.empty = True
|
253
|
return mem
|
254
|
|
255
|
for i in _mem.name:
|
256
|
mem.name += CHARSET[i & 0x7F]
|
257
|
mem.name = mem.name.rstrip()
|
258
|
|
259
|
mem.freq = chirp_common.fix_rounded_step(int(_mem.freq) * 1000)
|
260
|
mem.duplex = DUPLEX[_mem.duplex]
|
261
|
mem.offset = chirp_common.fix_rounded_step(int(_mem.offset) * 1000)
|
262
|
mem.rtone = mem.ctone = TONES[_mem.tone]
|
263
|
mem.tmode = TMODES[_mem.tmode]
|
264
|
mem.mode = MODES[_mem.mode]
|
265
|
mem.tuning_step = TUNING_STEPS[_mem.tuning_step]
|
266
|
mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
|
267
|
# Power is stored as 2 bits to describe the 3 low power levels
|
268
|
# High power is determined by a different bit.
|
269
|
if not _mem.ishighpower:
|
270
|
mem.power = POWER_LEVELS[3 - _mem.power]
|
271
|
else:
|
272
|
mem.power = POWER_LEVELS[0]
|
273
|
mem.skip = SKIP_VALUES[_flg.skip]
|
274
|
|
275
|
return mem
|
276
|
|
277
|
def set_memory(self, mem):
|
278
|
_mem = self._memobj.memory[mem.number-1]
|
279
|
_flg = self._memobj.flag[mem.number-1]
|
280
|
_flg_repeat = self._memobj.flag_repeat[mem.number-1]
|
281
|
|
282
|
if mem.empty:
|
283
|
_flg.used = False
|
284
|
return
|
285
|
|
286
|
if (len(mem.name) == 0):
|
287
|
_mem.name = [0x24] * 4
|
288
|
_mem.showname = 0
|
289
|
else:
|
290
|
_mem.showname = 1
|
291
|
for i in range(0, 4):
|
292
|
_mem.name[i] = CHARSET.index(mem.name.ljust(4)[i])
|
293
|
|
294
|
_mem.freq = int(mem.freq / 1000)
|
295
|
_mem.duplex = DUPLEX.index(mem.duplex)
|
296
|
_mem.offset = int(mem.offset / 1000)
|
297
|
_mem.mode = MODES.index(mem.mode)
|
298
|
_mem.tuning_step = TUNING_STEPS.index(mem.tuning_step)
|
299
|
if mem.power:
|
300
|
if (mem.power == POWER_LEVELS[0]):
|
301
|
# low power level is not changed when high power is selected
|
302
|
_mem.ishighpower = 0x01
|
303
|
if (_mem.power == 3):
|
304
|
# Set low power to L3 (0x02) if it is
|
305
|
# set to 3 (new object default)
|
306
|
LOG.debug("SETTING DEFAULT?")
|
307
|
_mem.power = 0x02
|
308
|
else:
|
309
|
_mem.ishighpower = 0x00
|
310
|
_mem.power = 3 - POWER_LEVELS.index(mem.power)
|
311
|
else:
|
312
|
_mem.ishighpower = 0x01
|
313
|
_mem.power = 0x02
|
314
|
_mem.tmode = TMODES.index(mem.tmode)
|
315
|
try:
|
316
|
_mem.tone = TONES.index(mem.rtone)
|
317
|
except ValueError:
|
318
|
raise errors.UnsupportedToneError(
|
319
|
("This radio does not support tone %s" % mem.rtone))
|
320
|
_mem.dtcs = chirp_common.DTCS_CODES.index(mem.dtcs)
|
321
|
|
322
|
_flg.skip = SKIP_VALUES.index(mem.skip)
|
323
|
|
324
|
# initialize new channel to safe defaults
|
325
|
if not mem.empty and not _flg.used:
|
326
|
_flg.used = True
|
327
|
_flg.mask = True # Mask = True to be visible on radio
|
328
|
_mem.unknown1 = 0x00
|
329
|
_mem.unknown2 = 0x00
|
330
|
_mem.unknown3 = 0x00
|
331
|
_mem.unknown4 = 0x00
|
332
|
_mem.unknown5 = 0x00
|
333
|
_mem.unknown6 = 0x00
|
334
|
_mem.codememno = 0x02 # Not implemented in chirp
|
335
|
_mem.codeorpage = 0x00 # Not implemented in chirp
|
336
|
|
337
|
# Duplicate flags to repeated part in memory
|
338
|
_flg_repeat.skip = _flg.skip
|
339
|
_flg_repeat.mask = _flg.mask
|
340
|
_flg_repeat.used = _flg.used
|
341
|
|
342
|
def _decode_cwid(self, inarr):
|
343
|
LOG.debug("@_decode_chars, type: %s" % type(inarr))
|
344
|
LOG.debug(inarr)
|
345
|
outstr = ""
|
346
|
for i in inarr:
|
347
|
if i == 0xFF:
|
348
|
break
|
349
|
outstr += CHARSET[i & 0x7F]
|
350
|
LOG.debug(outstr)
|
351
|
return outstr.rstrip()
|
352
|
|
353
|
def _encode_cwid(self, instr, length=16):
|
354
|
LOG.debug("@_encode_chars, type: %s" % type(instr))
|
355
|
LOG.debug(instr)
|
356
|
outarr = []
|
357
|
instr = str(instr)
|
358
|
for i in range(0, length):
|
359
|
if i < len(instr):
|
360
|
outarr.append(CHARSET.index(instr[i]))
|
361
|
else:
|
362
|
outarr.append(0xFF)
|
363
|
return outarr
|
364
|
|
365
|
def get_settings(self):
|
366
|
_settings = self._memobj.settings
|
367
|
basic = RadioSettingGroup("basic", "Basic")
|
368
|
dtmf = RadioSettingGroup("dtmf", "DTMF Code & Paging")
|
369
|
arts = RadioSettingGroup("arts", "ARTS")
|
370
|
autodial = RadioSettingGroup("autodial", "AutoDial")
|
371
|
top = RadioSettings(basic, autodial, arts, dtmf)
|
372
|
|
373
|
rs = RadioSetting(
|
374
|
"squelch", "Squelch",
|
375
|
RadioSettingValueInteger(0, 15, _settings.squelch))
|
376
|
basic.append(rs)
|
377
|
|
378
|
rs = RadioSetting(
|
379
|
"keybeep", "Keypad Beep",
|
380
|
RadioSettingValueBoolean(_settings.keybeep))
|
381
|
basic.append(rs)
|
382
|
|
383
|
rs = RadioSetting(
|
384
|
"scnl", "Scan Lamp",
|
385
|
RadioSettingValueBoolean(_settings.scnl))
|
386
|
basic.append(rs)
|
387
|
|
388
|
options = ["off", "30m", "1h", "3h", "5h", "8h"]
|
389
|
rs = RadioSetting(
|
390
|
"apo", "APO time (hrs)",
|
391
|
RadioSettingValueList(options, options[_settings.apo]))
|
392
|
basic.append(rs)
|
393
|
|
394
|
options = ["off", "1m", "2.5m", "5m", "10m"]
|
395
|
rs = RadioSetting(
|
396
|
"timeout", "Time Out Timer",
|
397
|
RadioSettingValueList(options, options[_settings.timeout]))
|
398
|
basic.append(rs)
|
399
|
|
400
|
options = ["key", "dial", "key+dial", "ptt",
|
401
|
"key+ptt", "dial+ptt", "all"]
|
402
|
rs = RadioSetting(
|
403
|
"lock", "Lock mode",
|
404
|
RadioSettingValueList(options, options[_settings.lock]))
|
405
|
basic.append(rs)
|
406
|
|
407
|
options = ["off", "0.2", "0.3", "0.5", "1.0", "2.0"]
|
408
|
rs = RadioSetting(
|
409
|
"rxsave", "RX Save (sec)",
|
410
|
RadioSettingValueList(options, options[_settings.rxsave]))
|
411
|
basic.append(rs)
|
412
|
|
413
|
options = ["5sec", "key", "tgl"]
|
414
|
rs = RadioSetting(
|
415
|
"lamp", "Lamp mode",
|
416
|
RadioSettingValueList(options, options[_settings.lamp]))
|
417
|
basic.append(rs)
|
418
|
|
419
|
options = ["off", "1", "3", "5", "8", "rpt"]
|
420
|
rs = RadioSetting(
|
421
|
"bell", "Bell Repetitions",
|
422
|
RadioSettingValueList(options, options[_settings.bell]))
|
423
|
basic.append(rs)
|
424
|
|
425
|
rs = RadioSetting(
|
426
|
"cwid_en", "CWID Enable",
|
427
|
RadioSettingValueBoolean(_settings.cwid_en))
|
428
|
arts.append(rs)
|
429
|
|
430
|
cwid = RadioSettingValueString(
|
431
|
0, 16, self._decode_cwid(_settings.cwid.get_value()))
|
432
|
cwid.set_charset(CHARSET)
|
433
|
rs = RadioSetting("cwid", "CWID", cwid)
|
434
|
arts.append(rs)
|
435
|
|
436
|
options = ["off", "rx", "tx", "trx"]
|
437
|
rs = RadioSetting(
|
438
|
"artsmode", "ARTS Mode",
|
439
|
RadioSettingValueList(
|
440
|
options, options[_settings.artsmode]))
|
441
|
arts.append(rs)
|
442
|
|
443
|
options = ["off", "in range", "always"]
|
444
|
rs = RadioSetting(
|
445
|
"artsbeep", "ARTS Beep",
|
446
|
RadioSettingValueList(options, options[_settings.artsbeep]))
|
447
|
arts.append(rs)
|
448
|
|
449
|
for i in range(0, 8):
|
450
|
dialsettings = _settings.autodial[i]
|
451
|
dialstr = ""
|
452
|
for c in dialsettings.digits:
|
453
|
if c < len(DTMFCHARSET):
|
454
|
dialstr += DTMFCHARSET[c]
|
455
|
dialentry = RadioSettingValueString(0, 16, dialstr)
|
456
|
dialentry.set_charset(DTMFCHARSET + list(" "))
|
457
|
rs = RadioSetting("autodial" + str(i+1),
|
458
|
"AutoDial " + str(i+1), dialentry)
|
459
|
autodial.append(rs)
|
460
|
|
461
|
dialstr = ""
|
462
|
for c in _settings.autodial9_ro.digits:
|
463
|
if c < len(DTMFCHARSET):
|
464
|
dialstr += DTMFCHARSET[c]
|
465
|
dialentry = RadioSettingValueString(0, 32, dialstr)
|
466
|
dialentry.set_mutable(False)
|
467
|
rs = RadioSetting("autodial9_ro", "AutoDial 9 (read only)", dialentry)
|
468
|
autodial.append(rs)
|
469
|
|
470
|
options = ["50ms", "100ms"]
|
471
|
rs = RadioSetting(
|
472
|
"pagingspeed", "Paging Speed",
|
473
|
RadioSettingValueList(options, options[_settings.pagingspeed]))
|
474
|
dtmf.append(rs)
|
475
|
|
476
|
options = ["250ms", "450ms", "750ms", "1000ms"]
|
477
|
rs = RadioSetting(
|
478
|
"pagingdelay", "Paging Delay",
|
479
|
RadioSettingValueList(options, options[_settings.pagingdelay]))
|
480
|
dtmf.append(rs)
|
481
|
|
482
|
options = ["off", "1", "3", "5", "8", "rpt"]
|
483
|
rs = RadioSetting(
|
484
|
"pagingbell", "Paging Bell Repetitions",
|
485
|
RadioSettingValueList(options, options[_settings.pagingbell]))
|
486
|
dtmf.append(rs)
|
487
|
|
488
|
options = ["off", "ans", "for"]
|
489
|
rs = RadioSetting(
|
490
|
"paginganswer", "Paging Answerback",
|
491
|
RadioSettingValueList(options,
|
492
|
options[_settings.paginganswer]))
|
493
|
dtmf.append(rs)
|
494
|
|
495
|
rs = RadioSetting(
|
496
|
"code_dec_c_en", "Paging Code C Decode Enable",
|
497
|
RadioSettingValueBoolean(_settings.code_dec_c_en))
|
498
|
dtmf.append(rs)
|
499
|
|
500
|
_str = str(bitwise.bcd_to_int(_settings.pagingcodec_ro))
|
501
|
code = RadioSettingValueString(0, 3, _str)
|
502
|
code.set_charset(NUMERIC_CHARSET + list(" "))
|
503
|
code.set_mutable(False)
|
504
|
rs = RadioSetting("pagingcodec_ro", "Paging Code C (read only)", code)
|
505
|
dtmf.append(rs)
|
506
|
|
507
|
rs = RadioSetting(
|
508
|
"code_dec_p_en", "Paging Code P Decode Enable",
|
509
|
RadioSettingValueBoolean(_settings.code_dec_p_en))
|
510
|
dtmf.append(rs)
|
511
|
|
512
|
_str = str(bitwise.bcd_to_int(_settings.pagingcodep))
|
513
|
code = RadioSettingValueString(0, 3, _str)
|
514
|
code.set_charset(NUMERIC_CHARSET + list(" "))
|
515
|
rs = RadioSetting("pagingcodep", "Paging Code P", code)
|
516
|
dtmf.append(rs)
|
517
|
|
518
|
for i in range(0, 6):
|
519
|
num = str(i+1)
|
520
|
name = "code_dec_" + num + "_en"
|
521
|
rs = RadioSetting(
|
522
|
name, "Paging Code " + num + " Decode Enable",
|
523
|
RadioSettingValueBoolean(getattr(_settings, name)))
|
524
|
dtmf.append(rs)
|
525
|
|
526
|
_str = str(bitwise.bcd_to_int(_settings.pagingcode[i].digits))
|
527
|
code = RadioSettingValueString(0, 3, _str)
|
528
|
code.set_charset(NUMERIC_CHARSET + list(" "))
|
529
|
rs = RadioSetting("pagingcode" + num, "Paging Code " + num, code)
|
530
|
dtmf.append(rs)
|
531
|
|
532
|
return top
|
533
|
|
534
|
def set_settings(self, uisettings):
|
535
|
for element in uisettings:
|
536
|
if not isinstance(element, RadioSetting):
|
537
|
self.set_settings(element)
|
538
|
continue
|
539
|
if not element.changed():
|
540
|
continue
|
541
|
try:
|
542
|
setting = element.get_name()
|
543
|
_settings = self._memobj.settings
|
544
|
if re.match(r'autodial\d', setting):
|
545
|
# set autodial fields
|
546
|
dtmfstr = str(element.value).strip()
|
547
|
newval = []
|
548
|
for i in range(0, 16):
|
549
|
if i < len(dtmfstr):
|
550
|
newval.append(DTMFCHARSET.index(dtmfstr[i]))
|
551
|
else:
|
552
|
newval.append(0xFF)
|
553
|
LOG.debug(newval)
|
554
|
idx = int(setting[-1:]) - 1
|
555
|
_settings = self._memobj.settings.autodial[idx]
|
556
|
_settings.digits = newval
|
557
|
continue
|
558
|
if (setting == "pagingcodep"):
|
559
|
bitwise.int_to_bcd(_settings.pagingcodep,
|
560
|
int(element.value))
|
561
|
continue
|
562
|
if re.match(r'pagingcode\d', setting):
|
563
|
idx = int(setting[-1:]) - 1
|
564
|
bitwise.int_to_bcd(_settings.pagingcode[idx].digits,
|
565
|
int(element.value))
|
566
|
continue
|
567
|
newval = element.value
|
568
|
oldval = getattr(_settings, setting)
|
569
|
if setting == "cwid":
|
570
|
newval = self._encode_cwid(newval)
|
571
|
LOG.debug("Setting %s(%s) <= %s" % (setting, oldval, newval))
|
572
|
setattr(_settings, setting, newval)
|
573
|
except Exception:
|
574
|
LOG.debug(element.get_name())
|
575
|
raise
|
576
|
|
577
|
@classmethod
|
578
|
def match_model(cls, filedata, filename):
|
579
|
return len(filedata) == cls._memsize
|
580
|
|
581
|
def sync_out(self):
|
582
|
self.update_checksums()
|
583
|
return _clone_out(self)
|
584
|
|
585
|
|
586
|
def _clone_out(radio):
|
587
|
try:
|
588
|
return __clone_out(radio)
|
589
|
except Exception as e:
|
590
|
raise errors.RadioError("Failed to communicate with the radio: %s" % e)
|
591
|
|
592
|
|
593
|
def __clone_out(radio):
|
594
|
pipe = radio.pipe
|
595
|
pipe.timeout = 1
|
596
|
block_lengths = radio._block_lengths
|
597
|
total_written = 0
|
598
|
|
599
|
def _status():
|
600
|
status = chirp_common.Status()
|
601
|
status.msg = "Cloning to radio"
|
602
|
status.max = sum(block_lengths)
|
603
|
status.cur = total_written
|
604
|
radio.status_fn(status)
|
605
|
|
606
|
start = time.time()
|
607
|
|
608
|
blocks = 0
|
609
|
pos = 0
|
610
|
for block in radio._block_lengths:
|
611
|
blocks += 1
|
612
|
data = radio.get_mmap()[pos:pos + block]
|
613
|
LOG.debug("Sending block: %s" % util.hexprint(data))
|
614
|
|
615
|
recvd = b""
|
616
|
# Radio echos every block received
|
617
|
for byte in data:
|
618
|
time.sleep(0.1)
|
619
|
pipe.write(bytes([byte]))
|
620
|
# flush & sleep so don't loose ack
|
621
|
pipe.flush()
|
622
|
time.sleep(0.1)
|
623
|
recvd += pipe.read(1) # chew the echo
|
624
|
LOG.debug("Echo was %s" % util.hexprint(recvd))
|
625
|
LOG.debug("Bytes sent: %i" % len(data))
|
626
|
|
627
|
# Radio does not ack last block
|
628
|
if (blocks < 8):
|
629
|
buf = pipe.read(block)
|
630
|
LOG.debug("ACK attempt: " + util.hexprint(buf))
|
631
|
if buf and buf[0] != yaesu_clone.CMD_ACK:
|
632
|
buf = pipe.read(block)
|
633
|
if not buf or buf[-1] != yaesu_clone.CMD_ACK:
|
634
|
raise errors.RadioError("Radio did not ack block %i" % blocks)
|
635
|
|
636
|
total_written += len(data)
|
637
|
_status()
|
638
|
pos += block
|
639
|
|
640
|
pipe.read(pos) # Chew the echo if using a 2-pin cable
|
641
|
|
642
|
LOG.debug("Clone completed in %i seconds" % (time.time() - start))
|