Project

General

Profile

Bug #4635 » baofeng_uv3r.PlusFix.20190306.py

Modified source to fix repeater offset issues with later UV-3R Plus firmware. - Jesse Byrd, 03/05/2019 11:27 PM

 
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)
(2-2/3)