Project

General

Profile

Bug #9499 » ftlx011_temporary_fix_#1.py

Jim Unroe, 11/01/2021 07:25 PM

 
1
# Copyright 2019 Pavel Milanes, CO7WT <pavelmc@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 struct
17
import os
18
import logging
19
import time
20

    
21
from time import sleep
22
from chirp import chirp_common, directory, memmap, errors, util, bitwise
23
from chirp.settings import RadioSettingGroup, RadioSetting, \
24
    RadioSettingValueBoolean, RadioSettingValueList, \
25
    RadioSettingValueString, RadioSettingValueInteger, \
26
    RadioSettingValueFloat, RadioSettings, InvalidValueError
27
from textwrap import dedent
28

    
29
LOG = logging.getLogger(__name__)
30

    
31
### SAMPLE MEM DUMP as sent from the radios
32

    
33
# FTL-1011
34
#0x000000  52 f0 16 90 04 08 38 c0  00 00 00 01 00 00 00 ff  |R.....8.........|
35
#0x000010  20 f1 00 20 00 00 00 20  04 47 25 04 47 25 00 00  | .. ... .G%.G%..|
36

    
37
# FTL-2011
38
#0x000000: 50 90 21 40 04 80 fc 40  00 00 00 01 00 00 00 ff  |P.!@...@........|
39
#0x000010: 20 f1 00 0b 00 00 00 0b  14 51 70 14 45 70 00 00  |.........Qp.Ep..|
40

    
41

    
42
MEM_FORMAT = """
43
#seekto 0x000;
44
u8 rid;                 // Radio Identification
45

    
46
struct {
47
u8 scan_time:4,         // Scan timer per channel: 0-15 (5-80msec in 5msec steps)
48
   unknownA:4;
49
bbcd if[2];             // Radio internal IF, depending on model (16.90, 21.40, 45.10, 47.90)
50
u8 chcount;             // how many channels are programmed
51
u8 scan_resume:1,           // Scan sesume: 0 = 0.5 seconds, 1 = Carrier
52
   priority_during_scan:1,  // Priority during scan: 0 = enabled, 1 = disabled
53
   priority_speed:1,        // Priority speed: 0 = slow, 1 = fast
54
   monitor:1,               // Monitor: 0 = enabled, 1 = disabled
55
   off_hook:1,              // Off hook: 0 = enabled, 1 = disabled
56
   home_channel:1,          // Home Channel: 0 = Scan Start ch, 1 = Priority 1ch
57
   talk_back:1,             // Talk Back: 0 = enabled, 1 = disabled
58
   tx_carrier_delay:1;      // TX carrier delay: 1 = enabled, 0 = disabled
59
u8 tot:4,                   // Time out timer: 16 values (0.0-7.5 in 0.5s step)
60
   tot_resume:2,            // Time out timer resume: 3, 2, 1, 0 => 0s, 6s, 20s, 60s 
61
   unknownB:2;
62
u8 a_key:2,                 // A key function: resume: 0-3: Talkaround, High/Low, Call, Accessory
63
   unknownB:6;
64
u8 pch1;                    // Priority channel 1
65
u8 pch2;                    // Priority channel 1
66
} settings;
67

    
68
#seekto 0x010;
69
struct {
70
  u8 notx:1,            // 0 = Tx posible,  1 = Tx disabled
71
     empty:1,           // 0 = channel enabled, 1 = channed empty
72
     tot:1,             // 0 = tot disabled, 1 = tot enabled
73
     power:1,           // 0 = high, 1 = low
74
     bclo_cw:1,         // 0 = disabled, 1 = Busy Channel Lock out by carrier
75
     bclo_tone:1,       // 0 = disabled, 1 = Busy Channel Lock out by tone (set rx tone)
76
     skip:1,            // 0 = scan enabled, 1 = skip on scanning
77
     unknownA0:1;
78
  u8 chname;
79
  u8 rx_tone[2];      // empty value is \x00\x0B / disabled is \x00\x00
80
  u8 unknown4;
81
  u8 unknown5;
82
  u8 tx_tone[2];      // empty value is \x00\x0B / disabled is \x00\x00
83
  bbcd rx_freq[3];      // RX freq
84
  bbcd tx_freq[3];      // TX freq
85
  u8 unknownA[2];
86
} memory[24];
87

    
88
#seekto 0x0190;
89
char filename[11];
90

    
91
#seekto 0x19C;
92
u8 checksum;
93
"""
94

    
95
MEM_SIZE = 0x019C
96
POWER_LEVELS = [chirp_common.PowerLevel("High", watts=50),
97
                chirp_common.PowerLevel("Low", watts=5)]
