]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Added incoming audio notch filtering, plus a bunch of command improvements, etc.
authorJim Dixon <telesistant@hotmail.com>
Wed, 24 May 2006 07:01:02 +0000 (07:01 +0000)
committerJim Dixon <telesistant@hotmail.com>
Wed, 24 May 2006 07:01:02 +0000 (07:01 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@29935 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_rpt.c

index f3edb044d77b82a967dddf2e5c766b603d5fe8ff..2ca6fa4184b7b3f3f4a1a0141ea4134a04584de5 100644 (file)
@@ -1,3 +1,4 @@
+/* #define OLD_ASTERISK */
 /*
  * Asterisk -- An open source telephony toolkit.
  *
@@ -20,7 +21,7 @@
 /*! \file
  *
  * \brief Radio Repeater / Remote Base program 
- *  version 0.42 02/25/06
+ *  version 0.47 05/23/06
  * 
  * \author Jim Dixon, WB6NIL <jim@lambdatel.com>
  *
  * Repeater / Remote Functions:
  * "Simple" Mode:  * - autopatch access, # - autopatch hangup
  * Normal mode:
- * See the function list in rpt.conf
+ * See the function list in rpt.conf (autopatchup, autopatchdn)
+ * autopatchup can optionally take comma delimited setting=value pairs:
+ *  
+ *
+ * context=string              :       Override default context with "string"
+ * dialtime=ms                 :       Specify the max number of milliseconds between phone number digits (1000 milliseconds = 1 second)
+ * farenddisconnect=1          :       Automatically disconnect when called party hangs up
+ * noct=1                      :       Don't send repeater courtesy tone during autopatch calls
+ * quiet=1                     :       Don't send dial tone, or connect messages. Do not send patch down message when called party hangs up
+ *
+ *
+ * Example: 123=autopatchup,dialtime=20000,noct=1,farenddisconnect=1
  *
  *  To send an asterisk (*) while dialing or talking on phone,
  *  use the autopatch acess code.
  *  140 - Link Status (brief)
  *
  *
+ *
+ * 'duplex' modes:  (defaults to duplex=2)
+ *
+ * 0 - Only remote links key Tx and no main repeat audio.
+ * 1 - Everything other then main Rx keys Tx, no main repeat audio.
+ * 2 - Normal mode
+ * 3 - Normal except no main repeat audio.
+ * 4 - Normal except no main repeat audio during autopatch only
+ *
 */
 
 /*** MODULEINFO
        <defaultenabled>no</defaultenabled>
  ***/
 
-/* The following is JUST GROSS!! There is some soft of underlying problem,
-   probably in channel_iax2.c, that causes an IAX2 connection to sometimes
-   stop transmitting randomly. We have been working for weeks to try to
-   locate it and fix it, but to no avail We finally decided to put our
-   tail between our legs, and just make the radio system re-connect upon
-   network failure. This just shouldnt have to be done. For normal operation,
-   comment-out the following line */
-#define        RECONNECT_KLUDGE 
+/* Un-comment the following to include support for MDC-1200 digital tone
+   signalling protocol (using KA6SQG's GPL'ed implementation) */
+/* file must be downloaded separately, not part of Asterisk distribution */
+/* #include "mdc_decode.c" */
+
+/* Un-comment the following to include support for notch filters in the
+   rx audio stream (using Tony Fisher's mknotch (mkfilter) implementation) */
+/* file must be downloaded separately, not part of Asterisk distribution */
+/* #include "rpt_notch.c" */
 
 /* maximum digits in DTMF buffer, and seconds after * for DTMF command timeout */
 
 #define        MACROPTIME 500
 #define        DTMF_TIMEOUT 3
 
+#ifdef __RPT_NOTCH
+#define        MAXFILTERS 10
+#endif
+
 #define        DISC_TIME 10000  /* report disc after 10 seconds of no connect */
 #define        MAX_RETRIES 5
 
 
 #define        RETRY_TIMER_MS 5000
 
+#define MAXPEERSTR 31
 #define        MAXREMSTR 15
 
 #define        DELIMCHR ','
 
 #define MAXNODESTR 300
 
+#define MAXPATCHCONTEXT 100
+
 #define ACTIONSIZE 32
 
 #define TELEPARAMSIZE 256
@@ -156,7 +185,7 @@ enum {REM_OFF,REM_MONITOR,REM_TX};
 enum{ID,PROC,TERM,COMPLETE,UNKEY,REMDISC,REMALREADY,REMNOTFOUND,REMGO,
        CONNECTED,CONNFAIL,STATUS,TIMEOUT,ID1, STATS_TIME,
        STATS_VERSION, IDTALKOVER, ARB_ALPHA, TEST_TONE, REV_PATCH,
-       TAILMSG, MACRO_NOTFOUND, MACRO_BUSY};
+       TAILMSG, MACRO_NOTFOUND, MACRO_BUSY, LASTNODEKEY};
 
 enum {REM_SIMPLEX,REM_MINUS,REM_PLUS};
 
@@ -215,7 +244,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/say.h"
 #include "asterisk/localtime.h"
 
-static  char *tdesc = "Radio Repeater / Remote Base  version 0.42  02/25/2006";
+static  char *tdesc = "Radio Repeater / Remote Base  version 0.47  05/23/2006";
 
 static char *app = "Rpt";
 
@@ -263,7 +292,9 @@ char *discstr = "!!DISCONNECT!!";
 static char *remote_rig_ft897="ft897";
 static char *remote_rig_rbi="rbi";
 
-struct ast_config *cfg;
+#ifdef OLD_ASTERISK
+STANDARD_LOCAL_USER;
+#endif
 
 LOCAL_USER_DECL;
 
@@ -272,6 +303,7 @@ LOCAL_USER_DECL;
 #define        TOTIME 180000
 #define        IDTIME 300000
 #define        MAXRPTS 20
+#define MAX_STAT_LINKS 32
 #define POLITEID 30000
 #define FUNCTDELAY 1500
 
@@ -299,10 +331,24 @@ struct rpt_link
        long    retrytimer;
        long    retxtimer;
        int     retries;
+       int     reconnects;
+       long long connecttime;
        struct ast_channel *chan;       
        struct ast_channel *pchan;      
 } ;
 
