diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c
new file mode 100644
index 0000000000000000000000000000000000000000..b3801aec4c6e4755317bb61eb05fc721d367691b
--- /dev/null
+++ b/channels/chan_usbradio.c
@@ -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
index 0000000000000000000000000000000000000000..a52b16e4064dfee29faf4b9fdbb058237b418fce
--- /dev/null
+++ b/channels/xpmr/LICENSE
@@ -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.
+
+		    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.)
+
+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.
+
+  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.
+
+  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
+
+	    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
index 0000000000000000000000000000000000000000..1ceb659e72601b68c4811ae3707080bdc058dcd2
--- /dev/null
+++ b/channels/xpmr/sinetabx.h
@@ -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
index 0000000000000000000000000000000000000000..c67e4084119162817e3f085f7b620bccdb542653
--- /dev/null
+++ b/channels/xpmr/xpmr.c
@@ -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
index 0000000000000000000000000000000000000000..d51aa6dca327250601ddf230f47bd8bd106aeb7b
--- /dev/null
+++ b/channels/xpmr/xpmr.h
@@ -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
index 0000000000000000000000000000000000000000..4b7274e5a950ae4580e4a8f92bda91e1599e2817
--- /dev/null
+++ b/channels/xpmr/xpmr_coef.h
@@ -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 */
+
+
+
+