diff --git a/libs/spandsp/Makefile.am b/libs/spandsp/Makefile.am
index 193c108c6b..ccc9240240 100644
--- a/libs/spandsp/Makefile.am
+++ b/libs/spandsp/Makefile.am
@@ -16,6 +16,8 @@
 ## License along with this program; if not, write to the Free Software
 ## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+ACLOCAL_AMFLAGS = -I m4
+
 AM_CFLAGS = $(COMP_VENDOR_CFLAGS)
 AM_LDFLAGS = $(COMP_VENDOR_LDFLAGS)
 
diff --git a/libs/spandsp/configure.ac b/libs/spandsp/configure.ac
index 7914885d79..9783f0a685 100644
--- a/libs/spandsp/configure.ac
+++ b/libs/spandsp/configure.ac
@@ -18,34 +18,25 @@
 
 # @start 1
 
-AC_INIT
-
-m4_include(config/ax_compiler_vendor.m4)
-m4_include(config/ax_check_real_file.m4)
-m4_include(config/ax_fixed_point_machine.m4)
-m4_include(config/ax_misaligned_access_fails.m4)
-m4_include(config/ax_c99_features.m4)
-m4_include(config/ax_check_export_capability.m4)
-
-SPANDSP_MAJOR_VERSION=0
-SPANDSP_MINOR_VERSION=0
-SPANDSP_MICRO_VERSION=6
+AC_PREREQ([2.59])
+AC_INIT([spandsp], [0.0.6])
 
 SPANDSP_LT_CURRENT=2
 SPANDSP_LT_REVISION=0
 SPANDSP_LT_AGE=0
 
-VERSION=$SPANDSP_MAJOR_VERSION.$SPANDSP_MINOR_VERSION.$SPANDSP_MICRO_VERSION
-PACKAGE=spandsp
-
-AC_SUBST(SPANDSP_LT_CURRENT)
-AC_SUBST(SPANDSP_LT_REVISION)
-AC_SUBST(SPANDSP_LT_AGE)
+m4_include(m4/ax_compiler_vendor.m4)
+m4_include(m4/ax_check_real_file.m4)
+m4_include(m4/ax_fixed_point_machine.m4)
+m4_include(m4/ax_misaligned_access_fails.m4)
+m4_include(m4/ax_c99_features.m4)
+m4_include(m4/ax_check_export_capability.m4)
 
 AC_CONFIG_SRCDIR([src/tone_generate.c])
-AC_CONFIG_AUX_DIR(config)
+AC_CONFIG_AUX_DIR([config])
+AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([src/config.h:config-h.in])
-AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
+AM_INIT_AUTOMAKE([1.9.5])
 
 AC_CANONICAL_HOST
 #AC_CANONICAL_BUILD
@@ -59,51 +50,51 @@ AX_COMPILER_VENDOR
 
 if test "${build}" != "${host}"
 then
-  # If we are doing a Canadian Cross, in which the host and build systems
-  # are not the same, we set reasonable default values for the tools.
+    # If we are doing a Canadian Cross, in which the host and build systems
+    # are not the same, we set reasonable default values for the tools.
 
-  CC_FOR_BUILD=${CC_FOR_BUILD-gcc}
-  CPPFLAGS_FOR_BUILD="\$(CPPFLAGS)"
-  CC=${CC-${host_alias}-gcc}
-  CFLAGS=${CFLAGS-"-g -O2"}
-  CXX=${CXX-${host_alias}-c++}
-  CXXFLAGS=${CXXFLAGS-"-g -O2"}
+    CC_FOR_BUILD=${CC_FOR_BUILD-gcc}
+    CPPFLAGS_FOR_BUILD="\$(CPPFLAGS)"
+    CC=${CC-${host_alias}-gcc}
+    CFLAGS=${CFLAGS-"-g -O2"}
+    CXX=${CXX-${host_alias}-c++}
+    CXXFLAGS=${CXXFLAGS-"-g -O2"}
 else
-  # Set reasonable default values for some tools even if not Canadian.
-  # Of course, these are different reasonable default values, originally
-  # specified directly in the Makefile.
-  # We don't export, so that autoconf can do its job.
-  # Note that all these settings are above the fragment inclusion point
-  # in Makefile.in, so can still be overridden by fragments.
-  # This is all going to change when we autoconfiscate...
-  CC_FOR_BUILD="\$(CC)"
-  CPPFLAGS_FOR_BUILD="\$(CPPFLAGS)"
-  AC_PROG_CC
+    # Set reasonable default values for some tools even if not Canadian.
+    # Of course, these are different reasonable default values, originally
+    # specified directly in the Makefile.
+    # We don't export, so that autoconf can do its job.
+    # Note that all these settings are above the fragment inclusion point
+    # in Makefile.in, so can still be overridden by fragments.
+    # This is all going to change when we autoconfiscate...
+    CC_FOR_BUILD="\$(CC)"
+    CPPFLAGS_FOR_BUILD="\$(CPPFLAGS)"
+    AC_PROG_CC
 
-  # We must set the default linker to the linker used by gcc for the correct
-  # operation of libtool.  If LD is not defined and we are using gcc, try to
-  # set the LD default to the ld used by gcc.
-  if test -z "$LD"
-  then
-    if test "$GCC" = yes
+    # We must set the default linker to the linker used by gcc for the correct
+    # operation of libtool.  If LD is not defined and we are using gcc, try to
+    # set the LD default to the ld used by gcc.
+    if test -z "$LD"
     then
-      case $build in
-      *-*-mingw*)
-        gcc_prog_ld=`$CC -print-prog-name=ld 2>&1 | tr -d '\015'` ;;
-      *)
-        gcc_prog_ld=`$CC -print-prog-name=ld 2>&1` ;;
-      esac
-      case $gcc_prog_ld in
-      # Accept absolute paths.
-      [[\\/]* | [A-Za-z]:[\\/]*)]
-        LD="$gcc_prog_ld" ;;
-      esac
+        if test "$GCC" = yes
+        then
+            case $build in
+            *-*-mingw*)
+                gcc_prog_ld=`$CC -print-prog-name=ld 2>&1 | tr -d '\015'` ;;
+            *)
+                gcc_prog_ld=`$CC -print-prog-name=ld 2>&1` ;;
+            esac
+            case $gcc_prog_ld in
+            # Accept absolute paths.
+            [[\\/]* | [A-Za-z]:[\\/]*)]
+                LD="$gcc_prog_ld" ;;
+            esac
+        fi
     fi
-  fi
 
-  CXX=${CXX-"c++"}
-  CFLAGS=${CFLAGS-"-g -O2"}
-  CXXFLAGS=${CXXFLAGS-"-g -O2"}
+    CXX=${CXX-"c++"}
+    CFLAGS=${CFLAGS-"-g -O2"}
+    CXXFLAGS=${CXXFLAGS-"-g -O2"}
 fi
 
 AC_DEFUN([REMOVE_FROM_VAR],[
@@ -143,6 +134,7 @@ AC_ARG_ENABLE(sse4_1,       [  --enable-sse4-1      Enable SSE4.1 support])
 AC_ARG_ENABLE(sse4_2,       [  --enable-sse4-2      Enable SSE4.2 support])
 AC_ARG_ENABLE(sse4a,        [  --enable-sse4a       Enable SSE4A support])
 AC_ARG_ENABLE(sse5,         [  --enable-sse5        Enable SSE5 support])
+AC_ARG_ENABLE(avx,          [  --enable-avx         Enable AVX support])
 AC_ARG_ENABLE(fixed_point,  [  --enable-fixed-point Enable fixed point support])
 # The following is for MSVC, where we may be using a local copy of libtiff, built alongside spandsp
 AC_ARG_ENABLE(builtin_tiff,
@@ -207,9 +199,9 @@ AC_CHECK_HEADERS([fenv.h])
 AC_CHECK_HEADERS([fftw3.h], , [AC_CHECK_HEADERS([fftw.h])])
 AC_CHECK_HEADERS([pcap.h])
 AC_CHECK_HEADERS([pthread.h])
-if test "${build}" = "${host}"
+if test "${build}" == "${host}"
 then
-  AC_CHECK_HEADERS([X11/X.h])
+    AC_CHECK_HEADERS([X11/X.h])
 fi
 
 # Determine XML2 include path
@@ -253,12 +245,12 @@ AC_CHECK_HEADERS([FL/Fl_Audio_Meter.H], [], [], [],[[#include <FL/Fl.H>
 
 AC_LANG([C])
 
-if test "${build}" = "${host}"
+if test "${build}" == "${host}"
 then
     case "${host}" in
     x86_64-*)
         # X86_64 Linux machines may have both 64 bit and 32 bit libraries. We need to choose the right set
-        AX_CHECK_REAL_FILE([${prefix}/lib64], libdir='$(exec_prefix)/lib64')
+        AX_CHECK_REAL_FILE([${prefix}/lib64], libdir='${exec_prefix}/lib64')
         AX_CHECK_REAL_FILE([/usr/X11R6/lib64], [TESTLIBS="$TESTLIBS -L/usr/X11R6/lib64"], AC_CHECK_FILE([/usr/X11R6/lib], [TESTLIBS="$TESTLIBS -L/usr/X11R6/lib"]))
         # The very oldest AMD 64 bit chips support SSE2, SSE and MMX
         enable_sse2="yes"
@@ -266,6 +258,10 @@ then
     esac
 fi
 
+#AC_DEFINE([SPANDSP_SUPPORT_T42], [1], [Support T.42 JPEG compression])
+SPANDSP_SUPPORT_T42="#undef SPANDSP_SUPPORT_T42"
+#AC_DEFINE([SPANDSP_SUPPORT_T43], [1], [Support T.43 JBIG gray and colour compression])
+SPANDSP_SUPPORT_T43="#undef SPANDSP_SUPPORT_T43"
 #AC_DEFINE([SPANDSP_SUPPORT_T85], [1], [Support T.85 JBIG compression])
 SPANDSP_SUPPORT_T85="#undef SPANDSP_SUPPORT_T85"
 #AC_DEFINE([SPANDSP_SUPPORT_V34], [1], [Support the V.34 FAX modem])
@@ -287,14 +283,27 @@ AC_SEARCH_LIBS([expf], [m], AC_DEFINE([HAVE_EXPF], [1], [Define to 1 if you have
 AC_SEARCH_LIBS([logf], [m], AC_DEFINE([HAVE_LOGF], [1], [Define to 1 if you have the logf() function.]))
 AC_SEARCH_LIBS([log10f], [m], AC_DEFINE([HAVE_LOG10F], [1], [Define to 1 if you have the log10f() function.]))
 
-# Checks for libraries.
-AC_CHECK_LIB([xml2], [xmlParseFile], [AC_DEFINE([HAVE_LIBXML2], [1], [Define to 1 if you have the 'libxml2' library (-lxml2).]) SIMLIBS="$SIMLIBS -lxml2"])
+AC_SEARCH_LIBS([open_memstream], [m], AC_DEFINE([HAVE_OPEN_MEMSTREAM], [1], [Define to 1 if you have the open_memstream() function.]))
 
 if test -n "$enable_tests" ; then
+    AC_CHECK_PROG([HAVE_SOX], [sox], yes)
+    if test "x$HAVE_SOX" != "xyes" ; then
+        AC_MSG_ERROR("Cannot make tests without sox installed")
+    fi
+    AC_CHECK_PROG([HAVE_PBMTOG3], [pbmtog3], yes)
+    if test "x$HAVE_PBMTOG3" != "xyes" ; then
+        AC_MSG_ERROR("Cannot make tests without pbmtog3 installed (does your system require a netpbm-progs package?)")
+    fi
+    AC_CHECK_PROG([HAVE_FAX2TIFF], [fax2tiff], yes)
+    if test "x$HAVE_FAX2TIFF" != "xyes" ; then
+        AC_MSG_ERROR("Cannot make tests without fax2tiff installed (does your system require a libtiff-tools package?)")
+    fi
     AC_LANG([C])
-    AC_CHECK_LIB([sndfile], [sf_open], SIMLIBS="$SIMLIBS -lsndfile", AC_MSG_ERROR("Can't make tests without libsndfile (does your system require a libsndfile-devel package?)"))
-    AC_CHECK_LIB([fftw3], [fftw_plan_dft_1d], SIMLIBS="$SIMLIBS -lfftw3", [AC_CHECK_LIB([fftw], [fftw_create_plan], SIMLIBS="$SIMLIBS -lfftw", AC_MSG_ERROR("Can't make tests without FFTW 2 or 3 (does your system require an fftw?-devel package?)"))])
-    AC_CHECK_LIB([pcap], [pcap_open_offline], TESTLIBS="$TESTLIBS -lpcap", AC_MSG_ERROR("Can't make tests without libpcap (does your system require a libpcap-devel package?)"))
+    # Checks for libraries.
+    AC_CHECK_LIB([sndfile], [sf_open], SIMLIBS="$SIMLIBS -lsndfile", AC_MSG_ERROR("Cannot make tests without libsndfile (does your system require a libsndfile-devel package?)"))
+    AC_CHECK_LIB([fftw3], [fftw_plan_dft_1d], SIMLIBS="$SIMLIBS -lfftw3", [AC_CHECK_LIB([fftw], [fftw_create_plan], SIMLIBS="$SIMLIBS -lfftw", AC_MSG_ERROR("Cannot make tests without FFTW 2 or 3 (does your system require an fftw?-devel package?)"))])
+    AC_CHECK_LIB([xml2], [xmlParseFile], TESTLIBS="$TESTLIBS -lxml2", AC_MSG_ERROR("Cannot make tests without libxml2 (does your system require a libxml2-devel package?)"))
+    AC_CHECK_LIB([pcap], [pcap_open_offline], TESTLIBS="$TESTLIBS -lpcap", [AC_CHECK_LIB([wpcap], [pcap_open_offline], TESTLIBS="$TESTLIBS -lwpcap",  AC_MSG_ERROR("Cannot make tests without libpcap (does your system require an libpcap-devel package?)"))])
     AC_CHECK_LIB([pthread], [pthread_attr_init], TESTLIBS="$TESTLIBS -lpthread")
     AC_CHECK_LIB([dl], [dlopen], TESTLIBS="$TESTLIBS -ldl")
     AC_CHECK_LIB([Xft], [XftFontOpen], TESTLIBS="$TESTLIBS -lXft",, $TESTLIBS)
@@ -314,7 +323,10 @@ AX_CHECK_EXPORT_CAPABILITY([$host],
 
 case "${ax_cv_c_compiler_vendor}" in
 gnu)
-    COMP_VENDOR_CFLAGS="-std=gnu99 -ffast-math -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
+    COMP_VENDOR_CFLAGS="-std=gnu99 -ffast-math -Wall -Wunused-variable -Wunused-but-set-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
+    if test "$enable_avx" = "yes" ; then
+        COMP_VENDOR_CFLAGS="-mavx $COMP_VENDOR_CFLAGS"
+    fi
     if test "$enable_sse5" = "yes" ; then
         COMP_VENDOR_CFLAGS="-msse5 $COMP_VENDOR_CFLAGS"
     fi
@@ -343,9 +355,12 @@ gnu)
         COMP_VENDOR_CFLAGS="-mmmx $COMP_VENDOR_CFLAGS"
     fi
     case $host_os in
-    mingw* | cygwin*)
+    cygwin*)
         COMP_VENDOR_LDFLAGS="-no-undefined"
         ;;
+    mingw*)
+        COMP_VENDOR_LDFLAGS="-no-undefined -lws2_32"
+        ;;
     *)
         COMP_VENDOR_LDFLAGS=
         ;;
@@ -369,7 +384,10 @@ sun)
     REMOVE_FROM_VAR(CFLAGS, -Xc)
     ;;
 intel)
-    COMP_VENDOR_CFLAGS="-std=c99 -D_POSIX_C_SOURCE=2 -D_GNU_SOURCE=1 -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
+    COMP_VENDOR_CFLAGS="-std=c99 -D_POSIX_C_SOURCE=2 -D_GNU_SOURCE=1 -Wall -Wunused-variable -Wunused-but-set-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
+    if test "$enable_avx" = "yes" ; then
+        COMP_VENDOR_CFLAGS="-mavx $COMP_VENDOR_CFLAGS"
+    fi
     if test "$enable_sse5" = "yes" ; then
         COMP_VENDOR_CFLAGS="-msse5 $COMP_VENDOR_CFLAGS"
     fi
@@ -400,25 +418,13 @@ intel)
     COMP_VENDOR_LDFLAGS=
     ;;
 *)
-    COMP_VENDOR_CFLAGS="-std=c99 -Wall -Wunused-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
+    COMP_VENDOR_CFLAGS="-std=c99 -Wall -Wunused-variable -Wunused-but-set-variable -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes $COMP_VENDOR_CFLAGS"
     COMP_VENDOR_LDFLAGS=
     ;;
 esac
 
 COMP_VENDOR_CFLAGS="-DNDEBUG $COMP_VENDOR_CFLAGS"
 
-AM_CONDITIONAL([COND_DOC], [test "$enable_doc" = yes])
-AM_CONDITIONAL([COND_TESTS], [test "$enable_tests" = yes])
-AM_CONDITIONAL([COND_TESTDATA], [test "$enable_test_data" = yes])
-AM_CONDITIONAL([COND_MMX], [test "$enable_mmx" = yes])
-AM_CONDITIONAL([COND_SSE], [test "$enable_sse" = yes])
-AM_CONDITIONAL([COND_SSE2], [test "$enable_sse2" = yes])
-AM_CONDITIONAL([COND_SSE3], [test "$enable_sse3" = yes])
-AM_CONDITIONAL([COND_SSSE3], [test "$enable_ssse3" = yes])
-AM_CONDITIONAL([COND_SSE4_1], [test "$enable_sse4_1" = yes])
-AM_CONDITIONAL([COND_SSE4_2], [test "$enable_sse4_2" = yes])
-AM_CONDITIONAL([COND_SSE4A], [test "$enable_sse4a" = yes])
-AM_CONDITIONAL([COND_SSE5], [test "$enable_sse5" = yes])
 if test "$enable_fixed_point" = "yes" ; then
     AC_DEFINE([SPANDSP_USE_FIXED_POINT], [1], [Enable fixed point processing, where possible, instead of floating point])
     SPANDSP_USE_FIXED_POINT="#define SPANDSP_USE_FIXED_POINT 1"
@@ -433,6 +439,10 @@ AX_MISALIGNED_ACCESS_FAILS([$host],
     SPANDSP_MISALIGNED_ACCESS_FAILS="#define SPANDSP_MISALIGNED_ACCESS_FAILS 1"],
     [SPANDSP_MISALIGNED_ACCESS_FAILS="#undef SPANDSP_MISALIGNED_ACCESS_FAILS"])
 
+if test "$enable_avx" = "yes" ; then
+    AC_DEFINE([SPANDSP_USE_AVX], [1], [Use the AVX instruction set (i386 and x86_64 only).])
+    enable_sse5"yes"
+fi
 if test "$enable_sse5" = "yes" ; then
     AC_DEFINE([SPANDSP_USE_SSE5], [1], [Use the SSE5 instruction set (i386 and x86_64 only).])
     enable_sse4a="yes"
@@ -469,23 +479,42 @@ if test "$enable_mmx" = "yes" ; then
     AC_DEFINE([SPANDSP_USE_MMX], [1], [Use the MMX instruction set (i386 and x86_64 only).])
 fi
 