+struct rpt_lstat
+{
+       struct  rpt_lstat *next;
+       struct  rpt_lstat *prev;
+       char    peer[MAXPEERSTR];
+       char    name[MAXNODESTR];
+       char    mode;
+       char    outbound;
+       char    reconnects;
+       long long       connecttime;
+} ;
+
 struct rpt_tele
 {
        struct rpt_tele *next;
@@ -339,53 +385,77 @@ struct telem_defaults
 
 static struct rpt
 {
-       char *name;
        ast_mutex_t lock;
+       struct ast_config *cfg;
+       char reload;
+
+       char *name;
        char *rxchanname;
        char *txchanname;
-       char *ourcontext;
-       char *ourcallerid;
-       char *acctcode;
-       char *ident;
-       char *tonezone;
-       char *functions;
-       char *link_functions;
-       char *phone_functions;
-       char *dphone_functions;
-       char *nodes;
+       char *remote;
+
+       struct {
+
+               char *ourcontext;
+               char *ourcallerid;
+               char *acctcode;
+               char *ident;
+               char *tonezone;
+               char simple;
+               char *functions;
+               char *link_functions;
+               char *phone_functions;
+               char *dphone_functions;
+               char *nodes;
+               int hangtime;
+               int totime;
+               int idtime;
+               int tailmessagetime;
+               int tailsquashedtime;
+               int duplex;
+               int politeid;
+               char *tailmessages[500];
+               int tailmessagemax;
+               char    *memory;
+               char    *macro;
+               char    *startupmacro;
+               int iobase;
+               char funcchar;
+               char endchar;
+               char    nobusyout;
+       } p;
        struct rpt_link links;
-       int hangtime;
-       int totime;
-       int idtime;
        int unkeytocttimer;
-       int duplex;
        char keyed;
        char exttx;
        char localtx;
        char remoterx;
        char remotetx;
        char remoteon;
-       char simple;
-       char *remote;
        char tounkeyed;
        char tonotify;
        char enable;
        char dtmfbuf[MAXDTMF];
        char macrobuf[MAXMACRO];
        char rem_dtmfbuf[MAXDTMF];
+       char lastdtmfcommand[MAXDTMF];
        char cmdnode[50];
        struct ast_channel *rxchannel,*txchannel;
        struct ast_channel *pchannel,*txpchannel, *remchannel;
        struct rpt_tele tele;
+       struct timeval lasttv,curtv;
        pthread_t rpt_call_thread,rpt_thread;
        time_t dtmf_time,rem_dtmf_time,dtmf_time_rem;
-       int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer,tmsgtimer;
+       int tailtimer,totimer,idtimer,txconf,conf,callmode,cidx,scantimer,tmsgtimer,skedtimer;
        int mustid,tailid;
-       int politeid;
+       int tailevent;
+       int telemrefcount;
        int dtmfidx,rem_dtmfidx;
+       int dailytxtime,dailykerchunks,totalkerchunks,dailykeyups,totalkeyups,timeouts;
+       int totalexecdcommands, dailyexecdcommands;
        long    retxtimer;
+       long long totaltxtime;
        char mydtmf;
-       int iobase;
        char exten[AST_MAX_EXTENSION];
        char freq[MAXREMSTR],rxpl[MAXREMSTR],txpl[MAXREMSTR];
        char offset;
@@ -397,9 +467,12 @@ static struct rpt
        char hfscanmode;
        int hfscanstatus;
        char lastlinknode[MAXNODESTR];
-       char funcchar;
-       char endchar;
        char stopgen;
+       char patchfarenddisconnect;
+       char patchnoct;
+       char patchquiet;
+       char patchcontext[MAXPATCHCONTEXT];
+       int patchdialtime;
        int macro_longest;
        int phone_longestfunc;
        int dphone_longestfunc;
@@ -407,18 +480,31 @@ static struct rpt
        int longestfunc;
        int longestnode;
        int threadrestarts;             
-       int tailmessagetime;
-       int tailsquashedtime;
-       char *tailmessages[500];
-       int tailmessagemax;
        int tailmessagen;
        time_t disgorgetime;
        time_t lastthreadrestarttime;
        long    macrotimer;
-       char    *macro;
-       char    *startupmacro;
-       char    *memory;
-       char    nobusyout;
+       char    lastnodewhichkeyedusup[MAXNODESTR];
+#ifdef __RPT_NOTCH
+       struct rptfilter
+       {
+               char    desc[100];
+               float   x0;
+               float   x1;
+               float   x2;
+               float   y0;
+               float   y1;
+               float   y2;
+               float   gain;
+               float   const0;
+               float   const1;
+               float   const2;
+       } filters[MAXFILTERS];
+#endif
+#ifdef _MDC_DECODE_H_
+       mdc_decoder_t *mdc;
+       unsigned short lastunit;
+#endif
 } rpt_vars[MAXRPTS];   
 
 
@@ -613,6 +699,10 @@ pthread_t id;
 /* Debug mode */
 static int rpt_do_debug(int fd, int argc, char *argv[]);
 static int rpt_do_dump(int fd, int argc, char *argv[]);
+static int rpt_do_stats(int fd, int argc, char *argv[]);
+static int rpt_do_lstats(int fd, int argc, char *argv[]);
+static int rpt_do_reload(int fd, int argc, char *argv[]);
+static int rpt_do_restart(int fd, int argc, char *argv[]);
 
 static char debug_usage[] =
 "Usage: rpt debug level {0-7}\n"
@@ -622,6 +712,22 @@ static char dump_usage[] =
 "Usage: rpt dump <nodename>\n"
 "       Dumps struct debug info to log\n";
 
+static char dump_stats[] =
+"Usage: rpt stats <nodename>\n"
+"       Dumps node statistics to console\n";
+
+static char dump_lstats[] =
+"Usage: rpt lstats <nodename>\n"
+"       Dumps link statistics to console\n";
+
+static char reload_usage[] =
+"Usage: rpt reload\n"
+"       Reloads app_rpt running config parameters\n";
+
+static char restart_usage[] =
+"Usage: rpt restart\n"
+"       Restarts app_rpt\n";
+
 static struct ast_cli_entry  cli_debug =
         { { "rpt", "debug", "level" }, rpt_do_debug, 
                "Enable app_rpt debugging", debug_usage };
@@ -630,6 +736,22 @@ static struct ast_cli_entry  cli_dump =
         { { "rpt", "dump" }, rpt_do_dump,
                "Dump app_rpt structs for debugging", dump_usage };
 
+static struct ast_cli_entry  cli_stats =
+        { { "rpt", "stats" }, rpt_do_stats,
+               "Dump node statistics", dump_stats };
+
+static struct ast_cli_entry  cli_lstats =
+        { { "rpt", "lstats" }, rpt_do_lstats,
+               "Dump link statistics", dump_lstats };
+
+static struct ast_cli_entry  cli_reload =
+        { { "rpt", "reload" }, rpt_do_reload,
+               "Reload app_rpt config", reload_usage };
+
+static struct ast_cli_entry  cli_restart =
+        { { "rpt", "restart" }, rpt_do_restart,
+               "Restart app_rpt", restart_usage };
+
 /*
 * Telemetry defaults
 */
@@ -682,10 +804,20 @@ static struct function_table_tag function_table[] = {
        {"remote", function_remote},
        {"macro", function_macro}
 } ;
+
+/*
+* Break up a delimited string into a table of substrings
+*
+* str - delimited string ( will be modified )
+* strp- list of pointers to substrings (this is built by this function), NULL will be placed at end of list
+* limit- maximum number of substrings to process
+*/
        
-static int finddelim(char *str,char *strp[])
+
+
+static int finddelim(char *str, char *strp[], int limit)
 {
-int     i,inquo;
+int     i,l,inquo;
 
         inquo = 0;
         i = 0;
@@ -695,7 +827,7 @@ int     i,inquo;
                 strp[0] = 0;
                 return(0);
            }
-        for(; *str; str++)
+        for(l = 0; *str && (l < limit) ; str++)
            {
                 if (*str == QUOTECHR)
                    {
@@ -713,6 +845,7 @@ int     i,inquo;
                 if ((*str == DELIMCHR) && (!inquo))
                 {
                         *str = 0;
+                       l++;
                         strp[i++] = str + 1;
                 }
            }
@@ -721,6 +854,53 @@ int     i,inquo;
 
 }
 
+/*
+* Match a keyword in a list, and return index of string plus 1 if there was a match,
+* else return 0. If param is passed in non-null, then it will be set to the first character past the match
+*/
+
+static int matchkeyword(char *string, char **param, char *keywords[])
+{
+int    i,ls;
+       for( i = 0 ; keywords[i] ; i++){
+               ls = strlen(keywords[i]);
+               if(!ls){
+                       *param = NULL;
+                       return 0;
+               }
+               if(!strncmp(string, keywords[i], ls)){
+                       if(param)
+                               *param = string + ls;
+                       return i + 1; 
+               }
+       }
+       param = NULL;
+       return 0;
+}
+
+/*
+* Skip characters in string which are in charlist, and return a pointer to the
+* first non-matching character
+*/
+
+static char *skipchars(char *string, char *charlist)
+{
+int i; 
+       while(*string){
+               for(i = 0; charlist[i] ; i++){
+                       if(*string == charlist[i]){
+                               string++;
+                               break;
+                       }
+               }
+               if(!charlist[i])
+                       return string;
+       }
+       return string;
+}      
+                                       
+
+
 static int myatoi(char *str)
 {
 int    ret;
@@ -731,6 +911,248 @@ int       ret;
        return ret;
 }
 
+
+#ifdef __RPT_NOTCH
+
+/* rpt filter routine */
+static void rpt_filter(struct rpt *myrpt, volatile short *buf, int len)
+{
+int    i,j;
+struct rptfilter *f;
+
+       for(i = 0; i < len; i++)
+       {
+               for(j = 0; j < MAXFILTERS; j++)
+               {
+                       f = &myrpt->filters[j];
+                       if (!*f->desc) continue;
+                       f->x0 = f->x1; f->x1 = f->x2;
+                       f->x2 = ((float)buf[i]) / f->gain;
+                       f->y0 = f->y1; f->y1 = f->y2;
+                       f->y2 =   (f->x0 + f->x2) +   f->const0 * f->x1
+                                    + (f->const1 * f->y0) + (f->const2 * f->y1);
+                       buf[i] = (short)f->y2;
+               }
+       }
+}
+
+#endif
+
+/* Retrieve an int from a config file */
+                                                                                
+static int retrieve_astcfgint(struct rpt *myrpt,char *category, char *name, int min, int max, int defl)
+{
+        char *var;
+        int ret;
+                                                                                
+        var = ast_variable_retrieve(myrpt->cfg, category, name);
+        if(var){
+                ret = myatoi(var);
+                if(ret < min)
+                        ret = min;
+                if(ret > max)
+                        ret = max;
+        }
+        else
+                ret = defl;
+        return ret;
+}
+
+
+static void load_rpt_vars(int n,int init)
+{
+char *this,*val;
+int    j,longestnode;
+struct ast_variable *vp;
+struct ast_config *cfg;
+#ifdef __RPT_NOTCH
+int    i;
+char *strs[100];
+#endif
+
+       if (option_verbose > 2)
+               ast_verbose(VERBOSE_PREFIX_3 "%s config for repeater %s\n",
+                       (init) ? "Loading initial" : "Re-Loading",rpt_vars[n].name);
+       ast_mutex_lock(&rpt_vars[n].lock);
+       if (rpt_vars[n].cfg) ast_config_destroy(rpt_vars[n].cfg);
+       cfg = ast_config_load("rpt.conf");
+       if (!cfg) {
+               ast_mutex_unlock(&rpt_vars[n].lock);
+               ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf.  Radio Repeater disabled.\n");
+               pthread_exit(NULL);
+       }
+       rpt_vars[n].cfg = cfg; 
+       this = rpt_vars[n].name;
+       memset(&rpt_vars[n].p,0,sizeof(rpt_vars[n].p));
+       if (init)
+       {
+               char *cp;
+               int savearea = (char *)&rpt_vars[n].p - (char *)&rpt_vars[n];
+
+               cp = (char *) &rpt_vars[n].p;
+               memset(cp + sizeof(rpt_vars[n].p),0,
+                       sizeof(rpt_vars[n]) - (sizeof(rpt_vars[n].p) + savearea));
+               rpt_vars[n].tele.next = &rpt_vars[n].tele;
+               rpt_vars[n].tele.prev = &rpt_vars[n].tele;
+               rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
+               rpt_vars[n].tailmessagen = 0;
+       }
+#ifdef __RPT_NOTCH
+       /* zot out filters stuff */
+       memset(&rpt_vars[n].filters,0,sizeof(rpt_vars[n].filters));
+#endif
+       val = ast_variable_retrieve(cfg,this,"context");
+       if (val) rpt_vars[n].p.ourcontext = val;
+       else rpt_vars[n].p.ourcontext = this;
+       val = ast_variable_retrieve(cfg,this,"callerid");
+       if (val) rpt_vars[n].p.ourcallerid = val;
+       val = ast_variable_retrieve(cfg,this,"accountcode");
+       if (val) rpt_vars[n].p.acctcode = val;
+       val = ast_variable_retrieve(cfg,this,"idrecording");
+       if (val) rpt_vars[n].p.ident = val;
+       val = ast_variable_retrieve(cfg,this,"hangtime");
+       if (val) rpt_vars[n].p.hangtime = atoi(val);
+               else rpt_vars[n].p.hangtime = HANGTIME;
+       val = ast_variable_retrieve(cfg,this,"totime");
+       if (val) rpt_vars[n].p.totime = atoi(val);
+               else rpt_vars[n].p.totime = TOTIME;
+       rpt_vars[n].p.tailmessagetime = retrieve_astcfgint(&rpt_vars[n],this, "tailmessagetime", 0, 2400000, 0);                
+       rpt_vars[n].p.tailsquashedtime = retrieve_astcfgint(&rpt_vars[n],this, "tailsquashedtime", 0, 2400000, 0);              
+       rpt_vars[n].p.duplex = retrieve_astcfgint(&rpt_vars[n],this,"duplex",0,4,2);
+       rpt_vars[n].p.idtime = retrieve_astcfgint(&rpt_vars[n],this, "idtime", 60000, 2400000, IDTIME); /* Enforce a min max */
+       rpt_vars[n].p.politeid = retrieve_astcfgint(&rpt_vars[n],this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */
+       val = ast_variable_retrieve(cfg,this,"tonezone");
+       if (val) rpt_vars[n].p.tonezone = val;
+       rpt_vars[n].p.tailmessages[0] = 0;
+       rpt_vars[n].p.tailmessagemax = 0;
+       val = ast_variable_retrieve(cfg,this,"tailmessagelist");
+       if (val) rpt_vars[n].p.tailmessagemax = finddelim(val, rpt_vars[n].p.tailmessages, 500);
+       val = ast_variable_retrieve(cfg,this,"memory");
+       if (!val) val = MEMORY;
+       rpt_vars[n].p.memory = val;
+       val = ast_variable_retrieve(cfg,this,"macro");
+       if (!val) val = MACRO;
+       rpt_vars[n].p.macro = val;
+       val = ast_variable_retrieve(cfg,this,"startup_macro");
+       if (val) rpt_vars[n].p.startupmacro = val;
+       val = ast_variable_retrieve(cfg,this,"iobase");
+       /* do not use atoi() here, we need to be able to have
+               the input specified in hex or decimal so we use
+               sscanf with a %i */
+       if ((!val) || (sscanf(val,"%i",&rpt_vars[n].p.iobase) != 1))
+       rpt_vars[n].p.iobase = DEFAULT_IOBASE;
+       val = ast_variable_retrieve(cfg,this,"functions");
+       if (!val)
+               {
+                       val = FUNCTIONS;
+                       rpt_vars[n].p.simple = 1;
+               } 
+       rpt_vars[n].p.functions = val;
+       val =  ast_variable_retrieve(cfg,this,"link_functions");
+       if (val) rpt_vars[n].p.link_functions = val;
+       else 
+               rpt_vars[n].p.link_functions = rpt_vars[n].p.functions;
+       val = ast_variable_retrieve(cfg,this,"phone_functions");
+       if (val) rpt_vars[n].p.phone_functions = val;
+       val = ast_variable_retrieve(cfg,this,"dphone_functions");
+       if (val) rpt_vars[n].p.dphone_functions = val;
+       val = ast_variable_retrieve(cfg,this,"funcchar");
+       if (!val) rpt_vars[n].p.funcchar = FUNCCHAR; else 
+               rpt_vars[n].p.funcchar = *val;          
+       val = ast_variable_retrieve(cfg,this,"endchar");
+       if (!val) rpt_vars[n].p.endchar = ENDCHAR; else 
+               rpt_vars[n].p.endchar = *val;           
+       val = ast_variable_retrieve(cfg,this,"nobusyout");
+       if (val) rpt_vars[n].p.nobusyout = ast_true(val);
+       val = ast_variable_retrieve(cfg,this,"nodes");
+       if (!val) val = NODES;
+       rpt_vars[n].p.nodes = val;
+#ifdef __RPT_NOTCH
+       val = ast_variable_retrieve(cfg,this,"rxnotch");
+       if (val) {
+               i = finddelim(val,strs,MAXFILTERS * 2);
+               i &= ~1; /* force an even number, rounded down */
+               if (i >= 2) for(j = 0; j < i; j += 2)
+               {
+                       rpt_mknotch(atof(strs[j]),atof(strs[j + 1]),
+                         &rpt_vars[n].filters[j >> 1].gain,
+                           &rpt_vars[n].filters[j >> 1].const0,
+                               &rpt_vars[n].filters[j >> 1].const1,
+                                   &rpt_vars[n].filters[j >> 1].const2);
+                       sprintf(rpt_vars[n].filters[j >> 1].desc,"%s Hz, BW = %s",
+                               strs[j],strs[j + 1]);
+               }
+
+       }
+#endif
+       longestnode = 0;
+
+       vp = ast_variable_browse(cfg, rpt_vars[n].p.nodes);
+               
+       while(vp){
+               j = strlen(vp->name);
+               if (j > longestnode)
+                       longestnode = j;
+               vp = vp->next;
+       }
+
+       rpt_vars[n].longestnode = longestnode;
+               
+       /*
+       * For this repeater, Determine the length of the longest function 
+       */
+       rpt_vars[n].longestfunc = 0;
+       vp = ast_variable_browse(cfg, rpt_vars[n].p.functions);
+       while(vp){
+               j = strlen(vp->name);
+               if (j > rpt_vars[n].longestfunc)
+                       rpt_vars[n].longestfunc = j;
+               vp = vp->next;
+       }
+       /*
+       * For this repeater, Determine the length of the longest function 
+       */
+       rpt_vars[n].link_longestfunc = 0;
+       vp = ast_variable_browse(cfg, rpt_vars[n].p.link_functions);
+       while(vp){
+               j = strlen(vp->name);
+               if (j > rpt_vars[n].link_longestfunc)
+                       rpt_vars[n].link_longestfunc = j;
+               vp = vp->next;
+       }
+       rpt_vars[n].phone_longestfunc = 0;
+       if (rpt_vars[n].p.phone_functions)
+       {
+               vp = ast_variable_browse(cfg, rpt_vars[n].p.phone_functions);
+               while(vp){
+                       j = strlen(vp->name);
+                       if (j > rpt_vars[n].phone_longestfunc)
+                               rpt_vars[n].phone_longestfunc = j;
+                       vp = vp->next;
+               }
+       }
+       rpt_vars[n].dphone_longestfunc = 0;
+       if (rpt_vars[n].p.dphone_functions)
+       {
+               vp = ast_variable_browse(cfg, rpt_vars[n].p.dphone_functions);
+               while(vp){
+                       j = strlen(vp->name);
+                       if (j > rpt_vars[n].dphone_longestfunc)
+                               rpt_vars[n].dphone_longestfunc = j;
+                       vp = vp->next;
+               }
+       }
+       rpt_vars[n].macro_longest = 1;
+       vp = ast_variable_browse(cfg, rpt_vars[n].p.macro);
+       while(vp){
+               j = strlen(vp->name);
+               if (j > rpt_vars[n].macro_longest)
+                       rpt_vars[n].macro_longest = j;
+               vp = vp->next;
+       }
+       ast_mutex_unlock(&rpt_vars[n].lock);
+}
+
 /*
 * Enable or disable debug output at a given level at the console
 */
@@ -776,6 +1198,307 @@ static int rpt_do_dump(int fd, int argc, char *argv[])
        return RESULT_FAILURE;
 }
 
+/*
+* Dump statistics onto console
+*/
+
+static int rpt_do_stats(int fd, int argc, char *argv[])
+{
+       int i,j;
+       int dailytxtime, dailykerchunks;
+       int totalkerchunks, dailykeyups, totalkeyups, timeouts;
+       int totalexecdcommands, dailyexecdcommands, hours, minutes, seconds;
+       long long totaltxtime;
+       struct  rpt_link *l;
+       char *listoflinks[MAX_STAT_LINKS];      
+       char *lastnodewhichkeyedusup, *lastdtmfcommand;
+       char *tot_state, *ider_state, *patch_state;
+       char *reverse_patch_state, *enable_state, *input_signal, *called_number;
+       struct rpt *myrpt;
+
+       static char *not_applicable = "N/A";
+
+       if(argc != 3)
+               return RESULT_SHOWUSAGE;
+
+       for(i = 0 ; i <= MAX_STAT_LINKS; i++)
+               listoflinks[i] = NULL;
+
+       tot_state = ider_state = 
+       patch_state = reverse_patch_state = 
+       input_signal = called_number = 
+       lastdtmfcommand = not_applicable;
+
+       for(i = 0; i < nrpts; i++)
+       {
+               if (!strcmp(argv[2],rpt_vars[i].name)){
+                       /* Make a copy of all stat variables while locked */
+                       myrpt = &rpt_vars[i];
+                       rpt_mutex_lock(&myrpt->lock); /* LOCK */
+
+                       dailytxtime = myrpt->dailytxtime;
+                       totaltxtime = myrpt->totaltxtime;
+                       dailykeyups = myrpt->dailykeyups;
+                       totalkeyups = myrpt->totalkeyups;
+                       dailykerchunks = myrpt->dailykerchunks;
+                       totalkerchunks = myrpt->totalkerchunks;
+                       dailyexecdcommands = myrpt->dailyexecdcommands;
+                       totalexecdcommands = myrpt->totalexecdcommands;
+                       timeouts = myrpt->timeouts;
+
+                       /* Traverse the list of connected nodes */
+                       reverse_patch_state = "DOWN";
+                       j = 0;
+                       l = myrpt->links.next;
+                       while(l != &myrpt->links){
+                               if (l->name[0] == '0'){ /* Skip '0' nodes */
+                                       reverse_patch_state = "UP";
+                                       l = l->next;
+                                       continue;
+                               }
+                               listoflinks[j] = ast_strdupa(l->name);
+                               if(listoflinks[j])
+                                       j++;
+                               l = l->next;
+                       }
+
+                       lastnodewhichkeyedusup = ast_strdupa(myrpt->lastnodewhichkeyedusup);                    
+                       if((!lastnodewhichkeyedusup) || (!strlen(lastnodewhichkeyedusup)))
+                               lastnodewhichkeyedusup = not_applicable;
+
+                       if(myrpt->keyed)
+                               input_signal = "YES";
+                       else
+                               input_signal = "NO";
+
+                       if(myrpt->enable)
+                               enable_state = "YES";
+                       else
+                               enable_state = "NO";
+
+                       if(!myrpt->totimer)
+                               tot_state = "TIMED OUT!";
+                       else if(myrpt->totimer != myrpt->p.totime)
+                               tot_state = "ARMED";
+                       else
+                               tot_state = "RESET";
+
+                       if(myrpt->tailid)
+                               ider_state = "QUEUED IN TAIL";
+                       else if(myrpt->mustid)
+                               ider_state = "QUEUED FOR CLEANUP";
+                       else
+                               ider_state = "CLEAN";
+
+                       switch(myrpt->callmode){
+                               case 1:
+                                       patch_state = "DIALING";
+                                       break;
+                               case 2:
+                                       patch_state = "CONNECTING";
+                                       break;
+                               case 3:
+                                       patch_state = "UP";
+                                       break;
+
+                               case 4:
+                                       patch_state = "CALL FAILED";
+                                       break;
+
+                               default:
+                                       patch_state = "DOWN";
+                       }
+
+                       if(strlen(myrpt->exten)){
+                               called_number = ast_strdupa(myrpt->exten);
+                               if(!called_number)
+                                       called_number = not_applicable;
+                       }
+
+                       if(strlen(myrpt->lastdtmfcommand)){
+                               lastdtmfcommand = ast_strdupa(myrpt->lastdtmfcommand);
+                               if(!lastdtmfcommand)
+                                       lastdtmfcommand = not_applicable;
+                       }
+
+                       rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+
+                       ast_cli(fd, "************************ NODE %s STATISTICS *************************\n\n", myrpt->name);
+                       ast_cli(fd, "Signal on input..................................: %s\n", input_signal);
+                       ast_cli(fd, "Transmitter enabled..............................: %s\n", enable_state);
+                       ast_cli(fd, "Time out timer state.............................: %s\n", tot_state);
+                       ast_cli(fd, "Time outs since system initialization............: %d\n", timeouts);
+                       ast_cli(fd, "Identifier state.................................: %s\n", ider_state);
+                       ast_cli(fd, "Kerchunks today..................................: %d\n", dailykerchunks);
+                       ast_cli(fd, "Kerchunks since system initialization............: %d\n", totalkerchunks);
+                       ast_cli(fd, "Keyups today.....................................: %d\n", dailykeyups);
+                       ast_cli(fd, "Keyups since system initialization...............: %d\n", totalkeyups);
+                       ast_cli(fd, "DTMF commands today..............................: %d\n", dailyexecdcommands);
+                       ast_cli(fd, "DTMF commands since system initialization........: %d\n", totalexecdcommands);
+                       ast_cli(fd, "Last DTMF command executed.......................: %s\n", lastdtmfcommand);
+
+                       hours = dailytxtime/3600000;
+                       dailytxtime %= 3600000;
+                       minutes = dailytxtime/60000;
+                       dailytxtime %= 60000;
+                       seconds = dailytxtime/1000;
+                       dailytxtime %= 1000;
+
+                       ast_cli(fd, "TX time today ...................................: %02d:%02d:%02d.%d\n",
+                               hours, minutes, seconds, dailytxtime);
+
+                       hours = (int) totaltxtime/3600000;
+                       totaltxtime %= 3600000;
+                       minutes = (int) totaltxtime/60000;
+                       totaltxtime %= 60000;
+                       seconds = (int)  totaltxtime/1000;
+                       totaltxtime %= 1000;
+
+                       ast_cli(fd, "TX time since system initialization..............: %02d:%02d:%02d.%d\n",
+                                hours, minutes, seconds, (int) totaltxtime);
+                       ast_cli(fd, "Nodes currently connected to us..................: ");
+                       for(j = 0 ;; j++){
+                               if(!listoflinks[j]){
+                                       if(!j){
+                                               ast_cli(fd,"<NONE>");
+                                       }
+                                       break;
+                               }
+                               ast_cli(fd, "%s", listoflinks[j]);
+                               if(j % 4 == 3){
+                                       ast_cli(fd, "\n");
+                                       ast_cli(fd, "                                                 : ");
+                               }
+                               else{
+                                       if(listoflinks[j + 1])
+                                               ast_cli(fd, ", ");
+                               }
+                       }
+                       ast_cli(fd,"\n");
+
+                       ast_cli(fd, "Last node which transmitted to us................: %s\n", lastnodewhichkeyedusup);
+                       ast_cli(fd, "Autopatch state..................................: %s\n", patch_state);
+                       ast_cli(fd, "Autopatch called number..........................: %s\n", called_number);
+                       ast_cli(fd, "Reverse patch/IAXRPT connected...................: %s\n\n", reverse_patch_state);
+
+                       return RESULT_SUCCESS;
+               }
+       }
+       return RESULT_FAILURE;
+}
+
+/*
+* Link stats function
+*/
+
+static int rpt_do_lstats(int fd, int argc, char *argv[])
+{
+       int i,j;
+       struct rpt *myrpt;
+       struct rpt_link *l;
+       struct rpt_lstat *s,*t;
+       struct rpt_lstat s_head;
+       if(argc != 3)
+               return RESULT_SHOWUSAGE;
+
+       s = NULL;
+       s_head.next = &s_head;
+       s_head.prev = &s_head;
+
+       for(i = 0; i < nrpts; i++)
+       {
+               if (!strcmp(argv[2],rpt_vars[i].name)){
+                       /* Make a copy of all stat variables while locked */
+                       myrpt = &rpt_vars[i];
+                       rpt_mutex_lock(&myrpt->lock); /* LOCK */
+                       /* Traverse the list of connected nodes */
+                       j = 0;
+                       l = myrpt->links.next;
+                       while(l != &myrpt->links){
+                               if (l->name[0] == '0'){ /* Skip '0' nodes */
+                                       l = l->next;
+                                       continue;
+                               }
+                               if((s = (struct rpt_lstat *) malloc(sizeof(struct rpt_lstat))) == NULL){
+                                       ast_log(LOG_ERROR, "Malloc failed in rpt_do_lstats\n");
+                                       rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+                                       return RESULT_FAILURE;
+                               }
+                               memset(s, 0, sizeof(struct rpt_lstat));
+                               strncpy(s->name, l->name, MAXREMSTR - 1);
+                               pbx_substitute_variables_helper(l->chan, "${IAXPEER(CURRENTCHANNEL)}", s->peer, MAXPEERSTR - 1);
+                               s->mode = l->mode;
+                               s->outbound = l->outbound;
+                               s->reconnects = l->reconnects;
+                               s->connecttime = l->connecttime;
+                               insque((struct qelem *) s, (struct qelem *) s_head.next);
+                               l = l->next;
+                       }
+                       rpt_mutex_unlock(&myrpt->lock); /* UNLOCK */
+                       ast_cli(fd, "NODE      PEER                RECONNECTS  DIRECTION  CONNECT TIME\n");
+                       ast_cli(fd, "----      ----                ----------  ---------  ------------\n");
+
+                       for(s = s_head.next; s != &s_head; s = s->next){
+                               int hours, minutes, seconds;
+                               long long connecttime = s->connecttime;
+                               char conntime[31];
+                               hours = (int) connecttime/3600000;
+                               connecttime %= 3600000;
+                               minutes = (int) connecttime/60000;
+                               connecttime %= 60000;
+                               seconds = (int)  connecttime/1000;
+                               connecttime %= 1000;
+                               snprintf(conntime, 30, "%02d:%02d:%02d.%d",
+                                       hours, minutes, seconds, (int) connecttime);
+                               conntime[30] = 0;
+                               ast_cli(fd, "%-10s%-20s%-12d%-11s%-30s\n",
+                                       s->name, s->peer, s->reconnects, (s->outbound)? "OUT":"IN", conntime);
+                       }       
+                       /* destroy our local link queue */
+                       s = s_head.next;
+                       while(s != &s_head){
+                               t = s;
+                               s = s->next;
+                               remque((struct qelem *)t);
+                               free(t);
+                       }                       
+                       return RESULT_SUCCESS;
+               }
+       }
+       return RESULT_FAILURE;
+}
+
+/*
+* reload vars 
+*/
+                                                                                                                                 
+static int rpt_do_reload(int fd, int argc, char *argv[])
+{
+int    n;
+
+        if (argc > 2) return RESULT_SHOWUSAGE;
+
+       for(n = 0; n < nrpts; n++) rpt_vars[n].reload = 1;
+
+       return RESULT_FAILURE;
+}
+
+/*
+* restart app_rpt
+*/
+                                                                                                                                 
+static int rpt_do_restart(int fd, int argc, char *argv[])
+{
+int    i;
+
+        if (argc > 2) return RESULT_SHOWUSAGE;
+       for(i = 0; i < nrpts; i++)
+       {
+               if (rpt_vars[i].rxchannel) ast_softhangup(rpt_vars[i].rxchannel,AST_SOFTHANGUP_DEV);
+       }
+       return RESULT_FAILURE;
+}
+
 static int play_tone_pair(struct ast_channel *chan, int f1, int f2, int duration, int amplitude)
 {
        int res;
@@ -1054,27 +1777,7 @@ static int saynum(struct ast_channel *mychannel, int num)
 }
 
 
-/* Retrieve an int from a config file */
-                                                                                
-static int retrieve_astcfgint(char *category, char *name, int min, int max, int defl)
-{
-        char *var;
-        int ret;
-                                                                                
-        var = ast_variable_retrieve(cfg, category, name);
-        if(var){
-                ret = myatoi(var);
-                if(ret < min)
-                        ret = min;
-                if(ret > max)
-                        ret = max;
-        }
-        else
-                ret = defl;
-        return ret;
-}
-
-static int telem_any(struct ast_channel *chan, char *entry)
+static int telem_any(struct rpt *myrpt,struct ast_channel *chan, char *entry)
 {
        int res;
        char c;
@@ -1089,11 +1792,11 @@ static int telem_any(struct ast_channel *chan, char *entry)
        res = 0;
        
        if(!morseidfreq){ /* Get the morse parameters if not already loaded */
-               morsespeed = retrieve_astcfgint( mcat, "speed", 5, 20, 20);
-               morsefreq = retrieve_astcfgint( mcat, "frequency", 300, 3000, 800);
-               morseampl = retrieve_astcfgint( mcat, "amplitude", 200, 8192, 4096);
-               morseidampl = retrieve_astcfgint( mcat, "idamplitude", 200, 8192, 2048);
-               morseidfreq = retrieve_astcfgint( mcat, "idfrequency", 300, 3000, 330); 
+               morsespeed = retrieve_astcfgint(myrpt, mcat, "speed", 5, 20, 20);
+               morsefreq = retrieve_astcfgint(myrpt, mcat, "frequency", 300, 3000, 800);
+               morseampl = retrieve_astcfgint(myrpt, mcat, "amplitude", 200, 8192, 4096);
+               morseidampl = retrieve_astcfgint(myrpt, mcat, "idamplitude", 200, 8192, 2048);
+               morseidfreq = retrieve_astcfgint(myrpt, mcat, "idfrequency", 300, 3000, 330);   
        }
        
        /* Is it a file, or a tone sequence? */
@@ -1130,7 +1833,7 @@ static int telem_any(struct ast_channel *chan, char *entry)
 * 4 types of telemtry are handled: Morse ID, Morse Message, Tone Sequence, and a File containing a recording.
 */
 
-static int telem_lookup(struct ast_channel *chan, char *node, char *name)
+static int telem_lookup(struct rpt *myrpt,struct ast_channel *chan, char *node, char *name)
 {
        
        int res;
@@ -1143,21 +1846,19 @@ static int telem_lookup(struct ast_channel *chan, char *node, char *name)
        telemetry_save = NULL;
        entry = NULL;
        
-       
        /* Retrieve the section name for telemetry from the node section */
-       
-       telemetry = ast_variable_retrieve(cfg, node, TELEMETRY);
-       if(telemetry){
+       telemetry = ast_variable_retrieve(myrpt->cfg, node, TELEMETRY);
+       if(telemetry ){
                telemetry_save = ast_strdupa(telemetry);
                if(!telemetry_save){
                        ast_log(LOG_WARNING,"ast_strdupa() failed in telem_lookup()\n");
                        return res;
                }
-               entry = ast_variable_retrieve(cfg, telemetry_save, name);
+               entry = ast_variable_retrieve(myrpt->cfg, telemetry_save, name);
        }
        
-       /* Try to look up the telemetry name */
-       
+       /* Try to look up the telemetry name */ 
+
        if(!entry){
                /* Telemetry name wasn't found in the config file, use the default */
                for(i = 0; i < sizeof(tele_defs)/sizeof(struct telem_defaults) ; i++){
@@ -1165,10 +1866,11 @@ static int telem_lookup(struct ast_channel *chan, char *node, char *name)
                                entry = tele_defs[i].value;
                }
        }
-       if(entry)       
-               telem_any(chan, entry);
+       if(entry){      
+               if(strlen(entry))
+                       telem_any(myrpt,chan, entry);
+       }
        else{
-               ast_log(LOG_WARNING, "Telemetry name not found: %s\n", name);
                res = -1;
        }
        return res;
@@ -1185,7 +1887,7 @@ static int get_wait_interval(struct rpt *myrpt, int type)
         char *wait_times_save;
                                                                                                                   
         wait_times_save = NULL;
-        wait_times = ast_variable_retrieve(cfg, myrpt->name, "wait_times");
+        wait_times = ast_variable_retrieve(myrpt->cfg, myrpt->name, "wait_times");
                                                                                                                   
         if(wait_times){
                 wait_times_save = ast_strdupa(wait_times);
@@ -1198,28 +1900,28 @@ static int get_wait_interval(struct rpt *myrpt, int type)
         switch(type){
                 case DLY_TELEM:
                         if(wait_times)
-                                interval = retrieve_astcfgint(wait_times_save, "telemwait", 500, 5000, 1000);
+                                interval = retrieve_astcfgint(myrpt,wait_times_save, "telemwait", 500, 5000, 1000);
                         else
                                 interval = 1000;
                         break;
                                                                                                                   
                 case DLY_ID:
                         if(wait_times)
-                                interval = retrieve_astcfgint(wait_times_save, "idwait",250,5000,500);
+                                interval = retrieve_astcfgint(myrpt,wait_times_save, "idwait",250,5000,500);
                         else
                                 interval = 500;
                         break;
                                                                                                                   
                 case DLY_UNKEY:
                         if(wait_times)
-                                interval = retrieve_astcfgint(wait_times_save, "unkeywait",500,5000,1000);
+                                interval = retrieve_astcfgint(myrpt,wait_times_save, "unkeywait",500,5000,1000);
                         else
                                 interval = 1000;
                         break;
                                                                                                                   
                 case DLY_CALLTERM:
                         if(wait_times)
-                                interval = retrieve_astcfgint(wait_times_save, "calltermwait",500,5000,1500);
+                                interval = retrieve_astcfgint(myrpt,wait_times_save, "calltermwait",500,5000,1500);
                         else
                                 interval = 1500;
                         break;
@@ -1239,8 +1941,13 @@ static int get_wait_interval(struct rpt *myrpt, int type)
 static void wait_interval(struct rpt *myrpt, int type, struct ast_channel *chan)
 {
        int interval;
-       if((interval = get_wait_interval(myrpt, type)))
+       interval = get_wait_interval(myrpt, type);
+       if(debug)
+               ast_log(LOG_NOTICE," Delay interval = %d\n", interval);
+       if(interval)
                ast_safe_sleep(chan,interval);
+       if(debug)
+               ast_log(LOG_NOTICE,"Delay complete\n");
        return;
 }
 
@@ -1264,9 +1971,8 @@ struct tm localtm;
 
        /* Snag copies of a few key myrpt variables */
        rpt_mutex_lock(&myrpt->lock);
-       insque((struct qelem *)mytele, (struct qelem *)myrpt->tele.next); /* Moved from rpt_telemetry() */
        nodename = ast_strdupa(myrpt->name);
-       ident = ast_strdupa(myrpt->ident);
+       ident = ast_strdupa(myrpt->p.ident);
        rpt_mutex_unlock(&myrpt->lock);
        
        /* allocate a pseudo-channel thru asterisk */
@@ -1314,35 +2020,41 @@ struct tm localtm;
            case ID1:
                /* wait a bit */
                wait_interval(myrpt, (mytele->mode == ID) ? DLY_ID : DLY_TELEM,mychannel);
-               res = telem_any(mychannel, ident); 
+               res = telem_any(myrpt,mychannel, ident); 
                imdone=1;       
                break;
                
            case TAILMSG:
-               res = ast_streamfile(mychannel, myrpt->tailmessages[myrpt->tailmessagen], mychannel->language); 
+               res = ast_streamfile(mychannel, myrpt->p.tailmessages[myrpt->tailmessagen], mychannel->language); 
                break;
                
            case IDTALKOVER:
-               p = ast_variable_retrieve(cfg, nodename, "idtalkover");
+               p = ast_variable_retrieve(myrpt->cfg, nodename, "idtalkover");
                if(p)
-                       res = telem_any(mychannel, p); 
+                       res = telem_any(myrpt,mychannel, p); 
                imdone=1;       
                break;
                        
            case PROC:
                /* wait a little bit longer */
                wait_interval(myrpt, DLY_TELEM, mychannel);
-               res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
+               res = telem_lookup(myrpt, mychannel, myrpt->name, "patchup");
+               if(res < 0){ /* Then default message */
+                       res = ast_streamfile(mychannel, "rpt/callproceeding", mychannel->language);
+               }
                break;
            case TERM:
                /* wait a little bit longer */
                wait_interval(myrpt, DLY_CALLTERM, mychannel);
-               res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
+               res = telem_lookup(myrpt, mychannel, myrpt->name, "patchdown");
+               if(res < 0){ /* Then default message */
+                       res = ast_streamfile(mychannel, "rpt/callterminated", mychannel->language);
+               }
                break;
            case COMPLETE:
                /* wait a little bit */
                wait_interval(myrpt, DLY_TELEM, mychannel);
-               res = telem_lookup(mychannel, myrpt->name, "functcomplete");
+               res = telem_lookup(myrpt,mychannel, myrpt->name, "functcomplete");
                break;
            case MACRO_NOTFOUND:
                /* wait a little bit */
@@ -1355,7 +2067,11 @@ struct tm localtm;
                res = ast_streamfile(mychannel, "rpt/macro_busy", mychannel->language);
                break;
            case UNKEY:
-
+               if(myrpt->patchnoct && myrpt->callmode){ /* If no CT during patch configured, then don't send one */
+                       imdone = 1;
+                       break;
+               }
+                       
                /*
                * Reset the Unkey to CT timer
                */
@@ -1411,7 +2127,12 @@ struct tm localtm;
                        imdone = 1;
                        break;
                }
-                       
+               
+               rpt_mutex_lock(&myrpt->lock); /* Update the kerchunk counters */
+               myrpt->dailykerchunks++;
+               myrpt->totalkerchunks++;
+               rpt_mutex_unlock(&myrpt->lock);
+       
                haslink = 0;
                hastx = 0;
                hasremote = 0;          
@@ -1438,7 +2159,7 @@ struct tm localtm;
                if (haslink)
                {
 
-                       res = telem_lookup(mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
+                       res = telem_lookup(myrpt,mychannel, myrpt->name, (!hastx) ? "remotemon" : "remotetx");
                        if(res)
                                ast_log(LOG_WARNING, "telem_lookup:remotexx failed on %s\n", mychannel->name);
                        
@@ -1447,15 +2168,15 @@ struct tm localtm;
                        if (myrpt->cmdnode[0])
                        {
                                ast_safe_sleep(mychannel,200);
-                               res = telem_lookup(mychannel, myrpt->name, "cmdmode");
+                               res = telem_lookup(myrpt,mychannel, myrpt->name, "cmdmode");
                                if(res)
                                        ast_log(LOG_WARNING, "telem_lookup:cmdmode failed on %s\n", mychannel->name);
                                ast_stopstream(mychannel);
                        }
                }
-               else if((ct = ast_variable_retrieve(cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */
+               else if((ct = ast_variable_retrieve(myrpt->cfg, nodename, "unlinkedct"))){ /* Unlinked Courtesy Tone */
                        ct_copy = ast_strdupa(ct);
-                       res = telem_lookup(mychannel, myrpt->name, ct_copy);
+                       res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy);
                        if(res)
                                ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);               
                }       
@@ -1477,14 +2198,42 @@ struct tm localtm;
                                ast_hangup(mychannel);
                                pthread_exit(NULL);
                        }
-                       if((ct = ast_variable_retrieve(cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */
+                       if((ct = ast_variable_retrieve(myrpt->cfg, nodename, "remotect"))){ /* Unlinked Courtesy Tone */
                                ast_safe_sleep(mychannel,200);
                                ct_copy = ast_strdupa(ct);
-                               res = telem_lookup(mychannel, myrpt->name, ct_copy);
+                               res = telem_lookup(myrpt,mychannel, myrpt->name, ct_copy);
                                if(res)
                                        ast_log(LOG_WARNING, "telem_lookup:ctx failed on %s\n", mychannel->name);               
                        }       
                }
+#ifdef _MDC_DECODE_H_
+               if (myrpt->lastunit)
+               {
+                       char mystr[10];
+
+                       ast_safe_sleep(mychannel,200);
+                       /* set for all to hear */
+                       ci.chan = 0;
+                       ci.confno = myrpt->txconf;
+                       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");
+                               rpt_mutex_lock(&myrpt->lock);
+                               remque((struct qelem *)mytele);
+                               rpt_mutex_unlock(&myrpt->lock);
+                               ast_log(LOG_NOTICE,"Telemetry thread aborted at line %d, mode: %d\n",__LINE__, mytele->mode); /*@@@@@@@@@@@*/
+                               free(mytele);           
+                               ast_hangup(mychannel);
+                               pthread_exit(NULL);
+                       }
+                       sprintf(mystr,"%04x",myrpt->lastunit);
+                       myrpt->lastunit = 0;
+                       ast_say_character_str(mychannel,mystr,NULL,mychannel->language);
+                       break;
+               }
+#endif
                imdone = 1;
                break;
            case REMDISC:
@@ -1637,6 +2386,34 @@ struct tm localtm;
                }                       
                imdone = 1;
                break;
+
+           case LASTNODEKEY: /* Identify last node which keyed us up */
+               rpt_mutex_lock(&myrpt->lock);
+               if(myrpt->lastnodewhichkeyedusup)
+                       p = ast_strdupa(myrpt->lastnodewhichkeyedusup); /* Make a local copy of the node name */
+               else
+                       p = NULL;
+               rpt_mutex_unlock(&myrpt->lock);
+               if(!p){
+                       imdone = 1; /* no node previously keyed us up, or the node which did has been disconnected */
+                       break;
+               }
+               wait_interval(myrpt, DLY_TELEM, mychannel);
+               res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
+               if (!res) 
+                       res = ast_waitstream(mychannel, "");
+               else
+                        ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+               ast_stopstream(mychannel);
+               ast_say_character_str(mychannel, p, NULL, mychannel->language);
+               if (!res) 
+                       res = ast_waitstream(mychannel, "");
+               else
+                       ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", mychannel->name);
+               ast_stopstream(mychannel);
+               imdone = 1;
+               break;          
+
            case TIMEOUT:
                res = ast_streamfile(mychannel, "rpt/node", mychannel->language);
                if (!res) 
@@ -1784,11 +2561,11 @@ struct tm localtm;
                if (!res)
                {
                        myrpt->tailmessagen++;
-                       if(myrpt->tailmessagen >= myrpt->tailmessagemax) myrpt->tailmessagen = 0;
+                       if(myrpt->tailmessagen >= myrpt->p.tailmessagemax) myrpt->tailmessagen = 0;
                }
                else
                {
-                       myrpt->tmsgtimer = myrpt->tailsquashedtime;
+                       myrpt->tmsgtimer = myrpt->p.tailsquashedtime;
                }
        }
        remque((struct qelem *)mytele);
@@ -1838,12 +2615,17 @@ pthread_attr_t attr;
                strncpy(tele->param, (char *) data, TELEPARAMSIZE - 1);
                tele->param[TELEPARAMSIZE - 1] = 0;
        }
+       insque((struct qelem *)tele, (struct qelem *)myrpt->tele.next);
        rpt_mutex_unlock(&myrpt->lock);
         pthread_attr_init(&attr);
         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        res = ast_pthread_create(&tele->threadid,&attr,rpt_tele_thread,(void *) tele);
-       if(res < 0)
+       if(res < 0){
+               rpt_mutex_lock(&myrpt->lock);
+               remque((struct qlem *) tele); /* We don't like stuck transmitters, remove it from the queue */
+               rpt_mutex_unlock(&myrpt->lock); 
                ast_log(LOG_WARNING, "Could not create telemetry thread: %s",strerror(res));
+       }
        return;
 }
 
@@ -1853,9 +2635,10 @@ ZT_CONFINFO ci;  /* conference info */
 struct rpt *myrpt = (struct rpt *)this;
 int    res;
 struct ast_frame wf;
-int stopped,congstarted;
+int stopped,congstarted,dialtimer,lastcidx,aborted;
 struct ast_channel *mychannel,*genchannel;
 
+
        myrpt->mydtmf = 0;
        /* allocate a pseudo-channel thru asterisk */
        mychannel = ast_request("zap",AST_FORMAT_SLINEAR,"pseudo",NULL);
@@ -1897,24 +2680,24 @@ struct ast_channel *mychannel,*genchannel;
                myrpt->callmode = 0;
                pthread_exit(NULL);
        }
-       if (myrpt->tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->tonezone) == -1))
+       if (myrpt->p.tonezone && (tone_zone_set_zone(mychannel->fds[0],myrpt->p.tonezone) == -1))
        {
-               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.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))
+       if (myrpt->p.tonezone && (tone_zone_set_zone(genchannel->fds[0],myrpt->p.tonezone) == -1))
        {
-               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->tonezone);
+               ast_log(LOG_WARNING, "Unable to set tone zone %s\n",myrpt->p.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)
+       /* start dialtone if patchquiet is 0. Special patch modes don't send dial tone */
+       if ((!myrpt->patchquiet) && (tone_zone_play_tone(mychannel->fds[0],ZT_TONE_DIALTONE) < 0))
        {
                ast_log(LOG_WARNING, "Cannot start dialtone\n");
                ast_hangup(mychannel);
@@ -1924,20 +2707,40 @@ struct ast_channel *mychannel,*genchannel;
        }
        stopped = 0;
        congstarted = 0;
