Project

General

Profile

New Model #1035 » th9000-v0.5.patch

hg patch file - David Fannin, 04/18/2015 10:16 PM

View differences:

/dev/null Thu Jan 01 00:00:00 1970 +0000 → chirp/drivers/th9000.py Sat Apr 18 22:02:22 2015 -0700
1
# Copyright 2012 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 os
17
import struct
18
import time
19
import logging
20

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

  
32
#from chirp.settings import RadioSetting, RadioSettingGroup, \
33
#        RadioSettingValueInteger, RadioSettingValueList, \
34
#        RadioSettingValueBoolean, RadioSettingValueString, \
35
#        RadioSettingValueFloat, InvalidValueError
36

  
37
LOG = logging.getLogger(__name__)
38

  
39
#
40
#  Chirp Driver for TYT TH-9000D  Radio (2 meter, 1.25 and 70cm radios)
41
#  by David Fannin <dfannin@sushisoft.com>, KK6DF
42
#
43
#  Version 0.5 (Experimental - Known Bugs and Issues)
44
#  Use for development purposes only!  
45
#  Features working:
46
#         - single class file for 3 radio types - 144, 220 and 440 mhz models)
47
#         - Download from Radio
48
#         - Display Memories (only None, Tone, TSQL signalling supported)
49
#         - Save image file
50
#         - memory map decoded (about 90%)
51
#         - Upload to radio
52
#         - Modification of memories
53
#         - feature settings
54
#         - added Startup ID label
55
#
56
#  Features not working:
57
#         - DCS , Cross Signaling
58
#         - Skip channels
59

  
60

  
61
#
62
# Global Parameters 
63
#
64
MMAPSIZE = 16128
65
TONES = [62.5] + list(chirp_common.TONES)
66
TMODES =  ['','Tone','DTCS'] 
67
DUPLEXES = ['','err','-','+'] # index 2 not used
68
MODES = ['WFM','FM','NFM']  #  25k, 20k,15k bw 
69
TUNING_STEPS=[ 5.0, 6.25, 8.33, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0 ] # index 0-9
70
POWER_LEVELS=[chirp_common.PowerLevel("High", watts=65),
71
              chirp_common.PowerLevel("Mid", watts=25),
72
              chirp_common.PowerLevel("Low", watts=10)]