98
DTCS_CODES = chirp_common.DTCS_CODES
99
SKIP_VALUES = ["", "S"]
100
LIST_BCL = ["OFF", "Carrier", "Tone"]
101
LIST_SCAN_RESUME = ["0.5 seconds", "Carrier drop"]
102
LIST_SCAN_TIME = ["%smsecs" % x for x in range(5, 85, 5)]
103
LIST_SCAN_P_SPEED = ["Slow", "Fast"]
104
LIST_HOME_CHANNEL = ["Scan Start ch", "Priority 1ch"]
105
LIST_TOT = ["Off"] + ["%.1f s" % (x/10.0) for x in range(5, 80, 5)]
106
# 3, 2, 1, 0 => 0s, 6s, 20s, 60s
107
LIST_TOT_RESUME = ["60s","20s","6s","0s"]
108
LIST_A_KEY = ["Talkaround", "High/Low", "Call", "Accessory"]
109
LIST_PCH = [] # dynamic, as depends on channel list.
110
# make a copy of the tones, is not funny to work with this directly
111
TONES = list(chirp_common.TONES)
112
# this old radios has not the full tone ranges in CST
113
invalid_tones = (
114
    69.3,
115
    159.8,
116
    165.5,
117
    171.3,
118
    177.3,
119
    183.5,
120
    189.9,
121
    196.6,
122
    199.5,
123
    206.5,
124
    229.1,
125
    245.1)
126

    
127
# remove invalid tones
128
for tone in invalid_tones:
129
    try:
130
        TONES.remove(tone)
131
    except:
132
        pass
133

    
134

    
135
def _set_serial(radio):
136
    """Set the serial protocol settings"""
137
    radio.pipe.timeout = 10
138
    radio.pipe.parity = "N"
139
    radio.pipe.baudrate = 9600
140

    
141

    
142
def _checksum(data):
143
    """the radio block checksum algorithm"""
144
    cs = 0
145
    for byte in data:
146
            cs += ord(byte)
147

    
148
    return cs % 256
149

    
150

    
151
def _update_cs(radio):
152
    """Update the checksum on the mmap"""
153
    payload = str(radio.get_mmap())[:-1]
154
    cs = _checksum(payload)
155
    radio._mmap[MEM_SIZE - 1] = cs
156

    
157

    
158
def _do_download(radio):
159
    """ The download function """
160
    # Get the whole 413 bytes (0x019D) bytes one at a time with plenty of time
161
    # to get to the user's pace
162

    
163
    # set serial discipline
164
    _set_serial(radio)
165

    
166
    # UI progress
167
    status = chirp_common.Status()
168
    status.cur = 0
169
    status.max = MEM_SIZE
170
    status.msg = " Press A to clone. "
171
    radio.status_fn(status)
172

    
173
    data = ""
174
    for i in range(0, MEM_SIZE):
175
        a = radio.pipe.read(1)
176
        if len(a) == 0:
177
            # error, no received data
178
            if len(data) != 0:
179
                # received some data, not the complete stream
180
                msg = "Just %02i bytes of the %02i received, try again." % \
181
                    (len(data), MEM_SIZE)
182
            else:
183
                # timeout, please retry
184
                msg = "No data received, try again."
185

    
186
            raise errors.RadioError(msg)
187

    
188
        data += a
189
        # UI Update
190
        status.cur = len(data)
191
        radio.status_fn(status)
192

    
193
    if len(data) != MEM_SIZE:
194
        msg = "Incomplete data, we need %02i but got %02i bytes." % \
195
            (MEM_SIZE, len(data))
196
        raise errors.RadioError(msg)
197

    
198
    if ord(data[-1]) != _checksum(data[:-1]):
199
        msg = "Bad checksum, please try again."
200
        raise errors.RadioError(msg)
201

    
202
    return data
203

    
204

    
205
def _do_upload(radio):
206
    """The upload function"""
207
    # set serial discipline
208
    _set_serial(radio)
209

    
210
    # UI progress
211
    status = chirp_common.Status()
212

    
213
    # 10 seconds timeout
214
    status.cur = 0
215
    status.max = 100
216
    status.msg = " Quick, press MON on the radio to start. "
