Complete re-vamp of Radio Repeater application (app_rpt) and VERY minor changes in...
authorJim Dixon <telesistant@hotmail.com>
Mon, 31 May 2004 16:04:10 +0000 (16:04 +0000)
committerJim Dixon <telesistant@hotmail.com>
Mon, 31 May 2004 16:04:10 +0000 (16:04 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@3117 65c4cc65-6c06-0410-ace0-fbb531ad65f3

Makefile
apps/Makefile
apps/app_rpt.c
channels/chan_zap.c
configs/rpt.conf.sample
dsp.c

index b6469d3..e52a71a 100755 (executable)
--- a/Makefile
+++ b/Makefile
@@ -57,6 +57,10 @@ DEBUG=-g #-pg
 # You can still use the old libpri if you do "cvs update -D "08/03/03" in libpri source code
 OPTIONS += -DNEW_PRI_HANGUP
 
+# If you are running a radio application, define RADIO_RELAX so that the DTMF
+# will be received more reliably
+#OPTIONS += -DRADIO_RELAX
+
 # Optional debugging parameters
 DEBUG_THREADS = #-DDEBUG_THREADS #-DDO_CRASH 
 
index e10209e..a51f5a0 100755 (executable)
@@ -35,9 +35,9 @@ endif
 
 #APPS+=app_sql_postgres.so
 #APPS+=app_sql_odbc.so
+#APPS+=app_rpt.so
 
 APPS+=$(shell if [ -f /usr/include/linux/zaptel.h ]; then echo "app_zapras.so app_meetme.so app_flash.so app_zapbarge.so app_zapscan.so" ; fi)
-#APPS+=$(shell if [ -f /usr/include/zap.h ]; then echo "app_rpt.so" ; fi)
 
 CFLAGS+=-fPIC
 
@@ -64,12 +64,6 @@ install: all
        for x in $(APPS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done
        rm -f $(DESTDIR)$(MODULES_DIR)/app_datetime.so
 
-app_todd.o: app_todd.c
-       gcc -pipe -O6 -g  -Iinclude -I../include -D_REENTRANT -march=i586 -DDO_CRASH -c -o  app_todd.o app_todd.c
-
-app_todd.so: app_todd.o
-       $(CC) $(SOLINK) -o $@ $< -L/usr/local/ssl/lib -lssl -lcrypto
-
 app_voicemail.so : app_voicemail.o
 ifeq ($(USE_MYSQL_VM_INTERFACE),1)
        $(CC) $(SOLINK) -o $@ $(MLFLAGS) $< -L/usr/lib/mysql -lmysqlclient -lz
index c99a06a..9325d39 100755 (executable)
@@ -2,17 +2,40 @@
  *
  * Asterisk -- A telephony toolkit for Linux.
  *
- * Radio Repeater program
+ * Radio Repeater / Remote Base program 
+ *  version 0.2 5/30/04
  * 
- * Copyright (C) 2002, Jim Dixon
+ * Copyright (C) 2002-2004, Jim Dixon
  *
  * Jim Dixon <jim@lambdatel.com>
  *
  * This program is free software, distributed under the terms of
  * the GNU General Public License
  *
+ * Repeater / Remote Functions:
+ * "Simple" Mode:  * - autopatch access, # - autopatch hangup
+ * Normal mode:
+ *  *0 - autopatch access
+ *  *1 - remote base off
+ *  *2 - remote base monitor
+ *  *3 - remote base tranceive
+ *  *8 - force ID
+ *  *9 - system reset
+ *
+ *  To send an asterisk (*) while dialing or talking on phone,
+ *  use the autopatch acess code.
  */
  
+/* number of digits for function after *. Must be at least 1 */
+#define        FUNCTION_LEN 1
+
+/* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
+
+#define        MAXDTMF 10
+#define        DTMF_TIMEOUT 3
+
+enum {REM_OFF,REM_MONITOR,REM_TX};
+
 #include <asterisk/lock.h>
 #include <asterisk/file.h>
 #include <asterisk/logger.h>
@@ -40,7 +63,7 @@
 #include <tonezone.h>
 #include <linux/zaptel.h>
 
-static  char *tdesc = "Radio Repeater";
+static  char *tdesc = "Radio Repeater / Remote Base  version 0.2  05/30/2004";
 static int debug = 0;
 STANDARD_LOCAL_USER;
 LOCAL_USER_DECL;
@@ -58,17 +81,25 @@ static struct rpt
        char *name;
        char *rxchanname;
        char *txchanname;
+       char *rem_rxchanname;
+       char *rem_txchanname;
        char *ourcontext;
        char *ourcallerid;
        char *acctcode;
        char *idrecording;
+       char *tonezone;
        int hangtime;
        int totime;
        int idtime;
-       struct ast_channel *rxchannel,*txchannel,*pchannel;
+       char remoterx;
+       char remotetx;
+       char remotemode;
+       char simple;
+       struct ast_channel *rxchannel,*txchannel,*rem_rxchannel;
+       struct ast_channel *rem_txchannel,*pchannel;
        int tailtimer,totimer,idtimer,txconf,pconf,callmode,cidx;
        pthread_t rpt_id_thread,rpt_term_thread,rpt_proc_thread,rpt_call_thread;
-       char mydtmf,iding,terming;
+       char mydtmf,iding,terming,teleing,comping,procing;
        char exten[AST_MAX_EXTENSION];
 } rpt_vars[MAXRPTS];           
 
@@ -130,7 +161,7 @@ struct ast_channel *mychannel;
        }
        /* make a conference for the tx */
        ci.chan = 0;
-       ci.confno = myrpt->pconf; /* use the tx conference */
+       ci.confno = myrpt->txconf; /* use the tx conference */
        ci.confmode = ZT_CONF_CONFANN;
        /* first put the channel on the conference in announce mode */
        if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
@@ -138,7 +169,7 @@ struct ast_channel *mychannel;
                ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
                pthread_exit(NULL);
        }
-       myrpt->terming = 1;
+       myrpt->procing = 1;
        ast_stopstream(mychannel);
        res = ast_streamfile(mychannel, "callproceeding", mychannel->language);
        if (!res) 
@@ -147,7 +178,7 @@ struct ast_channel *mychannel;
                ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
                res = 0;
        }
