Project

General

Profile

New Model #8263 » bf-t8 - concept demo.py

For download testing only - Jim Unroe, 04/30/2021 03:22 PM

 
1
# Copyright 2021 Jim Unroe <rock.unroe@gmail.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 time
17
import os
18
import struct
19
import logging
20

    
21
from chirp import (
22
    bitwise,
23
    chirp_common,
24
    directory,
25
    errors,
26
    memmap,
27
    util,
28
)
29
from chirp.settings import (
30
    RadioSetting,
31
    RadioSettingGroup,
32
    RadioSettings,
33
    RadioSettingValueBoolean,
34
    RadioSettingValueFloat,
35
    RadioSettingValueInteger,
36
    RadioSettingValueList,
37
    RadioSettingValueString,
38
)
39

    
40
LOG = logging.getLogger(__name__)
41

    
42
MEM_FORMAT = """
43
#seekto 0x0000;
44
struct {
45
  lbcd rxfreq[4];       // RX Frequency           0-3
46
  lbcd txfreq[4];       // TX Frequency           4-7     
47
  u8 rx_tmode;          // RX Tone Mode           8       
48
  u8 rx_tone;           // PL/DPL Decode          9
49
  u8 tx_tmode;          // TX Tone Mode           A       
50
  u8 tx_tone;           // PL/DPL Encode          B       
51
  u8 unknown1:3,        //                        C       
52
     skip:1,            // Scan Add: 1 = Skip, 0 = Scan   
53
     unknown2:2,
54
     isnarrow:1,        // W/N: 1 = Narrow, 0 = Wide
55
     lowpower:1;        // TX Power: 1 = Low, 0 = High    
56
  u8 unknown3[3];       //                        D-F
57
} memory[99];
58

    
59
#seekto 0x0630;
60
struct {
61
  u8 squelch;           // SQL                          630
62
  u8 vox;               // Vox Lv                       631
63
  u8 tot;               // TOT                          632
64
  u8 unk1:3,            //                              633
65
     ste:1,             // Tail Clear
66
     bcl:1,             // BCL
67
     save:1,            // Save
68
     tdr:1,             // TDR
69
     beep:1;            // Beep
70
  u8 voice;             // Voice                        634
71
  u8 abr;               // Back Light                   635
72
  u8 ring;              // Ring                         636
73
  u8 unknown;           //                              637
74
  u8 mra;               // MR Channel A                 638
75
  u8 mrb;               // MR Channel B                 639
76
  u8 disp_ab;           // Display A/B Selected         63A
77
  ul16 fmcur;           // Broadcast FM station         63B-63C
78
  u8 workmode;          // Work Mode                    63D
79
  u8 wx;                // NOAA WX ch#                  63E
80
  u8 area;              // Area Selected                63F
81
} settings;
82
"""
83

    
84
CMD_ACK = "\x06"
85

    
86
TONES = chirp_common.TONES
87
TMODES = ["", "Tone", "DTCS", "DTCS"]
88

    
89
AB_LIST = ["A", "B"]
90
ABR_LIST = ["OFF", "ON", "Key"]
91
AREA_LIST = ["China", "Japan", "Korea", "Malaysia", "American",
92
             "Australia", "Iran", "Taiwan", "Europe", "Russia"]
93
MDF_LIST = ["Frequency", "Channel #", "Name"]
94
RING_LIST = ["OFF"] + ["%s" % x for x in range(1, 11)]
95
TIMEOUTTIMER_LIST = ["OFF"] + ["%s seconds" % x for x in range(30, 210, 30)]
96
VOICE_LIST = ["Off", "Chinese", "English"]
97
VOX_LIST = ["OFF"] + ["%s" % x for x in range(1, 6)]
98
WORKMODE_LIST = ["General", "PMR"]
99
WX_LIST = ["CH01 - 162.550",
100
           "CH02 - 162.400",
101
           "CH03 - 162.475",
102
           "CH04 - 162.425",
103
           "CH05 - 162.450",
104
           "CH06 - 162.500",
105
           "CH07 - 162.525"
106
           ]
107

    
108
SETTING_LISTS = {
109
    "ab": AB_LIST,
110
    "abr": ABR_LIST,
111
    "area": AREA_LIST,
112
    "mdf": MDF_LIST,
113
    "ring": RING_LIST,
114
    "tot": TIMEOUTTIMER_LIST,
115
    "voice": VOICE_LIST,
116
    "vox": VOX_LIST,
117
    "workmode": WORKMODE_LIST,
118
    "wx": WX_LIST,
119
    }