217
    radio.status_fn(status)
218

    
219
    for byte in range(0,100):
220
        status.cur = byte
221
        radio.status_fn(status)
222
        time.sleep(0.1)
223

    
224

    
225
    # real upload if user don't cancel the timeout
226
    status.cur = 0
227
    status.max = MEM_SIZE
228
    status.msg = " Cloning to radio... "
229
    radio.status_fn(status)
230

    
231
    # send data
232
    data = str(radio.get_mmap())
233

    
234
    # this radio has a trick, the EEPROM is an ancient SPI one, so it needs
235
    # some time to write, so we send every byte and then allow
236
    # a 0.01 seg to complete the write from the MCU to the SPI EEPROM
237
    c = 0
238
    for byte in data:
239
        radio.pipe.write(byte)
240
        time.sleep(0.01)
241

    
242
        # UI Update
243
        status.cur = c
244
        radio.status_fn(status)
245

    
246
        # counter
247
        c = c + 1
248

    
249

    
250
def _model_match(cls, data):
251
    """Use a experimental guess to determine if the radio you just
252
    downloaded or the img you opened is for this model"""
253

    
254
    # It's hard to tell when this radio is really this radio.
255
    # I use the first byte, that appears to be the ID and the IF settings
256

    
257
    LOG.debug("Drivers's ID string:")
258
    LOG.debug(cls.finger)
259
    LOG.debug("Radio's ID string:")
260
    LOG.debug(util.hexprint(data[0:4]))
261

    
262
    radiod = [data[0], data[2:4]]
263
    if cls.finger == radiod:
264
        return True
265
    else:
266
        return False
267

    
268

    
269
def bcd_to_int(data):
270
    """Convert an array of bcdDataElement like \x12
271
    into an int like 12"""
272
    value = 0
273
    a = (data & 0xF0) >> 4
274
    b = data & 0x0F
275
    value = (a * 10) + b
276
    return value
277

    
278

    
279
def int_to_bcd(data):
280
    """Convert a int like 94 to 0x94"""
281
    data, lsb = divmod(data, 10)
282
    data, msb = divmod(data, 10)
283
    res = (msb << 4) + lsb
284
    return res
285

    
286

    
287
class ftlx011(chirp_common.CloneModeRadio, chirp_common.ExperimentalRadio):
288
    """Vertex FTL1011/2011/7011 4/8/12/24 channels"""
289
    VENDOR = "Vertex Standard"
290
    _memsize = MEM_SIZE
291
    _upper = 0
292
    _range = []
293
    finger = [] # two elements rid & IF
294

    
295
    @classmethod
296
    def get_prompts(cls):
297
        rp = chirp_common.RadioPrompts()
298
        rp.experimental = \
299
            ('This is a experimental driver, use it on your own risk.\n'
300
             '\n'
301
             'This driver is just for the 4/12/24 channels variants of '
302
             'these radios, 99 channel variants are not supported yet.\n'
303
             '\n'
304
             'The 99 channel versions appears to use another mem layout.\n'
305
             )
306
        rp.pre_download = _(dedent("""\
307
            Please follow this steps carefully:
308

    
309
            1 - Turn on your radio
310
            2 - Connect the interface cable to your radio.
311
            3 - Click the button on this window to start download
312
                (Radio will beep and led will flash)
313
            4 - Then press the "A" button in your radio to start cloning.
314
                (At the end radio will beep)
315
            """))
316
        rp.pre_upload = _(dedent("""\
317
            Please follow this steps carefully:
318

    
319
            1 - Turn on your radio
320
            2 - Connect the interface cable to your radio
321
            3 - Click the button on this window to start download
322
                (you may see another dialog, click ok)
323
            4 - Radio will beep and led will flash
324
            5 - You will get a 10 seconds timeout to press "MON" before
325
                data upload start
326
            6 - If all goes right radio will beep at end.
327

    
328
            After cloning remove the cable and power cycle your radio to
329
            get into normal mode.
330
            """))
331
        return rp
332

    
333
    def get_features(self):
334
        """Return information about this radio's features"""
335
        rf = chirp_common.RadioFeatures()
336
        rf.has_settings = True
337
        rf.has_bank = False
338
        rf.has_tuning_step = False
339
        rf.has_name = False
340
        rf.has_offset = True
341
        rf.has_mode = True
342
        rf.has_dtcs = True
343
        rf.has_rx_dtcs = True