-       myrpt->terming = 0;
+       myrpt->procing = 0;
        ast_stopstream(mychannel);
        ast_hangup(mychannel);
        pthread_exit(NULL);
@@ -171,7 +202,7 @@ struct ast_channel *mychannel;
        }
        /* make a conference for the tx */
        ci.chan = 0;
-       ci.confno = myrpt->pconf; /* use the tx conference */
+       ci.confno = myrpt->txconf; /* use the tx conference */
        ci.confmode = ZT_CONF_CONFANN;
        /* first put the channel on the conference in announce mode */
        if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
@@ -194,6 +225,89 @@ struct ast_channel *mychannel;
        pthread_exit(NULL);
 }
 
+static void *rpt_complete(void *this)
+{
+ZT_CONFINFO ci;  /* conference info */
+int    res;
+struct rpt *myrpt = (struct rpt *)this;
+struct ast_channel *mychannel;
+
+       /* wait a little bit */
+       usleep(1000000);
+       /* allocate a pseudo-channel thru asterisk */
+       mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
+       if (!mychannel)
+       {
+               fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+               pthread_exit(NULL);
+       }
+       /* make a conference for the tx */
+       ci.chan = 0;
+       ci.confno = myrpt->txconf; /* use the tx conference */
+       ci.confmode = ZT_CONF_CONFANN;
+       /* first put the channel on the conference in announce mode */
+       if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
+       {
+               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+               pthread_exit(NULL);
+       }
+       myrpt->comping = 1;
+       ast_stopstream(mychannel);
+       res = ast_streamfile(mychannel, "functioncomplete", mychannel->language);
+       if (!res) 
+               res = ast_waitstream(mychannel, "");
+       else {
+               ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+               res = 0;
+       }
+       myrpt->comping = 0;
+       ast_stopstream(mychannel);
+       ast_hangup(mychannel);
+       pthread_exit(NULL);
+}
+
+static void *rpt_remote_telemetry(void *this)
+{
+ZT_CONFINFO ci;  /* conference info */
+int res;
+struct rpt *myrpt = (struct rpt *)this;
+struct ast_channel *mychannel;
+
+       /* wait a little bit */
+       usleep(1000000);
+       /* allocate a pseudo-channel thru asterisk */
+       mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
+       if (!mychannel)
+       {
+               fprintf(stderr,"rpt:Sorry unable to obtain pseudo channel\n");
+               pthread_exit(NULL);
+       }
+       /* make a conference for the tx */
+       ci.chan = 0;
+       ci.confno = myrpt->txconf; /* use the tx conference */
+       ci.confmode = ZT_CONF_CONFANN;
+       /* first put the channel on the conference in announce mode */
+       if (ioctl(mychannel->fds[0],ZT_SETCONF,&ci) == -1)
+       {
+               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+               pthread_exit(NULL);
+       }
+       myrpt->teleing = 1;
+       ast_stopstream(mychannel);
+       res = ast_streamfile(mychannel, 
+               ((myrpt->remotemode == REM_MONITOR) ? "remote_monitor" : "remote_tx"),
+                       mychannel->language);
+       if (!res) 
+               res = ast_waitstream(mychannel, "");
+       else {
+               ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+               res = 0;
+       }
+       myrpt->teleing = 0;
+       ast_hangup(mychannel);
+       pthread_exit(NULL);
+}
+
 static void *rpt_call(void *this)
 {
 ZT_CONFINFO ci;  /* conference info */
@@ -244,6 +358,22 @@ struct ast_channel *mychannel,*genchannel;
                myrpt->callmode = 0;
                pthread_exit(NULL);
        }
