On 2013-02-19 06:38, Dan Smith wrote:
Okay, here is my ID-51 (id51.py) new driver.  Also included is an
image file for same.
Could you put it into the proper patch format?

http://chirp.danplanet.com/projects/chirp/wiki/DevelopersProcess#Submitting-a-patch

OK, I'm confoozed.  I previously read that page, and I know that's the correct format for a change.  Once you/me have decided that the changes I have for the ID-31 are appropriate as well, those will obviously be in patch format.  However, for a new driver, the whole file is new, so I don't understand the procedure for that.  For the image file, your page says:

If you are adding a new driver, you will need to add an image to the tests/images/ directory which is correctly named for your model. These do not communicate well in patch form, so just send a sample image to the development mailing list to accompany your patch submission.

So, that's what I did.  "hg status -mar" only shows the id31.py changes, which I'm not ready (per above) to submit yet.  I included the complete id51.py file in my previous eMail so you could see the differences (via the two diffs) before I formally submitted a patch.

So, just now I did "hg add chirp/id51.py", and now "hg status -mar" shows the id51.py.  I then did "hg diff" and got the output shown at the bottom of this eMail.  Note that the diff does not (yet) include adding the D-Star tag field into the "mycall" structure in id31.py;  that needs to be verified as needed (98% guess it's needed).

I didn't use the one in tests/images because there isn't one.
There is, but I suppose you're using a tarball snapshot which perhaps
doesn't have it:

% ls tests/images/Icom_ID-31A.img -l
-rw-r--r-- 1 dan dan 86K May  6  2012 tests/images/Icom_ID-31A.img


No, I did a "hg clone http://d-rats.com/hg/chirp.hg"  I got lots of test files, but none for the ID-31.  When I ran the tests, it ran one for the ID-51 (I suppose because I had an image file there -- the test passed), but none for the ID-31.

hg diff:


diff -r 5528bdcdc34e chirp/id31.py
--- a/chirp/id31.py     Sun Feb 17 18:58:44 2013 -0800
+++ b/chirp/id31.py     Tue Feb 19 08:19:42 2013 -0800
@@ -33,7 +33,7 @@
      duplex:2,
      dtcs_polarity:2;
   char name[16];
-  u8 unknow13;
+  u8 unknown13;
   u8 urcall[7];
   u8 rpt1call[7];
   u8 rpt2call[7];
@@ -88,6 +88,8 @@

 """

+MODES = [ "FM", "NFM", "DV" ]
+MODE_INDEX = [ 0, 1, 5 ]
 TMODES = ["", "Tone", "TSQL", "TSQL", "DTCS", "DTCS", "TSQL-R", "DTCS-R"]
 DUPLEX = ["", "-", "+"]
 DTCS_POLARITY = ["NN", "NR", "RN", "RR"]
@@ -198,7 +200,7 @@
         rf.has_bank_names = True
         rf.valid_tmodes = list(TMODES)
         rf.valid_tuning_steps = sorted(list(TUNING_STEPS))
-        rf.valid_modes = ["FM", "NFM", "DV"]
+        rf.valid_modes = list(MODES)
         rf.valid_skips = ["", "S", "P"]
         rf.valid_characters = chirp_common.CHARSET_ASCII
         rf.valid_name_length = 16
@@ -218,7 +220,7 @@

         bit = (1 << (number % 8))

-        if _mem.is_dv:
+        if MODES[MODE_INDEX.index(_mem.mode)] == "DV":
             mem = chirp_common.DVMemory()
         else:
             mem = chirp_common.Memory()
@@ -237,16 +239,12 @@
         mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
         mem.dtcs_polarity = DTCS_POLARITY[_mem.dtcs_polarity]
         mem.tuning_step = TUNING_STEPS[_mem.tune_step]
-
-        if _mem.is_dv:
-            mem.mode = "DV"
+        mem.mode = MODES[MODE_INDEX.index(_mem.mode)]
+
+        if mem.mode == "DV":
             mem.dv_urcall = _decode_call(_mem.urcall).rstrip()
             mem.dv_rpt1call = _decode_call(_mem.rpt1call).rstrip()
             mem.dv_rpt2call = _decode_call(_mem.rpt2call).rstrip()
-        elif _mem.is_narrow:
-            mem.mode = "NFM"
-        else:
-            mem.mode = "FM"

         if _psk & bit:
             mem.skip = "P"
@@ -279,9 +277,7 @@
         _mem.dtcs = chirp_common.DTCS_CODES.index(memory.dtcs)
         _mem.dtcs_polarity = DTCS_POLARITY.index(memory.dtcs_polarity)
         _mem.tune_step = TUNING_STEPS.index(memory.tuning_step)
-
-        _mem.is_narrow = memory.mode in ["NFM", "DV"]
-        _mem.is_dv = memory.mode == "DV"
+        _mem.mode = MODE_INDEX[MODES.index(memory.mode)]

         if isinstance(memory, chirp_common.DVMemory):
             _mem.urcall = _encode_call(memory.dv_urcall.ljust(8))
@@ -327,8 +323,3 @@
                 call = ""
             calls.append(call.rstrip())
         return calls
-
-if __name__ == "__main__":
-    print repr(_decode_call(_encode_call("KD7REX B")))
-    print repr(_decode_call(_encode_call("       B")))
-    print repr(_decode_call(_encode_call("        ")))
diff -r 5528bdcdc34e chirp/id51.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/chirp/id51.py     Tue Feb 19 08:19:42 2013 -0800
@@ -0,0 +1,323 @@
+# Copyright 2012 Dan Smith <dsmith@danplanet.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 directory, icf, bitwise, chirp_common
+
+MEM_FORMAT = """
+struct {
+  u24 freq;
+  u16 offset;
+  u16 rtone:6,
+      ctone:6,
+      unknown2:1,
+      mode:3;
+  u8 dtcs;
+  u8 tune_step:4,
+     unknown5:4;
+  u8 unknown4;
+  u8 tmode:4,
+     duplex:2,
+     dtcs_polarity:2;
+  char name[16];
+  u8 unknown13;
+  u8 urcall[7];
+  u8 rpt1call[7];
+  u8 rpt2call[7];
+} memory[500];
+
+#seekto 0x6A40;
+u8 used_flags[70];
+
+#seekto 0x6A86;
+u8 skip_flags[69];
+
+#seekto 0x6ACB;
+u8 pskp_flags[69];
+
+#seekto 0x6B40;
+struct {
+  u8 bank;
+  u8 index;
+} banks[500];
+
+#seekto 0x6FD0;
+struct {
+  char name[16];
+} bank_names[26];
+
+#seekto 0xA8C0;
+struct {
+  u24 freq;
+  u16 offset;
+  u8 unknown1[3];
+  u8 call[7];
+  char name[16];
+  char subname[8];
+  u8 unknown3[10];
+} repeaters[700];
+
+#seekto 0x1384E;
+struct {
+  u8 call[7];
+} rptcall[700];
+
+#seekto 0x14E60;
+struct {
+  char call[8];
+  char tag[4];
+} mycall[6];
+
+#seekto 0x14EA8;
+struct {
+  char call[8];
+} urcall[200];
+
+"""
+
+MODES = [ "FM", "NFM", "AM", "DV" ]
+MODE_INDEX = [ 0, 1, 3, 5 ]
+TMODES = ["", "Tone", "TSQL", "TSQL", "DTCS", "DTCS", "TSQL-R", "DTCS-R"]
+DUPLEX = ["", "-", "+"]
+DTCS_POLARITY = ["NN", "NR", "RN", "RR"]
+TUNING_STEPS = [5.0, 6.25, 0, 0, 10.0, 12.5, 15.0, 20.0, 25.0, 30.0, 50.0,
+                100.0, 125.0, 200.0]
+
+def _decode_call(_call):
+    # Why Icom, why?
+    call = ""
+    shift = 1
+    acc = 0
+    for val in _call:
+        mask = (1 << (shift)) - 1
+        call += chr((val >> shift) | acc)
+        acc = (val & mask) << (7 - shift)
+        shift += 1
+    call += chr(acc)
+    return call
+
+def _encode_call(call):
+    _call = [0x00] * 7
+    for i in range(0, 7):
+        val = ord(call[i]) << (i + 1)
+        if i > 0:
+            _call[i-1] |= (val & 0xFF00) >> 8
+        _call[i] = val
+    _call[6] |= (ord(call[7]) & 0x7F)
+
+    return _call
+
+def _get_freq(_mem):
+    freq = int(_mem.freq)
+    offs = int(_mem.offset)
+
+    if freq & 0x00200000:
+        mult = 6250
+    else:
+        mult = 5000
+
+    freq &= 0x0003FFFF
+
+    return (freq * mult), (offs * mult)
+
+def _set_freq(_mem, freq, offset):
+    if chirp_common.is_fractional_step(freq):
+        mult = 6250
+        flag = 0x00200000
+    else:
+        mult = 5000
+        flag = 0x00000000
+
+    _mem.freq = (freq / mult) | flag
+    _mem.offset = (offset / mult)
+
+class ID51Bank(icf.IcomBank):
+    """A ID-51 Bank"""
+    def get_name(self):
+        _banks = self._model._radio._memobj.bank_names
+        return str(_banks[self.index].name).rstrip()
+
+    def set_name(self, name):
+        _banks = self._model._radio._memobj.bank_names
+        _banks[self.index].name = str(name).ljust(16)[:16]
+
+@directory.register
+class ID51Radio(icf.IcomCloneModeRadio, chirp_common.IcomDstarSupport):
+    """Icom ID-51"""
+    MODEL = "ID-51A"
+
+    _memsize = 0x1FB40
+    _model = "\x33\x90\x00\x01"
+    _endframe = "Icom Inc\x2E\x44\x41"
+    _num_banks = 26
+    _bank_class = ID51Bank
+    _can_hispeed = True
+
+    _ranges = [(0x00000, 0x1FB40, 32)]
+
+    def _get_bank(self, loc):
+        _bank = self._memobj.banks[loc]
+        if _bank.bank == 0xFF:
+            return None
+        else:
+            return _bank.bank
+
+    def _set_bank(self, loc, bank):
+        _bank = self._memobj.banks[loc]
+        if bank is None:
+            _bank.bank = 0xFF
+        else:
+            _bank.bank = bank
+
+    def _get_bank_index(self, loc):
+        _bank = self._memobj.banks[loc]
+        return _bank.index
+
+    def _set_bank_index(self, loc, index):
+        _bank = self._memobj.banks[loc]
+        _bank.index = index
+
+    def get_features(self):
+        rf = chirp_common.RadioFeatures()
+        rf.memory_bounds = (0, 499)
+        rf.valid_bands = [(108000000, 174000000), (400000000, 479000000)]
+        rf.has_settings = True
+        rf.has_ctone = True
+        rf.has_bank_index = True
+        rf.has_bank_names = True
+        rf.valid_tmodes = list(TMODES)
+        rf.valid_tuning_steps = sorted(list(TUNING_STEPS))
+        rf.valid_modes = list(MODES)
+        rf.valid_skips = ["", "S", "P"]
+        rf.valid_characters = chirp_common.CHARSET_ASCII
+        rf.valid_name_length = 16
+        return rf
+
+    def process_mmap(self):
+        self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
+
+    def get_raw_memory(self, number):
+        return repr(self._memobj.memory[number])
+
+    def get_memory(self, number):
+        _mem = self._memobj.memory[number]
+        _usd = self._memobj.used_flags[number / 8]
+        _skp = self._memobj.skip_flags[number / 8]
+        _psk = self._memobj.pskp_flags[number / 8]
+
+        bit = (1 << (number % 8))
+
+        if MODES[MODE_INDEX.index(_mem.mode)] == "DV":
+            mem = chirp_common.DVMemory()
+        else:
+            mem = chirp_common.Memory()
+        mem.number = number
+
+        if _usd & bit:
+            mem.empty = True
+            return mem
+
+        mem.freq, mem.offset = _get_freq(_mem)
+        mem.name = str(_mem.name).rstrip()
+        mem.rtone = chirp_common.TONES[_mem.rtone]
+        mem.ctone = chirp_common.TONES[_mem.ctone]
+        mem.tmode = TMODES[_mem.tmode]
+        mem.duplex = DUPLEX[_mem.duplex]
+        mem.dtcs = chirp_common.DTCS_CODES[_mem.dtcs]
+        mem.dtcs_polarity = DTCS_POLARITY[_mem.dtcs_polarity]
+        mem.tuning_step = TUNING_STEPS[_mem.tune_step]
+        mem.mode = MODES[MODE_INDEX.index(_mem.mode)]
+
+        if mem.mode == "DV":
+            mem.dv_urcall = _decode_call(_mem.urcall).rstrip()
+            mem.dv_rpt1call = _decode_call(_mem.rpt1call).rstrip()
+            mem.dv_rpt2call = _decode_call(_mem.rpt2call).rstrip()
+
+        if _psk & bit:
+            mem.skip = "P"
+        if _skp & bit:
+            mem.skip = "S"
+
+        return mem
+
+    def set_memory(self, memory):
+        _mem = self._memobj.memory[memory.number]
+        _usd = self._memobj.used_flags[memory.number / 8]
+        _skp = self._memobj.skip_flags[memory.number / 8]
+        _psk = self._memobj.pskp_flags[memory.number / 8]
+
+        bit = (1 << (memory.number % 8))
+
+        if memory.empty:
+            _usd |= bit
+            self._set_bank(memory.number, None)
+            return
+
+        _usd &= ~bit
+
+        _set_freq(_mem, memory.freq, memory.offset)
+        _mem.name = memory.name.ljust(16)[:16]
+        _mem.rtone = chirp_common.TONES.index(memory.rtone)
+        _mem.ctone = chirp_common.TONES.index(memory.ctone)
+        _mem.tmode = TMODES.index(memory.tmode)
+        _mem.duplex = DUPLEX.index(memory.duplex)
+        _mem.dtcs = chirp_common.DTCS_CODES.index(memory.dtcs)
+        _mem.dtcs_polarity = DTCS_POLARITY.index(memory.dtcs_polarity)
+        _mem.tune_step = TUNING_STEPS.index(memory.tuning_step)
+        _mem.mode = MODE_INDEX[MODES.index(memory.mode)]
+
+        if isinstance(memory, chirp_common.DVMemory):
+            _mem.urcall = _encode_call(memory.dv_urcall.ljust(8))
+            _mem.rpt1call = _encode_call(memory.dv_rpt1call.ljust(8))
+            _mem.rpt2call = _encode_call(memory.dv_rpt2call.ljust(8))
+        elif memory.mode == "DV":
+            raise Exception("BUG")
+
+        if memory.skip == "S":
+            _skp |=  bit
+            _psk &= ~bit
+        elif memory.skip == "P":
+            _skp &= ~bit
+            _psk |=  bit
+        else:
+            _skp &= ~bit
+            _psk &= ~bit
+
+    def get_urcall_list(self):
+        calls = []
+        for i in range(0, 200):
+            call = str(self._memobj.urcall[i].call)
+            if call == "CALLSIGN":
+                call = ""
+            calls.append(call)
+        return calls
+
+    def get_mycall_list(self):
+        calls = []
+        for i in range(0, 6):
+            calls.append(str(self._memobj.mycall[i].call))
+        return calls
+
+    def get_repeater_call_list(self):
+        calls = []
+        for rptcall in self._memobj.rptcall:
+            call = _decode_call(rptcall.call)
+            if call.rstrip() and not call == "CALLSIGN":
+                calls.append(call)
+        for repeater in self._memobj.repeaters:
+            call = _decode_call(repeater.call)
+            if call == "CALLSIGN":
+                call = ""
+            calls.append(call.rstrip())
+        return calls