[chirp_devel] #553 Icom ID-51 support

Dean Gibson AE7Q
Tue Feb 19 13:29:22 PST 2013


Thanks for your reply!

On 2013-02-19 10:14, Tom Hayward wrote:
> ...
> A new file is a change to Chirp. After adding the new file, Chirp is
> not the same as it was before. But that's not the point here...

I understand that.  In CVS, to add a new file, I do "cvs add 
<filename".  I presume in Mercurial I do "hg add chirp\id51py" (which I 
did), which would be added to the repository by a commit. However, if I 
do the commit, Chirp's web site seems to imply that creates a fork that 
creates problems, so I haven't done that.


> ... "hg status -mar" does nothing to attribute the changes to you, or 
> label them with an appropriate commit message.

I understand that "hg status -mar" is the same as "cvs upd";  ie, just 
lists the files that have changed or been added, and will be committed 
on the next commit.


> Our goal here isn't just to add new lines of code, but to document who 
> added them and why, so that future readers of the code can ask "what 
> was the guy who wrote this thinking??" and have both the explanation 
> (commit message) and name/email of the guy who wrote it. If we find 
> that your change introduces a regression, we are also easily able to 
> search for commit messages containing "id?1" and revert each as needed 
> until the regression disappears.
>> hg diff:
> For this all to work, you must use a different command, "hg qnew ..."
> to create your patch. You will be prompted to enter a commit message,
> and when you export it ("hg export tip") your name and email address
> will be automatically be added to the header. The commands to
> accomplish this are all listed here:
>
> http://chirp.danplanet.com/projects/chirp/wiki/DevelopersProcess#Soft-committing-a-Change

So far, I've been sent that link THREE TIMES.  Each time I have read it 
IN DETAIL.  I don't want to start an argument here, BUT IT IS FAR FROM 
CLEAR.  The Mercurial website documentation is poorer;  I had to Google 
to find the format of the configuration file (named ".hg/hgrc", not 
".hg/config" as the Chirp website indicates).  Note that I have worked 
with several source control systems over the forty years I've been 
developing, so I have a bit of a clue.

EG:

 1. The first time I ran "hg export tip", it showed changes (by Dan) to
    files that I hadn't touched.  What's that about?
 2. Subsequent invocations return my changes to two files.  These two
    files need to be two separate patches (two separate bug #s).
 3. What the hell is "tip" ???
 4. OK, I did "hg qnew -l chirp\id51.py -ef <comment>". Then I removed
    it with "hg qpop".  Somewhere in these few steps my changes
    (including all of id51.py) were *deleted*.   Is that what you mean
    by "minimal learning curve" below? Fortunately (that means having
    some experience with the batch of new SCM packages out there), I had
    a complete backup elsewhere.
 5. I just now decided to have multiple backups of my changes. Perhaps
    I'll use CVS internally ...
 6. Another "hg qpop" also removed a "patch" named "help";  I'll let you
    guess as to how that got there (grin).


> When using "hg q.." commands, your changes are not permanent. You can
> send a work-in-progress patch (see Sending a Change), ask some
> questions, modify your code, then use "hg qref" to update the patch.
>
> A little bit of record keeping is necessary for managing a large
> codebase, and I think you'll find the learning curve for hg mq is
> minimal and saves you effort in the long run.
>
> Tom KD7LXL

*<rant>*

So far this has been all downhill.  I've spent *far more time* screwing 
around with the submission process, that I did to create id51.py (and 
make proposed changes to id31.py).  I don't have/need/want an ID-31A.  I 
have an ID-51A, and the free Icom CS-51 software is sufficient for my 
needs (including the import of some 250 memories via .CSV files).  
Frankly, it's less buggy as well.

I thought, "Chirp lacks ID-51 support, and I have one;  let's see if I 
can help".  That's my sole motivation.  Just as changing DStarCom to 
open source so that Chirp could use some of that code, was to help (see 
Dan's blog on my code).  The changes I have to id31.py are because 
(having cloned id51.py from it) I think there are errors in the memory 
layout, and it seems reasonable to help fix that as well.

We're all volunteers here, and I don't expect anyone to jump through 
hoops for me.  On the other hand, I'm retired, and have plenty of spare 
time to contribute to useful efforts.  Of course, I have MANY choices 
for how I spend that time (that includes being paid to fly small 
airplanes in wonderful weather), and screwing around with yet another 
inadequately documented tool is not high on my list.

I don't know if you have read the book "Psychology of Computer 
Programming" by Gerald Weinberg.  Written in 1972, it's considered a 
classic.  He reported that back then, someone was inventing a new 
computer language about once a week.  This industry is filled with 
people that "have to" create new ("vanity") languages or tools, with 
very marginal additional utility (or worse) between them.  Remember 
"BitKeeper"?  That was the SCM rage a couple years ago.  No one these 
days counts the productivity cost of learning new tools (for Microsoft, 
that means new versions of productive software, with the main change 
being useless changes to the GUI) that are not adequately documented.

*</rant>*

So, I'll try once more:

    hg qnew -l chirp\id51.py -ef "[id51] Add support for Icom ID-51"

I'm supposed to enter the bug # (553), but "qnew" won't take a "#" on 
the command line, and up pops a NotePad window with the changed file 
(??? I just closed it).  So, no info on that.  What follows is the 
output from the next command. I sure don't see my comment (above), and I 
sure thought that almost all of the lines would be prefixed by a "+".  
So,, my guess is that this will be unacceptable as well.  I'm sure not 
going to submit any changes to id31.py until I've sorted this out.

    hg export tip

# HG changeset patch
# User Dean Gibson <data at ae7q.net>
# Date 1361307603 28800
# Node ID b94ce8ef429fb5bf5750d96341c6d38354837b57
# Parent  5528bdcdc34e5966d5b7f0dbb6859f700b5f9366
# Copyright 2012 Dan Smith <dsmith at 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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://intrepid.danplanet.com/pipermail/chirp_devel/attachments/20130219/3a63cbb3/attachment-0001.html 


More information about the chirp_devel mailing list