344
        rf.has_dtcs_polarity = False
345
        rf.has_ctone = True
346
        rf.has_cross = True
347
        rf.valid_duplexes = ["", "-", "+", "off"]
348
        rf.valid_tmodes = ['', 'Tone', 'TSQL', 'DTCS', 'Cross']
349
        rf.valid_cross_modes = [
350
            "Tone->Tone",
351
            "DTCS->DTCS",
352
            "DTCS->",
353
            "->DTCS"]
354
        rf.valid_dtcs_codes = DTCS_CODES
355
        rf.valid_skips = SKIP_VALUES
356
        rf.valid_modes = ["FM"]
357
        rf.valid_power_levels = POWER_LEVELS
358
        #rf.valid_tuning_steps = [5.0]
359
        rf.valid_bands = [self._range]
360
        rf.memory_bounds = (1, self._upper)
361
        return rf
362

    
363
    def sync_in(self):
364
        """Do a download of the radio eeprom"""
365
        try:
366
            data = _do_download(self)
367
        except Exception, e:
368
            raise errors.RadioError("Failed to communicate with radio:\n %s" % e)
369

    
370
        # match model
371
        if _model_match(self, data) is False:
372
            raise errors.RadioError("Incorrect radio model")
373

    
374
        self._mmap = memmap.MemoryMap(data)
375
        self.process_mmap()
376

    
377
        # set the channel count from the radio eeprom
378
        self._upper = int(ord(data[4]))
379

    
380
    def sync_out(self):
381
        """Do an upload to the radio eeprom"""
382
        # update checksum
383
        _update_cs(self)
384

    
385
        # sanity check, match model
386
        data = str(self.get_mmap())
387
        if len(data) != MEM_SIZE:
388
            raise errors.RadioError("Wrong radio image? Size miss match.")
389

    
390
        if _model_match(self, data) is False:
391
            raise errors.RadioError("Wrong image? Fingerprint miss match")
392

    
393
        try:
394
            _do_upload(self)
395
        except Exception, e:
396
            msg = "Failed to communicate with radio:\n%s" % e
397
            raise errors.RadioError(msg)
398

    
399
    def process_mmap(self):
400
        """Process the memory object"""
401
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
402

    
403
    def get_raw_memory(self, number):
404
        """Return a raw representation of the memory object"""
405
        return repr(self._memobj.memory[number])
406

    
407
    def _decode_tone(self, mem, rx=True):
408
        """Parse the tone data to decode from mem tones are encodded like this
409
        CTCS: mapped [0x80...0xa5] = [67.0...250.3]
410
        DTCS: mixed  [0x88, 0x23] last is BCD and first is the 100 power - 88
411

    
412
        It return: ((''|DTCS|Tone), Value (None|###), None)"""
413
        mode = ""
414
        tone = None
415

    
416
        # get the tone depending of rx or tx
417
        if rx:
418
            t = mem.rx_tone
419
        else:
420
            t = mem.tx_tone
421
        
422
        tMSB = t[0]
423
        tLSB = t[1]
424

    
425
        # no tone at all
426
        if (tMSB == 0 and tLSB < 128):
427
            return ('', None, None)
428

    
429
        # extract the tone info
430
        if tMSB == 0x00:
431
            # CTCS
432
            mode = "Tone"
433
            try:
434
                tone = TONES[tLSB - 128]
435
            except IndexError:
436
                LOG.debug("Error decoding a CTCS tone")
437
                pass
438
        else:
439
            # DTCS
440
            mode = "DTCS"
441
            try:
442
                tone = ((tMSB - 0x88) * 100) + bcd_to_int(tLSB)
443
            except IndexError:
444
                LOG.debug("Error decoding a DTCS tone")
445
                pass
446

    
447
        return (mode, tone, None)
448

    
449
    def _encode_tone(self, mem, mode, value, pol, rx=True):
450
        """Parse the tone data to encode from UI to mem
451
        CTCS: mapped [0x80...0xa5] = [67.0...250.3]
452
        DTCS: mixed  [0x88, 0x23] last is BCD and first is the 100 power - 88
453
        """
454

    
455
        # array to pass
456
        tone = [0x00, 0x00]
457

    
458
        # which mod
459
        if mode == "DTCS":
460
            tone[0] = int(value / 100) + 0x88
461
            tone[1] = int_to_bcd(value % 100)
462
        
463
        if mode == "Tone":
