Project

General

Profile

New Model #1035 » tytpatch9000-001.patch

David Fannin, 09/28/2013 11:23 PM

View differences:

/dev/null Thu Jan 01 00:00:00 1970 +0000 → chirp/th9000.py Sat Sep 28 23:18:07 2013 -0700
1

  
2

  
3

  
4

  
5
# Copyright 2013 Dan Smith <dsmith@danplanet.com>
6
#
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19

  
20
import os
21
import struct
22
import time
23

  
24
from chirp import bitwise
25
from chirp import chirp_common
26
from chirp import directory
27
from chirp import errors
28
from chirp import memmap
29
from chirp import util
30

  
31
"""
32
 Description: Chirp Data Structure of the Memory Map for TYT TH-9000/VHF 2Meter 144-148Mhz Radio
33
 Version: 0.6
34
 Date: 9/28/2013
35
 Status: Imcomplete
36
    - This is the initial, incomplete version of the channel data structure. 
37
    - The memory map looks a lot like the Anytone 5888 model, however, these does appear to be significant differences.
38

  
39

  
40

  
41
Overall Memory Map:
42

  
43
    Memory Map (Range 0x0100-3FF0, step 0x10):
44

  
45
        Field                   Start  End  Size   
46
                                (hex)  (hex) (hex)  
47
        
48
        1 Channel Set Flag        0100  011F   20 
49
        2 Channel Skip Flag       0120  013F   20 
50
        3 Blank/Unknown           0140  01EF   B0 
51
        4 Unknown                 01F0  01FF   10
52
        5 TX/RX Range             0200  020F   10    
53
        6 Bootup Passwd           0210  021F   10 
54
        7 Options, Radio          0220  023F   20
55
        8 Unknown                 0240  019F   
56
        9 Channel Bank            2000  38FF 1900  
57
             Channel 000          2000  201F   20  
58
             Channel 001          2020  202F   20  
59
             ... 
60
             Channel 199          38E0  38FF   20 
61
        10 Blank/Unknown          3900  3FFF  6FF  14592  16383    1792   
62
            Total Map Size           16128 (2^8 = 16384)
63

  
64

  
65
   Protocol for Upload Sequence, Only Writes shown:
66

  
67
   1) Start 
68
      "Program"                   50 52 4F 47 52 41 4D
69
      0x02                        02
70
      "R\0x01\0x40\x10"           52 01 40 10  (?means "Read" address 1040, 16 bytes, but doesn't match write data field) 
71

  
72
   2) Write Records, Loop from address 0100 to 3900, step 0x10
73
     "W"<address><length><data><checksum>"\0x06"   57 .. .. 10 ..(*16)  .. 06  (write record of 16 bytes)  
74
       header[4 bytes]:
75
         command[1] = Write ("W")
76
         address[2] = 0100 to 3900
77
         length[1] = 0x10 (16 bytes length)
78
       data[16 bytes]:
79
         data[16] = 16 byte payload
80
       end[2 bytes]:
81
         checksum[1] = 1 byte,  uses digit check checksum ( sum of header and data bytes, modulo 256)
82
         end of record[1] = 0x06 (ascii ACK char)
83

  
84
   3) END
85
     "END"   45 4E 44
86

  
87
     
88

  
89
"""
90

  
91

  
92
"""
93
  TH9000/VHF  memory map 
94
  section: 1 and 2:  Channel Set/Skip Flags
95

  
96
    Channel Set (starts 0x100) : Channel Set  bit is value 0 if a memory location in the channel bank is active.
97
    Channel Skip (starts 0x120): Channel Skip bit is value 0 if a memory location in the channel bank is active.
98

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

  
102
    to index a channel:
103
        cbyte = channel / 8 ;
104
        cbit  = channel % 8 ;
105
        setflag  = csetflag[cbyte].c[cbit] ;
106
        skipflag = cskipflag[cbyte].c[cbit] ;
107

  
108
    channel range is 0-199, range is 32 bytes (last 7 unknown)
109
"""
110

  
111
mem_format = """
112
#seekto 0x0100;
113
struct {
114
   bit c[8];
115
} csetflag[24];
116

  
117
struct {
118
   u8 unknown0100[7] ;
119
}
120

  
121
#seekto 0x0120
122
struct {
123
   bit c[8];
124
} cskipflag[24] ;
125

  
126
struct {
127
   u8 unknown0120[7] ;
128
}
129
"""
130

  
131
"""
132
  TH9000/VHF  memory map 
133
  section: 5  TX/RX Range
134
     used to set the TX/RX range of the radio (e.g.  144-148Mhz for 2 meter)
135
     possible to set range to 136-172Mhz for tx/rx 
136

  
137
"""
138
mem_format = """
139
#seekto 0x0200;
140
struct {
141
    bbcd txrangelow[4];
142
    bbcd txrangehi[4];
143
    bbcd rxrangelow[4];
144
    bbcd rxrangehi[4];
145
}
146
"""
147

  
148
"""
149
  TH9000/VHF  memory map 
150
  section: 6  bootup_passwd
151
     used to set bootup passwd (see boot_passwd checkbox option)
152

  
153
  options - bootup password
154

  
155
  bytes:bit   type                 description
156
  ---------------------------------------------------------------------------
157
  6         u8 bootup_passwd[6]     bootup passwd, 6 chars, numberic chars 30-39 , see boot_passwd checkbox to set
158
  10        u8 unknown;  
159

  
160
"""
161

  
162
mem_format = mem_format + """
163
#seekto 0x0210
164
struct {
165
   u8 bootup_passwd[6];
166
   u8 unknown2010[10];
167
};
168
"""
169

  
170
"""
171
  TH9000/VHF  memory map 
172
  section: 7  Radio Options  
173
        used to set a number of radio options 
174

  
175
  bytes:bit   type                 description
176
  ---------------------------------------------------------------------------
177
  1         u8 display_mode     display mode, range 0-2, 0=freq,1=channel,2=name (selecting name affects vfo_mr)
178
  1         u8 vfo_mr;          vfo_mr , 0=vfo, mr=1 
179
  1         u8 unknown;  
180
  1         u8 squelch;         squelch level, range 0-19, hex for menu
181
  1         u8 unknown[2]; 
182
  1         u8 channel_lock;    if display_mode[channel] selected, then lock=1,no lock =0
183
  1         u8 unknown; 
184
  1         u8 bg_brightness ;  background brightness, range 0-21, hex, menu index 
185
  1         u8 unknown;     
186
  1         u8 bg_color ;       bg color, menu index,  blue 0 , orange 1, purple 2
187
  1         u8 tbst_freq ;      tbst freq , menu 0 = 1750Hz, 1=2100 , 2=1000 , 3=1450hz 
188
  1         u8 timeout_timer;   timeout timer, hex, value = minutes, 0= no timeout
189
  1         u8 unknown; 
190
  1         u8 auto_power_off;   auto power off, range 0-3, off,30min, 1hr, 2hr, hex menu index
191
  1         u8 voice_prompt;     voice prompt, value 0,1 , Beep ON = 1, Beep Off = 2
192
"""
193
mem_format = mem_format + """
194
#seekto 0x0220
195
struct {
196
   u8 display_mode; 
197
   u8 vfo_mr; 
198
   u8 unknown0220A; 
199
   u8 squelch; 
200
   u8 unknown0220B[2]; 
201
   u8 channel_lock; 
202
   u8 unknown0220C; 
203
   u8 bg_brightness; 
204
   u8 unknown0220D; 
205
   u8 bg_color;
206
   u8 tbst_freq;
207
   u8 timeout_timer;
208
   u8 unknown0220E;
209
   u8 auto_power_off;
210
   u8 voice_prompt; 
211
};
212
"""
213

  
214
"""
215
  description of function setup options, starting at 0x0230
216

  
217
  bytes:bit   type                 description
218
  ---------------------------------------------------------------------------
219
  1         u8  // 0
220
   :4       unknown:6
221
   :1       elim_sql_tail:1   eliminate squelsh tail when no ctcss checkbox (1=checked)
222
   :1       sql_key_function  "squelch off" 1 , "squelch momentary off" 0 , menu index
223
  2         u8 unknown[2] /1-2  
224
  1         u8 // 3
225
   :4       unknown:4
226
   :1       inhibit_init_ops:1 //bit 5
227
   :1       unknownD:1
228
   :1       inhibit_setup_bg_chk:1 //bit 7
229
   :1       unknown:1
230
  1         u8 tail_elim_type    menu , (off=0,120=1,180=2),  // 4
231
  1         u8 choose_tx_power    menu , (60w=0,25w=1) // 5
232
  2         u8 unknown[2]; // 6-7 
233
  1         u8 bootup_passwd_flag  checkbox 1=on, 0=off // 8
234
  7         u8 unknown[7]; // 9-F 
235
  
236
"""
237

  
238
mem_format = mem_format + """
239
#seekto 0x0230
240
struct {
241
   u8 unknown0230A:6,
242
      elim_sql_tail:1,
243
      sql_key_function:1;
244
   u8 unknown0230B[2];
245
   u8 unknown0230C:4, 
246
      inhibit_init_ops:1,
247
      unknown0230D:1,
248
      inhibit_setup_bg_chk,
249
      unknown0230E:1;
250
   u8 tail_elim_type;
251
   u8 choose_tx_power;
252
   u8 unknown0230F[2];
253
   u8 bootup_passwd_flag;
254
   u8 unknown0230G[7];
255
};
256
"""
257

  
258
"""
259
  TH9000/VHF  memory map 
260
  section: 9  Channel Bank
261
         description of channel bank (200 channels , range 0-199)
262
         Each 32 Byte (0x20 hex)  record:
263
  bytes:bit   type                 description
264
  ---------------------------------------------------------------------------
265
  4         bbcd freq[4]        receive frequency in packed binary coded decimal  
266
  4         bbcd offset[4]      transmit offset in packed binary coded decimal (note: plus/minus direction set by 'duplex' field)
267
  1         u8
268
   :4       unknown:4
269
   :4       tune_step:4         tuning step, menu index value from 0-9
270
  1         u8
271
   :4       unknown:4          not yet decoded, used for DCS coding?
272
   :2       channel_width:2     channel spacing, menu index value from 0-3
273
   :1       reverse:1           reverse flag, 0=off, 1=on (reverses tx and rx freqs)
274
   :1       txoff:1             transmitt off flag, 0=transmit , 1=do not transmit 
275
  1         u8
276
   :1       talkaround:1        talkaround flag, 0=off, 1=on (bypasses repeater) 
277
   :1       compander:1         compander flag, 0=off, 1=on (turns on/off voice compander option)  
278
   :2       unknown:2          
279
   :2       power:2             tx power setting, value range 0-2, 0=hi,1=med,2=lo 
280
   :2       duplex:2            duplex settings, 0=simplex,1= minus(-) offset, 2= plus (+) offset (see offset field) 
281
  1         u8 
282
   :4       unknown:4
283
   :2       rxtmode:2           rx tone mode, value range 0-2, 0=none, 1=CTCSS, 2=DCS  (ctcss tone in field rxtone)
284
   :2       txtmode:2           tx tone mode, value range 0-2, 0=none, 1=CTCSS, 3=DCS  (ctcss tone in field txtone)
285
  1         u8 
286
   :2       unknown:2
287
   :6       txtone:6            tx ctcss tone, menu index
288
  1         u8 
289
   :2       unknown:2 
290
   :6       rxtone:6            rx ctcss tone, menu index
291
  1         u8 txcode           ?, not used for ctcss
292
  1         u8 rxcode           ?, not used for ctcss
293
  3         u8 unknown[3]
294
  7         char name[7]        7 byte char string for channel name
295
  1         u8 
296
   :6       unknown:6,
297
   :2       busychannellockout:2 busy channel lockout option , 0=off, 1=repeater, 2=busy  (lock out tx if channel busy)
298
  4         u8 unknownI[4];
299
  1         u8 
300
   :7       unknown:7 
301
   :1       scrambler:1         scrambler flag, 0=off, 1=on (turns on tyt scrambler option)
302
"""
303

  
304
mem_format = mem_format + """
305
#seekto 0x2000;
306
struct memory {
307
  bbcd freq[4];
308
  bbcd offset[4];
309
  u8 unknown2000A:4,
310
     tune_step:4;
311
  u8 unknown2000B:4,
312
     channel_width:2,
313
     reverse:1,
314
     txoff:1;
315
  u8 talkaround:1,
316
     compander:1,
317
     unknown2000C:2,
318
     power:2,
319
     duplex:2;
320
  u8 unknown2000D:4,
321
     rxtmode:2,
322
     txtmode:2;
323
  u8 unknown2000E:2,
324
     txtone:6;
325
  u8 unknown2000F:2,
326
     rxtone:6;
327
  u8 txcode;
328
  u8 rxcode;
329
  u8 unknown2000G[3];
330
  char name[7];
331
  u8 unknown2000H:6,
332
     busychannellockout:2;
333
  u8 unknown2000I[4];
334
  u8 unknown2000J:7,
335
     scrambler:1; 
336
};
337
struct memory memory[200] ;
338
"""
339

  
340

  
341
def _debug(string):   
342
    if "CHIRP_DEBUG" in os.environ or True:
343
        print string
344

  
345
def _echo_write(radio, data):
346
    try:
347
        radio.pipe.write(data)
348
        radio.pipe.read(len(data))
349
    except Exception, e:
350
        print "Error writing to radio: %s" % e
351
        raise errors.RadioError("Unable to write to radio")
352

  
353
def _read(radio, length):
354
    try:
355
        data = radio.pipe.read(length)
356
    except Exception, e:
357
        print "Error reading from radio: %s" % e
358
        raise errors.RadioError("Unable to read from radio")
359

  
360
    if len(data) != length:
361
        print "Short read from radio (%i, expected %i)" % (len(data),
362
                                                           length)
363
        print util.hexprint(data)
364
        raise errors.RadioError("Short read from radio")
365
    return data
366

  
367
def _ident(radio):
368
    radio.pipe.setTimeout(1)
369
    _echo_write(radio, "PROGRAM")
370
    response = radio.pipe.read(3)
371
    if response != "QX\x06":
372
        print "Response was:\n%s" % util.hexprint(response)
373
        raise errors.RadioError("Unsupported model")
374
    _echo_write(radio, "\x02")
375
    response = radio.pipe.read(16)
376
    _debug(util.hexprint(response))
377
    if response[1:8] != "ITH-9000":
378
        print "Response was:\n%s" % util.hexprint(response)