+AM_CONDITIONAL([COND_DOC], [test "$enable_doc" = yes])
+AM_CONDITIONAL([COND_TESTS], [test "$enable_tests" = yes])
+AM_CONDITIONAL([COND_TESTDATA], [test "$enable_test_data" = yes])
+AM_CONDITIONAL([COND_MMX], [test "$enable_mmx" = yes])
+AM_CONDITIONAL([COND_SSE], [test "$enable_sse" = yes])
+AM_CONDITIONAL([COND_SSE2], [test "$enable_sse2" = yes])
+AM_CONDITIONAL([COND_SSE3], [test "$enable_sse3" = yes])
+AM_CONDITIONAL([COND_SSSE3], [test "$enable_ssse3" = yes])
+AM_CONDITIONAL([COND_SSE4_1], [test "$enable_sse4_1" = yes])
+AM_CONDITIONAL([COND_SSE4_2], [test "$enable_sse4_2" = yes])
+AM_CONDITIONAL([COND_SSE4A], [test "$enable_sse4a" = yes])
+AM_CONDITIONAL([COND_SSE5], [test "$enable_sse5" = yes])
+AM_CONDITIONAL([COND_AVX], [test "$enable_avx" = yes])
+
 if test "$enable_builtin_tiff" = "yes" ; then
