From 2f2acc20e0c60b85bd38337e7dfdd6d85964c7fb Mon Sep 17 00:00:00 2001 From: Serge Date: Sat, 13 Oct 2018 20:37:14 -0700 Subject: [PATCH 01/21] Add serial routines. --- Makefile | 2 +- Makefile-mingw | 2 +- radio.c | 8 +- serial.c | 631 +++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 12 + 5 files changed, 652 insertions(+), 3 deletions(-) create mode 100644 serial.c diff --git a/Makefile b/Makefile index 8398279..e6e2706 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ UNAME = $(shell uname) CFLAGS = -g -O -Wall -Werror -DVERSION='"$(VERSION).$(GITCOUNT)"' LDFLAGS = -g -OBJS = main.o util.o radio.o dfu-libusb.o uv380.o md380.o rd5r.o gd77.o hid.o +OBJS = main.o util.o radio.o dfu-libusb.o uv380.o md380.o rd5r.o gd77.o hid.o serial.o LIBS = -lusb-1.0 # Linux diff --git a/Makefile-mingw b/Makefile-mingw index d6bf430..9cbbe55 100644 --- a/Makefile-mingw +++ b/Makefile-mingw @@ -5,7 +5,7 @@ GITCOUNT = $(shell git rev-list HEAD --count) CFLAGS = -g -O -Wall -Werror -DVERSION='"$(VERSION).$(GITCOUNT)"' LDFLAGS = -g -s -OBJS = main.o util.o radio.o dfu-windows.o uv380.o md380.o rd5r.o gd77.o hid.o hid-windows.o +OBJS = main.o util.o radio.o dfu-windows.o uv380.o md380.o rd5r.o gd77.o hid.o hid-windows.o serial.o LIBS = -lhid -lsetupapi # Compiling Windows binary from Linux diff --git a/radio.c b/radio.c index f6dd4fb..0c860e5 100644 --- a/radio.c +++ b/radio.c @@ -68,6 +68,7 @@ void radio_disconnect() dfu_reboot(); dfu_close(); hid_close(); + serial_close(); } // @@ -89,10 +90,15 @@ void radio_connect() // Try TYT MD family. ident = dfu_init(0x0483, 0xdf11); if (! ident) { - // Try RD-5R. + // Try RD-5R and GD-77. if (hid_init(0x15a2, 0x0073) >= 0) ident = hid_identify(); } + if (! ident) { + // Try AT-D868UV. + if (serial_init(0x28e9, 0x018a) >= 0) + ident = serial_identify(); + } if (! ident) { fprintf(stderr, "No radio detected.\n"); fprintf(stderr, "Check your USB cable!\n"); diff --git a/serial.c b/serial.c new file mode 100644 index 0000000..ba5f2d8 --- /dev/null +++ b/serial.c @@ -0,0 +1,631 @@ +/* + * Interface to virtual serial USB port. + * + * Copyright (C) 2018 Serge Vakulenko, KK6ABQ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include "util.h" + +#if defined(__WIN32__) || defined(WIN32) + #include + #include + static void *fd = INVALID_HANDLE_VALUE; + static DCB saved_mode; +#else + #include + static int fd = -1; + static struct termios saved_mode; +#endif + +#ifdef __linux__ +# include +#endif + +#ifdef __APPLE__ +# include +# include +# include +# include +# include +#endif + +static char *dev_path; + +static const unsigned char CMD_PRG[] = "PROGRAM"; +static const unsigned char CMD_QX[] = "QX\6"; +static const unsigned char CMD_ACK2[] = "\2"; + +// +// Encode the speed in bits per second into bit value +// accepted by cfsetspeed() function. +// Return -1 when speed is not supported. +// +static int baud_encode(int bps) +{ +#if defined(__WIN32__) || defined(WIN32) || B115200 == 115200 + // Windows, BSD, Mac OS: any baud rate allowed. + return bps; +#else + // Linux: only a limited set of values permitted. + switch (bps) { +#ifdef B75 + case 75: return B75; +#endif +#ifdef B110 + case 110: return B110; +#endif +#ifdef B134 + case 134: return B134; +#endif +#ifdef B150 + case 150: return B150; +#endif +#ifdef B200 + case 200: return B200; +#endif +#ifdef B300 + case 300: return B300; +#endif +#ifdef B600 + case 600: return B600; +#endif +#ifdef B1200 + case 1200: return B1200; +#endif +#ifdef B1800 + case 1800: return B1800; +#endif +#ifdef B2400 + case 2400: return B2400; +#endif +#ifdef B4800 + case 4800: return B4800; +#endif +#ifdef B9600 + case 9600: return B9600; +#endif +#ifdef B19200 + case 19200: return B19200; +#endif +#ifdef B38400 + case 38400: return B38400; +#endif +#ifdef B57600 + case 57600: return B57600; +#endif +#ifdef B115200 + case 115200: return B115200; +#endif +#ifdef B230400 + case 230400: return B230400; +#endif +#ifdef B460800 + case 460800: return B460800; +#endif +#ifdef B500000 + case 500000: return B500000; +#endif +#ifdef B576000 + case 576000: return B576000; +#endif +#ifdef B921600 + case 921600: return B921600; +#endif +#ifdef B1000000 + case 1000000: return B1000000; +#endif +#ifdef B1152000 + case 1152000: return B1152000; +#endif +#ifdef B1500000 + case 1500000: return B1500000; +#endif +#ifdef B2000000 + case 2000000: return B2000000; +#endif +#ifdef B2500000 + case 2500000: return B2500000; +#endif +#ifdef B3000000 + case 3000000: return B3000000; +#endif +#ifdef B3500000 + case 3500000: return B3500000; +#endif +#ifdef B4000000 + case 4000000: return B4000000; +#endif + } +printf("Unknown baud\n"); + return -1; +#endif +} + +// +// Send data to device. +// Return number of bytes, or -1 on error. +// +int serial_write(const unsigned char *data, int len) +{ +#if defined(__WIN32__) || defined(WIN32) + DWORD written; + + if (! WriteFile(fd, data, len, &written, 0)) + return -1; + return written; +#else + return write(fd, data, len); +#endif +} + +// +// Receive data from device. +// Return number of bytes, or -1 on error. +// +int serial_read(unsigned char *data, int len, int timeout_msec) +{ +#if defined(__WIN32__) || defined(WIN32) + DWORD got; + COMMTIMEOUTS ctmo; + + // Reset the Windows RX timeout to the current timeout_msec + // value, as it may have changed since the last read. + // + memset(&ctmo, 0, sizeof(ctmo)); + ctmo.ReadIntervalTimeout = 0; + ctmo.ReadTotalTimeoutMultiplier = 0; + ctmo.ReadTotalTimeoutConstant = timeout_msec; + if (! SetCommTimeouts(fd, &ctmo)) { + fprintf(stderr, "Cannot set timeouts in serial_read()\n"); + return -1; + } + + if (! ReadFile(fd, data, len, &got, 0)) { + fprintf(stderr, "serial_read: read error\n"); + exit(-1); + } +#else + struct timeval timeout, to2; + long got; + fd_set rfds; + + timeout.tv_sec = timeout_msec / 1000; + timeout.tv_usec = timeout_msec % 1000 * 1000; +again: + to2 = timeout; + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + got = select(fd + 1, &rfds, 0, 0, &to2); + if (got < 0) { + if (errno == EINTR || errno == EAGAIN) { + if (trace_flag > 0) + printf("serial_read: retry on select\n"); + goto again; + } + fprintf(stderr, "serial_read: select error: %s\n", strerror(errno)); + exit(-1); + } +#endif + if (got == 0) { + if (trace_flag > 0) + printf("serial_read: no characters to read\n"); + return 0; + } + +#if ! defined(__WIN32__) && ! defined(WIN32) + got = read(fd, data, (len > 1024) ? 1024 : len); + if (got < 0) { + fprintf(stderr, "serial_read: read error\n"); + exit(-1); + } +#endif + return got; +} + +// +// Close the serial port. +// +void serial_close() +{ +#if defined(__WIN32__) || defined(WIN32) + if (fd != INVALID_HANDLE_VALUE) { + SetCommState(fd, &saved_mode); + CloseHandle(fd); + fd = INVALID_HANDLE_VALUE; + } +#else + if (fd >= 0) { + tcsetattr(fd, TCSANOW, &saved_mode); + close(fd); + fd = -1; + } +#endif +} + +// +// Open the serial port. +// Return -1 on error. +// +int serial_open(const char *devname, int baud_rate) +{ +#if defined(__WIN32__) || defined(WIN32) + DCB new_mode; +#else + struct termios new_mode; +#endif + +#if defined(__WIN32__) || defined(WIN32) + // Check for the Windows device syntax and bend a DOS device + // into that syntax to allow higher COM numbers than 9 + // + if (devname[0] != '\\') { + // Prepend device prefix: COM23 -> \\.\COM23 + char *buf = alloca(5 + strlen(devname)); + if (! buf) { + fprintf(stderr, "%s: Out of memory\n", devname); + return -1; + } + strcpy(buf, "\\\\.\\"); + strcat(buf, devname); + devname = buf; + } + + // Open port + fd = CreateFile(devname, GENERIC_READ | GENERIC_WRITE, + 0, 0, OPEN_EXISTING, 0, 0); + if (fd == INVALID_HANDLE_VALUE) { + fprintf(stderr, "%s: Cannot open\n", devname); + return -1; + } + + // Set serial attributes + memset(&saved_mode, 0, sizeof(saved_mode)); + if (! GetCommState(fd, &saved_mode)) { + fprintf(stderr, "%s: Cannot get state\n", devname); + return -1; + } + + new_mode = saved_mode; + + new_mode.fDtrControl = DTR_CONTROL_ENABLE; + new_mode.BaudRate = baud_rate; + new_mode.ByteSize = 8; + new_mode.StopBits = ONESTOPBIT; + new_mode.Parity = 0; + new_mode.fParity = FALSE; + new_mode.fOutX = FALSE; + new_mode.fInX = FALSE; + new_mode.fOutxCtsFlow = FALSE; + new_mode.fOutxDsrFlow = FALSE; + new_mode.fRtsControl = RTS_CONTROL_ENABLE; + new_mode.fNull = FALSE; + new_mode.fAbortOnError = FALSE; + new_mode.fBinary = TRUE; + if (! SetCommState(fd, &new_mode)) { + fprintf(stderr, "%s: Cannot set state\n", devname); + return -1; + } +#else + // Encode baud rate. + int baud_code = baud_encode(baud_rate); + if (baud_code < 0) { + fprintf(stderr, "%s: Bad baud rate %d\n", devname, baud_rate); + return -1; + } + + // Open port + fd = open(devname, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd < 0) { + perror(devname); + return -1; + } + + // Set serial attributes + memset(&saved_mode, 0, sizeof(saved_mode)); + tcgetattr(fd, &saved_mode); + + // 8n1, ignore parity + memset(&new_mode, 0, sizeof(new_mode)); + new_mode.c_cflag = CS8 | CLOCAL | CREAD; + new_mode.c_iflag = IGNBRK; + new_mode.c_oflag = 0; + new_mode.c_lflag = 0; + new_mode.c_cc[VTIME] = 0; + new_mode.c_cc[VMIN] = 1; + cfsetispeed(&new_mode, baud_code); + cfsetospeed(&new_mode, baud_code); + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &new_mode); + + // Clear O_NONBLOCK flag. + int flags = fcntl(fd, F_GETFL, 0); + if (flags >= 0) + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +#endif + return 0; +} + +// +// Find a device path by vid/pid. +// +static char *find_path(int vid, int pid) +{ + char *result = 0; + +#if defined(__linux__) + // Create the udev object. + struct udev *udev = udev_new(); + if (! udev) { + printf("Can't create udev\n"); + return 0; + } + + // Create a list of the devices in the 'tty' subsystem. + struct udev_enumerate *enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "tty"); + udev_enumerate_scan_devices(enumerate); + + struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); + + // For each item enumerated, figure out its information. + struct udev_list_entry *dev_list_entry; + udev_list_entry_foreach(dev_list_entry, devices) { + // Get the filename of the /sys entry for the device + // and create a udev_device object (dev) representing it. + const char *syspath = udev_list_entry_get_name(dev_list_entry); + struct udev_device *comport = udev_device_new_from_syspath(udev, syspath); + //printf("syspath = %s\n", syspath); + + // Get the parent device with the subsystem/devtype pair + // of "usb"/"usb_device". + struct udev_device *parent = udev_device_get_parent_with_subsystem_devtype(comport, + "usb", "usb_device"); + if (! parent) { + //printf("Unable to find parent usb device.\n"); + continue; + } + + // Get the path to the device node in /dev. + const char *devpath = udev_device_get_devnode(comport); + //printf("devpath = %s\n", devpath); + //printf("parent = %s\n", udev_device_get_devnode(parent)); + + const char *idVendor = udev_device_get_sysattr_value(parent, "idVendor"); + const char *idProduct = udev_device_get_sysattr_value(parent, "idProduct"); + if (! idVendor || ! idProduct) { + // No vendor and product ID. + continue; + } + //printf("vid = %s\n", idVendor); + //printf("pid = %s\n", idProduct); + + unsigned vendor_id = strtoul(idVendor, 0, 16); + unsigned product_id = strtoul(idProduct, 0, 16); + if (vendor_id != vid || product_id != pid) { + // Wrong ID. + continue; + } + + // Print names of vendor and product. + //const char *vendor = udev_device_get_sysattr_value(parent, "manufacturer"); + //const char *product = udev_device_get_sysattr_value(parent, "product"); + //printf("vendor = %s\n", vendor); + //printf("product = %s\n", product); + + // Return result. + udev_device_unref(parent); + result = strdup(devpath); + break; + } + + // Free the enumerator object + udev_enumerate_unref(enumerate); + udev_unref(udev); + return result; + +#elif defined(__APPLE__) + // Create a list of the devices in the 'IOMedia' class. + CFMutableDictionaryRef dict = IOServiceMatching(kIOMediaClass); + if (! dict) { + printf("Cannot create IO Service dictionary.\n"); + return; + } + + // Select devices with attributes Removeable=True and Whole=True. + CFDictionarySetValue(dict, CFSTR(kIOMediaRemovableKey), kCFBooleanTrue); + CFDictionarySetValue(dict, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); + + io_iterator_t devices = IO_OBJECT_NULL; + kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, + dict, &devices); + if (result != KERN_SUCCESS) { + printf("Cannot find matching IO services.\n"); + return; + } + + // For each matching device, print out its information. + io_object_t device; + while ((device = IOIteratorNext(devices)) != MACH_PORT_NULL) { + // Get device path. + char devname[1024] = "/dev/r"; + CFStringRef ref = (CFStringRef) IORegistryEntrySearchCFProperty(device, + kIOServicePlane, CFSTR(kIOBSDNameKey), + kCFAllocatorDefault, kIORegistryIterateRecursively); + if (! ref || ! CFStringGetCString(ref, devname + 6, + sizeof(devname) - 6, kCFStringEncodingUTF8)) { + // Cannot get device path. + continue; + } +#if 0 + // Get device size in bytes. + long long size; + ref = IORegistryEntryCreateCFProperty(device, + CFSTR(kIOMediaSizeKey), kCFAllocatorDefault, 0); + if (! ref || ! CFNumberGetValue((CFNumberRef)ref, kCFNumberLongLongType, &size)) { + // Cannot get device size. + continue; + } +#endif + // Get a list of device characteristics. + CFMutableDictionaryRef dict = (CFMutableDictionaryRef) + IORegistryEntrySearchCFProperty(device, kIOServicePlane, + CFSTR(kIOPropertyDeviceCharacteristicsKey), + kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively); + if (! dict) { + // Cannot get device characteristics. + continue; + } + + // Get vendor and product names. + char vendor[1024], product[1024]; + ref = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey)); + if (! ref || ! CFStringGetCString(ref, vendor, + sizeof(vendor), kCFStringEncodingUTF8)) { + // Cannot get vendor name. + continue; + } + ref = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey)); + if (! ref || ! CFStringGetCString(ref, product, + sizeof(product), kCFStringEncodingUTF8)) { + // Cannot get product name. + continue; + } + + char buf[1024]; + sprintf(buf, "%s - size %u MB, %s %s", + devname, (unsigned) (size / 1000000), vendor, product); + IOObjectRelease(device); + devtab[ndev++] = strdup(buf); + } + + // Free the iterator object + IOObjectRelease(devices); +#else + printf("Don't know how to get the list of CD devices on this system.\n"); + return 0; +#endif +} + +// +// Connect to the specified device. +// Initiate the programming session. +// +int serial_init(int vid, int pid) +{ + dev_path = find_path(vid, pid); + if (!dev_path) { + if (trace_flag) { + fprintf(stderr, "Cannot find USB device %04x:%04x\n", + vid, pid); + } + return -1; + } + + // Succeeded. + printf("Serial port: %s\n", dev_path); + return 0; +} + +// +// Send the command sequence and get back a response. +// +static int send_recv(const unsigned char *cmd, int cmdlen, + unsigned char *response, int reply_len) +{ + unsigned char *p; + int len, i, got; + + // + // Send command. + // + if (trace_flag > 0) { + fprintf(stderr, "----Send [%d] %x", cmdlen, cmd[0]); + for (i=1; i 0) { + fprintf(stderr, "----Recv [%d] %x", reply_len, response[0]); + for (i=1; i Date: Sat, 13 Oct 2018 21:16:03 -0700 Subject: [PATCH 02/21] Recognize the D868UV radio. --- Makefile | 4 +- Makefile-mingw | 4 +- d868uv.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++ radio.c | 1 + radio.h | 1 + serial.c | 60 ++++--- 6 files changed, 510 insertions(+), 26 deletions(-) create mode 100644 d868uv.c diff --git a/Makefile b/Makefile index e6e2706..a99c450 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ UNAME = $(shell uname) CFLAGS = -g -O -Wall -Werror -DVERSION='"$(VERSION).$(GITCOUNT)"' LDFLAGS = -g -OBJS = main.o util.o radio.o dfu-libusb.o uv380.o md380.o rd5r.o gd77.o hid.o serial.o +OBJS = main.o util.o radio.o dfu-libusb.o uv380.o md380.o rd5r.o gd77.o hid.o serial.o d868uv.o LIBS = -lusb-1.0 # Linux @@ -37,6 +37,7 @@ dmrconfig.linux: dmrconfig strip $@ ### +d868uv.o: d868uv.c radio.h util.h dfu-libusb.o: dfu-libusb.c util.h dfu-windows.o: dfu-windows.c util.h gd77.o: gd77.c radio.h util.h @@ -48,5 +49,6 @@ main.o: main.c radio.h util.h md380.o: md380.c radio.h util.h radio.o: radio.c radio.h util.h rd5r.o: rd5r.c radio.h util.h +serial.o: serial.c util.h util.o: util.c util.h uv380.o: uv380.c radio.h util.h diff --git a/Makefile-mingw b/Makefile-mingw index 9cbbe55..242a941 100644 --- a/Makefile-mingw +++ b/Makefile-mingw @@ -5,7 +5,7 @@ GITCOUNT = $(shell git rev-list HEAD --count) CFLAGS = -g -O -Wall -Werror -DVERSION='"$(VERSION).$(GITCOUNT)"' LDFLAGS = -g -s -OBJS = main.o util.o radio.o dfu-windows.o uv380.o md380.o rd5r.o gd77.o hid.o hid-windows.o serial.o +OBJS = main.o util.o radio.o dfu-windows.o uv380.o md380.o rd5r.o gd77.o hid.o hid-windows.o serial.o d868uv.o LIBS = -lhid -lsetupapi # Compiling Windows binary from Linux @@ -33,6 +33,7 @@ install: dmrconfig install -c -s dmrconfig /usr/local/bin/dmrconfig ### +d868uv.o: d868uv.c radio.h util.h dfu-libusb.o: dfu-libusb.c util.h dfu-windows.o: dfu-windows.c util.h gd77.o: gd77.c radio.h util.h @@ -44,5 +45,6 @@ main.o: main.c radio.h util.h md380.o: md380.c radio.h util.h radio.o: radio.c radio.h util.h rd5r.o: rd5r.c radio.h util.h +serial.o: serial.c util.h util.o: util.c util.h uv380.o: uv380.c radio.h util.h diff --git a/d868uv.c b/d868uv.c new file mode 100644 index 0000000..0a5c937 --- /dev/null +++ b/d868uv.c @@ -0,0 +1,466 @@ +/* + * Interface to Anytone D868UV. + * + * Copyright (C) 2018 Serge Vakulenko, KK6ABQ + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include "radio.h" +#include "util.h" + +#define NCHAN 4000 +#define NCONTACTS 10000 +#define NZONES 250 +#define NGLISTS 250 +#define NSCANL 250 +#define NMESSAGES 100 + +#define MEMSZ 0xd0000 + +// +// Print a generic information about the device. +// +static void d868uv_print_version(radio_device_t *radio, FILE *out) +{ + //TODO +#if 0 + unsigned char *timestamp = GET_TIMESTAMP(); + static const char charmap[16] = "0123456789:;<=>?"; + + if (*timestamp != 0xff) { + fprintf(out, "Last Programmed Date: %d%d%d%d-%d%d-%d%d", + timestamp[0] >> 4, timestamp[0] & 15, timestamp[1] >> 4, timestamp[1] & 15, + timestamp[2] >> 4, timestamp[2] & 15, timestamp[3] >> 4, timestamp[3] & 15); + fprintf(out, " %d%d:%d%d:%d%d\n", + timestamp[4] >> 4, timestamp[4] & 15, timestamp[5] >> 4, timestamp[5] & 15, + timestamp[6] >> 4, timestamp[6] & 15); + fprintf(out, "CPS Software Version: V%c%c.%c%c\n", + charmap[timestamp[7] & 15], charmap[timestamp[8] & 15], + charmap[timestamp[9] & 15], charmap[timestamp[10] & 15]); + } +#endif +} + +// +// Read memory image from the device. +// +static void d868uv_download(radio_device_t *radio) +{ + //TODO +#if 0 + int bno; + + for (bno=0; bnoscan_list_index != 0) { + scanlist_t *sl = GET_SCANLIST(ch->scan_list_index - 1); + + if (!VALID_SCANLIST(sl)) { + fprintf(stderr, "Channel %d '", i+1); + print_unicode(stderr, ch->name, 16, 0); + fprintf(stderr, "': scanlist %d not found.\n", ch->scan_list_index); + nerrors++; + } + } + if (ch->contact_name_index != 0) { + contact_t *ct = GET_CONTACT(ch->contact_name_index - 1); + + if (!VALID_CONTACT(ct)) { + fprintf(stderr, "Channel %d '", i+1); + print_unicode(stderr, ch->name, 16, 0); + fprintf(stderr, "': contact %d not found.\n", ch->contact_name_index); + nerrors++; + } + } + if (ch->group_list_index != 0) { + grouplist_t *gl = GET_GROUPLIST(ch->group_list_index - 1); + + if (!VALID_GROUPLIST(gl)) { + fprintf(stderr, "Channel %d '", i+1); + print_unicode(stderr, ch->name, 16, 0); + fprintf(stderr, "': grouplist %d not found.\n", ch->group_list_index); + nerrors++; + } + } + } + + // Zones: check references to channels. + for (i=0; imember_a[k]; + + if (cnum != 0) { + channel_t *ch = GET_CHANNEL(cnum - 1); + + if (!VALID_CHANNEL(ch)) { + fprintf(stderr, "Zone %da '", i+1); + print_unicode(stderr, z->name, 16, 0); + fprintf(stderr, "': channel %d not found.\n", cnum); + nerrors++; + } + } + } + for (k=0; k<48; k++) { + int cnum = zext->ext_a[k]; + + if (cnum != 0) { + channel_t *ch = GET_CHANNEL(cnum - 1); + + if (!VALID_CHANNEL(ch)) { + fprintf(stderr, "Zone %da '", i+1); + print_unicode(stderr, z->name, 16, 0); + fprintf(stderr, "': channel %d not found.\n", cnum); + nerrors++; + } + } + } + + // Zone B + for (k=0; k<64; k++) { + int cnum = zext->member_b[k]; + + if (cnum != 0) { + channel_t *ch = GET_CHANNEL(cnum - 1); + + if (!VALID_CHANNEL(ch)) { + fprintf(stderr, "Zone %db '", i+1); + print_unicode(stderr, z->name, 16, 0); + fprintf(stderr, "': channel %d not found.\n", cnum); + nerrors++; + } + } + } + } + + // Scanlists: check references to channels. + for (i=0; imember[k]; + + if (cnum != 0) { + channel_t *ch = GET_CHANNEL(cnum - 1); + + if (!VALID_CHANNEL(ch)) { + fprintf(stderr, "Scanlist %d '", i+1); + print_unicode(stderr, sl->name, 16, 0); + fprintf(stderr, "': channel %d not found.\n", cnum); + nerrors++; + } + } + } + } + + // Grouplists: check references to contacts. + for (i=0; imember[k]; + + if (cnum != 0) { + contact_t *ct = GET_CONTACT(cnum - 1); + + if (!VALID_CONTACT(ct)) { + fprintf(stderr, "Grouplist %d '", i+1); + print_unicode(stderr, gl->name, 16, 0); + fprintf(stderr, "': contact %d not found.\n", cnum); + nerrors++; + } + } + } + } + + // Count contacts. + for (i=0; i 0) { + fprintf(stderr, "Total %d errors.\n", nerrors); + return 0; + } + fprintf(stderr, "Total %d channels, %d zones, %d scanlists, %d contacts, %d grouplists.\n", + nchannels, nzones, nscanlists, ncontacts, ngrouplists); +#endif + return 1; +} + +// +// TYT MD-UV380 +// +radio_device_t radio_d868uv = { + "Anytone AT-D868UV", + d868uv_download, + d868uv_upload, + d868uv_is_compatible, + d868uv_read_image, + d868uv_save_image, + d868uv_print_version, + d868uv_print_config, + d868uv_verify_config, + d868uv_parse_parameter, + d868uv_parse_header, + d868uv_parse_row, + d868uv_update_timestamp, + //d868uv_write_csv, +}; diff --git a/radio.c b/radio.c index 0c860e5..0d5592f 100644 --- a/radio.c +++ b/radio.c @@ -49,6 +49,7 @@ static struct { { "ZN><:", &radio_rt27d }, // Radtel RT-27D { "BF-5R", &radio_rd5r }, // Baofeng RD-5R { "MD-760P", &radio_gd77 }, // Radioddity GD-77, version 3.1.1 and later + { "D868UVE", &radio_d868uv }, // Anytone AT-D868UV { 0, 0 } }; diff --git a/radio.h b/radio.h index b7dabe7..99f8582 100644 --- a/radio.h +++ b/radio.h @@ -124,6 +124,7 @@ extern radio_device_t radio_dp880; // Zastone DP880 extern radio_device_t radio_rt27d; // Radtel RT-27D extern radio_device_t radio_rd5r; // Baofeng RD-5R extern radio_device_t radio_gd77; // Radioddity GD-77, version 3.1.1 and later +extern radio_device_t radio_d868uv; // Anytone AT-D868UV // // Radio: memory contents. diff --git a/serial.c b/serial.c index ba5f2d8..756708b 100644 --- a/serial.c +++ b/serial.c @@ -59,8 +59,10 @@ static char *dev_path; static const unsigned char CMD_PRG[] = "PROGRAM"; +static const unsigned char CMD_PRG2[] = "\2"; static const unsigned char CMD_QX[] = "QX\6"; -static const unsigned char CMD_ACK2[] = "\2"; +static const unsigned char CMD_ACK[] = "\6"; +static const unsigned char CMD_END[] = "END"; // // Encode the speed in bits per second into bit value @@ -250,26 +252,6 @@ again: return got; } -// -// Close the serial port. -// -void serial_close() -{ -#if defined(__WIN32__) || defined(WIN32) - if (fd != INVALID_HANDLE_VALUE) { - SetCommState(fd, &saved_mode); - CloseHandle(fd); - fd = INVALID_HANDLE_VALUE; - } -#else - if (fd >= 0) { - tcsetattr(fd, TCSANOW, &saved_mode); - close(fd); - fd = -1; - } -#endif -} - // // Open the serial port. // Return -1 on error. @@ -601,8 +583,33 @@ static int send_recv(const unsigned char *cmd, int cmdlen, return 1; } +// +// Close the serial port. +// +void serial_close() +{ +#if defined(__WIN32__) || defined(WIN32) + if (fd != INVALID_HANDLE_VALUE) { + SetCommState(fd, &saved_mode); + CloseHandle(fd); + fd = INVALID_HANDLE_VALUE; + } +#else + if (fd >= 0) { + unsigned char ack[1]; + + send_recv(CMD_END, 3, ack, 1); + + tcsetattr(fd, TCSANOW, &saved_mode); + close(fd); + fd = -1; + } +#endif +} + // // Query and return the device identification string. +// On error, return NULL. // const char *serial_identify() { @@ -614,7 +621,7 @@ const char *serial_identify() } send_recv(CMD_PRG, 7, ack, 3); - if (ack[0] != CMD_QX[0] || ack[1] != CMD_QX[1] || ack[2] != CMD_QX[2]) { + if (memcmp(ack, CMD_QX, 3) != 0) { fprintf(stderr, "%s: Wrong PRG acknowledge %02x-%02x-%02x, expected %02x-%02x-%02x\n", __func__, ack[0], ack[1], ack[2], CMD_QX[0], CMD_QX[1], CMD_QX[2]); return 0; @@ -623,9 +630,14 @@ const char *serial_identify() // Reply: // 49 44 38 36 38 55 56 45 00 56 31 30 32 00 00 06 // I D 8 6 8 U V E V 1 0 2 - send_recv(CMD_ACK2, 1, reply, 16); + send_recv(CMD_PRG2, 1, reply, 16); + if (reply[0] != 'I' || reply[15] != CMD_ACK[0]) { + fprintf(stderr, "%s: Wrong PRG2 reply %02x-...-%02x, expected %02x-...-%02x\n", + __func__, reply[0], reply[15], 'I', CMD_ACK[0]); + return 0; + } // Terminate the string. reply[8] = 0; - return (char*)reply; + return (char*)&reply[1]; } From b9ce8e4acf030f4b387f3539a28e250dfe5c6451 Mon Sep 17 00:00:00 2001 From: Serge Date: Sat, 13 Oct 2018 22:33:50 -0700 Subject: [PATCH 03/21] Read full d868uv image: 67 mbytes. --- d868uv.c | 19 ++++++------------- radio.c | 2 +- serial.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 7 ++----- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/d868uv.c b/d868uv.c index 0a5c937..adee543 100644 --- a/d868uv.c +++ b/d868uv.c @@ -41,7 +41,8 @@ #define NSCANL 250 #define NMESSAGES 100 -#define MEMSZ 0xd0000 +//#define MEMSZ 0x100000 +#define MEMSZ 0x4300000 // // Print a generic information about the device. @@ -72,12 +73,10 @@ static void d868uv_print_version(radio_device_t *radio, FILE *out) // static void d868uv_download(radio_device_t *radio) { - //TODO -#if 0 - int bno; + int addr; - for (bno=0; bno> 24; + cmd[2] = (addr + n) >> 16; + cmd[3] = (addr + n) >> 8; + cmd[4] = addr + n; + cmd[5] = DATASZ; + send_recv(cmd, 6, reply, sizeof(reply)); + if (reply[0] != CMD_WRITE[0] || reply[7+DATASZ] != CMD_ACK[0]) { + fprintf(stderr, "%s: Wrong read reply %02x-...-%02x, expected %02x-...-%02x\n", + __func__, reply[0], reply[7+DATASZ], CMD_WRITE[0], CMD_ACK[0]); + exit(-1); + } + memcpy(data + n, reply + 6, DATASZ); + } +} + +void serial_write_region(int addr, unsigned char *data, int nbytes) +{ + //TODO +#if 0 + unsigned char ack, cmd[4+32]; + int n; + + for (n=0; n> 8; + cmd[2] = addr + n; + cmd[3] = 32; + memcpy(cmd + 4, data + n, 32); + send_recv(cmd, 4+32, &ack, 1); + if (ack != CMD_ACK[0]) { + fprintf(stderr, "%s: Wrong acknowledge %#x, expected %#x\n", + __func__, ack, CMD_ACK[0]); + exit(-1); + } + } +#endif +} diff --git a/util.h b/util.h index a64cf47..037bbfa 100644 --- a/util.h +++ b/util.h @@ -83,11 +83,8 @@ void hid_write_finish(void); int serial_init(int vid, int pid); const char *serial_identify(void); void serial_close(void); -//void serial_send_recv(const unsigned char *data, unsigned nbytes, unsigned char *rdata, unsigned rlength); -//void serial_read_block(int bno, unsigned char *data, int nbytes); -//void serial_read_finish(void); -//void serial_write_block(int bno, unsigned char *data, int nbytes); -//void serial_write_finish(void); +void serial_read_region(int addr, unsigned char *data, int nbytes); +void serial_write_region(int addr, unsigned char *data, int nbytes); // // Delay in milliseconds. From 0326291227ff6b50e89aecbf7020c614c3c79ff7 Mon Sep 17 00:00:00 2001 From: Serge Date: Sat, 13 Oct 2018 23:14:28 -0700 Subject: [PATCH 04/21] Use checksums for d868uv. --- d868uv.c | 4 ++-- serial.c | 47 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/d868uv.c b/d868uv.c index adee543..cb0c1bf 100644 --- a/d868uv.c +++ b/d868uv.c @@ -41,8 +41,8 @@ #define NSCANL 250 #define NMESSAGES 100 -//#define MEMSZ 0x100000 -#define MEMSZ 0x4300000 +#define MEMSZ 0x10000 +//#define MEMSZ 0x4300000 // // Print a generic information about the device. diff --git a/serial.c b/serial.c index ab28f75..f7f3632 100644 --- a/serial.c +++ b/serial.c @@ -648,7 +648,7 @@ void serial_read_region(int addr, unsigned char *data, int nbytes) { static const int DATASZ = 64; unsigned char cmd[6], reply[8 + DATASZ]; - int n; + int n, i, retry = 0; for (n=0; n> 8; cmd[4] = addr + n; cmd[5] = DATASZ; +again: send_recv(cmd, 6, reply, sizeof(reply)); if (reply[0] != CMD_WRITE[0] || reply[7+DATASZ] != CMD_ACK[0]) { fprintf(stderr, "%s: Wrong read reply %02x-...-%02x, expected %02x-...-%02x\n", __func__, reply[0], reply[7+DATASZ], CMD_WRITE[0], CMD_ACK[0]); exit(-1); } + + // Compute checksum. + unsigned char sum = reply[1]; + for (i=2; i<6+DATASZ; i++) + sum += reply[i]; + if (reply[6+DATASZ] != sum) { + fprintf(stderr, "%s: Wrong read checksum %02x, expected %02x\n", + __func__, sum, reply[6+DATASZ]); + if (retry++ < 3) + goto again; + exit(-1); + } + memcpy(data + n, reply + 6, DATASZ); } } void serial_write_region(int addr, unsigned char *data, int nbytes) { - //TODO -#if 0 - unsigned char ack, cmd[4+32]; - int n; + static const int DATASZ = 64; + unsigned char ack, cmd[8 + DATASZ]; + int n, i; - for (n=0; n> 8; - cmd[2] = addr + n; - cmd[3] = 32; - memcpy(cmd + 4, data + n, 32); - send_recv(cmd, 4+32, &ack, 1); + cmd[1] = (addr + n) >> 24; + cmd[2] = (addr + n) >> 16; + cmd[3] = (addr + n) >> 8; + cmd[4] = addr + n; + cmd[5] = DATASZ; + memcpy(cmd + 6, data + n, DATASZ); + + // Compute checksum. + unsigned char sum = cmd[1]; + for (i=2; i<6+DATASZ; i++) + sum += cmd[i]; + + cmd[6 + DATASZ] = sum; + cmd[7 + DATASZ] = CMD_ACK[0]; + + send_recv(cmd, 8 + DATASZ, &ack, 1); if (ack != CMD_ACK[0]) { fprintf(stderr, "%s: Wrong acknowledge %#x, expected %#x\n", __func__, ack, CMD_ACK[0]); exit(-1); } } -#endif } From 100cdc3a135f3dc108218d94792fe2f2459adc01 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Mon, 15 Oct 2018 12:49:23 -0700 Subject: [PATCH 05/21] Add d868uv address map. --- d868uv-map.h | 580 +++++++++++++++++++++++++++++++++++++++++++++++++++ d868uv.c | 85 ++++++-- 2 files changed, 646 insertions(+), 19 deletions(-) create mode 100644 d868uv-map.h diff --git a/d868uv-map.h b/d868uv-map.h new file mode 100644 index 0000000..db7fe29 --- /dev/null +++ b/d868uv-map.h @@ -0,0 +1,580 @@ +/* + * Address map of D868UV memory space. + */ +{ 0x02fa0010, 0x40 }, // Radio name and timestamp +{ 0x00800000, 0x2000 }, // Channels 1-128 +{ 0x00840000, 0x2000 }, // Channels 129-256 +{ 0x00880000, 0x2000 }, // Channels 257-384 +{ 0x008c0000, 0x2000 }, // Channels 385-512 +{ 0x00900000, 0x2000 }, // Channels 513-640 +{ 0x00940000, 0x2000 }, // Channels 641-768 +{ 0x00980000, 0x2000 }, // Channels 769-896 +{ 0x009c0000, 0x2000 }, // Channels 897-1024 +{ 0x00a00000, 0x2000 }, // Channels 1025-1152 +{ 0x00a40000, 0x2000 }, // Channels 1153-1280 +{ 0x00a80000, 0x2000 }, // Channels 1281-1408 +{ 0x00ac0000, 0x2000 }, // Channels 1409-1536 +{ 0x00b00000, 0x2000 }, // Channels 1537-1664 +{ 0x00b40000, 0x2000 }, // Channels 1665-1792 +{ 0x00b80000, 0x2000 }, // Channels 1793-1920 +{ 0x00bc0000, 0x2000 }, // Channels 1921-2048 +{ 0x00c00000, 0x2000 }, // Channels 2049-2176 +{ 0x00c40000, 0x2000 }, // Channels 2177-2304 +{ 0x00c80000, 0x2000 }, // Channels 2305-2432 +{ 0x00cc0000, 0x2000 }, // Channels 2433-2560 +{ 0x00d00000, 0x2000 }, // Channels 2561-2688 +{ 0x00d40000, 0x2000 }, // Channels 2689-2816 +{ 0x00d80000, 0x2000 }, // Channels 2817-2944 +{ 0x00dc0000, 0x2000 }, // Channels 2945-3072 +{ 0x00e00000, 0x2000 }, // Channels 3073-3200 +{ 0x00e40000, 0x2000 }, // Channels 3201-3328 +{ 0x00e80000, 0x2000 }, // Channels 3329-3456 +{ 0x00ec0000, 0x2000 }, // Channels 3457-3584 +{ 0x00f00000, 0x2000 }, // Channels 3585-3712 +{ 0x00f40000, 0x2000 }, // Channels 3713-3840 +{ 0x00f80000, 0x2000 }, // Channels 3841-3968 +{ 0x00fc0000, 0x0880 }, // Channels 3969-4000, VFO A, VFO B +{ 0x01000000, 0x1f400 }, // Channel bitmaps? +{ 0x01080000, 0xc0 }, // Scanlist 1 +{ 0x01080200, 0xc0 }, // Scanlist 2 +{ 0x01080400, 0xc0 }, // Scanlist 3 +{ 0x01080600, 0xc0 }, // Scanlist 4 +{ 0x01080800, 0xc0 }, // Scanlist 5 +{ 0x01080a00, 0xc0 }, // Scanlist 6 +{ 0x01080c00, 0xc0 }, // Scanlist 7 +{ 0x01080e00, 0xc0 }, // Scanlist 8 +{ 0x01081000, 0xc0 }, // Scanlist 9 +{ 0x01081200, 0xc0 }, // Scanlist 10 +{ 0x01081400, 0xc0 }, // Scanlist 11 +{ 0x01081600, 0xc0 }, // Scanlist 12 +{ 0x01081800, 0xc0 }, // Scanlist 13 +{ 0x01081a00, 0xc0 }, // Scanlist 14 +{ 0x01081c00, 0xc0 }, // Scanlist 15 +{ 0x01081e00, 0xc0 }, // Scanlist 16 +{ 0x010c0000, 0xc0 }, // Scanlist 17 +{ 0x010c0200, 0xc0 }, // Scanlist 18 +{ 0x010c0400, 0xc0 }, // Scanlist 19 +{ 0x010c0600, 0xc0 }, // Scanlist 20 +{ 0x010c0800, 0xc0 }, // Scanlist 21 +{ 0x010c0a00, 0xc0 }, // Scanlist 22 +{ 0x010c0c00, 0xc0 }, // Scanlist 23 +{ 0x010c0e00, 0xc0 }, // Scanlist 24 +{ 0x010c1000, 0xc0 }, // Scanlist 25 +{ 0x010c1200, 0xc0 }, // Scanlist 26 +{ 0x010c1400, 0xc0 }, // Scanlist 27 +{ 0x010c1600, 0xc0 }, // Scanlist 28 +{ 0x010c1800, 0xc0 }, // Scanlist 29 +{ 0x010c1a00, 0xc0 }, // Scanlist 30 +{ 0x010c1c00, 0xc0 }, // Scanlist 31 +{ 0x010c1e00, 0xc0 }, // Scanlist 32 +{ 0x01100000, 0xc0 }, // Scanlist 33 +{ 0x01100200, 0xc0 }, // Scanlist 34 +{ 0x01100400, 0xc0 }, // Scanlist 35 +{ 0x01100600, 0xc0 }, // Scanlist 36 +{ 0x01100800, 0xc0 }, // Scanlist 37 +{ 0x01100a00, 0xc0 }, // Scanlist 38 +{ 0x01100c00, 0xc0 }, // Scanlist 39 +{ 0x01100e00, 0xc0 }, // Scanlist 40 +{ 0x01101000, 0xc0 }, // Scanlist 41 +{ 0x01101200, 0xc0 }, // Scanlist 42 +{ 0x01101400, 0xc0 }, // Scanlist 43 +{ 0x01101600, 0xc0 }, // Scanlist 44 +{ 0x01101800, 0xc0 }, // Scanlist 45 +{ 0x01101a00, 0xc0 }, // Scanlist 46 +{ 0x01101c00, 0xc0 }, // Scanlist 47 +{ 0x01101e00, 0xc0 }, // Scanlist 48 +{ 0x01140000, 0xc0 }, // Scanlist 49 +{ 0x01140200, 0xc0 }, // Scanlist 50 +{ 0x01140400, 0xc0 }, // Scanlist 51 +{ 0x01140600, 0xc0 }, // Scanlist 52 +{ 0x01140800, 0xc0 }, // Scanlist 53 +{ 0x01140a00, 0xc0 }, // Scanlist 54 +{ 0x01140c00, 0xc0 }, // Scanlist 55 +{ 0x01140e00, 0xc0 }, // Scanlist 56 +{ 0x01141000, 0xc0 }, // Scanlist 57 +{ 0x01141200, 0xc0 }, // Scanlist 58 +{ 0x01141400, 0xc0 }, // Scanlist 59 +{ 0x01141600, 0xc0 }, // Scanlist 60 +{ 0x01141800, 0xc0 }, // Scanlist 61 +{ 0x01141a00, 0xc0 }, // Scanlist 62 +{ 0x01141c00, 0xc0 }, // Scanlist 63 +{ 0x01141e00, 0xc0 }, // Scanlist 64 +{ 0x01180000, 0xc0 }, // Scanlist 65 +{ 0x01180200, 0xc0 }, // Scanlist 66 +{ 0x01180400, 0xc0 }, // Scanlist 67 +{ 0x01180600, 0xc0 }, // Scanlist 68 +{ 0x01180800, 0xc0 }, // Scanlist 69 +{ 0x01180a00, 0xc0 }, // Scanlist 70 +{ 0x01180c00, 0xc0 }, // Scanlist 71 +{ 0x01180e00, 0xc0 }, // Scanlist 72 +{ 0x01181000, 0xc0 }, // Scanlist 73 +{ 0x01181200, 0xc0 }, // Scanlist 74 +{ 0x01181400, 0xc0 }, // Scanlist 75 +{ 0x01181600, 0xc0 }, // Scanlist 76 +{ 0x01181800, 0xc0 }, // Scanlist 77 +{ 0x01181a00, 0xc0 }, // Scanlist 78 +{ 0x01181c00, 0xc0 }, // Scanlist 79 +{ 0x01181e00, 0xc0 }, // Scanlist 80 +{ 0x011c0000, 0xc0 }, // Scanlist 81 +{ 0x011c0200, 0xc0 }, // Scanlist 82 +{ 0x011c0400, 0xc0 }, // Scanlist 83 +{ 0x011c0600, 0xc0 }, // Scanlist 84 +{ 0x011c0800, 0xc0 }, // Scanlist 85 +{ 0x011c0a00, 0xc0 }, // Scanlist 86 +{ 0x011c0c00, 0xc0 }, // Scanlist 87 +{ 0x011c0e00, 0xc0 }, // Scanlist 88 +{ 0x011c1000, 0xc0 }, // Scanlist 89 +{ 0x011c1200, 0xc0 }, // Scanlist 90 +{ 0x011c1400, 0xc0 }, // Scanlist 91 +{ 0x011c1600, 0xc0 }, // Scanlist 92 +{ 0x011c1800, 0xc0 }, // Scanlist 93 +{ 0x011c1a00, 0xc0 }, // Scanlist 94 +{ 0x011c1c00, 0xc0 }, // Scanlist 95 +{ 0x011c1e00, 0xc0 }, // Scanlist 96 +{ 0x01200000, 0xc0 }, // Scanlist 97 +{ 0x01200200, 0xc0 }, // Scanlist 98 +{ 0x01200400, 0xc0 }, // Scanlist 99 +{ 0x01200600, 0xc0 }, // Scanlist 100 +{ 0x01200800, 0xc0 }, // Scanlist 101 +{ 0x01200a00, 0xc0 }, // Scanlist 102 +{ 0x01200c00, 0xc0 }, // Scanlist 103 +{ 0x01200e00, 0xc0 }, // Scanlist 104 +{ 0x01201000, 0xc0 }, // Scanlist 105 +{ 0x01201200, 0xc0 }, // Scanlist 106 +{ 0x01201400, 0xc0 }, // Scanlist 107 +{ 0x01201600, 0xc0 }, // Scanlist 108 +{ 0x01201800, 0xc0 }, // Scanlist 109 +{ 0x01201a00, 0xc0 }, // Scanlist 110 +{ 0x01201c00, 0xc0 }, // Scanlist 111 +{ 0x01201e00, 0xc0 }, // Scanlist 112 +{ 0x01240000, 0xc0 }, // Scanlist 113 +{ 0x01240200, 0xc0 }, // Scanlist 114 +{ 0x01240400, 0xc0 }, // Scanlist 115 +{ 0x01240600, 0xc0 }, // Scanlist 116 +{ 0x01240800, 0xc0 }, // Scanlist 117 +{ 0x01240a00, 0xc0 }, // Scanlist 118 +{ 0x01240c00, 0xc0 }, // Scanlist 119 +{ 0x01240e00, 0xc0 }, // Scanlist 120 +{ 0x01241000, 0xc0 }, // Scanlist 121 +{ 0x01241200, 0xc0 }, // Scanlist 122 +{ 0x01241400, 0xc0 }, // Scanlist 123 +{ 0x01241600, 0xc0 }, // Scanlist 124 +{ 0x01241800, 0xc0 }, // Scanlist 125 +{ 0x01241a00, 0xc0 }, // Scanlist 126 +{ 0x01241c00, 0xc0 }, // Scanlist 127 +{ 0x01241e00, 0xc0 }, // Scanlist 128 +{ 0x01280000, 0xc0 }, // Scanlist 129 +{ 0x01280200, 0xc0 }, // Scanlist 130 +{ 0x01280400, 0xc0 }, // Scanlist 131 +{ 0x01280600, 0xc0 }, // Scanlist 132 +{ 0x01280800, 0xc0 }, // Scanlist 133 +{ 0x01280a00, 0xc0 }, // Scanlist 134 +{ 0x01280c00, 0xc0 }, // Scanlist 135 +{ 0x01280e00, 0xc0 }, // Scanlist 136 +{ 0x01281000, 0xc0 }, // Scanlist 137 +{ 0x01281200, 0xc0 }, // Scanlist 138 +{ 0x01281400, 0xc0 }, // Scanlist 139 +{ 0x01281600, 0xc0 }, // Scanlist 140 +{ 0x01281800, 0xc0 }, // Scanlist 141 +{ 0x01281a00, 0xc0 }, // Scanlist 142 +{ 0x01281c00, 0xc0 }, // Scanlist 143 +{ 0x01281e00, 0xc0 }, // Scanlist 144 +{ 0x012c0000, 0xc0 }, // Scanlist 145 +{ 0x012c0200, 0xc0 }, // Scanlist 146 +{ 0x012c0400, 0xc0 }, // Scanlist 147 +{ 0x012c0600, 0xc0 }, // Scanlist 148 +{ 0x012c0800, 0xc0 }, // Scanlist 149 +{ 0x012c0a00, 0xc0 }, // Scanlist 150 +{ 0x012c0c00, 0xc0 }, // Scanlist 151 +{ 0x012c0e00, 0xc0 }, // Scanlist 152 +{ 0x012c1000, 0xc0 }, // Scanlist 153 +{ 0x012c1200, 0xc0 }, // Scanlist 154 +{ 0x012c1400, 0xc0 }, // Scanlist 155 +{ 0x012c1600, 0xc0 }, // Scanlist 156 +{ 0x012c1800, 0xc0 }, // Scanlist 157 +{ 0x012c1a00, 0xc0 }, // Scanlist 158 +{ 0x012c1c00, 0xc0 }, // Scanlist 159 +{ 0x012c1e00, 0xc0 }, // Scanlist 160 +{ 0x01300000, 0xc0 }, // Scanlist 161 +{ 0x01300200, 0xc0 }, // Scanlist 162 +{ 0x01300400, 0xc0 }, // Scanlist 163 +{ 0x01300600, 0xc0 }, // Scanlist 164 +{ 0x01300800, 0xc0 }, // Scanlist 165 +{ 0x01300a00, 0xc0 }, // Scanlist 166 +{ 0x01300c00, 0xc0 }, // Scanlist 167 +{ 0x01300e00, 0xc0 }, // Scanlist 168 +{ 0x01301000, 0xc0 }, // Scanlist 169 +{ 0x01301200, 0xc0 }, // Scanlist 170 +{ 0x01301400, 0xc0 }, // Scanlist 171 +{ 0x01301600, 0xc0 }, // Scanlist 172 +{ 0x01301800, 0xc0 }, // Scanlist 173 +{ 0x01301a00, 0xc0 }, // Scanlist 174 +{ 0x01301c00, 0xc0 }, // Scanlist 175 +{ 0x01301e00, 0xc0 }, // Scanlist 176 +{ 0x01340000, 0xc0 }, // Scanlist 177 +{ 0x01340200, 0xc0 }, // Scanlist 178 +{ 0x01340400, 0xc0 }, // Scanlist 179 +{ 0x01340600, 0xc0 }, // Scanlist 180 +{ 0x01340800, 0xc0 }, // Scanlist 181 +{ 0x01340a00, 0xc0 }, // Scanlist 182 +{ 0x01340c00, 0xc0 }, // Scanlist 183 +{ 0x01340e00, 0xc0 }, // Scanlist 184 +{ 0x01341000, 0xc0 }, // Scanlist 185 +{ 0x01341200, 0xc0 }, // Scanlist 186 +{ 0x01341400, 0xc0 }, // Scanlist 187 +{ 0x01341600, 0xc0 }, // Scanlist 188 +{ 0x01341800, 0xc0 }, // Scanlist 189 +{ 0x01341a00, 0xc0 }, // Scanlist 190 +{ 0x01341c00, 0xc0 }, // Scanlist 191 +{ 0x01341e00, 0xc0 }, // Scanlist 192 +{ 0x01380000, 0xc0 }, // Scanlist 193 +{ 0x01380200, 0xc0 }, // Scanlist 194 +{ 0x01380400, 0xc0 }, // Scanlist 195 +{ 0x01380600, 0xc0 }, // Scanlist 196 +{ 0x01380800, 0xc0 }, // Scanlist 197 +{ 0x01380a00, 0xc0 }, // Scanlist 198 +{ 0x01380c00, 0xc0 }, // Scanlist 199 +{ 0x01380e00, 0xc0 }, // Scanlist 200 +{ 0x01381000, 0xc0 }, // Scanlist 201 +{ 0x01381200, 0xc0 }, // Scanlist 202 +{ 0x01381400, 0xc0 }, // Scanlist 203 +{ 0x01381600, 0xc0 }, // Scanlist 204 +{ 0x01381800, 0xc0 }, // Scanlist 205 +{ 0x01381a00, 0xc0 }, // Scanlist 206 +{ 0x01381c00, 0xc0 }, // Scanlist 207 +{ 0x01381e00, 0xc0 }, // Scanlist 208 +{ 0x013c0000, 0xc0 }, // Scanlist 209 +{ 0x013c0200, 0xc0 }, // Scanlist 210 +{ 0x013c0400, 0xc0 }, // Scanlist 211 +{ 0x013c0600, 0xc0 }, // Scanlist 212 +{ 0x013c0800, 0xc0 }, // Scanlist 213 +{ 0x013c0a00, 0xc0 }, // Scanlist 214 +{ 0x013c0c00, 0xc0 }, // Scanlist 215 +{ 0x013c0e00, 0xc0 }, // Scanlist 216 +{ 0x013c1000, 0xc0 }, // Scanlist 217 +{ 0x013c1200, 0xc0 }, // Scanlist 218 +{ 0x013c1400, 0xc0 }, // Scanlist 219 +{ 0x013c1600, 0xc0 }, // Scanlist 220 +{ 0x013c1800, 0xc0 }, // Scanlist 221 +{ 0x013c1a00, 0xc0 }, // Scanlist 222 +{ 0x013c1c00, 0xc0 }, // Scanlist 223 +{ 0x013c1e00, 0xc0 }, // Scanlist 224 +{ 0x01400000, 0xc0 }, // Scanlist 225 +{ 0x01400200, 0xc0 }, // Scanlist 226 +{ 0x01400400, 0xc0 }, // Scanlist 227 +{ 0x01400600, 0xc0 }, // Scanlist 228 +{ 0x01400800, 0xc0 }, // Scanlist 229 +{ 0x01400a00, 0xc0 }, // Scanlist 230 +{ 0x01400c00, 0xc0 }, // Scanlist 231 +{ 0x01400e00, 0xc0 }, // Scanlist 232 +{ 0x01401000, 0xc0 }, // Scanlist 233 +{ 0x01401200, 0xc0 }, // Scanlist 234 +{ 0x01401400, 0xc0 }, // Scanlist 235 +{ 0x01401600, 0xc0 }, // Scanlist 236 +{ 0x01401800, 0xc0 }, // Scanlist 237 +{ 0x01401a00, 0xc0 }, // Scanlist 238 +{ 0x01401c00, 0xc0 }, // Scanlist 239 +{ 0x01401e00, 0xc0 }, // Scanlist 240 +{ 0x01440000, 0xc0 }, // Scanlist 241 +{ 0x01440200, 0xc0 }, // Scanlist 242 +{ 0x01440400, 0xc0 }, // Scanlist 243 +{ 0x01440600, 0xc0 }, // Scanlist 244 +{ 0x01440800, 0xc0 }, // Scanlist 245 +{ 0x01440a00, 0xc0 }, // Scanlist 246 +{ 0x01440c00, 0xc0 }, // Scanlist 247 +{ 0x01440e00, 0xc0 }, // Scanlist 248 +{ 0x01441000, 0xc0 }, // Scanlist 249 +{ 0x01441200, 0xc0 }, // Scanlist 250 +{ 0x01640000, 0x640 }, // Unknown index +{ 0x01640800, 0xc0 }, // Zeroes? +{ 0x02140000, 0x800 }, // Messages 1-8 +{ 0x02180000, 0x800 }, // Messages 9-16 +{ 0x021c0000, 0x800 }, // Messages 17-24 +{ 0x02200000, 0x800 }, // Messages 25-32 +{ 0x02240000, 0x800 }, // Messages 33-40 +{ 0x02280000, 0x800 }, // Messages 41-48 +{ 0x022c0000, 0x800 }, // Messages 49-56 +{ 0x02300000, 0x800 }, // Messages 57-64 +{ 0x02340000, 0x800 }, // Messages 65-72 +{ 0x02380000, 0x800 }, // Messages 73-80 +{ 0x023c0000, 0x800 }, // Messages 81-88 +{ 0x02400000, 0x800 }, // Messages 89-96 +{ 0x02440000, 0x400 }, // Messages 97-100 +{ 0x02480000, 0x1c0 }, // Unknown index +{ 0x02480200, 0x40 }, // Unknown bitmap +{ 0x024c0000, 0x40 }, // Unknown data +{ 0x024c0c80, 0x40 }, // Unknown data +{ 0x024c0d00, 0x200 }, // Zeroes? +{ 0x024c1000, 0x140 }, // Unknown data +{ 0x024c1280, 0x40 }, // Unknown data +{ 0x024c1300, 0x80 }, // Unknown data +{ 0x024c1400, 0x80 }, // Unknown data +{ 0x024c1500, 0x240 }, // Unknown data +{ 0x024c1800, 0x500 }, // Zeroes? +{ 0x024c2000, 0x440 }, // Unknown data +{ 0x024c2600, 0x40 }, // Unknown data +{ 0x02500000, 0x640 }, // Unknown data +{ 0x02501000, 0x40 }, // Unknown data +{ 0x02501100, 0x140 }, // GPS message +{ 0x02540000, 0x1f40 }, // Zones 1-250 +{ 0x02580000, 0x1f40 }, // Radio ID 1-250 +{ 0x025c0000, 0x880 }, // Unknown data, status message +{ 0x025c0b00, 0x40 }, // Unknown data +{ 0x02600000, 0x9c40 }, // Unknown data, bitmap +{ 0x02640000, 0x500 }, // Unknown data, bitmap +{ 0x02680000, 0xf4240 }, // Contacts 1-10000 +{ 0x02900000, 0x80 }, // Unknown index +{ 0x02900100, 0x80 }, // Unknown index +{ 0x02940000, 0x180 }, // AnalogContacts 1-128 +{ 0x02980000, 0x140 }, // Grouplist 1 +{ 0x02980200, 0x140 }, // Grouplist 2 +{ 0x02980400, 0x140 }, // Grouplist 3 +{ 0x02980600, 0x140 }, // Grouplist 4 +{ 0x02980800, 0x140 }, // Grouplist 5 +{ 0x02980a00, 0x140 }, // Grouplist 6 +{ 0x02980c00, 0x140 }, // Grouplist 7 +{ 0x02980e00, 0x140 }, // Grouplist 8 +{ 0x02981000, 0x140 }, // Grouplist 9 +{ 0x02981200, 0x140 }, // Grouplist 10 +{ 0x02981400, 0x140 }, // Grouplist 11 +{ 0x02981600, 0x140 }, // Grouplist 12 +{ 0x02981800, 0x140 }, // Grouplist 13 +{ 0x02981a00, 0x140 }, // Grouplist 14 +{ 0x02981c00, 0x140 }, // Grouplist 15 +{ 0x02981e00, 0x140 }, // Grouplist 16 +{ 0x02982000, 0x140 }, // Grouplist 17 +{ 0x02982200, 0x140 }, // Grouplist 18 +{ 0x02982400, 0x140 }, // Grouplist 19 +{ 0x02982600, 0x140 }, // Grouplist 20 +{ 0x02982800, 0x140 }, // Grouplist 21 +{ 0x02982a00, 0x140 }, // Grouplist 22 +{ 0x02982c00, 0x140 }, // Grouplist 23 +{ 0x02982e00, 0x140 }, // Grouplist 24 +{ 0x02983000, 0x140 }, // Grouplist 25 +{ 0x02983200, 0x140 }, // Grouplist 26 +{ 0x02983400, 0x140 }, // Grouplist 27 +{ 0x02983600, 0x140 }, // Grouplist 28 +{ 0x02983800, 0x140 }, // Grouplist 29 +{ 0x02983a00, 0x140 }, // Grouplist 30 +{ 0x02983c00, 0x140 }, // Grouplist 31 +{ 0x02983e00, 0x140 }, // Grouplist 32 +{ 0x02984000, 0x140 }, // Grouplist 33 +{ 0x02984200, 0x140 }, // Grouplist 34 +{ 0x02984400, 0x140 }, // Grouplist 35 +{ 0x02984600, 0x140 }, // Grouplist 36 +{ 0x02984800, 0x140 }, // Grouplist 37 +{ 0x02984a00, 0x140 }, // Grouplist 38 +{ 0x02984c00, 0x140 }, // Grouplist 39 +{ 0x02984e00, 0x140 }, // Grouplist 40 +{ 0x02985000, 0x140 }, // Grouplist 41 +{ 0x02985200, 0x140 }, // Grouplist 42 +{ 0x02985400, 0x140 }, // Grouplist 43 +{ 0x02985600, 0x140 }, // Grouplist 44 +{ 0x02985800, 0x140 }, // Grouplist 45 +{ 0x02985a00, 0x140 }, // Grouplist 46 +{ 0x02985c00, 0x140 }, // Grouplist 47 +{ 0x02985e00, 0x140 }, // Grouplist 48 +{ 0x02986000, 0x140 }, // Grouplist 49 +{ 0x02986200, 0x140 }, // Grouplist 50 +{ 0x02986400, 0x140 }, // Grouplist 51 +{ 0x02986600, 0x140 }, // Grouplist 52 +{ 0x02986800, 0x140 }, // Grouplist 53 +{ 0x02986a00, 0x140 }, // Grouplist 54 +{ 0x02986c00, 0x140 }, // Grouplist 55 +{ 0x02986e00, 0x140 }, // Grouplist 56 +{ 0x02987000, 0x140 }, // Grouplist 57 +{ 0x02987200, 0x140 }, // Grouplist 58 +{ 0x02987400, 0x140 }, // Grouplist 59 +{ 0x02987600, 0x140 }, // Grouplist 60 +{ 0x02987800, 0x140 }, // Grouplist 61 +{ 0x02987a00, 0x140 }, // Grouplist 62 +{ 0x02987c00, 0x140 }, // Grouplist 63 +{ 0x02987e00, 0x140 }, // Grouplist 64 +{ 0x02988000, 0x140 }, // Grouplist 65 +{ 0x02988200, 0x140 }, // Grouplist 66 +{ 0x02988400, 0x140 }, // Grouplist 67 +{ 0x02988600, 0x140 }, // Grouplist 68 +{ 0x02988800, 0x140 }, // Grouplist 69 +{ 0x02988a00, 0x140 }, // Grouplist 70 +{ 0x02988c00, 0x140 }, // Grouplist 71 +{ 0x02988e00, 0x140 }, // Grouplist 72 +{ 0x02989000, 0x140 }, // Grouplist 73 +{ 0x02989200, 0x140 }, // Grouplist 74 +{ 0x02989400, 0x140 }, // Grouplist 75 +{ 0x02989600, 0x140 }, // Grouplist 76 +{ 0x02989800, 0x140 }, // Grouplist 77 +{ 0x02989a00, 0x140 }, // Grouplist 78 +{ 0x02989c00, 0x140 }, // Grouplist 79 +{ 0x02989e00, 0x140 }, // Grouplist 80 +{ 0x0298a000, 0x140 }, // Grouplist 81 +{ 0x0298a200, 0x140 }, // Grouplist 82 +{ 0x0298a400, 0x140 }, // Grouplist 83 +{ 0x0298a600, 0x140 }, // Grouplist 84 +{ 0x0298a800, 0x140 }, // Grouplist 85 +{ 0x0298aa00, 0x140 }, // Grouplist 86 +{ 0x0298ac00, 0x140 }, // Grouplist 87 +{ 0x0298ae00, 0x140 }, // Grouplist 88 +{ 0x0298b000, 0x140 }, // Grouplist 89 +{ 0x0298b200, 0x140 }, // Grouplist 90 +{ 0x0298b400, 0x140 }, // Grouplist 91 +{ 0x0298b600, 0x140 }, // Grouplist 92 +{ 0x0298b800, 0x140 }, // Grouplist 93 +{ 0x0298ba00, 0x140 }, // Grouplist 94 +{ 0x0298bc00, 0x140 }, // Grouplist 95 +{ 0x0298be00, 0x140 }, // Grouplist 96 +{ 0x0298c000, 0x140 }, // Grouplist 97 +{ 0x0298c200, 0x140 }, // Grouplist 98 +{ 0x0298c400, 0x140 }, // Grouplist 99 +{ 0x0298c600, 0x140 }, // Grouplist 100 +{ 0x0298c800, 0x140 }, // Grouplist 101 +{ 0x0298ca00, 0x140 }, // Grouplist 102 +{ 0x0298cc00, 0x140 }, // Grouplist 103 +{ 0x0298ce00, 0x140 }, // Grouplist 104 +{ 0x0298d000, 0x140 }, // Grouplist 105 +{ 0x0298d200, 0x140 }, // Grouplist 106 +{ 0x0298d400, 0x140 }, // Grouplist 107 +{ 0x0298d600, 0x140 }, // Grouplist 108 +{ 0x0298d800, 0x140 }, // Grouplist 109 +{ 0x0298da00, 0x140 }, // Grouplist 110 +{ 0x0298dc00, 0x140 }, // Grouplist 111 +{ 0x0298de00, 0x140 }, // Grouplist 112 +{ 0x0298e000, 0x140 }, // Grouplist 113 +{ 0x0298e200, 0x140 }, // Grouplist 114 +{ 0x0298e400, 0x140 }, // Grouplist 115 +{ 0x0298e600, 0x140 }, // Grouplist 116 +{ 0x0298e800, 0x140 }, // Grouplist 117 +{ 0x0298ea00, 0x140 }, // Grouplist 118 +{ 0x0298ec00, 0x140 }, // Grouplist 119 +{ 0x0298ee00, 0x140 }, // Grouplist 120 +{ 0x0298f000, 0x140 }, // Grouplist 121 +{ 0x0298f200, 0x140 }, // Grouplist 122 +{ 0x0298f400, 0x140 }, // Grouplist 123 +{ 0x0298f600, 0x140 }, // Grouplist 124 +{ 0x0298f800, 0x140 }, // Grouplist 125 +{ 0x0298fa00, 0x140 }, // Grouplist 126 +{ 0x0298fc00, 0x140 }, // Grouplist 127 +{ 0x0298fe00, 0x140 }, // Grouplist 128 +{ 0x02990000, 0x140 }, // Grouplist 129 +{ 0x02990200, 0x140 }, // Grouplist 130 +{ 0x02990400, 0x140 }, // Grouplist 131 +{ 0x02990600, 0x140 }, // Grouplist 132 +{ 0x02990800, 0x140 }, // Grouplist 133 +{ 0x02990a00, 0x140 }, // Grouplist 134 +{ 0x02990c00, 0x140 }, // Grouplist 135 +{ 0x02990e00, 0x140 }, // Grouplist 136 +{ 0x02991000, 0x140 }, // Grouplist 137 +{ 0x02991200, 0x140 }, // Grouplist 138 +{ 0x02991400, 0x140 }, // Grouplist 139 +{ 0x02991600, 0x140 }, // Grouplist 140 +{ 0x02991800, 0x140 }, // Grouplist 141 +{ 0x02991a00, 0x140 }, // Grouplist 142 +{ 0x02991c00, 0x140 }, // Grouplist 143 +{ 0x02991e00, 0x140 }, // Grouplist 144 +{ 0x02992000, 0x140 }, // Grouplist 145 +{ 0x02992200, 0x140 }, // Grouplist 146 +{ 0x02992400, 0x140 }, // Grouplist 147 +{ 0x02992600, 0x140 }, // Grouplist 148 +{ 0x02992800, 0x140 }, // Grouplist 149 +{ 0x02992a00, 0x140 }, // Grouplist 150 +{ 0x02992c00, 0x140 }, // Grouplist 151 +{ 0x02992e00, 0x140 }, // Grouplist 152 +{ 0x02993000, 0x140 }, // Grouplist 153 +{ 0x02993200, 0x140 }, // Grouplist 154 +{ 0x02993400, 0x140 }, // Grouplist 155 +{ 0x02993600, 0x140 }, // Grouplist 156 +{ 0x02993800, 0x140 }, // Grouplist 157 +{ 0x02993a00, 0x140 }, // Grouplist 158 +{ 0x02993c00, 0x140 }, // Grouplist 159 +{ 0x02993e00, 0x140 }, // Grouplist 160 +{ 0x02994000, 0x140 }, // Grouplist 161 +{ 0x02994200, 0x140 }, // Grouplist 162 +{ 0x02994400, 0x140 }, // Grouplist 163 +{ 0x02994600, 0x140 }, // Grouplist 164 +{ 0x02994800, 0x140 }, // Grouplist 165 +{ 0x02994a00, 0x140 }, // Grouplist 166 +{ 0x02994c00, 0x140 }, // Grouplist 167 +{ 0x02994e00, 0x140 }, // Grouplist 168 +{ 0x02995000, 0x140 }, // Grouplist 169 +{ 0x02995200, 0x140 }, // Grouplist 170 +{ 0x02995400, 0x140 }, // Grouplist 171 +{ 0x02995600, 0x140 }, // Grouplist 172 +{ 0x02995800, 0x140 }, // Grouplist 173 +{ 0x02995a00, 0x140 }, // Grouplist 174 +{ 0x02995c00, 0x140 }, // Grouplist 175 +{ 0x02995e00, 0x140 }, // Grouplist 176 +{ 0x02996000, 0x140 }, // Grouplist 177 +{ 0x02996200, 0x140 }, // Grouplist 178 +{ 0x02996400, 0x140 }, // Grouplist 179 +{ 0x02996600, 0x140 }, // Grouplist 180 +{ 0x02996800, 0x140 }, // Grouplist 181 +{ 0x02996a00, 0x140 }, // Grouplist 182 +{ 0x02996c00, 0x140 }, // Grouplist 183 +{ 0x02996e00, 0x140 }, // Grouplist 184 +{ 0x02997000, 0x140 }, // Grouplist 185 +{ 0x02997200, 0x140 }, // Grouplist 186 +{ 0x02997400, 0x140 }, // Grouplist 187 +{ 0x02997600, 0x140 }, // Grouplist 188 +{ 0x02997800, 0x140 }, // Grouplist 189 +{ 0x02997a00, 0x140 }, // Grouplist 190 +{ 0x02997c00, 0x140 }, // Grouplist 191 +{ 0x02997e00, 0x140 }, // Grouplist 192 +{ 0x02998000, 0x140 }, // Grouplist 193 +{ 0x02998200, 0x140 }, // Grouplist 194 +{ 0x02998400, 0x140 }, // Grouplist 195 +{ 0x02998600, 0x140 }, // Grouplist 196 +{ 0x02998800, 0x140 }, // Grouplist 197 +{ 0x02998a00, 0x140 }, // Grouplist 198 +{ 0x02998c00, 0x140 }, // Grouplist 199 +{ 0x02998e00, 0x140 }, // Grouplist 200 +{ 0x02999000, 0x140 }, // Grouplist 201 +{ 0x02999200, 0x140 }, // Grouplist 202 +{ 0x02999400, 0x140 }, // Grouplist 203 +{ 0x02999600, 0x140 }, // Grouplist 204 +{ 0x02999800, 0x140 }, // Grouplist 205 +{ 0x02999a00, 0x140 }, // Grouplist 206 +{ 0x02999c00, 0x140 }, // Grouplist 207 +{ 0x02999e00, 0x140 }, // Grouplist 208 +{ 0x0299a000, 0x140 }, // Grouplist 209 +{ 0x0299a200, 0x140 }, // Grouplist 210 +{ 0x0299a400, 0x140 }, // Grouplist 211 +{ 0x0299a600, 0x140 }, // Grouplist 212 +{ 0x0299a800, 0x140 }, // Grouplist 213 +{ 0x0299aa00, 0x140 }, // Grouplist 214 +{ 0x0299ac00, 0x140 }, // Grouplist 215 +{ 0x0299ae00, 0x140 }, // Grouplist 216 +{ 0x0299b000, 0x140 }, // Grouplist 217 +{ 0x0299b200, 0x140 }, // Grouplist 218 +{ 0x0299b400, 0x140 }, // Grouplist 219 +{ 0x0299b600, 0x140 }, // Grouplist 220 +{ 0x0299b800, 0x140 }, // Grouplist 221 +{ 0x0299ba00, 0x140 }, // Grouplist 222 +{ 0x0299bc00, 0x140 }, // Grouplist 223 +{ 0x0299be00, 0x140 }, // Grouplist 224 +{ 0x0299c000, 0x140 }, // Grouplist 225 +{ 0x0299c200, 0x140 }, // Grouplist 226 +{ 0x0299c400, 0x140 }, // Grouplist 227 +{ 0x0299c600, 0x140 }, // Grouplist 228 +{ 0x0299c800, 0x140 }, // Grouplist 229 +{ 0x0299ca00, 0x140 }, // Grouplist 230 +{ 0x0299cc00, 0x140 }, // Grouplist 231 +{ 0x0299ce00, 0x140 }, // Grouplist 232 +{ 0x0299d000, 0x140 }, // Grouplist 233 +{ 0x0299d200, 0x140 }, // Grouplist 234 +{ 0x0299d400, 0x140 }, // Grouplist 235 +{ 0x0299d600, 0x140 }, // Grouplist 236 +{ 0x0299d800, 0x140 }, // Grouplist 237 +{ 0x0299da00, 0x140 }, // Grouplist 238 +{ 0x0299dc00, 0x140 }, // Grouplist 239 +{ 0x0299de00, 0x140 }, // Grouplist 240 +{ 0x0299e000, 0x140 }, // Grouplist 241 +{ 0x0299e200, 0x140 }, // Grouplist 242 +{ 0x0299e400, 0x140 }, // Grouplist 243 +{ 0x0299e600, 0x140 }, // Grouplist 244 +{ 0x0299e800, 0x140 }, // Grouplist 245 +{ 0x0299ea00, 0x140 }, // Grouplist 246 +{ 0x0299ec00, 0x140 }, // Grouplist 247 +{ 0x0299ee00, 0x140 }, // Grouplist 248 +{ 0x0299f000, 0x140 }, // Grouplist 249 +{ 0x0299f200, 0x140 }, // Grouplist 250 +{ 0x04280000, 0x300 }, // Unknown index +{ 0, 0 }, diff --git a/d868uv.c b/d868uv.c index cb0c1bf..a5cd02b 100644 --- a/d868uv.c +++ b/d868uv.c @@ -41,8 +41,26 @@ #define NSCANL 250 #define NMESSAGES 100 -#define MEMSZ 0x10000 -//#define MEMSZ 0x4300000 +// +// Size of memory image. +// Essentialy a sum of all fragments defined ind868um-map.h. +// +#define MEMSZ 1607296 + +// +// D868UV radio has a huge internal address space, more than 64 Mbytes. +// The configuration data are dispersed over this space. +// Here is a table of fragments: starting address and length. +// We read these fragments and save them into a file continuously. +// +typedef struct { + unsigned address; + unsigned length; +} fragment_t; + +static fragment_t region_map[] = { +#include "d868uv-map.h" +}; // // Print a generic information about the device. @@ -73,17 +91,33 @@ static void d868uv_print_version(radio_device_t *radio, FILE *out) // static void d868uv_download(radio_device_t *radio) { - int addr; + fragment_t *f; + unsigned file_offset = 0; + unsigned last_printed = 0; - for (addr = 0x00000000; addr < MEMSZ; addr += 1024) { - serial_read_region(addr, &radio_mem[addr], 1024); + for (f=region_map; f->length; f++) { + unsigned addr = f->address; + unsigned nbytes = f->length; - ++radio_progress; - if (radio_progress % 32 == 0) { - fprintf(stderr, "#"); - fflush(stderr); + while (nbytes > 0) { + unsigned n = (nbytes > 32*1024) ? 32*1024 : nbytes; + serial_read_region(addr, &radio_mem[file_offset], n); + file_offset += n; + addr += n; + nbytes -= n; + + if (file_offset / (32*1024) != last_printed) { + fprintf(stderr, "#"); + fflush(stderr); + last_printed = file_offset / (32*1024); + } } } + if (file_offset != MEMSZ) { + fprintf(stderr, "\nWrong MEMSZ=%u for D868UV!\n", MEMSZ); + fprintf(stderr, "Should be %u; check d868uv-map.h!\n", file_offset); + exit(-1); + } } // @@ -91,20 +125,33 @@ static void d868uv_download(radio_device_t *radio) // static void d868uv_upload(radio_device_t *radio, int cont_flag) { - //TODO -#if 0 - int bno; + fragment_t *f; + unsigned file_offset = 0; + unsigned last_printed = 0; - for (bno=0; bnolength; f++) { + unsigned addr = f->address; + unsigned nbytes = f->length; - ++radio_progress; - if (radio_progress % 32 == 0) { - fprintf(stderr, "#"); - fflush(stderr); + while (nbytes > 0) { + unsigned n = (nbytes > 32*1024) ? 32*1024 : nbytes; + serial_write_region(addr, &radio_mem[file_offset], n); + file_offset += n; + addr += n; + nbytes -= n; + + if (file_offset / (32*1024) != last_printed) { + fprintf(stderr, "#"); + fflush(stderr); + last_printed = file_offset / (32*1024); + } } } -#endif + if (file_offset != MEMSZ) { + fprintf(stderr, "\nWrong MEMSZ=%u for D868UV!\n", MEMSZ); + fprintf(stderr, "Should be %u; check d868uv-map.h!\n", file_offset); + exit(-1); + } } // From db64c0694828bceac34f3ab89d1f34475d2f9723 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Mon, 15 Oct 2018 13:46:38 -0700 Subject: [PATCH 06/21] Recognize d868uv image size. --- d868uv.c | 51 +++++---------------------------------------------- radio.c | 3 +++ 2 files changed, 8 insertions(+), 46 deletions(-) diff --git a/d868uv.c b/d868uv.c index a5cd02b..9e7fac5 100644 --- a/d868uv.c +++ b/d868uv.c @@ -167,6 +167,10 @@ static int d868uv_is_compatible(radio_device_t *radio) // static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) { + fprintf(out, "Radio: %s\n", radio->name); + if (verbose) + d868uv_print_version(radio, out); + //TODO } @@ -175,8 +179,6 @@ static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) // static void d868uv_read_image(radio_device_t *radio, FILE *img) { - //TODO -#if 0 struct stat st; // Guess device type by file size. @@ -192,25 +194,10 @@ static void d868uv_read_image(radio_device_t *radio, FILE *img) exit(-1); } break; - case MEMSZ + 0x225 + 0x10: - // RTD file. - // Header 0x225 bytes and footer 0x10 bytes at 0x40225. - fseek(img, 0x225, SEEK_SET); - if (fread(&radio_mem[0], 1, 0x40000, img) != 0x40000) { - fprintf(stderr, "Error reading image data.\n"); - exit(-1); - } - fseek(img, 0x10, SEEK_CUR); - if (fread(&radio_mem[0x40000], 1, MEMSZ - 0x40000, img) != MEMSZ - 0x40000) { - fprintf(stderr, "Error reading image data.\n"); - exit(-1); - } - break; default: fprintf(stderr, "Unrecognized file size %u bytes.\n", (int) st.st_size); exit(-1); } -#endif } // @@ -280,35 +267,7 @@ static int d868uv_parse_row(radio_device_t *radio, int table_id, int first_row, // static void d868uv_update_timestamp(radio_device_t *radio) { - //TODO -#if 0 - unsigned char *timestamp = GET_TIMESTAMP(); - char p[16]; - - // Last Programmed Date - get_timestamp(p); - timestamp[0] = ((p[0] & 0xf) << 4) | (p[1] & 0xf); // year upper - timestamp[1] = ((p[2] & 0xf) << 4) | (p[3] & 0xf); // year lower - timestamp[2] = ((p[4] & 0xf) << 4) | (p[5] & 0xf); // month - timestamp[3] = ((p[6] & 0xf) << 4) | (p[7] & 0xf); // day - timestamp[4] = ((p[8] & 0xf) << 4) | (p[9] & 0xf); // hour - timestamp[5] = ((p[10] & 0xf) << 4) | (p[11] & 0xf); // minute - timestamp[6] = ((p[12] & 0xf) << 4) | (p[13] & 0xf); // second - - // CPS Software Version: Vdx.xx - const char *dot = strchr(VERSION, '.'); - if (dot) { - timestamp[7] = 0x0d; // Prints as '=' - timestamp[8] = dot[-1] & 0x0f; - if (dot[2] == '.') { - timestamp[9] = 0; - timestamp[10] = dot[1] & 0x0f; - } else { - timestamp[9] = dot[1] & 0x0f; - timestamp[10] = dot[2] & 0x0f; - } - } -#endif + // No timestamp. } // diff --git a/radio.c b/radio.c index cb1c0de..9a7fb28 100644 --- a/radio.c +++ b/radio.c @@ -200,6 +200,9 @@ void radio_read_image(const char *filename) case 262709: device = &radio_md380; break; + case 1607296: + device = &radio_d868uv; + break; case 131072: if (fread(ident, 1, 8, img) != 8) { fprintf(stderr, "%s: Cannot read header.\n", filename); From 8fc140ba1b460cad599d07d77702bd524efb5c4c Mon Sep 17 00:00:00 2001 From: Serge Date: Mon, 15 Oct 2018 14:46:17 -0700 Subject: [PATCH 07/21] Udev rules: ignore D868UV in modem manager. --- 99-dmr.rules | 4 ++++ radio.c | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/99-dmr.rules b/99-dmr.rules index ab74745..9323998 100644 --- a/99-dmr.rules +++ b/99-dmr.rules @@ -10,3 +10,7 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="666" # Baofeng RD-5R SUBSYSTEM=="usb", ATTRS{idVendor}=="15a2", ATTRS{idProduct}=="0073", MODE="666" + +# Anytone AT-D868UV +#TODO: Ignore this device in Modem Manager +# ATTRS{idVendor}=="2a03", ENV{ID_MM_DEVICE_IGNORE}="1"' diff --git a/radio.c b/radio.c index 9a7fb28..7ebf85b 100644 --- a/radio.c +++ b/radio.c @@ -44,12 +44,12 @@ static struct { { "MD-UV390", &radio_uv390 }, // TYT MD-UV390, Retevis RT3S { "2017", &radio_md2017 }, // TYT MD-2017, Retevis RT82 { "MD9600", &radio_md9600 }, // TYT MD-9600 - { "ZD3688", &radio_d900 }, // Zastone D900 - { "TP660", &radio_dp880 }, // Zastone DP880 - { "ZN><:", &radio_rt27d }, // Radtel RT-27D { "BF-5R", &radio_rd5r }, // Baofeng RD-5R { "MD-760P", &radio_gd77 }, // Radioddity GD-77, version 3.1.1 and later { "D868UVE", &radio_d868uv }, // Anytone AT-D868UV + { "ZD3688", &radio_d900 }, // Zastone D900 + { "TP660", &radio_dp880 }, // Zastone DP880 + { "ZN><:", &radio_rt27d }, // Radtel RT-27D { 0, 0 } }; From 0f258b2a10f62f6210ae3218267ed74099c007d9 Mon Sep 17 00:00:00 2001 From: Serge Date: Tue, 16 Oct 2018 00:21:59 -0700 Subject: [PATCH 08/21] Update d868uv. --- 99-dmr.rules | 4 ++-- d868uv-map.h | 4 ++-- d868uv.c | 40 ++++++++++++++++++++++++++++++++++++++++ todo-id.diff | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 todo-id.diff diff --git a/99-dmr.rules b/99-dmr.rules index 9323998..f9f7b6d 100644 --- a/99-dmr.rules +++ b/99-dmr.rules @@ -12,5 +12,5 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="666" SUBSYSTEM=="usb", ATTRS{idVendor}=="15a2", ATTRS{idProduct}=="0073", MODE="666" # Anytone AT-D868UV -#TODO: Ignore this device in Modem Manager -# ATTRS{idVendor}=="2a03", ENV{ID_MM_DEVICE_IGNORE}="1"' +# Ignore this device in Modem Manager +ATTRS{idVendor}=="28e9" ATTRS{idProduct}=="018a", ENV{ID_MM_DEVICE_IGNORE}="1" diff --git a/d868uv-map.h b/d868uv-map.h index db7fe29..7d7a97b 100644 --- a/d868uv-map.h +++ b/d868uv-map.h @@ -34,7 +34,7 @@ { 0x00f40000, 0x2000 }, // Channels 3713-3840 { 0x00f80000, 0x2000 }, // Channels 3841-3968 { 0x00fc0000, 0x0880 }, // Channels 3969-4000, VFO A, VFO B -{ 0x01000000, 0x1f400 }, // Channel bitmaps? +{ 0x01000000, 0x1f400 }, // Unknown bitmap { 0x01080000, 0xc0 }, // Scanlist 1 { 0x01080200, 0xc0 }, // Scanlist 2 { 0x01080400, 0xc0 }, // Scanlist 3 @@ -309,7 +309,7 @@ { 0x024c1280, 0x40 }, // Unknown data { 0x024c1300, 0x80 }, // Unknown data { 0x024c1400, 0x80 }, // Unknown data -{ 0x024c1500, 0x240 }, // Unknown data +{ 0x024c1500, 0x240 }, // Channel bitmap { 0x024c1800, 0x500 }, // Zeroes? { 0x024c2000, 0x440 }, // Unknown data { 0x024c2600, 0x40 }, // Unknown data diff --git a/d868uv.c b/d868uv.c index 9e7fac5..cffe25f 100644 --- a/d868uv.c +++ b/d868uv.c @@ -34,6 +34,9 @@ #include "radio.h" #include "util.h" +// +// Sizes of configuration tables. +// #define NCHAN 4000 #define NCONTACTS 10000 #define NZONES 250 @@ -41,6 +44,12 @@ #define NSCANL 250 #define NMESSAGES 100 +// +// Offsets in the image file. +// +#define OFFSET_BANK1 0x000040 +#define OFFSET_CHAN_BITMAP 0x070a40 + // // Size of memory image. // Essentialy a sum of all fragments defined ind868um-map.h. @@ -62,6 +71,15 @@ static fragment_t region_map[] = { #include "d868uv-map.h" }; +// +// Channel data. +// +typedef struct { + // Bytes 0-63 + uint8_t data[64]; + +} channel_t; + // // Print a generic information about the device. // @@ -208,6 +226,28 @@ static void d868uv_save_image(radio_device_t *radio, FILE *img) fwrite(&radio_mem[0], 1, MEMSZ, img); } +// +// Get channel bank by index. +// +static channel_t *get_bank(int i) +{ + return (channel_t*) &radio_mem[OFFSET_BANK1 + i*0x2000]; +} + +// +// Get channel by index. +// +/*static*/ channel_t *get_channel(int i) +{ + channel_t *bank = get_bank(i >> 7); + uint8_t *bitmap = &radio_mem[OFFSET_CHAN_BITMAP]; + + if ((bitmap[i / 8] >> (i & 7)) & 1) + return &bank[i % 128]; + else + return 0; +} + // // Parse the scalar parameter. // diff --git a/todo-id.diff b/todo-id.diff new file mode 100644 index 0000000..e54af01 --- /dev/null +++ b/todo-id.diff @@ -0,0 +1,35 @@ +--- new1.hd 2018-10-15 23:22:12.911010924 -0700 ++++ new1-id.hd 2018-10-16 00:05:47.408028203 -0700 +@@ -18103,7 +18103,7 @@ + 000715c0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000715d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * +-00071600 01 01 00 00 04 00 00 00 04 01 01 01 00 0f 01 02 |................| ++00071600 01 01 00 00 04 00 01 00 04 01 01 01 00 0f 01 02 |................| + ^^ + + 00071610 01 12 0c 13 08 01 01 01 02 05 05 05 05 00 01 00 |................| + 00071620 00 00 00 00 00 00 04 01 00 01 01 00 00 01 02 01 |................| + 00071630 14 00 01 01 00 00 00 02 01 01 01 08 00 00 00 00 |................| +@@ -18122,8 +18122,8 @@ + * + 00071b00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * +-00071c00 57 45 4c 43 4f 4d 00 00 00 00 00 00 00 00 00 00 |WELCOM..........| +-00071c10 41 4e 59 54 4f 4e 45 00 00 00 00 00 00 00 00 00 |ANYTONE.........| ++00071c00 4b 4b 36 41 42 51 20 41 42 43 44 45 46 47 00 00 |KK6ABQ ABCDEFG..| ++00071c10 53 45 52 47 45 59 20 4b 4c 4d 4e 4f 50 51 00 00 |SERGEY KLMNOPQ..| + 00071c20 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 |12345678........| + 00071c30 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00071c40 00 00 00 17 00 00 00 71 00 00 00 00 a1 0f a1 0f |.......q........| +@@ -18139,8 +18139,8 @@ + 00071dd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 00071de0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * +-00073d00 12 34 56 78 00 4d 79 20 52 61 64 69 6f 00 00 00 |.4Vx.My Radio...| +-00073d10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| ++00073d00 03 11 45 42 00 4b 4b 36 41 42 51 61 62 63 64 65 |..EB.KK6ABQabcde| ++00073d10 66 67 68 69 6a 00 00 00 00 00 00 00 00 00 00 00 |fghij...........| + 00073d20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * + 00075c40 00 ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 |................| From 0ca967d411887be419721f897dfee62203759a84 Mon Sep 17 00:00:00 2001 From: Serge Date: Wed, 17 Oct 2018 22:38:58 -0700 Subject: [PATCH 09/21] D868UV: print id, name and intro strings. Enhance serial prg/prg2 sequence. --- d868uv.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++- md380.c | 2 +- serial.c | 24 ++++++++++---- todo-id.diff | 4 +-- 4 files changed, 114 insertions(+), 10 deletions(-) diff --git a/d868uv.c b/d868uv.c index cffe25f..5596960 100644 --- a/d868uv.c +++ b/d868uv.c @@ -49,6 +49,13 @@ // #define OFFSET_BANK1 0x000040 #define OFFSET_CHAN_BITMAP 0x070a40 +#define OFFSET_SETTINGS 0x071600 +#define OFFSET_RADIOID 0x073d00 + +#define GET_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS]) +#define GET_RADIOID() ((radioid_t*) &radio_mem[OFFSET_RADIOID]) + +#define VALID_TEXT(txt) (*(txt) != 0 && *(txt) != 0xff) // // Size of memory image. @@ -80,6 +87,49 @@ typedef struct { } channel_t; +// +// General settings: 0x640 bytes at 0x02500000. +// TODO: verify the general settings with official CPS +// +typedef struct { + // Bytes 0-0x5ff. + uint8_t _unused0[0x600]; + + // Bytes 0x600-0x61f + uint8_t intro_line1[16]; // Up to 14 characters + uint8_t intro_line2[16]; // Up to 14 characters + + // Bytes 0x620-0x63f + uint8_t password[16]; // Up to 8 ascii digits + uint8_t _unused630[16]; // 0xff + +} general_settings_t; + +// +// Radio ID table: 250 entries, 0x1f40 bytes at 0x02580000. +// +typedef struct { + // Bytes 0-3. + uint8_t id[4]; // Up to 8 BCD digits +#define GET_ID(x) (((x)[0] >> 4) * 10000000 +\ + ((x)[0] & 15) * 1000000 +\ + ((x)[1] >> 4) * 100000 +\ + ((x)[1] & 15) * 10000 +\ + ((x)[2] >> 4) * 1000 +\ + ((x)[2] & 15) * 100 +\ + ((x)[3] >> 4) * 10 +\ + ((x)[3] & 15)) + // Byte 4. + uint8_t _unused4; // 0 + + // Bytes 5-20 + uint8_t name[16]; // Name + + // Bytes 21-31 + uint8_t _unused21[11]; // 0 + +} radioid_t; + // // Print a generic information about the device. // @@ -147,7 +197,8 @@ static void d868uv_upload(radio_device_t *radio, int cont_flag) unsigned file_offset = 0; unsigned last_printed = 0; - for (f=region_map; f->length; f++) { + // Skip first region. + for (f=region_map+1; f->length; f++) { unsigned addr = f->address; unsigned nbytes = f->length; @@ -180,6 +231,43 @@ static int d868uv_is_compatible(radio_device_t *radio) return 1; } +static void print_id(FILE *out, int verbose) +{ + radioid_t *ri = GET_RADIOID(); + unsigned id = GET_ID(ri->id); + + if (verbose) + fprintf(out, "\n# Unique DMR ID and name of this radio."); + fprintf(out, "\nID: %u\nName: ", id); + if (VALID_TEXT(ri->name)) { + print_ascii(out, ri->name, 16, 0); + } else { + fprintf(out, "-"); + } + fprintf(out, "\n"); +} + +static void print_intro(FILE *out, int verbose) +{ + general_settings_t *gs = GET_SETTINGS(); + + if (verbose) + fprintf(out, "\n# Text displayed when the radio powers up.\n"); + fprintf(out, "Intro Line 1: "); + if (VALID_TEXT(gs->intro_line1)) { + print_ascii(out, gs->intro_line1, 14, 0); + } else { + fprintf(out, "-"); + } + fprintf(out, "\nIntro Line 2: "); + if (VALID_TEXT(gs->intro_line2)) { + print_ascii(out, gs->intro_line2, 14, 0); + } else { + fprintf(out, "-"); + } + fprintf(out, "\n"); +} + // // Print full information about the device configuration. // @@ -190,6 +278,10 @@ static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) d868uv_print_version(radio, out); //TODO + + // General settings. + print_id(out, verbose); + print_intro(out, verbose); } // diff --git a/md380.c b/md380.c index a9d46fe..452fb43 100644 --- a/md380.c +++ b/md380.c @@ -402,7 +402,7 @@ static void md380_upload(radio_device_t *radio, int cont_flag) // static int md380_is_compatible(radio_device_t *radio) { - return 1; + return strncmp("D868UVE", (char*)&radio_mem[0], 7) == 0; } // diff --git a/serial.c b/serial.c index f7f3632..4b2d7e8 100644 --- a/serial.c +++ b/serial.c @@ -617,16 +617,23 @@ const char *serial_identify() { static unsigned char reply[16]; unsigned char ack[3]; + int retry = 0; if (serial_open(dev_path, 115200) < 0) { return 0; } +again: send_recv(CMD_PRG, 7, ack, 3); if (memcmp(ack, CMD_QX, 3) != 0) { - fprintf(stderr, "%s: Wrong PRG acknowledge %02x-%02x-%02x, expected %02x-%02x-%02x\n", - __func__, ack[0], ack[1], ack[2], CMD_QX[0], CMD_QX[1], CMD_QX[2]); - return 0; + if (++retry >= 10) { + fprintf(stderr, "%s: Wrong PRG acknowledge %02x-%02x-%02x, expected %02x-%02x-%02x\n", + __func__, ack[0], ack[1], ack[2], CMD_QX[0], CMD_QX[1], CMD_QX[2]); + return 0; + } + usleep(500000); + tcflush(fd, TCIOFLUSH); + goto again; } // Reply: @@ -634,9 +641,14 @@ const char *serial_identify() // I D 8 6 8 U V E V 1 0 2 send_recv(CMD_PRG2, 1, reply, 16); if (reply[0] != 'I' || reply[15] != CMD_ACK[0]) { - fprintf(stderr, "%s: Wrong PRG2 reply %02x-...-%02x, expected %02x-...-%02x\n", - __func__, reply[0], reply[15], 'I', CMD_ACK[0]); - return 0; + if (++retry >= 10) { + fprintf(stderr, "%s: Wrong PRG2 reply %02x-...-%02x, expected %02x-...-%02x\n", + __func__, reply[0], reply[15], 'I', CMD_ACK[0]); + return 0; + } + usleep(500000); + tcflush(fd, TCIOFLUSH); + goto again; } // Terminate the string. diff --git a/todo-id.diff b/todo-id.diff index e54af01..c72fb2b 100644 --- a/todo-id.diff +++ b/todo-id.diff @@ -15,7 +15,7 @@ * 00071b00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| * --00071c00 57 45 4c 43 4f 4d 00 00 00 00 00 00 00 00 00 00 |WELCOM..........| +-00071c00 57 45 4c 43 4f 4d 00 00 00 00 00 00 00 00 00 00 |WELCOM..........| 02500600 -00071c10 41 4e 59 54 4f 4e 45 00 00 00 00 00 00 00 00 00 |ANYTONE.........| +00071c00 4b 4b 36 41 42 51 20 41 42 43 44 45 46 47 00 00 |KK6ABQ ABCDEFG..| +00071c10 53 45 52 47 45 59 20 4b 4c 4d 4e 4f 50 51 00 00 |SERGEY KLMNOPQ..| @@ -26,7 +26,7 @@ 00071dd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00071de0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| * --00073d00 12 34 56 78 00 4d 79 20 52 61 64 69 6f 00 00 00 |.4Vx.My Radio...| +-00073d00 12 34 56 78 00 4d 79 20 52 61 64 69 6f 00 00 00 |.4Vx.My Radio...| 02580000 -00073d10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00073d00 03 11 45 42 00 4b 4b 36 41 42 51 61 62 63 64 65 |..EB.KK6ABQabcde| +00073d10 66 67 68 69 6a 00 00 00 00 00 00 00 00 00 00 00 |fghij...........| From 39b28f6026a9a9bd3bffea85c6976fe57afb49ea Mon Sep 17 00:00:00 2001 From: Serge Date: Wed, 17 Oct 2018 23:46:25 -0700 Subject: [PATCH 10/21] D868UV: Print and parse id, name and intro strings. Write image works correctly. --- d868uv.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- gd77.c | 16 ++++++++-------- rd5r.c | 16 ++++++++-------- serial.c | 3 ++- util.c | 32 ++++++++++++++++++++++++++++++-- util.h | 3 ++- 6 files changed, 98 insertions(+), 24 deletions(-) diff --git a/d868uv.c b/d868uv.c index 5596960..77ad752 100644 --- a/d868uv.c +++ b/d868uv.c @@ -92,8 +92,19 @@ typedef struct { // TODO: verify the general settings with official CPS // typedef struct { - // Bytes 0-0x5ff. - uint8_t _unused0[0x600]; + // Bytes 0-5. + uint8_t _unused0[6]; + + // Bytes 6-7. + uint8_t power_on; // Power-on Interface +#define PWON_DEFAULT 0 // Default +#define PWON_CUST_CHAR 1 // Custom Char +#define PWON_CUST_PICT 2 // Custom Picture + + uint8_t _unused7; + + // Bytes 8-0x5ff. + uint8_t _unused8[0x5f8]; // Bytes 0x600-0x61f uint8_t intro_line1[16]; // Up to 14 characters @@ -194,10 +205,11 @@ static void d868uv_download(radio_device_t *radio) static void d868uv_upload(radio_device_t *radio, int cont_flag) { fragment_t *f; - unsigned file_offset = 0; + unsigned file_offset; unsigned last_printed = 0; // Skip first region. + file_offset = region_map[0].length; for (f=region_map+1; f->length; f++) { unsigned addr = f->address; unsigned nbytes = f->length; @@ -345,7 +357,39 @@ static channel_t *get_bank(int i) // static void d868uv_parse_parameter(radio_device_t *radio, char *param, char *value) { - //TODO + if (strcasecmp("Radio", param) == 0) { + if (!radio_is_compatible(value)) { + fprintf(stderr, "Incompatible model: %s\n", value); + exit(-1); + } + return; + } + + radioid_t *ri = GET_RADIOID(); + if (strcasecmp ("Name", param) == 0) { + ascii_decode(ri->name, value, 16, 0); + return; + } + if (strcasecmp ("ID", param) == 0) { + uint32_t id = strtoul(value, 0, 0); + ri->id[0] = ((id / 10000000) << 4) | ((id / 1000000) % 10); + ri->id[1] = ((id / 100000 % 10) << 4) | ((id / 10000) % 10); + ri->id[2] = ((id / 1000 % 10) << 4) | ((id / 100) % 10); + ri->id[3] = ((id / 10 % 10) << 4) | (id % 10); + return; + } + + general_settings_t *gs = GET_SETTINGS(); + if (strcasecmp ("Intro Line 1", param) == 0) { + ascii_decode_uppercase(gs->intro_line1, value, 14, 0); + gs->power_on = PWON_CUST_CHAR; + return; + } + if (strcasecmp ("Intro Line 2", param) == 0) { + ascii_decode_uppercase(gs->intro_line2, value, 14, 0); + gs->power_on = PWON_CUST_CHAR; + return; + } fprintf(stderr, "Unknown parameter: %s = %s\n", param, value); exit(-1); } diff --git a/gd77.c b/gd77.c index b401727..93b130b 100644 --- a/gd77.c +++ b/gd77.c @@ -419,7 +419,7 @@ static void setup_zone(int index, const char *name) zonetab_t *zt = GET_ZONETAB(); zone_t *z = &zt->zone[index]; - ascii_decode(z->name, name, sizeof(z->name)); + ascii_decode(z->name, name, sizeof(z->name), 0xff); memset(z->member, 0, sizeof(z->member)); // Set valid bit. @@ -483,7 +483,7 @@ static void setup_scanlist(int index, const char *name, scanlist_t *sl = &st->scanlist[index]; memset(sl, 0, 88); - ascii_decode(sl->name, name, sizeof(sl->name)); + ascii_decode(sl->name, name, sizeof(sl->name), 0xff); sl->priority_ch1 = prio1; sl->priority_ch2 = prio2; @@ -571,7 +571,7 @@ static void setup_contact(int index, const char *name, int type, int id, int rxt ct->ring_style = 0; // TODO ct->_unused23 = (type < CALL_ALL) ? 0 : 0xff; - ascii_decode(ct->name, name, 16); + ascii_decode(ct->name, name, 16, 0xff); } // @@ -593,7 +593,7 @@ static void setup_grouplist(int index, const char *name) grouptab_t *gt = GET_GROUPTAB(); grouplist_t *gl = >->grouplist[index]; - ascii_decode(gl->name, name, sizeof(gl->name)); + ascii_decode(gl->name, name, sizeof(gl->name), 0xff); // Enable grouplist. gt->nitems1[index] = 1; @@ -712,7 +712,7 @@ static void setup_channel(int i, int mode, char *name, double rx_mhz, double tx_ ch->ctcss_dcs_receive = rxtone; ch->ctcss_dcs_transmit = txtone; - ascii_decode(ch->name, name, sizeof(ch->name)); + ascii_decode(ch->name, name, sizeof(ch->name), 0xff); // Set valid bit. b->bitmap[i % 128 / 8] |= 1 << (i & 7); @@ -1475,7 +1475,7 @@ static void gd77_parse_parameter(radio_device_t *radio, char *param, char *value general_settings_t *gs = GET_SETTINGS(); if (strcasecmp ("Name", param) == 0) { - ascii_decode(gs->radio_name, value, 8); + ascii_decode(gs->radio_name, value, 8, 0xff); return; } if (strcasecmp ("ID", param) == 0) { @@ -1497,11 +1497,11 @@ static void gd77_parse_parameter(radio_device_t *radio, char *param, char *value intro_text_t *it = GET_INTRO(); if (strcasecmp ("Intro Line 1", param) == 0) { - ascii_decode(it->intro_line1, value, 16); + ascii_decode(it->intro_line1, value, 16, 0xff); return; } if (strcasecmp ("Intro Line 2", param) == 0) { - ascii_decode(it->intro_line2, value, 16); + ascii_decode(it->intro_line2, value, 16, 0xff); return; } fprintf(stderr, "Unknown parameter: %s = %s\n", param, value); diff --git a/rd5r.c b/rd5r.c index e1cc553..9c670f0 100644 --- a/rd5r.c +++ b/rd5r.c @@ -419,7 +419,7 @@ static void setup_zone(int index, const char *name) zonetab_t *zt = GET_ZONETAB(); zone_t *z = &zt->zone[index]; - ascii_decode(z->name, name, sizeof(z->name)); + ascii_decode(z->name, name, sizeof(z->name), 0xff); memset(z->member, 0, sizeof(z->member)); // Set valid bit. @@ -483,7 +483,7 @@ static void setup_scanlist(int index, const char *name, scanlist_t *sl = &st->scanlist[index]; memset(sl, 0, 88); - ascii_decode(sl->name, name, sizeof(sl->name)); + ascii_decode(sl->name, name, sizeof(sl->name), 0xff); sl->priority_ch1 = prio1; sl->priority_ch2 = prio2; @@ -571,7 +571,7 @@ static void setup_contact(int index, const char *name, int type, int id, int rxt ct->ring_style = 0; // TODO ct->_unused23 = (type < CALL_ALL) ? 0 : 0xff; - ascii_decode(ct->name, name, 16); + ascii_decode(ct->name, name, 16, 0xff); } // @@ -593,7 +593,7 @@ static void setup_grouplist(int index, const char *name) grouptab_t *gt = GET_GROUPTAB(); grouplist_t *gl = >->grouplist[index]; - ascii_decode(gl->name, name, sizeof(gl->name)); + ascii_decode(gl->name, name, sizeof(gl->name), 0xff); // Enable grouplist. gt->nitems1[index] = 1; @@ -716,7 +716,7 @@ static void setup_channel(int i, int mode, char *name, double rx_mhz, double tx_ ch->ctcss_dcs_receive = rxtone; ch->ctcss_dcs_transmit = txtone; - ascii_decode(ch->name, name, sizeof(ch->name)); + ascii_decode(ch->name, name, sizeof(ch->name), 0xff); // Set valid bit. b->bitmap[i % 128 / 8] |= 1 << (i & 7); @@ -1479,7 +1479,7 @@ static void rd5r_parse_parameter(radio_device_t *radio, char *param, char *value general_settings_t *gs = GET_SETTINGS(); if (strcasecmp ("Name", param) == 0) { - ascii_decode(gs->radio_name, value, 8); + ascii_decode(gs->radio_name, value, 8, 0xff); return; } if (strcasecmp ("ID", param) == 0) { @@ -1501,11 +1501,11 @@ static void rd5r_parse_parameter(radio_device_t *radio, char *param, char *value intro_text_t *it = GET_INTRO(); if (strcasecmp ("Intro Line 1", param) == 0) { - ascii_decode(it->intro_line1, value, 16); + ascii_decode(it->intro_line1, value, 16, 0xff); return; } if (strcasecmp ("Intro Line 2", param) == 0) { - ascii_decode(it->intro_line2, value, 16); + ascii_decode(it->intro_line2, value, 16, 0xff); return; } fprintf(stderr, "Unknown parameter: %s = %s\n", param, value); diff --git a/serial.c b/serial.c index 4b2d7e8..1462378 100644 --- a/serial.c +++ b/serial.c @@ -696,7 +696,8 @@ again: void serial_write_region(int addr, unsigned char *data, int nbytes) { - static const int DATASZ = 64; + //static const int DATASZ = 64; + static const int DATASZ = 16; unsigned char ack, cmd[8 + DATASZ]; int n, i; diff --git a/util.c b/util.c index 27816ea..357e2b7 100644 --- a/util.c +++ b/util.c @@ -334,7 +334,7 @@ void utf8_decode(unsigned short *dst, const char *src, unsigned nsym) // Replace underscore by space. // Fill the rest with 0xff. // -void ascii_decode(unsigned char *dst, const char *src, unsigned nsym) +void ascii_decode(unsigned char *dst, const char *src, unsigned nsym, unsigned fill) { if (src[0] == '-' && src[1] == 0) src = ""; @@ -345,7 +345,7 @@ void ascii_decode(unsigned char *dst, const char *src, unsigned nsym) if (ch == 0) { // Clear the remaining bytes. while (nsym-- > 0) - *dst++ = 0xff; + *dst++ = fill; break; } if (ch == '_') @@ -355,6 +355,34 @@ void ascii_decode(unsigned char *dst, const char *src, unsigned nsym) } } +// +// Copy ASCII string, at most nsym characters. +// Replace underscore by space. +// Fill the rest with 0xff. +// +void ascii_decode_uppercase(unsigned char *dst, const char *src, unsigned nsym, unsigned fill) +{ + if (src[0] == '-' && src[1] == 0) + src = ""; + + for (; nsym > 0; nsym--) { + int ch = *src++; + + if (ch == 0) { + // Clear the remaining bytes. + while (nsym-- > 0) + *dst++ = fill; + break; + } + if (ch == '_') + ch = ' '; + else if (ch >= 'a' && ch <= 'z') + ch += 'A' - 'a'; + + *dst++ = ch; + } +} + // // Convert tone string to BCD format. // Four possible formats: diff --git a/util.h b/util.h index 037bbfa..f3b33d5 100644 --- a/util.h +++ b/util.h @@ -162,7 +162,8 @@ void utf8_decode(unsigned short *dst, const char *src, unsigned nsym); // Copy ASCII string, at most nsym characters. // Replace underscore by space. // -void ascii_decode(unsigned char *dst, const char *src, unsigned nsym); +void ascii_decode(unsigned char *dst, const char *src, unsigned nsym, unsigned fill); +void ascii_decode_uppercase(unsigned char *dst, const char *src, unsigned nsym, unsigned fill); // // Get local time in format: YYYYMMDDhhmmss From d1d2d1afc769485372ed9648ae9461ff83bf9821 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Thu, 18 Oct 2018 21:46:03 -0700 Subject: [PATCH 11/21] Delete id diff file. --- todo-id.diff | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 todo-id.diff diff --git a/todo-id.diff b/todo-id.diff deleted file mode 100644 index c72fb2b..0000000 --- a/todo-id.diff +++ /dev/null @@ -1,35 +0,0 @@ ---- new1.hd 2018-10-15 23:22:12.911010924 -0700 -+++ new1-id.hd 2018-10-16 00:05:47.408028203 -0700 -@@ -18103,7 +18103,7 @@ - 000715c0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| - 000715d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| - * --00071600 01 01 00 00 04 00 00 00 04 01 01 01 00 0f 01 02 |................| -+00071600 01 01 00 00 04 00 01 00 04 01 01 01 00 0f 01 02 |................| - ^^ - - 00071610 01 12 0c 13 08 01 01 01 02 05 05 05 05 00 01 00 |................| - 00071620 00 00 00 00 00 00 04 01 00 01 01 00 00 01 02 01 |................| - 00071630 14 00 01 01 00 00 00 02 01 01 01 08 00 00 00 00 |................| -@@ -18122,8 +18122,8 @@ - * - 00071b00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| - * --00071c00 57 45 4c 43 4f 4d 00 00 00 00 00 00 00 00 00 00 |WELCOM..........| 02500600 --00071c10 41 4e 59 54 4f 4e 45 00 00 00 00 00 00 00 00 00 |ANYTONE.........| -+00071c00 4b 4b 36 41 42 51 20 41 42 43 44 45 46 47 00 00 |KK6ABQ ABCDEFG..| -+00071c10 53 45 52 47 45 59 20 4b 4c 4d 4e 4f 50 51 00 00 |SERGEY KLMNOPQ..| - 00071c20 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 |12345678........| - 00071c30 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| - 00071c40 00 00 00 17 00 00 00 71 00 00 00 00 a1 0f a1 0f |.......q........| -@@ -18139,8 +18139,8 @@ - 00071dd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| - 00071de0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| - * --00073d00 12 34 56 78 00 4d 79 20 52 61 64 69 6f 00 00 00 |.4Vx.My Radio...| 02580000 --00073d10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -+00073d00 03 11 45 42 00 4b 4b 36 41 42 51 61 62 63 64 65 |..EB.KK6ABQabcde| -+00073d10 66 67 68 69 6a 00 00 00 00 00 00 00 00 00 00 00 |fghij...........| - 00073d20 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| - * - 00075c40 00 ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 |................| From 902fb721e380c80bfb2e50da459bcba692c81d82 Mon Sep 17 00:00:00 2001 From: Serge Date: Thu, 18 Oct 2018 22:59:12 -0700 Subject: [PATCH 12/21] Update RD-5R configuration example. --- examples/rd5r-south-bay-area.conf | 482 +++++++++++++++--------------- 1 file changed, 244 insertions(+), 238 deletions(-) diff --git a/examples/rd5r-south-bay-area.conf b/examples/rd5r-south-bay-area.conf index ebe0f27..baa6cf1 100644 --- a/examples/rd5r-south-bay-area.conf +++ b/examples/rd5r-south-bay-area.conf @@ -1,167 +1,191 @@ # # Generic DMR configuration for South Bay Area. # -# Schedules (PDT): -# Monday 8-10 pm: PAPA DMR Roundtable, California (3106/TS1) -# Tuesday 8-9 pm: SNARS DMR Net (31328/TS1) -# Wednesday 5:30-6:30 pm: Texas Statewide Net (3148/TS1) -# Thursday 7-7:30 pm: NorCal DMR Net (31068/TS2) -# Thursday 8-10 pm: PAPA Tech Roundtable, XRF012A, analog&D-STAR (31078/TS1) -# Saturday 9-11 am: Worldwide Net (91/TS1) -# Radio: Baofeng RD-5R # Table of digital channels. -# 1) Channel number: 1-1000 +# 1) Channel number: 1-1024 # 2) Name: up to 16 characters, use '_' instead of space # 3) Receive frequency in MHz # 4) Transmit frequency or +/- offset in MHz # 5) Transmit power: High, Low # 6) Scan list: - or index in Scanlist table -# 7) Autoscan flag: -, + -# 8) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 -# 9) Receive only: -, + -# 10) Admit criteria: -, Free, Color -# 11) Color code: 1, 2, 3... 15 -# 12) Time slot: 1 or 2 -# 13) In call criteria: -, Admit, TXInt -# 14) Receive group list: - or index in Grouplist table -# 15) Contact for transmit: - or index in Contacts table +# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555 +# 8) Receive only: -, + +# 9) Admit criteria: -, Free, Color +# 10) Color code: 0, 1, 2, 3... 15 +# 11) Time slot: 1 or 2 +# 12) Receive group list: - or index in Grouplist table +# 13) Contact for transmit: - or index in Contacts table # Digital Name Receive Transmit Power Scan TOT RO Admit Color Slot RxGL TxContact # (1) W6TCP, Milpitas, NorCal - 1 Mi_California 440.125 +5 High - 180 - Free 3 1 3 6 # 3106 California Static - 2 Mi_TAC_310 440.125 +5 High - 180 - Free 3 1 3 5 # 310 TAC 310 Dynamic - 3 Mi_Bay-Net 440.125 +5 High - 180 - Free 3 1 3 13 # 31075 Bay-Net Dynamic - 4 Mi_World 440.125 +5 High - 180 - Free 3 1 3 3 # 91 Worldwide Dynamic - 5 Mi_NorCal 440.125 +5 High - 180 - Free 3 2 3 11 # 31068 NorCal Static - 6 Mi_Anarchy 440.125 +5 High - 180 - Free 3 2 3 18 # 31666 DMR of Anarchy Static - 7 Mi_NC_5150 440.125 +5 High - 180 - Free 3 2 3 19 # 95150 NorCal 5150 Static - 8 Mi_Parrot 440.125 +5 High - 180 - Free 3 2 3 24 # 9990 Parrot Private + 1 Mi_RX_All 440.125 +5 Low - 180 + Color 3 1 1 - # Receive only + 2 Mi_World 440.125 +5 High - 180 - Color 3 1 11 91 # 91 Worldwide Dynamic + 3 Mi_North_America 440.125 +5 High - 180 - Color 3 1 12 93 # 93 North America Dynamic + 4 Mi_TAC_310 440.125 +5 High - 180 - Color 3 1 13 10 # 310 TAC 310 Dynamic + 5 Mi_TAC_311 440.125 +5 High - 180 - Color 3 1 14 11 # 311 TAC 311 Dynamic + 6 Mi_USA_Nation 440.125 +5 High - 180 - Color 3 1 15 100 # 3100 USA Nationwide Dynamic + 7 Mi_California 440.125 +5 High - 180 - Color 3 1 16 106 # 3106 California Static + 8 Mi_NorCal 440.125 +5 High - 180 - Color 3 2 19 68 # 31068 NorCal Static + 9 Mi_Bay-Net 440.125 +5 High - 180 - Color 3 1 20 75 # 31075 Bay-Net Dynamic + 10 Mi_Anarchy 440.125 +5 High - 180 - Color 3 2 23 66 # 31666 DMR of Anarchy Static + 11 Mi_NC_5150 440.125 +5 High - 180 - Color 3 2 24 95 # 95150 NorCal 5150 Static + 12 Mi_Parrot 440.125 +5 High - 180 - Color 3 1 - 90 # 9990 Parrot Private # (2) N6AMG, San Bruno, Baycom - 11 Br_North_America 440.500 +5 High - 180 - Free 1 1 3 4 # 93 North America Static - 12 Br_California 440.500 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 13 Br_NC_AllStr 440.500 +5 High - 180 - Free 1 1 3 10 # 31065 NorCal AllStar Static - 14 Br_Bay-Net 440.500 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Static - 15 Br_USA_Area6 440.500 +5 High - 180 - Free 1 1 3 15 # 31096 USA Area 6 Static - 16 Br_TAC_310 440.500 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 17 Br_World 440.500 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 18 Br_Baycom 440.500 +5 High - 180 - Free 1 2 3 20 # 97150 Baycom Static - 19 Br_NorCal 440.500 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Dynamic - 20 Br_Anarchy 440.500 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 21 Br_NC_5150 440.500 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 22 Br_Parrot 440.500 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 21 Br_RX_All 440.500 +5 Low - 180 + Color 1 1 1 - # Receive only + 22 Br_World 440.500 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 23 Br_North_America 440.500 +5 High - 180 - Color 1 1 12 93 # 93 North America Static + 24 Br_TAC_310 440.500 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 25 Br_TAC_311 440.500 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 26 Br_USA_Nation 440.500 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 27 Br_California 440.500 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 28 Br_NC_AllStr 440.500 +5 High - 180 - Color 1 1 18 65 # 31065 NorCal AllStar Static + 29 Br_NorCal 440.500 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Dynamic + 30 Br_Bay-Net 440.500 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Static + 31 Br_USA_Area6 440.500 +5 High - 180 - Color 1 1 22 96 # 31096 USA Area 6 Static + 32 Br_Anarchy 440.500 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 33 Br_NC_5150 440.500 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 34 Br_Baycom 440.500 +5 High - 180 - Color 1 2 25 97 # 97150 Baycom Static + 35 Br_Parrot 440.500 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (3) WB6ECE, Boulder Creek, Baycom - 31 Bo_North_America 440.5875 +5 High - 180 - Free 2 1 3 4 # 93 North America Static - 32 Bo_California 440.5875 +5 High - 180 - Free 2 1 3 6 # 3106 California Static - 33 Bo_Santa_Clara 440.5875 +5 High - 180 - Free 2 1 3 9 # 31064 Santa Clara County Static - 34 Bo_NC_AllStr 440.5875 +5 High - 180 - Free 2 1 3 10 # 31065 NorCal AllStar Static - 35 Bo_Bay-Net 440.5875 +5 High - 180 - Free 2 1 3 13 # 31075 Bay-Net Static - 36 Bo_USA_Area6 440.5875 +5 High - 180 - Free 2 1 3 15 # 31096 USA Area 6 Static - 37 Bo_TAC_310 440.5875 +5 High - 180 - Free 2 1 3 5 # 310 TAC 310 Dynamic - 38 Bo_World 440.5875 +5 High - 180 - Free 2 1 3 3 # 91 Worldwide Dynamic - 39 Bo_Baycom 440.5875 +5 High - 180 - Free 2 2 3 20 # 97150 Baycom Static - 40 Bo_NorCal 440.5875 +5 High - 180 - Free 2 2 3 11 # 31068 NorCal Dynamic - 41 Bo_Anarchy 440.5875 +5 High - 180 - Free 2 2 3 18 # 31666 DMR of Anarchy Dynamic - 42 Bo_NC_5150 440.5875 +5 High - 180 - Free 2 2 3 19 # 95150 NorCal 5150 Dynamic - 43 Bo_Parrot 440.5875 +5 High - 180 - Free 2 2 3 24 # 9990 Parrot Private + 41 Bo_RX_All 440.5875 +5 Low - 180 + Color 2 1 1 - # Receive only + 42 Bo_World 440.5875 +5 High - 180 - Color 2 1 11 91 # 91 Worldwide Dynamic + 43 Bo_North_America 440.5875 +5 High - 180 - Color 2 1 12 93 # 93 North America Static + 44 Bo_TAC_310 440.5875 +5 High - 180 - Color 2 1 13 10 # 310 TAC 310 Dynamic + 45 Bo_TAC_311 440.5875 +5 High - 180 - Color 2 1 14 11 # 311 TAC 311 Dynamic + 46 Bo_USA_Nation 440.5875 +5 High - 180 - Color 2 1 15 100 # 3100 USA Nationwide Dynamic + 47 Bo_California 440.5875 +5 High - 180 - Color 2 1 16 106 # 3106 California Static + 48 Bo_Santa_Clara 440.5875 +5 High - 180 - Color 2 1 17 64 # 31064 Santa Clara County Static + 49 Bo_NC_AllStr 440.5875 +5 High - 180 - Color 2 1 18 65 # 31065 NorCal AllStar Static + 50 Bo_NorCal 440.5875 +5 High - 180 - Color 2 2 19 68 # 31068 NorCal Dynamic + 51 Bo_Bay-Net 440.5875 +5 High - 180 - Color 2 1 20 75 # 31075 Bay-Net Static + 52 Bo_USA_Area6 440.5875 +5 High - 180 - Color 2 1 22 96 # 31096 USA Area 6 Static + 53 Bo_Anarchy 440.5875 +5 High - 180 - Color 2 2 23 66 # 31666 DMR of Anarchy Dynamic + 54 Bo_NC_5150 440.5875 +5 High - 180 - Color 2 2 24 95 # 95150 NorCal 5150 Dynamic + 55 Bo_Baycom 440.5875 +5 High - 180 - Color 2 2 25 97 # 97150 Baycom Static + 56 Bo_Parrot 440.5875 +5 High - 180 - Color 2 1 - 90 # 9990 Parrot Private # (4) K6OTR, Palo Alto, Baycom - 51 PA_North_America 441.850 +5 High - 180 - Free 1 1 3 4 # 93 North America Static - 52 PA_California 441.850 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 53 PA_NC_AllStr 441.850 +5 High - 180 - Free 1 1 3 10 # 31065 NorCal AllStar Static - 54 PA_Bay-Net 441.850 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Static - 55 PA_USA_Area6 441.850 +5 High - 180 - Free 1 1 3 15 # 31096 USA - Area 6 4646 Static - 56 PA_TAC_310 441.850 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 57 PA_World 441.850 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 58 PA_Baycom 441.850 +5 High - 180 - Free 1 2 3 20 # 97150 Baycom Static - 59 PA_NorCal 441.850 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Dynamic - 60 PA_Anarchy 441.850 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 61 PA_NC_5150 441.850 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 62 PA_Parrot 441.850 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 61 PA_RX_All 441.850 +5 Low - 180 + Color 1 1 1 - # Receive only + 62 PA_World 441.850 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 63 PA_North_America 441.850 +5 High - 180 - Color 1 1 12 93 # 93 North America Static + 64 PA_TAC_310 441.850 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 65 PA_TAC_311 441.850 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 66 USA_Nation 441.850 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 67 PA_California 441.850 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 68 PA_NC_AllStr 441.850 +5 High - 180 - Color 1 1 18 65 # 31065 NorCal AllStar Static + 69 PA_NorCal 441.850 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Dynamic + 70 PA_Bay-Net 441.850 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Static + 71 PA_USA_Area6 441.850 +5 High - 180 - Color 1 1 22 96 # 31096 USA - Area 6 4646 Static + 72 PA_Anarchy 441.850 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 73 PA_NC_5150 441.850 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 74 PA_Baycom 441.850 +5 High - 180 - Color 1 2 25 97 # 97150 Baycom Static + 75 PA_Parrot 441.850 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (5) KK6USZ, Saratoga - 71 Sa_Nebraska 441.950 +5 High - 180 - Free 1 1 3 7 # 3131 Nebraska Static - 72 Sa_World 441.950 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 73 Sa_California 441.950 +5 High - 180 - Free 1 1 3 6 # 3106 California Dynamic - 74 Sa_Texas 441.950 +5 High - 180 - Free 1 1 3 8 # 3148 Texas Dynamic - 75 Sa_PAPA 441.950 +5 High - 180 - Free 1 1 3 14 # 31078 XLX013D PAPA Dynamic - 76 Sa_TAC_310 441.950 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 77 Sa_Santa_Clara 441.950 +5 High - 180 - Free 1 2 3 9 # 31064 Santa Clara County Static - 78 Sa_NorCal 441.950 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Static - 79 Sa_Bay-Net 441.950 +5 High - 180 - Free 1 2 3 13 # 31075 Bay-Net Static - 80 Sa_Baycom 441.950 +5 High - 180 - Free 1 2 3 20 # 97150 Baycom Static - 81 Sa_Clst_KK6USZ 441.950 +5 High - 180 - Free 1 2 3 21 # 110670 Cluster KK6USZ Static - 82 Sa_Anarchy 441.950 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 83 Sa_NC_5150 441.950 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 84 Sa_Parrot 441.950 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 81 Sa_RX_All 441.950 +5 Low - 180 + Color 1 1 1 - # Receive only + 82 Sa_World 441.950 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 83 Sa_North_America 441.950 +5 High - 180 - Color 1 1 12 93 # 93 North America Dynamic + 84 Sa_TAC_310 441.950 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 85 Sa_TAC_311 441.950 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 86 Sa_USA_Nation 441.950 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 87 Sa_California 441.950 +5 High - 180 - Color 1 1 16 106 # 3106 California Dynamic + 88 Sa_Nebraska 441.950 +5 High - 180 - Color 1 1 26 131 # 3131 Nebraska Static + 89 Sa_Texas 441.950 +5 High - 180 - Color 1 1 27 148 # 3148 Texas Dynamic + 90 Sa_Santa_Clara 441.950 +5 High - 180 - Color 1 2 17 64 # 31064 Santa Clara County Static + 91 Sa_NorCal 441.950 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Static + 92 Sa_Bay-Net 441.950 +5 High - 180 - Color 1 2 20 75 # 31075 Bay-Net Static + 93 Sa_Anarchy 441.950 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 94 Sa_NC_5150 441.950 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 95 Sa_Baycom 441.950 +5 High - 180 - Color 1 2 25 97 # 97150 Baycom Static + 96 Sa_Parrot 441.950 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (6) WA6YCZ, Mt Umunhum, Baycom - 91 Um_North_America 442.5375 +5 High - 180 - Free 1 1 3 4 # 93 North America Static - 92 Um_California 442.5375 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 93 Um_Santa_Clara 442.5375 +5 High - 180 - Free 1 1 3 9 # 31064 Santa Clara County Static - 94 Um_NC_AllStr 442.5375 +5 High - 180 - Free 1 1 3 10 # 31065 NorCal AllStar Static - 95 Um_Bay-Net 442.5375 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Static - 96 Um_USA_Area6 442.5375 +5 High - 180 - Free 1 1 3 15 # 31096 USA Area 6 Static - 97 Um_TAC_310 442.5375 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 98 Um_World 442.5375 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 99 Um_Baycom 442.5375 +5 High - 180 - Free 1 2 3 20 # 97150 Baycom Static - 100 Um_NorCal 442.5375 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Dynamic - 101 Um_Anarchy 442.5375 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 102 Um_NC_5150 442.5375 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 103 Um_Parrot 442.5375 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 101 Um_RX_All 442.5375 +5 Low - 180 + Color 1 1 1 - # Receive only + 102 Um_World 442.5375 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 103 Um_North_America 442.5375 +5 High - 180 - Color 1 1 12 93 # 93 North America Static + 104 Um_TAC_310 442.5375 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 105 Um_TAC_311 442.5375 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 106 Um_USA_Nation 442.5375 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 107 Um_California 442.5375 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 108 Um_Santa_Clara 442.5375 +5 High - 180 - Color 1 1 17 64 # 31064 Santa Clara County Static + 109 Um_NC_AllStr 442.5375 +5 High - 180 - Color 1 1 18 65 # 31065 NorCal AllStar Static + 110 Um_NorCal 442.5375 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Dynamic + 111 Um_Bay-Net 442.5375 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Static + 112 Um_USA_Area6 442.5375 +5 High - 180 - Color 1 1 22 96 # 31096 USA Area 6 Static + 113 Um_Anarchy 442.5375 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 114 Um_NC_5150 442.5375 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 115 Um_Baycom 442.5375 +5 High - 180 - Color 1 2 25 97 # 97150 Baycom Static + 116 Um_Parrot 442.5375 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (7) K6LNK, Berkeley, NorCal, Carla - 101 Be_California 443.500 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 102 Be_KPARN 443.500 +5 High - 180 - Free 1 1 3 12 # 31072 KPARN Static - 103 Be_SNARS 443.500 +5 High - 180 - Free 1 1 3 16 # 31328 SNARS (Reno/Tahoe) Static - 104 Be_SNARS_2 443.500 +5 High - 180 - Free 1 1 3 17 # 31329 SNARS 2 Static - 105 Be_Clst_K6LNK 443.500 +5 High - 180 - Free 1 1 3 2 # 23 Cluster K6LNK Static - 106 Be_TAC_310 443.500 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 107 Be_Bay-Net 443.500 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Dynamic - 108 Be_World 443.500 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 109 Be_NorCal 443.500 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Static - 110 Be_CARLA 443.500 +5 High - 180 - Free 1 2 3 22 # 310604 CARLA Static - 111 Be_Tri-LERT 443.500 +5 High - 180 - Free 1 2 3 23 # 310703 Tri-LERT Static - 112 Be_Clst_CARLA 443.500 +5 High - 180 - Free 1 2 3 1 # 21 Cluster CARLA Static - 113 Be_Anarchy 443.500 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 114 Be_NC_5150 443.500 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 115 Be_Parrot 443.500 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 121 Be_RX_All 443.500 +5 Low - 180 + Color 1 1 1 - # Receive only + 122 Be_World 443.500 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 123 Be_North_America 443.500 +5 High - 180 - Color 1 1 12 93 # 93 North America Dynamic + 124 Be_TAC_310 443.500 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 125 Be_TAC_311 443.500 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 126 Be_USA_Nation 443.500 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 127 Be_California 443.500 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 128 Be_NorCal 443.500 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Static + 129 Be_Bay-Net 443.500 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Dynamic + 130 Be_Anarchy 443.500 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 131 Be_NC_5150 443.500 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 132 Be_Parrot 443.500 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (8) K6HLE, Loma Prieta, NorCal, AREA - 121 Lo_North_America 444.025 +5 High - 180 - Free 1 1 3 4 # 93 North America Static - 122 Lo_California 444.025 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 123 Lo_USA_Area_6 444.025 +5 High - 180 - Free 1 1 3 15 # 31096 USA - Area 6 4646 Static - 124 Lo_TAC_310 444.025 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 125 Lo_Bay-Net 444.025 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Dynamic - 126 Lo_World 444.025 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 127 Lo_NorCal 444.025 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Static - 128 Lo_Anarchy 444.025 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 129 Lo_NC_5150 444.025 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 130 Lo_Parrot 444.025 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 141 Lo_RX_All 444.025 +5 Low - 180 + Color 1 1 1 - # Receive only + 142 Lo_World 444.025 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 143 Lo_North_America 444.025 +5 High - 180 - Color 1 1 12 93 # 93 North America Static + 144 Lo_TAC_310 444.025 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 145 Lo_TAC_311 444.025 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 146 Lo_USA_Nation 444.025 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 147 Lo_California 444.025 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 148 Lo_NorCal 444.025 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Static + 149 Lo_Bay-Net 444.025 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Dynamic + 150 Lo_USA_Area6 444.025 +5 High - 180 - Color 1 1 22 96 # 31096 USA - Area 6 4646 Static + 151 Lo_Anarchy 444.025 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 152 Lo_NC_5150 444.025 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 153 Lo_Parrot 444.025 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (9) WW6BAY, Palo Alto, Bay-Net - 141 Bay_Santa_Clara 444.350 +5 High - 180 - Free 1 1 3 9 # 31064 Santa Clara County Static - 142 Bay_TAC_310 444.350 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 143 Bay_World 444.350 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 144 Bay_Bay-Net 444.350 +5 High - 180 - Free 1 2 3 13 # 31075 Bay-Net Static - 145 Bay_NorCal 444.350 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Dynamic - 146 Bay_Anarchy 444.350 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 147 Bay_NC_5150 444.350 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 148 Bay_Parrot 444.350 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 161 Bay_RX_All 444.350 +5 Low - 180 + Color 1 1 1 - # Receive only + 162 Bay_World 444.350 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 163 Bay_North_Am 444.350 +5 High - 180 - Color 1 1 12 93 # 93 North America Dynamic + 164 Bay_TAC_310 444.350 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 165 Bay_TAC_311 444.350 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 166 Bay_USA_Nation 444.350 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 167 Bay_Santa_Clara 444.350 +5 High - 180 - Color 1 1 17 64 # 31064 Santa Clara County Static + 168 Bay_NorCal 444.350 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Dynamic + 169 Bay_Bay-Net 444.350 +5 High - 180 - Color 1 2 20 75 # 31075 Bay-Net Static + 170 Bay_Anarchy 444.350 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 171 Bay_NC_5150 444.350 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 172 Bay_Parrot 444.350 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private # (10) W6OTX, Alum Rock, NorCal, PAARA - 151 Al_California 444.475 +5 High - 180 - Free 1 1 3 6 # 3106 California Static - 152 Al_TAC_310 444.475 +5 High - 180 - Free 1 1 3 5 # 310 TAC 310 Dynamic - 153 Al_Bay-Net 444.475 +5 High - 180 - Free 1 1 3 13 # 31075 Bay-Net Dynamic - 154 Al_World 444.475 +5 High - 180 - Free 1 1 3 3 # 91 Worldwide Dynamic - 155 Al_NorCal 444.475 +5 High - 180 - Free 1 2 3 11 # 31068 NorCal Static - 156 Al_Anarchy 444.475 +5 High - 180 - Free 1 2 3 18 # 31666 DMR of Anarchy Dynamic - 157 Al_NC_5150 444.475 +5 High - 180 - Free 1 2 3 19 # 95150 NorCal 5150 Dynamic - 158 Al_Parrot 444.475 +5 High - 180 - Free 1 2 3 24 # 9990 Parrot Private + 181 Al_RX_All 444.475 +5 Low - 180 + Color 1 1 1 - # Receive only + 182 Al_World 444.475 +5 High - 180 - Color 1 1 11 91 # 91 Worldwide Dynamic + 183 Al_North_Am 444.475 +5 High - 180 - Color 1 1 12 93 # 93 North America Dynamic + 184 Al_TAC_310 444.475 +5 High - 180 - Color 1 1 13 10 # 310 TAC 310 Dynamic + 185 Al_TAC_311 444.475 +5 High - 180 - Color 1 1 14 11 # 311 TAC 311 Dynamic + 186 Al_USA_Nation 444.475 +5 High - 180 - Color 1 1 15 100 # 3100 USA Nationwide Dynamic + 187 Al_California 444.475 +5 High - 180 - Color 1 1 16 106 # 3106 California Static + 188 Al_NorCal 444.475 +5 High - 180 - Color 1 2 19 68 # 31068 NorCal Static + 189 Al_Bay-Net 444.475 +5 High - 180 - Color 1 1 20 75 # 31075 Bay-Net Dynamic + 190 Al_Anarchy 444.475 +5 High - 180 - Color 1 2 23 66 # 31666 DMR of Anarchy Dynamic + 191 Al_NC_5150 444.475 +5 High - 180 - Color 1 2 24 95 # 95150 NorCal 5150 Dynamic + 192 Al_Parrot 444.475 +5 High - 180 - Color 1 1 - 90 # 9990 Parrot Private + +# (11) DMR simplex + 701 441.0 441.000 +0 High 3 555 - - 1 1 9 99 + 702 446.5 446.500 +0 High 3 555 - - 1 1 9 99 + 703 446.075 446.075 +0 High 3 555 - - 1 1 9 99 + 704 433.45 433.450 +0 High 3 555 - - 1 1 9 99 + 705 145.79 145.790 +0 High 3 555 - - 1 1 9 99 + 706 145.51 145.510 +0 High 3 555 - - 1 1 9 99 # Table of analog channels. # 1) Channel number: 1-1024 @@ -180,7 +204,7 @@ Digital Name Receive Transmit Power Scan TOT RO Admit Color Slot # Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxTone TxTone Width -# VHF Band 144-148 +# (12) VHF Band 144-148 201 Palo_Alto_N6NFI 145.230 -0.6 High 1 180 - Free 1 - 100.0 25 202 Milpitas_W6MLP 145.430 -0.6 High 1 180 - Free 1 - 85.4 25 203 Saratoga_K6SA 146.655 -0.6 High 1 180 - Free 1 - 114.8 25 @@ -194,7 +218,7 @@ Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxT 211 Morgan_Hil_K7DAA 147.330 +0.6 High 1 180 - Free 1 - 103.5 25 212 Los_Gatos_AB6LI 147.945 -0.6 High 1 180 - Free 1 - 156.7 25 -# UHF Band 440-446 +# (13) UHF Band 440-446 301 San_Jose_W6YOP 440.275 +5 High 1 180 - Free 1 - 127.3 25 302 Woodside_N6ZX 440.450 +5 High 1 180 - Free 1 - 107.2 25 303 Campbell_NO1PC 441.025 +5 High 1 180 - Free 1 - 110.9 25 @@ -210,7 +234,7 @@ Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxT 313 San_Jose_K6GOD 444.725 +5 High 1 180 - Free 1 - 162.2 25 314 Los_Gatos_WB6KHP 444.975 +5 High 1 180 - Free 1 - 127.3 25 -# Simplex frequencies +# (14) Simplex frequencies 401 S_446.0 446.000 +0 High 2 180 - Free 1 - - 25 402 S_446.5 446.500 +0 High 2 180 - Free 1 - - 25 403 S_441.0 441.000 +0 High 2 180 - Free 1 - - 25 @@ -220,45 +244,27 @@ Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxT 407 WX_1 162.450 +0 Low - 180 + Free 1 - - 25 408 WX_5 162.550 +0 Low - 180 + Free 1 - - 25 -# FRS/GMRS Band - 501 GMRS-1 462.5625 +0 High 3 300 - Free 1 - - 25 - 502 GMRS-2 462.5875 +0 High 3 300 - Free 1 - - 25 - 503 GMRS-3 462.6125 +0 High 3 300 - Free 1 - - 25 - 504 GMRS-4 462.6375 +0 High 3 300 - Free 1 - - 25 - 505 GMRS-5 462.6625 +0 High 3 300 - Free 1 - - 25 - 506 GMRS-6 462.6875 +0 High 3 300 - Free 1 - - 25 - 507 GMRS-7 462.7125 +0 High 3 300 - Free 1 - - 25 - 508 FRS-8 467.5625 +0 Low 3 300 - Free 1 - - 25 - 509 FRS-9 467.5875 +0 Low 3 300 - Free 1 - - 25 - 510 FRS-10 467.6125 +0 Low 3 300 - Free 1 - - 25 - 511 FRS-11 467.6375 +0 Low 3 300 - Free 1 - - 25 - 512 FRS-12 467.6625 +0 Low 3 300 - Free 1 - - 25 - 513 FRS-13 467.6875 +0 Low 3 300 - Free 1 - - 25 - 514 FRS-14 467.7125 +0 Low 3 300 - Free 1 - - 25 - -# # Table of channel zones. # 1) Zone number: 1-250 # 2) Name: up to 16 characters, use '_' instead of space # 3) List of channels: numbers and ranges (N-M) separated by comma # Zone Name Channels - 1 Milpitas 1-8 # 440.125 W6TCP - 2 San_Bruno 11-22 # 440.500 N6AMG - 3 Boulder_Creek 31-43 # 440.5875 WB6ECE - 4 Palo_Alto 51-62 # 441.850 K6OTR - 5 Saratoga 71-84 # 441.950 KK6USZ - 6 Mt_Umunhum 91-103 # 442.5375 WA6YCZ - 7 Berkeley 101-115 # 443.500 K6LNK - 8 Loma_Prieta 121-130 # 444.025 K6HLE - 9 Baynet_Palo_Alto 141-148 # 444.350 WW6BAY - 10 Alum_Rock 151-158 # 444.475 W6OTX - 11 VHF 201-212 # 144-148 VHF Band - 12 UHF 301-314 # 440-446 UHF Band - 13 Simplex 401-408 # Simplex VHF, UHF - 14 GMRS 501-514 # 462-467 GMRS + 1 Milpitas 1-12 # 440.125 W6TCP + 2 San_Bruno 21-35 # 440.500 N6AMG + 3 Boulder_Creek 41-56 # 440.5875 WB6ECE + 4 Palo_Alto 61-75 # 441.850 K6OTR + 5 Saratoga 81-96 # 441.950 KK6USZ + 6 Mt_Umunhum 101-116 # 442.5375 WA6YCZ + 7 Berkeley 121-132 # 443.500 K6LNK + 8 Loma_Prieta 141-153 # 444.025 K6HLE + 9 Baynet_Palo_Alto 161-172 # 444.350 WW6BAY + 10 Alum_Rock 181-192 # 444.475 W6OTX + 11 DMR_Simplex 701-706 # DMR Simplex: UHF, VHF + 12 VHF 201-212 # 144-148 VHF Band + 13 UHF 301-314 # 440-446 UHF Band + 14 FM_Simplex 401-408 # FM Simplex: VHF, UHF -# # Table of scan lists. # 1) Scan list number: 1-250 # 2) Name: up to 16 characters, use '_' instead of space @@ -269,64 +275,53 @@ Zone Name Channels # Scanlist Name PCh1 PCh2 TxCh Channels 1 Analog - - Last 201-212,301-314 - 2 Simplex - - Last 401-406 - 3 GMRS - - Last 501-514 + 2 FM_Simplex - - Last 401-406 + 3 DMR_Simplex - - Last 701-706 -# # Table of group lists. -# 1) Group list number: 1-250 +# 1) Group list number: 1-64 # 2) Name: up to 16 characters, use '_' instead of space # 3) List of contacts: numbers and ranges (N-M) separated by comma # Grouplist Name Contacts - 1 Timeslot_1 2 # 23 Cluster K6LNK - 1 Timeslot_1 3 # 91 Worldwide - 1 Timeslot_1 4 # 93 North America - 1 Timeslot_1 5 # 310 TAC 310 - 1 Timeslot_1 6 # 3106 California - 1 Timeslot_1 7 # 3131 Nebraska - 1 Timeslot_1 8 # 3148 Texas - 1 Timeslot_1 9 # 31064 Santa Clara County - 1 Timeslot_1 10 # 31065 NorCal AllStar - 1 Timeslot_1 12 # 31072 KPARN - 1 Timeslot_1 13 # 31075 Bay-Net - 1 Timeslot_1 14 # 31078 XLX013D PAPA - 1 Timeslot_1 15 # 31096 USA - Area 6 4646 - 1 Timeslot_1 16 # 31328 SNARS (Reno/Tahoe) - 1 Timeslot_1 17 # 31329 SNARS 2 + 1 All_Groups 91 # 91 Worldwide + 1 All_Groups 93 # 93 North America + 1 All_Groups 10 # 310 TAC 310 + 1 All_Groups 11 # 311 TAC 311 + 1 All_Groups 100 # 3100 USA Nationwide + 1 All_Groups 106 # 3106 California + 1 All_Groups 64 # 31064 Santa Clara County + 1 All_Groups 65 # 31065 NorCal AllStar + 1 All_Groups 68 # 31068 NorCal + 1 All_Groups 75 # 31075 Bay-Net + 1 All_Groups 78 # 31078 XLX013D PAPA + 1 All_Groups 96 # 31096 USA - Area 6 4646 + 1 All_Groups 66 # 31666 DMR of Anarchy + 1 All_Groups 95 # 95150 NorCal 5150 + 1 All_Groups 97 # 97150 Baycom - 2 Timeslot_2 1 # 21 Cluster CARLA - 2 Timeslot_2 22 # 310604 CARLA - 2 Timeslot_2 21 # 110670 Cluster KK6USZ - 2 Timeslot_2 23 # 310703 Tri-LERT - 2 Timeslot_2 9 # 31064 Santa Clara County - 2 Timeslot_2 11 # 31068 NorCal - 2 Timeslot_2 13 # 31075 Bay-Net - 2 Timeslot_2 18 # 31666 DMR of Anarchy - 2 Timeslot_2 19 # 95150 NorCal 5150 - 2 Timeslot_2 20 # 97150 Baycom + 9 Simplex 99 # 99 Simplex + 11 Worldwide 91 # 91 Worldwide + 12 North_America 93 # 93 North America + 13 TAC_310 10 # 310 TAC 310 + 14 TAC_311 11 # 311 TAC 311 + 15 USA_Nationwide 100 # 3100 USA Nationwide + 16 California 106 # 3106 California + 17 Santa_Clara_Cnty 64 # 31064 Santa Clara County + 18 NorCal_AllStar 65 # 31065 NorCal AllStar + 19 NorCal 68 # 31068 NorCal + 20 Bay-Net 75 # 31075 Bay-Net + 21 XLX013D_PAPA 78 # 31078 XLX013D PAPA + 22 USA_Area_6 96 # 31096 USA - Area 6 4646 + 23 DMR_of_Anarchy 66 # 31666 DMR of Anarchy + 24 NorCal_5150 95 # 95150 NorCal 5150 + 25 Baycom 97 # 97150 Baycom + 26 Nebraska 131 # 3131 Nebraska + 27 Texas 148 # 3148 Texas - 3 Both 3 # 91 Worldwide - 3 Both 4 # 93 North America - 3 Both 5 # 310 TAC 310 - 3 Both 6 # 3106 California - 3 Both 7 # 3131 Nebraska - 3 Both 8 # 3148 Texas - 3 Both 9 # 31064 Santa Clara County - 3 Both 10 # 31065 NorCal AllStar - 3 Both 11 # 31068 NorCal - 3 Both 13 # 31075 Bay-Net - 3 Both 14 # 31078 XLX013D PAPA - 3 Both 15 # 31096 USA - Area 6 4646 - 3 Both 18 # 31666 DMR of Anarchy - 3 Both 19 # 95150 NorCal 5150 - 3 Both 20 # 97150 Baycom - 3 Both 22 # 310604 CARLA - -# # Table of contacts. -# 1) Contact number: 1-10000 +# 1) Contact number: 1-256 # 2) Name: up to 16 characters, use '_' instead of space # 3) Call type: Group, Private, All # 4) Call ID: 1...16777215 @@ -335,29 +330,40 @@ Grouplist Name Contacts Contact Name Type ID RxTone # From all Bay Area repeaters - 1 Cluster_CARLA Group 21 - - 2 Cluster_K6LNK Group 23 - - 3 Worldwide Group 91 - - 4 North_America Group 93 - - 5 TAC_310 Group 310 - - 6 California Group 3106 - - 7 Nebraska Group 3131 - - 8 Texas Group 3148 - - 9 Santa_Clara_Cnty Group 31064 - - 10 NorCal_AllStar Group 31065 - - 11 NorCal Group 31068 - - 12 KPARN Group 31072 - - 13 Bay-Net Group 31075 - - 14 XLX013D_PAPA Group 31078 - - 15 USA_Area_6 Group 31096 - - 16 SNARS Group 31328 - - 17 SNARS_2 Group 31329 - - 18 DMR_of_Anarchy Group 31666 - - 19 NorCal_5150 Group 95150 - - 20 Baycom Group 97150 - - 21 Cluster_KK6USZ Group 110670 - - 22 CARLA Group 310604 - - 23 Tri-LERT Group 310703 - + 91 Worldwide Group 91 - + 93 North_America Group 93 - + 99 Simplex Group 99 - + 10 TAC_310 Group 310 - + 11 TAC_311 Group 311 - + 100 USA_Nationwide Group 3100 - + 106 California Group 3106 - + 131 Nebraska Group 3131 - + 148 Texas Group 3148 - + 64 Santa_Clara_Cnty Group 31064 - + 65 NorCal_AllStar Group 31065 - + 68 NorCal Group 31068 - + 72 KPARN Group 31072 - + 75 Bay-Net Group 31075 - + 78 XLX013D_PAPA Group 31078 - + 96 USA_Area_6 Group 31096 - + 28 SNARS Group 31328 - + 29 SNARS_2 Group 31329 - + 66 DMR_of_Anarchy Group 31666 - + 95 NorCal_5150 Group 95150 - + 97 Baycom Group 97150 - # Private - 24 Parrot Private 9990 - + 90 Parrot Private 9990 - + 42 Sergey_KK6ABQ Private 3114542 + # Sergey Vakulenko + +# Table of text messages. +# 1) Message number: 1-32 +# 2) Text: up to 144 characters +# +Message Text + 1 Hi there! + 2 QSY Norcal 31068, pls + 3 QSY California 3106, pls + 4 Be Right Back + 5 My email is callsign@arrl.net + 6 73 . . . From 94c0bc5efb74f27999dc3eb69e3a075637e490c2 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Mon, 22 Oct 2018 21:23:20 -0700 Subject: [PATCH 13/21] Define d868uv channel structure. --- d868uv.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/d868uv.c b/d868uv.c index 77ad752..c4338b7 100644 --- a/d868uv.c +++ b/d868uv.c @@ -82,9 +82,130 @@ static fragment_t region_map[] = { // Channel data. // typedef struct { - // Bytes 0-63 - uint8_t data[64]; + // Bytes 0-7 + uint32_t rx_frequency; // RX Frequency: 8 digits BCD + uint32_t tx_offset; // TX Offset: 8 digits BCD + + // Byte 8 + uint8_t channel_mode : 2, // Mode: Analog or Digital +#define MODE_ANALOG 0 +#define MODE_DIGITAL 1 +#define MODE_A_D 2 +#define MODE_D_A 3 + + power : 2, // Power: Low, Middle, High, Turbo +#define POWER_LOW 0 +#define POWER_MIDDLE 1 +#define POWER_HIGH 2 +#define POWER_TURBO 3 + + bandwidth : 1, // Bandwidth: 12.5 or 25 kHz +#define BW_12_5_KHZ 0 +#define BW_25_KHZ 1 + + _unused8 : 2, // 0 + neg_tx_off : 1; // Negative TX offset? + + // Byte 9 + uint8_t rx_ctcss : 1, // CTCSS Decode + rx_dcs : 1, // DCS Decode + tx_ctcss : 1, // CTCSS Encode + tx_dcs : 1, // DCS Encode + reverse : 1, // Reverse + tx_prohibit : 1, // TX Prohibit + call_confirm : 1, // Call Confirmation + talkaround : 1; // Talk Around + + // Bytes 10-15 + uint8_t ctcss_transmit; // CTCSS Encode: 0=62.5, 50=254.1, 51=Define + uint8_t ctcss_receive; // CTCSS Decode: 0=62.5, 50=254.1, 51=Define + uint16_t dcs_transmit; // DCS Encode: 0=D000N, 17=D021N, 1023=D777N + uint16_t dcs_receive; // DCS Decode: 0=D000N, 17=D021N, 1023=D777N + + // Bytes 16-19 + uint16_t custom_ctcss; // 0x09cf=251.1, 0x0a28=260 + uint8_t tone2_decode; // 2Tone Decode: 0x00=1, 0x0f=16 + uint8_t _unused19; // 0 + + // Bytes 20-23 + uint16_t contact_index; // Contact: 0=Contact1, 1=Contact2, ... + uint16_t _unused22; // 0 + + // Byte 24 + uint8_t id_index; // Index in Radio ID table + + // Byte 25 + uint8_t ptt_id : 2, // PTT ID +#define PTTID_OFF 0 +#define PTTID_START 1 +#define PTTID_END 2 +#define PTTID_START_END 3 + + _unused25_1 : 2, // 0 + + squelch_mode : 1, // Squelch Mode +#define SQ_CARRIER 0 // Carrier +#define SQ_TONE 1 // CTCSS/DCS + + _unused25_2 : 3; // 0 + + // Byte 26 + uint8_t tx_permit : 2, // TX Permit +#define PERMIT_ALWAYS 0 // Always +#define PERMIT_CH_FREE 1 // Channel Free +#define PERMIT_CC_DIF 2 // Different Color Code +#define PERMIT_CC_SAME 3 // Same Color Code + + _unused26_1 : 2, // 0 + + _opt_signal : 2, // Optional Signal +#define OPTSIG_OFF 0 // Off +#define OPTSIG_DTMF 1 // DTMF +#define OPTSIG_2TONE 2 // 2Tone +#define OPTSIG_5TONE 3 // 5Tone + + _unused26_2 : 2; // 0 + + // Bytes 27-31 + uint8_t scan_list_index; // Scan List: 0xff=None, 0=ScanList1... + uint8_t group_list_index; // Receive Group List: 0xff=None, 0=GroupList1... + uint8_t id_2tone; // 2Tone ID: 0=1, 0x17=24 + uint8_t id_5tone; // 5Tone ID: 0=1, 0x63=100 + uint8_t id_dtmf; // DTMF ID: 0=1, 0x0f=16 + + // Byte 32 + uint8_t color_code; // Color Code: 0-15 + + // Byte 33 + uint8_t slot2 : 1, // Slot: Slot2 + _unused33_1 : 1, // 0 + simplex_tdma : 1, // Simplex TDMA: On + _unused33_2 : 1, // 0 + tdma_adaptive : 1, // TDMA Adaptive: On + _unused33_3 : 1, // 0 + enh_encryption : 1, // Encryption Type: Enhanced Encryption + work_alone : 1; // Work Alone: On + + // Byte 34 + uint8_t encryption; // Digital Encryption: 1-32, 0=Off + + // Bytes 35-51 + uint8_t name[16]; // Channel Name, zero filled + uint8_t _unused51; // 0 + + // Byte 52 + uint8_t ranging : 1, // Ranging: On + through_mode : 1, // Through Mode: On + _unused52 : 6; // 0 + + // Byte 53 + uint8_t aprs_report : 1, // APRS Report: On + _unused53 : 7; // 0 + + // Bytes 54-63 + uint8_t aprs_channel; // APRS Report Channel: 0x00=1, ... 0x07=8 + uint8_t _unused55[9]; // 0 } channel_t; // From b03daba8ab5511a6c6c505258527dfe5d41f1dd9 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Thu, 25 Oct 2018 12:03:01 -0700 Subject: [PATCH 14/21] D868UV: able to print a list of channels. --- d868uv.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++--------- util.c | 24 +++++ util.h | 1 + 3 files changed, 294 insertions(+), 50 deletions(-) diff --git a/d868uv.c b/d868uv.c index c4338b7..26b5b0c 100644 --- a/d868uv.c +++ b/d868uv.c @@ -89,10 +89,10 @@ typedef struct { // Byte 8 uint8_t channel_mode : 2, // Mode: Analog or Digital -#define MODE_ANALOG 0 -#define MODE_DIGITAL 1 -#define MODE_A_D 2 -#define MODE_D_A 3 +#define MODE_ANALOG 0 // Analog +#define MODE_DIGITAL 1 // Digital +#define MODE_A_D 2 // A+D, transmit analog +#define MODE_D_A 3 // D+A, transmit digital power : 2, // Power: Low, Middle, High, Turbo #define POWER_LOW 0 @@ -105,7 +105,7 @@ typedef struct { #define BW_25_KHZ 1 _unused8 : 2, // 0 - neg_tx_off : 1; // Negative TX offset? + neg_tx_offset : 1; // Negative TX offset? // Byte 9 uint8_t rx_ctcss : 1, // CTCSS Decode @@ -113,7 +113,7 @@ typedef struct { tx_ctcss : 1, // CTCSS Encode tx_dcs : 1, // DCS Encode reverse : 1, // Reverse - tx_prohibit : 1, // TX Prohibit + rx_only : 1, // TX Prohibit call_confirm : 1, // Call Confirmation talkaround : 1; // Talk Around @@ -154,7 +154,7 @@ typedef struct { uint8_t tx_permit : 2, // TX Permit #define PERMIT_ALWAYS 0 // Always #define PERMIT_CH_FREE 1 // Channel Free -#define PERMIT_CC_DIF 2 // Different Color Code +#define PERMIT_CC_DIFF 2 // Different Color Code #define PERMIT_CC_SAME 3 // Same Color Code _unused26_1 : 2, // 0 @@ -262,6 +262,10 @@ typedef struct { } radioid_t; +static const char *POWER_NAME[] = { "Low", "Mid", "High", "Turbo" }; +static const char *ADMIT_NAME[] = { "-", "Free", "DiffCC", "Color" }; +static const char *BANDWIDTH[] = { "12.5", "25" }; + // // Print a generic information about the device. // @@ -401,6 +405,231 @@ static void print_intro(FILE *out, int verbose) fprintf(out, "\n"); } +// +// Get channel bank by index. +// +static channel_t *get_bank(int i) +{ + return (channel_t*) &radio_mem[OFFSET_BANK1 + i*0x2000]; +} + +// +// Get channel by index. +// +static channel_t *get_channel(int i) +{ + channel_t *bank = get_bank(i >> 7); + uint8_t *bitmap = &radio_mem[OFFSET_CHAN_BITMAP]; + + if ((bitmap[i / 8] >> (i & 7)) & 1) + return &bank[i % 128]; + else + return 0; +} + +// +// Do we have any channels of given mode? +// +static int have_channels(int mode) +{ + int i; + + for (i=0; ichannel_mode == mode) + return 1; + + // Treat D+A mode as digital. + if (mode == MODE_DIGITAL && ch->channel_mode == MODE_D_A) + return 1; + + // Treat A+D mode as analog. + if (mode == MODE_ANALOG && ch->channel_mode == MODE_A_D) + return 1; + } + return 0; +} + +// +// Print base parameters of the channel: +// Name +// RX Frequency +// TX Frequency +// Power +// Scan List +// TOT +// RX Only +// Admit Criteria +// +static void print_chan_base(FILE *out, channel_t *ch, int cnum) +{ + fprintf(out, "%5d ", cnum); + print_ascii(out, ch->name, 16, 1); + fprintf(out, " "); + print_freq(out, ch->rx_frequency); + fprintf(out, " "); + print_offset_delta(out, ch->rx_frequency, ch->tx_offset); + + fprintf(out, "%-4s ", POWER_NAME[ch->power]); + + if (ch->scan_list_index == 0) + fprintf(out, "- "); + else + fprintf(out, "%-4d ", ch->scan_list_index); + + //TODO +// if (ch->tot == 0) + fprintf(out, "- "); +// else +// fprintf(out, "%-3d ", ch->tot * 15); + + fprintf(out, "%c ", "-+"[ch->rx_only]); + + fprintf(out, "%-6s ", ADMIT_NAME[ch->tx_permit]); + //TODO: Busy Lock for analog channels +} + +static void print_digital_channels(FILE *out, int verbose) +{ + int i; + + if (verbose) { + fprintf(out, "# Table of digital channels.\n"); + fprintf(out, "# 1) Channel number: 1-%d\n", NCHAN); + fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); + fprintf(out, "# 3) Receive frequency in MHz\n"); + fprintf(out, "# 4) Transmit frequency or +/- offset in MHz\n"); + fprintf(out, "# 5) Transmit power: High, Low\n"); + fprintf(out, "# 6) Scan list: - or index in Scanlist table\n"); + fprintf(out, "# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n"); + fprintf(out, "# 8) Receive only: -, +\n"); + fprintf(out, "# 9) Admit criteria: -, Free, Color\n"); + fprintf(out, "# 10) Color code: 0, 1, 2, 3... 15\n"); + fprintf(out, "# 11) Time slot: 1 or 2\n"); + fprintf(out, "# 12) Receive group list: - or index in Grouplist table\n"); + fprintf(out, "# 13) Contact for transmit: - or index in Contacts table\n"); + fprintf(out, "#\n"); + } + fprintf(out, "Digital Name Receive Transmit Power Scan TOT RO Admit Color Slot RxGL TxContact"); + fprintf(out, "\n"); + for (i=0; ichannel_mode != MODE_DIGITAL && ch->channel_mode != MODE_D_A) { + // Select digital channels + continue; + } + print_chan_base(out, ch, i+1); + + // Print digital parameters of the channel: + // Color Code + // Repeater Slot + // Group List + // Contact Name + fprintf(out, "%-5d %-3d ", ch->color_code, 1 + ch->slot2); + + if (ch->group_list_index == 0) + fprintf(out, "- "); + else + fprintf(out, "%-4d ", ch->group_list_index); + + if (ch->contact_index == 0) + fprintf(out, "-"); + else + fprintf(out, "%-4d", ch->contact_index); +#if 0 + // Print contact name as a comment. + //TODO + if (ch->contact_index > 0) { + contact_t *ct = GET_CONTACT(ch->contact_index - 1); + if (VALID_CONTACT(ct)) { + fprintf(out, " # "); + print_ascii(out, ct->name, 16, 0); + } + } +#endif + fprintf(out, "\n"); + } +} + +static void print_ctcss(FILE *out, unsigned ctcss) +{ + //TODO + fprintf(out, "0x%02x ", ctcss); +} + +static void print_dcs(FILE *out, unsigned dcs) +{ + //TODO + fprintf(out, "0x%04x ", dcs); +} + +static void print_analog_channels(FILE *out, int verbose) +{ + int i; + + if (verbose) { + fprintf(out, "# Table of analog channels.\n"); + fprintf(out, "# 1) Channel number: 1-%d\n", NCHAN); + fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); + fprintf(out, "# 3) Receive frequency in MHz\n"); + fprintf(out, "# 4) Transmit frequency or +/- offset in MHz\n"); + fprintf(out, "# 5) Transmit power: High, Low\n"); + fprintf(out, "# 6) Scan list: - or index\n"); + fprintf(out, "# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n"); + fprintf(out, "# 8) Receive only: -, +\n"); + fprintf(out, "# 9) Admit criteria: -, Free, Tone\n"); + fprintf(out, "# 10) Squelch level: Normal, Tight\n"); + fprintf(out, "# 11) Guard tone for receive, or '-' to disable\n"); + fprintf(out, "# 12) Guard tone for transmit, or '-' to disable\n"); + fprintf(out, "# 13) Bandwidth in kHz: 12.5, 20, 25\n"); + fprintf(out, "#\n"); + } + fprintf(out, "Analog Name Receive Transmit Power Scan AS TOT RO Admit Squelch RxTone TxTone Width"); + fprintf(out, "\n"); + for (i=0; ichannel_mode != MODE_ANALOG && ch->channel_mode != MODE_A_D) { + // Select analog channels + continue; + } + print_chan_base(out, ch, i+1); + + // Print analog parameters of the channel: + // Squelch + // CTCSS/DCS Dec + // CTCSS/DCS Enc + // Bandwidth + fprintf(out, "%-7s ", "Normal"); + + if (ch->rx_ctcss) + print_ctcss(out, ch->ctcss_receive); + else if (ch->rx_dcs) + print_dcs(out, ch->dcs_receive); + else + fprintf(out, "- "); + + fprintf(out, " "); + if (ch->tx_ctcss) + print_ctcss(out, ch->ctcss_transmit); + else if (ch->tx_dcs) + print_dcs(out, ch->dcs_transmit); + else + fprintf(out, "- "); + + fprintf(out, " %s", BANDWIDTH[ch->bandwidth]); + fprintf(out, "\n"); + } +} + // // Print full information about the device configuration. // @@ -410,6 +639,18 @@ static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) if (verbose) d868uv_print_version(radio, out); + // + // Channels. + // + if (have_channels(MODE_DIGITAL)) { + fprintf(out, "\n"); + print_digital_channels(out, verbose); + } + if (have_channels(MODE_ANALOG)) { + fprintf(out, "\n"); + print_analog_channels(out, verbose); + } + //TODO // General settings. @@ -451,28 +692,6 @@ static void d868uv_save_image(radio_device_t *radio, FILE *img) fwrite(&radio_mem[0], 1, MEMSZ, img); } -// -// Get channel bank by index. -// -static channel_t *get_bank(int i) -{ - return (channel_t*) &radio_mem[OFFSET_BANK1 + i*0x2000]; -} - -// -// Get channel by index. -// -/*static*/ channel_t *get_channel(int i) -{ - channel_t *bank = get_bank(i >> 7); - uint8_t *bitmap = &radio_mem[OFFSET_CHAN_BITMAP]; - - if ((bitmap[i / 8] >> (i & 7)) & 1) - return &bank[i % 128]; - else - return 0; -} - // // Parse the scalar parameter. // @@ -580,9 +799,9 @@ static int d868uv_verify_config(radio_device_t *radio) // Channels: check references to scanlists, contacts and grouplists. for (i=0; iname, 16, 0); + print_ascii(stderr, ch->name, 16, 0); fprintf(stderr, "': scanlist %d not found.\n", ch->scan_list_index); nerrors++; } } - if (ch->contact_name_index != 0) { - contact_t *ct = GET_CONTACT(ch->contact_name_index - 1); + if (ch->contact_index != 0) { + contact_t *ct = GET_CONTACT(ch->contact_index - 1); if (!VALID_CONTACT(ct)) { fprintf(stderr, "Channel %d '", i+1); - print_unicode(stderr, ch->name, 16, 0); - fprintf(stderr, "': contact %d not found.\n", ch->contact_name_index); + print_ascii(stderr, ch->name, 16, 0); + fprintf(stderr, "': contact %d not found.\n", ch->contact_index); nerrors++; } } @@ -611,7 +830,7 @@ static int d868uv_verify_config(radio_device_t *radio) if (!VALID_GROUPLIST(gl)) { fprintf(stderr, "Channel %d '", i+1); - print_unicode(stderr, ch->name, 16, 0); + print_ascii(stderr, ch->name, 16, 0); fprintf(stderr, "': grouplist %d not found.\n", ch->group_list_index); nerrors++; } @@ -633,11 +852,11 @@ static int d868uv_verify_config(radio_device_t *radio) int cnum = z->member_a[k]; if (cnum != 0) { - channel_t *ch = GET_CHANNEL(cnum - 1); + channel_t *ch = get_channel(cnum - 1); - if (!VALID_CHANNEL(ch)) { + if (!ch) { fprintf(stderr, "Zone %da '", i+1); - print_unicode(stderr, z->name, 16, 0); + print_ascii(stderr, z->name, 16, 0); fprintf(stderr, "': channel %d not found.\n", cnum); nerrors++; } @@ -647,11 +866,11 @@ static int d868uv_verify_config(radio_device_t *radio) int cnum = zext->ext_a[k]; if (cnum != 0) { - channel_t *ch = GET_CHANNEL(cnum - 1); + channel_t *ch = get_channel(cnum - 1); - if (!VALID_CHANNEL(ch)) { + if (!ch) { fprintf(stderr, "Zone %da '", i+1); - print_unicode(stderr, z->name, 16, 0); + print_ascii(stderr, z->name, 16, 0); fprintf(stderr, "': channel %d not found.\n", cnum); nerrors++; } @@ -663,11 +882,11 @@ static int d868uv_verify_config(radio_device_t *radio) int cnum = zext->member_b[k]; if (cnum != 0) { - channel_t *ch = GET_CHANNEL(cnum - 1); + channel_t *ch = get_channel(cnum - 1); - if (!VALID_CHANNEL(ch)) { + if (!ch) { fprintf(stderr, "Zone %db '", i+1); - print_unicode(stderr, z->name, 16, 0); + print_ascii(stderr, z->name, 16, 0); fprintf(stderr, "': channel %d not found.\n", cnum); nerrors++; } @@ -687,11 +906,11 @@ static int d868uv_verify_config(radio_device_t *radio) int cnum = sl->member[k]; if (cnum != 0) { - channel_t *ch = GET_CHANNEL(cnum - 1); + channel_t *ch = get_channel(cnum - 1); - if (!VALID_CHANNEL(ch)) { + if (!ch) { fprintf(stderr, "Scanlist %d '", i+1); - print_unicode(stderr, sl->name, 16, 0); + print_ascii(stderr, sl->name, 16, 0); fprintf(stderr, "': channel %d not found.\n", cnum); nerrors++; } @@ -715,7 +934,7 @@ static int d868uv_verify_config(radio_device_t *radio) if (!VALID_CONTACT(ct)) { fprintf(stderr, "Grouplist %d '", i+1); - print_unicode(stderr, gl->name, 16, 0); + print_ascii(stderr, gl->name, 16, 0); fprintf(stderr, "': contact %d not found.\n", cnum); nerrors++; } diff --git a/util.c b/util.c index 357e2b7..00b8fbd 100644 --- a/util.c +++ b/util.c @@ -557,6 +557,30 @@ void print_offset(FILE *out, unsigned rx_bcd, unsigned tx_bcd) } } +// +// Print the transmit offset or frequency. +// TX value is a delta. +// +void print_offset_delta(FILE *out, unsigned rx_bcd, unsigned tx_delta_bcd) +{ + int rx_hz = freq_to_hz(rx_bcd); + int delta = freq_to_hz(tx_delta_bcd); + int tx_hz = rx_hz + delta; + + if (delta == 0) { + fprintf(out, "+0 "); + } else if (delta > 0 && delta/50000 <= 255) { + fprintf(out, "+"); + print_mhz(out, delta); + } else if (delta < 0 && -delta/50000 <= 255) { + fprintf(out, "-"); + print_mhz(out, -delta); + } else { + fprintf(out, " "); + print_mhz(out, tx_hz); + } +} + // // Compare channel index for qsort(). // diff --git a/util.h b/util.h index f3b33d5..af38d31 100644 --- a/util.h +++ b/util.h @@ -201,6 +201,7 @@ void print_mhz(FILE *out, unsigned hz); // Print the transmit offset or frequency. // void print_offset(FILE *out, unsigned rx_bcd, unsigned tx_bcd); +void print_offset_delta(FILE *out, unsigned rx_bcd, unsigned delta_bcd); // // Compare channel index for qsort(). From 0e7b2cbf62ffc0c92ee5534c6462dceb72b57daf Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Thu, 25 Oct 2018 15:26:26 -0700 Subject: [PATCH 15/21] Print d868uv frequencies. --- d868uv.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++-------- md380.c | 4 +-- util.c | 24 --------------- util.h | 1 - 4 files changed, 84 insertions(+), 39 deletions(-) diff --git a/d868uv.c b/d868uv.c index 26b5b0c..cfe8504 100644 --- a/d868uv.c +++ b/d868uv.c @@ -104,8 +104,11 @@ typedef struct { #define BW_12_5_KHZ 0 #define BW_25_KHZ 1 - _unused8 : 2, // 0 - neg_tx_offset : 1; // Negative TX offset? + _unused8 : 1, // 0 + repeater_mode : 2; // Sign of TX frequency offset +#define RM_SIMPLEX 0 // TX frequency = RX frequency +#define RM_TXPOS 1 // Positive TX offset +#define RM_TXNEG 2 // Negative TX offset // Byte 9 uint8_t rx_ctcss : 1, // CTCSS Decode @@ -453,6 +456,73 @@ static int have_channels(int mode) return 0; } +// +// Print frequency (BCD value). +// +static void print_rx_freq(FILE *out, unsigned data) +{ + fprintf(out, "%d%d%d.%d%d%d", (data >> 4) & 15, data & 15, + (data >> 12) & 15, (data >> 8) & 15, + (data >> 20) & 15, (data >> 16) & 15); + + if ((data & 0xff) == 0) { + fputs(" ", out); + } else { + fprintf(out, "%d", (data >> 28) & 15); + if (((data >> 24) & 15) == 0) { + fputs(" ", out); + } else { + fprintf(out, "%d", (data >> 24) & 15); + } + } +} + +// +// Convert a 4-byte frequency value from binary coded decimal +// to integer format (in Hertz). +// +static int bcd_to_hz(unsigned bcd) +{ + int a = (bcd >> 4) & 15; + int b = bcd & 15; + int c = (bcd >> 12) & 15; + int d = (bcd >> 8) & 15; + int e = (bcd >> 20) & 15; + int f = (bcd >> 16) & 15; + int g = (bcd >> 28) & 15; + int h = (bcd >> 24) & 15; + + return (((((((a*10 + b) * 10 + c) * 10 + d) * 10 + e) * 10 + f) * 10 + g) * 10 + h) * 10; +} + +// +// Print the transmit offset or frequency. +// TX value is a delta. +// +static void print_tx_offset(FILE *out, unsigned tx_offset_bcd, unsigned mode) +{ + int offset; + + switch (mode) { + default: + case RM_SIMPLEX: // TX frequency = RX frequency + fprintf(out, "+0 "); + break; + + case RM_TXPOS: // Positive TX offset + offset = bcd_to_hz(tx_offset_bcd); + fprintf(out, "+"); + print_mhz(out, offset); + break; + + case RM_TXNEG: // Negative TX offset + offset = bcd_to_hz(tx_offset_bcd); + fprintf(out, "-"); + print_mhz(out, offset); + break; + } +} + // // Print base parameters of the channel: // Name @@ -469,16 +539,16 @@ static void print_chan_base(FILE *out, channel_t *ch, int cnum) fprintf(out, "%5d ", cnum); print_ascii(out, ch->name, 16, 1); fprintf(out, " "); - print_freq(out, ch->rx_frequency); + print_rx_freq(out, ch->rx_frequency); fprintf(out, " "); - print_offset_delta(out, ch->rx_frequency, ch->tx_offset); + print_tx_offset(out, ch->tx_offset, ch->repeater_mode); - fprintf(out, "%-4s ", POWER_NAME[ch->power]); + fprintf(out, "%-5s ", POWER_NAME[ch->power]); - if (ch->scan_list_index == 0) + if (ch->scan_list_index == 0xff) fprintf(out, "- "); else - fprintf(out, "%-4d ", ch->scan_list_index); + fprintf(out, "%-4d ", ch->scan_list_index + 1); //TODO // if (ch->tot == 0) @@ -533,15 +603,15 @@ static void print_digital_channels(FILE *out, int verbose) // Contact Name fprintf(out, "%-5d %-3d ", ch->color_code, 1 + ch->slot2); - if (ch->group_list_index == 0) + if (ch->group_list_index == 0xff) fprintf(out, "- "); else - fprintf(out, "%-4d ", ch->group_list_index); + fprintf(out, "%-4d ", ch->group_list_index + 1); - if (ch->contact_index == 0) + if (ch->contact_index == 0xffff) fprintf(out, "-"); else - fprintf(out, "%-4d", ch->contact_index); + fprintf(out, "%-4d", ch->contact_index + 1); #if 0 // Print contact name as a comment. //TODO @@ -590,7 +660,7 @@ static void print_analog_channels(FILE *out, int verbose) fprintf(out, "# 13) Bandwidth in kHz: 12.5, 20, 25\n"); fprintf(out, "#\n"); } - fprintf(out, "Analog Name Receive Transmit Power Scan AS TOT RO Admit Squelch RxTone TxTone Width"); + fprintf(out, "Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxTone TxTone Width"); fprintf(out, "\n"); for (i=0; i 0 && delta/50000 <= 255) { - fprintf(out, "+"); - print_mhz(out, delta); - } else if (delta < 0 && -delta/50000 <= 255) { - fprintf(out, "-"); - print_mhz(out, -delta); - } else { - fprintf(out, " "); - print_mhz(out, tx_hz); - } -} - // // Compare channel index for qsort(). // diff --git a/util.h b/util.h index af38d31..f3b33d5 100644 --- a/util.h +++ b/util.h @@ -201,7 +201,6 @@ void print_mhz(FILE *out, unsigned hz); // Print the transmit offset or frequency. // void print_offset(FILE *out, unsigned rx_bcd, unsigned tx_bcd); -void print_offset_delta(FILE *out, unsigned rx_bcd, unsigned delta_bcd); // // Compare channel index for qsort(). From 56f86cfdbe92d331095463089f58e9b34881258f Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Thu, 25 Oct 2018 18:05:52 -0700 Subject: [PATCH 16/21] Fix admit values for d868uv. --- d868uv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/d868uv.c b/d868uv.c index cfe8504..8d8899a 100644 --- a/d868uv.c +++ b/d868uv.c @@ -266,7 +266,8 @@ typedef struct { } radioid_t; static const char *POWER_NAME[] = { "Low", "Mid", "High", "Turbo" }; -static const char *ADMIT_NAME[] = { "-", "Free", "DiffCC", "Color" }; +static const char *DIGITAL_ADMIT_NAME[] = { "-", "Free", "NColor", "Color" }; +static const char *ANALOG_ADMIT_NAME[] = { "-", "Tone", "Free", "Free" }; static const char *BANDWIDTH[] = { "12.5", "25" }; // @@ -532,7 +533,6 @@ static void print_tx_offset(FILE *out, unsigned tx_offset_bcd, unsigned mode) // Scan List // TOT // RX Only -// Admit Criteria // static void print_chan_base(FILE *out, channel_t *ch, int cnum) { @@ -557,9 +557,6 @@ static void print_chan_base(FILE *out, channel_t *ch, int cnum) // fprintf(out, "%-3d ", ch->tot * 15); fprintf(out, "%c ", "-+"[ch->rx_only]); - - fprintf(out, "%-6s ", ADMIT_NAME[ch->tx_permit]); - //TODO: Busy Lock for analog channels } static void print_digital_channels(FILE *out, int verbose) @@ -597,10 +594,12 @@ static void print_digital_channels(FILE *out, int verbose) print_chan_base(out, ch, i+1); // Print digital parameters of the channel: + // Admit Criteria // Color Code // Repeater Slot // Group List // Contact Name + fprintf(out, "%-6s ", DIGITAL_ADMIT_NAME[ch->tx_permit]); fprintf(out, "%-5d %-3d ", ch->color_code, 1 + ch->slot2); if (ch->group_list_index == 0xff) @@ -674,10 +673,12 @@ static void print_analog_channels(FILE *out, int verbose) print_chan_base(out, ch, i+1); // Print analog parameters of the channel: + // Admit Criteria // Squelch // CTCSS/DCS Dec // CTCSS/DCS Enc // Bandwidth + fprintf(out, "%-6s ", ANALOG_ADMIT_NAME[ch->tx_permit]); fprintf(out, "%-7s ", "Normal"); if (ch->rx_ctcss) From 68e8a5b3bdcdc7e215ea306b445b1117b5be90cd Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 26 Oct 2018 00:29:03 -0700 Subject: [PATCH 17/21] Detect d868uv on Mac. --- main.c | 59 ++++++++++++++-------------------- serial.c | 98 +++++++++++++++++++++++++++----------------------------- util.h | 13 -------- 3 files changed, 71 insertions(+), 99 deletions(-) diff --git a/main.c b/main.c index 9357024..6c43b6a 100644 --- a/main.c +++ b/main.c @@ -39,29 +39,29 @@ extern int optind; void usage() { - fprintf(stderr, _("DMR Config, Version %s, %s\n"), version, copyright); - fprintf(stderr, _("Usage:\n")); - fprintf(stderr, _(" dmrconfig -r [-t]\n")); - fprintf(stderr, _(" Read codeplug from the radio to a file 'device.img'.\n")); - fprintf(stderr, _(" Save configuration to a text file 'device.conf'.\n")); - fprintf(stderr, _(" dmrconfig -w [-t] file.img\n")); - fprintf(stderr, _(" Write codeplug to the radio.\n")); - fprintf(stderr, _(" dmrconfig -c [-t] file.conf\n")); - fprintf(stderr, _(" Apply configuration script to the radio.\n")); - fprintf(stderr, _(" dmrconfig -c file.img file.conf\n")); - fprintf(stderr, _(" Apply configuration script to the codeplug image.\n")); - fprintf(stderr, _(" Store modified copy to a file 'device.img'.\n")); - fprintf(stderr, _(" dmrconfig file.img\n")); - fprintf(stderr, _(" Display configuration from the codeplug image.\n")); - fprintf(stderr, _(" dmrconfig -u [-t] file.csv\n")); - fprintf(stderr, _(" Update contacts database from CSV file.\n")); - fprintf(stderr, _("Options:\n")); - fprintf(stderr, _(" -r Read codeplug from the radio.\n")); - fprintf(stderr, _(" -w Write codeplug to the radio.\n")); - fprintf(stderr, _(" -c Configure the radio from a text script.\n")); - fprintf(stderr, _(" -u Update contacts database.\n")); - fprintf(stderr, _(" -l List all supported radios.\n")); - fprintf(stderr, _(" -t Trace USB protocol.\n")); + fprintf(stderr, "DMR Config, Version %s, %s\n", version, copyright); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " dmrconfig -r [-t]\n"); + fprintf(stderr, " Read codeplug from the radio to a file 'device.img'.\n"); + fprintf(stderr, " Save configuration to a text file 'device.conf'.\n"); + fprintf(stderr, " dmrconfig -w [-t] file.img\n"); + fprintf(stderr, " Write codeplug to the radio.\n"); + fprintf(stderr, " dmrconfig -c [-t] file.conf\n"); + fprintf(stderr, " Apply configuration script to the radio.\n"); + fprintf(stderr, " dmrconfig -c file.img file.conf\n"); + fprintf(stderr, " Apply configuration script to the codeplug image.\n"); + fprintf(stderr, " Store modified copy to a file 'device.img'.\n"); + fprintf(stderr, " dmrconfig file.img\n"); + fprintf(stderr, " Display configuration from the codeplug image.\n"); + fprintf(stderr, " dmrconfig -u [-t] file.csv\n"); + fprintf(stderr, " Update contacts database from CSV file.\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " -r Read codeplug from the radio.\n"); + fprintf(stderr, " -w Write codeplug to the radio.\n"); + fprintf(stderr, " -c Configure the radio from a text script.\n"); + fprintf(stderr, " -u Update contacts database.\n"); + fprintf(stderr, " -l List all supported radios.\n"); + fprintf(stderr, " -t Trace USB protocol.\n"); exit(-1); } @@ -70,18 +70,7 @@ int main(int argc, char **argv) int read_flag = 0, write_flag = 0, config_flag = 0, csv_flag = 0; int list_flag = 0; - // Set locale and message catalogs. - setlocale(LC_ALL, ""); -#ifdef MINGW32 - // Files with localized messages should be placed in - // in c:/Program Files/dmrconfig/ directory. - bindtextdomain("dmrconfig", "c:/Program Files/dmrconfig"); -#else - bindtextdomain("dmrconfig", "/usr/local/share/locale"); -#endif - textdomain("dmrconfig"); - - copyright = _("Copyright (C) 2018 Serge Vakulenko KK6ABQ"); + copyright = "Copyright (C) 2018 Serge Vakulenko KK6ABQ"; trace_flag = 0; for (;;) { switch (getopt(argc, argv, "tcwrul")) { diff --git a/serial.c b/serial.c index 1462378..a7ba097 100644 --- a/serial.c +++ b/serial.c @@ -50,10 +50,8 @@ #ifdef __APPLE__ # include -# include -# include -# include # include +# include #endif static char *dev_path; @@ -436,82 +434,80 @@ static char *find_path(int vid, int pid) return result; #elif defined(__APPLE__) - // Create a list of the devices in the 'IOMedia' class. - CFMutableDictionaryRef dict = IOServiceMatching(kIOMediaClass); + // Create a list of the devices in the 'IOSerialBSDClient' class. + CFMutableDictionaryRef dict = IOServiceMatching(kIOSerialBSDServiceValue); if (! dict) { printf("Cannot create IO Service dictionary.\n"); - return; + return 0; } - // Select devices with attributes Removeable=True and Whole=True. - CFDictionarySetValue(dict, CFSTR(kIOMediaRemovableKey), kCFBooleanTrue); - CFDictionarySetValue(dict, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); - io_iterator_t devices = IO_OBJECT_NULL; - kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, + kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &devices); - if (result != KERN_SUCCESS) { + if (ret != KERN_SUCCESS) { printf("Cannot find matching IO services.\n"); - return; + return 0; } // For each matching device, print out its information. io_object_t device; while ((device = IOIteratorNext(devices)) != MACH_PORT_NULL) { // Get device path. - char devname[1024] = "/dev/r"; + char devname[1024]; CFStringRef ref = (CFStringRef) IORegistryEntrySearchCFProperty(device, - kIOServicePlane, CFSTR(kIOBSDNameKey), + kIOServicePlane, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, kIORegistryIterateRecursively); - if (! ref || ! CFStringGetCString(ref, devname + 6, - sizeof(devname) - 6, kCFStringEncodingUTF8)) { + if (! ref || ! CFStringGetCString(ref, devname, sizeof(devname), kCFStringEncodingUTF8)) { // Cannot get device path. continue; } -#if 0 - // Get device size in bytes. - long long size; - ref = IORegistryEntryCreateCFProperty(device, - CFSTR(kIOMediaSizeKey), kCFAllocatorDefault, 0); - if (! ref || ! CFNumberGetValue((CFNumberRef)ref, kCFNumberLongLongType, &size)) { - // Cannot get device size. - continue; - } -#endif - // Get a list of device characteristics. - CFMutableDictionaryRef dict = (CFMutableDictionaryRef) - IORegistryEntrySearchCFProperty(device, kIOServicePlane, - CFSTR(kIOPropertyDeviceCharacteristicsKey), - kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively); - if (! dict) { - // Cannot get device characteristics. + //printf("%s\n", devname); + + // Get device parent. + io_registry_entry_t parent = 0; + if (KERN_SUCCESS != IORegistryEntryGetParentEntry(device, kIOServicePlane, &parent)) { + //printf("Cannot get device parent.\n"); continue; } - // Get vendor and product names. - char vendor[1024], product[1024]; - ref = CFDictionaryGetValue(dict, CFSTR(kIOPropertyVendorNameKey)); - if (! ref || ! CFStringGetCString(ref, vendor, - sizeof(vendor), kCFStringEncodingUTF8)) { - // Cannot get vendor name. - continue; - } - ref = CFDictionaryGetValue(dict, CFSTR(kIOPropertyProductNameKey)); - if (! ref || ! CFStringGetCString(ref, product, - sizeof(product), kCFStringEncodingUTF8)) { - // Cannot get product name. + // Get device grandparent. + io_registry_entry_t grandparent = 0; + if (KERN_SUCCESS != IORegistryEntryGetParentEntry(parent, kIOServicePlane, &grandparent)) { + //printf("Cannot get device grandparent.\n"); continue; } - char buf[1024]; - sprintf(buf, "%s - size %u MB, %s %s", - devname, (unsigned) (size / 1000000), vendor, product); - IOObjectRelease(device); - devtab[ndev++] = strdup(buf); + // Get vendor ID. + int vendor_id; + ref = IORegistryEntryCreateCFProperty(grandparent, + CFSTR(kUSBVendorID), kCFAllocatorDefault, 0); + if (! ref || ! CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &vendor_id)) { + //printf("Cannot get vendor ID.\n"); + continue; + } + + // Get product ID. + int product_id; + ref = IORegistryEntryCreateCFProperty(grandparent, + CFSTR(kUSBProductID), kCFAllocatorDefault, 0); + if (! ref || ! CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType, &product_id)) { + //printf("Cannot get product ID.\n"); + continue; + } + + if (vendor_id != vid || product_id != pid) { + // Wrong ID. + continue; + } + + result = strdup(devname); + break; } // Free the iterator object IOObjectRelease(devices); + return result; + #else printf("Don't know how to get the list of CD devices on this system.\n"); return 0; diff --git a/util.h b/util.h index f3b33d5..658fb77 100644 --- a/util.h +++ b/util.h @@ -26,19 +26,6 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// -// Localization. -// -#if 0 -#include -#define _(str) gettext(str) -#else -#define _(str) str -#define setlocale(x,y) /* empty */ -#define bindtextdomain(x,y) /* empty */ -#define textdomain(x) /* empty */ -#endif - // // Program version. // From 39010def9342087a0c331278ebab37883dfa601e Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 26 Oct 2018 12:40:32 -0700 Subject: [PATCH 18/21] D868UV: print ctcss and dcs tones. --- d868uv.c | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/d868uv.c b/d868uv.c index 8d8899a..1c575ff 100644 --- a/d868uv.c +++ b/d868uv.c @@ -270,6 +270,20 @@ static const char *DIGITAL_ADMIT_NAME[] = { "-", "Free", "NColor", "Color" }; static const char *ANALOG_ADMIT_NAME[] = { "-", "Tone", "Free", "Free" }; static const char *BANDWIDTH[] = { "12.5", "25" }; +// +// CTCSS tones, Hz*10. +// +#define NCTCSS 51 + +static const int CTCSS_TONES[NCTCSS] = { + 625, 670, 693, 719, 744, 770, 797, 825, 854, 885, + 915, 948, 974, 1000, 1035, 1072, 1109, 1148, 1188, 1230, + 1273, 1318, 1365, 1413, 1462, 1514, 1567, 1598, 1622, 1655, + 1679, 1713, 1738, 1773, 1799, 1835, 1862, 1899, 1928, 1966, + 1995, 2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, + 2541, +}; + // // Print a generic information about the device. // @@ -466,7 +480,7 @@ static void print_rx_freq(FILE *out, unsigned data) (data >> 12) & 15, (data >> 8) & 15, (data >> 20) & 15, (data >> 16) & 15); - if ((data & 0xff) == 0) { + if (((data >> 24) & 0xff) == 0) { fputs(" ", out); } else { fprintf(out, "%d", (data >> 28) & 15); @@ -626,16 +640,34 @@ static void print_digital_channels(FILE *out, int verbose) } } -static void print_ctcss(FILE *out, unsigned ctcss) +// +// Print CTSS tone. +// +static void print_ctcss(FILE *out, unsigned index, unsigned custom) { - //TODO - fprintf(out, "0x%02x ", ctcss); + int dhz = (index < NCTCSS) ? CTCSS_TONES[index] : custom; + unsigned a = dhz / 1000; + unsigned b = (dhz / 100) % 10; + unsigned c = (dhz / 10) % 10; + unsigned d = dhz % 10; + + if (a == 0) + fprintf(out, "%d%d.%d ", b, c, d); + else + fprintf(out, "%d%d%d.%d", a, b, c, d); } +// +// Print DCS tone. +// static void print_dcs(FILE *out, unsigned dcs) { - //TODO - fprintf(out, "0x%04x ", dcs); + unsigned i = (dcs >> 9) & 1; + unsigned a = (dcs >> 6) & 7; + unsigned b = (dcs >> 3) & 7; + unsigned c = dcs & 7; + + fprintf(out, "D%d%d%d%c", a, b, c, i ? 'I' : 'N'); } static void print_analog_channels(FILE *out, int verbose) @@ -682,19 +714,19 @@ static void print_analog_channels(FILE *out, int verbose) fprintf(out, "%-7s ", "Normal"); if (ch->rx_ctcss) - print_ctcss(out, ch->ctcss_receive); + print_ctcss(out, ch->ctcss_receive, ch->custom_ctcss); else if (ch->rx_dcs) print_dcs(out, ch->dcs_receive); else - fprintf(out, "- "); + fprintf(out, "- "); fprintf(out, " "); if (ch->tx_ctcss) - print_ctcss(out, ch->ctcss_transmit); + print_ctcss(out, ch->ctcss_transmit, ch->custom_ctcss); else if (ch->tx_dcs) print_dcs(out, ch->dcs_transmit); else - fprintf(out, "- "); + fprintf(out, "- "); fprintf(out, " %s", BANDWIDTH[ch->bandwidth]); fprintf(out, "\n"); From ade9ebc00d2e8081909d6eb1404739739f452914 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 26 Oct 2018 16:43:13 -0700 Subject: [PATCH 19/21] Print zones for d868uv. --- d868uv.c | 177 ++++++++++++++++++++++++++++++++++++++++--------------- util.c | 21 +++++++ util.h | 1 + 3 files changed, 150 insertions(+), 49 deletions(-) diff --git a/d868uv.c b/d868uv.c index 1c575ff..47d925e 100644 --- a/d868uv.c +++ b/d868uv.c @@ -47,13 +47,19 @@ // // Offsets in the image file. // -#define OFFSET_BANK1 0x000040 -#define OFFSET_CHAN_BITMAP 0x070a40 -#define OFFSET_SETTINGS 0x071600 -#define OFFSET_RADIOID 0x073d00 +#define OFFSET_BANK1 0x000040 // Channels +#define OFFSET_ZONELISTS 0x03e8c0 // Channel lists of zones +#define OFFSET_CHAN_BITMAP 0x070a40 // Bitmap of valid channels +#define OFFSET_ZONEMAP 0x070940 // Bitmap of valid zones +#define OFFSET_SETTINGS 0x071600 // General settings +#define OFFSET_ZONENAMES 0x071dc0 // Names of zones +#define OFFSET_RADIOID 0x073d00 // Table of radio IDs #define GET_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS]) #define GET_RADIOID() ((radioid_t*) &radio_mem[OFFSET_RADIOID]) +#define GET_ZONEMAP() (&radio_mem[OFFSET_ZONEMAP]) +#define GET_ZONENAME(i) (&radio_mem[OFFSET_ZONENAMES + (i)*32]) +#define GET_ZONELIST(i) ((uint16_t*) &radio_mem[OFFSET_ZONELISTS + (i)*512]) #define VALID_TEXT(txt) (*(txt) != 0 && *(txt) != 0xff) @@ -583,11 +589,11 @@ static void print_digital_channels(FILE *out, int verbose) fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); fprintf(out, "# 3) Receive frequency in MHz\n"); fprintf(out, "# 4) Transmit frequency or +/- offset in MHz\n"); - fprintf(out, "# 5) Transmit power: High, Low\n"); + fprintf(out, "# 5) Transmit power: High, Mid, Low, Turbo\n"); fprintf(out, "# 6) Scan list: - or index in Scanlist table\n"); - fprintf(out, "# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n"); + fprintf(out, "# 7) Transmit timeout timer: (unused)\n"); fprintf(out, "# 8) Receive only: -, +\n"); - fprintf(out, "# 9) Admit criteria: -, Free, Color\n"); + fprintf(out, "# 9) Admit criteria: -, Free, Color, NColor\n"); fprintf(out, "# 10) Color code: 0, 1, 2, 3... 15\n"); fprintf(out, "# 11) Time slot: 1 or 2\n"); fprintf(out, "# 12) Receive group list: - or index in Grouplist table\n"); @@ -680,15 +686,15 @@ static void print_analog_channels(FILE *out, int verbose) fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); fprintf(out, "# 3) Receive frequency in MHz\n"); fprintf(out, "# 4) Transmit frequency or +/- offset in MHz\n"); - fprintf(out, "# 5) Transmit power: High, Low\n"); + fprintf(out, "# 5) Transmit power: High, Mid, Low, Turbo\n"); fprintf(out, "# 6) Scan list: - or index\n"); - fprintf(out, "# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n"); + fprintf(out, "# 7) Transmit timeout timer: (unused)\n"); fprintf(out, "# 8) Receive only: -, +\n"); fprintf(out, "# 9) Admit criteria: -, Free, Tone\n"); - fprintf(out, "# 10) Squelch level: Normal, Tight\n"); + fprintf(out, "# 10) Squelch level: Normal (unused)\n"); fprintf(out, "# 11) Guard tone for receive, or '-' to disable\n"); fprintf(out, "# 12) Guard tone for transmit, or '-' to disable\n"); - fprintf(out, "# 13) Bandwidth in kHz: 12.5, 20, 25\n"); + fprintf(out, "# 13) Bandwidth in kHz: 12.5, 25\n"); fprintf(out, "#\n"); } fprintf(out, "Analog Name Receive Transmit Power Scan TOT RO Admit Squelch RxTone TxTone Width"); @@ -733,11 +739,81 @@ static void print_analog_channels(FILE *out, int verbose) } } +// +// Return true when any zones are present. +// +static int have_zones() +{ + uint8_t *zmap = GET_ZONEMAP(); + int i; + + for (i=0; i<(NZONES+7)/8; i++) { + if (zmap[i] != 0) + return 1; + } + return 0; +} + +// +// Find a zone with given index. +// Return false when zone is not valid. +// Set zname and zlist to a zone name and member list. +// +static int get_zone(int i, uint8_t **zname, uint16_t **zlist) +{ + uint8_t *zmap = GET_ZONEMAP(); + + if ((zmap[i / 8] >> (i & 7)) & 1) { + // Zone is valid. + *zname = GET_ZONENAME(i); + *zlist = GET_ZONELIST(i); + return 1; + } else { + return 0; + } +} + +static void print_chanlist(FILE *out, uint16_t *unsorted, int nchan) +{ + int last = -1; + int range = 0; + int n; + uint16_t data[nchan]; + + // Sort the list before printing. + memcpy(data, unsorted, nchan * sizeof(uint16_t)); + qsort(data, nchan, sizeof(uint16_t), compare_index_ffff); + for (n=0; n 0) + fprintf(out, ","); + fprintf(out, "%d", cnum); + } + last = cnum; + } + if (range) + fprintf(out, "-%d", last); +} + // // Print full information about the device configuration. // static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) { + int i; + fprintf(out, "Radio: %s\n", radio->name); if (verbose) d868uv_print_version(radio, out); @@ -754,6 +830,40 @@ static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) print_analog_channels(out, verbose); } + // + // Zones. + // + if (have_zones()) { + fprintf(out, "\n"); + if (verbose) { + fprintf(out, "# Table of channel zones.\n"); + fprintf(out, "# 1) Zone number: 1-%d\n", NZONES); + fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); + fprintf(out, "# 3) List of channels: numbers and ranges (N-M) separated by comma\n"); + fprintf(out, "#\n"); + } + fprintf(out, "Zone Name Channels\n"); + for (i=0; imember_a[k]; + for (k=0; k<250; k++) { + int cnum = zlist[k] + 1; - if (cnum != 0) { + if (cnum != 0xffff) { channel_t *ch = get_channel(cnum - 1); if (!ch) { fprintf(stderr, "Zone %da '", i+1); - print_ascii(stderr, z->name, 16, 0); - fprintf(stderr, "': channel %d not found.\n", cnum); - nerrors++; - } - } - } - for (k=0; k<48; k++) { - int cnum = zext->ext_a[k]; - - if (cnum != 0) { - channel_t *ch = get_channel(cnum - 1); - - if (!ch) { - fprintf(stderr, "Zone %da '", i+1); - print_ascii(stderr, z->name, 16, 0); - fprintf(stderr, "': channel %d not found.\n", cnum); - nerrors++; - } - } - } - - // Zone B - for (k=0; k<64; k++) { - int cnum = zext->member_b[k]; - - if (cnum != 0) { - channel_t *ch = get_channel(cnum - 1); - - if (!ch) { - fprintf(stderr, "Zone %db '", i+1); - print_ascii(stderr, z->name, 16, 0); + print_ascii(stderr, zname, 16, 0); fprintf(stderr, "': channel %d not found.\n", cnum); nerrors++; } diff --git a/util.c b/util.c index 357e2b7..9da742f 100644 --- a/util.c +++ b/util.c @@ -559,6 +559,7 @@ void print_offset(FILE *out, unsigned rx_bcd, unsigned tx_bcd) // // Compare channel index for qsort(). +// Treat 0 as empty element. // int compare_index(const void *pa, const void *pb) { @@ -576,6 +577,26 @@ int compare_index(const void *pa, const void *pb) return 0; } +// +// Compare channel index for qsort(). +// Treat 0xffff as empty element. +// +int compare_index_ffff(const void *pa, const void *pb) +{ + unsigned short a = *(unsigned short*) pa; + unsigned short b = *(unsigned short*) pb; + + if (a == 0xffff) + return (b != 0xffff); + if (b == 0xffff) + return -1; + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + // // Print CTSS or DCS tone. // diff --git a/util.h b/util.h index 658fb77..7b2b8c9 100644 --- a/util.h +++ b/util.h @@ -193,6 +193,7 @@ void print_offset(FILE *out, unsigned rx_bcd, unsigned tx_bcd); // Compare channel index for qsort(). // int compare_index(const void *pa, const void *pb); +int compare_index_ffff(const void *pa, const void *pb); // // Print CTSS or DCS tone. From db356e18aa87dd2c05930d4c2385133f261ac5ed Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 26 Oct 2018 19:49:26 -0700 Subject: [PATCH 20/21] D868UV: print contacts. --- contact5.diff | 38 +++++++++++++ d868uv.c | 148 ++++++++++++++++++++++++++++++++++++++++++++------ zone3.diff | 44 +++++++++++++++ 3 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 contact5.diff create mode 100644 zone3.diff diff --git a/contact5.diff b/contact5.diff new file mode 100644 index 0000000..94c12e1 --- /dev/null +++ b/contact5.diff @@ -0,0 +1,38 @@ +--- d868uv-contacts.hd 2018-10-26 16:58:56.070106184 -0700 ++++ d868uv-contacts-delete5.hd 2018-10-26 17:10:11.651995288 -0700 +@@ -18204,10 +18204,9 @@ + 000764e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + 000764f0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + 00076500 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 |................| --- List of contacts? +-00076510 04 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff |................| +-00076520 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| ++00076510 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * +-00080140 e0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| ++00080140 f0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| --- Bitmap of invalid contacts + 00080150 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * + 00080620 ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +@@ -18233,43 +18232,12 @@ + 00080790 44 44 44 01 00 00 00 00 00 00 00 00 00 00 00 00 |DDD.............| + 000807a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| + * +-000807d0 00 43 6f 6e 74 61 63 74 20 35 00 00 00 00 00 00 |.Contact 5......| --- Contact5 structure +-000807e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +-000807f0 00 00 00 15 55 55 55 02 00 00 00 00 00 00 00 00 |....UUU.........| +-00080800 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +-* +-00080840 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| ++000807d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * +@@ -18306,8 +18274,7 @@ + 00174c30 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * + 00188380 f1 ac 68 24 00 00 00 00 66 66 66 26 02 00 00 00 |..h$....fff&....| +-00188390 88 88 88 28 03 00 00 00 aa aa aa 2a 04 00 00 00 |...(.......*....| +-001883a0 2a e4 ee 2c 01 00 00 00 ff ff ff ff ff ff ff ff |*..,............| +-001883b0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| ++00188390 88 88 88 28 03 00 00 00 2a e4 ee 2c 01 00 00 00 |...(....*..,....| --- Unknown data ++001883a0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| + * + 00188680 diff --git a/d868uv.c b/d868uv.c index 47d925e..8923cd3 100644 --- a/d868uv.c +++ b/d868uv.c @@ -49,17 +49,21 @@ // #define OFFSET_BANK1 0x000040 // Channels #define OFFSET_ZONELISTS 0x03e8c0 // Channel lists of zones -#define OFFSET_CHAN_BITMAP 0x070a40 // Bitmap of valid channels -#define OFFSET_ZONEMAP 0x070940 // Bitmap of valid zones +#define OFFSET_CHAN_MAP 0x070a40 // Bitmap of valid channels +#define OFFSET_ZONE_MAP 0x070940 // Bitmap of valid zones #define OFFSET_SETTINGS 0x071600 // General settings #define OFFSET_ZONENAMES 0x071dc0 // Names of zones #define OFFSET_RADIOID 0x073d00 // Table of radio IDs +#define OFFSET_CONTACT_MAP 0x080140 // Bitmap of invalid contacts +#define OFFSET_CONTACTS 0x080640 // Contacts #define GET_SETTINGS() ((general_settings_t*) &radio_mem[OFFSET_SETTINGS]) #define GET_RADIOID() ((radioid_t*) &radio_mem[OFFSET_RADIOID]) -#define GET_ZONEMAP() (&radio_mem[OFFSET_ZONEMAP]) +#define GET_ZONEMAP() (&radio_mem[OFFSET_ZONE_MAP]) +#define GET_CONTACT_MAP() (&radio_mem[OFFSET_CONTACT_MAP]) #define GET_ZONENAME(i) (&radio_mem[OFFSET_ZONENAMES + (i)*32]) #define GET_ZONELIST(i) ((uint16_t*) &radio_mem[OFFSET_ZONELISTS + (i)*512]) +#define GET_CONTACT(i) ((contact_t*) &radio_mem[OFFSET_CONTACTS + (i)*100]) #define VALID_TEXT(txt) (*(txt) != 0 && *(txt) != 0xff) @@ -271,10 +275,52 @@ typedef struct { } radioid_t; +// +// Contact data: 100 bytes per record. +// +typedef struct { + + // Byte 0 + uint8_t type; // Call Type: Group Call, Private Call or All Call +#define CALL_PRIVATE 0 +#define CALL_GROUP 1 +#define CALL_ALL 2 + + // Bytes 1-16 + uint8_t name[16]; // Contact Name (ASCII) + + // Bytes 17-34 + uint8_t _unused17[18]; // 0 + + // Bytes 35-38 + uint8_t id[4]; // Call ID: BCD coded 8 digits +#define GET_ID(x) (((x)[0] >> 4) * 10000000 +\ + ((x)[0] & 15) * 1000000 +\ + ((x)[1] >> 4) * 100000 +\ + ((x)[1] & 15) * 10000 +\ + ((x)[2] >> 4) * 1000 +\ + ((x)[2] & 15) * 100 +\ + ((x)[3] >> 4) * 10 +\ + ((x)[3] & 15)) +#define CONTACT_ID(ct) GET_ID((ct)->id) + + // Byte 39 + uint8_t call_alert; // Call Alert: None, Ring, Online Alert +#define ALERT_NONE 0 +#define ALERT_RING 1 +#define ALERT_ONLINE 2 + + // Bytes 40-99 + uint8_t _unused40[60]; // 0 + +} contact_t; + static const char *POWER_NAME[] = { "Low", "Mid", "High", "Turbo" }; static const char *DIGITAL_ADMIT_NAME[] = { "-", "Free", "NColor", "Color" }; static const char *ANALOG_ADMIT_NAME[] = { "-", "Tone", "Free", "Free" }; static const char *BANDWIDTH[] = { "12.5", "25" }; +static const char *CONTACT_TYPE[] = { "Private", "Group", "All", "Unknown" }; +static const char *ALERT_TYPE[] = { "-", "+", "Online", "Unknown" }; // // CTCSS tones, Hz*10. @@ -443,7 +489,7 @@ static channel_t *get_bank(int i) static channel_t *get_channel(int i) { channel_t *bank = get_bank(i >> 7); - uint8_t *bitmap = &radio_mem[OFFSET_CHAN_BITMAP]; + uint8_t *bitmap = &radio_mem[OFFSET_CHAN_MAP]; if ((bitmap[i / 8] >> (i & 7)) & 1) return &bank[i % 128]; @@ -477,6 +523,34 @@ static int have_channels(int mode) return 0; } +// +// Return true when any contacts are present. +// +static int have_contacts() +{ + uint8_t *cmap = GET_CONTACT_MAP(); + int i; + + for (i=0; i<(NCONTACTS+7)/8; i++) { + if (cmap[i] != 0xff) + return 1; + } + return 0; +} + +// +// Get contact by index. +// +static contact_t *get_contact(int i) +{ + uint8_t *cmap = GET_CONTACT_MAP(); + + if ((cmap[i / 8] >> (i & 7)) & 1) + return 0; + + return GET_CONTACT(i); +} + // // Print frequency (BCD value). // @@ -631,17 +705,16 @@ static void print_digital_channels(FILE *out, int verbose) fprintf(out, "-"); else fprintf(out, "%-4d", ch->contact_index + 1); -#if 0 + // Print contact name as a comment. - //TODO - if (ch->contact_index > 0) { - contact_t *ct = GET_CONTACT(ch->contact_index - 1); - if (VALID_CONTACT(ct)) { + if (ch->contact_index != 0xffff) { + contact_t *ct = get_contact(ch->contact_index); + + if (ct) { fprintf(out, " # "); print_ascii(out, ct->name, 16, 0); } } -#endif fprintf(out, "\n"); } } @@ -864,6 +937,49 @@ static void d868uv_print_config(radio_device_t *radio, FILE *out, int verbose) } } + // + // Scan lists. + // + //TODO + + // + // Contacts. + // + if (have_contacts()) { + fprintf(out, "\n"); + if (verbose) { + fprintf(out, "# Table of contacts.\n"); + fprintf(out, "# 1) Contact number: 1-%d\n", NCONTACTS); + fprintf(out, "# 2) Name: up to 16 characters, use '_' instead of space\n"); + fprintf(out, "# 3) Call type: Group, Private, All\n"); + fprintf(out, "# 4) Call ID: 1...16777215\n"); + fprintf(out, "# 5) Incoming call alert: -, +, Online\n"); + fprintf(out, "#\n"); + } + fprintf(out, "Contact Name Type ID RxTone\n"); + for (i=0; iname, 16, 1); + fprintf(out, " %-7s %-8d %s\n", CONTACT_TYPE[ct->type & 3], + CONTACT_ID(ct), ALERT_TYPE[ct->call_alert & 3]); + } + } + + // + // Group lists. + // + //TODO + + // + // Text messages. + // //TODO // General settings. @@ -1029,9 +1145,9 @@ static int d868uv_verify_config(radio_device_t *radio) } } if (ch->contact_index != 0) { - contact_t *ct = GET_CONTACT(ch->contact_index - 1); + contact_t *ct = get_contact(ch->contact_index - 1); - if (!VALID_CONTACT(ct)) { + if (!ct) { fprintf(stderr, "Channel %d '", i+1); print_ascii(stderr, ch->name, 16, 0); fprintf(stderr, "': contact %d not found.\n", ch->contact_index); @@ -1112,9 +1228,9 @@ static int d868uv_verify_config(radio_device_t *radio) int cnum = gl->member[k]; if (cnum != 0) { - contact_t *ct = GET_CONTACT(cnum - 1); + contact_t *ct = get_contact(cnum - 1); - if (!VALID_CONTACT(ct)) { + if (!ct) { fprintf(stderr, "Grouplist %d '", i+1); print_ascii(stderr, gl->name, 16, 0); fprintf(stderr, "': contact %d not found.\n", cnum); @@ -1126,9 +1242,9 @@ static int d868uv_verify_config(radio_device_t *radio) // Count contacts. for (i=0; i Date: Fri, 26 Oct 2018 19:56:28 -0700 Subject: [PATCH 21/21] Fix win32 build errors. --- serial.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/serial.c b/serial.c index a7ba097..6141047 100644 --- a/serial.c +++ b/serial.c @@ -64,6 +64,9 @@ static const unsigned char CMD_READ[] = "Raaaan"; static const unsigned char CMD_WRITE[] = "Waaaan..."; static const unsigned char CMD_END[] = "END"; +#if defined(__WIN32__) || defined(WIN32) + // No need for this function. +#else // // Encode the speed in bits per second into bit value // accepted by cfsetspeed() function. @@ -71,10 +74,6 @@ static const unsigned char CMD_END[] = "END"; // static int baud_encode(int bps) { -#if defined(__WIN32__) || defined(WIN32) || B115200 == 115200 - // Windows, BSD, Mac OS: any baud rate allowed. - return bps; -#else // Linux: only a limited set of values permitted. switch (bps) { #ifdef B75 @@ -165,10 +164,9 @@ static int baud_encode(int bps) case 4000000: return B4000000; #endif } -printf("Unknown baud\n"); return -1; -#endif } +#endif /* WIN32 */ // // Send data to device. @@ -431,7 +429,6 @@ static char *find_path(int vid, int pid) // Free the enumerator object udev_enumerate_unref(enumerate); udev_unref(udev); - return result; #elif defined(__APPLE__) // Create a list of the devices in the 'IOSerialBSDClient' class. @@ -506,12 +503,12 @@ static char *find_path(int vid, int pid) // Free the iterator object IOObjectRelease(devices); - return result; #else printf("Don't know how to get the list of CD devices on this system.\n"); - return 0; #endif + + return result; } // @@ -620,6 +617,11 @@ const char *serial_identify() } again: +#if defined(__WIN32__) || defined(WIN32) + //TODO: flush pending input and output buffers. +#else + tcflush(fd, TCIOFLUSH); +#endif send_recv(CMD_PRG, 7, ack, 3); if (memcmp(ack, CMD_QX, 3) != 0) { if (++retry >= 10) { @@ -628,7 +630,6 @@ again: return 0; } usleep(500000); - tcflush(fd, TCIOFLUSH); goto again; } @@ -643,7 +644,6 @@ again: return 0; } usleep(500000); - tcflush(fd, TCIOFLUSH); goto again; }