+       if (myrpt->tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->tonezone) == -1))
+       {
+               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+               ast_hangup(mychannel);
+               ast_hangup(genchannel);
+               myrpt->callmode = 0;
+               pthread_exit(NULL);
+       }
+       if (myrpt->tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->tonezone) == -1))
+       {
+               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+               ast_hangup(mychannel);
+               ast_hangup(genchannel);
+               myrpt->callmode = 0;
+               pthread_exit(NULL);
+       }
        /* start dialtone */
        if (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0)
        {
@@ -358,14 +488,104 @@ struct ast_channel *mychannel,*genchannel;
        pthread_exit(NULL);
 }
 
+static void process_dtmf(char *cmd,struct rpt *myrpt)
+{
+pthread_attr_t attr;
+ZT_CONFINFO ci;  /* conference info */
+
+       switch(atoi(cmd))
+       {
+       case 0: /* autopatch on / send asterisk (*) */
+               /* if on call, force * into current audio stream */
+               if ((myrpt->callmode == 2) || (myrpt->callmode == 3))
+               {
+                       myrpt->mydtmf = '*';
+                       break;
+               }
+               if (myrpt->callmode) return;
+               myrpt->callmode = 1;
+               myrpt->cidx = 0;
+               myrpt->exten[myrpt->cidx] = 0;
+               pthread_attr_init(&attr);
+               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+               pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt);
+               return;
+       case 9: /* master reset */
+               myrpt->callmode = 0;
+               /* fall thru intentionally */
+       case 1: /* remote base off */
+               if (myrpt->rem_rxchannel == NULL) return;
+               myrpt->remotemode = REM_OFF;
+               ci.chan = 0;
+               ci.confno = 0;
+               ci.confmode = 0;
+               /* Take off conf */
+               if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+               {
+                       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                       pthread_exit(NULL);
+               }
+               /* Take off conf */
+               if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1)
+               {
+                       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                       pthread_exit(NULL);
+               }
+               break;
+       case 2: /* remote base monitor */
+               if (myrpt->rem_rxchannel == NULL) return;
+               myrpt->remotemode = REM_MONITOR;
+               if (myrpt->remoterx && (!myrpt->remotetx))
+               {
+                       ci.chan = 0;
+                       ci.confno = myrpt->pconf;
+                       ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+                       /* Put on conf */
+                       if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                       {
+                               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                               pthread_exit(NULL);
+                       }
+               }
+               break;
+       case 3: /* remote base tranceieve */
+               if (myrpt->rem_rxchannel == NULL) return;
+               myrpt->remotemode = REM_TX;
+               if (myrpt->remoterx && (!myrpt->remotetx))
+               {
+                       ci.chan = 0;
+                       ci.confno = myrpt->pconf;
+                       ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+                       /* Put on conf */
+                       if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                       {
+                               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                               pthread_exit(NULL);
+                       }
+               }
+               break;
+       case 8: /* force ID */
+               myrpt->idtimer = 0;
+               return;
+       default:
+               return;
+       }
+       /* send function complete */
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+       pthread_create(&myrpt->rpt_call_thread,&attr,rpt_complete,(void *)myrpt);
+}
+
 /* single thread with one file (request) to dial */
 static void *rpt(void *this)
 {
 struct rpt *myrpt = (struct rpt *)this;
 char *tele;
-int ms = MSWAIT,lasttx,keyed,val;
+int ms = MSWAIT,lasttx,keyed,val,dtmfidx;
+char dtmfbuf[MAXDTMF];
 struct ast_channel *who;
 ZT_CONFINFO ci;  /* conference info */
+time_t dtmf_time,t;
 pthread_attr_t attr;
 
        tele = strchr(myrpt->rxchanname,'/');
@@ -390,7 +610,7 @@ pthread_attr_t attr;
        }
        else
        {
-               fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
+               fprintf(stderr,"rpt:Sorry unable to obtain Rx channel\n");
                pthread_exit(NULL);
        }
        if (myrpt->txchanname)