73

  
74
CROSS_MODES = chirp_common.CROSS_MODES
75

  
76
APO_LIST = [ "Off","30 min","1 hr","2 hrs" ] 
77
BGCOLOR_LIST = ["Blue","Orange","Purple"]
78
BGBRIGHT_LIST = ["%s" % x for x in range(1,32)]
79
SQUELCH_LIST = ["Off"] + ["Level %s" % x for x in range(1,20)] 
80
TIMEOUT_LIST = ["Off"] + ["%s min" % x for x in range(1,30)]
81
TXPWR_LIST = ["60W","25W"]  # maximum power for Hi setting
82
TBSTFREQ_LIST = ["1750Hz","2100Hz","1000Hz","1450Hz"]
83
BEEP_LIST = ["Off","On"]
84

  
85
SETTING_LISTS = {
86
        "auto_power_off": APO_LIST,
87
        "bg_color"      : BGCOLOR_LIST,
88
        "bg_brightness" : BGBRIGHT_LIST,
89
        "squelch"       : SQUELCH_LIST,
90
        "timeout_timer" : TIMEOUT_LIST,
91
        "choose_tx_power": TXPWR_LIST,
92
        "tbst_freq"     : TBSTFREQ_LIST,
93
        "voice_prompt"  : BEEP_LIST
94
}
95

  
96

  
97
#
98
#
99
#
100

  
101
MEM_FORMAT = """
102
#seekto 0x0000;
103
struct {
104
   u8 unknown0000[16];
105
   char idhdr[16];
106
   u8 unknown0001[16];
107
} fidhdr;
108
"""
109

  
110
"""
111
Overall Memory Map:
112

  
113
    Memory Map (Range 0x0100-3FF0, step 0x10):
114

  
115
        Field                   Start  End  Size   
116
                                (hex)  (hex) (hex)  
117
        
118
        1 Channel Set Flag        0100  011F   20 
119
        2 Channel Skip Flag       0120  013F   20 
120
        3 Blank/Unknown           0140  01EF   B0 
121
        4 Unknown                 01F0  01FF   10
122
        5 TX/RX Range             0200  020F   10    
123
        6 Bootup Passwd           0210  021F   10 
124
        7 Options, Radio          0220  023F   20
125
        8 Unknown                 0240  019F   
126
            8B Startup Label      03E0  03E7   07
127
        9 Channel Bank            2000  38FF 1900  
128
             Channel 000          2000  201F   20  
129
             Channel 001          2020  202F   20  
130
             ... 
131
             Channel 199          38E0  38FF   20 
132
        10 Blank/Unknown          3900  3FFF  6FF  14592  16383    1792   
133
            Total Map Size           16128 (2^8 = 16384)
134

  
135

  
136
"""
137

  
138
"""
139
  TH9000/220  memory map 
140
  section: 1 and 2:  Channel Set/Skip Flags
141

  
142
    Channel Set (starts 0x100) : Channel Set  bit is value 0 if a memory location in the channel bank is active.
143
    Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
144

  
145
    Both flag maps are a total 24 bytes in length, aligned on 32 byte records.
146
    bit = 0 channel set/no skip,  1 is channel not set/skip
147

  
148
    to index a channel:
149
        cbyte = channel / 8 ;
150
        cbit  = channel % 8 ;
151
        setflag  = csetflag[cbyte].c[cbit] ;
152
        skipflag = cskipflag[cbyte].c[cbit] ;
153

  
154
    channel range is 0-199, range is 32 bytes (last 7 unknown)
155
"""
156

  
157

  
158
MEM_FORMAT = MEM_FORMAT + """
159
#seekto 0x0100;
160
struct {
161
   bit c[8];
162
} csetflag[32];
163

  
164
struct {
165
   u8 unknown0100[7];
166
} ropt0100;
167

  
168
#seekto 0x0120;
169
struct {
170
   bit c[8];
171
} cskipflag[32];
172

  
173
struct {
174
   u8 unknown0120[7];
175
} ropt0120;
176
"""
177

  
178
"""
179
  TH9000/220  memory map 
180
  section: 5  TX/RX Range
181
     used to set the TX/RX range of the radio (e.g.  222-228Mhz for 220 meter)
182
     possible to set range to 220-260Mhz for tx/rx 
183

  
184
"""
185

  
186

  
187
MEM_FORMAT = MEM_FORMAT + """
188
#seekto 0x0200;
189
struct {
190
    bbcd txrangelow[4];
191
    bbcd txrangehi[4];
192
    bbcd rxrangelow[4];
193
    bbcd rxrangehi[4];
194
} freqrange;
195
"""
196

  
197
"""
198
  TH9000/220  memory map 
199
  section: 6  bootup_passwd
200
     used to set bootup passwd (see boot_passwd checkbox option)
201

  
202
  options - bootup password
203

  
204
  bytes:bit   type                 description
205
  ---------------------------------------------------------------------------
206
  6         u8 bootup_passwd[6]     bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
207
  10        u8 unknown;  
208

  
209
"""
210

  
211
MEM_FORMAT = MEM_FORMAT + """
212
#seekto 0x0210;
213
struct {
214
   u8 bootup_passwd[6];
215
   u8 unknown2010[10];
216
} ropt0210;
217
"""
218

  
219
"""
220
  TH9000/220  memory map 
221
  section: 7  Radio Options  
222
        used to set a number of radio options 
223

  
224
  bytes:bit   type                 description
225
  ---------------------------------------------------------------------------
226
  1         u8 display_mode     display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
227
  1         u8 vfo_mr;          vfo_mr , 0=vfo, mr=1 
228
  1         u8 unknown;  
229
  1         u8 squelch;         squelch level, range 0-19, hex for menu
230
  1         u8 unknown[2]; 
231
  1         u8 channel_lock;    if display_mode[channel] selected, then lock=1,no lock =0
232
  1         u8 unknown; 
233
  1         u8 bg_brightness ;  background brightness, range 0-21, hex, menu index 
234
  1         u8 unknown;     
235
  1         u8 bg_color ;       bg color, menu index,  blue 0 , orange 1, purple 2
236
  1         u8 tbst_freq ;      tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz 
237
  1         u8 timeout_timer;   timeout timer, hex, value = minutes, 0= no timeout
238
  1         u8 unknown; 
239
  1         u8 auto_power_off;   auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
240
  1         u8 voice_prompt;     voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
241

  
242
 description of function setup options, starting at 0x0230
243

  
244
  bytes:bit   type                 description
245
  ---------------------------------------------------------------------------
246
  1         u8  // 0
247
   :4       unknown:6
248
   :1       elim_sql_tail:1   eliminate squelsh tail when no ctcss checkbox (1=checked)
249
   :1       sql_key_function  "squelch off" 1 , "squelch momentary off" 0 , menu index
250
  2         u8 unknown[2] /1-2  
251
  1         u8 // 3
252
   :4       unknown:4
253
   :1       inhibit_init_ops:1 //bit 5
254
   :1       unknownD:1
255
   :1       inhibit_setup_bg_chk:1 //bit 7
256
   :1       unknown:1
257
  1         u8 tail_elim_type    menu , (off=0,120=1,180=2),  // 4
258
  1         u8 choose_tx_power    menu , (60w=0,25w=1) // 5
259
  2         u8 unknown[2]; // 6-7 
260
  1         u8 bootup_passwd_flag  checkbox 1=on, 0=off // 8
261
  7         u8 unknown[7]; // 9-F 
262

  
263
"""
264

  
265
MEM_FORMAT = MEM_FORMAT + """
266
#seekto 0x0220;
267
struct {
268
   u8 display_mode; 
269
   u8 vfo_mr; 
270
   u8 unknown0220A; 
271
   u8 squelch; 
272
   u8 unknown0220B[2]; 
273
   u8 channel_lock; 
274
   u8 unknown0220C; 
275
   u8 bg_brightness; 
276
   u8 unknown0220D; 
277
   u8 bg_color;
278
   u8 tbst_freq;
279
   u8 timeout_timer;
280
   u8 unknown0220E;
281
   u8 auto_power_off;
282
   u8 voice_prompt; 
283
   u8 unknown0230A:6,
284
      elim_sql_tail:1,
285
      sql_key_function:1;
286
   u8 unknown0230B[2];
287
   u8 unknown0230C:4, 
288
      inhibit_init_ops:1,
289
      unknown0230D:1,
290
      inhibit_setup_bg_chk:1,
291
      unknown0230E:1;
292
   u8 tail_elim_type;
293
   u8 choose_tx_power;
294
   u8 unknown0230F[2];
295
   u8 bootup_passwd_flag;
296
   u8 unknown0230G[7];
297
} settings;
298
"""
299

  
300
"""
301
  TH9000/220  memory map 
302
  section: 8B  Startup Label  
303

  
304
  bytes:bit   type                 description
305
  ---------------------------------------------------------------------------
306
  7     char start_label[7]    label displayed at startup (usually your call sign)
307
"""
308
MEM_FORMAT = MEM_FORMAT + """
309
#seekto 0x03E0;
310
struct {
311
    char startname[7];
312
} slabel;
313
"""
314

  
315
"""
316
  TH9000/220  memory map 
317
  section: 9  Channel Bank
318
         description of channel bank (200 channels , range 0-199)
319
         Each 32 Byte (0x20 hex)  record:
320
  bytes:bit   type                 description
321
  ---------------------------------------------------------------------------
322
  4         bbcd freq[4]        receive frequency in packed binary coded decimal  
323
  4         bbcd offset[4]      transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
324
  1         u8
325
   :4       unknown:4
326
   :4       tuning_step:4         tuning step, menu index value from 0-9
327
            5,6.25,8.33,10,12.5,15,20,25,30,50
328
  1         u8
329
   :4       unknown:4          not yet decoded, used for DCS coding?
330
   :2       channel_width:2     channel spacing, menu index value from 0-3
331
            25,20,12.5
332
   :1       reverse:1           reverse flag, 0=off, 1=on (reverses tx and rx freqs)
333
   :1       txoff:1             transmitt off flag, 0=transmit , 1=do not transmit 
334
  1         u8
335
   :1       talkaround:1        talkaround flag, 0=off, 1=on (bypasses repeater) 
336
   :1       compander:1         compander flag, 0=off, 1=on (turns on/off voice compander option)  
337
   :2       unknown:2          
338
   :2       power:2             tx power setting, value range 0-2, 0=hi,1=med,2=lo 
339
   :2       duplex:2            duplex settings, 0=simplex,2= minus(-) offset, 3= plus (+) offset (see offset field) 
340
            
341
  1         u8 
342
   :4       unknown:4
343
   :2       rxtmode:2           rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS  (ctcss tone in field rxtone)
344
   :2       txtmode:2           tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS  (ctcss tone in field txtone)
345
  1         u8 
346
   :2       unknown:2
347
   :6       txtone:6            tx ctcss tone, menu index
348
  1         u8 
349
   :2       unknown:2 
350
   :6       rxtone:6            rx ctcss tone, menu index
351
  1         u8 txcode           ?, not used for ctcss
352
  1         u8 rxcode           ?, not used for ctcss
353
  3         u8 unknown[3]
354
  7         char name[7]        7 byte char string for channel name
355
  1         u8 
356
   :6       unknown:6,
357
   :2       busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy  (lock out tx if channel busy)
358
  4         u8 unknownI[4];
359
  1         u8 
360
   :7       unknown:7 
361
   :1       scrambler:1         scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
362
"""
363

  
364

  
365

  
366
MEM_FORMAT = MEM_FORMAT + """
367
#seekto 0x2000;
368
struct {
369
  bbcd freq[4];
370
  bbcd offset[4];
371
  u8 unknown2000A:4,
372
     tuning_step:4;
373
  u8 unknown2000B:4,
374
     channel_width:2,
375
     reverse:1,
376
     txoff:1;
377
  u8 talkaround:1,
378
     compander:1,
379
     unknown2000C:2,
380
     power:2,
381
     duplex:2;
382
  u8 unknown2000D:4,
383
     rxtmode:2,
384
     txtmode:2;
385
  u8 unknown2000E:2,
386
     txtone:6;
387
  u8 unknown2000F:2,
388
     rxtone:6;
389
  u8 txcode;
390
  u8 rxcode;
391
  u8 unknown2000G[3];
392
  char name[7];
393
  u8 unknown2000H:6,
394
     busychannellockout:2;
395
  u8 unknown2000I[4];
396
  u8 unknown2000J:7,
397
     scrambler:1; 
398
} memory[200] ;
399
"""
400

  
401

  
402
def _echo_write(radio, data):
403
    try:
404
        radio.pipe.write(data)
405
        radio.pipe.read(len(data))
406
    except Exception, e:
407
        LOG.error("Error writing to radio: %s" % e)
408
        raise errors.RadioError("Unable to write to radio")
409

  
410

  
411
def _checksum(data):
412
    cs = 0
413
    for byte in data:
414
        cs += ord(byte)
415
    return cs % 256
416

  
417
def _read(radio, length):
418
    try:
419
        data = radio.pipe.read(length)
420
    except Exception, e:
421
        LOG.error( "Error reading from radio: %s" % e)
422
        raise errors.RadioError("Unable to read from radio")
423

  
424
    if len(data) != length:
425
        LOG.error( "Short read from radio (%i, expected %i)" % (len(data),
426
                                                           length))
427
        LOG.debug(util.hexprint(data))
428
        raise errors.RadioError("Short read from radio")
429
    return data
430

  
431

  
432

  
433
def _ident(radio):
434
    radio.pipe.setTimeout(1)
435
    _echo_write(radio,"PROGRAM")
436
    response = radio.pipe.read(3)
437
    if response != "QX\06":
438
        LOG.debug( "Response was :\n%s" % util.hexprint(response))
439
        raise errors.RadioError("Unsupported model")
440
    _echo_write(radio, "\x02")
