Project

General

Profile

Bug #3353 » uvb5.py

Jim Unroe, 11/21/2016 05:47 PM

 
1
# Copyright 2013 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 2 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
import struct
17
import logging
18
from chirp import chirp_common, directory, bitwise, memmap, errors, util
19
from chirp.settings import RadioSetting, RadioSettingGroup, \
20
                RadioSettingValueBoolean, RadioSettingValueList, \
21
                RadioSettingValueInteger, RadioSettingValueString, \
22
                RadioSettingValueFloat, RadioSettings
23
from textwrap import dedent
24

    
25
LOG = logging.getLogger(__name__)
26

    
27
mem_format = """
28
struct memory {
29
  lbcd freq[4];
30
  lbcd offset[4];
31
  u8 unknown1:2,
32
     txpol:1,
33
     rxpol:1,
34
     compander:1,
35
     unknown2:3;
36
  u8 rxtone;
37
  u8 txtone;
38
  u8 pttid:1,
39
     scanadd:1,
40
     isnarrow:1,
41
     bcl:1,
42
     highpower:1,
43
     revfreq:1,
44
     duplex:2;
45
  u8 step;
46
  u8 unknown[3];
47
};
48

    
49
#seekto 0x0000;
50
char ident[32];
51
u8 blank[16];
52
struct memory vfo1;
53
struct memory channels[99];
54
#seekto 0x0850;
55
struct memory vfo2;
56

    
57
#seekto 0x09D0;
58
u16 fm_presets[16];
59

    
60
#seekto 0x0A30;
61
struct {
62
  u8 name[5];
63
} names[99];
64

    
65
#seekto 0x0D30;
66
struct {
67
  u8 squelch;
68
  u8 freqmode_ab:1,
69
     save_funct:1,
70
     backlight:1,
71
     beep_tone_disabled:1,
72
     roger:1,
73
     tdr:1,
74
     scantype:2;
75
  u8 language:1,
76
     workmode_b:1,
77
     workmode_a:1,
78
     workmode_fm:1,
79
     voice_prompt:1,
80
     fm:1,
81
     pttid:2;
82
  u8 unknown_0:5,
83
     timeout:3;
84
  u8 mdf_b:2,
85
     mdf_a:2,
86
     unknown_1:2,
87
     txtdr:2;
88
  u8 unknown_2:4,
89
     ste_disabled:1,
90
     unknown_3:2,
91
     sidetone:1;
92
  u8 vox;
93
  u8 unk1;
94
  u8 mem_chan_a;
95
  u16 fm_vfo;
96
  u8 unk4;
97
  u8 unk5;
98
  u8 mem_chan_b;
99
  u8 unk6;
100
  u8 last_menu; // number of last menu item accessed
101
} settings;
102

    
103
#seekto 0x0D50;
104
struct {
105
  u8 code[6];
106
} pttid;
107

    
108
#seekto 0x0F30;
109
struct {
110
  lbcd lower_vhf[2];
111
  lbcd upper_vhf[2];
112
  lbcd lower_uhf[2];
113
  lbcd upper_uhf[2];
114
} limits;
115

    
116
#seekto 0x0FF0;
117
struct {
118
  u8 vhfsquelch0;
119
  u8 vhfsquelch1;
120
  u8 vhfsquelch2;
121
  u8 vhfsquelch3;
122
  u8 vhfsquelch4;
123
  u8 vhfsquelch5;
124
  u8 vhfsquelch6;
125
  u8 vhfsquelch7;
126
  u8 vhfsquelch8;
127
  u8 vhfsquelch9;
128
  u8 unknown1[6];
129
  u8 uhfsquelch0;
130
  u8 uhfsquelch1;
131
  u8 uhfsquelch2;
132
  u8 uhfsquelch3;
133
  u8 uhfsquelch4;
134
  u8 uhfsquelch5;
135
  u8 uhfsquelch6;
136
  u8 uhfsquelch7;
137
  u8 uhfsquelch8;
138
  u8 uhfsquelch9;
139
  u8 unknown2[6];
140
  u8 vhfhipwr0;
141
  u8 vhfhipwr1;
142
  u8 vhfhipwr2;
143
  u8 vhfhipwr3;
144
  u8 vhfhipwr4;
145
  u8 vhfhipwr5;
146
  u8 vhfhipwr6;
147
  u8 vhfhipwr7;
148
  u8 vhflopwr0;
149
  u8 vhflopwr1;
150
  u8 vhflopwr2;
151
  u8 vhflopwr3;
152
  u8 vhflopwr4;
153
  u8 vhflopwr5;
154
  u8 vhflopwr6;
155
  u8 vhflopwr7;
156
  u8 uhfhipwr0;
157
  u8 uhfhipwr1;
158
  u8 uhfhipwr2;
159
  u8 uhfhipwr3;
160
  u8 uhfhipwr4;
161
  u8 uhfhipwr5;
162
  u8 uhfhipwr6;
163
  u8 uhfhipwr7;
164
  u8 uhflopwr0;
165
  u8 uhflopwr1;
166
  u8 uhflopwr2;
167
  u8 uhflopwr3;
168
  u8 uhflopwr4;
169
  u8 uhflopwr5;
170
  u8 uhflopwr6;
171
  u8 uhflopwr7;
172
} test;
173
"""
174

    
175

    
176
def do_ident(radio):
177
    radio.pipe.timeout = 3
