# HG changeset patch
# User Marco Filippi <iz3gme.marco@gmail.com>
# Date 1327010631 -3600
# Node ID f88a7674686e21a04ed74861bcbf9806bb753a21
# Parent  df1ee5859394df02f4a0778a548b0d072b100f47
FT857 initial support

diff -r df1ee5859394 -r f88a7674686e chirp/chirp_common.py
--- a/chirp/chirp_common.py	Thu Jan 19 08:49:20 2012 -0800
+++ b/chirp/chirp_common.py	Thu Jan 19 23:03:51 2012 +0100
@@ -58,6 +58,8 @@
     "Tone->DCS",
     "DCS->CTCSS",
     "Tone->CTCSS",
+    "Off->Tone",
+    "Off->DCS",
 ]
 
 MODES = ["WFM", "FM", "NFM", "AM", "NAM", "DV", "USB", "LSB", "CW", "RTTY", "DIG", "PKT", "NCW", "NCWR", "CWR"]
diff -r df1ee5859394 -r f88a7674686e chirp/directory.py
--- a/chirp/directory.py	Thu Jan 19 08:49:20 2012 -0800
+++ b/chirp/directory.py	Thu Jan 19 23:03:51 2012 +0100
@@ -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, ft817
+from chirp import vx3, vx5, vx6, vx7, vx8, ft2800, ft7800, ft50, ft60, ft817, ft857
 from chirp import kenwood_live, tmv71, thd72
 from chirp import alinco
 from chirp import wouxun
@@ -60,6 +60,7 @@
     "ft60"           : ft60.FT60Radio,
     "ft817"          : ft817.FT817Radio,
     "ft817nd"        : ft817.FT817NDRadio,
+    "ft857"          : ft857.FT857Radio,
 
     # Kenwood
     "thd7"           : kenwood_live.THD7Radio,
diff -r df1ee5859394 -r f88a7674686e chirp/ft817.py
--- a/chirp/ft817.py	Thu Jan 19 08:49:20 2012 -0800
+++ b/chirp/ft817.py	Thu Jan 19 23:03:51 2012 +0100
@@ -127,10 +127,10 @@
        is_fm_narrow:1,
        freq_range:3;
   u8   skip:1,
-       unknown3:1,
+       unknown2:1,
        ipo:1,
        att:1,
-       unknown2:4;
+       unknown3:4;
   u8   ssb_step:2,
        am_step:3,
        fm_step:3;
@@ -139,9 +139,9 @@
   u8   unknown5:2,
        tx_mode:3,
        tx_freq_range:3;
-  u8   unknown7:2,
+  u8   unknown6:2,
        tone:6;
-  u8   unknown8:1,
+  u8   unknown7:1,
        dcs:7;
   ul16 rit;
   u32 freq;