379
        raise errors.RadioError("Unsupported model")
380

  
381
def _finish(radio):
382
    endframe = "\x45\x4E\x44"
383
    _echo_write(radio, endframe)
384
    result = radio.pipe.read(1)
385
    if result != "\x06":
386
        print "Got:\n%s" % util.hexprint(result)
387
        raise errors.RadioError("Radio did not finish cleanly")
388

  
389
def _checksum(data):
390
    cs = 0
391
    for byte in data:
392
        cs += ord(byte)
393
    return cs % 256
394

  
395
def _send(radio, cmd, addr, length, data=None):
396
    frame = struct.pack(">cHb", cmd, addr, length)
397
    if data:
398
        frame += data
399
        frame += chr(_checksum(frame[1:]))
400
        frame += "\x06"
401
    _echo_write(radio, frame)
402
    _debug("Sent:\n%s" % util.hexprint(frame))
403
    if data:
404
        result = radio.pipe.read(1)
405
        if result != "\x06":
406
            print "Ack was: %s" % repr(result)
407
            raise errors.RadioError("Radio did not accept block at %04x" % addr)
408
        return
409
    result = _read(radio, length + 6)
410
    _debug("Got:\n%s" % util.hexprint(result))
411
    header = result[0:4]
412
    data = result[4:-2]
