3
# Copyright 2010 Dan Smith <dsmith@danplanet.com>
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.
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.
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/>.
20
from chirp import memmap, chirp_common, errors
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
48
for k,v in V71_SPECIAL.items():
49
V71_SPECIAL_REV[v] = k
51
def command(s, cmd, timeout=0.5):
56
print "PC->V71: %s" % cmd
58
while not data.endswith("\r") and (time.time() - start) < timeout:
61
print "V71->PC: %s" % data.strip()
66
if r.startswith("ID "):
67
return r.split(" ")[1]
69
raise errors.RadioError("No response to ID command")
71
EXCH_R = "R\x00\x00\x00"
72
EXCH_W = "W\x00\x00\x00"
74
def read_block(s, block, count=256):
75
s.write(struct.pack("<cHB", "R", block, 0))
78
raise Exception("Did not receive block response")
80
cmd, _block, zero = struct.unpack("<cHB", r)
81
if cmd != "W" or _block != block:
82
raise Exception("Invalid response: %s %i" % (cmd, _block))
85
while len(data) < count:
86
data += s.read(count - len(data))
89
if s.read(1) != chr(0x06):
90
raise Exception("Did not receive post-block ACK!")
94
def write_block(s, block, map):
95
s.write(struct.pack("<cHB", "W", block, 0))
97
s.write(map[base:base+256])
101
return ack == chr(0x06)
104
if command(radio.pipe, "0M PROGRAM") != "0M":
105
raise errors.RadioError("No response from radio")
108
for i in range(0, 0x7F):
109
data += read_block(radio.pipe, i)
111
s = chirp_common.Status()
112
s.msg = "Cloning from radio"
117
radio.pipe.write("E")
119
return memmap.MemoryMap(data)
122
if command(radio.pipe, "0M PROGRAM") != "0M":
123
raise errors.RadioError("No response from radio")
125
for i in range(0, 0x7F):
126
r = write_block(radio.pipe, i, radio._mmap)
128
raise errors.RadioError("Radio NAK'd block %i" % i)
130
s = chirp_common.Status()
131
s.msg = "Cloning to radio"
136
radio.pipe.write("E")
138
def get_mem_offset(number):
139
return MEM_LOC_BASE + (MEM_LOC_SIZE * number)
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]
146
def get_used(map, number):
147
pos = MEM_FLG_BASE + (number * 2)
149
print "Flag byte is %02x" % flag
150
return not (flag & 0x80)
152
def set_used(map, number, freq):
153
pos = MEM_FLG_BASE + (number * 2)
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"
162
def get_skip(map, number):
163
pos = MEM_FLG_BASE + (number * 2)
164
flag = ord(map[pos+1])
170
def set_skip(map, number, skip):
171
pos = MEM_FLG_BASE + (number * 2)
172
flag = ord(map[pos+1])
180
freq, = struct.unpack("<I", mmap[0:4])
181
return freq / 1000000.0
183
def set_freq(mmap, freq):
184
mmap[0] = struct.pack("<I", int(freq * 1000000))
186
def get_name(map, number):
187
base = MEM_TAG_BASE + (8 * number)
188
return map[base:base+6].replace("\xff", "")
190
def set_name(mmap, number, name):
191
base = MEM_TAG_BASE + (8 * number)
192
mmap[base] = name.ljust(6)[:6].upper()
195
val = ord(mmap[POS_TMODE]) & 0x70
206
def set_tmode(mmap, tmode):
207
val = ord(mmap[POS_TMODE]) & 0x8F
216
mmap[POS_TMODE] = val | tmodemap[tmode]
218
def get_tone(mmap, offset):
219
val = ord(mmap[offset])
221
return chirp_common.TONES[val]
223
def set_tone(mmap, tone, offset):
225
mmap[offset] = chirp_common.TONES.index(tone)
228
val = ord(mmap[POS_DTCS])
230
return chirp_common.DTCS_CODES[val]
232
def set_dtcs(mmap, dtcs):
233
mmap[POS_DTCS] = chirp_common.DTCS_CODES.index(dtcs)
235
def get_duplex(mmap):
236
val = ord(mmap[POS_DUP]) & 0x03
246
def set_duplex(mmap, duplex):
247
val = ord(mmap[POS_DUP]) & 0xFC
255
mmap[POS_DUP] = val | dupmap[duplex]
257
def get_offset(mmap):
258
val, = struct.unpack("<I", mmap[POS_OFFSET:POS_OFFSET+4])
259
return val / 1000000.0
261
def set_offset(mmap, offset):
262
mmap[POS_OFFSET] = struct.pack("<I", int(offset * 1000000))
265
val = ord(mmap[POS_MODE]) & 0x03
274
def set_mode(mmap, mode):
275
val = ord(mmap[POS_MODE]) & 0xFC
282
mmap[POS_MODE] = val | modemap[mode]
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")
288
mem = chirp_common.Memory()
292
mem.extd_number = V71_SPECIAL_REV[number]
293
if not get_used(map, number):
297
mmap = get_raw_mem(map, number)
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)
310
mem.skip = get_skip(map, number)
313
mem.immutable = ["number", "bank", "extd_number", "name"]
314
if number > 1020 and number < 1030:
315
mem.immutable += ["freq"] # FIXME: ALL
319
def initialize(mmap):
321
"\x80\xc8\xb3\x08\x00\x01\x00\x08" + \
322
"\x08\x00\xc0\x27\x09\x00\x00\xff"
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")
328
mmap = memmap.MemoryMap(get_raw_mem(map, mem.number))
330
if not get_used(map, mem.number):
333
set_freq(mmap, mem.freq)
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)
344
base = get_mem_offset(mem.number)
345
map[base] = mmap.get_packed()
347
set_used(map, mem.number, mem.freq)
349
set_skip(map, mem.number, mem.skip)
353
if __name__ == "__main__":
356
s = serial.Serial(port=sys.argv[1], baudrate=9600, dsrdtr=True,
361
file(sys.argv[2], "wb").write(data)