178
    radio.pipe.write("\x05PROGRAM")
179
    for x in xrange(10):
180
        ack = radio.pipe.read(1)
181
        if ack == '\x06':
182
            break
183
    else:
184
        raise errors.RadioError("Radio did not ack programming mode")
185
    radio.pipe.write("\x02")
186
    ident = radio.pipe.read(8)
187
    LOG.debug(util.hexprint(ident))
188
    if not ident.startswith('HKT511'):
189
        raise errors.RadioError("Unsupported model")
190
    radio.pipe.write("\x06")
191
    ack = radio.pipe.read(1)
192
    if ack != "\x06":
193
        raise errors.RadioError("Radio did not ack ident")
194

    
195

    
196
def do_status(radio, direction, addr):
197
    status = chirp_common.Status()
198
    status.msg = "Cloning %s radio" % direction
199
    status.cur = addr
200
    status.max = 0x1000
201
    radio.status_fn(status)
202

    
203

    
204
def do_download(radio):
205
    do_ident(radio)
206
    data = "KT511 Radio Program data v1.08\x00\x00"
207
    data += ("\x00" * 16)
208
    firstack = None
209
    for i in range(0, 0x1000, 16):
210
        frame = struct.pack(">cHB", "R", i, 16)
211
        radio.pipe.write(frame)
212
        result = radio.pipe.read(20)
213
        if frame[1:4] != result[1:4]:
214
            LOG.debug(util.hexprint(result))
215
            raise errors.RadioError("Invalid response for address 0x%04x" % i)
216
        radio.pipe.write("\x06")
217
        ack = radio.pipe.read(1)
218
        if not firstack:
219
            firstack = ack
220
        else:
221
            if not ack == firstack:
222
                LOG.debug("first ack: %s ack received: %s",
223
                          util.hexprint(firstack), util.hexprint(ack))
224
                raise errors.RadioError("Unexpected response")
225
        data += result[4:]
226
        do_status(radio, "from", i)
227

    
228
    return memmap.MemoryMap(data)
229

    
230

    
231
def do_upload(radio):
232
    do_ident(radio)
233
    data = radio._mmap[0x0030:]
234

    
235
    for i in range(0, 0x1000, 16):
236
        frame = struct.pack(">cHB", "W", i, 16)
237
        frame += data[i:i + 16]
238
        radio.pipe.write(frame)
239
        ack = radio.pipe.read(1)
240
        if ack != "\x06":
241
            # new UV-B5/B6 radios do not support and will stop ACKing after 0x0f00
242
            if range == 0x0f10:
243
                # radio must not support test mode settings so quit uploading
244
                break
245
            else:
246
                raise errors.RadioError("Radio NAK'd block at address 0x%04x" % i)
247
        do_status(radio, "to", i)
248

    
249
DUPLEX = ["", "-", "+", 'off', "split"]
250
UVB5_STEPS = [5.00, 6.25, 10.0, 12.5, 20.0, 25.0]
251
CHARSET = "0123456789- ABCDEFGHIJKLMNOPQRSTUVWXYZ/_+*"
252
SPECIALS = {
253
    "VFO1": -2,
254
    "VFO2": -1,
255
    }
256
POWER_LEVELS = [chirp_common.PowerLevel("Low", watts=1),
257
                chirp_common.PowerLevel("High", watts=5)]