464
            #CTCS
465
            tone[1] = TONES.index(value) + 128
466

    
467
        # set it
468
        if rx:
469
            mem.rx_tone = tone
470
        else:
471
            mem.tx_tone = tone
472

    
473
    def get_memory(self, number):
474
        """Extract a memory object from the memory map"""
475
        # Get a low-level memory object mapped to the image
476
        _mem = self._memobj.memory[number - 1]
477
        # Create a high-level memory object to return to the UI
478
        mem = chirp_common.Memory()
479
        # number
480
        mem.number = number
481

    
482
        # empty
483
        if bool(_mem.empty) is True:
484
            mem.empty = True
485
            return mem
486

    
487
        if int(_mem.rx_freq) == 0:
488
            mem.empty = True
489
            return mem
490

    
491
        # rx freq
492
        mem.freq = int(_mem.rx_freq) * 1000
493

    
494
        # power
495
        mem.power = POWER_LEVELS[int(_mem.power)]
496

    
497
        # checking if tx freq is disabled
498
        if bool(_mem.notx) is True:
499
            mem.duplex = "off"
500
            mem.offset = 0
501
        else:
502
            tx = int(_mem.tx_freq) * 1000
503
            if tx == mem.freq:
504
                mem.offset = 0
505
                mem.duplex = ""
506
            else:
507
                mem.duplex = mem.freq > tx and "-" or "+"
508
                mem.offset = abs(tx - mem.freq)
509

    
510
        # skip
511
        mem.skip = SKIP_VALUES[_mem.skip]
512

    
513
        # tone data
514
        rxtone = txtone = None
515
        rxtone = self._decode_tone(_mem)
516
        txtone = self._decode_tone(_mem, False)
517
        chirp_common.split_tone_decode(mem, txtone, rxtone)
518

    
519
        # this radio has a primitive mode to show the channel number on a 7-segment
520
        # two digit LCD, we will use channel number
521
        # we will use a trick to show the numbers < 10 wit a space not a zero in front
522
        chname = int_to_bcd(mem.number)
523
        if mem.number < 10:
524
            # convert to F# as BCD
525
            chname = mem.number + 240
526

    
527
        _mem.chname = chname
528

    
529
        # Extra
530
        mem.extra = RadioSettingGroup("extra", "Extra")
531

    
532
        # bcl preparations: ["OFF", "Carrier", "Tone"]
533
        bcls = 0
534
        if _mem.bclo_cw:
535
            bcls = 1
536
        if _mem.bclo_tone:
537
            bcls = 2
538

    
539
        bcl = RadioSetting("bclo", "Busy channel lockout",
540
                           RadioSettingValueList(LIST_BCL,
541
                                                 LIST_BCL[bcls]))
542
        mem.extra.append(bcl)
543

    
544
        tot = RadioSetting("tot", "Time out timer",
545
                           RadioSettingValueBoolean(_mem.tot))
546
        mem.extra.append(tot)
547

    
548
        # return mem
549
        return mem
550

    
551
    def set_memory(self, mem):
552
        """Store details about a high-level memory to the memory map
553
        This is called when a user edits a memory in the UI"""
554
        # Get a low-level memory object mapped to the image
555
        _mem = self._memobj.memory[mem.number - 1]
556

    
557
        # Empty memory
558
        if mem.empty:
559
            _mem.empty = True
560
            _mem.rx_freq = _mem.tx_freq = 0
561
            return
562
            
563
        # freq rx
564
        _mem.rx_freq = mem.freq / 1000
565

    
566
        # power, # default power level is high
567
        _mem.power = 0 if mem.power is None else POWER_LEVELS.index(mem.power)
568

    
569
        # freq tx
570
        _mem.notx = 0
571
        if mem.duplex == "+":
572
            _mem.tx_freq = (mem.freq + mem.offset) / 1000
573
        elif mem.duplex == "-":
574
            _mem.tx_freq = (mem.freq - mem.offset) / 1000
575
        elif mem.duplex == "off":
576
            _mem.notx = 1
577
            _mem.tx_freq = _mem.rx_freq
578
        else:
579
            _mem.tx_freq = mem.freq / 1000
580

    
581
        # scan add property
582
        _mem.skip = SKIP_VALUES.index(mem.skip)
583

    
584
        # tone data
585
        ((txmode, txtone, txpol), (rxmode, rxtone, rxpol)) = \