441
    response = radio.pipe.read(16)
442
    LOG.debug(util.hexprint(response))
443
    if response[1:8] != "TH-9000":
444
        LOG.error( "Looking  for:\n%s" % util.hexprint("TH-9000"))
445
        LOG.error( "Response was:\n%s" % util.hexprint(response))
446
        raise errors.RadioError("Unsupported model")
447

  
448
def _send(radio, cmd, addr, length, data=None):
449
    frame = struct.pack(">cHb", cmd, addr, length)
450
    if data:
451
        frame += data
452
        frame += chr(_checksum(frame[1:]))
453
        frame += "\x06"
454
    _echo_write(radio, frame)
455
    LOG.debug("Sent:\n%s" % util.hexprint(frame))
456
    if data:
457
        result = radio.pipe.read(1)
458
        if result != "\x06":
459
            LOG.debug( "Ack was: %s" % repr(result))
460
            raise errors.RadioError("Radio did not accept block at %04x" % addr)
461
        return
462
    result = _read(radio, length + 6)
463
    LOG.debug("Got:\n%s" % util.hexprint(result))
464
    header = result[0:4]
465
    data = result[4:-2]
466
    ack = result[-1]
467
    if ack != "\x06":
468
        LOG.debug("Ack was: %s" % repr(ack))