@@ -417,7 +637,7 @@ pthread_attr_t attr;
                }
                else
                {
-                       fprintf(stderr,"rpt:Sorry unable to obtain channel\n");
+                       fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n");
                        pthread_exit(NULL);
                }
        }
@@ -425,6 +645,70 @@ pthread_attr_t attr;
        {
                myrpt->txchannel = myrpt->rxchannel;
        }
+       myrpt->rem_rxchannel = NULL;
+       myrpt->rem_txchannel = NULL;
+       myrpt->remoterx = 0;
+       myrpt->remotemode = REM_OFF;
+       if (myrpt->rem_rxchanname)
+       {
+               tele = strchr(myrpt->rem_rxchanname,'/');
+               if (!tele)
+               {
+                       fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
+                       pthread_exit(NULL);
+               }
+               *tele++ = 0;
+               myrpt->rem_rxchannel = ast_request(myrpt->rem_rxchanname,AST_FORMAT_SLINEAR,tele);
+               if (myrpt->rem_rxchannel)
+               {
+                       ast_set_read_format(myrpt->rem_rxchannel,AST_FORMAT_SLINEAR);
+                       ast_set_write_format(myrpt->rem_rxchannel,AST_FORMAT_SLINEAR);
+                       myrpt->rem_rxchannel->whentohangup = 0;
+                       myrpt->rem_rxchannel->appl = "Apprpt";
+                       myrpt->rem_rxchannel->data = "(Repeater/Remote Rx)";
+                       if (option_verbose > 2)
+                               ast_verbose(VERBOSE_PREFIX_3 "rpt (RemoteRx) initiating call to %s/%s on %s\n",
+                                       myrpt->rem_rxchanname,tele,myrpt->rem_rxchannel->name);
+                       ast_call(myrpt->rem_rxchannel,tele,999);
+               }
+               else
+               {
+                       fprintf(stderr,"rpt:Sorry unable to obtain RemoteRx channel\n");
+                       pthread_exit(NULL);
+               }
+               if (myrpt->rem_txchanname)  /* if in remote base mode */
+               {
+                       tele = strchr(myrpt->rem_txchanname,'/');
+                       if (!tele)
+                       {
+                               fprintf(stderr,"rpt:Dial number must be in format tech/number\n");
+                               pthread_exit(NULL);
+                       }
+                       *tele++ = 0;
+                       myrpt->rem_txchannel = ast_request(myrpt->rem_txchanname,AST_FORMAT_SLINEAR,tele);
+                       if (myrpt->rem_txchannel)
+                       {
+                               ast_set_read_format(myrpt->rem_txchannel,AST_FORMAT_SLINEAR);
+                               ast_set_write_format(myrpt->rem_txchannel,AST_FORMAT_SLINEAR);
+                               myrpt->rem_txchannel->whentohangup = 0;
+                               myrpt->rem_txchannel->appl = "Apprpt";
+                               myrpt->rem_txchannel->data = "(Repeater/Remote Tx)";
+                               if (option_verbose > 2)
+                                       ast_verbose(VERBOSE_PREFIX_3 "rpt (RemoteTx) initiating call to %s/%s on %s\n",
+                                               myrpt->rem_txchanname,tele,myrpt->rem_txchannel->name);
+                               ast_call(myrpt->rem_txchannel,tele,999);
+                       }
+                       else
+                       {
+                               fprintf(stderr,"rpt:Sorry unable to obtain Tx channel\n");
+                               pthread_exit(NULL);
+                       }
+               }
+               else
+               {
+                       myrpt->rem_txchannel = myrpt->rem_rxchannel;
+               }
+       }
        /* allocate a pseudo-channel thru asterisk */
        myrpt->pchannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo");
        if (!myrpt->pchannel)