586
            chirp_common.split_tone_encode(mem)
587

    
588
        # validate tone data from here
589
        if rxmode == "Tone" and rxtone in invalid_tones:
590
            msg = "The tone %shz is not valid for this radio" % rxtone
591
            raise errors.UnsupportedToneError(msg)
592

    
593
        if txmode == "Tone" and txtone in invalid_tones:
594
            msg = "The tone %shz is not valid for this radio" % txtone
595
            raise errors.UnsupportedToneError(msg)
596

    
597
        if rxmode == "DTCS" and rxtone not in DTCS_CODES:
598
            msg = "The digital tone %s is not valid for this radio" % rxtone
599
            raise errors.UnsupportedToneError(msg)
600

    
601
        if txmode == "DTCS" and txtone not in DTCS_CODES:
602
            msg = "The digital tone %s is not valid for this radio" % txtone
603
            raise errors.UnsupportedToneError(msg)
604

    
605
        self._encode_tone(_mem, rxmode, rxtone, rxpol)
606
        self._encode_tone(_mem, txmode, txtone, txpol, False)
607

    
608
        # this radio has a primitive mode to show the channel number on a 7-segment
609
        # two digit LCD, we will use channel number
610
        # we will use a trick to show the numbers < 10 wit a space not a zero in front
611
        chname = int_to_bcd(mem.number)
612
        if mem.number < 10:
613
            # convert to F# as BCD
614
            chname = mem.number + 240
615

    
616
        def _zero_settings():
617
            _mem.bclo_cw = 0
618
            _mem.bclo_tone = 0
619
            _mem.tot = 0
620

    
621
        # extra settings
622
        if len(mem.extra) > 0:
623
            # there are setting, parse
624
            LOG.debug("Extra-Setting supplied. Setting them.")
625
            # Zero them all first so any not provided by model don't
626
            # stay set
627
            _zero_settings()
628
            for setting in mem.extra:
629
                if setting.get_name() == "bclo":
630
                    sw = LIST_BCL.index(str(setting.value))
631
                    if sw == 0:
632
                        # empty
633
                        _zero_settings()
634
                    if sw == 1:
635
                        # carrier
636
                        _mem.bclo_cw = 1
637
                    if sw == 2:
638
                        # tone
639
                        _mem.bclo_tone = 1
640
                        # activate the tone
641
                        _mem.rx_tone = [0x00, 0x80]
642
            else:
643
                setattr(_mem, setting.get_name(), setting.value)
644
        else:
645
            # reset extra settings 
646
            _zero_settings()
647

    
648
        _mem.chname = chname
649

    
650
        return mem
651

    
652
    def get_settings(self):
653
        _settings = self._memobj.settings
654
        basic = RadioSettingGroup("basic", "Basic Settings")
655
        group = RadioSettings(basic)
656

    
657
        # ## Basic Settings 
658
        scanr = RadioSetting("scan_resume", "Scan resume by",
659
                          RadioSettingValueList(
660
                              LIST_SCAN_RESUME, LIST_SCAN_RESUME[_settings.scan_resume]))
661
        basic.append(scanr)
662

    
663
        scant = RadioSetting("scan_time", "Scan time per channel",
664
                          RadioSettingValueList(
665
                              LIST_SCAN_TIME, LIST_SCAN_TIME[_settings.scan_time]))
666
        basic.append(scant)
667

    
668
        LIST_PCH = ["%s" % x for x in range(1, _settings.chcount + 1)]
669
        pch1 = RadioSetting("pch1", "Priority channel 1",
670
                             RadioSettingValueList(
671
                                 LIST_PCH, LIST_PCH[_settings.pch1]))
672
        basic.append(pch1)
673

    
674
        pch2 = RadioSetting("pch2", "Priority channel 2",
675
                            RadioSettingValueList(
676
                                LIST_PCH, LIST_PCH[_settings.pch2]))
677
        basic.append(pch2)
678

    
679
        scanp = RadioSetting("priority_during_scan", "Disable priority during scan",
680
                             RadioSettingValueBoolean(_settings.priority_during_scan))
681
        basic.append(scanp)
682

    
683
        scanps = RadioSetting("priority_speed", "Priority scan speed",
684
                              RadioSettingValueList(
685
                                LIST_SCAN_P_SPEED, LIST_SCAN_P_SPEED[_settings.priority_speed]))
686
        basic.append(scanps)