469
        raise errors.RadioError("Radio NAK'd block at %04x" % addr)
470
    _cmd, _addr, _length = struct.unpack(">cHb", header)
471
    if _addr != addr or _length != _length:
472
        LOG.debug( "Expected/Received:")
473
        LOG.debug(" Length: %02x/%02x" % (length, _length))
474
        LOG.debug( " Addr: %04x/%04x" % (addr, _addr))
475
        raise errors.RadioError("Radio send unexpected block")
476
    cs = _checksum(result[1:-2])
477
    if cs != ord(result[-2]):
478
        LOG.debug( "Calculated: %02x" % cs)
479
        LOG.debug( "Actual:     %02x" % ord(result[-2]))
480
        raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
481
    return data
482

  
483

  
484
def _finish(radio):
485
    endframe = "\x45\x4E\x44"
486
    _echo_write(radio, endframe)
487
    result = radio.pipe.read(1)
488
    if result != "\x06":
489
        LOG.error( "Got:\n%s" % util.hexprint(result))
490
        raise errors.RadioError("Radio did not finish cleanly")
491

  
492
def do_download(radio):
493

  
494
    _ident(radio)
495

  
496
    _memobj = None
497
    data = ""
498

  
499
    for start,end in radio._ranges: 
500
        for addr in range(start,end,0x10):
501
            block = _send(radio,'R',addr,0x10) 
502
            data += block
503
            status = chirp_common.Status()
504
            status.cur = len(data)
505
            status.max = end
506
            status.msg = "Downloading from radio"
507
            radio.status_fn(status)
508

  
509
    _finish(radio)
510

  
511
    return memmap.MemoryMap(data)
512

  
513
def do_upload(radio):
514

  
515
    _ident(radio)
516

  
517
    for start,end in radio._ranges:
518
        for addr in range(start,end,0x10):
519
            if addr < 0x0100:
520
                continue
521
            block = radio._mmap[addr:addr+0x10]
522
            _send(radio,'W',addr,len(block),block)
523
            status = chirp_common.Status()
524
            status.cur = addr
525
            status.max = end
526
            status.msg = "Uploading to Radio"
527
            radio.status_fn(status)
528

  
529
    _finish(radio)
530
            
531

  
532

  
533
@directory.register
534
class Th9000Radio(chirp_common.CloneModeRadio,
535
                  chirp_common.ExperimentalRadio):
536
    """TYT TH-9000"""
537
    VENDOR = "TYT"    
538
    MODEL = "TH9000" 
539
    BAUD_RATE = 9600 
540
    _file_ident = "TH9000 XXX"
541
    valid_freq = [(220000000, 260000000)]
542
    
543

  
544
    _memsize = MMAPSIZE
545
    _ranges = [(0x0000,0x4000)]
546

  
547
    @classmethod
548
    def get_prompts(cls):
549
        rp = chirp_common.RadioPrompts()
550
        rp.experimental = ("The TYT TH-9000 driver is an alpha version."
551
                           "Use only for testing and development"
552
                           "Proceed with Caution and backup your data"
553
                           "as you may lose it using this driver!")
554
        return rp
555

  
556
    def get_features(self):
557
        rf = chirp_common.RadioFeatures()
558
        rf.has_settings = True
559
        rf.has_bank = False
560
        rf.has_cross = True
561
        rf.has_tuning_step = False
562
        rf.has_rx_dtcs = True
563
        rf.valid_skips = ["","S"]
564
        rf.memory_bounds = (0, 199) 
565
        rf.valid_name_length = 7
566
        rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC + "-"
567
        rf.valid_modes = MODES
