[chirp_devel] New Driver not registering
Rick DeWitt
Tue Feb 20 08:04:19 PST 2018
I have written a driver for the Radio Shack PRO-649 scanner, but it does
not register in the directory.
I can force it by manually adding the 'rs649' to the __init__.py file,
and then it runs perfectly.
But why won't it register itself? It is in the \chirp\drivers folder on
my Win7 system.
Also, does anyone know how to invoke the 'show_instructions' function in
the mainapp? I want to provide some configuration hints. I don't find
any driver currently using this call.
--
Rick DeWitt
AA0RD
Sequim, Washington, USA
360-681-3494
-------------- next part --------------
# Copyright 2017
#
# Developed for the Radio Shack PRO-649 programmable 200-channel scanner by Rick DeWitt (AA0RD)
# AA0RD at yahoo.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 2 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/>.
import struct
import logging
import math
import binascii #Used for debugging outputs
LOG = logging.getLogger(__name__)
from chirp import chirp_common, directory, memmap
from chirp import bitwise, errors, util, platform
from chirp.settings import RadioSettingGroup, RadioSetting, \
RadioSettingValueBoolean, \
RadioSettingValueFloat,InvalidValueError, RadioSettings
from textwrap import dedent
MEM_FORMAT = """
#seekto 0x0;
struct {
ul24 rxfreq;
u8 unk7:1
unk6:1
unk5:1
unk4:1
delay:1
lockout:1
unk1:1
unk0:1;
} chans[200];
struct blok {
u8 byx[32];
};
#seekto 0x320; // Going to store names here
struct {
u8 id[8]; // 8 bytes per name gives 32-byte blocks of 4
} names[200]; // Uses 1600 bytes, Takes us up to 0x960
#seekto 0x960;
struct blok empty27[27]; // 27 empty 32-byte blocks
#seekto 0xCC0; // unknown data here
struct {
u8 unkx1[32];
} unkblk1;
#seekto 0xCE0;
struct blok empty9[9];
#seekto 0xE00; // Settings
struct {
u8 mtb0[27];
u8 unk7:1
unk6:1
unk5:1
unk4:1
pri_set:1
unk2:1
unk1:1
unk0:1;
ul24 pri_frq;
u8 ux7:1
ux6:1
ux5:1
ux4:1
pri_dly:1
ux2:1
ux1:1
ux0:1;
} settings;
#seekto 0xE20; // Bank enable map
struct {
ul16 bnk16:1
bnk15:1
bnk14:1
bnk13:1
bnk12:1
bnk11:1
bnk10:1
bnk9:1
bnk8:1
bnk7:1
bnk6:1
bnk5:1
bnk4:1
bnk3:1
bnk2:1
bnk1:1;
u8 x1[30];
} banks;
#seekto 0xE40;
struct blok x2; // all 0x30 ???
#seekto 0xE60; // more 0x30 and 00
struct blok x3;
#seekto 0xE80;
struct {
char mod_num[7];
} mod_id;
"""
MEM_SIZE = 0xE80
BLOCK_SIZE = 32 # 8 4-byte Chans; no 2-byte checksum
CHANS_BLOCK = 8
BLOCKS = 116
STIMEOUT = 2
BAUD_RATE = 4800
prix = 0 # Start with no priority channel assigned
def _clean_buffer(radio):
"""Empty the radio read buffer."""
radio.pipe.timeout = 0.005
LOG.debug("Cleaning buffer..")
junk = radio.pipe.read(256)
radio.pipe.timeout = STIMEOUT
if junk:
LOG.warning("Got %i bytes of junk before starting" % len(junk))
def do_download(radio):
"""Download Scanner Memory."""
radio.pipe.baudrate = BAUD_RATE
radio.pipe.timeout = STIMEOUT
radio.pipe.xonxof = True # For dump mode
# Get the serial port connection
serial = radio.pipe
_clean_buffer(radio)
# UI progress
status = chirp_common.Status()
status.cur = 0
status.max = BLOCKS
status.msg = "Downloading from Scanner Memory..."
radio.status_fn(status)
#Download data array
# ---- SO FAR: Does not read the dump ----!!!
data = ""
serial.write("\xec") # 'echo' dump
ack = serial.read(1) # cmd readback
LOG.warning("CfgAck: " + binascii.b2a_hex(ack))
for nb in range(0, BLOCKS):
serial.write("\x44") # Gimme a block
ack = serial.read(1)
LOG.warning("44Ack: " + binascii.b2a_hex(ack))
for nc in range(0, CHANS_BLOCK):
cnt = 1
chx = serial.read(cnt)
if len(chx) == 0:
msg = "Timeout reading data from scanner; check your cable."
raise errors.RadioError(msg)
if len(chx) != cnt:
msg = "Error reading data from scanner: not the amount of data expected."
raise errors.RadioError(msg)
data += chx
LOG.warning("chx:" + binascii.b2a_hex(chx))
# end for nc
cbx = serial.read(2) # get 2 checksum bytes and ignore
# UI Update after each 8-chan block
status.cur = BLOCKS
radio.status_fn(status)
# end for nb
# Append model code
data += "PRO-649"
return data
def do_upload(radio):
"""Upload memory to scanner."""
radio.pipe.baudrate = BAUD_RATE
radio.pipe.parity = "N"
radio.pipe.timeout = STIMEOUT
# Get the serial port connection
serial = radio.pipe
_clean_buffer(radio)
# UI progress
status = chirp_common.Status()
status.cur = 0
status.max = BLOCKS
status.msg = "Uploading to Scanner Memory..."
radio.status_fn(status)
# Send prefix
serial.write("\x55\xab\xcd\x05\x02")
ack = serial.read(5)
# LOG.warning("Ack: " + binascii.b2a_hex(ack))
# Send 116 35-byte blocks of 8 chan info and checksum
i = 0 # data/memory index
for nb in range(0, BLOCKS):
cksum = 0
serial.write("\x55")
ack = serial.read(1)
for nc in range(0, BLOCK_SIZE): # Write 32 bytes; 8 4-byte chans
chx = radio.get_mmap()[i] # returns 1-byte as char
serial.write(chx)
ack = serial.read(1)
cksum += ord(chx) # numeric value of char
# LOG.warning("DatAck: " + binascii.b2a_hex(ack) + " # " + str(cksum))
i += 1
cb1 = cksum % 256
cb2 =int( math.floor(cksum / 256))
serial.write(chr(cb1) + chr(cb2))
ack = serial.read(2)
# UI Update
status.cur = nb
radio.status_fn(status)
# next nb
class WS1010Alias(chirp_common.Alias):
"""PRO-649 alias for Whistler WS1010."""
VENDOR = "Whistler"
MODEL = "WS1010"
class PRS404Alias(chirp_common.Alias):
"""PRO-649 alias for Radio Shack PRO-404."""
VENDOR= "RadioShack"
MODEL = "PRO-404"
class PSR100Alias(chirp_common.Alias):
"""PRO-649 alias GRE PSR-100."""
VENDOR = "GRE"
MODEL = "PSR-100"
@directory.register
class PRO649(chirp_common.CloneModeRadio):
"""Radio Shack PRO-649 Scanner."""
VENDOR = "RadioShack"
MODEL = "PRO-649"
ALIASES = [WS1010Alias, PRS404Alias, PSR100Alias]
NAME_LENGTH = 7
@classmethod
def get_prompts(cls):
"""Define the upload and download info prompts."""
rp = chirp_common.RadioPrompts()
rp.pre_download = _(dedent("""\
SORRY! But the PRO649 scanner does not support
handshake downloading!
At least not with the standard dongle.
If you try it you will see 'Sending...'
but the interface will time out.
"""))
rp.pre_upload = _(dedent("""\
Follow these instructions to upload your info:
1 - Turn off your scanner
2 - Connect your interface cable
3 - Turn on your scanner
4 - Do the upload of your scanner data
5 - Turn off your scanner
6 - Unplug the interface cable.
"""))
return rp
# Attributes defined in chirp_common.py class RadioFeatures
def get_features(self):
"""Define valid radio features."""
rf = chirp_common.RadioFeatures()
rf.has_bank = False
rf.has_settings = True
rf.has_comment = False
rf.has_tuning_step = False
rf.can_odd_split = False
rf.has_name = True
rf.has_offset = False
rf.has_mode = True # Using for Delay on/off
rf.valid_modes = ["DV","Auto"]
rf.has_dtcs = False
rf.has_rx_dtcs = False
rf.has_dtcs_polarity = False
rf.has_ctone = False
rf.has_cross = False
rf.valid_name_length = self.NAME_LENGTH
rf.valid_duplexes = []
rf.valid_skips = ["", "S", "P"]
rf.memory_bounds = (1, 200) # This radio supports memories 1-200
rf.valid_bands = [(29000000, 54000000), # 10m, 6m, VHF-Low
(108000000, 136987500), # Aircraft
(133700000, 174000000), # 2m, Military,land-mobile, VHF-hi
(380000000, 512000000), # 70-centimeters, UH-Air, feds
]
return rf
# Do a download of the radio from the serial port
def sync_in(self):
"""Standard function call to initiate radio download."""
try:
data =do_download(self)
except errors.RadioError:
# Pass through any real errors we raise
raise
except:
# If anything unexpected happens, make sure we raise
# a RadioError and log the problem
LOG.exception('Unexpected error during download')
raise errors.RadioError('Unexpected error communicating '
'with the radio')
self._mmap = memmap.MemoryMap(data)
self.process_mmap()
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
# Do an upload of the radio to the serial port
def sync_out(self):
"""Standard function call to initiate radio upload."""
try:
do_upload(self)
except:
# If anything unexpected happens, make sure we raise
# a RadioError and log the problem
LOG.exception('Unexpected error during upload')
raise errors.RadioError('Unexpected error communicating '
'with the radio')
# This function supports the 'Show Raw Memory' developer function
# which is invoked from a UI row right-click pull-down
def get_raw_memory(self, number):
"""Standard function call to return selected object representation string."""
return repr(self._memobj.chans[number-1]) + repr(self._memobj.names[number-1])
def process_mmap(self):
"""Process the mem map into the mem object"""
self._memobj = bitwise.parse(MEM_FORMAT, self._mmap)
# Extract a high-level memory object from the low-level memory map
# This is called to populate a memory in the UI
def get_memory(self, number):
"""Standard function call to populate the UI rows."""
global prix
# Get a low-level memory object mapped to the image
_mem = self._memobj.chans[number-1] # chans array is base-0, number ("loc") is base 1
_nam = self._memobj.names[number-1]
_sets = self._memobj.settings
_prfrq = _sets.pri_frq * 1250.0
# Create a high-level memory object to return to the UI
mem = chirp_common.Memory()
mem.number = number
mem.freq = _mem.rxfreq * 1250.0 # 1000 * 12.5 step
for char in _nam.id:
if char != 0xff:
mem.name += chr(char)
mem.skip = ""
if ((_prfrq != 0.0) and (mem.freq == _prfrq)): # This is the priority scan channel
mem.skip = "P"
prix = number
if (_mem.lockout ): # T/F
mem.skip = "S"
mem.mode = "Auto" # Delay On
if (_mem.delay ):
mem.mode = "DV"
# We'll consider any blank (i.e. 0MHz frequency) to be empty
if mem.freq == 0:
mem.empty = True
mem.mode = "Auto"
mem.skip = "S"
mem.name = "None"
return mem
# Store details about a high-level memory to the memory map
# This is called when a user edits a memory in the UI
def set_memory(self, mem):
"""Standard function call to update raw memory from UI values."""
global prix
# Get a low-level memory object mapped to the image
_mem = self._memobj.chans[mem.number-1]
_sets = self._memobj.settings
_nam = self._memobj.names[mem.number-1]
# Convert to low-level frequency representation
_mem.rxfreq = int(mem.freq / 1250.0)
_namelength = self.get_features().valid_name_length
for i in range(_namelength):
try:
_nam.id[i] = ord(mem.name[i])
except IndexError:
_nam.id[i] = 0xFF
# Set Lockout and Delay (mode)
_mem.lockout = (mem.skip == "S")
_mem.delay = (mem.mode == "DV")
if (mem.skip == "P" ): # User has designated this chan as the Priority freq
setattr(_sets, "pri_frq", _mem.rxfreq) # only the last one sticks
prix = mem.number
if ((mem.number == prix) and (mem.skip != "P")): # Clear the priority freq
setattr(_sets, "pri_frq",0.0)
def get_settings(self):
"""Translate the bits in the mem_struct into settings in the UI"""
_sets = self._memobj.settings # define mem struct write-back shortcut
_bnks = self._memobj.banks
basic = RadioSettingGroup("basic", "Basic")
group = RadioSettings(basic)
def Htz2raw(setting, obj, atrb): # < Callback
"""Convert floating Freq in Mhz to UL24 integer."""
vr = float(str(setting.value)) # This function is not invoked now
value = vr * 1250.0
setattr(obj, atrb, value)
return
def dumfun(setting, obj, atrb):
"""Dummy call back, to make the setting read-only."""
return
rs = RadioSetting("banks.bnk1", "Bank 1",
RadioSettingValueBoolean((_bnks.bnk1 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk2", "Bank 2",
RadioSettingValueBoolean((_bnks.bnk2 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk3", "Bank 3",
RadioSettingValueBoolean((_bnks.bnk3 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk4", "Bank 4",
RadioSettingValueBoolean((_bnks.bnk4 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk5", "Bank 5",
RadioSettingValueBoolean((_bnks.bnk5 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk6", "Bank 6",
RadioSettingValueBoolean((_bnks.bnk6 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk7", "Bank 7",
RadioSettingValueBoolean((_bnks.bnk7 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk8", "Bank 8",
RadioSettingValueBoolean((_bnks.bnk8 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk9", "Bank 9",
RadioSettingValueBoolean((_bnks.bnk9 == 1)))
basic.append(rs)
rs = RadioSetting("banks.bnk10", "Bank 10",
RadioSettingValueBoolean((_bnks.bnk10 == 1)))
basic.append(rs)
rs = RadioSetting("settings.pri_set", "Priority Scan",
RadioSettingValueBoolean((_sets.pri_set == 1)))
basic.append(rs)
rs = RadioSetting("settings.pri_dly", "Priority Scan Delay",
RadioSettingValueBoolean((_sets.pri_dly == 1)))
basic.append(rs)
shopri = False
if (shopri) : # Only for dev/debug, otherwise confuses user
val = _sets.pri_frq / 800.0 # display pri freq as read-only
rs = RadioSetting("settings.pri_frq", "Priority Scan Freq (MHz)",
RadioSettingValueFloat(0.0, 480.0,val, 0.001,3))
rs.set_apply_callback(dumfun, _sets,"pri_frq")
basic.append(rs)
return group # END get_settings()
def set_settings(self, settings):
"""Copy UI settings back into raw memory."""
_settings = self._memobj.settings
_mem = self._memobj
for element in settings:
if not isinstance(element, RadioSetting):
self.set_settings(element)
continue
else:
try:
name = element.get_name()
if "." in name:
bits = name.split(".")
obj = self._memobj
for bit in bits[:-1]:
if "/" in bit:
bit, index = bit.split("/", 1)
index = int(index)
obj = getattr(obj, bit)[index]
else:
obj = getattr(obj, bit)
setting = bits[-1]
else:
obj = _settings
setting = element.get_name()
if element.has_apply_callback():
LOG.debug("Using apply callback")
element.run_apply_callback()
elif element.value.get_mutable():
LOG.debug("Setting %s = %s" % (setting, element.value))
setattr(obj, setting, element.value)
except Exception, e:
LOG.debug(element.get_name())
raise
@classmethod
def match_model(cls, filedata, filename):
"""Test img memory belongs to this driver."""
match_size = False
match_model = False
# testing the file data size
if len(filedata) == MEM_SIZE + 7: # +'PRO-649'
match_size = True
# testing the firmware model fingerprint
rid = filedata[MEM_SIZE:MEM_SIZE+7] # 'PRO-649' should be appended
if rid == cls.MODEL:
match_model = True
if match_size and match_model:
return True
else:
return False
More information about the chirp_devel
mailing list