258

    
259

    
260
@directory.register
261
class BaofengUVB5(chirp_common.CloneModeRadio,
262
                  chirp_common.ExperimentalRadio):
263
    """Baofeng UV-B5"""
264
    VENDOR = "Baofeng"
265
    MODEL = "UV-B5"
266
    BAUD_RATE = 9600
267

    
268
    _memsize = 0x1000
269

    
270
    @classmethod
271
    def get_prompts(cls):
272
        rp = chirp_common.RadioPrompts()
273
        rp.experimental = \
274
            ('This version of the UV-B5 driver allows you to '
275
             'modify the Test Mode settings of your radio. This has been '
276
             'tested and reports from other users indicate that it is a '
277
             'safe thing to do. However, modifications to these values may '
278
             'have unintended consequences, including damage to your '
279
             'device. You have been warned. Proceed at your own risk!')
280
        rp.pre_download = _(dedent("""\
281
            1. Turn radio off.
282
            2. Connect cable to mic/spkr connector.
283
            3. Make sure connector is firmly connected.
284
            4. Turn radio on.
285
            5. Ensure that the radio is tuned to channel with no activity.
286
            6. Click OK to download image from device."""))
287
        rp.pre_upload = _(dedent("""\
288
            1. Turn radio off.
289
            2. Connect cable to mic/spkr connector.
290
            3. Make sure connector is firmly connected.
291
            4. Turn radio on.
292
            5. Ensure that the radio is tuned to channel with no activity.
293
            6. Click OK to upload image to device."""))
294
        return rp
295

    
296
    def get_features(self):
297
        rf = chirp_common.RadioFeatures()
298
        rf.has_settings = True
299
        rf.has_cross = True
300
        rf.has_rx_dtcs = True
301
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
302
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
303
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
304
        rf.valid_duplexes = DUPLEX
305
        rf.can_odd_split = True
306
        rf.valid_skips = ["", "S"]
307
        rf.valid_characters = CHARSET
308
        rf.valid_name_length = 5
309
        rf.valid_bands = [(130000000, 175000000),
310
                          (220000000, 269000000),
311
                          (400000000, 520000000)]
312
        rf.valid_modes = ["FM", "NFM"]
313
        rf.valid_special_chans = SPECIALS.keys()
314
        rf.valid_power_levels = POWER_LEVELS
315
        rf.has_ctone = True
316
        rf.has_bank = False
317
        rf.has_tuning_step = False
318
        rf.memory_bounds = (1, 99)
319
        return rf
320

    
321
    def sync_in(self):
322
        try:
323
            self._mmap = do_download(self)
324
        except errors.RadioError:
325
            raise
326
        except Exception, e:
327
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
328
        self.process_mmap()
329

    
330
    def sync_out(self):
331
        try:
332
            do_upload(self)
333
        except errors.RadioError:
334
            raise
335
        except Exception, e:
336
            raise errors.RadioError("Failed to communicate with radio: %s" % e)
337

    
338
    def process_mmap(self):
339
        self._memobj = bitwise.parse(mem_format, self._mmap)
340

    
341
    def get_raw_memory(self, number):
342
        return repr(self._memobj.channels[number - 1])
343

    
344
    def _decode_tone(self, value, flag):
345
        if value > 155:
346
            mode = val = pol = None
347
        elif value > 50:
348
            mode = 'DTCS'
349
            val = chirp_common.DTCS_CODES[value - 51]
350
            pol = flag and 'R' or 'N'
351
        elif value:
352
            mode = 'Tone'
353
            val = chirp_common.TONES[value - 1]
354
            pol = None
355
        else:
356
            mode = val = pol = None
357

    
358
        return mode, val, pol
359

    
360
    def _encode_tone(self, _mem, which, mode, val, pol):
361
        def _set(field, value):
362
            setattr(_mem, "%s%s" % (which, field), value)
363

    
364
        _set("pol", 0)
365
        if mode == "Tone":
366
            _set("tone", chirp_common.TONES.index(val) + 1)
367
        elif mode == "DTCS":
368
            _set("tone", chirp_common.DTCS_CODES.index(val) + 51)
369
            _set("pol", pol == "R")
370
        else:
371
            _set("tone", 0)
372

    
373
    def _get_memobjs(self, number):
374
        if isinstance(number, str):
375
            return (getattr(self._memobj, number.lower()), None)