@@ -463,19 +747,39 @@ pthread_attr_t attr;
        myrpt->totimer = 0;
        myrpt->idtimer = 0;
        lasttx = 0;
+       myrpt->remotetx = 0;
        keyed = 0;
        myrpt->callmode = 0;
+       dtmfidx = -1;
+       dtmfbuf[0] = 0;
+       dtmf_time = 0;
        val = 0;
        ast_channel_setoption(myrpt->rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0);
        val = 1;
        ast_channel_setoption(myrpt->rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0);
+       if (myrpt->rem_rxchannel)
+       {
+               val = 0;
+               ast_channel_setoption(myrpt->rem_rxchannel,AST_OPTION_TONE_VERIFY,&val,sizeof(char),0);
+               val = 1;
+               ast_channel_setoption(myrpt->rem_rxchannel,AST_OPTION_RELAXDTMF,&val,sizeof(char),0);
+       }
        while (ms >= 0)
        {
                struct ast_frame *f;
-               struct ast_channel *cs[3];
-               int totx,elap;
+               struct ast_channel *cs[5];
+               int totx,rem_totx,elap,n;
 
-               totx = (keyed || myrpt->callmode || myrpt->iding || myrpt->terming);
+               if (ast_check_hangup(myrpt->rxchannel)) break;
+               if (ast_check_hangup(myrpt->txchannel)) break;
+               if (myrpt->rem_rxchannel)
+               {
+                       if (ast_check_hangup(myrpt->rem_rxchannel)) break;
+                       if (ast_check_hangup(myrpt->rem_txchannel)) break;
+               }
+               totx = (keyed || myrpt->callmode || myrpt->iding || myrpt->terming
+                   || ((myrpt->remotemode != REM_OFF) && myrpt->remoterx) ||
+                       myrpt->teleing || myrpt->comping || myrpt->procing);
                if (!totx) myrpt->totimer = myrpt->totime;
                else myrpt->tailtimer = myrpt->hangtime;
                totx = (totx || myrpt->tailtimer) && myrpt->totimer;
@@ -486,6 +790,11 @@ pthread_attr_t attr;
                        myrpt->totimer = myrpt->totime;
                        continue;
                }
+               /* if timed-out and in circuit busy after call */
+               if ((!totx) && (!myrpt->totimer) && (myrpt->callmode == 4))
+               {
+                       myrpt->callmode = 0;
+               }
                if (totx && (!myrpt->idtimer))
                {
                        myrpt->idtimer = myrpt->idtime;
@@ -503,12 +812,75 @@ pthread_attr_t attr;
                        lasttx = 0;
                        ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_UNKEY);
                }