+       dialtimer = 0;
+       lastcidx = 0;
+       aborted = 0;
+
+
        while ((myrpt->callmode == 1) || (myrpt->callmode == 4))
        {
 
-               if ((myrpt->callmode == 1) && (myrpt->cidx > 0) && (!stopped))
+               if((myrpt->patchdialtime)&&(myrpt->callmode == 1)&&(myrpt->cidx != lastcidx)){
+                       dialtimer = 0;
+                       lastcidx = myrpt->cidx;
+               }               
+
+               if((myrpt->patchdialtime)&&(dialtimer >= myrpt->patchdialtime)){ 
+                       rpt_mutex_lock(&myrpt->lock);
+                       aborted = 1;
+                       myrpt->callmode = 0;
+                       rpt_mutex_unlock(&myrpt->lock);
+                       break;
+               }
+       
+               if ((!myrpt->patchquiet) && (!stopped) && (myrpt->callmode == 1) && (myrpt->cidx > 0))
                {
                        stopped = 1;
                        /* stop dial tone */
                        tone_zone_play_tone(mychannel->fds[0],-1);
                }
-               if ((myrpt->callmode == 4) && (!congstarted))
+               if (myrpt->callmode == 4)
                {
-                       congstarted = 1;
-                       /* start congestion tone */
-                       tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION);
+                       if(!congstarted){
+                               congstarted = 1;
+                               /* start congestion tone */
+                               tone_zone_play_tone(mychannel->fds[0],ZT_TONE_CONGESTION);
+                       }
                }
                res = ast_safe_sleep(mychannel, MSWAIT);
                if (res < 0)
