aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorMatthew Poletiek <matthew.poletiek@gmail.com>2020-12-08 21:03:16 -0600
committerMatthew Poletiek <matthew.poletiek@gmail.com>2020-12-08 21:03:16 -0600
commite99416456afd4aa8bde42016826f9a345291cbf3 (patch)
treea7a95639cd1cb5dbe2d91a2ca8e8defafac4296d /tools
parent194cf4e5e0b6a2811103a9b739a72b9afe2b886c (diff)
downloadchirp-e99416456afd4aa8bde42016826f9a345291cbf3.tar.gz
chirp-e99416456afd4aa8bde42016826f9a345291cbf3.tar.xz
Initial Commit
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile35
-rw-r--r--tools/bitdiff.py166
-rwxr-xr-xtools/check_for_bug.sh7
-rwxr-xr-xtools/checkpatch.sh72
-rw-r--r--tools/cpep8.blacklist3
-rw-r--r--tools/cpep8.exceptions10
-rw-r--r--tools/cpep8.manifest152
-rwxr-xr-xtools/cpep8.py137
-rwxr-xr-xtools/icomsio.sh95
-rw-r--r--tools/img2thd72.py127
-rw-r--r--tools/serialsniff.c440
11 files changed, 1244 insertions, 0 deletions
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..a9f08af
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,35 @@
+#
+#
+# Makefile for serialsniff
+#
+# Author: IT2 Stuart Blake Tener, USNR (N3GWG)
+#
+# Version 1.00
+#
+# This makefile checks to see if we are running under the OS "Darwin"
+# and passes the proper macro flag ("MACOS") if we are.
+#
+# Incidentally, it is noteworthy that MacOS (Darwin) is not the only
+# operating system which is deficient some of the library routines
+# MacOS is currently defficient. Thus, it is instructive that other
+# operating systems names might need to be added for compilation to
+# become occurring in their environments.
+#
+#
+
+ifndef SYSNAME
+ SYSNAME := $(shell uname -s)
+ ifeq ($(SYSNAME),Darwin)
+ MYFLAGS := "-DMACOS"
+ REMOVE := srm -vrfz
+ else
+ REMOVE := rm -rf
+ endif
+endif
+
+serialsniff: serialsniff.c
+ $(CC) $? -o $@ $(LDFLAGS) $(CFLAGS) $(MYFLAGS)
+
+clean:
+ $(REMOVE) serialsniff *~ *.o *.bak core tags shar a.out
+
diff --git a/tools/bitdiff.py b/tools/bitdiff.py
new file mode 100644
index 0000000..d06bdb2
--- /dev/null
+++ b/tools/bitdiff.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 Jens Jensen AF5MI <kd4tjx@yahoo.com>
+
+import sys
+import os
+import argparse
+import time
+
+
+def printDiff(pos, byte1, byte2, args):
+ bits1 = '{0:08b}'.format(byte1)
+ bits2 = '{0:08b}'.format(byte2)
+ print "@%04Xh" % pos
+ print "1:%02Xh, %sb" % (byte1, bits1)
+ print "2:%02Xh, %sb" % (byte2, bits2)
+ if args.csv:
+ writeDiffCSV(pos, byte1, byte2, args)
+
+
+def writeDiffCSV(pos, byte1, byte2, args):
+ bits1 = '{0:08b}'.format(byte1)
+ bits2 = '{0:08b}'.format(byte2)
+ csvline = '%s, %s, %04X, %02X, %s, %02X, %s, %s, %s' % \
+ (args.file1, args.file2, pos, byte1, bits1,
+ byte2, bits2, args.setting, args.value)
+ if not os.path.isfile(args.csv):
+ fh = open(args.csv, "w")
+ header = "filename1, filename2, byte_offset, byte1, " \
+ "bits1, byte2, bits2, item_msg, value_msg"
+ fh.write(header + os.linesep)
+ else:
+ fh = open(args.csv, "a")
+ fh.write(csvline + os.linesep)
+ fh.close()
+
+
+def compareFiles(args):
+ f1 = open(args.file1, "rb")
+ f1.seek(args.offset)
+ f2 = open(args.file2, "rb")
+ f2.seek(args.offset)
+
+ while True:
+ pos = f1.tell() - args.offset
+ c1 = f1.read(1)
+ c2 = f2.read(1)
+ if not (c1 and c2):
+ break
+ b1 = ord(c1)
+ b2 = ord(c2)
+ if b1 != b2:
+ printDiff(pos, b1, b2, args)
+
+ pos = f1.tell() - args.offset
+ print "bytes read: %02d" % pos
+ f1.close()
+ f2.close()
+
+
+def compareFilesDat(args):
+ f1 = open(args.file1, "r")
+ f1contents = f1.read()
+ f1.close()
+ f2 = open(args.file2, "r")
+ f2contents = f2.read()
+ f2.close()
+
+ f1strlist = f1contents.split()
+ f1intlist = map(int, f1strlist)
+ f2strlist = f2contents.split()
+ f2intlist = map(int, f2strlist)
+ f1bytes = bytearray(f1intlist)
+ f2bytes = bytearray(f2intlist)
+
+ length = len(f1intlist)
+ for i in range(length):
+ b1 = f1bytes[i]
+ b2 = f2bytes[i]
+ pos = i
+ if b1 != b2:
+ printDiff(pos, b1, b2, args)
+
+ pos = length
+ print "bytes read: %02d" % pos
+
+
+def convertFileToBin(args):
+ f1 = open(args.file1, "r")
+ f1contents = f1.read()
+ f1.close()
+ f1strlist = f1contents.split()
+ f1intlist = map(int, f1strlist)
+ f1bytes = bytearray(f1intlist)
+ f2 = open(args.file2, "wb")
+ f2.write(f1bytes)
+ f2.close
+
+
+def convertFileToDat(args):
+ f1 = open(args.file1, "rb")
+ f1contents = f1.read()
+ f1.close()
+ f2 = open(args.file2, "w")
+ for i in range(0, len(f1contents)):
+ f2.write(" %d " % (ord(f1contents[i]), ))
+ if i % 16 == 15:
+ f2.write("\r\n")
+ f2.close
+
+
+# main
+
+ap = argparse.ArgumentParser(description="byte-/bit- comparison of two files")
+ap.add_argument("file1", help="first (reference) file to parse")
+ap.add_argument("file2", help="second file to parse")
+
+mutexgrp1 = ap.add_mutually_exclusive_group()
+mutexgrp1.add_argument("-o", "--offset", default=0,
+ help="offset (hex) to start comparison")
+mutexgrp1.add_argument("-d", "--dat", action="store_true",
+ help="process input files from .DAT/.ADJ format "
+ "(from 'jujumao' oem programming software "
+ "for chinese radios)")
+mutexgrp1.add_argument("--convert2bin", action="store_true",
+ help="convert file1 from .dat/.adj to "
+ "binary image file2")
+mutexgrp1.add_argument("--convert2dat", action="store_true",
+ help="convert file1 from bin to .dat/.adj file2")
+
+ap.add_argument("-w", "--watch", action="store_true",
+ help="'watch' changes. runs in a loop")
+
+csvgrp = ap.add_argument_group("csv output")
+csvgrp.add_argument("-c", "--csv",
+ help="file to append csv results. format: filename1, "
+ "filename2, byte_offset, byte1, bits1, byte2, "
+ "bits2, item_msg, value_msg")
+csvgrp.add_argument("-s", "--setting",
+ help="user-meaningful field indicating setting/item "
+ "modified, e.g. 'beep' or 'txtone'")
+csvgrp.add_argument("-v", "--value",
+ help="user-meaningful field indicating values "
+ "changed, e.g. 'true->false' or '110.9->100.0'")
+
+args = ap.parse_args()
+if args.offset:
+ args.offset = int(args.offset, 16)
+
+print "f1:", args.file1, " f2:", args.file2
+if args.setting or args.value:
+ print "setting:", args.setting, "- value:", args.value
+
+while True:
+ if (args.dat):
+ compareFilesDat(args)
+ elif (args.convert2bin):
+ convertFileToBin(args)
+ elif (args.convert2dat):
+ convertFileToDat(args)
+ else:
+ compareFiles(args)
+ if not args.watch:
+ break
+ print "------"
+ time.sleep(delay)
diff --git a/tools/check_for_bug.sh b/tools/check_for_bug.sh
new file mode 100755
index 0000000..c8b042d
--- /dev/null
+++ b/tools/check_for_bug.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env sh
+
+# This checks a given revision to make sure that it has a bug number
+# in it, of the form "#123". It should be used in your pretxncommit
+# hook
+
+hg log -r $1 --template {desc} | egrep -q "\#[0-9]+"
diff --git a/tools/checkpatch.sh b/tools/checkpatch.sh
new file mode 100755
index 0000000..5cf3a6f
--- /dev/null
+++ b/tools/checkpatch.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+#
+# CHIRP coding standards compliance script
+#
+# To add a test to this file, create a new check_foo() function
+# and then add it to the list of TESTS= below
+#
+
+TESTS="check_long_lines check_bug_number check_commit_message_line_length"
+
+function check_long_lines() {
+ local rev="$1"
+ local files="$2"
+
+ # For now, ignore this check on chirp/
+ files=$(echo $files | sed -r 's#\bchirp[^ ]*\b##')
+
+ if [ -z "$files" ]; then
+ return
+ fi
+
+ pep8 --select=E501 $files || \
+ error "Please use <80 columns in source files"
+}
+
+function check_bug_number() {
+ local rev="$1"
+ hg log -vr $rev | grep -qE '#[0-9]+' || \
+ error "A bug number is required like #123"
+}
+
+function _less_than_80() {
+ while true; do
+ read line
+ if [ -z "$line" ]; then
+ break
+ elif [ $(echo -n "$line" | wc -c) -ge 80 ]; then
+ return 1
+ fi
+ done
+}
+
+function check_commit_message_line_length() {
+ local rev="$1"
+ hg log -vr $rev | (_less_than_80) || \
+ error "Please keep commit message lines to <80 columns"
+}
+
+# --- END OF TEST FUNCTIONS ---
+
+function error() {
+ echo FAIL: $*
+ ERROR=1
+}
+
+function get_touched_files() {
+ local rev="$1"
+ hg status -n --change $rev | grep '\.py$'
+}
+
+rev=${1:-tip}
+files=$(get_touched_files $rev)
+
+for testname in $TESTS; do
+ eval "$testname $rev \"$files\""
+done
+
+if [ -z "$ERROR" ]; then
+ echo "Patch '${rev}' is OK"
+else
+ exit 1
+fi
diff --git a/tools/cpep8.blacklist b/tools/cpep8.blacklist
new file mode 100644
index 0000000..6a98e72
--- /dev/null
+++ b/tools/cpep8.blacklist
@@ -0,0 +1,3 @@
+# cpep8.blacklist: The list of files that do not meet PEP8 standards.
+# DO NOT ADD NEW FILES!! Instead, fix the code to be compliant.
+# Over time, this list should shrink and (eventually) be eliminated.
diff --git a/tools/cpep8.exceptions b/tools/cpep8.exceptions
new file mode 100644
index 0000000..4efef30
--- /dev/null
+++ b/tools/cpep8.exceptions
@@ -0,0 +1,10 @@
+# This file contains a list of per-file exceptions to the pep8 style rules
+# Each line must contain the file name, a tab, and a comma-separated list
+# of style rules to ignore in that file. This mechanism should be used
+# sparingly and as a measure of last resort.
+./share/make_supported.py E402
+./tests/run_tests E402
+./tests/unit/test_memedit_edits.py E402
+./chirpw E402
+./chirp/memmap.py E731
+./chirp/util.py E731
diff --git a/tools/cpep8.manifest b/tools/cpep8.manifest
new file mode 100644
index 0000000..368c6d2
--- /dev/null
+++ b/tools/cpep8.manifest
@@ -0,0 +1,152 @@
+./chirp/__init__.py
+./chirp/bandplan.py
+./chirp/bandplan_au.py
+./chirp/bandplan_iaru_r1.py
+./chirp/bandplan_iaru_r2.py
+./chirp/bandplan_iaru_r3.py
+./chirp/bandplan_na.py
+./chirp/bitwise.py
+./chirp/bitwise_grammar.py
+./chirp/chirp_common.py
+./chirp/detect.py
+./chirp/directory.py
+./chirp/drivers/__init__.py
+./chirp/drivers/alinco.py
+./chirp/drivers/anytone.py
+./chirp/drivers/ap510.py
+./chirp/drivers/baofeng_uv3r.py
+./chirp/drivers/baofeng_wp970i.py
+./chirp/drivers/bjuv55.py
+./chirp/drivers/btech.py
+./chirp/drivers/ft1500m.py
+./chirp/drivers/ft1802.py
+./chirp/drivers/ft1d.py
+./chirp/drivers/ft2800.py
+./chirp/drivers/ft2900.py
+./chirp/drivers/ft4.py
+./chirp/drivers/ft50.py
+./chirp/drivers/ft60.py
+./chirp/drivers/ft7100.py
+./chirp/drivers/ft7800.py
+./chirp/drivers/ft817.py
+./chirp/drivers/ft818.py
+./chirp/drivers/ft857.py
+./chirp/drivers/ft90.py
+./chirp/drivers/ftm350.py
+./chirp/drivers/generic_csv.py
+./chirp/drivers/generic_tpe.py
+./chirp/drivers/h777.py
+./chirp/drivers/ic208.py
+./chirp/drivers/ic2100.py
+./chirp/drivers/ic2200.py
+./chirp/drivers/ic2720.py
+./chirp/drivers/ic2730.py
+./chirp/drivers/ic2820.py
+./chirp/drivers/ic9x.py
+./chirp/drivers/ic9x_icf.py
+./chirp/drivers/ic9x_icf_ll.py
+./chirp/drivers/ic9x_ll.py
+./chirp/drivers/icf.py
+./chirp/drivers/icomciv.py
+./chirp/drivers/icq7.py
+./chirp/drivers/ict70.py
+./chirp/drivers/ict7h.py
+./chirp/drivers/ict8.py
+./chirp/drivers/icw32.py
+./chirp/drivers/icx8x.py
+./chirp/drivers/icx8x_ll.py
+./chirp/drivers/id31.py
+./chirp/drivers/id51.py
+./chirp/drivers/id800.py
+./chirp/drivers/id880.py
+./chirp/drivers/idrp.py
+./chirp/drivers/kenwood_hmk.py
+./chirp/drivers/kenwood_itm.py
+./chirp/drivers/kenwood_live.py
+./chirp/drivers/kguv8d.py
+./chirp/drivers/kguv9dplus.py
+./chirp/drivers/kyd.py
+./chirp/drivers/leixen.py
+./chirp/drivers/puxing.py
+./chirp/drivers/radioddity_r2.py
+./chirp/drivers/rfinder.py
+./chirp/drivers/template.py
+./chirp/drivers/th350.py
+./chirp/drivers/th9800.py
+./chirp/drivers/th_uv3r.py
+./chirp/drivers/th_uv3r25.py
+./chirp/drivers/th_uv8000.py
+./chirp/drivers/th_uvf8d.py
+./chirp/drivers/thd72.py
+./chirp/drivers/thuv1f.py
+./chirp/drivers/tk8102.py
+./chirp/drivers/tk8180.py
+./chirp/drivers/tmv71.py
+./chirp/drivers/tmv71_ll.py
+./chirp/drivers/ts480.py
+./chirp/drivers/ts590.py
+./chirp/drivers/uv5r.py
+./chirp/drivers/uvb5.py
+./chirp/drivers/vx170.py
+./chirp/drivers/vx2.py
+./chirp/drivers/vx3.py
+./chirp/drivers/vx5.py
+./chirp/drivers/vx510.py
+./chirp/drivers/vx6.py
+./chirp/drivers/vx7.py
+./chirp/drivers/vx8.py
+./chirp/drivers/vxa700.py
+./chirp/drivers/wouxun.py
+./chirp/drivers/wouxun_common.py
+./chirp/drivers/yaesu_clone.py
+./chirp/elib_intl.py
+./chirp/errors.py
+./chirp/import_logic.py
+./chirp/logger.py
+./chirp/memmap.py
+./chirp/platform.py
+./chirp/pyPEG.py
+./chirp/radioreference.py
+./chirp/settings.py
+./chirp/ui/__init__.py
+./chirp/ui/bandplans.py
+./chirp/ui/bankedit.py
+./chirp/ui/clone.py
+./chirp/ui/cloneprog.py
+./chirp/ui/common.py
+./chirp/ui/config.py
+./chirp/ui/dstaredit.py
+./chirp/ui/editorset.py
+./chirp/ui/fips.py
+./chirp/ui/importdialog.py
+./chirp/ui/inputdialog.py
+./chirp/ui/mainapp.py
+./chirp/ui/memdetail.py
+./chirp/ui/memedit.py
+./chirp/ui/miscwidgets.py
+./chirp/ui/radiobrowser.py
+./chirp/ui/reporting.py
+./chirp/ui/settingsedit.py
+./chirp/ui/shiftdialog.py
+./chirp/util.py
+./chirpc
+./chirpw
+./locale/check_parameters.py
+./rpttool
+./setup.py
+./share/make_supported.py
+./tests/__init__.py
+./tests/run_tests
+./tests/unit/__init__.py
+./tests/unit/base.py
+./tests/unit/test_bitwise.py
+./tests/unit/test_chirp_common.py
+./tests/unit/test_import_logic.py
+./tests/unit/test_mappingmodel.py
+./tests/unit/test_memedit_edits.py
+./tests/unit/test_platform.py
+./tests/unit/test_settings.py
+./tests/unit/test_shiftdialog.py
+./tools/bitdiff.py
+./tools/cpep8.py
+./tools/img2thd72.py
diff --git a/tools/cpep8.py b/tools/cpep8.py
new file mode 100755
index 0000000..2bf55d0
--- /dev/null
+++ b/tools/cpep8.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+#
+# cpep8.py - Check Python source files for PEP8 compliance.
+#
+# Copyright 2015 Zachary T Welch <zach@mandolincreekfarm.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/>.
+
+import os
+import sys
+import logging
+import argparse
+import pep8
+
+parser = argparse.ArgumentParser()
+parser.add_argument("-a", "--all", action="store_true",
+ help="Check all files, ignoring blacklist")
+parser.add_argument("-d", "--dir", action="store", default=".",
+ help="Root directory of source tree")
+parser.add_argument("-s", "--stats", action="store_true",
+ help="Only show statistics")
+parser.add_argument("--strict", action="store_true",
+ help="Ignore listed exceptions")
+parser.add_argument("-S", "--scan", action="store_true",
+ help="Scan for additional files")
+parser.add_argument("-u", "--update", action="store_true",
+ help="Update manifest/blacklist files")
+parser.add_argument("-v", "--verbose", action="store_true",
+ help="Display list of checked files")
+parser.add_argument("files", metavar="file", nargs='*',
+ help="List of files to check (if none, check all)")
+args = parser.parse_args()
+
+
+def file_to_lines(name):
+ fh = file(name, "r")
+ lines = fh.read().split("\n")
+ lines.pop()
+ fh.close()
+ return lines
+
+
+scriptdir = os.path.dirname(sys.argv[0])
+manifest_filename = os.path.join(scriptdir, "cpep8.manifest")
+blacklist_filename = os.path.join(scriptdir, "cpep8.blacklist")
+exceptions_filename = os.path.join(scriptdir, "cpep8.exceptions")
+
+manifest = []
+if args.scan:
+ for root, dirs, files in os.walk(args.dir):
+ for f in files:
+ filename = os.path.join(root, f)
+ if f.endswith('.py'):
+ manifest.append(filename)
+ continue
+ with file(filename, "r") as fh:
+ shebang = fh.readline()
+ if shebang.startswith("#!/usr/bin/env python"):
+ manifest.append(filename)
+else:
+ manifest += file_to_lines(manifest_filename)
+
+
+# unless we are being --strict, load per-file style exceptions
+exceptions = {}
+if not args.strict:
+ exception_lines = file_to_lines(exceptions_filename)
+ exception_lists = [x.split('\t')
+ for x in exception_lines if not x.startswith('#')]
+ for filename, codes in exception_lists:
+ exceptions[filename] = codes
+
+
+def get_exceptions(f):
+ try:
+ ignore = exceptions[f]
+ except KeyError:
+ ignore = None
+ return ignore
+
+if args.update:
+ print "Starting update of %d files" % len(manifest)
+ bad = []
+ for f in manifest:
+ checker = pep8.StyleGuide(quiet=True, ignore=get_exceptions(f))
+ results = checker.check_files([f])
+ if results.total_errors:
+ bad.append(f)
+ print "%s: %s" % (results.total_errors and "FAIL" or "PASS", f)
+
+ with file(blacklist_filename, "w") as fh:
+ print >>fh, """\
+# cpep8.blacklist: The list of files that do not meet PEP8 standards.
+# DO NOT ADD NEW FILES!! Instead, fix the code to be compliant.
+# Over time, this list should shrink and (eventually) be eliminated."""
+ print >>fh, "\n".join(sorted(bad))
+
+ if args.scan:
+ with file(manifest_filename, "w") as fh:
+ print >>fh, "\n".join(sorted(manifest))
+ sys.exit(0)
+
+if args.files:
+ manifest = args.files
+
+# read the blacklisted source files
+blacklist = file_to_lines(blacklist_filename)
+
+check_list = []
+for f in manifest:
+ if args.all or f not in blacklist:
+ check_list.append(f)
+check_list = sorted(check_list)
+
+total_errors = 0
+for f in check_list:
+ if args.verbose:
+ print "Checking %s" % f
+
+ checker = pep8.Checker(f, quiet=args.stats, ignore=get_exceptions(f))
+ results = checker.check_all()
+ if args.stats:
+ checker.report.print_statistics()
+ total_errors += results
+
+sys.exit(total_errors and 1 or 0)
diff --git a/tools/icomsio.sh b/tools/icomsio.sh
new file mode 100755
index 0000000..f5f126d
--- /dev/null
+++ b/tools/icomsio.sh
@@ -0,0 +1,95 @@
+#!/usr/bin/env bash
+#
+# ICOM ID-RP* serial helper script
+#
+# Copyright 2009 Dan Smith <dsmith@danplanet.com>
+#
+# This script will scan the USB bus on this system and determine
+# the product ID of any attached ICOM repeater modules. It will
+# unload and then reload the FTDI serial driver with the proper
+# options to detect the device. After that, it will determine the
+# device name and link /dev/icom to that device for easy access.
+
+LINK="icom"
+VENDOR="0x0c26"
+DEVICE=$(lsusb -d ${VENDOR}: | cut -d ' ' -f 6 | cut -d : -f 2 | sed -r 's/\n/ /g')
+
+product_to_name() {
+ local prod=$1
+
+ if [ "$prod" = "0012" ]; then
+ echo "ID-RP2000V TX"
+ elif [ "$prod" = "0013" ]; then
+ echo "ID-RP2000V RX"
+ elif [ "$prod" = "0010" ]; then
+ echo "ID-RP4000V TX"
+ elif [ "$prod" = "0011" ]; then
+ echo "ID-RP4000V RX"
+ elif [ "$prod" = "000b" ]; then
+ echo "ID-RP2D"
+ elif [ "$prod" = "000c" ]; then
+ echo "ID-RP2V TX"
+ elif [ "$prod" = "000d" ]; then
+ echo "ID-RP2V RX"
+ else
+ echo "Unknown module (id=${prod})"
+ fi
+}
+
+if [ $(id -u) != 0 ]; then
+ echo "This script must be run as root"
+ exit 1
+fi
+
+if [ -z "$DEVICE" ]; then
+ echo "No devices found"
+ exit 1
+fi
+
+if echo $DEVICE | grep -q ' '; then
+ echo "Multiple devices found. Choose one:"
+ i=0
+ for dev in $DEVICE; do
+ name=$(product_to_name $dev)
+ echo " ${i}: ${name}"
+ i=$(($i + 1))
+ done
+
+ echo -n "> "
+ read num
+
+ array=($DEVICE)
+
+ DEVICE=${array[$num]}
+ if [ -z "$DEVICE" ]; then
+ exit
+ fi
+fi
+
+modprobe -r ftdi_sio || {
+ echo "Unable to unload ftdi_sio"
+ exit 1
+}
+
+modprobe ftdi_sio vendor=${VENDOR} product=0x${DEVICE} || {
+ echo "Failed to load ftdi_sio"
+ exit 1
+}
+
+sleep 0.5
+
+info=$(lsusb -d ${VENDOR}:0x${DEVICE})
+bus=$(echo $info | cut -d ' ' -f 2 | sed 's/^0*//')
+dev=$(echo $info | cut -d ' ' -f 4 | sed 's/^0*//')
+
+for usbserial in /sys/class/tty/ttyUSB*; do
+ driver=$(basename $(readlink -f ${usbserial}/device/driver))
+ device=$(basename $usbserial)
+ if [ "$driver" = "ftdi_sio" ]; then
+ name=$(product_to_name $DEVICE)
+ ln -sf /dev/${device} /dev/${LINK}
+ echo "Device $name is /dev/${device} -> /dev/${LINK}"
+ break
+ fi
+done
+
diff --git a/tools/img2thd72.py b/tools/img2thd72.py
new file mode 100644
index 0000000..407bdf7
--- /dev/null
+++ b/tools/img2thd72.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+# coding=utf-8
+# ex: set tabstop=4 expandtab shiftwidth=4 softtabstop=4:
+#
+# © Copyright Vernon Mauery <vernon@mauery.com>, 2010. All Rights Reserved
+#
+# This is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or (at
+# your option) any later version.
+#
+# This sofware 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 Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this software. If not, see <http://www.gnu.org/licenses/>.
+
+
+import sys
+import getopt
+from PIL import Image as im
+
+
+def die(msg):
+ print msg
+ sys.exit(1)
+
+
+def thd72bitmap(fname, invert):
+ img = im.open(ifname)
+ if img.size != (120, 48):
+ die("Image has wrong dimensions: must be 120x48")
+
+ colors = img.getcolors()
+ if len(colors) != 2:
+ die("Image must be 1 bits per pixel (black and white)")
+
+ if ('-i', '') in opts:
+ c, black = colors[0]
+ c, white = colors[1]
+ else:
+ c, white = colors[0]
+ c, black = colors[1]
+
+ colors = {black: 1, white: 0}
+ data = img.getdata()
+ buf = ''
+ for y in range(6):
+ for x in range(120):
+ b = 0
+ for i in range(8):
+ b |= colors[data[x + 120 * (y * 8 + i)]] << i
+ buf += chr(b)
+ return buf
+
+
+def display_thd72(buf):
+ dots = {0: '*', 1: ' '}
+ lines = []
+ for y in range(48):
+ line = ''
+ for x in range(120):
+ byte = y/8*120 + x
+ line += dots[(ord(buf[byte]) >> (y % 8)) & 0x01]
+ lines.append(line)
+ for l in lines:
+ print l
+
+
+def usage():
+ print "\nUsage: %s <-s|-g> [-i] [-d] " \
+ "<image-file> <thd72-nvram-file>" % sys.argv[0]
+ print "\nThis program will modify whatever nvram file provided or will"
+ print "create a new one if the file does not exist. After using this to"
+ print "modify the image, you can use that file to upload all or part of"
+ print "it to your radio"
+ print "\nOption explanations:"
+ print " -s Save as the startup image"
+ print " -g Save as the GPS logger image"
+ print " -i Invert colors (black for white)"
+ print " Depending on the file format the bits may be inverted."
+ print " If your bitmap file turns out to be inverted, use -i."
+ print " -d Display the bitmap as dots (for confirmation)"
+ print " Each black pixel is '*' and each white pixel is ' '"
+ print " This will print up to 120 dots wide, so beware your"
+ print " terminal size."
+ sys.exit(1)
+
+if __name__ == "__main__":
+ opts, args = getopt.getopt(sys.argv[1:], "idgs")
+ if len(args) != 2:
+ usage()
+ ifname = args[0]
+ ofname = args[1]
+ invert = ('-i', '') in opts
+ gps = ('-g', '') in opts
+ startup = ('-s', '') in opts
+ if (gps and startup) or not (gps or startup):
+ usage()
+ if gps:
+ imgpos = 0xe800
+ tagpos = 18
+ else:
+ imgpos = 0xe500
+ tagpos = 17
+
+ buf = thd72bitmap(ifname, invert)
+ imgfname = ifname + '\xff' * (48-len(ifname))
+ of = file(ofname, "rb+")
+ of.seek(tagpos)
+ of.write('\x01')
+ of.seek(imgpos)
+ of.write(buf)
+ of.write(imgfname)
+ of.seek(65536)
+ of.close()
+
+ if ('-d', '') in opts:
+ display_thd72(buf)
+
+ blocks = [0, ]
+ blocks.append(imgpos/256)
+ blocks.append(1+imgpos/256)
+ blocks.append(2+imgpos/256)
+ print "Modified block list:", blocks
diff --git a/tools/serialsniff.c b/tools/serialsniff.c
new file mode 100644
index 0000000..7a80ac4
--- /dev/null
+++ b/tools/serialsniff.c
@@ -0,0 +1,440 @@
+/*
+ *
+ * Copyright 2008 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/>.
+ */
+
+/*
+ *
+ * Modifications made by:
+ * Stuart Blake Tener, N3GWG
+ * Email: <teners@bh90210.net>
+ * Mobile phone: +1 (310) 358-0202
+ *
+ * 02 MAR 2009 - Version 1.01
+ *
+ * Logic was changed to use "ptsname" instead of "ptsname_r"
+ * in pursuance of provisioning greater compatibility with other
+ * Unix variants and Open Standards Unix flavors which have not
+ * otherwise implemented the "ptsname_r" system call.
+ * Changes developed and tested under MacOS 10.5.6 (Leopard)
+ *
+ * Added "--quiescent" switch, which when used on the command
+ * line prevents the printing of "Timeout" and count notices
+ * on the console.
+ * Changes developed and tested under MacOS 10.5.6 (Leopard)
+ *
+ * Added program title and version tagline, printed when the
+ * software is first started.
+ *
+ * 03 MAR 2009 - Version 1.02
+ *
+ * Added "--digits" switch, which when used on the command
+ * line allows for setting the number of hex digits print per
+ * line.
+ *
+ * Added code to allow "-q" shorthand for "quiescent mode".
+ *
+ * Changes were made to add "#ifdef" statements so that only code
+ * appropriate to MacOS would be compiled if a "#define MACOS" is
+ * defined early within the source code.
+ *
+ * Cleaned up comments in the source for my new source code.
+ *
+ * Changes developed and tested under MacOS 10.5.6 (Leopard)
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#define STREQ(a,b) (strcmp(a,b) == 0)
+
+char *version = "1.02 (03 MAR 2009)";
+int quiescent = 0;
+int total_hex = 20;
+
+struct path {
+ int fd;
+ char path[1024];
+ char name[1024];
+ int rawlog_fd;
+};
+
+void hexdump(char *buf, int len, FILE *dest)
+{
+ /*
+ * In precedence to the modification of this procedure to support the
+ * variable size hexadecimal output, the total bytes output was fixed
+ * to be a length of 8.
+ *
+ * The amendment of this procedure to support the "total_hex" variable
+ * allows for the user to pass a command line argument instantiating a
+ * desired number of hexadecimal bytes (and their ASCII equivelent) to
+ * be displayed.
+ *
+ */
+
+ int i;
+ int j;
+
+ for (i = 0; i < len; i += total_hex) {
+ for (j = i; j < i + total_hex; j++) {
+ if ((j % 4) == 0)
+ fprintf(dest, " ");
+
+ if (j < len)
+ fprintf(dest, "%02x", buf[j] & 0xFF);
+ else
+ fprintf(dest, "--");
+ }
+
+ fprintf(dest, " ");
+
+ for (j = i; j < i + total_hex; j++) {
+ if ((j % 4) == 0)
+ fprintf(dest, " ");
+
+ if (j > len)
+ fprintf(dest, ".");
+ else if ((buf[j] > ' ') && (buf[j] < '~'))
+ fprintf(dest, "%c", buf[j]);
+ else
+ fprintf(dest, ".");
+ }
+
+ fprintf(dest, "\n");
+ }
+}
+
+int saferead(int fd, char *buf, int len)
+{
+ struct itimerval val;
+ int ret;
+ int count = 0;
+
+ memset(&val, 0, sizeof(val));
+ val.it_value.tv_usec = 50000;
+ setitimer(ITIMER_REAL, &val, NULL);
+
+ while (count < len) {
+ getitimer(ITIMER_REAL, &val);
+ if ((val.it_value.tv_sec == 0) &&
+ (val.it_value.tv_usec == 0)) {
+ if (!quiescent)
+ printf("Timeout\n");
+ break;
+ }
+
+ ret = read(fd, &(buf[count]), len - count);
+ if (ret > 0)
+ count += ret;
+ }
+
+ return count;
+}
+
+void proxy(struct path *pathA, struct path *pathB)
+{
+ fd_set rfds;
+ int ret;
+ struct timeval tv;
+
+ while (1) {
+ int count = 0;
+ int ret;
+ char buf[4096];
+
+ FD_ZERO(&rfds);
+
+ FD_SET(pathA->fd, &rfds);
+ FD_SET(pathB->fd, &rfds);
+
+ ret = select(30, &rfds, NULL, NULL, NULL);
+ if (ret == -1) {
+ perror("select");
+ break;
+ }
+
+ if (FD_ISSET(pathA->fd, &rfds)) {
+ count = saferead(pathA->fd, buf, sizeof(buf));
+ if (count < 0)
+ break;
+
+ ret = write(pathB->fd, buf, count);
+ if (ret != count)
+ printf("Failed to write %i (%i)\n", count, ret);
+ if (!quiescent)
+ printf("%s %i:\n", pathA->name, count);
+ hexdump(buf, count, stdout);
+
+ if (pathA->rawlog_fd >= 0) {
+ ret = write(pathA->rawlog_fd, buf, count);
+ if (ret != count)
+ printf("Failed to write %i to %s log",
+ count,
+ pathA->name);
+ }
+
+ }
+
+ if (FD_ISSET(pathB->fd, &rfds)) {
+ count = saferead(pathB->fd, buf, sizeof(buf));
+ if (count < 0)
+ break;
+
+ ret = write(pathA->fd, buf, count);
+ if (ret != count)
+ printf("Failed to write %i (%i)\n", count, ret);
+ if (!quiescent)
+ printf("%s %i:\n", pathB->name, count);
+ hexdump(buf, count, stdout);
+
+ if (pathB->rawlog_fd >= 0) {
+ ret = write(pathB->rawlog_fd, buf, count);
+ if (ret != count)
+ printf("Failed to write %i to %s log",
+ count,
+ pathB->name);
+ }
+ }
+ }
+}
+
+static bool open_pty(struct path *path)
+{
+#ifdef MACOS
+ char *ptsname_path;
+#endif
+
+ path->fd = posix_openpt(O_RDWR);
+ if (path->fd < 0) {
+ perror("posix_openpt");
+ return false;
+ }
+
+ grantpt(path->fd);
+ unlockpt(path->fd);
+
+#ifdef MACOS
+ ptsname_path = ptsname(path->fd);
+ strncpy(path->path,ptsname_path,sizeof(path->path) - 1);
+#else
+ ptsname_r(path->fd, path->path, sizeof(path->path));
+#endif
+
+ fprintf(stderr, "%s\n", path->path);
+
+ return true;
+}
+
+static bool open_serial(const char *serpath, struct path *path)
+{
+ path->fd = open(serpath, O_RDWR);
+ if (path->fd < 0)
+ perror(serpath);
+
+ strncpy(path->path, serpath, sizeof(path->path));
+
+ return path->fd >= 0;
+}
+
+static bool open_socket(const char *foo, struct path *path)
+{
+ int lfd;
+ struct sockaddr_in srv;
+ struct sockaddr_in cli;
+ unsigned int cli_len = sizeof(cli);
+ int optval = 1;
+
+ lfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (lfd < 0) {
+ perror("socket");
+ return false;
+ }
+
+ srv.sin_family = AF_INET;
+ srv.sin_port = htons(2000);
+ srv.sin_addr.s_addr = INADDR_ANY;
+
+ setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+
+ if (bind(lfd, (struct sockaddr *)&srv, sizeof(srv)) < 0) {
+ perror("bind");
+ return false;
+ }
+
+ if (listen(lfd, 1) < 0) {
+ perror("listen");
+ return false;
+ }
+
+ printf("Waiting...\n");
+
+ path->fd = accept(lfd, (struct sockaddr *)&cli, &cli_len);
+ if (path->fd < 0) {
+ perror("accept");
+ return false;
+ }
+
+ printf("Accepted socket client\n");
+
+ strcpy(path->path, "SOCKET");
+
+ return true;
+}
+
+static bool open_path(const char *opt, struct path *path)
+{
+ if (STREQ(opt, "pty"))
+ return open_pty(path);
+ else if (STREQ(opt, "listen"))
+ return open_socket(opt, path);
+ else
+ return open_serial(opt, path);
+}
+
+static bool open_log(const char *filename, struct path *path)
+{
+ path->rawlog_fd = open(filename, O_WRONLY | O_CREAT, 0644);
+ if (path->rawlog_fd < 0)
+ perror(filename);
+
+ return path->rawlog_fd >= 0;
+}
+
+static void usage()
+{
+ printf("Usage:\n"
+ "serialsniff [OPTIONS]\n"
+ "Where OPTIONS are:\n"
+ "\n"
+ " -A,--pathA=DEV Path to device A (or 'pty')\n"
+ " -B,--pathB=DEV Path to device B (or 'pty')\n"
+ " --logA=FILE Log pathA (raw) to FILE\n"
+ " --logB=FILE Log pathB (raw) to FILE\n"
+ " --nameA=NAME Set pathA name to NAME\n"
+ " --nameB=NAME Set pathB name to NAME\n"
+ " --q,-q,--quiescent Run in quiescent mode\n"
+ " --d,-d,--digits Number of hex digits to print in one line\n\n"
+ " --d=nn or -d nn or --digits nn\n"
+ "\n"
+ );
+}
+
+int main(int argc, char **argv)
+{
+
+ struct sigaction sa;
+
+ struct path pathA;
+ struct path pathB;
+
+ int c;
+
+ strcpy(pathA.name, "A");
+ strcpy(pathB.name, "B");
+ pathA.fd = pathA.rawlog_fd = -1;
+ pathB.fd = pathB.rawlog_fd = -1;
+
+ printf("\nserialsniff - Version %s\n\n",version);
+
+ while (1) {
+ int optind;
+ static struct option lopts[] = {
+ {"pathA", 1, 0, 'A'},
+ {"pathB", 1, 0, 'B'},
+ {"logA", 1, 0, 1 },
+ {"logB", 1, 0, 2 },
+ {"nameA", 1, 0, 3 },
+ {"nameB", 1, 0, 4 },
+ {"quiescent", 0, 0, 'q' },
+ {"digits", 1, 0, 'd'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "A:B:d:l:q",
+ lopts, &optind);
+ if (c == -1)
+ break;
+
+ switch (c) {
+
+ case 'A':
+ if (!open_path(optarg, &pathA))
+ return 1;
+ break;
+
+ case 'B':
+ if (!open_path(optarg, &pathB))
+ return 2;
+ break;
+
+ case 1:
+ if (!open_log(optarg, &pathA))
+ return 3;
+ break;
+
+ case 2:
+ if (!open_log(optarg, &pathB))
+ return 4;
+ break;
+
+ case 3:
+ strncpy(pathA.name, optarg, sizeof(pathA.name));
+ break;
+
+ case 4:
+ strncpy(pathB.name, optarg, sizeof(pathB.name));
+ break;
+
+ case 'q':
+ quiescent = 1;
+ break;
+
+ case 'd':
+ total_hex=atoi(optarg);
+ break;
+
+ case '?':
+ return 3;
+ }
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGALRM, &sa, NULL);
+
+ if ((pathA.fd < 0) || (pathB.fd < 0)) {
+ usage();
+ return -1;
+ }
+
+ proxy(&pathA, &pathB);
+
+ return 0;
+}