-
-               cs[0] = myrpt->rxchannel;
-               cs[1] = myrpt->pchannel;
-               cs[2] = myrpt->txchannel;
+               rem_totx = ((keyed && (myrpt->remotemode == REM_TX)) && myrpt->totimer);
+               if (rem_totx && (!myrpt->remotetx))
+               {
+                       myrpt->remotetx = 1;
+                       ci.chan = 0;
+                       ci.confno = 0;
+                       ci.confmode = 0;
+                       /* Take off conf */
+                       if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                       {
+                               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                               pthread_exit(NULL);
+                       }
+                       ast_indicate(myrpt->rem_txchannel,AST_CONTROL_RADIO_KEY);
+                       ci.chan = 0;
+                       ci.confno = myrpt->txconf;
+                       ci.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER; 
+                       /* Put the channel on the conference in listener mode */
+                       if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                       {
+                               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                               pthread_exit(NULL);
+                       }
+               }
+               if ((!rem_totx) && myrpt->remotetx)
+               {
+                       myrpt->remotetx = 0;
+                       ast_indicate(myrpt->rem_txchannel,AST_CONTROL_RADIO_UNKEY);
+                       ci.chan = 0;
+                       ci.confno = 0;
+                       ci.confmode = 0;
+                       /* Take off conf */
+                       if (ioctl(myrpt->rem_txchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                       {
+                               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                               pthread_exit(NULL);
+                       }
+                       if (myrpt->remotemode != REM_OFF)
+                       {
+                               ci.chan = 0;
+                               ci.confno = myrpt->pconf;
+                               ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+                               /* Put on conf */
+                               if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                               {
+                                       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                                       pthread_exit(NULL);
+                               }
+                       }
+               }
+               time(&t);
+               /* if DTMF timeout */
+               if ((dtmfidx >= 0) && ((dtmf_time + DTMF_TIMEOUT) < t))
+               {
+                       dtmfidx = -1;
+                       dtmfbuf[0] = 0;
+               }                       
+               n = 0;
+               cs[n++] = myrpt->rxchannel;
+               cs[n++] = myrpt->pchannel;
+               if (myrpt->txchannel != myrpt->rxchannel) cs[n++] = myrpt->txchannel;
+               if (myrpt->rem_rxchannel)
+               {
+                       cs[n++] = myrpt->rem_rxchannel;
+                       if (myrpt->rem_txchannel != myrpt->rem_rxchannel)
+                               cs[n++] = myrpt->rem_txchannel;
+               }
                ms = MSWAIT;
-               who = ast_waitfor_n(cs,3,&ms);
+               who = ast_waitfor_n(cs,n,&ms);
                if (who == NULL) ms = 0;
                elap = MSWAIT - ms;
                if (myrpt->tailtimer) myrpt->tailtimer -= elap;
@@ -535,15 +907,44 @@ pthread_attr_t attr;
                                char c;
 
                                c = (char) f->subclass; /* get DTMF char */
-                               if ((!myrpt->callmode) && (c == '*'))
+                               if (!myrpt->simple)
                                {
-                                       myrpt->callmode = 1;
-                                       myrpt->cidx = 0;
-                                       myrpt->exten[myrpt->cidx] = 0;
-                                       pthread_attr_init(&attr);
-                                       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-                                       pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt);
-                                       continue;
+                                       if (c == '*')
+                                       {
+                                               dtmfidx = 0;
+                                               dtmfbuf[dtmfidx] = 0;
+                                               time(&dtmf_time);
+                                               continue;
+                                       } 
+                                       else if ((c != '#') && (dtmfidx >= 0))
+                                       {
+                                               time(&dtmf_time);
+                                               if (dtmfidx < MAXDTMF)
+                                               {
+                                                       dtmfbuf[dtmfidx++] = c;
+                                                       dtmfbuf[dtmfidx] = 0;
+                                               }
+                                               if (dtmfidx == FUNCTION_LEN)
+                                               {
+                                                       process_dtmf(dtmfbuf,myrpt);
+                                                       dtmfbuf[0] = 0;
+                                                       dtmfidx = -1;
+                                                       continue;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if ((!myrpt->callmode) && (c == '*'))
+                                       {
+                                               myrpt->callmode = 1;
+                                               myrpt->cidx = 0;
+                                               myrpt->exten[myrpt->cidx] = 0;
+                                               pthread_attr_init(&attr);
+                                               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+                                               pthread_create(&myrpt->rpt_call_thread,&attr,rpt_call,(void *)myrpt);
+                                               continue;
+                                       }
                                }
                                if (myrpt->callmode && (c == '#'))
                                {
@@ -595,9 +996,16 @@ pthread_attr_t attr;
                                {
                                        if (debug) printf("@@@@ rx un-key\n");
                                        keyed = 0;
+                                       if (myrpt->remotemode != REM_OFF)
+                                       {
+                                               pthread_attr_init(&attr);
+                                               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+                                               pthread_create(&myrpt->rpt_proc_thread,&attr,rpt_remote_telemetry,(void *) myrpt);
+                                       }
                                }
                        }
                        ast_frfree(f);
+                       continue;
                }
                if (who == myrpt->pchannel) /* if it was a read from pseudo */
                {
@@ -610,6 +1018,8 @@ pthread_attr_t attr;
                        if (f->frametype == AST_FRAME_VOICE)
                        {
                                ast_write(myrpt->txchannel,f);
+                               if (myrpt->remotemode == REM_TX)
+                                       ast_write(myrpt->rem_txchannel,f);
                        }
                        if (f->frametype == AST_FRAME_CONTROL)
                        {
@@ -621,6 +1031,7 @@ pthread_attr_t attr;
                                }
                        }
                        ast_frfree(f);
+                       continue;
                }
                if (who == myrpt->txchannel) /* if it was a read from tx */
                {
@@ -640,12 +1051,95 @@ pthread_attr_t attr;
                                }
                        }
                        ast_frfree(f);