@@ -1949,6 +2752,7 @@ struct ast_channel *mychannel,*genchannel;
                        rpt_mutex_unlock(&myrpt->lock);
                        pthread_exit(NULL);
                }
+               dialtimer += MSWAIT;
        }
        /* stop any tone generation */
        tone_zone_play_tone(mychannel->fds[0],-1);
@@ -1960,12 +2764,14 @@ struct ast_channel *mychannel,*genchannel;
                rpt_mutex_lock(&myrpt->lock);
                myrpt->callmode = 0;
                rpt_mutex_unlock(&myrpt->lock);
+               if((!myrpt->patchquiet) && aborted)
+                       rpt_telemetry(myrpt, TERM, NULL);
                pthread_exit(NULL);                     
        }
 
-       if (myrpt->ourcallerid && *myrpt->ourcallerid){
+       if (myrpt->p.ourcallerid && *myrpt->p.ourcallerid){
                char *name, *loc, *instr;
-               instr = strdup(myrpt->ourcallerid);
+               instr = strdup(myrpt->p.ourcallerid);
                if(instr){
                        ast_callerid_parse(instr, &name, &loc);
                        if(loc){
@@ -1983,9 +2789,10 @@ struct ast_channel *mychannel,*genchannel;
        }
 
        strncpy(mychannel->exten, myrpt->exten, sizeof(mychannel->exten) - 1);
-       strncpy(mychannel->context, myrpt->ourcontext, sizeof(mychannel->context) - 1);
-       if (myrpt->acctcode)
-               ast_string_field_set(mychannel, accountcode, myrpt->acctcode);
+       strncpy(mychannel->context, myrpt->patchcontext, sizeof(mychannel->context) - 1);
+       
+       if (myrpt->p.acctcode)
+               strncpy((char *)mychannel->accountcode, myrpt->p.acctcode, sizeof(mychannel->accountcode) - 1);
        mychannel->priority = 1;
        ast_channel_undefer_dtmf(mychannel);
        if (ast_pbx_start(mychannel) < 0)
@@ -2001,15 +2808,39 @@ struct ast_channel *mychannel,*genchannel;
        usleep(10000);
        rpt_mutex_lock(&myrpt->lock);
        myrpt->callmode = 3;
+       /* set appropriate conference for the pseudo */
+       ci.chan = 0;
+       ci.confno = myrpt->conf;
+       ci.confmode = (myrpt->p.duplex == 2) ? ZT_CONF_CONFANNMON :
+               (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
+       /* first put the channel on the conference in announce mode */
+       if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1)
+       {
+               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+               ast_hangup(mychannel);
+               ast_hangup(genchannel);
+               myrpt->callmode = 0;
+               pthread_exit(NULL);
+       }
        while(myrpt->callmode)
        {
                if ((!mychannel->pbx) && (myrpt->callmode != 4))
                {
-                       myrpt->callmode = 4;
-                       rpt_mutex_unlock(&myrpt->lock);
-                       /* start congestion tone */
-                       tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION);
-                       rpt_mutex_lock(&myrpt->lock);
+                       if(myrpt->patchfarenddisconnect){ /* If patch is setup for far end disconnect */
+                               myrpt->callmode = 0;
+                               if(!myrpt->patchquiet){
+                                       rpt_mutex_unlock(&myrpt->lock);
+                                       rpt_telemetry(myrpt, TERM, NULL);
+                                       rpt_mutex_lock(&myrpt->lock);
+                               }
+                       }
+                       else{ /* Send congestion until patch is downed by command */
+                               myrpt->callmode = 4;
+                               rpt_mutex_unlock(&myrpt->lock);
+                               /* start congestion tone */
+                               tone_zone_play_tone(genchannel->fds[0],ZT_TONE_CONGESTION);
+                               rpt_mutex_lock(&myrpt->lock);
+                       }
                }
                if (myrpt->mydtmf)
                {
@@ -2036,6 +2867,16 @@ struct ast_channel *mychannel,*genchannel;
        rpt_mutex_lock(&myrpt->lock);
        myrpt->callmode = 0;
        rpt_mutex_unlock(&myrpt->lock);
+       /* set appropriate conference for the pseudo */
+       ci.chan = 0;
+       ci.confno = myrpt->conf;
+       ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
+               (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
+       /* first put the channel on the conference in announce mode */
+       if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1)
+       {
+               ast_log(LOG_WARNING, "Unable to set conference mode to Announce\n");
+       }
        pthread_exit(NULL);
 }
 
@@ -2092,6 +2933,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
        char tmp[300], deststr[300] = "",modechange = 0;
        char digitbuf[MAXNODESTR];
        struct rpt_link *l;
+       int reconnects = 0;
        ZT_CONFINFO ci;  /* conference info */
 
        if(!param)
@@ -2110,7 +2952,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                case 1: /* Link off */
                        if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
                                strcpy(digitbuf,myrpt->lastlinknode);
-                       val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+                       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
                        if (!val){
                                if(strlen(digitbuf) >= myrpt->longestnode)
                                        return DC_ERROR;
@@ -2136,7 +2978,6 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                        }
                        if (l != &myrpt->links){ /* if found */
                                struct  ast_frame wf;
-
                                strncpy(myrpt->lastlinknode,digitbuf,MAXNODESTR - 1);
                                l->retries = MAX_RETRIES + 1;
                                l->disced = 1;
@@ -2162,7 +3003,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                case 2: /* Link Monitor */
                        if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
                                strcpy(digitbuf,myrpt->lastlinknode);
-                       val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+                       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
                        if (!val){
                                if(strlen(digitbuf) >= myrpt->longestnode)
                                        return DC_ERROR;
@@ -2196,6 +3037,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                                        return DC_COMPLETE;
                                        
                                }
+                               reconnects = l->reconnects;
                                rpt_mutex_unlock(&myrpt->lock);
                                if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
                                l->retries = MAX_RETRIES + 1;
@@ -2270,6 +3112,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                                return DC_ERROR;
                        }
                        rpt_mutex_lock(&myrpt->lock);
+                       l->reconnects = reconnects;
                        /* insert at end of queue */
                        insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
                        rpt_mutex_unlock(&myrpt->lock);
@@ -2278,7 +3121,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                case 3: /* Link transceive */
                        if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
                                strcpy(digitbuf,myrpt->lastlinknode);
-                       val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+                       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
                        if (!val){
                                if(strlen(digitbuf) >= myrpt->longestnode)
                                        return DC_ERROR;
@@ -2310,6 +3153,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                                        rpt_telemetry(myrpt, REMALREADY, NULL);
                                        return DC_COMPLETE;
                                }
+                               reconnects = l->reconnects;
                                rpt_mutex_unlock(&myrpt->lock);
                                if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV);
                                l->retries = MAX_RETRIES + 1;
@@ -2386,6 +3230,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                                return DC_ERROR;
                        }
                        rpt_mutex_lock(&myrpt->lock);
+                       l->reconnects = reconnects;
                        /* insert at end of queue */
                        insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
                        rpt_mutex_unlock(&myrpt->lock);
@@ -2406,7 +3251,7 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                        if ((digitbuf[0] == '0') && (myrpt->lastlinknode[0]))
                                strcpy(digitbuf,myrpt->lastlinknode);
                        /* node must at least exist in list */
-                       val = ast_variable_retrieve(cfg, myrpt->nodes, digitbuf);
+                       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, digitbuf);
                        if (!val){
                                if(strlen(digitbuf) >= myrpt->longestnode)
                                        return DC_ERROR;
@@ -2428,12 +3273,16 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
                case 6: /* All Links Off */
                        l = myrpt->links.next;
                        
-                       while(l != &myrpt->links){
+                       while(l != &myrpt->links){ /* This code is broke and needs to be changed to work with the reconnect kludge */
                                if (l->chan) ast_softhangup(l->chan, AST_SOFTHANGUP_DEV); /* Hang 'em up */
                                l = l->next;
                        }
                        rpt_telemetry(myrpt, COMPLETE, NULL);
                        break;
+
+               case 7: /* Identify last node which keyed us up */
+                       rpt_telemetry(myrpt, LASTNODEKEY, NULL);
+                       break;
        
                default:
                        return DC_ERROR;
@@ -2450,7 +3299,19 @@ static int function_ilink(struct rpt *myrpt, char *param, char *digits, int comm
 static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf, int command_source, struct rpt_link *mylink)
 {
        pthread_attr_t attr;
-       
+       int i, index, paramlength;
+       char *lparam;
+       char *value = NULL;
+       char *paramlist[20];
+
+       static char *keywords[] = {
+       "context",
+       "dialtime",
+       "farenddisconnect",
+       "noct",
+       "quiet",
+       NULL
+       };
                
        if (!myrpt->enable)
                return DC_ERROR;
@@ -2458,12 +3319,61 @@ static int function_autopatchup(struct rpt *myrpt, char *param, char *digitbuf,
        if(debug)
                printf("@@@@ Autopatch up\n");
 
+       if(!myrpt->callmode){
+               /* Set defaults */
+               myrpt->patchnoct = 0;
+               myrpt->patchdialtime = 0;
+               myrpt->patchfarenddisconnect = 0;
+               myrpt->patchquiet = 0;
+               strncpy(myrpt->patchcontext, myrpt->p.ourcontext, MAXPATCHCONTEXT);
+
+               if(param){
+                       /* Process parameter list */
+                       lparam = ast_strdupa(param);
+                       if(!lparam){
+                               ast_log(LOG_ERROR,"App_rpt out of memory on line %d\n",__LINE__);
+                               return DC_ERROR;        
+                       }
+                       paramlength = finddelim(lparam, paramlist, 20);                         
+                       for(i = 0; i < paramlength; i++){
+                               index = matchkeyword(paramlist[i], &value, keywords);
+                               if(value)
+                                       value = skipchars(value, "= ");
+                               switch(index){
+
+                                       case 1: /* context */
+                                               strncpy(myrpt->patchcontext, value, MAXPATCHCONTEXT - 1) ;
+                                               break;
+                                               
+                                       case 2: /* dialtime */
+                                               myrpt->patchdialtime = atoi(value);
+                                               break;
+
+                                       case 3: /* farenddisconnect */
+                                               myrpt->patchfarenddisconnect = atoi(value);
+                                               break;
+
+                                       case 4: /* noct */
+                                               myrpt->patchnoct = atoi(value);
+                                               break;
+
+                                       case 5: /* quiet */
+                                               myrpt->patchquiet = atoi(value);
+                                               break;
+                                                                       
+                                       default:
+                                               break;
+                               }
+                       }
+               }
+       }
+                                       
        rpt_mutex_lock(&myrpt->lock);
-       
+
        /* if on call, force * into current audio stream */
        
        if ((myrpt->callmode == 2) || (myrpt->callmode == 3)){
-               myrpt->mydtmf = myrpt->funcchar;
+               myrpt->mydtmf = myrpt->p.funcchar;
        }
        if (myrpt->callmode){
                rpt_mutex_unlock(&myrpt->lock);
@@ -2562,8 +3472,8 @@ struct    ast_channel *mychannel;
                        return DC_ERROR;
        }
    
-       if (*digitbuf == '0') val = myrpt->startupmacro;
-       else val = ast_variable_retrieve(cfg, myrpt->macro, digitbuf);
+       if (*digitbuf == '0') val = myrpt->p.startupmacro;
+       else val = ast_variable_retrieve(myrpt->cfg, myrpt->p.macro, digitbuf);
        /* param was 1 for local buf */
        if (!val){
                rpt_telemetry(myrpt, MACRO_NOTFOUND, NULL);
@@ -2631,7 +3541,7 @@ static int collect_function_digits(struct rpt *myrpt, char *digits,
        int i;
        char *stringp,*action,*param,*functiondigits;
        char function_table_name[30] = "";
-       char workstring[80];
+       char workstring[200];
        
        struct ast_variable *vp;
        
@@ -2639,18 +3549,18 @@ static int collect_function_digits(struct rpt *myrpt, char *digits,
                printf("@@@@ Digits collected: %s, source: %d\n", digits, command_source);
        
        if (command_source == SOURCE_DPHONE) {
-               if (!myrpt->dphone_functions) return DC_INDETERMINATE;
-               strncpy(function_table_name, myrpt->dphone_functions, sizeof(function_table_name) - 1);
+               if (!myrpt->p.dphone_functions) return DC_INDETERMINATE;
+               strncpy(function_table_name, myrpt->p.dphone_functions, sizeof(function_table_name) - 1);
                }
        else if (command_source == SOURCE_PHONE) {
-               if (!myrpt->phone_functions) return DC_INDETERMINATE;
-               strncpy(function_table_name, myrpt->phone_functions, sizeof(function_table_name) - 1);
+               if (!myrpt->p.phone_functions) return DC_INDETERMINATE;
+               strncpy(function_table_name, myrpt->p.phone_functions, sizeof(function_table_name) - 1);
                }
        else if (command_source == SOURCE_LNK)
-               strncpy(function_table_name, myrpt->link_functions, sizeof(function_table_name) - 1);
+               strncpy(function_table_name, myrpt->p.link_functions, sizeof(function_table_name) - 1);
        else
-               strncpy(function_table_name, myrpt->functions, sizeof(function_table_name) - 1);
-       vp = ast_variable_browse(cfg, function_table_name);
+               strncpy(function_table_name, myrpt->p.functions, sizeof(function_table_name) - 1);
+       vp = ast_variable_browse(myrpt->cfg, function_table_name);
        while(vp) {
                if(!strncasecmp(vp->name, digits, strlen(vp->name)))
                        break;
@@ -2795,21 +3705,23 @@ struct  ast_frame wf;
                return;
        }
        rpt_mutex_lock(&myrpt->lock);
-       if (c == myrpt->endchar) myrpt->stopgen = 1;
+       if (c == myrpt->p.endchar) myrpt->stopgen = 1;
        if (myrpt->callmode == 1)
        {
                myrpt->exten[myrpt->cidx++] = c;
                myrpt->exten[myrpt->cidx] = 0;
                /* if this exists */
-               if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+               if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
                {
                        myrpt->callmode = 2;
-                       rpt_mutex_unlock(&myrpt->lock);
-                       rpt_telemetry(myrpt,PROC,NULL); 
-                       rpt_mutex_lock(&myrpt->lock);
+                       if(!myrpt->patchquiet){
+                               rpt_mutex_unlock(&myrpt->lock);
+                               rpt_telemetry(myrpt,PROC,NULL); 
+                               rpt_mutex_lock(&myrpt->lock);
+                       }
                }
                /* if can continue, do so */
-               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) 
+               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL)) 
                {
                        /* call has failed, inform user */
                        myrpt->callmode = 4;
@@ -2819,7 +3731,7 @@ struct    ast_frame wf;
        {
                myrpt->mydtmf = c;
        }
-       if (c == myrpt->funcchar)
+       if (c == myrpt->p.funcchar)
        {
                myrpt->rem_dtmfidx = 0;
                myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
@@ -2827,7 +3739,7 @@ struct    ast_frame wf;
                rpt_mutex_unlock(&myrpt->lock);
                return;
        } 
-       else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0))
+       else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0))
        {
                time(&myrpt->rem_dtmf_time);
                if (myrpt->rem_dtmfidx < MAXDTMF)
@@ -2852,6 +3764,10 @@ struct   ast_frame wf;
                                
                                
                                case DC_COMPLETE:
+                                       myrpt->totalexecdcommands++;
+                                       myrpt->dailyexecdcommands++;
+                                       strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1);
+                                       myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
                                        myrpt->rem_dtmfbuf[0] = 0;
                                        myrpt->rem_dtmfidx = -1;
                                        myrpt->rem_dtmf_time = 0;
@@ -2879,7 +3795,7 @@ char      cmd[300];
 int    res;
 
        rpt_mutex_lock(&myrpt->lock);
-       if (c == myrpt->endchar)
+       if (c == myrpt->p.endchar)
        {
                if (mylink->lastrx)
                {
@@ -2909,15 +3825,17 @@ int     res;
                myrpt->exten[myrpt->cidx++] = c;
                myrpt->exten[myrpt->cidx] = 0;
                /* if this exists */
-               if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+               if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
                {
                        myrpt->callmode = 2;
-                       rpt_mutex_unlock(&myrpt->lock);
-                       rpt_telemetry(myrpt,PROC,NULL); 
-                       rpt_mutex_lock(&myrpt->lock);
+                       if(!myrpt->patchquiet){
+                               rpt_mutex_unlock(&myrpt->lock);
+                               rpt_telemetry(myrpt,PROC,NULL); 
+                               rpt_mutex_lock(&myrpt->lock);
+                       }
                }
                /* if can continue, do so */
-               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL)) 
+               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL)) 
                {
                        /* call has failed, inform user */
                        myrpt->callmode = 4;
@@ -2927,7 +3845,7 @@ int       res;
        {
                myrpt->mydtmf = c;
        }
-       if (c == myrpt->funcchar)
+       if (c == myrpt->p.funcchar)
        {
                myrpt->rem_dtmfidx = 0;
                myrpt->rem_dtmfbuf[myrpt->rem_dtmfidx] = 0;
@@ -2935,7 +3853,7 @@ int       res;
                rpt_mutex_unlock(&myrpt->lock);
                return;
        } 
-       else if ((c != myrpt->endchar) && (myrpt->rem_dtmfidx >= 0))
+       else if ((c != myrpt->p.endchar) && (myrpt->rem_dtmfidx >= 0))
        {
                time(&myrpt->rem_dtmf_time);
                if (myrpt->rem_dtmfidx < MAXDTMF)
@@ -2979,6 +3897,10 @@ int      res;
                                
                                
                                case DC_COMPLETE:
+                                       myrpt->totalexecdcommands++;
+                                       myrpt->dailyexecdcommands++;
+                                       strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1);
+                                       myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
                                        myrpt->rem_dtmfbuf[0] = 0;
                                        myrpt->rem_dtmfidx = -1;
                                        myrpt->rem_dtmf_time = 0;
@@ -3170,14 +4092,14 @@ static void rbi_out_parallel(struct rpt *myrpt,unsigned char *data)
         od = *data++; 
         for(j = 0 ; j < 8 ; j++){
             d = od & 1;
-            outb(d,myrpt->iobase);
+            outb(d,myrpt->p.iobase);
            /* >= 15 us */
            for(delayvar = 1; delayvar < 15000; delayvar++); 
             od >>= 1;
-            outb(d | 2,myrpt->iobase);
+            outb(d | 2,myrpt->p.iobase);
            /* >= 30 us */
            for(delayvar = 1; delayvar < 30000; delayvar++); 
-            outb(d,myrpt->iobase);
+            outb(d,myrpt->p.iobase);
            /* >= 10 us */
            for(delayvar = 1; delayvar < 10000; delayvar++); 
             }
@@ -3799,6 +4721,7 @@ static int multimode_bump_freq_ft897(struct rpt *myrpt, int interval)
 
 static int setrem(struct rpt *myrpt)
 {
+       return 0; /* XXX BROKEN!! */
        if(!strcmp(myrpt->remote, remote_rig_ft897))
                return set_ft897(myrpt);
        else if(!strcmp(myrpt->remote, remote_rig_rbi))
@@ -3809,6 +4732,7 @@ static int setrem(struct rpt *myrpt)
 
 static int closerem(struct rpt *myrpt)
 {
+       return 0; /* XXX BROKEN!! */
        if(!strcmp(myrpt->remote, remote_rig_ft897))
                return closerem_ft897(myrpt);
        else
@@ -4030,7 +4954,7 @@ static int function_remote(struct rpt *myrpt, char *param, char *digitbuf, int c
                                        return DC_ERROR;
                        }
            
-                       val = ast_variable_retrieve(cfg, myrpt->memory, digitbuf);
+                       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.memory, digitbuf);
                        if (!val){
                                if (ast_safe_sleep(mychannel,1000) == -1)
                                        return DC_ERROR;
@@ -4717,7 +5641,7 @@ int       ret,res = 0,src;
        if (myrpt->dtmfidx == -1)
        {
                /* if not lead-in digit, dont worry */
-               if (c != myrpt->funcchar) return 0;
+               if (c != myrpt->p.funcchar) return 0;
                myrpt->dtmfidx = 0;
                myrpt->dtmfbuf[0] = 0;
                myrpt->dtmf_time_rem = now;
@@ -4730,11 +5654,11 @@ int     ret,res = 0,src;
                myrpt->dtmfbuf[0] = 0;
                myrpt->dtmf_time_rem = now;
        }
-       if (c == myrpt->funcchar)
+       if (c == myrpt->p.funcchar)
        {
                /* if star at beginning, or 2 together, erase buffer */
                if ((myrpt->dtmfidx < 1) || 
-                       (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->funcchar))
+                       (myrpt->dtmfbuf[myrpt->dtmfidx - 1] == myrpt->p.funcchar))
                {
                        myrpt->dtmfidx = 0;
                        myrpt->dtmfbuf[0] = 0;
@@ -4771,6 +5695,10 @@ int      ret,res = 0,src;
                                
                                
                case DC_COMPLETE:
+                       myrpt->totalexecdcommands++;
+                       myrpt->dailyexecdcommands++;
+                       strncpy(myrpt->lastdtmfcommand, myrpt->dtmfbuf, MAXDTMF-1);
+                       myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
                        myrpt->dtmfbuf[0] = 0;
                        myrpt->dtmfidx = -1;
                        myrpt->dtmf_time_rem = 0;
@@ -4819,7 +5747,7 @@ int       seq,res;
                ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY);
        }
        if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1;
-       res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete");
+       res = telem_lookup(myrpt,myrpt->remchannel, myrpt->name, "functcomplete");
        rmt_telem_finish(myrpt,myrpt->remchannel);
        return res;
 }
@@ -4829,7 +5757,7 @@ static int handle_remote_phone_dtmf(struct rpt *myrpt, char c, char *keyed, int
 int    res;
 
 
-       if (keyed && *keyed && (c == myrpt->endchar))
+       if (keyed && *keyed && (c == myrpt->p.endchar))
        {
                *keyed = 0;
                return DC_INDETERMINATE;
@@ -4845,7 +5773,7 @@ int       res;
                ast_indicate(myrpt->remchannel,AST_CONTROL_RADIO_KEY);
        }
        if (ast_safe_sleep(myrpt->remchannel,1000) == -1) return -1;
-       res = telem_lookup(myrpt->remchannel, myrpt->name, "functcomplete");
+       res = telem_lookup(myrpt,myrpt->remchannel, myrpt->name, "functcomplete");
        rmt_telem_finish(myrpt,myrpt->remchannel);
        return res;
 }
@@ -4855,7 +5783,7 @@ static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
        char *val, *s, *s1, *s2, *tele;
        char tmp[300], deststr[300] = "";
 
-       val = ast_variable_retrieve(cfg, myrpt->nodes, l->name);
+       val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, l->name);
        if (!val)
        {
                fprintf(stderr,"attempt_reconnect: cannot find node %s\n",l->name);
@@ -4878,6 +5806,7 @@ static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
        }
        *tele++ = 0;
        l->elaptime = 0;