120

    
121
FRS_FREQS1 = [462.5625, 462.5875, 462.6125, 462.6375, 462.6625,
122
              462.6875, 462.7125]
123
FRS_FREQS2 = [467.5625, 467.5875, 467.6125, 467.6375, 467.6625,
124
              467.6875, 467.7125]
125
FRS_FREQS3 = [462.5500, 462.5750, 462.6000, 462.6250, 462.6500,
126
              462.6750, 462.7000, 462.7250]
127
FRS_FREQS = FRS_FREQS1 + FRS_FREQS2 + FRS_FREQS3
128

    
129

    
130
def _enter_programming_mode(radio):
131
    serial = radio.pipe
132

    
133
    exito = False
134
    for i in range(0, 5):
135
        serial.write(radio._magic)
136
        ack = serial.read(1)
137

    
138
        try:
139
            if ack == CMD_ACK:
140
                exito = True
141
                break
142
        except:
143
            LOG.debug("Attempt #%s, failed, trying again" % i)
144
            pass
145

    
146
    # check if we had EXITO
147
    if exito is False:
148
        _exit_programming_mode(radio)
149
        msg = "The radio did not accept program mode after five tries.\n"
150
        msg += "Check you interface cable and power cycle your radio."
151
        raise errors.RadioError(msg)
152

    
153
    try:
154
        serial.write("\x02")
155
        ident = serial.read(len(radio._fingerprint))
156
    except:
157
        _exit_programming_mode(radio)
158
        raise errors.RadioError("Error communicating with radio")
159

    
160
    if not ident == radio._fingerprint:
161
        _exit_programming_mode(radio)
162
        LOG.debug(util.hexprint(ident))
163
        raise errors.RadioError("Radio returned unknown identification string")
164

    
165
    try:
166
        serial.write(CMD_ACK)
167
        ack = serial.read(1)
168
    except:
169
        _exit_programming_mode(radio)
170
        raise errors.RadioError("Error communicating with radio")
171

    
172
    if ack != CMD_ACK:
173
        _exit_programming_mode(radio)
174
        raise errors.RadioError("Radio refused to enter programming mode")
175

    
176

    
177
def _exit_programming_mode(radio):
178
    serial = radio.pipe
179
    try:
180
        serial.write("E")
181
    except:
182
        raise errors.RadioError("Radio refused to exit programming mode")
183

    
184

    
185
def _read_block(radio, block_addr, block_size):
186
    serial = radio.pipe
187

    
188
    cmd = struct.pack(">cHb", 'R', block_addr, block_size)
189
    expectedresponse = "W" + cmd[1:]
190
    LOG.debug("Reading block %04x..." % (block_addr))
191

    
192
    try:
193
        serial.write(cmd)
194
        response = serial.read(4 + block_size)
195
        if response[:4] != expectedresponse:
196
            raise Exception("Error reading block %04x." % (block_addr))
197

    
198
        block_data = response[4:]
199

    
200
        serial.write(CMD_ACK)
201
        ack = serial.read(1)
202
    except:
203
        _exit_programming_mode(radio)
204
        raise errors.RadioError("Failed to read block at %04x" % block_addr)
205

    
206
    if ack != CMD_ACK:
207
        _exit_programming_mode(radio)
208
        raise Exception("No ACK reading block %04x." % (block_addr))
209

    
210
    return block_data
211

    
212

    
213
def _write_block(radio, block_addr, block_size):
214
    serial = radio.pipe
215

    
216
    cmd = struct.pack(">cHb", 'W', block_addr, block_size)
217
    data = radio.get_mmap()[block_addr:block_addr + block_size]
218

    
219
    LOG.debug("Writing Data:")
220
    LOG.debug(util.hexprint(cmd + data))
221

    
222
    try:
223
        serial.write(cmd + data)
224
        if serial.read(1) != CMD_ACK:
225
            raise Exception("No ACK")
226
    except:
227
        _exit_programming_mode(radio)
228
        raise errors.RadioError("Failed to send block "
229
                                "to radio at %04x" % block_addr)
230

    
231

    
232
def do_download(radio):
233
    LOG.debug("download")
234
    _enter_programming_mode(radio)
235

    
236
    data = ""
237

    
238
    status = chirp_common.Status()
239
    status.msg = "Cloning from radio"
240

    
241
    status.cur = 0
242
    status.max = radio._memsize
243

    
244
    for addr in range(0, radio._memsize, radio.BLOCK_SIZE):