376
        elif number < 0:
377
            for k, v in SPECIALS.items():
378
                if number == v:
379
                    return (getattr(self._memobj, k.lower()), None)
380
        else:
381
            return (self._memobj.channels[number - 1],
382
                    self._memobj.names[number - 1].name)
383

    
384
    def get_memory(self, number):
385
        _mem, _nam = self._get_memobjs(number)
386
        mem = chirp_common.Memory()
387
        if isinstance(number, str):
388
            mem.number = SPECIALS[number]
389
            mem.extd_number = number
390
        else:
391
            mem.number = number
392

    
393
        if _mem.freq.get_raw()[0] == "\xFF":
394
            mem.empty = True
395
            return mem
396

    
397
        mem.freq = int(_mem.freq) * 10
398
        mem.offset = int(_mem.offset) * 10
399

    
400
        chirp_common.split_tone_decode(
401
            mem,
402
            self._decode_tone(_mem.txtone, _mem.txpol),
403
            self._decode_tone(_mem.rxtone, _mem.rxpol))
404

    
405
        if _mem.step > 0x05:
406
            _mem.step = 0x00
407
        mem.duplex = DUPLEX[_mem.duplex]
408
        mem.mode = _mem.isnarrow and "NFM" or "FM"
409
        mem.skip = "" if _mem.scanadd else "S"
410
        mem.power = POWER_LEVELS[_mem.highpower]
411

    
412
        if mem.freq == mem.offset and mem.duplex == "-":
413
            mem.duplex = "off"
414
            mem.offset = 0
415

    
416
        if _nam:
417
            for char in _nam:
418
                try:
419
                    mem.name += CHARSET[char]
420
                except IndexError:
421
                    break
422
            mem.name = mem.name.rstrip()
423

    
424
        mem.extra = RadioSettingGroup("Extra", "extra")
425

    
426
        rs = RadioSetting("bcl", "BCL",
427
                          RadioSettingValueBoolean(_mem.bcl))
428
        mem.extra.append(rs)
429

    
430
        rs = RadioSetting("revfreq", "Reverse Duplex",
431
                          RadioSettingValueBoolean(_mem.revfreq))
432
        mem.extra.append(rs)
433

    
434
        rs = RadioSetting("pttid", "PTT ID",
435
                          RadioSettingValueBoolean(_mem.pttid))
436
        mem.extra.append(rs)
437

    
438
        rs = RadioSetting("compander", "Compander",
439
                          RadioSettingValueBoolean(_mem.compander))
440
        mem.extra.append(rs)
441

    
442
        return mem
443

    
444
    def set_memory(self, mem):
445
        _mem, _nam = self._get_memobjs(mem.number)
446

    
447
        if mem.empty:
448
            if _nam is None:
449
                raise errors.InvalidValueError("VFO channels can not be empty")
450
            _mem.set_raw("\xFF" * 16)
451
            return
452

    
453
        if _mem.get_raw() == ("\xFF" * 16):
454
            _mem.set_raw("\x00" * 13 + "\xFF" * 3)
455

    
456
        _mem.freq = mem.freq / 10
457

    
458
        if mem.duplex == "off":
459
            _mem.duplex = DUPLEX.index("-")
460
            _mem.offset = _mem.freq
461
        elif mem.duplex == "split":
462
            diff = mem.offset - mem.freq
463
            _mem.duplex = DUPLEX.index("-") if diff < 0 else DUPLEX.index("+")
464
            _mem.offset = abs(diff) / 10
465
        else:
466
            _mem.offset = mem.offset / 10
467
            _mem.duplex = DUPLEX.index(mem.duplex)
468

    
469
        tx, rx = chirp_common.split_tone_encode(mem)
470
        self._encode_tone(_mem, 'tx', *tx)
471
        self._encode_tone(_mem, 'rx', *rx)
472

    
473
        _mem.isnarrow = mem.mode == "NFM"
474
        _mem.scanadd = mem.skip == ""
475
        _mem.highpower = mem.power == POWER_LEVELS[1]
476

    
477
        if _nam:
478
            for i in range(0, 5):
479
                try:
480
                    _nam[i] = CHARSET.index(mem.name[i])
481
                except IndexError:
482
                    _nam[i] = 0xFF
483

    
484
        for setting in mem.extra:
485
            setattr(_mem, setting.get_name(), setting.value)
486

    
487
    def get_settings(self):