+       l->connecttime = 0;
        l->chan = ast_request(deststr, AST_FORMAT_SLINEAR, tele,NULL);
        if (l->chan){
                ast_set_read_format(l->chan, AST_FORMAT_SLINEAR);
@@ -4902,7 +5831,7 @@ static int attempt_reconnect(struct rpt *myrpt, struct rpt_link *l)
                return -1;
        }
        rpt_mutex_lock(&myrpt->lock);
-       /* put back in queue queue */
+       /* put back in queue */
        insque((struct qelem *)l,(struct qelem *)myrpt->links.next);
        rpt_mutex_unlock(&myrpt->lock);
        ast_log(LOG_NOTICE,"Reconnect Attempt to %s in process\n",l->name);
@@ -4916,10 +5845,10 @@ int     res;
 pthread_attr_t attr;
 char   cmd[MAXDTMF+1] = "";
 
-       if (c == myrpt->endchar)
+       if (c == myrpt->p.endchar)
        {
        /* if in simple mode, kill autopatch */
-               if (myrpt->simple && myrpt->callmode)
+               if (myrpt->p.simple && myrpt->callmode)
                {
                        rpt_mutex_lock(&myrpt->lock);
                        myrpt->callmode = 0;
@@ -4946,9 +5875,9 @@ char      cmd[MAXDTMF+1] = "";
                send_link_dtmf(myrpt,c);
                return;
        }
-       if (!myrpt->simple)
+       if (!myrpt->p.simple)
        {
-               if (c == myrpt->funcchar)
+               if (c == myrpt->p.funcchar)
                {
                        myrpt->dtmfidx = 0;
                        myrpt->dtmfbuf[myrpt->dtmfidx] = 0;
@@ -4956,7 +5885,7 @@ char      cmd[MAXDTMF+1] = "";
                        time(&myrpt->dtmf_time);
                        return;
                } 
-               else if ((c != myrpt->endchar) && (myrpt->dtmfidx >= 0))
+               else if ((c != myrpt->p.endchar) && (myrpt->dtmfidx >= 0))
                {
                        time(&myrpt->dtmf_time);
                        
@@ -4978,6 +5907,10 @@ char     cmd[MAXDTMF+1] = "";
                                        myrpt->dtmfbuf[0] = 0;
                                        break;
                                    case DC_COMPLETE:
+                                       myrpt->totalexecdcommands++;
+                                       myrpt->dailyexecdcommands++;
+                                       strncpy(myrpt->lastdtmfcommand, cmd, MAXDTMF-1);
+                                       myrpt->lastdtmfcommand[MAXDTMF-1] = '\0';
                                        myrpt->dtmfbuf[0] = 0;
                                        myrpt->dtmfidx = -1;
                                        myrpt->dtmf_time = 0;
@@ -4999,9 +5932,14 @@ char     cmd[MAXDTMF+1] = "";
        }
        else /* if simple */
        {
-               if ((!myrpt->callmode) && (c == myrpt->funcchar))
+               if ((!myrpt->callmode) && (c == myrpt->p.funcchar))
                {
                        myrpt->callmode = 1;
+                       myrpt->patchnoct = 0;
+                       myrpt->patchquiet = 0;
+                       myrpt->patchfarenddisconnect = 0;
+                       myrpt->patchdialtime = 0;
+                       strncpy(myrpt->patchcontext, myrpt->p.ourcontext, MAXPATCHCONTEXT);
                        myrpt->cidx = 0;
                        myrpt->exten[myrpt->cidx] = 0;
                        rpt_mutex_unlock(&myrpt->lock);
@@ -5016,15 +5954,16 @@ char    cmd[MAXDTMF+1] = "";
                myrpt->exten[myrpt->cidx++] = c;
                myrpt->exten[myrpt->cidx] = 0;
                /* if this exists */
-               if (ast_exists_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+               if (ast_exists_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
                {
                        myrpt->callmode = 2;
                        rpt_mutex_unlock(&myrpt->lock);
-                       rpt_telemetry(myrpt,PROC,NULL); 
+                       if(!myrpt->patchquiet)
+                               rpt_telemetry(myrpt,PROC,NULL); 
                        return;
                }
                /* if can continue, do so */
-               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->ourcontext,myrpt->exten,1,NULL))
+               if (!ast_canmatch_extension(myrpt->pchannel,myrpt->patchcontext,myrpt->exten,1,NULL))
                {
                        /* call has failed, inform user */
                        myrpt->callmode = 4;
@@ -5046,19 +5985,48 @@ char    cmd[MAXDTMF+1] = "";
 static void queue_id(struct rpt *myrpt)
 {
        myrpt->mustid = myrpt->tailid = 0;
-       myrpt->idtimer = myrpt->idtime; /* Reset our ID timer */
+       myrpt->idtimer = myrpt->p.idtime; /* Reset our ID timer */
        rpt_mutex_unlock(&myrpt->lock);
        rpt_telemetry(myrpt,ID,NULL);
        rpt_mutex_lock(&myrpt->lock);
 }
 
+/* Scheduler */
+
+static void do_scheduler(struct rpt *myrpt)
+{
+       int res;
+       struct tm tmnow;
+
+       memcpy(&myrpt->lasttv, &myrpt->curtv, sizeof(struct timeval));
+       
+       if( (res = gettimeofday(&myrpt->curtv, NULL)) < 0)
+               ast_log(LOG_NOTICE, "Scheduler gettime of day returned: %s\n", strerror(res));
+
+       /* Try to get close to a 1 second resolution */
+       
+       if(myrpt->lasttv.tv_sec == myrpt->curtv.tv_sec)
+               return;
+
+       ast_localtime(&myrpt->curtv.tv_sec, &tmnow, NULL);
+
+       /* If midnight, then reset all daily statistics */
+       
+       if((tmnow.tm_hour == 0)&&(tmnow.tm_min == 0)&&(tmnow.tm_sec == 0)){
+               myrpt->dailykeyups = 0;
+               myrpt->dailytxtime = 0;
+               myrpt->dailykerchunks = 0;
+               myrpt->dailyexecdcommands = 0;
+       }
+}
+
 
 /* single thread with one file (request) to dial */
 static void *rpt(void *this)
 {
 struct rpt *myrpt = (struct rpt *)this;
 char *tele,*idtalkover,c;
-int ms = MSWAIT,i,lasttx=0,val,remrx=0,identqueued,nonidentqueued,tailmessagequeued,ctqueued;
+int ms = MSWAIT,i,lasttx=0,val,remrx=0,identqueued,othertelemqueued,tailmessagequeued,ctqueued;
 struct ast_channel *who;
 ZT_CONFINFO ci;  /* conference info */
 time_t t;
@@ -5066,13 +6034,30 @@ struct rpt_link *l,*m;
 struct rpt_tele *telem;
 char tmpstr[300];
 
+       rpt_mutex_lock(&myrpt->lock);
 
+       telem = myrpt->tele.next;
+       while(telem != &myrpt->tele)
+       {
+               ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
+               telem = telem->next;
+       }
+       rpt_mutex_unlock(&myrpt->lock);
+       /* find our index, and load the vars initially */
+       for(i = 0; i < nrpts; i++)
+       {
+               if (&rpt_vars[i] == myrpt)
+               {
+                       load_rpt_vars(i,0);
+                       break;
+               }
+       }
        rpt_mutex_lock(&myrpt->lock);
        strncpy(tmpstr,myrpt->rxchanname,sizeof(tmpstr) - 1);
        tele = strchr(tmpstr,'/');
        if (!tele)
        {
-               fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->rxchanname);
+               fprintf(stderr,"rpt:Rxchannel Dial number (%s) must be in format tech/number\n",myrpt->rxchanname);
                rpt_mutex_unlock(&myrpt->lock);
                myrpt->rpt_thread = AST_PTHREADT_STOP;
                pthread_exit(NULL);
@@ -5119,7 +6104,7 @@ char tmpstr[300];
                tele = strchr(tmpstr,'/');
                if (!tele)
                {
-                       fprintf(stderr,"rpt:Dial number (%s) must be in format tech/number\n",myrpt->txchanname);
+                       fprintf(stderr,"rpt:Txchannel Dial number (%s) must be in format tech/number\n",myrpt->txchanname);
                        rpt_mutex_unlock(&myrpt->lock);
                        ast_hangup(myrpt->rxchannel);
                        myrpt->rpt_thread = AST_PTHREADT_STOP;
@@ -5204,7 +6189,7 @@ char tmpstr[300];
        /* make a conference for the pseudo */
        ci.chan = 0;
        ci.confno = -1; /* make a new conf */
-       ci.confmode = (myrpt->duplex == 2) ? ZT_CONF_CONFANNMON :
+       ci.confmode = ((myrpt->p.duplex == 2) || (myrpt->p.duplex == 4)) ? ZT_CONF_CONFANNMON :
                (ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER);
        /* first put the channel on the conference in announce mode */
        if (ioctl(myrpt->pchannel->fds[0],ZT_SETCONF,&ci) == -1)
@@ -5257,16 +6242,18 @@ char tmpstr[300];
        myrpt->links.prev = &myrpt->links;
        myrpt->tailtimer = 0;
        myrpt->totimer = 0;
-       myrpt->tmsgtimer = myrpt->tailmessagetime;
-       myrpt->idtimer = myrpt->politeid;
+       myrpt->tmsgtimer = myrpt->p.tailmessagetime;
+       myrpt->idtimer = myrpt->p.politeid;
        myrpt->mustid = myrpt->tailid = 0;
        myrpt->callmode = 0;
        myrpt->tounkeyed = 0;
        myrpt->tonotify = 0;
        myrpt->retxtimer = 0;
+       myrpt->skedtimer = 0;
+       myrpt->tailevent = 0;
        lasttx = 0;
        myrpt->keyed = 0;
-       idtalkover = ast_variable_retrieve(cfg, myrpt->name, "idtalkover");
+       idtalkover = ast_variable_retrieve(myrpt->cfg, myrpt->name, "idtalkover");
        myrpt->dtmfidx = -1;
        myrpt->dtmfbuf[0] = 0;
        myrpt->rem_dtmfidx = -1;
@@ -5275,9 +6262,21 @@ char tmpstr[300];
        myrpt->rem_dtmf_time = 0;
        myrpt->enable = 1;
        myrpt->disgorgetime = 0;
-       if (myrpt->startupmacro)
+       myrpt->lastnodewhichkeyedusup[0] = '\0';
+       myrpt->dailytxtime = 0;
+       myrpt->totaltxtime = 0;
+       myrpt->dailykeyups = 0;
+       myrpt->totalkeyups = 0;
+       myrpt->dailykerchunks = 0;
+       myrpt->totalkerchunks = 0;
+       myrpt->dailyexecdcommands = 0;
+       myrpt->totalexecdcommands = 0;
+       myrpt->timeouts = 0;
+       myrpt->exten[0] = '\0';
+       myrpt->lastdtmfcommand[0] = '\0';
+       if (myrpt->p.startupmacro)
        {
-               snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->startupmacro);
+               snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->p.startupmacro);
        }
        rpt_mutex_unlock(&myrpt->lock);
        val = 0;
@@ -5313,8 +6312,7 @@ char tmpstr[300];
                        ast_log(LOG_NOTICE,"myrpt->retxtimer = %ld\n",myrpt->retxtimer);
                        ast_log(LOG_NOTICE,"myrpt->totimer = %d\n",myrpt->totimer);
                        ast_log(LOG_NOTICE,"myrpt->tailtimer = %d\n",myrpt->tailtimer);
-                       ast_log(LOG_NOTICE,"myrpt->mustid = %d\n",myrpt->mustid);
-                       ast_log(LOG_NOTICE,"myrpt->tailid = %d\n",myrpt->tailid);
+                       ast_log(LOG_NOTICE,"myrpt->tailevent = %d\n",myrpt->tailevent);
 
                        zl = myrpt->links.next;
                        while(zl != &myrpt->links){
@@ -5329,7 +6327,7 @@ char tmpstr[300];
                                ast_log(LOG_NOTICE,"        link->disctime %ld\n",zl->disctime);
                                ast_log(LOG_NOTICE,"        link->retrytimer %ld\n",zl->retrytimer);
                                ast_log(LOG_NOTICE,"        link->retries = %d\n",zl->retries);
-
+                               ast_log(LOG_NOTICE,"        link->reconnects = %d\n",zl->reconnects);
                                zl = zl->next;
                        }
                                                                                                                                
@@ -5345,98 +6343,122 @@ char tmpstr[300];
                }       
 
 
+               if (myrpt->reload)
+               {
+                       struct rpt_tele *telem;
+
+                       rpt_mutex_lock(&myrpt->lock);
+                       telem = myrpt->tele.next;
+                       while(telem != &myrpt->tele)
+                       {
+                               ast_softhangup(telem->chan,AST_SOFTHANGUP_DEV);
+                               telem = telem->next;
+                       }
+                       myrpt->reload = 0;
+                       rpt_mutex_unlock(&myrpt->lock);
+                       usleep(10000);
+                       /* find our index, and load the vars */
+                       for(i = 0; i < nrpts; i++)
+                       {
+                               if (&rpt_vars[i] == myrpt)
+                               {
+                                       load_rpt_vars(i,0);
+                                       break;
+                               }
+                       }
+               }
+
                rpt_mutex_lock(&myrpt->lock);
                if (ast_check_hangup(myrpt->rxchannel)) break;
                if (ast_check_hangup(myrpt->txchannel)) break;
                if (ast_check_hangup(myrpt->pchannel)) break;
                if (ast_check_hangup(myrpt->txpchannel)) break;
+
+               /* Update local tx with keyed if not parsing a command */
                myrpt->localtx = myrpt->keyed && (myrpt->dtmfidx == -1) && (!myrpt->cmdnode[0]);
-               
                /* If someone's connected, and they're transmitting from their end to us, set remrx true */
-               
                l = myrpt->links.next;
                remrx = 0;
                while(l != &myrpt->links)
                {
-                       if (l->lastrx) remrx = 1;
+                       if (l->lastrx){
+                               remrx = 1;
+                               if(l->name[0] != '0') /* Ignore '0' nodes */
+                                       strcpy(myrpt->lastnodewhichkeyedusup, l->name); /* Note the node which is doing the key up */
+                       }
                        l = l->next;
                }
-               
-               /* Create a "must_id" flag for the cleanup ID */        
-                       
+               /* Create a "must_id" flag for the cleanup ID */                
                myrpt->mustid |= (myrpt->idtimer) && (myrpt->keyed || remrx) ;
-
                /* Build a fresh totx from myrpt->keyed and autopatch activated */
-               
                totx = myrpt->callmode;
-               if (myrpt->duplex > 1) totx = totx || myrpt->localtx;
-                
-               /* Traverse the telemetry list to see if there's an ID queued and if there is not an ID queued */
-               
+               /* If full duplex, add local tx to totx */
+               if (myrpt->p.duplex > 1) totx = totx || myrpt->localtx;
+               /* Traverse the telemetry list to see what's queued */
                identqueued = 0;
-               nonidentqueued = 0;
+               othertelemqueued = 0;
                tailmessagequeued = 0;
                ctqueued = 0;
                telem = myrpt->tele.next;
                while(telem != &myrpt->tele)
                {
                        if((telem->mode == ID) || (telem->mode == IDTALKOVER)){
-                               identqueued = 1;
+                               identqueued = 1; /* Identification telemetry */
                        }
                        else if(telem->mode == TAILMSG)
                        {
-                               tailmessagequeued = 1;
+                               tailmessagequeued = 1; /* Tail message telemetry */
                        }
                        else
                        {
                                if (telem->mode != UNKEY)
-                                       nonidentqueued = 1;
+                                       othertelemqueued = 1;  /* Other telemetry */
                                else
-                                       ctqueued = 1;
+                                       ctqueued = 1; /* Courtesy tone telemetry */
                        }
                        telem = telem->next;
                }
        
-               /* Add in any non-id telemetry */
-               
-               if (myrpt->duplex > 0) totx = totx || nonidentqueued;
-               
-               /* Update external transmitter PTT state with everything but ID telemetry */
-               
+               /* Add in any "other" telemetry, if 3/4 or full duplex */
+               if (myrpt->p.duplex > 0) totx = totx || othertelemqueued;
+               /* Update external (to links) transmitter PTT state with everything but ID, CT, and tailmessage telemetry */
                myrpt->exttx = totx;
-               if (myrpt->duplex < 2) myrpt->exttx = myrpt->exttx || myrpt->localtx;
-               
+               /* If half or 3/4 duplex, add localtx to external link tx */
+               if (myrpt->p.duplex < 2) myrpt->exttx = myrpt->exttx || myrpt->localtx;
                /* Add in ID telemetry to local transmitter */
                totx = totx || remrx;
-               if (myrpt->duplex > 0)
+               /* If 3/4 or full duplex, add in ident and CT telemetry */
+               if (myrpt->p.duplex > 0)
                        totx = totx || identqueued || ctqueued;
+               /* Reset time out timer variables if there is no activity */
                if (!totx) 
                {
-                       myrpt->totimer = myrpt->totime;
+                       myrpt->totimer = myrpt->p.totime;
                        myrpt->tounkeyed = 0;
                        myrpt->tonotify = 0;
                }
                else
-                       myrpt->tailtimer = myrpt->hangtime; /* Initialize tail timer */
-
+                       myrpt->tailtimer = myrpt->p.hangtime; /* Initialize tail timer */
+               /* Disable the local transmitter if we are timed out */
                totx = totx && myrpt->totimer;
                /* if timed-out and not said already, say it */
                if ((!myrpt->totimer) && (!myrpt->tonotify))
                {
                        myrpt->tonotify = 1;
+                       myrpt->timeouts++;
                        rpt_mutex_unlock(&myrpt->lock);
                        rpt_telemetry(myrpt,TIMEOUT,NULL);
                        rpt_mutex_lock(&myrpt->lock);
                }
-               /* if wants to transmit and in phone call, but timed out, 
-                       reset time-out timer if keyed */
+
+               /* If unkey and re-key, reset time out timer */
                if ((!totx) && (!myrpt->totimer) && (!myrpt->tounkeyed) && (!myrpt->keyed))
                {
                        myrpt->tounkeyed = 1;
                }
                if ((!totx) && (!myrpt->totimer) && myrpt->tounkeyed && myrpt->keyed)
                {
-                       myrpt->totimer = myrpt->totime;
+                       myrpt->totimer = myrpt->p.totime;
                        myrpt->tounkeyed = 0;
                        myrpt->tonotify = 0;
                        rpt_mutex_unlock(&myrpt->lock);
@@ -5477,35 +6499,42 @@ char tmpstr[300];
                /* If within 30 seconds of the time to ID, try do it in the tail */
                /* else if at ID time limit, do it right over the top of them */
                /* Lastly, if the repeater has been keyed, and the ID timer is expired, do a clean up ID */
-
-/*             if (((totx && (!myrpt->exttx) && (myrpt->idtimer <= myrpt->politeid) && myrpt->tailtimer)) ||
-                  (myrpt->mustid && (!myrpt->idtimer)))
-               {
-                       myrpt->mustid = 0;
-                       myrpt->idtimer = myrpt->idtime; 
-                       rpt_mutex_unlock(&myrpt->lock);
-                       rpt_telemetry(myrpt,ID,NULL);
-                       rpt_mutex_lock(&myrpt->lock);
-               }
-*/
-
                if(myrpt->mustid && (!myrpt->idtimer))
                        queue_id(myrpt);
 
                if ((totx && (!myrpt->exttx) &&
-                        (myrpt->idtimer <= myrpt->politeid) && myrpt->tailtimer)) 
+                        (myrpt->idtimer <= myrpt->p.politeid) && myrpt->tailtimer)) 
                        {
                                myrpt->tailid = 1;
                        }
 
+               /* If tail timer expires, then check for tail messages */
 
+               if(myrpt->tailevent){
+                       myrpt->tailevent = 0;
+                       if(myrpt->tailid){
+                               totx = 1;
+                               queue_id(myrpt);
+                       }
+                       else if ((myrpt->p.tailmessages[0]) &&
+                               (myrpt->p.tailmessagetime) && (myrpt->tmsgtimer == 0)){
+                                       totx = 1;
+                                       myrpt->tmsgtimer = myrpt->p.tailmessagetime;    
+                                       rpt_mutex_unlock(&myrpt->lock);
+                                       rpt_telemetry(myrpt, TAILMSG, NULL);
+                                       rpt_mutex_lock(&myrpt->lock);
+                       }       
+               }
 
+               /* Main TX control */
 
                /* let telemetry transmit anyway (regardless of timeout) */
-               if (myrpt->duplex > 0) totx = totx || (myrpt->tele.next != &myrpt->tele);
+               if (myrpt->p.duplex > 0) totx = totx || (myrpt->tele.next != &myrpt->tele);
                if (totx && (!lasttx))
                {
                        lasttx = 1;
+                       myrpt->dailykeyups++;
+                       myrpt->totalkeyups++;
                        rpt_mutex_unlock(&myrpt->lock);
                        ast_indicate(myrpt->txchannel,AST_CONTROL_RADIO_KEY);
                        rpt_mutex_lock(&myrpt->lock);
@@ -5531,8 +6560,9 @@ char tmpstr[300];
                        myrpt->rem_dtmfidx = -1;
                        myrpt->rem_dtmfbuf[0] = 0;
                }       
-               
-               /* Reconnect kludge */
+
+               /* Reconnect */
+       
                l = myrpt->links.next;
                while(l != &myrpt->links)
                {
@@ -5586,7 +6616,6 @@ char tmpstr[300];
                                        if (l->chan) ast_indicate(l->chan,AST_CONTROL_RADIO_UNKEY);
                                }
                        } else l->retxtimer = 0;
-#ifdef RECONNECT_KLUDGE
                        if (l->disctime) /* Disconnect timer active on a channel ? */
                        {
                                l->disctime -= elap;
@@ -5599,7 +6628,10 @@ char tmpstr[300];
                                l->retrytimer -= elap;
                                if (l->retrytimer < 0) l->retrytimer = 0;
                        }
-#endif
+
+                       /* Tally connect time */
+                       l->connecttime += elap;
+
                        /* ignore non-timing channels */
                        if (l->elaptime < 0)
                        {
@@ -5614,13 +6646,9 @@ char tmpstr[300];
                                l->elaptime = 0;
                                rpt_mutex_unlock(&myrpt->lock);
                                if (l->chan) ast_softhangup(l->chan,AST_SOFTHANGUP_DEV);
-#ifndef        RECONNECT_KLUDGE
-                               rpt_telemetry(myrpt,CONNFAIL,l);
-#endif
                                rpt_mutex_lock(&myrpt->lock);
                                break;
                        }
-#ifdef RECONNECT_KLUDGE
                        if ((!l->chan) && (!l->retrytimer) && l->outbound && 
                                (l->retries++ < MAX_RETRIES) && (l->hasconnected))
                        {
@@ -5677,26 +6705,17 @@ char tmpstr[300];
                                 rpt_mutex_lock(&myrpt->lock);
                                 break;
                         }
-#endif
                        l = l->next;
                }
+               if(totx){
+                       myrpt->dailytxtime += elap;
+                       myrpt->totaltxtime += elap;
+               }
                i = myrpt->tailtimer;
                if (myrpt->tailtimer) myrpt->tailtimer -= elap;
                if (myrpt->tailtimer < 0) myrpt->tailtimer = 0;
-
-               if((i) && (myrpt->tailtimer == 0)){
-                       if(myrpt->tailid){
-                               queue_id(myrpt);
-                       }
-                       else if ((myrpt->tailmessages[0]) &&
-                               (myrpt->tailmessagetime) && (myrpt->tmsgtimer == 0)){
-                                       myrpt->tmsgtimer = myrpt->tailmessagetime;      
-                                       rpt_mutex_unlock(&myrpt->lock);
-                                       rpt_telemetry(myrpt, TAILMSG, NULL);
-                                       rpt_mutex_lock(&myrpt->lock);
-                       }       
-               }
-               if (myrpt->tailtimer < 0) myrpt->tailtimer = 0;
+               if((i) && (myrpt->tailtimer == 0))
+                       myrpt->tailevent = 1;
                if (myrpt->totimer) myrpt->totimer -= elap;
                if (myrpt->totimer < 0) myrpt->totimer = 0;
                if (myrpt->idtimer) myrpt->idtimer -= elap;
@@ -5706,6 +6725,13 @@ char tmpstr[300];
                /* do macro timers */
                if (myrpt->macrotimer) myrpt->macrotimer -= elap;
                if (myrpt->macrotimer < 0) myrpt->macrotimer = 0;
+               /* Execute scheduler appx. every 2 tenths of a second */
+               if (myrpt->skedtimer <= 0){
+                       myrpt->skedtimer = 200;
+                       do_scheduler(myrpt);
+               }
+               else
+                       myrpt->skedtimer -=elap;
                if (!ms) 
                {
                        rpt_mutex_unlock(&myrpt->lock);
@@ -5731,8 +6757,59 @@ char tmpstr[300];
                        }
                        if (f->frametype == AST_FRAME_VOICE)
                        {
-                               if (!myrpt->localtx)
+#ifdef _MDC_DECODE_H_
+                               unsigned char ubuf[2560];
+                               short *sp;
+                               int n;
+#endif
+
+                               if (!myrpt->localtx) {
                                        memset(f->data,0,f->datalen);
+                               }
+
+#ifdef _MDC_DECODE_H_
+                               sp = (short *) f->data;
+                               /* convert block to unsigned char */
+                               for(n = 0; n < f->datalen / 2; n++)
+                               {
+                                       ubuf[n] = (*sp++ >> 8) + 128;
+                               }
+                               n = mdc_decoder_process_samples(myrpt->mdc,ubuf,f->datalen / 2);
+                               if (n == 1)
+                               {
+                                               unsigned char op,arg;
+                                               unsigned short unitID;
+
+                                               mdc_decoder_get_packet(myrpt->mdc,&op,&arg,&unitID);
+                                               if (debug > 2)
+                                               {
+                                                       ast_log(LOG_NOTICE,"Got (single-length) packet:\n");
+                                                       ast_log(LOG_NOTICE,"op: %02x, arg: %02x, UnitID: %04x\n",
+                                                               op & 255,arg & 255,unitID);
+                                               }
+                                               if ((op == 1) && (arg == 0))
+                                               {
+                                                       myrpt->lastunit = unitID;
+                                               }
+                               }
+                               if ((debug > 2) && (i == 2))
+                               {
+                                       unsigned char op,arg,ex1,ex2,ex3,ex4;
+                                       unsigned short unitID;
+
+                                       mdc_decoder_get_double_packet(myrpt->mdc,&op,&arg,&unitID,
+                                               &ex1,&ex2,&ex3,&ex4);
+                                       ast_log(LOG_NOTICE,"Got (double-length) packet:\n");
+                                       ast_log(LOG_NOTICE,"op: %02x, arg: %02x, UnitID: %04x\n",
+                                               op & 255,arg & 255,unitID);
+                                       ast_log(LOG_NOTICE,"ex1: %02x, ex2: %02x, ex3: %02x, ex4: %02x\n",
+                                               ex1 & 255, ex2 & 255, ex3 & 255, ex4 & 255);
+                               }
+#endif
+#ifdef __RPT_NOTCH
+                               /* apply inbound filters, if any */
+                               rpt_filter(myrpt,f->data,f->datalen / 2);
+#endif
                                ast_write(myrpt->pchannel,f);
                        }
                        else if (f->frametype == AST_FRAME_DTMF)
@@ -5853,7 +6930,6 @@ char tmpstr[300];
                                f = ast_read(l->chan);
                                if (!f)
                                {
-#ifdef RECONNECT_KLUDGE
                                        if ((!l->disced) && (!l->outbound))
                                        {
                                                if ((l->name[0] == '0') || l->isremote)
@@ -5884,7 +6960,6 @@ char tmpstr[300];
                                                rpt_mutex_lock(&myrpt->lock);
                                                break;
                                        }
-#endif
                                        rpt_mutex_lock(&myrpt->lock);
                                        /* remove from queue */
                                        remque((struct qelem *) l);
@@ -5926,7 +7001,10 @@ char tmpstr[300];
                                                l->hasconnected = 1;
                                                l->elaptime = -1;
                                                l->retries = 0;
-                                               if (!lconnected) rpt_telemetry(myrpt,CONNECTED,l);
+                                               if (!lconnected) 
+                                                       rpt_telemetry(myrpt,CONNECTED,l);
+                                               else
+                                                       l->reconnects++;
                                        }
                                        /* if RX key */
                                        if (f->subclass == AST_CONTROL_RADIO_KEY)
@@ -5943,7 +7021,6 @@ char tmpstr[300];
                                        if (f->subclass == AST_CONTROL_HANGUP)
                                        {
                                                ast_frfree(f);
-#ifdef RECONNECT_KLUDGE
                                                if ((!l->outbound) && (!l->disced))
                                                {
                                                        if ((l->name[0] == '0') || l->isremote)
@@ -5973,7 +7050,6 @@ char tmpstr[300];
                                                        rpt_mutex_lock(&myrpt->lock);
                                                        break;
                                                }
-#endif
                                                rpt_mutex_lock(&myrpt->lock);
                                                /* remove from queue */
                                                remque((struct qelem *) l);
@@ -6075,187 +7151,56 @@ char tmpstr[300];
        return NULL;
 }
 
-
+       
 static void *rpt_master(void *ignore)
 {
-char *this,*val;
-struct ast_variable *vp;
-int    i,j,n,longestnode;
+int    i,n;
 pthread_attr_t attr;
+struct ast_config *cfg;
+char *this,*val;
 
-       /* start with blank config */
-       memset(&rpt_vars,0,sizeof(rpt_vars));
-
-       cfg = ast_config_load("rpt.conf");
+       /* go thru all the specified repeaters */
+       this = NULL;
+       n = 0;
+       rpt_vars[n].cfg = ast_config_load("rpt.conf");
+       cfg = rpt_vars[n].cfg;
        if (!cfg) {
                ast_log(LOG_NOTICE, "Unable to open radio repeater configuration rpt.conf.  Radio Repeater disabled.\n");
                pthread_exit(NULL);
        }
-
-       /* go thru all the specified repeaters */
-       this = NULL;
-       n = 0;
        while((this = ast_category_browse(cfg,this)) != NULL)
        {
-       
                for(i = 0 ; i < strlen(this) ; i++){
                        if((this[i] < '0') || (this[i] > '9'))
                                break;
                }
-               if(i != strlen(this))
-                       continue; /* Not a node defn */
-                       
-               ast_log(LOG_DEBUG,"Loading config for repeater %s\n",this);
+               if(i != strlen(this)) continue; /* Not a node defn */
+               memset(&rpt_vars[n],0,sizeof(rpt_vars[n]));
+               rpt_vars[n].name = strdup(this);
+               val = ast_variable_retrieve(cfg,this,"rxchannel");
+               if (val) rpt_vars[n].rxchanname = strdup(val);
+               val = ast_variable_retrieve(cfg,this,"txchannel");
+               if (val) rpt_vars[n].txchanname = strdup(val);
+               val = ast_variable_retrieve(cfg,this,"remote");
+               if (val) rpt_vars[n].remote = strdup(val);
                ast_mutex_init(&rpt_vars[n].lock);
                rpt_vars[n].tele.next = &rpt_vars[n].tele;
                rpt_vars[n].tele.prev = &rpt_vars[n].tele;
                rpt_vars[n].rpt_thread = AST_PTHREADT_NULL;
-               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].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");
-               rpt_vars[n].acctcode = ast_variable_retrieve(cfg,this,"accountcode");
-               rpt_vars[n].ident = ast_variable_retrieve(cfg,this,"idrecording");
-               val = ast_variable_retrieve(cfg,this,"hangtime");
-               if (val) rpt_vars[n].hangtime = atoi(val);
-                       else rpt_vars[n].hangtime = HANGTIME;
-               val = ast_variable_retrieve(cfg,this,"totime");
-               if (val) rpt_vars[n].totime = atoi(val);
-                       else rpt_vars[n].totime = TOTIME;
-
-               rpt_vars[n].tailmessagetime = retrieve_astcfgint(this, "tailmessagetime", 0, 2400000, 0);               
-               rpt_vars[n].tailsquashedtime = retrieve_astcfgint(this, "tailsquashedtime", 0, 2400000, 0);             
-               rpt_vars[n].duplex = retrieve_astcfgint(this,"duplex",0,3,2);
-               rpt_vars[n].idtime = retrieve_astcfgint( this, "idtime", 60000, 2400000, IDTIME);       /* Enforce a min max */
-               rpt_vars[n].politeid = retrieve_astcfgint( this, "politeid", 30000, 300000, POLITEID); /* Enforce a min max */
-               rpt_vars[n].remote = ast_variable_retrieve(cfg,this,"remote");
-               rpt_vars[n].tonezone = ast_variable_retrieve(cfg,this,"tonezone");
-               rpt_vars[n].tailmessages[0] = 0;
-               rpt_vars[n].tailmessagemax = 0;
                rpt_vars[n].tailmessagen = 0;
-               val = ast_variable_retrieve(cfg,this,"tailmessagelist");
-               if (val) rpt_vars[n].tailmessagemax = finddelim(val,rpt_vars[n].tailmessages);
-               val = ast_variable_retrieve(cfg,this,"memory");
-               if (!val) val = MEMORY;
-               rpt_vars[n].memory = val;
-               val = ast_variable_retrieve(cfg,this,"macro");
-               if (!val) val = MACRO;
-               rpt_vars[n].macro = val;
-               rpt_vars[n].startupmacro = ast_variable_retrieve(cfg,this,"startup_macro");
-               val = ast_variable_retrieve(cfg,this,"iobase");
-               /* do not use atoi() here, we need to be able to have
-                       the input specified in hex or decimal so we use
-                       sscanf with a %i */
-               if ((!val) || (sscanf(val,"%i",&rpt_vars[n].iobase) != 1))
-                       rpt_vars[n].iobase = DEFAULT_IOBASE;
-               rpt_vars[n].simple = 0;
-               rpt_vars[n].functions = ast_variable_retrieve(cfg,this,"functions");
-               if (!rpt_vars[n].functions) 
-               {
-                       rpt_vars[n].functions = FUNCTIONS;
-                       rpt_vars[n].simple = 1;
-               }
-               rpt_vars[n].link_functions = ast_variable_retrieve(cfg,this,"link_functions");
-               if (!rpt_vars[n].link_functions) 
-                       rpt_vars[n].link_functions = rpt_vars[n].functions;
-               rpt_vars[n].phone_functions = ast_variable_retrieve(cfg,this,"phone_functions");
-               rpt_vars[n].dphone_functions = ast_variable_retrieve(cfg,this,"dphone_functions");
-               val = ast_variable_retrieve(cfg,this,"funcchar");
-               if (!val) rpt_vars[n].funcchar = FUNCCHAR; else 
-                       rpt_vars[n].funcchar = *val;            
-               val = ast_variable_retrieve(cfg,this,"endchar");
-               if (!val) rpt_vars[n].endchar = ENDCHAR; else 
-                       rpt_vars[n].endchar = *val;             
-               val = ast_variable_retrieve(cfg,this,"nobusyout");
-               if (val) rpt_vars[n].nobusyout = ast_true(val);
-               rpt_vars[n].nodes = ast_variable_retrieve(cfg,this,"nodes");
-               if (!rpt_vars[n].nodes) 
-                       rpt_vars[n].nodes = NODES;
+#ifdef _MDC_DECODE_H_
+               rpt_vars[n].mdc = mdc_decoder_new(8000);
+#endif
                n++;
        }
        nrpts = n;
-       ast_log(LOG_DEBUG, "Total of %d repeaters configured.\n",n);
+       ast_config_destroy(cfg);
+
        /* start em all */
        for(i = 0; i < n; i++)
        {
+               load_rpt_vars(i,1);
 
-               /*
-               * Go through the node list to determine the longest node
-               */
-               longestnode = 0;
-
-               vp = ast_variable_browse(cfg, rpt_vars[i].nodes);
-               
-               while(vp){
-                       j = strlen(vp->name);
-                       if (j > longestnode)
-                               longestnode = j;
-                       vp = vp->next;
-               }
-
-
-               rpt_vars[i].longestnode = longestnode;
-               
-               /*
-               * For this repeater, Determine the length of the longest function 
-               */
-               rpt_vars[i].longestfunc = 0;
-               vp = ast_variable_browse(cfg, rpt_vars[i].functions);
-               while(vp){
-                       j = strlen(vp->name);
-                       if (j > rpt_vars[i].longestfunc)
-                               rpt_vars[i].longestfunc = j;
-                       vp = vp->next;
-               }
-               /*
-               * For this repeater, Determine the length of the longest function 
-               */
-               rpt_vars[i].link_longestfunc = 0;
-               vp = ast_variable_browse(cfg, rpt_vars[i].link_functions);
-               while(vp){
-                       j = strlen(vp->name);
-                       if (j > rpt_vars[i].link_longestfunc)
-                               rpt_vars[i].link_longestfunc = j;
-                       vp = vp->next;
-               }
-               rpt_vars[i].phone_longestfunc = 0;
-               if (rpt_vars[i].phone_functions)
-               {
-                       vp = ast_variable_browse(cfg, rpt_vars[i].phone_functions);
-                       while(vp){
-                               j = strlen(vp->name);
-                               if (j > rpt_vars[i].phone_longestfunc)
-                                       rpt_vars[i].phone_longestfunc = j;
-                               vp = vp->next;
-                       }
-               }
-               rpt_vars[i].dphone_longestfunc = 0;
-               if (rpt_vars[i].dphone_functions)
-               {
-                       vp = ast_variable_browse(cfg, rpt_vars[i].dphone_functions);
-                       while(vp){
-                               j = strlen(vp->name);
-                               if (j > rpt_vars[i].dphone_longestfunc)
-                                       rpt_vars[i].dphone_longestfunc = j;
-                               vp = vp->next;
-                       }
-               }
-               rpt_vars[i].macro_longest = 1;
-               vp = ast_variable_browse(cfg, rpt_vars[i].macro);
-               while(vp){
-                       j = strlen(vp->name);
-                       if (j > rpt_vars[i].macro_longest)
-                               rpt_vars[i].macro_longest = j;
-                       vp = vp->next;
-               }
-               if (!rpt_vars[i].rxchanname)
-               {
-                       ast_log(LOG_WARNING,"Did not specify rxchanname for node %s\n",rpt_vars[i].name);
-                       ast_config_destroy(cfg);
-                       pthread_exit(NULL);
-               }
                /* if is a remote, dont start one for it */
                if (rpt_vars[i].remote)
                {
@@ -6268,7 +7213,7 @@ pthread_attr_t attr;
                        rpt_vars[i].powerlevel = REM_MEDPWR;
                        continue;
                }
-               if (!rpt_vars[i].ident)
+               if (!rpt_vars[i].p.ident)
                {
                        ast_log(LOG_WARNING,"Did not specify ident for node %s\n",rpt_vars[i].name);
                        ast_config_destroy(cfg);
@@ -6392,7 +7337,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                m = myrpt->callmode;
                rpt_mutex_unlock(&myrpt->lock);
 
-               if ((!myrpt->nobusyout) && m)
+               if ((!myrpt->p.nobusyout) && m)
                {
                        if (chan->_state != AST_STATE_UP)
                        {
@@ -6457,9 +7402,13 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                }
                /* At this point we have a priority and maybe an extension and a context */
                chan->priority = atoi(priority);
-               if (exten)
+#ifdef OLD_ASTERISK
+               if(exten && strcasecmp(exten, "BYEXTENSION"))
+#else
+               if(exten)
+#endif
                        strncpy(chan->exten, exten, sizeof(chan->exten)-1);
-               if (context)
+               if(context)
                        strncpy(chan->context, context, sizeof(chan->context)-1);
                } else {  /* increment the priority by default*/
                        chan->priority++;
@@ -6528,7 +7477,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
 
 
                /* look for his reported node string */
-               val = ast_variable_retrieve(cfg, myrpt->nodes, b1);
+               val = ast_variable_retrieve(myrpt->cfg, myrpt->p.nodes, b1);
                if (!val)
                {
                        ast_log(LOG_WARNING, "Reported node %s cannot be found!!\n",b1);
@@ -6580,6 +7529,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
        {
 
                char *b,*b1;
+               int reconnects = 0;
 
                /* look at callerid to see what node this comes from */
                if (!chan->cid.cid_num) /* if doesn't have caller id */
@@ -6615,6 +7565,8 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                        l->killme = 1;
                        l->retries = MAX_RETRIES + 1;
                        l->disced = 2;
+                       reconnects = l->reconnects;
+                       reconnects++;
                         rpt_mutex_unlock(&myrpt->lock);
                        usleep(500000); 
                } else 
@@ -6634,6 +7586,7 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                l->chan = chan;
                l->connected = 1;
                l->hasconnected = 1;
+               l->reconnects = reconnects;
                l->phonemode = phone_mode;
                ast_set_read_format(l->chan,AST_FORMAT_SLINEAR);
                ast_set_write_format(l->chan,AST_FORMAT_SLINEAR);
@@ -6680,13 +7633,24 @@ static int rpt_exec(struct ast_channel *chan, void *data)
                rpt_mutex_lock(&myrpt->lock);
        }
        myrpt->remoteon = 1;
-       if (ioperm(myrpt->iobase,1,1) == -1)
+       if (ioperm(myrpt->p.iobase,1,1) == -1)
        {
                rpt_mutex_unlock(&myrpt->lock);
-               ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->iobase);
+               ast_log(LOG_WARNING, "Cant get io permission on IO port %x hex\n",myrpt->p.iobase);
                return -1;
        }
        LOCAL_USER_ADD(u);
+       rpt_mutex_unlock(&myrpt->lock);
+       /* find our index, and load the vars initially */
+       for(i = 0; i < nrpts; i++)
+       {
+               if (&rpt_vars[i] == myrpt)
+               {
+                       load_rpt_vars(i,0);
+                       break;
+               }
+       }
+       rpt_mutex_lock(&myrpt->lock);
        tele = strchr(myrpt->rxchanname,'/');
        if (!tele)
        {
@@ -6765,11 +7729,12 @@ static int rpt_exec(struct ast_channel *chan, void *data)
        myrpt->dtmf_time_rem = 0;
        myrpt->hfscanmode = 0;
        myrpt->hfscanstatus = 0;
-       if (myrpt->startupmacro)
+       if (myrpt->p.startupmacro)
        {
                myrpt->remchannel = chan; /* Save copy of channel */
-               snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->startupmacro);
+               snprintf(myrpt->macrobuf,MAXMACRO - 1,"PPPP%s",myrpt->p.startupmacro);
        }
+       myrpt->reload = 0;
        rpt_mutex_unlock(&myrpt->lock);
        setrem(myrpt); 
        ast_set_write_format(chan, AST_FORMAT_SLINEAR);
@@ -6801,6 +7766,21 @@ static int rpt_exec(struct ast_channel *chan, void *data)
        {
                if (ast_check_hangup(chan)) break;
                if (ast_check_hangup(myrpt->rxchannel)) break;
+               if (myrpt->reload)
+               {
+                       myrpt->reload = 0;
+                       rpt_mutex_unlock(&myrpt->lock);
+                       /* find our index, and load the vars */
+                       for(i = 0; i < nrpts; i++)
+                       {
+                               if (&rpt_vars[i] == myrpt)
+                               {
+                                       load_rpt_vars(i,0);
+                                       break;
+                               }
+                       }
+                       rpt_mutex_lock(&myrpt->lock);
+               }
                ms = MSWAIT;
                who = ast_waitfor_n(cs,n,&ms);
                if (who == NULL) ms = 0;
@@ -7028,13 +8008,17 @@ static int rpt_exec(struct ast_channel *chan, void *data)
        return res;
 }
 
-static int unload_module(void *mod)
+#ifdef OLD_ASTERISK
+int unload_module()
+#else
+static int unload_module(void* mod)
+#endif
 {
        int i;
 
        STANDARD_HANGUP_LOCALUSERS;
        for(i = 0; i < nrpts; i++) {
-               if (!strcmp(rpt_vars[i].name,rpt_vars[i].nodes)) continue;
+               if (!strcmp(rpt_vars[i].name,rpt_vars[i].p.nodes)) continue;
                 ast_mutex_destroy(&rpt_vars[i].lock);
        }
        i = ast_unregister_application(app);
@@ -7042,30 +8026,72 @@ static int unload_module(void *mod)
        /* Unregister cli extensions */
        ast_cli_unregister(&cli_debug);
        ast_cli_unregister(&cli_dump);
+       ast_cli_unregister(&cli_stats);
+       ast_cli_unregister(&cli_lstats);
+       ast_cli_unregister(&cli_reload);
+       ast_cli_unregister(&cli_restart);
 
        return i;
 }
 
+#ifdef OLD_ASTERISK
+int load_module()
+#else
 static int load_module(void *mod)
+#endif
 {
        ast_pthread_create(&rpt_master_thread,NULL,rpt_master,NULL);
 
        /* Register cli extensions */
        ast_cli_register(&cli_debug);
        ast_cli_register(&cli_dump);
+       ast_cli_register(&cli_stats);
+       ast_cli_register(&cli_lstats);
+       ast_cli_register(&cli_reload);
+       ast_cli_register(&cli_restart);
 
        return ast_register_application(app, rpt_exec, synopsis, descrip);
 }
 
+#ifdef OLD_ASTERISK
+char *description()
+#else
 static const char *description(void)
+#endif
 {
        return tdesc;
 }
 
+#ifdef OLD_ASTERISK
+int usecount(void)
+{
+       int res;
+       STANDARD_USECOUNT(res);
+       return res;
+}
+#endif
+
+#ifdef OLD_ASTERISK
+char *key()
+#else
 static const char *key(void)
+#endif
 {
        return ASTERISK_GPL_KEY;
 }
 
-STD_MOD1;
+#ifdef OLD_ASTERISK
+int reload()
+#else
+static int reload(void *mod)
+#endif
+{
+int    n;
+
+       for(n = 0; n < nrpts; n++) rpt_vars[n].reload = 1;
+       return(0);
+}
 
+#ifndef        OLD_ASTERISK
+STD_MOD(MOD_1, reload, NULL, NULL);
+#endif