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
|
|
16
|
"""Baofeng UV3r radio management module"""
|
17
|
|
18
|
import time
|
19
|
import os
|
20
|
import logging
|
21
|
|
22
|
from wouxun_common import do_download, do_upload
|
23
|
from chirp import util, chirp_common, bitwise, errors, directory
|
24
|
from chirp.settings import RadioSetting, RadioSettingGroup, \
|
25
|
RadioSettingValueBoolean, RadioSettingValueList, \
|
26
|
RadioSettingValueInteger, RadioSettingValueString, \
|
27
|
RadioSettingValueFloat, RadioSettings
|
28
|
|
29
|
LOG = logging.getLogger(__name__)
|
30
|
|
31
|
|
32
|
def _uv3r_prep(radio):
|
33
|
radio.pipe.write("\x05PROGRAM")
|
34
|
ack = radio.pipe.read(1)
|
35
|
if ack != "\x06":
|
36
|
raise errors.RadioError("Radio did not ACK first command")
|
37
|
|
38
|
radio.pipe.write("\x02")
|
39
|
ident = radio.pipe.read(8)
|
40
|
if len(ident) != 8:
|
41
|
LOG.debug(util.hexprint(ident))
|
42
|
raise errors.RadioError("Radio did not send identification")
|
43
|
|
44
|
radio.pipe.write("\x06")
|
45
|
if radio.pipe.read(1) != "\x06":
|
46
|
raise errors.RadioError("Radio did not ACK ident")
|
47
|
|
48
|
|
49
|
def uv3r_prep(radio):
|
50
|
"""Do the UV3R identification dance"""
|
51
|
for _i in range(0, 10):
|
52
|
try:
|
53
|
return _uv3r_prep(radio)
|
54
|
except errors.RadioError, e:
|
55
|
time.sleep(1)
|
56
|
|
57
|
raise e
|
58
|
|
59
|
|
60
|
def uv3r_download(radio):
|
61
|
"""Talk to a UV3R and do a download"""
|
62
|
try:
|
63
|
uv3r_prep(radio)
|
64
|
return do_download(radio, 0x0000, 0x0E40, 0x0010)
|
65
|
except errors.RadioError:
|
66
|
raise
|
67
|
except Exception, e:
|
68
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
69
|
|
70
|
|
71
|
def uv3r_upload(radio):
|
72
|
"""Talk to a UV3R and do an upload"""
|
73
|
try:
|
74
|
uv3r_prep(radio)
|
75
|
return do_upload(radio, 0x0000, 0x0E40, 0x0010)
|
76
|
except errors.RadioError:
|
77
|
raise
|
78
|
except Exception, e:
|
79
|
raise errors.RadioError("Failed to communicate with radio: %s" % e)
|
80
|
|
81
|
#the second hex value in unknown is the step
|
82
|
|
83
|
UV3R_MEM_FORMAT = """
|
84
|
#seekto 0x0010;
|
85
|
struct {
|
86
|
lbcd rx_freq[4];
|
87
|
u8 rxtone;
|
88
|
lbcd offset[4];
|
89
|
u8 txtone;
|
90
|
u8 ishighpower:1,
|
91
|
iswide:1,
|
92
|
dtcsinvt:1,
|
93
|
unknown1:1,
|
94
|
dtcsinvr:1,
|
95
|
unknown2:1,
|
96
|
duplex:2;
|
97
|
u8 step;
|
98
|
lbcd tx_freq[4];
|
99
|
} tx_memory[99];
|
100
|
|
101
|
#seekto 0x0780;
|
102
|
struct {
|
103
|
lbcd lower_vhf[2];
|
104
|
lbcd upper_vhf[2];
|
105
|
lbcd lower_uhf[2];
|
106
|
lbcd upper_uhf[2];
|
107
|
} limits;
|
108
|
|
109
|
struct vfosettings {
|
110
|
lbcd freq[4];
|
111
|
u8 rxtone;
|
112
|
u8 unknown1;
|
113
|
lbcd offset[3];
|
114
|
u8 txtone;
|
115
|
u8 power:1,
|
116
|
bandwidth:1,
|
117
|
unknown2:4,
|
118
|
duplex:2;
|
119
|
u8 step;
|
120
|
u8 unknown3[4];
|
121
|
};
|
122
|
|
123
|
#seekto 0x0790;
|
124
|
struct {
|
125
|
struct vfosettings uhf;
|
126
|
struct vfosettings vhf;
|
127
|
} vfo;
|
128
|
|
129
|
#seekto 0x07C2;
|
130
|
struct {
|
131
|
u8 squelch;
|
132
|
u8 vox;
|
133
|
u8 timeout;
|
134
|
u8 save:1,
|
135
|
unknown_1:1,
|
136
|
dw:1,
|
137
|
ste:1,
|
138
|
beep:1,
|
139
|
unknown_2:1,
|
140
|
bclo:1,
|
141
|
ch_flag:1;
|
142
|
u8 backlight:2,
|
143
|
relaym:1,
|
144
|
scanm:1,
|
145
|
pri:1,
|
146
|
unknown_3:3;
|
147
|
u8 unknown_4[3];
|
148
|
u8 pri_ch;
|
149
|
} settings;
|
150
|
|
151
|
#seekto 0x07E0;
|
152
|
u16 fm_presets[16];
|
153
|
|
154
|
#seekto 0x0810;
|
155
|
struct {
|
156
|
lbcd rx_freq[4];
|
157
|
u8 rxtone;
|
158
|
lbcd offset[4];
|
159
|
u8 txtone;
|
160
|
u8 ishighpower:1,
|
161
|
iswide:1,
|
162
|
dtcsinvt:1,
|
163
|
unknown1:1,
|
164
|
dtcsinvr:1,
|
165
|
unknown2:1,
|
166
|
duplex:2;
|
167
|
u8 step;
|
168
|
lbcd tx_freq[4];
|
169
|
} rx_memory[99];
|
170
|
|
171
|
#seekto 0x1008;
|
172
|
struct {
|
173
|
u8 unknown[8];
|
174
|
u8 name[6];
|
175
|
u8 pad[2];
|
176
|
} names[128];
|
177
|
"""
|
178
|
|
179
|
STEPS = [5.0, 6.25, 10.0, 12.5, 20.0, 25.0]
|
180
|
STEP_LIST = [str(x) for x in STEPS]
|
181
|
BACKLIGHT_LIST = ["Off", "Key", "Continuous"]
|
182
|
TIMEOUT_LIST = ["Off"] + ["%s sec" % x for x in range(30, 210, 30)]
|
183
|
SCANM_LIST = ["TO", "CO"]
|
184
|
PRI_CH_LIST = ["Off"] + ["%s" % x for x in range(1, 100)]
|
185
|
CH_FLAG_LIST = ["Freq Mode", "Channel Mode"]
|
186
|
POWER_LIST = ["Low", "High"]
|
187
|
BANDWIDTH_LIST = ["Narrow", "Wide"]
|
188
|
DUPLEX_LIST = ["Off", "-", "+"]
|
189
|
STE_LIST = ["On", "Off"]
|
190
|
|
191
|
UV3R_DUPLEX = ["", "-", "+", ""]
|
192
|
UV3R_POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
|
193
|
chirp_common.PowerLevel("Low", watts=0.50)]
|
194
|
UV3R_DTCS_POL = ["NN", "NR", "RN", "RR"]
|
195
|
|
196
|
|
197
|
@directory.register
|
198
|
class UV3RRadio(chirp_common.CloneModeRadio):
|
199
|
"""Baofeng UV-3R"""
|
200
|
VENDOR = "Baofeng"
|
201
|
MODEL = "UV-3R"
|
202
|
|
203
|
def get_features(self):
|
204
|
rf = chirp_common.RadioFeatures()
|
205
|
rf.has_settings = True
|
206
|
rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
|
207
|
rf.valid_modes = ["FM", "NFM"]
|
208
|
rf.valid_power_levels = UV3R_POWER_LEVELS
|
209
|
rf.valid_bands = [(136000000, 235000000), (400000000, 529000000)]
|
210
|
rf.valid_skips = []
|
211
|
rf.valid_duplexes = ["", "-", "+", "split"]
|
212
|
rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
|
213
|
"->Tone", "->DTCS"]
|
214
|
rf.valid_tuning_steps = [5.0, 6.25, 10.0, 12.5, 20.0, 25.0]
|
215
|
rf.has_ctone = True
|
216
|
rf.has_cross = True
|
217
|
rf.has_tuning_step = True
|
218
|
rf.has_bank = False
|
219
|
rf.has_name = False
|
220
|
rf.can_odd_split = True
|
221
|
rf.memory_bounds = (1, 99)
|
222
|
return rf
|
223
|
|
224
|
def sync_in(self):
|
225
|
self._mmap = uv3r_download(self)
|
226
|
self.process_mmap()
|
227
|
|
228
|
def sync_out(self):
|
229
|
uv3r_upload(self)
|
230
|
|
231
|
def process_mmap(self):
|
232
|
self._memobj = bitwise.parse(UV3R_MEM_FORMAT, self._mmap)
|
233
|
|
234
|
def get_memory(self, number):
|
235
|
_mem = self._memobj.rx_memory[number - 1]
|
236
|
mem = chirp_common.Memory()
|
237
|
mem.number = number
|
238
|
|
239
|
if _mem.get_raw()[0] == "\xff":
|
240
|
mem.empty = True
|
241
|
return mem
|
242
|
|
243
|
mem.freq = int(_mem.rx_freq) * 10
|
244
|
mem.offset = int(_mem.offset) * 1000
|
245
|
mem.duplex = UV3R_DUPLEX[_mem.duplex]
|
246
|
if mem.offset > 60000000:
|
247
|
if mem.duplex == "+":
|
248
|
mem.offset = mem.freq + mem.offset
|
249
|
elif mem.duplex == "-":
|
250
|
mem.offset = mem.freq - mem.offset
|
251
|
mem.duplex = "split"
|
252
|
mem.power = UV3R_POWER_LEVELS[1 - _mem.ishighpower]
|
253
|
if not _mem.iswide:
|
254
|
mem.mode = "NFM"
|
255
|
|
256
|
dtcspol = (int(_mem.dtcsinvt) << 1) + _mem.dtcsinvr
|
257
|
mem.dtcs_polarity = UV3R_DTCS_POL[dtcspol]
|
258
|
|
259
|
if _mem.txtone in [0, 0xFF]:
|
260
|
txmode = ""
|
261
|
elif _mem.txtone < 0x33:
|
262
|
mem.rtone = chirp_common.TONES[_mem.txtone - 1]
|
263
|
txmode = "Tone"
|
264
|
elif _mem.txtone >= 0x33:
|
265
|
tcode = chirp_common.DTCS_CODES[_mem.txtone - 0x33]
|
266
|
mem.dtcs = tcode
|
267
|
txmode = "DTCS"
|
268
|
else:
|
269
|
LOG.warn("Bug: tx_mode is %02x" % _mem.txtone)
|
270
|
|
271
|
if _mem.rxtone in [0, 0xFF]:
|
272
|
rxmode = ""
|
273
|
elif _mem.rxtone < 0x33:
|
274
|
mem.ctone = chirp_common.TONES[_mem.rxtone - 1]
|
275
|
rxmode = "Tone"
|
276
|
elif _mem.rxtone >= 0x33:
|
277
|
rcode = chirp_common.DTCS_CODES[_mem.rxtone - 0x33]
|
278
|
mem.dtcs = rcode
|
279
|
rxmode = "DTCS"
|
280
|
else:
|
281
|
LOG.warn("Bug: rx_mode is %02x" % _mem.rxtone)
|
282
|
|
283
|
if txmode == "Tone" and not rxmode:
|
284
|
mem.tmode = "Tone"
|
285
|
elif txmode == rxmode and txmode == "Tone" and mem.rtone == mem.ctone:
|
286
|
mem.tmode = "TSQL"
|
287
|
elif txmode == rxmode and txmode == "DTCS":
|
288
|
mem.tmode = "DTCS"
|
289
|
elif rxmode or txmode:
|
290
|
mem.tmode = "Cross"
|
291
|
mem.cross_mode = "%s->%s" % (txmode, rxmode)
|
292
|
|
293
|
|
294
|
mem.tuning_step = chirp_common.TUNING_STEPS[_mem.step]
|
295
|
|
296
|
return mem
|
297
|
|
298
|
def _set_tone(self, _mem, which, value, mode):
|
299
|
if mode == "Tone":
|
300
|
val = chirp_common.TONES.index(value) + 1
|
301
|
elif mode == "DTCS":
|
302
|
val = chirp_common.DTCS_CODES.index(value) + 0x33
|
303
|
elif mode == "":
|
304
|
val = 0
|
305
|
else:
|
306
|
raise errors.RadioError("Internal error: tmode %s" % mode)
|
307
|
|
308
|
setattr(_mem, which, val)
|
309
|
|
310
|
def _set_memory(self, mem, _mem):
|
311
|
if mem.empty:
|
312
|
_mem.set_raw("\xff" * 16)
|
313
|
return
|
314
|
|
315
|
_mem.rx_freq = mem.freq / 10
|
316
|
if mem.duplex == "split":
|
317
|
diff = mem.freq - mem.offset
|
318
|
_mem.offset = abs(diff) / 1000
|
319
|
_mem.duplex = UV3R_DUPLEX.index(diff < 0 and "+" or "-")
|
320
|
for i in range(0, 4):
|
321
|
_mem.tx_freq[i].set_raw("\xFF")
|
322
|
elif mem.duplex == "+":
|
323
|
_mem.offset = mem.offset / 1000
|
324
|
_mem.duplex = UV3R_DUPLEX.index(mem.duplex)
|
325
|
_mem.tx_freq = (mem.freq + mem.offset) / 10
|
326
|
elif mem.duplex == "-":
|
327
|
_mem.offset = mem.offset / 1000
|
328
|
_mem.duplex = UV3R_DUPLEX.index(mem.duplex)
|
329
|
_mem.tx_freq = (mem.freq - mem.offset) / 10
|
330
|
elif mem.duplex == "":
|
331
|
_mem.offset = mem.offset / 1000
|
332
|
_mem.duplex = UV3R_DUPLEX.index(mem.duplex)
|
333
|
_mem.tx_freq = (mem.freq) / 10
|
334
|
|
335
|
|
336
|
_mem.ishighpower = mem.power == UV3R_POWER_LEVELS[0]
|
337
|
_mem.iswide = mem.mode == "FM"
|
338
|
|
339
|
_mem.dtcsinvt = mem.dtcs_polarity[0] == "R"
|
340
|
_mem.dtcsinvr = mem.dtcs_polarity[1] == "R"
|
341
|
|
342
|
rxtone = txtone = 0
|
343
|
rxmode = txmode = ""
|
344
|
|
345
|
if mem.tmode == "DTCS":
|
346
|
rxmode = txmode = "DTCS"
|
347
|
rxtone = txtone = mem.dtcs
|
348
|
elif mem.tmode and mem.tmode != "Cross":
|
349
|
rxtone = txtone = mem.tmode == "Tone" and mem.rtone or mem.ctone
|
350
|
txmode = "Tone"
|
351
|
rxmode = mem.tmode == "TSQL" and "Tone" or ""
|
352
|
elif mem.tmode == "Cross":
|
353
|
txmode, rxmode = mem.cross_mode.split("->", 1)
|
354
|
|
355
|
if txmode == "DTCS":
|
356
|
txtone = mem.dtcs
|
357
|
elif txmode == "Tone":
|
358
|
txtone = mem.rtone
|
359
|
|
360
|
if rxmode == "DTCS":
|
361
|
rxtone = mem.dtcs
|
362
|
elif rxmode == "Tone":
|
363
|
rxtone = mem.ctone
|
364
|
|
365
|
self._set_tone(_mem, "txtone", txtone, txmode)
|
366
|
self._set_tone(_mem, "rxtone", rxtone, rxmode)
|
367
|
|
368
|
_mem.step = chirp_common.TUNING_STEPS.index(mem.tuning_step)
|
369
|
|
370
|
|
371
|
def set_memory(self, mem):
|
372
|
_tmem = self._memobj.tx_memory[mem.number - 1]
|
373
|
_rmem = self._memobj.rx_memory[mem.number - 1]
|
374
|
|
375
|
self._set_memory(mem, _tmem)
|
376
|
self._set_memory(mem, _rmem)
|
377
|
|
378
|
def get_settings(self):
|
379
|
_settings = self._memobj.settings
|
380
|
_vfo = self._memobj.vfo
|
381
|
basic = RadioSettingGroup("basic", "Basic Settings")
|
382
|
group = RadioSettings(basic)
|
383
|
|
384
|
rs = RadioSetting("squelch", "Squelch Level",
|
385
|
RadioSettingValueInteger(0, 9, _settings.squelch))
|
386
|
basic.append(rs)
|
387
|
|
388
|
rs = RadioSetting("backlight", "LCD Back Light",
|
389
|
RadioSettingValueList(
|
390
|
BACKLIGHT_LIST,
|
391
|
BACKLIGHT_LIST[_settings.backlight]))
|
392
|
basic.append(rs)
|
393
|
|
394
|
rs = RadioSetting("beep", "Keypad Beep",
|
395
|
RadioSettingValueBoolean(_settings.beep))
|
396
|
basic.append(rs)
|
397
|
|
398
|
rs = RadioSetting("vox", "VOX Level (0=OFF)",
|
399
|
RadioSettingValueInteger(0, 9, _settings.vox))
|
400
|
basic.append(rs)
|
401
|
|
402
|
rs = RadioSetting("dw", "Dual Watch",
|
403
|
RadioSettingValueBoolean(_settings.dw))
|
404
|
basic.append(rs)
|
405
|
|
406
|
rs = RadioSetting("ste", "Squelch Tail Eliminate",
|
407
|
RadioSettingValueList(
|
408
|
STE_LIST, STE_LIST[_settings.ste]))
|
409
|
basic.append(rs)
|
410
|
|
411
|
rs = RadioSetting("save", "Battery Saver",
|
412
|
RadioSettingValueBoolean(_settings.save))
|
413
|
basic.append(rs)
|
414
|
|
415
|
rs = RadioSetting("timeout", "Time Out Timer",
|
416
|
RadioSettingValueList(
|
417
|
TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout]))
|
418
|
basic.append(rs)
|
419
|
|
420
|
rs = RadioSetting("scanm", "Scan Mode",
|
421
|
RadioSettingValueList(
|
422
|
SCANM_LIST, SCANM_LIST[_settings.scanm]))
|
423
|
basic.append(rs)
|
424
|
|
425
|
rs = RadioSetting("relaym", "Repeater Sound Response",
|
426
|
RadioSettingValueBoolean(_settings.relaym))
|
427
|
basic.append(rs)
|
428
|
|
429
|
rs = RadioSetting("bclo", "Busy Channel Lock Out",
|
430
|
RadioSettingValueBoolean(_settings.bclo))
|
431
|
basic.append(rs)
|
432
|
|
433
|
rs = RadioSetting("pri", "Priority Channel Scanning",
|
434
|
RadioSettingValueBoolean(_settings.pri))
|
435
|
basic.append(rs)
|
436
|
|
437
|
rs = RadioSetting("pri_ch", "Priority Channel",
|
438
|
RadioSettingValueList(
|
439
|
PRI_CH_LIST, PRI_CH_LIST[_settings.pri_ch]))
|
440
|
basic.append(rs)
|
441
|
|
442
|
rs = RadioSetting("ch_flag", "Display Mode",
|
443
|
RadioSettingValueList(
|
444
|
CH_FLAG_LIST, CH_FLAG_LIST[_settings.ch_flag]))
|
445
|
basic.append(rs)
|
446
|
|
447
|
_limit = int(self._memobj.limits.lower_vhf) / 10
|
448
|
if _limit < 115 or _limit > 239:
|
449
|
_limit = 144
|
450
|
rs = RadioSetting("limits.lower_vhf", "VHF Lower Limit (115-239 MHz)",
|
451
|
RadioSettingValueInteger(115, 235, _limit))
|
452
|
|
453
|
def apply_limit(setting, obj):
|
454
|
value = int(setting.value) * 10
|
455
|
obj.lower_vhf = value
|
456
|
rs.set_apply_callback(apply_limit, self._memobj.limits)
|
457
|
basic.append(rs)
|
458
|
|
459
|
_limit = int(self._memobj.limits.upper_vhf) / 10
|
460
|
if _limit < 115 or _limit > 239:
|
461
|
_limit = 146
|
462
|
rs = RadioSetting("limits.upper_vhf", "VHF Upper Limit (115-239 MHz)",
|
463
|
RadioSettingValueInteger(115, 235, _limit))
|
464
|
|
465
|
def apply_limit(setting, obj):
|
466
|
value = int(setting.value) * 10
|
467
|
obj.upper_vhf = value
|
468
|
rs.set_apply_callback(apply_limit, self._memobj.limits)
|
469
|
basic.append(rs)
|
470
|
|
471
|
_limit = int(self._memobj.limits.lower_uhf) / 10
|
472
|
if _limit < 200 or _limit > 529:
|
473
|
_limit = 420
|
474
|
rs = RadioSetting("limits.lower_uhf", "UHF Lower Limit (200-529 MHz)",
|
475
|
RadioSettingValueInteger(200, 529, _limit))
|
476
|
|
477
|
def apply_limit(setting, obj):
|
478
|
value = int(setting.value) * 10
|
479
|
obj.lower_uhf = value
|
480
|
rs.set_apply_callback(apply_limit, self._memobj.limits)
|
481
|
basic.append(rs)
|
482
|
|
483
|
_limit = int(self._memobj.limits.upper_uhf) / 10
|
484
|
if _limit < 200 or _limit > 529:
|
485
|
_limit = 450
|
486
|
rs = RadioSetting("limits.upper_uhf", "UHF Upper Limit (200-529 MHz)",
|
487
|
RadioSettingValueInteger(200, 529, _limit))
|
488
|
|
489
|
def apply_limit(setting, obj):
|
490
|
value = int(setting.value) * 10
|
491
|
obj.upper_uhf = value
|
492
|
rs.set_apply_callback(apply_limit, self._memobj.limits)
|
493
|
basic.append(rs)
|
494
|
|
495
|
vfo_preset = RadioSettingGroup("vfo_preset", "VFO Presets")
|
496
|
group.append(vfo_preset)
|
497
|
|
498
|
def convert_bytes_to_freq(bytes):
|
499
|
real_freq = 0
|
500
|
real_freq = bytes
|
501
|
return chirp_common.format_freq(real_freq * 10)
|
502
|
|
503
|
def apply_vhf_freq(setting, obj):
|
504
|
value = chirp_common.parse_freq(str(setting.value)) / 10
|
505
|
obj.vhf.freq = value
|
506
|
|
507
|
val = RadioSettingValueString(
|
508
|
0, 10, convert_bytes_to_freq(int(_vfo.vhf.freq)))
|
509
|
rs = RadioSetting("vfo.vhf.freq",
|
510
|
"VHF RX Frequency (115.00000-236.00000)", val)
|
511
|
rs.set_apply_callback(apply_vhf_freq, _vfo)
|
512
|
vfo_preset.append(rs)
|
513
|
|
514
|
rs = RadioSetting("vfo.vhf.duplex", "Shift Direction",
|
515
|
RadioSettingValueList(
|
516
|
DUPLEX_LIST, DUPLEX_LIST[_vfo.vhf.duplex]))
|
517
|
vfo_preset.append(rs)
|
518
|
|
519
|
def convert_bytes_to_offset(bytes):
|
520
|
real_offset = 0
|
521
|
real_offset = bytes
|
522
|
return chirp_common.format_freq(real_offset * 10000)
|
523
|
|
524
|
def apply_vhf_offset(setting, obj):
|
525
|
value = chirp_common.parse_freq(str(setting.value)) / 10000
|
526
|
obj.vhf.offset = value
|
527
|
|
528
|
val = RadioSettingValueString(
|
529
|
0, 10, convert_bytes_to_offset(int(_vfo.vhf.offset)))
|
530
|
rs = RadioSetting("vfo.vhf.offset", "Offset (0.00-37.995)", val)
|
531
|
rs.set_apply_callback(apply_vhf_offset, _vfo)
|
532
|
vfo_preset.append(rs)
|
533
|
|
534
|
rs = RadioSetting("vfo.vhf.power", "Power Level",
|
535
|
RadioSettingValueList(
|
536
|
POWER_LIST, POWER_LIST[_vfo.vhf.power]))
|
537
|
vfo_preset.append(rs)
|
538
|
|
539
|
rs = RadioSetting("vfo.vhf.bandwidth", "Bandwidth",
|
540
|
RadioSettingValueList(
|
541
|
BANDWIDTH_LIST,
|
542
|
BANDWIDTH_LIST[_vfo.vhf.bandwidth]))
|
543
|
vfo_preset.append(rs)
|
544
|
|
545
|
rs = RadioSetting("vfo.vhf.step", "Step",
|
546
|
RadioSettingValueList(
|
547
|
STEP_LIST, STEP_LIST[_vfo.vhf.step]))
|
548
|
vfo_preset.append(rs)
|
549
|
|
550
|
def apply_uhf_freq(setting, obj):
|
551
|
value = chirp_common.parse_freq(str(setting.value)) / 10
|
552
|
obj.uhf.freq = value
|
553
|
|
554
|
val = RadioSettingValueString(
|
555
|
0, 10, convert_bytes_to_freq(int(_vfo.uhf.freq)))
|
556
|
rs = RadioSetting("vfo.uhf.freq",
|
557
|
"UHF RX Frequency (200.00000-529.00000)", val)
|
558
|
rs.set_apply_callback(apply_uhf_freq, _vfo)
|
559
|
vfo_preset.append(rs)
|
560
|
|
561
|
rs = RadioSetting("vfo.uhf.duplex", "Shift Direction",
|
562
|
RadioSettingValueList(
|
563
|
DUPLEX_LIST, DUPLEX_LIST[_vfo.uhf.duplex]))
|
564
|
vfo_preset.append(rs)
|
565
|
|
566
|
def apply_uhf_offset(setting, obj):
|
567
|
value = chirp_common.parse_freq(str(setting.value)) / 10000
|
568
|
obj.uhf.offset = value
|
569
|
|
570
|
val = RadioSettingValueString(
|
571
|
0, 10, convert_bytes_to_offset(int(_vfo.uhf.offset)))
|
572
|
rs = RadioSetting("vfo.uhf.offset", "Offset (0.00-69.995)", val)
|
573
|
rs.set_apply_callback(apply_uhf_offset, _vfo)
|
574
|
vfo_preset.append(rs)
|
575
|
|
576
|
rs = RadioSetting("vfo.uhf.power", "Power Level",
|
577
|
RadioSettingValueList(
|
578
|
POWER_LIST, POWER_LIST[_vfo.uhf.power]))
|
579
|
vfo_preset.append(rs)
|
580
|
|
581
|
rs = RadioSetting("vfo.uhf.bandwidth", "Bandwidth",
|
582
|
RadioSettingValueList(
|
583
|
BANDWIDTH_LIST,
|
584
|
BANDWIDTH_LIST[_vfo.uhf.bandwidth]))
|
585
|
vfo_preset.append(rs)
|
586
|
|
587
|
rs = RadioSetting("vfo.uhf.step", "Step",
|
588
|
RadioSettingValueList(
|
589
|
STEP_LIST, STEP_LIST[_vfo.uhf.step]))
|
590
|
vfo_preset.append(rs)
|
591
|
|
592
|
fm_preset = RadioSettingGroup("fm_preset", "FM Radio Presets")
|
593
|
group.append(fm_preset)
|
594
|
|
595
|
for i in range(0, 16):
|
596
|
if self._memobj.fm_presets[i] < 0x01AF:
|
597
|
used = True
|
598
|
preset = self._memobj.fm_presets[i] / 10.0 + 65
|
599
|
else:
|
600
|
used = False
|
601
|
preset = 65
|
602
|
rs = RadioSetting("fm_presets_%1i" % i, "FM Preset %i" % (i + 1),
|
603
|
RadioSettingValueBoolean(used),
|
604
|
RadioSettingValueFloat(65, 108, preset, 0.1, 1))
|
605
|
fm_preset.append(rs)
|
606
|
|
607
|
return group
|
608
|
|
609
|
def set_settings(self, settings):
|
610
|
_settings = self._memobj.settings
|
611
|
for element in settings:
|
612
|
if not isinstance(element, RadioSetting):
|
613
|
if element.get_name() == "fm_preset":
|
614
|
self._set_fm_preset(element)
|
615
|
else:
|
616
|
self.set_settings(element)
|
617
|
continue
|
618
|
else:
|
619
|
try:
|
620
|
name = element.get_name()
|
621
|
if "." in name:
|
622
|
bits = name.split(".")
|
623
|
obj = self._memobj
|
624
|
for bit in bits[:-1]:
|
625
|
if "/" in bit:
|
626
|
bit, index = bit.split("/", 1)
|
627
|
index = int(index)
|
628
|
obj = getattr(obj, bit)[index]
|
629
|
else:
|
630
|
obj = getattr(obj, bit)
|
631
|
setting = bits[-1]
|
632
|
else:
|
633
|
obj = _settings
|
634
|
setting = element.get_name()
|
635
|
|
636
|
if element.has_apply_callback():
|
637
|
LOG.debug("Using apply callback")
|
638
|
element.run_apply_callback()
|
639
|
else:
|
640
|
LOG.debug("Setting %s = %s" % (setting, element.value))
|
641
|
setattr(obj, setting, element.value)
|
642
|
except Exception, e:
|
643
|
LOG.debug(element.get_name())
|
644
|
raise
|
645
|
|
646
|
def _set_fm_preset(self, settings):
|
647
|
for element in settings:
|
648
|
try:
|
649
|
index = (int(element.get_name().split("_")[-1]))
|
650
|
val = element.value
|
651
|
if val[0].get_value():
|
652
|
value = int(val[1].get_value() * 10 - 650)
|
653
|
else:
|
654
|
value = 0x01AF
|
655
|
LOG.debug("Setting fm_presets[%1i] = %s" % (index, value))
|
656
|
setting = self._memobj.fm_presets
|
657
|
setting[index] = value
|
658
|
except Exception, e:
|
659
|
LOG.debug(element.get_name())
|
660
|
raise
|
661
|
|
662
|
@classmethod
|
663
|
def match_model(cls, filedata, filename):
|
664
|
return len(filedata) == 3648
|
665
|
|
666
|
def get_raw_memory(self, number):
|
667
|
_rmem = self._memobj.tx_memory[number - 1]
|
668
|
_tmem = self._memobj.rx_memory[number - 1]
|
669
|
return repr(_rmem) + repr(_tmem)
|