-  abs_tiffdir="`cd $srcdir/../tiff-3.8.2/ && pwd`"
-  save_CFLAGS=$CFLAGS
-  CFLAGS="$CFLAGS -I$abs_tiffdir/libtiff"
-  AC_CHECK_HEADERS([tiffio.h])
-  CFLAGS="$save_CFLAGS"
-  COMP_VENDOR_CFLAGS="-I$abs_tiffdir/libtiff $COMP_VENDOR_CFLAGS"
-  COMP_VENDOR_LDFLAGS="-L$abs_tiffdir/libtiff $COMP_VENDOR_LDFLAGS"
-  LIBS="$LIBS $abs_tiffdir/libtiff/libtiff.la"
-  AC_DEFINE([HAVE_LIBTIFF], [1], [Define to 1 if you have the `tiff' library (-ltiff).])
+    abs_tiffdir="`cd $srcdir/../tiff-3.8.2/ && pwd`"
+    save_CFLAGS=$CFLAGS
+    CFLAGS="$CFLAGS -I$abs_tiffdir/libtiff"
+    AC_CHECK_HEADERS([tiffio.h])
+    CFLAGS="$save_CFLAGS"
+    COMP_VENDOR_CFLAGS="-I$abs_tiffdir/libtiff $COMP_VENDOR_CFLAGS"
+    COMP_VENDOR_LDFLAGS="-L$abs_tiffdir/libtiff $COMP_VENDOR_LDFLAGS"
+    LIBS="$LIBS $abs_tiffdir/libtiff/libtiff.la"
+    AC_DEFINE([HAVE_LIBTIFF], [1], [Define to 1 if you have the `tiff' library (-ltiff).])
 else
-  AC_CHECK_HEADERS([tiffio.h])
-  AC_CHECK_LIB([tiff], [TIFFOpen], , AC_MSG_ERROR("Can't build without libtiff (does your system require a libtiff-devel package?)"), -lm)
+    AC_CHECK_HEADERS([tiffio.h])
+    AC_CHECK_LIB([tiff], [TIFFOpen], , AC_MSG_ERROR("Cannot build without libtiff (does your system require a libtiff-devel package?)"), -lm)
 fi
+AC_CHECK_HEADERS([jpeglib.h])
+AC_CHECK_LIB([jpeg], [jpeg_start_compress])
 
 TESTLIBS="$SIMLIBS $TESTLIBS"
 
+AC_SUBST(SPANDSP_LT_CURRENT)
+AC_SUBST(SPANDSP_LT_REVISION)
+AC_SUBST(SPANDSP_LT_AGE)
 AC_SUBST(CC_FOR_BUILD)
 AC_SUBST(CPPFLAGS_FOR_BUILD)
 AC_SUBST(COMP_VENDOR_CFLAGS)
@@ -495,6 +524,8 @@ AC_SUBST(TESTLIBS)
 AC_SUBST(SPANDSP_USE_FIXED_POINT)
 AC_SUBST(SPANDSP_MISALIGNED_ACCESS_FAILS)
 AC_SUBST(SPANDSP_USE_EXPORT_CAPABILITY)
+AC_SUBST(SPANDSP_SUPPORT_T42)
+AC_SUBST(SPANDSP_SUPPORT_T43)
 AC_SUBST(SPANDSP_SUPPORT_T85)
 AC_SUBST(SPANDSP_SUPPORT_V34)
 AC_SUBST(INSERT_INTTYPES_HEADER)
diff --git a/libs/spandsp/doc/doxygen.in b/libs/spandsp/doc/doxygen.in
index 87c3c34572..38af500c1a 100644
--- a/libs/spandsp/doc/doxygen.in
+++ b/libs/spandsp/doc/doxygen.in
@@ -357,12 +357,6 @@ MAX_INITIALIZER_LINES  = 30
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories 
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
-# in the documentation. The default is YES.
-
-SHOW_DIRECTORIES       = YES
-
 # The FILE_VERSION_FILTER tag can be used to specify a program or script that 
 # doxygen should invoke to get the current version for each file (typically from the 
 # version control system). Doxygen will invoke the program by executing (via 
@@ -635,12 +629,6 @@ HTML_FOOTER            =
 
 HTML_STYLESHEET        = css.css
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
-# files or namespaces will be aligned in HTML using tables. If set to 
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
 # If the GENERATE_HTMLHELP tag is set to YES, additional index files 
 # will be generated that can be used as input for tools like the 
 # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
@@ -1123,7 +1111,7 @@ CALL_GRAPH             = NO
 
 GRAPHICAL_HIERARCHY    = YES
 
-# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES 
 # then doxygen will show the dependencies a directory has on other directories 
 # in a graphical way. The dependency relations are determined by the #include
 # relations between the files in the directories.
diff --git a/libs/spandsp/config/ax_c99_features.m4 b/libs/spandsp/m4/ax_c99_features.m4
similarity index 100%
rename from libs/spandsp/config/ax_c99_features.m4
rename to libs/spandsp/m4/ax_c99_features.m4
diff --git a/libs/spandsp/config/ax_check_export_capability.m4 b/libs/spandsp/m4/ax_check_export_capability.m4
similarity index 100%
rename from libs/spandsp/config/ax_check_export_capability.m4
rename to libs/spandsp/m4/ax_check_export_capability.m4
diff --git a/libs/spandsp/config/ax_check_real_file.m4 b/libs/spandsp/m4/ax_check_real_file.m4
similarity index 100%
rename from libs/spandsp/config/ax_check_real_file.m4
rename to libs/spandsp/m4/ax_check_real_file.m4
diff --git a/libs/spandsp/config/ax_compiler_vendor.m4 b/libs/spandsp/m4/ax_compiler_vendor.m4
similarity index 100%
rename from libs/spandsp/config/ax_compiler_vendor.m4
rename to libs/spandsp/m4/ax_compiler_vendor.m4
diff --git a/libs/spandsp/config/ax_fixed_point_machine.m4 b/libs/spandsp/m4/ax_fixed_point_machine.m4
similarity index 100%
rename from libs/spandsp/config/ax_fixed_point_machine.m4
rename to libs/spandsp/m4/ax_fixed_point_machine.m4
diff --git a/libs/spandsp/config/ax_misaligned_access_fails.m4 b/libs/spandsp/m4/ax_misaligned_access_fails.m4
similarity index 100%
rename from libs/spandsp/config/ax_misaligned_access_fails.m4
rename to libs/spandsp/m4/ax_misaligned_access_fails.m4
diff --git a/libs/spandsp/spandsp.pc.in b/libs/spandsp/spandsp.pc.in
index 1a91ba04e1..19fb1d501a 100644
--- a/libs/spandsp/spandsp.pc.in
+++ b/libs/spandsp/spandsp.pc.in
@@ -1,5 +1,5 @@
 prefix=@prefix@
-exec_prefix=@prefix@
+exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
 
@@ -7,5 +7,6 @@ Name: spandsp
 Description: A DSP library for telephony.
 Requires:
 Version: @VERSION@
-Libs: -L${libdir} -lspandsp -ltiff -lm
+Libs: -L${libdir} -lspandsp
+Libs.private: -ltiff -lm
 Cflags: -I${includedir}
diff --git a/libs/spandsp/spandsp.spec b/libs/spandsp/spandsp.spec
index 5ba3f2cc8d..bf4fcb13da 100644
--- a/libs/spandsp/spandsp.spec
+++ b/libs/spandsp/spandsp.spec
@@ -1,19 +1,22 @@
-Summary:    A DSP library for telephony.
-Name:       spandsp
-Version:    0.0.6
-Release:    1
-License:    LGPL
-Group:      System Environment/Libraries
-URL:        http://www.soft-switch.org/spandsp
-BuildRoot:  %{_tmppath}/%{name}-%{version}-root
-Source:     http://www.soft-switch.org/downloads/spandsp/spandsp-0.0.6.tar.gz
-BuildRoot:  %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+%global pre 21
 
-BuildRequires: libtiff-devel
-BuildRequires: audiofile-devel
+Summary: A DSP library for telephony.
+Name: spandsp
+Version: 0.0.6
+Release: 1
+License: LGPLv2 and GPLv2
+Group: System Environment/Libraries
+URL: http://www.soft-switch.org/spandsp
+Source: http://www.soft-switch.org/downloads/spandsp/spandsp-0.0.6.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires: libtiff-devel%{?_isa}
+BuildRequires: libjpeg-turbo-devel%{?_isa}
+BuildRequires: libxml2-devel%{?_isa}
+BuildRequires: libsndfile-devel%{?_isa}
 BuildRequires: doxygen
-# for xsltproc:
 BuildRequires: libxslt
+BuildRequires: docbook-style-xsl
 
 %description
 SpanDSP is a library of DSP functions for telephony, in the 8000
@@ -26,49 +29,66 @@ relevant patents have expired. See the file DueDiligence for important
 information about these intellectual property issues.
 
 %package devel
-Summary:    SpanDSP development files
-Group:      Development/Libraries
-Requires:   spandsp = %{version}
-Requires:   libtiff-devel
-PreReq:     /sbin/install-info
+Summary: SpanDSP development files
+Group: Development/Libraries
+Requires: spandsp%{?_isa} = %{version}-%{release}
+Requires: libtiff-devel%{?_isa}
+Requires: libjpeg-turbo-devel%{?_isa}
 
 %description devel
 SpanDSP development files.
 
+%package apidoc
+Summary: SpanDSP API documentation
+Group: Development/Libraries
+
+%description apidoc
+SpanDSP API documentation.
+
 %prep
 %setup -q
 
 %build
 %configure --enable-doc --disable-static --disable-rpath
 make
+find doc/api -type f | xargs touch -r configure
 
 %install
 rm -rf %{buildroot}
 make install DESTDIR=%{buildroot}
 rm %{buildroot}%{_libdir}/libspandsp.la
+mkdir -p %{buildroot}%{_datadir}/spandsp
 
 %clean
 rm -rf %{buildroot}
 
 %files
 %defattr(-,root,root,-)
-%doc DueDiligence ChangeLog AUTHORS COPYING NEWS README 
+%doc DueDiligence ChangeLog AUTHORS COPYING NEWS README
 
 %{_libdir}/libspandsp.so.*
 
+%{_datadir}/spandsp
+
 %files devel
 %defattr(-,root,root,-)
-%doc doc/api
 %{_includedir}/spandsp.h
 %{_includedir}/spandsp
 %{_libdir}/libspandsp.so
 %{_libdir}/pkgconfig/spandsp.pc
 
+%files apidoc
+%defattr(-,root,root,-)
+%doc doc/api/html/*
+
 %post -p /sbin/ldconfig
 
 %postun -p /sbin/ldconfig
 
 %changelog
+* Mon Oct 03 2011 Steve Underwood <steveu@coppice.org> 0.0.6-1
+- Converge with what Fedora do
+
 * Wed Sep 24 2008 Tzafrir Cohen <tzafrir.cohen@xorcom.com> 0.0.5-1
 - Preparing for 0.0.5pre4 release
 - License: LGPL
diff --git a/libs/spandsp/spandsp.spec.in b/libs/spandsp/spandsp.spec.in
index 7b8406fa7c..759dd17c07 100644
--- a/libs/spandsp/spandsp.spec.in
+++ b/libs/spandsp/spandsp.spec.in
@@ -1,19 +1,22 @@
-Summary:    A DSP library for telephony.
-Name:       @PACKAGE@
-Version:    @VERSION@
-Release:    1
-License:    LGPL
-Group:      System Environment/Libraries
-URL:        http://www.soft-switch.org/spandsp
-BuildRoot:  %{_tmppath}/%{name}-%{version}-root
-Source:     http://www.soft-switch.org/downloads/spandsp/@PACKAGE@-@VERSION@.tar.gz
-BuildRoot:  %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+%global pre 21
 
-BuildRequires: libtiff-devel
-BuildRequires: audiofile-devel
+Summary: A DSP library for telephony.
+Name: @PACKAGE@
+Version: @VERSION@
+Release: 1
+License: LGPLv2 and GPLv2
+Group: System Environment/Libraries
+URL: http://www.soft-switch.org/spandsp
+Source: http://www.soft-switch.org/downloads/spandsp/@PACKAGE@-@VERSION@.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
+BuildRequires: libtiff-devel%{?_isa}
+BuildRequires: libjpeg-turbo-devel%{?_isa}
+BuildRequires: libxml2-devel%{?_isa}
+BuildRequires: libsndfile-devel%{?_isa}
 BuildRequires: doxygen
-# for xsltproc:
 BuildRequires: libxslt
+BuildRequires: docbook-style-xsl
 
 %description
 SpanDSP is a library of DSP functions for telephony, in the 8000
@@ -26,49 +29,66 @@ relevant patents have expired. See the file DueDiligence for important
 information about these intellectual property issues.
 
 %package devel
-Summary:    SpanDSP development files
-Group:      Development/Libraries
-Requires:   spandsp = %{version}
-Requires:   libtiff-devel
-PreReq:     /sbin/install-info
+Summary: SpanDSP development files
+Group: Development/Libraries
+Requires: spandsp%{?_isa} = %{version}-%{release}
+Requires: libtiff-devel%{?_isa}
+Requires: libjpeg-turbo-devel%{?_isa}
 
 %description devel
 SpanDSP development files.
 
+%package apidoc
+Summary: SpanDSP API documentation
+Group: Development/Libraries
+
+%description apidoc
+SpanDSP API documentation.
+
 %prep
 %setup -q
 
 %build
 %configure --enable-doc --disable-static --disable-rpath
 make
+find doc/api -type f | xargs touch -r configure
 
 %install
 rm -rf %{buildroot}
 make install DESTDIR=%{buildroot}
 rm %{buildroot}%{_libdir}/libspandsp.la
+mkdir -p %{buildroot}%{_datadir}/spandsp
 
 %clean
 rm -rf %{buildroot}
 
 %files
 %defattr(-,root,root,-)
-%doc DueDiligence ChangeLog AUTHORS COPYING NEWS README 
+%doc DueDiligence ChangeLog AUTHORS COPYING NEWS README
 
 %{_libdir}/libspandsp.so.*
 
+%{_datadir}/spandsp
+
 %files devel
 %defattr(-,root,root,-)
-%doc doc/api
 %{_includedir}/spandsp.h
 %{_includedir}/spandsp
 %{_libdir}/libspandsp.so
 %{_libdir}/pkgconfig/spandsp.pc
 
+%files apidoc
+%defattr(-,root,root,-)
+%doc doc/api/html/*
+
 %post -p /sbin/ldconfig
 
 %postun -p /sbin/ldconfig
 
 %changelog
+* Mon Oct 03 2011 Steve Underwood <steveu@coppice.org> 0.0.6-1
+- Converge with what Fedora do
+
 * Wed Sep 24 2008 Tzafrir Cohen <tzafrir.cohen@xorcom.com> 0.0.5-1
 - Preparing for 0.0.5pre4 release
 - License: LGPL
diff --git a/libs/spandsp/src/Makefile.am b/libs/spandsp/src/Makefile.am
index 96924bfe4c..ce4f5bad4c 100644
--- a/libs/spandsp/src/Makefile.am
+++ b/libs/spandsp/src/Makefile.am
@@ -87,7 +87,8 @@ INCLUDES = -I$(top_builddir)
 
 lib_LTLIBRARIES = libspandsp.la
 
-libspandsp_la_SOURCES = adsi.c \
+libspandsp_la_SOURCES = ademco_contactid.c \
+                        adsi.c \
                         async.c \
                         at_interpreter.c \
                         awgn.c \
@@ -173,7 +174,8 @@ libspandsp_la_SOURCES = adsi.c \
 
 libspandsp_la_LDFLAGS = -version-info @SPANDSP_LT_CURRENT@:@SPANDSP_LT_REVISION@:@SPANDSP_LT_AGE@ $(COMP_VENDOR_LDFLAGS)
 
-nobase_include_HEADERS = spandsp/adsi.h \
+nobase_include_HEADERS = spandsp/ademco_contactid.h \
+                         spandsp/adsi.h \
                          spandsp/async.h \
                          spandsp/arctan2.h \
                          spandsp/at_interpreter.h \
@@ -257,6 +259,7 @@ nobase_include_HEADERS = spandsp/adsi.h \
                          spandsp/vector_float.h \
                          spandsp/vector_int.h \
                          spandsp/version.h \
+                         spandsp/private/ademco_contactid.h \
                          spandsp/private/adsi.h \
                          spandsp/private/async.h \
                          spandsp/private/at_interpreter.h \
diff --git a/libs/spandsp/src/ademco_contactid.c b/libs/spandsp/src/ademco_contactid.c
new file mode 100644
index 0000000000..1ba11f4763
--- /dev/null
+++ b/libs/spandsp/src/ademco_contactid.c
@@ -0,0 +1,1062 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ademco_contactid.c - Ademco ContactID alarm protocol
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if defined(HAVE_TGMATH_H)
+#include <tgmath.h>
+#endif
+#if defined(HAVE_MATH_H)
+#include <math.h>
+#endif
+#include "floating_fudge.h"
+#include <memory.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "spandsp/telephony.h"
+#include "spandsp/fast_convert.h"
+#include "spandsp/logging.h"
+#include "spandsp/queue.h"
+#include "spandsp/complex.h"
+#include "spandsp/dds.h"
+#include "spandsp/power_meter.h"
+#include "spandsp/async.h"
+#include "spandsp/vector_float.h"
+#include "spandsp/complex_vector_float.h"
+#include "spandsp/vector_int.h"
+#include "spandsp/complex_vector_int.h"
+#include "spandsp/tone_detect.h"
+#include "spandsp/tone_generate.h"
+#include "spandsp/super_tone_rx.h"
+#include "spandsp/dtmf.h"
+#include "spandsp/ademco_contactid.h"
+
+#include "spandsp/private/logging.h"
+#include "spandsp/private/queue.h"
+#include "spandsp/private/tone_detect.h"
+#include "spandsp/private/tone_generate.h"
+#include "spandsp/private/dtmf.h"
+#include "spandsp/private/ademco_contactid.h"
+
+/*
+Ademco ContactID Protocol
+
+Answer
+Wait 0.5s to 2s for the line to settle
+Send 1400Hz for 100ms
+Send silence for 100ms
+Send 2300Hz for 100ms
+Receiver now waits
+
+(both timing and frequency errors specified as 3%, but sender side should accept these tones with 5% frequency error.)
+
+Sender waits 250-300ms after end of 2300Hz tone
+
+Send ACCT MT QXYZ GG CCC
+
+ACCT = 4 digit account code (0-9, B-F)
+MT = 2 digit message type (18 preferred, 98 optional)
+Q = 1 digit event qualifier. 1 = New event or opening. 3 = New restore or closing. 6 = Previous condition still present
+XYZ = 3 digit event code (0-9, B-F)
+GG = 2 digit group or partition number (0-9, B-F). 00=no specific group
+CCC = 3 digit zone number (event reports) or user number (open/close reports). 000=no specific zone or user information
+S = 1 digit hex checksum (sum all message digits + S) mod 15 == 0
+
+DTMF tones are 50-60ms on 50-60ms off
+
+0       10 (counted as 10 in checksum calculations)
+1       1
+2       2
+3       3
+4       4
+5       5
+6       6
+7       7
+8       8
+9       9
+B (*)   11
+C (#)   12
+D (A)   13
+E (B)   14
+F (C)   15
+
+DTMF D is not used
+
+Wait 1.25s for a kiss-off tone
+Detect at least 400ms of kissoff to be valid, then wait for end of tone
+
+Wait 250-300ms before sending the next DTMF message
+
+If kissoff doesn't start within 1.25s of the end of the DTMF, repeat the DTMF message
+
+Receiver sends 750-1000ms of 1400Hz as the kissoff tone
+
+Sender shall make 4 attempts before giving up. One successful kissoff resets the attempt counter
+*/
+
+struct ademco_code_s
+{
+    int code;
+    const char *name;
+    int data_type;
+};
+
+static const struct ademco_code_s ademco_codes[] =
+{
+    {0x100, "Medical",                                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x101, "Personal emergency",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x102, "Fail to report in",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x110, "Fire",                                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x111, "Smoke",                                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x112, "Combustion",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x113, "Water flow",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x114, "Heat",                                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x115, "Pull station",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x116, "Duct",                                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x117, "Flame",                                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x118, "Near alarm",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x120, "Panic",                                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x121, "Duress",                                   ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x122, "Silent",                                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x123, "Audible",                                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x124, "Duress - Access granted",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x125, "Duress - Egress granted",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x130, "Burglary",                                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x131, "Perimeter",                                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x132, "Interior",                                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x133, "24 hour (safe)",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x134, "Entry/Exit",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x135, "Day/Night",                                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x136, "Outdoor",                                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x137, "Tamper",                                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x138, "Near alarm",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x139, "Intrusion verifier",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x140, "General alarm",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x141, "Polling loop open",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x142, "Polling loop short",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x143, "Expansion module failure",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x144, "Sensor tamper",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x145, "Expansion module tamper",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x146, "Silent burglary",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x147, "Sensor supervision failure",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x150, "24 hour non-burglary",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x151, "Gas detected",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x152, "Refrigeration",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x153, "Loss of heat",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x154, "Water leakage",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x155, "Foil break",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x156, "Day trouble",                              ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x157, "Low bottled gas level",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x158, "High temp",                                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x159, "Low temp",                                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x161, "Loss of air flow",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x162, "Carbon monoxide detected",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x163, "Tank level",                               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x200, "Fire supervisory",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x201, "Low water pressure",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x202, "Low CO2",                                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x203, "Gate valve sensor",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x204, "Low water level",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x205, "Pump activated",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x206, "Pump failure",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x300, "System trouble",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x301, "AC loss",                                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x302, "Low system battery",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x303, "RAM checksum bad",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x304, "ROM checksum bad",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x305, "System reset",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x306, "Panel programming changed",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x307, "Self-test failure",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x308, "System shutdown",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x309, "Battery test failure",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x310, "Ground fault",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x311, "Battery missing/dead",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x312, "Power supply overcurrent",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x313, "Engineer reset",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x320, "Sounder/relay",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x321, "Bell 1",                                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x322, "Bell 2",                                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x323, "Alarm relay",                              ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x324, "Trouble relay",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x325, "Reversing relay",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x326, "Notification appliance ckt. #3",           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x327, "Notification appliance ckt. #4",           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x330, "System peripheral trouble",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x331, "Polling loop open",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x332, "Polling loop short",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x333, "Expansion module failure",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x334, "Repeater failure",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x335, "Local printer out of paper",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x336, "Local printer failure",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x337, "Exp. module DC loss",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x338, "Exp. module low battery",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x339, "Exp. module reset",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x341, "Exp. module tamper",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x342, "Exp. module AC loss",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x343, "Exp. module self-test fail",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x344, "RF receiver jam detect",                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x350, "Communication trouble",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x351, "Telco 1 fault",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x352, "Telco 2 fault",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x353, "Long range radio transmitter fault",       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x354, "Failure to communicate event",             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x355, "Loss of radio supervision",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x356, "Loss of central polling",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x357, "Long range radio VSWR problem",            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x370, "Protection loop",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x371, "Protection loop open",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x372, "Protection loop short",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x373, "Fire trouble",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x374, "Exit error alarm (zone)",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x375, "Panic zone trouble",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x376, "Hold-up zone trouble",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x377, "Swinger trouble",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x378, "Cross-zone trouble",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x380, "Sensor trouble",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x381, "Loss of supervision - RF",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x382, "Loss of supervision - RPM",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x383, "Sensor tamper",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x384, "RF low battery",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x385, "Smoke detector high sensitivity",          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x386, "Smoke detector low sensitivity",           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x387, "Intrusion detector high sensitivity",      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x388, "Intrusion detector low sensitivity",       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x389, "Sensor self-test failure",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x391, "Sensor Watch trouble",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x392, "Drift compensation error",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x393, "Maintenance alert",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x400, "Open/Close",                               ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x401, "O/C by user",                              ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x402, "Group O/C",                                ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x403, "Automatic O/C",                            ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x404, "Late to O/C",                              ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x405, "Deferred O/C",                             ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x406, "Cancel",                                   ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x407, "Remote arm/disarm",                        ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x408, "Quick arm",                                ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x409, "Keyswitch O/C",                            ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x441, "Armed STAY",                               ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x442, "Keyswitch Armed STAY",                     ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x450, "Exception O/C",                            ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x451, "Early O/C",                                ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x452, "Late O/C",                                 ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x453, "Failed to open",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x454, "Failed to close",                          ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x455, "Auto-arm failed",                          ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x456, "Partial arm",                              ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x457, "Exit error (user)",                        ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x458, "User on Premises",                         ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x459, "Recent close",                             ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x461, "Wrong code entry",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x462, "Legal code entry",                         ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x463, "Re-arm after alarm",                       ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x464, "Auto-arm time extended",                   ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x465, "Panic alarm reset",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x466, "Service on/off premises",                  ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x411, "Callback request made",                    ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x412, "Successful download/access",               ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x413, "Unsuccessful access",                      ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x414, "System shutdown command received",         ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x415, "Dialer shutdown command received",         ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x416, "Successful Upload",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x421, "Access denied",                            ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x422, "Access report by user",                    ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x423, "Forced Access",                            ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x424, "Egress Denied",                            ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x425, "Egress Granted",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x426, "Access Door propped open",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x427, "Access point door status monitor trouble", ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x428, "Access point request to exit trouble",     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x429, "Access program mode entry",                ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x430, "Access program mode exit",                 ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x431, "Access threat level change",               ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x432, "Access relay/trigger fail",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x433, "Access RTE shunt",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x434, "Access DSM shunt",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x501, "Access reader disable",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x520, "Sounder/Relay disable",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x521, "Bell 1 disable",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x522, "Bell 2 disable",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x523, "Alarm relay disable",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x524, "Trouble relay disable",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x525, "Reversing relay disable",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x526, "Notification appliance ckt. #3 disable",   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x527, "Notification appliance ckt. #4 disable",   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x531, "Module added",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x532, "Module removed",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x551, "Dialer disabled",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x552, "Radio transmitter disabled",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x553, "Remote upload/download disabled",          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x570, "Zone/Sensor bypass",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x571, "Fire bypass",                              ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x572, "24 hour zone bypass",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x573, "Burg. bypass",                             ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x574, "Group bypass",                             ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x575, "Swinger bypass",                           ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x576, "Access zone shunt",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x577, "Access point bypass",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x601, "Manual trigger test report",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x602, "Periodic test report",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x603, "Periodic RF transmission",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x604, "Fire test",                                ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x605, "Status report to follow",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x606, "Listen-in to follow",                      ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x607, "Walk test mode",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x608, "Periodic test - system trouble present",   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x609, "Video transmitter active",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x611, "Point tested OK",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x612, "Point not tested",                         ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x613, "Intrusion zone walk tested",               ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x614, "Fire zone walk tested",                    ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x615, "Panic zone walk tested",                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x616, "Service request",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x621, "Event log reset",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x622, "Event log 50% full",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x623, "Event log 90% full",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x624, "Event log overflow",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x625, "Time/Date reset",                          ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x626, "Time/Date inaccurate",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x627, "Program mode entry",                       ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x628, "Program mode exit",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x629, "32 hour event log marker",                 ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x630, "Schedule change",                          ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x631, "Exception schedule change",                ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x632, "Access schedule change",                   ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x641, "Senior watch trouble",                     ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x642, "Latch-key supervision",                    ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x651, "Reserved for Ademco use",                  ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x652, "Reserved for Ademco use",                  ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x653, "Reserved for Ademco use",                  ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x654, "System inactivity",                        ADEMCO_CONTACTID_DATA_IS_ZONE},
+    {0x900, "Download abort",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x901, "Download start/end",                       ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x902, "Download interrupted",                     ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x910, "Auto-close with bypass",                   ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x911, "Bypass closing",                           ADEMCO_CONTACTID_DATA_IS_USER},
+    {0x999, "32 hour no read of event log",             ADEMCO_CONTACTID_DATA_IS_USER},
+    {-1,    "???"}
+};
+
+#if defined(SPANDSP_USE_FIXED_POINT)
+#define GOERTZEL_SAMPLES_PER_BLOCK  55              /* We need to detect over a +-5% range */
+#define DETECTION_THRESHOLD         16439           /* -42dBm0 [((GOERTZEL_SAMPLES_PER_BLOCK*GOERTZEL_SAMPLES_PER_BLOCK*32768.0/(1.4142*128.0))*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
+#define TONE_TWIST                  4               /* 6dB */
+#define TONE_TO_TOTAL_ENERGY        64              /* -3dB */
+#else
+#define GOERTZEL_SAMPLES_PER_BLOCK  55              /* We need to detect over a +-5% range */
+#define DETECTION_THRESHOLD         2104205.6f      /* -42dBm0 [((GOERTZEL_SAMPLES_PER_BLOCK*GOERTZEL_SAMPLES_PER_BLOCK*32768.0/1.4142)*10^((-42 - DBM0_MAX_SINE_POWER)/20.0))^2] */
+#define TONE_TWIST                  3.981f          /* 6dB */
+#define TONE_TO_TOTAL_ENERGY        1.995f          /* 3dB */
+#endif
+
+static goertzel_descriptor_t tone_1400_desc;
+static goertzel_descriptor_t tone_2300_desc;
+
+SPAN_DECLARE(int) encode_msg(char buf[], const ademco_contactid_report_t *report)
+{
+    char *s;
+    int sum;
+    int x;
+    static const char remap[] = {'D', '*', '#', 'A', 'B', 'C'};
+
+    sprintf(buf, "%04X%02X%1X%03X%02X%03X", report->acct, report->mt, report->q, report->xyz, report->gg, report->ccc);
+    for (sum = 0, s = buf;  *s;  s++)
+    {
+        if (*s == 'A')
+            return -1;
+        if (*s > '9')
+        {
+            x = *s - ('A' - 10);
+            /* Remap the Ademco B-F digits to normal DTMF *#ABC digits */
+            *s = remap[x - 10];
+        }
+        else
+        {
+            x = *s - '0';
+            if (x == 0)
+                x = 10;
+        }
+        sum += x;
+    }
+    sum = ((sum + 15)/15)*15 - sum;
+    if (sum == 0)
+        sum = 'C';
+    else if (sum <= 9)
+        sum += '0';
+    else
+        sum = remap[sum - 10];
+    *s++ = sum;
+    *s = '\0';
+    return s - buf;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) decode_msg(ademco_contactid_report_t *report, const char buf[])
+{
+    const char *s;
+    char *t;
+    int sum;
+    int x;
+    char buf2[20];
+
+    /* We need to remap normal DTMF (0-0, *, #, A-D) to Ademco's psuedo-hex (0-0, B-F, nothing for A)
+       and calculate the checksum */
+    for (sum = 0, s = buf, t = buf2;  *s;  s++, t++)
+    {
+        x = *s;
+        switch (x)
+        {
+        case '*':
+            x = 'B';
+            break;
+        case '#':
+            x = 'C';
+            break;
+        case 'A':
+            x = 'D';
+            break;
+        case 'B':
+            x = 'E';
+            break;
+        case 'C':
+            x = 'F';
+            break;
+        case 'D':
+            /* This should not happen in the Ademco protocol */
+            x = 'A';
+            break;
+        default:
+            x = *s;
+            break;
+        }
+        *t = x;
+        if (x > '9')
+        {
+            x -= ('B' - 11);
+        }
+        else
+        {
+            if (x == '0')
+                x = 10;
+            else
+                x -= '0';
+        }
+        sum += x;
+    }
+    *t = '\0';
+    if (sum%15 != 0)
+        return -1;
+    if (sscanf(buf2, "%04x%02x%1x%03x%02x%03x", &report->acct, &report->mt, &report->q, &report->xyz, &report->gg, &report->ccc) != 6)
+        return -1;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(const char *) ademco_contactid_msg_qualifier_to_str(int q)
+{
+    switch (q)
+    {
+    case ADEMCO_CONTACTID_QUALIFIER_NEW_EVENT:
+        return "New event";
+    case ADEMCO_CONTACTID_QUALIFIER_NEW_RESTORE:
+        return "New restore";
+    case ADEMCO_CONTACTID_QUALIFIER_STATUS_REPORT:
+        return "Status report";
+    }
+    return "???";
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(const char *) ademco_contactid_event_to_str(int xyz)
+{
+    int entry;
+
+    for (entry = 0;  ademco_codes[entry].code >= 0;  entry++)
+    {
+        if (xyz == ademco_codes[entry].code)
+            return ademco_codes[entry].name;
+    }
+    return "???";
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_log_msg(ademco_contactid_receiver_state_t *s, const ademco_contactid_report_t *report)
+{
+    const char *t;
+
+    span_log(&s->logging, SPAN_LOG_FLOW, "Ademco Contact ID message:\n");
+    span_log(&s->logging, SPAN_LOG_FLOW, "    Account %X\n", report->acct);
+    switch (report->mt)
+    {
+    case ADEMCO_CONTACTID_MESSAGE_TYPE_18:
+    case ADEMCO_CONTACTID_MESSAGE_TYPE_98:
+        t = "Contact ID";
+        break;
+    default:
+        t = "???";
+        break;
+    }
+    span_log(&s->logging, SPAN_LOG_FLOW, "    Message type %s (%X)\n", t, report->mt);
+    t = ademco_contactid_msg_qualifier_to_str(report->q);
+    span_log(&s->logging, SPAN_LOG_FLOW, "    Qualifier %s (%X)\n", t, report->q);
+    t = ademco_contactid_event_to_str(report->xyz);
+    span_log(&s->logging, SPAN_LOG_FLOW, "    Event %s (%X)\n", t, report->xyz);
+    span_log(&s->logging, SPAN_LOG_FLOW, "    Group/partition %X\n", report->gg);
+    span_log(&s->logging, SPAN_LOG_FLOW, "    User/Zone information %X\n", report->ccc);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_tx(ademco_contactid_receiver_state_t *s, int16_t amp[], int max_samples)
+{
+    int i;
+    int samples;
+
+    switch (s->step)
+    {
+    case 0:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        vec_zeroi16(amp, samples);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Initial silence finished\n");
+        s->step++;
+        s->tone_phase_rate = dds_phase_rate(1400.0);
+        s->tone_level = dds_scaling_dbm0(-11);
+        s->tone_phase = 0;
+        s->remaining_samples = ms_to_samples(100);
+        return samples;
+    case 1:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        for (i = 0;  i < samples;  i++)
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->tone_level, 0);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "1400Hz tone finished\n");
+        s->step++;
+        s->remaining_samples = ms_to_samples(100);
+        return samples;
+    case 2:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        vec_zeroi16(amp, samples);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Second silence finished\n");
+        s->step++;
+        s->tone_phase_rate = dds_phase_rate(2300.0);
+        s->tone_level = dds_scaling_dbm0(-11);
+        s->tone_phase = 0;
+        s->remaining_samples = ms_to_samples(100);
+        return samples;
+    case 3:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        for (i = 0;  i < samples;  i++)
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->tone_level, 0);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "2300Hz tone finished\n");
+        s->step++;
+        s->remaining_samples = ms_to_samples(100);
+        return samples;
+    case 4:
+        /* Idle here, waiting for a response */
+        return 0;
+    case 5:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        vec_zeroi16(amp, samples);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "Sending kissoff\n");
+        s->step++;
+        s->tone_phase_rate = dds_phase_rate(1400.0);
+        s->tone_level = dds_scaling_dbm0(-11);
+        s->tone_phase = 0;
+        s->remaining_samples = ms_to_samples(850);
+        return samples;
+    case 6:
+        samples = (s->remaining_samples > max_samples)  ?  max_samples  :  s->remaining_samples;
+        for (i = 0;  i < samples;  i++)
+            amp[i] = dds_mod(&s->tone_phase, s->tone_phase_rate, s->tone_level, 0);
+        s->remaining_samples -= samples;
+        if (s->remaining_samples > 0)
+            return samples;
+        span_log(&s->logging, SPAN_LOG_FLOW, "1400Hz tone finished\n");
+        s->step = 4;
+        s->remaining_samples = ms_to_samples(100);
+        return samples;
+    }
+    return max_samples;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_rx(ademco_contactid_receiver_state_t *s, const int16_t amp[], int samples)
+{
+    return dtmf_rx(&s->dtmf, amp, samples);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_fillin(ademco_contactid_receiver_state_t *s, int samples)
+{
+    return dtmf_rx_fillin(&s->dtmf, samples);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(logging_state_t *) ademco_contactid_receiver_get_logging_state(ademco_contactid_receiver_state_t *s)
+{
+    return &s->logging;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void dtmf_digit_delivery(void *user_data, const char *digits, int len)
+{
+    ademco_contactid_receiver_state_t *s;
+    ademco_contactid_report_t report;
+
+    s = (ademco_contactid_receiver_state_t *) user_data;
+    memcpy(&s->rx_digits[s->rx_digits_len], digits, len);
+    s->rx_digits_len += len;
+    if (s->rx_digits_len == 16)
+    {
+        s->rx_digits[16] = '\0';
+        if (decode_msg(&report, s->rx_digits) == 0)
+        {
+            ademco_contactid_receiver_log_msg(s, &report);
+            if (s->callback)
+                s->callback(s->callback_user_data, &report);
+            s->step++;
+        }
+        s->rx_digits_len = 0;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) ademco_contactid_receiver_set_realtime_callback(ademco_contactid_receiver_state_t *s,
+                                                                   ademco_contactid_report_func_t callback,
+                                                                   void *user_data)
+{
+    s->callback = callback;
+    s->callback_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(ademco_contactid_receiver_state_t *) ademco_contactid_receiver_init(ademco_contactid_receiver_state_t *s,
+                                                                                 ademco_contactid_report_func_t callback,
+                                                                                 void *user_data)
+{
+    if (s == NULL)
+    {
+        if ((s = (ademco_contactid_receiver_state_t *) malloc(sizeof (*s))) == NULL)
+            return  NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "Ademco");
+
+    dtmf_rx_init(&s->dtmf, dtmf_digit_delivery, (void *) s);
+    s->rx_digits_len = 0;
+
+    s->callback = callback;
+    s->callback_user_data = user_data;
+
+    s->step = 0;
+    s->remaining_samples = ms_to_samples(500);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_release(ademco_contactid_receiver_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_receiver_free(ademco_contactid_receiver_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_tx(ademco_contactid_sender_state_t *s, int16_t amp[], int max_samples)
+{
+    int sample;
+    int samples;
+
+    for (sample = 0;  sample < max_samples;  sample += samples)
+    {
+        switch (s->step)
+        {
+        case 0:
+            if (!s->clear_to_send)
+                return 0;
+            s->clear_to_send = FALSE;
+            s->step++;
+            s->remaining_samples = ms_to_samples(250);
+            /* Fall through */
+        case 1:
+            samples = (s->remaining_samples > (max_samples - sample))  ?  (max_samples - sample)  :  s->remaining_samples;
+            vec_zeroi16(&amp[sample], samples);
+            s->remaining_samples -= samples;
+            if (s->remaining_samples > 0)
+                return samples;
+            span_log(&s->logging, SPAN_LOG_FLOW, "Pre-send silence finished\n");
+            s->step++;
+            break;
+        case 2:
+            samples = dtmf_tx(&s->dtmf, &amp[sample], max_samples - sample);
+            if (samples == 0)
+            {
+                s->clear_to_send = FALSE;
+                s->step = 0;
+                return sample;
+            }
+            break;
+        default:
+            return sample;
+        }
+    }
+    return sample;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_rx(ademco_contactid_sender_state_t *s, const int16_t amp[], int samples)
+{
+#if defined(SPANDSP_USE_FIXED_POINT)
+    int32_t energy_1400;
+    int32_t energy_2300;
+    int16_t xamp;
+#else
+    float energy_1400;
+    float energy_2300;
+    float xamp;
+#endif
+    int sample;
+    int limit;
+    int hit;
+    int j;
+
+    for (sample = 0;  sample < samples;  sample = limit)
+    {
+        if ((samples - sample) >= (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample))
+            limit = sample + (GOERTZEL_SAMPLES_PER_BLOCK - s->current_sample);
+        else
+            limit = samples;
+        for (j = sample;  j < limit;  j++)
+        {
+            xamp = amp[j];
+            xamp = goertzel_preadjust_amp(xamp);
+#if defined(SPANDSP_USE_FIXED_POINT)
+            s->energy += ((int32_t) xamp*xamp);
+#else
+            s->energy += xamp*xamp;
+#endif
+            goertzel_samplex(&s->tone_1400, xamp);
+            goertzel_samplex(&s->tone_2300, xamp);
+        }
+        s->current_sample += (limit - sample);
+        if (s->current_sample < GOERTZEL_SAMPLES_PER_BLOCK)
+            continue;
+
+        energy_1400 = goertzel_result(&s->tone_1400);
+        energy_2300 = goertzel_result(&s->tone_2300);
+        hit = 0;
+        if (energy_1400 > DETECTION_THRESHOLD  ||  energy_2300 > DETECTION_THRESHOLD)
+        {
+            if (energy_1400 > energy_2300)
+            {
+                if (energy_1400 > TONE_TO_TOTAL_ENERGY*s->energy)
+                    hit = 1;
+            }
+            else
+            {
+                if (energy_2300 > TONE_TO_TOTAL_ENERGY*s->energy)
+                    hit = 2;
+            }
+        }
+        if (hit != s->in_tone  &&  hit == s->last_hit)
+        {
+            /* We have two successive indications that something has changed to a
+               specific new state. */
+            switch (s->tone_state)
+            {
+            case 0:
+                if (hit == 1)
+                {
+                    span_log(&s->logging, SPAN_LOG_FLOW, "Receiving initial 1400Hz\n");
+                    s->in_tone = hit;
+                    s->tone_state = 1;
+                    s->duration = 0;
+                }
+                break;
+            case 1:
+                /* We are looking for a burst of 1400Hz which is 100ms +- 5% long */
+                if (hit == 0)
+                {
+                    if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 1400Hz tone duration\n");
+                        s->tone_state = 0;
+                    }
+                    else
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Received 1400Hz tone\n");
+                        s->tone_state = 2;
+                    }
+                    s->in_tone = hit;
+                    s->duration = 0;
+                }
+                break;
+            case 2:
+                /* We are looking for 100ms +-5% of silence after the 1400Hz tone */
+                if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
+                {
+                    span_log(&s->logging, SPAN_LOG_FLOW, "Bad silence length\n");
+                    s->tone_state = 0;
+                    s->in_tone = hit;
+                }
+                else if (hit == 2)
+                {
+                    span_log(&s->logging, SPAN_LOG_FLOW, "Received silence\n");
+                    s->tone_state = 3;
+                    s->in_tone = hit;
+                }
+                else
+                {
+                    s->tone_state = 0;
+                    s->in_tone = 0;
+                }
+                s->duration = 0;
+                break;
+            case 3:
+                /* We are looking for a burst of 2300Hz which is 100ms +- 5% long */
+                if (hit == 0)
+                {
+                    if (s->duration < ms_to_samples(70)  ||  s->duration > ms_to_samples(130))
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad initial 2300Hz tone duration\n");
+                        s->tone_state = 0;
+                    }
+                    else
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Received 2300Hz\n");
+                        if (s->callback)
+                            s->callback(s->callback_user_data, -1, 0, 0);
+                        s->tone_state = 4;
+                        /* Release the transmit side, and it will time the 250ms post tone delay */
+                        s->clear_to_send = TRUE;
+                        s->tries = 0;
+                        if (s->tx_digits_len)
+                            s->timer = ms_to_samples(3000);
+                    }
+                    s->in_tone = hit;
+                    s->duration = 0;
+                }
+                break;
+            case 4:
+                if (hit == 1)
+                {
+                    span_log(&s->logging, SPAN_LOG_FLOW, "Receiving kissoff\n");
+                    s->tone_state = 5;
+                    s->in_tone = hit;
+                    s->duration = 0;
+                }
+                break;
+            case 5:
+                if (hit == 0)
+                {
+                    s->busy = FALSE;
+                    if (s->duration < ms_to_samples(400)  ||  s->duration > ms_to_samples(1500))
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Bad kissoff duration %d\n", s->duration);
+                        if (++s->tries < 4)
+                        {
+                            dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len);
+                            s->timer = ms_to_samples(3000);
+                            s->tone_state = 4;
+                        }
+                        else
+                        {
+                            s->timer = 0;
+                            if (s->callback)
+                                s->callback(s->callback_user_data, FALSE, 0, 0);
+                        }
+                    }
+                    else
+                    {
+                        span_log(&s->logging, SPAN_LOG_FLOW, "Received good kissoff\n");
+                        s->clear_to_send = TRUE;
+                        s->tx_digits_len = 0;
+                        if (s->callback)
+                            s->callback(s->callback_user_data, TRUE, 0, 0);
+                        s->tone_state = 4;
+                        s->clear_to_send = TRUE;
+                        s->tries = 0;
+                        if (s->tx_digits_len)
+                            s->timer = ms_to_samples(3000);
+                    }
+                    s->in_tone = hit;
+                    s->duration = 0;
+                }
+                break;
+            }
+        }
+        s->last_hit = hit;
+        s->duration += GOERTZEL_SAMPLES_PER_BLOCK;
+        if (s->timer > 0)
+        {
+            s->timer -= GOERTZEL_SAMPLES_PER_BLOCK;
+            if (s->timer <= 0)
+            {
+                span_log(&s->logging, SPAN_LOG_FLOW, "Timer expired\n");
+                if (s->tone_state == 4  &&  s->tx_digits_len)
+                {
+                    if (++s->tries < 4)
+                    {
+                        dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len);
+                        s->timer = ms_to_samples(3000);
+                    }
+                    else
+                    {
+                        s->timer = 0;
+                        if (s->callback)
+                            s->callback(s->callback_user_data, FALSE, 0, 0);
+                    }
+                }
+            }
+        }
+        s->energy = 0;
+        s->current_sample = 0;
+    }
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_fillin(ademco_contactid_sender_state_t *s, int samples)
+{
+    /* Restart any Goertzel and energy gathering operation we might be in the middle of. */
+    goertzel_reset(&s->tone_1400);
+    goertzel_reset(&s->tone_2300);
+#if defined(SPANDSP_USE_FIXED_POINT)
+    s->energy = 0;
+#else
+    s->energy = 0.0f;
+#endif
+    s->current_sample = 0;
+    /* Don't update the hit detection. Pretend it never happened. */
+    /* TODO: Surely we can be cleverer than this. */
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_put(ademco_contactid_sender_state_t *s, const ademco_contactid_report_t *report)
+{
+    if (s->busy)
+        return -1;
+    if ((s->tx_digits_len = encode_msg(s->tx_digits, report)) < 0)
+        return -1;
+    s->busy = TRUE;
+    return dtmf_tx_put(&s->dtmf, s->tx_digits, s->tx_digits_len);
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(logging_state_t *) ademco_contactid_sender_get_logging_state(ademco_contactid_sender_state_t *s)
+{
+    return &s->logging;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) ademco_contactid_sender_set_realtime_callback(ademco_contactid_sender_state_t *s,
+                                                                 tone_report_func_t callback,
+                                                                 void *user_data)
+{
+    s->callback = callback;
+    s->callback_user_data = user_data;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(ademco_contactid_sender_state_t *) ademco_contactid_sender_init(ademco_contactid_sender_state_t *s,
+                                                                             tone_report_func_t callback,
+                                                                             void *user_data)
+{
+    static int initialised = FALSE;
+
+    if (s == NULL)
+    {
+        if ((s = (ademco_contactid_sender_state_t *) malloc(sizeof (*s))) == NULL)
+            return  NULL;
+    }
+    memset(s, 0, sizeof(*s));
+    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+    span_log_set_protocol(&s->logging, "Ademco");
+
+    if (!initialised)
+    {
+        make_goertzel_descriptor(&tone_1400_desc, 1400.0f, GOERTZEL_SAMPLES_PER_BLOCK);
+        make_goertzel_descriptor(&tone_2300_desc, 2300.0f, GOERTZEL_SAMPLES_PER_BLOCK);
+    }
+    goertzel_init(&s->tone_1400, &tone_1400_desc);
+    goertzel_init(&s->tone_2300, &tone_2300_desc);
+    s->current_sample = 0;
+
+    s->callback = callback;
+    s->callback_user_data = user_data;
+
+    s->step = 0;
+    s->remaining_samples = ms_to_samples(100);
+    dtmf_tx_init(&s->dtmf);
+    /* The specified timing is 50-60ms on, 50-60ms off */
+    dtmf_tx_set_timing(&s->dtmf, 55, 55);
+    return s;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_release(ademco_contactid_sender_state_t *s)
+{
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) ademco_contactid_sender_free(ademco_contactid_sender_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/libspandsp.2010.vcxproj b/libs/spandsp/src/libspandsp.2010.vcxproj
index bb2ae97878..393a1b6d45 100644
--- a/libs/spandsp/src/libspandsp.2010.vcxproj
+++ b/libs/spandsp/src/libspandsp.2010.vcxproj
@@ -155,6 +155,7 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="ademco_contactid.c" />
     <ClCompile Include="adsi.c" />
     <ClCompile Include="async.c" />
     <ClCompile Include="at_interpreter.c" />
@@ -239,6 +240,7 @@
     <ClCompile Include="msvc\gettimeofday.c" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="spandsp\ademco_contactid.h" />
     <ClInclude Include="spandsp\adsi.h" />
     <ClInclude Include="spandsp\async.h" />
     <ClInclude Include="spandsp\arctan2.h" />
@@ -279,6 +281,7 @@
     <ClInclude Include="spandsp\playout.h" />
     <ClInclude Include="spandsp\plc.h" />
     <ClInclude Include="spandsp\power_meter.h" />
+    <ClInclude Include="spandsp\private\ademco_contactid.h" />
     <ClInclude Include="spandsp\queue.h" />
     <ClInclude Include="spandsp\saturated.h" />
     <ClInclude Include="spandsp\schedule.h" />
diff --git a/libs/spandsp/src/libspandsp.2010.vcxproj.filters b/libs/spandsp/src/libspandsp.2010.vcxproj.filters
index 2a6d60a9d4..c75936a851 100644
--- a/libs/spandsp/src/libspandsp.2010.vcxproj.filters
+++ b/libs/spandsp/src/libspandsp.2010.vcxproj.filters
@@ -253,6 +253,12 @@
     <ClCompile Include="msvc\gettimeofday.c">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="timezone.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="ademco_contactid.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="spandsp\adsi.h">
@@ -660,6 +666,14 @@
     <ClInclude Include="spandsp.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="spandsp\t4_t6_decode.h" />
+    <ClInclude Include="spandsp\t4_t6_encode.h" />
+    <ClInclude Include="spandsp\ademco_contactid.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="spandsp\private\ademco_contactid.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="msvc\spandsp.h" />
diff --git a/libs/spandsp/src/msvc/spandsp.h b/libs/spandsp/src/msvc/spandsp.h
index 95ee8bf11b..ba0bc280d6 100644
--- a/libs/spandsp/src/msvc/spandsp.h
+++ b/libs/spandsp/src/msvc/spandsp.h
@@ -118,6 +118,7 @@
 #include <spandsp/t38_terminal.h>
 #include <spandsp/t31.h>
 #include <spandsp/adsi.h>
+#include <spandsp/ademco_contactid.h>
 #include <spandsp/oki_adpcm.h>
 #include <spandsp/ima_adpcm.h>
 #include <spandsp/g722.h>
diff --git a/libs/spandsp/src/sig_tone.c b/libs/spandsp/src/sig_tone.c
index 6aaecdb211..369218438c 100644
--- a/libs/spandsp/src/sig_tone.c
+++ b/libs/spandsp/src/sig_tone.c
@@ -647,9 +647,9 @@ SPAN_DECLARE(sig_tone_rx_state_t *) sig_tone_rx_init(sig_tone_rx_state_t *s, int
     }
     memset(s, 0, sizeof(*s));
 #if !defined(SPANDSP_USE_FIXED_POINT)
-    for (j = 0;  j < 2;  j++)
+    for (j = 0;  j < 3;  j++)
     {
-        for (i = 0;  i < 3;  i++)
+        for (i = 0;  i < 2;  i++)
         {
             s->tone[j].notch_z1[i] = 0.0f;
             s->tone[j].notch_z2[i] = 0.0f;
diff --git a/libs/spandsp/src/spandsp.h.in b/libs/spandsp/src/spandsp.h.in
index acb8a18ed5..e3c0d4f477 100644
--- a/libs/spandsp/src/spandsp.h.in
+++ b/libs/spandsp/src/spandsp.h.in
@@ -33,15 +33,19 @@
 
 @SPANDSP_USE_EXPORT_CAPABILITY@
 
+@SPANDSP_SUPPORT_T42@
+@SPANDSP_SUPPORT_T43@
+@SPANDSP_SUPPORT_T85@
+@SPANDSP_SUPPORT_V34@
+
 #include <stdlib.h>
 @INSERT_INTTYPES_HEADER@
 #include <string.h>
 #include <limits.h>
 #include <time.h>
 @INSERT_MATH_HEADER@
-#if !defined(SPANDSP_NO_TIFF)
 #include <tiffio.h>
-#endif
+
 #include <spandsp/telephony.h>
 #include <spandsp/fast_convert.h>
 #include <spandsp/logging.h>
@@ -106,6 +110,8 @@
 #include <spandsp/t4_t6_encode.h>
 /*#include <spandsp/t81_t82_arith_coding.h>*/
 /*#include <spandsp/t85.h>*/
+/*#include <spandsp/t42.h>*/
+/*#include <spandsp/t43.h>*/
 #include <spandsp/t30.h>
 #include <spandsp/t30_api.h>
 #include <spandsp/t30_fcf.h>
@@ -120,6 +126,7 @@
 #include <spandsp/t38_terminal.h>
 #include <spandsp/t31.h>
 #include <spandsp/adsi.h>
+#include <spandsp/ademco_contactid.h>
 #include <spandsp/oki_adpcm.h>
 #include <spandsp/ima_adpcm.h>
 #include <spandsp/g722.h>
diff --git a/libs/spandsp/src/spandsp/ademco_contactid.h b/libs/spandsp/src/spandsp/ademco_contactid.h
new file mode 100644
index 0000000000..8e42d70256
--- /dev/null
+++ b/libs/spandsp/src/spandsp/ademco_contactid.h
@@ -0,0 +1,365 @@
+/*
+ * SpanDSP  a series of DSP components for telephony
+ *
+ * ademco_contactid.h  Ademco ContactID alarm protocol
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_ADEMCO_CONTACTID_H_)
+#define _SPANDSP_ADEMCO_CONTACTID_H_
+
+enum
+{
+    ADEMCO_CONTACTID_MESSAGE_TYPE_18 = 0x18,
+    ADEMCO_CONTACTID_MESSAGE_TYPE_98 = 0x98
+};
+    
+enum
+{
+    ADEMCO_CONTACTID_QUALIFIER_NEW_EVENT = 1,
+    ADEMCO_CONTACTID_QUALIFIER_NEW_RESTORE = 3,
+    ADEMCO_CONTACTID_QUALIFIER_STATUS_REPORT = 6
+};
+
+enum
+{
+    ADEMCO_CONTACTID_DATA_IS_ZONE = 0,
+    ADEMCO_CONTACTID_DATA_IS_USER = 1
+};
+
+enum
+{
+    ADEMCO_CONTACTID_MEDICAL = 0x100,
+    ADEMCO_CONTACTID_PERSONAL_EMERGENCY = 0x101,
+    ADEMCO_CONTACTID_FAIL_TO_REPORT_IN = 0x102,
+    ADEMCO_CONTACTID_FIRE = 0x110,
+    ADEMCO_CONTACTID_SMOKE = 0x111,
+    ADEMCO_CONTACTID_COMBUSTION = 0x112,
+    ADEMCO_CONTACTID_WATER_FLOW = 0x113,
+    ADEMCO_CONTACTID_HEAT = 0x114,
+    ADEMCO_CONTACTID_PULL_STATION = 0x115,
+    ADEMCO_CONTACTID_DUCT = 0x116,
+    ADEMCO_CONTACTID_FLAME = 0x117,
+    ADEMCO_CONTACTID_NEAR_ALARM_A = 0x118,
+    ADEMCO_CONTACTID_PANIC = 0x120,
+    ADEMCO_CONTACTID_DURESS = 0x121,
+    ADEMCO_CONTACTID_SILENT = 0x122,
+    ADEMCO_CONTACTID_AUDIBLE = 0x123,
+    ADEMCO_CONTACTID_DURESS_ACCESS_GRANTED = 0x124,
+    ADEMCO_CONTACTID_DURESS_EGRESS_GRANTED = 0x125,
+    ADEMCO_CONTACTID_BURGLARY = 0x130,
+    ADEMCO_CONTACTID_PERIMETER = 0x131,
+    ADEMCO_CONTACTID_INTERIOR = 0x132,
+    ADEMCO_CONTACTID_24_HOUR_SAFE = 0x133,
+    ADEMCO_CONTACTID_ENTRY_EXIT = 0x134,
+    ADEMCO_CONTACTID_DAY_NIGHT = 0x135,
+    ADEMCO_CONTACTID_OUTDOOR = 0x136,
+    ADEMCO_CONTACTID_TAMPER = 0x137,
+    ADEMCO_CONTACTID_NEAR_ALARM_B = 0x138,
+    ADEMCO_CONTACTID_INTRUSION_VERIFIER = 0x139,
+    ADEMCO_CONTACTID_GENERAL_ALARM = 0x140,
+    ADEMCO_CONTACTID_POLLING_LOOP_OPEN_A = 0x141,
+    ADEMCO_CONTACTID_POLLING_LOOP_SHORT_A = 0x142,
+    ADEMCO_CONTACTID_EXPANSION_MODULE_FAILURE_A = 0x143,
+    ADEMCO_CONTACTID_SENSOR_TAMPER_A = 0x144,
+    ADEMCO_CONTACTID_EXPANSION_MODULE_TAMPER = 0x145,
+    ADEMCO_CONTACTID_SILENT_BURGLARY = 0x146,
+    ADEMCO_CONTACTID_SENSOR_SUPERVISION_FAILURE = 0x147,
+    ADEMCO_CONTACTID_24_HOUR_NONBURGLARY = 0x150,
+    ADEMCO_CONTACTID_GAS_DETECTED = 0x151,
+    ADEMCO_CONTACTID_REFRIGERATION = 0x152,
+    ADEMCO_CONTACTID_LOSS_OF_HEAT = 0x153,
+    ADEMCO_CONTACTID_WATER_LEAKAGE = 0x154,
+    ADEMCO_CONTACTID_FOIL_BREAK = 0x155,
+    ADEMCO_CONTACTID_DAY_TROUBLE = 0x156,
+    ADEMCO_CONTACTID_LOW_BOTTLED_GAS_LEVEL = 0x157,
+    ADEMCO_CONTACTID_HIGH_TEMP = 0x158,
+    ADEMCO_CONTACTID_LOW_TEMP = 0x159,
+    ADEMCO_CONTACTID_LOSS_OF_AIR_FLOW = 0x161,
+    ADEMCO_CONTACTID_CARBON_MONOXIDE_DETECTED = 0x162,
+    ADEMCO_CONTACTID_TANK_LEVEL = 0x163,
+    ADEMCO_CONTACTID_FIRE_SUPERVISORY = 0x200,
+    ADEMCO_CONTACTID_LOW_WATER_PRESSURE = 0x201,
+    ADEMCO_CONTACTID_LOW_CO2 = 0x202,
+    ADEMCO_CONTACTID_GATE_VALVE_SENSOR = 0x203,
+    ADEMCO_CONTACTID_LOW_WATER_LEVEL = 0x204,
+    ADEMCO_CONTACTID_PUMP_ACTIVATED = 0x205,
+    ADEMCO_CONTACTID_PUMP_FAILURE = 0x206,
+    ADEMCO_CONTACTID_SYSTEM_TROUBLE = 0x300,
+    ADEMCO_CONTACTID_AC_LOSS = 0x301,
+    ADEMCO_CONTACTID_LOW_SYSTEM_BATTERY = 0x302,
+    ADEMCO_CONTACTID_RAM_CHECKSUM_BAD = 0x303,
+    ADEMCO_CONTACTID_ROM_CHECKSUM_BAD = 0x304,
+    ADEMCO_CONTACTID_SYSTEM_RESET = 0x305,
+    ADEMCO_CONTACTID_PANEL_PROGRAMMING_CHANGED = 0x306,
+    ADEMCO_CONTACTID_SELFTEST_FAILURE = 0x307,
+    ADEMCO_CONTACTID_SYSTEM_SHUTDOWN = 0x308,
+    ADEMCO_CONTACTID_BATTERY_TEST_FAILURE = 0x309,
+    ADEMCO_CONTACTID_GROUND_FAULT = 0x310,
+    ADEMCO_CONTACTID_BATTERY_MISSING_DEAD = 0x311,
+    ADEMCO_CONTACTID_POWER_SUPPLY_OVERCURRENT = 0x312,
+    ADEMCO_CONTACTID_ENGINEER_RESET = 0x313,
+    ADEMCO_CONTACTID_SOUNDER_RELAY = 0x320,
+    ADEMCO_CONTACTID_BELL_1 = 0x321,
+    ADEMCO_CONTACTID_BELL_2 = 0x322,
+    ADEMCO_CONTACTID_ALARM_RELAY = 0x323,
+    ADEMCO_CONTACTID_TROUBLE_RELAY = 0x324,
+    ADEMCO_CONTACTID_REVERSING_RELAY = 0x325,
+    ADEMCO_CONTACTID_NOTIFICATION_APPLIANCE_CKT_3 = 0x326,
+    ADEMCO_CONTACTID_NOTIFICATION_APPLIANCE_CKT_4 = 0x327,
+    ADEMCO_CONTACTID_SYSTEM_PERIPHERAL_TROUBLE = 0x330,
+    ADEMCO_CONTACTID_POLLING_LOOP_OPEN_B = 0x331,
+    ADEMCO_CONTACTID_POLLING_LOOP_SHORT_B = 0x332,
+    ADEMCO_CONTACTID_EXPANSION_MODULE_FAILURE_B = 0x333,
+    ADEMCO_CONTACTID_REPEATER_FAILURE = 0x334,
+    ADEMCO_CONTACTID_LOCAL_PRINTER_OUT_OF_PAPER = 0x335,
+    ADEMCO_CONTACTID_LOCAL_PRINTER_FAILURE = 0x336,
+    ADEMCO_CONTACTID_EXP_MODULE_DC_LOSS = 0x337,
+    ADEMCO_CONTACTID_EXP_MODULE_LOW_BATTERY = 0x338,
+    ADEMCO_CONTACTID_EXP_MODULE_RESET = 0x339,
+    ADEMCO_CONTACTID_EXP_MODULE_TAMPER = 0x341,
+    ADEMCO_CONTACTID_EXP_MODULE_AC_LOSS = 0x342,
+    ADEMCO_CONTACTID_EXP_MODULE_SELFTEST_FAIL = 0x343,
+    ADEMCO_CONTACTID_RF_RECEIVER_JAM_DETECT = 0x344,
+    ADEMCO_CONTACTID_COMMUNICATION_TROUBLE = 0x350,
+    ADEMCO_CONTACTID_TELCO_1_FAULT = 0x351,
+    ADEMCO_CONTACTID_TELCO_2_FAULT = 0x352,
+    ADEMCO_CONTACTID_LONG_RANGE_RADIO_TRANSMITTER_FAULT = 0x353,
+    ADEMCO_CONTACTID_FAILURE_TO_COMMUNICATE_EVENT = 0x354,
+    ADEMCO_CONTACTID_LOSS_OF_RADIO_SUPERVISION = 0x355,
+    ADEMCO_CONTACTID_LOSS_OF_CENTRAL_POLLING = 0x356,
+    ADEMCO_CONTACTID_LONG_RANGE_RADIO_VSWR_PROBLEM = 0x357,
+    ADEMCO_CONTACTID_PROTECTION_LOOP = 0x370,
+    ADEMCO_CONTACTID_PROTECTION_LOOP_OPEN = 0x371,
+    ADEMCO_CONTACTID_PROTECTION_LOOP_SHORT = 0x372,
+    ADEMCO_CONTACTID_FIRE_TROUBLE = 0x373,
+    ADEMCO_CONTACTID_EXIT_ERROR_ALARM_ZONE = 0x374,
+    ADEMCO_CONTACTID_PANIC_ZONE_TROUBLE = 0x375,
+    ADEMCO_CONTACTID_HOLDUP_ZONE_TROUBLE = 0x376,
+    ADEMCO_CONTACTID_SWINGER_TROUBLE = 0x377,
+    ADEMCO_CONTACTID_CROSSZONE_TROUBLE = 0x378,
+    ADEMCO_CONTACTID_SENSOR_TROUBLE = 0x380,
+    ADEMCO_CONTACTID_LOSS_OF_SUPERVISION__RF = 0x381,
+    ADEMCO_CONTACTID_LOSS_OF_SUPERVISION__RPM = 0x382,
+    ADEMCO_CONTACTID_SENSOR_TAMPER_B = 0x383,
+    ADEMCO_CONTACTID_RF_LOW_BATTERY = 0x384,
+    ADEMCO_CONTACTID_SMOKE_DETECTOR_HIGH_SENSITIVITY = 0x385,
+    ADEMCO_CONTACTID_SMOKE_DETECTOR_LOW_SENSITIVITY = 0x386,
+    ADEMCO_CONTACTID_INTRUSION_DETECTOR_HIGH_SENSITIVITY = 0x387,
+    ADEMCO_CONTACTID_INTRUSION_DETECTOR_LOW_SENSITIVITY = 0x388,
+    ADEMCO_CONTACTID_SENSOR_SELFTEST_FAILURE = 0x389,
+    ADEMCO_CONTACTID_SENSOR_WATCH_TROUBLE = 0x391,
+    ADEMCO_CONTACTID_DRIFT_COMPENSATION_ERROR = 0x392,
+    ADEMCO_CONTACTID_MAINTENANCE_ALERT = 0x393,
+    ADEMCO_CONTACTID_OPEN_CLOSE = 0x400,
+    ADEMCO_CONTACTID_OC_BY_USER = 0x401,
+    ADEMCO_CONTACTID_GROUP_OC = 0x402,
+    ADEMCO_CONTACTID_AUTOMATIC_OC = 0x403,
+    ADEMCO_CONTACTID_LATE_TO_OC = 0x404,
+    ADEMCO_CONTACTID_DEFERRED_OC = 0x405,
+    ADEMCO_CONTACTID_CANCEL = 0x406,
+    ADEMCO_CONTACTID_REMOTE_ARM_DISARM = 0x407,
+    ADEMCO_CONTACTID_QUICK_ARM = 0x408,
+    ADEMCO_CONTACTID_KEYSWITCH_OC = 0x409,
+    ADEMCO_CONTACTID_ARMED_STAY = 0x441,
+    ADEMCO_CONTACTID_KEYSWITCH_ARMED_STAY = 0x442,
+    ADEMCO_CONTACTID_EXCEPTION_OC = 0x450,
+    ADEMCO_CONTACTID_EARLY_OC = 0x451,
+    ADEMCO_CONTACTID_LATE_OC = 0x452,
+    ADEMCO_CONTACTID_FAILED_TO_OPEN = 0x453,
+    ADEMCO_CONTACTID_FAILED_TO_CLOSE = 0x454,
+    ADEMCO_CONTACTID_AUTOARM_FAILED = 0x455,
+    ADEMCO_CONTACTID_PARTIAL_ARM = 0x456,
+    ADEMCO_CONTACTID_EXIT_ERROR_USER = 0x457,
+    ADEMCO_CONTACTID_USER_ON_PREMISES = 0x458,
+    ADEMCO_CONTACTID_RECENT_CLOSE = 0x459,
+    ADEMCO_CONTACTID_WRONG_CODE_ENTRY = 0x461,
+    ADEMCO_CONTACTID_LEGAL_CODE_ENTRY = 0x462,
+    ADEMCO_CONTACTID_REARM_AFTER_ALARM = 0x463,
+    ADEMCO_CONTACTID_AUTOARM_TIME_EXTENDED = 0x464,
+    ADEMCO_CONTACTID_PANIC_ALARM_RESET = 0x465,
+    ADEMCO_CONTACTID_SERVICE_ON_OFF_PREMISES = 0x466,
+    ADEMCO_CONTACTID_CALLBACK_REQUEST_MADE = 0x411,
+    ADEMCO_CONTACTID_SUCCESSFUL_DOWNLOAD_ACCESS = 0x412,
+    ADEMCO_CONTACTID_UNSUCCESSFUL_ACCESS = 0x413,
+    ADEMCO_CONTACTID_SYSTEM_SHUTDOWN_COMMAND_RECEIVED = 0x414,
+    ADEMCO_CONTACTID_DIALER_SHUTDOWN_COMMAND_RECEIVED = 0x415,
+    ADEMCO_CONTACTID_SUCCESSFUL_UPLOAD = 0x416,
+    ADEMCO_CONTACTID_ACCESS_DENIED = 0x421,
+    ADEMCO_CONTACTID_ACCESS_REPORT_BY_USER = 0x422,
+    ADEMCO_CONTACTID_FORCED_ACCESS = 0x423,
+    ADEMCO_CONTACTID_EGRESS_DENIED = 0x424,
+    ADEMCO_CONTACTID_EGRESS_GRANTED = 0x425,
+    ADEMCO_CONTACTID_ACCESS_DOOR_PROPPED_OPEN = 0x426,
+    ADEMCO_CONTACTID_ACCESS_POINT_DOOR_STATUS_MONITOR_TROUBLE = 0x427,
+    ADEMCO_CONTACTID_ACCESS_POINT_REQUEST_TO_EXIT_TROUBLE = 0x428,
+    ADEMCO_CONTACTID_ACCESS_PROGRAM_MODE_ENTRY = 0x429,
+    ADEMCO_CONTACTID_ACCESS_PROGRAM_MODE_EXIT = 0x430,
+    ADEMCO_CONTACTID_ACCESS_THREAT_LEVEL_CHANGE = 0x431,
+    ADEMCO_CONTACTID_ACCESS_RELAY_TRIGGER_FAIL = 0x432,
+    ADEMCO_CONTACTID_ACCESS_RTE_SHUNT = 0x433,
+    ADEMCO_CONTACTID_ACCESS_DSM_SHUNT = 0x434,
+    ADEMCO_CONTACTID_ACCESS_READER_DISABLE = 0x501,
+    ADEMCO_CONTACTID_SOUNDER_RELAY_DISABLE = 0x520,
+    ADEMCO_CONTACTID_BELL_1_DISABLE = 0x521,
+    ADEMCO_CONTACTID_BELL_2_DISABLE = 0x522,
+    ADEMCO_CONTACTID_ALARM_RELAY_DISABLE = 0x523,
+    ADEMCO_CONTACTID_TROUBLE_RELAY_DISABLE = 0x524,
+    ADEMCO_CONTACTID_REVERSING_RELAY_DISABLE = 0x525,
+    ADEMCO_CONTACTID_NOTIFICATION_APPLIANCE_CKT_3_DISABLE = 0x526,
+    ADEMCO_CONTACTID_NOTIFICATION_APPLIANCE_CKT_4_DISABLE = 0x527,
+    ADEMCO_CONTACTID_MODULE_ADDED = 0x531,
+    ADEMCO_CONTACTID_MODULE_REMOVED = 0x532,
+    ADEMCO_CONTACTID_DIALER_DISABLED = 0x551,
+    ADEMCO_CONTACTID_RADIO_TRANSMITTER_DISABLED = 0x552,
+    ADEMCO_CONTACTID_REMOTE_UPLOAD_DOWNLOAD_DISABLED = 0x553,
+    ADEMCO_CONTACTID_ZONE_SENSOR_BYPASS = 0x570,
+    ADEMCO_CONTACTID_FIRE_BYPASS = 0x571,
+    ADEMCO_CONTACTID_24_HOUR_ZONE_BYPASS = 0x572,
+    ADEMCO_CONTACTID_BURG_BYPASS = 0x573,
+    ADEMCO_CONTACTID_GROUP_BYPASS = 0x574,
+    ADEMCO_CONTACTID_SWINGER_BYPASS = 0x575,
+    ADEMCO_CONTACTID_ACCESS_ZONE_SHUNT = 0x576,
+    ADEMCO_CONTACTID_ACCESS_POINT_BYPASS = 0x577,
+    ADEMCO_CONTACTID_MANUAL_TRIGGER_TEST_REPORT = 0x601,
+    ADEMCO_CONTACTID_PERIODIC_TEST_REPORT = 0x602,
+    ADEMCO_CONTACTID_PERIODIC_RF_TRANSMISSION = 0x603,
+    ADEMCO_CONTACTID_FIRE_TEST = 0x604,
+    ADEMCO_CONTACTID_STATUS_REPORT_TO_FOLLOW = 0x605,
+    ADEMCO_CONTACTID_LISTENIN_TO_FOLLOW = 0x606,
+    ADEMCO_CONTACTID_WALK_TEST_MODE = 0x607,
+    ADEMCO_CONTACTID_PERIODIC_TEST__SYSTEM_TROUBLE_PRESENT = 0x608,
+    ADEMCO_CONTACTID_VIDEO_TRANSMITTER_ACTIVE = 0x609,
+    ADEMCO_CONTACTID_POINT_TESTED_OK = 0x611,
+    ADEMCO_CONTACTID_POINT_NOT_TESTED = 0x612,
+    ADEMCO_CONTACTID_INTRUSION_ZONE_WALK_TESTED = 0x613,
+    ADEMCO_CONTACTID_FIRE_ZONE_WALK_TESTED = 0x614,
+    ADEMCO_CONTACTID_PANIC_ZONE_WALK_TESTED = 0x615,
+    ADEMCO_CONTACTID_SERVICE_REQUEST = 0x616,
+    ADEMCO_CONTACTID_EVENT_LOG_RESET = 0x621,
+    ADEMCO_CONTACTID_EVENT_LOG_50PC_FULL = 0x622,
+    ADEMCO_CONTACTID_EVENT_LOG_90PC_FULL = 0x623,
+    ADEMCO_CONTACTID_EVENT_LOG_OVERFLOW = 0x624,
+    ADEMCO_CONTACTID_TIME_DATE_RESET = 0x625,
+    ADEMCO_CONTACTID_TIME_DATE_INACCURATE = 0x626,
+    ADEMCO_CONTACTID_PROGRAM_MODE_ENTRY = 0x627,
+    ADEMCO_CONTACTID_PROGRAM_MODE_EXIT = 0x628,
+    ADEMCO_CONTACTID_32_HOUR_EVENT_LOG_MARKER = 0x629,
+    ADEMCO_CONTACTID_SCHEDULE_CHANGE = 0x630,
+    ADEMCO_CONTACTID_EXCEPTION_SCHEDULE_CHANGE = 0x631,
+    ADEMCO_CONTACTID_ACCESS_SCHEDULE_CHANGE = 0x632,
+    ADEMCO_CONTACTID_SENIOR_WATCH_TROUBLE = 0x641,
+    ADEMCO_CONTACTID_LATCHKEY_SUPERVISION = 0x642,
+    ADEMCO_CONTACTID_RESERVED_FOR_ADEMCO_USE_1 = 0x651,
+    ADEMCO_CONTACTID_RESERVED_FOR_ADEMCO_USE_2 = 0x652,
+    ADEMCO_CONTACTID_RESERVED_FOR_ADEMCO_USE_3 = 0x653,
+    ADEMCO_CONTACTID_SYSTEM_INACTIVITY = 0x654,
+    ADEMCO_CONTACTID_DOWNLOAD_ABORT = 0x900,
+    ADEMCO_CONTACTID_DOWNLOAD_START_END = 0x901,
+    ADEMCO_CONTACTID_DOWNLOAD_INTERRUPTED = 0x902,
+    ADEMCO_CONTACTID_AUTOCLOSE_WITH_BYPASS = 0x910,
+    ADEMCO_CONTACTID_BYPASS_CLOSING = 0x911,
+    ADEMCO_CONTACTID_32_HOUR_NO_READ_OF_EVENT_LOG = 0x999
+};
+
+typedef struct ademco_contactid_sender_state_s ademco_contactid_sender_state_t;
+
+typedef struct ademco_contactid_receiver_state_s ademco_contactid_receiver_state_t;
+
+typedef struct
+{
+    int acct;
+    int mt;
+    int q;
+    int xyz;
+    int gg;
+    int ccc;
+} ademco_contactid_report_t;
+
+typedef void (*ademco_contactid_report_func_t)(void *user_data, const ademco_contactid_report_t *report);
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+SPAN_DECLARE(const char *) ademco_contactid_msg_qualifier_to_str(int q);
+
+SPAN_DECLARE(const char *) ademco_contactid_event_to_str(int xyz);
+
+SPAN_DECLARE(int) encode_msg(char buf[], const ademco_contactid_report_t *report);
+
+SPAN_DECLARE(int) decode_msg(ademco_contactid_report_t *report, const char buf[]);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_log_msg(ademco_contactid_receiver_state_t *s, const ademco_contactid_report_t *report);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_tx(ademco_contactid_receiver_state_t *s, int16_t amp[], int max_samples);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_rx(ademco_contactid_receiver_state_t *s, const int16_t amp[], int samples);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_fillin(ademco_contactid_receiver_state_t *s, int samples);
+
+SPAN_DECLARE(logging_state_t *) ademco_contactid_receiver_get_logging_state(ademco_contactid_receiver_state_t *s);
+
+SPAN_DECLARE(void) ademco_contactid_receiver_set_realtime_callback(ademco_contactid_receiver_state_t *s,
+                                                                   ademco_contactid_report_func_t callback,
+                                                                   void *user_data);
+
+SPAN_DECLARE(ademco_contactid_receiver_state_t *) ademco_contactid_receiver_init(ademco_contactid_receiver_state_t *s,
+                                                                                 ademco_contactid_report_func_t callback,
+                                                                                 void *user_data);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_release(ademco_contactid_receiver_state_t *s);
+
+SPAN_DECLARE(int) ademco_contactid_receiver_free(ademco_contactid_receiver_state_t *s);
+
+
+
+SPAN_DECLARE(int) ademco_contactid_sender_tx(ademco_contactid_sender_state_t *s, int16_t amp[], int max_samples);
+
+SPAN_DECLARE(int) ademco_contactid_sender_rx(ademco_contactid_sender_state_t *s, const int16_t amp[], int samples);
+
+SPAN_DECLARE(int) ademco_contactid_sender_fillin(ademco_contactid_sender_state_t *s, int samples);
+
+SPAN_DECLARE(int) ademco_contactid_sender_put(ademco_contactid_sender_state_t *s, const ademco_contactid_report_t *report);
+
+SPAN_DECLARE(logging_state_t *) ademco_contactid_sender_get_logging_state(ademco_contactid_sender_state_t *s);
+
+SPAN_DECLARE(void) ademco_contactid_sender_set_realtime_callback(ademco_contactid_sender_state_t *s,
+                                                                 tone_report_func_t callback,
+                                                                 void *user_data);
+
+SPAN_DECLARE(ademco_contactid_sender_state_t *) ademco_contactid_sender_init(ademco_contactid_sender_state_t *s,
+                                                                             tone_report_func_t callback,
+                                                                             void *user_data);
+
+SPAN_DECLARE(int) ademco_contactid_sender_release(ademco_contactid_sender_state_t *s);
+
+SPAN_DECLARE(int) ademco_contactid_sender_free(ademco_contactid_sender_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/expose.h b/libs/spandsp/src/spandsp/expose.h
index ac10084535..75c16f2405 100644
--- a/libs/spandsp/src/spandsp/expose.h
+++ b/libs/spandsp/src/spandsp/expose.h
@@ -93,6 +93,7 @@
 #include <spandsp/private/t31.h>
 #include <spandsp/private/v18.h>
 #include <spandsp/private/adsi.h>
+#include <spandsp/private/ademco_contactid.h>
 
 #endif
 /*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/spandsp/private/ademco_contactid.h b/libs/spandsp/src/spandsp/private/ademco_contactid.h
new file mode 100644
index 0000000000..bada9193a1
--- /dev/null
+++ b/libs/spandsp/src/spandsp/private/ademco_contactid.h
@@ -0,0 +1,95 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/ademco_contactid.h - Ademco ContactID alarm protocol
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \file */
+
+#if !defined(_SPANDSP_PRIVATE_ADEMCO_CONTACTID_H_)
+#define _SPANDSP_PRIVATE_ADEMCO_CONTACTID_H_
+
+struct ademco_contactid_receiver_state_s
+{
+    ademco_contactid_report_func_t callback;
+    void *callback_user_data;
+
+    int step;
+    int remaining_samples;
+    uint32_t tone_phase;
+    int32_t tone_phase_rate;
+    int16_t tone_level;
+    dtmf_rx_state_t dtmf;
+
+    char rx_digits[16 + 1];
+    int rx_digits_len;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+struct ademco_contactid_sender_state_s
+{
+    tone_report_func_t callback;
+    void *callback_user_data;
+
+    int step;
+    int remaining_samples;
+
+    dtmf_tx_state_t dtmf;
+#if defined(SPANDSP_USE_FIXED_POINT)
+    /*! Minimum acceptable tone level for detection. */
+    int32_t threshold;
+    /*! The accumlating total energy on the same period over which the Goertzels work. */
+    int32_t energy;
+#else
+    /*! Minimum acceptable tone level for detection. */
+    float threshold;
+    /*! The accumlating total energy on the same period over which the Goertzels work. */
+    float energy;
+#endif
+    goertzel_state_t tone_1400;
+    goertzel_state_t tone_2300;
+    /*! The current sample number within a processing block. */
+    int current_sample;
+
+    /*! \brief A buffer to save the sent message, in case we need to retry. */
+    char tx_digits[16 + 1];
+    int tx_digits_len;
+    /*! \brief The number of consecutive retries. */
+    int tries;
+    
+    int tone_state;
+    int duration;
+    int last_hit;
+    int in_tone;
+    int clear_to_send;
+    int timer;
+
+    int busy;
+
+    /*! \brief Error and flow logging control */
+    logging_state_t logging;
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/src/t30.c b/libs/spandsp/src/t30.c
index 68a5c901c3..92564fcb0e 100644
--- a/libs/spandsp/src/t30.c
+++ b/libs/spandsp/src/t30.c
@@ -2534,7 +2534,7 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
            we sent - which would have been a T30_MCF - If the block is for the previous
            page, or the previous block of the current page, we can assume we have hit this
            condition. */
-        if (((s->rx_page_number & 0xFF) == page  &&  (s->ecm_block & 0xFF) == block)
+        if (((s->rx_page_number & 0xFF) == page  &&  ((s->ecm_block - 1) & 0xFF) == block)
             ||
             (((s->rx_page_number - 1) & 0xFF) == page  &&  s->ecm_block == 0))
         {
@@ -2568,7 +2568,6 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
         for (j = 0;  j < 8;  j++)
         {
             frame_no = (i << 3) + j;
-#if defined(VET_ALL_FCD_FRAMES)
             if (s->ecm_len[frame_no] >= 0)
             {
                 /* The correct pattern of frame lengths is they will all be 64 or 256 octets long, except the
@@ -2598,7 +2597,6 @@ static int process_rx_pps(t30_state_t *s, const uint8_t *msg, int len)
                     }
                 }
             }            
-#endif
             if (s->ecm_len[frame_no] < 0)
             {
                 s->ecm_frame_map[i + 3] |= (1 << j);
diff --git a/libs/spandsp/src/t38_gateway.c b/libs/spandsp/src/t38_gateway.c
index 03552efc41..85022b8d54 100644
--- a/libs/spandsp/src/t38_gateway.c
+++ b/libs/spandsp/src/t38_gateway.c
@@ -383,7 +383,6 @@ static void hdlc_underflow_handler(void *user_data)
 {
     t38_gateway_state_t *s;
     t38_gateway_hdlc_state_t *t;
-    int old_data_type;
 
     s = (t38_gateway_state_t *) user_data;
     t = &s->core.hdlc_to_modem;
@@ -392,7 +391,6 @@ static void hdlc_underflow_handler(void *user_data)
        underflow must be an end of preamble condition. */
     if ((t->buf[t->out].flags & HDLC_FLAG_PROCEED_WITH_OUTPUT))
     {
-        old_data_type = t->buf[t->out].contents;
         t->buf[t->out].len = 0;
         t->buf[t->out].flags = 0;
         t->buf[t->out].contents = 0;
@@ -2117,6 +2115,7 @@ static void t38_hdlc_rx_put_bit(hdlc_rx_state_t *t, int new_bit)
 
 static int restart_rx_modem(t38_gateway_state_t *s)
 {
+    fax_modems_state_t *t;
     put_bit_func_t put_bit_func;
     void *put_bit_user_data;
 
@@ -2138,19 +2137,20 @@ static int restart_rx_modem(t38_gateway_state_t *s)
              s->core.short_train,
              s->core.ecm_mode);
 
-    hdlc_rx_init(&(s->audio.modems.hdlc_rx), FALSE, TRUE, HDLC_FRAMING_OK_THRESHOLD, NULL, s);
-    s->audio.modems.rx_signal_present = FALSE;
-    s->audio.modems.rx_trained = FALSE;
+    t = &s->audio.modems;
+    hdlc_rx_init(&t->hdlc_rx, FALSE, TRUE, HDLC_FRAMING_OK_THRESHOLD, NULL, s);
+    t->rx_signal_present = FALSE;
+    t->rx_trained = FALSE;
     /* Default to the transmit data being V.21, unless a faster modem pops up trained. */
     s->t38x.current_tx_data_type = T38_DATA_V21;
-    fsk_rx_init(&(s->audio.modems.v21_rx), &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) t38_hdlc_rx_put_bit, &(s->audio.modems.hdlc_rx));
+    fsk_rx_init(&t->v21_rx, &preset_fsk_specs[FSK_V21CH2], FSK_FRAME_MODE_SYNC, (put_bit_func_t) t38_hdlc_rx_put_bit, &t->hdlc_rx);
 #if 0
-    fsk_rx_signal_cutoff(&(s->audio.modems.v21_rx), -45.5f);
+    fsk_rx_signal_cutoff(&t->v21_rx, -45.5f);
 #endif
     if (s->core.image_data_mode  &&  s->core.ecm_mode)
     {
         put_bit_func = (put_bit_func_t) t38_hdlc_rx_put_bit;
-        put_bit_user_data = (void *) &(s->audio.modems.hdlc_rx);
+        put_bit_user_data = (void *) &t->hdlc_rx;
     }
     else
     {
@@ -2166,26 +2166,26 @@ static int restart_rx_modem(t38_gateway_state_t *s)
     s->core.to_t38.octets_per_data_packet = 1;
     switch (s->core.fast_rx_modem)
     {
-    case FAX_MODEM_V17_RX:
-        v17_rx_restart(&s->audio.modems.fast_modems.v17_rx, s->core.fast_bit_rate, s->core.short_train);
-        v17_rx_set_put_bit(&s->audio.modems.fast_modems.v17_rx, put_bit_func, put_bit_user_data);
-        set_rx_handler(s, &v17_v21_rx, &v17_v21_rx_fillin, s);
-        s->core.fast_rx_active = FAX_MODEM_V17_RX;
-        break;
     case FAX_MODEM_V27TER_RX:
-        v27ter_rx_restart(&s->audio.modems.fast_modems.v27ter_rx, s->core.fast_bit_rate, FALSE);
-        v27ter_rx_set_put_bit(&s->audio.modems.fast_modems.v27ter_rx, put_bit_func, put_bit_user_data);
+        v27ter_rx_restart(&t->fast_modems.v27ter_rx, s->core.fast_bit_rate, FALSE);
+        v27ter_rx_set_put_bit(&t->fast_modems.v27ter_rx, put_bit_func, put_bit_user_data);
         set_rx_handler(s, &v27ter_v21_rx, &v27ter_v21_rx_fillin, s);
         s->core.fast_rx_active = FAX_MODEM_V27TER_RX;
         break;
     case FAX_MODEM_V29_RX:
-        v29_rx_restart(&s->audio.modems.fast_modems.v29_rx, s->core.fast_bit_rate, FALSE);
-        v29_rx_set_put_bit(&s->audio.modems.fast_modems.v29_rx, put_bit_func, put_bit_user_data);
+        v29_rx_restart(&t->fast_modems.v29_rx, s->core.fast_bit_rate, FALSE);
+        v29_rx_set_put_bit(&t->fast_modems.v29_rx, put_bit_func, put_bit_user_data);
         set_rx_handler(s, &v29_v21_rx, &v29_v21_rx_fillin, s);
         s->core.fast_rx_active = FAX_MODEM_V29_RX;
         break;
+    case FAX_MODEM_V17_RX:
+        v17_rx_restart(&t->fast_modems.v17_rx, s->core.fast_bit_rate, s->core.short_train);
+        v17_rx_set_put_bit(&t->fast_modems.v17_rx, put_bit_func, put_bit_user_data);
+        set_rx_handler(s, &v17_v21_rx, &v17_v21_rx_fillin, s);
+        s->core.fast_rx_active = FAX_MODEM_V17_RX;
+        break;
     default:
-        set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &(s->audio.modems.v21_rx));
+        set_rx_handler(s, (span_rx_handler_t *) &fsk_rx, (span_rx_fillin_handler_t *) &fsk_rx_fillin, &t->v21_rx);
         s->core.fast_rx_active = FAX_MODEM_NONE;
         break;
     }
diff --git a/libs/spandsp/src/t38_terminal.c b/libs/spandsp/src/t38_terminal.c
index 1a2c3eca2e..1efa1b09d6 100644
--- a/libs/spandsp/src/t38_terminal.c
+++ b/libs/spandsp/src/t38_terminal.c
@@ -152,9 +152,12 @@ enum
     T38_TIMED_STEP_NO_SIGNAL = 0x60
 };
 
-static __inline__ void front_end_status(t38_terminal_state_t *s, int status)
+static __inline__ int front_end_status(t38_terminal_state_t *s, int status)
 {
     t30_front_end_status(&s->t30, status);
+    if (s->t38_fe.timed_step == T38_TIMED_STEP_NONE)
+        return -1;
+    return 0;
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -749,6 +752,9 @@ static int stream_non_ecm(t38_terminal_state_t *s)
                contain data. Hopefully, following the current spec will not cause compatibility
                issues. */
             len = t30_non_ecm_get_chunk(&s->t30, buf, fe->octets_per_data_packet);
+            if (len < 0)
+                return -1;
+            /*endif*/
             if (len > 0)
                 bit_reverse(buf, buf, len);
             /*endif*/
@@ -776,7 +782,8 @@ static int stream_non_ecm(t38_terminal_state_t *s)
                         return res;
                     /*endif*/
                     fe->timed_step = T38_TIMED_STEP_NON_ECM_MODEM_5;
-                    front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                    if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                        return -1;
                     break;
                 }
                 /*endif*/
@@ -806,7 +813,8 @@ static int stream_non_ecm(t38_terminal_state_t *s)
                 if (fe->us_per_tx_chunk)
                     delay = bits_to_us(s, 8*len) + 60000;
                 /*endif*/
-                front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                    return -1;
                 break;
             }
             /*endif*/
@@ -910,7 +918,8 @@ static int stream_hdlc(t38_terminal_state_t *s)
                     previous = fe->current_tx_data_type;
                     fe->hdlc_tx.ptr = 0;
                     fe->hdlc_tx.len = 0;
-                    front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                    if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                        return -1;
                     /* The above step should have got the next HDLC step ready - either another frame, or an instruction to stop transmission. */
                     if (fe->hdlc_tx.len >= 0)
                     {
@@ -940,7 +949,8 @@ static int stream_hdlc(t38_terminal_state_t *s)
                         if (fe->us_per_tx_chunk)
                             delay += 100000;
                         /*endif*/
-                        front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                        if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                            return -1;
                     }
                     /*endif*/
                     break;
@@ -969,7 +979,8 @@ static int stream_hdlc(t38_terminal_state_t *s)
             previous = fe->current_tx_data_type;
             fe->hdlc_tx.ptr = 0;
             fe->hdlc_tx.len = 0;
-            front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+            if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                return -1;
             /* The above step should have got the next HDLC step ready - either another frame, or an instruction to stop transmission. */
             if (fe->hdlc_tx.len >= 0)
             {
@@ -1003,7 +1014,8 @@ static int stream_hdlc(t38_terminal_state_t *s)
                 if (fe->us_per_tx_chunk)
                     delay += 100000;
                 /*endif*/
-                front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+                if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                    return -1;
             }
             /*endif*/
             break;
@@ -1059,7 +1071,8 @@ static int stream_ced(t38_terminal_state_t *s)
         case T38_TIMED_STEP_CED_3:
             /* End of CED */
             fe->timed_step = fe->queued_timed_step;
-            front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE);
+            if (front_end_status(s, T30_FRONT_END_SEND_STEP_COMPLETE) < 0)
+                return -1;
             return 0;
         }
         /*endswitch*/
diff --git a/libs/spandsp/src/timezone.c b/libs/spandsp/src/timezone.c
index 04dfa51daf..79966f1d4c 100644
--- a/libs/spandsp/src/timezone.c
+++ b/libs/spandsp/src/timezone.c
@@ -112,9 +112,11 @@ static const int year_lengths[2] =
     DAYS_PER_LEAP_YEAR
 };
 
-static int increment_overflow(int *number, int delta)
+static int add_with_overflow_detection(int *number, int delta)
 {
-    int last_number;
+    /* This needs to be considered volatile, or clever optimisation destroys
+       the effect of the the rollover detection logic */
+    volatile int last_number;
 
     last_number = *number;
     *number += delta;
@@ -209,7 +211,7 @@ static struct tm *time_sub(const time_t * const timep, const long int offset, co
         if (idelta == 0)
             idelta = (tdays < 0)  ?  -1  :  1;
         newy = y;
-        if (increment_overflow(&newy, idelta))
+        if (add_with_overflow_detection(&newy, idelta))
             return NULL;
         leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1);
         tdays -= ((time_t) newy - y)*DAYS_PER_NON_LEAP_YEAR;
@@ -234,18 +236,18 @@ static struct tm *time_sub(const time_t * const timep, const long int offset, co
     }
     while (idays < 0)
     {
-        if (increment_overflow(&y, -1))
+        if (add_with_overflow_detection(&y, -1))
             return NULL;
         idays += year_lengths[isleap(y)];
     }
     while (idays >= year_lengths[isleap(y)])
     {
         idays -= year_lengths[isleap(y)];
-        if (increment_overflow(&y, 1))
+        if (add_with_overflow_detection(&y, 1))
             return NULL;
     }
     tmp->tm_year = y;
-    if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+    if (add_with_overflow_detection(&tmp->tm_year, -TM_YEAR_BASE))
         return NULL;
     tmp->tm_yday = idays;
     /* The "extra" mods below avoid overflow problems. */
diff --git a/libs/spandsp/src/v18.c b/libs/spandsp/src/v18.c
index bb26644b62..b35d5fa4e9 100644
--- a/libs/spandsp/src/v18.c
+++ b/libs/spandsp/src/v18.c
@@ -567,9 +567,11 @@ SPAN_DECLARE(uint8_t) v18_decode_baudot(v18_state_t *s, uint8_t ch)
 
 static void v18_rx_dtmf(void *user_data, const char digits[], int len)
 {
+#if 0
     v18_state_t *s;
 
     s = (v18_state_t *) user_data;
+#endif
 }
 /*- End of function --------------------------------------------------------*/
 
diff --git a/libs/spandsp/src/v8.c b/libs/spandsp/src/v8.c
index 78f2904bac..906f96ba95 100644
--- a/libs/spandsp/src/v8.c
+++ b/libs/spandsp/src/v8.c
@@ -124,7 +124,7 @@ SPAN_DECLARE(const char *) v8_call_function_to_str(int call_function)
     case V8_CALL_FUNCTION_EXTENSION:
         return "Call function is in extension octet";
     }
-    return "???";
+    return "Unknown call function";
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -199,7 +199,7 @@ SPAN_DECLARE(const char *) v8_pstn_access_to_str(int pstn_access)
     case V8_PSTN_ACCESS_DCE_ON_DIGITAL | V8_PSTN_ACCESS_ANSWER_DCE_CELLULAR | V8_PSTN_ACCESS_CALL_DCE_CELLULAR:
         return "DCE on digital, and answering and calling modems on cellular";
     }
-    return "???";
+    return "PSTN access unknown";
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -235,7 +235,7 @@ SPAN_DECLARE(const char *) v8_pcm_modem_availability_to_str(int pcm_modem_availa
     case V8_PSTN_PCM_MODEM_V91 | V8_PSTN_PCM_MODEM_V90_V92_DIGITAL | V8_PSTN_PCM_MODEM_V90_V92_ANALOGUE:
         return "V.91 and V.90/V.92 digital/analogue available";
     }
-    return "???";
+    return "PCM availability unknown";
 }
 /*- End of function --------------------------------------------------------*/
 
@@ -374,6 +374,7 @@ static const uint8_t *process_pstn_access(v8_state_t *s, const uint8_t *p)
 
 static const uint8_t *process_non_standard_facilities(v8_state_t *s, const uint8_t *p)
 {
+    /* TODO: This is wrong */
     s->result.nsf = (*p >> 5) & 0x07;
     span_log(&s->logging, SPAN_LOG_FLOW, "%s\n", v8_nsf_to_str(s->result.nsf));
     return p;
diff --git a/libs/spandsp/tests/Makefile.am b/libs/spandsp/tests/Makefile.am
index 401e8f41e6..c370f20a46 100644
--- a/libs/spandsp/tests/Makefile.am
+++ b/libs/spandsp/tests/Makefile.am
@@ -50,7 +50,8 @@ INCLUDES = -I$(top_builddir)/src -I$(top_builddir)/spandsp-sim -DDATADIR="\"$(pk
 
 LIBDIR = -L$(top_builddir)/src
 
-noinst_PROGRAMS =   adsi_tests \
+noinst_PROGRAMS =   ademco_contactid_tests \
+                    adsi_tests \
                     async_tests \
                     at_interpreter_tests \
                     awgn_tests \
@@ -134,6 +135,9 @@ noinst_HEADERS =    echo_monitor.h \
                     pcap_parse.h \
                     udptl.h
 
+ademco_contactid_tests_SOURCES = ademco_contactid_tests.c
+ademco_contactid_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp
+
 adsi_tests_SOURCES = adsi_tests.c
 adsi_tests_LDADD = -L$(top_builddir)/spandsp-sim -lspandsp-sim $(LIBDIR) -lspandsp
 
diff --git a/libs/spandsp/tests/ademco_contactid_tests.c b/libs/spandsp/tests/ademco_contactid_tests.c
new file mode 100644
index 0000000000..c8e87f1fb8
--- /dev/null
+++ b/libs/spandsp/tests/ademco_contactid_tests.c
@@ -0,0 +1,391 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ademco_contactid.c - Ademco ContactID alarm protocol
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2012 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page ademco_contactid_tests_page Ademco ContactID tests
+\section ademco_contactid_tests_page_sec_1 What does it do?
+
+\section ademco_contactid_tests_page_sec_2 How does it work?
+*/
+
+/* Enable the following definition to enable direct probing into the FAX structures */
+//#define WITH_SPANDSP_INTERNALS
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <sndfile.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+//#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+#include "spandsp-sim.h"
+
+#define SAMPLES_PER_CHUNK           160
+
+#define OUTPUT_FILE_NAME            "ademco_contactid.wav"
+
+#define MITEL_DIR                   "../test-data/mitel/"
+#define BELLCORE_DIR                "../test-data/bellcore/"
+
+const char *bellcore_files[] =
+{
+    MITEL_DIR    "mitel-cm7291-talkoff.wav",
+    BELLCORE_DIR "tr-tsy-00763-1.wav",
+    BELLCORE_DIR "tr-tsy-00763-2.wav",
+    BELLCORE_DIR "tr-tsy-00763-3.wav",
+    BELLCORE_DIR "tr-tsy-00763-4.wav",
+    BELLCORE_DIR "tr-tsy-00763-5.wav",
+    BELLCORE_DIR "tr-tsy-00763-6.wav",
+    ""
+};
+
+static const ademco_contactid_report_t reports[] =
+{
+    {0x1234, 0x18, 0x1, 0x131, 0x1, 0x15},
+    {0x1234, 0x18, 0x3, 0x131, 0x1, 0x15},
+    {0x1234, 0x18, 0x1, 0x401, 0x2, 0x3},
+    {0x1234, 0x18, 0x3, 0x401, 0x3, 0x5},
+    {0x1234, 0x56, 0x7, 0x890, 0xBC, 0xDEF},
+    {0x1234, 0x56, 0x7, 0x89A, 0xBC, 0xDEF}     /* This one is bad, as it contains a hex 'A' */
+};
+static int reports_entry = 0;
+
+static int16_t amp[1000000];
+
+int tx_callback_reported = FALSE;
+int rx_callback_reported = FALSE;
+
+int sending_complete = FALSE;
+
+SNDFILE *outhandle;
+
+static void talkoff_tx_callback(void *user_data, int tone, int level, int duration)
+{
+    printf("Ademco sender report %d\n", tone);
+    tx_callback_reported = TRUE;
+}
+
+static int mitel_cm7291_side_2_and_bellcore_tests(void)
+{
+    int j;
+    SNDFILE *inhandle;
+    int frames;
+    ademco_contactid_sender_state_t *sender;
+    logging_state_t *logging;
+
+    if ((sender = ademco_contactid_sender_init(NULL, talkoff_tx_callback, NULL)) == NULL)
+        return -1;
+    logging = ademco_contactid_sender_get_logging_state(sender);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+    span_log_set_tag(logging, "Ademco-tx");
+
+    tx_callback_reported = FALSE;
+
+    /* The remainder of the Mitel tape is the talk-off test */
+    /* Here we use the Bellcore test tapes (much tougher), in six
+      files - 1 from each side of the original 3 cassette tapes */
+    /* Bellcore say you should get no more than 470 false detections with
+       a good receiver. Dialogic claim 20. Of course, we can do better than
+       that, eh? */
+    printf("Talk-off test\n");
+    for (j = 0;  bellcore_files[j][0];  j++)
+    {
+        if ((inhandle = sf_open_telephony_read(bellcore_files[j], 1)) == NULL)
+        {
+            printf("    Cannot open speech file '%s'\n", bellcore_files[j]);
+            return -1;
+        }
+        while ((frames = sf_readf_short(inhandle, amp, SAMPLE_RATE)))
+        {
+            ademco_contactid_sender_rx(sender, amp, frames);
+        }
+        if (sf_close_telephony(inhandle))
+        {
+            printf("    Cannot close speech file '%s'\n", bellcore_files[j]);
+            return -1;
+        }
+        printf("    File %d gave %d false hits.\n", j + 1, 0);
+    }
+    if (tx_callback_reported)
+    {
+        printf("    Failed\n");
+        return -1;
+    }
+    printf("    Passed\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void rx_callback(void *user_data, const ademco_contactid_report_t *report)
+{
+    printf("Ademco Contact ID message:\n");
+    printf("    Account %X\n", report->acct);
+    printf("    Message type %X\n", report->mt);
+    printf("    Qualifier %X\n", report->q);
+    printf("    Event %X\n", report->xyz);
+    printf("    Group/partition %X\n", report->gg);
+    printf("    User/Zone information %X\n", report->ccc);
+    if (memcmp(&reports[reports_entry], report, sizeof (*report)))
+    {
+        printf("Report mismatch\n");
+        exit(2);
+    }
+    rx_callback_reported = TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void tx_callback(void *user_data, int tone, int level, int duration)
+{
+    ademco_contactid_sender_state_t *sender;
+
+    sender = (ademco_contactid_sender_state_t *) user_data;
+    printf("Ademco sender report %d\n", tone);
+    switch (tone)
+    {
+    case -1:
+        /* We are connected and ready to send */
+        ademco_contactid_sender_put(sender, &reports[reports_entry]);
+        break;
+    case 1:
+        /* We have succeeded in sending, and are ready to send another message. */
+        if (++reports_entry < 5)
+            ademco_contactid_sender_put(sender, &reports[reports_entry]);
+        else
+            sending_complete = TRUE;
+        break;
+    case 0:
+        /* Sending failed after retries */
+        sending_complete = TRUE;
+        break;
+    }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int end_to_end_tests(void)
+{
+    ademco_contactid_receiver_state_t *receiver;
+    ademco_contactid_sender_state_t *sender;
+    logging_state_t *logging;
+    codec_munge_state_t *munge;
+    awgn_state_t noise_source;
+    int16_t amp[SAMPLES_PER_CHUNK];
+    int16_t sndfile_buf[2*SAMPLES_PER_CHUNK];
+    int samples;
+    int i;
+    int j;
+
+    printf("End to end tests\n");
+
+    if ((outhandle = sf_open_telephony_write(OUTPUT_FILE_NAME, 2)) == NULL)
+    {
+        fprintf(stderr, "    Cannot open audio file '%s'\n", OUTPUT_FILE_NAME);
+        exit(2);
+    }
+
+    if ((receiver = ademco_contactid_receiver_init(NULL, rx_callback, NULL)) == NULL)
+        return -1;
+    ademco_contactid_receiver_set_realtime_callback(receiver, rx_callback, receiver);
+
+    logging = ademco_contactid_receiver_get_logging_state(receiver);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+    span_log_set_tag(logging, "Ademco-rx");
+
+    if ((sender = ademco_contactid_sender_init(NULL, tx_callback, NULL)) == NULL)
+        return -1;
+    ademco_contactid_sender_set_realtime_callback(sender, tx_callback, sender);
+    logging = ademco_contactid_sender_get_logging_state(sender);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+    span_log_set_tag(logging, "Ademco-tx");
+
+    awgn_init_dbm0(&noise_source, 1234567, -50);
+    munge = codec_munge_init(MUNGE_CODEC_ALAW, 0);
+
+    sending_complete = FALSE;
+    rx_callback_reported = FALSE;
+
+    for (i = 0;  i < 1000;  i++)
+    {
+        samples = ademco_contactid_sender_tx(sender, amp, SAMPLES_PER_CHUNK);
+        for (j = samples;  j < SAMPLES_PER_CHUNK;  j++)
+            amp[j] = 0;
+        for (j = 0;  j < SAMPLES_PER_CHUNK;  j++)
+            sndfile_buf[2*j] = amp[j];
+        /* There is no point in impairing this signal. It is just DTMF tones, which
+           will work as wel as the DTMF detector beign used. */
+        ademco_contactid_receiver_rx(receiver, amp, SAMPLES_PER_CHUNK);
+
+        samples = ademco_contactid_receiver_tx(receiver, amp, SAMPLES_PER_CHUNK);
+        for (j = samples;  j < SAMPLES_PER_CHUNK;  j++)
+            amp[j] = 0;
+        
+        /* We add AWGN and codec impairments to the signal, to stress the tone detector. */
+        codec_munge(munge, amp, SAMPLES_PER_CHUNK);
+        for (j = 0;  j < SAMPLES_PER_CHUNK;  j++)
+        {
+            sndfile_buf[2*j + 1] = amp[j];
+            /* Add noise to the tones */
+            amp[j] += awgn(&noise_source);
+        }
+        codec_munge(munge, amp, SAMPLES_PER_CHUNK);
+        ademco_contactid_sender_rx(sender, amp, SAMPLES_PER_CHUNK);
+
+        sf_writef_short(outhandle, sndfile_buf, SAMPLES_PER_CHUNK);
+    }
+    if (!rx_callback_reported)
+    {
+        fprintf(stderr, "    Report not received\n");
+        return -1;
+    }
+
+    if (sf_close_telephony(outhandle))
+    {
+        fprintf(stderr, "    Cannot close audio file '%s'\n", OUTPUT_FILE_NAME);
+        return -1;
+    }
+    printf("    Passed\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_decode_tests(void)
+{
+    char buf[100];
+    ademco_contactid_receiver_state_t *receiver;
+    ademco_contactid_sender_state_t *sender;
+    logging_state_t *logging;
+    ademco_contactid_report_t result;
+    int i;
+
+    printf("Encode and decode tests\n");
+
+    if ((receiver = ademco_contactid_receiver_init(NULL, NULL, NULL)) == NULL)
+        return 2;
+    logging = ademco_contactid_receiver_get_logging_state(receiver);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+    span_log_set_tag(logging, "Ademco-rx");
+
+    if ((sender = ademco_contactid_sender_init(NULL, NULL, NULL)) == NULL)
+        return 2;
+    logging = ademco_contactid_sender_get_logging_state(sender);
+    span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+    span_log_set_tag(logging, "Ademco-tx");
+
+    for (i = 0;  i < 5;  i++)
+    {
+        if (encode_msg(buf, &reports[i]) < 0)
+        {
+            printf("Bad encode message\n");
+            return -1;
+        }
+        printf("'%s'\n", buf);
+        if (decode_msg(&result, buf))
+        {
+            printf("Bad decode message\n");
+            return -1;
+        }
+        ademco_contactid_receiver_log_msg(receiver, &result);
+        printf("\n");
+        if (memcmp(&reports[i], &result, sizeof(result)))
+        {
+            printf("Received message does not match the one sent\n");
+            return -1;
+        }
+    }
+
+    if (encode_msg(buf, &reports[5]) >= 0)
+    {
+        printf("Incorrectly good message\n");
+        return -1;
+    }
+    printf("'%s'\n", buf);
+    printf("\n");
+    printf("    Passed\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void decode_file(const char *file)
+{
+    //SPAN_DECLARE(int) decode_msg(ademco_contactid_report_t *report, const char buf[])
+}
+/*- End of function --------------------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+    int opt;
+    const char *decode_test_file;
+
+    decode_test_file = NULL;
+    while ((opt = getopt(argc, argv, "d:")) != -1)
+    {
+        switch (opt)
+        {
+        case 'd':
+            decode_test_file = optarg;
+            break;
+        default:
+            //usage();
+            exit(2);
+            break;
+        }
+    }
+
+    if (decode_test_file)
+    {
+        decode_file(decode_test_file);
+        return 0;
+    }
+
+    if (encode_decode_tests())
+    {
+        printf("Tests failed\n");
+        return 2;
+    }
+
+    if (mitel_cm7291_side_2_and_bellcore_tests())
+    {
+        printf("Tests failed\n");
+        return 2;
+    }
+
+    if (end_to_end_tests())
+    {
+        printf("Tests failed\n");
+        return 2;
+    }
+
+    printf("Tests passed\n");
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/libs/spandsp/tests/echo_tests.c b/libs/spandsp/tests/echo_tests.c
index 86b6cda961..2bf0d68799 100644
--- a/libs/spandsp/tests/echo_tests.c
+++ b/libs/spandsp/tests/echo_tests.c
@@ -633,20 +633,20 @@ static int perform_test_sanity(void)
     int16_t tx;
     int16_t clean;
     int far_tx;
-    int16_t far_sound[SAMPLE_RATE];
+    //int16_t far_sound[SAMPLE_RATE];
     int16_t result_sound[64000];
     int result_cur;
     int outframes;
-    int local_cur;
-    int far_cur;
+    //int local_cur;
+    //int far_cur;
     //int32_t coeffs[200][128];
     //int coeff_index;
 
     print_test_title("Performing basic sanity test\n");
     ctx = echo_can_init(TEST_EC_TAPS, 0);
 
-    local_cur = 0;
-    far_cur = 0;
+    //local_cur = 0;
+    //far_cur = 0;
     result_cur = 0;
 
     echo_can_flush(ctx);
@@ -684,7 +684,7 @@ static int perform_test_sanity(void)
             far_tx = 0;
         }
 #else
-        far_sound[0] = 0;
+        //far_sound[0] = 0;
         far_tx = 0;
 #endif
         rx = channel_model(&chan_model, tx, far_tx);
diff --git a/libs/spandsp/tests/fax_decode.c b/libs/spandsp/tests/fax_decode.c
index dd60fe9280..e175a6168b 100644
--- a/libs/spandsp/tests/fax_decode.c
+++ b/libs/spandsp/tests/fax_decode.c
@@ -89,7 +89,7 @@ int decode_test = FALSE;
 int rx_bits = 0;
 
 t30_state_t t30_dummy;
-t4_state_t t4_state;
+t4_state_t t4_rx_state;
 int t4_up = FALSE;
 
 hdlc_rx_state_t hdlcrx;
@@ -291,12 +291,12 @@ static void t4_begin(void)
     int i;
 
     //printf("Begin T.4 - %d %d %d %d\n", line_encoding, x_resolution, y_resolution, image_width);
-    t4_rx_set_rx_encoding(&t4_state, line_encoding);
-    t4_rx_set_x_resolution(&t4_state, x_resolution);
-    t4_rx_set_y_resolution(&t4_state, y_resolution);
-    t4_rx_set_image_width(&t4_state, image_width);
+    t4_rx_set_rx_encoding(&t4_rx_state, line_encoding);
+    t4_rx_set_x_resolution(&t4_rx_state, x_resolution);
+    t4_rx_set_y_resolution(&t4_rx_state, y_resolution);
+    t4_rx_set_image_width(&t4_rx_state, image_width);
 
-    t4_rx_start_page(&t4_state);
+    t4_rx_start_page(&t4_rx_state);
     t4_up = TRUE;
 
     for (i = 0;  i < 256;  i++)
@@ -316,13 +316,13 @@ static void t4_end(void)
         for (i = 0;  i < 256;  i++)
         {
             if (ecm_len[i] > 0)
-                t4_rx_put_chunk(&t4_state, ecm_data[i], ecm_len[i]);
+                t4_rx_put_chunk(&t4_rx_state, ecm_data[i], ecm_len[i]);
             fprintf(stderr, "%d", (ecm_len[i] <= 0)  ?  0  :  1);
         }
         fprintf(stderr, "\n");
     }
-    t4_rx_end_page(&t4_state);
-    t4_rx_get_transfer_statistics(&t4_state, &stats);
+    t4_rx_end_page(&t4_rx_state);
+    t4_rx_get_transfer_statistics(&t4_rx_state, &stats);
     fprintf(stderr, "Pages = %d\n", stats.pages_transferred);
     fprintf(stderr, "Image size = %dx%d\n", stats.width, stats.length);
     fprintf(stderr, "Image resolution = %dx%d\n", stats.x_resolution, stats.y_resolution);
@@ -378,7 +378,7 @@ static void v17_put_bit(void *user_data, int bit)
     }
     else
     {
-        if (t4_rx_put_bit(&t4_state, bit))
+        if (t4_rx_put_bit(&t4_rx_state, bit))
         {
             t4_end();
             fprintf(stderr, "End of page detected\n");
@@ -414,7 +414,7 @@ static void v29_put_bit(void *user_data, int bit)
     }
     else
     {
-        if (t4_rx_put_bit(&t4_state, bit))
+        if (t4_rx_put_bit(&t4_rx_state, bit))
         {
             t4_end();
             fprintf(stderr, "End of page detected\n");
@@ -450,7 +450,7 @@ static void v27ter_put_bit(void *user_data, int bit)
     }
     else
     {
-        if (t4_rx_put_bit(&t4_state, bit))
+        if (t4_rx_put_bit(&t4_rx_state, bit))
         {
             t4_end();
             fprintf(stderr, "End of page detected\n");
@@ -527,7 +527,7 @@ int main(int argc, char *argv[])
     span_log_set_level(logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_FLOW);
 #endif
 
-    if (t4_rx_init(&t4_state, "fax_decode.tif", T4_COMPRESSION_ITU_T4_2D) == NULL)
+    if (t4_rx_init(&t4_rx_state, "fax_decode.tif", T4_COMPRESSION_ITU_T4_2D) == NULL)
     {
         fprintf(stderr, "Failed to init\n");
         exit(0);
@@ -543,7 +543,7 @@ int main(int argc, char *argv[])
         v29_rx(v29, amp, len);
         //v27ter_rx(v27ter, amp, len);
     }
-    t4_rx_release(&t4_state);
+    t4_rx_release(&t4_rx_state);
 
     if (sf_close(inhandle))
     {
diff --git a/libs/spandsp/tests/fax_tester.c b/libs/spandsp/tests/fax_tester.c
index 79e8a09ae8..20af011981 100644
--- a/libs/spandsp/tests/fax_tester.c
+++ b/libs/spandsp/tests/fax_tester.c
@@ -271,14 +271,11 @@ static void non_ecm_rx_status(void *user_data, int status)
 
 static void non_ecm_put_bit(void *user_data, int bit)
 {
-    faxtester_state_t *s;
-
     if (bit < 0)
     {
         non_ecm_rx_status(user_data, bit);
         return;
     }
-    s = (faxtester_state_t *) user_data;
 }
 /*- End of function --------------------------------------------------------*/
 
diff --git a/libs/spandsp/tests/fax_tests.sh b/libs/spandsp/tests/fax_tests.sh
index bd29f3bf34..3c78fe51a2 100755
--- a/libs/spandsp/tests/fax_tests.sh
+++ b/libs/spandsp/tests/fax_tests.sh
@@ -18,7 +18,7 @@
 run_fax_test()
 {
     rm -f fax_tests_1.tif
-    echo -i ${FILE} ${OPTS} -i ${FILE}
+    echo ./fax_tests -i ${FILE} ${OPTS} -i ${FILE}
     ./fax_tests ${OPTS} -i ${FILE} >xyzzy 2>xyzzy2
     RETVAL=$?
     if [ $RETVAL != 0 ]
@@ -28,7 +28,7 @@ run_fax_test()
     fi
     # Now use tiffcmp to check the results. It will return non-zero if any page images differ. The -t
     # option means the normal differences in tags will be ignored.
-    tiffcmp -t ${FILE} fax_tests.tif #>/dev/null
+    tiffcmp -t ${FILE} fax_tests.tif >/dev/null
     RETVAL=$?
     if [ $RETVAL != 0 ]
     then
diff --git a/libs/spandsp/tests/g722_tests.c b/libs/spandsp/tests/g722_tests.c
index c33d9e67b6..863ea3e63d 100644
--- a/libs/spandsp/tests/g722_tests.c
+++ b/libs/spandsp/tests/g722_tests.c
@@ -307,7 +307,7 @@ static void itu_compliance_tests(void)
             /* Get the upper reference output data */
             len_comp_upper = get_test_vector(decode_test_files[file + 4], itu_ref_upper, MAX_TEST_VECTOR_LEN);
 
-            if (len_data != len_comp_lower  ||  len_data != len_comp_lower)
+            if (len_data != len_comp_lower  ||  len_data != len_comp_upper)
             {
                 printf("Test data length mismatch\n");
                 exit(2);
diff --git a/libs/spandsp/tests/regression_tests.sh b/libs/spandsp/tests/regression_tests.sh
index 4f32ebbc32..fe1e1a8ad9 100755
--- a/libs/spandsp/tests/regression_tests.sh
+++ b/libs/spandsp/tests/regression_tests.sh
@@ -634,7 +634,7 @@ do
 done
 echo v27ter_tests completed OK
 
-for OPTS in "-b 9600 -s -42 -n -62" "-b 7200 -s -42 -n -58" "-b 4800 -s -42 -n -55"
+for OPTS in "-b 9600 -s -42 -n -62" "-b 7200 -s -42 -n -59" "-b 4800 -s -42 -n -55"
 do
     ./v29_tests ${OPTS} >$STDOUT_DEST 2>$STDERR_DEST
     RETVAL=$?
diff --git a/libs/spandsp/tests/schedule_tests.c b/libs/spandsp/tests/schedule_tests.c
index 81c467c927..a9fa466199 100644
--- a/libs/spandsp/tests/schedule_tests.c
+++ b/libs/spandsp/tests/schedule_tests.c
@@ -86,7 +86,7 @@ int main(int argc, char *argv[])
     int i;
     span_sched_state_t sched;
     uint64_t when;
-    
+
     span_schedule_init(&sched);
 
     span_schedule_event(&sched, 500000, callback1, NULL);
diff --git a/libs/spandsp/tests/super_tone_rx_tests.c b/libs/spandsp/tests/super_tone_rx_tests.c
index 717768d0c5..b9b673855d 100644
--- a/libs/spandsp/tests/super_tone_rx_tests.c
+++ b/libs/spandsp/tests/super_tone_rx_tests.c
@@ -410,7 +410,6 @@ static int detection_range_tests(super_tone_rx_state_t *super)
     int16_t amp[SAMPLES_PER_CHUNK];
     int i;
     int j;
-    int x;
     uint32_t phase;
     int32_t phase_inc;
     int scale;
@@ -427,7 +426,7 @@ static int detection_range_tests(super_tone_rx_state_t *super)
         {
             for (i = 0;  i < SAMPLES_PER_CHUNK;  i++)
                 amp[i] = (dds(&phase, phase_inc)*scale) >> 15;
-            x = super_tone_rx(super, amp, SAMPLES_PER_CHUNK);
+            super_tone_rx(super, amp, SAMPLES_PER_CHUNK);
         }
     }
     return 0;
diff --git a/libs/spandsp/tests/tsb85_tests.c b/libs/spandsp/tests/tsb85_tests.c
index aa78e5dfba..74587fbb90 100644
--- a/libs/spandsp/tests/tsb85_tests.c
+++ b/libs/spandsp/tests/tsb85_tests.c
@@ -80,6 +80,8 @@ int test_local_interrupt = FALSE;
 
 const char *output_tiff_file_name;
 
+int log_audio = FALSE;
+
 fax_state_t *fax;
 faxtester_state_t state;
 
@@ -1058,10 +1060,8 @@ static void exchange(faxtester_state_t *s)
     int len;
     int i;
     int total_audio_time;
-    int log_audio;
     logging_state_t *logging;
 
-    log_audio = TRUE;
     output_tiff_file_name = OUTPUT_TIFF_FILE_NAME;
 
     if (log_audio)
@@ -1295,10 +1295,14 @@ int main(int argc, char *argv[])
 
     xml_file_name = "../spandsp/tsb85.xml";
     test_name = "MRGN01";
-    while ((opt = getopt(argc, argv, "x:")) != -1)
+    log_audio = FALSE;
+    while ((opt = getopt(argc, argv, "lx:")) != -1)
     {
         switch (opt)
         {
+        case 'l':
+            log_audio = TRUE;
+            break;
         case 'x':
             xml_file_name = optarg;
             break;
diff --git a/src/include/switch_event.h b/src/include/switch_event.h
index 5909c0e0c0..d0474a6929 100644
--- a/src/include/switch_event.h
+++ b/src/include/switch_event.h
@@ -403,6 +403,7 @@ SWITCH_DECLARE(void) switch_event_deliver(switch_event_t **event);
 SWITCH_DECLARE(char *) switch_event_build_param_string(switch_event_t *event, const char *prefix, switch_hash_t *vars_map);
 SWITCH_DECLARE(int) switch_event_check_permission_list(switch_event_t *list, const char *name);
 SWITCH_DECLARE(void) switch_event_add_presence_data_cols(switch_channel_t *channel, switch_event_t *event, const char *prefix);
+SWITCH_DECLARE(void) switch_event_launch_dispatch_threads(uint32_t max);
 
 ///\}
 
diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c
index c98db6a04b..344544f705 100755
--- a/src/mod/applications/mod_dptools/mod_dptools.c
+++ b/src/mod/applications/mod_dptools/mod_dptools.c
@@ -2869,6 +2869,10 @@ SWITCH_STANDARD_APP(audio_bridge_function)
 			moh = switch_channel_get_variable(caller_channel, "campon_hold_music");
 		}
 
+		if (!zstr(moh) && !strcasecmp(moh, "silence")) { 
+			moh = NULL;
+		}
+
 		do {
 			fail = 0;
 
diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c
index b08d7efc17..68763cd452 100644
--- a/src/mod/endpoints/mod_sofia/sofia_glue.c
+++ b/src/mod/endpoints/mod_sofia/sofia_glue.c
@@ -5764,6 +5764,8 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
 		switch_channel_set_variable(channel, "sip_handle_full_to", switch_channel_get_variable(channel, "sip_full_to"));
 	} else {
 		
+		int break_rfc = switch_true(switch_channel_get_variable(channel, "sip_recovery_break_rfc"));
+
 		tech_pvt->redirected = switch_core_session_sprintf(session, "sip:%s", switch_channel_get_variable(channel, "sip_contact_uri"));
 
 		if (zstr(rr)) {
@@ -5776,11 +5778,11 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName
 		tech_pvt->dest = switch_core_session_sprintf(session, "sip:%s", switch_channel_get_variable(channel, "sip_from_uri"));
 
 		if (!switch_channel_get_variable_dup(channel, "sip_handle_full_from", SWITCH_FALSE, -1)) {
-			switch_channel_set_variable(channel, "sip_handle_full_from", switch_channel_get_variable(channel, "sip_full_to"));
+			switch_channel_set_variable(channel, "sip_handle_full_from", switch_channel_get_variable(channel, break_rfc ? "sip_full_from" : "sip_full_to"));
 		}
 
 		if (!switch_channel_get_variable_dup(channel, "sip_handle_full_to", SWITCH_FALSE, -1)) {
-			switch_channel_set_variable(channel, "sip_handle_full_to", switch_channel_get_variable(channel, "sip_full_from"));
+			switch_channel_set_variable(channel, "sip_handle_full_to", switch_channel_get_variable(channel, break_rfc ? "sip_full_to" : "sip_full_from"));
 		}
 	}
 
diff --git a/src/switch_core.c b/src/switch_core.c
index d706563f83..2a4cae408f 100644
--- a/src/switch_core.c
+++ b/src/switch_core.c
@@ -1538,7 +1538,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc
 	switch_log_init(runtime.memory_pool, runtime.colorize_console);
 
 	if (flags & SCF_MINIMAL) return SWITCH_STATUS_SUCCESS;
-													   
+			
 	runtime.tipping_point = 0;
 	runtime.timer_affinity = -1;
 	runtime.microseconds_per_tick = 20000;
@@ -1813,6 +1813,23 @@ static void switch_load_core_config(const char *file)
 					switch_core_min_idle_cpu(atof(val));
 				} else if (!strcasecmp(var, "tipping-point") && !zstr(val)) {
 					runtime.tipping_point = atoi(val);
+				} else if (!strcasecmp(var, "initial-event-threads") && !zstr(val)) {
+					int tmp = atoi(val);
+
+
+					if (tmp > runtime.cpu_count / 2) {
+						tmp = runtime.cpu_count / 2;
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be higher than %d so setting it to that value\n", 
+										  runtime.cpu_count / 2);
+					}
+
+					if (tmp < 1) {
+						tmp = 1;
+						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be lower than 1 so setting it to that level\n");
+					}
+
+					switch_event_launch_dispatch_threads(tmp);
+
 				} else if (!strcasecmp(var, "1ms-timer") && switch_true(val)) {
 					runtime.microseconds_per_tick = 1000;
 				} else if (!strcasecmp(var, "timer-affinity") && !zstr(val)) {
diff --git a/src/switch_event.c b/src/switch_event.c
index defae3586c..0f08b496c0 100644
--- a/src/switch_event.c
+++ b/src/switch_event.c
@@ -34,6 +34,7 @@
 
 #include <switch.h>
 #include <switch_event.h>
+
 //#define SWITCH_EVENT_RECYCLE
 #define DISPATCH_QUEUE_LEN 100
 //#define DEBUG_DISPATCH_QUEUES
@@ -87,7 +88,6 @@ static uint64_t EVENT_SEQUENCE_NR = 0;
 static switch_queue_t *EVENT_RECYCLE_QUEUE = NULL;
 static switch_queue_t *EVENT_HEADER_RECYCLE_QUEUE = NULL;
 #endif
-static void launch_dispatch_threads(uint32_t max, switch_memory_pool_t *pool);
 
 static char *my_dup(const char *s)
 {
@@ -305,7 +305,7 @@ static switch_status_t switch_event_queue_dispatch_event(switch_event_t **eventp
 		
 		if (launch) {
 			if (SOFT_MAX_DISPATCH + 1 < MAX_DISPATCH) {
-				launch_dispatch_threads(SOFT_MAX_DISPATCH + 1, RUNTIME_POOL);
+				switch_event_launch_dispatch_threads(SOFT_MAX_DISPATCH + 1);
 			}
 		}
 
@@ -515,13 +515,15 @@ SWITCH_DECLARE(switch_status_t) switch_event_shutdown(void)
 	return SWITCH_STATUS_SUCCESS;
 }
 
-static void launch_dispatch_threads(uint32_t max, switch_memory_pool_t *pool)
+SWITCH_DECLARE(void) switch_event_launch_dispatch_threads(uint32_t max)
 {
 	switch_threadattr_t *thd_attr;
 	uint32_t index = 0;
 	int launched = 0;
 	uint32_t sanity = 200;
 
+	switch_memory_pool_t *pool = RUNTIME_POOL;
+
 	if (max > MAX_DISPATCH) {
 		return;
 	}
@@ -532,6 +534,7 @@ static void launch_dispatch_threads(uint32_t max, switch_memory_pool_t *pool)
 
 	for (index = SOFT_MAX_DISPATCH; index < max && index < MAX_DISPATCH; index++) {
 		if (EVENT_DISPATCH_QUEUE_THREADS[index]) {
+			printf("Index exists continue\n");
 			continue;
 		}
 
@@ -540,13 +543,13 @@ static void launch_dispatch_threads(uint32_t max, switch_memory_pool_t *pool)
 		switch_threadattr_priority_increase(thd_attr);
 		switch_thread_create(&EVENT_DISPATCH_QUEUE_THREADS[index], thd_attr, switch_event_dispatch_thread, EVENT_DISPATCH_QUEUE, pool);
 		while(--sanity && !EVENT_DISPATCH_QUEUE_RUNNING[index]) switch_yield(10000);
+
 		if (index == 1) {
-			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Create event dispatch thread %d\n", index);
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Create event dispatch thread %d\n", index);
 		} else {
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Create additional event dispatch thread %d\n", index);
 		}
 		launched++;
-		break;
 	}
 
 	SOFT_MAX_DISPATCH = index;
@@ -598,7 +601,7 @@ SWITCH_DECLARE(switch_status_t) switch_event_init(switch_memory_pool_t *pool)
 	//switch_threadattr_priority_increase(thd_attr);
 
 	switch_queue_create(&EVENT_DISPATCH_QUEUE, DISPATCH_QUEUE_LEN * MAX_DISPATCH, pool);
-	launch_dispatch_threads(1, RUNTIME_POOL);
+	switch_event_launch_dispatch_threads(1);
 
 	//switch_thread_create(&EVENT_QUEUE_THREADS[0], thd_attr, switch_event_thread, EVENT_QUEUE[0], RUNTIME_POOL);
 	//switch_thread_create(&EVENT_QUEUE_THREADS[1], thd_attr, switch_event_thread, EVENT_QUEUE[1], RUNTIME_POOL);
diff --git a/src/switch_ivr.c b/src/switch_ivr.c
index 8f50fe1257..cac064b544 100644
--- a/src/switch_ivr.c
+++ b/src/switch_ivr.c
@@ -1734,7 +1734,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_
 														   (long) switch_epoch_time_now(NULL), new_profile->uuid_str,
 														   extension, use_context, use_dialplan);
 		switch_channel_add_variable_var_check(channel, SWITCH_TRANSFER_HISTORY_VARIABLE, new_profile->transfer_source, SWITCH_FALSE, SWITCH_STACK_PUSH);
-		switch_channel_set_variable(channel, SWITCH_TRANSFER_SOURCE_VARIABLE, new_profile->transfer_source);
+		switch_channel_set_variable_var_check(channel, SWITCH_TRANSFER_SOURCE_VARIABLE, new_profile->transfer_source, SWITCH_FALSE);
 		return SWITCH_STATUS_SUCCESS;
 	}
 
diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c
index 72d0ab0393..6d3ad966ec 100644
--- a/src/switch_ivr_originate.c
+++ b/src/switch_ivr_originate.c
@@ -2331,7 +2331,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
 					q = !q;
 				}
 
-				if (end && p < end && *p == ',') {
+				if (end && p < end && *p == ',' && *(p-1) != '\\') {
 					
 					if (q || alt) {
 						*p = QUOTED_ESC_COMMA;
diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c
index 8738071cb4..c15da6fb03 100644
--- a/src/switch_ivr_play_say.c
+++ b/src/switch_ivr_play_say.c
@@ -1879,6 +1879,12 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_read(switch_core_session_t *session,
 	switch_status_t status = SWITCH_STATUS_SUCCESS;
 	size_t len = 0;
 	char tb[2] = "";
+	int term_required = 0;
+	
+
+	if (valid_terminators && *valid_terminators == '=') {
+		term_required = 1;
+	}
 
 	switch_assert(session);
 
@@ -1945,6 +1951,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_read(switch_core_session_t *session,
 				}
 			}
 		}
+	} else if (term_required) {
+		status = SWITCH_STATUS_TOO_SMALL;
 	}
 
 	len = strlen(digit_buffer);
diff --git a/src/switch_log.c b/src/switch_log.c
index 1bd931a44e..9799a3698f 100644
--- a/src/switch_log.c
+++ b/src/switch_log.c
@@ -296,6 +296,7 @@ static void *SWITCH_THREAD_FUNC log_thread(switch_thread_t *t, void *obj)
 		}
 
 		if (!pop) {
+			THREAD_RUNNING = -1;
 			break;
 		}
 
@@ -558,7 +559,7 @@ SWITCH_DECLARE(switch_status_t) switch_log_shutdown(void)
 {
 	switch_status_t st;
 
-	THREAD_RUNNING = -1;
+
 	switch_queue_push(LOG_QUEUE, NULL);
 	while (THREAD_RUNNING) {
 		switch_cond_next();
diff --git a/src/switch_rtp.c b/src/switch_rtp.c
index 33be911b2c..c8e6ff94cb 100644
--- a/src/switch_rtp.c
+++ b/src/switch_rtp.c
@@ -2883,7 +2883,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
 #endif
 			
 #ifdef ENABLE_SRTP
-			if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_RECV) && rtp_session->rtcp_recv_msg.header.version == 2) {
+			if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_RECV)) {
 				int sbytes = (int) *bytes;
 				err_status_t stat = 0;
 
@@ -3002,7 +3002,7 @@ static switch_status_t read_rtcp_packet(switch_rtp_t *rtp_session, switch_size_t
 	}
 
 #ifdef ENABLE_SRTP
-	if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_RECV) && rtp_session->rtcp_recv_msg.header.version == 2) {
+	if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE_RECV)) {
 		int sbytes = (int) *bytes;
 		err_status_t stat = 0;