488
        _settings = self._memobj.settings
489
        basic = RadioSettingGroup("basic", "Basic Settings")
490

    
491
        group = RadioSettings(basic)
492

    
493
        options = ["Time", "Carrier", "Search"]
494
        rs = RadioSetting("scantype", "Scan Type",
495
                          RadioSettingValueList(options,
496
                                                options[_settings.scantype]))
497
        basic.append(rs)
498

    
499
        options = ["Off"] + ["%s min" % x for x in range(1, 8)]
500
        rs = RadioSetting("timeout", "Time Out Timer",
501
                          RadioSettingValueList(
502
                              options, options[_settings.timeout]))
503
        basic.append(rs)
504

    
505
        options = ["A", "B"]
506
        rs = RadioSetting("freqmode_ab", "Frequency Mode",
507
                          RadioSettingValueList(
508
                              options, options[_settings.freqmode_ab]))
509
        basic.append(rs)
510

    
511
        options = ["Frequency Mode", "Channel Mode"]
512
        rs = RadioSetting("workmode_a", "Radio Work Mode(A)",
513
                          RadioSettingValueList(
514
                              options, options[_settings.workmode_a]))
515
        basic.append(rs)
516

    
517
        rs = RadioSetting("workmode_b", "Radio Work Mode(B)",
518
                          RadioSettingValueList(
519
                              options, options[_settings.workmode_b]))
520
        basic.append(rs)
521

    
522
        options = ["Frequency", "Name", "Channel"]
523
        rs = RadioSetting("mdf_a", "Display Format(F1)",
524
                          RadioSettingValueList(
525
                              options, options[_settings.mdf_a]))
526
        basic.append(rs)
527

    
528
        rs = RadioSetting("mdf_b", "Display Format(F2)",
529
                          RadioSettingValueList(
530
                              options, options[_settings.mdf_b]))
531
        basic.append(rs)
532

    
533
        rs = RadioSetting("mem_chan_a", "Mem Channel (A)",
534
                          RadioSettingValueInteger(
535
                              1, 99, _settings.mem_chan_a))
536
        basic.append(rs)
537

    
538
        rs = RadioSetting("mem_chan_b", "Mem Channel (B)",
539
                          RadioSettingValueInteger(
540
                              1, 99, _settings.mem_chan_b))
541
        basic.append(rs)
542

    
543
        options = ["Off", "BOT", "EOT", "Both"]
544
        rs = RadioSetting("pttid", "PTT-ID",
545
                          RadioSettingValueList(
546
                              options, options[_settings.pttid]))
547
        basic.append(rs)
548

    
549
        dtmfchars = "0123456789ABCD*#"
550
        _codeobj = self._memobj.pttid.code
551
        _code = "".join([dtmfchars[x] for x in _codeobj if int(x) < 0x1F])
552
        val = RadioSettingValueString(0, 6, _code, False)
553
        val.set_charset(dtmfchars)
554
        rs = RadioSetting("pttid.code", "PTT-ID Code", val)
555

    
556
        def apply_code(setting, obj):
557
            code = []
558
            for j in range(0, 6):
559
                try:
560
                    code.append(dtmfchars.index(str(setting.value)[j]))
561
                except IndexError:
562
                    code.append(0xFF)
563
            obj.code = code
564
        rs.set_apply_callback(apply_code, self._memobj.pttid)
565
        basic.append(rs)
566

    
567
        rs = RadioSetting("squelch", "Squelch Level",
568
                          RadioSettingValueInteger(0, 9, _settings.squelch))
569
        basic.append(rs)
570

    
571
        rs = RadioSetting("vox", "VOX Level",
572
                          RadioSettingValueInteger(0, 9, _settings.vox))
573
        basic.append(rs)
574

    
575
        options = ["Frequency Mode", "Channel Mode"]
576
        rs = RadioSetting("workmode_fm", "FM Work Mode",
577
                          RadioSettingValueList(
578
                              options, options[_settings.workmode_fm]))
579
        basic.append(rs)
580

    
581
        options = ["Current Frequency", "F1 Frequency", "F2 Frequency"]
582
        rs = RadioSetting("txtdr", "Dual Standby TX Priority",
583
                          RadioSettingValueList(options,
584
                                                options[_settings.txtdr]))
585
        basic.append(rs)
586

    
587
        options = ["English", "Chinese"]