+                       continue;
+               }
+               if (who == myrpt->rem_rxchannel) /* if it was a read from rx */
+               {
+                       f = ast_read(myrpt->rem_rxchannel);
+                       if (!f)
+                       {
+                               if (debug) printf("@@@@ rpt:Hung Up\n");
+                               break;
+                       }
+                       if (f->frametype == AST_FRAME_CONTROL)
+                       {
+                               if (f->subclass == AST_CONTROL_HANGUP)
+                               {
+                                       if (debug) printf("@@@@ rpt:Hung Up\n");
+                                       ast_frfree(f);
+                                       break;
+                               }
+                               /* if RX key */
+                               if (f->subclass == AST_CONTROL_RADIO_KEY)
+                               {
+                                       if (debug) printf("@@@@ remote rx key\n");
+                                       if (!myrpt->remotetx)
+                                       {
+                                               myrpt->remoterx = 1;
+                                               ci.chan = 0;
+                                               ci.confno = myrpt->pconf;
+                                               ci.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
+                                               /* Put on conf */
+                                               if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                                               {
+                                                       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                                                       pthread_exit(NULL);
+                                               }
+                                       }
+                               }
+                               /* if RX un-key */
+                               if (f->subclass == AST_CONTROL_RADIO_UNKEY)
+                               {
+                                       if (debug) printf("@@@@ remote rx un-key\n");
+                                       if (!myrpt->remotetx) 
+                                       {
+                                               myrpt->remoterx = 0;
+                                               ci.chan = 0;
+                                               ci.confno = 0;
+                                               ci.confmode = 0;
+                                               /* Take off conf */
+                                               if (ioctl(myrpt->rem_rxchannel->fds[0],ZT_SETCONF,&ci) == -1)
+                                               {
+                                                       ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+                                                       pthread_exit(NULL);
+                                               }
+                                       }
+                               }
+                       }
+                       ast_frfree(f);
+                       continue;
+               }
+               if (who == myrpt->rem_txchannel) /* if it was a read from remote tx */
+               {
+                       f = ast_read(myrpt->rem_txchannel);
+                       if (!f)
+                       {
+                               if (debug) printf("@@@@ rpt:Hung Up\n");
+                               break;
+                       }
+                       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;
                }
 
        }
        ast_hangup(myrpt->pchannel);
        ast_hangup(myrpt->rxchannel);
-       ast_hangup(myrpt->txchannel);
+       if (myrpt->txchannel != myrpt->rxchannel) ast_hangup(myrpt->txchannel);
+       if (myrpt->rem_rxchannel)
+       {
+               ast_hangup(myrpt->rem_rxchannel);
+               if (myrpt->rem_txchannel != myrpt->rem_rxchannel) 
+                       ast_hangup(myrpt->rem_txchannel);
+       }
        if (debug) printf("@@@@ rpt:Hung up channel\n");
        pthread_exit(NULL);
        return NULL;
@@ -675,6 +1169,8 @@ int        i,n;
                rpt_vars[n].name = this;
                rpt_vars[n].rxchanname = ast_variable_retrieve(cfg,this,"rxchannel");
                rpt_vars[n].txchanname = ast_variable_retrieve(cfg,this,"txchannel");