245
        status.cur = addr + radio.BLOCK_SIZE
246
        radio.status_fn(status)
247

    
248
        block = _read_block(radio, addr, radio.BLOCK_SIZE)
249
        data += block
250

    
251
        LOG.debug("Address: %04x" % addr)
252
        LOG.debug(util.hexprint(block))
253

    
254
    _exit_programming_mode(radio)
255

    
256
    return memmap.MemoryMap(data)
257

    
258

    
259
def do_upload(radio):
260
    status = chirp_common.Status()
261
    status.msg = "Uploading to radio"
262

    
263
    _enter_programming_mode(radio)
264

    
265
    status.cur = 0
266
    status.max = radio._memsize
267

    
268
    #for start_addr, end_addr in radio._ranges:
269
    #    for addr in range(start_addr, end_addr, radio.BLOCK_SIZE_UP):
270
    #        status.cur = addr + radio.BLOCK_SIZE_UP
271
    #        radio.status_fn(status)
272
    #        _write_block(radio, addr, radio.BLOCK_SIZE_UP)
273

    
274
    _exit_programming_mode(radio)
275

    
276

    
277
@directory.register
278
class BFT8Radio(chirp_common.CloneModeRadio):
279
    """Baofeng BF-T8"""
280
    VENDOR = "Baofeng"
281
    MODEL = "BF-T8"
282
    BAUD_RATE = 9600
283
    BLOCK_SIZE = BLOCK_SIZE_UP = 0x10
284
    SKIP_VALUES = []
285
    HAS_NAMES = False
286
    DTCS_CODES = sorted(chirp_common.DTCS_CODES)
287

    
288
    POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
289
                    chirp_common.PowerLevel("Low", watts=0.50)]
290

    
291
    _magic = "\x02" + "PROGRAM"
292
    _fingerprint = "\x2E" + "BF-T6" + "\x2E"
293
    _frs = False ##True
294
    _upper = 99
295

    
296
    _ranges = [
297
               (0x0000, 0x0B60),
298
              ]
299
    _memsize = 0x0B60
300

    
301

    
302
    def get_features(self):
303
        rf = chirp_common.RadioFeatures()
304
        rf.has_settings = False ##True
305
        rf.has_bank = False
306
        rf.has_ctone = True
307
        rf.has_cross = True
308
        rf.has_rx_dtcs = True
309
        rf.has_tuning_step = False
310
        rf.can_odd_split = False
311
        rf.has_name = self.HAS_NAMES
312
        rf.valid_skips = self.SKIP_VALUES
313
        rf.valid_tmodes = ["", "Tone", "TSQL", "DTCS", "Cross"]
314
        rf.valid_cross_modes = ["Tone->Tone", "Tone->DTCS", "DTCS->Tone",
315
                                "->Tone", "->DTCS", "DTCS->", "DTCS->DTCS"]
316
        rf.valid_dtcs_codes = self.DTCS_CODES
317
        rf.valid_power_levels = self.POWER_LEVELS
318
        rf.valid_duplexes = ["", "-", "+", "split", "off"]
319
        rf.valid_modes = ["FM", "NFM"]  # 25 kHz, 12.5 KHz.
320
        rf.memory_bounds = (1, self._upper)
321
        rf.valid_tuning_steps = [2.5, 5., 6.25, 10., 12.5, 25.]
322
        rf.valid_bands = [(400000000, 470000000)]
323

    
324
        return rf
325

    
326
    def process_mmap(self):
327
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
328

    
329
    def validate_memory(self, mem):
330
        msgs = ""
331
        msgs = chirp_common.CloneModeRadio.validate_memory(self, mem)
332

    
333
        _msg_freq = 'Memory location cannot change frequency'
334
        _msg_simplex = 'Memory location only supports Duplex:(None)'
335
        _msg_nfm = 'Memory location only supports Mode: NFM'
336
        _msg_txp = 'Memory location only supports Power: Low'
337

    
338
        # FRS models
339
        if self._frs:
340
            # range of memories with values set by FCC rules
341
            if mem.freq != int(FRS_FREQS[mem.number - 1] * 1000000):
342
                # warn user can't change frequency
343
                msgs.append(chirp_common.ValidationError(_msg_freq))
344

    
345
            # channels 1 - 22 are simplex only
346
            if str(mem.duplex) != "":
347
                # warn user can't change duplex
348
                msgs.append(chirp_common.ValidationError(_msg_simplex))
349

    
350
            # channels 1 - 22 are NFM only
