~ubuntu-branches/ubuntu/trusty/chirp/trusty

« back to all changes in this revision

Viewing changes to chirp/tmv71_ll.py

  • Committer: Bazaar Package Importer
  • Author(s): Steve Conklin
  • Date: 2011-09-26 12:55:16 UTC
  • Revision ID: james.westby@ubuntu.com-20110926125516-4jjwh7yy9mgps8mf
Tags: upstream-0.1.12
ImportĀ upstreamĀ versionĀ 0.1.12

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
#
 
3
# Copyright 2010 Dan Smith <dsmith@danplanet.com>
 
4
#
 
5
# This program is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU General Public License as published by
 
7
# the Free Software Foundation, either version 3 of the License, or
 
8
# (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 
 
18
import struct, time
 
19
 
 
20
from chirp import memmap, chirp_common, errors
 
21
 
 
22
DEBUG = True
 
23
 
 
24
POS_MODE   = 5
 
25
POS_DUP    = 6
 
26
POS_TMODE  = 6
 
27
POS_RTONE  = 7
 
28
POS_CTONE  = 8
 
29
POS_DTCS   = 9
 
30
POS_OFFSET = 10
 
31
 
 
32
MEM_LOC_BASE = 0x1700
 
33
MEM_LOC_SIZE = 16
 
34
MEM_TAG_BASE = 0x5800
 
35
MEM_FLG_BASE = 0x0E00
 
36
 
 
37
V71_SPECIAL = {}
 
38
 
 
39
for i in range(0, 10):
 
40
    V71_SPECIAL["L%i" % i] = 1000 + (i * 2)
 
41
    V71_SPECIAL["U%i" % i] = 1000 + (i * 2) + 1
 
42
for i in range(0, 10):
 
43
    V71_SPECIAL["WX%i" % (i + 1)] = 1020 + i
 
44
V71_SPECIAL["C VHF"] = 1030
 
45
V71_SPECIAL["C UHF"] = 1031
 
46
 
 
47
V71_SPECIAL_REV = {}
 
48
for k,v in V71_SPECIAL.items():
 
49
    V71_SPECIAL_REV[v] = k
 
50
 
 
51
def command(s, cmd, timeout=0.5):
 
52
    start = time.time()
 
53
 
 
54
    data = ""
 
55
    if DEBUG:
 
56
        print "PC->V71: %s" % cmd
 
57
    s.write(cmd + "\r")
 
58
    while not data.endswith("\r") and (time.time() - start) < timeout:
 
59
        data += s.read(1)
 
60
    if DEBUG:
 
61
        print "V71->PC: %s" % data.strip()
 
62
    return data.strip()
 
63
 
 
64
def get_id(s):
 
65
    r = command(s, "ID")
 
66
    if r.startswith("ID "):
 
67
        return r.split(" ")[1]
 
68
    else:
 
69
        raise errors.RadioError("No response to ID command")
 
70
 
 
71
EXCH_R = "R\x00\x00\x00"
 
72
EXCH_W = "W\x00\x00\x00"
 
73
 
 
74
def read_block(s, block, count=256):
 
75
    s.write(struct.pack("<cHB", "R", block, 0))
 
76
    r = s.read(4)
 
77
    if len(r) != 4:
 
78
        raise Exception("Did not receive block response")
 
79
 
 
80
    cmd, _block, zero = struct.unpack("<cHB", r)
 
81
    if cmd != "W" or _block != block:
 
82
        raise Exception("Invalid response: %s %i" % (cmd, _block))
 
83
 
 
84
    data = ""
 
85
    while len(data) < count:
 
86
        data += s.read(count - len(data))
 
87
 
 
88
    s.write(chr(0x06))
 
89
    if s.read(1) != chr(0x06):
 
90
        raise Exception("Did not receive post-block ACK!")
 
91
 
 
92
    return data
 
93
 
 
94
def write_block(s, block, map):
 
95
    s.write(struct.pack("<cHB", "W", block, 0))
 
96
    base = block * 256
 
97
    s.write(map[base:base+256])
 
98
 
 
99
    ack = s.read(1)
 
100
 
 
101
    return ack == chr(0x06)
 
102
 
 
103
def download(radio):
 
104
    if command(radio.pipe, "0M PROGRAM") != "0M":
 
105
        raise errors.RadioError("No response from radio")
 
106
 
 
107
    data = ""
 
108
    for i in range(0, 0x7F):
 
109
        data += read_block(radio.pipe, i)
 
110
        if radio.status_fn:
 
111
            s = chirp_common.Status()
 
112
            s.msg = "Cloning from radio"
 
113
            s.max = 256 * 0x7E
 
114
            s.cur = len(data)
 
115
            radio.status_fn(s)
 
116
 
 
117
    radio.pipe.write("E")
 
118
 
 
119
    return memmap.MemoryMap(data)
 
120
 
 
121
def upload(radio):
 
122
    if command(radio.pipe, "0M PROGRAM") != "0M":
 
