aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdixon <jdixon@f38db490-d61c-443f-a65b-d21fe96a405b>2007-09-14 00:34:13 +0000
committerjdixon <jdixon@f38db490-d61c-443f-a65b-d21fe96a405b>2007-09-14 00:34:13 +0000
commitd2a6abb108dfc6464a7a8d610349b79490bca137 (patch)
tree32c4e4f89b8d3cbf8b42a183197bce1b6d0fd5ff
parentcb7cdf96a39392d713753d099e6e1521826dde14 (diff)
Added channel driver for USB Radio device and
support thereof. git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@82366 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--apps/app_rpt.c341
-rw-r--r--channels/Makefile6
-rw-r--r--channels/chan_usbradio.c2811
-rwxr-xr-xchannels/xpmr/LICENSE341
-rwxr-xr-xchannels/xpmr/sinetabx.h300
-rwxr-xr-xchannels/xpmr/xpmr.c2266
-rwxr-xr-xchannels/xpmr/xpmr.h553
-rwxr-xr-xchannels/xpmr/xpmr_coef.h963
-rw-r--r--configs/usbradio.conf.sample54
9 files changed, 7559 insertions, 76 deletions
diff --git a/apps/app_rpt.c b/apps/app_rpt.c
index 056af1456..95af32fd4 100644
--- a/apps/app_rpt.c
+++ b/apps/app_rpt.c
@@ -18,11 +18,10 @@
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
-
/*! \file
*
* \brief Radio Repeater / Remote Base program
- * version 0.70 07/22/07
+ * version 0.73 09/04/07
*
* \author Jim Dixon, WB6NIL <jim@lambdatel.com>
*
@@ -179,7 +178,7 @@
#define RETRY_TIMER_MS 5000
-#define START_DELAY 10
+#define START_DELAY 2
#define MAXPEERSTR 31
#define MAXREMSTR 15
@@ -227,6 +226,8 @@
#define IC706_PL_MEMORY_OFFSET 50
+#define ALLOW_LOCAL_CHANNELS
+
enum {REM_OFF,REM_MONITOR,REM_TX};
enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
@@ -301,6 +302,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/say.h"
#include "asterisk/localtime.h"
#include "asterisk/cdr.h"
+#include "asterisk/options.h"
#include <termios.h>
/* Start a tone-list going */
@@ -308,7 +310,7 @@ int ast_playtones_start(struct ast_channel *chan, int vol, const char* tonelist,
/*! Stop the tones from playing */
void ast_playtones_stop(struct ast_channel *chan);
-static char *tdesc = "Radio Repeater / Remote Base version 0.70 07/22/2007";
+static char *tdesc = "Radio Repeater / Remote Base version 0.73 09/04/2007";
static char *app = "Rpt";
@@ -591,7 +593,7 @@ static struct rpt
char lastdtmfcommand[MAXDTMF];
char cmdnode[50];
struct ast_channel *rxchannel,*txchannel, *monchannel;
- struct ast_channel *pchannel,*txpchannel;
+ struct ast_channel *pchannel,*txpchannel, *zaprxchannel, *zaptxchannel;
struct ast_frame *lastf1,*lastf2;
struct rpt_tele tele;
struct timeval lasttv,curtv;
@@ -1187,6 +1189,57 @@ static int openserial(char *fname)
return(fd);
}
+static void mdc1200_notify(struct rpt *myrpt,char *fromnode, unsigned int unit)
+{
+ if (!fromnode)
+ {
+ ast_verbose("Got MDC-1200 ID %04X from local system (%s)\n",
+ unit,myrpt->name);
+ }
+ else
+ {
+ ast_verbose("Got MDC-1200 ID %04X from node %s (%s)\n",
+ unit,fromnode,myrpt->name);
+ }
+}
+
+#ifdef _MDC_DECODE_H_
+
+static void mdc1200_send(struct rpt *myrpt, unsigned int unit)
+{
+struct rpt_link *l;
+struct ast_frame wf;
+char str[200];
+
+
+ sprintf(str,"I %s %04X",myrpt->name,unit);
+
+ wf.frametype = AST_FRAME_TEXT;
+ wf.subclass = 0;
+ wf.offset = 0;
+ wf.mallocd = 0;
+ wf.datalen = strlen(str) + 1;
+ wf.samples = 0;
+
+
+ l = myrpt->links.next;
+ /* otherwise, send it to all of em */
+ while(l != &myrpt->links)
+ {
+ if (l->name[0] == '0')
+ {
+ l = l->next;
+ continue;
+ }
+ wf.data = str;
+ if (l->chan) ast_write(l->chan,&wf);
+ l = l->next;
+ }
+ return;
+}
+
+#endif
+
static char func_xlat(struct rpt *myrpt,char c,struct rpt_xlat *xlat)
{
time_t now;
@@ -3113,7 +3166,7 @@ struct zt_params par;
ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);
}
}
-#ifdef _MDC_DECODE_H_
+#if defined(_MDC_DECODE_H_) && defined(MDC_SAY_WHEN_DOING_CT)
if (myrpt->lastunit)
{
char mystr[10];
@@ -3334,14 +3387,14 @@ struct zt_params par;
break;
}
i = ZT_FLUSH_EVENT;
- if (ioctl(myrpt->txchannel->fds[0],ZT_FLUSH,&i) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_FLUSH,&i) == -1)
{
ast_mutex_unlock(&myrpt->remlock);
ast_log(LOG_ERROR,"Cant flush events");
res = -1;
break;
}
- if (ioctl(myrpt->rxchannel->fds[0],ZT_GET_PARAMS,&par) == -1)
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_GET_PARAMS,&par) == -1)
{
ast_mutex_unlock(&myrpt->remlock);
ast_log(LOG_ERROR,"Cant get params");
@@ -4490,7 +4543,14 @@ static int connect_link(struct rpt *myrpt, char* node, int mode, int perma)
l->isremote = (s && ast_true(s));
if (modechange) l->connected = 1;
l->hasconnected = l->perma = perma;
+#ifdef ALLOW_LOCAL_CHANNELS
+ if ((strncasecmp(s1,"iax2/", 5) == 0) || (strncasecmp(s1, "local/", 6) == 0))
+ strncpy(deststr, s1, sizeof(deststr));
+ else
+ snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
+#else
snprintf(deststr, sizeof(deststr), "IAX2/%s", s1);
+#endif
tele = strchr(deststr, '/');
if (!tele){
ast_log(LOG_WARNING,"link3:Dial number (%s) must be in format tech/number\n",deststr);
@@ -4771,6 +4831,14 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
break;
+#ifdef _MDC_DECODE_H_
+ case 8:
+ myrpt->lastunit = 0xd00d;
+ mdc1200_notify(myrpt,NULL,myrpt->lastunit);
+ mdc1200_send(myrpt,myrpt->lastunit);
+ break;
+#endif
+
case 16: /* Restore links disconnected with "disconnect all links" command */
strcpy(tmp, myrpt->savednodes); /* Make a copy */
finddelim(tmp, strs, MAXLINKLIST); /* convert into substrings */
@@ -5253,15 +5321,28 @@ struct ast_frame wf;
myrpt->name,tmp,mylink->name);
return;
}
- if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5)
+ if (tmp[0] == 'I')
{
- ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
- return;
+ if (sscanf(tmp,"%s %s %x",cmd,src,&seq) != 3)
+ {
+ ast_log(LOG_WARNING, "Unable to parse ident string %s\n",str);
+ return;
+ }
+ mdc1200_notify(myrpt,src,seq);
+ strcpy(dest,"*");
}
- if (strcmp(cmd,"D"))
+ else
{
- ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
- return;
+ if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5)
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return;
+ }
+ if (strcmp(cmd,"D"))
+ {
+ ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
+ return;
+ }
}
if (dest[0] == '0')
{
@@ -5727,6 +5808,7 @@ char *s;
static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data)
{
+#ifdef __i386__
int i,j;
unsigned char od,d;
static volatile long long delayvar;
@@ -5749,6 +5831,7 @@ static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data)
}
/* >= 50 us */
for(delayvar = 1; delayvar < 50000; delayvar++);
+#endif
}
static void rbi_out(struct rpt *myrpt,unsigned char *data)
@@ -5759,16 +5842,16 @@ struct zt_radio_param r;
r.radpar = ZT_RADPAR_REMMODE;
r.data = ZT_RADPAR_REM_RBI1;
/* if setparam ioctl fails, its probably not a pciradio card */
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
{
rbi_out_parallel(myrpt,data);
return;
}
r.radpar = ZT_RADPAR_REMCOMMAND;
memcpy(&r.data,data,5);
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&r) == -1)
{
- ast_log(LOG_WARNING,"Cannot send RBI command for channel %s\n",myrpt->rxchannel->name);
+ ast_log(LOG_WARNING,"Cannot send RBI command for channel %s\n",myrpt->zaprxchannel->name);
return;
}
}
@@ -5803,36 +5886,39 @@ static int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes
if (c == '\r') break;
}
}
- if(debug){
- printf("String returned was: ");
- for(j = 0; j < i; j++)
- printf("%02X ", (unsigned char ) rxbuf[j]);
- printf("\n");
- }
+ if(debug){
+ printf("String returned was: ");
+ for(j = 0; j < i; j++)
+ printf("%02X ", (unsigned char ) rxbuf[j]);
+ printf("\n");
+ }
return(i);
}
-
+
+ /* if not a zap channel, cant use pciradio stuff */
+ if (myrpt->rxchannel != myrpt->zaprxchannel) return -1;
+
prm.radpar = ZT_RADPAR_UIOMODE;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_GETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_GETPARAM,&prm) == -1) return -1;
oldmode = prm.data;
prm.radpar = ZT_RADPAR_UIODATA;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_GETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_GETPARAM,&prm) == -1) return -1;
olddata = prm.data;
prm.radpar = ZT_RADPAR_REMMODE;
if (asciiflag & 1) prm.data = ZT_RADPAR_REM_SERIAL_ASCII;
else prm.data = ZT_RADPAR_REM_SERIAL;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
if (asciiflag & 2)
{
i = ZT_ONHOOK;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_HOOK,&i) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_HOOK,&i) == -1) return -1;
usleep(100000);
}
prm.radpar = ZT_RADPAR_REMCOMMAND;
prm.data = rxmaxbytes;
memcpy(prm.buf,txbuf,txbytes);
prm.index = txbytes;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
if (rxbuf)
{
*rxbuf = 0;
@@ -5841,18 +5927,18 @@ static int serial_remote_io(struct rpt *myrpt, unsigned char *txbuf, int txbytes
index = prm.index;
prm.radpar = ZT_RADPAR_REMMODE;
prm.data = ZT_RADPAR_REM_NONE;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
if (asciiflag & 2)
{
i = ZT_OFFHOOK;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_HOOK,&i) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_HOOK,&i) == -1) return -1;
}
prm.radpar = ZT_RADPAR_UIOMODE;
prm.data = oldmode;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
prm.radpar = ZT_RADPAR_UIODATA;
prm.data = olddata;
- if (ioctl(myrpt->rxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_RADIO_SETPARAM,&prm) == -1) return -1;
return(index);
}
@@ -8238,6 +8324,19 @@ int seq,res;
/* put string in our buffer */
strncpy(tmp,str,sizeof(tmp) - 1);
if (!strcmp(tmp,discstr)) return 0;
+
+#ifndef DO_NOT_NOTIFY_MDC1200_ON_REMOTE_BASES
+ if (tmp[0] == 'I')
+ {
+ if (sscanf(tmp,"%s %s %x",cmd,src,&seq) != 3)
+ {
+ ast_log(LOG_WARNING, "Unable to parse ident string %s\n",str);
+ return 0;
+ }
+ mdc1200_notify(myrpt,src,seq);
+ return 0;
+ }
+#endif
if (sscanf(tmp,"%s %s %s %d %c",cmd,dest,src,&seq,&c) != 5)
{
ast_log(LOG_WARNING, "Unable to parse link string %s\n",str);
@@ -8645,7 +8744,6 @@ static void do_scheduler(struct rpt *myrpt)
}
-
/* single thread with one file (request) to dial */
static void *rpt(void *this)
{
@@ -8694,6 +8792,9 @@ char tmpstr[300],lstr[MAXLINKLIST];
}
*tele++ = 0;
myrpt->rxchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL);
+ myrpt->zaprxchannel = NULL;
+ if (!strcasecmp(tmpstr,"Zap"))
+ myrpt->zaprxchannel = myrpt->rxchannel;
if (myrpt->rxchannel)
{
if (myrpt->rxchannel->_state == AST_STATE_BUSY)
@@ -8728,6 +8829,7 @@ char tmpstr[300],lstr[MAXLINKLIST];
myrpt->rpt_thread = AST_PTHREADT_STOP;
pthread_exit(NULL);
}
+ myrpt->zaptxchannel = NULL;
if (myrpt->txchanname)
{
strncpy(tmpstr,myrpt->txchanname,sizeof(tmpstr) - 1);
@@ -8742,6 +8844,8 @@ char tmpstr[300],lstr[MAXLINKLIST];
}
*tele++ = 0;
myrpt->txchannel = ast_request(tmpstr,AST_FORMAT_SLINEAR,tele,NULL);
+ if (!strcasecmp(tmpstr,"Zap"))
+ myrpt->zaptxchannel = myrpt->txchannel;
if (myrpt->txchannel)
{
if (myrpt->txchannel->_state == AST_STATE_BUSY)
@@ -8798,6 +8902,24 @@ char tmpstr[300],lstr[MAXLINKLIST];
myrpt->rpt_thread = AST_PTHREADT_STOP;
pthread_exit(NULL);
}
+ if (!myrpt->zaprxchannel) myrpt->zaprxchannel = myrpt->pchannel;
+ if (!myrpt->zaptxchannel)
+ {
+ /* allocate a pseudo-channel thru asterisk */
+ myrpt->zaptxchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
+ if (!myrpt->zaptxchannel)
+ {
+ fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+ rpt_mutex_unlock(&myrpt->lock);
+ if (myrpt->txchannel != myrpt->rxchannel)
+ ast_hangup(myrpt->txchannel);
+ ast_hangup(myrpt->rxchannel);
+ myrpt->rpt_thread = AST_PTHREADT_STOP;
+ pthread_exit(NULL);
+ }
+ ast_set_read_format(myrpt->zaptxchannel,AST_FORMAT_SLINEAR);
+ ast_set_write_format(myrpt->zaptxchannel,AST_FORMAT_SLINEAR);
+ }
/* allocate a pseudo-channel thru asterisk */
myrpt->monchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
if (!myrpt->monchannel)
@@ -8817,7 +8939,7 @@ char tmpstr[300],lstr[MAXLINKLIST];
ci.confno = -1; /* make a new conf */
ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
/* first put the channel on the conference in proper mode */
- if (ioctl(myrpt->txchannel->fds[0],ZT_SETCONF,&ci) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_SETCONF,&ci) == -1)
{
ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
rpt_mutex_unlock(&myrpt->lock);
@@ -8853,7 +8975,8 @@ char tmpstr[300],lstr[MAXLINKLIST];
myrpt->conf = ci.confno;
/* make a conference for the pseudo */
ci.chan = 0;
- if (strstr(myrpt->txchannel->name,"pseudo") == NULL)
+ if ((strstr(myrpt->txchannel->name,"pseudo") == NULL) &&
+ (myrpt->zaptxchannel == myrpt->txchannel))
{
/* get tx channel's port number */
if (ioctl(myrpt->txchannel->fds[0],ZT_CHANNO,&ci.confno) == -1)
@@ -9059,6 +9182,7 @@ char tmpstr[300],lstr[MAXLINKLIST];
if (ast_check_hangup(myrpt->pchannel)) break;
if (ast_check_hangup(myrpt->monchannel)) break;
if (ast_check_hangup(myrpt->txpchannel)) break;
+ if (myrpt->zaptxchannel && ast_check_hangup(myrpt->zaptxchannel)) break;
/* Set local tx with keyed */
myrpt->localtx = myrpt->keyed;
@@ -9316,6 +9440,8 @@ char tmpstr[300],lstr[MAXLINKLIST];
cs[n++] = myrpt->monchannel;
cs[n++] = myrpt->txpchannel;
if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel;
+ if (myrpt->zaptxchannel != myrpt->txchannel)
+ cs[n++] = myrpt->zaptxchannel;
l = myrpt->links.next;
while(l != &myrpt->links)
{
@@ -9609,6 +9735,8 @@ char tmpstr[300],lstr[MAXLINKLIST];
if ((op == 1) && (arg == 0))
{
myrpt->lastunit = unitID;
+ mdc1200_notify(myrpt,NULL,myrpt->lastunit);
+ mdc1200_send(myrpt,myrpt->lastunit);
}
}
if ((debug > 2) && (i == 2))
@@ -9629,7 +9757,7 @@ char tmpstr[300],lstr[MAXLINKLIST];
/* apply inbound filters, if any */
rpt_filter(myrpt,f->data,f->datalen / 2);
#endif
- if (ioctl(myrpt->rxchannel->fds[0], ZT_GETCONFMUTE, &ismuted) == -1)
+ if (ioctl(myrpt->zaprxchannel->fds[0], ZT_GETCONFMUTE, &ismuted) == -1)
{
ismuted = 0;
}
@@ -9770,6 +9898,30 @@ char tmpstr[300],lstr[MAXLINKLIST];
ast_frfree(f);
continue;
}
+ if (who == myrpt->zaptxchannel) /* if it was a read from pseudo-tx */
+ {
+ f = ast_read(myrpt->zaptxchannel);
+ if (!f)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ break;
+ }
+ if (f->frametype == AST_FRAME_VOICE)
+ {
+ ast_write(myrpt->txchannel,f);
+ }
+ if (f->frametype == AST_FRAME_CONTROL)
+ {
+ if (f->subclass == AST_CONTROL_HANGUP)
+ {
+ if (debug) printf("@@@@ rpt:Hung Up\n");
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ continue;
+ }
toexit = 0;
rpt_mutex_lock(&myrpt->lock);
l = myrpt->links.next;
@@ -10178,6 +10330,7 @@ char tmpstr[300],lstr[MAXLINKLIST];
ast_hangup(myrpt->monchannel);
ast_hangup(myrpt->txpchannel);
if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel);
+ if (myrpt->zaptxchannel != myrpt->txchannel) ast_hangup(myrpt->zaptxchannel);
if (myrpt->lastf1) ast_frfree(myrpt->lastf1);
myrpt->lastf1 = NULL;
if (myrpt->lastf2) ast_frfree(myrpt->lastf2);
@@ -10216,6 +10369,9 @@ char *this,*val;
/* go thru all the specified repeaters */
this = NULL;
n = 0;
+ /* wait until asterisk starts */
+ while(!ast_test_flag(&ast_options,AST_OPT_FLAG_FULLY_BOOTED))
+ usleep(250000);
rpt_vars[n].cfg = ast_config_load("rpt.conf");
cfg = rpt_vars[n].cfg;
if (!cfg) {
@@ -10395,6 +10551,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
ast_log(LOG_WARNING, "Rpt requires an argument (system node)\n");
return -1;
}
+
strncpy(tmp, (char *)data, sizeof(tmp)-1);
time(&t);
/* if time has externally shifted negative, screw it */
@@ -10443,11 +10600,19 @@ static int rpt_exec(struct ast_channel *chan, void *data)
}
else
{
+#ifdef ALLOW_LOCAL_CHANNELS
+ /* Check to insure the connection is IAX2 or Local*/
+ if ( (strncmp(chan->name,"IAX2",4)) && (strncmp(chan->name,"Local",5)) ) {
+ ast_log(LOG_WARNING, "We only accept links via IAX2 or Local!!\n");
+ return -1;
+ }
+#else
if (strncmp(chan->name,"IAX2",4))
{
ast_log(LOG_WARNING, "We only accept links via IAX2!!\n");
return -1;
}
+#endif
}
if (options && (*options == 'R'))
{
@@ -10581,7 +10746,17 @@ static int rpt_exec(struct ast_channel *chan, void *data)
/* get his IP from IAX2 module */
memset(hisip,0,sizeof(hisip));
+#ifdef ALLOW_LOCAL_CHANNELS
+ /* set IP address if this is a local connection*/
+ if (strncmp(chan->name,"Local",5)==0) {
+ strcpy(hisip,"127.0.0.1");
+ } else {
+ pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1);
+ }
+#else
pbx_substitute_variables_helper(chan,"${IAXPEER(CURRENTCHANNEL)}",hisip,sizeof(hisip) - 1);
+#endif
+
if (!hisip[0])
{
ast_log(LOG_WARNING, "Link IP address cannot be determined!!\n");
@@ -10815,6 +10990,9 @@ static int rpt_exec(struct ast_channel *chan, void *data)
}
*tele++ = 0;
myrpt->rxchannel = ast_request(myrpt->rxchanname,AST_FORMAT_SLINEAR,tele,NULL);
+ myrpt->zaprxchannel = NULL;
+ if (!strcasecmp(myrpt->rxchanname,"Zap"))
+ myrpt->zaprxchannel = myrpt->rxchannel;
if (myrpt->rxchannel)
{
ast_set_read_format(myrpt->rxchannel,AST_FORMAT_SLINEAR);
@@ -10836,6 +11014,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
pthread_exit(NULL);
}
*--tele = '/';
+ myrpt->zaptxchannel = NULL;
if (myrpt->txchanname)
{
tele = strchr(myrpt->txchanname,'/');
@@ -10848,6 +11027,8 @@ static int rpt_exec(struct ast_channel *chan, void *data)
}
*tele++ = 0;
myrpt->txchannel = ast_request(myrpt->txchanname,AST_FORMAT_SLINEAR,tele,NULL);
+ if (!strcasecmp(myrpt->txchanname,"Zap"))
+ myrpt->zaptxchannel = myrpt->txchannel;
if (myrpt->txchannel)
{
ast_set_read_format(myrpt->txchannel,AST_FORMAT_SLINEAR);
@@ -10888,6 +11069,8 @@ static int rpt_exec(struct ast_channel *chan, void *data)
}
ast_set_read_format(myrpt->pchannel,AST_FORMAT_SLINEAR);
ast_set_write_format(myrpt->pchannel,AST_FORMAT_SLINEAR);
+ if (!myrpt->zaprxchannel) myrpt->zaprxchannel = myrpt->pchannel;
+ if (!myrpt->zaptxchannel) myrpt->zaptxchannel = myrpt->pchannel;
/* make a conference for the pseudo */
ci.chan = 0;
ci.confno = -1; /* make a new conf */
@@ -10903,6 +11086,8 @@ static int rpt_exec(struct ast_channel *chan, void *data)
ast_hangup(myrpt->rxchannel);
pthread_exit(NULL);
}
+ /* save pseudo channel conference number */
+ myrpt->conf = myrpt->txconf = ci.confno;
/* if serial io port, open it */
myrpt->iofd = -1;
if (myrpt->p.ioport && ((myrpt->iofd = openserial(myrpt->p.ioport)) == -1))
@@ -10916,30 +11101,30 @@ static int rpt_exec(struct ast_channel *chan, void *data)
}
iskenwood_pci4 = 0;
memset(&z,0,sizeof(z));
- if (myrpt->iofd < 1)
+ if ((myrpt->iofd < 1) && (myrpt->txchannel == myrpt->zaptxchannel))
{
z.radpar = ZT_RADPAR_REMMODE;
z.data = ZT_RADPAR_REM_NONE;
- res = ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z);
+ res = ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z);
/* if PCIRADIO and kenwood selected */
if ((!res) && (!strcmp(myrpt->remote,remote_rig_kenwood)))
{
z.radpar = ZT_RADPAR_UIOMODE;
z.data = 1;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIOMODE\n");
return -1;
}
z.radpar = ZT_RADPAR_UIODATA;
z.data = 3;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIODATA\n");
return -1;
}
i = ZT_OFFHOOK;
- if (ioctl(myrpt->txchannel->fds[0],ZT_HOOK,&i) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_HOOK,&i) == -1)
{
ast_log(LOG_ERROR,"Cannot set hook\n");
return -1;
@@ -10947,30 +11132,31 @@ static int rpt_exec(struct ast_channel *chan, void *data)
iskenwood_pci4 = 1;
}
}
- i = ZT_ONHOOK;
- ioctl(myrpt->txchannel->fds[0],ZT_HOOK,&i);
- /* if PCIRADIO and Yaesu ft897/ICOM IC-706 selected */
- if ((myrpt->iofd < 1) && (!res) &&
- (!strcmp(myrpt->remote,remote_rig_ft897) ||
- (!strcmp(myrpt->remote,remote_rig_ic706))))
+ if (myrpt->txchannel == myrpt->zaptxchannel)
{
- z.radpar = ZT_RADPAR_UIOMODE;
- z.data = 1;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
- {
- ast_log(LOG_ERROR,"Cannot set UIOMODE\n");
- return -1;
- }
- z.radpar = ZT_RADPAR_UIODATA;
- z.data = 3;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ i = ZT_ONHOOK;
+ ioctl(myrpt->zaptxchannel->fds[0],ZT_HOOK,&i);
+ /* if PCIRADIO and Yaesu ft897/ICOM IC-706 selected */
+ if ((myrpt->iofd < 1) && (!res) &&
+ (!strcmp(myrpt->remote,remote_rig_ft897) ||
+ (!strcmp(myrpt->remote,remote_rig_ic706))))
{
- ast_log(LOG_ERROR,"Cannot set UIODATA\n");
- return -1;
+ z.radpar = ZT_RADPAR_UIOMODE;
+ z.data = 1;
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ {
+ ast_log(LOG_ERROR,"Cannot set UIOMODE\n");
+ return -1;
+ }
+ z.radpar = ZT_RADPAR_UIODATA;
+ z.data = 3;
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ {
+ ast_log(LOG_ERROR,"Cannot set UIODATA\n");
+ return -1;
+ }
}
}
- /* save pseudo channel conference number */
- myrpt->conf = myrpt->txconf = ci.confno;
myrpt->remoterx = 0;
myrpt->remotetx = 0;
myrpt->retxtimer = 0;
@@ -11000,19 +11186,22 @@ static int rpt_exec(struct ast_channel *chan, void *data)
if (myrpt->remote && (myrpt->rxchannel == myrpt->txchannel))
{
i = 128;
- ioctl(myrpt->rxchannel->fds[0],ZT_ECHOCANCEL,&i);
+ ioctl(myrpt->zaprxchannel->fds[0],ZT_ECHOCANCEL,&i);
}
if (chan->_state != AST_STATE_UP) {
ast_answer(chan);
}
- if (ioctl(myrpt->rxchannel->fds[0],ZT_GET_PARAMS,&par) != -1)
+ if (myrpt->rxchannel == myrpt->zaprxchannel)
{
- if (par.rxisoffhook)
+ if (ioctl(myrpt->zaprxchannel->fds[0],ZT_GET_PARAMS,&par) != -1)
{
- ast_indicate(chan,AST_CONTROL_RADIO_KEY);
- myrpt->remoterx = 1;
- remkeyed = 1;
+ if (par.rxisoffhook)
+ {
+ ast_indicate(chan,AST_CONTROL_RADIO_KEY);
+ myrpt->remoterx = 1;
+ remkeyed = 1;
+ }
}
}
if (myrpt->p.archivedir)
@@ -11268,11 +11457,11 @@ static int rpt_exec(struct ast_channel *chan, void *data)
if((myrpt->remtxfreqok = check_tx_freq(myrpt)))
{
time(&myrpt->last_activity_time);
- if (iskenwood_pci4)
+ if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel))
{
z.radpar = ZT_RADPAR_UIODATA;
z.data = 1;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIODATA\n");
return -1;
@@ -11292,11 +11481,11 @@ static int rpt_exec(struct ast_channel *chan, void *data)
if(!myrpt->remtxfreqok){
rpt_telemetry(myrpt,UNAUTHTX,NULL);
}
- if (iskenwood_pci4)
+ if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel))
{
z.radpar = ZT_RADPAR_UIODATA;
z.data = 3;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIODATA\n");
return -1;
@@ -11569,24 +11758,24 @@ static int rpt_exec(struct ast_channel *chan, void *data)
myrpt->lastf1 = NULL;
if (myrpt->lastf2) ast_frfree(myrpt->lastf2);
myrpt->lastf2 = NULL;
- if (iskenwood_pci4)
+ if ((iskenwood_pci4) && (myrpt->txchannel == myrpt->zaptxchannel))
{
z.radpar = ZT_RADPAR_UIOMODE;
z.data = 3;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIOMODE\n");
return -1;
}
z.radpar = ZT_RADPAR_UIODATA;
z.data = 3;
- if (ioctl(myrpt->txchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_RADIO_SETPARAM,&z) == -1)
{
ast_log(LOG_ERROR,"Cannot set UIODATA\n");
return -1;
}
i = ZT_OFFHOOK;
- if (ioctl(myrpt->txchannel->fds[0],ZT_HOOK,&i) == -1)
+ if (ioctl(myrpt->zaptxchannel->fds[0],ZT_HOOK,&i) == -1)
{
ast_log(LOG_ERROR,"Cannot set hook\n");
return -1;
diff --git a/channels/Makefile b/channels/Makefile
index beba0d1ff..7aea7d857 100644
--- a/channels/Makefile
+++ b/channels/Makefile
@@ -116,3 +116,9 @@ misdn/isdn_lib.o: ASTCFLAGS+=-Wno-strict-aliasing
$(if $(filter chan_misdn,$(EMBEDDED_MODS)),modules.link,chan_misdn.so): chan_misdn.o misdn_config.o misdn/isdn_lib.o misdn/isdn_msg_parser.o
chan_vpb.oo: ASTCFLAGS:=$(filter-out -Wdeclaration-after-statement,$(ASTCFLAGS))
+
+chan_usbradio.o: chan_usbradio.c xpmr/xpmr.c xpmr/xpmr.h xpmr/xpmr_coef.h xpmr/sinetabx.h busy.h ringtone.h
+
+chan_usbradio.so: LIBS+=-lusb -lasound
+
+
diff --git a/channels/chan_usbradio.c b/channels/chan_usbradio.c
new file mode 100644
index 000000000..9d5651aa0
--- /dev/null
+++ b/channels/chan_usbradio.c
@@ -0,0 +1,2811 @@
+/*
+ * 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>ossaudio</depend>
+ ***/
+
+#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 000000000..a52b16e40
--- /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 000000000..1ceb659e7
--- /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 000000000..c67e40841
--- /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 000000000..d51aa6dca
--- /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 000000000..4b7274e5a
--- /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 */
+
+
+
+
diff --git a/configs/usbradio.conf.sample b/configs/usbradio.conf.sample
new file mode 100644
index 000000000..5ba9815ca
--- /dev/null
+++ b/configs/usbradio.conf.sample
@@ -0,0 +1,54 @@
+[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
+
+; rxboost=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".
+;-----------------------------------------------------------------------------------
+
+