588
        rs = RadioSetting("language", "Language",
589
                          RadioSettingValueList(options,
590
                                                options[_settings.language]))
591
        basic.append(rs)
592

    
593
        rs = RadioSetting("tdr", "Dual Standby",
594
                          RadioSettingValueBoolean(_settings.tdr))
595
        basic.append(rs)
596

    
597
        rs = RadioSetting("roger", "Roger Beep",
598
                          RadioSettingValueBoolean(_settings.roger))
599
        basic.append(rs)
600

    
601
        rs = RadioSetting("backlight", "Backlight",
602
                          RadioSettingValueBoolean(_settings.backlight))
603
        basic.append(rs)
604

    
605
        rs = RadioSetting("save_funct", "Save Mode",
606
                          RadioSettingValueBoolean(_settings.save_funct))
607
        basic.append(rs)
608

    
609
        rs = RadioSetting("fm", "FM Function",
610
                          RadioSettingValueBoolean(_settings.fm))
611
        basic.append(rs)
612

    
613
        rs = RadioSetting("beep_tone_disabled", "Beep Prompt",
614
                          RadioSettingValueBoolean(
615
                              not _settings.beep_tone_disabled))
616
        basic.append(rs)
617

    
618
        rs = RadioSetting("voice_prompt", "Voice Prompt",
619
                          RadioSettingValueBoolean(_settings.voice_prompt))
620
        basic.append(rs)
621

    
622
        rs = RadioSetting("sidetone", "DTMF Side Tone",
623
                          RadioSettingValueBoolean(_settings.sidetone))
624
        basic.append(rs)
625

    
626
        rs = RadioSetting("ste_disabled", "Squelch Tail Eliminate",
627
                          RadioSettingValueBoolean(not _settings.ste_disabled))
628
        basic.append(rs)
629

    
630
        _limit = int(self._memobj.limits.lower_vhf) / 10
631
        rs = RadioSetting("limits.lower_vhf", "VHF Lower Limit (MHz)",
632
                          RadioSettingValueInteger(128, 270, _limit))
633

    
634
        def apply_limit(setting, obj):
635
            value = int(setting.value) * 10
636
            obj.lower_vhf = value
637
        rs.set_apply_callback(apply_limit, self._memobj.limits)
638
        basic.append(rs)
639

    
640
        _limit = int(self._memobj.limits.upper_vhf) / 10
641
        rs = RadioSetting("limits.upper_vhf", "VHF Upper Limit (MHz)",
642
                          RadioSettingValueInteger(128, 270, _limit))
643

    
644
        def apply_limit(setting, obj):
645
            value = int(setting.value) * 10
646
            obj.upper_vhf = value
647
        rs.set_apply_callback(apply_limit, self._memobj.limits)
648
        basic.append(rs)
649

    
650
        _limit = int(self._memobj.limits.lower_uhf) / 10
651
        rs = RadioSetting("limits.lower_uhf", "UHF Lower Limit (MHz)",
652
                          RadioSettingValueInteger(400, 520, _limit))
653

    
654
        def apply_limit(setting, obj):
655
            value = int(setting.value) * 10
656
            obj.lower_uhf = value
657
        rs.set_apply_callback(apply_limit, self._memobj.limits)
658
        basic.append(rs)
659

    
660
        _limit = int(self._memobj.limits.upper_uhf) / 10
661
        rs = RadioSetting("limits.upper_uhf", "UHF Upper Limit (MHz)",
662
                          RadioSettingValueInteger(400, 520, _limit))
663

    
664
        def apply_limit(setting, obj):
665
            value = int(setting.value) * 10
666
            obj.upper_uhf = value
667
        rs.set_apply_callback(apply_limit, self._memobj.limits)
668
        basic.append(rs)
669

    
670
        fm_preset = RadioSettingGroup("fm_preset", "FM Radio Presets")
671
        group.append(fm_preset)
672

    
673
        for i in range(0, 16):
674
            if self._memobj.fm_presets[i] < 0x01AF:
675
                used = True
676
                preset = self._memobj.fm_presets[i] / 10.0 + 65
677
            else:
678
                used = False
679
                preset = 65
680
            rs = RadioSetting("fm_presets_%1i" % i, "FM Preset %i" % (i + 1),
681
                              RadioSettingValueBoolean(used),
682
                              RadioSettingValueFloat(65, 108, preset, 0.1, 1))