568
        rf.valid_tmodes = chirp_common.TONE_MODES
569
        rf.valid_cross_modes = CROSS_MODES
570
        rf.valid_power_levels = POWER_LEVELS
571
        rf.valid_dtcs_codes = chirp_common.ALL_DTCS_CODES
572
        rf.valid_bands = self.valid_freq
573
        return rf
574

  
575
    # Do a download of the radio from the serial port
576
    def sync_in(self):
577
        self._mmap = do_download(self)
578
        self.process_mmap()
579

  
580
    # Do an upload of the radio to the serial port
581
    def sync_out(self):
582
        do_upload(self)
583

  
584
    def process_mmap(self):
585
        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
586

  
587

  
588
    # Return a raw representation of the memory object, which 
589
    # is very helpful for development
590
    def get_raw_memory(self, number):
591
        return repr(self._memobj.memory[number])
592

  
593

  
594
    # not working
595
    def _get_dcs_index(self, _mem,which):
596
        base = getattr(_mem, '%scode' % which)
597
        extra = getattr(_mem, '%sdcsextra' % which)
598
        return (int(extra) << 8) | int(base)
599

  
600
    def _set_dcs_index(self, _mem, which, index):
601
        base = getattr(_mem, '%scode' % which)
602
        extra = getattr(_mem, '%sdcsextra' % which)
603
        base.set_value(index & 0xFF)
604
        extra.set_value(index >> 8)
605

  
606

  
607
    # Extract a high-level memory object from the low-level memory map
608
    # This is called to populate a memory in the UI
609
    def get_memory(self, number):
610
        # Get a low-level memory object mapped to the image
611
        _mem = self._memobj.memory[number]
612

  
613
        # get flag info
614
        cbyte = number / 8 ;
615
        cbit =  7 - (number % 8) ;
616
        setflag = self._memobj.csetflag[cbyte].c[cbit]; 
617
        skipflag = self._memobj.cskipflag[cbyte].c[cbit]; 
618

  
619
        mem = chirp_common.Memory()
620

  
621
        mem.number = number  # Set the memory number
622

  
623
        if setflag == 1:
624
            mem.empty = True
625
            return mem
626

  
627
        mem.freq = int(_mem.freq) * 100    
628
        mem.offset = int(_mem.offset) * 100
629
        mem.name = str(_mem.name).rstrip() # Set the alpha tag
630
        mem.duplex = DUPLEXES[_mem.duplex]
631
        mem.mode = MODES[_mem.channel_width]
632
        mem.power = POWER_LEVELS[_mem.power]
633

  
634
        rxtone = txtone = None
635

  
636

  
637
        rxmode = TMODES[_mem.rxtmode]
638
        txmode = TMODES[_mem.txtmode]
639

  
640

  
641
        rxpol = txpol =  ""
642

  
643
        # doesn't work
644
        if rxmode == "Tone":
645
            rxpol = ""
646
            rxtone = TONES[_mem.rxtone]
647
        elif rxmode == "DTCS":
648
            rxpol = "N"
649
            rxtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'rx')]
650

  
651
        if txmode == "Tone":
652
            txpol = ""
653
            txtone = TONES[_mem.txtone]
654
        elif txmode == "DTCS":
655
            txpol = "N"
656
            txtone = chirp_common.ALL_DTCS_CODES[self._get_dcs_index(_mem,'tx')]
657

  
658

  
659
        chirp_common.split_tone_decode(mem,
660
                                       (txmode, txtone, txpol),
661
                                       (rxmode, rxtone, rxpol))
662

  
663
        mem.skip = "S" if skipflag == 1 else ""
664

  
665

  
666
        # We'll consider any blank (i.e. 0MHz frequency) to be empty
667
        if mem.freq == 0:
668
            mem.empty = True
669

  
670
        return mem
671

  
672
    # Store details about a high-level memory to the memory map
673
    # This is called when a user edits a memory in the UI
674
    def set_memory(self, mem):
675
        # Get a low-level memory object mapped to the image
676

  
677
        _mem = self._memobj.memory[mem.number]
678

  
679
        cbyte = mem.number / 8 
680
        cbit =  7 - (mem.number % 8) 
681

  
682
        if mem.empty:
683
            self._memobj.csetflag[cbyte].c[cbit] = 1