351
            if str(mem.mode) != "NFM":
352
                # warn user can't change mode
353
                msgs.append(chirp_common.ValidationError(_msg_nfm))
354

    
355
            # channels 8 - 14 are low power NFM only
356
            if mem.number >= 8 and mem.number <= 14:
357
                if str(mem.power) != "Low":
358
                    # warn user can't change power
359
                    msgs.append(chirp_common.ValidationError(_msg_txp))
360

    
361
        return msgs
362

    
363
    def sync_in(self):
364
        """Download from radio"""
365
        try:
366
            data = do_download(self)
367
        except errors.RadioError:
368
            # Pass through any real errors we raise
369
            raise
370
        except:
371
            # If anything unexpected happens, make sure we raise
372
            # a RadioError and log the problem
373
            LOG.exception('Unexpected error during download')
374
            raise errors.RadioError('Unexpected error communicating '
375
                                    'with the radio')
376
        self._mmap = data
377
        self.process_mmap()
378

    
379
    def sync_out(self):
380
        """Upload to radio"""
381
        try:
382
            do_upload(self)
383
        except:
384
            # If anything unexpected happens, make sure we raise
385
            # a RadioError and log the problem
386
            LOG.exception('Unexpected error during upload')
387
            raise errors.RadioError('Unexpected error communicating '
388
                                    'with the radio')
389

    
390
    def get_raw_memory(self, number):
391
        return repr(self._memobj.memory[number - 1])
392

    
393
    def _get_tone(self, mem, _mem):
394
        rx_tone = tx_tone = None
395

    
396
        tx_tmode = TMODES[_mem.tx_tmode]
397
        rx_tmode = TMODES[_mem.rx_tmode]
398

    
399
        if tx_tmode == "Tone":
400
            tx_tone = TONES[_mem.tx_tone]
401
        elif tx_tmode == "DTCS":
402
            tx_tone = self.DTCS_CODES[_mem.tx_tone]
403

    
404
        if rx_tmode == "Tone":
405
            rx_tone = TONES[_mem.rx_tone]
406
        elif rx_tmode == "DTCS":
407
            rx_tone = self.DTCS_CODES[_mem.rx_tone]
408

    
409
        tx_pol = _mem.tx_tmode == 0x03 and "R" or "N"
410
        rx_pol = _mem.rx_tmode == 0x03 and "R" or "N"
411

    
412
        chirp_common.split_tone_decode(mem, (tx_tmode, tx_tone, tx_pol),
413
                                       (rx_tmode, rx_tone, rx_pol))
414

    
415
    def _get_mem(self, number):
416
        return self._memobj.memory[number]
417

    
418
    def get_memory(self, number):
419
        _mem = self._get_mem(number - 1)
420

    
421
        mem = chirp_common.Memory()
422

    
423
        mem.number = number
424
        mem.freq = int(_mem.rxfreq) * 10
425

    
426
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
427
        if mem.freq == 0:
428
            mem.empty = True
429
            return mem
430

    
431
        if _mem.rxfreq.get_raw() == "\xFF\xFF\xFF\xFF":
432
            mem.freq = 0
433
            mem.empty = True
434
            return mem
435

    
436
        if _mem.get_raw() == ("\xFF" * 16):
437
            LOG.debug("Initializing empty memory")
438
            _mem.set_raw("\x00" * 13 + "\xFF" * 3)
439

    
440
        if int(_mem.rxfreq) == int(_mem.txfreq):
441
            mem.duplex = ""
442
            mem.offset = 0
443
        else:
444
            mem.duplex = int(_mem.rxfreq) > int(_mem.txfreq) and "-" or "+"
445
            mem.offset = abs(int(_mem.rxfreq) - int(_mem.txfreq)) * 10
446

    
447
        # wide/narrow
448
        mem.mode = _mem.isnarrow and "NFM" or "FM"
449

    
450
        # tone data
451
        self._get_tone(mem, _mem)
452

    
453
        # tx power
454
        levels = self.POWER_LEVELS
455
        try:
456
            mem.power = levels[_mem.lowpower]
457
        except IndexError:
458
            LOG.error("Radio reported invalid power level %s (in %s)" %
459
                      (_mem.power, levels))
460
            mem.power = levels[0]
461

    
462
        if self._frs:
463
            FRS_IMMUTABLE = ["freq", "duplex", "offset", "mode"]
464
            if mem.number >= 8 and mem.number <= 14:
465
                mem.immutable = FRS_IMMUTABLE + ["power"]
