# HG changeset patch
# User Eric Allen <eric@hackerengineer.net>
# Date 1422595106 28800
#      Thu Jan 29 21:18:26 2015 -0800
# Node ID fd4bb2f6fd60680f6f811b7573f6fef1132fa9d3
# Parent  bd1d2bac911911051c0a7b7f345cb6a0eba58ea7
Add support for new firmware in TYT TH-UV3R that supports 2.5kHz steps.
 
This hopefully fixes #1227
 
diff -r bd1d2bac9119 -r fd4bb2f6fd60 chirp/th_uv3r25.py
--- /dev/nullThu Jan 01 00:00:00 1970 +0000
+++ b/chirp/th_uv3r25.pyThu Jan 29 21:18:26 2015 -0800
@@ -0,0 +1,175 @@
+# Copyright 2015 Eric Allen <eric@hackerengineer.net>
+#
+# 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/>.
+
+"""TYT uv3r (2.5kHz) radio management module"""
+
+from chirp import chirp_common, bitwise, directory
+from chirp.wouxun_common import do_download, do_upload
+
+from th_uv3r import TYTUV3RRadio, tyt_uv3r_prep, THUV3R_CHARSET
+
+
+def tyt_uv3r_download(radio):
+    tyt_uv3r_prep(radio)
+    return do_download(radio, 0x0000, 0x0B30, 0x0010)
+
+
+def tyt_uv3r_upload(radio):
+    tyt_uv3r_prep(radio)
+    return do_upload(radio, 0x0000, 0x0B30, 0x0010)
+
+mem_format = """
+// 20 bytes per memory
+struct memory {
+  ul32 rx_freq; // 4 bytes
+  ul32 tx_freq; // 8 bytes
+
+  ul16 rx_tone; // 10 bytes
+  ul16 tx_tone; // 12 bytes
+
+  u8 unknown1a:1,
+     iswide:1,
+     bclo_n:1, // TODO: expose in UI
+     vox_n:1, // TODO: expose in UI
+     epilogue:1, // TODO: figure out what this even is
+     power_high:1,
+     voice_mode:2; // TODO: expose in UI
+  u8 name[6]; // 19 bytes
+  u8 unknown2; // 20 bytes
+};
+
+#seekto 0x0010;
+struct memory memory[128];
+
+#seekto 0x0A80;
+u8 emptyflags[16];
+u8 skipflags[16];
+"""
+
+
+@directory.register
+class TYTUV3R25Radio(TYTUV3RRadio):
+    MODEL = "TH-UV3R (2.5kHz)"
+    _memsize = 2864
+
+    POWER_LEVELS = [chirp_common.PowerLevel("High", watts=2.00),
+                    chirp_common.PowerLevel("Low", watts=0.80)]
+
+    def get_features(self):
+        rf = super(TYTUV3R25Radio, self).get_features()
+
+        rf.valid_tuning_steps = [2.5, 5.0, 6.25, 10.0, 12.5, 25.0, 37.50,
+                                 50.0, 100.0]
+        rf.valid_power_levels = self.POWER_LEVELS
+        return rf
+
+    def sync_in(self):
+        self.pipe.setTimeout(2)
+        self._mmap = tyt_uv3r_download(self)
+        self.process_mmap()
+
+    def sync_out(self):
+        tyt_uv3r_upload(self)
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(mem_format, self._mmap)
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number - 1])
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number - 1]
+        mem = chirp_common.Memory()
+        mem.number = number
+
+        bit = 1 << ((number - 1) % 8)
+        byte = (number - 1) / 8
+
+        if self._memobj.emptyflags[byte] & bit:
+            mem.empty = True
+            return mem
+
+        mem.freq = _mem.rx_freq * 10
+        mem.offset = abs(_mem.rx_freq - _mem.tx_freq)
+        if _mem.tx_freq == _mem.rx_freq:
+            mem.duplex = ""
+        elif _mem.tx_freq < _mem.rx_freq:
+            mem.duplex = "-"
+        elif _mem.tx_freq > _mem.rx_freq:
+            mem.duplex = "+"
+
+        mem.mode = _mem.iswide and "FM" or "NFM"
+        self._decode_tone(mem, _mem)
+        mem.skip = (self._memobj.skipflags[byte] & bit) and "S" or ""
+
+        for char in _mem.name:
+            try:
+                c = THUV3R_CHARSET[char]
+            except:
+                c = ""
+            mem.name += c
+        mem.name = mem.name.rstrip()
+
+        mem.power = self.POWER_LEVELS[not _mem.power_high]
+
+        return mem
+
+    def set_memory(self, mem):
+        _mem = self._memobj.memory[mem.number - 1]
+        bit = 1 << ((mem.number - 1) % 8)
+        byte = (mem.number - 1) / 8
+
+        if mem.empty:
+            self._memobj.emptyflags[byte] |= bit
+            _mem.set_raw("\xFF" * 16)
+            return
+
+        self._memobj.emptyflags[byte] &= ~bit
+
+        _mem.rx_freq = mem.freq / 10
+
+        if mem.duplex == "":
+            _mem.tx_freq = _mem.rx_freq
+        elif mem.duplex == "-":
+            _mem.tx_freq = _mem.rx_freq - mem.offset
+        elif mem.duplex == "+":
+            _mem.tx_freq = _mem.rx_freq + mem.offset
+
+        _mem.iswide = mem.mode == "FM"
+
+        self._encode_tone(mem, _mem)
+
+        if mem.skip:
+            self._memobj.skipflags[byte] |= bit
+        else:
+            self._memobj.skipflags[byte] &= ~bit
+
+        name = []
+        for char in mem.name.ljust(6):
+            try:
+                c = THUV3R_CHARSET.index(char)
+            except:
+                c = THUV3R_CHARSET.index(" ")
+            name.append(c)
+        _mem.name = name
+
+        if mem.power == self.POWER_LEVELS[0]:
+            _mem.power_high = 1
+        else:
+            _mem.power_high = 0
+
+    @classmethod
+    def match_model(cls, filedata, filename):
+        return len(filedata) == cls._memsize