123
        raise errors.RadioError("No response from radio")
 
124
 
 
125
    for i in range(0, 0x7F):
 
126
        r = write_block(radio.pipe, i, radio._mmap)
 
127
        if not r:
 
128
            raise errors.RadioError("Radio NAK'd block %i" % i)
 
129
        if radio.status_fn:
 
130
            s = chirp_common.Status()
 
131
            s.msg = "Cloning to radio"
 
132
            s.max = 256 * 0x7E
 
133
            s.cur = 256 * i
 
134
            radio.status_fn(s)
 
135
 
 
136
    radio.pipe.write("E")
 
137
 
 
138
def get_mem_offset(number):
 
139
    return MEM_LOC_BASE + (MEM_LOC_SIZE * number)
 
140
 
 
141
def get_raw_mem(map, number):
 
142
    base = get_mem_offset(number)
 
143
    #print "Offset for %i is %04x" % (number, base)
 
144
    return map[base:base+MEM_LOC_SIZE]
 
145
 
 
146
def get_used(map, number):
 
147
    pos = MEM_FLG_BASE + (number * 2)
 
148
    flag = ord(map[pos])
 
149
    print "Flag byte is %02x" % flag
 
150
    return not (flag & 0x80)
 
151
 
 
152
def set_used(map, number, freq):
 
153
    pos = MEM_FLG_BASE + (number * 2)
 
154
    if freq == 0:
 
155
        # Erase
 
156
        map[pos] = "\xff\xff"
 
157
    elif int(freq / 100) == 1:
 
158
        map[pos] = "\x05\x00"
 
159
    elif int(freq / 100) == 4:
 
160
        map[pos] = "\x08\x00"
 
161
 
 
162
def get_skip(map, number):
 
163
    pos = MEM_FLG_BASE + (number * 2)
 
164
    flag = ord(map[pos+1])
 
165
    if flag & 0x01:
 
166
        return "S"
 
167
    else:
 
168
        return ""
 
169
 
 
170
def set_skip(map, number, skip):
 
171
    pos = MEM_FLG_BASE + (number * 2)
 
172
    flag = ord(map[pos+1])
 
173
    if skip:
 
174
        flag |= 0x01
 
175
    else:
 
176
        flag &= ~0x01
 
177
    map[pos+1] = flag
 
178
 
 
179
def get_freq(mmap):
 
180
    freq, = struct.unpack("<I", mmap[0:4])
 
181
    return freq / 1000000.0
 
182
 
 
183
def set_freq(mmap, freq):
 
184
    mmap[0] = struct.pack("<I", int(freq * 1000000))
 
185
 
 
186
def get_name(map, number):
 
187
    base = MEM_TAG_BASE + (8 * number)
 
188
    return map[base:base+6].replace("\xff", "")
 
189
 
 
190
def set_name(mmap, number, name):
 
191
    base = MEM_TAG_BASE + (8 * number)
 
192
    mmap[base] = name.ljust(6)[:6].upper()
 
193
 
 
194
def get_tmode(mmap):
 
195
    val = ord(mmap[POS_TMODE]) & 0x70
 
196
 
 
197
    tmodemap = {
 
198
        0x00 : "",
 
199
        0x40 : "Tone",
 
200
        0x20 : "TSQL",
 
201
        0x10 : "DTCS",
 
202
        }
 
203
 
 
204
    return tmodemap[val]
 
205
 
 
206
def set_tmode(mmap, tmode):
 
207
    val = ord(mmap[POS_TMODE]) & 0x8F
 
208
 
 
209
    tmodemap = {
 
210
        ""     : 0x00,
 
211
        "Tone" : 0x40,
 
212
        "TSQL" : 0x20,
 
213
        "DTCS" : 0x10,
 
214
        }
 
215
 
 
216
    mmap[POS_TMODE] = val | tmodemap[tmode]
 
217
 
 
218
def get_tone(mmap, offset):
 
219
    val = ord(mmap[offset])
 
220
 
 
221
    return chirp_common.TONES[val]
 
222
 
 
223
def set_tone(mmap, tone, offset):
 
224
    print tone
 
225
    mmap[offset] = chirp_common.TONES.index(tone)
 
226
 
 
227
def get_dtcs(mmap):
 
228
    val = ord(mmap[POS_DTCS])
 
229
 
 
230
    return chirp_common.DTCS_CODES[val]
 
231
 
 
232
def set_dtcs(mmap, dtcs):
 
233
    mmap[POS_DTCS] = chirp_common.DTCS_CODES.index(dtcs)
 
234
 
 
235
def get_duplex(mmap):
 
236
    val = ord(mmap[POS_DUP]) & 0x03
 
237
 
 
238
    dupmap = {
 
239
        0x00 : "",
 
240
        0x01 : "+",
 
241
        0x02 : "-",
 
242
        }
 
243
 
 
244
    return dupmap[val]
 
245
 
 
246
def set_duplex(mmap, duplex):
 