466
            else:
467
                mem.immutable = FRS_IMMUTABLE
468

    
469
        return mem
470

    
471
    def _set_tone(self, mem, _mem):
472
        ((txmode, txtone, txpol),
473
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
474

    
475
        _mem.tx_tmode = TMODES.index(txmode)
476
        _mem.rx_tmode = TMODES.index(rxmode)
477
        if txmode == "Tone":
478
            _mem.tx_tone = TONES.index(txtone)
479
        elif txmode == "DTCS":
480
            _mem.tx_tmode = txpol == "R" and 0x03 or 0x02
481
            _mem.tx_tone = self.DTCS_CODES.index(txtone)
482
        if rxmode == "Tone":
483
            _mem.rx_tone = TONES.index(rxtone)
484
        elif rxmode == "DTCS":
485
            _mem.rx_tmode = rxpol == "R" and 0x03 or 0x02
486
            _mem.rx_tone = self.DTCS_CODES.index(rxtone)
487

    
488
    def set_memory(self, mem):
489
        _mem = self._get_mem(mem.number - 1)
490

    
491
        # if empty memmory
492
        if mem.empty:
493
            if mem.number <= 22:
494
                _mem.set_raw("\x00" * 13 + "\xFF" * 3)
495
                FRS_FREQ = int(FRS_FREQS[mem.number - 1] * 100000)
496
                _mem.rxfreq = _mem.txfreq = FRS_FREQ
497
                _mem.isnarrow = True
498
                if mem.number >= 8 and mem.number <= 14:
499
                    _mem.lowpower = True
500
                else:
501
                    _mem.lowpower = False
502
            else:
503
                ####_mem.set_raw("\xFF" * (_mem.size() / 8))
504
                _mem.set_raw("\xFF" * 8 + "\x00" * 4 + "\x03" + "\xFF" * 3)
505

    
506
            return mem
507

    
508
        _mem.set_raw("\x00" * 13 + "\xFF" * 3)
509

    
510
        # frequency
511
        _mem.rxfreq = mem.freq / 10
512

    
513
        if mem.duplex == "off":
514
            for i in range(0, 4):
515
                _mem.txfreq[i].set_raw("\xFF")
516
        elif mem.duplex == "split":
517
            _mem.txfreq = mem.offset / 10
518
        elif mem.duplex == "+":
519
            _mem.txfreq = (mem.freq + mem.offset) / 10
520
        elif mem.duplex == "-":
521
            _mem.txfreq = (mem.freq - mem.offset) / 10
522
        else:
523
            _mem.txfreq = mem.freq / 10
524

    
525
        # wide/narrow
526
        _mem.isnarrow = mem.mode == "NFM"
527

    
528
        # tone data
529
        self._set_tone(mem, _mem)
530

    
531
        # tx power
532
        if mem.power:
533
            _mem.lowpower = self.POWER_LEVELS.index(mem.power)
534
        else:
535
            _mem.lowpower = 0
536

    
537
        return mem
538

    
539
    def get_settings(self):
540
        _settings = self._memobj.settings
541
        basic = RadioSettingGroup("basic", "Basic Settings")
542
        top = RadioSettings(basic)
543

    
544
        # Menu 03
545
        rs = RadioSettingValueInteger(0, 9, _settings.squelch)
546
        rset = RadioSetting("squelch", "Squelch Level", rs)
547
        basic.append(rset)
548

    
549
        # Menu 11
550
        rs = RadioSettingValueList(TIMEOUTTIMER_LIST,
551
                                   TIMEOUTTIMER_LIST[_settings.tot])
552
        rset = RadioSetting("tot", "Time-out timer", rs)
553
        basic.append(rset)
554

    
555
        # Menu 06
556
        rs = RadioSettingValueList(VOX_LIST, VOX_LIST[_settings.vox])
557
        rset = RadioSetting("vox", "VOX Level", rs)
558
        basic.append(rset)
559

    
560
        # Menu 15 (BF-T8)
561
        rs = RadioSettingValueList(VOICE_LIST, VOICE_LIST[_settings.voice])
562
        rset = RadioSetting("voice", "Voice", rs)
563
        basic.append(rset)
564

    
565
        # Menu 12
566
        rs = RadioSettingValueBoolean(_settings.bcl)
567
        rset = RadioSetting("bcl", "Busy Channel Lockout", rs)
568
        basic.append(rset)
569

    
570
        # Menu 10
571
        rs = RadioSettingValueBoolean(_settings.save)
572
        rset = RadioSetting("save", "Battery Saver", rs)
573
        basic.append(rset)
574

    
575
        # Menu 08
576
        rs = RadioSettingValueBoolean(_settings.tdr)
577
        rset = RadioSetting("tdr", "Dual Watch", rs)
578
        basic.append(rset)
579

    
580
        # Menu 05
581
        rs = RadioSettingValueBoolean(_settings.beep)
582
        rset = RadioSetting("beep", "Beep", rs)
583
        basic.append(rset)
584

    
585
        # Menu 04
586
        rs = RadioSettingValueList(ABR_LIST, ABR_LIST[_settings.abr])
587
        rset = RadioSetting("abr", "Back Light", rs)
588
        basic.append(rset)
589

    
590
        # Menu 13
591
        rs = RadioSettingValueList(RING_LIST, RING_LIST[_settings.ring])
592
        rset = RadioSetting("ring", "Ring", rs)
593
        basic.append(rset)
594

    
595
        rs = RadioSettingValueBoolean(not _settings.ste)
596
        rset = RadioSetting("ste", "Squelch Tail Eliminate", rs)
597
        basic.append(rset)
598

    
599
        #
600
        #
601
        #
602
        #
603
        
604
        rs = RadioSettingValueInteger(1, self._upper, _settings.mra)
605
        rset = RadioSetting("mra", "MR A Channel #", rs)
606
        basic.append(rset)
607

    
608
        rs = RadioSettingValueInteger(1, self._upper, _settings.mrb)
609
        rset = RadioSetting("mrb", "MR B Channel #", rs)
610
        basic.append(rset)
611

    
612
        rs = RadioSettingValueList(AB_LIST, AB_LIST[_settings.disp_ab])
613
        rset = RadioSetting("disp_ab", "Selected Display Line", rs)
614
        basic.append(rset)
615

    
616
        rs = RadioSettingValueList(WX_LIST, WX_LIST[_settings.wx])
617
        rset = RadioSetting("wx", "NOAA WX Radio", rs)
618
        basic.append(rset)
619

    
620
        def myset_freq(setting, obj, atrb, mult):
621
            """ Callback to set frequency by applying multiplier"""
622
            value = int(float(str(setting.value)) * mult)
623
            setattr(obj, atrb, value)
624
            return
625

    
626
        # FM Broadcast Settings
627
        val = _settings.fmcur
628
        val = val / 10.0
629
        if val < 76.0 or val > 108.0:
630
            val = 90.4
631
        rx = RadioSettingValueFloat(76.0, 108.0, val, 0.1, 1)
632
        rset = RadioSetting("settings.fmcur", "Broadcast FM Radio (MHz)", rx)
633
        rset.set_apply_callback(myset_freq, _settings, "fmcur", 10)
634
        basic.append(rset)
635

    
636
        rs = RadioSettingValueList(WORKMODE_LIST, WORKMODE_LIST[_settings.workmode])
637
        rset = RadioSetting("workmode", "Work Mode", rs)
638
        basic.append(rset)
639

    
640
        rs = RadioSettingValueList(AREA_LIST, AREA_LIST[_settings.area])
641
        rs.set_mutable(False)
642
        rset = RadioSetting("area", "Area", rs)
643
        basic.append(rset)
644

    
645
        return top
646

    
647
    def set_settings(self, settings):
648
        for element in settings:
649
            if not isinstance(element, RadioSetting):
650
                self.set_settings(element)
651
                continue
652
            else:
653
                try:
654
                    if "." in element.get_name():
655
                        bits = element.get_name().split(".")
656
                        obj = self._memobj
657
                        for bit in bits[:-1]:
658
                            obj = getattr(obj, bit)
659
                        setting = bits[-1]
660
                    else:
661
                        obj = self._memobj.settings
662
                        setting = element.get_name()
663

    
664
                    if element.has_apply_callback():
665
                        LOG.debug("Using apply callback")
666
                        element.run_apply_callback()
667
                    elif setting == "tail":
668
                        setattr(obj, setting, not int(element.value))
669
                    elif element.value.get_mutable():
670
                        LOG.debug("Setting %s = %s" % (setting, element.value))
671
                        setattr(obj, setting, element.value)
672
                except Exception, e:
673
                    LOG.debug(element.get_name())
674
                    raise
675

    
676

    
677
    @classmethod
678
    def match_model(cls, filedata, filename):
679
        # This radio has always been post-metadata, so never do
680
        # old-school detection
681
        return False
(1-1/3)