687

    
688
        oh = RadioSetting("off_hook", "Off Hook", #inverted
689
                        RadioSettingValueBoolean(not _settings.off_hook))
690
        basic.append(oh)
691

    
692
        tb = RadioSetting("talk_back", "Talk Back",  # inverted
693
                          RadioSettingValueBoolean(not _settings.talk_back))
694
        basic.append(tb)
695

    
696
        tot = RadioSetting("tot", "Time out timer",
697
                             RadioSettingValueList(
698
                                 LIST_TOT, LIST_TOT[_settings.tot]))
699
        basic.append(tot)
700

    
701
        totr = RadioSetting("tot_resume", "Time out timer resume guard",
702
                           RadioSettingValueList(
703
                               LIST_TOT_RESUME, LIST_TOT_RESUME[_settings.tot_resume]))
704
        basic.append(totr)
705

    
706
        ak = RadioSetting("a_key", "A Key function",
707
                            RadioSettingValueList(
708
                                LIST_A_KEY, LIST_A_KEY[_settings.a_key]))
709
        basic.append(ak)
710

    
711
        monitor = RadioSetting("monitor", "Monitor",  # inverted
712
                               RadioSettingValueBoolean(not _settings.monitor))
713
        basic.append(monitor)
714

    
715
        homec = RadioSetting("home_channel", "Home Channel is",
716
                             RadioSettingValueList(
717
                                 LIST_HOME_CHANNEL, LIST_HOME_CHANNEL[_settings.home_channel]))
718
        basic.append(homec)
719

    
720
        txd = RadioSetting("tx_carrier_delay", "Talk Back",
721
                           RadioSettingValueBoolean(_settings.tx_carrier_delay))
722
        basic.append(txd)
723

    
724

    
725
        return group
726

    
727
    def set_settings(self, uisettings):
728
        _settings = self._memobj.settings
729

    
730
        for element in uisettings:
731
            if not isinstance(element, RadioSetting):
732
                self.set_settings(element)
733
                continue
734
            if not element.changed():
735
                continue
736

    
737
            try:
738
                name = element.get_name()
739
                value = element.value
740

    
741
                print("== Setting %s: %s" % (name, value))
742

    
743
                obj = getattr(_settings, name)
744
                if name in ["off_hook", "talk_back", "monitor"]:
745
                    setattr(_settings, name, not value)
746
                else:
747
                    setattr(_settings, name, value)
748

    
749
                LOG.debug("Setting %s: %s" % (name, value))
750
            except Exception, e:
751
                LOG.debug(element.get_name())
752
                raise
753

    
754
    @classmethod
755
    def match_model(cls, filedata, filename):
756
        match_size = False
757
        match_model = False
758

    
759
        # testing the file data size
760
        if len(filedata) == cls._memsize:
761
            match_size = True
762
            print("Comp: %i file / %i memzise" % (len(filedata), cls._memsize) )
763

    
764
        # testing the firmware fingerprint, this experimental
765
        match_model = _model_match(cls, filedata)
766

    
767
        if match_size and match_model:
768
            return True
769
        else:
770
            return False
771

    
772

    
773
@directory.register
774
class ftl1011(ftlx011):
775
    """Vertex FTL-1011"""
776
    MODEL = "FTL-1011"
777
    _memsize = MEM_SIZE
778
    _upper = 4
779
    _range = [44000000, 56000000]
780
    finger = ["\x52", "\x16\x90"]
781

    
782

    
783
@directory.register
784
class ftl2011(ftlx011):
785
    """Vertex FTL-2011"""
786
    MODEL = "FTL-2011"
787
    _memsize = MEM_SIZE
788
    _upper = 24
789
    _range = [134000000, 174000000]
790
    finger = ["\x50", "\x21\x40"]
791

    
792

    
793
@directory.register
794
class ftl7011(ftlx011):
795
    """Vertex FTL-7011"""
796
    MODEL = "FTL-7011"
797
    _memsize = MEM_SIZE
798
    _upper = 24
799
    _range = [400000000, 512000000]
800
    finger = ["\x54", "\x47\x90"]
801

    
802

    
803
@directory.register
804
class ftl8011(ftlx011):
805
    """Vertex FTL-8011"""
806
    MODEL = "FTL-8011"
807
    _memsize = MEM_SIZE
808
    _upper = 24
809
    _range = [400000000, 512000000]
810
    finger = ["\x5c", "\x45\x10"]
(3-3/9)