# HG changeset patch # Parent ba2876002bef8e64adbb090d3b73723c45041479 Add FT-817/ND driver diff -r ba2876002bef chirp/chirp_common.py --- a/chirp/chirp_common.py Mon Jan 16 08:33:06 2012 -0800 +++ b/chirp/chirp_common.py Mon Jan 16 08:40:28 2012 -0800 @@ -60,7 +60,7 @@ "Tone->CTCSS", ] -MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY"] +MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "PKT", "CWN", "CWRN"] STD_2M_OFFSETS = [ (145100000, 145500000, -600000), @@ -94,7 +94,7 @@ 5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0, 100.0, 125.0, 200.0, # Need to fix drivers using this list as an index! - 9.0, + 9.0, 1.0 ] SKIP_VALUES = [ "", "S", "P" ] diff -r ba2876002bef chirp/directory.py --- a/chirp/directory.py Mon Jan 16 08:33:06 2012 -0800 +++ b/chirp/directory.py Mon Jan 16 08:40:28 2012 -0800 @@ -18,7 +18,7 @@ from chirp import id800, id880, ic2820, ic2200, ic9x, icx8x, ic2100, ic2720 from chirp import icq7, icomciv, idrp, icf, ic9x_icf, icw32, ict70 -from chirp import vx3, vx5, vx6, vx7, vx8, ft2800, ft7800, ft50, ft60 +from chirp import vx3, vx5, vx6, vx7, vx8, ft2800, ft7800, ft50, ft60, ft817nd from chirp import kenwood_live, tmv71, thd72 from chirp import alinco from chirp import wouxun @@ -58,6 +58,8 @@ "ft8900" : ft7800.FT8900Radio, #"ft50" : ft50.FT50Radio, "ft60" : ft60.FT60Radio, + "ft817" : ft817nd.FT817Radio, + "ft817nd" : ft817nd.FT817NDRadio, # Kenwood "thd7" : kenwood_live.THD7Radio, diff -r ba2876002bef chirp/ft817nd.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chirp/ft817nd.py Mon Jan 16 08:40:28 2012 -0800 @@ -0,0 +1,334 @@ +#!/usr/bin/python +# +# Copyright 2012 Filippi Marco +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from chirp import chirp_common, yaesu_clone, util, memmap +from chirp import bitwise +import time, os + +CMD_ACK = 0x06 + +def ft817_read(pipe, block, blocknum): + for i in range(0,60): + data = pipe.read(block+2) + if data: + break + time.sleep(0.5) + if len(data) == block+2 and data[0] == chr(blocknum): + checksum = yaesu_clone.YaesuChecksum(1, block) + if checksum.get_existing(data) != \ + checksum.get_calculated(data): + raise Exception("Checksum Failed [%02X<>%02X] block %02X" % (checksum.get_existing(data), checksum.get_calculated(data), blocknum)) + data = data[1:block+1] # Chew away the block number and the checksum + else: + raise Exception("Unable to read block %02X expected %i got %i" % (blocknum, block+2, len(data))) + + if os.getenv("CHIRP_DEBUG"): + print "Read %i" % len(data) + return data + +def clone_in(radio): + pipe = radio.pipe + + start = time.time() + + data = "" + blocks = 0 + status = chirp_common.Status() + status.msg = "Cloning from radio" + status.max = len(radio._block_lengths) + 39 + for block in radio._block_lengths: + if blocks == 8: + repeat = 40 # repeated read of 40 block same size (memory area btw) + else: + repeat = 1 + for i in range(0, repeat): + data += ft817_read(pipe, block, blocks) + pipe.write(chr(CMD_ACK)) + blocks += 1 + status.cur = blocks + radio.status_fn(status) + + print "Clone completed in %i seconds" % (time.time() - start) + + return memmap.MemoryMap(data) + +def clone_out(radio): + delay = 0.5 + pipe = radio.pipe + + start = time.time() + + blocks = 0 + pos = 0 + status = chirp_common.Status() + status.msg = "Cloning to radio" + status.max = len(radio._block_lengths) + 39 + for block in radio._block_lengths: + if blocks == 8: + repeat = 40 # repeated read of 40 block same size (memory area btw) + else: + repeat = 1 + for i in range(0, repeat): + checksum = yaesu_clone.YaesuChecksum(pos, pos+block-1) + if os.getenv("CHIRP_DEBUG"): + print "Block %i - will send from %i to %i byte " % (blocks, pos, pos+block) + print util.hexprint(chr(blocks)) + print util.hexprint(radio._mmap[pos:pos+block]) + print util.hexprint(chr(checksum.get_calculated(radio._mmap))) + pipe.write(chr(blocks)) + pipe.write(radio._mmap[pos:pos+block]) + pipe.write(chr(checksum.get_calculated(radio._mmap))) + buf = pipe.read(1) + if not buf or buf[0] != chr(CMD_ACK): + time.sleep(delay) + buf = pipe.read(1) + if not buf or buf[0] != chr(CMD_ACK): + if os.getenv("CHIRP_DEBUG"): + print util.hexprint(buf) + raise Exception("Radio did not ack block %i" % blocks) + pos += block + blocks += 1 + status.cur = blocks + radio.status_fn(status) + + print "Clone completed in %i seconds" % (time.time() - start) + +mem_format = """ +#seekto 0x3fd; +u8 visible[25]; + +#seekto 0x417; +u8 filled[25]; + +#seekto 0x431; +struct { + u8 tag_on_off:1, + tag_default:1, + is_hf:1, + unknown1:2, + mode:3; + u8 duplex:2, + is_duplex:1, + is_cwdig_narrow:1, + is_fm_narrow:1, + freq_range:3; + u8 skip:1, + unknown3:1, + ipo:1, + att:1, + unknown2:2, + tmode:2; + u8 ssb_step:2, + am_step:3, + fm_step:3; + u8 unknown4[2]; + u8 unknown5:3, + tone:5; + u8 unknown6:1, + dcs:7; + ul16 rit; + u32 freq; + u32 offset; + u8 name[8]; +} memory[200]; +""" + +DUPLEX = ["", "-", "+", "split"] +MODES = ["LSB", "USB", "CW", "CWR", "AM", "FM", "DIG", "PKT", "CWN", "CWRN", "FMN"] # narrow modes has to be at end +TMODES = ["", "Tone", "TSQL", "DTCS"] +STEPSFM = [5.0, 6.25, 10.0, 12.5, 15.0, 20.0, 25.0, 50.0] +STEPSAM = [2.5, 5.0, 9.0, 10.0, 12.5, 25.0] +STEPSSSB = [1.0, 2.5, 5.0] +VALID_BANDS = [(100000,33000000), (33000000,56000000), (76000000,108000000), (108000000,137000000), (137000000,154000000), (420000000,470000000)] # warning ranges has to be in this exact order + + +CHARSET = [chr(x) for x in range(0, 256)] + +POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.00), # not used in memory + chirp_common.PowerLevel("L3", watts=2.50), + chirp_common.PowerLevel("L2", watts=1.00), + chirp_common.PowerLevel("L1", watts=0.5)] + +class FT817Radio(yaesu_clone.YaesuCloneModeRadio): + BAUD_RATE = 9600 + MODEL = "FT-817" + + _model = "" + _memsize = 6509 + # block 9 (130 Bytes long) is to be repeted 40 times + _block_lengths = [ 2, 40, 208, 182, 208, 182, 198, 53, 130, 118, 118] + + + def sync_in(self): + self._mmap = clone_in(self) + self.process_mmap() + + def sync_out(self): + clone_out(self) + + def process_mmap(self): + self._memobj = bitwise.parse(mem_format, self._mmap) + + def get_features(self): + rf = chirp_common.RadioFeatures() + rf.has_bank = False + rf.has_dtcs_polarity = False + rf.valid_modes = list(set(MODES)) + rf.valid_tmodes = list(TMODES) + rf.valid_duplexes = list(DUPLEX) + rf.valid_tuning_steps = list(STEPSFM) + rf.valid_bands = VALID_BANDS + rf.valid_skips = ["", "S"] + rf.valid_power_levels = POWER_LEVELS + rf.valid_characters = "".join(CHARSET) + rf.valid_name_length = 8 + rf.memory_bounds = (1, 200) + rf.can_odd_split = True + rf.has_ctone = False + return rf + + def get_raw_memory(self, number): + return repr(self._memobj.memory[number-1]) + + def get_memory(self, number): + _mem = self._memobj.memory[number-1] + used = (self._memobj.visible[(number-1)/8] >> (number-1)%8) & 0x01 + + mem = chirp_common.Memory() + mem.number = number + if not used: + mem.empty = True + return mem + + mem.freq = int(_mem.freq) * 10 + mem.offset = int(_mem.offset) * 10 + if _mem.is_duplex == 1: + mem.duplex = DUPLEX[_mem.duplex] + else: + mem.duplex = "" + mem.mode = MODES[_mem.mode] + if mem.mode == "FM": + if _mem.is_fm_narrow == 1: + mem.mode = "FMN" + mem.tuning_step = STEPSFM[_mem.fm_step] + elif mem.mode == "AM": + mem.tuning_step = STEPSAM[_mem.am_step] + elif mem.mode == "CW" or mem.mode == "CWR": + if mem.is_cwdig_narrow == 1: + mem.mode = mem.mode + "N" + mem.tuning_step = STEPSSB[_mem.ssb_step] + else: # TODO more investigation needed on steps for other modes + mem.tuning_step = STEPSSB[_mem.ssb_step] + mem.skip = _mem.skip and "S" or "" + mem.tmode = TMODES[_mem.tmode] + mem.rtone = mem.ctone = chirp_common.TONES[_mem.tone] + mem.dtcs = chirp_common.DTCS_CODES[_mem.dcs] + + for i in _mem.name: + if i == "\xFF": + break + mem.name += CHARSET[i] + mem.name = mem.name.rstrip() + + return mem + + def set_memory(self, mem): + _mem = self._memobj.memory[mem.number-1] + was_valid = (self._memobj.visible[(mem.number-1)/8] >> (mem.number-1)%8) & 0x01 + + if mem.empty: + self._memobj.visible[(mem.number-1)/8] &= ~ (1 << (mem.number-1)%8) + self._memobj.filled[(mem.number-1)/8] = self._memobj.visible[(mem.number-1)/8] + return + + self._memobj.visible[(mem.number-1)/8] |= 1 << (mem.number-1)%8 + self._memobj.filled[(mem.number-1)/8] = self._memobj.visible[(mem.number-1)/8] + + if len(mem.name) > 0: # not supported in chirp + # so I make label visible if have one + _mem.tag_on_off = 1 + else: + _mem.tag_on_off = 0 + _mem.tag_default = 0 # never use default label "CH-nnn" + lo, hi = VALID_BANDS[0] # first band is HF + if mem.freq < hi: + _mem.is_hf = 1 + else: + _mem.is_hf = 0 + _mem.duplex = DUPLEX.index(mem.duplex) + _mem.is_duplex = mem.duplex != "" + if mem.mode[len(mem.mode)-1] == "N": # is it narrow? + _mem.mode = MODES.index(mem.mode[0:len(mem.mode)-1]) + _mem.is_fm_narrow = _mem.is_cwdig_narrow = 1 # here I suppose it's safe to set both + else: + _mem.mode = MODES.index(mem.mode) + _mem.is_fm_narrow = _mem.is_cwdig_narrow = 0 # here I suppose it's safe to set both + i = 0 # This search can probably be written better but + for lo, hi in VALID_BANDS: # I just don't know python enought + if mem.freq > lo and mem.freq < hi: + break + i+=1 + _mem.freq_range = i + _mem.skip = mem.skip == "S" + _mem.ipo = 0 # not supported in chirp + _mem.att = 0 # not supported in chirp + _mem.tmode = TMODES.index(mem.tmode) + try: + _mem.ssb_step = STEPSSSB.index(mem.tuning_step) + except ValueError: + pass + try: + _mem.am_step = STEPSAM.index(mem.tuning_step) + except ValueError: + pass + try: + _mem.fm_step = STEPSFM.index(mem.tuning_step) + except ValueError: + pass + _mem.tone = chirp_common.TONES.index(mem.rtone) + _mem.dcs = chirp_common.DTCS_CODES.index(mem.dtcs) + _mem.rit = 0 # not supported in chirp + _mem.freq = mem.freq / 10 + _mem.offset = mem.offset / 10 + for i in range(0, 8): + _mem.name[i] = CHARSET.index(mem.name.ljust(8)[i]) + + def get_banks(self): + return [] + + def validate_memory(self, mem): + msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem) + + lo, hi = VALID_BANDS[3] # this is fm broadcasting + if mem.freq >= lo and mem.freq <= hi: + if mem.mode != "WFM": + msgs.append(chirp_common.ValidationError("Only wide FM is supported in this band")) + # TODO check that step is valid in current mode + return msgs + + @classmethod + def match_model(cls, filedata): + return len(filedata) == cls._memsize + +class FT817NDRadio(FT817Radio): + BAUD_RATE = 9600 + MODEL = "FT-817ND" + + _model = "" + _memsize = 6521 + # block 9 (130 Bytes long) is to be repeted 40 times + _block_lengths = [ 2, 40, 208, 182, 208, 182, 198, 53, 130, 118, 130]