247
    val = ord(mmap[POS_DUP]) & 0xFC
 
248
 
 
249
    dupmap = {
 
250
        ""  : 0x00,
 
251
        "+" : 0x01,
 
252
        "-" : 0x02,
 
253
        }
 
254
 
 
255
    mmap[POS_DUP] = val | dupmap[duplex]
 
256
 
 
257
def get_offset(mmap):
 
258
    val, = struct.unpack("<I", mmap[POS_OFFSET:POS_OFFSET+4])
 
259
    return val / 1000000.0
 
260
 
 
261
def set_offset(mmap, offset):
 
262
    mmap[POS_OFFSET] = struct.pack("<I", int(offset * 1000000))
 
263
 
 
264
def get_mode(mmap):
 
265
    val = ord(mmap[POS_MODE]) & 0x03
 
266
    modemap = {
 
267
        0x00 : "FM",
 
268
        0x01 : "NFM",
 
269
        0x02 : "AM",
 
270
        }
 
271
 
 
272
    return modemap[val]
 
273
 
 
274
def set_mode(mmap, mode):
 
275
    val = ord(mmap[POS_MODE]) & 0xFC
 
276
    modemap = {
 
277
        "FM" : 0x00,
 
278
        "NFM": 0x01,
 
279
        "AM" : 0x02,
 
280
        }
 
281
 
 
282
    mmap[POS_MODE] = val | modemap[mode]
 
283
 
 
284
def get_memory(map, number):
 
285
    if number < 0 or number > (max(V71_SPECIAL.values()) + 1):
 
286
        raise errors.InvalidMemoryLocation("Number must be between 0 and 999")
 
287
 
 
288
    mem = chirp_common.Memory()
 
289
    mem.number = number
 
290
 
 
291
    if number > 999:
 
292
        mem.extd_number = V71_SPECIAL_REV[number]
 
293
    if not get_used(map, number):
 
294
        mem.empty = True
 
295
        return mem
 
296
 
 
297
    mmap = get_raw_mem(map, number)
 
298
 
 
299
    mem.freq = get_freq(mmap)
 
300
    mem.name = get_name(map, number)
 
301
    mem.tmode = get_tmode(mmap)
 
302
    mem.rtone = get_tone(mmap, POS_RTONE)
 
303
    mem.ctone = get_tone(mmap, POS_CTONE)
 
304
    mem.dtcs = get_dtcs(mmap)
 
305
    mem.duplex = get_duplex(mmap)
 
306
    mem.offset = get_offset(mmap)
 
307
    mem.mode = get_mode(mmap)
 
308
 
 
309
    if number < 999:
 
310
        mem.skip = get_skip(map, number)
 
311
 
 
312
    if number > 999:
 
313
        mem.immutable = ["number", "bank", "extd_number", "name"]
 
314
    if number > 1020 and number < 1030:
 
315
        mem.immutable += ["freq"] # FIXME: ALL
 
316
 
 
317
    return mem
 
318
 
 
319
def initialize(mmap):
 
320
    mmap[0] = \
 
321
        "\x80\xc8\xb3\x08\x00\x01\x00\x08" + \
 
322
        "\x08\x00\xc0\x27\x09\x00\x00\xff"
 
323
 
 
324
def set_memory(map, mem):
 
325
    if mem.number < 0 or mem.number > (max(V71_SPECIAL.values()) + 1):
 
326
        raise errors.InvalidMemoryLocation("Number must be between 0 and 999")
 
327
 
 
328
    mmap = memmap.MemoryMap(get_raw_mem(map, mem.number))
 
329
 
 
330
    if not get_used(map, mem.number):
 
331
        initialize(mmap)
 
332
 
 
333
    set_freq(mmap, mem.freq)
 
334
    if mem.number < 999:
 
335
        set_name(map, mem.number, mem.name)
 
336
    set_tmode(mmap, mem.tmode)
 
337
    set_tone(mmap, mem.rtone, POS_RTONE)
 
338
    set_tone(mmap, mem.ctone, POS_CTONE)
 
339
    set_dtcs(mmap, mem.dtcs)
 
340
    set_duplex(mmap, mem.duplex)
 
341
    set_offset(mmap, mem.offset)
 
342
    set_mode(mmap, mem.mode)
 
343
 
 
344
    base = get_mem_offset(mem.number)
 
345
    map[base] = mmap.get_packed()
 
346
 
 
347
    set_used(map, mem.number, mem.freq)
 
348
    if mem.number < 999:
 
349
        set_skip(map, mem.number, mem.skip)
 
350
 
 
351
    return map
 
352
 
 
353
if __name__ == "__main__":
 
354
    import sys
 
355
    import serial
 
356
    s = serial.Serial(port=sys.argv[1], baudrate=9600, dsrdtr=True,
 
357
                      timeout=0.25)
 
358
    #s.write("\r\r")
 
359
    #print get_id(s)
 
360
    data = download(s)
 
361
    file(sys.argv[2], "wb").write(data)
 
362