#!/usr/bin/python # # Copyright 2010 Dan Smith # # 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) if data: break time.sleep(0.5) if len(data) == block and data[0] == chr(blocknum): checksum = yaesu_clone.YaesuChecksum(1, block-2) 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, 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: for i in range(0, 40): # repeated read of 40 block same size (memory area btw) data += ft817_read(pipe, block, blocks) pipe.write(chr(CMD_ACK)) blocks += 1 status.cur = blocks radio.status_fn(status) else: 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) mem_format = """ #seekto 0x417; u8 flags[25]; #seekto 0x431; struct { u8 tag_on_off:1, unknown1_1:1, hf_vhf:1, unknown1:2, mode:3; u8 duplex:2, is_uhf: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"] 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] CHARSET = [chr(x) for x in range(0, 256)] POWER_LEVELS = [chirp_common.PowerLevel("Hi", watts=5.00), chirp_common.PowerLevel("L3", watts=2.50), chirp_common.PowerLevel("L2", watts=1.00), chirp_common.PowerLevel("L1", watts=0.5)] class FT817NDRadio(yaesu_clone.YaesuCloneModeRadio): BAUD_RATE = 9600 VENDOR = "Yaesu" MODEL = "FT-817ND" _model = "" _memsize = 6521 # block 9 (132Bytes long) is to be repeted 40 times _block_lengths = [ 4, 42, 210, 184, 210, 184, 200, 55, 132, 120, 132] def sync_in(self): self._mmap = clone_in(self) self.process_mmap() 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 = [(100000,33000000), (33000000,56000000), (76000000,108000000), (108000000,137000000), (137000000,154000000), (420000000,470000000)] 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]) def get_memory(self, number): _mem = self._memobj.memory[number-1] used = (self._memobj.flags[(number-1)/8] >> (number-1)%8) & 0x01 #nibble = ((number-1) % 2) and "even" or "odd" #used = _flag["%s_masked" % nibble] and _flag["%s_valid" % nibble] #pskip = _flag["%s_pskip" % nibble] #skip = _flag["%s_skip" % nibble] 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 mem.rtone = mem.ctone = chirp_common.TONES[_mem.tone] mem.tmode = TMODES[_mem.tmode] mem.duplex = DUPLEX[_mem.duplex] mem.mode = MODES[_mem.mode] mem.dtcs = chirp_common.DTCS_CODES[_mem.dcs] if mem.mode == "FM": mem.tuning_step = STEPSFM[_mem.fm_step] elif mem.mode == "AM": mem.tuning_step = STEPSAM[_mem.am_step] else: mem.tuning_step = STEPSSB[_mem.ssb_step] #mem.skip = pskip and "P" or skip and "S" or "" mem.skip = _mem.skip and "S" or "" #if mem.freq > 220000000 and mem.freq < 225000000: # mem.power = POWER_LEVELS_220[1 - _mem.power] #else: # mem.power = POWER_LEVELS[3 - _mem.power] for i in _mem.name: if i == "\xFF": break mem.name += CHARSET[i] mem.name = mem.name.rstrip() return mem def _wipe_memory(self, mem): mem.set_raw("\x00" * mem.size() ) def set_memory(self, mem): _mem = self._memobj.memory[mem.number-1] _flag = self._memobj.flags[(mem.number-1)/2] nibble = ((mem.number-1) % 2) and "even" or "odd" was_valid = int(_flag["%s_valid" % nibble]) _flag["%s_masked" % nibble] = not mem.empty _flag["%s_valid" % nibble] = not mem.empty if mem.empty: return if not was_valid: self._wipe_memory(_mem) _mem.freq = mem.freq / 1000 _mem.offset = mem.offset / 1000 _mem.tone = chirp_common.TONES.index(mem.rtone) _mem.tmode = TMODES.index(mem.tmode) _mem.duplex = DUPLEX.index(mem.duplex) _mem.mode = MODES.index(mem.mode) _mem.dcs = chirp_common.DTCS_CODES.index(mem.dtcs) _mem.tune_step = STEPS.index(mem.tuning_step) if mem.power: _mem.power = 3 - POWER_LEVELS.index(mem.power) else: _mem.power = 0 _flag["%s_pskip" % nibble] = mem.skip == "P" _flag["%s_skip" % nibble] = mem.skip == "S" 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) if mem.freq >= 222000000 and mem.freq <= 225000000: if mem.power not in POWER_LEVELS_220: msgs.append(chirp_common.ValidationError(\ "Power level %s not supported on 220MHz band" % mem.power)) return msgs @classmethod def match_model(cls, filedata): return len(filedata) == cls._memsize