Add chan_usbradio to trunk
authorRussell Bryant <russell@russellbryant.com>
Fri, 14 Sep 2007 15:58:31 +0000 (15:58 +0000)
committerRussell Bryant <russell@russellbryant.com>
Fri, 14 Sep 2007 15:58:31 +0000 (15:58 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@82389 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_usbradio.c [new file with mode: 0644]
channels/xpmr/LICENSE [new file with mode: 0755]
channels/xpmr/sinetabx.h [new file with mode: 0755]
channels/xpmr/xpmr.c [new file with mode: 0755]
channels/xpmr/xpmr.h [new file with mode: 0755]
channels/xpmr/xpmr_coef.h [new file with mode: 0755]

diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c
new file mode 100644 (file)
index 0000000..b3801ae
--- /dev/null
@@ -0,0 +1,2813 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ * Copyright (C) 2007, Jim Dixon
+ *
+ * Jim Dixon, WB6NIL <jim@lambdatel.com>
+ * Steve Henke, W9SH  <w9sh@arrl.net>
+ * Based upon work by Mark Spencer <markster@digium.com> and Luigi Rizzo
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+  * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Channel driver for CM108 USB Cards with Radio Interface
+ *
+ * \author Jim Dixon  <jim@lambdatel.com>
+ * \author Steve Henke  <w9sh@arrl.net>
+ *
+ * \par See also
+ * \arg \ref Config_usbradio
+ *
+ * \ingroup channel_drivers
+ */
+
+/*** MODULEINFO
+       <depend>alsa</depend>
+       <depend>usb</depend>
+       <defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <usb.h>
+#include <alsa/asoundlib.h>
+
+#define CHAN_USBRADIO           1
+
+#define DEBUG_USBRADIO          0      
+#define DEBUG_CAPTURES                 1
+               
+#define DEBUG_CAP_RX_OUT               0               
+#define DEBUG_CAP_TX_OUT           0                   
+
+#define DEBUG_FILETEST                 0                        
+
+#define RX_CAP_RAW_FILE                        "/tmp/rx_cap_in.pcm"
+#define RX_CAP_TRACE_FILE              "/tmp/rx_trace.pcm"
+#define RX_CAP_OUT_FILE                        "/tmp/rx_cap_out.pcm"
+
+#define TX_CAP_RAW_FILE                        "/tmp/tx_cap_in.pcm"
+#define TX_CAP_TRACE_FILE              "/tmp/tx_trace.pcm"
+#define TX_CAP_OUT_FILE                        "/tmp/tx_cap_out.pcm"
+
+#define        MIXER_PARAM_MIC_PLAYBACK_SW "Mic Playback Switch"
+#define MIXER_PARAM_MIC_PLAYBACK_VOL "Mic Playback Volume"
+#define        MIXER_PARAM_MIC_CAPTURE_SW "Mic Capture Switch"
+#define        MIXER_PARAM_MIC_CAPTURE_VOL "Mic Capture Volume"
+#define        MIXER_PARAM_MIC_BOOST "Auto Gain Control"
+#define        MIXER_PARAM_SPKR_PLAYBACK_SW "Speaker Playback Switch"
+#define        MIXER_PARAM_SPKR_PLAYBACK_VOL "Speaker Playback Volume"
+
+#include "./xpmr/xpmr.h"
+
+#if 0
+#define traceusb1(a) {printf a;}
+#else
+#define traceusb1(a)
+#endif
+
+#if 0
+#define traceusb2(a) {printf a;}
+#else
+#define traceusb2(a)
+#endif
+
+#ifdef __linux
+#include <linux/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#else
+#include <soundcard.h>
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/logger.h"
+#include "asterisk/callerid.h"
+#include "asterisk/channel.h"
+#include "asterisk/module.h"
+#include "asterisk/options.h"
+#include "asterisk/pbx.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/endian.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/abstract_jb.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/dsp.h"
+
+/* ringtones we use */
+#include "busy.h"
+#include "ringtone.h"
+#include "ring10.h"
+#include "answer.h"
+
+#define C108_VENDOR_ID         0x0d8c
+#define C108_PRODUCT_ID        0x000c
+#define C108_HID_INTERFACE     3
+
+#define HID_REPORT_GET         0x01
+#define HID_REPORT_SET         0x09
+
+#define HID_RT_INPUT           0x01
+#define HID_RT_OUTPUT          0x02
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf =
+{
+       .flags = 0,
+       .max_size = -1,
+       .resync_threshold = -1,
+       .impl = "",
+};
+static struct ast_jb_conf global_jbconf;
+
+/*
+ * usbradio.conf parameters are
+START_CONFIG
+
+[general]
+    ; General config options, with default values shown.
+    ; You should use one section per device, with [general] being used
+    ; for the device.
+    ;
+    ;
+    ; debug = 0x0              ; misc debug flags, default is 0
+
+       ; Set the device to use for I/O
+       ; devicenum = 0
+       ; Set hardware type here
+       ; hdwtype=0               ; 0=limey, 1=sph
+
+       ; rxboostset=0          ; no rx gain boost
+       ; rxctcssrelax=1        ; reduce talkoff from radios w/o CTCSS Tx HPF
+       ; rxctcssfreq=100.0      ; rx ctcss freq in floating point. must be in table
+       ; txctcssfreq=100.0      ; tx ctcss freq, any frequency permitted
+
+       ; carrierfrom=dsp     ;no,usb,usbinvert,dsp,vox
+       ; ctcssfrom=dsp       ;no,usb,dsp
+
+       ; rxdemod=flat            ; input type from radio: no,speaker,flat
+       ; txprelim=yes            ; output is pre-emphasised and limited
+       ; txtoctype=no            ; no,phase,notone
+
+       ; txmixa=composite        ;no,voice,tone,composite,auxvoice
+       ; txmixb=no               ;no,voice,tone,composite,auxvoice
+
+       ; invertptt=0
+
+    ;------------------------------ JITTER BUFFER CONFIGURATION --------------------------
+    ; jbenable = yes              ; Enables the use of a jitterbuffer on the receiving side of an
+                                  ; USBRADIO channel. Defaults to "no". An enabled jitterbuffer will
+                                  ; be used only if the sending side can create and the receiving
+                                  ; side can not accept jitter. The USBRADIO channel can't accept jitter,
+                                  ; thus an enabled jitterbuffer on the receive USBRADIO side will always
+                                  ; be used if the sending side can create jitter.
+
+    ; jbmaxsize = 200             ; Max length of the jitterbuffer in milliseconds.
+
+    ; jbresyncthreshold = 1000    ; Jump in the frame timestamps over which the jitterbuffer is
+                                  ; resynchronized. Useful to improve the quality of the voice, with
+                                  ; big jumps in/broken timestamps, usualy sent from exotic devices
+                                  ; and programs. Defaults to 1000.
+
+    ; jbimpl = fixed              ; Jitterbuffer implementation, used on the receiving side of an USBRADIO
+                                  ; channel. Two implementations are currenlty available - "fixed"
+                                  ; (with size always equals to jbmax-size) and "adaptive" (with
+                                  ; variable size, actually the new jb of IAX2). Defaults to fixed.
+
+    ; jblog = no                  ; Enables jitterbuffer frame logging. Defaults to "no".
+    ;-----------------------------------------------------------------------------------
+
+
+END_CONFIG
+
+ */
+
+/*
+ * Helper macros to parse config arguments. They will go in a common
+ * header file if their usage is globally accepted. In the meantime,
+ * we define them here. Typical usage is as below.
+ * Remember to open a block right before M_START (as it declares
+ * some variables) and use the M_* macros WITHOUT A SEMICOLON:
+ *
+ *     {
+ *             M_START(v->name, v->value) 
+ *
+ *             M_BOOL("dothis", x->flag1)
+ *             M_STR("name", x->somestring)
+ *             M_F("bar", some_c_code)
+ *             M_END(some_final_statement)
+ *             ... other code in the block
+ *     }
+ *
+ * XXX NOTE these macros should NOT be replicated in other parts of asterisk. 
+ * Likely we will come up with a better way of doing config file parsing.
+ */
+#define M_START(var, val) \
+        char *__s = var; char *__val = val;
+#define M_END(x)   x;
+#define M_F(tag, f)                    if (!strcasecmp((__s), tag)) { f; } else
+#define M_BOOL(tag, dst)       M_F(tag, (dst) = ast_true(__val) )
+#define M_UINT(tag, dst)       M_F(tag, (dst) = strtoul(__val, NULL, 0) )
+#define M_STR(tag, dst)                M_F(tag, ast_copy_string(dst, __val, sizeof(dst)))
+
+/*
+ * The following parameters are used in the driver:
+ *
+ *  FRAME_SIZE the size of an audio frame, in samples.
+ *             160 is used almost universally, so you should not change it.
+ *
+ *  FRAGS      the argument for the SETFRAGMENT ioctl.
+ *             Overridden by the 'frags' parameter in usbradio.conf
+ *
+ *             Bits 0-7 are the base-2 log of the device's block size,
+ *             bits 16-31 are the number of blocks in the driver's queue.
+ *             There are a lot of differences in the way this parameter
+ *             is supported by different drivers, so you may need to
+ *             experiment a bit with the value.
+ *             A good default for linux is 30 blocks of 64 bytes, which
+ *             results in 6 frames of 320 bytes (160 samples).
+ *             FreeBSD works decently with blocks of 256 or 512 bytes,
+ *             leaving the number unspecified.
+ *             Note that this only refers to the device buffer size,
+ *             this module will then try to keep the lenght of audio
+ *             buffered within small constraints.
+ *
+ *  QUEUE_SIZE The max number of blocks actually allowed in the device
+ *             driver's buffer, irrespective of the available number.
+ *             Overridden by the 'queuesize' parameter in usbradio.conf
+ *
+ *             Should be >=2, and at most as large as the hw queue above
+ *             (otherwise it will never be full).
+ */
+
+#define FRAME_SIZE     160
+#define        QUEUE_SIZE      20
+
+#if defined(__FreeBSD__)
+#define        FRAGS   0x8
+#else
+#define        FRAGS   ( ( (6 * 5) << 16 ) | 0xc )
+#endif
+
+/*
+ * XXX text message sizes are probably 256 chars, but i am
+ * not sure if there is a suitable definition anywhere.
+ */
+#define TEXT_SIZE      256
+
+#if 0
+#define        TRYOPEN 1                               /* try to open on startup */
+#endif
+#define        O_CLOSE 0x444                   /* special 'close' mode for device */
+/* Which device to use */
+#if defined( __OpenBSD__ ) || defined( __NetBSD__ )
+#define DEV_DSP "/dev/audio"
+#else
+#define DEV_DSP "/dev/dsp"
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static char *config = "usbradio.conf"; /* default config file */
+static char *config1 = "usbradio_tune.conf";    /* tune config file */
+
+static FILE *frxcapraw = NULL, *frxcaptrace = NULL, *frxoutraw = NULL;
+static FILE *ftxcapraw = NULL, *ftxcaptrace = NULL, *ftxoutraw = NULL;
+
+static int usbradio_debug;
+#if 0 //maw asdf sph
+static int usbradio_debug_level = 0;
+#endif
+
+enum {RX_AUDIO_NONE,RX_AUDIO_SPEAKER,RX_AUDIO_FLAT};
+enum {CD_IGNORE,CD_XPMR_NOISE,CD_XPMR_VOX,CD_HID,CD_HID_INVERT};
+enum {SD_IGNORE,SD_HID,SD_HID_INVERT,SD_XPMR};                                  // no,external,externalinvert,software
+enum {RX_KEY_CARRIER,RX_KEY_CARRIER_CODE};
+enum {TX_OUT_OFF,TX_OUT_VOICE,TX_OUT_LSD,TX_OUT_COMPOSITE,TX_OUT_AUX};
+enum {TOC_NONE,TOC_PHASE,TOC_NOTONE};
+
+/*     DECLARE STRUCTURES */
+
+/*
+ * Each sound is made of 'datalen' samples of sound, repeated as needed to
+ * generate 'samplen' samples of data, then followed by 'silencelen' samples
+ * of silence. The loop is repeated if 'repeat' is set.
+ */
+struct sound {
+       int ind;
+       char *desc;
+       short *data;
+       int datalen;
+       int samplen;
+       int silencelen;
+       int repeat;
+};
+
+static struct sound sounds[] = {
+       { AST_CONTROL_RINGING, "RINGING", ringtone, sizeof(ringtone)/2, 16000, 32000, 1 },
+       { AST_CONTROL_BUSY, "BUSY", busy, sizeof(busy)/2, 4000, 4000, 1 },
+       { AST_CONTROL_CONGESTION, "CONGESTION", busy, sizeof(busy)/2, 2000, 2000, 1 },
+       { AST_CONTROL_RING, "RING10", ring10, sizeof(ring10)/2, 16000, 32000, 1 },
+       { AST_CONTROL_ANSWER, "ANSWER", answer, sizeof(answer)/2, 2200, 0, 0 },
+       { -1, NULL, 0, 0, 0, 0 },       /* end marker */
+};
+
+
+/*
+ * descriptor for one of our channels.
+ * There is one used for 'default' values (from the [general] entry in
+ * the configuration file), and then one instance for each device
+ * (the default is cloned from [general], others are only created
+ * if the relevant section exists).
+ */
+struct chan_usbradio_pvt {
+       struct chan_usbradio_pvt *next;
+
+       char *name;
+       /*
+        * cursound indicates which in struct sound we play. -1 means nothing,
+        * any other value is a valid sound, in which case sampsent indicates
+        * the next sample to send in [0..samplen + silencelen]
+        * nosound is set to disable the audio data from the channel
+        * (so we can play the tones etc.).
+        */
+       int sndcmd[2];                          /* Sound command pipe */
+       int cursound;                           /* index of sound to send */
+       int sampsent;                           /* # of sound samples sent  */
+       int nosound;                            /* set to block audio from the PBX */
+
+       int total_blocks;                       /* total blocks in the output device */
+       int sounddev;
+       enum { M_UNSET, M_FULL, M_READ, M_WRITE } duplex;
+       i16 cdMethod;
+       int autoanswer;
+       int autohangup;
+       int hookstate;
+       unsigned int queuesize;         /* max fragments in queue */
+       unsigned int frags;                     /* parameter for SETFRAGMENT */
+
+       int warned;                                     /* various flags used for warnings */
+#define WARN_used_blocks       1
+#define WARN_speed             2
+#define WARN_frag              4
+       int w_errors;                           /* overfull in the write path */
+       struct timeval lastopen;
+
+       int overridecontext;
+       int mute;
+
+       /* boost support. BOOST_SCALE * 10 ^(BOOST_MAX/20) must
+        * be representable in 16 bits to avoid overflows.
+        */
+#define        BOOST_SCALE     (1<<9)
+#define        BOOST_MAX       40                      /* slightly less than 7 bits */
+       int boost;                                      /* input boost, scaled by BOOST_SCALE */
+       char devicenum;
+       int spkrmax;
+       int micmax;
+
+       pthread_t sthread;
+       pthread_t hidthread;
+
+       int stophid;
+       struct ast_channel *owner;
+       char ext[AST_MAX_EXTENSION];
+       char ctx[AST_MAX_CONTEXT];
+       char language[MAX_LANGUAGE];
+       char cid_name[256];                     /*XXX */
+       char cid_num[256];                      /*XXX */
+       char mohinterpret[MAX_MUSICCLASS];
+
+       /* buffers used in usbradio_write, 2 per int by 2 channels by 6 times oversampling (48KS/s) */
+       char usbradio_write_buf[FRAME_SIZE * 2 * 2 * 6];    
+       char usbradio_write_buf_1[FRAME_SIZE * 2 * 2* 6];
+
+       int usbradio_write_dst;
+       /* buffers used in usbradio_read - AST_FRIENDLY_OFFSET space for headers
+        * plus enough room for a full frame
+        */
+       char usbradio_read_buf[FRAME_SIZE * (2 * 12) + AST_FRIENDLY_OFFSET];
+       char usbradio_read_buf_8k[FRAME_SIZE * 2 + AST_FRIENDLY_OFFSET];
+       int readpos;                            /* read position above */
+       struct ast_frame read_f;        /* returned by usbradio_read */
+       
+
+       char debuglevel;
+       char radioduplex;                       // 
+
+       char lastrx;
+       char rxhidsq;
+       char rxcarrierdetect;           // status from pmr channel
+       char rxctcssdecode;                     // status from pmr channel
+
+       char rxkeytype;
+       char rxkeyed;                           // indicates rx signal present
+
+       char lasttx;
+       char txkeyed;                           // tx key request from upper layers 
+       char txchankey;
+       char txtestkey;
+
+       time_t lasthidtime;
+    struct ast_dsp *dsp;
+
+       t_pmr_chan      *pmrChan;
+
+       char    rxcpusaver;
+       char    txcpusaver;
+
+       char    rxdemod;
+       float   rxgain;
+       char    rxcdtype;
+       char    rxsdtype;
+       int             rxsquelchadj;   /* this copy needs to be here for initialization */
+       char    txtoctype;
+
+       char    txprelim;
+       float   txctcssgain;
+       char    txmixa;
+       char    txmixb;
+
+       char    invertptt;
+
+       char    rxctcssrelax;
+       float   rxctcssgain;
+       float   rxctcssfreq;
+       float   txctcssfreq;
+
+       int             rxmixerset;             
+       int     rxboostset;
+       float   rxvoiceadj;
+       float   rxctcssadj;
+       int     txmixaset;
+       int     txmixbset;
+       int     txctcssadj;
+
+       int     hdwtype;
+       int             hid_gpio_ctl;           
+       int             hid_gpio_ctl_loc;       
+       int             hid_io_cor;             
+       int             hid_io_cor_loc;         
+       int             hid_io_ctcss;           
+       int             hid_io_ctcss_loc;       
+       int             hid_io_ptt;             
+       int             hid_gpio_loc;           
+
+       struct {
+           unsigned rxcapraw:1;
+               unsigned txcapraw:1;
+               unsigned txcap2:1;
+               unsigned rxcap2:1;
+       }b;
+};
+
+// maw add additional defaults !!!
+static struct chan_usbradio_pvt usbradio_default = {
+       .cursound = -1,
+       .sounddev = -1,
+       .duplex = M_UNSET,                      /* XXX check this */
+       .autoanswer = 1,
+       .autohangup = 1,
+       .queuesize = QUEUE_SIZE,
+       .frags = FRAGS,
+       .ext = "s",
+       .ctx = "default",
+       .readpos = AST_FRIENDLY_OFFSET, /* start here on reads */
+       .lastopen = { 0, 0 },
+       .boost = BOOST_SCALE,
+};
+
+/*     DECLARE FUNCTION PROTOTYPES     */
+
+static void store_txtoctype(struct chan_usbradio_pvt *o, char *s);
+static int     hidhdwconfig(struct chan_usbradio_pvt *o);
+static int set_txctcss_level(struct chan_usbradio_pvt *o);
+static void pmrdump(struct chan_usbradio_pvt *o);
+static void mult_set(struct chan_usbradio_pvt *o);
+static int  mult_calc(int value);
+static void mixer_write(struct chan_usbradio_pvt *o);
+static void tune_rxinput(struct chan_usbradio_pvt *o);
+static void tune_rxvoice(struct chan_usbradio_pvt *o);
+static void tune_rxctcss(struct chan_usbradio_pvt *o);
+static void tune_txoutput(struct chan_usbradio_pvt *o, int value);
+static void tune_write(struct chan_usbradio_pvt *o);
+
+static char *usbradio_active;   /* the active device */
+
+static int setformat(struct chan_usbradio_pvt *o, int mode);
+
+static struct ast_channel *usbradio_request(const char *type, int format, void *data
+, int *cause);
+static int usbradio_digit_begin(struct ast_channel *c, char digit);
+static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration);
+static int usbradio_text(struct ast_channel *c, const char *text);
+static int usbradio_hangup(struct ast_channel *c);
+static int usbradio_answer(struct ast_channel *c);
+static struct ast_frame *usbradio_read(struct ast_channel *chan);
+static int usbradio_call(struct ast_channel *c, char *dest, int timeout);
+static int usbradio_write(struct ast_channel *chan, struct ast_frame *f);
+static int usbradio_indicate(struct ast_channel *chan, int cond, const void *data, size_t datalen);
+static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+
+#if    DEBUG_FILETEST == 1
+static int RxTestIt(struct chan_usbradio_pvt *o);
+#endif
+
+static char tdesc[] = "USB (CM108) Radio Channel Driver";
+
+static const struct ast_channel_tech usbradio_tech = {
+       .type = "Radio",
+       .description = tdesc,
+       .capabilities = AST_FORMAT_SLINEAR,
+       .requester = usbradio_request,
+       .send_digit_begin = usbradio_digit_begin,
+       .send_digit_end = usbradio_digit_end,
+       .send_text = usbradio_text,
+       .hangup = usbradio_hangup,
+       .answer = usbradio_answer,
+       .read = usbradio_read,
+       .call = usbradio_call,
+       .write = usbradio_write,
+       .indicate = usbradio_indicate,
+       .fixup = usbradio_fixup,
+};
+
+/* Call with:  devnum: alsa major device number, param: ascii Formal
+Parameter Name, val1, first or only value, val2 second value, or 0 
+if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
+
+Note: must add -lasound to end of linkage */
+
+static int amixer_max(int devnum,char *param)
+{
+int    rv,type;
+char   str[100];
+snd_hctl_t *hctl;
+snd_ctl_elem_id_t *id;
+snd_hctl_elem_t *elem;
+snd_ctl_elem_info_t *info;
+
+       sprintf(str,"hw:%d",devnum);
+       if (snd_hctl_open(&hctl, str, 0)) return(-1);
+       snd_hctl_load(hctl);
+       snd_ctl_elem_id_alloca(&id);
+       snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+       snd_ctl_elem_id_set_name(id, param);  
+       elem = snd_hctl_find_elem(hctl, id);
+       if (!elem)
+       {
+               snd_hctl_close(hctl);
+               return(-1);
+       }
+       snd_ctl_elem_info_alloca(&info);
+       snd_hctl_elem_info(elem,info);
+       type = snd_ctl_elem_info_get_type(info);
+       rv = 0;
+       switch(type)
+       {
+           case SND_CTL_ELEM_TYPE_INTEGER:
+               rv = snd_ctl_elem_info_get_max(info);
+               break;
+           case SND_CTL_ELEM_TYPE_BOOLEAN:
+               rv = 1;
+               break;
+       }
+       snd_hctl_close(hctl);
+       return(rv);
+}
+
+/* Call with:  devnum: alsa major device number, param: ascii Formal
+Parameter Name, val1, first or only value, val2 second value, or 0 
+if only 1 value. Values: 0-99 (percent) or 0-1 for baboon.
+
+Note: must add -lasound to end of linkage */
+
+static int setamixer(int devnum,char *param, int v1, int v2)
+{
+int    type;
+char   str[100];
+snd_hctl_t *hctl;
+snd_ctl_elem_id_t *id;
+snd_ctl_elem_value_t *control;
+snd_hctl_elem_t *elem;
+snd_ctl_elem_info_t *info;
+
+       sprintf(str,"hw:%d",devnum);
+       if (snd_hctl_open(&hctl, str, 0)) return(-1);
+       snd_hctl_load(hctl);
+       snd_ctl_elem_id_alloca(&id);
+       snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+       snd_ctl_elem_id_set_name(id, param);  
+       elem = snd_hctl_find_elem(hctl, id);
+       if (!elem)
+       {
+               snd_hctl_close(hctl);
+               return(-1);
+       }
+       snd_ctl_elem_info_alloca(&info);
+       snd_hctl_elem_info(elem,info);
+       type = snd_ctl_elem_info_get_type(info);
+       snd_ctl_elem_value_alloca(&control);
+       snd_ctl_elem_value_set_id(control, id);    
+       switch(type)
+       {
+           case SND_CTL_ELEM_TYPE_INTEGER:
+               snd_ctl_elem_value_set_integer(control, 0, v1);
+               if (v2 > 0) snd_ctl_elem_value_set_integer(control, 1, v2);
+               break;
+           case SND_CTL_ELEM_TYPE_BOOLEAN:
+               snd_ctl_elem_value_set_integer(control, 0, (v1 != 0));
+               break;
+       }
+       if (snd_hctl_elem_write(elem, control))
+       {
+               snd_hctl_close(hctl);
+               return(-1);
+       }
+       snd_hctl_close(hctl);
+       return(0);
+}
+
+static void hid_set_outputs(struct usb_dev_handle *handle,
+         unsigned char *outputs)
+{
+       usb_control_msg(handle,
+             USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
+             HID_REPORT_SET,
+             0 + (HID_RT_OUTPUT << 8),
+             C108_HID_INTERFACE,
+             (char*)outputs, 4, 5000);
+}
+
+static void hid_get_inputs(struct usb_dev_handle *handle,
+         unsigned char *inputs)
+{
+       usb_control_msg(handle,
+             USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
+             HID_REPORT_GET,
+             0 + (HID_RT_INPUT << 8),
+             C108_HID_INTERFACE,
+             (char*)inputs, 4, 5000);
+}
+
+static struct usb_device *hid_device_init(void)
+{
+    struct usb_bus *usb_bus;
+    struct usb_device *dev;
+    usb_init();
+    usb_find_busses();
+    usb_find_devices();
+    for (usb_bus = usb_busses;
+         usb_bus;
+         usb_bus = usb_bus->next) {
+        for (dev = usb_bus->devices;
+             dev;
+             dev = dev->next) {
+            if ((dev->descriptor.idVendor
+                  == C108_VENDOR_ID) &&
+                (dev->descriptor.idProduct
+                  == C108_PRODUCT_ID))
+                return dev;
+        }
+    }
+    return NULL;
+}
+
+static int     hidhdwconfig(struct chan_usbradio_pvt *o)
+{
+       if(o->hdwtype==1)         //sphusb
+       {
+               o->hid_gpio_ctl         =  0x08;        /* set GPIO4 to output mode */
+               o->hid_gpio_ctl_loc     =  2;   /* For CTL of GPIO */
+               o->hid_io_cor           =  4;   /* GPIO3 is COR */
+               o->hid_io_cor_loc       =  1;   /* GPIO3 is COR */
+               o->hid_io_ctcss         =  2;   /* GPIO 2 is External CTCSS */
+               o->hid_io_ctcss_loc =  1;       /* is GPIO 2 */
+               o->hid_io_ptt           =  8;   /* GPIO 4 is PTT */
+               o->hid_gpio_loc         =  1;   /* For ALL GPIO */
+       }
+       else if(o->hdwtype==0)  //dudeusb
+       {
+               o->hid_gpio_ctl         =  0x0c;        /* set GPIO 3 & 4 to output mode */
+               o->hid_gpio_ctl_loc     =  2;   /* For CTL of GPIO */
+               o->hid_io_cor           =  2;   /* VOLD DN is COR */
+               o->hid_io_cor_loc       =  0;   /* VOL DN COR */
+               o->hid_io_ctcss         =  2;   /* GPIO 2 is External CTCSS */
+               o->hid_io_ctcss_loc =  1;       /* is GPIO 2 */
+               o->hid_io_ptt           =  4;   /* GPIO 3 is PTT */
+               o->hid_gpio_loc         =  1;   /* For ALL GPIO */
+       }
+       else if(o->hdwtype==3)  // custom version
+       {
+               o->hid_gpio_ctl         =  0x0c;        /* set GPIO 3 & 4 to output mode */
+               o->hid_gpio_ctl_loc     =  2;   /* For CTL of GPIO */
+               o->hid_io_cor           =  2;   /* VOLD DN is COR */
+               o->hid_io_cor_loc       =  0;   /* VOL DN COR */
+               o->hid_io_ctcss         =  2;   /* GPIO 2 is External CTCSS */
+               o->hid_io_ctcss_loc =  1;       /* is GPIO 2 */
+               o->hid_io_ptt           =  4;   /* GPIO 3 is PTT */
+               o->hid_gpio_loc         =  1;   /* For ALL GPIO */
+       }
+
+       return 0;
+}
+
+
+static void *hidthread(void *arg)
+{
+       unsigned char buf[4],keyed;
+       char lastrx, txtmp;
+       struct usb_device *usb_dev;
+       struct usb_dev_handle *usb_handle;
+       struct chan_usbradio_pvt *o = (struct chan_usbradio_pvt *) arg;
+
+       usb_dev = hid_device_init();
+       if (usb_dev == NULL) {
+               ast_log(LOG_ERROR,"USB HID device not found\n");
+               pthread_exit(NULL);
+       }
+       usb_handle = usb_open(usb_dev);
+       if (usb_handle == NULL) {
+               ast_log(LOG_ERROR,"Not able to open USB device\n");
+               pthread_exit(NULL);
+       }
+       if (usb_claim_interface(usb_handle,C108_HID_INTERFACE) < 0)
+       {
+               if (usb_detach_kernel_driver_np(usb_handle,C108_HID_INTERFACE) < 0) {
+                       ast_log(LOG_ERROR,"Not able to detach the USB device\n");
+                       pthread_exit(NULL);
+               }
+               if (usb_claim_interface(usb_handle,C108_HID_INTERFACE) < 0) {
+                       ast_log(LOG_ERROR,"Not able to claim the USB device\n");
+                       pthread_exit(NULL);
+               }
+       }
+       memset(buf,0,sizeof(buf));
+       buf[2] = o->hid_gpio_ctl;
+       buf[1] = 0;
+       hid_set_outputs(usb_handle,buf);
+       traceusb1(("hidthread: Starting normally!!\n"));
+       lastrx = 0;
+       while(!o->stophid)
+       {
+               buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+               hid_get_inputs(usb_handle,buf);
+               keyed = !(buf[o->hid_io_cor_loc] & o->hid_io_cor);
+               if (keyed != o->rxhidsq)
+               {
+                       if(o->debuglevel)printf("chan_usbradio() hidthread: update rxhidsq = %d\n",keyed);
+                       o->rxhidsq=keyed;                
+               }
+
+               /* if change in tx stuff */
+               txtmp=0;
+               if(o->txkeyed || o->txchankey || o->txtestkey || o->pmrChan->txPttOut) txtmp=1;
+               
+               if (o->lasttx != txtmp)
+               {
+                       o->lasttx = txtmp;
+                       if(o->debuglevel)printf("hidthread: tx set to %d\n",txtmp);
+                       buf[o->hid_gpio_loc] = 0;
+                       if (txtmp) buf[o->hid_gpio_loc] = o->hid_io_ptt;
+                       buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+                       hid_set_outputs(usb_handle,buf);
+               }
+
+               time(&o->lasthidtime);
+               usleep(50000);
+       }
+       buf[o->hid_gpio_loc] = 0;
+       if (o->invertptt) buf[o->hid_gpio_loc] = o->hid_io_ptt;
+       buf[o->hid_gpio_ctl_loc] = o->hid_gpio_ctl;
+       hid_set_outputs(usb_handle,buf);
+       pthread_exit(0);
+}
+
+/*
+ * returns a pointer to the descriptor with the given name
+ */
+static struct chan_usbradio_pvt *find_desc(char *dev)
+{
+       struct chan_usbradio_pvt *o = NULL;
+
+       if (!dev)
+               ast_log(LOG_WARNING, "null dev\n");
+
+       for (o = usbradio_default.next; o && o->name && dev && strcmp(o->name, dev) != 0; o = o->next);
+
+       if (!o)
+               ast_log(LOG_WARNING, "could not find <%s>\n", dev ? dev : "--no-device--");
+
+       return o;
+}
+
+/*
+ * split a string in extension-context, returns pointers to malloc'ed
+ * strings.
+ * If we do not have 'overridecontext' then the last @ is considered as
+ * a context separator, and the context is overridden.
+ * This is usually not very necessary as you can play with the dialplan,
+ * and it is nice not to need it because you have '@' in SIP addresses.
+ * Return value is the buffer address.
+ */
+#if    0
+static char *ast_ext_ctx(const char *src, char **ext, char **ctx)
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+       if (ext == NULL || ctx == NULL)
+               return NULL;                    /* error */
+
+       *ext = *ctx = NULL;
+
+       if (src && *src != '\0')
+               *ext = ast_strdup(src);
+
+       if (*ext == NULL)
+               return NULL;
+
+       if (!o->overridecontext) {
+               /* parse from the right */
+               *ctx = strrchr(*ext, '@');
+               if (*ctx)
+                       *(*ctx)++ = '\0';
+       }
+
+       return *ext;
+}
+#endif
+
+/*
+ * Returns the number of blocks used in the audio output channel
+ */
+static int used_blocks(struct chan_usbradio_pvt *o)
+{
+       struct audio_buf_info info;
+
+       if (ioctl(o->sounddev, SNDCTL_DSP_GETOSPACE, &info)) {
+               if (!(o->warned & WARN_used_blocks)) {
+                       ast_log(LOG_WARNING, "Error reading output space\n");
+                       o->warned |= WARN_used_blocks;
+               }
+               return 1;
+       }
+
+       if (o->total_blocks == 0) {
+               if (0)                                  /* debugging */
+                       ast_log(LOG_WARNING, "fragtotal %d size %d avail %d\n", info.fragstotal, info.fragsize, info.fragments);
+               o->total_blocks = info.fragments;
+       }
+
+       return o->total_blocks - info.fragments;
+}
+
+/* Write an exactly FRAME_SIZE sized frame */
+static int soundcard_writeframe(struct chan_usbradio_pvt *o, short *data)
+{
+       int res;
+
+       if (o->sounddev < 0)
+               setformat(o, O_RDWR);
+       if (o->sounddev < 0)
+               return 0;                               /* not fatal */
+       /*
+        * Nothing complex to manage the audio device queue.
+        * If the buffer is full just drop the extra, otherwise write.
+        * XXX in some cases it might be useful to write anyways after
+        * a number of failures, to restart the output chain.
+        */
+       res = used_blocks(o);
+       if (res > o->queuesize) {       /* no room to write a block */
+               if (o->w_errors++ == 0 && (usbradio_debug & 0x4))
+                       ast_log(LOG_WARNING, "write: used %d blocks (%d)\n", res, o->w_errors);
+               return 0;
+       }
+       o->w_errors = 0;
+
+       return write(o->sounddev, ((void *) data), FRAME_SIZE * 2 * 12);
+}
+
+/*
+ * Handler for 'sound writable' events from the sound thread.
+ * Builds a frame from the high level description of the sounds,
+ * and passes it to the audio device.
+ * The actual sound is made of 1 or more sequences of sound samples
+ * (s->datalen, repeated to make s->samplen samples) followed by
+ * s->silencelen samples of silence. The position in the sequence is stored
+ * in o->sampsent, which goes between 0 .. s->samplen+s->silencelen.
+ * In case we fail to write a frame, don't update o->sampsent.
+ */
+static void send_sound(struct chan_usbradio_pvt *o)
+{
+       short myframe[FRAME_SIZE];
+       int ofs, l, start;
+       int l_sampsent = o->sampsent;
+       struct sound *s;
+
+       if (o->cursound < 0)            /* no sound to send */
+               return;
+
+       s = &sounds[o->cursound];
+
+       for (ofs = 0; ofs < FRAME_SIZE; ofs += l) {
+               l = s->samplen - l_sampsent;    /* # of available samples */
+               if (l > 0) {
+                       start = l_sampsent % s->datalen;        /* source offset */
+                       if (l > FRAME_SIZE - ofs)       /* don't overflow the frame */
+                               l = FRAME_SIZE - ofs;
+                       if (l > s->datalen - start)     /* don't overflow the source */
+                               l = s->datalen - start;
+                       bcopy(s->data + start, myframe + ofs, l * 2);
+                       if (0)
+                               ast_log(LOG_WARNING, "send_sound sound %d/%d of %d into %d\n", l_sampsent, l, s->samplen, ofs);
+                       l_sampsent += l;
+               } else {                                /* end of samples, maybe some silence */
+                       static const short silence[FRAME_SIZE] = { 0, };
+
+                       l += s->silencelen;
+                       if (l > 0) {
+                               if (l > FRAME_SIZE - ofs)
+                                       l = FRAME_SIZE - ofs;
+                               bcopy(silence, myframe + ofs, l * 2);
+                               l_sampsent += l;
+                       } else {                        /* silence is over, restart sound if loop */
+                               if (s->repeat == 0) {   /* last block */
+                                       o->cursound = -1;
+                                       o->nosound = 0; /* allow audio data */
+                                       if (ofs < FRAME_SIZE)   /* pad with silence */
+                                               bcopy(silence, myframe + ofs, (FRAME_SIZE - ofs) * 2);
+                               }
+                               l_sampsent = 0;
+                       }
+               }
+       }
+       l = soundcard_writeframe(o, myframe);
+       if (l > 0)
+               o->sampsent = l_sampsent;       /* update status */
+}
+
+static void *sound_thread(void *arg)
+{
+       char ign[4096];
+       struct chan_usbradio_pvt *o = (struct chan_usbradio_pvt *) arg;
+
+       /*
+        * Just in case, kick the driver by trying to read from it.
+        * Ignore errors - this read is almost guaranteed to fail.
+        */
+       read(o->sounddev, ign, sizeof(ign));
+       for (;;) {
+               fd_set rfds, wfds;
+               int maxfd, res;
+
+               FD_ZERO(&rfds);
+               FD_ZERO(&wfds);
+               FD_SET(o->sndcmd[0], &rfds);
+               maxfd = o->sndcmd[0];   /* pipe from the main process */
+               if (o->cursound > -1 && o->sounddev < 0)
+                       setformat(o, O_RDWR);   /* need the channel, try to reopen */
+               else if (o->cursound == -1 && o->owner == NULL)
+               {
+                       setformat(o, O_CLOSE);  /* can close */
+               }
+               if (o->sounddev > -1) {
+                       if (!o->owner) {        /* no one owns the audio, so we must drain it */
+                               FD_SET(o->sounddev, &rfds);
+                               maxfd = MAX(o->sounddev, maxfd);
+                       }
+                       if (o->cursound > -1) {
+                               FD_SET(o->sounddev, &wfds);
+                               maxfd = MAX(o->sounddev, maxfd);
+                       }
+               }
+               /* ast_select emulates linux behaviour in terms of timeout handling */
+               res = ast_select(maxfd + 1, &rfds, &wfds, NULL, NULL);
+               if (res < 1) {
+                       ast_log(LOG_WARNING, "select failed: %s\n", strerror(errno));
+                       sleep(1);
+                       continue;
+               }
+               if (FD_ISSET(o->sndcmd[0], &rfds)) {
+                       /* read which sound to play from the pipe */
+                       int i, what = -1;
+
+                       read(o->sndcmd[0], &what, sizeof(what));
+                       for (i = 0; sounds[i].ind != -1; i++) {
+                               if (sounds[i].ind == what) {
+                                       o->cursound = i;
+                                       o->sampsent = 0;
+                                       o->nosound = 1; /* block audio from pbx */
+                                       break;
+                               }
+                       }
+                       if (sounds[i].ind == -1)
+                               ast_log(LOG_WARNING, "invalid sound index: %d\n", what);
+               }
+               if (o->sounddev > -1) {
+                       if (FD_ISSET(o->sounddev, &rfds))       /* read and ignore errors */
+                               read(o->sounddev, ign, sizeof(ign)); 
+                       if (FD_ISSET(o->sounddev, &wfds))
+                               send_sound(o);
+               }
+       }
+       return NULL;                            /* Never reached */
+}
+
+/*
+ * reset and close the device if opened,
+ * then open and initialize it in the desired mode,
+ * trigger reads and writes so we can start using it.
+ */
+static int setformat(struct chan_usbradio_pvt *o, int mode)
+{
+       int fmt, desired, res, fd;
+       char device[100];
+
+       if (o->sounddev >= 0) {
+               ioctl(o->sounddev, SNDCTL_DSP_RESET, 0);
+               close(o->sounddev);
+               o->duplex = M_UNSET;
+               o->sounddev = -1;
+       }
+       if (mode == O_CLOSE)            /* we are done */
+               return 0;
+       if (ast_tvdiff_ms(ast_tvnow(), o->lastopen) < 1000)
+               return -1;                              /* don't open too often */
+       o->lastopen = ast_tvnow();
+       strcpy(device,"/dev/dsp");
+       if (o->devicenum)
+               sprintf(device,"/dev/dsp%d",o->devicenum);
+       fd = o->sounddev = open(device, mode | O_NONBLOCK);
+       if (fd < 0) {
+               ast_log(LOG_WARNING, "Unable to re-open DSP device %d: %s\n", o->devicenum, strerror(errno));
+               return -1;
+       }
+       if (o->owner)
+               o->owner->fds[0] = fd;
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       fmt = AFMT_S16_LE;
+#else
+       fmt = AFMT_S16_BE;
+#endif
+       res = ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Unable to set format to 16-bit signed\n");
+               return -1;
+       }
+       switch (mode) {
+               case O_RDWR:
+                       res = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
+                       /* Check to see if duplex set (FreeBSD Bug) */
+                       res = ioctl(fd, SNDCTL_DSP_GETCAPS, &fmt);
+                       if (res == 0 && (fmt & DSP_CAP_DUPLEX)) {
+                               if (option_verbose > 1)
+                                       ast_verbose(VERBOSE_PREFIX_2 "Console is full duplex\n");
+                               o->duplex = M_FULL;
+                       };
+                       break;
+               case O_WRONLY:
+                       o->duplex = M_WRITE;
+                       break;
+               case O_RDONLY:
+                       o->duplex = M_READ;
+                       break;
+       }
+
+       fmt = 1;
+       res = ioctl(fd, SNDCTL_DSP_STEREO, &fmt);
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+               return -1;
+       }
+       fmt = desired = 48000;                                                  /* 8000 Hz desired */
+       res = ioctl(fd, SNDCTL_DSP_SPEED, &fmt);
+
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to set audio device to mono\n");
+               return -1;
+       }
+       if (fmt != desired) {
+               if (!(o->warned & WARN_speed)) {
+                       ast_log(LOG_WARNING,
+                           "Requested %d Hz, got %d Hz -- sound may be choppy\n",
+                           desired, fmt);
+                       o->warned |= WARN_speed;
+               }
+       }
+       /*
+        * on Freebsd, SETFRAGMENT does not work very well on some cards.
+        * Default to use 256 bytes, let the user override
+        */
+       if (o->frags) {
+               fmt = o->frags;
+               res = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fmt);
+               if (res < 0) {
+                       if (!(o->warned & WARN_frag)) {
+                               ast_log(LOG_WARNING,
+                                       "Unable to set fragment size -- sound may be choppy\n");
+                               o->warned |= WARN_frag;
+                       }
+               }
+       }
+       /* on some cards, we need SNDCTL_DSP_SETTRIGGER to start outputting */
+       res = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+       res = ioctl(fd, SNDCTL_DSP_SETTRIGGER, &res);
+       /* it may fail if we are in half duplex, never mind */
+       return 0;
+}
+
+/*
+ * some of the standard methods supported by channels.
+ */
+static int usbradio_digit_begin(struct ast_channel *c, char digit)
+{
+       return 0;
+}
+
+static int usbradio_digit_end(struct ast_channel *c, char digit, unsigned int duration)
+{
+       /* no better use for received digits than print them */
+       ast_verbose(" << Console Received digit %c of duration %u ms >> \n", 
+               digit, duration);
+       return 0;
+}
+
+static int usbradio_text(struct ast_channel *c, const char *text)
+{
+       /* print received messages */
+       ast_verbose(" << Console Received text %s >> \n", text);
+       return 0;
+}
+
+/* Play ringtone 'x' on device 'o' */
+static void ring(struct chan_usbradio_pvt *o, int x)
+{
+       write(o->sndcmd[1], &x, sizeof(x));
+}
+
+/*
+ * handler for incoming calls. Either autoanswer, or start ringing
+ */
+static int usbradio_call(struct ast_channel *c, char *dest, int timeout)
+{
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+
+       time(&o->lasthidtime);
+       ast_pthread_create_background(&o->hidthread, NULL, hidthread, o);
+       ast_setstate(c, AST_STATE_UP);
+       return 0;
+}
+
+/*
+ * remote side answered the phone
+ */
+static int usbradio_answer(struct ast_channel *c)
+{
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+
+       ast_setstate(c, AST_STATE_UP);
+       o->cursound = -1;
+       o->nosound = 0;
+       return 0;
+}
+
+static int usbradio_hangup(struct ast_channel *c)
+{
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+
+       o->cursound = -1;
+       o->nosound = 0;
+       c->tech_pvt = NULL;
+       o->owner = NULL;
+       ast_module_unref(ast_module_info->self);
+       if (o->hookstate) {
+               if (o->autoanswer || o->autohangup) {
+                       /* Assume auto-hangup too */
+                       o->hookstate = 0;
+                       setformat(o, O_CLOSE);
+               } else {
+                       /* Make congestion noise */
+                       ring(o, AST_CONTROL_CONGESTION);
+               }
+       }
+       o->stophid = 1;
+       pthread_join(o->hidthread,NULL);
+       return 0;
+}
+
+
+/* used for data coming from the network */
+static int usbradio_write(struct ast_channel *c, struct ast_frame *f)
+{
+       int src,datalen;
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+
+       traceusb2(("usbradio_write() o->nosound= %i\n",o->nosound));    //sph maw asdf
+
+       /* Immediately return if no sound is enabled */
+       if (o->nosound)
+               return 0;
+       /* Stop any currently playing sound */
+       o->cursound = -1;
+       /*
+        * we could receive a block which is not a multiple of our
+        * FRAME_SIZE, so buffer it locally and write to the device
+        * in FRAME_SIZE chunks.
+        * Keep the residue stored for future use.
+        */
+
+       if(o->txkeyed||o->txtestkey)o->pmrChan->txPttIn=1;
+       else o->pmrChan->txPttIn=0;
+
+       #if DEBUG_CAPTURES == 1 // to write input data to a file   datalen=320
+       if (ftxcapraw && o->b.txcapraw)
+       {
+               i16 i, tbuff[f->datalen];
+               for(i=0;i<f->datalen;i+=2)
+               {
+                       tbuff[i]= ((i16*)(f->data))[i/2];
+                       tbuff[i+1]= o->txkeyed*M_Q13;
+               }
+               fwrite(tbuff,2,f->datalen,ftxcapraw);
+               //fwrite(f->data,1,f->datalen,ftxcapraw);
+       }
+       #endif
+
+       PmrTx(o->pmrChan,(i16*)f->data,(i16*)o->usbradio_write_buf_1);
+
+       #if 0   // to write 48KS/s stereo data to a file
+       if (!ftxoutraw) ftxoutraw = fopen(TX_CAP_OUT_FILE,"w");
+       if (ftxoutraw) fwrite(o->usbradio_write_buf_1,1,f->datalen * 2 * 6,ftxoutraw);
+       #endif
+
+       #if DEBUG_CAPTURES == 1
+    if (o->b.txcap2 && ftxcaptrace) fwrite((o->pmrChan->ptxDebug),1,FRAME_SIZE * 2 * 16,ftxcaptrace);
+       #endif
+
+       src = 0;                                        /* read position into f->data */
+       datalen = f->datalen * 12;
+       while (src < datalen) {
+               /* Compute spare room in the buffer */
+               int l = sizeof(o->usbradio_write_buf) - o->usbradio_write_dst;
+
+               if (datalen - src >= l) {       /* enough to fill a frame */
+                       memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l);
+                       soundcard_writeframe(o, (short *) o->usbradio_write_buf);
+                       src += l;
+                       o->usbradio_write_dst = 0;
+               } else {                                /* copy residue */
+                       l = datalen - src;
+                       memcpy(o->usbradio_write_buf + o->usbradio_write_dst, o->usbradio_write_buf_1 + src, l);
+                       src += l;                       /* but really, we are done */
+                       o->usbradio_write_dst += l;
+               }
+       }
+       return 0;
+}
+
+static struct ast_frame *usbradio_read(struct ast_channel *c)
+{
+       int res;
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+       struct ast_frame *f = &o->read_f,*f1;
+       struct ast_frame wf = { AST_FRAME_CONTROL };
+       time_t now;
+
+       traceusb2(("usbradio_read()\n"));       //sph maw asdf
+
+       if (o->lasthidtime)
+       {
+               time(&now);
+               if ((now - o->lasthidtime) > 3)
+               {
+                       ast_log(LOG_ERROR,"HID process has died or something!!\n");
+                       return NULL;
+               }
+       }
+       if (o->lastrx && (!o->rxkeyed))
+       {
+               o->lastrx = 0;
+               wf.subclass = AST_CONTROL_RADIO_UNKEY;
+               ast_queue_frame(o->owner, &wf);
+       } else if ((!o->lastrx) && (o->rxkeyed))
+       {
+               o->lastrx = 1;
+               wf.subclass = AST_CONTROL_RADIO_KEY;
+               ast_queue_frame(o->owner, &wf);
+       }
+       /* XXX can be simplified returning &ast_null_frame */
+       /* prepare a NULL frame in case we don't have enough data to return */
+       bzero(f, sizeof(struct ast_frame));
+       f->frametype = AST_FRAME_NULL;
+       f->src = usbradio_tech.type;
+
+       res = read(o->sounddev, o->usbradio_read_buf + o->readpos, 
+               sizeof(o->usbradio_read_buf) - o->readpos);
+       if (res < 0)                            /* audio data not ready, return a NULL frame */
+               return f;
+
+       o->readpos += res;
+       if (o->readpos < sizeof(o->usbradio_read_buf))  /* not enough samples */
+               return f;
+
+       if (o->mute)
+               return f;
+
+       #if DEBUG_CAPTURES == 1
+       if (o->b.rxcapraw && frxcapraw) fwrite((o->usbradio_read_buf + AST_FRIENDLY_OFFSET),1,FRAME_SIZE * 2 * 2 * 6,frxcapraw);
+       #endif
+
+       #if 1
+       PmrRx(         o->pmrChan, 
+                  (i16 *)(o->usbradio_read_buf + AST_FRIENDLY_OFFSET),
+                  (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET));
+
+       #else
+       static FILE *hInput;
+       i16 iBuff[FRAME_SIZE*2*6];
+
+       o->pmrChan->b.rxCapture=1;
+
+       if(!hInput)
+       {
+               hInput  = fopen("/usr/src/xpmr/testdata/rx_in.pcm","r");
+               if(!hInput)
+               {
+                       printf(" Input Data File Not Found.\n");
+                       return 0;
+               }
+       }
+
+       if(0==fread((void *)iBuff,2,FRAME_SIZE*2*6,hInput))exit;
+
+       PmrRx(         o->pmrChan, 
+                  (i16 *)iBuff,
+                  (i16 *)(o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET));
+
+       #endif
+
+       #if 0
+       if (!frxoutraw) frxoutraw = fopen(RX_CAP_OUT_FILE,"w");
+    if (frxoutraw) fwrite((o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET),1,FRAME_SIZE * 2,frxoutraw);
+       #endif
+
+       #if DEBUG_CAPTURES == 1
+    if (frxcaptrace && o->b.rxcap2) fwrite((o->pmrChan->prxDebug),1,FRAME_SIZE * 2 * 16,frxcaptrace);
+       #endif
+
+       if(o->rxcdtype==CD_HID && (o->pmrChan->rxExtCarrierDetect!=o->rxhidsq))
+               o->pmrChan->rxExtCarrierDetect=o->rxhidsq;
+       if(o->rxcdtype==CD_HID_INVERT && (o->pmrChan->rxExtCarrierDetect==o->rxhidsq))
+               o->pmrChan->rxExtCarrierDetect=!o->rxhidsq;
+               
+       if( (o->rxcdtype==CD_HID && o->rxhidsq) ||
+               (o->rxcdtype==CD_HID_INVERT && !o->rxhidsq) ||
+               (o->rxcdtype==CD_XPMR_NOISE && o->pmrChan->rxCarrierDetect) ||
+               (o->rxcdtype==CD_XPMR_VOX && o->pmrChan->rxCarrierDetect)
+         )
+       {
+               res=1;  
+       }
+       else res=0;
+
+       if(res!=o->rxcarrierdetect)
+       {
+               o->rxcarrierdetect=res;
+               if(o->debuglevel)printf("rxcarrierdetect = %i\n",res);
+       }
+
+       if(o->pmrChan->rxCtcss->decode!=o->rxctcssdecode)
+       {
+               if(o->debuglevel)printf("rxctcssdecode = %i\n",o->pmrChan->rxCtcss->decode);
+               o->rxctcssdecode=o->pmrChan->rxCtcss->decode;
+       }
+
+       if (     
+             (  o->rxctcssfreq &&  (o->rxctcssdecode == o->pmrChan->rxCtcssIndex)) || 
+             ( !o->rxctcssfreq &&      o->rxcarrierdetect)
+          ) 
+       {
+               o->rxkeyed = 1;
+       }
+       else o->rxkeyed = 0;
+
+
+       o->readpos = AST_FRIENDLY_OFFSET;       /* reset read pointer for next frame */
+       if (c->_state != AST_STATE_UP)  /* drop data if frame is not up */
+               return f;
+       /* ok we can build and deliver the frame to the caller */
+       f->frametype = AST_FRAME_VOICE;
+       f->subclass = AST_FORMAT_SLINEAR;
+       f->samples = FRAME_SIZE;
+       f->datalen = FRAME_SIZE * 2;
+       f->data = o->usbradio_read_buf_8k + AST_FRIENDLY_OFFSET;
+       if (o->boost != BOOST_SCALE) {  /* scale and clip values */
+               int i, x;
+               int16_t *p = (int16_t *) f->data;
+               for (i = 0; i < f->samples; i++) {
+                       x = (p[i] * o->boost) / BOOST_SCALE;
+                       if (x > 32767)
+                               x = 32767;
+                       else if (x < -32768)
+                               x = -32768;
+                       p[i] = x;
+               }
+       }
+
+       f->offset = AST_FRIENDLY_OFFSET;
+       if (o->dsp)
+         {
+           f1 = ast_dsp_process(c,o->dsp,f);
+               if ((f1->frametype == AST_FRAME_DTMF_END) ||
+               (f1->frametype == AST_FRAME_DTMF_BEGIN))
+               {
+               if ((f1->subclass == 'm') || (f1->subclass == 'u'))
+                   f1->frametype = AST_FRAME_DTMF_BEGIN;
+               if (f1->frametype == AST_FRAME_DTMF_END)
+                   ast_log(LOG_NOTICE,"Got DTMF char %c\n",f1->subclass);
+               return(f1);
+               }
+         }
+       return f;
+}
+
+static int usbradio_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+       struct chan_usbradio_pvt *o = newchan->tech_pvt;
+       ast_log(LOG_WARNING,"usbradio_fixup()\n");
+       o->owner = newchan;
+       return 0;
+}
+
+static int usbradio_indicate(struct ast_channel *c, int cond, const void *data, size_t datalen)
+{
+       struct chan_usbradio_pvt *o = c->tech_pvt;
+       int res = -1;
+
+       switch (cond) {
+               case AST_CONTROL_BUSY:
+               case AST_CONTROL_CONGESTION:
+               case AST_CONTROL_RINGING:
+                       res = cond;
+                       break;
+
+               case -1:
+                       o->cursound = -1;
+                       o->nosound = 0;         /* when cursound is -1 nosound must be 0 */
+                       return 0;
+
+               case AST_CONTROL_VIDUPDATE:
+                       res = -1;
+                       break;
+               case AST_CONTROL_HOLD:
+                       ast_verbose(" << Console Has Been Placed on Hold >> \n");
+                       ast_moh_start(c, data, o->mohinterpret);
+                       break;
+               case AST_CONTROL_UNHOLD:
+                       ast_verbose(" << Console Has Been Retrieved from Hold >> \n");
+                       ast_moh_stop(c);
+                       break;
+               case AST_CONTROL_PROCEEDING:
+                       ast_verbose(" << Call Proceeding... >> \n");
+                       ast_moh_stop(c);
+                       break;
+               case AST_CONTROL_PROGRESS:
+                       ast_verbose(" << Call Progress... >> \n");
+                       ast_moh_stop(c);
+                       break;
+               case AST_CONTROL_RADIO_KEY:
+                       o->txkeyed = 1;
+                       if(o->debuglevel)ast_verbose(" << Radio Transmit On. >> \n");
+                       break;
+               case AST_CONTROL_RADIO_UNKEY:
+                       o->txkeyed = 0;
+                       if(o->debuglevel)ast_verbose(" << Radio Transmit Off. >> \n");
+                       break;
+               default:
+                       ast_log(LOG_WARNING, "Don't know how to display condition %d on %s\n", cond, c->name);
+                       return -1;
+       }
+
+       if (res > -1)
+               ring(o, res);
+
+       return 0;
+}
+
+/*
+ * allocate a new channel.
+ */
+static struct ast_channel *usbradio_new(struct chan_usbradio_pvt *o, char *ext, char *ctx, int state)
+{
+       struct ast_channel *c;
+       char device[100];
+
+       strcpy(device,"dsp");
+       if (o->devicenum) sprintf(device,"dsp%d",o->devicenum);
+       c = ast_channel_alloc(1, state, o->cid_num, o->cid_name, "", ext, ctx, 0, "usbRadio/%s", device);
+       if (c == NULL)
+               return NULL;
+       c->tech = &usbradio_tech;
+       if (o->sounddev < 0)
+               setformat(o, O_RDWR);
+       c->fds[0] = o->sounddev;        /* -1 if device closed, override later */
+       c->nativeformats = AST_FORMAT_SLINEAR;
+       c->readformat = AST_FORMAT_SLINEAR;
+       c->writeformat = AST_FORMAT_SLINEAR;
+       c->tech_pvt = o;
+
+       if (!ast_strlen_zero(o->language))
+               ast_string_field_set(c, language, o->language);
+       /* Don't use ast_set_callerid() here because it will
+        * generate a needless NewCallerID event */
+       c->cid.cid_num = ast_strdup(o->cid_num);
+       c->cid.cid_ani = ast_strdup(o->cid_num);
+       c->cid.cid_name = ast_strdup(o->cid_name);
+       if (!ast_strlen_zero(ext))
+               c->cid.cid_dnid = ast_strdup(ext);
+
+       o->owner = c;
+       ast_module_ref(ast_module_info->self);
+       ast_jb_configure(c, &global_jbconf);
+       if (state != AST_STATE_DOWN) {
+               if (ast_pbx_start(c)) {
+                       ast_log(LOG_WARNING, "Unable to start PBX on %s\n", c->name);
+                       ast_hangup(c);
+                       o->owner = c = NULL;
+                       /* XXX what about the channel itself ? */
+                       /* XXX what about usecnt ? */
+               }
+       }
+
+       return c;
+}
+
+static struct ast_channel *usbradio_request(const char *type, int format, void *data, int *cause)
+{
+       struct ast_channel *c;
+       struct chan_usbradio_pvt *o = find_desc(data);
+
+       if (0)
+       {
+               ast_log(LOG_WARNING, "usbradio_request ty <%s> data 0x%p <%s>\n", type, data, (char *) data);
+       }
+       if (o == NULL) {
+               ast_log(LOG_NOTICE, "Device %s not found\n", (char *) data);
+               /* XXX we could default to 'dsp' perhaps ? */
+               return NULL;
+       }
+       if ((format & AST_FORMAT_SLINEAR) == 0) {
+               ast_log(LOG_NOTICE, "Format 0x%x unsupported\n", format);
+               return NULL;
+       }
+       if (o->owner) {
+               ast_log(LOG_NOTICE, "Already have a call (chan %p) on the usb channel\n", o->owner);
+               *cause = AST_CAUSE_BUSY;
+               return NULL;
+       }
+       c = usbradio_new(o, NULL, NULL, AST_STATE_DOWN);
+       if (c == NULL) {
+               ast_log(LOG_WARNING, "Unable to create new usb channel\n");
+               return NULL;
+       }
+       return c;
+}
+
+static int console_key(int fd, int argc, char *argv[])
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+       if (argc != 2)
+               return RESULT_SHOWUSAGE; 
+       o->txtestkey = 1;
+       return RESULT_SUCCESS;
+}
+
+static int console_unkey(int fd, int argc, char *argv[])
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       o->txtestkey = 0;
+
+       return RESULT_SUCCESS;
+}
+
+static int radio_tune(int fd, int argc, char *argv[])
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+       int i=0;
+
+       if ((argc < 2) || (argc > 4))
+               return RESULT_SHOWUSAGE; 
+
+       if (argc == 2) /* just show stuff */
+       {
+               ast_cli(fd,"Output A is currently set to ");
+               if(o->txmixa==TX_OUT_COMPOSITE)ast_cli(fd,"composite.\n");
+               else if (o->txmixa==TX_OUT_VOICE)ast_cli(fd,"voice.\n");
+               else if (o->txmixa==TX_OUT_LSD)ast_cli(fd,"tone.\n");
+               else if (o->txmixa==TX_OUT_AUX)ast_cli(fd,"auxvoice.\n");
+               else ast_cli(fd,"off.\n");
+
+               ast_cli(fd,"Output B is currently set to ");
+               if(o->txmixb==TX_OUT_COMPOSITE)ast_cli(fd,"composite.\n");
+               else if (o->txmixb==TX_OUT_VOICE)ast_cli(fd,"voice.\n");
+               else if (o->txmixb==TX_OUT_LSD)ast_cli(fd,"tone.\n");
+               else if (o->txmixb==TX_OUT_AUX)ast_cli(fd,"auxvoice.\n");
+               else ast_cli(fd,"off.\n");
+
+               ast_cli(fd,"Tx Voice Level currently set to %d\n",o->txmixaset);
+               ast_cli(fd,"Tx Tone Level currently set to %d\n",o->txctcssadj);
+               ast_cli(fd,"Rx Squelch currently set to %d\n",o->rxsquelchadj);
+               return RESULT_SHOWUSAGE;
+       }
+
+       if (!strcasecmp(argv[2],"rxnoise")) tune_rxinput(o);
+       else if (!strcasecmp(argv[2],"rxvoice")) tune_rxvoice(o);
+       else if (!strcasecmp(argv[2],"rxtone")) tune_rxctcss(o);
+       else if (!strcasecmp(argv[2],"rxsquelch"))
+       {
+               if (argc == 3)
+               {
+                   ast_cli(fd,"Current Signal Strength is %d\n",((32767-o->pmrChan->rxRssi)*1000/32767));
+                   ast_cli(fd,"Current Squelch setting is %d\n",o->rxsquelchadj);
+                       //ast_cli(fd,"Current Raw RSSI        is %d\n",o->pmrChan->rxRssi);
+                   //ast_cli(fd,"Current (real) Squelch setting is %d\n",*(o->pmrChan->prxSquelchAdjust));
+               } else {
+                       i = atoi(argv[3]);
+                       if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE;
+                       ast_cli(fd,"Changed Squelch setting to %d\n",i);
+                       o->rxsquelchadj = i;
+                       *(o->pmrChan->prxSquelchAdjust)= ((999 - i) * 32767) / 1000;
+               }
+       }
+       else if (!strcasecmp(argv[2],"txvoice")) {
+               i = 0;
+
+               if( (o->txmixa!=TX_OUT_VOICE) && (o->txmixb!=TX_OUT_VOICE) &&
+                       (o->txmixa!=TX_OUT_COMPOSITE) && (o->txmixb!=TX_OUT_COMPOSITE)
+                 )
+               {
+                       ast_log(LOG_ERROR,"No txvoice output configured.\n");
+               }
+               else if (argc == 3)
+               {
+                       if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE))
+                               ast_cli(fd,"Current txvoice setting on Channel A is %d\n",o->txmixaset);
+                       else
+                               ast_cli(fd,"Current txvoice setting on Channel B is %d\n",o->txmixbset);
+               }
+               else
+               {
+                       i = atoi(argv[3]);
+                       if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE;
+
+                       if((o->txmixa==TX_OUT_VOICE)||(o->txmixa==TX_OUT_COMPOSITE))
+                       {
+                               o->txmixaset=i;
+                               ast_cli(fd,"Changed txvoice setting on Channel A to %d\n",o->txmixaset);
+                       }
+                       else
+                       {
+                               o->txmixbset=i;   
+                               ast_cli(fd,"Changed txvoice setting on Channel B to %d\n",o->txmixbset);
+                       }
+                       mixer_write(o);
+                       mult_set(o);
+                       ast_cli(fd,"Changed Tx Voice Output setting to %d\n",i);
+               }
+               tune_txoutput(o,i);
+       }
+       else if (!strcasecmp(argv[2],"auxvoice")) {
+               i = 0;
+               if( (o->txmixa!=TX_OUT_AUX) && (o->txmixb!=TX_OUT_AUX))
+               {
+                       ast_log(LOG_WARNING,"No auxvoice output configured.\n");
+               }
+               else if (argc == 3)
+               {
+                       if(o->txmixa==TX_OUT_AUX)
+                               ast_cli(fd,"Current auxvoice setting on Channel A is %d\n",o->txmixaset);
+                       else
+                               ast_cli(fd,"Current auxvoice setting on Channel B is %d\n",o->txmixbset);
+               }
+               else
+               {
+                       i = atoi(argv[3]);
+                       if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE;
+                       if(o->txmixa==TX_OUT_AUX)
+                       {
+                               o->txmixbset=i;
+                               ast_cli(fd,"Changed auxvoice setting on Channel A to %d\n",o->txmixaset);
+                       }
+                       else
+                       {
+                               o->txmixbset=i;
+                               ast_cli(fd,"Changed auxvoice setting on Channel B to %d\n",o->txmixbset);
+                       }
+                       mixer_write(o);
+                       mult_set(o);
+               }
+               //tune_auxoutput(o,i);
+       }
+       else if (!strcasecmp(argv[2],"txtone"))
+       {
+               if (argc == 3)
+                       ast_cli(fd,"Current Tx CTCSS modulation setting = %d\n",o->txctcssadj);
+               else
+               {
+                       i = atoi(argv[3]);
+                       if ((i < 0) || (i > 999)) return RESULT_SHOWUSAGE;
+                       o->txctcssadj = i;
+                       set_txctcss_level(o);
+                       ast_cli(fd,"Changed Tx CTCSS modulation setting to %i\n",i);
+               }
+               o->txtestkey=1;
+               usleep(5000000);
+               o->txtestkey=0;
+       }
+       else if (!strcasecmp(argv[2],"dump")) pmrdump(o);
+       else if (!strcasecmp(argv[2],"nocap")) 
+       {
+               ast_cli(fd,"File capture (trace) was rx=%d tx=%d and now off.\n",o->b.rxcap2,o->b.txcap2);
+               ast_cli(fd,"File capture (raw)   was rx=%d tx=%d and now off.\n",o->b.rxcapraw,o->b.txcapraw);
+               o->b.rxcapraw=o->b.txcapraw=o->b.rxcap2=o->b.txcap2=o->pmrChan->b.rxCapture=o->pmrChan->b.txCapture=0;
+               if (frxcapraw) { fclose(frxcapraw); frxcapraw = NULL; }
+               if (frxcaptrace) { fclose(frxcaptrace); frxcaptrace = NULL; }
+               if (frxoutraw) { fclose(frxoutraw); frxoutraw = NULL; }
+               if (ftxcapraw) { fclose(ftxcapraw); ftxcapraw = NULL; }
+               if (ftxcaptrace) { fclose(ftxcaptrace); ftxcaptrace = NULL; }
+               if (ftxoutraw) { fclose(ftxoutraw); ftxoutraw = NULL; }
+       }
+       else if (!strcasecmp(argv[2],"rxtracecap")) 
+       {
+               if (!frxcaptrace) frxcaptrace= fopen(RX_CAP_TRACE_FILE,"w");
+               ast_cli(fd,"Trace rx on.\n");
+               o->b.rxcap2=o->pmrChan->b.rxCapture=1;
+       }
+       else if (!strcasecmp(argv[2],"txtracecap")) 
+       {
+               if (!ftxcaptrace) ftxcaptrace= fopen(TX_CAP_TRACE_FILE,"w");
+               ast_cli(fd,"Trace tx on.\n");
+               o->b.txcap2=o->pmrChan->b.txCapture=1;
+       }
+       else if (!strcasecmp(argv[2],"rxcap")) 
+       {
+               if (!frxcapraw) frxcapraw = fopen(RX_CAP_RAW_FILE,"w");
+               ast_cli(fd,"cap rx raw on.\n");
+               o->b.rxcapraw=1;
+       }
+       else if (!strcasecmp(argv[2],"txcap")) 
+       {
+               if (!ftxcapraw) ftxcapraw = fopen(TX_CAP_RAW_FILE,"w");
+               ast_cli(fd,"cap tx raw on.\n");
+               o->b.txcapraw=1;
+       }
+       else if (!strcasecmp(argv[2],"save"))
+       {
+               tune_write(o);
+               ast_cli(fd,"Saved radio tuning settings to usbradio_tune.conf\n");
+       }
+       else return RESULT_SHOWUSAGE;
+       return RESULT_SUCCESS;
+}
+
+/*
+       set transmit ctcss modulation level
+       adjust mixer output or internal gain depending on output type
+       setting range is 0.0 to 0.9
+*/
+static int set_txctcss_level(struct chan_usbradio_pvt *o)
+{                                                        
+       if (o->txmixa == TX_OUT_LSD)
+       {
+               o->txmixaset=(151*o->txctcssadj) / 1000;
+               mixer_write(o);
+               mult_set(o);
+       }
+       else if (o->txmixb == TX_OUT_LSD)
+       {
+               o->txmixbset=(151*o->txctcssadj) / 1000;
+               mixer_write(o);
+               mult_set(o);
+       }
+       else
+       {
+               *o->pmrChan->ptxCtcssAdjust=(o->txctcssadj * M_Q8) / 1000;
+       }
+       return 0;
+}
+/*
+       CLI debugging on and off
+*/
+static int radio_set_debug(int fd, int argc, char *argv[])
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+       o->debuglevel=1;
+       ast_cli(fd,"usbradio debug on.\n");
+
+       return RESULT_SUCCESS;
+}
+
+static int radio_set_debug_off(int fd, int argc, char *argv[])
+{
+       struct chan_usbradio_pvt *o = find_desc(usbradio_active);
+
+       o->debuglevel=0;
+       ast_cli(fd,"usbradio debug off.\n");
+       return RESULT_SUCCESS;
+}
+
+static char key_usage[] =
+       "Usage: radio key\n"
+       "       Simulates COR active.\n";
+
+static char unkey_usage[] =
+       "Usage: radio unkey\n"
+       "       Simulates COR un-active.\n";
+
+/*
+radio tune 6 3000              measured tx value
+*/
+static char radio_tune_usage[] =
+       "Usage: radio tune <function>\n"
+       "       rxnoise\n"
+       "       rxvoice\n"
+       "       rxtone\n"
+       "       rxsquelch [newsetting]\n"
+       "       txvoice [newsetting]\n"
+       "       txtone [newsetting]\n"
+       "       auxvoice [newsetting]\n"
+       "       save (settings to tuning file)\n"
+       "\n       All [newsetting]'s are values 0-999\n\n";
+                                         
+static struct ast_cli_entry cli_usbradio[] = {
+       { { "radio", "key", NULL },
+       console_key, "Simulate Rx Signal Present",
+       key_usage, NULL, NULL},
+
+       { { "radio", "unkey", NULL },
+       console_unkey, "Simulate Rx Signal Lusb",
+       unkey_usage, NULL, NULL },
+
+       { { "radio", "tune", NULL },
+       radio_tune, "Radio Tune",
+       radio_tune_usage, NULL, NULL },
+
+       { { "radio", "set", "debug", NULL },
+       radio_set_debug, "Radio Debug",
+       radio_tune_usage, NULL, NULL },
+
+       { { "radio", "set", "debug", "off", NULL },
+       radio_set_debug_off, "Radio Debug",
+       radio_tune_usage, NULL, NULL },
+};
+
+/*
+ * store the callerid components
+ */
+#if 0
+static void store_callerid(struct chan_usbradio_pvt *o, char *s)
+{
+       ast_callerid_split(s, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
+}
+#endif
+
+static void store_rxdemod(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no")){
+               o->rxdemod = RX_AUDIO_NONE;
+       }
+       else if (!strcasecmp(s,"speaker")){
+               o->rxdemod = RX_AUDIO_SPEAKER;
+       }
+       else if (!strcasecmp(s,"flat")){
+                       o->rxdemod = RX_AUDIO_FLAT;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized rxdemod parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set rxdemod = %s\n", s);
+}
+
+                                          
+static void store_txmixa(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no")){
+               o->txmixa = TX_OUT_OFF;
+       }
+       else if (!strcasecmp(s,"voice")){
+               o->txmixa = TX_OUT_VOICE;
+       }
+       else if (!strcasecmp(s,"tone")){
+                       o->txmixa = TX_OUT_LSD;
+       }       
+       else if (!strcasecmp(s,"composite")){
+               o->txmixa = TX_OUT_COMPOSITE;
+       }       
+       else if (!strcasecmp(s,"auxvoice")){
+               o->txmixb = TX_OUT_AUX;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized txmixa parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set txmixa = %s\n", s);
+}
+
+static void store_txmixb(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no")){
+               o->txmixb = TX_OUT_OFF;
+       }
+       else if (!strcasecmp(s,"voice")){
+               o->txmixb = TX_OUT_VOICE;
+       }
+       else if (!strcasecmp(s,"tone")){
+                       o->txmixb = TX_OUT_LSD;
+       }       
+       else if (!strcasecmp(s,"composite")){
+               o->txmixb = TX_OUT_COMPOSITE;
+       }       
+       else if (!strcasecmp(s,"auxvoice")){
+               o->txmixb = TX_OUT_AUX;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized txmixb parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set txmixb = %s\n", s);
+}
+/*
+*/
+static void store_rxcdtype(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no")){
+               o->rxcdtype = CD_IGNORE;
+       }
+       else if (!strcasecmp(s,"usb")){
+               o->rxcdtype = CD_HID;
+       }
+       else if (!strcasecmp(s,"dsp")){
+               o->rxcdtype = CD_XPMR_NOISE;
+       }       
+       else if (!strcasecmp(s,"vox")){
+               o->rxcdtype = CD_XPMR_VOX;
+       }       
+       else if (!strcasecmp(s,"usbinvert")){
+               o->rxcdtype = CD_HID_INVERT;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized rxcdtype parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set rxcdtype = %s\n", s);
+}
+/*
+*/
+static void store_rxsdtype(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no") || !strcasecmp(s,"SD_IGNORE")){
+               o->rxsdtype = SD_IGNORE;
+       }
+       else if (!strcasecmp(s,"usb") || !strcasecmp(s,"SD_HID")){
+               o->rxsdtype = SD_HID;
+       }
+       else if (!strcasecmp(s,"usbinvert") || !strcasecmp(s,"SD_HID_INVERT")){
+               o->rxsdtype = SD_HID_INVERT;
+       }       
+       else if (!strcasecmp(s,"software") || !strcasecmp(s,"SD_XPMR")){
+               o->rxsdtype = SD_XPMR;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized rxsdtype parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set rxsdtype = %s\n", s);
+}
+/*
+*/
+static void store_rxgain(struct chan_usbradio_pvt *o, char *s)
+{
+       float f;
+       sscanf(s,"%f",&f);
+       o->rxgain = f;
+       //ast_log(LOG_WARNING, "set rxgain = %f\n", f);
+}
+/*
+*/
+static void store_rxvoiceadj(struct chan_usbradio_pvt *o, char *s)
+{
+       float f;
+       sscanf(s,"%f",&f);
+       o->rxvoiceadj = f;
+       //ast_log(LOG_WARNING, "set rxvoiceadj = %f\n", f);
+}
+/*
+*/
+static void store_rxctcssadj(struct chan_usbradio_pvt *o, char *s)
+{
+       float f;
+       sscanf(s,"%f",&f);
+       o->rxctcssadj = f;
+       //ast_log(LOG_WARNING, "set rxctcssadj = %f\n", f);
+}
+/*
+*/
+static void store_txtoctype(struct chan_usbradio_pvt *o, char *s)
+{
+       if (!strcasecmp(s,"no") || !strcasecmp(s,"TOC_NONE")){
+               o->txtoctype = TOC_NONE;
+       }
+       else if (!strcasecmp(s,"phase") || !strcasecmp(s,"TOC_PHASE")){
+               o->txtoctype = TOC_PHASE;
+       }
+       else if (!strcasecmp(s,"notone") || !strcasecmp(s,"TOC_NOTONE")){
+               o->txtoctype = TOC_NOTONE;
+       }       
+       else {
+               ast_log(LOG_WARNING,"Unrecognized txtoctype parameter: %s\n",s);
+       }
+
+       //ast_log(LOG_WARNING, "set txtoctype = %s\n", s);
+}
+/*
+*/
+static void store_rxctcssfreq(struct chan_usbradio_pvt *o, char *s)
+{
+       float f;
+       sscanf(s,"%f",&f);
+       o->rxctcssfreq = f;
+       //ast_log(LOG_WARNING, "set rxctcss = %f\n", f);
+}
+/*
+*/
+static void store_txctcssfreq(struct chan_usbradio_pvt *o, char *s)
+{
+       float f;
+       sscanf(s,"%f",&f);
+       o->txctcssfreq = f;
+       //ast_log(LOG_WARNING, "set txctcss = %f\n", f);
+}
+/*
+*/
+static void tune_txoutput(struct chan_usbradio_pvt *o, int value)
+{
+       o->txtestkey=1;
+       o->pmrChan->txPttIn=1;
+
+       // generate 1KHz tone at 7200 peak
+       //o->pmrChan->spsSigGen1->freq=10000;
+       //o->pmrChan->spsSigGen1->outputGain=(float)(0.22*M_Q8);
+       //o->pmrChan->b.startSpecialTone=1;
+
+       TxTestTone(o->pmrChan, 1);
+
+       usleep(5000000);
+       //o->pmrChan->b.stopSpecialTone=1;
+       usleep(100000);
+
+       TxTestTone(o->pmrChan, 0);
+
+       o->pmrChan->txPttIn=0;
+       o->txtestkey=0;
+}
+/*
+*/
+static void tune_rxinput(struct chan_usbradio_pvt *o)
+{
+       const int target=23000;
+       const int tolerance=2000;
+       const int settingmin=1;
+       const int settingstart=2;
+       const int maxtries=12;
+
+       float settingmax;
+       
+       int setting=0, tries=0, tmpdiscfactor, meas;
+       int tunetype=0;
+
+       settingmax = o->micmax;
+
+       if(o->pmrChan->rxDemod)tunetype=1;
+
+       setting = settingstart;
+
+       while(tries<maxtries)
+       {
+               setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL,setting,0);
+               setamixer(o->devicenum,MIXER_PARAM_MIC_BOOST,o->rxboostset,0);
+               usleep(100000);
+               if(o->rxcdtype==CD_XPMR_VOX || o->rxdemod==RX_AUDIO_SPEAKER)
+               {
+                       // printf("Measure Direct Input\n");
+                       o->pmrChan->spsMeasure->source = o->pmrChan->spsRx->source;
+                       o->pmrChan->spsMeasure->discfactor=1000;
+                       o->pmrChan->spsMeasure->enabled=1;
+                       o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+                       usleep(400000); 
+                       meas=o->pmrChan->spsMeasure->apeak;
+                       o->pmrChan->spsMeasure->enabled=0;      
+               }
+               else
+               {
+                       // printf("Measure HF Noise\n");
+                       tmpdiscfactor=o->pmrChan->spsRx->discfactor;
+                       o->pmrChan->spsRx->discfactor=(i16)1000;
+                       o->pmrChan->spsRx->discounteru=o->pmrChan->spsRx->discounterl=0;
+                       o->pmrChan->spsRx->amax=o->pmrChan->spsRx->amin=0;
+                       usleep(200000);
+                       meas=o->pmrChan->rxRssi;
+                       o->pmrChan->spsRx->discfactor=tmpdiscfactor;
+                       o->pmrChan->spsRx->discounteru=o->pmrChan->spsRx->discounterl=0;
+                       o->pmrChan->spsRx->amax=o->pmrChan->spsRx->amin=0;
+               }
+        if(!meas)meas++;
+               printf("tries=%d, setting=%d, meas=%i\n",tries,setting,meas);
+
+               if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){
+                       setting=setting*target/meas;
+               }
+               else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) )
+               {
+                       break;
+               }
+
+               if(setting<settingmin)setting=settingmin;
+               else if(setting>settingmax)setting=settingmax;
+
+               tries++;
+       }
+       printf("DONE tries=%d, setting=%d, meas=%i\n",tries,
+               (setting * 1000) / o->micmax,meas);
+       if( meas<(target-tolerance) || meas>(target+tolerance) ){
+               printf("ERROR: RX INPUT ADJUST FAILED.\n");
+       }else{
+               printf("INFO: RX INPUT ADJUST SUCCESS.\n");     
+               o->rxmixerset=(setting * 1000) / o->micmax;
+       }
+}
+/*
+*/
+static void tune_rxvoice(struct chan_usbradio_pvt *o)
+{
+       const int target=7200;                          // peak
+       const int tolerance=360;                        // peak to peak
+       const float settingmin=0.1;
+       const float settingmax=4;
+       const float settingstart=1;
+       const int maxtries=12;
+
+       float setting;
+
+       int tries=0, meas;
+
+       printf("INFO: RX VOICE ADJUST START.\n");       
+       printf("target=%i tolerance=%i \n",target,tolerance);
+
+       if(!o->pmrChan->spsMeasure)
+               printf("ERROR: NO MEASURE BLOCK.\n");
+
+       if(!o->pmrChan->spsMeasure->source || !o->pmrChan->prxVoiceAdjust )
+               printf("ERROR: NO SOURCE OR MEASURE SETTING.\n");
+
+       o->pmrChan->spsMeasure->source=o->pmrChan->spsRxOut->sink;
+       o->pmrChan->spsMeasure->enabled=1;
+       o->pmrChan->spsMeasure->discfactor=1000;
+       
+       setting=settingstart;
+
+       // printf("ERROR: NO MEASURE BLOCK.\n");
+
+       while(tries<maxtries)
+       {
+               *(o->pmrChan->prxVoiceAdjust)=setting*M_Q8;
+               usleep(10000);
+       o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+               usleep(1000000);
+               meas = o->pmrChan->spsMeasure->apeak;
+               printf("tries=%d, setting=%f, meas=%i\n",tries,setting,meas);
+
+               if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){
+                       setting=setting*target/meas;
+               }
+               else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) )
+               {
+                       break;
+               }
+               if(setting<settingmin)setting=settingmin;
+               else if(setting>settingmax)setting=settingmax;
+
+               tries++;
+       }
+
+       o->pmrChan->spsMeasure->enabled=0;
+
+       printf("DONE tries=%d, setting=%f, meas=%f\n",tries,setting,(float)meas);
+       if( meas<(target-tolerance) || meas>(target+tolerance) ){
+               printf("ERROR: RX VOICE GAIN ADJUST FAILED.\n");
+       }else{
+               printf("INFO: RX VOICE GAIN ADJUST SUCCESS.\n");
+               o->rxvoiceadj=setting;
+       }
+}
+/*
+*/
+static void tune_rxctcss(struct chan_usbradio_pvt *o)
+{
+       const int target=4096;
+       const int tolerance=100;
+       const float settingmin=0.1;
+       const float settingmax=4;
+       const float settingstart=1;
+       const int maxtries=12;
+
+       float setting;
+       int tries=0, meas;
+
+       printf("INFO: RX CTCSS ADJUST START.\n");       
+       printf("target=%i tolerance=%i \n",target,tolerance);
+
+       o->pmrChan->spsMeasure->source=o->pmrChan->prxCtcssMeasure;
+       o->pmrChan->spsMeasure->discfactor=400;
+       o->pmrChan->spsMeasure->enabled=1;
+
+       setting=settingstart;
+
+       while(tries<maxtries)
+       {
+               *(o->pmrChan->prxCtcssAdjust)=setting*M_Q8;
+               usleep(10000);
+       o->pmrChan->spsMeasure->amax = o->pmrChan->spsMeasure->amin = 0;
+               usleep(500000);
+               meas = o->pmrChan->spsMeasure->apeak;
+               printf("tries=%d, setting=%f, meas=%i\n",tries,setting,meas);
+
+               if( meas<(target-tolerance) || meas>(target+tolerance) || tries<3){
+                       setting=setting*target/meas;
+               }
+               else if(tries>4 && meas>(target-tolerance) && meas<(target+tolerance) )
+               {
+                       break;
+               }
+               if(setting<settingmin)setting=settingmin;
+               else if(setting>settingmax)setting=settingmax;
+
+               tries++;
+       }
+       o->pmrChan->spsMeasure->enabled=0;
+       printf("DONE tries=%d, setting=%f, meas=%f\n",tries,setting,(float)meas);
+       if( meas<(target-tolerance) || meas>(target+tolerance) ){
+               printf("ERROR: RX CTCSS GAIN ADJUST FAILED.\n");
+       }else{
+               printf("INFO: RX CTCSS GAIN ADJUST SUCCESS.\n");
+               o->rxctcssadj=setting;
+       }
+}
+/*
+       this file then is included in chan_usbradio.conf
+       #include /etc/asterisk/usbradio_tune.conf 
+*/
+static void tune_write(struct chan_usbradio_pvt *o)
+{
+       FILE *fp;
+       
+       fp=fopen("/etc/asterisk/usbradio_tune.conf","w");
+       if (!strcmp(o->name,"dsp"))
+               fprintf(fp,"[general]\n");
+       else
+               fprintf(fp,"[%s]\n",o->name);
+
+       fprintf(fp,"; name=%s\n",o->name);
+       fprintf(fp,"; devicenum=%i\n",o->devicenum);
+
+       fprintf(fp,"rxmixerset=%i\n",o->rxmixerset);
+       fprintf(fp,"rxboostset=%i\n",o->rxboostset);
+       fprintf(fp,"txmixaset=%i\n",o->txmixaset);
+       fprintf(fp,"txmixbset=%i\n",o->txmixbset);
+
+       fprintf(fp,"rxvoiceadj=%f\n",o->rxvoiceadj);
+       fprintf(fp,"rxctcssadj=%f\n",o->rxctcssadj);
+       fprintf(fp,"txctcssadj=%i\n",o->txctcssadj);
+
+       fprintf(fp,"rxsquelchadj=%i\n",o->rxsquelchadj);
+       fclose(fp);
+}
+//
+static void mixer_write(struct chan_usbradio_pvt *o)
+{
+       setamixer(o->devicenum,MIXER_PARAM_MIC_PLAYBACK_SW,0,0);
+       setamixer(o->devicenum,MIXER_PARAM_MIC_PLAYBACK_VOL,0,0);
+       setamixer(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_SW,1,0);
+       setamixer(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_VOL,
+               o->txmixaset * o->spkrmax / 1000,
+               o->txmixbset * o->spkrmax / 1000);
+       setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL,
+               o->rxmixerset * o->micmax / 1000,0);
+       setamixer(o->devicenum,MIXER_PARAM_MIC_BOOST,o->rxboostset,0);
+       setamixer(o->devicenum,MIXER_PARAM_MIC_CAPTURE_SW,1,0);
+}
+/*
+       adjust dsp multiplier to add resolution to tx level adjustment
+*/
+static void mult_set(struct chan_usbradio_pvt *o)
+{
+
+       if(o->pmrChan->spsTxOutA) {
+               o->pmrChan->spsTxOutA->outputGain = 
+                       mult_calc((o->txmixaset * 152) / 1000);
+       }
+       if(o->pmrChan->spsTxOutB){
+               o->pmrChan->spsTxOutB->outputGain = 
+                       mult_calc((o->txmixbset * 152) / 1000);
+       }
+}
+//
+// input 0 - 151 outputs are pot and multiplier
+//
+static int mult_calc(int value)
+{
+       const int multx=M_Q8;
+       int pot,mult;
+
+       pot=((int)(value/4)*4)+2;
+       mult = multx-( ( multx * (3-(value%4)) ) / (pot+2) );
+       return(mult);
+}
+
+#define pd(x) {printf(#x" = %d\n",x);}
+#define pp(x) {printf(#x" = %p\n",x);}
+#define ps(x) {printf(#x" = %s\n",x);}
+#define pf(x) {printf(#x" = %f\n",x);}
+/*
+*/
+static void pmrdump(struct chan_usbradio_pvt *o)
+{
+       t_pmr_chan *p;
+
+       p=o->pmrChan;
+
+       printf("\nodump()\n");
+
+       pd(o->devicenum);
+
+       pd(o->rxdemod);
+       pd(o->rxcdtype);
+       pd(o->rxsdtype);
+       pd(o->txtoctype);
+
+       pd(o->rxmixerset);
+       pf(o->rxvoiceadj);
+       pf(o->rxctcssadj);
+       pd(o->rxsquelchadj);
+        
+       pd(o->txprelim);
+       pd(o->txmixa);
+       pd(o->txmixb);
+       
+       pd(o->txmixaset);
+       pd(o->txmixbset);
+       
+       printf("\npmrdump()\n");
+       printf("prxSquelchAdjust=%i\n",*(o->pmrChan->prxSquelchAdjust));
+
+       pd(p->rxCarrierPoint);
+       pd(p->rxCarrierHyst);
+
+       pd(p->rxCtcss->relax);
+       pf(p->rxCtcssFreq);     
+       pd(p->rxCtcssIndex);
+       pf(p->txCtcssFreq);
+
+       pd(p->txMixA);
+       pd(p->txMixB);
+    
+       pd(p->rxDeEmpEnable);
+       pd(p->rxCenterSlicerEnable);
+       pd(p->rxCtcssDecodeEnable);
+       pd(p->rxDcsDecodeEnable);
+
+       pd(p->txHpfEnable);
+       pd(p->txLimiterEnable);
+       pd(p->txPreEmpEnable);
+       pd(p->txLpfEnable);
+
+       if(p->spsTxOutA)pd(p->spsTxOutA->outputGain);
+       if(p->spsTxOutB)pd(p->spsTxOutB->outputGain);
+
+       return;
+}
+
+
+/*
+ * grab fields from the config file, init the descriptor and open the device.
+ */
+static struct chan_usbradio_pvt *store_config(struct ast_config *cfg, char *ctg)
+{
+       struct ast_variable *v;
+       struct chan_usbradio_pvt *o;
+       struct ast_config *cfg1;
+
+       if (ctg == NULL) {
+               traceusb1((" store_config() ctg == NULL\n"));
+               o = &usbradio_default;
+               ctg = "general";
+       } else {
+               if (!(o = ast_calloc(1, sizeof(*o)))){
+                       return NULL;
+               }
+               *o = usbradio_default;
+               /* "general" is also the default thing */
+               if (strcmp(ctg, "general") == 0) {
+                       o->name = ast_strdup("dsp");
+                       usbradio_active = o->name;
+               }
+               else o->name = ast_strdup(ctg);
+       }
+
+       strcpy(o->mohinterpret, "default");
+       o->micmax = amixer_max(o->devicenum,MIXER_PARAM_MIC_CAPTURE_VOL);
+       o->spkrmax = amixer_max(o->devicenum,MIXER_PARAM_SPKR_PLAYBACK_VOL);
+       /* fill other fields from configuration */
+       for (v = ast_variable_browse(cfg, ctg); v; v = v->next) {
+               M_START(v->name, v->value);
+
+               /* handle jb conf */
+               if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+                       continue;
+
+#if    0
+                       M_BOOL("autoanswer", o->autoanswer)
+                       M_BOOL("autohangup", o->autohangup)
+                       M_BOOL("overridecontext", o->overridecontext)
+                       M_STR("context", o->ctx)
+                       M_STR("language", o->language)
+                       M_STR("mohinterpret", o->mohinterpret)
+                       M_STR("extension", o->ext)
+                       M_F("callerid", store_callerid(o, v->value))
+#endif
+                       M_UINT("frags", o->frags)
+                       M_UINT("queuesize", o->queuesize)
+                       M_UINT("devicenum", o->devicenum)
+                       M_UINT("debug", usbradio_debug)
+                       M_BOOL("rxcpusaver",o->rxcpusaver)
+                       M_BOOL("txcpusaver",o->txcpusaver)
+                       M_BOOL("invertptt",o->invertptt)
+                       M_F("rxdemod",store_rxdemod(o,v->value))
+                       M_BOOL("txprelim",o->txprelim);
+                       M_F("txmixa",store_txmixa(o,v->value))
+                       M_F("txmixb",store_txmixb(o,v->value))
+                       M_F("carrierfrom",store_rxcdtype(o,v->value))
+                       M_F("rxsdtype",store_rxsdtype(o,v->value))
+                       M_F("rxctcssfreq",store_rxctcssfreq(o,v->value))
+                       M_F("txctcssfreq",store_txctcssfreq(o,v->value))
+                       M_F("rxgain",store_rxgain(o,v->value))
+                       M_BOOL("rxboostset", o->rxboostset)
+                       M_UINT("rxctcssrelax", o->rxctcssrelax)
+                       M_F("txtoctype",store_txtoctype(o,v->value))
+                       M_UINT("hdwtype", o->hdwtype)
+                       M_UINT("duplex", o->radioduplex)
+                       M_END(;
+                       );
+       }
+       
+       cfg1 = ast_config_load(config1);
+       if (!cfg1)
+       {
+               o->rxmixerset = 500;
+               o->txmixaset = 500;
+               o->txmixbset = 500;
+               o->rxvoiceadj = 0.5;
+               o->rxctcssadj = 0.5;
+               o->txctcssadj = 200;
+               o->rxsquelchadj = 500;
+               ast_log(LOG_WARNING,"File %s not found, using default parameters.\n",config1);
+       } else  {
+               for (v = ast_variable_browse(cfg1, ctg); v; v = v->next) {
+       
+                       M_START(v->name, v->value);
+                       M_UINT("rxmixerset", o->rxmixerset)
+                       M_UINT("txmixaset", o->txmixaset)
+                       M_UINT("txmixbset", o->txmixbset)
+                       M_F("rxvoiceadj",store_rxvoiceadj(o,v->value))
+                       M_F("rxctcssadj",store_rxctcssadj(o,v->value))
+                       M_UINT("txctcssadj",o->txctcssadj);
+                       M_UINT("rxsquelchadj", o->rxsquelchadj)
+                       M_END(;
+                       );
+               }
+               ast_config_destroy(cfg1);
+       }
+
+       o->debuglevel=0;
+
+       if (o == &usbradio_default)             /* we are done with the default */
+               return NULL;
+
+       o->lastopen = ast_tvnow();      /* don't leave it 0 or tvdiff may wrap */
+       o->dsp = ast_dsp_new();
+         if (o->dsp)
+         {
+           ast_dsp_set_features(o->dsp,DSP_FEATURE_DTMF_DETECT);
+           ast_dsp_digitmode(o->dsp,DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_RELAXDTMF);
+         }
+
+       if(o->rxctcssfreq!=0 && o->rxdemod==RX_AUDIO_SPEAKER)
+       {
+               ast_log(LOG_ERROR, "Incompatable Options  o->rxctcssfreq=%f and o->rxdemod=speaker\n", o->rxctcssfreq); 
+       }
+
+       if(o->pmrChan==NULL)
+       {
+               t_pmr_chan      tChan;
+
+               memset(&tChan,0,sizeof(t_pmr_chan));
+
+               tChan.rxDemod=o->rxdemod;
+               tChan.rxCdType=o->rxcdtype;
+
+               tChan.txMod=o->txprelim;
+
+               tChan.txMixA = o->txmixa;
+               tChan.txMixB = o->txmixb;
+
+               tChan.rxCpuSaver=o->rxcpusaver;
+               tChan.txCpuSaver=o->txcpusaver;
+
+               tChan.rxCtcssFreq=o->rxctcssfreq;
+               tChan.txCtcssFreq=o->txctcssfreq;
+
+               o->pmrChan=createPmrChannel(&tChan,FRAME_SIZE);
+
+               o->pmrChan->radioDuplex=o->radioduplex;
+
+               o->pmrChan->rxCpuSaver=o->rxcpusaver;
+               o->pmrChan->txCpuSaver=o->txcpusaver;
+
+               *(o->pmrChan->prxSquelchAdjust) = 
+                       ((999 - o->rxsquelchadj) * 32767) / 1000;
+
+               o->pmrChan->spsRx->outputGain = o->rxvoiceadj*M_Q8;
+
+               o->pmrChan->txTocType = o->txtoctype;
+
+        if ((o->txmixa ==  TX_OUT_LSD) ||
+                (o->txmixa == TX_OUT_COMPOSITE) ||
+                (o->txmixb ==  TX_OUT_LSD) ||
+                (o->txmixb == TX_OUT_COMPOSITE))
+        {
+                *(o->pmrChan->prxCtcssAdjust)=o->rxctcssadj*M_Q8;
+                set_txctcss_level(o);
+        }
+
+        o->pmrChan->rxCtcss->relax=o->rxctcssrelax;
+
+       }
+
+       if( (o->txmixa!=TX_OUT_VOICE) && (o->txmixb!=TX_OUT_VOICE) &&
+               (o->txmixa!=TX_OUT_COMPOSITE) && (o->txmixb!=TX_OUT_COMPOSITE)
+         )
+       {
+               ast_log(LOG_ERROR,"No txvoice output configured.\n");
+       }
+
+       if( o->txctcssfreq && 
+           o->txmixa!=TX_OUT_LSD && o->txmixa!=TX_OUT_COMPOSITE  &&
+               o->txmixb!=TX_OUT_LSD && o->txmixb!=TX_OUT_COMPOSITE
+         )
+       {
+               ast_log(LOG_ERROR,"No txtone output configured.\n");
+       }
+
+       if( o->rxctcssfreq && o->pmrChan->rxCtcssIndex<0 )
+       {
+               ast_log(LOG_ERROR,"Invalid CTCSS Frequency.\n");
+       }
+
+       // RxTestIt(o);
+
+       mixer_write(o);
+       mult_set(o);    
+       hidhdwconfig(o);
+
+       // pmrdump(o);
+
+       if (pipe(o->sndcmd) != 0) {
+               ast_log(LOG_ERROR, "Unable to create pipe\n");
+               goto error;
+       }
+
+       printf("creating sound thread\n");
+       ast_pthread_create_background(&o->sthread, NULL, sound_thread, o);
+
+       /* link into list of devices */
+       if (o != &usbradio_default) {
+               o->next = usbradio_default.next;
+               usbradio_default.next = o;
+       }
+       return o;
+  
+  error:
+       if (o != &usbradio_default)
+               free(o);
+       return NULL;
+}
+
+#if    DEBUG_FILETEST == 1
+/*
+       Test It on a File
+*/
+int RxTestIt(struct chan_usbradio_pvt *o)
+{
+       const int numSamples = SAMPLES_PER_BLOCK;
+       const int numChannels = 16;
+
+       i16 sample,i,ii;
+       
+       i32 txHangTime;
+
+       i16 txEnable;
+
+       t_pmr_chan      tChan;
+       t_pmr_chan *pChan;
+
+       FILE *hInput=NULL, *hOutput=NULL, *hOutputTx=NULL;
+       i16 iBuff[numSamples*2*6], oBuff[numSamples];
+                                 
+       printf("RxTestIt()\n");
+
+       pChan=o->pmrChan;
+       pChan->b.txCapture=1;
+       pChan->b.rxCapture=1;
+
+       txEnable = 0;
+
+       hInput  = fopen("/usr/src/xpmr/testdata/rx_in.pcm","r");
+       if(!hInput){
+               printf(" RxTestIt() File Not Found.\n");
+               return 0;
+       }
+       hOutput = fopen("/usr/src/xpmr/testdata/rx_debug.pcm","w");
+
+       printf(" RxTestIt() Working...\n");
+                               
+       while(!feof(hInput))
+       {
+               fread((void *)iBuff,2,numSamples*2*6,hInput);
+                
+               if(txHangTime)txHangTime-=numSamples;
+               if(txHangTime<0)txHangTime=0;
+               
+               if(pChan->rxCtcss->decode)txHangTime=(8000/1000*2000);
+
+               if(pChan->rxCtcss->decode && !txEnable)
+               {
+                       txEnable=1;
+                       //pChan->inputBlanking=(8000/1000*200);
+               }
+               else if(!pChan->rxCtcss->decode && txEnable)
+               {
+                       txEnable=0;     
+               }
+
+               PmrRx(pChan,iBuff,oBuff);
+
+               fwrite((void *)pChan->prxDebug,2,numSamples*numChannels,hOutput);
+       }
+       pChan->b.txCapture=0;
+       pChan->b.rxCapture=0;
+
+       if(hInput)fclose(hInput);
+       if(hOutput)fclose(hOutput);
+
+       printf(" RxTestIt() Complete.\n");
+
+       return 0;
+}
+#endif
+
+#include "./xpmr/xpmr.c"
+/*
+*/
+static int load_module(void)
+{
+       struct ast_config *cfg = NULL;
+       char *ctg = NULL;
+
+       /* Copy the default jb config over global_jbconf */
+       memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+       /* load config file */
+       if (!(cfg = ast_config_load(config))) {
+               ast_log(LOG_NOTICE, "Unable to load config %s\n", config);
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       do {
+               store_config(cfg, ctg);
+       } while ( (ctg = ast_category_browse(cfg, ctg)) != NULL);
+
+       ast_config_destroy(cfg);
+
+       if (find_desc(usbradio_active) == NULL) {
+               ast_log(LOG_NOTICE, "Device %s not found\n", usbradio_active);
+               /* XXX we could default to 'dsp' perhaps ? */
+               /* XXX should cleanup allocated memory etc. */
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       if (ast_channel_register(&usbradio_tech)) {
+               ast_log(LOG_ERROR, "Unable to register channel type 'usb'\n");
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       ast_cli_register_multiple(cli_usbradio, sizeof(cli_usbradio) / sizeof(struct ast_cli_entry));
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+/*
+*/
+static int unload_module(void)
+{
+       struct chan_usbradio_pvt *o;
+
+       ast_log(LOG_WARNING, "unload_module() called\n");
+
+       ast_channel_unregister(&usbradio_tech);
+       ast_cli_unregister_multiple(cli_usbradio, sizeof(cli_usbradio) / sizeof(struct ast_cli_entry));
+
+       for (o = usbradio_default.next; o; o = o->next) {
+
+               ast_log(LOG_WARNING, "destroyPmrChannel() called\n");
+               if(o->pmrChan)destroyPmrChannel(o->pmrChan);
+               
+               #if DEBUG_CAPTURES == 1
+               if (frxcapraw) { fclose(frxcapraw); frxcapraw = NULL; }
+               if (frxcaptrace) { fclose(frxcaptrace); frxcaptrace = NULL; }
+               if (frxoutraw) { fclose(frxoutraw); frxoutraw = NULL; }
+               if (ftxcapraw) { fclose(ftxcapraw); ftxcapraw = NULL; }
+               if (ftxcaptrace) { fclose(ftxcaptrace); ftxcaptrace = NULL; }
+               if (ftxoutraw) { fclose(ftxoutraw); ftxoutraw = NULL; }
+               #endif
+
+               close(o->sounddev);
+               if (o->sndcmd[0] > 0) {
+                       close(o->sndcmd[0]);
+                       close(o->sndcmd[1]);
+               }
+               if (o->dsp) ast_dsp_free(o->dsp);
+               if (o->owner)
+                       ast_softhangup(o->owner, AST_SOFTHANGUP_APPUNLOAD);
+               if (o->owner)                   /* XXX how ??? */
+                       return -1;
+               /* XXX what about the thread ? */
+               /* XXX what about the memory allocated ? */
+       }
+       return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "usb Console Channel Driver");
+
+/*     end of file */
+
+
diff --git a/channels/xpmr/LICENSE b/channels/xpmr/LICENSE
new file mode 100755 (executable)
index 0000000..a52b16e
--- /dev/null
@@ -0,0 +1,341 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/channels/xpmr/sinetabx.h b/channels/xpmr/sinetabx.h
new file mode 100755 (executable)
index 0000000..1ceb659
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * sinetabx.h - for Xelatec Private Mobile Radio Processes
+ * 
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ * 
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.com
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *              
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_SINETABX_H
+#define XPMR_SINETABX_H 1
+               
+#define SAMPLES_PER_SINE 256           
+               
+const i16 sinetablex[]={               
+0,                     // 0
+804,           // 1
+1608,          // 2
+2410,          // 3
+3212,          // 4
+4011,          // 5
+4808,          // 6
+5602,          // 7
+6393,          // 8
+7179,          // 9
+7962,          // 10
+8739,          // 11
+9512,          // 12
+10278,         // 13
+11039,         // 14
+11793,         // 15
+12539,         // 16
+13279,         // 17
+14010,         // 18
+14732,         // 19
+15446,         // 20
+16151,         // 21
+16846,         // 22
+17530,         // 23
+18204,         // 24
+18868,         // 25
+19519,         // 26
+20159,         // 27
+20787,         // 28
+21403,         // 29
+22005,         // 30
+22594,         // 31
+23170,         // 32
+23731,         // 33
+24279,         // 34
+24811,         // 35
+25329,         // 36
+25832,         // 37
+26319,         // 38
+26790,         // 39
+27245,         // 40
+27683,         // 41
+28105,         // 42
+28510,         // 43
+28898,         // 44
+29268,         // 45
+29621,         // 46
+29956,         // 47
+30273,         // 48
+30571,         // 49
+30852,         // 50
+31113,         // 51
+31356,         // 52
+31580,         // 53
+31785,         // 54
+31971,         // 55
+32137,         // 56
+32285,         // 57
+32412,         // 58
+32521,         // 59
+32609,         // 60
+32678,         // 61
+32728,         // 62
+32757,         // 63
+32767,         // 64
+32757,         // 65
+32728,         // 66
+32678,         // 67
+32609,         // 68
+32521,         // 69
+32412,         // 70
+32285,         // 71
+32137,         // 72
+31971,         // 73
+31785,         // 74
+31580,         // 75
+31356,         // 76
+31113,         // 77
+30852,         // 78
+30571,         // 79
+30273,         // 80
+29956,         // 81
+29621,         // 82
+29268,         // 83
+28898,         // 84
+28510,         // 85
+28105,         // 86
+27683,         // 87
+27245,         // 88
+26790,         // 89
+26319,         // 90
+25832,         // 91
+25329,         // 92
+24811,         // 93
+24279,         // 94
+23731,         // 95
+23170,         // 96
+22594,         // 97
+22005,         // 98
+21403,         // 99
+20787,         // 100
+20159,         // 101
+19519,         // 102
+18868,         // 103
+18204,         // 104
+17530,         // 105
+16846,         // 106
+16151,         // 107
+15446,         // 108
+14732,         // 109
+14010,         // 110
+13279,         // 111
+12539,         // 112
+11793,         // 113
+11039,         // 114
+10278,         // 115
+9512,          // 116
+8739,          // 117
+7962,          // 118
+7179,          // 119
+6393,          // 120
+5602,          // 121
+4808,          // 122
+4011,          // 123
+3212,          // 124
+2410,          // 125
+1608,          // 126
+804,           // 127
+0,                     // 128
+-804,          // 129
+-1608,         // 130
+-2410,         // 131
+-3212,         // 132
+-4011,         // 133
+-4808,         // 134
+-5602,         // 135
+-6393,         // 136
+-7179,         // 137
+-7962,         // 138
+-8739,         // 139
+-9512,         // 140
+-10278,                // 141
+-11039,                // 142
+-11793,                // 143
+-12539,                // 144
+-13279,                // 145
+-14010,                // 146
+-14732,                // 147
+-15446,                // 148
+-16151,                // 149
+-16846,                // 150
+-17530,                // 151
+-18204,                // 152
+-18868,                // 153
+-19519,                // 154
+-20159,                // 155
+-20787,                // 156
+-21403,                // 157
+-22005,                // 158
+-22594,                // 159
+-23170,                // 160
+-23731,                // 161
+-24279,                // 162
+-24811,                // 163
+-25329,                // 164
+-25832,                // 165
+-26319,                // 166
+-26790,                // 167
+-27245,                // 168
+-27683,                // 169
+-28105,                // 170
+-28510,                // 171
+-28898,                // 172
+-29268,                // 173
+-29621,                // 174
+-29956,                // 175
+-30273,                // 176
+-30571,                // 177
+-30852,                // 178
+-31113,                // 179
+-31356,                // 180
+-31580,                // 181
+-31785,                // 182
+-31971,                // 183
+-32137,                // 184
+-32285,                // 185
+-32412,                // 186
+-32521,                // 187
+-32609,                // 188
+-32678,                // 189
+-32728,                // 190
+-32757,                // 191
+-32767,                // 192
+-32757,                // 193
+-32728,                // 194
+-32678,                // 195
+-32609,                // 196
+-32521,                // 197
+-32412,                // 198
+-32285,                // 199
+-32137,                // 200
+-31971,                // 201
+-31785,                // 202
+-31580,                // 203
+-31356,                // 204
+-31113,                // 205
+-30852,                // 206
+-30571,                // 207
+-30273,                // 208
+-29956,                // 209
+-29621,                // 210
+-29268,                // 211
+-28898,                // 212
+-28510,                // 213
+-28105,                // 214
+-27683,                // 215
+-27245,                // 216
+-26790,                // 217
+-26319,                // 218
+-25832,                // 219
+-25329,                // 220
+-24811,                // 221
+-24279,                // 222
+-23731,                // 223
+-23170,                // 224
+-22594,                // 225
+-22005,                // 226
+-21403,                // 227
+-20787,                // 228
+-20159,                // 229
+-19519,                // 230
+-18868,                // 231
+-18204,                // 232
+-17530,                // 233
+-16846,                // 234
+-16151,                // 235
+-15446,                // 236
+-14732,                // 237
+-14010,                // 238
+-13279,                // 239
+-12539,                // 240
+-11793,                // 241
+-11039,                // 242
+-10278,                // 243
+-9512,         // 244
+-8739,         // 245
+-7962,         // 246
+-7179,         // 247
+-6393,         // 248
+-5602,         // 249
+-4808,         // 250
+-4011,         // 251
+-3212,         // 252
+-2410,         // 253
+-1608,         // 254
+-804,          // 255
+};
+
+#endif /* !XPMR_SINETABX_H */
diff --git a/channels/xpmr/xpmr.c b/channels/xpmr/xpmr.c
new file mode 100755 (executable)
index 0000000..c67e408
--- /dev/null
@@ -0,0 +1,2266 @@
+/*
+ * xpmr.c - Xelatec Private Mobile Radio Processes
+ * 
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ * 
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.com
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *              
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+/*
+       FYI     = For Your Information
+       PMR     = Private Mobile Radio
+       RX      = Receive
+       TX      = Transmit
+       CTCSS   = Continuous Tone Coded Squelch System
+       TONE    = Same as above.
+       LSD     = Low Speed Data, subaudible signaling. May be tones or codes.
+       VOX     = Voice Operated Transmit
+       DSP     = Digital Signal Processing
+       LPF     = Low Pass Filter
+       FIR     = Finite Impulse Response (Filter)
+       IIR     = Infinite Impulse Response (Filter)
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "xpmr.h"
+#include "xpmr_coef.h"
+#include "sinetabx.h"
+
+static i16 pmrChanIndex=0;                             // count of created pmr instances
+
+/*
+       Convert a Frequency in Hz to a zero based CTCSS Table index
+*/
+i16 CtcssFreqIndex(float freq)
+{
+       i16 i,hit=-1;
+
+       for(i=0;i<CTCSS_NUM_CODES;i++){
+               if(freq==freq_ctcss[i])hit=i;
+       }
+       return hit;
+}
+/*
+       pmr_rx_frontend 
+       Takes a block of data and low pass filters it.
+       Determines the amplitude of high frequency noise for carrier detect.
+       Decimates input data to change the rate.
+*/
+i16 pmr_rx_frontend(t_pmr_sps *mySps) 
+{
+       #define DCgainBpfNoise  65536
+       i16 samples,iOutput, *input, *output, *noutput;
+       i16 *x, *coef, *coef2;
+    i32 i, naccum, outputGain, calcAdjust;
+       i64 y;
+       i16 nx, hyst, setpt, compOut;
+       i16 amax, amin, apeak, discounteru, discounterl, discfactor;
+       i16 decimator, decimate, doNoise;
+
+       TRACEX(("pmr_rx_frontend()\n"));
+
+       if(!mySps->enabled)return(1);
+
+       decimator = mySps->decimator;
+       decimate = mySps->decimate;
+
+       input     = mySps->source;
+       output    = mySps->sink;
+       noutput   = mySps->parentChan->pRxNoise;
+
+       nx        = mySps->nx;
+       coef      = mySps->coef;
+       coef2     = mySps->coef2;
+
+       calcAdjust = mySps->calcAdjust;
+       outputGain = mySps->outputGain;
+
+       amax=mySps->amax;
+       amin=mySps->amin;
+       apeak=mySps->apeak;
+       discounteru=mySps->discounteru;
+       discounterl=mySps->discounterl;
+       discfactor=mySps->discfactor;
+       setpt=mySps->setpt;
+       hyst=mySps->hyst;
+       compOut=mySps->compOut;
+
+       samples=mySps->nSamples*decimate;
+       x=mySps->x;             
+       iOutput=0;
+
+       if(mySps->parentChan->rxCdType!=CD_XPMR_VOX)doNoise=1;
+       else doNoise=0;
+                       
+       for(i=0;i<samples;i++)
+       {
+               i16 n;
+
+               //shift the old samples
+           for(n=nx-1; n>0; n--)
+              x[n] = x[n-1];
+
+           x[0] = input[i*2];
+       
+               --decimator;
+
+               if(decimator<=0)
+               {
+                       decimator=decimate;
+                       
+                   y=0; 
+                   for(n=0; n<nx; n++)
+                       y += coef[n] * x[n];
+
+                   y=((y/calcAdjust)*outputGain)/M_Q8;
+
+                       if(y>32767)y=32767;
+                       else if(y<-32767)y=-32767;
+
+                   output[iOutput]=y;                                  // Rx Baseband decimated
+                       noutput[iOutput++] = apeak;                     // Rx Noise
+               }
+
+               if(doNoise)
+               {
+                       // calculate noise output
+                       naccum=0;
+                   for(n=0; n<nx; n++)
+                       naccum += coef_fir_bpf_noise_1[n] * x[n];
+                   
+                   naccum /= DCgainBpfNoise;
+       
+                       if(naccum>amax)
+                       {
+                               amax=naccum;
+                               discounteru=discfactor;
+                       }
+                       else if(--discounteru<=0)
+                       {
+                               discounteru=discfactor;
+                               amax=(i32)((amax*32700)/32768);
+                       }
+
+                       if(naccum<amin)
+                       {
+                               amin=naccum;
+                               discounterl=discfactor;
+                       }
+                       else if(--discounterl<=0)
+                       {
+                               discounterl=discfactor;
+                               amin=(i32)((amin*32700)/32768);
+                       }
+               
+                       apeak=(amax-amin)/2;
+                       
+               }  // if doNoise
+       }
+
+       if(doNoise)
+       {
+               ((t_pmr_chan *)(mySps->parentChan))->rxRssi=apeak;
+               
+               if(apeak>setpt || (compOut&&(apeak>(setpt-hyst)))) compOut=1;
+               else compOut=0;
+               mySps->compOut=compOut;
+               mySps->amax=amax;
+               mySps->amin=amin;                               
+               mySps->apeak=apeak;
+               mySps->discounteru=discounteru;
+               mySps->discounterl=discounterl; 
+       }
+
+       return 0;
+}
+/*             
+       pmr general purpose fir
+       works on a block of samples
+*/
+i16 pmr_gp_fir(t_pmr_sps *mySps) 
+{
+       i32 nsamples,inputGain,outputGain,calcAdjust;
+       i16 *input, *output;
+       i16 *x, *coef;
+    i32 i, ii;
+       i16 nx, hyst, setpt, compOut;
+       i16 amax, amin, apeak=0, discounteru=0, discounterl=0, discfactor;
+       i16 decimator, decimate, interpolate;
+       i16 numChanOut, selChanOut, mixOut, monoOut;
+
+       TRACEX(("pmr_gp_fir() %i\n",mySps->enabled));
+
+       if(!mySps->enabled)return(1);
+
+       inputGain  = mySps->inputGain;
+       calcAdjust = mySps->calcAdjust;
+       outputGain = mySps->outputGain;
+
+       input      = mySps->source;
+       output     = mySps->sink;
+       x          = mySps->x;
+       nx         = mySps->nx;
+       coef       = mySps->coef;
+       
+       decimator   = mySps->decimator;
+       decimate        = mySps->decimate;
+       interpolate = mySps->interpolate;
+
+       setpt      = mySps->setpt;
+       compOut    = mySps->compOut;
+
+       inputGain  = mySps->inputGain;
+       outputGain = mySps->outputGain;
+       numChanOut = mySps->numChanOut;
+       selChanOut = mySps->selChanOut;
+       mixOut     = mySps->mixOut;
+       monoOut    = mySps->monoOut;
+
+       amax=mySps->amax;
+       amin=mySps->amin;                               
+
+       discfactor=mySps->discfactor;
+       hyst=mySps->hyst;                               
+       setpt=mySps->setpt;     
+       nsamples=mySps->nSamples;
+
+       if(mySps->option==3)
+       {
+               mySps->option=0;
+               mySps->enabled=0;
+               for(i=0;i<nsamples;i++)
+               {
+                       if(monoOut)
+                               output[(i*2)]=output[(i*2)+1]=0;
+                       else
+                               output[(i*numChanOut)+selChanOut]=0;
+               }
+               return 0;
+       }
+
+       ii=0;
+       for(i=0;i<nsamples;i++)
+       {
+               int ix;
+
+               int64_t y=0;
+               
+               if(decimate<0)
+               {
+                       decimator=decimate;     
+               }
+
+               for(ix=0;ix<interpolate;ix++)
+               {
+                       i16 n; 
+                       y=0;
+       
+                   for(n=nx-1; n>0; n--)
+                      x[n] = x[n-1];
+                   x[0] = (input[i]*inputGain)/M_Q8;
+               
+                       #if 0
+                       --decimator;
+                       if(decimator<=0)
+                       {
+                               decimator=decimate;
+                           for(n=0; n<nx; n++)
+                               y += coef[n] * x[n];
+                               y /= (outputGain*3);  
+                               output[ii++]=y;
+                       }
+                       #else
+                   for(n=0; n<nx; n++)
+                       y += coef[n] * x[n];
+
+                       y=((y/calcAdjust)*outputGain)/M_Q8;
+                       
+                       if(mixOut){
+                               if(monoOut){
+                                       output[(ii*2)]=output[(ii*2)+1]+=y;
+                               }
+                               else{
+                                       output[(ii*numChanOut)+selChanOut]+=y;  
+                               }
+                       }
+                       else{
+                               if(monoOut){
+                                       output[(ii*2)]=output[(ii*2)+1]=y;
+                               }
+                               else{
+                                       output[(ii*numChanOut)+selChanOut]=y;
+                               }
+                       }
+                       ii++;
+                   #endif
+               }
+
+               // amplitude detector
+               if(setpt)
+               {
+                       i16 accum=y;
+
+                       if(accum>amax)
+                       {
+                               amax=accum;
+                               discounteru=discfactor;
+                       }
+                       else if(--discounteru<=0)
+                       {
+                               discounteru=discfactor;
+                               amax=(i32)((amax*32700)/32768);
+                       }
+       
+                       if(accum<amin)
+                       {
+                               amin=accum;
+                               discounterl=discfactor;
+                       }
+                       else if(--discounterl<=0)
+                       {
+                               discounterl=discfactor;
+                               amin=(i32)((amin*32700)/32768);
+                       }
+       
+                       apeak = (i32)(amax-amin)/2;
+                        
+                       if(apeak>setpt)compOut=1;
+                       else if(compOut&&(apeak<(setpt-hyst)))compOut=0;
+               }
+       }
+
+       mySps->decimator = decimator;
+
+       mySps->amax=amax;
+       mySps->amin=amin;                               
+       mySps->apeak=apeak;  
+       mySps->discounteru=discounteru;
+       mySps->discounterl=discounterl; 
+       
+       mySps->compOut=compOut;
+       
+       return 0;
+}
+/*
+       general purpose integrator lpf
+*/
+i16 gp_inte_00(t_pmr_sps *mySps)
+{
+       i16 npoints;
+       i16 *input, *output;
+
+       i32 inputGain, outputGain,calcAdjust;
+       i32     i;
+       i32 accum;
+
+       i32 state00;
+       i16 coeff00, coeff01;
+
+       TRACEX(("gp_inte_00() %i\n",mySps->enabled));
+       if(!mySps->enabled)return(1);
+
+       input   = mySps->source;
+       output  = mySps->sink;
+
+       npoints=mySps->nSamples;
+
+       inputGain=mySps->inputGain;
+       outputGain=mySps->outputGain;
+       calcAdjust=mySps->calcAdjust;
+
+       coeff00=((i16*)mySps->coef)[0];
+       coeff01=((i16*)mySps->coef)[1];
+       state00=((i32*)mySps->x)[0];
+
+       // note fixed gain of 2 to compensate for attenuation
+       // in passband
+
+       for(i=0;i<npoints;i++)
+       {
+               accum=input[i];
+               state00 = accum + (state00*coeff01)/M_Q15;
+               accum = (state00*coeff00)/(M_Q15/4);
+               output[i]=(accum*outputGain)/M_Q8;
+       }
+
+       ((i32*)(mySps->x))[0]=state00;
+
+       return 0;
+}
+/*
+       general purpose differentiator hpf
+*/
+i16 gp_diff(t_pmr_sps *mySps)
+{
+       i16 *input, *output;
+       i16 npoints;
+       i32 inputGain, outputGain, calcAdjust;
+       i32     i;
+       i32 temp0,temp1;
+       i16 x0;
+       i32 y0;
+       i16 a0,a1;
+       i16 b0;
+       i16 *coef;
+       i16 *x;
+
+       input   = mySps->source;
+       output  = mySps->sink;
+
+       npoints=mySps->nSamples;
+
+       inputGain=mySps->inputGain;
+       outputGain=mySps->outputGain;
+       calcAdjust=mySps->calcAdjust;
+
+       coef=(i16*)(mySps->coef);
+       x=(i16*)(mySps->x);
+       a0=coef[0];
+       a1=coef[1];
+       b0=coef[2];
+
+       x0=x[0];
+
+       TRACEX(("gp_diff()\n"));
+
+       for (i=0;i<npoints;i++)
+    {
+               temp0 = x0 * a1;
+                  x0 = input[i];
+               temp1 = input[i] * a0;
+                  y0 = (temp0 + temp1)/calcAdjust;
+               output[i]=(y0*outputGain)/M_Q8;
+    }
+
+       x[0]=x0;
+
+       return 0;
+}
+/*     ----------------------------------------------------------------------
+       CenterSlicer
+*/
+i16 CenterSlicer(t_pmr_sps *mySps)
+{
+       i16 npoints,lhit,uhit;
+       i16 *input, *output, *buff;
+
+       i32 inputGain, outputGain, inputGainB;
+       i32     i;
+       i32 accum;
+
+       i32  amax;                      // buffer amplitude maximum
+       i32  amin;                      // buffer amplitude minimum
+       i32  apeak;                     // buffer amplitude peak
+       i32  center;
+       i32  setpt;                     // amplitude set point for peak tracking
+
+       i32  discounteru;       // amplitude detector integrator discharge counter upper
+       i32  discounterl;       // amplitude detector integrator discharge counter lower
+       i32  discfactor;        // amplitude detector integrator discharge factor
+
+       TRACEX(("CenterSlicer() %i\n",mySps->enabled));
+
+       input   = mySps->source;
+       output  = mySps->sink;
+       buff    = mySps->buff;
+
+       npoints=mySps->nSamples;
+
+       inputGain=mySps->inputGain;
+       outputGain=mySps->outputGain;
+       inputGainB=mySps->inputGainB;
+
+       amax=mySps->amax;
+       amin=mySps->amin;
+       setpt=mySps->setpt;
+       apeak=mySps->apeak;
+       discounteru=mySps->discounteru;
+       discounterl=mySps->discounterl;
+
+       discfactor=mySps->discfactor;
+       npoints=mySps->nSamples;
+
+       for(i=0;i<npoints;i++)
+       {
+               accum=input[i];
+
+               lhit=uhit=0;
+
+               if(accum>amax)
+               {
+                       amax=accum;
+                       uhit=1;
+                       if(amin<(amax-setpt))
+                       {
+                               amin=(amax-setpt);
+                               lhit=1;
+                       }
+               }
+               else if(accum<amin)
+               {
+                       amin=accum;
+                       lhit=1;
+                       if(amax>(amin+setpt))
+                       {
+                               amax=(amin+setpt);
+                               uhit=1;
+                       }
+               }
+       
+               if(--discounteru<=0 && amax>0)
+               {
+                       amax--;
+                       uhit=1;
+               }
+       
+               if(--discounterl<=0 && amin<0)
+               {
+                       amin++;
+                       lhit=1;
+               }
+       
+               if(uhit)discounteru=discfactor; 
+               if(lhit)discounterl=discfactor; 
+               
+               apeak = (amax-amin)/2;
+               center = (amax+amin)/2;
+               accum = accum - center;
+               output[i]=accum;
+       
+               // do limiter function
+               if(accum>inputGainB)accum=inputGainB;
+               else if(accum<-inputGainB)accum=-inputGainB;
+               buff[i]=accum;
+
+               #if XPMR_DEBUG0 == 1
+               #if 0
+               mySps->debugBuff0[i]=center;
+               #endif
+           #if 0
+               if(mySps->parentChan->frameCountRx&0x01) mySps->parentChan->prxDebug1[i]=amax;
+               else mySps->parentChan->prxDebug1[i]=amin;
+               #endif
+               #endif
+       }
+
+       mySps->amax=amax;
+       mySps->amin=amin;
+       mySps->apeak=apeak;
+       mySps->discounteru=discounteru;
+       mySps->discounterl=discounterl;
+
+       return 0;
+}
+/*     ----------------------------------------------------------------------
+       MeasureBlock
+       determine peak amplitude
+*/
+i16 MeasureBlock(t_pmr_sps *mySps)
+{
+       i16 npoints;
+       i16 *input, *output;
+
+       i32 inputGain, outputGain;
+       i32     i;
+       i32 accum;
+
+       i16  amax;                      // buffer amplitude maximum
+       i16  amin;                      // buffer amplitude minimum
+       i16  apeak=0;                   // buffer amplitude peak (peak to peak)/2
+       i16  setpt;                     // amplitude set point for amplitude comparator
+
+       i32  discounteru;       // amplitude detector integrator discharge counter upper
+       i32  discounterl;       // amplitude detector integrator discharge counter lower
+       i32  discfactor;        // amplitude detector integrator discharge factor
+
+       TRACEX(("MeasureBlock() %i\n",mySps->enabled));
+
+       if(!mySps->enabled)return 1;
+
+       if(mySps->option==3)
+       {
+               mySps->amax = mySps->amin = mySps->apeak = \
+               mySps->discounteru = mySps->discounterl = \
+               mySps->enabled = 0;
+               return 1;
+       }
+
+       input   = mySps->source;
+       output  = mySps->sink;
+
+       npoints=mySps->nSamples;
+
+       inputGain=mySps->inputGain;
+       outputGain=mySps->outputGain;
+
+       amax=mySps->amax;
+       amin=mySps->amin;
+       setpt=mySps->setpt;
+       discounteru=mySps->discounteru;
+       discounterl=mySps->discounterl;
+
+       discfactor=mySps->discfactor;
+       npoints=mySps->nSamples;
+
+       for(i=0;i<npoints;i++)
+       {
+               accum=input[i];
+
+               if(accum>amax)
+               {
+                       amax=accum;
+                       discounteru=discfactor;
+               }
+               else if(--discounteru<=0)
+               {
+                       discounteru=discfactor;
+                       amax=(i32)((amax*32700)/32768);
+               }
+
+               if(accum<amin)
+               {
+                       amin=accum;
+                       discounterl=discfactor;
+               }
+               else if(--discounterl<=0)
+               {
+                       discounterl=discfactor;
+                       amin=(i32)((amin*32700)/32768);
+               }
+
+               apeak = (i32)(amax-amin)/2;
+               if(output)output[i]=apeak;
+       }
+    
+       mySps->amax=amax;
+       mySps->amin=amin;
+       mySps->apeak=apeak;
+       mySps->discounteru=discounteru;
+       mySps->discounterl=discounterl;
+       if(apeak>=setpt) mySps->compOut=1;
+       else mySps->compOut=0;
+       
+       //TRACEX((" -MeasureBlock()=%i\n",mySps->apeak));
+       return 0;
+}
+/*
+       SoftLimiter
+*/
+i16 SoftLimiter(t_pmr_sps *mySps)
+{
+       i16 npoints;
+       //i16 samples, lhit,uhit;
+       i16 *input, *output;
+
+       i32 inputGain, outputGain;
+       i32     i;
+       i32 accum;
+       i32  tmp;
+
+       i32  amax;                      // buffer amplitude maximum
+       i32  amin;                      // buffer amplitude minimum
+       //i32  apeak;           // buffer amplitude peak
+       i32  setpt;                     // amplitude set point for amplitude comparator
+       i16  compOut;           // amplitude comparator output
+
+       input   = mySps->source;
+       output  = mySps->sink;
+
+       inputGain=mySps->inputGain;
+       outputGain=mySps->outputGain;
+
+       npoints=mySps->nSamples;
+
+       setpt=mySps->setpt;
+       amax=(setpt*124)/128;
+       amin=-amax;
+
+       TRACEX(("SoftLimiter() %i %i %i) \n",amin, amax,setpt));
+
+       for(i=0;i<npoints;i++)
+       {
+               accum=input[i];
+               //accum=input[i]*mySps->inputGain/256;
+
+               if(accum>setpt)
+               {
+                   tmp=((accum-setpt)*4)/128;
+                   accum=setpt+tmp;
+                       if(accum>amax)accum=amax;
+                       compOut=1;
+                       accum=setpt;
+               }
+               else if(accum<-setpt)
+               {
+                   tmp=((accum+setpt)*4)/128;
+                   accum=(-setpt)-tmp;
+                       if(accum<amin)accum=amin;
+                       compOut=1;
+                       accum=-setpt;
+               }
+
+               output[i]=(accum*outputGain)/M_Q8;
+       }
+
+       return 0;
+}
+/*
+       SigGen() - sine, square function generator
+       sps overloaded values
+       discfactor  = phase factor
+       discfactoru = phase index
+       if source is not NULL then mix it in!
+
+       sign table and output gain are in Q15 format (32767=.999)
+*/
+i16    SigGen(t_pmr_sps *mySps)
+{
+       #define PH_FRACT_FACT   128
+
+       i32 ph;
+       i16 i,outputgain,waveform,numChanOut,selChanOut;
+       i32 accum;                               
+
+       TRACEX(("SigGen(%i) \n",mySps->option));
+
+       if(!mySps->freq ||!mySps->enabled)return 0;
+
+       outputgain=mySps->outputGain;
+       waveform=0;
+       numChanOut=mySps->numChanOut; 
+       selChanOut=mySps->selChanOut;
+
+    if(mySps->option==1)
+       {
+               mySps->option=0;
+               mySps->state=1;
+               mySps->discfactor=
+                       (SAMPLES_PER_SINE*mySps->freq*PH_FRACT_FACT)/mySps->sampleRate/10;
+       
+               TRACEX((" SigGen() discfactor = %i\n",mySps->discfactor));
+               if(mySps->discounterl)mySps->state=2;
+       }
+       else if(mySps->option==2)
+       {
+               i16 shiftfactor=CTCSS_TURN_OFF_SHIFT;
+               // phase shift request
+               mySps->option=0;
+               mySps->state=2;
+               mySps->discounterl=CTCSS_TURN_OFF_TIME-(2*MS_PER_FRAME);                // 
+
+               mySps->discounteru = \
+                       (mySps->discounteru + (((SAMPLES_PER_SINE*shiftfactor)/360)*PH_FRACT_FACT)) % (SAMPLES_PER_SINE*PH_FRACT_FACT);
+               //printf("shiftfactor = %i\n",shiftfactor);
+               //shiftfactor+=10;
+       }
+       else if(mySps->option==3)
+       {
+               // stop it and clear the output buffer
+               mySps->option=0;
+               mySps->state=0;
+               mySps->enabled=0;
+               for(i=0;i<mySps->nSamples;i++)
+                       mySps->sink[(i*numChanOut)+selChanOut]=0;
+               return(0);
+       }
+       else if(mySps->state==2)
+       {
+               // doing turn off
+               mySps->discounterl-=MS_PER_FRAME;
+               if(mySps->discounterl<=0)
+               {
+                       mySps->option=3;
+                       mySps->state=2;
+               }
+       }
+       else if(mySps->state==0)
+       {
+               return(0);
+       }
+
+       ph=mySps->discounteru;
+
+       for(i=0;i<mySps->nSamples;i++)
+       {
+               if(!waveform)
+               {
+                       // sine
+                       //tmp=(sinetablex[ph/PH_FRACT_FACT]*amplitude)/M_Q16;
+                       accum=sinetablex[ph/PH_FRACT_FACT];
+                       accum=(accum*outputgain)/M_Q8;
+           }
+               else
+               {
+                       // square
+                       if(ph>SAMPLES_PER_SINE/2)
+                               accum=outputgain/M_Q8;
+                       else
+                               accum=-outputgain/M_Q8;
+               }
+
+               if(mySps->source)accum+=mySps->source[i];
+
+               mySps->sink[(i*numChanOut)+selChanOut]=accum;
+
+               ph=(ph+mySps->discfactor)%(SAMPLES_PER_SINE*PH_FRACT_FACT);
+       }
+
+       mySps->discounteru=ph;
+
+       return 0;
+}
+/*
+       adder/mixer
+       takes existing buffer and adds source buffer to destination buffer
+       sink buffer = (sink buffer * gain) + source buffer 
+*/
+i16 pmrMixer(t_pmr_sps *mySps)
+{
+       i32 accum;
+       i16 i, *input, *inputB, *output;
+       i16  inputGain, inputGainB;             // apply to input data   in Q7.8 format
+       i16  outputGain;        // apply to output data  in Q7.8 format
+       i16      discounteru,discounterl,amax,amin,setpt,discfactor;
+       i16      npoints,uhit,lhit,apeak,measPeak;
+
+       TRACEX(("pmrMixer()\n"));
+
+       input     = mySps->source;
+       inputB    = mySps->sourceB;
+       output    = mySps->sink;
+
+       inputGain=mySps->inputGain;
+       inputGainB=mySps->inputGainB;
+       outputGain=mySps->outputGain;
+       
+       amax=mySps->amax;
+       amin=mySps->amin;
+       setpt=mySps->setpt;
+       discounteru=mySps->discounteru;
+       discounterl=mySps->discounteru;
+
+       discfactor=mySps->discfactor;
+       npoints=mySps->nSamples;
+       measPeak=mySps->measPeak;
+
+       for(i=0;i<npoints;i++)
+       {
+               accum = ((input[i]*inputGain)/M_Q8) + 
+                               ((inputB[i]*inputGainB)/M_Q8);
+
+               accum=(accum*outputGain)/M_Q8;
+               output[i]=accum;
+
+               if(measPeak){
+                       lhit=uhit=0;
+       
+                       if(accum>amax){
+                               amax=accum;
+                               uhit=1;
+                               if(amin<(amax-setpt)){
+                                       amin=(amax-setpt);
+                                       lhit=1;
+                               }
+                       }
+                       else if(accum<amin){
+                               amin=accum;
+                               lhit=1;
+                               if(amax>(amin+setpt)){
+                                       amax=(amin+setpt);
+                                       uhit=1;
+                               }
+                       }
+               
+                       if(--discounteru<=0 && amax>0){
+                               amax--;
+                               uhit=1;
+                       }
+               
+                       if(--discounterl<=0 && amin<0){
+                               amin++;
+                               lhit=1;
+                       }
+               
+                       if(uhit)discounteru=discfactor; 
+                       if(lhit)discounterl=discfactor;
+               }       
+       }
+
+       if(measPeak){
+               apeak = (amax-amin)/2;
+               mySps->apeak=apeak;
+               mySps->amax=amax;
+               mySps->amin=amin;
+               mySps->discounteru=discounteru;
+               mySps->discounterl=discounterl;
+       }
+       
+       return 0;
+}
+/*
+       DelayLine 
+*/
+i16 DelayLine(t_pmr_sps *mySps)
+{
+       i16 *input, *output, *buff;
+       i16      i, npoints,buffsize,inindex,outindex;
+
+       TRACEX((" DelayLine() %i\n",mySps->enabled));
+       
+       input           = mySps->source;
+       output          = mySps->sink;
+       buff            = (i16*)(mySps->buff);
+       buffsize        = mySps->buffSize;
+       npoints         = mySps->nSamples;
+
+       outindex        = mySps->buffOutIndex;
+       inindex         = outindex + mySps->buffLead;
+
+       for(i=0;i<npoints;i++)
+       {
+               inindex %= buffsize;
+               outindex %= buffsize;
+               
+               buff[inindex]=input[i];
+               output[i]=buff[outindex];
+               inindex++;
+               outindex++;
+       }
+       mySps->buffOutIndex=outindex;
+
+       return 0;
+}
+/*
+       Continuous Tone Coded Squelch (CTCSS) Detector
+*/
+i16 ctcss_detect(t_pmr_chan *pmrChan)
+{
+       i16 i,points2do, points=0, *pInput, hit, thit,relax; 
+       i16 tnum, tmp, indexWas=0, indexNow, gain, peakwas=0, diffpeak;
+       i16 difftrig;
+       i16 lasttv0=0, lasttv1=0, lasttv2=0, tv0, tv1, tv2, indexDebug;
+
+       TRACEX(("ctcss_detect(%p) %i %i %i %i\n",pmrChan, 
+               pmrChan->rxCtcss->enabled,
+               pmrChan->rxCtcssIndex,
+               pmrChan->rxCtcss->testIndex,
+               pmrChan->rxCtcss->decode));
+
+       if(!pmrChan->rxCtcss->enabled)return(1);
+
+       relax  = pmrChan->rxCtcss->relax; 
+       pInput = pmrChan->rxCtcss->input;
+       gain   = pmrChan->rxCtcss->gain;
+       
+       if(relax) difftrig=(-0.1*M_Q15);
+       else difftrig=(-0.05*M_Q15);
+
+       thit=hit=-1;
+
+       //TRACEX((" ctcss_detect() %i  %i  %i  %i\n", CTCSS_NUM_CODES,0,0,0));
+
+       for(tnum=0;tnum<CTCSS_NUM_CODES;tnum++)
+       {
+               i32 accum, peak;
+               t_tdet  *ptdet;
+               i16 fudgeFactor;
+               i16 binFactor;
+
+               //TRACEX((" ctcss_detect() tnum=%i %i\n",tnum,pmrChan->rxCtcssMap[tnum]));
+
+               if( (pmrChan->rxCtcssMap[tnum] < 0) || 
+                   (pmrChan->rxCtcss->decode>=0 && (tnum!= pmrChan->rxCtcss->decode)) ||
+                       (!pmrChan->rxCtcss->multiFreq && (tnum!= pmrChan->rxCtcssIndex))
+                 )
+                       continue;
+
+               //TRACEX((" ctcss_detect() tnum=%i\n",tnum));
+
+               ptdet=&(pmrChan->rxCtcss->tdet[tnum]);
+               indexDebug=0;
+               points=points2do=pmrChan->nSamplesRx;
+               fudgeFactor=ptdet->fudgeFactor;
+               binFactor=ptdet->binFactor;
+
+               while(ptdet->counter < (points2do*CTCSS_SCOUNT_MUL))
+               {
+                       //TRACEX((" ctcss_detect() - inner loop\n"));
+                       tmp=(ptdet->counter/CTCSS_SCOUNT_MUL)+1;
+                   ptdet->counter-=(tmp*CTCSS_SCOUNT_MUL);
+                       points2do-=tmp;
+                       indexNow=points-points2do;
+                       
+                       ptdet->counter += ptdet->counterFactor;
+
+                       accum = pInput[indexNow-1];             // dude's major bug fix!
+
+                       peakwas=ptdet->peak;
+
+                       ptdet->z[ptdet->zIndex]+=
+                               (((accum - ptdet->z[ptdet->zIndex])*binFactor)/M_Q15);
+
+                       peak = abs(ptdet->z[0]-ptdet->z[2]) + abs(ptdet->z[1]-ptdet->z[3]);
+
+                       if (ptdet->peak < peak)
+                               ptdet->peak += ( ((peak-ptdet->peak)*binFactor)/M_Q15);
+                       else
+                               ptdet->peak=peak;
+
+                       {
+                               static const i16 a0=13723;
+                               static const i16 a1=-13723;
+                               i32 temp0,temp1;
+                               i16 x0;
+       
+                               //differentiate
+                               x0=ptdet->zd;
+                               temp0 = x0 * a1;
+                               ptdet->zd = ptdet->peak;
+                               temp1 = ptdet->peak * a0;
+                           diffpeak = (temp0 + temp1)/1024;
+                       }
+
+                       if(diffpeak<(-0.03*M_Q15))ptdet->dvd-=4;
+                       else if(ptdet->dvd<0)ptdet->dvd++;
+
+                       if((ptdet->dvd < -12) && diffpeak > (-0.02*M_Q15))ptdet->dvu+=2;
+                       else if(ptdet->dvu)ptdet->dvu--;
+
+                       tmp=ptdet->setpt;
+                       if(pmrChan->rxCtcss->decode==tnum)
+                       {
+                               if(relax)tmp=(tmp*55)/100;
+                               else tmp=(tmp*80)/100;
+                       }
+
+                       if(ptdet->peak > tmp)
+                       {
+                           if(ptdet->decode<(fudgeFactor*32))ptdet->decode++;
+                       }
+                       else if(pmrChan->rxCtcss->decode==tnum)
+                       {
+                               if(ptdet->peak > ptdet->hyst)ptdet->decode--;
+                               else if(relax) ptdet->decode--; 
+                               else ptdet->decode-=4; 
+                       }
+                       else
+                       {
+                               ptdet->decode=0;
+                       }
+
+                       if((pmrChan->rxCtcss->decode==tnum) && !relax && (ptdet->dvu > (0.00075*M_Q15)))
+                       {
+                               ptdet->decode=0;
+                               ptdet->z[0]=ptdet->z[1]=ptdet->z[2]=ptdet->z[3]=ptdet->dvu=0;
+                               //printf("ctcss_detect() turnoff code!\n");
+                       }
+
+                       if(ptdet->decode<0 || !pmrChan->rxCarrierDetect)ptdet->decode=0;
+
+                       if(ptdet->decode>=fudgeFactor)thit=tnum;  
+
+                       #if XPMR_DEBUG0 == 1
+                       //if(thit>=0 && thit==tnum)
+                       //      printf(" ctcss_detect() %i %i %i %i \n",tnum,ptdet->peak,ptdet->setpt,ptdet->hyst);
+
+                       // tv0=accum;
+                       tv0=ptdet->peak;
+                       tv1=diffpeak;
+                       tv2=ptdet->dvu;
+                       
+                       //tv1=ptdet->zi*100;
+                       while(indexDebug<indexNow)
+                       {
+                               if(indexDebug==0)lasttv0=ptdet->pDebug0[points-1];
+                               if(ptdet->pDebug0)ptdet->pDebug0[indexDebug]=lasttv0;
+
+                               if(indexDebug==0)lasttv1=ptdet->pDebug1[points-1];
+                               if(ptdet->pDebug1)ptdet->pDebug1[indexDebug]=lasttv1;
+
+                               if(indexDebug==0)lasttv2=ptdet->pDebug2[points-1];
+                               if(ptdet->pDebug2)ptdet->pDebug2[indexDebug]=lasttv2;
+
+                               indexDebug++;
+                       }
+                       lasttv0=tv0;
+                       lasttv1=tv1;
+                       lasttv2=tv2*100;
+                       #endif
+                       indexWas=indexNow;
+                       ptdet->zIndex=(++ptdet->zIndex)%4;
+               }
+               ptdet->counter-=(points2do*CTCSS_SCOUNT_MUL);
+
+               #if XPMR_DEBUG0 == 1
+               for(i=indexWas;i<points;i++)
+               {
+                       if(ptdet->pDebug0)ptdet->pDebug0[i]=lasttv0;
+                       if(ptdet->pDebug1)ptdet->pDebug1[i]=lasttv1;
+                       if(ptdet->pDebug2)ptdet->pDebug2[i]=lasttv2;
+               }
+               #endif
+       }
+
+       //TRACEX((" ctcss_detect() thit %i\n",thit));
+
+       if(pmrChan->rxCtcss->BlankingTimer>0)pmrChan->rxCtcss->BlankingTimer-=points;
+       if(pmrChan->rxCtcss->BlankingTimer<0)pmrChan->rxCtcss->BlankingTimer=0;
+
+    if(thit>=0 && pmrChan->rxCtcss->decode<0 && !pmrChan->rxCtcss->BlankingTimer)
+    {
+               pmrChan->rxCtcss->decode=thit;          
+       }
+       else if(thit<0 && pmrChan->rxCtcss->decode>=0)
+       {
+               pmrChan->rxCtcss->BlankingTimer=SAMPLE_RATE_NETWORK/5;
+               pmrChan->rxCtcss->decode=-1;    
+
+               for(tnum=0;tnum<CTCSS_NUM_CODES;tnum++)
+               {
+                   t_tdet      *ptdet=NULL;
+                       ptdet=&(pmrChan->rxCtcss->tdet[tnum]);
+                   ptdet->decode=0;
+                       ptdet->z[0]=ptdet->z[1]=ptdet->z[2]=ptdet->z[3]=0;
+               }
+       }
+       //TRACEX((" ctcss_detect() thit %i %i\n",thit,pmrChan->rxCtcss->decode));
+       return(0);
+}
+/*
+       TxTestTone
+*/
+static i16     TxTestTone(t_pmr_chan *pChan, i16 function)
+{
+       if(function==1)
+       {
+               pChan->spsSigGen1->enabled=1;
+               pChan->spsSigGen1->option=1;
+               pChan->spsTx->source=pChan->spsSigGen1->sink;
+       }
+       else
+       {
+               pChan->spsSigGen1->option=3;
+       }
+       return 0;
+}
+/*     
+       assumes:
+       sampling rate is 48KS/s
+       samples are all 16 bits
+    samples are filtered and decimated by 1/6th
+*/
+t_pmr_chan     *createPmrChannel(t_pmr_chan *tChan, i16 numSamples)
+{
+       i16 i, *inputTmp;
+
+       t_pmr_chan      *pChan;
+       t_pmr_sps       *pSps;
+       t_dec_ctcss     *pDecCtcss;
+       t_tdet          *ptdet;
+
+       TRACEX(("createPmrChannel(%p,%i)\n",tChan,numSamples));
+
+       pChan = (t_pmr_chan *)calloc(sizeof(t_pmr_chan),1);
+
+       if(pChan==NULL)
+       {
+               printf("createPmrChannel() failed\n");
+               return(NULL);
+       }
+       
+       pChan->nSamplesRx=numSamples;
+       pChan->nSamplesTx=numSamples;
+
+       pChan->index=pmrChanIndex++;
+
+       for(i=0;i<CTCSS_NUM_CODES;i++)
+       {
+               pChan->rxCtcssMap[i]=-1;        
+       }
+
+       pChan->rxCtcssIndex=-1;
+
+       if(tChan==NULL)
+       {
+               pChan->rxNoiseSquelchEnable=0;
+               pChan->rxHpfEnable=0;
+               pChan->rxDeEmpEnable=0;
+               pChan->rxCenterSlicerEnable=0;
+               pChan->rxCtcssDecodeEnable=0;
+               pChan->rxDcsDecodeEnable=0;
+
+               pChan->rxCarrierPoint = 17000;
+               pChan->rxCarrierHyst = 2500;
+
+               pChan->rxCtcssFreq=103.5;
+
+               pChan->txHpfEnable=0;
+               pChan->txLimiterEnable=0;
+               pChan->txPreEmpEnable=0;
+               pChan->txLpfEnable=1;
+               pChan->txCtcssFreq=103.5;
+               pChan->txMixA=TX_OUT_VOICE;
+               pChan->txMixB=TX_OUT_LSD;
+       }
+       else
+       {
+               pChan->rxDemod=tChan->rxDemod;
+               pChan->rxCdType=tChan->rxCdType;
+               pChan->rxSquelchPoint = tChan->rxSquelchPoint;
+               pChan->rxCarrierHyst = 3000;
+               pChan->rxCtcssFreq=tChan->rxCtcssFreq;
+
+               for(i=0;i<CTCSS_NUM_CODES;i++)
+                       pChan->rxCtcssMap[i]=tChan->rxCtcssMap[i];
+               
+               pChan->txMod=tChan->txMod;
+               pChan->txHpfEnable=1; 
+               pChan->txLpfEnable=1;
+               pChan->txCtcssFreq=tChan->txCtcssFreq;
+               pChan->txMixA=tChan->txMixA;
+               pChan->txMixB=tChan->txMixB;
+               pChan->radioDuplex=tChan->radioDuplex;
+       }
+
+       TRACEX(("misc settings \n"));
+
+       if(pChan->rxCdType==CD_XPMR_NOISE){
+               pChan->rxNoiseSquelchEnable=1;
+       }
+
+       if(pChan->rxDemod==RX_AUDIO_FLAT){
+               pChan->rxHpfEnable=1;
+               pChan->rxDeEmpEnable=1;
+       }
+
+       pChan->rxCarrierPoint=(pChan->rxSquelchPoint*32767)/100;
+       pChan->rxCarrierHyst = 3000; //pChan->rxCarrierPoint/15;
+       
+       pChan->rxDcsDecodeEnable=0;
+
+       if(pChan->rxCtcssFreq!=0){
+               pChan->rxHpfEnable=1;
+               pChan->rxCenterSlicerEnable=1;
+               pChan->rxCtcssDecodeEnable=1;
+               pChan->rxCtcssIndex=CtcssFreqIndex(pChan->rxCtcssFreq);
+       }
+
+       if(pChan->txMod){
+               pChan->txPreEmpEnable=1;
+               pChan->txLimiterEnable=1;
+       }
+       
+       TRACEX(("calloc buffers \n"));
+
+       pChan->pRxDemod         = calloc(numSamples,2);
+       pChan->pRxNoise         = calloc(numSamples,2);
+       pChan->pRxBase          = calloc(numSamples,2);
+       pChan->pRxHpf           = calloc(numSamples,2);
+       pChan->pRxLsd           = calloc(numSamples,2);
+       pChan->pRxSpeaker       = calloc(numSamples,2);
+       pChan->pRxCtcss         = calloc(numSamples,2);
+       pChan->pRxDcTrack       = calloc(numSamples,2);
+       pChan->pRxLsdLimit      = calloc(numSamples,2);
+       
+       
+       pChan->pTxBase          = calloc(numSamples,2);
+       pChan->pTxHpf           = calloc(numSamples,2);
+       pChan->pTxPreEmp        = calloc(numSamples,2);
+       pChan->pTxLimiter       = calloc(numSamples,2);
+       pChan->pTxLsd           = calloc(numSamples,2);
+       pChan->pTxLsdLpf    = calloc(numSamples,2);
+       pChan->pTxComposite     = calloc(numSamples,2);
+       pChan->pSigGen0         = calloc(numSamples,2);
+    pChan->pSigGen1            = calloc(numSamples,2);
+
+       pChan->pTxCode      = calloc(numSamples,2);
+       pChan->pTxOut           = calloc(numSamples,2*2*6);             // output buffer
+       
+       #if XPMR_DEBUG0 == 1
+       pChan->pTxPttIn     = calloc(numSamples,2);
+       pChan->pTxPttOut    = calloc(numSamples,2);
+       pChan->prxDebug0        = calloc(numSamples,2);
+       pChan->prxDebug1        = calloc(numSamples,2);
+       pChan->prxDebug2        = calloc(numSamples,2);
+       pChan->prxDebug3        = calloc(numSamples,2);
+       pChan->ptxDebug0        = calloc(numSamples,2);
+       pChan->ptxDebug1        = calloc(numSamples,2);
+       pChan->ptxDebug2        = calloc(numSamples,2);
+       pChan->ptxDebug3        = calloc(numSamples,2);
+       pChan->pNull            = calloc(numSamples,2);
+       for(i=0;i<numSamples;i++)pChan->pNull[i]=((i%(numSamples/2))*8000)-4000;
+       #endif
+
+       TRACEX(("create ctcss\n"));
+
+       pDecCtcss = (t_dec_ctcss *)calloc(sizeof(t_dec_ctcss),1);
+        
+       pChan->rxCtcss=pDecCtcss; 
+       pDecCtcss->enabled=1;
+       pDecCtcss->gain=1*M_Q8;
+       pDecCtcss->limit=8192;
+       pDecCtcss->input=pChan->pRxLsdLimit;
+       pDecCtcss->testIndex=pChan->rxCtcssIndex;
+       if(!pDecCtcss->testIndex)pDecCtcss->testIndex=1;
+       pChan->rxCtcssMap[pChan->rxCtcssIndex]=pChan->rxCtcssIndex;
+       pDecCtcss->decode=-1;
+
+       for(i=0;i<CTCSS_NUM_CODES;i++)
+       {
+               ptdet=&(pChan->rxCtcss->tdet[i]);
+               ptdet->state=1;
+               ptdet->setpt=(M_Q15*0.067);                                     // 0.069
+               ptdet->hyst =(M_Q15*0.020);
+               ptdet->counterFactor=coef_ctcss_div[i];
+               ptdet->binFactor=(M_Q15*0.135);                         // was 0.140
+               ptdet->fudgeFactor=8;   
+       }
+
+       // General Purpose Function Generator
+       pSps=pChan->spsSigGen1=createPmrSps();
+       pSps->parentChan=pChan;
+       pSps->sink=pChan->pSigGen1; 
+       pSps->numChanOut=1;
+       pSps->selChanOut=0;
+       pSps->sigProc=SigGen; 
+       pSps->nSamples=pChan->nSamplesTx;
+       pSps->sampleRate=SAMPLE_RATE_NETWORK;
+       pSps->freq=10000;                                               // in increments of 0.1 Hz
+       pSps->outputGain=(.25*M_Q8);
+       pSps->option=0;
+       pSps->interpolate=1;
+       pSps->decimate=1;
+       pSps->enabled=0;
+
+
+       // CTCSS ENCODER
+       pSps = pChan->spsSigGen0 = createPmrSps();
+       pSps->parentChan=pChan;
+       pSps->sink=pChan->pTxLsd;  
+       pSps->sigProc=SigGen; 
+       pSps->numChanOut=1;
+       pSps->selChanOut=0;
+       pSps->nSamples=pChan->nSamplesTx;
+       pSps->sampleRate=SAMPLE_RATE_NETWORK;
+       pSps->freq=pChan->txCtcssFreq*10;               // in increments of 0.1 Hz
+       pSps->outputGain=(0.5*M_Q8);
+       pSps->option=0;
+       pSps->interpolate=1;
+       pSps->decimate=1;
+       pSps->enabled=0;
+
+
+       // Tx LSD Low Pass Filter
+       pSps=pChan->spsTxLsdLpf=pSps->nextSps=createPmrSps();
+       pSps->source=pChan->pTxLsd;
+       pSps->sink=pChan->pTxLsdLpf;  
+       pSps->sigProc=pmr_gp_fir;
+       pSps->enabled=0;
+       pSps->numChanOut=1;
+       pSps->selChanOut=0;
+       pSps->nSamples=pChan->nSamplesTx;
+       pSps->decimator=pSps->decimate=1;
+       pSps->interpolate=1;
+       pSps->inputGain=(1*M_Q8);
+       pSps->outputGain=(1*M_Q8);
+
+       if(pChan->txCtcssFreq>203.0)
+       {
+               pSps->ncoef=taps_fir_lpf_250_9_66;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_lpf_250_9_66;
+               pSps->nx=taps_fir_lpf_250_9_66;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               pSps->calcAdjust=gain_fir_lpf_250_9_66;
+       }
+       else
+       {
+               pSps->ncoef=taps_fir_lpf_215_9_88;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_lpf_215_9_88;
+               pSps->nx=taps_fir_lpf_215_9_88;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               pSps->calcAdjust=gain_fir_lpf_215_9_88;
+       }
+
+       pSps->inputGain=(1*M_Q8);
+       pSps->outputGain=(1*M_Q8);
+       
+       if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n"); 
+
+               
+       // RX Process
+       TRACEX(("create rx\n"));
+       pSps = NULL;
+
+       // allocate space for first sps and set pointers
+       pSps=pChan->spsRx=createPmrSps();
+       pSps->parentChan=pChan;
+       pSps->source=NULL;                                      //set when called
+       pSps->sink=pChan->pRxBase;
+       pSps->sigProc=pmr_rx_frontend;
+       pSps->enabled=1;
+       pSps->decimator=pSps->decimate=6;
+       pSps->interpolate=pSps->interpolate=1;
+       pSps->nSamples=pChan->nSamplesRx;
+       pSps->ncoef=taps_fir_bpf_noise_1;
+       pSps->size_coef=2;
+       pSps->coef=(void*)coef_fir_lpf_3K_1;
+       pSps->coef2=(void*)coef_fir_bpf_noise_1;
+       pSps->nx=taps_fir_bpf_noise_1;
+       pSps->size_x=2;
+       pSps->x=(void*)(calloc(pSps->nx,pSps->size_coef));
+       pSps->calcAdjust=(gain_fir_lpf_3K_1*256)/0x0100;
+       pSps->outputGain=(1.0*M_Q8);
+       pSps->discfactor=2;       
+       pSps->hyst=pChan->rxCarrierHyst;
+       pSps->setpt=pChan->rxCarrierPoint;
+       pChan->prxSquelchAdjust=&pSps->setpt;
+       #if XPMR_DEBUG0 == 1
+       pSps->debugBuff0=pChan->pRxDemod;
+       pSps->debugBuff1=pChan->pRxNoise;
+       pSps->debugBuff2=pChan->prxDebug0;
+       #endif
+
+       // allocate space for next sps and set pointers
+       // Rx SubAudible Decoder Low Pass Filter
+       pSps=pSps->nextSps=createPmrSps();
+       pSps->parentChan=pChan;
+       pSps->source=pChan->pRxBase;
+       pSps->sink=pChan->pRxLsd;
+       pSps->sigProc=pmr_gp_fir;
+       pSps->enabled=1;
+       pSps->numChanOut=1;
+       pSps->selChanOut=0;
+       pSps->nSamples=pChan->nSamplesRx;
+       pSps->decimator=pSps->decimate=1;
+       pSps->interpolate=1;
+
+       if(pChan->rxCtcssFreq>203.5)
+       {
+               pSps->ncoef=taps_fir_lpf_250_9_66;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_lpf_250_9_66;
+               pSps->nx=taps_fir_lpf_250_9_66;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               pSps->calcAdjust=gain_fir_lpf_250_9_66;
+       }
+       else
+       {
+               pSps->ncoef=taps_fir_lpf_215_9_88;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_lpf_215_9_88;
+               pSps->nx=taps_fir_lpf_215_9_88;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               pSps->calcAdjust=gain_fir_lpf_215_9_88;
+       }
+
+       pSps->inputGain=(1*M_Q8);
+       pSps->outputGain=(1*M_Q8);
+       pChan->prxCtcssMeasure=pSps->sink;
+       pChan->prxCtcssAdjust=&(pSps->outputGain);      
+
+
+       // allocate space for next sps and set pointers
+       // CenterSlicer
+       if(pChan->rxCenterSlicerEnable)
+       {
+               pSps=pSps->nextSps=createPmrSps();
+               pSps->parentChan=pChan;
+               pSps->source=pChan->pRxLsd;
+               pSps->sink=pChan->pRxDcTrack;
+               pSps->buff=pChan->pRxLsdLimit;
+               pSps->sigProc=CenterSlicer;
+               pSps->enabled=1;
+               pSps->nSamples=pChan->nSamplesRx;
+               pSps->discfactor=800;
+               pSps->inputGain=(1*M_Q8);
+               pSps->outputGain=(1*M_Q8);
+               pSps->setpt=3000;
+               pSps->inputGainB=1000;                  // limiter set point
+       }
+
+       // allocate space for next sps and set pointers
+       // Rx HPF
+       pSps=pSps->nextSps=createPmrSps();
+       pSps->parentChan=pChan;
+       pChan->spsRxHpf=pSps;
+       pSps->source=pChan->pRxBase;
+       pSps->sink=pChan->pRxHpf;  
+       pSps->sigProc=pmr_gp_fir;
+       pSps->enabled=1;
+       pSps->numChanOut=1;
+       pSps->selChanOut=0;
+       pSps->nSamples=pChan->nSamplesRx;
+       pSps->decimator=pSps->decimate=1;
+       pSps->interpolate=1;
+       pSps->ncoef=taps_fir_hpf_300_9_66;
+       pSps->size_coef=2;
+       pSps->coef=(void*)coef_fir_hpf_300_9_66;
+       pSps->nx=taps_fir_hpf_300_9_66;
+       pSps->size_x=2;
+       pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+       if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n"); 
+       pSps->calcAdjust=gain_fir_hpf_300_9_66;
+       pSps->inputGain=(1*M_Q8);
+       pSps->outputGain=(1*M_Q8);
+       pChan->spsRxOut=pSps;
+
+       // allocate space for next sps and set pointers
+       // Rx DeEmp
+       if(pChan->rxDeEmpEnable){
+               pSps=pSps->nextSps=createPmrSps();
+               pSps->parentChan=pChan;
+               pChan->spsRxDeEmp=pSps;
+               pSps->source=pChan->pRxHpf;
+               pSps->sink=pChan->pRxSpeaker;  
+               pChan->spsRxOut=pSps;                                    // OUTPUT STRUCTURE! maw
+               pSps->sigProc=gp_inte_00;
+               pSps->enabled=1;
+               pSps->nSamples=pChan->nSamplesRx;
+       
+               pSps->ncoef=taps_int_lpf_300_1_2;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_int_lpf_300_1_2;
+       
+               pSps->nx=taps_int_lpf_300_1_2;
+               pSps->size_x=4;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n"); 
+               pSps->calcAdjust=gain_int_lpf_300_1_2/2;
+               pSps->inputGain=(1.0*M_Q8);
+               pSps->outputGain=(1.0*M_Q8);
+               pChan->prxVoiceMeasure=pSps->sink;
+               pChan->prxVoiceAdjust=&(pSps->outputGain);      
+       }
+
+       if(pChan->rxDelayLineEnable)
+       {
+               TRACEX(("create delayline\n"));
+               pSps=pChan->spsDelayLine=pSps->nextSps=createPmrSps();
+               pSps->sigProc=DelayLine;
+               pSps->source=pChan->pRxSpeaker;  
+               pSps->sink=pChan->pRxSpeaker;
+               pSps->enabled=0;
+               pSps->inputGain=1*M_Q8; 
+               pSps->outputGain=1*M_Q8;
+               pSps->nSamples=pChan->nSamplesRx;
+               pSps->buffSize=4096;
+               pSps->buff=calloc(4096,2);                      // one second maximum
+               pSps->buffLead = (SAMPLE_RATE_NETWORK*0.100);
+               pSps->buffOutIndex=0;
+       }
+
+       if(pChan->rxCdType==CD_XPMR_VOX)
+       {
+               TRACEX(("create vox measureblock\n"));
+               pSps=pChan->spsRxVox=pSps->nextSps=createPmrSps();
+               pSps->sigProc=MeasureBlock;
+               pSps->parentChan=pChan;
+               pSps->source=pChan->pRxBase;
+               pSps->sink=pChan->prxDebug1;
+               pSps->inputGain=1*M_Q8; 
+               pSps->outputGain=1*M_Q8;
+               pSps->nSamples=pChan->nSamplesRx;
+               pSps->discfactor=3;
+               pSps->setpt=(0.01*M_Q15);
+               pSps->hyst=(pSps->setpt/10);
+               pSps->enabled=1;
+       }
+
+       // tuning measure block
+       pSps=pChan->spsMeasure=pSps->nextSps=createPmrSps();
+       pSps->parentChan=pChan;
+       pSps->source=pChan->spsRx->sink;                                         
+       pSps->sink=pChan->prxDebug2;
+       pSps->sigProc=MeasureBlock;
+       pSps->enabled=0;
+       pSps->nSamples=pChan->nSamplesRx;
+       pSps->discfactor=10;        
+
+       pSps->nextSps=NULL;             // last sps in chain RX
+
+
+       // CREATE TRANSMIT CHAIN
+       TRACEX((" create tx\n"));
+       inputTmp=NULL;
+       pSps = NULL;
+
+       // allocate space for first sps and set pointers
+
+       // Tx HPF SubAudible
+       if(pChan->txHpfEnable)
+       {
+               pSps=createPmrSps();
+               pChan->spsTx=pSps;
+               pSps->source=pChan->pTxBase;
+               pSps->sink=pChan->pTxHpf;  
+               pSps->sigProc=pmr_gp_fir;
+               pSps->enabled=1;
+               pSps->numChanOut=1;
+               pSps->selChanOut=0;
+               pSps->nSamples=pChan->nSamplesTx;
+               pSps->decimator=pSps->decimate=1;
+               pSps->interpolate=1;
+               pSps->ncoef=taps_fir_hpf_300_9_66;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_hpf_300_9_66;
+               pSps->nx=taps_fir_hpf_300_9_66;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n"); 
+               pSps->calcAdjust=gain_fir_hpf_300_9_66;
+               pSps->inputGain=(1*M_Q8);
+               pSps->outputGain=(1*M_Q8);
+               inputTmp=pChan->pTxHpf;
+       }
+
+       // Tx PreEmphasis
+       if(pChan->txPreEmpEnable)
+       {
+               if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+               else pSps=pSps->nextSps=createPmrSps();
+               
+               pSps->parentChan=pChan;
+               pSps->source=inputTmp;
+               pSps->sink=pChan->pTxPreEmp;  
+               
+               pSps->sigProc=gp_diff;
+               pSps->enabled=1;
+               pSps->nSamples=pChan->nSamplesTx;
+       
+               pSps->ncoef=taps_int_hpf_4000_1_2;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_int_hpf_4000_1_2;
+       
+               pSps->nx=taps_int_hpf_4000_1_2;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+               pSps->outputGain=(1*M_Q8);
+               pSps->calcAdjust=gain_int_hpf_4000_1_2;
+               pSps->inputGain=(1*M_Q8);
+               pSps->outputGain=(1*M_Q8);
+               inputTmp=pSps->sink;
+       }
+
+       // Tx Limiter
+       if(pChan->txLimiterEnable)
+       {
+               if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+               else pSps=pSps->nextSps=createPmrSps();
+               pSps->source=inputTmp;
+               pSps->sink=pChan->pTxLimiter;
+               pSps->sigProc=SoftLimiter;
+               pSps->enabled=1;
+               pSps->nSamples=pChan->nSamplesTx;
+               pSps->inputGain=(1*M_Q8);
+               pSps->outputGain=(1*M_Q8);
+               pSps->setpt=12000;
+               inputTmp=pSps->sink;
+       }
+
+       // Composite Mix of Voice and LSD
+       if((pChan->txMixA==TX_OUT_COMPOSITE)||(pChan->txMixB==TX_OUT_COMPOSITE))
+       {
+               if(pSps==NULL)
+                       pSps=pChan->spsTx=createPmrSps();
+               else
+                       pSps=pSps->nextSps=createPmrSps();
+               pSps->source=inputTmp;
+               pSps->sourceB=pChan->pTxLsdLpf;          //asdf ??? !!! maw pTxLsdLpf
+               pSps->sink=pChan->pTxComposite;
+               pSps->sigProc=pmrMixer;
+               pSps->enabled=1;
+               pSps->nSamples=pChan->nSamplesTx;
+               pSps->inputGain=2*M_Q8; 
+               pSps->inputGainB=1*M_Q8/8; 
+               pSps->outputGain=1*M_Q8;
+               pSps->setpt=0;
+               inputTmp=pSps->sink;
+               pChan->ptxCtcssAdjust=&pSps->inputGainB;
+       }
+
+       // Chan A Upsampler and Filter 
+       if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+       else pSps=pSps->nextSps=createPmrSps();
+
+       pChan->spsTxOutA=pSps;
+       if(!pChan->spsTx)pChan->spsTx=pSps;
+       pSps->parentChan=pChan;
+
+       if(pChan->txMixA==TX_OUT_COMPOSITE)
+       {
+               pSps->source=pChan->pTxComposite;       
+       }
+       else if(pChan->txMixA==TX_OUT_LSD)
+       {
+               pSps->source=pChan->pTxLsdLpf;  
+       }
+       else if(pChan->txMixA==TX_OUT_VOICE)
+       {
+               pSps->source=pChan->pTxHpf;
+       }
+       else if (pChan->txMixA==TX_OUT_AUX)
+       {
+               pSps->source=inputTmp;                           
+       }
+       else
+       {
+               pSps->source=NULL;                               
+       }
+       
+       pSps->sink=pChan->pTxOut;  
+       pSps->sigProc=pmr_gp_fir;
+       pSps->enabled=1;
+       pSps->numChanOut=2;
+       pSps->selChanOut=0;
+       pSps->nSamples=pChan->nSamplesTx;
+       pSps->interpolate=6;
+       pSps->ncoef=taps_fir_lpf_3K_1;
+       pSps->size_coef=2;
+       pSps->coef=(void*)coef_fir_lpf_3K_1;
+       pSps->nx=taps_fir_lpf_3K_1;
+       pSps->size_x=2;
+       pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+       if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n"); 
+       pSps->calcAdjust=gain_fir_lpf_3K_1;
+       pSps->inputGain=(1*M_Q8);
+       pSps->outputGain=(1*M_Q8);
+       if(pChan->txMixA==pChan->txMixB)pSps->monoOut=1;
+       else pSps->monoOut=0;
+
+
+       // Chan B Upsampler and Filter 
+       if((pChan->txMixA!=pChan->txMixB)&&(pChan->txMixB!=TX_OUT_OFF))
+       {
+               if(pSps==NULL) pSps=pChan->spsTx=createPmrSps();
+               else pSps=pSps->nextSps=createPmrSps();
+
+               pChan->spsTxOutB=pSps;
+               pSps->parentChan=pChan;
+               if(pChan->txMixB==TX_OUT_COMPOSITE)
+               {
+                       pSps->source=pChan->pTxComposite;       
+               }
+               else if(pChan->txMixB==TX_OUT_LSD)
+               {
+                       pSps->source=pChan->pTxLsdLpf;
+                       // pChan->ptxCtcssAdjust=&pSps->inputGain;
+               }
+               else if(pChan->txMixB==TX_OUT_VOICE)
+               {
+                       pSps->source=inputTmp;
+               }
+               else if(pChan->txMixB==TX_OUT_AUX)
+               {
+                       pSps->source=pChan->pTxHpf;
+               }
+               else
+               {
+                       pSps->source=NULL;       
+               }
+
+               pSps->sink=pChan->pTxOut;  
+               pSps->sigProc=pmr_gp_fir;
+               pSps->enabled=1;
+               pSps->numChanOut=2;
+               pSps->selChanOut=1;
+               pSps->mixOut=0;
+               pSps->nSamples=pChan->nSamplesTx;
+               pSps->interpolate=6;
+               pSps->ncoef=taps_fir_lpf_3K_1;
+               pSps->size_coef=2;
+               pSps->coef=(void*)coef_fir_lpf_3K_1;
+               pSps->nx=taps_fir_lpf_3K_1;
+               pSps->size_x=2;
+               pSps->x=(void*)(calloc(pSps->nx,pSps->size_x));
+               if(pSps==NULL)printf("Error: calloc(), createPmrChannel()\n");
+               pSps->calcAdjust=(gain_fir_lpf_3K_1);
+               pSps->inputGain=(1*M_Q8);
+               pSps->outputGain=(1*M_Q8);
+               
+       }
+
+       pSps->nextSps=NULL;
+
+       #if XPMR_DEBUG0 == 1
+       {
+               TRACEX((" configure tracing\n"));
+           t_tdet      *ptdet;
+               
+               pChan->rxCtcss->pDebug0=calloc(numSamples,2);
+               pChan->rxCtcss->pDebug1=calloc(numSamples,2);
+               pChan->rxCtcss->pDebug2=calloc(numSamples,2);
+
+               for(i=0;i<CTCSS_NUM_CODES;i++){
+                       ptdet=&(pChan->rxCtcss->tdet[i]);
+                       ptdet->pDebug0=calloc(numSamples,2);
+                       ptdet->pDebug1=calloc(numSamples,2);
+                       ptdet->pDebug2=calloc(numSamples,2);
+               }
+
+               // buffer, 2 bytes per sample, and 16 channels
+               pChan->prxDebug=calloc(numSamples*16,2);
+               pChan->ptxDebug=calloc(numSamples*16,2);
+       }
+       #endif
+
+       TRACEX((" createPmrChannel() end\n"));
+
+       return pChan;
+}
+/*     
+*/
+i16 destroyPmrChannel(t_pmr_chan *pChan)
+{
+       t_pmr_sps       *pmr_sps, *tmp_sps;
+
+       TRACEX(("destroyPmrChannel()\n"));
+
+       free(pChan->pRxDemod);
+       free(pChan->pRxNoise);
+       free(pChan->pRxBase);
+       free(pChan->pRxHpf);
+       free(pChan->pRxLsd);
+       free(pChan->pRxSpeaker);
+       free(pChan->pRxDcTrack);
+       if(pChan->pRxLsdLimit)free(pChan->pRxLsdLimit);
+       free(pChan->pTxBase);
+       free(pChan->pTxHpf);
+       free(pChan->pTxPreEmp);
+       free(pChan->pTxLimiter);
+       free(pChan->pTxLsd);
+       free(pChan->pTxLsdLpf);
+       if(pChan->pTxComposite)free(pChan->pTxComposite);
+       free(pChan->pTxCode);
+       free(pChan->pTxOut);
+
+       if(pChan->pSigGen0)free(pChan->pSigGen0);
+       if(pChan->pSigGen1)free(pChan->pSigGen1);
+
+       #if XPMR_DEBUG0 == 1
+       free(pChan->pTxPttIn);
+       free(pChan->pTxPttOut);
+       if(pChan->prxDebug)free(pChan->prxDebug);
+       if(pChan->ptxDebug)free(pChan->ptxDebug);
+       free(pChan->rxCtcss->pDebug0);
+       free(pChan->rxCtcss->pDebug1);
+
+       free(pChan->prxDebug0);
+       free(pChan->prxDebug1);
+       free(pChan->prxDebug2);
+       free(pChan->prxDebug3);
+
+       free(pChan->ptxDebug0);
+       free(pChan->ptxDebug1);
+       free(pChan->ptxDebug2);
+       free(pChan->ptxDebug3);
+
+       i16 i;
+       for(i=0;i<CTCSS_NUM_CODES;i++)
+       {
+               free(pChan->rxCtcss->tdet[i].pDebug0);
+               free(pChan->rxCtcss->tdet[i].pDebug1);
+               free(pChan->rxCtcss->tdet[i].pDebug2);
+       }
+       #endif
+
+       free(pChan->pRxCtcss);
+
+       pmr_sps=pChan->spsRx;
+                                                       
+       while(pmr_sps)
+       {
+               tmp_sps = pmr_sps;
+               pmr_sps = tmp_sps->nextSps;
+               destroyPmrSps(tmp_sps);
+       }
+
+       free(pChan);
+
+       return 0;
+}
+/*
+*/
+t_pmr_sps *createPmrSps(void)
+{
+       t_pmr_sps  *pSps;
+
+       TRACEX(("createPmrSps()\n"));
+
+       pSps = (t_pmr_sps *)calloc(sizeof(t_pmr_sps),1);
+
+       if(!pSps)printf("Error: createPmrSps()\n");
+
+       // pSps->x=calloc(pSps->nx,pSps->size_x);
+
+       return pSps;
+}
+/*
+*/
+i16 destroyPmrSps(t_pmr_sps  *pSps)
+{
+       TRACEX(("destroyPmrSps(%i)\n",pSps->index));
+
+       if(pSps->x!=NULL)free(pSps->x);
+       free(pSps);
+       return 0;
+}
+/*     
+       PmrRx does the whole buffer
+*/
+i16 PmrRx(t_pmr_chan *pChan, i16 *input, i16 *output)
+{
+       int i,ii;
+       t_pmr_sps *pmr_sps;
+
+       TRACEX(("PmrRx() %i\n",pChan->frameCountRx));
+
+       if(pChan==NULL){
+               printf("PmrRx() pChan == NULL\n");
+               return 1;
+       }
+
+       pChan->frameCountRx++;
+
+       pmr_sps=pChan->spsRx;           // first sps
+       pmr_sps->source=input;
+
+       if(output!=NULL)pChan->spsRxOut->sink=output;    //last sps
+
+       #if 0
+       if(pChan->inputBlanking>0)
+       {
+               pChan->inputBlanking-=pChan->nSamplesRx;
+               if(pChan->inputBlanking<0)pChan->inputBlanking=0;
+               for(i=0;i<pChan->nSamplesRx*6;i++)
+                       input[i]=0;
+       }
+       #endif
+       
+       // || (pChan->radioDuplex && (pChan->pttIn || pChan->pttOut)))
+       if(pChan->rxCpuSaver && !pChan->rxCarrierDetect) 
+       {
+               if(pChan->spsRxHpf)pChan->spsRxHpf->enabled=0;
+               if(pChan->spsRxDeEmp)pChan->spsRxDeEmp->enabled=0;
+       }
+       else
+       {
+               if(pChan->spsRxHpf)pChan->spsRxHpf->enabled=1;
+               if(pChan->spsRxDeEmp)pChan->spsRxDeEmp->enabled=1;
+       }
+
+       i=0;
+       while(pmr_sps!=NULL && pmr_sps!=0)
+       {
+               TRACEX(("PmrRx() sps %i\n",i++));
+               pmr_sps->sigProc(pmr_sps);
+               pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+               //pmr_sps=NULL; // sph maw
+       }
+
+       #define XPMR_VOX_HANGTIME       2000
+       
+       if(pChan->rxCdType==CD_XPMR_VOX)
+       {
+               if(pChan->spsRxVox->compOut)
+               {
+                       pChan->rxVoxTimer=XPMR_VOX_HANGTIME;    //VOX HangTime in ms
+               }
+               if(pChan->rxVoxTimer>0)
+               {
+                       pChan->rxVoxTimer-=MS_PER_FRAME;
+                       pChan->rxCarrierDetect=1;
+               }
+               else
+               {
+                       pChan->rxVoxTimer=0;
+                       pChan->rxCarrierDetect=0;
+               }
+       }
+       else
+       {
+               pChan->rxCarrierDetect=!pChan->spsRx->compOut;
+       }
+
+       if( !pChan->rxCpuSaver || pChan->rxCarrierDetect 
+               || pChan->rxCtcss->decode!=-1) ctcss_detect(pChan);
+
+       #if XPMR_DEBUG0 == 1
+       // TRACEX(("Write file.\n"));
+       ii=0;
+       if(pChan->b.rxCapture)
+       {
+               for(i=0;i<pChan->nSamplesRx;i++)
+               {
+                       pChan->prxDebug[ii++]=input[i*2*6];                                                                                                             // input data
+                       pChan->prxDebug[ii++]=output[i];                                                                                                                // output data
+                       pChan->prxDebug[ii++]=pChan->rxCarrierDetect*M_Q14;                                                                             // carrier detect
+                       if(pChan->rxCtcss)
+                               pChan->prxDebug[ii++]=pChan->rxCtcss->decode*M_Q15/CTCSS_NUM_CODES;                                     // decoded ctcss
+                       else
+                               pChan->prxDebug[ii++]=0;                                                                                                                        
+       
+                       pChan->prxDebug[ii++]=pChan->pRxNoise[i];                                                                                               // rssi
+                       pChan->prxDebug[ii++]=pChan->pRxBase[i];                                                                                                // decimated, low pass filtered
+                       pChan->prxDebug[ii++]=pChan->pRxHpf[i];                                                                                                 // output to network
+                       pChan->prxDebug[ii++]=pChan->pRxSpeaker[i];
+       
+                       pChan->prxDebug[ii++]=pChan->pRxLsd[i];                                                                                                 // CTCSS Filtered
+                       pChan->prxDebug[ii++]=pChan->pRxDcTrack[i];                                                                                             // DC Restoration
+                       pChan->prxDebug[ii++]=pChan->pRxLsdLimit[i];                                                                                    // Amplitude Limited
+                       
+                       //pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex+1].pDebug0[i];   // Upper Adjacent CTCSS Code
+                       pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug0[i];               // Primary CTCSS Code
+                       pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug1[i];               // dv/dt of decoder output
+                       pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex].pDebug2[i];
+
+                       //pChan->prxDebug[ii++]=pChan->rxCtcss->tdet[pChan->rxCtcss->testIndex-1].pDebug0[i];           // Lower Adjacent CTCSS Code
+                       
+                       pChan->prxDebug[ii++]=pChan->prxDebug1[i];              // Measure Output for VOX
+                       pChan->prxDebug[ii++]=pChan->prxDebug2[i];              // Measure Output for Tuning
+               }
+       }
+       #endif
+
+       return 0;
+}
+/*     
+       PmrTx does the whole buffer
+*/
+i16 PmrTx(t_pmr_chan *pChan, i16 *input, i16 *output)
+{
+       int i, hit=0;
+       t_pmr_sps *pmr_sps;
+
+       pChan->frameCountTx++;
+
+       TRACEX(("PmrTx() %i\n",pChan->frameCountTx));
+
+       if(pChan==NULL){
+               printf("PmrTx() pChan == NULL\n");
+               return 1;
+       }
+
+       if(pChan->b.startSpecialTone)
+       {
+               pChan->b.startSpecialTone=0;
+               pChan->spsSigGen1->option=1;
+               pChan->spsSigGen1->enabled=1;
+               pChan->b.doingSpecialTone=1;
+       } else if(pChan->b.stopSpecialTone)
+       {
+               pChan->b.stopSpecialTone=0;
+               pChan->spsSigGen1->option=0;
+               pChan->b.doingSpecialTone=0;
+               pChan->spsSigGen1->enabled=0;
+       } else if(pChan->b.doingSpecialTone)
+       {
+               pChan->spsSigGen1->sink=output;
+               pChan->spsSigGen1->sigProc(pChan->spsSigGen1);
+               for(i=0;i<(pChan->nSamplesTx*2*6);i+=2)output[i+1]=output[i];
+               return 0;
+       }
+
+       // handle transmitter ptt input
+       hit=0;
+       if( pChan->txPttIn && pChan->txState==0)
+       {
+               pChan->txState = 2;
+               pChan->txPttOut=1;
+               pChan->spsSigGen0->freq=pChan->txCtcssFreq*10;
+               pChan->spsSigGen0->option=1;
+               pChan->spsSigGen0->enabled=1;
+               if(pChan->spsTxOutA)pChan->spsTxOutA->enabled=1;
+               if(pChan->spsTxOutB)pChan->spsTxOutB->enabled=1;
+               if(pChan->spsTxLsdLpf)pChan->spsTxLsdLpf->enabled=1;
+               TRACEX((" TxOn\n"));
+       }
+       else if(!pChan->txPttIn && pChan->txState==2)
+       {
+               if( pChan->txTocType==TOC_NONE || !pChan->txCtcssFreq )
+               {
+                       hit=1;
+                       TRACEX((" Tx Off Immediate.\n"));
+        }
+               else if(pChan->txCtcssFreq && pChan->txTocType==TOC_NOTONE)
+               {
+                       pChan->txState=3;
+                       pChan->txHangTime=TOC_NOTONE_TIME/MS_PER_FRAME;
+                       pChan->spsSigGen0->option=3;
+                       TRACEX((" Tx Turn Off No Tone Start.\n"));
+               }
+               else
+               {
+                       pChan->txState=3;
+                       pChan->txHangTime=0;
+                       pChan->spsSigGen0->option=2;
+                       TRACEX((" Tx Turn Off Phase Shift Start.\n"));
+               }
+       } 
+       else if(pChan->txState==3)
+       {
+               if(pChan->txHangTime)
+               {
+                       if(--pChan->txHangTime==0)hit=1;
+
+               }
+               else if(pChan->txHangTime<=0 && pChan->spsSigGen0->state==0)
+               {       
+                       hit=1;
+                       TRACEX((" Tx Off TOC.\n"));
+               }
+               if(pChan->txPttIn)
+               {
+                       TRACEX((" Tx Key During HangTime\n"));           
+                       if((pChan->txTocType==TOC_PHASE)||(pChan->txTocType==TOC_NONE))
+                       {
+                               pChan->txState = 2;
+                               hit=0;
+                       }
+               }
+       }
+
+       if( pChan->txCpuSaver && !hit && !pChan->txPttIn && !pChan->txPttOut && pChan->txState==0 ) return (1); 
+
+       if(hit)
+       {
+               pChan->txPttOut=0;
+               pChan->txState=0;
+               if(pChan->spsTxLsdLpf)pChan->spsTxLsdLpf->option=3;
+               if(pChan->spsTxOutA)pChan->spsTxOutA->option=3;
+               if(pChan->spsTxOutB)pChan->spsTxOutB->option=3;
+               TRACEX((" Tx Off hit.\n"));
+       }
+
+       if(pChan->spsSigGen0)
+       {
+               pChan->spsSigGen0->sigProc(pChan->spsSigGen0);
+               pmr_sps=pChan->spsSigGen0->nextSps;
+               i=0;
+               while(pmr_sps!=NULL && pmr_sps!=0)
+               {
+                       TRACEX((" PmrTx() subaudible sps %i\n",i++));
+                       //printf(" CTCSS ENCODE %i %i\n",pChan->spsSigGen0->freq,pChan->spsSigGen0->outputGain);
+                       pmr_sps->sigProc(pmr_sps);
+                       pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+               }
+       }
+
+       if(pChan->spsSigGen1 && pChan->spsSigGen1->enabled)
+       {
+               pChan->spsSigGen1->sigProc(pChan->spsSigGen1);
+       }
+
+       // Do Voice
+       pmr_sps=pChan->spsTx;
+       if(!pChan->spsSigGen1->enabled)pmr_sps->source=input;
+       else input=pmr_sps->source;
+
+       if(output!=NULL)
+       {
+               if(pChan->spsTxOutA)pChan->spsTxOutA->sink=output;
+               if(pChan->spsTxOutB)pChan->spsTxOutB->sink=output;
+       }
+
+       i=0;
+       while(pmr_sps!=NULL && pmr_sps!=0)
+       {
+               TRACEX((" PmrTx() sps %i\n",i++));
+               pmr_sps->sigProc(pmr_sps);
+               pmr_sps = (t_pmr_sps *)(pmr_sps->nextSps);
+       }
+
+
+       if(pChan->txMixA==TX_OUT_OFF || !pChan->txPttOut){
+               for(i=0;i<pChan->nSamplesTx*2*6;i+=2)output[i]=0;
+       }
+
+       if(pChan->txMixB==TX_OUT_OFF || !pChan->txPttOut ){
+               for(i=0;i<pChan->nSamplesTx*2*6;i+=2)output[i+1]=0;
+       }
+
+       #if XPMR_DEBUG0 == 1
+       if(pChan->b.txCapture)
+       {
+               i16 ii=0;
+               for(i=0;i<pChan->nSamplesTx;i++)
+               {
+                       pChan->ptxDebug[ii++]=input[i];
+                       pChan->ptxDebug[ii++]=output[i*2*6];
+                       pChan->ptxDebug[ii++]=output[(i*2*6)+1];
+                       pChan->ptxDebug[ii++]=pChan->txPttIn*8192;
+       
+                       pChan->ptxDebug[ii++]=pChan->txPttOut*8192;
+                       if(pChan->txHpfEnable)pChan->ptxDebug[ii++]=pChan->pTxHpf[i];
+                       else pChan->ptxDebug[ii++]=0; 
+                       if(pChan->txPreEmpEnable)pChan->ptxDebug[ii++]=pChan->pTxPreEmp[i];
+                       else pChan->ptxDebug[ii++]=0;
+                       if(pChan->txLimiterEnable)pChan->ptxDebug[ii++]=pChan->pTxLimiter[i];
+                       else pChan->ptxDebug[ii++]=0;
+       
+                       pChan->ptxDebug[ii++]=pChan->pTxLsd[i];
+                       pChan->ptxDebug[ii++]=pChan->pTxLsdLpf[i];
+                       pChan->ptxDebug[ii++]=pChan->pTxComposite[i];
+                       pChan->ptxDebug[ii++]=pChan->pSigGen1[i];
+                                        
+                       #if 1
+                       pChan->ptxDebug[ii++]=pChan->ptxDebug0[i];
+                       pChan->ptxDebug[ii++]=pChan->ptxDebug1[i];
+                       pChan->ptxDebug[ii++]=pChan->ptxDebug2[i];
+                       pChan->ptxDebug[ii++]=pChan->ptxDebug3[i];
+                       #else
+                       pChan->ptxDebug[ii++]=0;
+                       pChan->ptxDebug[ii++]=0;
+                       pChan->ptxDebug[ii++]=0;
+                       pChan->ptxDebug[ii++]=0;
+                       #endif
+               }
+       }
+       #endif
+
+       return 0;
+}
+/* end of file */
diff --git a/channels/xpmr/xpmr.h b/channels/xpmr/xpmr.h
new file mode 100755 (executable)
index 0000000..d51aa6d
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * xpmr.h - for Xelatec Private Mobile Radio Processes
+ * 
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ * 
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.com
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *              
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_H
+#define XPMR_H                 1
+
+#ifdef CHAN_USBRADIO
+#define XPMR_DEBUG0            1
+#define XPMR_TRACE             0
+#else
+#define XPMR_DEBUG0            1
+#define XPMR_TRACE             1
+#endif
+
+#if(XPMR_TRACE == 1)
+#define TRACEX(a) {printf a;}
+#define TRACEXL(a) {printf("%s @ %u : ",__FILE__ ,__LINE__); printf a; }
+#define TRACEXT(a) { struct timeval hack; gettimeofday(&hack,NULL); printf("%ld.",hack.tv_sec%100000); printf("%i : ",(int)hack.tv_usec); printf a; }
+#else                                            
+#define TRACEX(a)
+#define TRACEXL(a)
+#define TRACEXT(a)
+#endif
+
+#define i8     int8_t
+#define u8     u_int8_t
+#define i16            int16_t
+#define u16    u_int16_t
+#define i32            int32_t
+#define u32    u_int32_t
+#define i64    int64_t
+#define u64    u_int64_t
+                       
+#define M_Q24                  0x01000000              //
+#define M_Q23                  0x00800000              //
+#define M_Q22                  0x00400000              //                                      
+#define M_Q21                  0x00200000              //              
+#define M_Q20                  0x00100000              // 1048576
+#define M_Q19                  0x00080000              // 524288                  
+#define M_Q18                  0x00040000              // 262144
+#define M_Q17           0x00020000             // 131072
+#define M_Q16           0x00010000             // 65536
+#define M_Q15           0x00008000             // 32768
+#define M_Q14           0x00004000             // 16384
+#define M_Q13           0x00002000             // 8182
+#define M_Q12           0x00001000             // 4096
+#define M_Q11           0x00000800             // 2048
+#define M_Q10           0x00000400             // 1024
+#define M_Q9            0x00000200             // 512
+#define M_Q8            0x00000100             // 256
+#define M_Q7            0x00000080             // 128
+#define M_Q6            0x00000040             // 64
+#define M_Q5            0x00000020             // 32
+#define M_Q4            0x00000010             // 16
+#define M_Q3            0x00000008             // 16
+#define M_Q2            0x00000004             // 16
+#define M_Q1            0x00000002             // 16
+#define M_Q0            0x00000001             // 16
+
+#define RADIANS_PER_CYCLE              (2*M_PI)
+
+#define SAMPLE_RATE_INPUT       48000
+#define SAMPLE_RATE_NETWORK     8000
+
+#define SAMPLES_PER_BLOCK       160
+#define MS_PER_FRAME            20
+
+#define CTCSS_NUM_CODES                        38
+#define CTCSS_SCOUNT_MUL               100
+#define CTCSS_INTEGRATE                        3932       // 32767*.120 // 120/1000  // 0.120
+#define CTCSS_INPUT_LIMIT              1000
+#define CTCSS_DETECT_POINT             1989
+#define CTCSS_HYSTERSIS                    200
+
+#define CTCSS_TURN_OFF_TIME            160                     // ms
+#define CTCSS_TURN_OFF_SHIFT    240                    // degrees
+#define TOC_NOTONE_TIME                        600                     // ms
+
+#ifndef CHAN_USBRADIO  
+enum {RX_AUDIO_NONE,RX_AUDIO_SPEAKER,RX_AUDIO_FLAT};
+enum {TX_AUDIO_NONE,TX_AUDIO_FLAT,TX_AUDIO_FILTERED,TX_AUDIO_PROC};
+enum {CD_IGNORE,CD_XPMR_NOISE,CD_XPMR_VOX,CD_HID,CD_HID_INVERT};
+enum {SD_IGNORE,SD_HID,SD_HID_INVERT,SD_XPMR};                                  // no,external,externalinvert,software
+enum {RX_KEY_CARRIER,RX_KEY_CARRIER_CODE};
+enum {TX_OUT_OFF,TX_OUT_VOICE,TX_OUT_LSD,TX_OUT_COMPOSITE,TX_OUT_AUX};
+enum {TOC_NONE,TOC_PHASE,TOC_NOTONE};
+#endif
+
+/*
+       one structure for each ctcss tone to decode 
+*/
+typedef struct
+{
+       i16 counter;                    // counter to next sample
+       i16 counterFactor;              // full divisor used to increment counter
+       i16 binFactor;
+       i16 fudgeFactor;
+       i16 peak;                               // peak amplitude now   maw sph now
+       i16 enabled;
+       i16 state;                              // dead, running, error                          
+       i16 zIndex;                             // z bucket index
+       i16 z[4];                               // maw sph today
+       i16 zi;
+       i16 dvu;
+       i16 dvd;
+       i16 zd;
+       i16 setpt;
+       i16 hyst;
+       i16 decode;
+       i16 diffpeak;
+       i16 debug;                              // value held from last pass
+       i16 *pDebug0;                   // pointer to debug output
+       i16 *pDebug1;                   // pointer to debug output
+       i16 *pDebug2;                   // pointer to debug output
+
+} t_tdet;
+
+typedef struct
+{
+       i16 enabled;                                            // if 0 none, 0xFFFF all tones, or single tone
+       i16 *input;
+       i16 clamplitude;
+       i16 center;
+       i16 decode;                                                     // current ctcss decode index
+       i32 BlankingTimer;
+       u32 TurnOffTimer;
+       t_tdet tdet[CTCSS_NUM_CODES];   
+       i16 gain;
+       i16 limit;
+       i16 *pDebug0;
+       i16 *pDebug1;
+       i16 *pDebug2;
+       i16 testIndex;
+       i16 multiFreq;
+       i8 relax;
+
+} t_dec_ctcss;
+
+typedef struct
+{
+       i16 enabled;                                            // if 0 none, 0xFFFF all tones, or single tone
+       i16 clamplitude;
+       i16 center;
+       i16 decode;                                             // current ctcss decode value
+       i32 BlankingTimer;
+       u32 TurnOffTimer;
+       i16 gain;
+       i16 limit;
+       i16 *pDebug0;
+       i16 *pDebug1;
+       i16 rxPolarity;
+} t_dec_dcs;
+
+/*
+       Low Speed Data decoding both polarities
+*/
+typedef struct
+{
+       i16 counter;                    // counter to next sample
+       i16 synced;
+       u32 syncCorr[2];
+       u32 data[2];
+       i16 state;                              // disabled, enabled,
+       i16 decode;
+       i16 debug;
+
+       i16 polarity;
+       u32 frameNum;
+
+       u16 area;
+       u16 chan;
+       u16 home;
+       u16 id;
+       u16 free;
+
+       u16 crc;
+       i16 rssi;
+
+} t_decLsd;
+
+
+/* general purpose pmr signal processing element */
+
+struct t_pmr_chan;
+
+typedef struct t_pmr_sps
+{
+       i16  index;                     // unique to each instance
+
+       i16  enabled;           // enabled/disabled
+
+       struct t_pmr_chan *parentChan;
+       
+       i16  *source;           // source buffer
+       i16  *sourceB;          // source buffer B
+       i16  *sink;                     // sink buffer
+
+       i16  numChanOut;        // allows output direct to interleaved buffer
+       i16  selChanOut;
+
+       u32  ticks;
+
+       void *buff;                     // this structure's internal buffer
+
+       i16  *debugBuff0;       // debug buffer
+       i16  *debugBuff1;       // debug buffer
+       i16  *debugBuff2;       // debug buffer
+       i16  *debugBuff3;       // debug buffer
+
+       i16  nSamples;          // number of samples in the buffer
+
+       u32      buffSize;              // buffer maximum index
+       u32  buffInIndex;       // index to current input point
+       u32  buffOutIndex;      // index to current output point
+       u32  buffLead;          // lead of input over output through cb
+
+       i16  decimate;          // decimation or interpolation factor (could be put in coef's)
+       i16  interpolate;
+       i16      decimator;             // like the state this must be saved between calls (could be put in x's)
+
+       u32  sampleRate;    // in Hz for elements in this structure
+       u32  freq;                      // in 0.1 Hz
+
+       i16  measPeak;          // do measure Peak
+       i16  amax;                      // buffer amplitude maximum
+       i16  amin;                      // buffer amplitude minimum
+       i16  apeak;                     // buffer amplitude peak value (peak to peak)/2
+       i16  setpt;                     // amplitude set point for amplitude comparator
+       i16  hyst;                      // hysterysis for amplitude comparator
+       i16  compOut;           // amplitude comparator output
+
+       i32  discounteru;       // amplitude detector integrator discharge counter upper
+       i32  discounterl;       // amplitude detector integrator discharge counter lower
+       i32  discfactor;        // amplitude detector integrator discharge factor
+
+       i16  err;                       // error condition
+       i16  option;            // option / request zero
+       i16  state;         // stopped, start, stopped assumes zero'd
+
+       i16  cleared;           // output buffer cleared
+
+       i16  delay;
+       i16  decode;
+
+       i32  inputGain;         // apply to input data   ? in Q7.8 format
+       i32  inputGainB;        // apply to input data   ? in Q7.8 format
+       i32  outputGain;        // apply to output data  ? in Q7.8 format
+       i16  mixOut;
+       i16  monoOut;
+
+       i16  filterType;        // iir, fir, 1, 2, 3, 4 ...
+
+       i16 (*sigProc)(struct t_pmr_sps *sps);  // function to call
+
+       i32      calcAdjust;    // final adjustment
+       i16      nx;                    // number of x history elements
+       i16  ncoef;                     // number of coefficients
+       i16  size_x;            // size of each x history element
+       i16  size_coef;         // size of each coefficient
+       void  *x;                       // history registers
+       void  *x2;                      // history registers, 2nd bank 
+       void  *coef;            // coefficients
+       void  *coef2;           // coefficients 2
+
+       void  *nextSps;         // next Sps function
+
+} t_pmr_sps;
+
+/*
+       pmr channel
+*/
+typedef struct t_pmr_chan
+{
+       i16 index;                              // which one
+       i16 enabled;                    // enabled/disabled
+       i16 status;                             // ok, error, busy, idle, initializing
+
+       i16 nSamplesRx;                 // max frame size
+       i16 nSamplesTx;
+
+       i32 inputSampleRate;    // in S/s  48000
+       i32 baseSampleRate;             // in S/s   8000 
+
+       i16 inputGain;
+       i16 inputOffset;
+
+       u32  frameCountRx;              // number processed
+       u32  frameCountTx;
+
+       i32  txHangTime;
+       i32  txTurnOff;
+
+       i16 rxDC;                           // average DC value of input
+       i16 rxSqSet;                    // carrier squelch threshold
+       i16 rxSqHyst;                   // carrier squelch hysterysis
+       i16 rxRssi;                             // current Rssi level
+       i16 rxQuality;                  // signal quality metric
+       i16 rxCarrierDetect;    // carrier detect
+       i16 rxCdType;
+       i16 rxExtCarrierDetect; 
+       i32 inputBlanking;      // Tx pulse eliminator
+
+       i16 rxDemod;            // see enum
+       i16 txMod;                      //
+
+       i16 rxNoiseSquelchEnable;
+       i16 rxHpfEnable;
+       i16 rxDeEmpEnable;
+       i16 rxCenterSlicerEnable;
+       i16 rxCtcssDecodeEnable;
+       i16 rxDcsDecodeEnable;
+       i16 rxDelayLineEnable;
+
+       i16 txHpfEnable;
+       i16 txLimiterEnable;
+       i16 txPreEmpEnable;
+       i16 txLpfEnable;
+
+       char radioDuplex;
+
+       struct {
+               unsigned pmrNoiseSquelch:1;
+               unsigned rxHpf:1;
+               unsigned txHpf:1;
+               unsigned txLpf:1;
+               unsigned rxDeEmphasis:1;
+               unsigned txPreEmphasis:1;
+               unsigned startSpecialTone:1;
+               unsigned stopSpecialTone:1;
+               unsigned doingSpecialTone:1;
+               unsigned extCarrierDetect:1;
+               unsigned txCapture:1;
+               unsigned rxCapture:1;
+       }b;
+
+       i16 dummy;
+
+       i32 txScramFreq;
+       i32 rxScramFreq;
+
+       i16 gainVoice;
+       i16 gainSubAudible;
+
+       i16 txMixA;                             // Off, Ctcss, Voice, Composite
+       i16 txMixB;                             // Off, Ctcss, Voice, Composite
+       
+       i16 rxMuting;
+
+       i16 rxCpuSaver;
+       i16 txCpuSaver;
+
+       i8      rxSqMode;                       // 0 open, 1 carrier, 2 coded
+
+       i8      cdMethod;
+
+       i16     rxSquelchPoint;
+
+       i16 rxCarrierPoint;
+       i16 rxCarrierHyst;
+
+       i16 rxCtcssMap[CTCSS_NUM_CODES];
+       
+       i16 txCtcssTocShift;
+       i16 txCtcssTocTime;
+       i8      txTocType;
+
+       float txCtcssFreq;
+       float rxCtcssFreq;
+       float rxInputGain;
+       
+       i16 rxCtcssIndex;
+
+       i16 txPttIn;                    // from external request
+       i16 txPttOut;                   // to radio hardware
+
+       i16 bandwidth;                  // wide/narrow
+       i16 txCompand;                  // type
+       i16 rxCompand;                  // 
+       
+       i16 txEqRight;                  // muted, flat, pre-emp limited filtered
+       i16 txEqLeft;
+
+       i16 txPotRight;                 // 
+       i16 txPotLeft;                  //
+
+       i16 rxPotRight;                 // 
+       i16 rxPotLeft;                  //
+
+       i16 function;
+
+       i16 txState;                    // off,settling,on,hangtime,turnoff
+
+       t_pmr_sps *spsMeasure;  // measurement block
+
+       t_pmr_sps *spsRx;                       // 1st signal processing struct
+       t_pmr_sps *spsRxLsd;
+       t_pmr_sps *spsRxDeEmp;
+       t_pmr_sps *spsRxHpf;
+       t_pmr_sps *spsRxVox;
+       t_pmr_sps *spsDelayLine;        // Last signal processing struct
+       t_pmr_sps *spsRxOut;            // Last signal processing struct
+
+       t_pmr_sps *spsTx;                       // 1st  signal processing struct
+
+       t_pmr_sps *spsTxLsdLpf;
+       t_pmr_sps *spsTxOutA;           // Last signal processing struct
+
+       t_pmr_sps *spsTxOutB;           // Last signal processing struct
+
+       t_pmr_sps *spsSigGen0;          // ctcss
+       t_pmr_sps *spsSigGen1;          // test and other tones
+
+       // tune tweaks
+
+       i32 rxVoxTimer;                         // Vox Hang Timer
+
+       i16     *prxSquelchAdjust;
+
+       // i16  *prxNoiseMeasure;       // for autotune
+       // i32  *prxNoiseAdjust;
+
+       i16     *prxVoiceMeasure;
+       i32     *prxVoiceAdjust;        
+       
+       i16     *prxCtcssMeasure;
+       i32     *prxCtcssAdjust;                 
+       
+       i16     *ptxVoiceAdjust;                // from calling application
+       i32     *ptxCtcssAdjust;                // from calling application
+
+       i32     *ptxLimiterAdjust;              // from calling application
+
+       i16 *pRxDemod;                          // buffers
+       i16 *pRxBase;                           // decimated lpf input
+       i16 *pRxNoise;   
+       i16 *pRxLsd;                            // subaudible only 
+       i16 *pRxHpf;                            // subaudible removed
+       i16 *pRxDeEmp;                  // EIA Audio
+       i16 *pRxSpeaker;                // EIA Audio
+       i16 *pRxDcTrack;                        // DC Restored LSD
+       i16 *pRxLsdLimit;               // LSD Limited
+       i16 *pRxCtcss;                          //
+       i16 *pRxSquelch;
+
+       i16 *pTxBase;                           // input data
+       i16 *pTxHpf;
+       i16 *pTxPreEmp;
+       i16 *pTxLimiter;
+       i16 *pTxLsd;
+       i16 *pTxLsdLpf;
+       i16 *pTxComposite;
+       i16 *pTxMod;                    // upsampled, low pass filtered
+       
+       i16 *pTxOut;                    // 
+       
+       i16 *pTxPttIn;
+       i16 *pTxPttOut;
+       i16 *pTxHang;
+       i16 *pTxCode;
+
+       i16     *pSigGen0;
+       i16     *pSigGen1;
+
+       i16 *pAlt0;
+       i16 *pAlt1;
+
+       i16 *pNull;
+
+       i16 *prxDebug;                  // consolidated debug buffer
+       i16 *ptxDebug;                  // consolidated debug buffer
+
+       i16 *prxDebug0;
+       i16 *prxDebug1;
+       i16 *prxDebug2;
+       i16 *prxDebug3;
+
+       i16 *ptxDebug0;
+       i16 *ptxDebug1;
+       i16 *ptxDebug2;
+       i16 *ptxDebug3;
+
+       t_dec_ctcss     *rxCtcss;
+                         
+       i16 clamplitudeDcs;
+       i16 centerDcs;
+       u32 dcsBlankingTimer;
+       i16 dcsDecode;                                                  // current dcs decode value
+
+       i16 clamplitudeLsd;
+       i16 centerLsd;
+       t_decLsd decLsd[2];                                             // for both polarities
+
+} t_pmr_chan;
+
+static i16                     TxTestTone(t_pmr_chan *pChan, i16 function);
+
+t_pmr_chan     *createPmrChannel(t_pmr_chan *tChan, i16 numSamples);
+t_pmr_sps      *createPmrSps(void);
+i16                    destroyPmrChannel(t_pmr_chan *pChan);
+i16                    destroyPmrSps(t_pmr_sps  *pSps);
+i16            pmr_rx_frontend(t_pmr_sps *mySps);
+i16            pmr_gp_fir(t_pmr_sps *mySps);
+i16            pmr_gp_iir(t_pmr_sps *mySps);
+i16            gp_inte_00(t_pmr_sps *mySps);
+i16            gp_diff(t_pmr_sps *mySps);
+i16            CenterSlicer(t_pmr_sps *mySps);
+i16            ctcss_detect(t_pmr_chan *pmrChan);
+i16            SoftLimiter(t_pmr_sps *mySps);
+i16                    SigGen(t_pmr_sps *mySps);
+i16            pmrMixer(t_pmr_sps *mySps);
+i16            DelayLine(t_pmr_sps *mySps);
+i16                    PmrRx(t_pmr_chan *PmrChan, i16 *input, i16 *output);
+i16                    PmrTx(t_pmr_chan *PmrChan, i16 *input, i16 *output);
+i16            CtcssFreqIndex(float freq);
+i16            MeasureBlock(t_pmr_sps *mySps);
+#endif /* ! XPMR_H */
+
+/* end of file */
+
+
+
diff --git a/channels/xpmr/xpmr_coef.h b/channels/xpmr/xpmr_coef.h
new file mode 100755 (executable)
index 0000000..4b7274e
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ * xpmr_coef.h - for Xelatec Private Mobile Radio Processes
+ * 
+ * All Rights Reserved. Copyright (C)2007, Xelatec, LLC
+ * 
+ * 20070808 1235 Steven Henke, W9SH, sph@xelatec.com
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *              
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ * Some filter coeficients via 'WinFilter' http://www.winfilter.20m.com.
+ *
+ */
+
+/*! \file
+ *
+ * \brief Private Land Mobile Radio Channel Voice and Signaling Processor
+ *
+ * \author Steven Henke, W9SH <sph@xelatec.com> Xelatec, LLC
+ */
+
+#ifndef XPMR_COEF_H
+#define XMPR_COEF_H 1
+
+// frequencies in 0.1 Hz
+const u32 dtmf_row[] =
+{
+       6970,  7700,  8520,  9410
+};
+const u32 dtmf_col[] =
+{
+       12090, 13360, 14770, 16330
+};
+
+const i16 coef_dcs_rx  = 1488;                 // dcs rx data divisor for oversampling 8000/134.4
+const i16 coef_dcs_tx  = 5952;                 // dcs tx data divisor
+
+const i16 coef_lsd_div  = 672;                 // low speed data divisor
+const u32 coef_lsd_sync = 0x158;        // 000101011000
+const u32 coef_lsd_sync_pattern[] = {0x0000000F, 0x0F0FF000};
+
+#define CTCSS_COEF_INT         120
+#define CTCSS_SAMPLE_RATE   8000
+#define TDIV(x) ((CTCSS_SAMPLE_RATE*1000/x)+5)/10
+
+i32 coef_ctcss[4][5]=
+{
+       // freq, divisor, integrator, filter
+       {770,TDIV(770),CTCSS_COEF_INT,0,0},
+       {1000,TDIV(1000),CTCSS_COEF_INT,0,0},
+       {1035,TDIV(1035),CTCSS_COEF_INT,0,0},
+       {0,0,0,0}
+};
+
+
+i16 coef_ctcss_div[]=
+{
+2985,    // 00   067.0
+2782,    // 01   071.9
+2688,    // 02   074.4
+2597,    // 03   077.0
+2509,    // 04   079.7
+2424,    // 05   082.5
+2342,    // 06   085.4
+2260,    // 07   088.5
+2186,    // 08   091.5
+2110,    // 09   094.8
+2053,    // 10   097.4
+2000,    // 11   100.0
+1932,    // 12   103.5
+1866,    // 13   107.2
+1803,    // 14   110.9
+1742,    // 15   114.8
+1684,    // 16   118.8
+1626,    // 17   123.0
+1571,    // 18   127.3
+1517,    // 19   131.8
+1465,    // 20   136.5
+1415,    // 21   141.3
+1368,    // 22   146.2
+1321,    // 23   151.4
+1276,    // 24   156.7
+1233,    // 25   162.2
+1191,    // 26   167.9
+1151,    // 27   173.8
+1112,    // 28   179.9
+1074,    // 29   186.2
+1037,    // 30   192.8
+983,    // 31   203.5
+949,    // 32   210.7
+917,    // 33   218.1
+886,    // 34   225.7
+856,    // 35   233.6
+827,    // 36   241.8
+799     // 37   250.3
+};
+
+float freq_ctcss[]=
+{
+067.0,    // 00   
+071.9,    // 01   
+074.4,    // 02   
+077.0,    // 03   
+079.7,    // 04   
+082.5,    // 05   
+085.4,    // 06   
+088.5,    // 07   
+091.5,    // 08   
+094.8,    // 09   
+097.4,    // 10   
+100.0,    // 11   
+103.5,    // 12   
+107.2,    // 13   
+110.9,    // 14   
+114.8,    // 15   
+118.8,    // 16   
+123.0,    // 17   
+127.3,    // 18   
+131.8,    // 19   
+136.5,    // 20   
+141.3,    // 21   
+146.2,    // 22   
+151.4,    // 23   
+156.7,    // 24   
+162.2,    // 25   
+167.9,    // 26   
+173.8,    // 27   
+179.9,    // 28   
+186.2,    // 29   
+192.8,    // 30   
+203.5,    // 31  
+210.7 ,    // 32  
+218.1 ,    // 33  
+225.7 ,    // 34  
+233.6 ,    // 35  
+241.8 ,    // 36  
+250.3      // 37  
+};
+
+/*
+       noise squelch carrier detect filter
+*/
+static const int16_t taps_fir_bpf_noise_1 = 66;
+static const int32_t gain_fir_bpf_noise_1 = 65536;
+static const int16_t coef_fir_bpf_noise_1[] = { 
+      139,
+     -182,
+     -269,
+      -66,
+       56,
+       59,
+      250,
+      395,
+      -80,
+     -775,
+     -557,
+      437,
+      779,
+      210,
+      -17,
+      123,
+     -692,
+    -1664,
+     -256,
+     2495,
+     2237,
+    -1018,
+    -2133,
+     -478,
+    -1134,
+    -2711,
+     2642,
+    10453,
+     4010,
+    -14385,
+    -16488,
+     6954,
+    23030,
+     6954,
+    -16488,
+    -14385,
+     4010,
+    10453,
+     2642,
+    -2711,
+    -1134,
+     -478,
+    -2133,
+    -1018,
+     2237,
+     2495,
+     -256,
+    -1664,
+     -692,
+      123,
+      -17,
+      210,
+      779,
+      437,
+     -557,
+     -775,
+      -80,
+      395,
+      250,
+       59,
+       56,
+      -66,
+     -269,
+     -182,
+      139,
+      257
+};
+/*
+       tbd
+*/
+static const int16_t taps_fir_lpf_3K_1 = 66;
+static const int32_t gain_fir_lpf_3K_1 = 131072;
+static const int16_t coef_fir_lpf_3K_1[] = { 
+      259,
+       58,
+     -185,
+     -437,
+     -654,
+     -793,
+     -815,
+     -696,
+     -434,
+      -48,
+      414,
+      886,
+     1284,
+     1523,
+     1529,
+     1254,
+      691,
+     -117,
+    -1078,
+    -2049,
+    -2854,
+    -3303,
+    -3220,
+    -2472,
+     -995,
+     1187,
+     3952,
+     7086,
+    10300,
+    13270,
+    15672,
+    17236,
+    17778,
+    17236,
+    15672,
+    13270,
+    10300,
+     7086,
+     3952,
+     1187,
+     -995,
+    -2472,
+    -3220,
+    -3303,
+    -2854,
+    -2049,
+    -1078,
+     -117,
+      691,
+     1254,
+     1529,
+     1523,
+     1284,
+      886,
+      414,
+      -48,
+     -434,
+     -696,
+     -815,
+     -793,
+     -654,
+     -437,
+     -185,
+       58,
+      259,
+      393
+};
+
+/**************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.250000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_250_11_64 = 64;
+static const int32_t gain_fir_lpf_250_11_64 = 262144;
+static const int16_t coef_fir_lpf_250_11_64[] = 
+{
+      366,
+       -3,
+     -418,
+     -865,
+    -1328,
+    -1788,
+    -2223,
+    -2609,
+    -2922,
+    -3138,
+    -3232,
+    -3181,
+    -2967,
+    -2573,
+    -1988,
+    -1206,
+     -228,
+      937,
+     2277,
+     3767,
+     5379,
+     7077,
+     8821,
+    10564,
+    12259,
+    13855,
+    15305,
+    16563,
+    17588,
+    18346,
+    18812,
+    18968,
+    18812,
+    18346,
+    17588,
+    16563,
+    15305,
+    13855,
+    12259,
+    10564,
+     8821,
+     7077,
+     5379,
+     3767,
+     2277,
+      937,
+     -228,
+    -1206,
+    -1988,
+    -2573,
+    -2967,
+    -3181,
+    -3232,
+    -3138,
+    -2922,
+    -2609,
+    -2223,
+    -1788,
+    -1328,
+     -865,
+     -418,
+       -3,
+      366,
+      680
+};
+
+// de-emphasis integrator 300 Hz with 8KS/s
+// a0, b1
+static const int16_t taps_int_lpf_300_1_2 = 2;
+static const int32_t gain_int_lpf_300_1_2 = 8182;
+static const int16_t coef_int_lpf_300_1_2[]={
+6878,
+25889
+};
+
+// pre-emphasis differentiator 4000 Hz with 8KS/s
+// a0,a1,b0,
+static const int16_t taps_int_hpf_4000_1_2 = 2;
+static const int32_t gain_int_hpf_4000_1_2 = 16384;
+static const int16_t coef_int_hpf_4000_1_2[]={
+17610,
+-17610,
+2454
+};
+
+
+/*
+       ltr crc table
+       from http://www.radioreference.com/forums/showthread.php?t=24126
+*/
+
+static const u8        ltr_table[]=
+{
+0x38, // 00 Area               0111000
+0x1c, // 01    Channel 4       0011100
+0x0e, // 02 Channel 3  0001110
+0x46, // 03 Channel 2  1000110
+0x23, // 04 Channel 1  0100011
+0x51, // 05 Channel 0  1010001
+0x68, // 06 Home 4     1101000
+0x75, // 07 Home 3             1110101
+0x7a, // 08 Home 2             1111010
+0x3d, // 09 Home 1             0111101
+0x1f, // 10 Home 0             0011111
+0x4f, // 11 Group 7    1001111
+0x26, // 12 Group 6    0100110
+0x52, // 13 Group 5    1010010
+0x29, // 14 Group 4    0101001
+0x15, // 15 Group 3    0010101
+0x0d, // 16 Group 2    0001101
+0x45, // 17 Group 1    1000101
+0x62, // 18 Group 0    1100010
+0x31, // 19 Free 4             0110001
+0x19, // 20 Free 3             0011001
+0x0d, // 21 Free 2             0001101
+0x07, // 22 Free 1             0000111
+0x43  // 23 Free 0             1000011
+};
+
+static const i16 bitWeight[]=
+{
+0,   // 0
+1,   // 1
+1,   // 2
+2,   // 3
+1,   // 4
+2,   // 5
+2,   // 6
+3,   // 7
+1,   // 8
+2,   // 9
+2,   // 10
+3,   // 11
+2,   // 12
+3,   // 13
+3,   // 14
+4,   // 15
+1,   // 16
+2,   // 17
+2,   // 18
+3,   // 19
+2,   // 20
+3,   // 21
+3,   // 22
+4,   // 23
+2,   // 24
+3,   // 25
+3,   // 26
+4,   // 27
+3,   // 28
+4,   // 29
+4,   // 30
+5,   // 31
+1,   // 32
+2,   // 33
+2,   // 34
+3,   // 35
+2,   // 36
+3,   // 37
+3,   // 38
+4,   // 39
+2,   // 40
+3,   // 41
+3,   // 42
+4,   // 43
+3,   // 44
+4,   // 45
+4,   // 46
+5,   // 47
+2,   // 48
+3,   // 49
+3,   // 50
+4,   // 51
+3,   // 52
+4,   // 53
+4,   // 54
+5,   // 55
+3,   // 56
+4,   // 57
+4,   // 58
+5,   // 59
+4,   // 60
+5,   // 61
+5,   // 62
+6,   // 63
+1,   // 64
+2,   // 65
+2,   // 66
+3,   // 67
+2,   // 68
+3,   // 69
+3,   // 70
+4,   // 71
+2,   // 72
+3,   // 73
+3,   // 74
+4,   // 75
+3,   // 76
+4,   // 77
+4,   // 78
+5,   // 79
+2,   // 80
+3,   // 81
+3,   // 82
+4,   // 83
+3,   // 84
+4,   // 85
+4,   // 86
+5,   // 87
+3,   // 88
+4,   // 89
+4,   // 90
+5,   // 91
+4,   // 92
+5,   // 93
+5,   // 94
+6,   // 95
+2,   // 96
+3,   // 97
+3,   // 98
+4,   // 99
+3,   // 100
+4,   // 101
+4,   // 102
+5,   // 103
+3,   // 104
+4,   // 105
+4,   // 106
+5,   // 107
+4,   // 108
+5,   // 109
+5,   // 110
+6,   // 111
+3,   // 112
+4,   // 113
+4,   // 114
+5,   // 115
+4,   // 116
+5,   // 117
+5,   // 118
+6,   // 119
+4,   // 120
+5,   // 121
+5,   // 122
+6,   // 123
+5,   // 124
+6,   // 125
+6,   // 126
+7,   // 127
+1,   // 128
+2,   // 129
+2,   // 130
+3,   // 131
+2,   // 132
+3,   // 133
+3,   // 134
+4,   // 135
+2,   // 136
+3,   // 137
+3,   // 138
+4,   // 139
+3,   // 140
+4,   // 141
+4,   // 142
+5,   // 143
+2,   // 144
+3,   // 145
+3,   // 146
+4,   // 147
+3,   // 148
+4,   // 149
+4,   // 150
+5,   // 151
+3,   // 152
+4,   // 153
+4,   // 154
+5,   // 155
+4,   // 156
+5,   // 157
+5,   // 158
+6,   // 159
+2,   // 160
+3,   // 161
+3,   // 162
+4,   // 163
+3,   // 164
+4,   // 165
+4,   // 166
+5,   // 167
+3,   // 168
+4,   // 169
+4,   // 170
+5,   // 171
+4,   // 172
+5,   // 173
+5,   // 174
+6,   // 175
+3,   // 176
+4,   // 177
+4,   // 178
+5,   // 179
+4,   // 180
+5,   // 181
+5,   // 182
+6,   // 183
+4,   // 184
+5,   // 185
+5,   // 186
+6,   // 187
+5,   // 188
+6,   // 189
+6,   // 190
+7,   // 191
+2,   // 192
+3,   // 193
+3,   // 194
+4,   // 195
+3,   // 196
+4,   // 197
+4,   // 198
+5,   // 199
+3,   // 200
+4,   // 201
+4,   // 202
+5,   // 203
+4,   // 204
+5,   // 205
+5,   // 206
+6,   // 207
+3,   // 208
+4,   // 209
+4,   // 210
+5,   // 211
+4,   // 212
+5,   // 213
+5,   // 214
+6,   // 215
+4,   // 216
+5,   // 217
+5,   // 218
+6,   // 219
+5,   // 220
+6,   // 221
+6,   // 222
+7,   // 223
+3,   // 224
+4,   // 225
+4,   // 226
+5,   // 227
+4,   // 228
+5,   // 229
+5,   // 230
+6,   // 231
+4,   // 232
+5,   // 233
+5,   // 234
+6,   // 235
+5,   // 236
+6,   // 237
+6,   // 238
+7,   // 239
+4,   // 240
+5,   // 241
+5,   // 242
+6,   // 243
+5,   // 244
+6,   // 245
+6,   // 246
+7,   // 247
+5,   // 248
+6,   // 249
+6,   // 250
+7,   // 251
+6,   // 252
+7,   // 253
+7,   // 254
+8    // 255
+};
+
+
+/*
+       ctcss decode filter
+*/
+/**************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.250000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_250_9_66 = 66;
+static const int32_t gain_fir_lpf_250_9_66 = 262144;
+static const int16_t coef_fir_lpf_250_9_66[] = 
+{ 
+  676,
+  364,
+   -3,
+ -415,
+ -860,
+-1320,
+-1777,
+-2209,
+-2593,
+-2904,
+-3119,
+-3212,
+-3162,
+-2949,
+-2557,
+-1975,
+-1198,
+ -226,
+  932,
+ 2263,
+ 3744,
+ 5346,
+ 7034,
+ 8767,
+10499,
+12184,
+13770,
+15211,
+16462,
+17480,
+18234,
+18696,
+18852,
+18696,
+18234,
+17480,
+16462,
+15211,
+13770,
+12184,
+10499,
+ 8767,
+ 7034,
+ 5346,
+ 3744,
+ 2263,
+  932,
+ -226,
+-1198,
+-1975,
+-2557,
+-2949,
+-3162,
+-3212,
+-3119,
+-2904,
+-2593,
+-2209,
+-1777,
+-1320,
+ -860,
+ -415,
+   -3,
+  364,
+  676,
+  927
+};
+/* *************************************************************
+Filter type: Low Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.215 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_lpf_215_9_88 = 88;
+static const int32_t gain_fir_lpf_215_9_88 = 524288;
+static const int16_t coef_fir_lpf_215_9_88[] = {
+ 2038,
+ 2049,
+ 1991,
+ 1859,
+ 1650,
+ 1363,
+  999,
+  562,
+   58,
+ -502,
+-1106,
+-1739,
+-2382,
+-3014,
+-3612,
+-4153,
+-4610,
+-4959,
+-5172,
+-5226,
+-5098,
+-4769,
+-4222,
+-3444,
+-2430,
+-1176,
+  310,
+ 2021,
+ 3937,
+ 6035,
+ 8284,
+10648,
+13086,
+15550,
+17993,
+20363,
+22608,
+24677,
+26522,
+28099,
+29369,
+30299,
+30867,
+31058,
+30867,
+30299,
+29369,
+28099,
+26522,
+24677,
+22608,
+20363,
+17993,
+15550,
+13086,
+10648,
+ 8284,
+ 6035,
+ 3937,
+ 2021,
+  310,
+-1176,
+-2430,
+-3444,
+-4222,
+-4769,
+-5098,
+-5226,
+-5172,
+-4959,
+-4610,
+-4153,
+-3612,
+-3014,
+-2382,
+-1739,
+-1106,
+ -502,
+   58,
+  562,
+  999,
+ 1363,
+ 1650,
+ 1859,
+ 1991,
+ 2049,
+ 2038,
+ 1966
+};
+// end coef fir_lpf_215_9_88
+//
+/**************************************************************
+Filter type: High Pass
+Filter model: Butterworth
+Filter order: 9
+Sampling Frequency: 8 KHz
+Cut Frequency: 0.300000 KHz
+Coefficents Quantization: 16-bit
+***************************************************************/
+static const int16_t taps_fir_hpf_300_9_66 = 66;
+static const int32_t gain_fir_hpf_300_9_66 = 32768;
+static const int16_t coef_fir_hpf_300_9_66[] = 
+{ 
+ -141,
+ -114,
+  -77,
+  -30,
+   23,
+   83,
+  147,
+  210,
+  271,
+  324,
+  367,
+  396,
+  407,
+  396,
+  362,
+  302,
+  216,
+  102,
+  -36,
+ -199,
+ -383,
+ -585,
+ -798,
+-1017,
+-1237,
+-1452,
+-1653,
+-1836,
+-1995,
+-2124,
+-2219,
+-2278,
+30463,
+-2278,
+-2219,
+-2124,
+-1995,
+-1836,
+-1653,
+-1452,
+-1237,
+-1017,
+ -798,
+ -585,
+ -383,
+ -199,
+  -36,
+  102,
+  216,
+  302,
+  362,
+  396,
+  407,
+  396,
+  367,
+  324,
+  271,
+  210,
+  147,
+   83,
+   23,
+  -30,
+  -77,
+ -114,
+ -141,
+ -158
+    };
+#endif /* !XPMR_COEF_H */
+/* end of file */
+
+
+
+