684
            self._memobj.cskipflag[cbyte].c[cbit] = 1
685
            return
686

  
687
        self._memobj.csetflag[cbyte].c[cbit] =  0 
688
        self._memobj.cskipflag[cbyte].c[cbit]  =  1 if (mem.skip == "S") else 0
689

  
690
        _mem.set_raw("\x00" * 32)
691

  
692
        _mem.freq = mem.freq / 100         # Convert to low-level frequency
693
        _mem.offset = mem.offset / 100         # Convert to low-level frequency
694

  
695
        _mem.name = mem.name.ljust(7)[:7]  # Store the alpha tag
696
        _mem.duplex = DUPLEXES.index(mem.duplex)
697

  
698

  
699
        try:
700
            _mem.channel_width = MODES.index(mem.mode)
701
        except ValueError:
702
            _mem.channel_width = 0
703

  
704
        ((txmode, txtone, txpol),
705
         (rxmode, rxtone, rxpol)) = chirp_common.split_tone_encode(mem)
706

  
707
        _mem.txtmode = TMODES.index(txmode)
708
        _mem.rxtmode = TMODES.index(rxmode)
709

  
710
        if txmode == "Tone":
711
            _mem.txtone = TONES.index(txtone)
712
        elif txmode == "DTCS":
713
            self._set_dcs_index(_mem,'tx',chirp_common.ALL_DTCS_CODES.index(txtone))
714

  
715
        if rxmode == "Tone":
716
            _mem.rxtone = TONES.index(rxtone)
717
        elif rxmode == "DTCS":
718
            self._set_dcs_index(_mem, 'rx', chirp_common.ALL_DTCS_CODES.index(rxtone))
719

  
720
        #_mem.txinv = txpol == "N"
721
        #_mem.rxinv = rxpol == "N"
722

  
723
       
724
        if mem.power:
725
            _mem.power = POWER_LEVELS.index(mem.power)
726
        else:
727
            _mem.power = 0
728

  
729
    def _get_settings(self):
730
        _settings = self._memobj.settings
731
        _freqrange = self._memobj.freqrange
732
        _slabel = self._memobj.slabel
733

  
734
        basic = RadioSettingGroup("basic","Global Settings")
735
        freqrange = RadioSettingGroup("freqrange","Frequency Ranges")
736
        top = RadioSettingGroup("top","All Settings",basic,freqrange)
737
        settings = RadioSettings(top)
738

  
739
        def _filter(name):
740
            filtered = ""
741
            for char in str(name):
742
                if char in chirp_common.CHARSET_ASCII:
743
                    filtered += char
744
                else:
745
                    filtered += ""
746
            return filtered
747
                   
748
        val = RadioSettingValueString(0,7,_filter(_slabel.startname))
749
        rs = RadioSetting("startname","Startup Label",val)
750
        basic.append(rs)
751

  
752
        rs = RadioSetting("bg_color","LCD Color",
753
                           RadioSettingValueList(BGCOLOR_LIST, BGCOLOR_LIST[_settings.bg_color]))
754
        basic.append(rs)
755

  
756
        rs = RadioSetting("bg_brightness","LCD Brightness",
757
                           RadioSettingValueList(BGBRIGHT_LIST, BGBRIGHT_LIST[_settings.bg_brightness]))
758
        basic.append(rs)
759

  
760
        rs = RadioSetting("squelch","Squelch Level",
761
                           RadioSettingValueList(SQUELCH_LIST, SQUELCH_LIST[_settings.squelch]))
762
        basic.append(rs)
763

  
764
        rs = RadioSetting("timeout_timer","Timeout Timer (TOT)",
765
                           RadioSettingValueList(TIMEOUT_LIST, TIMEOUT_LIST[_settings.timeout_timer]))
766
        basic.append(rs)
767

  
768
        rs = RadioSetting("auto_power_off","Auto Power Off (APO)",
769
                           RadioSettingValueList(APO_LIST, APO_LIST[_settings.auto_power_off]))
770
        basic.append(rs)
771

  
772
        rs = RadioSetting("voice_prompt","Beep Prompt",
773
                           RadioSettingValueList(BEEP_LIST, BEEP_LIST[_settings.voice_prompt]))
774
        basic.append(rs)