413
    ack = result[-1]
414
    if ack != "\x06":
415
        print "Ack was: %s" % repr(ack)
416
        raise errors.RadioError("Radio NAK'd block at %04x" % addr)
417
    _cmd, _addr, _length = struct.unpack(">cHb", header)
418
    if _addr != addr or _length != _length:
419
        print "Expected/Received:"
420
        print " Length: %02x/%02x" % (length, _length)
421
        print " Addr: %04x/%04x" % (addr, _addr)
422
        raise errors.RadioError("Radio send unexpected block")
423
    cs = _checksum(result[1:-2])
424
    if cs != ord(result[-2]):
425
        print "Calculated: %02x" % cs
426
        print "Actual:     %02x" % ord(result[-2])
427
        raise errors.RadioError("Block at 0x%04x failed checksum" % addr)
428
    return data
429

  
430
def _download(radio):
431
    _ident(radio)
432

  
433
    memobj = None
434

  
435
    data = ""
436
    for start, end in radio._ranges:
437
        for addr in range(start, end, 0x10):
438
            block = _send(radio, 'R', addr, 0x10)
439
            data += block
440

  
441
            status = chirp_common.Status()
442
            status.cur = len(data)
443
            status.max = end
444
            status.msg = "Cloning from radio"
445
            radio.status_fn(status)
446

  
447
    memobj = bitwise.parse(_mem_format, data)
448
    _finish(radio)
449

  
450
    return memmap.MemoryMap(data)
451

  
452
def _upload(radio):
453
    _ident(radio)
454

  
455
    for start, end in radio._ranges:
456
        for addr in range(start, end, 0x10):
457
            block = radio._mmap[addr:addr + 0x10]
458
            _send(radio, 'W', addr, len(block), block)
459

  
460
            status = chirp_common.Status()
461
            status.cur = addr
462
            status.max = end
463
            status.msg = "Cloning to radio"
464
            radio.status_fn(status)
465

  
466
    _finish(radio)
467

  
468

  
469

  
470
@directory.register
471
class TytTh9000VHFRadio(chirp_common.CloneModeRadio,
472
                         chirp_common.ExperimentalRadio):
473
    """TYT TH-9000 VHF"""
474
    VENDOR = "TYT"
475
    MODEL = "TH9000"
476
    BAUD_RATE = 9600
477

  
478
    # May try to mirror the OEM behavior later
479
    _ranges = [
480
        (0x0100, 0x3FF0),
481
        ]
482

  
483
    @classmethod
484
    def get_experimental_warning(cls):
485
        return "FOO"
486

  
487
    def get_features(self):
488
        rf = chirp_common.RadioFeatures()
489
        rf.valid_bands = [(136000000)]
490
        rf.valid_characters = chirp_common.CHARSET_UPPER_NUMERIC
491
        rf.valid_name_length = 7
492
        rf.memory_bounds = (0, 199)
493
        return rf
494

  
495
    @classmethod
496
    def match_model(cls, filedata, filename):
497
        return filedata[0x21:0x28] == "ITH-9000"
(2-2/14)