683
            fm_preset.append(rs)
684

    
685
        testmode = RadioSettingGroup("testmode", "Test Mode Settings")
686
        group.append(testmode)
687

    
688
        vhfdata = ["136-139", "140-144", "145-149", "150-154",
689
                   "155-159", "160-164", "165-169", "170-174"]
690
        uhfdata = ["400-409", "410-419", "420-429", "430-439",
691
                   "440-449", "450-459", "460-469", "470-479"]
692
        powernamedata = ["Hi", "Lo"]
693
        powerkeydata = ["hipwr", "lopwr"]
694

    
695
        for power in range(0, 2):
696
            for index in range(0, 8):
697
                key = "test.vhf%s%i" % (powerkeydata[power], index)
698
                name = "%s Mhz %s Power" % (vhfdata[index],
699
                                            powernamedata[power])
700
                rs = RadioSetting(
701
                        key, name, RadioSettingValueInteger(
702
                            0, 255, getattr(
703
                                self._memobj.test,
704
                                "vhf%s%i" % (powerkeydata[power], index))))
705
                testmode.append(rs)
706

    
707
        for power in range(0, 2):
708
            for index in range(0, 8):
709
                key = "test.uhf%s%i" % (powerkeydata[power], index)
710
                name = "%s Mhz %s Power" % (uhfdata[index],
711
                                            powernamedata[power])
712
                rs = RadioSetting(
713
                        key, name, RadioSettingValueInteger(
714
                            0, 255, getattr(
715
                                self._memobj.test,
716
                                "uhf%s%i" % (powerkeydata[power], index))))
717
                testmode.append(rs)
718

    
719
        for band in ["vhf", "uhf"]:
720
            for index in range(0, 10):
721
                key = "test.%ssquelch%i" % (band, index)
722
                name = "%s Squelch %i" % (band.upper(), index)
723
                rs = RadioSetting(
724
                        key, name, RadioSettingValueInteger(
725
                            0, 255, getattr(
726
                                self._memobj.test,
727
                                "%ssquelch%i" % (band, index))))
728
                testmode.append(rs)
729

    
730
        return group
731

    
732
    def set_settings(self, settings):
733
        _settings = self._memobj.settings
734
        for element in settings:
735
            if not isinstance(element, RadioSetting):
736
                if element.get_name() == "fm_preset":
737
                    self._set_fm_preset(element)
738
                else:
739
                    self.set_settings(element)
740
                    continue
741
            else:
742
                try:
743
                    name = element.get_name()
744
                    if "." in name:
745
                        bits = name.split(".")
746
                        obj = self._memobj
747
                        for bit in bits[:-1]:
748
                            if "/" in bit:
749
                                bit, index = bit.split("/", 1)
750
                                index = int(index)
751
                                obj = getattr(obj, bit)[index]
752
                            else:
753
                                obj = getattr(obj, bit)
754
                        setting = bits[-1]
755
                    else:
756
                        obj = _settings
757
                        setting = element.get_name()
758

    
759
                    if element.has_apply_callback():
760
                        LOG.debug("Using apply callback")
761
                        element.run_apply_callback()
762
                    elif setting == "beep_tone_disabled":
763
                        setattr(obj, setting, not int(element.value))
764
                    elif setting == "ste_disabled":
765
                        setattr(obj, setting, not int(element.value))
766
                    else:
767
                        LOG.debug("Setting %s = %s" % (setting, element.value))
768
                        setattr(obj, setting, element.value)
769
                except Exception, e:
770
                    LOG.debug(element.get_name())
771
                    raise
772

    
773
    def _set_fm_preset(self, settings):
774
        for element in settings:
775
            try:
776
                index = (int(element.get_name().split("_")[-1]))
777
                val = element.value
778
                if val[0].get_value():
779
                    value = int(val[1].get_value() * 10 - 650)
780
                else:
781
                    value = 0x01AF
782
                LOG.debug("Setting fm_presets[%1i] = %s" % (index, value))
783
                setting = self._memobj.fm_presets
784
                setting[index] = value
785
            except Exception, e:
786
                LOG.debug(element.get_name())
787
                raise
788

    
789
    @classmethod
790
    def match_model(cls, filedata, filename):
791
        return (filedata.startswith("KT511 Radio Program data") and
792
                len(filedata) == (cls._memsize + 0x30))
(3-3/5)