775

  
776
        rs = RadioSetting("tbst_freq","Tone Burst Frequency",
777
                           RadioSettingValueList(TBSTFREQ_LIST, TBSTFREQ_LIST[_settings.tbst_freq]))
778
        basic.append(rs)
779

  
780
        rs = RadioSetting("choose_tx_power","Max Level of TX Power",
781
                           RadioSettingValueList(TXPWR_LIST, TXPWR_LIST[_settings.choose_tx_power]))
782
        basic.append(rs)
783

  
784
        (flow,fhigh)  = self.valid_freq[0]
785
        flow  /= 1000
786
        fhigh /= 1000
787
        fmidrange = (fhigh- flow)/2000
788

  
789
        rs = RadioSetting("txrangelow","TX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
790
            flow + fmidrange,
791
            int(_freqrange.txrangelow)/10))
792
        freqrange.append(rs)
793

  
794
        rs = RadioSetting("txrangehi","TX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
795
            fhigh,
796
            int(_freqrange.txrangehi)/10))
797
        freqrange.append(rs)
798

  
799
        rs = RadioSetting("rxrangelow","RX Freq, Lower Limit (khz)", RadioSettingValueInteger(flow,
800
            flow+fmidrange,
801
            int(_freqrange.rxrangelow)/10))
802
        freqrange.append(rs)
803

  
804
        rs = RadioSetting("rxrangehi","RX Freq, Upper Limit (khz)", RadioSettingValueInteger(fhigh-fmidrange,
805
            fhigh,
806
            int(_freqrange.rxrangehi)/10))
807
        freqrange.append(rs)
808

  
809
        return settings
810

  
811
    def get_settings(self):
812
        try:
813
            return self._get_settings()
814
        except:
815
            import traceback
816
            LOG.error( "failed to parse settings")
817
            traceback.print_exc()
818
            return None
819

  
820
    def set_settings(self,settings):
821
        _settings = self._memobj.settings
822
        for element in settings:
823
            if not isinstance(element,RadioSetting):
824
                self.set_settings(element)
825
                continue
826
            else:
827
                try:
828
                    name = element.get_name()
829

  
830
                    if  name in ["txrangelow","txrangehi","rxrangelow","rxrangehi"]:
831
                        LOG.debug( "setting %s = %s" % (name,int(element.value)*10))
832
                        setattr(self._memobj.freqrange,name,int(element.value)*10)
833
                        continue
834

  
835
                    if name in ["startname"]:
836
                        LOG.debug( "setting %s = %s" % (name, element.value))
837
                        setattr(self._memobj.slabel,name,element.value)
838
                        continue
839

  
840
                    obj = _settings
841
                    setting = element.get_name()
842

  
843
                    if element.has_apply_callback():
844
                        LOG.debug( "using apply callback")
845
                        element.run_apply_callback()
846
                    else:
847
                        LOG.debug( "Setting %s = %s" % (setting, element.value))
848
                        setattr(obj, setting, element.value)
849
                except Exception, e:
850
                    LOG.debug( element.get_name())
851
                    raise
852

  
853
    @classmethod
854
    def match_model(cls, filedata, filename):
855
        return cls._file_ident in filedata[16:27]
856

  
857
@directory.register
858
class Th9000220Radio(Th9000Radio):
859
    """TYT TH-9000 220"""
860
    VENDOR = "TYT"    
861
    MODEL = "TH9000_220" 
862
    BAUD_RATE = 9600 
863
    _file_ident = "TH9000 220"
864
    valid_freq = [(220000000, 260000000)]
865

  
866
@directory.register
867
class Th9000144Radio(Th9000220Radio):
868
    """TYT TH-9000 144"""
869
    VENDOR = "TYT"    
870
    MODEL = "TH9000_144" 
871
    BAUD_RATE = 9600 
872
    _file_ident = "TH9000 144"
873
    valid_freq = [(138000000, 152000000)]
874

  
875
@directory.register
876
class Th9000440Radio(Th9000220Radio):
877
    """TYT TH-9000 440"""
878
    VENDOR = "TYT"    
879
    MODEL = "TH9000_440" 
880
    BAUD_RATE = 9600 
881
    _file_ident = "TH9000 440"
882
    valid_freq = [(400000000, 450000000)]
(8-8/14)