+               rpt_vars[n].rem_rxchanname = ast_variable_retrieve(cfg,this,"remote_rxchannel");
+               rpt_vars[n].rem_txchanname = ast_variable_retrieve(cfg,this,"remote_txchannel");
                rpt_vars[n].ourcontext = ast_variable_retrieve(cfg,this,"context");
                if (!rpt_vars[n].ourcontext) rpt_vars[n].ourcontext = this;
                rpt_vars[n].ourcallerid = ast_variable_retrieve(cfg,this,"callerid");
@@ -689,6 +1185,10 @@ int       i,n;
                val = ast_variable_retrieve(cfg,this,"idtime");
                if (val) rpt_vars[n].idtime = atoi(val);
                        else rpt_vars[n].idtime = IDTIME;
+               val = ast_variable_retrieve(cfg,this,"simple");
+               if (val) rpt_vars[n].simple = ast_true(val); 
+                       else rpt_vars[n].simple = 0;
+               rpt_vars[n].tonezone = ast_variable_retrieve(cfg,this,"tonezone");
                n++;
        }
        ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n);
index 79584d2..956c39b 100755 (executable)
@@ -5676,11 +5676,15 @@ static struct zt_pvt *mkintf(int channel, int signalling, int radio)
                                p.debouncetime = cur_debounce;
                }
 
-               res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_PARAMS, &p);
-               if (res < 0) {
-                       ast_log(LOG_ERROR, "Unable to set parameters\n");
-                       free(tmp);
-                       return NULL;
+               /* dont set parms on a pseudo-channel */
+               if (tmp->subs[SUB_REAL].zfd >= 0)
+               {
+                       res = ioctl(tmp->subs[SUB_REAL].zfd, ZT_SET_PARAMS, &p);
+                       if (res < 0) {
+                               ast_log(LOG_ERROR, "Unable to set parameters\n");
+                               free(tmp);
+                               return NULL;
+                       }
                }
 #if 1
                if (!here && (tmp->subs[SUB_REAL].zfd > -1)) {
index c970409..546a61d 100755 (executable)
@@ -1,4 +1,4 @@
-; Radio Repeater configuration file (for use with app_rpt)
+; Radio Repeater / Remote Base configuration file (for use with app_rpt)
 ;
 
 ;[oakhurst]                            ; Name of First Repeater
@@ -7,8 +7,14 @@
 ; Note: if you use a unified interface (tx/rx on one channel), only
 ; specify the rxchannel and the txchannel will be assumed from the rxchannel
 ;txchannel = Zap/2                     ; Tx audio/signalling channel
+;remote_rxchannel = Zap/3              ; Remote Base Rx audio/signalling channel
+; Note: if you use a unified interface (tx/rx on one channel), only
+; specify the remote_rxchannel and the remote_txchannel will be assumed from the rxchannel
+;remote_txchannel = Zap/4              ; Remote Base Tx audio/signalling channel
+;tonezone = us                         ; use US tones
+;simple = no                           ; Not in "simple" mode
 ;context = default                     ; dialing context for phone
-;callerid = "WB6NIL Repeater" <(818) 325-2757>  ; Callerid for phone calls
+;callerid = "WB6NIL Repeater" <(559) 692-1234>  ; Callerid for phone calls
 ;idrecording = wb6nil                  ; id recording
 ;accountcode=RADIO                     ; account code (optional)
 ;hangtime=1000                         ; squelch tail hang time (in ms) (optional)
diff --git a/dsp.c b/dsp.c
index 7748752..fa66318 100755 (executable)
--- a/dsp.c
+++ b/dsp.c
@@ -123,7 +123,11 @@ static struct progress {
 #define FAX_THRESHOLD              8.0e7
 #define FAX_2ND_HARMONIC                       2.0     /* 4dB */
 #define DTMF_NORMAL_TWIST           6.3     /* 8dB */
+#ifdef RADIO_RELAX
+#define DTMF_REVERSE_TWIST          ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 6.5 : 2.5)     /* 4dB normal */
+#else
 #define DTMF_REVERSE_TWIST          ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5)     /* 4dB normal */
+#endif
 #define DTMF_RELATIVE_PEAK_ROW      6.3     /* 8dB */
 #define DTMF_RELATIVE_PEAK_COL      6.3     /* 8dB */
 #define DTMF_2ND_HARMONIC_ROW       ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5)     /* 4dB normal */