#!/usr/bin/env python # # Copyright 2008 Dan Smith # # 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 . import serial import os import sys import argparse import logging from chirp import logger from chirp.drivers import * from chirp import chirp_common, errors, directory, util LOG = logging.getLogger("chirpc") RADIOS = directory.DRV_TO_RADIO def fail_unsupported(): LOG.error("Operation not supported by selected radio") sys.exit(1) def fail_missing_mmap(): LOG.error("mmap-only operation requires specification of an mmap file") sys.exit(1) class ToneAction(argparse.Action): def __call__(self, parser, namespace, value, option_string=None): if value in chirp_common.TONES: raise argparse.ArgumentError("Invalid tone valeu: %.1f" % value) setattr(namespace, self.dest, value) class DTCSAction(argparse.Action): def __call__(self, parser, namespace, value, option_string=None): try: value = int(value, 10) except ValueError: raise argparse.ArgumentError("Invalid DTCS value: %s" % value) if value not in chirp_common.DTCS_CODES: raise argparse.ArgumentError("Invalid DTCS value: %03i" % value) setattr(namespace, self.dest, value) class DTCSPolarityAction(argparse.Action): def __call__(self, parser, namespace, value, option_string=None): if value not in ["NN", "RN", "NR", "RR"]: raise optparse.OptionValueError("Invaid DTCS polarity: %s" % value) setattr(parser.values, option.dest, value) def parse_memory_number(radio, args): if len(args) < 1: LOG.error("You must provide an argument specifying the memory number.") sys.exit(1) try: memnum = int(args[0]) except ValueError: LOG.error("'%s' is not a valid memory number", args[0]) sys.exit(1) rf = radio.get_features() start, end = rf.memory_bounds if memnum < start or memnum > end: LOG.error("memory number must be between %d and %d (got %d)", start, end, memnum) sys.exit(1) return memnum if __name__ == "__main__": parser = argparse.ArgumentParser() logger.add_version_argument(parser) parser.add_argument("-s", "--serial", dest="serial", default="mmap", help="Serial port (default: mmap)") parser.add_argument("--list-settings", action="store_true", help="List settings") parser.add_argument("-i", "--id", dest="id", default=False, action="store_true", help="Request radio ID string") memarg = parser.add_argument_group("Memory/Channel Options") memarg.add_argument("--list-mem", action="store_true", help="List all memory locations") memarg.add_argument("--raw", action="store_true", help="Dump raw memory location") memarg.add_argument("--get-mem", action="store_true", help="Get and print memory location") memarg.add_argument("--copy-mem", action="store_true", help="Copy memory location") memarg.add_argument("--clear-mem", action="store_true", help="Clear memory location") memarg.add_argument("--set-mem-name", help="Set memory name") memarg.add_argument("--set-mem-freq", type=float, help="Set memory frequency") memarg.add_argument("--set-mem-tencon", action="store_true", help="Set tone encode enabled flag") memarg.add_argument("--set-mem-tencoff", action="store_true", help="Set tone decode disabled flag") memarg.add_argument("--set-mem-tsqlon", action="store_true", help="Set tone squelch enabled flag") memarg.add_argument("--set-mem-tsqloff", action="store_true", help="Set tone squelch disabled flag") memarg.add_argument("--set-mem-dtcson", action="store_true", help="Set DTCS enabled flag") memarg.add_argument("--set-mem-dtcsoff", action="store_true", help="Set DTCS disabled flag") memarg.add_argument("--set-mem-tenc", type=float, action=ToneAction, nargs=1, help="Set memory encode tone") memarg.add_argument("--set-mem-tsql", type=float, action=ToneAction, nargs=1, help="Set memory squelch tone") memarg.add_argument("--set-mem-dtcs", type=int, action=DTCSAction, nargs=1, help="Set memory DTCS code") memarg.add_argument("--set-mem-dtcspol", action=DTCSPolarityAction, nargs=1, help="Set memory DTCS polarity (NN, NR, RN, RR)") memarg.add_argument("--set-mem-dup", help="Set memory duplex (+,-, or blank)") memarg.add_argument("--set-mem-offset", type=float, help="Set memory duplex offset (in MHz)") memarg.add_argument("--set-mem-mode", help="Set mode (%s)" % ",".join(chirp_common.MODES)) parser.add_argument("-r", "--radio", dest="radio", default=None, help="Radio model (see --list-radios)") parser.add_argument("--list-radios", action="store_true", help="List radio models") parser.add_argument("--mmap", dest="mmap", default=None, help="Radio memory map file location") parser.add_argument("--download-mmap", dest="download_mmap", action="store_true", default=False, help="Download memory map from radio") parser.add_argument("--upload-mmap", dest="upload_mmap", action="store_true", default=False, help="Upload memory map to radio") logger.add_arguments(parser) parser.add_argument("args", metavar="arg", nargs='*', help="Some commands require additional arguments") if len(sys.argv) <= 1: parser.print_help() sys.exit(0) options = parser.parse_args() args = options.args logger.handle_options(options) if options.list_radios: print "Supported Radios:\n\t", "\n\t".join(sorted(RADIOS.keys())) sys.exit(0) if options.id: from chirp import icf s = serial.Serial(port=options.serial, baudrate=9600, timeout=0.5) md = icf.get_model_data(s) print "Model:\n%s" % util.hexprint(md) sys.exit(0) if not options.radio: if options.mmap: rclass = directory.get_radio_by_image(options.mmap).__class__ else: print "You must specify a radio model. See --list-radios." sys.exit(1) else: rclass = directory.get_radio(options.radio) if options.serial == "mmap": if options.mmap: s = options.mmap else: s = options.radio + ".img" if not os.path.exists(s): LOG.error("Image file '%s' does not exist" % s) sys.exit(1) else: LOG.info("opening %s at %i" % (options.serial, rclass.BAUD_RATE)) s = serial.Serial(port=options.serial, baudrate=rclass.BAUD_RATE, timeout=0.5) radio = rclass(s) if options.list_settings: print radio.get_settings() sys.exit(0) if options.list_mem: rf = radio.get_features() start, end = rf.memory_bounds for i in range(start, end + 1): mem = radio.get_memory(i) if mem.empty and not logger.is_visible(logging.INFO): continue print mem sys.exit(0) if options.copy_mem: src = parse_memory_number(radio, args) dst = parse_memory_number(radio, args[1:]) try: mem = radio.get_memory(src) except errors.InvalidMemoryLocation, e: LOG.exception(e) sys.exit(1) LOG.info("copying memory %d to %d", src, dst) mem.number = dst radio.set_memory(mem) if options.clear_mem: memnum = parse_memory_number(radio, args) try: mem = radio.get_memory(memnum) except errors.InvalidMemoryLocation, e: LOG.exception(e) sys.exit(1) if mem.empty: LOG.warn("memory %d is already empty, exiting", memnum) sys.exit(0) mem.empty = True radio.set_memory(mem) if options.raw: memnum = parse_memory_number(radio, args) data = radio.get_raw_memory(memnum) for i in data: if ord(i) > 0x7F: print "Memory location %i (%i):\n%s" % \ (memnum, len(data), util.hexprint(data)) sys.exit(0) print data sys.exit(0) if options.set_mem_dup is not None: if options.set_mem_dup != "+" and \ options.set_mem_dup != "-" and \ options.set_mem_dup != "": LOG.error("Invalid duplex value `%s'" % options.set_mem_dup) LOG.error("Valid values are: '+', '-', ''") sys.exit(1) else: _dup = options.set_mem_dup else: _dup = None if options.set_mem_mode: LOG.info("Set mode: %s" % options.set_mem_mode) if options.set_mem_mode not in chirp_common.MODES: LOG.error("Invalid mode `%s'") sys.exit(1) else: _mode = options.set_mem_mode else: _mode = None if options.set_mem_name or options.set_mem_freq or \ options.set_mem_tencon or options.set_mem_tencoff or \ options.set_mem_tsqlon or options.set_mem_tsqloff or \ options.set_mem_dtcson or options.set_mem_dtcsoff or \ options.set_mem_tenc or options.set_mem_tsql or \ options.set_mem_dtcs or options.set_mem_dup is not None or \ options.set_mem_mode or options.set_mem_dtcspol or\ options.set_mem_offset: memnum = parse_memory_number(radio, args) try: mem = radio.get_memory(memnum) except errors.InvalidMemoryLocation, e: LOG.exception(e) sys.exit(1) if mem.empty: LOG.info("creating new memory (#%d)", memnum) mem = chirp_common.Memory() mem.number = memnum mem.name = options.set_mem_name or mem.name mem.freq = options.set_mem_freq or mem.freq mem.rtone = options.set_mem_tenc or mem.rtone mem.ctone = options.set_mem_tsql or mem.ctone mem.dtcs = options.set_mem_dtcs or mem.dtcs mem.dtcs_polarity = options.set_mem_dtcspol or mem.dtcs_polarity if _dup is not None: mem.duplex = _dup mem.offset = options.set_mem_offset or mem.offset mem.mode = _mode or mem.mode if options.set_mem_tencon: mem.tencEnabled = True elif options.set_mem_tencoff: mem.tencEnabled = False if options.set_mem_tsqlon: mem.tsqlEnabled = True elif options.set_mem_tsqloff: mem.tsqlEnabled = False if options.set_mem_dtcson: mem.dtcsEnabled = True elif options.set_mem_dtcsoff: mem.dtcsEnabled = False radio.set_memory(mem) if options.get_mem: pos = parse_memory_number(radio, args) try: mem = radio.get_memory(pos) except errors.InvalidMemoryLocation, e: mem = chirp_common.Memory() mem.number = pos print mem sys.exit(0) if options.download_mmap: if not issubclass(rclass, chirp_common.CloneModeRadio): LOG.error("%s is not a clone mode radio" % options.radio) sys.exit(1) if not options.mmap: LOG.error("You must specify the destination file name with --mmap") sys.exit(1) try: radio.sync_in() radio.save_mmap(options.mmap) except Exception, e: LOG.exception(e) sys.exit(1) if options.upload_mmap: if not issubclass(rclass, chirp_common.CloneModeRadio): LOG.error("%s is not a clone mode radio" % options.radio) sys.exit(1) if not options.mmap: LOG.error("You must specify the source file name with --mmap") sys.exit(1) try: radio.load_mmap(options.mmap) radio.sync_out() print "Upload successful" except Exception, e: LOG.exception(e) sys.exit(1) if options.mmap and isinstance(radio, chirp_common.CloneModeRadio): radio.save_mmap(options.mmap)