@@ -150,26 +150,26 @@
 } memory[200];
 """
 
-DUPLEX = ["", "-", "+", "split"]
-MODES  = ["LSB", "USB", "CW", "CWR", "AM", "FM", "DIG", "PKT", "NCW", "NCWR", "NFM"]   # 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"
 
+    DUPLEX = ["", "-", "+", "split"]
+    MODES  = ["LSB", "USB", "CW", "CWR", "AM", "FM", "DIG", "PKT", "NCW", "NCWR", "NFM"]   # 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)]
+
     _model = ""
     _memsize = 6509
     # block 9 (130 Bytes long) is to be repeted 40 times
@@ -191,14 +191,14 @@
         rf.has_bank = False
         rf.has_dtcs_polarity = False
         rf.has_nostep_tuning = True
-        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_modes = list(set(self.MODES))
+        rf.valid_tmodes = list(self.TMODES)
+        rf.valid_duplexes = list(self.DUPLEX)
+        rf.valid_tuning_steps = list(self.STEPSFM)
+        rf.valid_bands = self.VALID_BANDS
         rf.valid_skips = ["", "S"]
-        rf.valid_power_levels = POWER_LEVELS
-        rf.valid_characters = "".join(CHARSET)
+        rf.valid_power_levels = self.POWER_LEVELS
+        rf.valid_characters = "".join(self.CHARSET)
         rf.valid_name_length = 8
         rf.memory_bounds = (1, 200)
         rf.can_odd_split = True
@@ -208,6 +208,15 @@
     def get_raw_memory(self, number):
         return repr(self._memobj.memory[number-1])
 
+    def get_duplex(self, mem, _mem):
+        if _mem.is_duplex == 1:
+            mem.duplex = self.DUPLEX[_mem.duplex]
+        else:
+            mem.duplex = ""
+
+    def get_tmode(self, mem, _mem):
+        mem.tmode = self.TMODES[_mem.tmode]
+
     def get_memory(self, number):
         _mem = self._memobj.memory[number-1]
         used = (self._memobj.visible[(number-1)/8] >> (number-1)%8) & 0x01
@@ -220,25 +229,22 @@
 
         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]
+        self.get_duplex(mem, _mem)
+        mem.mode = self.MODES[_mem.mode]
         if mem.mode == "FM":
             if _mem.is_fm_narrow == 1:
                 mem.mode = "NFM"
-            mem.tuning_step = STEPSFM[_mem.fm_step]
+            mem.tuning_step = self.STEPSFM[_mem.fm_step]
         elif mem.mode == "AM":
-            mem.tuning_step = STEPSAM[_mem.am_step]
+            mem.tuning_step = self.STEPSAM[_mem.am_step]
         elif mem.mode == "CW" or mem.mode == "CWR":
             if _mem.is_cwdig_narrow == 1:
                 mem.mode = "N" + mem.mode
-            mem.tuning_step = STEPSSSB[_mem.ssb_step]
+            mem.tuning_step = self.STEPSSSB[_mem.ssb_step]
         else:   # TODO more investigation needed on steps for other modes 
-            mem.tuning_step = STEPSSSB[_mem.ssb_step]
+            mem.tuning_step = self.STEPSSSB[_mem.ssb_step]
         mem.skip = _mem.skip and "S" or ""
-        mem.tmode = TMODES[_mem.tmode]
+        self.get_tmode(mem, _mem)
         mem.rtone = mem.ctone = chirp_common.TONES[_mem.tone]
         mem.dtcs = chirp_common.DTCS_CODES[_mem.dcs]
 
@@ -246,18 +252,29 @@
             for i in _mem.name:
                 if i == "\xFF":
                     break
-                mem.name += CHARSET[i]
+                mem.name += self.CHARSET[i]
             mem.name = mem.name.rstrip()
         else:
             mem.name = ""
 
         return mem
 
+    def set_duplex(self, mem, _mem):
+        _mem.duplex = self.DUPLEX.index(mem.duplex)
+        _mem.is_duplex = mem.duplex != ""
+
+    def set_tmode(self, mem, _mem):
+        _mem.tmode = self.TMODES.index(mem.tmode)
+
     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:
+            if mem.number == 1:
+                # as Dan says "yaesus are not good about that :("
+		# if you ulpoad an empty image you can brick your radio
+                raise Exception("Sorry, can't delete first memory") 
             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
@@ -271,16 +288,15 @@
         else:
             _mem.tag_on_off = 0
         _mem.tag_default = 0       # never use default label "CH-nnn"
-        _mem.duplex = DUPLEX.index(mem.duplex)
-        _mem.is_duplex = mem.duplex != ""
+        self.set_duplex(mem, _mem)
         if mem.mode[0] == "N": # is it narrow?
-            _mem.mode = MODES.index(mem.mode[1:])
+            _mem.mode = self.MODES.index(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.mode = self.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
+        for lo, hi in self.VALID_BANDS:              # I just don't know python enought
             if mem.freq > lo and mem.freq < hi:
                 break 
             i+=1
@@ -288,7 +304,7 @@
         if mem.duplex == "split":	# all this should be safe also when not in split but ... 
             _mem.tx_mode = _mem.mode
             i = 0                                   # This search can probably be written better but
-            for lo, hi in VALID_BANDS:              # I just don't know python enought
+            for lo, hi in self.VALID_BANDS:              # I just don't know python enought
                 if mem.offset >= lo and mem.offset < hi:
                     break 
                 i+=1
@@ -296,17 +312,17 @@
         _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)
+        self.set_tmode(mem, _mem)
         try:
-            _mem.ssb_step = STEPSSSB.index(mem.tuning_step)
+            _mem.ssb_step = self.STEPSSSB.index(mem.tuning_step)
         except ValueError:
             pass
         try:
-            _mem.am_step = STEPSAM.index(mem.tuning_step)
+            _mem.am_step = self.STEPSAM.index(mem.tuning_step)
         except ValueError:
             pass
         try:
-            _mem.fm_step = STEPSFM.index(mem.tuning_step)
+            _mem.fm_step = self.STEPSFM.index(mem.tuning_step)
         except ValueError:
             pass
         _mem.tone = chirp_common.TONES.index(mem.rtone)
@@ -315,7 +331,7 @@
         _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])
+            _mem.name[i] = self.CHARSET.index(mem.name.ljust(8)[i])
         
     def get_banks(self):
         return []
@@ -323,10 +339,10 @@
     def validate_memory(self, mem):
         msgs = yaesu_clone.YaesuCloneModeRadio.validate_memory(self, mem)
 
-	lo, hi = VALID_BANDS[3]    # this is fm broadcasting
+	lo, hi = self.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"))
+            if mem.mode != "FM":
+                msgs.append(chirp_common.ValidationError("Only FM is supported in this band"))
         # TODO check that step is valid in current mode
         return msgs
 
diff -r df1ee5859394 -r f88a7674686e chirp/ft857.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/ft857.py	Thu Jan 19 23:03:51 2012 +0100
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+#
+# Copyright 2012 Filippi Marco <iz3gme.marco@gmail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from chirp import ft817
+from chirp import bitwise
+
+mem_format = """
+#seekto 0x4a9;
+u8 visible[25];
+
+#seekto 0x4c4;
+u8 filled[25];
+
+#seekto 0x4df;
+struct {
+  u8   tag_on_off:1,
+       tag_default:1,
+       unknown1:3,
+       mode:3;
+  u8   duplex:2,
+       is_duplex:1,
+       is_cwdig_narrow:1,
+       is_fm_narrow:1,
+       freq_range:3;
+  u8   skip:1,
+       unknokwn1_1:1,
+       ipo:1,
+       att:1,
+       unknown2:4;
+  u8   ssb_step:2,
+       am_step:3,
+       fm_step:3;
+  u8   unknown3:3,
+       is_split_tone:1,
+       tmode:4;
+  u8   unknown4:2,
+       tx_mode:3,
+       tx_freq_range:3;
+  u8   unknown5:2,
+       tone:6;
+  u8   unknown6:8;
+  u8   unknown7:1,
+       dcs:7;
+  u8   unknown8:8;
+  ul16 rit;
+  u32 freq;
+  u32 offset;
+  u8   name[8];
+} memory[200];
+"""
+
+
+
+class FT857Radio(ft817.FT817Radio):
+    MODEL = "FT-857"
+
+    TMODES = {
+        0x04 : "Tone",
+        0x05 : "TSQL",
+        0x0a : "DTCS",
+        0xff : "Cross",
+        0x00 : "",
+    }
+    TMODES_REV = {
+        ""      : 0x00,
+        "Cross" : 0xff,
+        "DTCS"  : 0x0a,
+        "TSQL"  : 0x05,
+        "Tone"  : 0x04,
+    }
+
+    CROSS_MODES = {
+        0x08 : "DCS->Off",
+        0x06 : "Tone->DCS",
+        0x09 : "DCS->CTCSS",
+        0x01 : "Off->Tone",
+        0x02 : "Off->DCS",
+    }
+
+    CROSS_MODES_REV = {
+        "DCS->Off"   : 0x08,
+        "Tone->DCS"  : 0x06,
+        "DCS->CTCSS" : 0x09,
+        "Off->Tone"  : 0x01,
+        "Off->DCS"   : 0x02,
+    }
+
+    _model = ""
+    _memsize = 7341
+    # block 9 (140 Bytes long) is to be repeted 40 times 
+    # should be 42 times but this way I cam use original 817 functions
+    _block_lengths = [ 2, 82, 252, 196, 252, 196, 212, 55, 140, 140, 140, 38, 176]
+
+    def get_features(self):
+        rf = ft817.FT817Radio.get_features(self)
+        rf.has_cross = True
+        rf.valid_tmodes = self.TMODES_REV.keys()
+        # rf.valid_cross_mode = CROSS_MODES_REV.keys() # TODO will do this later
+        return rf
+
+    def get_duplex(self, mem, _mem):
+        # radio set is_duplex only for + and - but not for split
+        # at the same time it does not complain if we set it same way 817 does (so no set_duplex here)
+        mem.duplex = self.DUPLEX[_mem.duplex]
+
+    def get_tmode(self, mem, _mem):
+	# I do not use is_split_tone here because the radio sometimes set it also for standard tone mode
+        try:
+            mem.tmode = self.TMODES[int(_mem.tmode)]
+        except KeyError:
+            mem.tmode = "Cross"
+            mem.cross_mode = self.CROSS_MODES[int(_mem.tmode)]
+
+    def set_tmode(self, mem, _mem):
+        if mem.tmode != "Cross":
+            _mem.is_split_tone = 0
+            _mem.tmode = self.TMODES_REV[mem.tmode]
+        else:
+            _mem.tmode = self.CROSS_MODES_REV[mem.cross_mode]
+            _mem.is_split_tone = 1
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(mem_format, self._mmap)
+
+
